Skip to content

Commit

Permalink
Send setConsentCookie only when legislationId is 'gdpr'
Browse files Browse the repository at this point in the history
  • Loading branch information
oliverturner committed Apr 29, 2021
1 parent a4bd719 commit a2e6b9c
Show file tree
Hide file tree
Showing 14 changed files with 182 additions and 133 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
{
"compilerOptions": {
"outDir": "dist/types",
"allowJs": true,
"target": "es2015",
"module": "commonjs",
"strict": true,
Expand Down
110 changes: 80 additions & 30 deletions components/x-privacy-manager/src/__tests__/helpers.js
Original file line number Diff line number Diff line change
@@ -1,39 +1,58 @@
const React = require('react')

// eslint-disable-next-line no-unused-vars
const { h } = require('@financial-times/x-engine')
const { mount } = require('@financial-times/x-test-utils/enzyme')

import { PrivacyManager } from '../privacy-manager'

export const CONSENT_PROXY_HOST = 'https://consent.ft.com'
export const CONSENT_PROXY_ENDPOINT = 'https://consent.ft.com/__consent/consent-record/FTPINK/abcde'

export const buildPayload = (consent) => ({
setConsentCookie: true,
consentSource: 'consuming-app',
data: {
behaviouralAds: {
onsite: {
fow: 'privacyCCPA/H0IeyQBalorD.6nTqqzhNTKECSgOPJCG',
lbi: true,
source: 'consuming-app',
status: consent
/**
*
* @param {{
* setConsentCookie: boolean,
* consent: boolean
* }}
* @returns
*/
export const buildPayload = ({ setConsentCookie, consent }) => {
return {
setConsentCookie,
consentSource: 'consuming-app',
data: {
behaviouralAds: {
onsite: {
fow: 'privacyCCPA/H0IeyQBalorD.6nTqqzhNTKECSgOPJCG',
lbi: true,
source: 'consuming-app',
status: consent
}
},
demographicAds: {
onsite: {
fow: 'privacyCCPA/H0IeyQBalorD.6nTqqzhNTKECSgOPJCG',
lbi: true,
source: 'consuming-app',
status: consent
}
},
programmaticAds: {
onsite: {
fow: 'privacyCCPA/H0IeyQBalorD.6nTqqzhNTKECSgOPJCG',
lbi: true,
source: 'consuming-app',
status: consent
}
}
},
demographicAds: {
onsite: {
fow: 'privacyCCPA/H0IeyQBalorD.6nTqqzhNTKECSgOPJCG',
lbi: true,
source: 'consuming-app',
status: consent
}
},
programmaticAds: {
onsite: {
fow: 'privacyCCPA/H0IeyQBalorD.6nTqqzhNTKECSgOPJCG',
lbi: true,
source: 'consuming-app',
status: consent
}
}
},
cookieDomain: '.ft.com',
formOfWordsId: 'privacyCCPA'
})
cookieDomain: '.ft.com',
formOfWordsId: 'privacyCCPA'
}
}

/** @type {XPrivacyManager.BasePrivacyManagerProps} */
export const defaultProps = {
userId: 'abcde',
legislationId: 'ccpa',
Expand All @@ -48,5 +67,36 @@ export const defaultProps = {
actions: {
onConsentChange: jest.fn(() => {}),
sendConsent: jest.fn().mockReturnValue({ _response: { ok: undefined } })
},
onConsentSavedCallbacks: [jest.fn(), jest.fn()]
}

/**
* Configure an instance of PrivacyManager and set up
* - Handlers for submit events
* - Post-submission callbacks
*
* @param {Partial<XPrivacyManager.BasePrivacyManagerProps>} propOverrides
*
* @returns {{
* subject: ReactWrapper<any, Readonly<{}>, React.Component<{}, {}, any>>;
* callbacks: XPrivacyManager.OnSaveCallback[] | jest.Mock<any, any>[];
* submitConsent(value: boolean): Promise<void>;
* }}
*/
export function setupPrivacyManager(propOverrides = {}) {
const props = Object.assign({}, defaultProps, propOverrides)
const subject = mount(<PrivacyManager {...props} />)

return {
subject,
callbacks: props.onConsentSavedCallbacks,
async submitConsent(value) {
await subject.find(`input[value="${value}"]`).first().prop('onChange')(undefined)
await subject.find('form').first().prop('onSubmit')(undefined)

// Reconcile snapshot with state
subject.update()
}
}
}
82 changes: 41 additions & 41 deletions components/x-privacy-manager/src/__tests__/messaging.test.jsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
const { h } = require('@financial-times/x-engine');
const { mount } = require('@financial-times/x-test-utils/enzyme');
const { h } = require('@financial-times/x-engine')
const { mount } = require('@financial-times/x-test-utils/enzyme')

import { defaultProps } from './helpers';
import { defaultProps } from './helpers'

import { BasePrivacyManager } from '../privacy-manager';
import { BasePrivacyManager } from '../privacy-manager'

function findMessageComponent(props) {
const subject = mount(<BasePrivacyManager {...props} />);
const messages = subject.find('[data-o-component="o-message"]');
const message = messages.first();
const link = message.find('[data-component="referrer-link"]');
const subject = mount(<BasePrivacyManager {...props} />)
const messages = subject.find('[data-o-component="o-message"]')
const message = messages.first()
const link = message.find('[data-component="referrer-link"]')

return {
messages,
message,
link,
};
link
}
}

describe('x-privacy-manager', () => {
Expand All @@ -25,50 +25,50 @@ describe('x-privacy-manager', () => {
consent: true,
legislation: ['ccpa'],
isLoading: false,
_response: undefined,
};
_response: undefined
}

it('None by default', () => {
const { messages } = findMessageComponent(messageProps);
expect(messages).toHaveLength(0);
});
const { messages } = findMessageComponent(messageProps)
expect(messages).toHaveLength(0)
})

it('While loading', () => {
const { messages, message } = findMessageComponent({ ...messageProps, isLoading: true });
expect(messages).toHaveLength(1);
expect(message).toHaveClassName('o-message--neutral');
});
const { messages, message } = findMessageComponent({ ...messageProps, isLoading: true })
expect(messages).toHaveLength(1)
expect(message).toHaveClassName('o-message--neutral')
})

it('On receiving a response with a status of 200', () => {
const _response = { ok: true, status: 200 };
const { messages, message, link } = findMessageComponent({ ...messageProps, _response });
const _response = { ok: true, status: 200 }
const { messages, message, link } = findMessageComponent({ ...messageProps, _response })

expect(messages).toHaveLength(1);
expect(message).toHaveClassName('o-message--success');
expect(link).toHaveProp('href', 'https://www.ft.com/');
});
expect(messages).toHaveLength(1)
expect(message).toHaveClassName('o-message--success')
expect(link).toHaveProp('href', 'https://www.ft.com/')
})

it('On receiving a response with a non-200 status', () => {
const _response = { ok: false, status: 400 };
const { messages, message, link } = findMessageComponent({ ...messageProps, _response });
const _response = { ok: false, status: 400 }
const { messages, message, link } = findMessageComponent({ ...messageProps, _response })

expect(messages).toHaveLength(1);
expect(message).toHaveClassName('o-message--error');
expect(link).toHaveProp('href', 'https://www.ft.com/');
});
expect(messages).toHaveLength(1)
expect(message).toHaveClassName('o-message--error')
expect(link).toHaveProp('href', 'https://www.ft.com/')
})

it('On receiving any response with referrer undefined', () => {
const _response = { ok: false, status: 400 };
const referrer = undefined;
const _response = { ok: false, status: 400 }
const referrer = undefined
const { messages, message, link } = findMessageComponent({
...messageProps,
referrer,
_response,
});
_response
})

expect(messages).toHaveLength(1);
expect(message).toHaveClassName('o-message--error');
expect(link).toHaveLength(0);
});
});
});
expect(messages).toHaveLength(1)
expect(message).toHaveClassName('o-message--error')
expect(link).toHaveLength(0)
})
})
})
79 changes: 34 additions & 45 deletions components/x-privacy-manager/src/__tests__/state.test.jsx
Original file line number Diff line number Diff line change
@@ -1,39 +1,13 @@
const { h } = require('@financial-times/x-engine')
const { mount } = require('@financial-times/x-test-utils/enzyme')
const fetchMock = require('fetch-mock')

import * as helpers from './helpers'

import { PrivacyManager } from '../privacy-manager'

function getLastFetchPayload() {
return JSON.parse(fetchMock.lastOptions().body)
}

describe('x-privacy-manager', () => {
describe('handling consent choices', () => {
function setup(propOverrides = {}) {
const props = {
...helpers.defaultProps,
onConsentSavedCallbacks: [jest.fn(), jest.fn()],
...propOverrides
}
const subject = mount(<PrivacyManager {...props} />)

return {
subject,
callbacks: props.onConsentSavedCallbacks,
async submitConsent(value) {
// Switch consent to false and submit form
await subject.find(`input[value="${value}"]`).first().prop('onChange')(undefined)
await subject.find('form').first().prop('onSubmit')(undefined)

// Reconcile snapshot with state
subject.update()
}
}
}

beforeEach(() => {
fetchMock.reset()
fetchMock.config.overwriteRoutes = true
Expand All @@ -45,25 +19,25 @@ describe('x-privacy-manager', () => {
})

it('handles consecutive changes of consent', async () => {
let payload
const { subject, callbacks, submitConsent } = setup({ consent: true })
let expectedPayload
const { subject, callbacks, submitConsent } = helpers.setupPrivacyManager({ consent: true })
const optInInput = subject.find('[data-trackable="ccpa-advertising-toggle-allow"]').first()

await submitConsent(false)

// Check fetch and both callbacks were run with correct `payload` values
payload = helpers.buildPayload(false)
expect(getLastFetchPayload()).toEqual(payload)
expect(callbacks[0]).toHaveBeenCalledWith(null, { payload, consent: false })
expect(callbacks[1]).toHaveBeenCalledWith(null, { payload, consent: false })
expectedPayload = helpers.buildPayload({ setConsentCookie: false, consent: false })
expect(getLastFetchPayload()).toEqual(expectedPayload)
expect(callbacks[0]).toHaveBeenCalledWith(null, { payload: expectedPayload, consent: false })
expect(callbacks[1]).toHaveBeenCalledWith(null, { payload: expectedPayload, consent: false })

await submitConsent(true)

// Check fetch and both callbacks were run with correct `payload` values
payload = helpers.buildPayload(true)
expect(getLastFetchPayload()).toEqual(payload)
expect(callbacks[0]).toHaveBeenCalledWith(null, { payload, consent: true })
expect(callbacks[1]).toHaveBeenCalledWith(null, { payload, consent: true })
expectedPayload = helpers.buildPayload({ setConsentCookie: false, consent: true })
expect(getLastFetchPayload()).toEqual(expectedPayload)
expect(callbacks[0]).toHaveBeenCalledWith(null, { payload: expectedPayload, consent: true })
expect(callbacks[1]).toHaveBeenCalledWith(null, { payload: expectedPayload, consent: true })

// Verify that confimatory nmessage is displayed
const message = subject.find('[data-o-component="o-message"]').first()
Expand All @@ -74,35 +48,50 @@ describe('x-privacy-manager', () => {
})

it('when provided, passes the cookieDomain prop in the fetch and callback payload', async () => {
const { callbacks, submitConsent } = setup({ cookieDomain: '.ft.com' })
const payload = { ...helpers.buildPayload(false), cookieDomain: '.ft.com' }
const { callbacks, submitConsent } = helpers.setupPrivacyManager({ cookieDomain: '.ft.com' })
const expectedPayload = {
...helpers.buildPayload({ setConsentCookie: false, consent: false }),
cookieDomain: '.ft.com'
}

await submitConsent(false)

// Check fetch and both callbacks were run with correct `payload` values
expect(getLastFetchPayload()).toEqual(payload)
expect(callbacks[0]).toHaveBeenCalledWith(null, { payload, consent: false })
expect(callbacks[1]).toHaveBeenCalledWith(null, { payload, consent: false })
expect(getLastFetchPayload()).toEqual(expectedPayload)
expect(callbacks[0]).toHaveBeenCalledWith(null, { payload: expectedPayload, consent: false })
expect(callbacks[1]).toHaveBeenCalledWith(null, { payload: expectedPayload, consent: false })
})

it('passes error object to callbacks when fetch fails', async () => {
const { callbacks, submitConsent } = setup()
const payload = helpers.buildPayload(false)
const { callbacks, submitConsent } = helpers.setupPrivacyManager()
const expectedPayload = helpers.buildPayload({ setConsentCookie: false, consent: false })

// Override fetch-mock to fail requests
fetchMock.mock(helpers.CONSENT_PROXY_ENDPOINT, { status: 500 }, { delay: 500 })

await submitConsent(false)

// calls fetch with the correct payload
expect(getLastFetchPayload()).toEqual(payload)
expect(getLastFetchPayload()).toEqual(expectedPayload)

// Calls both callbacks with an error as first argument
callbacks.forEach((callback) => {
const [errorArgument, resultArgument] = callback.mock.calls.pop()
expect(errorArgument).toBeInstanceOf(Error)
expect(resultArgument).toEqual({ payload, consent: false })
expect(resultArgument).toEqual({ payload: expectedPayload, consent: false })
})
})

it('Sends legislation-specific values (e.g. setConsentCookie)', async () => {
const expectedPayload = helpers.buildPayload({ setConsentCookie: true, consent: false })
const { submitConsent } = helpers.setupPrivacyManager({
legislationId: 'gdpr',
consent: true
})

await submitConsent(false)

expect(getLastFetchPayload()).toEqual(expectedPayload)
})
})
})
Loading

0 comments on commit a2e6b9c

Please sign in to comment.