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

privacy-center: Introduce fides-consent script package #1756

Merged
merged 5 commits into from
Nov 16, 2022
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
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ The types of changes are:
* Added phone number validation in back-end and react phone number form in Privacy Center [#1745](https://github.com/ethyca/fides/pull/1745)
* Adds SMS message template for all subject notifications [#1743](https://github.com/ethyca/fides/pull/1743)
* Privacy-Center-Cypress workflow for CI checks of the Privacy Center. [#1722](https://github.com/ethyca/fides/pull/1722)
* Privacy Center `fides-consent.js` script for accessing consent on external pages. [Details](/clients/privacy-center/packages/fides-consent/README.md)

### Changed

Expand All @@ -36,7 +37,11 @@ The types of changes are:
### Developer Experience

* Admin-UI-Cypress tests that fail in CI will now upload screen recordings for debugging. [#1728](https://github.com/ethyca/fides/pull/1728/files/c23e62fea284f7910028c8483feff893903068b8#r1019491323)
* Enable remote debugging from VSCode of live dev app (#1780)(https://github.com/ethyca/fides/pull/1780)
* Enable remote debugging from VSCode of live dev app [#1780](https://github.com/ethyca/fides/pull/1780)

### Removed

* Removed the Privacy Center `cookieName` config introduced in 2.0.0. [#1756](https://github.com/ethyca/fides/pull/1756)

### Fixed

Expand Down
6 changes: 3 additions & 3 deletions clients/privacy-center/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -79,15 +79,15 @@ typings/
# Next.js build output
.next

# The consent script is a build artifact.
public/fides-consent.js

# Nuxt.js build / generate output
.nuxt
dist

# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and *not* Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public

# vuepress build output
.vuepress/dist
Expand Down
1 change: 0 additions & 1 deletion clients/privacy-center/config/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@
"icon_path": "/consent.svg",
"title": "Manage your consent",
"description": "Manage your consent preferences, including the option to select 'Do Not Sell My Personal Information'.",
"cookieName": "fides_consent",
"consentOptions": [
{
"fidesDataUseKey": "advertising",
Expand Down
63 changes: 44 additions & 19 deletions clients/privacy-center/cypress/e2e/consent.cy.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { hostUrl } from "~/constants";
import { CONSENT_COOKIE_NAME } from "~/features/consent/cookie";
import { CONSENT_COOKIE_NAME } from "fides-consent";

describe("Consent settings", () => {
beforeEach(() => {
Expand Down Expand Up @@ -68,45 +68,70 @@ describe("Consent settings", () => {
`${hostUrl}/consent-request/consent-request-id/verify`,
{ fixture: "consent/verify" }
).as("postConsentRequestVerify");

cy.intercept(
"PATCH",
`${hostUrl}/consent-request/consent-request-id/preferences/`,
(req) => {
req.reply(req.body);
}
).as("patchConsentPreferences");
});

it("lets the user update their consent", () => {
cy.visit("/consent");
cy.getByTestId("consent");

cy.getByTestId(`consent-item-card-advertising.first_party`).within(() => {
cy.get('input[type="radio"][value="true"]').should("not.be.checked");
cy.getRadio().should("not.be.checked");
});
cy.getByTestId(`consent-item-card-improve`).within(() => {
cy.get('input[type="radio"][value="true"]').should("be.checked");
cy.getRadio().should("be.checked");
});

// Consent to an item that was opted-out.
cy.getByTestId(`consent-item-card-advertising`).within(() => {
cy.get('input[type="radio"][value="true"]')
.should("not.be.checked")
.check({ force: true });
cy.getRadio().should("not.be.checked").check({ force: true });
});

cy.intercept(
"PATCH",
`${hostUrl}/consent-request/consent-request-id/preferences/`,
(req) => {
const consent = req.body.consent.find(
(c: any) => c.data_use === "advertising"
);
expect(consent?.opt_in).to.eq(true);
req.reply(req.body);
}
).as("patchConsentPreferences");
cy.getByTestId("save-btn").click();
cy.wait("@patchConsentPreferences");

cy.wait("@patchConsentPreferences").then((interception) => {
const consent = interception.request.body.consent.find(
(c: any) => c.data_use === "advertising"
);
expect(consent?.opt_in).to.eq(true);
});

// The cookie should also have been updated.
cy.getCookie(CONSENT_COOKIE_NAME).then((cookie) => {
const cookieKeyConsent = JSON.parse(decodeURIComponent(cookie!.value));
expect(cookieKeyConsent.data_sales).to.eq(true);
});
});

it("reflects their choices using fides-consent.js", () => {
cy.visit("/fides-consent-demo.html");
cy.get("#consent-json");
cy.window().then((win) => {
// Before visiting the privacy center the consent object exists, but it has no keys.
expect(win).to.have.nested.property("Fides.consent").that.eql({});
});

// Consent to an item that was opted-out.
cy.visit("/consent");
cy.getByTestId(`consent-item-card-advertising`).within(() => {
cy.getRadio().check({ force: true });
});
cy.getByTestId("save-btn").click();

cy.visit("/fides-consent-demo.html");
cy.get("#consent-json");
cy.window().then((win) => {
// Now all of the cookie keys should be populated.
expect(win).to.have.nested.property("Fides.consent").that.eql({
data_sales: true,
});
});
});
});
});
17 changes: 17 additions & 0 deletions clients/privacy-center/cypress/support/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ Cypress.Commands.add("getByTestId", (selector, ...args) =>
cy.get(`[data-testid='${selector}']`, ...args)
);

Cypress.Commands.add("getRadio", (value = "true", ...args) =>
cy.get(`input[type="radio"][value="${value}"]`, ...args)
);

declare global {
namespace Cypress {
interface Chainable {
Expand All @@ -20,6 +24,19 @@ declare global {
Cypress.Shadow
>
): Chainable<JQuery<HTMLElement>>;
/**
* Custom command to select a radio input by its value. Value defaults to "true".
* @example cy.getRadio().should("be.checked");
*/
getRadio(
value?: string,
options?: Partial<
Cypress.Loggable &
Cypress.Timeoutable &
Cypress.Withinable &
Cypress.Shadow
>
): Chainable<JQuery<HTMLElement>>;
}
}
}
Expand Down
8 changes: 2 additions & 6 deletions clients/privacy-center/features/consent/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
import { CookieKeyConsent } from "fides-consent";
import { ConfigConsentOption } from "~/types/config";

import {
ConsentItem,
ApiUserConsents,
ApiUserConsent,
CookieKeyConsent,
} from "./types";
import { ConsentItem, ApiUserConsents, ApiUserConsent } from "./types";

export const makeConsentItems = (
data: ApiUserConsents,
Expand Down
7 changes: 0 additions & 7 deletions clients/privacy-center/features/consent/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,3 @@ export type ApiUserConsent = {
export type ApiUserConsents = {
consent?: ApiUserConsent[];
};

/**
* A mapping from the cookie keys (defined in config.json) to the resolved consent value.
*/
export type CookieKeyConsent = {
[cookieKey: string]: boolean | undefined;
};
16 changes: 8 additions & 8 deletions clients/privacy-center/next.config.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
const path = require('path');
const path = require("path");

/** @type {import('next').NextConfig} */
const withBundleAnalyzer = require('@next/bundle-analyzer')({
enabled: process.env.ANALYZE === 'true',
const withBundleAnalyzer = require("@next/bundle-analyzer")({
enabled: process.env.ANALYZE === "true",
});

const nextConfig = {
reactStrictMode: true,
poweredByHeader: false,
webpack(config) {
Object.assign(config.resolve.alias, {
react: path.resolve(__dirname, 'node_modules', 'react'),
'react-dom': path.resolve(__dirname, 'node_modules', 'react-dom'),
'@emotion/react': path.resolve(
react: path.resolve(__dirname, "node_modules", "react"),
"react-dom": path.resolve(__dirname, "node_modules", "react-dom"),
"@emotion/react": path.resolve(
__dirname,
'node_modules',
'@emotion/react'
"node_modules",
"@emotion/react"
),
});

Expand Down
Loading