Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

get/set local/session storage/cookies through cmp #866

Draft
wants to merge 21 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/dirty-radios-stare.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@guardian/consent-management-platform': patch
---

testing consent wrappers
113 changes: 113 additions & 0 deletions src/cmpCookies.tests.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import { onConsentChange } from './onConsentChange';
import type { Callback, ConsentState } from './types';
import type { TCFv2ConsentState } from './types/tcfv2';
import {cmpGetCookie, cmpSetCookie } from './cmpCookies';
import { getCookie as getCookie_, setCookie as setCookie_} from '@guardian/libs';

jest.mock('./onConsentChange');
jest.mock('@guardian/libs', () => ({
getCookie: jest.fn(),
setCookie: jest.fn(),
storage: {
local: {
get: jest.fn(),
set: jest.fn(),
},
session: {
get: jest.fn(),
set: jest.fn(),
},
}
}));

const tcfv2ConsentState: TCFv2ConsentState = {
consents: { 1: true },
eventStatus: 'tcloaded',
vendorConsents: {
['5efefe25b8e05c06542b2a77']: true,
},
addtlConsent: 'xyz',
gdprApplies: true,
tcString: 'YAAA',
};

const tcfv2ConsentStateNoConsent: TCFv2ConsentState = {
consents: { 1: false },
eventStatus: 'tcloaded',
vendorConsents: {},
addtlConsent: 'xyz',
gdprApplies: true,
tcString: 'YAAA',
};

const mockOnConsentChange = (consentState: ConsentState) =>
(onConsentChange as jest.Mock).mockImplementation((cb: Callback) => cb(consentState));

describe('cookies return the expected consent', () => {
let mockContains:any;

beforeEach(() => {
mockContains = 'someTestData';

(getCookie_ as jest.Mock).mockImplementation(({name }: {
name: string;
}) => {
if (name === 'gu.mock') {return mockContains}
else {return(null)}
});

(setCookie_ as jest.Mock).mockImplementation(({ name, value }: {
name: string;
value: string;
}) => {
if (name === 'gu.mock') {mockContains = value;}
});
});

test('Targeted advertising get cookie returns null when canTarget is false', async () => {
const consentState: ConsentState = {
tcfv2: tcfv2ConsentState,
canTarget: false,
framework: 'tcfv2',
};
mockOnConsentChange(consentState);
const cookieValue = await cmpGetCookie({useCase: 'Targeted advertising', name: 'gu.mock'});
expect(cookieValue).toEqual(null);
});
test('Targeted advertising can set and get cookies when canTarget is true', async () => {
const consentState: ConsentState = {
tcfv2: tcfv2ConsentState,
canTarget: true,
framework: 'tcfv2',
};
mockOnConsentChange(consentState);
const cookieValueDefault = await cmpGetCookie({useCase: 'Targeted advertising', name: 'gu.mock'});
expect(cookieValueDefault).toEqual('someTestData');
await cmpSetCookie({useCase: 'Essential', name: 'gu.mock', value: 'testdataAd'});
const cookieValue = await cmpGetCookie({useCase: 'Targeted advertising', name: 'gu.mock'});
expect(cookieValue).toEqual('testdataAd');
});
test('Essential can set and get cookies when no consents', async () => {
const consentState: ConsentState = {
tcfv2: tcfv2ConsentStateNoConsent,
canTarget: false,
framework: 'tcfv2',
};
mockOnConsentChange(consentState);
const cookieValueDefault = await cmpGetCookie({useCase: 'Essential', name:'gu.mock'});
expect(cookieValueDefault).toEqual('someTestData');
await cmpSetCookie({useCase: 'Essential', name: 'gu.mock', value: 'testdata'});
const cookieValue = await cmpGetCookie({useCase: 'Essential', name: 'gu.mock'});
expect(cookieValue).toEqual('testdata');
});
test('get null if cookie does not exist', async () => {
const consentState: ConsentState = {
tcfv2: tcfv2ConsentStateNoConsent,
canTarget: false,
framework: 'tcfv2',
};
mockOnConsentChange(consentState);
const cookieValue = await cmpGetCookie({useCase: 'Essential', name: 'gu.does_not_exist'});
expect(cookieValue).toEqual(null);
});
});
99 changes: 99 additions & 0 deletions src/cmpCookies.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { getCookie, setCookie, setSessionCookie } from '@guardian/libs';
import { hasConsentForUseCaseWithConsentState } from './hasConsentForUseCase';
import { onConsent } from './onConsent';
import { ConsentState } from './types';
import type { ConsentUseCases } from './types/consentUseCases';

//TODO?: Write wrappers for the other cookie functions in @guardian/libs

export const cmpGetCookie = async({ useCase, name, shouldMemoize, }: {
useCase: ConsentUseCases,
name: string;
shouldMemoize?: boolean | undefined;
}): Promise<string | null> =>
{
const consentState = await onConsent();
return(cmpGetCookieWithConsentState({useCase, consentState, name, shouldMemoize}))
}

export const cmpGetCookieWithConsentState = ({ useCase, consentState, name, shouldMemoize}: {
useCase: ConsentUseCases,
consentState: ConsentState,
name: string;
shouldMemoize?: boolean | undefined;
}): string | null =>
{
console.log('in cmpGetCookie');

if(hasConsentForUseCaseWithConsentState(useCase, consentState))
{
return getCookie({name: name, shouldMemoize: shouldMemoize})
}
else
{
console.error('cmp', `Cannot get cookie ${name} due to missing consent for use-case ${useCase}`)
return(null)
}

Check warning on line 36 in src/cmpCookies.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement

Check warning on line 36 in src/cmpCookies.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🌿 Branch is not covered

Warning! Not covered branch
};

export const cmpSetCookie = async ({ useCase, name, value, daysToLive, isCrossSubdomain, }: {
useCase: ConsentUseCases,
name: string;
value: string;
daysToLive?: number | undefined;
isCrossSubdomain?: boolean | undefined;
}): Promise<void> =>
{
const consentState = await onConsent();
return(cmpSetCookieWithConsentState({useCase, consentState, name, value, daysToLive, isCrossSubdomain}))
}

export const cmpSetCookieWithConsentState = ({ useCase, consentState, name, value, daysToLive, isCrossSubdomain, }: {
useCase: ConsentUseCases,
consentState: ConsentState,
name: string;
value: string;
daysToLive?: number | undefined;
isCrossSubdomain?: boolean | undefined;
}): void =>
{
console.log('in cmpSetCookie');

if(hasConsentForUseCaseWithConsentState(useCase, consentState))
{
setCookie({name:name, value:value, daysToLive:daysToLive, isCrossSubdomain:isCrossSubdomain})
}
else
{
console.error('cmp', `Cannot set cookie ${name} due to missing consent for use-case ${useCase}`)
}

Check warning on line 69 in src/cmpCookies.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement

Check warning on line 69 in src/cmpCookies.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🌿 Branch is not covered

Warning! Not covered branch
};

export const cmpSetSessionCookie = async ({ useCase, name, value }: {
useCase: ConsentUseCases,
name: string;
value: string;
}): Promise<void> =>
{
const consentState = await onConsent();
return(cmpSetSessionCookieWithConsentState({useCase, consentState, name, value}))
};

export const cmpSetSessionCookieWithConsentState= async ({ useCase, consentState, name, value }: {
useCase: ConsentUseCases,
consentState: ConsentState,
name: string;
value: string;
}): Promise<void> =>
{
console.log('in cmpSetSessionCookie');

if(hasConsentForUseCaseWithConsentState(useCase, consentState))
{
setSessionCookie({name:name, value:value})
}
else
{
console.error('cmp', `Cannot set cookie ${name} due to missing consent for use-case ${useCase}`)
}

Check warning on line 98 in src/cmpCookies.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement

Check warning on line 98 in src/cmpCookies.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🌿 Branch is not covered

Warning! Not covered branch
};
175 changes: 175 additions & 0 deletions src/cmpStorage.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
import { onConsentChange } from './onConsentChange';
import type { Callback, ConsentState } from './types';
import type { TCFv2ConsentState } from './types/tcfv2';
import {storage} from './cmpStorage';
import { storage as storageStub} from '@guardian/libs';

//TODO: add tests for all use-cases and all other storage functions

jest.mock('./onConsentChange');
jest.mock('@guardian/libs', () => ({
getCookie: jest.fn(),
setCookie: jest.fn(),
storage: {
local: {
get: jest.fn(),
set: jest.fn(),
},
session: {
get: jest.fn(),
set: jest.fn(),
},
}
}));

const tcfv2ConsentState: TCFv2ConsentState = {
consents: { 1: true },
eventStatus: 'tcloaded',
vendorConsents: {
['5efefe25b8e05c06542b2a77']: true,
},
addtlConsent: 'xyz',
gdprApplies: true,
tcString: 'YAAA',
};

const tcfv2ConsentStateNoConsent: TCFv2ConsentState = {
consents: { 1: false },
eventStatus: 'tcloaded',
vendorConsents: {},
addtlConsent: 'xyz',
gdprApplies: true,
tcString: 'YAAA',
};

const mockOnConsentChange = (consentState: ConsentState) =>
(onConsentChange as jest.Mock).mockImplementation((cb: Callback) => cb(consentState));

describe('local storage returns the expected consent', () => {
let mockContains:any;

beforeEach(() => {
mockContains = 'someTestData';

(storageStub.local.get as jest.Mock).mockImplementation((key:string) => {
if (key === 'gu.mock') {return mockContains}
else {return(null)}
});

(storageStub.local.set as jest.Mock).mockImplementation((key:string, data:unknown) => {
if (key === 'gu.mock') {mockContains = data;}
});
});

test('Targeted advertising get local storage returns null when canTarget is false', async () => {
const consentState: ConsentState = {
tcfv2: tcfv2ConsentState,
canTarget: false,
framework: 'tcfv2',
};
mockOnConsentChange(consentState);
const localStorageValue = await storage.local.get('Targeted advertising', 'gu.mock');
expect(localStorageValue).toEqual(null);
});
test('Targeted advertising can set and get local storage value when canTarget is true', async () => {
const consentState: ConsentState = {
tcfv2: tcfv2ConsentState,
canTarget: true,
framework: 'tcfv2',
};
mockOnConsentChange(consentState);
const localStorageValueDefault = await storage.local.get('Targeted advertising', 'gu.mock');
expect(localStorageValueDefault).toEqual('someTestData');
await storage.local.set('Essential', 'gu.mock', 'testdataAd');
const localStorageValue = await storage.local.get('Targeted advertising', 'gu.mock');
expect(localStorageValue).toEqual('testdataAd');
});
test('Essential can set and get local storage when no consents', async () => {
const consentState: ConsentState = {
tcfv2: tcfv2ConsentStateNoConsent,
canTarget: false,
framework: 'tcfv2',
};
mockOnConsentChange(consentState);
const localStorageValueDefault = await storage.local.get('Essential', 'gu.mock');
expect(localStorageValueDefault).toEqual('someTestData');
await storage.local.set('Essential', 'gu.mock', 'testdata');
const localStorageValue = await storage.local.get('Essential', 'gu.mock');
expect(localStorageValue).toEqual('testdata');
});
test('get null if local storage item does not exist', async () => {
const consentState: ConsentState = {
tcfv2: tcfv2ConsentStateNoConsent,
canTarget: false,
framework: 'tcfv2',
};
mockOnConsentChange(consentState);
const localStorageValue = await storage.local.get('Essential', 'gu.does_not_exist');
expect(localStorageValue).toEqual(null);
});
});


describe('session storage returns the expected consent', () => {
let mockContains:any;

beforeEach(() => {
mockContains = 'someTestData';

(storageStub.session.get as jest.Mock).mockImplementation((key:string) => {
if (key === 'gu.mock') {return mockContains}
else {return(null)}
});

(storageStub.session.set as jest.Mock).mockImplementation((key:string, data:unknown) => {
if (key === 'gu.mock') {mockContains = data;}
});
});

test('Targeted advertising get session storage returns null when canTarget is false', async () => {
const consentState: ConsentState = {
tcfv2: tcfv2ConsentState,
canTarget: false,
framework: 'tcfv2',
};
mockOnConsentChange(consentState);
const sessionStorageValue = await storage.session.get('Targeted advertising', 'gu.mock');
expect(sessionStorageValue).toEqual(null);
});
test('Targeted advertising can set and get session storage value when canTarget is true', async () => {
const consentState: ConsentState = {
tcfv2: tcfv2ConsentState,
canTarget: true,
framework: 'tcfv2',
};
mockOnConsentChange(consentState);
const sessionStorageValueDefault = await storage.session.get('Targeted advertising', 'gu.mock');
expect(sessionStorageValueDefault).toEqual('someTestData');
await storage.session.set('Essential', 'gu.mock', 'testdataAd');
const sessionStorageValue = await storage.session.get('Targeted advertising', 'gu.mock');
expect(sessionStorageValue).toEqual('testdataAd');
});
test('Essential can set and get session storage when no consents', async () => {
const consentState: ConsentState = {
tcfv2: tcfv2ConsentStateNoConsent,
canTarget: false,
framework: 'tcfv2',
};
mockOnConsentChange(consentState);
const sessionStorageValueDefault = await storage.session.get('Essential', 'gu.mock');
expect(sessionStorageValueDefault).toEqual('someTestData');
await storage.session.set('Essential', 'gu.mock', 'testdata');
const sessionStorageValue = await storage.session.get('Essential', 'gu.mock');
expect(sessionStorageValue).toEqual('testdata');
});
test('get null if session storage item does not exist', async () => {
const consentState: ConsentState = {
tcfv2: tcfv2ConsentStateNoConsent,
canTarget: false,
framework: 'tcfv2',
};
mockOnConsentChange(consentState);
const sessionStorageValue = await storage.session.get('Essential', 'gu.does_not_exist');
expect(sessionStorageValue).toEqual(null);
});
});