Kayote
Kayote

Reputation: 15627

Cypress Testing: How To Compare Count Of Elements Before & After AJAX Call

I have a React app with infinite scrolling, so when the user scrolls to the bottom of the page, ajax request is made to retrieve more results. I have been struggling to test this code and below is what I was able to get working based on google / Stackoverflow searches. I am not clear on what Cypress.$ does, or how its different to say Cy.* commands.

I feel, I am not clear on how to get the logic to work with the async nature of Cypress. The following code is commented to explain my thinking.

  it("should scroll to bottom, retrieve & display next results", () => {
    // wait because some ajax request takes longer upon page load
    cy.wait(500);
    let initialCount = null;
    // store the length of elements in variable
    initialCount = Cypress.$("div.experienceThumbnail").length;
    // scroll down to bottom
    cy.scrollTo("bottom", { duration: 1000 });
    // wait because an ajax request is made for the pagination
    cy.wait(1111);
    // get the same elements again
    cy.get("div.experienceThumbnail")
      .its("length")
    // compare the new count to prev. count we stored above in initialCount var
      .should("be.gt", initialCount);
  });

My main question is what is the proper way of testing something like above.

Upvotes: 1

Views: 2307

Answers (2)

Jonas
Jonas

Reputation: 396

In my opinion one of the disadvantages with Cypress (as compared to other test-automation tools). Before and after comparisons. Either you have such a nested function which can only used in this one specific location or you store the variable in aliases. Both are not satisfying. Since Toteff did not write the alias version, my attempt (also without verifying the code), but works in similar situation:

const initialCount = Cypress.$("div.experienceThumbnail").length;
cy.wrap(initialCount).as('before');
cy.scrollTo("bottom", { duration: 1000 });
const afterCount = Cypress.$("div.experienceThumbnail").length;
cy.wrap(afterCount).as('after');     
this.compareAliasNumbers('after', 'before', 'After <= Before');

Then this util function:

compareAliasNumbers(smaller: string, bigger: string, message: string) {
    cy.get(`@${bigger}`).then((a1) => {
      const biggerNr = parseInt(a1 as unknown as string, 10) + 1; // plus one = greater than or equal
      cy.get(`@${smaller}`).then((a2) => {
        // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
        const smallerNr = parseInt(a2 as unknown as string, 10);
        expect(initialCount <= biggerNr).to.be.true
      });
    });
  }

In my opinion: both version (the nested by Toteff or this via alias creation) are difficult to read. Code reuse is only possible in the latter, but I also dont like it (coming from another automatic testing tool). Are there other solutions?

Upvotes: 0

Dejan Toteff
Dejan Toteff

Reputation: 2207

In Cypress one has to learn to live with nested Promises if you want a fine control over assertions.

I haven't tested the next two suggestions, but at least one of them should work:

it("should scroll to bottom, retrieve & display next results", () => {
  const selector = "div.experienceThumbnail"
  cy.wait(500);
  cy.get(selector).then($els => {
    const initialCount = $els.length
    cy.scrollTo("bottom", { duration: 1000 });
    cy.wait(1111);

    cy.get(selector)
      .its("length")
      .should("be.gt", initialCount);
  })
});
it("should scroll to bottom, retrieve & display next results", () => {
  const selector = "div.experienceThumbnail"
  cy.wait(500);
  cy.get(selector).then($els => {
    const initialCount = $els.length
    cy.scrollTo("bottom", { duration: 1000 });
    cy.wait(1111);

    cy.get(selector).then($els2 => {
      const currentCount = $els2.length
      expect(initialCount <= currentCount).to.be.true
    })
  })
});

Upvotes: 1

Related Questions