diff --git a/package.json b/package.json index 39d2853..ea3893a 100644 --- a/package.json +++ b/package.json @@ -19,14 +19,14 @@ "json": "^9.0.6", "mocha": "^6.2.2", "npm-run-all": "^4.0.0", - "nyc": "^14.1.1", + "nyc": "^15.0.0", "sinon": "^7.5.0", "sinon-chai": "^3.3.0", "stylelint": "^7.9.0", "stylelint-config-standard": "^16.0.0", "stylelint-order": "^0.3.0", "web-ext": "^2.9.3", - "webextensions-jsdom": "^1.1.0" + "webextensions-jsdom": "^1.2.1" }, "homepage": "https://github.com/mozilla/multi-account-containers#readme", "license": "MPL-2.0", @@ -44,9 +44,8 @@ "lint:js": "eslint .", "package": "rm -rf src/web-ext-artifacts && npm run build && mv src/web-ext-artifacts/firefox_multi-account_containers-*.zip addon.xpi", "test": "npm run lint && npm run coverage", - "mocha": "mocha ./test/setup.js test/**/*.test.js", - "mochaSingle": "mocha ./test/setup.js", - "test-watch": "mocha ./test/setup.js test/**/*.test.js --watch", - "coverage": "nyc --reporter=html --reporter=text mocha ./test/setup.js test/**/*.test.js --timeout 60000" + "test:once": "mocha test/**/*.test.js", + "test:watch": "npm run test:once -- --watch", + "coverage": "nyc --reporter=html --reporter=text mocha test/**/*.test.js --timeout 60000" } } diff --git a/src/js/background/assignManager.js b/src/js/background/assignManager.js index 91ff8e6..9dd6e88 100644 --- a/src/js/background/assignManager.js +++ b/src/js/background/assignManager.js @@ -1,4 +1,4 @@ -const assignManager = { +window.assignManager = { MENU_ASSIGN_ID: "open-in-this-container", MENU_REMOVE_ID: "remove-open-in-this-container", MENU_SEPARATOR_ID: "separator", diff --git a/src/js/background/identityState.js b/src/js/background/identityState.js index 2525f57..8c01eb7 100644 --- a/src/js/background/identityState.js +++ b/src/js/background/identityState.js @@ -1,4 +1,4 @@ -const identityState = { +window.identityState = { storageArea: { area: browser.storage.local, diff --git a/src/js/background/sync.js b/src/js/background/sync.js index cc9b183..f95d122 100644 --- a/src/js/background/sync.js +++ b/src/js/background/sync.js @@ -1,6 +1,6 @@ const SYNC_DEBUG = true; -const sync = { +window.sync = { storageArea: { area: browser.storage.sync, diff --git a/test/.eslintrc.js b/test/.eslintrc.js index d32e265..6d733f0 100644 --- a/test/.eslintrc.js +++ b/test/.eslintrc.js @@ -6,15 +6,7 @@ module.exports = { "parserOptions": { "ecmaVersion": 2018 }, - globals: { - "sinon": false, - "expect": false, - "nextTick": false, - "buildDom": false, - "buildBackgroundDom": false, - "background": false, - "buildPopupDom": false, - "popup": false, - "helper": false + "rules": { + "no-restricted-globals": ["error", "browser"] } } diff --git a/test/setup.js b/test/common.js similarity index 51% rename from test/setup.js rename to test/common.js index b674ba0..b87fe1c 100644 --- a/test/setup.js +++ b/test/common.js @@ -2,15 +2,16 @@ if (!process.listenerCount("unhandledRejection")) { // eslint-disable-next-line no-console process.on("unhandledRejection", r => console.log(r)); } + const path = require("path"); const chai = require("chai"); const sinonChai = require("sinon-chai"); const crypto = require("crypto"); -global.sinon = require("sinon"); -global.expect = chai.expect; +const sinon = require("sinon"); +const expect = chai.expect; chai.should(); chai.use(sinonChai); -global.nextTick = () => { +const nextTick = () => { return new Promise(resolve => { setTimeout(() => { process.nextTick(resolve); @@ -18,12 +19,10 @@ global.nextTick = () => { }); }; -global.helper = require("./helper"); - const webExtensionsJSDOM = require("webextensions-jsdom"); const manifestPath = path.resolve(path.join(__dirname, "../src/manifest.json")); -global.buildDom = async ({background = {}, popup = {}}) => { +const buildDom = async ({background = {}, popup = {}}) => { background = { ...background, jsdom: { @@ -53,33 +52,60 @@ global.buildDom = async ({background = {}, popup = {}}) => { popup }); - // eslint-disable-next-line require-atomic-updates - global.background = webExtension.background; - // eslint-disable-next-line require-atomic-updates - global.popup = webExtension.popup; + webExtension.browser = webExtension.background.browser; + return webExtension; }; -global.buildBackgroundDom = async background => { - await global.buildDom({ +const buildBackgroundDom = background => { + return buildDom({ background, popup: false }); }; -global.buildPopupDom = async popup => { - await global.buildDom({ +const buildPopupDom = popup => { + return buildDom({ popup, background: false }); }; +const initializeWithTab = async (details = { + cookieStoreId: "firefox-default" +}) => { + let tab; + const webExtension = await buildDom({ + background: { + async afterBuild(background) { + tab = await background.browser.tabs._create(details); + } + }, + popup: { + jsdom: { + beforeParse(window) { + window.browser.storage.local.set({ + "browserActionBadgesClicked": [], + "onboarding-stage": 6, + "achievements": [], + "syncEnabled": true + }); + window.browser.storage.local.set.resetHistory(); + window.browser.storage.sync.clear(); + } + } + } + }); + webExtension.tab = tab; -global.afterEach(() => { - if (global.background) { - global.background.destroy(); - } + return webExtension; +}; - if (global.popup) { - global.popup.destroy(); - } -}); +module.exports = { + buildDom, + buildBackgroundDom, + buildPopupDom, + initializeWithTab, + sinon, + expect, + nextTick, +}; \ No newline at end of file diff --git a/test/features/assignment.test.js b/test/features/assignment.test.js index 43bb981..ca50f49 100644 --- a/test/features/assignment.test.js +++ b/test/features/assignment.test.js @@ -1,25 +1,30 @@ -describe("Assignment Feature", () => { +const {initializeWithTab} = require("../common"); + +describe("Assignment Feature", function () { const url = "http://example.com"; - let activeTab; - beforeEach(async () => { - activeTab = await helper.browser.initializeWithTab({ + beforeEach(async function () { + this.webExt = await initializeWithTab({ cookieStoreId: "firefox-container-1", url }); }); - describe("click the 'Always open in' checkbox in the popup", () => { - beforeEach(async () => { + afterEach(function () { + this.webExt.destroy(); + }); + + describe("click the 'Always open in' checkbox in the popup", function () { + beforeEach(async function () { // popup click to set assignment for activeTab.url - await helper.popup.clickElementById("container-page-assigned"); + await this.webExt.popup.helper.clickElementById("container-page-assigned"); }); - describe("open new Tab with the assigned URL in the default container", () => { + describe("open new Tab with the assigned URL in the default container", function () { let newTab; - beforeEach(async () => { + beforeEach(async function () { // new Tab opening activeTab.url in default container - newTab = await helper.browser.openNewTab({ + newTab = await this.webExt.background.browser.tabs._create({ cookieStoreId: "firefox-default", url }, { @@ -29,12 +34,12 @@ describe("Assignment Feature", () => { }); }); - it("should open the confirm page", async () => { + it("should open the confirm page", async function () { // should have created a new tab with the confirm page - background.browser.tabs.create.should.have.been.calledWithMatch({ + this.webExt.background.browser.tabs.create.should.have.been.calledWithMatch({ url: "moz-extension://fake/confirm-page.html?" + `url=${encodeURIComponent(url)}` + - `&cookieStoreId=${activeTab.cookieStoreId}`, + `&cookieStoreId=${this.webExt.tab.cookieStoreId}`, cookieStoreId: undefined, openerTabId: null, index: 2, @@ -42,29 +47,29 @@ describe("Assignment Feature", () => { }); }); - it("should remove the new Tab that got opened in the default container", () => { - background.browser.tabs.remove.should.have.been.calledWith(newTab.id); + it("should remove the new Tab that got opened in the default container", function () { + this.webExt.background.browser.tabs.remove.should.have.been.calledWith(newTab.id); }); }); - describe("click the 'Always open in' checkbox in the popup again", () => { - beforeEach(async () => { + describe("click the 'Always open in' checkbox in the popup again", function () { + beforeEach(async function () { // popup click to remove assignment for activeTab.url - await helper.popup.clickElementById("container-page-assigned"); + await this.webExt.popup.helper.clickElementById("container-page-assigned"); }); - describe("open new Tab with the no longer assigned URL in the default container", () => { - beforeEach(async () => { + describe("open new Tab with the no longer assigned URL in the default container", function () { + beforeEach(async function () { // new Tab opening activeTab.url in default container - await helper.browser.openNewTab({ + await this.webExt.background.browser.tabs._create({ cookieStoreId: "firefox-default", url }); }); - it("should not open the confirm page", async () => { + it("should not open the confirm page", async function () { // should not have created a new tab - background.browser.tabs.create.should.not.have.been.called; + this.webExt.background.browser.tabs.create.should.not.have.been.called; }); }); }); diff --git a/test/features/containers.test.js b/test/features/containers.test.js index ac5bccd..d50a6b8 100644 --- a/test/features/containers.test.js +++ b/test/features/containers.test.js @@ -1,27 +1,33 @@ -describe("Containers Management", () => { - beforeEach(async () => { - await helper.browser.initializeWithTab(); +const {initializeWithTab} = require("../common"); + +describe("Containers Management", function () { + beforeEach(async function () { + this.webExt = await initializeWithTab(); }); - describe("creating a new container", () => { - beforeEach(async () => { - await helper.popup.clickElementById("container-add-link"); - await helper.popup.clickElementById("edit-container-ok-link"); + afterEach(function () { + this.webExt.destroy(); + }); + + describe("creating a new container", function () { + beforeEach(async function () { + await this.webExt.popup.helper.clickElementById("container-add-link"); + await this.webExt.popup.helper.clickElementById("edit-container-ok-link"); }); - it("should create it in the browser as well", () => { - background.browser.contextualIdentities.create.should.have.been.calledOnce; + it("should create it in the browser as well", function () { + this.webExt.background.browser.contextualIdentities.create.should.have.been.calledOnce; }); - describe("removing it afterwards", () => { - beforeEach(async () => { - await helper.popup.clickElementById("edit-containers-link"); - await helper.popup.clickLastMatchingElementByQuerySelector(".delete-container-icon"); - await helper.popup.clickElementById("delete-container-ok-link"); + describe("removing it afterwards", function () { + beforeEach(async function () { + await this.webExt.popup.helper.clickElementById("edit-containers-link"); + await this.webExt.popup.helper.clickElementByQuerySelectorAll(".delete-container-icon", "last"); + await this.webExt.popup.helper.clickElementById("delete-container-ok-link"); }); - it("should remove it in the browser as well", () => { - background.browser.contextualIdentities.remove.should.have.been.calledOnce; + it("should remove it in the browser as well", function () { + this.webExt.background.browser.contextualIdentities.remove.should.have.been.calledOnce; }); }); }); diff --git a/test/features/external-webextensions.test.js b/test/features/external-webextensions.test.js index 76db303..f3c6810 100644 --- a/test/features/external-webextensions.test.js +++ b/test/features/external-webextensions.test.js @@ -1,17 +1,24 @@ -describe("External Webextensions", () => { +const {expect, initializeWithTab} = require("../common"); + +describe("External Webextensions", function () { const url = "http://example.com"; - beforeEach(async () => { - await helper.browser.initializeWithTab({ + beforeEach(async function () { + this.webExt = await initializeWithTab({ cookieStoreId: "firefox-container-1", url }); - await helper.popup.clickElementById("container-page-assigned"); + + await this.webExt.popup.helper.clickElementById("container-page-assigned"); }); - describe("with contextualIdentities permissions", () => { - it("should be able to get assignments", async () => { - background.browser.management.get.resolves({ + afterEach(function () { + this.webExt.destroy(); + }); + + describe("with contextualIdentities permissions", function () { + it("should be able to get assignments", async function () { + this.webExt.background.browser.management.get.resolves({ permissions: ["contextualIdentities"] }); @@ -23,7 +30,7 @@ describe("External Webextensions", () => { id: "external-webextension" }; - const [promise] = background.browser.runtime.onMessageExternal.addListener.yield(message, sender); + const [promise] = this.webExt.background.browser.runtime.onMessageExternal.addListener.yield(message, sender); const answer = await promise; expect(answer.userContextId === "1").to.be.true; expect(answer.neverAsk === false).to.be.true; @@ -33,9 +40,9 @@ describe("External Webextensions", () => { }); }); - describe("without contextualIdentities permissions", () => { - it("should throw an error", async () => { - background.browser.management.get.resolves({ + describe("without contextualIdentities permissions", function () { + it("should throw an error", async function () { + this.webExt.background.browser.management.get.resolves({ permissions: [] }); @@ -47,7 +54,7 @@ describe("External Webextensions", () => { id: "external-webextension" }; - const [promise] = background.browser.runtime.onMessageExternal.addListener.yield(message, sender); + const [promise] = this.webExt.background.browser.runtime.onMessageExternal.addListener.yield(message, sender); return promise.catch(error => { expect(error.message).to.equal("Missing contextualIdentities permission"); }); diff --git a/test/features/sync.test.js b/test/features/sync.test.js index fdfa1b8..bd6341b 100644 --- a/test/features/sync.test.js +++ b/test/features/sync.test.js @@ -1,64 +1,695 @@ -describe("Sync", () => { +const {initializeWithTab} = require("../common"); - it("should init sync on startup", async () => { - const tab = await helper.browser.initializeWithTab(); - console.log(await background.browser.storage.local.get()); - const mozContainer = await background.browser.contextualIdentities.create({ - name: "Mozilla", - color: "red", - icon: "briefcase", - }); - await background.browser.contextualIdentities.update("firefox-container-2", {color:"purple"}); - await background.browser.contextualIdentities.update("firefox-container-4", {icon:"pet"}); - - await Promise.all([ - { - userContextId: "1", - url: "https://twitter.com", - }, - { - userContextId: "2", - url: "https://www.facebook.com", - }, - { - userContextId: "4", - url: "https://www.linkedin.com", - neverAsk: true, - }, - { - userContextId: mozContainer.cookieStoreId.replace("firefox-container-", ""), - url: "https://developer.mozilla.org", - neverAsk: true, - } - ].map(async (assign) => { - await background.browser.tabs.update(tab.id, { - cookieStoreId: `firefox-container-${assign.userContextId}` - }); - - await background.browser.runtime.onMessage.addListener.yield({ - method: "setOrRemoveAssignment", - tabId: tab.id, - url: assign.url, - userContextId: assign.userContextId, - value: !true - }); - - if (assign.neverAsk) { - await nextTick(); - await background.browser.runtime.onMessage.addListener.yield({ - method: "neverAsk", - neverAsk: true, - pageUrl: assign.url, - }); - } - })); - - // await background.browser.storage.onChanged.addListener.yield(); - await nextTick(); - - const sync = await background.browser.storage.sync.get(); - console.log("sync", sync); - // expect(sync.length).to.equal(4); +describe("Sync", function() { + beforeEach(async function() { + this.webExt = await initializeWithTab(); + this.syncHelper = new SyncTestHelper(this.webExt); }); -}); \ No newline at end of file + afterEach(function() { + this.webExt.destroy(); + delete this.syncHelper; + }); + + it("testIdentityStateCleanup", async function() { + await this.syncHelper.stopSyncListeners(); + console.log("Testing the cleanup of local storage"); + + await this.syncHelper.setState({}, LOCAL_DATA, TEST_CONTAINERS, []); + + await this.webExt.browser.storage.local.set({ + "identitiesState@@_firefox-container-5": { + "hiddenTabs": [] + } + }); + console.log("local storage set: ", await this.webExt.browser.storage.local.get()); + + await this.webExt.background.window.identityState.storageArea.upgradeData(); + + const macConfigs = await this.webExt.browser.storage.local.get(); + const identities = []; + for(const configKey of Object.keys(macConfigs)) { + if (configKey.includes("identitiesState@@_") && !configKey.includes("default")) { + identities.push(macConfigs[configKey]); + } + } + + console.assert(identities.length === 5, "There should be 5 identity entries"); + for (const identity of identities) { + console.assert(!!identity.macAddonUUID, `${identity.name} should have a uuid`); + } + console.log("!!!Finished!!!"); + }); + + it("testAssignManagerCleanup", async function() { + await this.syncHelper.stopSyncListeners(); + console.log("Testing the cleanup of local storage"); + + await this.syncHelper.setState({}, LOCAL_DATA, TEST_CONTAINERS, TEST_ASSIGNMENTS); + + await this.webExt.browser.storage.local.set({ + "siteContainerMap@@_www.goop.com": { + "userContextId": "6", + "neverAsk": true, + "hostname": "www.goop.com" + } + }); + console.log("local storage set: ", await this.webExt.browser.storage.local.get()); + + await this.webExt.background.window.identityState.storageArea.upgradeData(); + await this.webExt.background.window.assignManager.storageArea.upgradeData(); + + const macConfigs = await this.webExt.browser.storage.local.get(); + const assignments = []; + for(const configKey of Object.keys(macConfigs)) { + if (configKey.includes("siteContainerMap@@_")) { + macConfigs[configKey].configKey = configKey; + assignments.push(macConfigs[configKey]); + } + } + + console.assert(assignments.length === 5, "There should be 5 identity entries"); + for (const assignment of assignments) { + console.log(assignment); + console.assert(!!assignment.identityMacAddonUUID, `${assignment.configKey} should have a uuid`); + } + console.log("!!!Finished!!!"); + }); + + it("testReconcileSiteAssignments", async function() { + await this.syncHelper.stopSyncListeners(); + console.log("Testing reconciling Site Assignments"); + + await this.syncHelper.setState( + DUPE_TEST_SYNC, + LOCAL_DATA, + TEST_CONTAINERS, + SITE_ASSIGNMENT_TEST + ); + + // add 200ok (bad data). + const testSites = { + "siteContainerMap@@_developer.mozilla.org": { + "userContextId": "588", + "neverAsk": true, + "identityMacAddonUUID": "d20d7af2-9866-468e-bb43-541efe8c2c2e", + "hostname": "developer.mozilla.org" + }, + "siteContainerMap@@_reddit.com": { + "userContextId": "592", + "neverAsk": true, + "identityMacAddonUUID": "3dc916fb-8c0a-4538-9758-73ef819a45f7", + "hostname": "reddit.com" + }, + "siteContainerMap@@_twitter.com": { + "userContextId": "589", + "neverAsk": true, + "identityMacAddonUUID": "cdd73c20-c26a-4c06-9b17-735c1f5e9187", + "hostname": "twitter.com" + }, + "siteContainerMap@@_www.facebook.com": { + "userContextId": "590", + "neverAsk": true, + "identityMacAddonUUID": "32cc4a9b-05ed-4e54-8e11-732468de62f4", + "hostname": "www.facebook.com" + }, + "siteContainerMap@@_www.linkedin.com": { + "userContextId": "591", + "neverAsk": true, + "identityMacAddonUUID": "9ff381e3-4c11-420d-8e12-e352a3318be1", + "hostname": "www.linkedin.com" + }, + "siteContainerMap@@_200ok.us": { + "userContextId": "1", + "neverAsk": true, + "identityMacAddonUUID": "b5f5f794-b37e-4cec-9f4e-6490df620336", + "hostname": "www.linkedin.com" + } + }; + + for (const site of Object.keys(testSites)) { + await this.webExt.browser.storage.sync.set({[site]:testSites[site]}); + } + + await this.webExt.browser.storage.sync.set({ + deletedSiteList: ["siteContainerMap@@_www.google.com"] + }); + console.log(await this.webExt.browser.storage.sync.get()); + await this.webExt.background.window.sync.runSync(); + + const assignedSites = await this.webExt.background.window.assignManager.storageArea.getAssignedSites(); + Object.keys(assignedSites).should.have.lengthOf(6); + console.log("!!!Finished!!!"); + }); + + it("testInitialSync", async function() { + await this.syncHelper.stopSyncListeners(); + console.log("Testing new install with no sync"); + + await this.syncHelper.setState({}, LOCAL_DATA, TEST_CONTAINERS, []); + + await this.webExt.background.window.sync.runSync(); + + const getAssignedSites = + await this.webExt.background.window.assignManager.storageArea.getAssignedSites(); + const identities = await this.webExt.browser.contextualIdentities.query({}); + + console.assert( + identities.length === 5, + "There should be 5 identities" + ); + + console.assert( + Object.keys(getAssignedSites).length === 0, + "There should be no site assignments" + ); + console.log("!!!Finished!!!"); + }); + + it("test2", async function() { + await this.syncHelper.stopSyncListeners(); + console.log("Testing sync differing from local"); + + await this.syncHelper.setState(SYNC_DATA, LOCAL_DATA, TEST_CONTAINERS, TEST_ASSIGNMENTS); + + await this.webExt.background.window.sync.runSync(); + + const getAssignedSites = + await this.webExt.background.window.assignManager.storageArea.getAssignedSites(); + + const identities = await this.webExt.browser.contextualIdentities.query({}); + + console.assert( + identities.length === 6, + "There should be 6 identities" + ); + + console.assert( + Object.keys(getAssignedSites).length === 5, + "There should be 5 site assignments" + ); + console.log("!!!Finished!!!"); + }); + + it("dupeTest", async function() { + await this.syncHelper.stopSyncListeners(); + console.log("Test state from sync that duped everything initially"); + + await this.syncHelper.setState( + DUPE_TEST_SYNC, + DUPE_TEST_LOCAL, + DUPE_TEST_IDENTS, + DUPE_TEST_ASSIGNMENTS + ); + + await this.webExt.background.window.sync.runSync(); + + const getAssignedSites = + await this.webExt.background.window.assignManager.storageArea.getAssignedSites(); + + const identities = await this.webExt.browser.contextualIdentities.query({}); + + console.assert( + identities.length === 7, + "There should be 7 identities" + ); + + console.assert( + Object.keys(getAssignedSites).length === 5, + "There should be 5 site assignments" + ); + + const personalContainer = + this.syncHelper.lookupIdentityBy(identities, {name: "Personal"}); + console.log(personalContainer); + console.assert( + personalContainer.color === "red", + "Personal Container should be red" + ); + const mozillaContainer = + this.syncHelper.lookupIdentityBy(identities, {name: "Mozilla"}); + console.assert( + mozillaContainer.icon === "pet", + "Mozilla Container should be pet" + ); + console.log("!!!Finished!!!"); + }); + + it("CIerrorTest", async function() { + await this.syncHelper.stopSyncListeners(); + console.log("Test state from sync that duped everything initially"); + + await this.syncHelper.setState( + CI_ERROR_TEST_SYNC, + CI_ERROR_TEST_LOCAL, + CI_ERROR_TEST_IDENTS, + CI_ERROR_TEST_SITES + ); + + await this.webExt.background.window.sync.runSync(); + + const getSync = await this.webExt.browser.storage.sync.get(); + const getAssignedSites = + await this.webExt.background.window.assignManager.storageArea.getAssignedSites(); + + const identities = await this.webExt.browser.contextualIdentities.query({}); + + console.assert( + Object.keys(getSync.cookieStoreIDmap).length === 7, + "cookieStoreIDmap should have 7 entries" + ); + + console.assert( + identities.length === 7, + "There should be 7 identities" + ); + + console.assert( + Object.keys(getAssignedSites).length === 5, + "There should be 5 site assignments" + ); + + const personalContainer = + this.syncHelper.lookupIdentityBy(identities, {name: "Personal"}); + console.log(personalContainer); + console.assert( + personalContainer.color === "red", + "Personal Container should be red" + ); + const mozillaContainer = + this.syncHelper.lookupIdentityBy(identities, {name: "Mozilla"}); + console.assert( + mozillaContainer.icon === "pet", + "Mozilla Container should be pet" + ); + console.log("!!!Finished!!!"); + }); +}); + +class SyncTestHelper { + constructor(webExt) { + this.webExt = webExt; + } + + async stopSyncListeners() { + await this.webExt.browser.storage.onChanged.removeListener(this.webExt.background.window.sync.storageArea.onChangedListener); + await this.webExt.background.window.sync.removeContextualIdentityListeners(); + } + + async setState(syncData, localData, identityData, assignmentData){ + await this.removeAllContainers(); + await this.webExt.browser.storage.sync.clear(); + await this.webExt.browser.storage.sync.set(syncData); + await this.webExt.browser.storage.local.clear(); + await this.webExt.browser.storage.local.set(localData); + for (let i=0; i < identityData.length; i++) { + //build identities + const newIdentity = + await this.webExt.browser.contextualIdentities.create(identityData[i]); + // fill identies with site assignments + if (assignmentData && assignmentData[i]) { + const data = { + "userContextId": + String( + newIdentity.cookieStoreId.replace(/^firefox-container-/, "") + ), + "neverAsk": true + }; + + await this.webExt.browser.storage.local.set({[assignmentData[i]]: data}); + } + } + console.log("local storage set: ", await this.webExt.browser.storage.local.get()); + return; + } + + async removeAllContainers() { + const identities = await this.webExt.browser.contextualIdentities.query({}); + console.log(identities); + for (const identity of identities) { + await this.webExt.browser.contextualIdentities.remove(identity.cookieStoreId); + } + } + + lookupIdentityBy(identities, options) { + for (const identity of identities) { + if (options && options.name) { + if (identity.name === options.name) return identity; + } + if (options && options.color) { + if (identity.color === options.color) return identity; + } + if (options && options.color) { + if (identity.color === options.color) return identity; + } + } + return false; + } +} + +const TEST_CONTAINERS = [ + { + name: "Personal", + color: "blue", + icon: "fingerprint" + }, + { + name: "Banking", + color: "green", + icon: "dollar" + }, + { + name: "Mozilla", + color: "red", + icon: "briefcase" + }, + { + name: "Groceries, obviously", + color: "yellow", + icon: "cart" + }, + { + name: "Facebook", + color: "toolbar", + icon: "fence" + }, +]; + +const TEST_ASSIGNMENTS = [ + "siteContainerMap@@_developer.mozilla.org", + "siteContainerMap@@_twitter.com", + "siteContainerMap@@_www.facebook.com", + "siteContainerMap@@_www.linkedin.com", + "siteContainerMap@@_reddit.com" +]; + +const LOCAL_DATA = { + "browserActionBadgesClicked": [ "6.1.1" ], + "containerTabsOpened": 7, + "identitiesState@@_firefox-default": { "hiddenTabs": [] }, + "onboarding-stage": 5 +}; + +const SYNC_DATA = { + "identity@@_22ded543-5173-44a5-a47a-8813535945ca": { + "name": "Personal", + "icon": "fingerprint", + "color": "red", + "cookieStoreId": "firefox-container-146", + "macAddonUUID": "22ded543-5173-44a5-a47a-8813535945ca" + }, + "identity@@_63e5212f-0858-418e-b5a3-09c2dea61fcd": { + "name": "Oscar", + "icon": "dollar", + "color": "green", + "cookieStoreId": "firefox-container-147", + "macAddonUUID": "3e5212f-0858-418e-b5a3-09c2dea61fcd" + }, + "identity@@_71335417-158e-4d74-a55b-e9e9081601ec": { + "name": "Mozilla", + "icon": "pet", + "color": "red", + "cookieStoreId": "firefox-container-148", + "macAddonUUID": "71335417-158e-4d74-a55b-e9e9081601ec" + }, + "identity@@_59c4e5f7-fe3b-435a-ae60-1340db31a91b": { + "name": "Groceries, obviously", + "icon": "cart", + "color": "pink", + "cookieStoreId": "firefox-container-149", + "macAddonUUID": "59c4e5f7-fe3b-435a-ae60-1340db31a91b" + }, + "identity@@_3dc916fb-8c0a-4538-9758-73ef819a45f7": { + "name": "Facebook", + "icon": "fence", + "color": "toolbar", + "cookieStoreId": "firefox-container-150", + "macAddonUUID": "3dc916fb-8c0a-4538-9758-73ef819a45f7" + } +}; + +const DUPE_TEST_SYNC = { + "identity@@_d20d7af2-9866-468e-bb43-541efe8c2c2e": { + "name": "Personal", + "icon": "fingerprint", + "color": "red", + "cookieStoreId": "firefox-container-588", + "macAddonUUID": "d20d7af2-9866-468e-bb43-541efe8c2c2e" + }, + "identity@@_cdd73c20-c26a-4c06-9b17-735c1f5e9187": { + "name": "Big Bird", + "icon": "pet", + "color": "yellow", + "cookieStoreId": "firefox-container-589", + "macAddonUUID": "cdd73c20-c26a-4c06-9b17-735c1f5e9187" + }, + "identity@@_32cc4a9b-05ed-4e54-8e11-732468de62f4": { + "name": "Mozilla", + "icon": "pet", + "color": "red", + "cookieStoreId": "firefox-container-590", + "macAddonUUID": "32cc4a9b-05ed-4e54-8e11-732468de62f4" + }, + "identity@@_9ff381e3-4c11-420d-8e12-e352a3318be1": { + "name": "Groceries, obviously", + "icon": "cart", + "color": "pink", + "cookieStoreId": "firefox-container-591", + "macAddonUUID": "9ff381e3-4c11-420d-8e12-e352a3318be1" + }, + "identity@@_3dc916fb-8c0a-4538-9758-73ef819a45f7": { + "name": "Facebook", + "icon": "fence", + "color": "toolbar", + "cookieStoreId": "firefox-container-592", + "macAddonUUID": "3dc916fb-8c0a-4538-9758-73ef819a45f7" + }, + "identity@@_63e5212f-0858-418e-b5a3-09c2dea61fcd": { + "name": "Oscar", + "icon": "dollar", + "color": "green", + "cookieStoreId": "firefox-container-593", + "macAddonUUID": "63e5212f-0858-418e-b5a3-09c2dea61fcd" + }, + "siteContainerMap@@_developer.mozilla.org": { + "userContextId": "588", + "neverAsk": true, + "identityMacAddonUUID": "d20d7af2-9866-468e-bb43-541efe8c2c2e", + "hostname": "developer.mozilla.org" + }, + "siteContainerMap@@_reddit.com": { + "userContextId": "592", + "neverAsk": true, + "identityMacAddonUUID": "3dc916fb-8c0a-4538-9758-73ef819a45f7", + "hostname": "reddit.com" + }, + "siteContainerMap@@_twitter.com": { + "userContextId": "589", + "neverAsk": true, + "identityMacAddonUUID": "cdd73c20-c26a-4c06-9b17-735c1f5e9187", + "hostname": "twitter.com" + }, + "siteContainerMap@@_www.facebook.com": { + "userContextId": "590", + "neverAsk": true, + "identityMacAddonUUID": "32cc4a9b-05ed-4e54-8e11-732468de62f4", + "hostname": "www.facebook.com" + }, + "siteContainerMap@@_www.linkedin.com": { + "userContextId": "591", + "neverAsk": true, + "identityMacAddonUUID": "9ff381e3-4c11-420d-8e12-e352a3318be1", + "hostname": "www.linkedin.com" + } +}; + +const DUPE_TEST_LOCAL = { + "beenSynced": true, + "browserActionBadgesClicked": [ + "6.1.1" + ], + "containerTabsOpened": 7, + "identitiesState@@_firefox-default": { + "hiddenTabs": [] + }, + "onboarding-stage": 5, +}; + +const DUPE_TEST_ASSIGNMENTS = [ + "siteContainerMap@@_developer.mozilla.org", + "siteContainerMap@@_reddit.com", + "siteContainerMap@@_twitter.com", + "siteContainerMap@@_www.facebook.com", + "siteContainerMap@@_www.linkedin.com" +]; + +const SITE_ASSIGNMENT_TEST = [ + "siteContainerMap@@_developer.mozilla.org", + "siteContainerMap@@_www.facebook.com", + "siteContainerMap@@_www.google.com", + "siteContainerMap@@_bugzilla.mozilla.org" +]; + +const DUPE_TEST_IDENTS = [ + { + "name": "Personal", + "icon": "fingerprint", + "color": "blue", + }, + { + "name": "Banking", + "icon": "pet", + "color": "green", + }, + { + "name": "Mozilla", + "icon": "briefcase", + "color": "red", + }, + { + "name": "Groceries, obviously", + "icon": "cart", + "color": "orange", + }, + { + "name": "Facebook", + "icon": "fence", + "color": "toolbar", + }, + { + "name": "Big Bird", + "icon": "dollar", + "color": "yellow", + } +]; + +const CI_ERROR_TEST_SYNC = { + "identities": [ + { + "name": "Personal", + "icon": "fingerprint", + "iconUrl": "resource://usercontext-content/fingerprint.svg", + "color": "blue", + "colorCode": "#37adff", + "cookieStoreId": "firefox-container-6" + }, + { + "name": "Mozilla", + "icon": "fruit", + "iconUrl": "resource://usercontext-content/fruit.svg", + "color": "purple", + "colorCode": "#af51f5", + "cookieStoreId": "firefox-container-8" + }, + { + "name": "Groceries, obviously", + "icon": "cart", + "iconUrl": "resource://usercontext-content/cart.svg", + "color": "yellow", + "colorCode": "#ffcb00", + "cookieStoreId": "firefox-container-9" + }, + { + "name": "Facebook", + "icon": "circle", + "iconUrl": "resource://usercontext-content/circle.svg", + "color": "blue", + "colorCode": "#37adff", + "cookieStoreId": "firefox-container-10" + }, + { + "name": "Work", + "icon": "briefcase", + "iconUrl": "resource://usercontext-content/briefcase.svg", + "color": "orange", + "colorCode": "#ff9f00", + "cookieStoreId": "firefox-container-11" + }, + { + "name": "Greg's container", + "icon": "vacation", + "iconUrl": "resource://usercontext-content/vacation.svg", + "color": "yellow", + "colorCode": "#ffcb00", + "cookieStoreId": "firefox-container-14" + } + ], + "deletedIdentityList": [ + "8098140e-d406-4321-b4f5-24763b4f9513", + "73aebc7a-286f-408a-9a94-a06d29b288e0", + "8f153224-bbe8-4664-ba02-0293ddec3e78" + ], + "cookieStoreIDmap": { + "firefox-container-10": "58956e95-43fb-44af-95c0-1ec8d83e1e13", + "firefox-container-11": "0269558d-6be7-487b-beb1-b720b346d09b", + "firefox-container-14": "e48d04cf-6277-4236-8f3d-611287d0caf2", + "firefox-container-6": "869a7563-030d-4a63-8a84-209270561d3c", + "firefox-container-8": "73aebc7a-286f-408a-9a94-a06d29b288e0", + "firefox-container-9": "4831fef4-6f43-47fb-a578-ccdc3ee7f883" + }, + "assignedSites": { + "siteContainerMap@@_bugzilla.mozilla.org": { + "userContextId": "11", + "neverAsk": true, + "identityMacAddonUUID": "0269558d-6be7-487b-beb1-b720b346d09b", + "hostname": "bugzilla.mozilla.org" + }, + "siteContainerMap@@_www.amazon.com": { + "userContextId": "14", + "neverAsk": false, + "identityMacAddonUUID": "e48d04cf-6277-4236-8f3d-611287d0caf2", + "hostname": "www.amazon.com" + } + }, + "deletedSiteList": [ + "siteContainerMap@@_www.facebook.com" + ] +}; + +const CI_ERROR_TEST_LOCAL = { + "browserActionBadgesClicked": [ + "6.1.1" + ], + "containerTabsOpened": 6, + "onboarding-stage": 5, +}; + +const CI_ERROR_TEST_SITES = [ + "siteContainerMap@@_bugzilla.mozilla.org", + "siteContainerMap@@_www.bankofoklahoma.com", + "siteContainerMap@@_www.mozilla.org", + "siteContainerMap@@_www.reddit.com" +]; + +const CI_ERROR_TEST_IDENTS = [ + { + "name": "Personal", + "icon": "fingerprint", + "color": "blue", + }, + { + "name": "Work", + "icon": "briefcase", + "color": "orange", + }, + { + "name": "Banking", + "icon": "dollar", + "color": "green", + }, + { + "name": "Mozilla", + "icon": "fruit", + "color": "purple", + }, + { + "name": "Groceries, obviously", + "icon": "cart", + "color": "yellow", + }, + { + "name": "Facebook", + "icon": "circle", + "color": "blue", + } +]; \ No newline at end of file diff --git a/test/helper.js b/test/helper.js deleted file mode 100644 index 24363a6..0000000 --- a/test/helper.js +++ /dev/null @@ -1,81 +0,0 @@ -module.exports = { - browser: { - async initializeWithTab(details = { - cookieStoreId: "firefox-default" - }) { - let tab; - await buildDom({ - background: { - async afterBuild(background) { - tab = await background.browser.tabs._create(details); - } - }, - popup: { - jsdom: { - beforeParse(window) { - window.browser.storage.local.set({ - "browserActionBadgesClicked": [], - "onboarding-stage": 6, - "achievements": [], - "syncEnabled": true - }); - window.browser.storage.local.set.resetHistory(); - window.browser.storage.sync.clear(); - } - } - } - }); - - return tab; - }, - - async openNewTab(tab, options = {}) { - return background.browser.tabs._create(tab, options); - }, - - async initSyncTest(details = {}) { - if (!details.cookieStoreId) details.cookieStoreId = "firefox-default"; - if (!details.localStorage) { - details.localStorage = { - "browserActionBadgesClicked": [], - "onboarding-stage": 6, - "achievements": [], - "syncEnabled": true - }; - } - if (!details.syncStorage) details.syncStorage = {}; - let tab; - await buildDom({ - background: { - async afterBuild(background) { - tab = await background.browser.tabs._create({ cookieStoreId: details.cookieStoreId }); - } - }, - popup: { - jsdom: { - beforeParse(window) { - window.browser.storage.clear(); - window.browser.storage.local.set(details.localStorage); - window.browser.storage.local.set.resetHistory(); - window.browser.storage.sync.clear(); - window.browser.storage.sync.set(details.syncStorage); - window.browser.storage.sync.set.resetHistory(); - } - }, - } - }); - - return tab; - }, - }, - - popup: { - async clickElementById(id) { - await popup.helper.clickElementById(id); - }, - - async clickLastMatchingElementByQuerySelector(querySelector) { - await popup.helper.clickElementByQuerySelectorAll(querySelector, "last"); - } - } -}; diff --git a/test/issues/1168.test.js b/test/issues/1168.test.js index 50f463a..45d87e6 100644 --- a/test/issues/1168.test.js +++ b/test/issues/1168.test.js @@ -1,16 +1,19 @@ -describe("#1168", () => { - describe("when navigation happens too slow after opening new tab to a page which then redirects", () => { - let clock, tab; +const {expect, sinon, initializeWithTab} = require("../common"); - beforeEach(async () => { - await helper.browser.initializeWithTab({ +describe("#1168", function () { + describe("when navigation happens too slow after opening new tab to a page which then redirects", function () { + let clock, tab, background; + + beforeEach(async function () { + this.webExt = await initializeWithTab({ cookieStoreId: "firefox-container-1", url: "https://bugzilla.mozilla.org" }); - await helper.popup.clickElementById("container-page-assigned"); + + await this.webExt.popup.helper.clickElementById("container-page-assigned"); clock = sinon.useFakeTimers(); - tab = await helper.browser.openNewTab({}); + tab = await this.webExt.browser.tabs._create({}); clock.tick(2000); @@ -20,15 +23,16 @@ describe("#1168", () => { ]); }); + afterEach(function () { + this.webExt.destroy(); + clock.restore(); + }); + // Not solved yet // See: https://github.com/mozilla/multi-account-containers/issues/1168#issuecomment-378394091 - it.skip("should remove the old tab", async () => { + it.skip("should remove the old tab", async function () { expect(background.browser.tabs.create).to.have.been.calledOnce; expect(background.browser.tabs.remove).to.have.been.calledWith(tab.id); }); - - afterEach(() => { - clock.restore(); - }); }); }); \ No newline at end of file diff --git a/test/issues/940.test.js b/test/issues/940.test.js index 4a7eb0e..bbe4a64 100644 --- a/test/issues/940.test.js +++ b/test/issues/940.test.js @@ -1,15 +1,18 @@ -describe("#940", () => { - describe("when other onBeforeRequestHandlers are faster and redirect with the same requestId", () => { - it("should not open two confirm pages", async () => { - await helper.browser.initializeWithTab({ +const {expect, sinon, initializeWithTab} = require("../common"); + +describe("#940", function () { + describe("when other onBeforeRequestHandlers are faster and redirect with the same requestId", function () { + it("should not open two confirm pages", async function () { + const webExtension = await initializeWithTab({ cookieStoreId: "firefox-container-1", url: "http://example.com" }); - await helper.popup.clickElementById("container-page-assigned"); + + await webExtension.popup.helper.clickElementById("container-page-assigned"); const responses = {}; - await helper.browser.openNewTab({ - url: "http://example.com" + await webExtension.background.browser.tabs._create({ + url: "https://example.com" }, { options: { webRequestRedirects: ["https://example.com"], @@ -23,46 +26,55 @@ describe("#940", () => { expect(result).to.deep.equal({ cancel: true }); - background.browser.tabs.create.should.have.been.calledOnce; + webExtension.browser.tabs.create.should.have.been.calledOnce; + + webExtension.destroy(); }); }); - describe("when redirects change requestId midflight", () => { - let newTab; - const newTabResponses = {}; - const redirectedRequest = async (options = {}) => { - global.clock = sinon.useFakeTimers(); - newTab = await helper.browser.openNewTab({ - url: "http://youtube.com" - }, { - options: Object.assign({ - webRequestRedirects: [ - "https://youtube.com", - "https://www.youtube.com", - { - url: "https://www.youtube.com", - webRequest: { - requestId: 2 - } - } - ], - webRequestError: true, - instantRedirects: true - }, options), - responses: newTabResponses - }); - }; - - beforeEach(async () => { - await helper.browser.initializeWithTab({ + describe("when redirects change requestId midflight", function () { + beforeEach(async function () { + + this.webExt = await initializeWithTab({ cookieStoreId: "firefox-container-1", url: "https://www.youtube.com" }); - await helper.popup.clickElementById("container-page-assigned"); + await this.webExt.popup.helper.clickElementById("container-page-assigned"); + + global.clock = sinon.useFakeTimers(); + this.redirectedRequest = async (options = {}) => { + const newTabResponses = {}; + const newTab = await this.webExt.browser.tabs._create({ + url: "http://youtube.com" + }, { + options: Object.assign({ + webRequestRedirects: [ + "https://youtube.com", + "https://www.youtube.com", + { + url: "https://www.youtube.com", + webRequest: { + requestId: 2 + } + } + ], + webRequestError: true, + instantRedirects: true + }, options), + responses: newTabResponses + }); + + return [newTabResponses, newTab]; + }; }); - it("should not open two confirm pages", async () => { - await redirectedRequest(); + afterEach(function () { + this.webExt.destroy(); + global.clock.restore(); + }); + + it("should not open two confirm pages", async function () { + const [newTabResponses] = await this.redirectedRequest(); // http://youtube.com is not assigned, no cancel, no reopening expect(await newTabResponses.webRequest.onBeforeRequest[0]).to.deep.equal({}); @@ -80,17 +92,17 @@ describe("#940", () => { cancel: true }); - background.browser.tabs.create.should.have.been.calledOnce; + this.webExt.background.browser.tabs.create.should.have.been.calledOnce; }); - it("should uncancel after webRequest.onCompleted", async () => { - await redirectedRequest(); + it("should uncancel after webRequest.onCompleted", async function () { + const [newTabResponses, newTab] = await this.redirectedRequest(); // remove onCompleted listeners because in the real world this request would never complete // and thus might trigger unexpected behavior because the tab gets removed when reopening - background.browser.webRequest.onCompleted.addListener = sinon.stub(); - background.browser.tabs.create.resetHistory(); + this.webExt.background.browser.webRequest.onCompleted.addListener = sinon.stub(); + this.webExt.background.browser.tabs.create.resetHistory(); // we create a tab with the same id and use the same request id to see if uncanceled - await helper.browser.openNewTab({ + await this.webExt.browser.tabs._create({ id: newTab.id, url: "https://www.youtube.com" }, { @@ -101,14 +113,14 @@ describe("#940", () => { } }); - background.browser.tabs.create.should.have.been.calledOnce; + this.webExt.background.browser.tabs.create.should.have.been.calledOnce; }); - it("should uncancel after webRequest.onErrorOccurred", async () => { - await redirectedRequest(); - background.browser.tabs.create.resetHistory(); + it("should uncancel after webRequest.onErrorOccurred", async function () { + const [newTabResponses, newTab] = await this.redirectedRequest(); + this.webExt.background.browser.tabs.create.resetHistory(); // we create a tab with the same id and use the same request id to see if uncanceled - await helper.browser.openNewTab({ + await this.webExt.browser.tabs._create({ id: newTab.id, url: "https://www.youtube.com" }, { @@ -120,18 +132,18 @@ describe("#940", () => { } }); - background.browser.tabs.create.should.have.been.calledOnce; + this.webExt.background.browser.tabs.create.should.have.been.calledOnce; }); - it("should uncancel after 2 seconds", async () => { - await redirectedRequest({ + it("should uncancel after 2 seconds", async function () { + const [newTabResponses, newTab] = await this.redirectedRequest({ webRequestDontYield: ["onCompleted", "onErrorOccurred"] }); global.clock.tick(2000); - background.browser.tabs.create.resetHistory(); + this.webExt.background.browser.tabs.create.resetHistory(); // we create a tab with the same id and use the same request id to see if uncanceled - await helper.browser.openNewTab({ + await this.webExt.browser.tabs._create({ id: newTab.id, url: "https://www.youtube.com" }, { @@ -143,13 +155,13 @@ describe("#940", () => { } }); - background.browser.tabs.create.should.have.been.calledOnce; + this.webExt.background.browser.tabs.create.should.have.been.calledOnce; }); - it("should not influence the canceled url in other tabs", async () => { - await redirectedRequest(); - background.browser.tabs.create.resetHistory(); - await helper.browser.openNewTab({ + it("should not influence the canceled url in other tabs", async function () { + await this.redirectedRequest(); + this.webExt.background.browser.tabs.create.resetHistory(); + await this.webExt.browser.tabs._create({ cookieStoreId: "firefox-default", url: "https://www.youtube.com" }, { @@ -158,11 +170,7 @@ describe("#940", () => { } }); - background.browser.tabs.create.should.have.been.calledOnce; - }); - - afterEach(() => { - global.clock.restore(); + this.webExt.background.browser.tabs.create.should.have.been.calledOnce; }); }); });