Cypress Test Case: Cart Modal Popup Issue

cypress cart modal popup issue

Today I was asked to do a Cypress testing of the IQOS website. The task was to test the fastest way the user can order a product. Basically, as soon as the users land on the IQOS Homepage they will see a promotion banner with a call to action. After clicking on it they will be able to choose and order the product in a few steps.

During this testing, everything went according to my plans until I came to the Cart Modal component, which is a typical element of every shopping website. In the case of IQOS, it appears on the product page as a popup. No matter how I’ve tried to command Cypress, it couldn’t locate it on the page. In the end, I’ve still managed to set the code up properly. So, I’m calling you to check it out 🙂

Task steps

We will understand the task the best if we describe its steps. Basically, our goal is to simulate user behavior. So here are the steps we have to reproduce:

  1. Visit the IQOS website: https://www.iqos.com/gb/en/home.html?gr=false
  2. Go through the Soft Age Gate. I have covered that in more detail in another article you may refer to here.
  3. As the users go through the SAG, they will end up on the Homepage. The first component has a promotion with a call to action: Click on the “Buy from £29” button. It will lead the users to the Shop page.
  4. Let’s suppose that the user wants to order a product from the shop page. For testing purposes we will choose the first one listed: IQOS ILUMA ONE Starter Kit. Click on it. It will lead us to the products page.
  5. Cookie popup will appear on the product page. Click on the reject all button.
  6. On the product page click on the Add to Basket button. It will popup the Cart Modal component on the same page.
  7. In the Cart Modal popup click on the Proceed to Checkout button.
  8. We should end up on the Checkout page.

> “undefined” is not valid JSON Error

The first issue I met during the testing was the following:

The following error originated from your application code, not from Cypress. It was caused by an unhandled promise rejection.
> “undefined” is not valid JSON
When Cypress detects uncaught errors originating from your application it will automatically fail the current test.
This behavior is configurable, and you can choose to turn this off by listening to the uncaught:exception event.

This issue can be, in most cases, easily solved by implementing the following piece of code on the very top of our Cypress testing code:

Cypress.on('uncaught:exception', (err, runnable) => {
  // Return false to prevent the test from failing
  return false;
});

In the end, we have solved this issue another way, as you will see in the final code, but this method also works well.

How my initial code looked like

Here, I will show you what my initial code looked like. It includes all the steps that I successfully completed.

Cypress.on('uncaught:exception', (err, runnable) => {
  // Return false to prevent the test from failing
  return false;
});
describe('Iqos Shop Test', () => {
  it('Add a product in the basket and proceed to checkout', () => {
    // Visit the specified URL
    cy.visit('https://www.iqos.com/gb/en/home.html?gr=false');

    // Select the month from the dropdown
    cy.get('#dropdownMonths')
      .parent().click();
    cy.get('#sag-month-01').click({ force: true });

    // Select the year from the dropdown
    cy.get('#dropdownYears')
      .parent().click();
    cy.get('#sagyear1999').click({ force: true });

    // Click the confirm button
    cy.get('span.sav-btn-text:contains("Confirm")').click();
    
    // Click on the promotion button on the main banner
    cy.get('.btn-white-turquoise').eq(0).click();
    
    // Buy IQOS ILUMA ONE Starter Kit
    cy.get('a[href="/gb/en/shop/iqos-iluma-one-starter-kit.html"]')
      .should('be.visible')
      .eq(0)
      .click();
  
    // click on the "Add to cart" button
    cy.get('section.product-info__wrapper')
      .find('div.product-detail__react--container')
      .should('be.visible')
      .wait(200)
      .within(() => {
       cy.contains('button', 'Add to basket').should('be.visible').click({ force: true })
      });
      
  });
});

As we will see, the final code simplified this initial code and implemented some useful steps that helped me reach the Checkout page as the final goal.

The main issue: Cart modal is not visible

The main issue I encountered during the testing was that the cart modal component appeared after clicking on the Add to Basket button but disappeared so fast that Cypress couldn’t find it on the page.

Ultimately, we could conclude that the main problem is that the cart modal is slow to become fully visible.

It looks like the following things were blocking:

  • After the product is added to the cart, one or two API calls are made, and the page waits for the response before the modal is shown.
  • There is some animation attached to the modal, indicated by opacity: 0, which is the start of the animation, which then gets increased to opacity: 1 for a fade-in effect.
  • The parent #nbw__header--desktop has display: none which is changed after the API call response arrives.

Features that helped the testing performance

I moved the visit and age consent into the form beforeEach to show a more concise log (since the focus is on the Cart Modal).

beforeEach(() => {
  cy.intercept({ resourceType: /xhr|fetch/ }, { log: false })
  cy.viewport(2000, 2000)
  cy.visit('https://www.iqos.com/gb/en/home.html?gr=false');
  cy.get('#dropdownMonths').click();
  cy.contains('January').click()
  cy.get('#dropdownYears').click();
  cy.contains('1999').click()
  cy.contains('span.sav-btn-text', 'Confirm').click();
  cy.contains('Buy from £29', {timeout:10_000}).click() // Click on the promotion button
})

Also added cy.intercept({ resourceType: /xhr|fetch/ }, { log: false }) to remove the noisy XHR logs.

The viewport was set to 2000, 2000. It was necessary to reduce the issue of elements being invisible because they are off-screen (primarily referring to the Cart Modal).

cy.viewport(2000, 2000)

I added a 30-second timeout to the cart modal query, which consistently passes the test. As an alternative to adding a long timeout, we could look into intercepting and waiting on the API calls. But I simply prefer to go with a timeout 🙂

cy.get('#minicart', {timeout:30_000}).should('be.visible')
cy.contains('Proceed to checkout').click()

I changed some other elements to search by text since the class-based selectors aren’t the best, as the color themes may be changed. Initially, I tried to refer to every element by its ID or class.

Final code

With the help of TesterDick from StackOverflow, I’ve managed to get the code that works perfectly. You may find the report here.

Here is the Cypress code:

beforeEach(() => {
  cy.intercept({ resourceType: /xhr|fetch/ }, { log: false })
  cy.viewport(2000, 2000)
  cy.visit('https://www.iqos.com/gb/en/home.html?gr=false');
  cy.get('#dropdownMonths').click();
  cy.contains('January').click()
  cy.get('#dropdownYears').click();
  cy.contains('1999').click()
  cy.contains('span.sav-btn-text', 'Confirm').click();
  cy.contains('Buy from £29', {timeout:10_000}).click() // Click on the promotion button
})

it('Add a product in the basket and proceed to checkout', () => {
  cy.get('a[href="/gb/en/shop/iqos-iluma-one-starter-kit.html"]').eq(0).click();
  cy.get('#onetrust-reject-all-handler').click()  // get rid of cookies popup
  cy.get('section.product-info__wrapper')
    .find('div.product-detail__react--container')
    .should('be.visible')
  cy.contains('button', 'Add to basket').should('be.visible').click()

  cy.get('#minicart', {timeout:30_000}).should('be.visible')
  cy.contains('Proceed to checkout').click()

  // confirm checkout is visible
  cy.contains('Welcome to secure checkout', {timeout:10_000}).should('be.visible')
})

Cypress successfully managed to land on the Checkout page:

Scroll to Top