简体   繁体   中英

Cypress expectations only work with cy.wait or without cypress-testing-lib within

I recently installed Cypress 10 on a personal project and I was implementing a dummy test to ensure everything works. I decided to go a bit "the extra" mile on the example and I am wondering why the following assertions fail. The scenario is the following: the user types into a search box, and a list of cultural events is displayed. The user clicks an anchor on the first one; This causes the page to go from / to /events/:event_id (most of these things are still hard-coded hence the usage of .first when finding items (instead of looking for a specific listitem )... Now, the problem arises with the expectations that come after that.

If I add a cy.wait expression, the assertions work; If I do not use cy.wait nor cypress-testing-library#within , the assertions work... If I do not use cy.wait and use within , the assertions fail regardless of having or not having timeouts spread all over the code. Note that, I am confident by verification, that the assertions are supposed to be within a main HTML element. I have the browser element-inspect open right next to me, as well as the Next code that renders the <AppLayout> and sticks the content within the main element.

I would like to know the alternatives to cy.wait since this is not a very reliable way of waiting for elements to render. Also, if someone can explain the reasoning behind these failings, it would be great.

describe("dummy spec", () => {
  it("passes", () => {
    const testEvent = "networking for introverts";
    const testEventRegExp = new RegExp(testEvent, "i");

    cy.visit("/");

    cy.findByRole("banner").within(() => {
      cy.findByRole("searchbox", { name: /search events/i }).type(`${testEvent}{enter}`);
    });

    cy.findByRole("main").within(() => {
      cy.findAllByRole("listitem")
        .first()
        .within(() => {
          cy.findByRole("link", { name: testEventRegExp }).click();
        });
    });

    cy.location("pathname").should("eq", "/events/e2");

    // Waiting the tinniest bit and the expectations below pass as expected
    // cy.wait(25);

    cy.findByRole("main").within(() => {
      cy.findByRole("heading", { name: testEventRegExp })
        // Assertion fails regardless of specified timeout amount
        .should("be.visible", { timeout: 1000 })
        .contains(testEvent, {
          matchCase: false
        });
      cy.findByTestId("event-summary").should("be.visible");
      cy.findByTestId("event-description").should("be.visible");
    });
  });
});

First thing I would say is 25 ms is nothing compared to the overall test duration (about 1/12 the time of a blink).

Don't be concerned about the principle of cy.wait() if it's what makes the test work, but burn-test to make sure you have the right duration.

Since you have a Next app which has React as base framework, and if you use functional components (hooks) you may be able to use cy.wait(0) .

Javascript is single-threaded, meaning any code running in the test will hog the thread until blocked by, say a network call or a setTimeout() .

Using wait(0) blocks the test momentarily and allows the React hooks to complete.

This also fits with your observation that adding timeout does not work.

// Assertion fails regardless of specified timeout amount
.should("be.visible", { timeout: 1000 })

However timeout: 1000 is going the wrong way - normal timeout is 4000 so you want to try 10000 .

Using hardcoded wait timer will not address the flakiness of a test and will be troublesome in the longer time frame. Here are a few ways to make a test stronger.

If your app makes an API calls, you can spy, wait on it, and choose to check the response data. This will make your test more resilient and less flaky.

cy.intercept('GET', 'fruit').as('fruit')
// action to redirect user to new page where API call occurs
cy.location('pathname').should('eq', '/new-url') // good practice
cy.wait('@fruit') //wait until request and response occur
  .its('response.body') // now have access to response body and can make assertions

Adding assertions on elements your query will make your tests stronger. Using .should('be.visible') will retry the previous command until the element is visible to the user as oppose to the default assertion of the element existing in the DOM.

// 
cy.findByRole("main").should('be.visible').within(() => {
  cy.findByRole("heading", { name: testEventRegExp })
    .should("be.visible", { timeout: 1000 })
    .contains(testEvent, {
     matchCase: false
     });
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM