From 561d9354f3a8ba8602f092140d5f7263c89e3a52 Mon Sep 17 00:00:00 2001 From: Alec Gibson Date: Fri, 5 May 2017 16:02:38 +0100 Subject: [PATCH 1/2] Revert "Merge pull request #1017 from alphagov/server-side-nav-tracking" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit dfcf9e8a8ef9ac51454671e62c759ae3a8128e6a, reversing changes made to d3dd5f9554be1ff43dad43701efd2816ab84c20b. We’ve seen a bug in our Google Analytics (GA) tracking, where the number of links and sections we report is sometimes incorrectly reported as zero (‘0’) to GA, even despite performing the count server-side and presenting it in a `meta` tag. We realised that part of the answer was because we aggregate `smart-answers` paths, so pages after the start page (which legitimately don't have a sidebar) will report a `0`, which is aggregated into the `smart-answers` "root" path. However, we also found a number of guidance content items which still exhibit the bug, but without any of the path aggregation. Since the bug is still affecting us even with server-side counting, we will revert to using a JavaScript count, because a JavaScript count: - will more accurately track what a user actually sees - makes it easier to count things like accordions, which are split across several Ruby partials The bug is extremely hard to track down, and so far no one has been able to reproduce it on their own machines. I’ve tried also using both Smokey and Wraith to attempt a high volume of traffic on Production, but all the counts were correct. The symptoms are listed here for posterity: - For some pages, we see two values tracked for the number of sidebar sections and links. One value is the correct count. The other value is zero (‘0’) - Inconsistent counts are seen independent of: - Navigation A/B test bucket - Rendering application - Browser - Operating System - Where we see an incorrect count, the incorrect count has a very high ratio of (unique page views/page views) (~100+). This would mean each of these users was refreshing/revisiting the page over 100 times in a single half-hour session --- .../javascripts/analytics/static-analytics.js | 34 +- .../analytics_meta_tags.raw.html.erb | 2 - ...nalytics_meta_tags_navigation.raw.html.erb | 9 - .../docs/analytics_meta_tags_navigation.yml | 10 - .../related_items.raw.html.erb | 18 +- .../taxonomy_sidebar.raw.html.erb | 16 +- .../analytics/static-analytics-spec.js | 302 +++++++++++++++++- .../analytics_meta_tags_navigation_test.rb | 13 - .../analytics_meta_tags_test.rb | 6 +- 9 files changed, 318 insertions(+), 92 deletions(-) delete mode 100644 app/views/govuk_component/analytics_meta_tags_navigation.raw.html.erb delete mode 100644 app/views/govuk_component/docs/analytics_meta_tags_navigation.yml delete mode 100644 test/govuk_component/analytics_meta_tags_navigation_test.rb diff --git a/app/assets/javascripts/analytics/static-analytics.js b/app/assets/javascripts/analytics/static-analytics.js index 3150ffae9..56316b4a5 100644 --- a/app/assets/javascripts/analytics/static-analytics.js +++ b/app/assets/javascripts/analytics/static-analytics.js @@ -108,8 +108,8 @@ this.setTaxonIdDimension(dimensions['taxon-id']); this.setTaxonSlugsDimension(dimensions['taxon-slugs']); this.setTaxonIdsDimension(dimensions['taxon-ids']); - this.setNavigationSectionsDimension(dimensions['navigation-sections']); - this.setNavigationLinksDimension(dimensions['navigation-links']); + this.setTotalNumberOfSections(); + this.setTotalNumberOfSectionLinks(); }; StaticAnalytics.prototype.setDimensionsThatDoNotHaveDefaultValues = function(dimensions) { @@ -176,7 +176,7 @@ StaticAnalytics.prototype.setThemesDimension = function(themes) { this.setDimension(3, themes || 'other'); - } + }; StaticAnalytics.prototype.setContentIdDimension = function(contentId) { this.setDimension(4, contentId || '00000000-0000-0000-0000-000000000000'); @@ -202,10 +202,6 @@ this.setDimension(10, locations); }; - StaticAnalytics.prototype.setSchemaNameDimension = function(position) { - this.setDimension(17, position); - }; - StaticAnalytics.prototype.setRenderingApplicationDimension = function(app) { this.setDimension(20, app); }; @@ -214,12 +210,28 @@ this.setDimension(21, position); }; - StaticAnalytics.prototype.setNavigationSectionsDimension = function(sections) { - this.setDimension(26, sections || '0'); + StaticAnalytics.prototype.setSchemaNameDimension = function(position) { + this.setDimension(17, position); }; - StaticAnalytics.prototype.setNavigationLinksDimension = function(links) { - this.setDimension(27, links || '0'); + StaticAnalytics.prototype.setTotalNumberOfSections = function() { + var sidebarSections = $('[data-track-count="sidebarRelatedItemSection"]').length; + var sidebarTaxons = $('[data-track-count="sidebarTaxonSection"]').length; + var accordionSubsections = $('[data-track-count="accordionSection"]').length; + var gridSections = $('a[data-track-category="navGridLinkClicked"]').length; + var totalNumberOfSections = sidebarSections || sidebarTaxons || accordionSubsections || gridSections; + this.setDimension(26, totalNumberOfSections); + }; + + StaticAnalytics.prototype.setTotalNumberOfSectionLinks = function() { + var relatedLinks = $('a[data-track-category="relatedLinkClicked"]').length; + var accordionLinks = $('a[data-track-category="navAccordionLinkClicked"]').length; + // Grid links are counted both as "sections" (see dimension 26), and as part of the total link count + var gridLinks = $('a[data-track-category="navGridLinkClicked"]').length + + $('a[data-track-category="navGridLeafLinkClicked"]').length; + var leafLinks = $('a[data-track-category="navLeafLinkClicked"]').length; + var totalNumberOfSectionLinks = relatedLinks || accordionLinks || gridLinks || leafLinks; + this.setDimension(27, totalNumberOfSectionLinks); }; StaticAnalytics.prototype.setNavigationPageTypeDimension = function(pageType) { diff --git a/app/views/govuk_component/analytics_meta_tags.raw.html.erb b/app/views/govuk_component/analytics_meta_tags.raw.html.erb index 784c726b5..07eb51490 100644 --- a/app/views/govuk_component/analytics_meta_tags.raw.html.erb +++ b/app/views/govuk_component/analytics_meta_tags.raw.html.erb @@ -81,5 +81,3 @@ <% meta_tags.each do |name, content| %> <% end %> - -<%= yield :analytics_meta_tags %> diff --git a/app/views/govuk_component/analytics_meta_tags_navigation.raw.html.erb b/app/views/govuk_component/analytics_meta_tags_navigation.raw.html.erb deleted file mode 100644 index 0579a8031..000000000 --- a/app/views/govuk_component/analytics_meta_tags_navigation.raw.html.erb +++ /dev/null @@ -1,9 +0,0 @@ -<% - number_of_sections ||= 0 - number_of_links ||= 0 -%> - -<% content_for :analytics_meta_tags do %> - - -<% end %> diff --git a/app/views/govuk_component/docs/analytics_meta_tags_navigation.yml b/app/views/govuk_component/docs/analytics_meta_tags_navigation.yml deleted file mode 100644 index b4f884335..000000000 --- a/app/views/govuk_component/docs/analytics_meta_tags_navigation.yml +++ /dev/null @@ -1,10 +0,0 @@ -name: Analytics Meta Tags -description: Meta tags to provide analytics information about the current page -body: | - A component that appends counts for navigation elements to the analytics_meta_tags component. - - The code which reads the meta tags can be found in static-analytics.js. -fixtures: - default: - number_of_sections: 2 - number_of_links: 6 diff --git a/app/views/govuk_component/related_items.raw.html.erb b/app/views/govuk_component/related_items.raw.html.erb index 80bf8bef3..9112f81c1 100644 --- a/app/views/govuk_component/related_items.raw.html.erb +++ b/app/views/govuk_component/related_items.raw.html.erb @@ -1,14 +1,7 @@ <% if local_assigns[:sections] && !sections.blank? %> - - <% - number_of_sections = 0 - number_of_links = 0 - %> - - - <%= render file: 'govuk_component/analytics_meta_tags_navigation.raw', - locals: { - number_of_sections: number_of_sections, - number_of_links: number_of_links, - } %> - <% end %> diff --git a/app/views/govuk_component/taxonomy_sidebar.raw.html.erb b/app/views/govuk_component/taxonomy_sidebar.raw.html.erb index 0c08cbce1..e39cd1ff4 100644 --- a/app/views/govuk_component/taxonomy_sidebar.raw.html.erb +++ b/app/views/govuk_component/taxonomy_sidebar.raw.html.erb @@ -1,14 +1,8 @@ <% if local_assigns[:items] && !items.blank? %> - <% - number_of_sections = 0 - number_of_links = 0 - %> - - <%= render file: 'govuk_component/analytics_meta_tags_navigation.raw', - locals: { - number_of_sections: number_of_sections, - number_of_links: number_of_links, - } %> - <% end %> diff --git a/spec/javascripts/analytics/static-analytics-spec.js b/spec/javascripts/analytics/static-analytics-spec.js index 42ec6a5cc..078f7cf23 100644 --- a/spec/javascripts/analytics/static-analytics-spec.js +++ b/spec/javascripts/analytics/static-analytics-spec.js @@ -181,18 +181,6 @@ describe("GOVUK.StaticAnalytics", function() { number: 59, defaultValue: 'other', setupArgumentsIndex: 13 - }, - { - name: 'navigation-sections', - number: 26, - defaultValue: '0', - setupArgumentsIndex: 14 - }, - { - name: 'navigation-links', - number: 27, - defaultValue: '0', - setupArgumentsIndex: 15 } ].forEach(function (dimension) { it('sets the ' + dimension.name + ' dimension from a meta tag if present', function () { @@ -214,6 +202,296 @@ describe("GOVUK.StaticAnalytics", function() { }); }); + describe('when tracking the number of sections and links on a page', function() { + describe('on a page with a normal sidebar', function() { + beforeEach(function() { + $('body').append('\ +
\ + \ +
\ + '); + }); + + afterEach(function() { + $('.test-fixture').remove(); + }); + + it('tracks the number of sidebar sections', function() { + analytics = new GOVUK.StaticAnalytics({universalId: 'universal-id'}); + setupArguments = window.ga.calls.allArgs(); + expect(setupArguments[14]) + .toEqual(['set', 'dimension26', '2']); + }); + + it('tracks the total number of related links', function() { + analytics = new GOVUK.StaticAnalytics({universalId: 'universal-id'}); + setupArguments = window.ga.calls.allArgs(); + expect(setupArguments[15]) + .toEqual(['set', 'dimension27', '3']); + }); + }); + + describe('on a page with a taxon sidebar', function() { + beforeEach(function() { + $('body').append('\ +
\ + \ +
\ + '); + }); + + afterEach(function() { + $('.test-fixture').remove(); + }); + + it('tracks the number of sidebar sections', function() { + analytics = new GOVUK.StaticAnalytics({universalId: 'universal-id'}); + setupArguments = window.ga.calls.allArgs(); + expect(setupArguments[14]) + .toEqual(['set', 'dimension26', '2']); + }); + + it('tracks the total number of related links, including headers', function() { + analytics = new GOVUK.StaticAnalytics({universalId: 'universal-id'}); + setupArguments = window.ga.calls.allArgs(); + expect(setupArguments[15]) + .toEqual(['set', 'dimension27', '5']); + }); + }); + + describe('on a page with an accordion', function() { + beforeEach(function() { + $('body').append('\ +
\ +
\ +
\ +
\ +
\ +

Section 1

\ +
\ +
\ +
    \ +
  1. \ + \ + Link 1.1\ + \ +
  2. \ +
  3. \ + \ + Link 1.2\ + \ +
  4. \ +
\ +
\ +
\ +
\ +
\ +

Section 2

\ +
\ +
\ +
    \ +
  1. \ + \ + Link 2.1\ + \ +
  2. \ +
\ +
\ +
\ +
\ +
\ +
\ + '); + }); + + afterEach(function() { + $('.test-fixture').remove(); + }); + + it('tracks the number of accordion sections', function() { + analytics = new GOVUK.StaticAnalytics({universalId: 'universal-id'}); + setupArguments = window.ga.calls.allArgs(); + expect(setupArguments[14]) + .toEqual(['set', 'dimension26', '2']); + }); + + it('tracks the total number of accordion section links', function() { + analytics = new GOVUK.StaticAnalytics({universalId: 'universal-id'}); + setupArguments = window.ga.calls.allArgs(); + expect(setupArguments[15]) + .toEqual(['set', 'dimension27', '3']); + }); + }); + + describe('on a page with an grid', function() { + beforeEach(function() { + $('body').append('\ +
\ +
\ + \ +
\ +
\ +
\ +

Grid leaves

\ +
    \ +
  1. \ + \ + Leaf 1\ + \ +

  2. \ + \
  3. \ + \ + Leaf 2\ + \ +

  4. \ +
\ +
\ +
\ +
\ +
\ +
\ + '); + }); + + afterEach(function() { + $('.test-fixture').remove(); + }); + + it('does tracks sections equal to the number of grid links', function() { + analytics = new GOVUK.StaticAnalytics({universalId: 'universal-id'}); + setupArguments = window.ga.calls.allArgs(); + expect(setupArguments[14]) + .toEqual(['set', 'dimension26', '3']); + }); + + it('tracks the total number of grid links and leaf links', function() { + analytics = new GOVUK.StaticAnalytics({universalId: 'universal-id'}); + setupArguments = window.ga.calls.allArgs(); + expect(setupArguments[15]) + .toEqual(['set', 'dimension27', '5']); + }); + }); + }); + + describe('on a navigation leaf page', function() { + beforeEach(function() { + $('body').append('\ +
\ +
\ +
    \ +
  1. \ +

    \ + \ + Link 1\ + \ +

    \ +
  2. \ +
  3. \ +

    \ + \ + Link 2\ + \ +

    \ +
  4. \ +
\ +
\ +
\ + '); + }); + + afterEach(function() { + $('.test-fixture').remove(); + }); + + it('does not track any sections', function() { + analytics = new GOVUK.StaticAnalytics({universalId: 'universal-id'}); + setupArguments = window.ga.calls.allArgs(); + expect(setupArguments[14]) + .toEqual(['set', 'dimension26', '0']); + }); + + it('tracks the total number of leaf links', function() { + analytics = new GOVUK.StaticAnalytics({universalId: 'universal-id'}); + setupArguments = window.ga.calls.allArgs(); + expect(setupArguments[15]) + .toEqual(['set', 'dimension27', '2']); + }); + }); + function dimensionSetupArguments() { // Remove the default calls to the analytics object return window.ga.calls.allArgs().slice(expectedDefaultArgumentCount, -1); diff --git a/test/govuk_component/analytics_meta_tags_navigation_test.rb b/test/govuk_component/analytics_meta_tags_navigation_test.rb deleted file mode 100644 index fb1c42189..000000000 --- a/test/govuk_component/analytics_meta_tags_navigation_test.rb +++ /dev/null @@ -1,13 +0,0 @@ -require 'govuk_component_test_helper' - -class AnalyticsMetaTagsNavigationTestCase < ComponentTestCase - def component_name - "analytics_meta_tags_navigation" - end - - test "no error if no parameters passed in" do - assert_nothing_raised do - render_component({}) - end - end -end diff --git a/test/govuk_component/analytics_meta_tags_test.rb b/test/govuk_component/analytics_meta_tags_test.rb index 81be9bbe5..6b5da7aa2 100644 --- a/test/govuk_component/analytics_meta_tags_test.rb +++ b/test/govuk_component/analytics_meta_tags_test.rb @@ -19,7 +19,7 @@ def example_document_for(format, example_name) end test "no meta tags are rendered when there's no trackable data" do - assert_empty render_component(content_item: {}).strip + assert_empty render_component(content_item: {}) end test "renders format in a meta tag" do @@ -85,8 +85,8 @@ def example_document_for(format, example_name) end test "does not render publishing government or political status when political or government is missing" do - assert_empty render_component(content_item: { details: { government: { current: true, slug: 'government' } } }).strip - assert_empty render_component(content_item: { details: { political: true } }).strip + assert_empty render_component(content_item: { details: { government: { current: true, slug: 'government' } } }) + assert_empty render_component(content_item: { details: { political: true } }) end test "renders user journey stage when user journey supertype is included" do From 6cfb4f1131b57b631b780098c43b75921708e4aa Mon Sep 17 00:00:00 2001 From: Alec Gibson Date: Fri, 5 May 2017 16:07:23 +0100 Subject: [PATCH 2/2] =?UTF-8?q?Revert=20=E2=80=9CRevert=20"Pass=20custom?= =?UTF-8?q?=20dimensions=20directly=20to=20page=20tracking=E2=80=9D?= =?UTF-8?q?=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit reverts 428ec4273e8e6e180d1ad4ee696af9918550f798 --- .../javascripts/analytics/static-analytics.js | 308 ++++++++---------- .../analytics/static-analytics-spec.js | 168 +++++----- 2 files changed, 214 insertions(+), 262 deletions(-) diff --git a/app/assets/javascripts/analytics/static-analytics.js b/app/assets/javascripts/analytics/static-analytics.js index 56316b4a5..c46512c0d 100644 --- a/app/assets/javascripts/analytics/static-analytics.js +++ b/app/assets/javascripts/analytics/static-analytics.js @@ -1,24 +1,18 @@ -(function() { +(function () { "use strict"; window.GOVUK = window.GOVUK || {}; - var StaticAnalytics = function(config) { + var StaticAnalytics = function (config) { // Create universal tracker // https://github.com/alphagov/govuk_frontend_toolkit/blob/master/docs/analytics.md // https://github.com/alphagov/govuk_frontend_toolkit/blob/master/javascripts/govuk/analytics/analytics.js - var analytics = new GOVUK.Analytics(config); - this.analytics = analytics; - - setPixelDensityDimension(); - setHTTPStatusCodeDimension(); - setTLSVersionDimension(); - this.setDimensionsFromMetaTags(); - this.setAbTestDimensionsFromMetaTags(); + this.analytics = new GOVUK.Analytics(config); + this.callMethodRequestedByPreviousPage(); // Track initial pageview - analytics.trackPageview(); + this.trackPageview(); // Begin error and print tracking GOVUK.analyticsPlugins.error({filenameMustMatch: /gov\.uk/}); @@ -28,25 +22,9 @@ GOVUK.analyticsPlugins.downloadLinkTracker({ selector: 'a[href*="/government/uploads"], a[href*="assets.publishing.service.gov.uk"]' }); - - function setPixelDensityDimension() { - if (window.devicePixelRatio) { - analytics.setDimension(11, window.devicePixelRatio); - } - } - - function setTLSVersionDimension() { - var tls_version = GOVUK.cookie('TLSversion') || 'unknown'; - analytics.setDimension(16, tls_version); - } - - function setHTTPStatusCodeDimension() { - analytics.setDimension(15, window.httpStatusCode || 200); - } - }; - StaticAnalytics.prototype.callOnNextPage = function(method, params) { + StaticAnalytics.prototype.callOnNextPage = function (method, params) { params = params || []; if (!$.isArray(params)) { @@ -59,14 +37,15 @@ } }; - StaticAnalytics.prototype.callMethodRequestedByPreviousPage = function() { + StaticAnalytics.prototype.callMethodRequestedByPreviousPage = function () { if (GOVUK.cookie && GOVUK.cookie('analytics_next_page_call') !== null) { var params, method; try { params = JSON.parse(GOVUK.cookie('analytics_next_page_call')); method = params.shift(); - } catch(e) {} + } catch (e) { + } if (method && typeof this[method] === "function") { this[method].apply(this, params); @@ -77,80 +56,38 @@ } }; - StaticAnalytics.load = function() { + StaticAnalytics.load = function () { GOVUK.Analytics.load(); }; - StaticAnalytics.prototype.setDimensionsFromMetaTags = function() { - var $metas = $('meta[name^="govuk:"]'), - dimensions = {}; - - $metas.each(function() { - var $meta = $(this), - key = $meta.attr('name').split('govuk:')[1], - value = $meta.attr('content'); - - dimensions[key] = value; - }); - - // Set defaults first, so that we get a stable ordering in tests - this.setDimensionsThatHaveDefaultValues(dimensions); - this.setDimensionsThatDoNotHaveDefaultValues(dimensions); + // TODO: Remove this, and its corresponding call in collections + StaticAnalytics.prototype.setSectionDimension = function () { }; - StaticAnalytics.prototype.setDimensionsThatHaveDefaultValues = function(dimensions) { - this.setThemesDimension(dimensions['themes']); - this.setNavigationPageTypeDimension(dimensions['navigation-page-type']); - this.setUserJourneyStage(dimensions['user-journey-stage']); - this.setNavigationDocumentTypeDimension(dimensions['navigation-document-type']); - this.setContentIdDimension(dimensions['content-id']); - this.setTaxonSlugDimension(dimensions['taxon-slug']); - this.setTaxonIdDimension(dimensions['taxon-id']); - this.setTaxonSlugsDimension(dimensions['taxon-slugs']); - this.setTaxonIdsDimension(dimensions['taxon-ids']); - this.setTotalNumberOfSections(); - this.setTotalNumberOfSectionLinks(); + // TODO: We're setting this at a session level, because it's called in frontend's live-search.js to update + // the search count. We should make this consistent with the other dimensions and pass the dimension + // directly into the pageview arguments. + StaticAnalytics.prototype.setResultCountDimension = function (count) { + this.setDimension(5, count); }; - StaticAnalytics.prototype.setDimensionsThatDoNotHaveDefaultValues = function(dimensions) { - this.setSectionDimension(dimensions['section']); - this.setFormatDimension(dimensions['format']); - this.setResultCountDimension(dimensions['search-result-count']); - this.setPublishingGovernmentDimension(dimensions['publishing-government']); - this.setPoliticalStatusDimension(dimensions['political-status']); - this.setOrganisationsDimension(dimensions['analytics:organisations']); - this.setWorldLocationsDimension(dimensions['analytics:world-locations']); - this.setRenderingApplicationDimension(dimensions['rendering-application']); - this.setSchemaNameDimension(dimensions['schema-name']); + // TODO: We're setting this at a session level, because it's used by search through callOnNextPage. We should + // make this consistent with the other dimensions and pass the dimension directly into the pageview arguments. + StaticAnalytics.prototype.setSearchPositionDimension = function (position) { + this.setDimension(21, position); }; - StaticAnalytics.prototype.setAbTestDimensionsFromMetaTags = function() { - var $abMetas = $('meta[name^="govuk:ab-test"]'), - staticAnalytics = this, - // This is the block of dimensions assigned to A/B tests - minAbTestDimension = 40, - maxAbTestDimension = 49; - - $abMetas.each(function() { - var $meta = $(this), - dimension = parseInt($meta.data('analytics-dimension')), - testNameAndBucket = $meta.attr('content'); - - if (dimension >= minAbTestDimension && dimension <= maxAbTestDimension) { - staticAnalytics.setDimension(dimension, testNameAndBucket); - } - }); - } - - StaticAnalytics.prototype.trackPageview = function(path, title, options) { - this.analytics.trackPageview(path, title, options); + StaticAnalytics.prototype.trackPageview = function (path, title, options) { + var trackingOptions = this.customDimensions(); + $.extend(trackingOptions, options); + this.analytics.trackPageview(path, title, trackingOptions); }; - StaticAnalytics.prototype.trackEvent = function(category, action, options) { + StaticAnalytics.prototype.trackEvent = function (category, action, options) { this.analytics.trackEvent(category, action, options); }; - StaticAnalytics.prototype.setDimension = function(index, value, name, scope) { + StaticAnalytics.prototype.setDimension = function (index, value, name, scope) { if (typeof value === "undefined") { return; } @@ -158,112 +95,131 @@ this.analytics.setDimension(index, value, name, scope); }; - StaticAnalytics.prototype.trackShare = function(network) { + StaticAnalytics.prototype.trackShare = function (network) { this.analytics.trackShare(network); }; - StaticAnalytics.prototype.addLinkedTrackerDomain = function(trackerId, name, domain) { + StaticAnalytics.prototype.addLinkedTrackerDomain = function (trackerId, name, domain) { this.analytics.addLinkedTrackerDomain(trackerId, name, domain); }; - StaticAnalytics.prototype.setSectionDimension = function(section) { - this.setDimension(1, section); - }; - - StaticAnalytics.prototype.setFormatDimension = function(format) { - this.setDimension(2, format); - }; - - StaticAnalytics.prototype.setThemesDimension = function(themes) { - this.setDimension(3, themes || 'other'); - }; + StaticAnalytics.prototype.customDimensions = function () { + var dimensions = $.extend( + {}, + customDimensionsFromBrowser(), + customDimensionsFromMetaTags(), + customDimensionsFromDom(), + abTestCustomDimensions() + ); - StaticAnalytics.prototype.setContentIdDimension = function(contentId) { - this.setDimension(4, contentId || '00000000-0000-0000-0000-000000000000'); - }; - - StaticAnalytics.prototype.setResultCountDimension = function(count) { - this.setDimension(5, count); - }; - - StaticAnalytics.prototype.setPublishingGovernmentDimension = function(government) { - this.setDimension(6, government); - }; - - StaticAnalytics.prototype.setPoliticalStatusDimension = function(status) { - this.setDimension(7, status); - }; - - StaticAnalytics.prototype.setOrganisationsDimension = function(orgs) { - this.setDimension(9, orgs); - }; - - StaticAnalytics.prototype.setWorldLocationsDimension = function(locations) { - this.setDimension(10, locations); + return $.each(dimensions, function (key, value) { + dimensions[key] = String(value); + }); }; - StaticAnalytics.prototype.setRenderingApplicationDimension = function(app) { - this.setDimension(20, app); - }; + function customDimensionsFromBrowser() { + var customDimensions = { + dimension15: window.httpStatusCode || 200, + dimension16: GOVUK.cookie('TLSversion') || 'unknown' + }; - StaticAnalytics.prototype.setSearchPositionDimension = function(position) { - this.setDimension(21, position); - }; + if (window.devicePixelRatio) { + customDimensions.dimension11 = window.devicePixelRatio; + } - StaticAnalytics.prototype.setSchemaNameDimension = function(position) { - this.setDimension(17, position); - }; + return customDimensions; + } - StaticAnalytics.prototype.setTotalNumberOfSections = function() { - var sidebarSections = $('[data-track-count="sidebarRelatedItemSection"]').length; - var sidebarTaxons = $('[data-track-count="sidebarTaxonSection"]').length; - var accordionSubsections = $('[data-track-count="accordionSection"]').length; - var gridSections = $('a[data-track-category="navGridLinkClicked"]').length; - var totalNumberOfSections = sidebarSections || sidebarTaxons || accordionSubsections || gridSections; - this.setDimension(26, totalNumberOfSections); - }; + function customDimensionsFromMetaTags() { + var dimensionMappings = { + 'section': {dimension: 1}, + 'format': {dimension: 2}, + 'themes': {dimension: 3, defaultValue: 'other'}, + 'content-id': {dimension: 4, defaultValue: '00000000-0000-0000-0000-000000000000'}, + 'search-result-count': {dimension: 5}, + 'publishing-government': {dimension: 6}, + 'political-status': {dimension: 7}, + 'analytics:organisations': {dimension: 9}, + 'analytics:world-locations': {dimension: 10}, + 'schema-name': {dimension: 17}, + 'rendering-application': {dimension: 20}, + 'navigation-page-type': {dimension: 32, defaultValue: 'none'}, + 'user-journey-stage': {dimension: 33, defaultValue: 'thing'}, + 'navigation-document-type': {dimension: 34, defaultValue: 'other'}, + 'taxon-slug': {dimension: 56, defaultValue: 'other'}, + 'taxon-id': {dimension: 57, defaultValue: 'other'}, + 'taxon-slugs': {dimension: 58, defaultValue: 'other'}, + 'taxon-ids': {dimension: 59, defaultValue: 'other'} + }; + + var $metas = $('meta[name^="govuk:"]'); + var customDimensions = {}; + var tags = {}; + + $metas.each(function () { + var $meta = $(this); + var key = $meta.attr('name').split('govuk:')[1]; + + var dimension = dimensionMappings[key]; + if (dimension) { + tags[key] = $meta.attr('content'); + } + }); - StaticAnalytics.prototype.setTotalNumberOfSectionLinks = function() { - var relatedLinks = $('a[data-track-category="relatedLinkClicked"]').length; - var accordionLinks = $('a[data-track-category="navAccordionLinkClicked"]').length; - // Grid links are counted both as "sections" (see dimension 26), and as part of the total link count - var gridLinks = $('a[data-track-category="navGridLinkClicked"]').length - + $('a[data-track-category="navGridLeafLinkClicked"]').length; - var leafLinks = $('a[data-track-category="navLeafLinkClicked"]').length; - var totalNumberOfSectionLinks = relatedLinks || accordionLinks || gridLinks || leafLinks; - this.setDimension(27, totalNumberOfSectionLinks); - }; + $.each(dimensionMappings, function (key, dimension) { + var value = tags[key] || dimension.defaultValue; + if (typeof value !== 'undefined') { + customDimensions['dimension' + dimension.dimension] = value; + } + }); - StaticAnalytics.prototype.setNavigationPageTypeDimension = function(pageType) { - this.setDimension(32, pageType || 'none'); - }; + return customDimensions; + } - StaticAnalytics.prototype.setUserJourneyStage = function(journeyStage) { - // Track the stage of a user's journey through GOV.UK. If the page does not - // set a page type in a meta tag, default to "thing", which identifes a page - // as containing content rather than some kind of navigation. - this.setDimension(33, journeyStage || "thing"); - }; + function customDimensionsFromDom() { + return { + dimension26: totalNumberOfSections(), + dimension27: totalNumberOfSectionLinks() + }; + + function totalNumberOfSections() { + var sidebarSections = $('[data-track-count="sidebarRelatedItemSection"]').length; + var sidebarTaxons = $('[data-track-count="sidebarTaxonSection"]').length; + var accordionSubsections = $('[data-track-count="accordionSection"]').length; + var gridSections = $('a[data-track-category="navGridLinkClicked"]').length; + return sidebarSections || sidebarTaxons || accordionSubsections || gridSections; + } - StaticAnalytics.prototype.setNavigationDocumentTypeDimension = function(documentType) { - this.setDimension(34, documentType || "other"); - }; + function totalNumberOfSectionLinks() { + var relatedLinks = $('a[data-track-category="relatedLinkClicked"]').length; + var accordionLinks = $('a[data-track-category="navAccordionLinkClicked"]').length; + // Grid links are counted both as "sections" (see dimension 26), and as part of the total link count + var gridLinks = $('a[data-track-category="navGridLinkClicked"]').length + + $('a[data-track-category="navGridLeafLinkClicked"]').length; + var leafLinks = $('a[data-track-category="navLeafLinkClicked"]').length; + return relatedLinks || accordionLinks || gridLinks || leafLinks; + } + } - StaticAnalytics.prototype.setTaxonSlugDimension = function(taxonSlug) { - this.setDimension(56, taxonSlug || "other"); - }; + function abTestCustomDimensions() { + // This is the block of dimensions assigned to A/B tests + var minAbTestDimension = 40; + var maxAbTestDimension = 49; + var $abMetas = $('meta[name^="govuk:ab-test"]'); + var customDimensions = {}; - StaticAnalytics.prototype.setTaxonIdDimension = function(taxonId) { - this.setDimension(57, taxonId || "other"); - }; + $abMetas.each(function () { + var $meta = $(this); + var dimension = parseInt($meta.data('analytics-dimension')); + var testNameAndBucket = $meta.attr('content'); - StaticAnalytics.prototype.setTaxonSlugsDimension = function(taxonSlugs) { - this.setDimension(58, taxonSlugs || "other"); - }; + if (dimension >= minAbTestDimension && dimension <= maxAbTestDimension) { + customDimensions['dimension' + dimension] = testNameAndBucket; + } + }); - StaticAnalytics.prototype.setTaxonIdsDimension = function (taxonIds) { - this.setDimension(59, taxonIds || "other"); - }; + return customDimensions; + } GOVUK.StaticAnalytics = StaticAnalytics; })(); diff --git a/spec/javascripts/analytics/static-analytics-spec.js b/spec/javascripts/analytics/static-analytics-spec.js index 078f7cf23..dd79c6fbb 100644 --- a/spec/javascripts/analytics/static-analytics-spec.js +++ b/spec/javascripts/analytics/static-analytics-spec.js @@ -14,12 +14,14 @@ describe("GOVUK.StaticAnalytics", function() { describe('when created', function() { // The number of setup arguments which are set before the dimensions - const expectedDefaultArgumentCount = 17; + const numberOfDimensionsWithDefaultValues = 14; var universalSetupArguments; + var pageViewObject; beforeEach(function() { universalSetupArguments = window.ga.calls.allArgs(); + pageViewObject = universalSetupArguments[3][2]; }); it('configures a universal tracker', function() { @@ -27,15 +29,16 @@ describe("GOVUK.StaticAnalytics", function() { }); it('sets the device pixel ratio', function() { - expect(window.ga).toHaveBeenCalledWith('set', 'dimension11', '1'); + expect(Object.keys(pageViewObject)).toContain('dimension11'); }); it('sets the HTTP status code', function() { - expect(window.ga).toHaveBeenCalledWith('set', 'dimension15', '200'); + expect(Object.keys(pageViewObject)).toContain('dimension15'); }); it('tracks a pageview in universal', function() { - expect(window.ga).toHaveBeenCalledWith('send', 'pageview'); + expect(universalSetupArguments[3][0]).toEqual('send'); + expect(universalSetupArguments[3][1]).toEqual('pageview'); }); it('begins print tracking', function() { @@ -68,23 +71,26 @@ describe("GOVUK.StaticAnalytics", function() { '); analytics = new GOVUK.StaticAnalytics({universalId: 'universal-id'}); - - expect(window.ga).toHaveBeenCalledWith('set', 'dimension1', 'section'); - expect(window.ga).toHaveBeenCalledWith('set', 'dimension2', 'format'); - expect(window.ga).toHaveBeenCalledWith('set', 'dimension5', '1000'); - expect(window.ga).toHaveBeenCalledWith('set', 'dimension6', '2005-to-2010-labour-government'); - expect(window.ga).toHaveBeenCalledWith('set', 'dimension7', 'historic'); - expect(window.ga).toHaveBeenCalledWith('set', 'dimension9', ''); - expect(window.ga).toHaveBeenCalledWith('set', 'dimension10', ''); - expect(window.ga).toHaveBeenCalledWith('set', 'dimension17', 'schema-name'); + pageViewObject = getPageViewObject(); + + expect(pageViewObject.dimension1).toEqual('section'); + expect(pageViewObject.dimension2).toEqual('format'); + expect(pageViewObject.dimension5).toEqual('1000'); + expect(pageViewObject.dimension6).toEqual('2005-to-2010-labour-government'); + expect(pageViewObject.dimension7).toEqual('historic'); + expect(pageViewObject.dimension9).toEqual(''); + expect(pageViewObject.dimension10).toEqual(''); + expect(pageViewObject.dimension17).toEqual('schema-name'); }); it('ignores meta tags not set', function() { $('head').append(''); analytics = new GOVUK.StaticAnalytics({universalId: 'universal-id'}); + pageViewObject = getPageViewObject(); - expect(window.ga).toHaveBeenCalledWith('set', 'dimension1', 'section'); + expect(Object.keys(pageViewObject).length).toEqual(1 + numberOfDimensionsWithDefaultValues); + expect(pageViewObject.dimension1).toEqual('section'); }); it('sets A/B meta tags as dimensions', function() { @@ -94,9 +100,10 @@ describe("GOVUK.StaticAnalytics", function() { '); analytics = new GOVUK.StaticAnalytics({universalId: 'universal-id'}); + pageViewObject = getPageViewObject(); - expect(window.ga).toHaveBeenCalledWith('set', 'dimension42', 'name-of-test:name-of-ab-bucket'); - expect(window.ga).toHaveBeenCalledWith('set', 'dimension48', 'name-of-other-test:name-of-other-ab-bucket'); + expect(pageViewObject.dimension42).toEqual('name-of-test:name-of-ab-bucket'); + expect(pageViewObject.dimension48).toEqual('name-of-other-test:name-of-other-ab-bucket'); }); it('ignores dimensions outside of the A/B test range', function () { @@ -108,11 +115,11 @@ describe("GOVUK.StaticAnalytics", function() { '); analytics = new GOVUK.StaticAnalytics({universalId: 'universal-id'}); + pageViewObject = getPageViewObject(); - expect(window.ga).toHaveBeenCalledWith('set', 'dimension40', 'name-of-valid-test:some-bucket'); - expect(window.ga).toHaveBeenCalledWith('set', 'dimension49', 'name-of-other-valid-test:some-bucket'); - expect(window.ga).not.toHaveBeenCalledWith('set', 'dimension39', jasmine.any(Object)); - expect(window.ga).not.toHaveBeenCalledWith('set', 'dimension50', jasmine.any(Object)); + expect(Object.keys(pageViewObject).length).toEqual(2 + numberOfDimensionsWithDefaultValues); + expect(pageViewObject.dimension40).toEqual('name-of-valid-test:some-bucket'); + expect(pageViewObject.dimension49).toEqual('name-of-other-valid-test:some-bucket'); }); it('ignores A/B meta tags with invalid dimensions', function () { @@ -122,65 +129,56 @@ describe("GOVUK.StaticAnalytics", function() { '); analytics = new GOVUK.StaticAnalytics({universalId: 'universal-id'}); - setupArguments = dimensionSetupArguments(); + pageViewObject = getPageViewObject(); - expect(setupArguments.length).toEqual(0); + expect(Object.keys(pageViewObject).length).toEqual(numberOfDimensionsWithDefaultValues); }); [ { name: 'themes', number: 3, - defaultValue: 'other', - setupArgumentsIndex: 5 + defaultValue: 'other' }, { name: 'navigation-page-type', number: 32, - defaultValue: 'none', - setupArgumentsIndex: 6 + defaultValue: 'none' }, { name: 'user-journey-stage', number: 33, - defaultValue: 'thing', - setupArgumentsIndex: 7 + defaultValue: 'thing' }, { name: 'navigation-document-type', number: 34, - defaultValue: 'other', - setupArgumentsIndex: 8 + defaultValue: 'other' }, { name: 'content-id', number: 4, - defaultValue: '00000000-0000-0000-0000-000000000000', - setupArgumentsIndex: 9 + defaultValue: '00000000-0000-0000-0000-000000000000' }, { name: 'taxon-slug', number: 56, - defaultValue: 'other', - setupArgumentsIndex: 10 + defaultValue: 'other' }, { name: 'taxon-id', number: 57, - defaultValue: 'other', - setupArgumentsIndex: 11 + defaultValue: 'other' }, { name: 'taxon-slugs', number: 58, - defaultValue: 'other', - setupArgumentsIndex: 12 + defaultValue: 'other' }, { name: 'taxon-ids', number: 59, - defaultValue: 'other', - setupArgumentsIndex: 13 + defaultValue: 'other' } ].forEach(function (dimension) { it('sets the ' + dimension.name + ' dimension from a meta tag if present', function () { @@ -189,16 +187,16 @@ describe("GOVUK.StaticAnalytics", function() { '); analytics = new GOVUK.StaticAnalytics({universalId: 'universal-id'}); + pageViewObject = getPageViewObject(); - expect(window.ga) - .toHaveBeenCalledWith('set', 'dimension' + dimension.number, 'some-' + dimension.name + '-value'); + expect(pageViewObject['dimension' + dimension.number]).toEqual('some-' + dimension.name + '-value'); }); it('sets the default dimension if no ' + dimension.name + ' meta tag is present', function () { analytics = new GOVUK.StaticAnalytics({universalId: 'universal-id'}); + pageViewObject = getPageViewObject(); - expect(window.ga) - .toHaveBeenCalledWith('set', 'dimension' + dimension.number, dimension.defaultValue); + expect(pageViewObject['dimension' + dimension.number]).toEqual(dimension.defaultValue); }); }); @@ -244,16 +242,14 @@ describe("GOVUK.StaticAnalytics", function() { it('tracks the number of sidebar sections', function() { analytics = new GOVUK.StaticAnalytics({universalId: 'universal-id'}); - setupArguments = window.ga.calls.allArgs(); - expect(setupArguments[14]) - .toEqual(['set', 'dimension26', '2']); + pageViewObject = getPageViewObject(); + expect(pageViewObject.dimension26).toEqual('2'); }); it('tracks the total number of related links', function() { analytics = new GOVUK.StaticAnalytics({universalId: 'universal-id'}); - setupArguments = window.ga.calls.allArgs(); - expect(setupArguments[15]) - .toEqual(['set', 'dimension27', '3']); + pageViewObject = getPageViewObject(); + expect(pageViewObject.dimension27).toEqual('3'); }); }); @@ -302,16 +298,14 @@ describe("GOVUK.StaticAnalytics", function() { it('tracks the number of sidebar sections', function() { analytics = new GOVUK.StaticAnalytics({universalId: 'universal-id'}); - setupArguments = window.ga.calls.allArgs(); - expect(setupArguments[14]) - .toEqual(['set', 'dimension26', '2']); + pageViewObject = getPageViewObject(); + expect(pageViewObject.dimension26).toEqual('2'); }); it('tracks the total number of related links, including headers', function() { analytics = new GOVUK.StaticAnalytics({universalId: 'universal-id'}); - setupArguments = window.ga.calls.allArgs(); - expect(setupArguments[15]) - .toEqual(['set', 'dimension27', '5']); + pageViewObject = getPageViewObject(); + expect(pageViewObject.dimension27).toEqual('5'); }); }); @@ -366,16 +360,14 @@ describe("GOVUK.StaticAnalytics", function() { it('tracks the number of accordion sections', function() { analytics = new GOVUK.StaticAnalytics({universalId: 'universal-id'}); - setupArguments = window.ga.calls.allArgs(); - expect(setupArguments[14]) - .toEqual(['set', 'dimension26', '2']); + pageViewObject = getPageViewObject(); + expect(pageViewObject.dimension26).toEqual('2'); }); it('tracks the total number of accordion section links', function() { analytics = new GOVUK.StaticAnalytics({universalId: 'universal-id'}); - setupArguments = window.ga.calls.allArgs(); - expect(setupArguments[15]) - .toEqual(['set', 'dimension27', '3']); + pageViewObject = getPageViewObject(); + expect(pageViewObject.dimension27).toEqual('3'); }); }); @@ -433,16 +425,14 @@ describe("GOVUK.StaticAnalytics", function() { it('does tracks sections equal to the number of grid links', function() { analytics = new GOVUK.StaticAnalytics({universalId: 'universal-id'}); - setupArguments = window.ga.calls.allArgs(); - expect(setupArguments[14]) - .toEqual(['set', 'dimension26', '3']); + pageViewObject = getPageViewObject(); + expect(pageViewObject.dimension26).toEqual('3'); }); it('tracks the total number of grid links and leaf links', function() { analytics = new GOVUK.StaticAnalytics({universalId: 'universal-id'}); - setupArguments = window.ga.calls.allArgs(); - expect(setupArguments[15]) - .toEqual(['set', 'dimension27', '5']); + pageViewObject = getPageViewObject(); + expect(pageViewObject.dimension27).toEqual('5'); }); }); }); @@ -479,59 +469,61 @@ describe("GOVUK.StaticAnalytics", function() { it('does not track any sections', function() { analytics = new GOVUK.StaticAnalytics({universalId: 'universal-id'}); - setupArguments = window.ga.calls.allArgs(); - expect(setupArguments[14]) - .toEqual(['set', 'dimension26', '0']); + pageViewObject = getPageViewObject(); + expect(pageViewObject.dimension26).toEqual('0'); }); it('tracks the total number of leaf links', function() { analytics = new GOVUK.StaticAnalytics({universalId: 'universal-id'}); - setupArguments = window.ga.calls.allArgs(); - expect(setupArguments[15]) - .toEqual(['set', 'dimension27', '2']); + pageViewObject = getPageViewObject(); + expect(pageViewObject.dimension27).toEqual('2'); }); }); - - function dimensionSetupArguments() { - // Remove the default calls to the analytics object - return window.ga.calls.allArgs().slice(expectedDefaultArgumentCount, -1); - } }); }); describe('when there is a TLSversion cookie', function() { + var pageViewObject; + beforeEach(function() { GOVUK.cookie('TLSversion', '2'); window.ga.calls.reset(); analytics = new GOVUK.StaticAnalytics({universalId: 'universal-id'}); + pageViewObject = getPageViewObject(); }); + it("sets the cookie value as the value of the tls version custom dimension", function() { - expect(window.ga).toHaveBeenCalledWith('set', 'dimension16', '2'); + expect(pageViewObject.dimension16).toEqual('2'); }); }); describe('when there is no TLSversion cookie', function() { + var pageViewObject; + beforeEach(function() { GOVUK.cookie('TLSversion', null); window.ga.calls.reset(); analytics = new GOVUK.StaticAnalytics({universalId: 'universal-id'}); + pageViewObject = getPageViewObject(); }); + it("sets unknown as the value of the tls version custom dimension", function() { - expect(window.ga).toHaveBeenCalledWith('set', 'dimension16', 'unknown'); + expect(pageViewObject.dimension16).toEqual('unknown'); }); }); - describe('when tracking pageviews, events and custom dimensions', function() { + describe('when tracking pageviews and events', function() { it('tracks them in universal', function() { analytics.trackPageview('/path', 'Title'); - expect(window.ga.calls.mostRecent().args).toEqual(['send', 'pageview', {page: '/path', title: 'Title'}]); + trackingArguments = window.ga.calls.mostRecent().args; + expect(trackingArguments[0]).toEqual('send'); + expect(trackingArguments[1]).toEqual('pageview'); + expect(trackingArguments[2].page).toEqual('/path'); + expect(trackingArguments[2].title).toEqual('Title'); analytics.trackEvent('category', 'action'); expect(window.ga.calls.mostRecent().args).toEqual(['send', {hitType: 'event', eventCategory: 'category', eventAction: 'action'}]); - - analytics.setSectionDimension('value'); - expect(window.ga.calls.mostRecent().args).toEqual(['set', 'dimension1', 'value']); }); }); @@ -595,4 +587,8 @@ describe("GOVUK.StaticAnalytics", function() { expect(analytics.trackPageview).toHaveBeenCalledWith('/path', 'Title'); }); }); + + function getPageViewObject() { + return window.ga.calls.allArgs()[3][2]; + } });