cypresscypress-testing-library

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");
    });
  });
});


Solution

  • 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.