summaryrefslogtreecommitdiffstats
path: root/toolkit/components/cookiebanners/test/unit/test_cookiebannerlistservice.js
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /toolkit/components/cookiebanners/test/unit/test_cookiebannerlistservice.js
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'toolkit/components/cookiebanners/test/unit/test_cookiebannerlistservice.js')
-rw-r--r--toolkit/components/cookiebanners/test/unit/test_cookiebannerlistservice.js611
1 files changed, 611 insertions, 0 deletions
diff --git a/toolkit/components/cookiebanners/test/unit/test_cookiebannerlistservice.js b/toolkit/components/cookiebanners/test/unit/test_cookiebannerlistservice.js
new file mode 100644
index 0000000000..bc69a49d0f
--- /dev/null
+++ b/toolkit/components/cookiebanners/test/unit/test_cookiebannerlistservice.js
@@ -0,0 +1,611 @@
+/* Any copyright is dedicated to the Public Domain.
+http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const { RemoteSettings } = ChromeUtils.importESModule(
+ "resource://services-settings/remote-settings.sys.mjs"
+);
+
+let { sinon } = ChromeUtils.importESModule(
+ "resource://testing-common/Sinon.sys.mjs"
+);
+
+// Name of the RemoteSettings collection containing the rules.
+const COLLECTION_NAME = "cookie-banner-rules-list";
+
+// Name of pref used to import test rules.
+const PREF_TEST_RULES = "cookiebanners.listService.testRules";
+
+let rulesInserted = [];
+let rulesRemoved = [];
+let insertCallback = null;
+
+function genUUID() {
+ return Services.uuid.generateUUID().number.slice(1, -1);
+}
+
+const RULE_A_ORIGINAL = {
+ id: genUUID(),
+ click: {
+ optOut: "#fooOut",
+ presence: "#foobar",
+ },
+ domains: ["example.com"],
+ cookies: {
+ optOut: [
+ {
+ name: "doOptOut",
+ value: "true",
+ isSecure: true,
+ isSession: false,
+ },
+ ],
+ },
+};
+
+const RULE_B = {
+ id: genUUID(),
+ click: {
+ optOut: "#fooOutB",
+ presence: "#foobarB",
+ },
+ domains: ["example.org"],
+ cookies: {
+ optOut: [
+ {
+ name: "doOptOutB",
+ value: "true",
+ isSecure: true,
+ },
+ ],
+ },
+};
+
+const RULE_C = {
+ id: genUUID(),
+ click: {
+ optOut: "#fooOutC",
+ presence: "#foobarC",
+ },
+ domains: ["example.net"],
+ cookies: {
+ optIn: [
+ {
+ name: "gdpr",
+ value: "1",
+ path: "/myPath",
+ host: "foo.example.net",
+ },
+ ],
+ },
+};
+
+const RULE_A_UPDATED = {
+ id: RULE_A_ORIGINAL.id,
+ click: {
+ optOut: "#fooOut",
+ optIn: "#barIn",
+ presence: "#foobar",
+ },
+ domains: ["example.com"],
+ cookies: {
+ optOut: [
+ {
+ name: "doOptOutUpdated",
+ value: "true",
+ isSecure: true,
+ },
+ {
+ name: "hideBanner",
+ value: "1",
+ },
+ ],
+ },
+};
+
+const INVALID_RULE_CLICK = {
+ id: genUUID(),
+ domains: ["foobar.com"],
+ click: {
+ presence: 1,
+ optIn: "#foo",
+ },
+};
+
+const INVALID_RULE_EMPTY = {};
+
+const RULE_D_GLOBAL = {
+ id: genUUID(),
+ click: {
+ optOut: "#globalOptOutD",
+ presence: "#globalBannerD",
+ },
+ domains: [],
+ cookies: {},
+};
+
+const RULE_E_GLOBAL = {
+ id: genUUID(),
+ click: {
+ optOut: "#globalOptOutE",
+ presence: "#globalBannerE",
+ },
+ domains: [],
+ cookies: {},
+};
+
+const RULE_F_EMPTY_CLICK = {
+ id: genUUID(),
+ click: {},
+ domains: ["example.com"],
+ cookies: {
+ optOut: [
+ {
+ name: "doOptOut",
+ value: "true",
+ isSecure: true,
+ },
+ {
+ name: "hideBanner",
+ value: "1",
+ },
+ ],
+ },
+};
+
+// Testing with RemoteSettings requires a profile.
+do_get_profile();
+
+add_setup(async () => {
+ // Enable debug logging.
+ Services.prefs.setStringPref("cookiebanners.listService.logLevel", "Debug");
+
+ // Stub some nsICookieBannerService methods for easy testing.
+ let removeRule = sinon.stub().callsFake(rule => {
+ rulesRemoved.push(rule);
+ });
+
+ let insertRule = sinon.stub().callsFake(rule => {
+ rulesInserted.push(rule);
+ insertCallback?.();
+ });
+
+ let oldCookieBanners = Services.cookieBanners;
+ Services.cookieBanners = {
+ isEnabled: true,
+ insertRule,
+ removeRule,
+ resetRules() {},
+ resetDomainTelemetryRecord() {},
+ };
+
+ // Remove stubs on test end.
+ registerCleanupFunction(() => {
+ Services.cookieBanners = oldCookieBanners;
+ Services.prefs.clearUserPref("cookiebanners.listService.logLevel");
+ });
+});
+
+/**
+ * Promise wrapper to listen for Services.cookieBanners.insertRule calls from
+ * the CookieBannerListService.
+ * @param {function} checkFn - Function which returns true or false to indicate
+ * if this is the insert the caller is looking for.
+ * @returns {Promise} - Promise which resolves when checkFn matches after
+ * insertRule call.
+ */
+function waitForInsert(checkFn) {
+ return new Promise(resolve => {
+ insertCallback = () => {
+ if (checkFn()) {
+ insertCallback = null;
+ resolve();
+ }
+ };
+ });
+}
+
+/**
+ * Tests that the cookie banner list service imports all rules on init.
+ */
+add_task(async function test_initial_import() {
+ info("Initializing RemoteSettings collection " + COLLECTION_NAME);
+
+ let db = RemoteSettings(COLLECTION_NAME).db;
+ db.clear();
+ await db.create(RULE_A_ORIGINAL);
+ await db.create(RULE_C);
+ await db.importChanges({}, Date.now());
+
+ Assert.equal(rulesInserted.length, 0, "No inserted rules initially.");
+ Assert.equal(rulesRemoved.length, 0, "No removed rules initially.");
+
+ let insertPromise = waitForInsert(() => rulesInserted.length >= 2);
+
+ info(
+ "Initializing the cookie banner list service which triggers a collection get call"
+ );
+ let cookieBannerListService = Cc[
+ "@mozilla.org/cookie-banner-list-service;1"
+ ].getService(Ci.nsICookieBannerListService);
+ await cookieBannerListService.initForTest();
+
+ await insertPromise;
+
+ Assert.equal(rulesInserted.length, 2, "Two inserted rules after init.");
+ Assert.equal(rulesRemoved.length, 0, "No removed rules after init.");
+
+ let ruleA = rulesInserted.find(rule => rule.id == RULE_A_UPDATED.id);
+ let cookieRuleA = ruleA.cookiesOptOut[0].cookie;
+ let ruleC = rulesInserted.find(rule => rule.id == RULE_C.id);
+ let cookieRuleC = ruleC.cookiesOptIn[0].cookie;
+
+ Assert.ok(ruleA, "Has rule A.");
+ Assert.deepEqual(
+ ruleA.domains,
+ RULE_A_UPDATED.domains,
+ "Domains for ruleA should match."
+ );
+ // Test the defaults which CookieBannerListService sets when the rule does
+ // not.
+ Assert.equal(
+ cookieRuleA.isSession,
+ false,
+ "Cookie for rule A should not be a session cookie."
+ );
+ Assert.equal(
+ cookieRuleA.host,
+ null,
+ "Cookie host for rule A should be default."
+ );
+ Assert.equal(
+ cookieRuleA.path,
+ "/",
+ "Cookie path for rule A should be default."
+ );
+
+ Assert.ok(ruleC, "Has rule C.");
+ Assert.deepEqual(
+ ruleC.domains,
+ RULE_C.domains,
+ "Domains for ruleA should match."
+ );
+ Assert.equal(
+ ruleC.cookiesOptIn[0].cookie.isSession,
+ true,
+ "Cookie for rule C should should be a session cookie."
+ );
+ Assert.equal(
+ cookieRuleC.host,
+ "foo.example.net",
+ "Cookie host for rule C should be custom."
+ );
+ Assert.equal(
+ cookieRuleC.path,
+ "/myPath",
+ "Cookie path for rule C should be custom."
+ );
+
+ // Cleanup
+ cookieBannerListService.shutdown();
+ rulesInserted = [];
+ rulesRemoved = [];
+
+ db.clear();
+ await db.importChanges({}, Date.now());
+});
+
+/**
+ * Tests that the cookie banner list service updates rules on sync.
+ */
+add_task(async function test_remotesettings_sync() {
+ // Initialize the cookie banner list service so it subscribes to
+ // RemoteSettings updates.
+ let cookieBannerListService = Cc[
+ "@mozilla.org/cookie-banner-list-service;1"
+ ].getService(Ci.nsICookieBannerListService);
+ await cookieBannerListService.initForTest();
+
+ const payload = {
+ current: [RULE_A_ORIGINAL, RULE_C, RULE_D_GLOBAL],
+ created: [RULE_B, RULE_E_GLOBAL],
+ updated: [{ old: RULE_A_ORIGINAL, new: RULE_A_UPDATED }],
+ deleted: [RULE_C, RULE_D_GLOBAL],
+ };
+
+ Assert.equal(rulesInserted.length, 0, "No inserted rules initially.");
+ Assert.equal(rulesRemoved.length, 0, "No removed rules initially.");
+
+ info("Dispatching artificial RemoteSettings sync event");
+ await RemoteSettings(COLLECTION_NAME).emit("sync", { data: payload });
+
+ Assert.equal(rulesInserted.length, 3, "Three inserted rules after sync.");
+ Assert.equal(rulesRemoved.length, 2, "Two removed rules after sync.");
+
+ let ruleA = rulesInserted.find(rule => rule.id == RULE_A_UPDATED.id);
+ let ruleB = rulesInserted.find(rule => rule.id == RULE_B.id);
+ let ruleE = rulesInserted.find(rule => rule.id == RULE_E_GLOBAL.id);
+ let ruleC = rulesRemoved[0];
+
+ info("Testing that service inserted updated version of RULE_A.");
+ Assert.deepEqual(
+ ruleA.domains,
+ RULE_A_UPDATED.domains,
+ "Domains should match RULE_A."
+ );
+ Assert.equal(
+ ruleA.cookiesOptOut.length,
+ RULE_A_UPDATED.cookies.optOut.length,
+ "cookiesOptOut length should match RULE_A."
+ );
+
+ info("Testing opt-out cookies of RULE_A");
+ for (let i = 0; i < RULE_A_UPDATED.cookies.optOut.length; i += 1) {
+ Assert.equal(
+ ruleA.cookiesOptOut[i].cookie.name,
+ RULE_A_UPDATED.cookies.optOut[i].name,
+ "cookiesOptOut cookie name should match RULE_A."
+ );
+ Assert.equal(
+ ruleA.cookiesOptOut[i].cookie.value,
+ RULE_A_UPDATED.cookies.optOut[i].value,
+ "cookiesOptOut cookie value should match RULE_A."
+ );
+ }
+
+ Assert.equal(ruleB.id, RULE_B.id, "Should have inserted RULE_B");
+ Assert.equal(ruleC.id, RULE_C.id, "Should have removed RULE_C");
+ Assert.equal(
+ ruleE.id,
+ RULE_E_GLOBAL.id,
+ "Should have inserted RULE_E_GLOBAL"
+ );
+
+ // Cleanup
+ cookieBannerListService.shutdown();
+ rulesInserted = [];
+ rulesRemoved = [];
+
+ let { db } = RemoteSettings(COLLECTION_NAME);
+ db.clear();
+ await db.importChanges({}, Date.now());
+});
+
+/**
+ * Tests the cookie banner rule test pref.
+ */
+add_task(async function test_rule_test_pref() {
+ Services.prefs.setStringPref(
+ PREF_TEST_RULES,
+ JSON.stringify([RULE_A_ORIGINAL, RULE_B])
+ );
+
+ Assert.equal(rulesInserted.length, 0, "No inserted rules initially.");
+ Assert.equal(rulesRemoved.length, 0, "No removed rules initially.");
+
+ let insertPromise = waitForInsert(() => rulesInserted.length >= 2);
+
+ // Initialize the cookie banner list service so it imports test rules and listens for pref changes.
+ let cookieBannerListService = Cc[
+ "@mozilla.org/cookie-banner-list-service;1"
+ ].getService(Ci.nsICookieBannerListService);
+ await cookieBannerListService.initForTest();
+
+ info("Wait for rules to be inserted");
+ await insertPromise;
+
+ Assert.equal(rulesInserted.length, 2, "Should have inserted two rules.");
+ Assert.equal(rulesRemoved.length, 0, "Should not have removed any rules.");
+
+ Assert.ok(
+ rulesInserted.some(rule => rule.id == RULE_A_ORIGINAL.id),
+ "Should have inserted RULE_A"
+ );
+ Assert.ok(
+ rulesInserted.some(rule => rule.id == RULE_B.id),
+ "Should have inserted RULE_B"
+ );
+
+ rulesInserted = [];
+ rulesRemoved = [];
+
+ let insertPromise2 = waitForInsert(() => rulesInserted.length >= 3);
+
+ info(
+ "Updating test rules via pref. The list service should detect the pref change."
+ );
+ // This includes some invalid rules, they should be skipped.
+ Services.prefs.setStringPref(
+ PREF_TEST_RULES,
+ JSON.stringify([
+ RULE_A_ORIGINAL,
+ RULE_B,
+ INVALID_RULE_EMPTY,
+ RULE_C,
+ INVALID_RULE_CLICK,
+ ])
+ );
+
+ info("Wait for rules to be inserted");
+ await insertPromise2;
+
+ Assert.equal(rulesInserted.length, 3, "Should have inserted three rules.");
+ Assert.equal(rulesRemoved.length, 0, "Should not have removed any rules.");
+
+ Assert.ok(
+ rulesInserted.some(rule => rule.id == RULE_A_ORIGINAL.id),
+ "Should have inserted RULE_A"
+ );
+ Assert.ok(
+ rulesInserted.some(rule => rule.id == RULE_B.id),
+ "Should have inserted RULE_B"
+ );
+ Assert.ok(
+ rulesInserted.some(rule => rule.id == RULE_C.id),
+ "Should have inserted RULE_C"
+ );
+
+ // Cleanup
+ cookieBannerListService.shutdown();
+ rulesInserted = [];
+ rulesRemoved = [];
+
+ Services.prefs.clearUserPref(PREF_TEST_RULES);
+});
+
+/**
+ * Tests that runContext string values get properly translated into nsIClickRule::RunContext.
+ */
+add_task(async function test_runContext_conversion() {
+ info("Initializing RemoteSettings collection " + COLLECTION_NAME);
+
+ let ruleA = {
+ id: genUUID(),
+ click: {
+ presence: "#foobar",
+ runContext: "child",
+ },
+ domains: ["a.com"],
+ };
+ let ruleB = {
+ id: genUUID(),
+ click: {
+ presence: "#foobar",
+ },
+ domains: ["b.com"],
+ };
+ let ruleC = {
+ id: genUUID(),
+ click: {
+ presence: "#foobar",
+ runContext: "all",
+ },
+ domains: ["c.com"],
+ };
+ let ruleD = {
+ id: genUUID(),
+ click: {
+ presence: "#foobar",
+ runContext: "top",
+ },
+ domains: ["d.com"],
+ };
+ let ruleE = {
+ id: genUUID(),
+ click: {
+ presence: "#foobar",
+ runContext: "thisIsNotValid",
+ },
+ domains: ["e.com"],
+ };
+
+ let db = RemoteSettings(COLLECTION_NAME).db;
+ db.clear();
+ await db.create(ruleA);
+ await db.create(ruleB);
+ await db.create(ruleC);
+ await db.create(ruleD);
+ await db.create(ruleE);
+ await db.importChanges({}, Date.now());
+
+ let insertPromise = waitForInsert(() => rulesInserted.length >= 4);
+
+ info(
+ "Initializing the cookie banner list service which triggers a collection get call"
+ );
+ let cookieBannerListService = Cc[
+ "@mozilla.org/cookie-banner-list-service;1"
+ ].getService(Ci.nsICookieBannerListService);
+ await cookieBannerListService.initForTest();
+
+ await insertPromise;
+
+ let resultA = rulesInserted.find(rule =>
+ rule.domains.includes("a.com")
+ ).clickRule;
+ let resultB = rulesInserted.find(rule =>
+ rule.domains.includes("b.com")
+ ).clickRule;
+ let resultC = rulesInserted.find(rule =>
+ rule.domains.includes("c.com")
+ ).clickRule;
+ let resultD = rulesInserted.find(rule =>
+ rule.domains.includes("d.com")
+ ).clickRule;
+ let resultE = rulesInserted.find(rule =>
+ rule.domains.includes("e.com")
+ ).clickRule;
+
+ Assert.equal(
+ resultA.runContext,
+ Ci.nsIClickRule.RUN_CHILD,
+ "Rule A should have been imported with the correct runContext"
+ );
+ Assert.equal(
+ resultB.runContext,
+ Ci.nsIClickRule.RUN_TOP,
+ "Rule B should have fallen back to default runContext"
+ );
+ Assert.equal(
+ resultC.runContext,
+ Ci.nsIClickRule.RUN_ALL,
+ "Rule C should have been imported with the correct runContext"
+ );
+ Assert.equal(
+ resultD.runContext,
+ Ci.nsIClickRule.RUN_TOP,
+ "Rule D should have been imported with the correct runContext"
+ );
+ Assert.equal(
+ resultE.runContext,
+ Ci.nsIClickRule.RUN_TOP,
+ "Rule E should have fallen back to default runContext"
+ );
+
+ // Cleanup
+ cookieBannerListService.shutdown();
+ rulesInserted = [];
+ rulesRemoved = [];
+
+ db.clear();
+ await db.importChanges({}, Date.now());
+});
+
+/**
+ * Tests empty click rules don't get imported.
+ */
+add_task(async function test_empty_click_rule() {
+ info("Initializing RemoteSettings collection " + COLLECTION_NAME);
+
+ let db = RemoteSettings(COLLECTION_NAME).db;
+ db.clear();
+ await db.create(RULE_F_EMPTY_CLICK);
+ await db.importChanges({}, Date.now());
+
+ let insertPromise = waitForInsert(() => rulesInserted.length >= 1);
+
+ info(
+ "Initializing the cookie banner list service which triggers a collection get call"
+ );
+ let cookieBannerListService = Cc[
+ "@mozilla.org/cookie-banner-list-service;1"
+ ].getService(Ci.nsICookieBannerListService);
+ await cookieBannerListService.initForTest();
+
+ await insertPromise;
+
+ let ruleF = rulesInserted.find(rule => rule.id == RULE_F_EMPTY_CLICK.id);
+
+ Assert.ok(ruleF, "Has rule F.");
+ Assert.ok(ruleF.cookiesOptOut?.length, "Should have imported a cookie rule.");
+ Assert.equal(ruleF.clickRule, null, "Should not have imported a click rule.");
+
+ // Cleanup
+ cookieBannerListService.shutdown();
+ rulesInserted = [];
+ rulesRemoved = [];
+
+ db.clear();
+ await db.importChanges({}, Date.now());
+});