Skip to content

Commit

Permalink
Merge pull request #733 from frontity/frontity-google-analytics-4
Browse files Browse the repository at this point in the history
Update @frontity/google-analytics to work with the new GA4 format
  • Loading branch information
SantosGuillamot committed Mar 2, 2021
2 parents 0ae4a04 + 7e80738 commit 66a4d75
Show file tree
Hide file tree
Showing 7 changed files with 180 additions and 164 deletions.
5 changes: 5 additions & 0 deletions .changeset/strange-pillows-think.md
@@ -0,0 +1,5 @@
---
"@frontity/google-analytics": major
---

Update @frontity/google-analytics to be compatible with GA4.
240 changes: 133 additions & 107 deletions e2e/integration/frontity-01/google-analytics.spec.js
@@ -1,11 +1,28 @@
import expect from "expect";

/**
* Transforms an array into an object.
*
* @param data - The array that needs to be transformed.
* @returns The object.
*/
const arrayToArguments = (data) =>
data.reduce((out, item, i) => {
out[i] = item;
return out;
}, {});

// Function to manually check that the spy created with `cy.spy` has been called
// with an argument. I am not checking the order because sometimes pageviews
// take a bit longer to be sent and that cause race conditions.
const expectGaToHaveBeenCalledWith = (win, data) => {
expect(win.ga.args).toEqual(
expect.arrayContaining([expect.arrayContaining(data)])
const expectGtagToHaveBeenCalledWith = (win, data) => {
expect(win.gtag.args).toEqual(
expect.arrayContaining([
expect.objectContaining(
// If this is an array, transform it to a arguments object.
Array.isArray(data) ? arrayToArguments(data) : data
),
])
);
};

Expand All @@ -24,175 +41,183 @@ describe("Google Analytics", () => {
cy.visit("http://localhost:3001?frontity_name=google-analytics");

// Wait for Google Analytics to load its <script> and create `window.ga`.
cy.window().should("have.property", "ga");
cy.window().should("have.property", "gtag");

// Add a spy on `window.ga`.
cy.window().then((win) => {
cy.spy(win, "ga");
cy.spy(win, "gtag");
});
});

it("should load the Google Analytics library", () => {
// Make sure the <script> was created.
cy.get(
`script[src="https://www.google-analytics.com/analytics.js"][async]`
);
cy.get(`script[src^="https://www.googletagmanager.com/gtag/js"][async]`);

// Make sure the Google Analytics library has loaded.
cy.window().should("have.property", "ga");
cy.window().should("have.property", "gtag");
});

// I was not able to find a way to stub/spy `window.ga` between the moment
// that it is created by the Google Analytics <script> and the moment it sends
// the first pageviews so I'm just checking that it has a `hitcount` of 1 in
// its internal count.
it("should have sent the first pageview", () => {
cy.window()
.its("gaData")
.its("UA-XXXXXX-X")
.its("hitcount")
.should("equal", 1);
cy.window().then((win) => {
const dataLayer = win.dataLayer;

cy.window()
.its("gaData")
.its("UA-YYYYYY-Y")
.its("hitcount")
.should("equal", 1);
expect(dataLayer).toEqual(
expect.arrayContaining([
// This is due the fact the dataLayer holds the data as arguments
// which is not an array, but rather an object.
expect.objectContaining(arrayToArguments(["config", "UA-XXXXXX-X"])),
expect.objectContaining(arrayToArguments(["config", "UA-YYYYYY-Y"])),
])
);
});
});

it("should sent a pageview if the page changes", () => {
cy.get("button#change-link").click();

cy.window().then((win) => {
expectGaToHaveBeenCalledWith(win, [
"tracker_UA_XXXXXX_X.send",
{
hitType: "pageview",
page: pageviewSomePost.link,
title: pageviewSomePost.title,
},
expectGtagToHaveBeenCalledWith(win, [
"event",
"page_view",
expect.objectContaining({
send_to: "UA-XXXXXX-X",
page_title: pageviewSomePost.title,
page_location: pageviewSomePost.link,
}),
]);
expectGaToHaveBeenCalledWith(win, [
"tracker_UA_YYYYYY_Y.send",
{
hitType: "pageview",
page: pageviewSomePost.link,
title: pageviewSomePost.title,
},

expectGtagToHaveBeenCalledWith(win, [
"event",
"page_view",
expect.objectContaining({
send_to: "UA-YYYYYY-Y",
page_title: pageviewSomePost.title,
page_location: pageviewSomePost.link,
}),
]);
});

// Make sure the real library sent two pageviews for each tracker.
// Make sure the real library sent two pageviews for each tracker + a config call.
cy.window()
.its("gaData")
.its("UA-XXXXXX-X")
.its("hitcount")
.should("equal", 2);
.should("equal", 3);

cy.window()
.its("gaData")
.its("UA-YYYYYY-Y")
.its("hitcount")
.should("equal", 2);
.should("equal", 3);
});

it("should sent pageviews when going back or forward", () => {
cy.get("button#change-link").click();
cy.go("back");

cy.window().then((win) => {
expectGaToHaveBeenCalledWith(win, [
"tracker_UA_XXXXXX_X.send",
{
hitType: "pageview",
page: pageviewHome.link,
title: pageviewHome.title,
},
expectGtagToHaveBeenCalledWith(win, [
"event",
"page_view",
expect.objectContaining({
send_to: "UA-XXXXXX-X",
page_title: pageviewHome.title,
page_location: pageviewHome.link,
}),
]);
expectGaToHaveBeenCalledWith(win, [
"tracker_UA_YYYYYY_Y.send",
{
hitType: "pageview",
page: pageviewHome.link,
title: pageviewHome.title,
},
expectGtagToHaveBeenCalledWith(win, [
"event",
"page_view",
expect.objectContaining({
send_to: "UA-YYYYYY-Y",
page_title: pageviewHome.title,
page_location: pageviewHome.link,
}),
]);
});

cy.go("forward");

cy.window().then((win) => {
expectGaToHaveBeenCalledWith(win, [
"tracker_UA_XXXXXX_X.send",
{
hitType: "pageview",
page: pageviewSomePost.link,
title: pageviewSomePost.title,
},
expectGtagToHaveBeenCalledWith(win, [
"event",
"page_view",
expect.objectContaining({
send_to: "UA-XXXXXX-X",
page_title: pageviewSomePost.title,
page_location: pageviewSomePost.link,
}),
]);
expectGaToHaveBeenCalledWith(win, [
"tracker_UA_YYYYYY_Y.send",
{
hitType: "pageview",
page: pageviewSomePost.link,
title: pageviewSomePost.title,
},

expectGtagToHaveBeenCalledWith(win, [
"event",
"page_view",
expect.objectContaining({
send_to: "UA-YYYYYY-Y",
page_title: pageviewSomePost.title,
page_location: pageviewSomePost.link,
}),
]);
});

// Make sure the real library sent four pageviews for each tracker.
// Make sure the real library sent five pageviews for each tracker.
cy.window()
.its("gaData")
.its("UA-XXXXXX-X")
.its("hitcount")
.should("equal", 4);
.should("equal", 5);

cy.window()
.its("gaData")
.its("UA-YYYYYY-Y")
.its("hitcount")
.should("equal", 4);
.should("equal", 5);
});

it("should send events", () => {
cy.get("button#send-event").click();

cy.window().then((win) => {
expectGaToHaveBeenCalledWith(win, [
"tracker_UA_XXXXXX_X.send",
{
hitType: "event",
eventAction: "some event",
expectGtagToHaveBeenCalledWith(win, [
"event",
"some event",
expect.objectContaining({
send_to: "UA-XXXXXX-X",
content: "some content",
eventCategory: undefined,
eventLabel: undefined,
eventValue: undefined,
},
value: undefined,
event_category: undefined,
event_label: undefined,
}),
]);
expectGaToHaveBeenCalledWith(win, [
"tracker_UA_YYYYYY_Y.send",
{
hitType: "event",
eventAction: "some event",

expectGtagToHaveBeenCalledWith(win, [
"event",
"some event",
expect.objectContaining({
send_to: "UA-YYYYYY-Y",
content: "some content",
eventCategory: undefined,
eventLabel: undefined,
eventValue: undefined,
},
value: undefined,
event_category: undefined,
event_label: undefined,
}),
]);
});

cy.window()
.its("gaData")
.its("UA-XXXXXX-X")
.its("hitcount")
.should("equal", 2);
.should("equal", 3);

cy.window()
.its("gaData")
.its("UA-YYYYYY-Y")
.its("hitcount")
.should("equal", 2);
.should("equal", 3);

// Change testEvent to send Google Analytics specific data.
const someEvent = {
Expand All @@ -211,38 +236,39 @@ describe("Google Analytics", () => {
cy.get("button#send-event").click();

cy.window().then((win) => {
expectGaToHaveBeenCalledWith(win, [
"tracker_UA_XXXXXX_X.send",
{
hitType: "event",
eventAction: someEvent.name,
eventCategory: someEvent.payload.category,
eventLabel: someEvent.payload.label,
eventValue: someEvent.payload.value,
},
expectGtagToHaveBeenCalledWith(win, [
"event",
someEvent.name,
expect.objectContaining({
send_to: "UA-XXXXXX-X",
value: someEvent.payload.value,
event_category: someEvent.payload.category,
event_label: someEvent.payload.label,
}),
]);
expectGaToHaveBeenCalledWith(win, [
"tracker_UA_YYYYYY_Y.send",
{
hitType: "event",
eventAction: someEvent.name,
eventCategory: someEvent.payload.category,
eventLabel: someEvent.payload.label,
eventValue: someEvent.payload.value,
},

expectGtagToHaveBeenCalledWith(win, [
"event",
someEvent.name,
expect.objectContaining({
send_to: "UA-YYYYYY-Y",
value: someEvent.payload.value,
event_category: someEvent.payload.category,
event_label: someEvent.payload.label,
}),
]);
});

cy.window()
.its("gaData")
.its("UA-XXXXXX-X")
.its("hitcount")
.should("equal", 3);
.should("equal", 4);

cy.window()
.its("gaData")
.its("UA-YYYYYY-Y")
.its("hitcount")
.should("equal", 3);
.should("equal", 4);
});
});
1 change: 1 addition & 0 deletions jest.config.js
Expand Up @@ -15,6 +15,7 @@ module.exports = {
"!**/vendor/**",
"!**/dist/**",
"!**/build/**",
"!**/coverage/**",
"!**/jest.config.{js,ts}",
],
};

0 comments on commit 66a4d75

Please sign in to comment.