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

Remove duplicate FidesUpdated event and trigger FidesInitialized twice instead #4365

Merged
merged 5 commits into from
Oct 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ The types of changes are:
### Changed
- Determine if the TCF overlay needs to surface based on backend calculated version hash [#4356](https://github.com/ethyca/fides/pull/4356)
- Refactor Fides.js embedded modal to not use A11y dialog [#4355](https://github.com/ethyca/fides/pull/4355)
- Only call `FidesUpdated` when a preference has been saved, not during initialization [#4365](https://github.com/ethyca/fides/pull/4365)

### Fixed
- Bug where vendor opt-ins would not initialize properly based on a `fides_string` in the TCF overlay [#4368](https://github.com/ethyca/fides/pull/4368)
Expand Down
12 changes: 2 additions & 10 deletions clients/fides-js/src/fides-tcf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@ import {
experienceIsValid,
FidesCookie,
hasSavedTcfPreferences,
isNewFidesCookie,
isPrivacyExperience,
tcfConsentCookieObjHasSomeConsentSet,
transformTcfPreferencesToCookieKeys,
Expand Down Expand Up @@ -213,7 +212,6 @@ const init = async (config: FidesConfig) => {
if (initialFides) {
Object.assign(_Fides, initialFides);
dispatchFidesEvent("FidesInitialized", cookie, config.options.debug);
dispatchFidesEvent("FidesUpdated", cookie, config.options.debug);
}
const experience = initialFides?.experience ?? config.experience;
const updatedFides = await initialize({
Expand All @@ -225,14 +223,8 @@ const init = async (config: FidesConfig) => {
});
Object.assign(_Fides, updatedFides);

// Dispatch the "FidesInitialized" event to update listeners with the initial
// state. Skip if we already initialized due to an existing cookie.
// For convenience, also dispatch the "FidesUpdated" event; this allows
// listeners to ignore the initialization event if they prefer
if (isNewFidesCookie(cookie)) {
dispatchFidesEvent("FidesInitialized", cookie, config.options.debug);
}
dispatchFidesEvent("FidesUpdated", cookie, config.options.debug);
// Dispatch the "FidesInitialized" event to update listeners with the initial state.
dispatchFidesEvent("FidesInitialized", cookie, config.options.debug);
};

// The global Fides object; this is bound to window.Fides if available
Expand Down
17 changes: 3 additions & 14 deletions clients/fides-js/src/fides.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,7 @@ import { gtm } from "./integrations/gtm";
import { meta } from "./integrations/meta";
import { shopify } from "./integrations/shopify";

import {
FidesCookie,
buildCookieConsentForExperiences,
isNewFidesCookie,
} from "./lib/cookie";
import { FidesCookie, buildCookieConsentForExperiences } from "./lib/cookie";
import {
FidesConfig,
FidesOptionOverrides,
Expand Down Expand Up @@ -114,7 +110,6 @@ const init = async (config: FidesConfig) => {
if (initialFides) {
Object.assign(_Fides, initialFides);
dispatchFidesEvent("FidesInitialized", cookie, config.options.debug);
dispatchFidesEvent("FidesUpdated", cookie, config.options.debug);
}
const experience = initialFides?.experience ?? config.experience;
const updatedFides = await initialize({
Expand All @@ -126,14 +121,8 @@ const init = async (config: FidesConfig) => {
});
Object.assign(_Fides, updatedFides);

// Dispatch the "FidesInitialized" event to update listeners with the initial
// state. Skip if we already initialized due to an existing cookie.
// For convenience, also dispatch the "FidesUpdated" event; this allows
// listeners to ignore the initialization event if they prefer
if (isNewFidesCookie(cookie)) {
dispatchFidesEvent("FidesInitialized", cookie, config.options.debug);
}
dispatchFidesEvent("FidesUpdated", cookie, config.options.debug);
// Dispatch the "FidesInitialized" event to update listeners with the initial state.
dispatchFidesEvent("FidesInitialized", cookie, config.options.debug);
};

// The global Fides object; this is bound to window.Fides if available
Expand Down
6 changes: 4 additions & 2 deletions clients/fides-js/src/lib/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { debugLog } from "./consent-utils";
/**
* Defines the available event names:
* - FidesInitialized: dispatched when initialization is complete, from Fides.init()
* - FidesUpdated: dispatched when preferences are updated, from updateConsentPreferences() or Fides.init()
* - FidesUpdated: dispatched when preferences are updated from updateConsentPreferences()
* - FidesUIShown: dispatched when either the banner or modal is shown to the user
* - FidesUIChanged: dispatched when preferences are changed but not saved, i.e. "dirty".
* - FidesModalClosed: dispatched when the modal is closed
Expand Down Expand Up @@ -45,11 +45,13 @@ export type FidesEvent = CustomEvent<FidesEventDetail>;
*
* Example usage:
* ```
* window.addEventListener("FidesInitialized", (evt) => console.log("Fides.consent initialized:", evt.detail.consent));
* window.addEventListener("FidesUpdated", (evt) => console.log("Fides.consent updated:", evt.detail.consent));
* ```
*
* The snippet above will print a console log whenever consent preferences are initialized/updated, like:
* The snippet above will print a console log whenever consent preferences are updated, like:
* ```
* Fides.consent initialized: { data_sales_and_sharing: false }
* Fides.consent updated: { data_sales_and_sharing: true }
* ```
*/
Expand Down
17 changes: 8 additions & 9 deletions clients/privacy-center/cypress/e2e/consent-banner-tcf.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ describe("Fides-js TCF", () => {
cy.getByTestId("consent-modal").within(() => {
cy.get("button").contains("Opt in to all").click();
});
cy.get("@FidesUpdated").should("have.been.calledTwice");
cy.get("@FidesUpdated").should("have.been.calledOnce");
cy.window().then((win) => {
win.__tcfapi("getTCData", 2, cy.stub().as("getTCData"));
cy.get("@getTCData")
Expand Down Expand Up @@ -1280,12 +1280,12 @@ describe("Fides-js TCF", () => {

describe("setting fields", () => {
it("can opt in to all and set legitimate interests", () => {
cy.get("@FidesUpdated").should("have.been.calledOnce");
cy.get("@FidesUpdated").should("not.have.been.called");
cy.getByTestId("consent-modal").within(() => {
cy.get("button").contains("Opt in to all").click();
});
// On slow connections, we should explicitly wait for FidesUpdated
cy.get("@FidesUpdated").should("have.been.calledTwice");
cy.get("@FidesUpdated").should("have.been.calledOnce");
cy.get("@TCFEvent")
.its("lastCall.args")
.then(([tcData, success]) => {
Expand Down Expand Up @@ -1349,7 +1349,7 @@ describe("Fides-js TCF", () => {
cy.getByTestId("consent-modal").within(() => {
cy.get("button").contains("Opt in to all").click();
});
cy.get("@FidesUpdated").should("have.been.calledTwice");
cy.get("@FidesUpdated").should("have.been.calledOnce");
cy.get("@TCFEvent2")
.its("lastCall.args")
.then(([tcData, success]) => {
Expand Down Expand Up @@ -2173,8 +2173,8 @@ describe("Fides-js TCF", () => {
cy.get("button").contains("Save").click();
cy.wait("@patchPrivacyPreference");
cy.get("@FidesUpdated")
.should("have.been.calledTwice")
.its("secondCall.args.0.detail.fides_string")
.should("have.been.calledOnce")
.its("lastCall.args.0.detail.fides_string")
.then((fidesString) => {
const parts = fidesString.split(",");
expect(parts.length).to.eql(2);
Expand Down Expand Up @@ -2203,7 +2203,7 @@ describe("Fides-js TCF", () => {
});
cy.get("button").contains("Save").click();
cy.wait("@patchPrivacyPreference");
cy.get("@FidesUpdated").should("have.been.calledTwice");
cy.get("@FidesUpdated").should("have.been.calledOnce");
// Call getTCData
cy.window().then((win) => {
win.__tcfapi("getTCData", 2, cy.stub().as("getTCData"));
Expand Down Expand Up @@ -2253,8 +2253,7 @@ describe("Fides-js TCF", () => {
});

cy.get("@FidesInitialized")
.should("have.been.calledOnce")
.its("firstCall.args.0.detail.tcf_consent")
.its("lastCall.args.0.detail.tcf_consent")
.then((tcfConsent) => {
// TC string setting worked
expect(tcfConsent.purpose_consent_preferences).to.eql({
Expand Down
105 changes: 30 additions & 75 deletions clients/privacy-center/cypress/e2e/consent-banner.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -407,7 +407,7 @@ describe("Consent banner", () => {

it("can remove all cookies when rejecting all", () => {
cy.contains("button", "Reject Test").click();
cy.get("@FidesUpdated").should("have.been.calledTwice");
cy.get("@FidesUpdated").should("have.been.calledOnce");
cy.getAllCookies().then((allCookies) => {
expect(allCookies.map((c) => c.name)).to.eql([CONSENT_COOKIE_NAME]);
});
Expand All @@ -418,7 +418,7 @@ describe("Consent banner", () => {
// opt out of the first notice
cy.getByTestId("toggle-one").click();
cy.getByTestId("Save test-btn").click();
cy.get("@FidesUpdated").should("have.been.calledTwice");
cy.get("@FidesUpdated").should("have.been.calledOnce");
cy.getAllCookies().then((allCookies) => {
expect(allCookies.map((c) => c.name)).to.eql([
CONSENT_COOKIE_NAME,
Expand Down Expand Up @@ -1090,7 +1090,7 @@ describe("Consent banner", () => {

// NOTE: See definition of cy.visitConsentDemo in commands.ts for where we
// register listeners for these window events
it("emits both a FidesInitialized and FidesUpdated event when initialized", () => {
it("emits a FidesInitialized but not a FidesUpdated event when initialized", () => {
cy.window()
.its("Fides")
.its("consent")
Expand All @@ -1105,51 +1105,47 @@ describe("Consent banner", () => {
[PRIVACY_NOTICE_KEY_1]: false,
[PRIVACY_NOTICE_KEY_2]: true,
});
cy.get("@FidesUpdated")
.should("have.been.calledOnce")
.its("firstCall.args.0.detail.consent")
.should("deep.equal", {
[PRIVACY_NOTICE_KEY_1]: false,
[PRIVACY_NOTICE_KEY_2]: true,
});
cy.get("@FidesUpdated").should("not.have.been.called");
cy.get("@FidesUIChanged").should("not.have.been.called");
});

describe("when preferences are changed / saved", () => {
it("emits another FidesUpdated event when reject all is clicked", () => {
it("emits a FidesUpdated event when reject all is clicked", () => {
cy.contains("button", "Reject Test").should("be.visible").click();
cy.get("@FidesUIChanged").should("not.have.been.called");
cy.get("@FidesUpdated")
.should("have.been.calledTwice")
// First call should be from initialization, before the user rejects all
cy.get("@FidesInitialized")
// First event, before the user rejects all
.should("have.been.calledOnce")
.its("firstCall.args.0.detail.consent")
.should("deep.equal", {
[PRIVACY_NOTICE_KEY_1]: false,
[PRIVACY_NOTICE_KEY_2]: true,
});
cy.get("@FidesUpdated")
// Second call is when the user rejects all
.its("secondCall.args.0.detail.consent")
// Update event, when the user rejects all
.should("have.been.calledOnce")
.its("firstCall.args.0.detail.consent")
.should("deep.equal", {
[PRIVACY_NOTICE_KEY_1]: false,
[PRIVACY_NOTICE_KEY_2]: true,
});
});

it("emits another FidesUpdated event when accept all is clicked", () => {
it("emits a FidesUpdated event when accept all is clicked", () => {
cy.contains("button", "Accept Test").should("be.visible").click();
cy.get("@FidesUIChanged").should("not.have.been.called");
cy.get("@FidesUpdated")
.should("have.been.calledTwice")
// First call should be from initialization, before the user accepts all
cy.get("@FidesInitialized")
// First event, before the user accepts all
.should("have.been.calledOnce")
.its("firstCall.args.0.detail.consent")
.should("deep.equal", {
[PRIVACY_NOTICE_KEY_1]: false,
[PRIVACY_NOTICE_KEY_2]: true,
});
cy.get("@FidesUpdated")
// Second call is when the user accepts all
.its("secondCall.args.0.detail.consent")
// Update event, when the user accepts all
.should("have.been.calledOnce")
.its("firstCall.args.0.detail.consent")
.should("deep.equal", {
[PRIVACY_NOTICE_KEY_1]: true,
[PRIVACY_NOTICE_KEY_2]: true,
Expand All @@ -1163,17 +1159,18 @@ describe("Consent banner", () => {
cy.getByTestId("toggle-Test privacy notice").click();
cy.getByTestId("consent-modal").contains("Save").click();
cy.get("@FidesUIChanged").should("have.been.calledOnce");
cy.get("@FidesUpdated")
.should("have.been.calledTwice")
// First call should be from initialization, before the user saved preferences
cy.get("@FidesInitialized")
// First event, before the user saved preferences
.should("have.been.calledOnce")
.its("firstCall.args.0.detail.consent")
.should("deep.equal", {
[PRIVACY_NOTICE_KEY_1]: false,
[PRIVACY_NOTICE_KEY_2]: true,
});
cy.get("@FidesUpdated")
// Second call is when the user saved preferences and opted-in to the first notice
.its("secondCall.args.0.detail.consent")
// Update event, when the user saved preferences and opted-in to the first notice
.should("have.been.calledOnce")
.its("firstCall.args.0.detail.consent")
.should("deep.equal", {
[PRIVACY_NOTICE_KEY_1]: true,
[PRIVACY_NOTICE_KEY_2]: true,
Expand Down Expand Up @@ -1255,21 +1252,14 @@ describe("Consent banner", () => {
[PRIVACY_NOTICE_KEY_2]: true,
});
cy.get("@FidesInitialized")
.should("have.been.calledOnce")
.its("firstCall.args.0.detail.consent")
.should("deep.equal", {
data_sales: false,
tracking: false,
analytics: true,
});
cy.get("@FidesUpdated")
.should("have.been.calledTwice")
.its("firstCall.args.0.detail.consent")
.should("deep.equal", {
data_sales: false,
tracking: false,
analytics: true,
});
cy.get("@FidesUpdated")
cy.get("@FidesInitialized")
.its("secondCall.args.0.detail.consent")
.should("deep.equal", {
[PRIVACY_NOTICE_KEY_1]: false,
Expand Down Expand Up @@ -1316,21 +1306,14 @@ describe("Consent banner", () => {
[PRIVACY_NOTICE_KEY_2]: true,
});
cy.get("@FidesInitialized")
.should("have.been.calledOnce")
.its("firstCall.args.0.detail.consent")
.should("deep.equal", {
data_sales: false,
tracking: false,
analytics: true,
});
cy.get("@FidesUpdated")
.should("have.been.calledTwice")
.its("firstCall.args.0.detail.consent")
.should("deep.equal", {
data_sales: false,
tracking: false,
analytics: true,
});
cy.get("@FidesUpdated")
cy.get("@FidesInitialized")
.its("secondCall.args.0.detail.consent")
.should("deep.equal", {
[PRIVACY_NOTICE_KEY_1]: false,
Expand Down Expand Up @@ -1377,27 +1360,13 @@ describe("Consent banner", () => {
analytics: true,
});
cy.get("@FidesInitialized")
.should("have.been.calledOnce")
.its("firstCall.args.0.detail.consent")
.should("deep.equal", {
data_sales: false,
tracking: false,
analytics: true,
});
cy.get("@FidesUpdated")
.should("have.been.calledTwice")
.its("firstCall.args.0.detail.consent")
.should("deep.equal", {
data_sales: false,
tracking: false,
analytics: true,
});
cy.get("@FidesUpdated")
.its("secondCall.args.0.detail.consent")
.should("deep.equal", {
data_sales: false,
tracking: false,
analytics: true,
});
});
});

Expand Down Expand Up @@ -1438,27 +1407,13 @@ describe("Consent banner", () => {
analytics: true,
});
cy.get("@FidesInitialized")
.should("have.been.calledOnce")
.its("firstCall.args.0.detail.consent")
.should("deep.equal", {
data_sales: false,
tracking: false,
analytics: true,
});
cy.get("@FidesUpdated")
.should("have.been.calledTwice")
.its("firstCall.args.0.detail.consent")
.should("deep.equal", {
data_sales: false,
tracking: false,
analytics: true,
});
cy.get("@FidesUpdated")
.its("secondCall.args.0.detail.consent")
.should("deep.equal", {
data_sales: false,
tracking: false,
analytics: true,
});
});
});
});
Expand Down