0

I am trying to add a custom command to my Cypress framework to check if elements are enabled/disabled.

Here is my attempted custom command in commands.js:

Cypress.Commands.add('isEnabled', (element) => {
    element.invoke('attr', 'class').then(classAttribute => {
        if (classAttribute.includes('mat-button-disabled')) {
            // do something
        }
    });
});

And I am trying to call this command through my step defintion here:

cy.isEnabled(widgets.getPaginatorLast());

And here is my page object model:

getPaginatorLast () {
    return cy.get('.mat-paginator-navigation-last');
}

When I run this code, I get the following error:

Timed out retrying after 20000ms: cy.invoke() errored because your subject is: null. You cannot invoke any functions such as attr on a null value.

If you expect your subject to be null, then add an assertion such as:

cy.wrap(null).should('be.null')

If I update the isEnabled() command to directly use the element like below, rather than pass in the element via my step defintion, then the class attribute is being picked up:

Cypress.Commands.add('isEnabled', (element) => {
    widgets.getPaginatorLast().invoke('attr', 'class').then(classAttribute => {
        if (classAttribute.includes('mat-button-disabled')) {
            // do something
        }
    });
});

The only change I made is replace element with widgets.getPaginatorLast(). I don't know why I'm getting the null error though because that's the value I'm passing in.

Can someone please tell me why this is happening, & what changes are required to fix it?

user9847788
  • 2,135
  • 5
  • 31
  • 79

2 Answers2

1

Cypress custom commands should be chained like this

widgets.getPaginatorLast().isEnabled()

and the custom command

Cypress.Commands.add('isEnabled', {prevSubject:true}, (element) => {
  if (element.hasClass('mat-button-disabled')) {
    // do something
  }
})

The // do something is called a "side effect", and should really be done outside the isEnabled() method

Cypress.Commands.add('isEnabled', {prevSubject:true}, (element) => {
  return !element.hasClass('mat-button-disabled')
})

widgets.getPaginatorLast().isEnabled().then(enabled => {
  if (enabled) {
    // do something
  }
})
TesterDick
  • 3,830
  • 5
  • 18
0

Cypress' .get() command yields a JQuery element, and not a Cypress-ready element. So, your getPaginatorLast() function returns a JQuery element. In order to easily use this in a Cypress chain, we just need to wrap it with cy.wrap(). The reason substituting the command works is because it is directly chained into the command, instead of resolving as a JQuery element (I'm probably misusing terms here).

Cypress.Commands.add('isEnabled', (element) => {
    cy.wrap(element).invoke('attr', 'class').then(classAttribute => {
        if (classAttribute.includes('mat-button-disabled')) {
            // do something
        }
    });
});
agoff
  • 5,818
  • 1
  • 7
  • 20
  • If the `isEnabled` will be a chained command then the `prevSubject` will need to be added to the custom command to be used as `getPaginatorLast().isEnabled()`, right? – jjhelguero May 19 '22 at 18:23
  • Hmm.. from OPs example, it looked like they just wanted to do `cy.isEnabled(widgets.getPaginatorLast())`, so they wouldn't need to write the command as a child command. But yes, if they wanted to do `getPaginatorLast().isEnabled()`, then they would need to turn `isEnabled` into a child command. – agoff May 19 '22 at 18:38
  • 1
    That is true. I made an incorrect assumption on my part. – jjhelguero May 19 '22 at 22:19
  • @agoff, are you sure it's really possible to pass a chain into a custom command as an argument? Have you ever tried this way? It looks like you would have to have 2 active chains at the moment that is not allowed in Cypress. – Mikhail Bolotov May 20 '22 at 06:12
  • It is definitely possible. Look at the Cypress documentation for dual commands -> their example says that if _some element_ is passed in, they start a new `cy.wrap()` chain. https://docs.cypress.io/api/cypress-api/custom-commands#Dual-Commands – agoff May 20 '22 at 13:23