Skip to content

Commit

Permalink
feat: Add GoogleAnalytics 4 to frontend-platform
Browse files Browse the repository at this point in the history
PR  openedx#472 

---------

Co-authored-by: Glib Glugovskiy <[email protected]>
  • Loading branch information
UvgenGen and GlugovGrGlib authored Apr 18, 2023
1 parent 299ad79 commit c174b29
Show file tree
Hide file tree
Showing 4 changed files with 146 additions and 0 deletions.
15 changes: 15 additions & 0 deletions src/initialize.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ import {
import {
configure as configureAnalytics, SegmentAnalyticsService, identifyAnonymousUser, identifyAuthenticatedUser,
} from './analytics';
import { GoogleAnalyticsLoader } from './scripts';
import {
getAuthenticatedHttpClient,
configure as configureAuth,
Expand Down Expand Up @@ -157,6 +158,13 @@ export async function runtimeConfig() {
}
}

export function loadExternalScripts(externalScripts, data) {
externalScripts.forEach(ExternalScript => {
const script = new ExternalScript(data);
script.loadScript();
});
}

/**
* The default handler for the initialization lifecycle's `analytics` phase.
*
Expand Down Expand Up @@ -221,6 +229,8 @@ function applyOverrideHandlers(overrides) {
* @param {*} [options.analyticsService=SegmentAnalyticsService] The `AnalyticsService`
* implementation to use.
* @param {*} [options.authMiddleware=[]] An array of middleware to apply to http clients in the auth service.
* @param {*} [options.externalScripts=[GoogleAnalyticsLoader]] An array of externalScripts.
* By default added GoogleAnalyticsLoader.
* @param {*} [options.requireAuthenticatedUser=false] If true, turns on automatic login
* redirection for unauthenticated users. Defaults to false, meaning that by default the
* application will allow anonymous/unauthenticated sessions.
Expand All @@ -240,6 +250,7 @@ export async function initialize({
analyticsService = SegmentAnalyticsService,
authService = AxiosJwtAuthService,
authMiddleware = [],
externalScripts = [GoogleAnalyticsLoader],
requireAuthenticatedUser: requireUser = false,
hydrateAuthenticatedUser: hydrateUser = false,
messages,
Expand All @@ -256,6 +267,10 @@ export async function initialize({
await runtimeConfig();
publish(APP_CONFIG_INITIALIZED);

loadExternalScripts(externalScripts, {
config: getConfig(),
});

// Logging
configureLogging(loggingService, {
config: getConfig(),
Expand Down
53 changes: 53 additions & 0 deletions src/scripts/GoogleAnalyticsLoader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/**
* @implements {GoogleAnalyticsLoader}
* @memberof module:GoogleAnalytics
*/
class GoogleAnalyticsLoader {
constructor({ config }) {
this.analyticsId = config.GOOGLE_ANALYTICS_4_ID;
}

loadScript() {
if (!this.analyticsId) {
return;
}

global.googleAnalytics = global.googleAnalytics || [];
const { googleAnalytics } = global;

// If the snippet was invoked do nothing.
if (googleAnalytics.invoked) {
return;
}

// Invoked flag, to make sure the snippet
// is never invoked twice.
googleAnalytics.invoked = true;

googleAnalytics.load = (key, options) => {
const scriptSrc = document.createElement('script');
scriptSrc.type = 'text/javascript';
scriptSrc.async = true;
scriptSrc.src = `https://www.googletagmanager.com/gtag/js?id=${key}`;

const scriptGtag = document.createElement('script');
scriptGtag.innerHTML = `
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '${key}');
`;

// Insert our scripts next to the first script element.
const first = document.getElementsByTagName('script')[0];
first.parentNode.insertBefore(scriptSrc, first);
first.parentNode.insertBefore(scriptGtag, first);
googleAnalytics._loadOptions = options; // eslint-disable-line no-underscore-dangle
};

// Load GoogleAnalytics with your key.
googleAnalytics.load(this.analyticsId);
}
}

export default GoogleAnalyticsLoader;
76 changes: 76 additions & 0 deletions src/scripts/GoogleAnalyticsLoader.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { GoogleAnalyticsLoader } from './index';

const googleAnalyticsId = 'test-key';

describe('GoogleAnalytics', () => {
let body;
let gaScriptSrc;
let gaScriptGtag;
let data;

beforeEach(() => {
window.googleAnalytics = [];
});

function loadGoogleAnalytics(scriptData) {
const script = new GoogleAnalyticsLoader(scriptData);
script.loadScript();
}

describe('with valid GOOGLE_ANALYTICS_4_ID', () => {
beforeEach(() => {
document.body.innerHTML = '<script id="stub" />';
data = {
config: {
GOOGLE_ANALYTICS_4_ID: googleAnalyticsId,
},
};
loadGoogleAnalytics(data);
expect(global.googleAnalytics.invoked).toBe(true);
body = document.body.innerHTML;
gaScriptSrc = `https://www.googletagmanager.com/gtag/js?id=${googleAnalyticsId}`;
gaScriptGtag = `gtag('config', '${googleAnalyticsId}');`;
});

it('should initialize google analytics', () => {
expect(body).toMatch(gaScriptSrc);
expect(body).toMatch(gaScriptGtag);
});

it('should not invoke snippet twice', () => {
loadGoogleAnalytics(data);

expect(global.googleAnalytics.invoked).toBe(true);

expect(body).toMatch(gaScriptSrc);
expect(body).toMatch(gaScriptGtag);

let count = (body.match(new RegExp(gaScriptSrc.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g')) || []).length;
expect(count).toBe(1);

count = (body.match(new RegExp(gaScriptGtag.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g')) || []).length;
expect(count).toBe(1);
});
});

describe('with invalid GOOGLE_ANALYTICS_ID', () => {
beforeEach(() => {
document.body.innerHTML = '<script id="stub" />';
data = {
config: {
GOOGLE_ANALYTICS_4_ID: '',
},
};
loadGoogleAnalytics(data);
body = document.body.innerHTML;
gaScriptSrc = 'https://www.googletagmanager.com/gtag/js?id=';
gaScriptGtag = "gtag('config', '');";
expect(global.googleAnalytics.invoked).toBeFalsy();
});

it('should not initialize google analytics', () => {
expect(body).not.toMatch(gaScriptSrc);
expect(body).not.toMatch(gaScriptGtag);
});
});
});
2 changes: 2 additions & 0 deletions src/scripts/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/* eslint-disable import/prefer-default-export */
export { default as GoogleAnalyticsLoader } from './GoogleAnalyticsLoader';

0 comments on commit c174b29

Please sign in to comment.