How Cypress test HTML element is assigned after Ajax call

Viewed 29

I have a search input and result display area that are being handled by Ajax call. When user enter a keyword, Ajax call the backend and return HTML string. The JS handler then appends the response HTML into result display area.

Here my steps:

  • Search input: Vancouver (auto input by browser location)
  • Result: Welcome to Vancouver
  • Type into search input: Calgary
  • Expected: Welcome to Calgary
// Test case
Cypress.Commands.add('assessHeadingInfo', (options) => {
    cy.fixture('selectors/index-page').then((selectors) => {
        cy.xpath(selectors.head_info).then((heading) => {
            cy.searchForArea('Calgary'); // Steps to input keyword and click search
            
            cy.get(heading)
                .children('h1')
                .should('be.visible');

            cy.get(heading)
                .children('h1')
                .invoke('text')
                .should('equals', 'Welcome to Calgary');

        });
    });
});

Test error:

AssertionError
Timed out retrying after 4000ms: expected 'Welcome to Vancouver' to equal 'Welcome to Calgary'

However, the visual screenshot showed that the 'Welcome to Calgary' text was displayed while the test could not see.

I did follow the guide from Cypress app

Timed out retrying after 4000ms: cy.should() failed because this element is detached from the DOM.
...
You typically need to re-query for the element or add 'guards' which delay Cypress from running new commands. Learn more

I added 'a guard', cy.wait()...but nothing works.

Would you please teach me how to handle this issue?

Thank you.


UPDATE SOLUTION:

Thanks for @jhelguero helped me to find out.

Solution 1:

I added this and it worked well.

cy.contains('h1', 'Welcome to Calgary')
   .should('be.visible');

Because I have put all the tests in block of

cy.xpath(selectors.head_info).then((heading) => {
   ...
});

So, when I do cy.get(heading) it caught the element before Ajax done. It was 'Welcome to Vancouver' instead.

Solution 2:

Then, I separated test steps like this.

cy.searchForArea('Calgary');

cy.xpath(selectors.head_info)
   .children('h1')
   .should('have.text', 'Welcome to Calgary');

and it was successful.

Summary:

Do this:

cy.xpath({selector}).should(have.text,'ABC');
cy.xpath({selector}).find('h1').should('have.text, '123');

DON'T do this:

cy.xpath({selector}).then((ele) => {
   cy.get(ele).should(have.text, 'ABC');
   cy.get(ele).find('h1').should('have.text, '123');
});
2 Answers

If you visually confirmed the text "Welcome to Calgary", the problem might be that .should('equals', 'Welcome to Calgary') is not retrying enough of preceding steps.

Try shortening the query.

You can also increase the timeout if you notice a lag before the text appears.

cy.get(`${heading} > h1`, {timeout:10_000})   
  .should('have.text', 'Welcome to Calgary')

It looks like your DOM rerenders after the ajax call and thus the element('Welcome to') you were previously reference has been detached.

To fix this you will need to re query using the same selector after the ajax call is completed. You will not need cy.xpath(selectors.head_info).then().

Cypress.Commands.add('assessHeadingInfo', (options) => {
    cy.fixture('selectors/index-page').then((selectors) => {
        cy.searchForArea('Calgary'); // Steps to input keyword and click search
        // use .contains() to search a selector with regex text
        cy.contains(selectors.head_info, /Welcome to Calgary/)
          .should('be.visible');
    });
});
Related