diff --git a/.changeset/giant-nails-sniff.md b/.changeset/giant-nails-sniff.md new file mode 100644 index 000000000..acd20b40f --- /dev/null +++ b/.changeset/giant-nails-sniff.md @@ -0,0 +1,5 @@ +--- +'@guardian/commercial': minor +--- + +Updates @guardian/libs to latest version which migrated frameworks from ccpa to usnat. Therefore, commercial needed to be updated to reflect this. diff --git a/docs/e2e-testing/# E2E testing.md b/docs/e2e-testing/# E2E testing.md index 379126ac8..9746a0caf 100644 --- a/docs/e2e-testing/# E2E testing.md +++ b/docs/e2e-testing/# E2E testing.md @@ -29,7 +29,7 @@ - TCF: If the user doesn't consent we shouldn't show ads. - TCF: Ads don't render until the user gives consent. - AUS: Check if a user selects "non personalized advertising" that the targeting is different. - - CCPA: Check if a user selects "do not sell my information" that the targeting is different. + - USNAT: Check if a user selects "do not sell my information" that the targeting is different. - Lower priorities features: - Commercial metrics diff --git a/package.json b/package.json index 19fe0ebe2..e0580e788 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,7 @@ "@guardian/eslint-config-typescript": "^12.0.0", "@guardian/identity-auth": "^3.0.0", "@guardian/identity-auth-frontend": "^5.0.0", - "@guardian/libs": "18.0.2", + "@guardian/libs": "19.0.0", "@guardian/prettier": "8.0.1", "@guardian/source": "8.0.0", "@playwright/test": "1.46.0", @@ -127,7 +127,7 @@ "@guardian/core-web-vitals": "^7.0.0", "@guardian/identity-auth": "^3.0.0", "@guardian/identity-auth-frontend": "^5.0.0", - "@guardian/libs": "^18.0.0", + "@guardian/libs": "^19.1.0", "@guardian/source": "^8.0.0", "typescript": "~5.5.3" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 320f5ed03..2f8fb70ae 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -75,19 +75,19 @@ devDependencies: version: 6.1.1(browserslist@4.23.3)(tslib@2.7.0) '@guardian/core-web-vitals': specifier: 7.0.0 - version: 7.0.0(@guardian/libs@18.0.2)(tslib@2.7.0)(typescript@5.5.3)(web-vitals@4.2.3) + version: 7.0.0(@guardian/libs@19.0.0)(tslib@2.7.0)(typescript@5.5.3)(web-vitals@4.2.3) '@guardian/eslint-config-typescript': specifier: ^12.0.0 version: 12.0.0(eslint@8.57.0)(tslib@2.7.0)(typescript@5.5.3) '@guardian/identity-auth': specifier: ^3.0.0 - version: 3.0.1(@guardian/libs@18.0.2)(tslib@2.7.0)(typescript@5.5.3) + version: 3.0.1(@guardian/libs@19.0.0)(tslib@2.7.0)(typescript@5.5.3) '@guardian/identity-auth-frontend': specifier: ^5.0.0 - version: 5.0.0(@guardian/identity-auth@3.0.1)(@guardian/libs@18.0.2)(tslib@2.7.0)(typescript@5.5.3) + version: 5.0.0(@guardian/identity-auth@3.0.1)(@guardian/libs@19.0.0)(tslib@2.7.0)(typescript@5.5.3) '@guardian/libs': - specifier: 18.0.2 - version: 18.0.2(tslib@2.7.0)(typescript@5.5.3) + specifier: 19.0.0 + version: 19.0.0(tslib@2.7.0)(typescript@5.5.3) '@guardian/prettier': specifier: 8.0.1 version: 8.0.1(prettier@3.3.3)(tslib@2.7.0) @@ -1883,7 +1883,7 @@ packages: tslib: 2.7.0 dev: true - /@guardian/core-web-vitals@7.0.0(@guardian/libs@18.0.2)(tslib@2.7.0)(typescript@5.5.3)(web-vitals@4.2.3): + /@guardian/core-web-vitals@7.0.0(@guardian/libs@19.0.0)(tslib@2.7.0)(typescript@5.5.3)(web-vitals@4.2.3): resolution: {integrity: sha512-1JLUQjkLY8SXYJqcy0TiE9/9hCcmyIlmMpRoW8Ygn/qGtyNxG+zzwkwsgtJIP+B0ZjtDqfukra2IV9l7wX5A0g==} peerDependencies: '@guardian/libs': ^18.0.0 @@ -1894,7 +1894,7 @@ packages: typescript: optional: true dependencies: - '@guardian/libs': 18.0.2(tslib@2.7.0)(typescript@5.5.3) + '@guardian/libs': 19.0.0(tslib@2.7.0)(typescript@5.5.3) tslib: 2.7.0 typescript: 5.5.3 web-vitals: 4.2.3 @@ -1940,7 +1940,7 @@ packages: - supports-color dev: true - /@guardian/identity-auth-frontend@5.0.0(@guardian/identity-auth@3.0.1)(@guardian/libs@18.0.2)(tslib@2.7.0)(typescript@5.5.3): + /@guardian/identity-auth-frontend@5.0.0(@guardian/identity-auth@3.0.1)(@guardian/libs@19.0.0)(tslib@2.7.0)(typescript@5.5.3): resolution: {integrity: sha512-Y5wIg8Zo92jl/ChZ4v/Ve3CGQWnoZSfkrhwSOIoZPm88YvijEASxHteVF04QOy/rhVs1O6AQJfl5Ilx244zS6w==} peerDependencies: '@guardian/identity-auth': ^3.0.0 @@ -1951,13 +1951,13 @@ packages: typescript: optional: true dependencies: - '@guardian/identity-auth': 3.0.1(@guardian/libs@18.0.2)(tslib@2.7.0)(typescript@5.5.3) - '@guardian/libs': 18.0.2(tslib@2.7.0)(typescript@5.5.3) + '@guardian/identity-auth': 3.0.1(@guardian/libs@19.0.0)(tslib@2.7.0)(typescript@5.5.3) + '@guardian/libs': 19.0.0(tslib@2.7.0)(typescript@5.5.3) tslib: 2.7.0 typescript: 5.5.3 dev: true - /@guardian/identity-auth@3.0.1(@guardian/libs@18.0.2)(tslib@2.7.0)(typescript@5.5.3): + /@guardian/identity-auth@3.0.1(@guardian/libs@19.0.0)(tslib@2.7.0)(typescript@5.5.3): resolution: {integrity: sha512-QwvQY6BmirP9Me2DkVkNCq0VQeNMzko9zG73G9l0XpQKk5+tTPUf31kyj06Uw11pWUfNacscjbfSU4rDqZOxCA==} peerDependencies: '@guardian/libs': ^18.0.0 @@ -1967,7 +1967,7 @@ packages: typescript: optional: true dependencies: - '@guardian/libs': 18.0.2(tslib@2.7.0)(typescript@5.5.3) + '@guardian/libs': 19.0.0(tslib@2.7.0)(typescript@5.5.3) tslib: 2.7.0 typescript: 5.5.3 dev: true @@ -1983,6 +1983,20 @@ packages: dependencies: tslib: 2.7.0 typescript: 5.5.3 + dev: false + + /@guardian/libs@19.0.0(tslib@2.7.0)(typescript@5.5.3): + resolution: {integrity: sha512-UEcVsjSAjkZzbHRrEtkPW/5ngOCNzVk/BtJBkAdX3enYjHLYLxNfuGZW/Z5W5K9Plf/7xH2Jtcker5tQq9R6sQ==} + peerDependencies: + tslib: ^2.6.2 + typescript: ~5.5.2 + peerDependenciesMeta: + typescript: + optional: true + dependencies: + tslib: 2.7.0 + typescript: 5.5.3 + dev: true /@guardian/prebid.js@8.52.0-4(babel-core@7.0.0-bridge.0)(tslib@2.7.0)(typescript@5.5.3): resolution: {integrity: sha512-rshCk2zJKdgCNMiqNy/d50SsZne/CFf8VQqQ3que5Hln5cxufOE5dEHIfmVKjqDeffKX5WEbsa3gVCtNx+sZaQ==} diff --git a/src/core/send-commercial-metrics.spec.ts b/src/core/send-commercial-metrics.spec.ts index 161cda385..165c607ed 100644 --- a/src/core/send-commercial-metrics.spec.ts +++ b/src/core/send-commercial-metrics.spec.ts @@ -1,4 +1,4 @@ -import type { ConsentState } from '@guardian/libs'; +import type { ConsentState, USNATConsentState } from '@guardian/libs'; import { onConsent } from '@guardian/libs'; import { EventTimer } from './event-timer'; import { @@ -103,16 +103,26 @@ const tcfv2AllConsentExceptPurpose8: ConsentState = { framework: 'tcfv2', }; -const ccpaConsent: ConsentState = { - ccpa: { doNotSell: false }, +const usnatNonConsentObject: USNATConsentState = { + doNotSell: true, + signalStatus: 'ready', +}; + +const usnatConsentObject: USNATConsentState = { + doNotSell: false, + signalStatus: 'ready', +}; + +const usnatConsent: ConsentState = { + usnat: usnatConsentObject, canTarget: true, - framework: 'ccpa', + framework: 'usnat', }; -const ccpaNonConsent: ConsentState = { - ccpa: { doNotSell: true }, +const usnatNonConsent: ConsentState = { + usnat: usnatNonConsentObject, canTarget: false, - framework: 'ccpa', + framework: 'usnat', }; const setVisibility = (value: 'hidden' | 'visible'): void => { @@ -212,7 +222,7 @@ describe('send commercial metrics', () => { }); it('sends metrics when non-TCFv2 user (i.e. USA or Australia) consents', async () => { - mockOnConsent(ccpaConsent); + mockOnConsent(usnatConsent); await initCommercialMetrics({ pageViewId: PAGE_VIEW_ID, @@ -231,7 +241,7 @@ describe('send commercial metrics', () => { }); it('sends metrics when non-TCFv2 user (i.e. USA or Australia) does not consent', async () => { - mockOnConsent(ccpaNonConsent); + mockOnConsent(usnatNonConsent); await initCommercialMetrics({ pageViewId: PAGE_VIEW_ID, diff --git a/src/core/targeting/build-page-targeting.spec.ts b/src/core/targeting/build-page-targeting.spec.ts index 6e40e380b..ff6445859 100644 --- a/src/core/targeting/build-page-targeting.spec.ts +++ b/src/core/targeting/build-page-targeting.spec.ts @@ -1,4 +1,8 @@ -import type { ConsentState, TCFv2ConsentState } from '@guardian/libs'; +import type { + ConsentState, + TCFv2ConsentState, + USNATConsentState, +} from '@guardian/libs'; import { cmp as cmp_, setCookie, storage } from '@guardian/libs'; import { getAuthStatus as getAuthStatus_ } from 'lib/identity/api'; import type { AuthStatus } from 'lib/identity/api'; @@ -52,17 +56,27 @@ const mockViewport = (width: number, height: number): void => { }); }; -// CCPA -const ccpaWithConsentMock: ConsentState = { - ccpa: { doNotSell: false }, +const usnatConsent: USNATConsentState = { + doNotSell: false, + signalStatus: 'ready', +}; + +const usnatNonConsent: USNATConsentState = { + doNotSell: true, + signalStatus: 'ready', +}; + +// USNAT +const usnatWithConsentMock: ConsentState = { + usnat: usnatConsent, canTarget: true, - framework: 'ccpa', + framework: 'usnat', }; -const ccpaWithoutConsentMock: ConsentState = { - ccpa: { doNotSell: true }, +const usnatWithoutConsentMock: ConsentState = { + usnat: usnatNonConsent, canTarget: false, - framework: 'ccpa', + framework: 'usnat', }; // AUS @@ -258,7 +272,7 @@ describe('Build Page Targeting', () => { buildPageTargeting({ adFree: false, clientSideParticipations: {}, - consentState: ccpaWithConsentMock, + consentState: usnatWithConsentMock, isSignedIn: true, }).pa, ).toBe('t'); @@ -266,7 +280,7 @@ describe('Build Page Targeting', () => { buildPageTargeting({ adFree: false, clientSideParticipations: {}, - consentState: ccpaWithoutConsentMock, + consentState: usnatWithoutConsentMock, isSignedIn: true, }).pa, ).toBe('f'); @@ -293,7 +307,7 @@ describe('Build Page Targeting', () => { buildPageTargeting({ adFree: false, clientSideParticipations: {}, - consentState: ccpaWithConsentMock, + consentState: usnatWithConsentMock, isSignedIn: true, }).rdp, ).toBe('f'); @@ -301,7 +315,7 @@ describe('Build Page Targeting', () => { buildPageTargeting({ adFree: false, clientSideParticipations: {}, - consentState: ccpaWithoutConsentMock, + consentState: usnatWithoutConsentMock, isSignedIn: true, }).rdp, ).toBe('t'); @@ -598,7 +612,7 @@ describe('Build Page Targeting', () => { buildPageTargeting({ adFree: false, clientSideParticipations: {}, - consentState: ccpaWithConsentMock, + consentState: usnatWithConsentMock, isSignedIn: true, }).permutive, ).toEqual(['1', '2', '3']); @@ -612,7 +626,7 @@ describe('Build Page Targeting', () => { buildPageTargeting({ adFree: false, clientSideParticipations: {}, - consentState: ccpaWithConsentMock, + consentState: usnatWithConsentMock, isSignedIn: true, }).fr, ).toEqual('5'); @@ -624,7 +638,7 @@ describe('Build Page Targeting', () => { buildPageTargeting({ adFree: false, clientSideParticipations: {}, - consentState: ccpaWithConsentMock, + consentState: usnatWithConsentMock, isSignedIn: true, }).fr, ).toEqual('16-19'); @@ -636,7 +650,7 @@ describe('Build Page Targeting', () => { buildPageTargeting({ adFree: false, clientSideParticipations: {}, - consentState: ccpaWithConsentMock, + consentState: usnatWithConsentMock, isSignedIn: true, }).fr, ).toEqual('30plus'); @@ -648,7 +662,7 @@ describe('Build Page Targeting', () => { buildPageTargeting({ adFree: false, clientSideParticipations: {}, - consentState: ccpaWithConsentMock, + consentState: usnatWithConsentMock, isSignedIn: true, }).fr, ).toEqual('0'); @@ -660,7 +674,7 @@ describe('Build Page Targeting', () => { buildPageTargeting({ adFree: false, clientSideParticipations: {}, - consentState: ccpaWithConsentMock, + consentState: usnatWithConsentMock, isSignedIn: true, }).fr, ).toEqual('0'); @@ -672,7 +686,7 @@ describe('Build Page Targeting', () => { buildPageTargeting({ adFree: false, clientSideParticipations: {}, - consentState: ccpaWithoutConsentMock, + consentState: usnatWithoutConsentMock, isSignedIn: true, }).fr, ).toEqual('0'); @@ -902,8 +916,8 @@ describe('Build Page Targeting', () => { }); it.each([ - [ccpaWithConsentMock, '9'], - [ccpaWithoutConsentMock, '9'], + [usnatWithConsentMock, '9'], + [usnatWithoutConsentMock, '9'], [ausWithConsentMock, '9'], [ausWithoutConsentMock, '9'], @@ -1126,7 +1140,7 @@ describe('Build Page Targeting', () => { buildPageTargeting({ adFree: false, clientSideParticipations: {}, - consentState: ccpaWithConsentMock, + consentState: usnatWithConsentMock, isSignedIn: true, }).firstvisit, ).toBeUndefined(); diff --git a/src/core/targeting/personalised.spec.ts b/src/core/targeting/personalised.spec.ts index 71f2014ed..c532175d4 100644 --- a/src/core/targeting/personalised.spec.ts +++ b/src/core/targeting/personalised.spec.ts @@ -1,4 +1,4 @@ -import type { ConsentState } from '@guardian/libs'; +import type { ConsentState, USNATConsentState } from '@guardian/libs'; import { storage } from '@guardian/libs'; import type { PersonalisedTargeting } from './personalised'; import { getPersonalisedTargeting } from './personalised'; @@ -6,6 +6,16 @@ import { getPersonalisedTargeting } from './personalised'; const FREQUENCY_KEY = 'gu.alreadyVisited'; const AMTGRP_STORAGE_KEY = 'gu.adManagerGroup'; +const usnatConsent: USNATConsentState = { + doNotSell: false, + signalStatus: 'ready', +}; + +const usnatNonConsent: USNATConsentState = { + doNotSell: true, + signalStatus: 'ready', +}; + describe('Personalised targeting', () => { describe('TCFv2', () => { test('Full consent', () => { @@ -72,12 +82,12 @@ describe('Personalised targeting', () => { }); }); - describe('CCPA', () => { + describe('USNAT', () => { it('Full Consent', () => { const state: ConsentState = { - ccpa: { doNotSell: false }, + usnat: usnatConsent, canTarget: true, - framework: 'ccpa', + framework: 'usnat', }; storage.local.setRaw(FREQUENCY_KEY, '4'); @@ -97,9 +107,9 @@ describe('Personalised targeting', () => { it('Do Not Sell', () => { const state: ConsentState = { - ccpa: { doNotSell: true }, + usnat: usnatNonConsent, canTarget: false, - framework: 'ccpa', + framework: 'usnat', }; storage.local.setRaw(FREQUENCY_KEY, '4'); @@ -191,9 +201,9 @@ describe('Personalised targeting', () => { ]; test.each(frequencies)('Should get `%s` for %f', (fr, val) => { const state: ConsentState = { - ccpa: { doNotSell: false }, + usnat: usnatConsent, canTarget: true, - framework: 'ccpa', + framework: 'usnat', }; storage.local.setRaw(FREQUENCY_KEY, String(val)); @@ -232,9 +242,9 @@ describe('Personalised targeting', () => { test.each(groups)('Should get `%s` if it exists', (amtgrp, val) => { const state: ConsentState = { - ccpa: { doNotSell: false }, + usnat: usnatConsent, canTarget: true, - framework: 'ccpa', + framework: 'usnat', }; storage.local.remove(AMTGRP_STORAGE_KEY); @@ -277,11 +287,11 @@ describe('Personalised targeting', () => { expect(storage.local.get(AMTGRP_STORAGE_KEY)).toBeNull(); }); - test('Ad manager group IS set if ccpa and consent not given', () => { + test('Ad manager group IS set if usnat and consent not given', () => { const state: ConsentState = { - ccpa: { doNotSell: true }, + usnat: usnatNonConsent, canTarget: false, - framework: 'ccpa', + framework: 'usnat', }; const targeting = getPersonalisedTargeting({ @@ -311,9 +321,9 @@ describe('Personalised targeting', () => { test('Should set `permutive` to correct values if `youtube` is set to false', () => { const state: ConsentState = { - ccpa: { doNotSell: false }, + usnat: usnatConsent, canTarget: true, - framework: 'ccpa', + framework: 'usnat', }; storage.local.setRaw(PERMUTIVE_KEY, '[1, 2, 3]'); @@ -328,9 +338,9 @@ describe('Personalised targeting', () => { test('Should set `permutive` to correct values if `youtube` is set to true', () => { const state: ConsentState = { - ccpa: { doNotSell: false }, + usnat: usnatConsent, canTarget: true, - framework: 'ccpa', + framework: 'usnat', }; storage.local.setRaw(PERMUTIVE_KEY, '[]'); diff --git a/src/core/targeting/personalised.ts b/src/core/targeting/personalised.ts index 07b4afe4f..810196151 100644 --- a/src/core/targeting/personalised.ts +++ b/src/core/targeting/personalised.ts @@ -125,8 +125,8 @@ const getRawWithConsent = (key: string, state: ConsentState): string | null => { if (state.tcfv2) { if (state.tcfv2.consents['1']) return storage.local.getRaw(key); } - if (state.ccpa) { - if (!state.ccpa.doNotSell) return storage.local.getRaw(key); + if (state.usnat) { + if (!state.usnat.doNotSell) return storage.local.getRaw(key); } if (state.aus) { if (state.aus.personalisedAdvertising) return storage.local.getRaw(key); @@ -175,7 +175,7 @@ const getCMPTargeting = (state: ConsentState): CMPTargeting => { }; } - if (state.ccpa) { + if (state.usnat) { return { consent_tcfv2: 'na', rdp: !state.canTarget ? 't' : 'f', diff --git a/src/core/track-gpc-signal.spec.ts b/src/core/track-gpc-signal.spec.ts index 737c9c762..827f1b1ce 100644 --- a/src/core/track-gpc-signal.spec.ts +++ b/src/core/track-gpc-signal.spec.ts @@ -1,17 +1,19 @@ -import type { ConsentState } from '@guardian/libs'; +import type { ConsentState, USNATConsentState } from '@guardian/libs'; import { EventTimer } from './event-timer'; import { initTrackGpcSignal } from './track-gpc-signal'; describe('initTrackGpcSignal', () => { + const usnatConsent: USNATConsentState = { + doNotSell: false, + signalStatus: 'ready', + }; test('tracks an undefined gpcSignal on ConsentState', () => { const eventTimer = EventTimer.get(); const consentState: ConsentState = { - ccpa: { - doNotSell: false, - }, + usnat: usnatConsent, canTarget: true, - framework: 'ccpa', + framework: 'usnat', }; initTrackGpcSignal(consentState); @@ -23,11 +25,9 @@ describe('initTrackGpcSignal', () => { const eventTimer = EventTimer.get(); const consentState: ConsentState = { - ccpa: { - doNotSell: false, - }, + usnat: usnatConsent, canTarget: true, - framework: 'ccpa', + framework: 'usnat', gpcSignal: false, }; @@ -40,11 +40,9 @@ describe('initTrackGpcSignal', () => { const eventTimer = EventTimer.get(); const consentState: ConsentState = { - ccpa: { - doNotSell: false, - }, + usnat: usnatConsent, canTarget: true, - framework: 'ccpa', + framework: 'usnat', gpcSignal: true, }; diff --git a/src/core/types.ts b/src/core/types.ts index 8f7af4a67..8a7a19d3b 100644 --- a/src/core/types.ts +++ b/src/core/types.ts @@ -88,7 +88,7 @@ export type AdsConfigBasic = { }; }; -export type AdsConfigCCPAorAus = AdsConfigBasic & { +export type AdsConfigUSNATorAus = AdsConfigBasic & { restrictedDataProcessor: boolean; }; @@ -103,7 +103,7 @@ export type AdsConfigTCFV2 = AdsConfigBasic & { export type AdsConfigEnabled = | AdsConfigBasic - | AdsConfigCCPAorAus + | AdsConfigUSNATorAus | AdsConfigTCFV2; export type AdsConfig = AdsConfigEnabled | AdsConfigDisabled; diff --git a/src/init/consented/comscore.spec.ts b/src/init/consented/comscore.spec.ts index 37807be0e..8f98a2bdf 100644 --- a/src/init/consented/comscore.spec.ts +++ b/src/init/consented/comscore.spec.ts @@ -1,4 +1,8 @@ -import type { ConsentState, TCFv2ConsentState } from '@guardian/libs'; +import type { + ConsentState, + TCFv2ConsentState, + USNATConsentState, +} from '@guardian/libs'; import { getConsentFor, loadScript, onConsent } from '@guardian/libs'; import { commercialFeatures } from 'lib/commercial-features'; import { _ } from './comscore'; @@ -16,6 +20,16 @@ const defaultTCFv2State = { tcString: 'YAAA', } as TCFv2ConsentState; +const usnatConsent: USNATConsentState = { + doNotSell: false, + signalStatus: 'ready', +}; + +const usnatNonConsent: USNATConsentState = { + doNotSell: true, + signalStatus: 'ready', +}; + const tcfv2WithConsent = { tcfv2: { ...defaultTCFv2State, @@ -38,20 +52,16 @@ const tcfv2WithoutConsent = { framework: 'tcfv2', } as ConsentState; -const ccpaWithConsent = { - ccpa: { - doNotSell: false, - }, +const usnatWithConsent = { + usnat: usnatConsent, canTarget: true, - framework: 'ccpa', + framework: 'usnat', } as ConsentState; -const ccpaWithoutConsent = { - ccpa: { - doNotSell: true, - }, +const usnatWithoutConsent = { + usnat: usnatNonConsent, canTarget: false, - framework: 'ccpa', + framework: 'usnat', } as ConsentState; const AusWithoutConsent = { @@ -121,14 +131,14 @@ describe('setupComscore', () => { await setupComscore(); expect(loadScript).not.toBeCalled(); }); - it('CCPA with consent: runs', async () => { - mockOnConsent(ccpaWithConsent); + it('USNAT with consent: runs', async () => { + mockOnConsent(usnatWithConsent); await setupComscore(); expect(loadScript).toBeCalled(); }); - it('CCPA without consent: does not run', async () => { - mockOnConsent(ccpaWithoutConsent); + it('USNAT without consent: does not run', async () => { + mockOnConsent(usnatWithoutConsent); await setupComscore(); expect(loadScript).not.toBeCalled(); }); diff --git a/src/init/consented/comscore.ts b/src/init/consented/comscore.ts index 1b9c96226..bc558b432 100644 --- a/src/init/consented/comscore.ts +++ b/src/init/consented/comscore.ts @@ -44,16 +44,17 @@ const setupComscore = async (): Promise => { /* Rule is that comscore can run: - in Tcfv2: Based on consent for comscore - in Australia: Always - - in CCPA: If the user hasn't chosen Do Not Sell + - in USNAT: If the user hasn't chosen Do Not Sell TODO move this logic to getConsentFor */ const canRunTcfv2 = (consentState.tcfv2 && getConsentFor('comscore', consentState)) ?? false; const canRunAus = !!consentState.aus; - const canRunCcpa = !!consentState.ccpa && !consentState.ccpa.doNotSell; + const canRunUsnat = + !!consentState.usnat && !consentState.usnat.doNotSell; - if (!(canRunTcfv2 || canRunAus || canRunCcpa)) { + if (!(canRunTcfv2 || canRunAus || canRunUsnat)) { throw Error('No consent for comscore'); } await initOnConsent(); diff --git a/src/init/consented/prepare-googletag.spec.ts b/src/init/consented/prepare-googletag.spec.ts index 648b14764..ab848350b 100644 --- a/src/init/consented/prepare-googletag.spec.ts +++ b/src/init/consented/prepare-googletag.spec.ts @@ -1,4 +1,4 @@ -import type { ConsentState } from '@guardian/libs'; +import type { ConsentState, USNATConsentState } from '@guardian/libs'; import { getConsentFor, loadScript, onConsent } from '@guardian/libs'; import type * as AdSizesType from 'core/ad-sizes'; import { commercialFeatures } from 'lib/commercial-features'; @@ -50,6 +50,16 @@ const getCurrentBreakpoint = getCurrentBreakpoint_ as jest.MockedFunction< typeof getCurrentBreakpoint_ >; +const usnatConsent: USNATConsentState = { + doNotSell: false, + signalStatus: 'ready', +}; + +const usnatNonConsent: USNATConsentState = { + doNotSell: true, + signalStatus: 'ready', +}; + jest.mock('define/init-slot-ias', () => ({ initSlotIas: jest.fn(() => Promise.resolve()), })); @@ -213,16 +223,16 @@ const ausRejected: ConsentState = { framework: 'aus', }; -const ccpaWithConsent: ConsentState = { - ccpa: { doNotSell: false }, +const usnatWithConsent: ConsentState = { + usnat: usnatConsent, canTarget: true, - framework: 'ccpa', + framework: 'usnat', }; -const ccpaWithoutConsent: ConsentState = { - ccpa: { doNotSell: true }, +const usnatWithoutConsent: ConsentState = { + usnat: usnatNonConsent, canTarget: false, - framework: 'ccpa', + framework: 'usnat', }; describe('DFP', () => { @@ -582,17 +592,17 @@ describe('DFP', () => { }); }); }); - describe('restrictDataProcessing flag in CCPA', () => { - it('when CCPA consent is given', async () => { - mockOnConsent(ccpaWithConsent); + describe('restrictDataProcessing flag in USNAT', () => { + it('when USNAT consent is given', async () => { + mockOnConsent(usnatWithConsent); mockGetConsentFor(true); await prepareGoogletag(); expect(pubAds.setPrivacySettings).toHaveBeenCalledWith({ restrictDataProcessing: false, }); }); - it('when CCPA consent is denied', async () => { - mockOnConsent(ccpaWithoutConsent); + it('when USNAT consent is denied', async () => { + mockOnConsent(usnatWithoutConsent); mockGetConsentFor(false); await prepareGoogletag(); expect(pubAds.setPrivacySettings).toHaveBeenCalledWith({ diff --git a/src/init/consented/prepare-googletag.ts b/src/init/consented/prepare-googletag.ts index f30992a33..2717c768b 100644 --- a/src/init/consented/prepare-googletag.ts +++ b/src/init/consented/prepare-googletag.ts @@ -51,8 +51,8 @@ export const init = (): Promise => { window.googletag.cmd.push(setCookieDeprecationLabel); } - if (consentState.ccpa) { - // CCPA mode + if (consentState.usnat) { + // USNAT mode // canRun stays true, set RDP flag window.googletag.cmd.push(() => { window.googletag.pubads().setPrivacySettings({ diff --git a/src/init/consented/prepare-prebid.spec.ts b/src/init/consented/prepare-prebid.spec.ts index 8c5220b9f..d267b2c7e 100644 --- a/src/init/consented/prepare-prebid.spec.ts +++ b/src/init/consented/prepare-prebid.spec.ts @@ -1,4 +1,8 @@ -import type { ConsentState, TCFv2ConsentState } from '@guardian/libs'; +import type { + ConsentState, + TCFv2ConsentState, + USNATConsentState, +} from '@guardian/libs'; import { getConsentFor, log, onConsent } from '@guardian/libs'; import { commercialFeatures } from 'lib/commercial-features'; import { isInCanada } from 'utils/geo-utils'; @@ -91,16 +95,26 @@ const tcfv2WithoutConsent = { framework: 'tcfv2', } as ConsentState; -const ccpaWithConsent = { - ccpa: { doNotSell: false }, +const usnatConsent: USNATConsentState = { + doNotSell: false, + signalStatus: 'ready', +}; + +const usnatNonConsent: USNATConsentState = { + doNotSell: true, + signalStatus: 'ready', +}; + +const usnatWithConsent = { + usnat: usnatConsent, canTarget: true, - framework: 'ccpa', + framework: 'usnat', } as ConsentState; -const ccpaWithoutConsent = { - ccpa: { doNotSell: true }, +const usnatWithoutConsent = { + usnat: usnatNonConsent, canTarget: false, - framework: 'ccpa', + framework: 'usnat', } as ConsentState; const ausWithConsent = { @@ -306,7 +320,7 @@ describe('init', () => { expect(prebid.initialise).not.toBeCalled(); }); - it('should initialise Prebid in CCPA if doNotSell is false', async () => { + it('should initialise Prebid in USNAT if doNotSell is false', async () => { expect.hasAssertions(); window.guardian.config.switches = { @@ -314,13 +328,13 @@ describe('init', () => { }; commercialFeatures.shouldLoadGoogletag = true; commercialFeatures.adFree = false; - mockOnConsent(ccpaWithConsent); + mockOnConsent(usnatWithConsent); mockGetConsentFor(true); await setupPrebid(); expect(prebid.initialise).toBeCalled(); }); - it('should not initialise Prebid in CCPA if doNotSell is true', async () => { + it('should not initialise Prebid in USNAT if doNotSell is true', async () => { expect.assertions(2); window.guardian.config.switches = { @@ -328,7 +342,7 @@ describe('init', () => { }; commercialFeatures.shouldLoadGoogletag = true; commercialFeatures.adFree = false; - mockOnConsent(ccpaWithoutConsent); + mockOnConsent(usnatWithoutConsent); mockGetConsentFor(false); await setupPrebid(); diff --git a/src/init/consented/third-party-tags.spec.ts b/src/init/consented/third-party-tags.spec.ts index 06161b714..f425797dd 100644 --- a/src/init/consented/third-party-tags.spec.ts +++ b/src/init/consented/third-party-tags.spec.ts @@ -1,5 +1,5 @@ import { getConsentFor, onConsent } from '@guardian/libs'; -import type { ConsentState } from '@guardian/libs'; +import type { ConsentState, USNATConsentState } from '@guardian/libs'; import { commercialFeatures } from 'lib/commercial-features'; import type { ThirdPartyTag } from 'types/global'; import { _, init } from './third-party-tags'; @@ -85,6 +85,11 @@ const tcfv2WithoutConsent = { framework: 'tcfv2', } as ConsentState; +const usnatConsent: USNATConsentState = { + doNotSell: false, + signalStatus: 'ready', +}; + beforeEach(() => { const firstScript = document.createElement('script'); document.body.appendChild(firstScript); @@ -194,11 +199,11 @@ describe('third party tags', () => { ); expect(document.scripts.length).toBe(2); }); - it('should add scripts to the document when CCPA consent has been given', async () => { + it('should add scripts to the document when USNAT consent has been given', async () => { mockOnConsent({ - ccpa: { doNotSell: false }, + usnat: usnatConsent, canTarget: true, - framework: 'ccpa', + framework: 'usnat', }); mockGetConsentFor(true); await insertScripts( @@ -207,11 +212,10 @@ describe('third party tags', () => { ); expect(document.scripts.length).toBe(3); }); - it('should only add performance scripts to the document when CCPA consent has not been given', async () => { + it('should only add performance scripts to the document when USNAT consent has not been given', async () => { mockOnConsent({ - ccpa: { doNotSell: true }, canTarget: false, - framework: 'ccpa', + framework: 'usnat', }); mockGetConsentFor(false); await insertScripts( diff --git a/src/lib/header-bidding/a9/a9.spec.ts b/src/lib/header-bidding/a9/a9.spec.ts index d2f6f2da3..dc62b1548 100644 --- a/src/lib/header-bidding/a9/a9.spec.ts +++ b/src/lib/header-bidding/a9/a9.spec.ts @@ -1,5 +1,8 @@ import { getConsentFor, onConsentChange } from '@guardian/libs'; -import type { OnConsentChangeCallback } from '@guardian/libs'; +import type { + OnConsentChangeCallback, + USNATConsentState, +} from '@guardian/libs'; import { _, a9 } from './a9'; const tcfv2WithConsentMock = (callback: OnConsentChangeCallback) => @@ -27,11 +30,16 @@ const tcfv2WithConsentMock = (callback: OnConsentChangeCallback) => framework: 'tcfv2', }); -const CcpaWithConsentMock = (callback: OnConsentChangeCallback) => +const usnatConsent: USNATConsentState = { + doNotSell: false, + signalStatus: 'ready', +}; + +const usnatWithConsentMock = (callback: OnConsentChangeCallback) => callback({ - ccpa: { doNotSell: false }, + usnat: usnatConsent, canTarget: true, - framework: 'ccpa', + framework: 'usnat', }); jest.mock('define/Advert', () => @@ -88,8 +96,8 @@ describe('initialise', () => { expect(window.apstag?.init).toHaveBeenCalled(); }); - it('should generate initialise A9 library when CCPA consent has been given', () => { - mockOnConsentChange(CcpaWithConsentMock); + it('should generate initialise A9 library when USNAT consent has been given', () => { + mockOnConsentChange(usnatWithConsentMock); mockGetConsentFor(true); a9.initialise(); expect(window.apstag).toBeDefined(); diff --git a/src/lib/header-bidding/prebid/prebid.spec.ts b/src/lib/header-bidding/prebid/prebid.spec.ts index c2429c5c7..36aed4a56 100644 --- a/src/lib/header-bidding/prebid/prebid.spec.ts +++ b/src/lib/header-bidding/prebid/prebid.spec.ts @@ -112,10 +112,10 @@ describe('initialise', () => { }); }); - test('should generate correct Prebid config consent management in CCPA', () => { - prebid.initialise(window, 'ccpa'); + test('should generate correct Prebid config consent management in USNAT', () => { + prebid.initialise(window, 'usnat'); expect(window.pbjs?.getConfig('consentManagement')).toEqual({ - usp: { + gpp: { cmpApi: 'iab', timeout: 1500, }, diff --git a/src/lib/header-bidding/prebid/prebid.ts b/src/lib/header-bidding/prebid/prebid.ts index 6908f4ce7..2cecdca2d 100644 --- a/src/lib/header-bidding/prebid/prebid.ts +++ b/src/lib/header-bidding/prebid/prebid.ts @@ -53,6 +53,9 @@ type ConsentManagement = } | { usp: USPConfig; + } + | { + gpp: USPConfig; }; type UserId = { @@ -288,7 +291,6 @@ const initialise = ( const consentManagement = (): ConsentManagement => { switch (framework) { case 'aus': - case 'ccpa': // https://docs.prebid.org/dev-docs/modules/consentManagementUsp.html return { usp: { @@ -296,6 +298,14 @@ const initialise = ( timeout: 1500, }, }; + case 'usnat': + // https://docs.prebid.org/dev-docs/modules/consentManagementGpp.html + return { + gpp: { + cmpApi: 'iab', + timeout: 1500, + }, + }; case 'tcfv2': default: // https://docs.prebid.org/dev-docs/modules/consentManagement.html