diff --git a/Gruntfile.js b/Gruntfile.js index 76203f99..ef050da6 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -30,10 +30,13 @@ module.exports = function(grunt) { javascripts: { src: [ 'node_modules/jquery-browser/lib/jquery.js', + 'javascripts/govuk/analytics/google-analytics-classic-tracker.js', + 'javascripts/govuk/analytics/google-analytics-universal-tracker.js', + 'javascripts/govuk/analytics/tracker.js', 'javascripts/**/*.js' ], options: { - specs: 'spec/unit/*Spec.js', + specs: 'spec/unit/**/*Spec.js', helpers: 'spec/unit/*Helper.js' } } diff --git a/javascripts/govuk/analytics/error-tracking.js b/javascripts/govuk/analytics/error-tracking.js new file mode 100644 index 00000000..d06bf04d --- /dev/null +++ b/javascripts/govuk/analytics/error-tracking.js @@ -0,0 +1,22 @@ +// Extension to track errors using google analytics as a data store. +(function() { + + "use strict"; + var trackJavaScriptError = function (e) { + var errorSource = e.filename + ': ' + e.lineno; + GOVUK.analytics.trackEvent('JavaScript Error', e.message, { + label: errorSource, + value: 1, + nonInteraction: true + }); + }; + + if (window.addEventListener) { + window.addEventListener('error', trackJavaScriptError, false); + } else if (window.attachEvent) { + window.attachEvent('onerror', trackJavaScriptError); + } else { + window.onerror = trackJavaScriptError; + } + +}()); diff --git a/javascripts/govuk/analytics/google-analytics-classic-tracker.js b/javascripts/govuk/analytics/google-analytics-classic-tracker.js new file mode 100644 index 00000000..3d5f2e0f --- /dev/null +++ b/javascripts/govuk/analytics/google-analytics-classic-tracker.js @@ -0,0 +1,111 @@ +(function() { + "use strict"; + window.GOVUK = window.GOVUK || {}; + + var GoogleAnalyticsClassicTracker = function(id, cookieDomain) { + window._gaq = window._gaq || []; + configureProfile(id, cookieDomain); + allowCrossDomainTracking(); + anonymizeIp(); + + function configureProfile(id, cookieDomain) { + _gaq.push(['_setAccount', id]); + _gaq.push(['_setDomainName', cookieDomain]); + } + + function allowCrossDomainTracking() { + _gaq.push(['_setAllowLinker', true]); + } + + // https://developers.google.com/analytics/devguides/collection/gajs/methods/gaJSApi_gat#_gat._anonymizeIp + function anonymizeIp() { + _gaq.push(['_gat._anonymizeIp']); + } + }; + + GoogleAnalyticsClassicTracker.load = function() { + var ga = document.createElement('script'), + s = document.getElementsByTagName('script')[0]; + + ga.async = true; + ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; + s.parentNode.insertBefore(ga, s); + }; + + // https://developers.google.com/analytics/devguides/collection/gajs/asyncMigrationExamples#VirtualPageviews + GoogleAnalyticsClassicTracker.prototype.trackPageview = function(path) { + var pageview = ['_trackPageview']; + + if (typeof path === "string") { + pageview.push(path); + } + + _gaq.push(pageview); + }; + + // https://developers.google.com/analytics/devguides/collection/gajs/eventTrackerGuide + GoogleAnalyticsClassicTracker.prototype.trackEvent = function(category, action, options) { + var value, + options = options || {}, + hasLabel = false, + hasValue = false, + evt = ["_trackEvent", category, action]; + + // Label is optional + if (typeof options.label === "string") { + hasLabel = true; + evt.push(options.label); + } + + // Value is optional, but when used must be an + // integer, otherwise the event will be invalid + // and not logged + if (options.value || options.value === 0) { + value = parseInt(options.value, 10); + if (typeof value === "number" && !isNaN(value)) { + hasValue = true; + + // Push an empty label if not set for correct final argument order + if (!hasLabel) { + evt.push(''); + } + + evt.push(value); + } + } + + // Prevents an event from affecting bounce rate + // https://developers.google.com/analytics/devguides/collection/gajs/eventTrackerGuide#non-interaction + if (options.nonInteraction) { + + // Push empty label/value if not already set, for correct final argument order + if (!hasValue) { + if (!hasLabel) { + evt.push(''); + } + evt.push(0); + } + + evt.push(true); + } + + _gaq.push(evt); + }; + + /* + https://developers.google.com/analytics/devguides/collection/gajs/methods/gaJSApiSocialTracking + network – The network on which the action occurs (e.g. Facebook, Twitter) + action – The type of action that happens (e.g. Like, Send, Tweet) + target – The text value that indicates the subject of the action + */ + GoogleAnalyticsClassicTracker.prototype.trackSocial = function(network, action, target) { + _gaq.push(['_trackSocial', network, action, target]); + }; + + // https://developers.google.com/analytics/devguides/collection/gajs/gaTrackingCustomVariables + GoogleAnalyticsClassicTracker.prototype.setCustomVariable = function(index, value, name, scope) { + _gaq.push(['_setCustomVar', index, name, String(value), scope]); + }; + + GOVUK.GoogleAnalyticsClassicTracker = GoogleAnalyticsClassicTracker; +})(); diff --git a/javascripts/govuk/analytics/google-analytics-universal-tracker.js b/javascripts/govuk/analytics/google-analytics-universal-tracker.js new file mode 100644 index 00000000..cdc86a35 --- /dev/null +++ b/javascripts/govuk/analytics/google-analytics-universal-tracker.js @@ -0,0 +1,104 @@ +(function() { + "use strict"; + window.GOVUK = window.GOVUK || {}; + + var GoogleAnalyticsUniversalTracker = function(id, cookieDomain) { + configureProfile(id, cookieDomain); + anonymizeIp(); + + function configureProfile(id, cookieDomain) { + sendToGa('create', id, {'cookieDomain': cookieDomain}); + } + + function anonymizeIp() { + // https://developers.google.com/analytics/devguides/collection/analyticsjs/advanced#anonymizeip + sendToGa('set', 'anonymizeIp', true); + } + }; + + GoogleAnalyticsUniversalTracker.load = function() { + (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'); + }; + + // https://developers.google.com/analytics/devguides/collection/analyticsjs/pages + GoogleAnalyticsUniversalTracker.prototype.trackPageview = function(path, title) { + if (typeof path === "string") { + var pageviewObject = { + page: path + }; + + if (typeof title === "string") { + pageviewObject.title = title; + } + sendToGa('send', 'pageview', pageviewObject); + } else { + sendToGa('send', 'pageview'); + } + }; + + // https://developers.google.com/analytics/devguides/collection/analyticsjs/events + GoogleAnalyticsUniversalTracker.prototype.trackEvent = function(category, action, options) { + var value, + options = options || {}, + evt = { + hitType: 'event', + eventCategory: category, + eventAction: action + }; + + // Label is optional + if (typeof options.label === "string") { + evt.eventLabel = options.label; + } + + // Value is optional, but when used must be an + // integer, otherwise the event will be invalid + // and not logged + if (options.value || options.value === 0) { + value = parseInt(options.value, 10); + if (typeof value === "number" && !isNaN(value)) { + evt.eventValue = value; + } + } + + // Prevents an event from affecting bounce rate + // https://developers.google.com/analytics/devguides/collection/analyticsjs/events#implementation + if (options.nonInteraction) { + evt.nonInteraction = 1; + } + + sendToGa('send', evt); + }; + + /* + https://developers.google.com/analytics/devguides/collection/analyticsjs/social-interactions + network – The network on which the action occurs (e.g. Facebook, Twitter) + action – The type of action that happens (e.g. Like, Send, Tweet) + target – Specifies the target of a social interaction. + This value is typically a URL but can be any text. + */ + GoogleAnalyticsUniversalTracker.prototype.trackSocial = function(network, action, target) { + sendToGa('send', { + 'hitType': 'social', + 'socialNetwork': network, + 'socialAction': action, + 'socialTarget': target + }); + }; + + // https://developers.google.com/analytics/devguides/collection/analyticsjs/custom-dims-mets + GoogleAnalyticsUniversalTracker.prototype.setDimension = function(index, value) { + sendToGa('set', 'dimension' + index, String(value)); + }; + + function sendToGa() { + if (typeof window.ga === "function") { + ga.apply(window, arguments); + } + } + + GOVUK.GoogleAnalyticsUniversalTracker = GoogleAnalyticsUniversalTracker; +})(); diff --git a/javascripts/govuk/analytics/print-intent.js b/javascripts/govuk/analytics/print-intent.js new file mode 100644 index 00000000..54f13f76 --- /dev/null +++ b/javascripts/govuk/analytics/print-intent.js @@ -0,0 +1,31 @@ +// Extension to monitor attempts to print pages. +(function() { + + "use strict"; + var printAttempt = (function() { + GOVUK.analytics.trackEvent('Print Intent', document.location.pathname); + }); + + // Most browsers + if (window.matchMedia) { + var mediaQueryList = window.matchMedia('print'), + mqlListenerCount = 0; + mediaQueryList.addListener(function(mql) { + if (!mql.matches && mqlListenerCount === 0) { + printAttempt(); + mqlListenerCount++; + // If we try and print again in 3 seconds, don't log it + window.setTimeout(function(){ + mqlListenerCount = 0; + // printing will be tracked again now + },1e3); + } + }); + } + + // IE < 10 + if(window.onafterprint){ + window.onafterprint = printAttempt; + } + +}()); diff --git a/javascripts/govuk/analytics/sample-init.js b/javascripts/govuk/analytics/sample-init.js new file mode 100644 index 00000000..0b427db5 --- /dev/null +++ b/javascripts/govuk/analytics/sample-init.js @@ -0,0 +1,20 @@ +(function() { + "use strict"; + + // Load Google Analytics libraries + GOVUK.Tracker.load(); + + // Use document.domain in dev, preview and staging so that tracking works + // Otherwise explicitly set the domain as www.gov.uk (and not gov.uk). + var cookieDomain = (document.domain === 'www.gov.uk') ? '.www.gov.uk' : document.domain; + + // Configure profiles, setup custom vars, track initial pageview + var analytics = new GOVUK.Tracker({ + universalId: 'UA-XXXXXXXX-X', + classicId: 'UA-XXXXXXXX-X', + cookieDomain: cookieDomain + }); + + // Make interface public for virtual pageviews and events + GOVUK.analytics = analytics; +})(); diff --git a/javascripts/govuk/analytics/tracker.js b/javascripts/govuk/analytics/tracker.js new file mode 100644 index 00000000..3797f5cf --- /dev/null +++ b/javascripts/govuk/analytics/tracker.js @@ -0,0 +1,67 @@ +(function() { + "use strict"; + window.GOVUK = window.GOVUK || {}; + + var Tracker = function(config) { + + this.universal = new GOVUK.GoogleAnalyticsUniversalTracker(config.universalId, config.cookieDomain); + this.classic = new GOVUK.GoogleAnalyticsClassicTracker(config.classicId, config.cookieDomain); + + // Dimensions should be set before page view is tracked + + if (config.prePageviewConfiguration && typeof config.prePageviewConfiguration === 'function') { + config.prePageviewConfiguration(); + } + + this.trackPageview(); + + }; + + Tracker.load = function() { + GOVUK.GoogleAnalyticsClassicTracker.load(); + GOVUK.GoogleAnalyticsUniversalTracker.load(); + }; + + Tracker.prototype.trackPageview = function(path, title) { + this.classic.trackPageview(path); + this.universal.trackPageview(path, title); + }; + + /* + https://developers.google.com/analytics/devguides/collection/analyticsjs/events + options.label – Useful for categorizing events (eg nav buttons) + options.value – Values must be non-negative. Useful to pass counts + options.nonInteraction – Prevent event from impacting bounce rate + */ + Tracker.prototype.trackEvent = function(category, action, options) { + this.classic.trackEvent(category, action, options); + this.universal.trackEvent(category, action, options); + }; + + Tracker.prototype.trackShare = function(network) { + var target = location.pathname; + this.classic.trackSocial(network, 'share', target); + this.universal.trackSocial(network, 'share', target); + }; + + /* + Assumes that the index of the dimension is the same for both classic and universal. Check this for your app before using this + */ + Tracker.prototype.setDimension = function(index, value, name, scope) { + var PAGE_LEVEL_SCOPE = 3; + scope = scope || PAGE_LEVEL_SCOPE; + + if (typeof index !== "number") { + index = parseInt(index, 10); + } + + if (typeof scope !== "number") { + scope = parseInt(scope, 10); + } + + this.universal.setDimension(index, value); + this.classic.setCustomVariable(index, value, name, scope); + }; + + GOVUK.Tracker = Tracker; +})(); diff --git a/spec/unit/analytics/GoogleAnalyticsClassicTrackerSpec.js b/spec/unit/analytics/GoogleAnalyticsClassicTrackerSpec.js new file mode 100644 index 00000000..9673d5e5 --- /dev/null +++ b/spec/unit/analytics/GoogleAnalyticsClassicTrackerSpec.js @@ -0,0 +1,124 @@ +describe("GOVUK.GoogleAnalyticsClassicTracker", function() { + var classic; + + beforeEach(function() { + window._gaq = []; + classic = new GOVUK.GoogleAnalyticsClassicTracker('id', 'cookie-domain.com'); + }); + + it('can load the libraries needed to run classic Google Analytics', function() { + delete window._gaq; + $('[src*="google-analytics.com/ga.js"]').remove(); + + GOVUK.GoogleAnalyticsClassicTracker.load(); + expect($('script[async][src*="google-analytics.com/ga.js"]').length).toBe(1); + }); + + describe('when created', function() { + it('configures a Google tracker using the provided profile ID and cookie domain', function() { + expect(window._gaq[0]).toEqual(['_setAccount', 'id']); + expect(window._gaq[1]).toEqual(['_setDomainName', 'cookie-domain.com']); + }); + + it('allows cross site linking', function() { + expect(window._gaq[2]).toEqual(['_setAllowLinker', true]); + }); + + it('anonymises the IP', function() { + expect(window._gaq[3]).toEqual(['_gat._anonymizeIp']); + }); + }); + + describe('when pageviews are tracked', function() { + beforeEach(function() { + // reset queue after setup + window._gaq = []; + }); + + it('sends them to Google Analytics', function() { + classic.trackPageview(); + expect(window._gaq[0]).toEqual(['_trackPageview']); + }); + + it('can track a virtual pageview', function() { + classic.trackPageview('/nicholas-page'); + expect(window._gaq[0]).toEqual(['_trackPageview', '/nicholas-page']); + }); + }); + + describe('when events are tracked', function() { + beforeEach(function() { + // reset queue after setup + window._gaq = []; + }); + + it('sends them to Google Analytics', function() { + classic.trackEvent('category', 'action', {label: 'label'}); + expect(window._gaq[0]).toEqual(['_trackEvent', 'category', 'action', 'label']); + }); + + it('the label is optional', function() { + classic.trackEvent('category', 'action'); + expect(window._gaq[0]).toEqual(['_trackEvent', 'category', 'action']); + }); + + it('a value can be tracked if the label is omitted', function() { + classic.trackEvent('category', 'action', {value: 10}); + expect(window._gaq[0]).toEqual(['_trackEvent', 'category', 'action', '', 10]); + }); + + it('only sends values if they are parseable as numbers', function() { + classic.trackEvent('category', 'action', {label: 'label', value: '10'}); + expect(window._gaq[0]).toEqual(['_trackEvent', 'category', 'action', 'label', 10]); + + classic.trackEvent('category', 'action', {label: 'label', value: 10}); + expect(window._gaq[1]).toEqual(['_trackEvent', 'category', 'action', 'label', 10]); + + classic.trackEvent('category', 'action', {label: 'label', value: 'not a number'}); + expect(window._gaq[2]).toEqual(['_trackEvent', 'category', 'action', 'label']); + }); + + it('can mark an event as non interactive', function() { + classic.trackEvent('category', 'action', {label: 'label', value: 1, nonInteraction: true}); + expect(window._gaq[0]).toEqual(['_trackEvent', 'category', 'action', 'label', 1, true]); + }); + + it('can mark an event as non interactive without a value', function() { + classic.trackEvent('category', 'action', {label: 'label', nonInteraction: true}); + expect(window._gaq[0]).toEqual(['_trackEvent', 'category', 'action', 'label', 0, true]); + }); + + it('can mark an event as non interactive without a label or value', function() { + classic.trackEvent('category', 'action', {nonInteraction: true}); + expect(window._gaq[0]).toEqual(['_trackEvent', 'category', 'action', '', 0, true]); + }); + }); + + describe('when social events are tracked', function() { + beforeEach(function() { + window._gaq = []; + }); + + it('sends them to Google Analytics', function() { + classic.trackSocial('network', 'action', 'target'); + expect(window._gaq[0]).toEqual(['_trackSocial', 'network', 'action', 'target']); + }); + }); + + describe('when setting a custom variable', function() { + beforeEach(function() { + // reset queue after setup + window._gaq = []; + }); + + it('sends the variable to Google Analytics', function() { + classic.setCustomVariable(1, 'value', 'name', 10); + expect(window._gaq[0]).toEqual(['_setCustomVar', 1, 'name', 'value', 10]); + }); + + it('coerces the value to a string', function() { + classic.setCustomVariable(1, 100, 'name', 10); + expect(window._gaq[0]).toEqual(['_setCustomVar', 1, 'name', '100', 10]); + }); + }); +}); diff --git a/spec/unit/analytics/GoogleAnalyticsUniversalTrackerSpec.js b/spec/unit/analytics/GoogleAnalyticsUniversalTrackerSpec.js new file mode 100644 index 00000000..286dbbca --- /dev/null +++ b/spec/unit/analytics/GoogleAnalyticsUniversalTrackerSpec.js @@ -0,0 +1,122 @@ +describe("GOVUK.GoogleAnalyticsUniversalTracker", function() { + var universal; + + beforeEach(function() { + window.ga = function() {}; + spyOn(window, 'ga'); + universal = new GOVUK.GoogleAnalyticsUniversalTracker('id', 'cookie-domain.com'); + }); + + it('can load the libraries needed to run universal Google Analytics', function() { + delete window.ga; + $('[src="//www.google-analytics.com/analytics.js"]').remove(); + GOVUK.GoogleAnalyticsUniversalTracker.load(); + expect($('script[async][src="//www.google-analytics.com/analytics.js"]').length).toBe(1); + expect(typeof window.ga).toBe('function'); + + window.ga('send message'); + expect(window.ga.q[0]).toEqual(jasmine.any(Object)); + }); + + describe('when created', function() { + var setupArguments; + + beforeEach(function() { + setupArguments = window.ga.calls.allArgs(); + }); + + it('configures a Google tracker using the provided profile ID and cookie domain', function() { + expect(setupArguments[0]).toEqual(['create', 'id', {'cookieDomain': 'cookie-domain.com'}]); + }); + + it('anonymises the IP', function() { + expect(setupArguments[1]).toEqual(['set', 'anonymizeIp', true]); + }); + }); + + describe('when pageviews are tracked', function() { + it('sends them to Google Analytics', function() { + universal.trackPageview(); + expect(window.ga.calls.mostRecent().args).toEqual(['send', 'pageview']); + }); + + it('can track a virtual pageview', function() { + universal.trackPageview('/nicholas-page'); + expect(window.ga.calls.mostRecent().args).toEqual(['send', 'pageview', {page: '/nicholas-page'}]); + }); + + it('can track a virtual pageview with a custom title', function() { + universal.trackPageview('/nicholas-page', 'Nicholas Page'); + expect(window.ga.calls.mostRecent().args).toEqual(['send', 'pageview', {page: '/nicholas-page', title: 'Nicholas Page'}]); + }); + }); + + describe('when events are tracked', function() { + function eventObjectFromSpy() { + return window.ga.calls.mostRecent().args[1]; + } + + it('sends them to Google Analytics', function() { + universal.trackEvent('category', 'action', {label: 'label'}); + expect(window.ga.calls.mostRecent().args).toEqual( + ['send', {hitType: 'event', eventCategory: 'category', eventAction: 'action', eventLabel: 'label'}] + ); + }); + + it('the label is optional', function() { + universal.trackEvent('category', 'action'); + expect(window.ga.calls.mostRecent().args).toEqual( + ['send', {hitType: 'event', eventCategory: 'category', eventAction: 'action'}] + ); + }); + + it('only sends values if they are parseable as numbers', function() { + universal.trackEvent('category', 'action', {label: 'label', value: '10'}); + expect(eventObjectFromSpy()['eventValue']).toEqual(10); + + universal.trackEvent('category', 'action', {label: 'label', value: 10}); + expect(eventObjectFromSpy()['eventValue']).toEqual(10); + + universal.trackEvent('category', 'action', {label: 'label', value: 'not a number'}); + expect(eventObjectFromSpy()['eventValue']).toEqual(undefined); + }); + + it('can mark an event as non interactive', function() { + universal.trackEvent('category', 'action', {label: 'label', value: 0, nonInteraction: true}); + expect(window.ga.calls.mostRecent().args).toEqual( + ['send', { + hitType: 'event', + eventCategory: 'category', + eventAction: 'action', + eventLabel: 'label', + eventValue: 0, + nonInteraction: 1 + }] + ); + }); + }); + + describe('when social events are tracked', function() { + it('sends them to Google Analytics', function() { + universal.trackSocial('network', 'action', 'target'); + expect(window.ga.calls.mostRecent().args).toEqual(['send', { + 'hitType': 'social', + 'socialNetwork': 'network', + 'socialAction': 'action', + 'socialTarget': 'target' + }]); + }); + }); + + describe('when setting a custom dimension', function() { + it('sends the dimension to Google Analytics with the specified index and value', function() { + universal.setDimension(1, 'value'); + expect(window.ga.calls.mostRecent().args).toEqual(['set', 'dimension1', 'value']); + }); + + it('coerces the value to a string', function() { + universal.setDimension(1, 10); + expect(window.ga.calls.mostRecent().args).toEqual(['set', 'dimension1', '10']); + }); + }); +}); diff --git a/spec/unit/analytics/TrackerSpec.js b/spec/unit/analytics/TrackerSpec.js new file mode 100644 index 00000000..96eb10cb --- /dev/null +++ b/spec/unit/analytics/TrackerSpec.js @@ -0,0 +1,94 @@ +describe("GOVUK.Tracker", function() { + var tracker; + + beforeEach(function() { + window._gaq = []; + window.ga = function() {}; + spyOn(window, 'ga'); + this.config = { + universalId: 'universal-id', + classicId: 'classic-id', + cookieDomain: '.www.gov.uk' + }; + }); + + describe('when created', function() { + var universalSetupArguments; + + beforeEach(function () { + tracker = new GOVUK.Tracker(this.config); + universalSetupArguments = window.ga.calls.allArgs(); + }); + + it('configures classic and universal trackers', function () { + expect(window._gaq[0]).toEqual(['_setAccount', 'classic-id']); + expect(window._gaq[1]).toEqual(['_setDomainName', '.www.gov.uk']); + expect(universalSetupArguments[0]).toEqual(['create', 'universal-id', {'cookieDomain': '.www.gov.uk'}]); + }); + + it('tracks a pageview in both classic and universal', function () { + expect(window._gaq[4]).toEqual(['_trackPageview']); + expect(universalSetupArguments[2]).toEqual(['send', 'pageview']); + }); + + }); + + + describe('created with a pre-pageview callback', function() { + + it('calls a pre-pageview callback function, if supplied', function () { + var config = { + universalId: 'universal-id', + prePageviewConfiguration: jasmine.createSpy() + }; + tracker = new GOVUK.Tracker(config); + expect(config.prePageviewConfiguration).toHaveBeenCalled(); + }); + + }); + + describe('when tracking pageviews, events and custom dimensions', function() { + + beforeEach(function() { + tracker = new GOVUK.Tracker(this.config); + }); + + it('tracks in both classic and universal', function() { + window._gaq = []; + tracker.trackPageview('/path', 'Title'); + expect(window._gaq[0]).toEqual(['_trackPageview', '/path']); + expect(window.ga.calls.mostRecent().args).toEqual(['send', 'pageview', {page: '/path', title: 'Title'}]); + + window._gaq = []; + tracker.trackEvent('category', 'action'); + expect(window._gaq[0]).toEqual(['_trackEvent', 'category', 'action']); + expect(window.ga.calls.mostRecent().args).toEqual(['send', {hitType: 'event', eventCategory: 'category', eventAction: 'action'}]); + + window._gaq = []; + tracker.setDimension(1, 'value', 'name'); + expect(window._gaq[0]).toEqual(['_setCustomVar', 1, 'name', 'value', 3]); + expect(window.ga.calls.mostRecent().args).toEqual(['set', 'dimension1', 'value']); + }); + }); + + describe('when tracking social media shares', function() { + + beforeEach(function() { + tracker = new GOVUK.Tracker(this.config); + }); + + it('tracks in both classic and universal', function() { + window._gaq = []; + tracker.trackShare('network'); + + expect(window._gaq[0]).toEqual(['_trackSocial', 'network', 'share', jasmine.any(String)]); + expect(window.ga.calls.mostRecent().args).toEqual(['send', { + hitType: 'social', + socialNetwork: 'network', + socialAction: 'share', + socialTarget: jasmine.any(String) + }]); + }); + }); + +});