diff --git a/clients/fides-js/__tests__/integrations/blueconic.test.ts b/clients/fides-js/__tests__/integrations/blueconic.test.ts new file mode 100644 index 0000000000..b1813e5381 --- /dev/null +++ b/clients/fides-js/__tests__/integrations/blueconic.test.ts @@ -0,0 +1,107 @@ +import { FidesGlobal } from "~/fides"; +import { MARKETING_CONSENT_KEYS } from "~/lib/consent-constants"; + +import { blueconic } from "../../src/integrations/blueconic"; + +const getBlueConicEvent = () => + ({ + subscribe: () => {}, + }) as const; + +const setupBlueConicClient = ( + initialized: "initialized" | "uinitialized" = "initialized", +) => { + const client = { + profile: { + setConsentedObjectives: jest.fn(), + setRefusedObjectives: jest.fn(), + updateProfile: jest.fn(), + }, + event: initialized === "initialized" ? getBlueConicEvent() : undefined, + } as const satisfies typeof window.blueConicClient; + + window.blueConicClient = client; + + return client; +}; + +const setupFidesWithConsent = (key: string, optInStatus: boolean) => { + window.Fides = { + consent: { + [key]: optInStatus, + }, + } as any as FidesGlobal; +}; + +describe("blueconic", () => { + afterEach(() => { + window.blueConicClient = undefined; + window.Fides = undefined as any; + jest.resetAllMocks(); + }); + + test("that other modes are not supported", () => { + expect(() => blueconic({ approach: "other mode" })).toThrow(); + }); + + test("that nothing happens when blueconic and fides are not initialized", () => { + setupBlueConicClient("uinitialized"); + + blueconic(); + + expect( + window.blueConicClient?.profile?.setConsentedObjectives, + ).not.toHaveBeenCalled(); + expect( + window.blueConicClient?.profile?.setConsentedObjectives, + ).not.toHaveBeenCalled(); + expect( + window.blueConicClient?.profile?.updateProfile, + ).not.toHaveBeenCalled(); + }); + + describe.each(MARKETING_CONSENT_KEYS)( + "when consent is set via the %s key", + (key) => { + test.each([ + [ + "opted in", + true, + ["iab_purpose_1", "iab_purpose_2", "iab_purpose_3", "iab_purpose_4"], + [], + ], + [ + "opted out", + false, + ["iab_purpose_1"], + ["iab_purpose_2", "iab_purpose_3", "iab_purpose_4"], + ], + ])( + "that a user who has %s gets the correct consented and refused objectives", + (_, optInStatus, consented, refused) => { + const blueConicClient = setupBlueConicClient(); + setupFidesWithConsent(key, optInStatus); + + blueconic(); + + expect( + blueConicClient.profile.setConsentedObjectives, + ).toHaveBeenCalledWith(consented); + expect( + blueConicClient.profile.setRefusedObjectives, + ).toHaveBeenCalledWith(refused); + expect(blueConicClient.profile.updateProfile).toHaveBeenCalled(); + }, + ); + }, + ); + + test.each(["FidesInitialized", "FidesUpdated", "onBlueConicLoaded"])( + "that %s event can cause objectives to be set", + (eventName) => { + const spy = jest.spyOn(window, "addEventListener"); + blueconic(); + expect(spy).toHaveBeenCalledWith(eventName, expect.any(Function)); + }, + ); +}); diff --git a/clients/fides-js/src/integrations/blueconic.ts b/clients/fides-js/src/integrations/blueconic.ts index 5bfcf07dba..9d735d2da5 100644 --- a/clients/fides-js/src/integrations/blueconic.ts +++ b/clients/fides-js/src/integrations/blueconic.ts @@ -1,12 +1,14 @@ +import { MARKETING_CONSENT_KEYS } from "~/lib/consent-constants"; + declare global { interface Window { blueConicClient?: { - profile: { + profile?: { setConsentedObjectives: (objectives: string[]) => void; setRefusedObjectives: (objectives: string[]) => void; updateProfile: () => void; }; - event: { + event?: { subscribe: any; }; }; @@ -26,7 +28,8 @@ const configureObjectives = () => { const profile = window.blueConicClient?.profile; const { consent } = window.Fides; - if (consent.targeted_advertising_gpp_us_state) { + const optedIn = MARKETING_CONSENT_KEYS.some((key) => consent[key]); + if (optedIn) { profile.setConsentedObjectives([ "iab_purpose_1", "iab_purpose_2", @@ -46,7 +49,13 @@ const configureObjectives = () => { profile.updateProfile(); }; -export const blueconic = () => { +export const blueconic = ( + { approach }: { approach: string } = { approach: "onetrust" }, +) => { + if (approach !== "onetrust") { + throw new Error("Unsupported approach"); + } + window.addEventListener("FidesInitialized", configureObjectives); window.addEventListener("FidesUpdated", configureObjectives); window.addEventListener("onBlueConicLoaded", configureObjectives); diff --git a/clients/fides-js/src/integrations/shopify.ts b/clients/fides-js/src/integrations/shopify.ts index 9d9f992e15..260e616fd9 100644 --- a/clients/fides-js/src/integrations/shopify.ts +++ b/clients/fides-js/src/integrations/shopify.ts @@ -1,3 +1,4 @@ +import { MARKETING_CONSENT_KEYS } from "../lib/consent-constants"; import { NoticeConsent } from "../lib/consent-types"; declare global { @@ -18,14 +19,7 @@ declare global { } const CONSENT_MAP = { - marketing: [ - "marketing", - "data_sales_and_sharing", - "data_sales_sharing_gpp_us_state", - "data_sharing_gpp_us_state", - "data_sales_gpp_us_state", - "targeted_advertising_gpp_us_state", - ], + marketing: MARKETING_CONSENT_KEYS, sale_of_data: [ "marketing", "data_sales_and_sharing", diff --git a/clients/fides-js/src/lib/consent-constants.ts b/clients/fides-js/src/lib/consent-constants.ts index 7f8b1ea8b3..0b8a76f0e5 100644 --- a/clients/fides-js/src/lib/consent-constants.ts +++ b/clients/fides-js/src/lib/consent-constants.ts @@ -125,3 +125,12 @@ export const FIDES_OVERRIDE_EXPERIENCE_LANGUAGE_VALIDATOR_MAP: { export const FIDES_OVERLAY_WRAPPER = "fides-overlay-wrapper"; export const FIDES_I18N_ICON = "fides-i18n-icon"; + +export const MARKETING_CONSENT_KEYS = [ + "marketing", + "data_sales_and_sharing", + "data_sales_sharing_gpp_us_state", + "data_sharing_gpp_us_state", + "data_sales_gpp_us_state", + "targeted_advertising_gpp_us_state", +];