Skip to content

Commit ece53d3

Browse files
authored
Merge pull request #115 from Financial-Times/ADSDEV-1243-add-privacy-info-request-consent-store
ADSDEV-1243: add privacy info + fetch legislation
2 parents 7f8fafa + 735b01a commit ece53d3

File tree

8 files changed

+193
-19
lines changed

8 files changed

+193
-19
lines changed

.babelrc

Lines changed: 0 additions & 13 deletions
This file was deleted.

babel.config.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
module.exports = {
2+
presets: [
3+
[
4+
"@babel/preset-env",
5+
{
6+
targets: {
7+
node: 14,
8+
},
9+
},
10+
],
11+
],
12+
plugins: ["@babel/plugin-transform-react-jsx"],
13+
};

package-lock.json

Lines changed: 44 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,11 @@
3636
"npm": "7.x || 8.x"
3737
},
3838
"dependencies": {
39+
"@financial-times/ads-personalised-consent": "^5.2.3",
3940
"@financial-times/o-grid": "^5.0.0",
4041
"@financial-times/o-tracking": "^4.5.0",
4142
"@financial-times/o-viewport": "^4.0.0",
43+
"@financial-times/privacy-us-privacy": "^1.0.0",
4244
"ready-state": "^2.0.5",
4345
"web-vitals": "^3.0.0"
4446
},

src/client/__test__/index.spec.js

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@ jest.mock('@financial-times/o-tracking', () => {
77
}, { virtual: true });
88
jest.mock('@financial-times/o-viewport', () => ({ getOrientation: jest.fn() }), { virtual: true });
99
jest.mock('@financial-times/o-grid', () => ({ getCurrentLayout: jest.fn() }), { virtual: true });
10+
jest.mock('../utils/getConsentData', () => jest.fn());
1011

1112
import oTracking from '@financial-times/o-tracking';
1213
import oViewport from '@financial-times/o-viewport';
1314
import oGrid from '@financial-times/o-grid';
15+
import getConsentData from '../utils/getConsentData';
1416
import { init, SPOOR_API_INGEST_URL } from '..';
1517

1618
const appContext = {
@@ -23,7 +25,16 @@ const appContext = {
2325
};
2426

2527
describe('src/index', () => {
26-
afterEach(() => jest.clearAllMocks());
28+
beforeAll(() => {
29+
getConsentData.mockResolvedValue({});
30+
window.console.warn = jest.fn()
31+
})
32+
33+
afterEach(() => {
34+
jest.clearAllMocks();
35+
// Clean global instance left on the window after each init() call
36+
delete window.oTracking
37+
});
2738

2839
describe('.init()', () => {
2940
it('initialises o-tracking', () => {
@@ -39,6 +50,24 @@ describe('src/index', () => {
3950
);
4051
});
4152

53+
it('attaches the o-tracking instance to the window', () => {
54+
const oTrackingInstance = init({ appContext });
55+
expect(window.oTracking).toBe(oTrackingInstance);
56+
});
57+
58+
it("warns the user in case an instance of o-tracking is already attached to the window without overriding the value", () => {
59+
window.oTracking = "initialValue";
60+
const ourInstance = init({ appContext });
61+
expect(window.console.warn).toHaveBeenCalledWith(
62+
"An oTracking instance already exists on window, skipping",
63+
{
64+
currentInstance: 'initialValue',
65+
ourInstance,
66+
}
67+
);
68+
expect(window.oTracking).toBe("initialValue");
69+
});
70+
4271
it('configures o-tracking with context data', () => {
4372
init({ appContext });
4473

@@ -74,14 +103,35 @@ describe('src/index', () => {
74103
);
75104
});
76105

77-
it('triggers a page view with page view context', () => {
106+
it('triggers a page view with page view context and consents', async () => {
107+
getConsentData.mockResolvedValue({ some: 'consentData' });
108+
78109
init({ appContext, pageViewContext: { foo: 'bar' } });
79110

111+
// We don't await the promise returned while getting consent data.
112+
// The line below lets us wait for the mocked getConsentData promise to resolve.
113+
await new Promise(process.nextTick);
114+
80115
expect(oTracking.page).toHaveBeenCalledTimes(1);
116+
expect(oTracking.page).toHaveBeenCalledWith(
117+
expect.objectContaining({
118+
foo: 'bar',
119+
consents: {
120+
some: 'consentData'
121+
},
122+
})
123+
);
124+
});
125+
126+
it('Does not pass consents down if getting consents fails', async () => {
127+
getConsentData.mockRejectedValueOnce(new Error('no consent data'));
128+
init({ appContext, pageViewContext: { foo: 'bar' } });
129+
await new Promise(process.nextTick);
81130

131+
expect(oTracking.page).toHaveBeenCalledTimes(1);
82132
expect(oTracking.page).toHaveBeenCalledWith(
83133
expect.objectContaining({
84-
foo: 'bar'
134+
foo: 'bar',
85135
})
86136
);
87137
});

src/client/index.js

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import getUserData from './tracking/getUserData';
33
import getQueryParams from './tracking/getQueryParams';
44
import getErrorPageParams from './tracking/getErrorPageParams';
55
import transformContextData from './utils/transformContextData';
6+
import getConsentData from './utils/getConsentData';
67

78
export const SPOOR_API_INGEST_URL = 'https://spoor-api.ft.com/ingest';
89

@@ -15,7 +16,7 @@ export function init ({ appContext, extraContext, pageViewContext }) {
1516
context: transformContextData({
1617
...queryParams,
1718
...appContext,
18-
...extraContext
19+
...extraContext,
1920
}),
2021
user: getUserData(),
2122
// Using the Beacon API ensures that no tracking event data is lost
@@ -30,10 +31,23 @@ export function init ({ appContext, extraContext, pageViewContext }) {
3031
if (window === window.top || window.location.hostname === 'errors-next.ft.com') {
3132
const errorPageParams = getErrorPageParams();
3233

33-
oTracking.page({
34+
const pageViewEventParams = {
3435
...errorPageParams,
3536
...pageViewContext,
36-
});
37+
};
38+
39+
getConsentData()
40+
.then((consents) => {
41+
oTracking.page({
42+
...pageViewEventParams,
43+
consents,
44+
});
45+
})
46+
.catch((err) => {
47+
// eslint-disable-next-line no-console
48+
console.warn("Could not get consent data", err);
49+
oTracking.page(pageViewEventParams);
50+
});
3751
}
3852

3953
// Initialise click event tracking for interactive elements
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
jest.mock("@financial-times/privacy-us-privacy", () => ({
2+
getUsPrivacyForTracking: jest.fn(),
3+
}));
4+
5+
jest.mock("@financial-times/ads-personalised-consent", () => ({
6+
getPersonalisedConsent: jest.fn(),
7+
}));
8+
9+
import { getUsPrivacyForTracking } from "@financial-times/privacy-us-privacy";
10+
import { getPersonalisedConsent } from "@financial-times/ads-personalised-consent";
11+
12+
import getConsentData from "../getConsentData";
13+
14+
describe("getConsentData", () => {
15+
16+
beforeEach(() => {
17+
getPersonalisedConsent.mockResolvedValue({
18+
isAllowed: jest.fn().mockReturnValue({
19+
consent1: true,
20+
consent2: false,
21+
}),
22+
});
23+
getUsPrivacyForTracking.mockReturnValue("usprivacy");
24+
})
25+
afterEach(() => {
26+
jest.clearAllMocks();
27+
});
28+
29+
30+
it("returns the consent data and awaits for getPersonalisedConsent.isAllowed()", async () => {
31+
const consentData = await getConsentData();
32+
33+
expect(consentData).toEqual({
34+
consent1: true,
35+
consent2: false,
36+
usprivacy: "usprivacy",
37+
});
38+
});
39+
40+
it("Errors if getPersonalisedConsent.isAllowed() rejects", async () => {
41+
getPersonalisedConsent.mockRejectedValue(new Error("no consent"));
42+
43+
await expect(getConsentData()).rejects.toThrow("no consent");
44+
});
45+
46+
it("Errors if getUsPrivacyForTracking rejects", async () => {
47+
getUsPrivacyForTracking.mockImplementation(() => {
48+
throw new Error("us privacy error");
49+
});
50+
51+
await expect(getConsentData()).rejects.toThrow("us privacy error");
52+
});
53+
});

src/client/utils/getConsentData.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { getUsPrivacyForTracking } from "@financial-times/privacy-us-privacy";
2+
import { getPersonalisedConsent } from "@financial-times/ads-personalised-consent";
3+
4+
export default async function getConsentData() {
5+
const consentValues = (await getPersonalisedConsent()).isAllowed();
6+
const usprivacy = getUsPrivacyForTracking();
7+
return {
8+
...consentValues,
9+
usprivacy,
10+
};
11+
}

0 commit comments

Comments
 (0)