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

Opt in improvements #17

Merged
merged 10 commits into from
Jul 19, 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
5 changes: 5 additions & 0 deletions lib/cmps/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,11 @@ async function evaluateRuleStep(rule: AutoConsentRuleStep) {
results.push(hide(rule.hide, rule.method));
}

if (results.length === 0) {
enableLogs && console.warn('Unrecognized rule', rule);
return false;
}

// boolean and of results
const all = await Promise.all(results);
return all.reduce((a, b) => a && b, true);
Expand Down
2 changes: 1 addition & 1 deletion lib/cmps/onetrust.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export default class Onetrust extends AutoConsentCMPBase {
}

async optIn() {
return click("onetrust-accept-btn-handler,js-accept-cookies");
return click("#onetrust-accept-btn-handler,.js-accept-cookies");
}

async test() {
Expand Down
16 changes: 9 additions & 7 deletions lib/cmps/sourcepoint-frame.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export default class SourcePoint extends AutoConsentCMPBase {
}

async optIn() {
await waitForElement(".sp_choice_type_11,.sp_choice_type_ACCEPT_ALL", 2000);
if (click(".sp_choice_type_11")) {
return true;
}
Expand All @@ -56,10 +57,13 @@ export default class SourcePoint extends AutoConsentCMPBase {

async optOut() {
if (!this.isManagerOpen()) {
const actionable = await waitForElement('button.sp_choice_type_12,button.sp_choice_type_13');
if (!actionable) {
return false;
}
if (!elementExists("button.sp_choice_type_12")) {
// do not sell button
click("button.sp_choice_type_13");
return true;
return click("button.sp_choice_type_13");
}

click("button.sp_choice_type_12");
Expand All @@ -82,10 +86,9 @@ export default class SourcePoint extends AutoConsentCMPBase {
]);
if (path === 0) {
await wait(1000);
click(rejectSelector1);
return true;
return click(rejectSelector1);
} else if (path === 1) {
click(rejectSelector2);
return click(rejectSelector2);
} else if (path === 2) {
// TODO: check if this is still working
await waitForElement('.pm-features', 10000);
Expand All @@ -96,7 +99,6 @@ export default class SourcePoint extends AutoConsentCMPBase {
} catch (e) {
enableLogs && console.warn(e);
}
click('.sp_choice_type_SAVE_AND_EXIT');
return true;
return click('.sp_choice_type_SAVE_AND_EXIT');
}
}
6 changes: 6 additions & 0 deletions lib/cmps/trustarc-top.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,22 @@ export default class TrustArcTop extends AutoConsentCMPBase {
}

_shortcutButton: HTMLElement;
_optInDone: boolean;

constructor() {
super("TrustArc-top");
this._shortcutButton = null; // indicates if the "reject all" button is detected
this._optInDone = false;
}

get hasSelfTest(): boolean {
return false;
}

get isIntermediate(): boolean {
if (this._optInDone) {
return false;
}
return !this._shortcutButton;
}

Expand Down Expand Up @@ -76,6 +81,7 @@ export default class TrustArcTop extends AutoConsentCMPBase {
}

async optIn() {
this._optInDone = true; // just a hack to force autoconsentDone
return click(shortcutOptIn);
}

Expand Down
9 changes: 7 additions & 2 deletions lib/web.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ export default class AutoConsent {
if (config) {
this.initialize(config, declarativeRules);
} else {
if (declarativeRules) {
this.parseRules(declarativeRules);
}
const initMsg: InitMessage = {
type: "init",
url: window.location.href,
Expand All @@ -39,7 +42,9 @@ export default class AutoConsent {
return;
}

this.parseRules(declarativeRules);
if (declarativeRules) {
this.parseRules(declarativeRules);
}
if (config.disabledCmps?.length > 0) {
this.disableCMPs(config.disabledCmps);
}
Expand Down Expand Up @@ -241,7 +246,7 @@ export default class AutoConsent {
type: 'optInResult',
cmp: this.foundCmp ? this.foundCmp.name : 'none',
result: optInResult,
scheduleSelfTest: this.foundCmp && this.foundCmp.hasSelfTest,
scheduleSelfTest: false, // self-tests are only for opt-out at the moment
url: location.href,
});

Expand Down
8 changes: 1 addition & 7 deletions playwright/content.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,7 @@ declare global {
}

if (!window.autoconsentReceiveMessage) {
const consent = new AutoConsent(window.autoconsentSendMessage, {
enabled: true,
autoAction: 'optOut',
disabledCmps: [],
enablePrehide: true,
detectRetries: 20,
}, <RuleBundle>rules);
const consent = new AutoConsent(window.autoconsentSendMessage, null, <RuleBundle>rules);

window.autoconsentReceiveMessage = (message: BackgroundMessage) => {
return Promise.resolve(consent.receiveMessageCallback(message));
Expand Down
177 changes: 104 additions & 73 deletions playwright/runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,20 @@ import { test, expect, Page, Frame } from "@playwright/test";
import { waitFor } from "../lib/utils";
import { ContentScriptMessage } from "../lib/messages";
import { enableLogs } from "../lib/config";
import { AutoAction } from "../lib/types";

const testRegion = (process.env.REGION || "NA").trim();

type TestOptions = {
testOptOut: boolean;
testSelfTest: boolean;
testOptIn: boolean;
skipRegions?: string[];
onlyRegions?: string[];
};
const defaultOptions: TestOptions = {
testOptOut: true,
testOptIn: true,
testSelfTest: true,
skipRegions: [],
onlyRegions: [],
Expand All @@ -37,85 +40,113 @@ export async function injectContentScript(page: Page | Frame) {
export function generateTest(
url: string,
expectedCmp: string,
options: TestOptions = { testOptOut: true, testSelfTest: true }
options: TestOptions = defaultOptions
) {
test(`${url.split("://")[1]} .${testRegion}`, async ({ page }) => {
if (options.onlyRegions && options.onlyRegions.length > 0 && !options.onlyRegions.includes(testRegion)) {
test.skip();
}
if (options.skipRegions && options.skipRegions.includes(testRegion)) {
test.skip();
}
enableLogs && page.on('console', async msg => {
console.log(` page log:`, msg.text());
});
await page.exposeBinding("autoconsentSendMessage", messageCallback);
await page.goto(url, { waitUntil: "commit" });

// set up a messaging function
const received: ContentScriptMessage[] = [];

function isMessageReceived(msg: Partial<ContentScriptMessage>, partial = true) {
return received.some((m) => {
const keysMatch = partial || Object.keys(m).length === Object.keys(msg).length;
return keysMatch && Object.keys(msg).every(
(k) => (<any>m)[k] === (<any>msg)[k]
);
function genTest(autoAction: AutoAction) {
test(`${url.split("://")[1]} .${testRegion} ${autoAction}`, async ({ page }) => {
if (options.onlyRegions && options.onlyRegions.length > 0 && !options.onlyRegions.includes(testRegion)) {
test.skip();
}
if (options.skipRegions && options.skipRegions.includes(testRegion)) {
test.skip();
}
enableLogs && page.on('console', async msg => {
console.log(` page log:`, msg.text());
});
}

let selfTestFrame: Frame = null;
async function messageCallback({ frame }: { frame: Frame }, msg: ContentScriptMessage) {
enableLogs && msg.type !== 'eval' && console.log(msg);
received.push(msg);
switch (msg.type) {
case 'optInResult':
case 'optOutResult': {
if (msg.scheduleSelfTest) {
selfTestFrame = frame;
await page.exposeBinding("autoconsentSendMessage", messageCallback);
await page.goto(url, { waitUntil: "commit" });

// set up a messaging function
const received: ContentScriptMessage[] = [];

function isMessageReceived(msg: Partial<ContentScriptMessage>, partial = true) {
return received.some((m) => {
const keysMatch = partial || Object.keys(m).length === Object.keys(msg).length;
return keysMatch && Object.keys(msg).every(
(k) => (<any>m)[k] === (<any>msg)[k]
);
});
}

let selfTestFrame: Frame = null;
async function messageCallback({ frame }: { frame: Frame }, msg: ContentScriptMessage) {
enableLogs && msg.type !== 'eval' && console.log(msg);
received.push(msg);
switch (msg.type) {
case 'init': {
await frame.evaluate(`autoconsentReceiveMessage({ type: "initResp", config: ${JSON.stringify({
enabled: true,
autoAction: autoAction,
disabledCmps: [],
enablePrehide: true,
detectRetries: 20,
})} })`);
break;
}
break;
}
case 'autoconsentDone': {
if (selfTestFrame && options.testSelfTest) {
await selfTestFrame.evaluate(`autoconsentReceiveMessage({ type: "selfTest" })`);
case 'optInResult':
case 'optOutResult': {
if (msg.scheduleSelfTest) {
selfTestFrame = frame;
}
break;
}
case 'autoconsentDone': {
if (selfTestFrame && options.testSelfTest) {
await selfTestFrame.evaluate(`autoconsentReceiveMessage({ type: "selfTest" })`);
}
break;
}
case 'eval': {
const result = await frame.evaluate(msg.code);
await frame.evaluate(`autoconsentReceiveMessage({ id: "${msg.id}", type: "evalResp", result: ${JSON.stringify(result)} })`);
break;
}
case 'autoconsentError': {
console.error(url, msg.details);
break;
}
break;
}
case 'eval': {
const result = await frame.evaluate(msg.code);
await frame.evaluate(`autoconsentReceiveMessage({ id: "${msg.id}", type: "evalResp", result: ${JSON.stringify(result)} })`);
break;
}
case 'autoconsentError': {
console.error(url, msg.details);
break;
}
}
}

// inject content scripts into every frame
await injectContentScript(page);
page.frames().forEach(injectContentScript);
page.on("framenavigated", injectContentScript);

// wait for all messages and assertions
await waitFor(() => isMessageReceived({ type: "popupFound", cmp: expectedCmp }), 50, 500);
expect(isMessageReceived({ type: "popupFound", cmp: expectedCmp })).toBe(true);

if (options.testOptOut) {
await waitFor(() => isMessageReceived({ type: "optOutResult", result: true }), 50, 300);
expect(isMessageReceived({ type: "optOutResult", result: true })).toBe(true);
}
if (options.testSelfTest && selfTestFrame) {
await waitFor(() => isMessageReceived({ type: "selfTestResult", result: true }), 50, 300);
expect(isMessageReceived({ type: "selfTestResult", result: true })).toBe(true);
}
await waitFor(() => isMessageReceived({ type: "autoconsentDone" }), 10, 500);
expect(isMessageReceived({ type: "autoconsentDone" })).toBe(true);

expect(isMessageReceived({ type: "autoconsentError" })).toBe(false);
});

// inject content scripts into every frame
await injectContentScript(page);
page.frames().forEach(injectContentScript);
page.on("framenavigated", injectContentScript);

// wait for all messages and assertions
await waitFor(() => isMessageReceived({ type: "popupFound", cmp: expectedCmp }), 50, 500);
expect(isMessageReceived({ type: "popupFound", cmp: expectedCmp })).toBe(true);

if (autoAction === 'optOut') {
await waitFor(() => isMessageReceived({ type: "optOutResult", result: true }), 50, 300);
expect(isMessageReceived({ type: "optOutResult", result: true })).toBe(true);
}
if (autoAction === 'optIn') {
await waitFor(() => isMessageReceived({ type: "optInResult", result: true }), 50, 300);
expect(isMessageReceived({ type: "optInResult", result: true })).toBe(true);
}
if (options.testSelfTest && selfTestFrame) {
await waitFor(() => isMessageReceived({ type: "selfTestResult", result: true }), 50, 300);
expect(isMessageReceived({ type: "selfTestResult", result: true })).toBe(true);
}
await waitFor(() => isMessageReceived({ type: "autoconsentDone" }), 10, 500);
expect(isMessageReceived({ type: "autoconsentDone" })).toBe(true);

expect(isMessageReceived({ type: "autoconsentError" })).toBe(false);
})
}

if (!options.testOptIn && !options.testOptOut) {
genTest(null);
}

if (options.testOptIn) {
genTest('optIn');
}

if (options.testOptOut) {
genTest('optOut');
}
}

export default function generateCMPTests(
Expand Down
36 changes: 0 additions & 36 deletions playwright/standalone.ts

This file was deleted.

Loading