From da623c87d6ec5612258f808c722a2a7003fad4ef Mon Sep 17 00:00:00 2001 From: Steven Reilly Date: Mon, 12 Jun 2023 13:52:38 -0400 Subject: [PATCH] Add Google Tag Manager for analytics (#541) --- app/assets/javascripts/analytics/analytics.js | 69 ---------- app/assets/javascripts/analytics/init.js | 40 ------ app/assets/js/gtm_head.js | 5 + app/templates/admin_template.html | 5 + gulpfile.js | 9 +- .../test_email_branding_requests.py | 4 +- tests/javascripts/analytics/analytics.test.js | 127 ------------------ tests/javascripts/analytics/init.test.js | 123 ----------------- 8 files changed, 18 insertions(+), 364 deletions(-) delete mode 100644 app/assets/javascripts/analytics/analytics.js delete mode 100644 app/assets/javascripts/analytics/init.js create mode 100644 app/assets/js/gtm_head.js delete mode 100644 tests/javascripts/analytics/analytics.test.js delete mode 100644 tests/javascripts/analytics/init.test.js diff --git a/app/assets/javascripts/analytics/analytics.js b/app/assets/javascripts/analytics/analytics.js deleted file mode 100644 index e589c9859..000000000 --- a/app/assets/javascripts/analytics/analytics.js +++ /dev/null @@ -1,69 +0,0 @@ -(function (window) { - "use strict"; - - window.GOVUK = window.GOVUK || {}; - - // Stripped-down wrapper for Google Analytics, based on: - // https://github.com/alphagov/static/blob/master/doc/analytics.md - const Analytics = function (config) { - window.ga('create', { - 'trackingId': config.trackingId, - 'cookieDomain': config.cookieDomain, - 'cookieExpires': config.expires * 24 * 60 * 60 - }); - - window.ga('set', 'anonymizeIp', config.anonymizeIp); - window.ga('set', 'allowAdFeatures', config.allowAdFeatures); - window.ga('set', 'transport', config.transport); - window.ga('set', 'title', 'U.S. Notify'); - - }; - - Analytics.load = function () { - /* jshint ignore:start */ - (function (i, s, o, g, r, a, m) { - i['GoogleAnalyticsObject'] = r; i[r] = i[r] || function () { - (i[r].q = i[r].q || []).push(arguments) - }, i[r].l = 1 * new Date(); a = s.createElement(o), - m = s.getElementsByTagName(o)[0]; a.async = 1; a.src = g; m.parentNode.insertBefore(a, m) - })(window, document, 'script', '//www.google-analytics.com/analytics.js', 'ga'); - /* jshint ignore:end */ - - }; - - Analytics.prototype.trackPageview = function (path, title, options) { - - // strip UUIDs - const page = `${window.location.pathname}${window.location.search}`.replace( - /[a-f0-9]{8}-?[a-f0-9]{4}-?4[a-f0-9]{3}-?[89ab][a-f0-9]{3}-?[a-f0-9]{12}/g, '…' - ); - window.ga('send', 'pageview', page); - - }; - - // https://developers.google.com/analytics/devguides/collection/analyticsjs/events - Analytics.prototype.trackEvent = function (category, action, options) { - - options = options || {}; - - var evt = { - eventCategory: category, - eventAction: action - }; - - if (options.label) { - evt.eventLabel = options.label; - delete options.label; - } - - if (typeof options === 'object') { - $.extend(evt, options); - } - - window.ga('send', 'event', evt); - - }; - - window.GOVUK.Analytics = Analytics; - -})(window); diff --git a/app/assets/javascripts/analytics/init.js b/app/assets/javascripts/analytics/init.js deleted file mode 100644 index 63f8b495a..000000000 --- a/app/assets/javascripts/analytics/init.js +++ /dev/null @@ -1,40 +0,0 @@ -(function (window) { - "use strict"; - - window.GOVUK = window.GOVUK || {}; - - const trackingId = 'UA-75215134-1'; - - // Disable analytics by default - window[`ga-disable-${trackingId}`] = true; - - const initAnalytics = function () { - - // guard against being called more than once - if (!('analytics' in window.GOVUK)) { - - window[`ga-disable-${trackingId}`] = false; - - // Load Google Analytics libraries - window.GOVUK.Analytics.load(); - - // Configure profiles and make interface public - // for custom dimensions, virtual pageviews and events - window.GOVUK.analytics = new GOVUK.Analytics({ - trackingId: trackingId, - cookieDomain: 'auto', - anonymizeIp: true, - allowAdFeatures: false, - transport: 'beacon', - expires: 365 - }); - - // Track initial pageview - window.GOVUK.analytics.trackPageview(); - - } - - }; - - window.GOVUK.initAnalytics = initAnalytics; -})(window); diff --git a/app/assets/js/gtm_head.js b/app/assets/js/gtm_head.js new file mode 100644 index 000000000..e6f73d28d --- /dev/null +++ b/app/assets/js/gtm_head.js @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/app/templates/admin_template.html b/app/templates/admin_template.html index 4cd04d3b4..8e91178f2 100644 --- a/app/templates/admin_template.html +++ b/app/templates/admin_template.html @@ -31,6 +31,7 @@ {% endblock %} {% block meta %} {% endblock %} + {% endblock %} {% block pageTitle %} @@ -39,6 +40,10 @@ {% block bodyStart %} {% block extra_javascripts_before_body %} + + + {% endblock %} {% endblock %} diff --git a/gulpfile.js b/gulpfile.js index 862b88486..8bc1aeb45 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -54,6 +54,10 @@ const copy = { fonts: () => { return src(paths.src + 'fonts/**/*') .pipe(dest(paths.dist + 'fonts/')); + }, + gtm: () => { + return src(paths.src + 'js/gtm_head.js') + .pipe(dest(paths.dist + 'js/')); } }; @@ -103,8 +107,6 @@ const javascripts = () => { paths.toolkit + 'javascripts/govuk/show-hide-content.js', paths.src + 'javascripts/govuk/cookie-functions.js', paths.src + 'javascripts/consent.js', - paths.src + 'javascripts/analytics/analytics.js', - paths.src + 'javascripts/analytics/init.js', paths.src + 'javascripts/cookieMessage.js', paths.src + 'javascripts/stick-to-window-when-scrolling.js', paths.src + 'javascripts/copyToClipboard.js', @@ -244,7 +246,8 @@ const defaultTask = parallel( ), sass, uswds.compile, - uswds.copyAssets + uswds.copyAssets, + copy.gtm ) ); diff --git a/tests/app/main/views/service_settings/test_email_branding_requests.py b/tests/app/main/views/service_settings/test_email_branding_requests.py index 5e20236cb..5739f91dc 100644 --- a/tests/app/main/views/service_settings/test_email_branding_requests.py +++ b/tests/app/main/views/service_settings/test_email_branding_requests.py @@ -37,7 +37,7 @@ def test_email_branding_request_page_when_no_branding_is_set( ) assert mock_get_email_branding.called is False - assert page.find('iframe')['src'] == url_for('main.email_template', branding_style='__NONE__') + assert page.find_all('iframe')[1]['src'] == url_for('main.email_template', branding_style='__NONE__') button_text = normalize_spaces(page.select_one('.page-footer button').text) @@ -68,7 +68,7 @@ def test_email_branding_request_page_shows_branding_if_set( page = client_request.get( '.email_branding_request', service_id=SERVICE_ONE_ID ) - assert page.find('iframe')['src'] == url_for('main.email_template', branding_style='some-random-branding') + assert page.find_all('iframe')[1]['src'] == url_for('main.email_template', branding_style='some-random-branding') def test_email_branding_request_page_back_link( diff --git a/tests/javascripts/analytics/analytics.test.js b/tests/javascripts/analytics/analytics.test.js deleted file mode 100644 index 65189025c..000000000 --- a/tests/javascripts/analytics/analytics.test.js +++ /dev/null @@ -1,127 +0,0 @@ -const helpers = require('../support/helpers'); - -beforeAll(() => { - - // add the script GA looks for in the document - document.body.appendChild(document.createElement('script')); - - require('../../../app/assets/javascripts/govuk/cookie-functions.js'); - require('../../../app/assets/javascripts/analytics/analytics.js'); - require('../../../app/assets/javascripts/analytics/init.js'); - -}); - -afterAll(() => { - - require('../support/teardown.js'); - -}); - -describe("Analytics", () => { - - let analytics; - - beforeEach(() => { - - window.ga = jest.fn(); - - analytics = new GOVUK.Analytics({ - trackingId: 'UA-75215134-1', - cookieDomain: 'auto', - anonymizeIp: true, - allowAdFeatures: false, - transport: 'beacon', - expires: 365 - }); - - }); - - afterEach(() => { - - window.ga.mockClear(); - - }); - - describe("When created", () => { - - test("It configures a tracker", () => { - - setUpArguments = window.ga.mock.calls; - - expect(setUpArguments[0]).toEqual(['create', { 'trackingId': 'UA-75215134-1', 'cookieDomain': 'auto', 'cookieExpires': 31536000 }]); - expect(setUpArguments[1]).toEqual(['set', 'anonymizeIp', true]); - expect(setUpArguments[2]).toEqual(['set', 'allowAdFeatures', false]); - expect(setUpArguments[3]).toEqual(['set', 'transport', 'beacon']); - expect(setUpArguments[4]).toEqual(['set', 'title', 'U.S. Notify']); - - }); - - }); - - describe("When tracking pageviews", () => { - - beforeEach(() => { - - // clear calls to window.ga from set up - window.ga.mockClear(); - - }); - - test("It sends the right URL for the page if no arguments", () => { - - jest.spyOn(window, 'location', 'get').mockImplementation(() => { - return { - 'pathname': '/privacy', - 'search': '' - }; - }); - - analytics.trackPageview(); - - expect(window.ga.mock.calls[0]).toEqual(['send', 'pageview', '/privacy']); - - }); - - test("It strips the UUIDs from URLs", () => { - - jest.spyOn(window, 'location', 'get').mockImplementation(() => { - return { - 'pathname': '/services/6658542f-0cad-491f-bec8-ab8457700ead', - 'search': '' - }; - }); - - analytics.trackPageview(); - - expect(window.ga.mock.calls[0]).toEqual(['send', 'pageview', '/services/…']); - - }); - - }); - - describe("When tracking events", () => { - - beforeEach(() => { - - // clear calls to window.ga from set up - window.ga.mockClear(); - - }); - - test("It sends the right arguments to `ga`", () => { - - analytics.trackEvent('Error', 'Enter a valid email address', { - 'label': 'email_address' - }); - - expect(window.ga.mock.calls[0]).toEqual(['send', 'event', { - 'eventCategory': 'Error', - 'eventAction': 'Enter a valid email address', - 'eventLabel': 'email_address' - }]); - - }); - - }); - -}); diff --git a/tests/javascripts/analytics/init.test.js b/tests/javascripts/analytics/init.test.js deleted file mode 100644 index 6bf7a31c8..000000000 --- a/tests/javascripts/analytics/init.test.js +++ /dev/null @@ -1,123 +0,0 @@ -const helpers = require('../support/helpers'); - -beforeAll(() => { - - // add the script GA looks for in the document - document.body.appendChild(document.createElement('script')); - - require('../../../app/assets/javascripts/govuk/cookie-functions.js'); - require('../../../app/assets/javascripts/analytics/analytics.js'); - require('../../../app/assets/javascripts/analytics/init.js'); - -}); - -afterAll(() => { - - require('../support/teardown.js'); - -}); - -describe("Analytics init", () => { - - beforeAll(() => { - - window.ga = jest.fn(); - jest.spyOn(window.GOVUK.Analytics, 'load'); - - // pretend we're on the /privacy page - jest.spyOn(window, 'location', 'get').mockImplementation(() => { - return { - 'pathname': '/privacy', - 'search': '' - }; - }); - - }); - - afterEach(() => { - - window.GOVUK.Analytics.load.mockClear(); - window.ga.mockClear(); - - }); - - test("After the init.js script has been loaded, Google Analytics will be disabled", () => { - - expect(window['ga-disable-UA-75215134-1']).toBe(true); - - }); - - describe("If initAnalytics has already been called", () => { - - beforeAll(() => { - - // Fake a tracker instance - window.GOVUK.analytics = {}; - - }); - - beforeEach(() => { - - window.GOVUK.initAnalytics(); - - }); - - afterAll(() => { - - delete window.GOVUK.analytics; - - }); - - test("The Google Analytics libraries will not be loaded", () => { - - expect(window.GOVUK.Analytics.load).not.toHaveBeenCalled(); - - }); - - }); - - describe("If initAnalytics has not been called", () => { - - beforeEach(() => { - - window.GOVUK.initAnalytics(); - - }); - - afterEach(() => { - - // window.GOVUK.initAnalytics sets up a new window.GOVUK.analytics which needs clearing - delete window.GOVUK.analytics; - - }); - - test("Google Analytics will not be disabled", () => { - - expect(window['ga-disable-UA-75215134-1']).toBe(false); - - }); - - test("The Google Analytics libraries will have been loaded", () => { - - expect(window.GOVUK.Analytics.load).toHaveBeenCalled(); - - }); - - test("There will be an interface with the Google Analytics API", () => { - - expect(window.GOVUK.analytics).toBeDefined(); - - }); - - test("A pageview will be registered", () => { - - expect(window.ga.mock.calls.length).toEqual(6); - - // The first 5 calls configure the analytics tracker. All subsequent calls send data - expect(window.ga.mock.calls[5]).toEqual(['send', 'pageview', '/privacy']); - - }); - - }); - -});