Reputation: 1032
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
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