Search code examples
cypresscypress-each

Cypress deep equal works weirdly


I started learning cypress today and trying to write the test below

it('Validate Price High - Low sorting', () => {
    let unsortedItemPrices = [];
    const sortedItemPrices = [];

    unsortedItemPrices.sort((a, b) => parseFloat(a) - parseFloat(b)).reverse();
    cy.get(inventoryPage.itemsPrice).each((item) => {
      unsortedItemPrices.push(item.text().replace('$', ''));
    });

    cy.get(inventoryPage.sortDropdown).get('option').should('have.length', 4);
    cy.get(inventoryPage.sortDropdown).select('hilo');
    cy.get(inventoryPage.itemsPrice).each(($item) => {
      sortedItemPrices.push($item.text().replace('$', ''));
    });
    

    cy.task('log', unsortedItemPrices.sort((a, b) => parseFloat(a) - parseFloat(b)).reverse());
    cy.task('log', sortedItemPrices);
    expect(sortedItemPrices).to.deep.equal(unsortedItemPrices.sort().reverse());
  });

The 2 log statements returns these lines, which is weird as when I try separately I see the data sorted properly. However the expect().to.deep.equal() statement above doesn't return an error.

[ '29.99', '9.99', '15.99', '49.99', '7.99', '15.99' ]
[ '49.99', '29.99', '15.99', '15.99', '9.99', '7.99' ]

What is more weird that if I edit the log with like this

cy.task('log', unsortedItemPrices.sort((a, b) => parseFloat(a) - parseFloat(b)).reverse().push(1));

then I do get an error, but it says

AssertionError: expected [] to deeply equal [ 1 ]
      + expected - actual

      -[]
      +[ 1 ]

Can someone please explain what is going on? This is totally confusing for me :|


Solution

  • In your test, you have some cy.get() commands to get the values off the page, but you go ahead and run the expect().to.deep.eq() before these command have completed.

    That's because cy.get() commands are asynchronous, and need to be waited on before using the values extracted from the page.

    So your expect is comparing the empty arrays, equivalent to

    expect([]).to.deep.equal([])
    

    which explains this message after you push(1)

    AssertionError: expected [] to deeply equal [ 1 ]


    How to fix

    Just wrap your comparison in a cy.then() to ensure it is performed after the values are extracted.

    Also note .sort() and .reverse() are in-place methods that affect the original array, so you don't want to call then twice (which you have correctly avoided doing, but it's easy to overlook that issue).

    const sortFn = (a, b) => parseFloat(a) - parseFloat(b)
    
    let unsortedItemPrices = [];
    const sortedItemPrices = [];
    
    cy.get(inventoryPage.itemsPrice).each($item => {
      unsortedItemPrices.push($item.text().replace('$', ''))
    })
    
    cy.get(inventoryPage.sortDropdown).get('option').should('have.length', 4);
    cy.get(inventoryPage.sortDropdown).select('hilo');
    cy.get(inventoryPage.itemsPrice).each($item => {
      sortedItemPrices.push($item.text().replace('$', ''))
    })
    
    cy.then(() => {
      // sort() and reverse() work in-place, so we can apply first
      unsortedItemPrices.sort(sortFn).reverse()
      // then compare
      expect(sortedItemPrices).to.deep.equal(unsortedItemPrices)
    })
    

    A clearer test

    IMO a cleaner way to structure the test is to map elements to values instead of using .each()

    
    const sortFn = (a, b) => parseFloat(a) - parseFloat(b)
    const mapFn = ($items) => [...$items].map(item => item.innerText.replace('$', '')
    
    cy.get(inventoryPage.itemsPrice)
      .then(mapFn)
      .then(unsortedItemPrices => {
    
        // create a new array for expected values
        const expectedSortedItemPrices = [...unsortedItemPrices.sort(sortFn).reverse()]
    
        cy.get(inventoryPage.sortDropdown).get('option')
          .should('have.length', 4);
        cy.get(inventoryPage.sortDropdown).select('hilo');
        cy.get(inventoryPage.itemsPrice)
          .then(mapFn)
          .should(sortedItemPrices => {
    
            // using should() instead of then() gives some retry
            // in case the page sorting happens asynchronously
            // (not likely but you don't know the internal implementation)
    
            expect(sortedItemPrices).to.deep.equal(expectedSortedItemPrices)
          })
      })