tarponjargon
tarponjargon

Reputation: 1032

Cypress - testing click events before page navigation

I'm trying to test that my google tag manager 'productClick' event is registering after the click but before the page navigation. Here's what I have:

describe("Test Product Listing Page", () => {
    beforeEach(function() {
        cy.visit("/productlisting");
    });

    ...(other tests)...

    it("Test GTM productClick", function() {
      cy.window().then(win => {

        // listen to GTM's dataLayer.push method
        const dlp = cy.spy(win.dataLayer, "push");

        // extract SKUID from first product on the page
        cy.get('[data-js="productlisting"] [data-product-skuid]')
          .first() 
          .invoke("attr", "data-product-skuid")
          .then(skuid => {

            // click on the first link in the item's container
            cy.get(`[data-product-skuid="${skuid}"] a`)
              .first()
              .click()
              .then(() => {

                // passes in interactive mode but not CI mode!
                expect(dlp).to.be.calledOnce;

                // test that argument passed is the item clicked
                expect(dlp.args[0][0].ecommerce.click.products[0].id).to.equal(skuid);

                // test that GTM returns true
                expect(dlp.returnValues[0]).to.be.true;

              });
          });
      });
    });

}); 

cy.spy() appears to be what I need and in fact it works great in interactive mode. But in CI mode it fails with:

 AssertionError: expected push to have been called exactly once, but it was called twice

The following calls were made:

push(Object{3}) => true at Array.proxy [as push] (https://my.site.com/__cypress/runner/cypress_runner.js:62335:22)
push(Object{4}) => true at Array.proxy [as push] (https://my.site.com/__cypress/runner/cypress_runner.js:62335:22)

but only when there are other tests on the spec! If I change the test to it.only it passes in CI mode. The other tests are testing things unrelated to google tag manager, but there are GTM push calls happening in the window. It's almost like cy.spy started spying before I told it to.

I'm baffled. Am I missing something here? Is there a better way to test something post-click pre-navigation?

Upvotes: 0

Views: 2582

Answers (1)

tarponjargon
tarponjargon

Reputation: 1032

Figured it out. I am still unsure why other push calls are ending up in the spy object, but I may not be able to control that. So rather than listening for one call, find the call that relates to this event and assert against that. Also needed to call the spy with cy.get. The answer in this post helped me.

describe("Test Product Listing Page", () => {
    beforeEach(function() {
        cy.visit("/productlisting");
    });

    ...(other tests)...

    it("Test GTM productClick", function() {
      cy.window().then(win => {

        // listen to GTM's dataLayer.push method
        cy.spy(win.dataLayer, "push").as("dlp");

        // extract SKUID from first product on the page
        cy.get('[data-js="productlisting"] [data-product-skuid]')
          .first() 
          .invoke("attr", "data-product-skuid")
          .then(skuid => {

            // click on the first link in the item's container
            cy.get(`[data-product-skuid="${skuid}"] a`)
              .first()
              .click()
              .then(() => {

                // use cy.get for retry-ability
                cy.get("@dlp").should(dlp => {

                    // find the index of the argument that corresponds to this event
                    const idx = dlp.args.findIndex(i => i[0].event === "productClick");

                    // then you can run assertions
                    expect(dlp.args[idx][0].ecommerce.add.products[0].id).to.equal(skuid);
                    expect(dlp.returnValues[idx]).to.be.true;
                });

              });
          });
      });
    });

}); 

Upvotes: 2

Related Questions