diff --git a/CHANGELOG.md b/CHANGELOG.md index c4c6059a74..922ee405f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ useful summary for people upgrading their application, not a replication of the commit log. +## Unreleased + +* Migrate cross domain tracking script from static ([PR #2607](https://github.com/alphagov/govuk_publishing_components/pull/2607)) + ## 28.9.1 * Update the list of popular links in the super navigation header ([#2660](https://github.com/alphagov/govuk_publishing_components/pull/2660)) diff --git a/app/assets/javascripts/govuk_publishing_components/analytics.js b/app/assets/javascripts/govuk_publishing_components/analytics.js index f2c89f42ac..e3648f6c77 100644 --- a/app/assets/javascripts/govuk_publishing_components/analytics.js +++ b/app/assets/javascripts/govuk_publishing_components/analytics.js @@ -1,3 +1,5 @@ +// these scripts are specifically ordered + //= require govuk_publishing_components/lib/cookie-functions //= require govuk_publishing_components/lib/extend-object //= require ./analytics/pii @@ -18,3 +20,4 @@ //= require ./analytics/explicit-cross-domain-links //= require ./analytics/track-click //= require ./analytics/track-select-change +//= require ./analytics/cross-domain-tracking diff --git a/app/assets/javascripts/govuk_publishing_components/analytics/cross-domain-tracking.js b/app/assets/javascripts/govuk_publishing_components/analytics/cross-domain-tracking.js new file mode 100644 index 0000000000..63c5155300 --- /dev/null +++ b/app/assets/javascripts/govuk_publishing_components/analytics/cross-domain-tracking.js @@ -0,0 +1,55 @@ +window.GOVUK = window.GOVUK || {} +window.GOVUK.Modules = window.GOVUK.Modules || {}; + +(function (Modules) { + function CrossDomainTracking ($module) { + this.$module = $module + Modules.crossDomainLinkedTrackers = Modules.crossDomainLinkedTrackers || [] + } + + CrossDomainTracking.prototype.init = function () { + if (this.isTrackable(this.$module)) { + this.addLinkedTrackerDomain(this.$module) + } else { + this.findTrackableElements() + } + } + + CrossDomainTracking.prototype.isTrackable = function (element) { + if (element.getAttribute('href') && element.getAttribute('data-tracking-code') && element.getAttribute('data-tracking-name')) { + return true + } + } + + CrossDomainTracking.prototype.findTrackableElements = function () { + var links = this.$module.querySelectorAll('a') + for (var i = 0; i < links.length; i++) { + if (this.isTrackable(links[i])) { + this.addLinkedTrackerDomain(links[i]) + } + } + } + + CrossDomainTracking.prototype.addLinkedTrackerDomain = function (element) { + var name = element.getAttribute('data-tracking-name') + var code = element.getAttribute('data-tracking-code') + var trackEvent = (element.getAttribute('data-tracking-track-event') === 'true') + + if (window.GOVUK.analytics !== 'undefined') { + if (Modules.crossDomainLinkedTrackers.indexOf(name) === -1) { + var hostname = element.hostname + window.GOVUK.analytics.addLinkedTrackerDomain(code, name, hostname) + Modules.crossDomainLinkedTrackers.push(name) + } + + if (trackEvent) { + element.addEventListener('click', function (e) { + var target = e.target + window.GOVUK.analytics.trackEvent('External Link Clicked', target.textContent, { trackerName: name }) + }) + } + } + } + + Modules.CrossDomainTracking = CrossDomainTracking +})(window.GOVUK.Modules) diff --git a/spec/javascripts/govuk_publishing_components/analytics/analytics.spec.js b/spec/javascripts/govuk_publishing_components/analytics/analytics.spec.js index 045c98e8ca..30fd18cc19 100644 --- a/spec/javascripts/govuk_publishing_components/analytics/analytics.spec.js +++ b/spec/javascripts/govuk_publishing_components/analytics/analytics.spec.js @@ -22,6 +22,8 @@ describe('GOVUK.Analytics', function () { cookieDomain: '.www.gov.uk', siteSpeedSampleRate: 100 }) + + GOVUK.analytics.setDimension = GOVUK.analytics.setDimension || {} }) afterEach(function () { diff --git a/spec/javascripts/govuk_publishing_components/analytics/cross-domain-tracking.spec.js b/spec/javascripts/govuk_publishing_components/analytics/cross-domain-tracking.spec.js new file mode 100644 index 0000000000..6b98b6cdf0 --- /dev/null +++ b/spec/javascripts/govuk_publishing_components/analytics/cross-domain-tracking.spec.js @@ -0,0 +1,176 @@ +/* eslint-env jasmine, jquery */ + +describe('Cross Domain Tracking', function () { + 'use strict' + var module + var GOVUK = window.GOVUK + + beforeEach(function () { + GOVUK.Modules.crossDomainLinkedTrackers = [] + spyOn(GOVUK.analytics, 'addLinkedTrackerDomain') + }) + + it('tracks realistic example', function () { + var anchorToTest = document.createElement('a') + anchorToTest.href = 'https://www.registertovote.service.gov.uk/register-to-vote/start' + anchorToTest.className = 'gem-c-button gem-c-button--start' + anchorToTest.setAttribute('role', 'button') + anchorToTest.setAttribute('data-module', 'cross-domain-tracking') + anchorToTest.setAttribute('data-tracking-code', 'UA-23066786-5') + anchorToTest.setAttribute('data-tracking-name', 'transactionTracker') + anchorToTest.textContent = 'Start Now' + + module = new GOVUK.Modules.CrossDomainTracking($(anchorToTest)[0]) + module.init() + + expect( + GOVUK.analytics.addLinkedTrackerDomain + ).toHaveBeenCalledWith('UA-23066786-5', 'transactionTracker', 'www.registertovote.service.gov.uk') + }) + + it('tracks links with cross-domain-analytics data attributes', function () { + var anchorToTest = document.createElement('a') + anchorToTest.href = 'https://www.gov.uk/browse/citizenship/voting' + anchorToTest.setAttribute('data-module', 'cross-domain-tracking') + anchorToTest.setAttribute('data-tracking-code', 'UA-XXXXXXXXX-Y') + anchorToTest.setAttribute('data-tracking-name', 'govspeakButtonTracker') + + var wrapperDiv = document.createElement('div') + wrapperDiv.appendChild(anchorToTest) + + module = new GOVUK.Modules.CrossDomainTracking($(wrapperDiv)[0]) + module.init() + + expect( + GOVUK.analytics.addLinkedTrackerDomain + ).toHaveBeenCalledWith('UA-XXXXXXXXX-Y', 'govspeakButtonTracker', 'www.gov.uk') + }) + + it('tracks multiple links', function () { + var anchorToTest = document.createElement('a') + anchorToTest.href = 'https://www.gov.uk/browse/citizenship/voting' + anchorToTest.setAttribute( + 'data-tracking-code', + 'UA-XXXXXXXXX-Y' + ) + anchorToTest.setAttribute( + 'data-tracking-name', + 'govspeakButtonTracker' + ) + + var secondAnchorToTest = document.createElement('a') + secondAnchorToTest.href = 'https://www.registertovote.service.gov.uk/register-to-vote/start' + secondAnchorToTest.setAttribute( + 'data-tracking-code', + 'UA-23066786-5' + ) + secondAnchorToTest.setAttribute( + 'data-tracking-name', + 'transactionTracker' + ) + + var wrapperDiv = document.createElement('div') + wrapperDiv.appendChild(anchorToTest) + wrapperDiv.appendChild(secondAnchorToTest) + + module = new GOVUK.Modules.CrossDomainTracking($(wrapperDiv)[0]) + module.init() + + expect( + GOVUK.analytics.addLinkedTrackerDomain + ).toHaveBeenCalledWith('UA-XXXXXXXXX-Y', 'govspeakButtonTracker', 'www.gov.uk') + + expect( + GOVUK.analytics.addLinkedTrackerDomain + ).toHaveBeenCalledWith('UA-23066786-5', 'transactionTracker', 'www.registertovote.service.gov.uk') + }) + + it('tracks doesnt track if data attributes are not there', function () { + var anchorToTest = document.createElement('a') + anchorToTest.href = 'https://www.registertovote.service.gov.uk/register-to-vote/start' + + var wrapperDiv = document.createElement('div') + wrapperDiv.appendChild(anchorToTest) + + module = new GOVUK.Modules.CrossDomainTracking($(wrapperDiv)[0]) + module.init() + + expect(GOVUK.analytics.addLinkedTrackerDomain).not.toHaveBeenCalled() + }) + + it('can be configured to track events', function () { + GOVUK.analytics = { trackEvent: function () {}, addLinkedTrackerDomain: function () {} } + spyOn(GOVUK.analytics, 'trackEvent') + spyOn(GOVUK.analytics, 'addLinkedTrackerDomain') + + var anchorToTest = document.createElement('a') + anchorToTest.href = 'https://www.gov.uk/browse/citizenship/voting' + anchorToTest.innerText = 'Do some voting' + anchorToTest.setAttribute('data-module', 'cross-domain-tracking') + anchorToTest.setAttribute('data-tracking-code', 'UA-XXXXXXXXX-Y') + anchorToTest.setAttribute('data-tracking-name', 'govspeakButtonTracker') + anchorToTest.setAttribute('data-tracking-track-event', 'true') + + var wrapperDiv = document.createElement('div') + wrapperDiv.appendChild(anchorToTest) + + module = new GOVUK.Modules.CrossDomainTracking($(wrapperDiv)[0]) + module.init() + + expect( + GOVUK.analytics.addLinkedTrackerDomain + ).toHaveBeenCalledWith('UA-XXXXXXXXX-Y', 'govspeakButtonTracker', 'www.gov.uk') + + window.GOVUK.triggerEvent($(anchorToTest)[0], 'click') + + expect( + GOVUK.analytics.trackEvent + ).toHaveBeenCalledWith('External Link Clicked', 'Do some voting', { trackerName: 'govspeakButtonTracker' }) + }) + + it('adds the linked tracker domain once for multiple cross domain tracking elements', function () { + GOVUK.analytics = { trackEvent: function () {}, addLinkedTrackerDomain: function () {} } + spyOn(GOVUK.analytics, 'trackEvent') + spyOn(GOVUK.analytics, 'addLinkedTrackerDomain') + + var anchor1 = document.createElement('a') + anchor1.href = 'https://www.gov.uk/browse/citizenship/surfing' + anchor1.setAttribute('data-module', 'cross-domain-tracking') + anchor1.setAttribute('data-tracking-code', 'UA-XXXXXXXXX-Y') + anchor1.setAttribute('data-tracking-name', 'govspeakButtonTracker') + anchor1.setAttribute('data-tracking-track-event', 'true') + anchor1.innerText = 'Do some surfing' + + var anchor2 = document.createElement('a') + anchor2.href = 'https://www.gov.uk/browse/citizenship/shopping' + anchor2.setAttribute('data-module', 'cross-domain-tracking') + anchor2.setAttribute('data-tracking-code', 'UA-XXXXXXXXX-Y') + anchor2.setAttribute('data-tracking-name', 'govspeakButtonTracker') + anchor2.setAttribute('data-tracking-track-event', 'true') + anchor2.innerText = 'Do some shopping' + + var wrapperDiv = document.createElement('div') + wrapperDiv.appendChild(anchor1) + wrapperDiv.appendChild(anchor2) + + module = new GOVUK.Modules.CrossDomainTracking($(wrapperDiv)[0]) + module.init() + + var moduleDup = new GOVUK.Modules.CrossDomainTracking($(anchor2)[0]) + moduleDup.init() + + expect(GOVUK.analytics.addLinkedTrackerDomain.calls.count()).toBe(1) + + window.GOVUK.triggerEvent($(anchor1)[0], 'click') + + expect( + GOVUK.analytics.trackEvent + ).toHaveBeenCalledWith('External Link Clicked', 'Do some surfing', { trackerName: 'govspeakButtonTracker' }) + + window.GOVUK.triggerEvent($(anchor2)[0], 'click') + + expect( + GOVUK.analytics.trackEvent + ).toHaveBeenCalledWith('External Link Clicked', 'Do some shopping', { trackerName: 'govspeakButtonTracker' }) + }) +}) diff --git a/spec/javascripts/govuk_publishing_components/analytics/external-link-tracker.spec.js b/spec/javascripts/govuk_publishing_components/analytics/external-link-tracker.spec.js index 5677595ef5..9082b4545d 100644 --- a/spec/javascripts/govuk_publishing_components/analytics/external-link-tracker.spec.js +++ b/spec/javascripts/govuk_publishing_components/analytics/external-link-tracker.spec.js @@ -22,6 +22,7 @@ describe('GOVUK.analyticsPlugins.externalLinkTracker', function () { $('html').on('click', function (evt) { evt.preventDefault() }) $('body').append($links) + GOVUK.analytics.setDimension = GOVUK.analytics.setDimension || {} spyOn(GOVUK.analyticsPlugins.externalLinkTracker, 'getHostname').and.returnValue('fake-hostname.com') spyOn(GOVUK.analytics, 'trackEvent') diff --git a/spec/javascripts/govuk_publishing_components/analytics/google-analytics-universal-tracker.spec.js b/spec/javascripts/govuk_publishing_components/analytics/google-analytics-universal-tracker.spec.js index 1953e9f0eb..d90b976a39 100644 --- a/spec/javascripts/govuk_publishing_components/analytics/google-analytics-universal-tracker.spec.js +++ b/spec/javascripts/govuk_publishing_components/analytics/google-analytics-universal-tracker.spec.js @@ -23,6 +23,8 @@ describe('GOVUK.GoogleAnalyticsUniversalTracker', function () { cookieDomain: 'cookie-domain.com', siteSpeedSampleRate: 100 }) + + GOVUK.analytics.setDimension = GOVUK.analytics.setDimension || {} }) afterEach(function () {