Skip to content

Commit

Permalink
Merge pull request #211 from alphagov/ga-events
Browse files Browse the repository at this point in the history
Track JavaScript interactions in Google Analytics
  • Loading branch information
jamiecobbett committed Mar 10, 2014
2 parents ae34f2d + 06a4845 commit d7f2c00
Show file tree
Hide file tree
Showing 16 changed files with 187 additions and 4 deletions.
34 changes: 34 additions & 0 deletions app/assets/javascripts/govuk.js
Expand Up @@ -61,4 +61,38 @@
GOVUK.start();
}

// Google Analytics event tracking
// https://developers.google.com/analytics/devguides/collection/gajs/eventTrackerGuide
// Label and value are optional
GOVUK.track = function(action, label, value) {

// Default category to the page an event occurs on
var category = root.location.pathname,
event;

// _gaq is the Google Analytics tracking object we
// push events to, GA asynchronously sends them on
root._gaq = root._gaq || [];

event = ["_trackEvent", category, action];

// Label is optional
if (typeof label === "string") {
event.push(label);
}

// Value is optional, but when used must be an
// integer, otherwise the event will be invalid
// and not logged
if (value) {
value = parseInt(value, 10);
if (typeof value === "number" && !isNaN(value)) {
event.push(value);
}
}

// Useful for debugging: console.log(event);
_gaq.push(event);
}

})(jQuery, window);
15 changes: 15 additions & 0 deletions app/assets/javascripts/modules/auto_track_event.js
@@ -0,0 +1,15 @@
(function(Modules) {
"use strict"

Modules.AutoTrackEvent = function() {
var that = this;
that.start = function(element) {
var action = element.data('track-action'),
label = element.data('track-label'),
value = element.data('track-value');

GOVUK.track(action, label, value);
}
};

})(window.GOVUK.Modules);
3 changes: 3 additions & 0 deletions app/controllers/mappings_controller.rb
Expand Up @@ -27,6 +27,7 @@ def create_multiple

flash[:success] = bulk_add.success_message
flash[:saved_mapping_ids] = bulk_add.modified_mappings.map {|m| m.id}
flash[:saved_operation] = bulk_add.operation_description
redirect_to site_mappings_path(@site)
end

Expand Down Expand Up @@ -82,6 +83,7 @@ def update
if @mapping.save
flash[:success] = 'Mapping saved'
flash[:saved_mapping_ids] = [@mapping.id]
flash[:saved_operation] = "update-single"

if params[:return_path] && params[:return_path].start_with?('/')
redirect_to params[:return_path]
Expand Down Expand Up @@ -123,6 +125,7 @@ def update_multiple
else
flash[:success] = bulk_edit.success_message
flash[:saved_mapping_ids] = bulk_edit.mappings.map {|m| m.id}
flash[:saved_operation] = bulk_edit.operation_description
redirect_to bulk_edit.return_path
end
end
Expand Down
8 changes: 7 additions & 1 deletion app/models/view/mappings/bulk_adder.rb
Expand Up @@ -8,6 +8,12 @@ def http_status
params[:http_status] if Mapping::TYPES.keys.include?(params[:http_status])
end

def operation_description
type = Mapping::TYPES[http_status]
update_type = update_existing? ? 'overwrite' : 'ignore'
"bulk-add-#{type}-#{update_type}-existing"
end

# Take either a multiline string of paths, one per line (as submitted by
# the new_multiple form) or an array of path strings (as submitted by the
# hidden fields on the confirmation page) and return the paths in an
Expand Down Expand Up @@ -114,7 +120,7 @@ def created_count
def updated_count
outcomes.count(:updated)
end

def modified_mappings
@modified_mappings
end
Expand Down
4 changes: 4 additions & 0 deletions app/models/view/mappings/bulk_editor.rb
Expand Up @@ -12,6 +12,10 @@ def operation
params[:operation]
end

def operation_description
"bulk-edit-#{Mapping::TYPES[operation]}"
end

def return_path
@return_path ||=
# Make sure that this looks like a path and not a full URL (which
Expand Down
4 changes: 4 additions & 0 deletions app/models/view/mappings/bulk_tagger.rb
Expand Up @@ -16,6 +16,10 @@ def would_fail?
false
end

def operation_description
"bulk-edit-tag"
end

def tag_list
prettified_tag_list || common_tags.join(glue)
end
Expand Down
1 change: 1 addition & 0 deletions app/views/layouts/application.html.erb
Expand Up @@ -72,6 +72,7 @@
<%= render partial: 'mappings/saved_mappings_modal',
locals: {
site: @site,
operation: flash[:saved_operation],
message: flash[:success],
mappings: mappings_from_ids(flash[:saved_mapping_ids])
}
Expand Down
7 changes: 6 additions & 1 deletion app/views/mappings/_saved_mappings_modal.html.erb
@@ -1,5 +1,10 @@
<div class="modal modal-saved-mappings" tabindex="-1" role="dialog" aria-hidden="true" data-backdrop="static" data-module="auto-show-modal">
<div class="modal-dialog modal-lg">
<div class="modal-dialog modal-lg"
data-module="auto-track-event"
data-track-action="saved-mappings"
data-track-label="<%= operation %>"
data-track-value="<%= mappings.size %>"
>
<div class="modal-content">
<header class="modal-header">
<h3 class="modal-title alert alert-success">
Expand Down
11 changes: 10 additions & 1 deletion app/views/mappings/edit_multiple_modal.html.erb
@@ -1,4 +1,13 @@
<div id="bulk-editing-modal" class="modal" tabindex="-1" role="dialog" aria-hidden="true">
<div id="bulk-editing-modal"
class="modal"
tabindex="-1"
role="dialog"
aria-hidden="true"
data-module="auto-track-event"
data-track-action="select-mappings"
data-track-label="<%= @bulk_edit.operation_description %>"
data-track-value="<%= @bulk_edit.mappings.size %>"
>
<%= form_tag update_multiple_site_mappings_path(@site), class: 'modal-dialog', role: 'form' do %>
<div class="modal-content">
<header class="modal-header">
Expand Down
1 change: 1 addition & 0 deletions features/mapping_create_multiple.feature
Expand Up @@ -26,6 +26,7 @@ Feature: Create mappings
Then I should see "1 mapping created. 0 mappings updated." in a modal window
And I should see a table with 1 saved mapping in the modal
And I should see "/needs/canonicalizing" in a modal window
And an analytics event with "bulk-add-redirect-ignore-existing" has fired

Scenario: I don't have access
Given I have logged in as a member of another organisation
Expand Down
4 changes: 4 additions & 0 deletions features/mapping_edit_multiple.feature
Expand Up @@ -73,13 +73,15 @@ Feature: Editing multiple mappings for a site
Then I should see an open modal window
And I should see a form that contains my selection within the modal
And I should see "Redirect mappings" in the modal window
And an analytics event with "bulk-edit-redirect" has fired
When I enter a new URL to redirect to
And I save my changes
Then I should see an open modal window
And I should see "Mappings updated" in the modal window
And I should see a table with 2 saved mappings in the modal
And I should see "/a" in the modal window
And I should see "/about/branding" in the modal window
And an analytics event with "bulk-edit-redirect" has fired

@javascript
Scenario: Selecting multiple mappings to archive with javascript
Expand All @@ -88,12 +90,14 @@ Feature: Editing multiple mappings for a site
Then I should see an open modal window
And I should see a form that contains my selection within the modal
And I should see "Archive mappings" in the modal window
And an analytics event with "bulk-edit-archive" has fired
When I save my changes
Then I should see an open modal window
And I should see "Mappings updated" in the modal window
And I should see a table with 2 saved mappings in the modal
And I should see "/a" in the modal window
And I should see "/about/branding" in the modal window
And an analytics event with "bulk-edit-archive" has fired

@javascript
Scenario: Truncating a table of mappings in a modal
Expand Down
1 change: 1 addition & 0 deletions features/mapping_tag.feature
Expand Up @@ -61,6 +61,7 @@ Scenario: Bulk adding tags to existing mappings (JS)
And mapping 3 should have the tags "fo, fum"
And I should see an open modal window
And I should see a table with 2 saved mappings in the modal
And an analytics event with "bulk-edit-tag" has fired

@javascript
Scenario: Autocompleting popular tags
Expand Down
7 changes: 7 additions & 0 deletions features/step_definitions/page_assertion_steps.rb
Expand Up @@ -51,6 +51,13 @@
expect(page).to have_selector('span.page.current', text: page_number)
end

# Google analytics tracking

Then(/^an analytics event with "([^"]*)" has fired$/) do |contents|
gaq = page.evaluate_script('window._gaq').flatten
expect(gaq.include?(contents)).to be_true
end

# HTML structure

Then(/^I should see the header "([^"]*)"$/) do |header_text|
Expand Down
22 changes: 22 additions & 0 deletions spec/javascripts/spec/auto_track_event.spec.js
@@ -0,0 +1,22 @@
describe('An auto event tracker', function() {
"use strict"

var root = window,
tracker,
element;

beforeEach(function() {
element = $('\
<div data-track-action="action" data-track-label="label" data-track-value="10">\
</div>\
');

tracker = new GOVUK.Modules.AutoTrackEvent();
});

it('tracks events on start', function() {
spyOn(root.GOVUK, 'track');
tracker.start(element);
expect(GOVUK.track).toHaveBeenCalledWith('action', 'label', 10)
});
});
38 changes: 38 additions & 0 deletions spec/javascripts/spec/govuk.spec.js
Expand Up @@ -84,4 +84,42 @@ describe('A GOVUK app', function() {
});
});

describe('when events are tracked', function() {

beforeEach(function() {
window._gaq = [];
});

it('uses the current path as the category', function() {
GOVUK.track('action', 'label');
expect(window._gaq[0][1]).toEqual('/');
});

it('sends them to Google Analytics', function() {
GOVUK.track('action', 'label');
expect(window._gaq).toEqual([['_trackEvent', '/', 'action', 'label']]);
});

it('creates a _gaq object when one isn\'t already present', function() {
delete window._gaq;
GOVUK.track('action');
expect(window._gaq).toEqual([['_trackEvent', '/', 'action']]);
});

it('label is optional', function() {
GOVUK.track('action');
expect(window._gaq).toEqual([['_trackEvent', '/', 'action']]);
});

it('only sends values if they are parseable as numbers', function() {
GOVUK.track('action', 'label', '10');
expect(window._gaq[0]).toEqual(['_trackEvent', '/', 'action', 'label', 10]);

GOVUK.track('action', 'label', 10);
expect(window._gaq[1]).toEqual(['_trackEvent', '/', 'action', 'label', 10]);

GOVUK.track('action', 'label', 'not a number');
expect(window._gaq[2]).toEqual(['_trackEvent', '/', 'action', 'label']);
});
});
});
31 changes: 30 additions & 1 deletion spec/models/view/bulk_adder_spec.rb
Expand Up @@ -94,7 +94,7 @@
let(:paths_input) { "http://#{site.default_host.hostname}/about\nhttp://#{site.default_host.hostname}/another" }
it { should be_true }
end

describe 'invalid hosts should be false' do
let(:paths_input) { "http//googlecomnopunctuation" }
it { should be_false }
Expand Down Expand Up @@ -382,4 +382,33 @@
end
end
end

describe '#operation_description' do
subject { bulk_adder.operation_description }
context 'bulk adding archives' do
let(:bulk_adder) { View::Mappings::BulkAdder.new(site, {http_status: '410'}) }
it { should eql('bulk-add-archive-ignore-existing') }
end

context 'bulk adding redirects' do
let(:bulk_adder) { View::Mappings::BulkAdder.new(site, {http_status: '301'}) }
it { should eql('bulk-add-redirect-ignore-existing') }
end

context 'bulk adding archives with overwrite' do
let(:bulk_adder) { View::Mappings::BulkAdder.new(site, {
http_status: '410',
update_existing: 'true'
}) }
it { should eql('bulk-add-archive-overwrite-existing') }
end

context 'bulk adding redirects with overwrite' do
let(:bulk_adder) { View::Mappings::BulkAdder.new(site, {
http_status: '301',
update_existing: 'true'
}) }
it { should eql('bulk-add-redirect-overwrite-existing') }
end
end
end

0 comments on commit d7f2c00

Please sign in to comment.