edhel
edhel

Reputation: 203

Cypress: wrap chained commands in one

I use this long line to check the selected value on any list on my page (using Ember power-select which is not a but a complex set of ), selector being the parent so I can target the list I want, and trimming being there so I can chain a .should('eq', expected_value)

cy.get(selector).find('span[class="ember-power-select-selected-item"]').invoke("text").then((text) => text.trim())

I would love to shorten all the commands after get in one, and be able to call something like

cy.get(selector).selected_value()

I've started reading on custom commands, wrap, invoke... but am too new on Cypress to understand the right way to do this.

Upvotes: 0

Views: 1822

Answers (2)

Fody
Fody

Reputation: 31904

Creating a custom command can make your test more readable, encapsulate a group of commands that are repeated and need to be parameterized.

For your example testing the text of dropdown items, you can pass in the text and return the select so that chaining is possible

NOTES

  1. In your code, .find().invoke("text") returns all the item text in one string, so I added .contains() to select an individual item.

  2. If you're only interested in a partial match, the command chain can stop at .contain(text.trim())

Cypress.Commands.add('hasSelectedItemText',
  { prevSubject: true },
  (subject, text) => {
    cy.wrap(subject)
      .find('span[class="ember-power-select-selected-item"]')
      .contains(text.trim())
      .invoke("text")
      .should('eq', text.trim())

    cy.wrap(subject)   // returns the original select
  }
)

cy.get(selector)
  .hasSelectedItemText('one')
  .hasSelectedItemText('two')
  .hasSelectedItemText('three')

A more complicated example using the dual type command. Here the command can be parent or child, so the parameters have different meanings depending on usage


Cypress.Commands.add("dropdownItemText",
  { prevSubject: "optional" },
  (subject, arg1, arg2) => {
    if (subject) {
      const text = arg1 
      cy.wrap(subject)
        .find('span[class="ember-power-select-selected-item"]')
        .contains(text.trim())
        .invoke("text")
        .should('eq', text.trim())

      cy.wrap(subject)             // returns the original select

    } else {
      const text = arg2
      cy.get(arg1)
        .find('span[class="ember-power-select-selected-item"]')
        .contains(text.trim())
        .invoke("text")
        .should('eq', text.trim())

      cy.get(arg1)        // make select the returned "subject" for further chaining
    }
  }
)

cy.dropdownItemText(selector, 'one')
  .dropdownItemText('two')
  .dropdownItemText('three')

Upvotes: 3

Mikhail Bolotov
Mikhail Bolotov

Reputation: 1104

To add a custom command, you may add the following inside cypress/support/commands.js file:

Cypress.Commands.add('selected_value', { prevSubject: true}, (subject) => {
  return cy.wrap(subject).find('span[class="ember-power-select-selected-item"]').invoke("text").then((text) => text.trim())
})

Note that we use the prevSubject option to be able to chain our command from an initial cy.get(selector) one.

And now you are able to use the command:

   cy.get(selector).selected_value().should('eq', expected_value)

Also, it's better to provide a type script definition file for the new command so IDE can be aware of it and provide the autocomplete and other features.

Adding a command might look a bit complicated for a beginner or annoying for an experienced user. There is Cypress Pro plugin (I'm the author) for the IntelliJ platform that can simplify creating and maintaining custom commands.

Upvotes: 1

Related Questions