diff options
Diffstat (limited to 'toolkit/components/contextualidentity/tests/unit')
5 files changed, 620 insertions, 0 deletions
diff --git a/toolkit/components/contextualidentity/tests/unit/test_basic.html b/toolkit/components/contextualidentity/tests/unit/test_basic.html new file mode 100644 index 0000000000..fbdbc772a9 --- /dev/null +++ b/toolkit/components/contextualidentity/tests/unit/test_basic.html @@ -0,0 +1,71 @@ +"use strict"; + +do_get_profile(); + +const { ContextualIdentityService } = ChromeUtils.importESModule( + "resource://gre/modules/ContextualIdentityService.sys.mjs" +); + +const TEST_STORE_FILE_NAME = "test-containers.json"; + +let cis; + +// Basic tests +add_task(function() { + ok(!!ContextualIdentityService, "ContextualIdentityService exists"); + + cis = ContextualIdentityService.createNewInstanceForTesting(TEST_STORE_FILE_NAME); + ok(!!cis, "We have our instance of ContextualIdentityService"); + + equal(cis.getPublicIdentities().length, 4, "By default, 4 containers."); + equal(cis.getPublicIdentityFromId(0), null, "No identity with id 0"); + + ok(!!cis.getPublicIdentityFromId(1), "Identity 1 exists"); + ok(!!cis.getPublicIdentityFromId(2), "Identity 2 exists"); + ok(!!cis.getPublicIdentityFromId(3), "Identity 3 exists"); + ok(!!cis.getPublicIdentityFromId(4), "Identity 4 exists"); +}); + +// Create a new identity +add_task(function() { + equal(cis.getPublicIdentities().length, 4, "By default, 4 containers."); + + let identity = cis.create("New Container", "Icon", "Color"); + ok(!!identity, "New container created"); + equal(identity.name, "New Container", "Name matches"); + equal(identity.icon, "Icon", "Icon matches"); + equal(identity.color, "Color", "Color matches"); + + equal(cis.getPublicIdentities().length, 5, "Expected 5 containers."); + + ok(!!cis.getPublicIdentityFromId(identity.userContextId), "Identity exists"); + equal(cis.getPublicIdentityFromId(identity.userContextId).name, "New Container", "Identity name is OK"); + equal(cis.getPublicIdentityFromId(identity.userContextId).icon, "Icon", "Identity icon is OK"); + equal(cis.getPublicIdentityFromId(identity.userContextId).color, "Color", "Identity color is OK"); + equal(cis.getUserContextLabel(identity.userContextId), "New Container", "Identity label is OK"); +}); + +// Remove an identity +add_task(function() { + equal(cis.getPublicIdentities().length, 5, "Expected 5 containers."); + + equal(cis.remove(-1), false, "cis.remove() returns false if identity doesn't exist."); + equal(cis.remove(1), true, "cis.remove() returns true if identity exists."); + + equal(cis.getPublicIdentities().length, 4, "Expected 4 containers."); +}); + +// Update an identity +add_task(function() { + ok(!!cis.getPublicIdentityFromId(2), "Identity 2 exists"); + + equal(cis.update(-1, "Container", "Icon", "Color"), false, "Update returns true if everything is OK"); + + equal(cis.update(2, "Container", "Icon", "Color"), true, "Update returns true if everything is OK"); + + ok(!!cis.getPublicIdentityFromId(2), "Identity exists"); + equal(cis.getPublicIdentityFromId(2).name, "Container", "Identity name is OK"); + equal(cis.getPublicIdentityFromId(2).icon, "Icon", "Identity icon is OK"); + equal(cis.getPublicIdentityFromId(2).color, "Color", "Identity color is OK"); + equal(cis.getUserContextLabel(2), "Container", "Identity label is OK"); +}); diff --git a/toolkit/components/contextualidentity/tests/unit/test_basic.js b/toolkit/components/contextualidentity/tests/unit/test_basic.js new file mode 100644 index 0000000000..0ee1869371 --- /dev/null +++ b/toolkit/components/contextualidentity/tests/unit/test_basic.js @@ -0,0 +1,233 @@ +"use strict"; + +const profileDir = do_get_profile(); + +const { ContextualIdentityService } = ChromeUtils.importESModule( + "resource://gre/modules/ContextualIdentityService.sys.mjs" +); + +const TEST_STORE_FILE_PATH = PathUtils.join( + profileDir.path, + "test-containers.json" +); + +let cis; + +// Basic tests +add_task(function () { + ok(!!ContextualIdentityService, "ContextualIdentityService exists"); + + cis = + ContextualIdentityService.createNewInstanceForTesting(TEST_STORE_FILE_PATH); + ok(!!cis, "We have our instance of ContextualIdentityService"); + + equal(cis.getPublicIdentities().length, 4, "By default, 4 containers."); + equal(cis.getPublicIdentityFromId(0), null, "No identity with id 0"); + + ok(!!cis.getPublicIdentityFromId(1), "Identity 1 exists"); + ok(!!cis.getPublicIdentityFromId(2), "Identity 2 exists"); + ok(!!cis.getPublicIdentityFromId(3), "Identity 3 exists"); + ok(!!cis.getPublicIdentityFromId(4), "Identity 4 exists"); + + Assert.deepEqual( + cis.getPublicUserContextIds(), + cis.getPublicIdentities().map(ident => ident.userContextId), + "getPublicUserContextIds has matching user context IDs" + ); +}); + +// Make sure we are not allowed to only use whitespaces as a container name +add_task(function () { + Assert.throws( + () => + cis.create( + "\u0009\u000B\u000C\u0020\u00A0\uFEFF\u000A\u000D\u2028\u2029", + "icon", + "color" + ), + /Contextual identity names cannot contain only whitespace./, + "Contextual identity names cannot contain only whitespace." + ); +}); + +add_task(function () { + ok(!!cis.getPublicIdentityFromId(2), "Identity 2 exists"); + Assert.throws( + () => + cis.update( + 2, + "\u0009\u000B\u000C\u0020\u00A0\uFEFF\u000A\u000D\u2028\u2029", + "icon", + "color" + ), + /Contextual identity names cannot contain only whitespace./, + "Contextual identity names cannot contain only whitespace." + ); +}); + +// Create a new identity +add_task(function () { + equal(cis.getPublicIdentities().length, 4, "By default, 4 containers."); + + let identity = cis.create("New Container", "Icon", "Color"); + ok(!!identity, "New container created"); + equal(identity.name, "New Container", "Name matches"); + equal(identity.icon, "Icon", "Icon matches"); + equal(identity.color, "Color", "Color matches"); + + equal(cis.getPublicIdentities().length, 5, "Expected 5 containers."); + + ok(!!cis.getPublicIdentityFromId(identity.userContextId), "Identity exists"); + equal( + cis.getPublicIdentityFromId(identity.userContextId).name, + "New Container", + "Identity name is OK" + ); + equal( + cis.getPublicIdentityFromId(identity.userContextId).icon, + "Icon", + "Identity icon is OK" + ); + equal( + cis.getPublicIdentityFromId(identity.userContextId).color, + "Color", + "Identity color is OK" + ); + equal( + cis.getUserContextLabel(identity.userContextId), + "New Container", + "Identity label is OK" + ); + + Assert.deepEqual( + cis.getPublicUserContextIds(), + cis.getPublicIdentities().map(ident => ident.userContextId), + "getPublicUserContextIds has matching user context IDs" + ); + + // Remove an identity + equal( + cis.remove(-1), + false, + "cis.remove() returns false if identity doesn't exist." + ); + equal(cis.remove(1), true, "cis.remove() returns true if identity exists."); + + equal(cis.getPublicIdentities().length, 4, "Expected 4 containers."); + Assert.deepEqual( + cis.getPublicUserContextIds(), + cis.getPublicIdentities().map(ident => ident.userContextId), + "getPublicUserContextIds has matching user context IDs" + ); +}); + +// Reorder identities +add_task(function () { + equal(cis.getPublicIdentities().length, 4, "By default, 4 containers."); + // Get whatever the initial order is + const [id0, id1, id2, id3] = cis.getPublicUserContextIds(); + + ok(cis.move([id3], 0), "Moving one valid id"); + Assert.deepEqual( + cis.getPublicUserContextIds(), + [id3, id0, id1, id2], + "Moving one valid ID works" + ); + + ok(cis.move([id1, id2], 1), "Moving several valid ids"); + Assert.deepEqual( + cis.getPublicUserContextIds(), + [id3, id1, id2, id0], + "Moving several valid IDs works" + ); + + ok(!cis.move([100], 0), "Moving non-existing ids"); + Assert.deepEqual( + cis.getPublicUserContextIds(), + [id3, id1, id2, id0], + "Moving only non-existing IDs leaves list unchanged" + ); + + ok(cis.move([100, id1], 1), "Moving non-existing and existing ids"); + Assert.deepEqual( + cis.getPublicUserContextIds(), + [id3, id1, id2, id0], + "Moving existing and non-existing IDs ignores non-existing ones" + ); + + ok(cis.move([id1, 100], 1), "Moving existing and non-existing ids"); + Assert.deepEqual( + cis.getPublicUserContextIds(), + [id3, id1, id2, id0], + "Moving existing and non-existing IDs ignores non-existing ones" + ); + + ok(cis.move([id2], -1), "Moving to -1"); + Assert.deepEqual( + cis.getPublicUserContextIds(), + [id3, id1, id0, id2], + "Moving to -1 works" + ); + + ok(!cis.move([id3], -10), "Moving to other negative positions"); + Assert.deepEqual( + cis.getPublicUserContextIds(), + [id3, id1, id0, id2], + "Moving to other negative positions leaves list unchanged" + ); + + ok(cis.move([id3], 3), "Moving to last public position"); + Assert.deepEqual( + cis.getPublicUserContextIds(), + [id1, id0, id2, id3], + "Moving to last position correctly skips private context ids" + ); + + ok(cis.move([id1, id2], 1), "Moving past current position"); + Assert.deepEqual( + cis.getPublicUserContextIds(), + [id0, id1, id2, id3], + "Target position is index in resulting list" + ); + + ok(cis.move([id2, id2], 2), "Moving duplicate ids"); + Assert.deepEqual( + cis.getPublicUserContextIds(), + [id0, id1, id2, id3], + "Resulting list does not contain duplicate ids" + ); + + ok(!cis.move([], 2), "Moving empty list"); + Assert.deepEqual( + cis.getPublicUserContextIds(), + [id0, id1, id2, id3], + "Resulting list does not contain duplicate ids" + ); +}); + +// Update an identity +add_task(function () { + ok(!!cis.getPublicIdentityFromId(2), "Identity 2 exists"); + + equal( + cis.update(-1, "Container", "Icon", "Color"), + false, + "Update returns false if the identity doesn't exist" + ); + + equal( + cis.update(2, "Container", "Icon", "Color"), + true, + "Update returns true if everything is OK" + ); + + ok(!!cis.getPublicIdentityFromId(2), "Identity exists"); + equal( + cis.getPublicIdentityFromId(2).name, + "Container", + "Identity name is OK" + ); + equal(cis.getPublicIdentityFromId(2).icon, "Icon", "Identity icon is OK"); + equal(cis.getPublicIdentityFromId(2).color, "Color", "Identity color is OK"); + equal(cis.getUserContextLabel(2), "Container", "Identity label is OK"); +}); diff --git a/toolkit/components/contextualidentity/tests/unit/test_corruptedFile.js b/toolkit/components/contextualidentity/tests/unit/test_corruptedFile.js new file mode 100644 index 0000000000..9dfa13ae2e --- /dev/null +++ b/toolkit/components/contextualidentity/tests/unit/test_corruptedFile.js @@ -0,0 +1,169 @@ +"use strict"; + +const profileDir = do_get_profile(); + +const { ContextualIdentityService } = ChromeUtils.importESModule( + "resource://gre/modules/ContextualIdentityService.sys.mjs" +); + +const TEST_STORE_FILE_PATH = PathUtils.join( + profileDir.path, + "test-containers.json" +); + +const BASE_URL = "http://example.org/"; + +const COOKIE = { + host: BASE_URL, + path: "/", + name: "test", + value: "yes", + isSecure: false, + isHttpOnly: false, + isSession: true, + expiry: 2145934800, +}; + +function createCookie(userContextId) { + Services.cookies.add( + COOKIE.host, + COOKIE.path, + COOKIE.name, + COOKIE.value, + COOKIE.isSecure, + COOKIE.isHttpOnly, + COOKIE.isSession, + COOKIE.expiry, + { userContextId }, + Ci.nsICookie.SAMESITE_NONE, + Ci.nsICookie.SCHEME_HTTP + ); +} + +function hasCookie(userContextId) { + let found = false; + for (let cookie of Services.cookies.getCookiesFromHost(BASE_URL, { + userContextId, + })) { + if (cookie.originAttributes.userContextId == userContextId) { + found = true; + break; + } + } + return found; +} + +// Correpted file should delete all. +add_task(async function corruptedFile() { + const thumbnailPrivateId = ContextualIdentityService._defaultIdentities + .filter(identity => identity.name === "userContextIdInternal.thumbnail") + .pop().userContextId; + + const webextStoragePrivateId = ContextualIdentityService._defaultIdentities + .filter( + identity => identity.name === "userContextIdInternal.webextStorageLocal" + ) + .pop().userContextId; + + // Create a cookie in the default Firefox identity (userContextId 0). + createCookie(0); + + // Create a cookie in the userContextId 1. + createCookie(1); + + // Create a cookie in the thumbnail private userContextId. + createCookie(thumbnailPrivateId); + + // Create a cookie in the extension storage private userContextId. + createCookie(webextStoragePrivateId); + + ok(hasCookie(0), "We have the new cookie the default firefox identity!"); + ok(hasCookie(1), "We have the new cookie in a public identity!"); + ok( + hasCookie(thumbnailPrivateId), + "We have the new cookie in the thumbnail private identity!" + ); + ok( + hasCookie(webextStoragePrivateId), + "We have the new cookie in the extension storage private identity!" + ); + + // Let's create a corrupted file. + await IOUtils.writeUTF8(TEST_STORE_FILE_PATH, "{ vers", { + tmpPath: TEST_STORE_FILE_PATH + ".tmp", + }); + + let cis = + ContextualIdentityService.createNewInstanceForTesting(TEST_STORE_FILE_PATH); + ok(!!cis, "We have our instance of ContextualIdentityService"); + + equal( + cis.getPublicIdentities().length, + 4, + "We should have the default public identities" + ); + + Assert.deepEqual( + cis.getPublicUserContextIds(), + cis.getPublicIdentities().map(identity => identity.userContextId), + "getPublicUserContextIds has matching user context IDs" + ); + + // Verify that when the containers.json file is being rebuilt, the computed lastUserContextId + // is the expected one. + equal( + cis._lastUserContextId, + thumbnailPrivateId, + "Expect cis._lastUserContextId to be equal to the thumbnails userContextId" + ); + + const privThumbnailIdentity = cis.getPrivateIdentity( + "userContextIdInternal.thumbnail" + ); + equal( + privThumbnailIdentity && privThumbnailIdentity.userContextId, + thumbnailPrivateId, + "We should have the default thumbnail private identity" + ); + + const privWebextStorageIdentity = cis.getPrivateIdentity( + "userContextIdInternal.webextStorageLocal" + ); + equal( + privWebextStorageIdentity && privWebextStorageIdentity.userContextId, + webextStoragePrivateId, + "We should have the default extensions storage.local private identity" + ); + + // Cookie is gone! + ok( + !hasCookie(1), + "We should not have the new cookie in the userContextId 1!" + ); + + // The data stored in the Firefox default userContextId (0), should have not be cleared. + ok( + hasCookie(0), + "We should not have the new cookie in the default Firefox identity!" + ); + + // The data stored in the non-public userContextId (e.g. thumbnails private identity) + // should have not be cleared. + ok( + hasCookie(thumbnailPrivateId), + "We should have the new cookie in the thumbnail private userContextId!" + ); + ok( + hasCookie(webextStoragePrivateId), + "We should have the new cookie in the extension storage private userContextId!" + ); + + // Verify the version of the newly created containers.json file. + cis.save(); + const stateFileText = await IOUtils.readUTF8(TEST_STORE_FILE_PATH); + equal( + JSON.parse(stateFileText).version, + cis.LAST_CONTAINERS_JSON_VERSION, + "Expect the new containers.json file to have the expected version" + ); +}); diff --git a/toolkit/components/contextualidentity/tests/unit/test_migratedFile.js b/toolkit/components/contextualidentity/tests/unit/test_migratedFile.js new file mode 100644 index 0000000000..2c7a37f19b --- /dev/null +++ b/toolkit/components/contextualidentity/tests/unit/test_migratedFile.js @@ -0,0 +1,136 @@ +"use strict"; + +const profileDir = do_get_profile(); + +const { ContextualIdentityService } = ChromeUtils.importESModule( + "resource://gre/modules/ContextualIdentityService.sys.mjs" +); + +const TEST_STORE_FILE_PATH = PathUtils.join( + profileDir.path, + "test-containers.json" +); + +// Test the containers JSON file migrations. +add_task(async function migratedFile() { + // Let's create a file that has to be migrated. + const oldFileData = { + version: 2, + lastUserContextId: 6, + identities: [ + { + userContextId: 1, + public: true, + icon: "fingerprint", + color: "blue", + l10nID: "userContextPersonal.label", + accessKey: "userContextPersonal.accesskey", + }, + { + userContextId: 2, + public: true, + icon: "briefcase", + color: "orange", + l10nID: "userContextWork.label", + accessKey: "userContextWork.accesskey", + }, + { + userContextId: 3, + public: true, + icon: "dollar", + color: "green", + l10nID: "userContextBanking.label", + accessKey: "userContextBanking.accesskey", + }, + { + userContextId: 4, + public: true, + icon: "cart", + color: "pink", + l10nID: "userContextShopping.label", + accessKey: "userContextShopping.accesskey", + }, + { + userContextId: 5, + public: false, + icon: "", + color: "", + name: "userContextIdInternal.thumbnail", + accessKey: "", + }, + { + userContextId: 6, + public: true, + icon: "cart", + color: "ping", + name: "Custom user-created identity", + }, + ], + }; + + await IOUtils.writeJSON(TEST_STORE_FILE_PATH, oldFileData, { + tmpPath: TEST_STORE_FILE_PATH + ".tmp", + }); + + let cis = + ContextualIdentityService.createNewInstanceForTesting(TEST_STORE_FILE_PATH); + ok(!!cis, "We have our instance of ContextualIdentityService"); + + // Check that the custom user-created identity exists. + + const expectedPublicLength = oldFileData.identities.filter( + identity => identity.public + ).length; + const publicIdentities = cis.getPublicIdentities(); + const oldLastIdentity = + oldFileData.identities[oldFileData.identities.length - 1]; + const customUserCreatedIdentity = publicIdentities + .filter(identity => identity.name === oldLastIdentity.name) + .pop(); + + equal( + publicIdentities.length, + expectedPublicLength, + "We should have the expected number of public identities" + ); + ok(!!customUserCreatedIdentity, "Got the custom user-created identity"); + + Assert.deepEqual( + cis.getPublicUserContextIds(), + cis.getPublicIdentities().map(identity => identity.userContextId), + "getPublicUserContextIds has matching user context IDs" + ); + + // Check that the reserved userContextIdInternal.webextStorageLocal identity exists. + + const webextStorageLocalPrivateId = + ContextualIdentityService._defaultIdentities + .filter( + identity => identity.name === "userContextIdInternal.webextStorageLocal" + ) + .pop().userContextId; + + const privWebExtStorageLocal = cis.getPrivateIdentity( + "userContextIdInternal.webextStorageLocal" + ); + equal( + privWebExtStorageLocal && privWebExtStorageLocal.userContextId, + webextStorageLocalPrivateId, + "We should have the default userContextIdInternal.webextStorageLocal private identity" + ); + + // Check that all StringBundle references are replaced by Fluent references. + + equal( + cis + .getPublicIdentities() + .filter(identity => identity.l10nID || identity.accessKey).length, + 0, + "No StringBundle l10nID or accessKey should be set" + ); + equal( + cis.getPublicIdentities().filter(identity => identity.l10nId).length, + oldFileData.identities.filter(identity => identity.l10nID).length, + "All StringBundle references should be replaced by Fluent references" + ); +}); diff --git a/toolkit/components/contextualidentity/tests/unit/xpcshell.toml b/toolkit/components/contextualidentity/tests/unit/xpcshell.toml new file mode 100644 index 0000000000..733734eba2 --- /dev/null +++ b/toolkit/components/contextualidentity/tests/unit/xpcshell.toml @@ -0,0 +1,11 @@ +[DEFAULT] +firefox-appdir = "browser" + +["test_basic.js"] +skip-if = ["appname == 'thunderbird'"] + +["test_corruptedFile.js"] +skip-if = ["appname == 'thunderbird'"] + +["test_migratedFile.js"] +skip-if = ["appname == 'thunderbird'"] |