Cameron Hudson
Cameron Hudson

Reputation: 3931

How to use Cypress to test Angular Material app, with elements automatically inserted by Angular?

I'm having trouble writing Cypress end-to-end tests for my Angular application due to the additional nested elements that Angular inserts. For example, if I have an Angular component with this HTML:

<a href="{{loginUrl}}" mat-raised-button>
    Log In
</a>

Then I try to write a Cypress test like this:

it('has a login button with a login URL', () => {
  cy.contains('Log In')
    .should('have.attr', 'href');
});

But the test fails because Angular automatically inserts many elements, and the resulting HTML looks like this. cy.contains('Log In') picks up the span, which has no href.

<a href="login-url">
    <button class="...">
        <span>Log In</span>
    </button>
</a>

Is there a generic way to tell Cypress to "put a virtual cursor over 'Log In' and then get the corresponding href"? Or, is there a better way to structure my HTML so that such tests can pass?

Upvotes: 1

Views: 551

Answers (1)

Fody
Fody

Reputation: 31904

There's a couple of ways to use .contains().

The way you have used it cy.contains('Log In') gets the immediate "owner" of the text, which is the <span>.

Since you know the HTML structure and you want to test the href two levels above, you can use traversal commands to move up those two levels

cy.contains('Log In')         // I want the element "owning" text "Log In"
  .parents('a')
  .should('have.attr', 'href', 'login-url');

The other way to use .contains() is to specify a selector with it, and get the element you want to test directly. It works because .contains() will search for specified text in descendent elements.

cy.contains('a', 'Log In')    // I want <a> where this or descendent has "Log In"
  .should('have.attr', 'href', 'login-url');

Upvotes: 1

Related Questions