Skip to content

Commit

Permalink
Add single consent library
Browse files Browse the repository at this point in the history
- adds code to include and manage our use of the single consent API code
- init is called by the cookie banner code on every page, which determines which API endpoint to use (staging or prod) and creates the GovSingleConsent object
- useConsentApi is called across the code to check whether or not to use the consent API, this will be set in static
- the default callback for the consent object checks the returned response and if the user has consented, triggers the cookie-consent event, which will initialise analytics and hide the cookie banner
- setPreferences allows user consent to be set through the API
- use load analytics environment list for consent API URL
  • Loading branch information
andysellick committed Mar 8, 2024
1 parent 88c9f34 commit fe5418e
Show file tree
Hide file tree
Showing 3 changed files with 296 additions and 5 deletions.
@@ -0,0 +1,59 @@
/* global GovSingleConsent */
// = require govuk-single-consent/dist/singleconsent.iife.js

(function (root) {
'use strict'
window.GOVUK = window.GOVUK || {}

window.GOVUK.singleConsent = {
init: function (callback) {
if (!window.GOVUK.useSingleConsentApi) {
return
}
callback = callback || this.apiCallback
// determine where we are and set the consent api URL accordingly
if (!this.url) {
this.url = 'staging'
var environment = window.GOVUK.loadAnalytics.getEnvironment(window.GOVUK.analyticsGa4.core.trackFunctions.getHostname())
if (environment) {
this.url = environment.consentApiUrl
}
}
// create the consent API object
this.consentApiObj = new GovSingleConsent(callback, this.url)
},

apiCallback: function (consents, consentsPreferencesSet, error) {
if (error) {
console.error('Single consent error: ', error, window.location)
return
}
if (consentsPreferencesSet) {
if (consents && consents.usage) {
window.GOVUK.triggerEvent(window, 'cookie-consent')
}
} else {
window.GOVUK.triggerEvent(window, 'show-cookie-banner')
}
},

setPreferences: function (type, options) {
if (window.GOVUK.useSingleConsentApi) {
try {
switch (type) {
case 'accept':
this.consentApiObj.setConsents(GovSingleConsent.ACCEPT_ALL)
break
case 'reject':
this.consentApiObj.setConsents(GovSingleConsent.REJECT_ALL)
break
default:
this.consentApiObj.setConsents(options)
}
} catch (e) {
console.error('Single consent ' + type + ' error: ', e, window.location)
}
}
}
}
}(window))
Expand Up @@ -18,7 +18,8 @@ window.GOVUK.loadAnalytics = {
auth: 'bRiZ-jiEHtw6hHpGd6dF9w',
preview: 'env-3',
gaProperty: 'UA-UNSET',
gaPropertyCrossDomain: 'UA-UNSET'
gaPropertyCrossDomain: 'UA-UNSET',
consentApiUrl: 'staging'
},
{
name: 'production',
Expand All @@ -30,7 +31,8 @@ window.GOVUK.loadAnalytics = {
initialiseGA4: true,
id: 'GTM-MG7HG5W',
gaProperty: 'UA-26179049-1',
gaPropertyCrossDomain: 'UA-145652997-1'
gaPropertyCrossDomain: 'UA-145652997-1',
consentApiUrl: 'production'
},
{
name: 'staging',
Expand All @@ -44,7 +46,8 @@ window.GOVUK.loadAnalytics = {
auth: 'oJWs562CxSIjZKn_GlB5Bw',
preview: 'env-5',
gaProperty: 'UA-26179049-20',
gaPropertyCrossDomain: 'UA-145652997-1'
gaPropertyCrossDomain: 'UA-145652997-1',
consentApiUrl: 'staging'
},
{
name: 'integration',
Expand All @@ -58,15 +61,17 @@ window.GOVUK.loadAnalytics = {
auth: 'C7iYdcsOlYgGmiUJjZKrHQ',
preview: 'env-4',
gaProperty: 'UA-26179049-22',
gaPropertyCrossDomain: 'UA-145652997-1'
gaPropertyCrossDomain: 'UA-145652997-1',
consentApiUrl: 'staging'
},
{
name: 'devdocs',
domains: [
'docs.publishing.service.gov.uk'
],
initialiseGA4: true,
id: 'GTM-TNKCK97'
id: 'GTM-TNKCK97',
consentApiUrl: 'production'
}
],

Expand Down
@@ -0,0 +1,227 @@
/* eslint-env jasmine */

describe('The single consent cookie code', function () {
var acceptAll = {
essential: true,
usage: true,
campaigns: true,
settings: true
}
var rejectAll = {
essential: true,
usage: false,
campaigns: false,
settings: false
}
var mix = {
essential: true,
usage: false,
campaigns: true,
settings: true
}

beforeEach(function () {
delete window.GOVUK.singleConsent.consentApiObj
delete window.GOVUK.singleConsent.url
delete window.GOVUK.useSingleConsentApi
spyOn(window.GOVUK, 'triggerEvent').and.callThrough()
spyOn(window.GOVUK.singleConsent, 'apiCallback').and.callThrough()
jasmine.Ajax.install()
})

afterEach(function () {
jasmine.Ajax.uninstall()
delete window.GOVUK.singleConsent.consentApiObj
delete window.GOVUK.singleConsent.url
delete window.GOVUK.useSingleConsentApi
})

describe('if the single consent API should not be used', function () {
beforeEach(function () {
delete window.GOVUK.useSingleConsentApi
})

it('will not initialise', function () {
window.GOVUK.singleConsent.init()
expect(window.GOVUK.singleConsent.consentApiObj).not.toBeDefined()
})
})

describe('if the single consent API should be used', function () {
beforeEach(function () {
window.GOVUK.useSingleConsentApi = true
})

afterEach(function () {
delete window.GOVUK.useSingleConsentApi
})

it('does nothing if there is no unique user id', function () {
window.GOVUK.singleConsent.init()
expect(window.GOVUK.singleConsent.consentApiObj).toBeDefined()
expect(jasmine.Ajax.requests.count()).toEqual(0)
expect(window.GOVUK.singleConsent.apiCallback).toHaveBeenCalledWith(null, false, null)
})

it('accepts a function for the callback', function () {
var test = {
testFunction: function () {}
}
spyOn(test, 'testFunction')
window.GOVUK.singleConsent.init(test.testFunction)
expect(test.testFunction).toHaveBeenCalled()
})

describe('when determining the environment', function () {
it('starts without a URL for the consent API', function () {
expect(window.GOVUK.singleConsent.url).toBeFalsy()
})

it('defaults to staging if the environment is not recognised', function () {
spyOn(window.GOVUK.analyticsGa4.core.trackFunctions, 'getHostname').and.returnValue('moo')
window.GOVUK.singleConsent.init()
expect(window.GOVUK.singleConsent.url).toEqual('staging')
})

it('switches to production when on production', function () {
spyOn(window.GOVUK.analyticsGa4.core.trackFunctions, 'getHostname').and.returnValue('www.gov.uk')
window.GOVUK.singleConsent.init()
expect(window.GOVUK.singleConsent.url).toEqual('production')
})
})

describe('when there is a user id', function () {
beforeEach(function () {
spyOn(window.GOVUK, 'checkConsentCookie').and.returnValue(true)
window.GOVUK.cookie('gov_singleconsent_uid', '1234')
window.GOVUK.singleConsent.init()
})

afterEach(function () {
window.GOVUK.cookie('gov_singleconsent_uid', null)
})

it('does everything expected when full consent is given', function () {
window.GOVUK.singleConsent.setPreferences('accept')
jasmine.Ajax.requests.mostRecent().respondWith({
status: 200,
contentType: 'text/plain',
responseText: '{ "uid": "1234", "status": ' + JSON.stringify(acceptAll) + '}'
})

expect(window.GOVUK.singleConsent.consentApiObj).toBeDefined()
expect(window.GOVUK.singleConsent.apiCallback).toHaveBeenCalledWith(acceptAll, true, null)
expect(window.GOVUK.triggerEvent).toHaveBeenCalledWith(window, 'cookie-consent')
expect(window.GOVUK.cookie('cookies_policy')).toEqual('{"essential":true,"usage":true,"campaigns":true,"settings":true}')
})

it('does everything expected when consent is rejected', function () {
window.GOVUK.singleConsent.setPreferences('reject')
jasmine.Ajax.requests.mostRecent().respondWith({
status: 200,
contentType: 'text/plain',
responseText: '{ "uid": "1234", "status": ' + JSON.stringify(rejectAll) + '}'
})

expect(window.GOVUK.singleConsent.consentApiObj).toBeDefined()
expect(window.GOVUK.singleConsent.apiCallback).toHaveBeenCalledWith(rejectAll, true, null)
expect(window.GOVUK.cookie('cookies_policy')).toEqual('{"essential":true,"usage":false,"campaigns":false,"settings":false}')
})

it('does everything expected when partial consent is given', function () {
window.GOVUK.singleConsent.setPreferences(false, mix)
jasmine.Ajax.requests.mostRecent().respondWith({
status: 200,
contentType: 'text/plain',
responseText: '{ "uid": "1234", "status": ' + JSON.stringify(mix) + '}'
})

expect(window.GOVUK.singleConsent.consentApiObj).toBeDefined()
expect(window.GOVUK.singleConsent.apiCallback).toHaveBeenCalledWith(mix, true, null)
expect(window.GOVUK.cookie('cookies_policy')).toEqual('{"essential":true,"usage":false,"campaigns":true,"settings":true}')
})
})
})

describe('when there is a problem with the consent api', function () {
beforeEach(function () {
spyOn(window.GOVUK, 'checkConsentCookie').and.returnValue(true)
window.GOVUK.cookie('gov_singleconsent_uid', '1234')
window.GOVUK.useSingleConsentApi = true
jasmine.clock().install()
})

afterEach(function () {
delete window.GOVUK.useSingleConsentApi
jasmine.clock().uninstall()
window.GOVUK.cookie('gov_singleconsent_uid', null)
})

it('handles a timeout gracefully', function () {
window.GOVUK.singleConsent.init()
window.GOVUK.singleConsent.setPreferences('accept')
jasmine.Ajax.requests.mostRecent().responseTimeout()
expect(window.GOVUK.singleConsent.consentApiObj).toBeDefined()
expect(window.GOVUK.cookie('cookies_policy')).toEqual('{"essential":true,"usage":false,"campaigns":false,"settings":false}')
})

it('fails gracefully if pointed at an incorrect endpoint URL', function () {
window.GOVUK.singleConsent.init()
window.GOVUK.singleConsent.setPreferences('accept')
jasmine.Ajax.requests.mostRecent().respondWith({
status: 404,
contentType: 'text/plain',
responseText: 'error not found'
})
expect(window.GOVUK.singleConsent.consentApiObj).toBeDefined()
expect(window.GOVUK.cookie('cookies_policy')).toEqual('{"essential":true,"usage":false,"campaigns":false,"settings":false}')
})

it('fails gracefully when the server errors', function () {
window.GOVUK.singleConsent.init()
window.GOVUK.singleConsent.setPreferences('accept')
jasmine.Ajax.requests.mostRecent().respondWith({
status: 500,
contentType: 'text/plain',
responseText: 'error not found'
})
expect(window.GOVUK.singleConsent.consentApiObj).toBeDefined()
expect(window.GOVUK.cookie('cookies_policy')).toEqual('{"essential":true,"usage":false,"campaigns":false,"settings":false}')
})

it('fails gracefully when the response is invalid', function () {
window.GOVUK.singleConsent.init()
window.GOVUK.singleConsent.setPreferences('accept')
jasmine.Ajax.requests.mostRecent().respondWith({
status: 200,
contentType: 'text/plain',
responseText: 'not valid json'
})
expect(window.GOVUK.singleConsent.consentApiObj).toBeDefined()
expect(window.GOVUK.cookie('cookies_policy')).toEqual('{"essential":true,"usage":false,"campaigns":false,"settings":false}')
})

it('fails gracefully when the response is not as expected', function () {
window.GOVUK.singleConsent.init()
window.GOVUK.singleConsent.setPreferences('accept')
jasmine.Ajax.requests.mostRecent().respondWith({
status: 200,
contentType: 'text/plain',
responseText: '{"valid json but not":"what should be returned by the api"}'
})
expect(window.GOVUK.singleConsent.consentApiObj).toBeDefined()
expect(window.GOVUK.cookie('cookies_policy')).toEqual('{"essential":true,"usage":false,"campaigns":false,"settings":false}')
})

it('restores correct cookie consent', function () {
window.GOVUK.cookie('cookies_policy', '{"essential":true,"usage":false,"campaigns":false,"settings":false}')
window.GOVUK.singleConsent.init()
jasmine.Ajax.requests.mostRecent().respondWith({
status: 200,
contentType: 'text/plain',
responseText: '{ "uid": "1234", "status": ' + JSON.stringify(acceptAll) + '}'
})
expect(window.GOVUK.cookie('cookies_policy')).toEqual('{"essential":true,"usage":true,"campaigns":true,"settings":true}')
})
})
})

0 comments on commit fe5418e

Please sign in to comment.