summaryrefslogtreecommitdiffstats
path: root/browser/components/pocket/test
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--browser/components/pocket/test/browser.ini11
-rw-r--r--browser/components/pocket/test/browser_pocket_button_icon_state.js131
-rw-r--r--browser/components/pocket/test/browser_pocket_context_menu_action.js53
-rw-r--r--browser/components/pocket/test/browser_pocket_home_panel.js53
-rw-r--r--browser/components/pocket/test/browser_pocket_panel.js78
-rw-r--r--browser/components/pocket/test/browser_pocket_panel_closemenu.js54
-rw-r--r--browser/components/pocket/test/browser_pocket_ui_check.js85
-rw-r--r--browser/components/pocket/test/head.js85
-rw-r--r--browser/components/pocket/test/test.html12
-rw-r--r--browser/components/pocket/test/unit/browser.ini7
-rw-r--r--browser/components/pocket/test/unit/browser_pocket_AboutPocketParent.js372
-rw-r--r--browser/components/pocket/test/unit/browser_pocket_pktTelemetry.js54
-rw-r--r--browser/components/pocket/test/unit/browser_pocket_pktUI.js100
-rw-r--r--browser/components/pocket/test/unit/head.js12
-rw-r--r--browser/components/pocket/test/unit/panels/browser.ini5
-rw-r--r--browser/components/pocket/test/unit/panels/browser_pocket_main.js49
-rw-r--r--browser/components/pocket/test/unit/panels/head.js24
17 files changed, 1185 insertions, 0 deletions
diff --git a/browser/components/pocket/test/browser.ini b/browser/components/pocket/test/browser.ini
new file mode 100644
index 0000000000..c32f1083f2
--- /dev/null
+++ b/browser/components/pocket/test/browser.ini
@@ -0,0 +1,11 @@
+[DEFAULT]
+support-files =
+ head.js
+ test.html
+
+[browser_pocket_button_icon_state.js]
+[browser_pocket_context_menu_action.js]
+[browser_pocket_home_panel.js]
+[browser_pocket_panel.js]
+[browser_pocket_panel_closemenu.js]
+[browser_pocket_ui_check.js]
diff --git a/browser/components/pocket/test/browser_pocket_button_icon_state.js b/browser/components/pocket/test/browser_pocket_button_icon_state.js
new file mode 100644
index 0000000000..a11e7c43e0
--- /dev/null
+++ b/browser/components/pocket/test/browser_pocket_button_icon_state.js
@@ -0,0 +1,131 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+ChromeUtils.defineESModuleGetters(this, {
+ SaveToPocket: "chrome://pocket/content/SaveToPocket.sys.mjs",
+});
+
+function test_runner(test) {
+ let testTask = async () => {
+ // Before each
+ const sandbox = sinon.createSandbox();
+
+ // We're faking logged in tests, so initially we need to fake the logged in state.
+ sandbox.stub(pktApi, "isUserLoggedIn").callsFake(() => true);
+
+ // Also we cannot actually make remote requests, so make sure we stub any functions
+ // we need that that make requests to api.getpocket.com.
+ sandbox.stub(pktApi, "addLink").callsFake(() => true);
+
+ try {
+ await test({ sandbox });
+ } finally {
+ // After each
+ sandbox.restore();
+ }
+ };
+
+ // Copy the name of the test function to identify the test
+ Object.defineProperty(testTask, "name", { value: test.name });
+ add_task(testTask);
+}
+
+async function isPocketPanelShown() {
+ info("clicking on pocket button in toolbar");
+ // The panel is created on the fly, so we can't simply wait for focus
+ // inside it.
+ let pocketPanelShowing = BrowserTestUtils.waitForEvent(
+ document,
+ "popupshown",
+ true
+ );
+ return pocketPanelShowing;
+}
+
+async function isPocketPanelHidden() {
+ let pocketPanelHidden = BrowserTestUtils.waitForEvent(
+ document,
+ "popuphidden"
+ );
+ return pocketPanelHidden;
+}
+
+function fakeSavingPage() {
+ // Because we're not actually logged into a remote Pocket account,
+ // and because we're not actually saving anything,
+ // we fake it, instead, by calling the function we care about.
+ SaveToPocket.itemSaved();
+ // This fakes the button from just opened, to also pocketed,
+ // we currently expect both from a save.
+ SaveToPocket.updateToolbarNodeState(window);
+}
+
+function checkPanelOpen() {
+ let pocketButton = document.getElementById("save-to-pocket-button");
+ // The Pocket button should be set to open.
+ is(pocketButton.open, true, "Pocket button is open");
+ is(pocketButton.getAttribute("pocketed"), "true", "Pocket item is pocketed");
+}
+
+function checkPanelClosed() {
+ let pocketButton = document.getElementById("save-to-pocket-button");
+ // Something should have closed the Pocket panel, icon should no longer be red.
+ is(pocketButton.open, false, "Pocket button is closed");
+ is(pocketButton.getAttribute("pocketed"), "", "Pocket item is not pocketed");
+}
+
+test_runner(async function test_pocketButtonState_changeTabs({ sandbox }) {
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ "https://example.com/browser/browser/components/pocket/test/test.html"
+ );
+
+ let pocketPanelShown = isPocketPanelShown();
+ let pocketButton = document.getElementById("save-to-pocket-button");
+ pocketButton.click();
+ await pocketPanelShown;
+ fakeSavingPage();
+
+ // Testing the panel states.
+ checkPanelOpen();
+
+ let pocketPanelHidden = isPocketPanelHidden();
+ // Mochitests start with an open tab, so use that to trigger a tab change.
+ await BrowserTestUtils.switchTab(gBrowser, gBrowser.tabs[0]);
+ await pocketPanelHidden;
+
+ // Testing the panel states.
+ checkPanelClosed();
+
+ BrowserTestUtils.removeTab(tab);
+});
+
+test_runner(async function test_pocketButtonState_changeLocation({ sandbox }) {
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ "https://example.com/browser/browser/components/pocket/test/test.html"
+ );
+
+ let pocketPanelShown = isPocketPanelShown();
+ let pocketButton = document.getElementById("save-to-pocket-button");
+ pocketButton.click();
+ await pocketPanelShown;
+ fakeSavingPage();
+
+ // Testing the panel states.
+ checkPanelOpen();
+
+ let pocketPanelHidden = isPocketPanelHidden();
+ // Simulate a location change, and check the panel state.
+ let browser = gBrowser.selectedBrowser;
+ let loaded = BrowserTestUtils.browserLoaded(browser);
+ BrowserTestUtils.loadURIString(browser, "about:robots");
+ await loaded;
+ await pocketPanelHidden;
+
+ // Testing the panel states.
+ checkPanelClosed();
+
+ BrowserTestUtils.removeTab(tab);
+});
diff --git a/browser/components/pocket/test/browser_pocket_context_menu_action.js b/browser/components/pocket/test/browser_pocket_context_menu_action.js
new file mode 100644
index 0000000000..878d23811e
--- /dev/null
+++ b/browser/components/pocket/test/browser_pocket_context_menu_action.js
@@ -0,0 +1,53 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+add_task(async function () {
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ "https://example.com/browser/browser/components/pocket/test/test.html"
+ );
+
+ info("opening context menu");
+ let contextMenu = document.getElementById("contentAreaContextMenu");
+ let popupShown = BrowserTestUtils.waitForEvent(contextMenu, "popupshown");
+ let popupHidden = BrowserTestUtils.waitForEvent(contextMenu, "popuphidden");
+
+ await BrowserTestUtils.synthesizeMouseAtCenter(
+ "body",
+ {
+ type: "contextmenu",
+ button: 2,
+ },
+ tab.linkedBrowser
+ );
+ await popupShown;
+
+ info("opening pocket panel");
+ let contextPocket = contextMenu.querySelector("#context-pocket");
+ // The panel is created on the fly, so we can't simply wait for focus
+ // inside it.
+ let pocketPanelShown = BrowserTestUtils.waitForEvent(
+ document,
+ "popupshown",
+ true
+ );
+ contextMenu.activateItem(contextPocket);
+ await pocketPanelShown;
+ checkElements(true, ["customizationui-widget-panel"]);
+
+ info("closing pocket panel");
+ let pocketPanel = document.getElementById("customizationui-widget-panel");
+ let pocketPanelHidden = BrowserTestUtils.waitForEvent(
+ pocketPanel,
+ "popuphidden"
+ );
+
+ pocketPanel.hidePopup();
+ await pocketPanelHidden;
+ checkElements(false, ["customizationui-widget-panel"]);
+
+ contextMenu.hidePopup();
+ await popupHidden;
+ BrowserTestUtils.removeTab(tab);
+});
diff --git a/browser/components/pocket/test/browser_pocket_home_panel.js b/browser/components/pocket/test/browser_pocket_home_panel.js
new file mode 100644
index 0000000000..d58f3e44c7
--- /dev/null
+++ b/browser/components/pocket/test/browser_pocket_home_panel.js
@@ -0,0 +1,53 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+add_task(async function () {
+ // The recent saves feature makes an external call to api.getpocket.com.
+ // External calls are not permitted in tests.
+ // however, we're not testing the content of the panel,
+ // we're just testing that the right panel is used for certain urls,
+ // so we can turn recent saves off for this test.
+ await SpecialPowers.pushPrefEnv({
+ set: [["extensions.pocket.refresh.hideRecentSaves.enabled", true]],
+ });
+ // Home panel is used on about: pages, so we use about:robots to test.
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ "about:robots"
+ );
+
+ const stub = sinon.stub(pktApi, "isUserLoggedIn").callsFake(() => true);
+
+ info("clicking on pocket button in toolbar");
+ let pocketButton = document.getElementById("save-to-pocket-button");
+ // The panel is created on the fly, so we can't simply wait for focus
+ // inside it.
+ let pocketPanelShowing = BrowserTestUtils.waitForEvent(
+ document,
+ "popupshowing",
+ true
+ );
+ pocketButton.click();
+ await pocketPanelShowing;
+
+ let pocketPanel = document.getElementById("customizationui-widget-panel");
+ let pocketFrame = pocketPanel.querySelector("browser");
+
+ await TestUtils.waitForCondition(
+ () => pocketFrame.src.startsWith("about:pocket-home?"),
+ "pocket home panel is showing"
+ );
+
+ info("closing pocket panel");
+ let pocketPanelHidden = BrowserTestUtils.waitForEvent(
+ pocketPanel,
+ "popuphidden"
+ );
+ pocketPanel.hidePopup();
+ await pocketPanelHidden;
+ checkElements(false, ["customizationui-widget-panel"]);
+
+ stub.restore();
+ BrowserTestUtils.removeTab(tab);
+});
diff --git a/browser/components/pocket/test/browser_pocket_panel.js b/browser/components/pocket/test/browser_pocket_panel.js
new file mode 100644
index 0000000000..6cebc423bd
--- /dev/null
+++ b/browser/components/pocket/test/browser_pocket_panel.js
@@ -0,0 +1,78 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+add_task(async function () {
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ "https://example.com/browser/browser/components/pocket/test/test.html"
+ );
+
+ info("clicking on pocket button in toolbar");
+ let pocketButton = document.getElementById("save-to-pocket-button");
+ // The panel is created on the fly, so we can't simply wait for focus
+ // inside it.
+ let pocketPanelShowing = BrowserTestUtils.waitForEvent(
+ document,
+ "popupshowing",
+ true
+ );
+ pocketButton.click();
+ await pocketPanelShowing;
+
+ checkElements(true, ["customizationui-widget-panel"]);
+ let pocketPanel = document.getElementById("customizationui-widget-panel");
+ is(pocketPanel.state, "showing", "pocket panel is showing");
+
+ info("Trigger context menu in a pocket panel element");
+ let contextMenu = document.getElementById("contentAreaContextMenu");
+ is(contextMenu.state, "closed", "context menu popup is closed");
+ let popupShown = BrowserTestUtils.waitForEvent(contextMenu, "popupshown");
+ let popupHidden = BrowserTestUtils.waitForEvent(contextMenu, "popuphidden");
+
+ let pocketFrame = pocketPanel.querySelector("browser");
+
+ const getReadyState = async frame =>
+ SpecialPowers.spawn(frame, [], () => content.document.readyState);
+
+ // Ensure Pocket panel is ready to avoid intermittency.
+ await TestUtils.waitForCondition(
+ async () => (await getReadyState(pocketFrame)) == "complete"
+ );
+
+ // Ensure that the document layout has been flushed before triggering the mouse event
+ // (See Bug 1519808 for a rationale).
+ await pocketFrame.ownerGlobal.promiseDocumentFlushed(() => {});
+ await BrowserTestUtils.synthesizeMouseAtCenter(
+ "body",
+ {
+ type: "contextmenu",
+ button: 2,
+ },
+ pocketFrame
+ );
+
+ await popupShown;
+ is(contextMenu.state, "open", "context menu popup is open");
+ const emeLearnMoreContextItem = contextMenu.querySelector(
+ "#context-media-eme-learnmore"
+ );
+ ok(
+ BrowserTestUtils.is_hidden(emeLearnMoreContextItem),
+ "Unrelated context menu items should be hidden"
+ );
+
+ contextMenu.hidePopup();
+ await popupHidden;
+
+ info("closing pocket panel");
+ let pocketPanelHidden = BrowserTestUtils.waitForEvent(
+ pocketPanel,
+ "popuphidden"
+ );
+ pocketPanel.hidePopup();
+ await pocketPanelHidden;
+ checkElements(false, ["customizationui-widget-panel"]);
+
+ BrowserTestUtils.removeTab(tab);
+});
diff --git a/browser/components/pocket/test/browser_pocket_panel_closemenu.js b/browser/components/pocket/test/browser_pocket_panel_closemenu.js
new file mode 100644
index 0000000000..b3a2f2d324
--- /dev/null
+++ b/browser/components/pocket/test/browser_pocket_panel_closemenu.js
@@ -0,0 +1,54 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+// This is testing the fix in bug 1729847, specifically
+// clicking enter while the pocket panel is open should not close the panel.
+add_task(async function () {
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ "https://example.com/browser/browser/components/pocket/test/test.html"
+ );
+
+ info("clicking on pocket button in toolbar");
+ let pocketButton = document.getElementById("save-to-pocket-button");
+ // The panel is created on the fly, so we can't simply wait for focus
+ // inside it.
+ let pocketPanelShown = BrowserTestUtils.waitForEvent(
+ document,
+ "popupshown",
+ true
+ );
+ pocketButton.click();
+ await pocketPanelShown;
+
+ let pocketPanel = document.getElementById("customizationui-widget-panel");
+ let pocketFrame = pocketPanel.querySelector("browser");
+
+ // Ensure that the document layout has been flushed before triggering the focus event
+ // (See Bug 1519808 for a rationale).
+ await pocketFrame.ownerGlobal.promiseDocumentFlushed(() => {});
+
+ // The panelview should have closemenu="none".
+ // Without closemenu="none", the following sequence of
+ // frame focus then enter would close the panel,
+ // but we don't want it to close, we want it to stay open.
+ let focusEventPromise = BrowserTestUtils.waitForEvent(pocketFrame, "focus");
+ pocketFrame.focus();
+ await focusEventPromise;
+ EventUtils.synthesizeKey("VK_RETURN");
+
+ // Is the Pocket panel still open?
+ is(pocketPanel.state, "open", "pocket panel is open");
+
+ // We're done now, we can close the panel.
+ info("closing pocket panel");
+ let pocketPanelHidden = BrowserTestUtils.waitForEvent(
+ pocketPanel,
+ "popuphidden"
+ );
+ pocketPanel.hidePopup();
+ await pocketPanelHidden;
+
+ BrowserTestUtils.removeTab(tab);
+});
diff --git a/browser/components/pocket/test/browser_pocket_ui_check.js b/browser/components/pocket/test/browser_pocket_ui_check.js
new file mode 100644
index 0000000000..e8b82a0b5c
--- /dev/null
+++ b/browser/components/pocket/test/browser_pocket_ui_check.js
@@ -0,0 +1,85 @@
+"use strict";
+
+add_task(async function test_setup() {
+ let clearValue = Services.prefs.prefHasUserValue("extensions.pocket.enabled");
+ let enabledOnStartup = Services.prefs.getBoolPref(
+ "extensions.pocket.enabled"
+ );
+ registerCleanupFunction(() => {
+ if (clearValue) {
+ Services.prefs.clearUserPref("extensions.pocket.enabled");
+ } else {
+ Services.prefs.setBoolPref("extensions.pocket.enabled", enabledOnStartup);
+ }
+ });
+});
+
+add_task(async function () {
+ await promisePocketEnabled();
+
+ // check context menu exists
+ info("checking content context menu");
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ "https://example.com/browser/browser/components/pocket/test/test.html"
+ );
+
+ let contextMenu = document.getElementById("contentAreaContextMenu");
+ let popupShown = BrowserTestUtils.waitForEvent(contextMenu, "popupshown");
+ let popupHidden = BrowserTestUtils.waitForEvent(contextMenu, "popuphidden");
+ await BrowserTestUtils.synthesizeMouseAtCenter(
+ "body",
+ {
+ type: "contextmenu",
+ button: 2,
+ },
+ tab.linkedBrowser
+ );
+ await popupShown;
+
+ checkElementsShown(true, ["save-to-pocket-button", "context-pocket"]);
+
+ contextMenu.hidePopup();
+ await popupHidden;
+ popupShown = BrowserTestUtils.waitForEvent(contextMenu, "popupshown");
+ popupHidden = BrowserTestUtils.waitForEvent(contextMenu, "popuphidden");
+ await BrowserTestUtils.synthesizeMouseAtCenter(
+ "a",
+ {
+ type: "contextmenu",
+ button: 2,
+ },
+ tab.linkedBrowser
+ );
+ await popupShown;
+
+ checkElementsShown(true, ["context-savelinktopocket"]);
+ contextMenu.hidePopup();
+ await popupHidden;
+
+ await promisePocketDisabled();
+
+ popupShown = BrowserTestUtils.waitForEvent(contextMenu, "popupshown");
+ popupHidden = BrowserTestUtils.waitForEvent(contextMenu, "popuphidden");
+ await BrowserTestUtils.synthesizeMouseAtCenter(
+ "a",
+ {
+ type: "contextmenu",
+ button: 2,
+ },
+ tab.linkedBrowser
+ );
+ await popupShown;
+
+ checkElementsShown(false, [
+ "context-pocket",
+ "context-savelinktopocket",
+ "save-to-pocket-button",
+ ]);
+
+ contextMenu.hidePopup();
+ await popupHidden;
+ BrowserTestUtils.removeTab(tab);
+
+ await promisePocketReset();
+});
diff --git a/browser/components/pocket/test/head.js b/browser/components/pocket/test/head.js
new file mode 100644
index 0000000000..c3ce73e42e
--- /dev/null
+++ b/browser/components/pocket/test/head.js
@@ -0,0 +1,85 @@
+// Currently Pocket is disabled in tests. We want these tests to work under
+// either case that Pocket is disabled or enabled on startup of the browser,
+// and that at the end we're reset to the correct state.
+let enabledOnStartup = false;
+
+ChromeUtils.defineESModuleGetters(this, {
+ pktApi: "chrome://pocket/content/pktApi.sys.mjs",
+});
+
+const { sinon } = ChromeUtils.importESModule(
+ "resource://testing-common/Sinon.sys.mjs"
+);
+
+// PocketEnabled/Disabled promises return true if it was already
+// Enabled/Disabled, and false if it need to Enable/Disable.
+function promisePocketEnabled() {
+ if (
+ Services.prefs.getPrefType("extensions.pocket.enabled") !=
+ Services.prefs.PREF_INVALID &&
+ Services.prefs.getBoolPref("extensions.pocket.enabled")
+ ) {
+ info("pocket was already enabled, assuming enabled by default for tests");
+ enabledOnStartup = true;
+ return Promise.resolve(true);
+ }
+ info("pocket is not enabled");
+ Services.prefs.setBoolPref("extensions.pocket.enabled", true);
+ return BrowserTestUtils.waitForCondition(() => {
+ return !!CustomizableUI.getWidget("save-to-pocket-button");
+ });
+}
+
+function promisePocketDisabled() {
+ if (
+ Services.prefs.getPrefType("extensions.pocket.enabled") ==
+ Services.prefs.PREF_INVALID ||
+ !Services.prefs.getBoolPref("extensions.pocket.enabled")
+ ) {
+ info("pocket-button already disabled");
+ return Promise.resolve(true);
+ }
+ info("reset pocket enabled pref");
+ // testing/profiles/common/user.js uses user_pref to disable pocket, set
+ // back to false.
+ Services.prefs.setBoolPref("extensions.pocket.enabled", false);
+ return BrowserTestUtils.waitForCondition(() => {
+ return !CustomizableUI.getWidget("save-to-pocket-button");
+ });
+}
+
+function promisePocketReset() {
+ if (enabledOnStartup) {
+ info("reset is enabling pocket addon");
+ return promisePocketEnabled();
+ }
+ info("reset is disabling pocket addon");
+ return promisePocketDisabled();
+}
+
+function checkElements(expectPresent, l, win = window) {
+ for (let id of l) {
+ let el =
+ win.document.getElementById(id) ||
+ win.gNavToolbox.palette.querySelector("#" + id);
+ is(
+ !!el && !el.hidden,
+ expectPresent,
+ "element " + id + (expectPresent ? " is" : " is not") + " present"
+ );
+ }
+}
+
+function checkElementsShown(expectPresent, l, win = window) {
+ for (let id of l) {
+ let el =
+ win.document.getElementById(id) ||
+ win.gNavToolbox.palette.querySelector("#" + id);
+ let elShown = !!el && window.getComputedStyle(el).display != "none";
+ is(
+ elShown,
+ expectPresent,
+ "element " + id + (expectPresent ? " is" : " is not") + " present"
+ );
+ }
+}
diff --git a/browser/components/pocket/test/test.html b/browser/components/pocket/test/test.html
new file mode 100644
index 0000000000..51207f2f97
--- /dev/null
+++ b/browser/components/pocket/test/test.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+
+<html>
+<head>
+ <title>Page Title</title>
+ <meta charset="utf-8" />
+</head>
+
+<body>
+ <a href="/">Test link</a>
+</body>
+</html>
diff --git a/browser/components/pocket/test/unit/browser.ini b/browser/components/pocket/test/unit/browser.ini
new file mode 100644
index 0000000000..5d616810e5
--- /dev/null
+++ b/browser/components/pocket/test/unit/browser.ini
@@ -0,0 +1,7 @@
+[DEFAULT]
+support-files =
+ head.js
+
+[browser_pocket_AboutPocketParent.js]
+[browser_pocket_pktTelemetry.js]
+[browser_pocket_pktUI.js]
diff --git a/browser/components/pocket/test/unit/browser_pocket_AboutPocketParent.js b/browser/components/pocket/test/unit/browser_pocket_AboutPocketParent.js
new file mode 100644
index 0000000000..5abe3b3db1
--- /dev/null
+++ b/browser/components/pocket/test/unit/browser_pocket_AboutPocketParent.js
@@ -0,0 +1,372 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+const { AboutPocketParent } = ChromeUtils.importESModule(
+ "resource:///actors/AboutPocketParent.sys.mjs"
+);
+const { pktApi } = ChromeUtils.importESModule(
+ "chrome://pocket/content/pktApi.sys.mjs"
+);
+let aboutPocketParent;
+
+function test_runner(test) {
+ let testTask = async () => {
+ // Before each
+ const sandbox = sinon.createSandbox();
+ aboutPocketParent = new AboutPocketParent();
+
+ const manager = {
+ isClosed: false,
+ };
+ const browsingContext = {
+ topChromeWindow: {
+ pktUI: {
+ onShowSignup: sandbox.spy(),
+ onShowSaved: sandbox.spy(),
+ closePanel: sandbox.spy(),
+ onOpenTabWithUrl: sandbox.spy(),
+ onOpenTabWithPocketUrl: sandbox.spy(),
+ resizePanel: sandbox.spy(),
+ getPanelFrame: () => ({ setAttribute: () => {} }),
+ },
+ },
+ embedderElement: {
+ csp: "csp",
+ contentPrincipal: "contentPrincipal",
+ },
+ };
+
+ sandbox.stub(aboutPocketParent, "manager").get(() => manager);
+ sandbox
+ .stub(aboutPocketParent, "browsingContext")
+ .get(() => browsingContext);
+
+ try {
+ await test({ sandbox });
+ } finally {
+ // After each
+ sandbox.restore();
+ }
+ };
+
+ // Copy the name of the test function to identify the test
+ Object.defineProperty(testTask, "name", { value: test.name });
+ add_task(testTask);
+}
+
+test_runner(async function test_AboutPocketParent_sendResponseMessageToPanel({
+ sandbox,
+}) {
+ const sendAsyncMessage = sandbox.stub(aboutPocketParent, "sendAsyncMessage");
+
+ aboutPocketParent.sendResponseMessageToPanel("PKT_testMessage", {
+ foo: 1,
+ });
+
+ const { args } = sendAsyncMessage.firstCall;
+
+ Assert.ok(
+ sendAsyncMessage.calledOnce,
+ "Should fire sendAsyncMessage once with sendResponseMessageToPanel"
+ );
+ Assert.deepEqual(
+ args,
+ ["PKT_testMessage_response", { foo: 1 }],
+ "Should fire sendAsyncMessage with proper args from sendResponseMessageToPanel"
+ );
+});
+
+test_runner(
+ async function test_AboutPocketParent_receiveMessage_PKT_show_signup({
+ sandbox,
+ }) {
+ await aboutPocketParent.receiveMessage({
+ name: "PKT_show_signup",
+ });
+
+ const { onShowSignup } =
+ aboutPocketParent.browsingContext.topChromeWindow.pktUI;
+
+ Assert.ok(
+ onShowSignup.calledOnce,
+ "Should fire onShowSignup once with PKT_show_signup"
+ );
+ }
+);
+
+test_runner(
+ async function test_AboutPocketParent_receiveMessage_PKT_show_saved({
+ sandbox,
+ }) {
+ await aboutPocketParent.receiveMessage({
+ name: "PKT_show_saved",
+ });
+
+ const { onShowSaved } =
+ aboutPocketParent.browsingContext.topChromeWindow.pktUI;
+
+ Assert.ok(
+ onShowSaved.calledOnce,
+ "Should fire onShowSaved once with PKT_show_saved"
+ );
+ }
+);
+
+test_runner(async function test_AboutPocketParent_receiveMessage_PKT_close({
+ sandbox,
+}) {
+ await aboutPocketParent.receiveMessage({
+ name: "PKT_close",
+ });
+
+ const { closePanel } =
+ aboutPocketParent.browsingContext.topChromeWindow.pktUI;
+
+ Assert.ok(
+ closePanel.calledOnce,
+ "Should fire closePanel once with PKT_close"
+ );
+});
+
+test_runner(
+ async function test_AboutPocketParent_receiveMessage_PKT_openTabWithUrl({
+ sandbox,
+ }) {
+ await aboutPocketParent.receiveMessage({
+ name: "PKT_openTabWithUrl",
+ data: { foo: 1 },
+ });
+
+ const { onOpenTabWithUrl } =
+ aboutPocketParent.browsingContext.topChromeWindow.pktUI;
+ const { args } = onOpenTabWithUrl.firstCall;
+
+ Assert.ok(
+ onOpenTabWithUrl.calledOnce,
+ "Should fire onOpenTabWithUrl once with PKT_openTabWithUrl"
+ );
+ Assert.deepEqual(
+ args,
+ [{ foo: 1 }, "contentPrincipal", "csp"],
+ "Should fire onOpenTabWithUrl with proper args from PKT_openTabWithUrl"
+ );
+ }
+);
+
+test_runner(
+ async function test_AboutPocketParent_receiveMessage_PKT_openTabWithPocketUrl({
+ sandbox,
+ }) {
+ await aboutPocketParent.receiveMessage({
+ name: "PKT_openTabWithPocketUrl",
+ data: { foo: 1 },
+ });
+
+ const { onOpenTabWithPocketUrl } =
+ aboutPocketParent.browsingContext.topChromeWindow.pktUI;
+ const { args } = onOpenTabWithPocketUrl.firstCall;
+
+ Assert.ok(
+ onOpenTabWithPocketUrl.calledOnce,
+ "Should fire onOpenTabWithPocketUrl once with PKT_openTabWithPocketUrl"
+ );
+ Assert.deepEqual(
+ args,
+ [{ foo: 1 }, "contentPrincipal", "csp"],
+ "Should fire onOpenTabWithPocketUrl with proper args from PKT_openTabWithPocketUrl"
+ );
+ }
+);
+
+test_runner(
+ async function test_AboutPocketParent_receiveMessage_PKT_resizePanel({
+ sandbox,
+ }) {
+ const sendResponseMessageToPanel = sandbox.stub(
+ aboutPocketParent,
+ "sendResponseMessageToPanel"
+ );
+ await aboutPocketParent.receiveMessage({
+ name: "PKT_resizePanel",
+ data: { foo: 1 },
+ });
+
+ const { resizePanel } =
+ aboutPocketParent.browsingContext.topChromeWindow.pktUI;
+ const { args } = resizePanel.firstCall;
+
+ Assert.ok(
+ resizePanel.calledOnce,
+ "Should fire resizePanel once with PKT_resizePanel"
+ );
+ Assert.deepEqual(
+ args,
+ [{ foo: 1 }],
+ "Should fire resizePanel with proper args from PKT_resizePanel"
+ );
+ Assert.ok(
+ sendResponseMessageToPanel.calledOnce,
+ "Should fire sendResponseMessageToPanel once with PKT_resizePanel"
+ );
+ Assert.deepEqual(
+ sendResponseMessageToPanel.firstCall.args,
+ ["PKT_resizePanel"],
+ "Should fire sendResponseMessageToPanel with proper args from PKT_resizePanel"
+ );
+ }
+);
+
+test_runner(async function test_AboutPocketParent_receiveMessage_PKT_getTags({
+ sandbox,
+}) {
+ const sendResponseMessageToPanel = sandbox.stub(
+ aboutPocketParent,
+ "sendResponseMessageToPanel"
+ );
+ await aboutPocketParent.receiveMessage({
+ name: "PKT_getTags",
+ });
+ Assert.ok(
+ sendResponseMessageToPanel.calledOnce,
+ "Should fire sendResponseMessageToPanel once with PKT_getTags"
+ );
+ Assert.deepEqual(
+ sendResponseMessageToPanel.firstCall.args,
+ ["PKT_getTags", { tags: [] }],
+ "Should fire sendResponseMessageToPanel with proper args from PKT_getTags"
+ );
+});
+
+test_runner(
+ async function test_AboutPocketParent_receiveMessage_PKT_getSuggestedTags({
+ sandbox,
+ }) {
+ const sendResponseMessageToPanel = sandbox.stub(
+ aboutPocketParent,
+ "sendResponseMessageToPanel"
+ );
+ sandbox.stub(pktApi, "getSuggestedTagsForURL").callsFake((url, options) => {
+ options.success({ suggested_tags: "foo" });
+ });
+
+ await aboutPocketParent.receiveMessage({
+ name: "PKT_getSuggestedTags",
+ data: { url: "https://foo.com" },
+ });
+
+ Assert.ok(
+ pktApi.getSuggestedTagsForURL.calledOnce,
+ "Should fire getSuggestedTagsForURL once with PKT_getSuggestedTags"
+ );
+ Assert.equal(
+ pktApi.getSuggestedTagsForURL.firstCall.args[0],
+ "https://foo.com",
+ "Should fire getSuggestedTagsForURL with proper url from PKT_getSuggestedTags"
+ );
+ Assert.ok(
+ sendResponseMessageToPanel.calledOnce,
+ "Should fire sendResponseMessageToPanel once with PKT_getSuggestedTags"
+ );
+ Assert.deepEqual(
+ sendResponseMessageToPanel.firstCall.args,
+ [
+ "PKT_getSuggestedTags",
+ {
+ status: "success",
+ value: { suggestedTags: "foo" },
+ },
+ ],
+ "Should fire sendResponseMessageToPanel with proper args from PKT_getSuggestedTags"
+ );
+ }
+);
+
+test_runner(async function test_AboutPocketParent_receiveMessage_PKT_addTags({
+ sandbox,
+}) {
+ const sendResponseMessageToPanel = sandbox.stub(
+ aboutPocketParent,
+ "sendResponseMessageToPanel"
+ );
+ sandbox.stub(pktApi, "addTagsToURL").callsFake((url, tags, options) => {
+ options.success();
+ });
+
+ await aboutPocketParent.receiveMessage({
+ name: "PKT_addTags",
+ data: { url: "https://foo.com", tags: "tags" },
+ });
+
+ Assert.ok(
+ pktApi.addTagsToURL.calledOnce,
+ "Should fire addTagsToURL once with PKT_addTags"
+ );
+ Assert.equal(
+ pktApi.addTagsToURL.firstCall.args[0],
+ "https://foo.com",
+ "Should fire addTagsToURL with proper url from PKT_addTags"
+ );
+ Assert.equal(
+ pktApi.addTagsToURL.firstCall.args[1],
+ "tags",
+ "Should fire addTagsToURL with proper tags from PKT_addTags"
+ );
+ Assert.ok(
+ sendResponseMessageToPanel.calledOnce,
+ "Should fire sendResponseMessageToPanel once with PKT_addTags"
+ );
+ Assert.deepEqual(
+ sendResponseMessageToPanel.firstCall.args,
+ [
+ "PKT_addTags",
+ {
+ status: "success",
+ },
+ ],
+ "Should fire sendResponseMessageToPanel with proper args from PKT_addTags"
+ );
+});
+
+test_runner(
+ async function test_AboutPocketParent_receiveMessage_PKT_deleteItem({
+ sandbox,
+ }) {
+ const sendResponseMessageToPanel = sandbox.stub(
+ aboutPocketParent,
+ "sendResponseMessageToPanel"
+ );
+ sandbox.stub(pktApi, "deleteItem").callsFake((itemId, options) => {
+ options.success();
+ });
+
+ await aboutPocketParent.receiveMessage({
+ name: "PKT_deleteItem",
+ data: { itemId: "itemId" },
+ });
+
+ Assert.ok(
+ pktApi.deleteItem.calledOnce,
+ "Should fire deleteItem once with PKT_deleteItem"
+ );
+ Assert.equal(
+ pktApi.deleteItem.firstCall.args[0],
+ "itemId",
+ "Should fire deleteItem with proper itemId from PKT_deleteItem"
+ );
+ Assert.ok(
+ sendResponseMessageToPanel.calledOnce,
+ "Should fire sendResponseMessageToPanel once with PKT_deleteItem"
+ );
+ Assert.deepEqual(
+ sendResponseMessageToPanel.firstCall.args,
+ [
+ "PKT_deleteItem",
+ {
+ status: "success",
+ },
+ ],
+ "Should fire sendResponseMessageToPanel with proper args from PKT_deleteItem"
+ );
+ }
+);
diff --git a/browser/components/pocket/test/unit/browser_pocket_pktTelemetry.js b/browser/components/pocket/test/unit/browser_pocket_pktTelemetry.js
new file mode 100644
index 0000000000..197a152ee5
--- /dev/null
+++ b/browser/components/pocket/test/unit/browser_pocket_pktTelemetry.js
@@ -0,0 +1,54 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+ChromeUtils.defineESModuleGetters(this, {
+ pktTelemetry: "chrome://pocket/content/pktTelemetry.sys.mjs",
+});
+
+function test_runner(test) {
+ let testTask = async () => {
+ // Before each
+ const sandbox = sinon.createSandbox();
+ try {
+ await test({ sandbox });
+ } finally {
+ // After each
+ sandbox.restore();
+ }
+ };
+
+ // Copy the name of the test function to identify the test
+ Object.defineProperty(testTask, "name", { value: test.name });
+ add_task(testTask);
+}
+
+test_runner(async function test_createPingPayload({ sandbox }) {
+ const impressionId = "{7fd5a1ac-6089-4212-91a7-fcdec1d2f533}";
+ const creationDate = "18578";
+ await SpecialPowers.pushPrefEnv({
+ set: [["browser.newtabpage.activity-stream.impressionId", impressionId]],
+ });
+ sandbox.stub(pktTelemetry, "_profileCreationDate").returns(creationDate);
+ const result = pktTelemetry.createPingPayload({ test: "test" });
+
+ Assert.deepEqual(result, {
+ test: "test",
+ pocket_logged_in_status: false,
+ profile_creation_date: creationDate,
+ impression_id: impressionId,
+ });
+});
+
+test_runner(async function test_generateStructuredIngestionEndpoint({
+ sandbox,
+}) {
+ sandbox
+ .stub(pktTelemetry, "_generateUUID")
+ .returns("{7fd5a1ac-6089-4212-91a7-fcdec1d2f533}");
+ const endpoint = pktTelemetry._generateStructuredIngestionEndpoint();
+ Assert.equal(
+ endpoint,
+ "https://incoming.telemetry.mozilla.org/submit/activity-stream/pocket-button/1/7fd5a1ac-6089-4212-91a7-fcdec1d2f533"
+ );
+});
diff --git a/browser/components/pocket/test/unit/browser_pocket_pktUI.js b/browser/components/pocket/test/unit/browser_pocket_pktUI.js
new file mode 100644
index 0000000000..92cb78eea6
--- /dev/null
+++ b/browser/components/pocket/test/unit/browser_pocket_pktUI.js
@@ -0,0 +1,100 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+function test_runner(test) {
+ let testTask = async () => {
+ // Before each
+ pktUI.initPrefs();
+ const sandbox = sinon.createSandbox();
+ try {
+ await test({ sandbox });
+ } finally {
+ // After each
+ sandbox.restore();
+ }
+ };
+
+ // Copy the name of the test function to identify the test
+ Object.defineProperty(testTask, "name", { value: test.name });
+ add_task(testTask);
+}
+
+test_runner(async function test_pktUI_getAndShowRecsForItem_on({ sandbox }) {
+ await SpecialPowers.pushPrefEnv({
+ set: [["extensions.pocket.onSaveRecs", true]],
+ });
+ pktUI.initPrefs();
+ const getRecsForItemStub = sandbox.stub(pktApi, "getRecsForItem");
+
+ pktUI.getAndShowRecsForItem(
+ {
+ resolved_id: "1234",
+ },
+ {
+ success() {},
+ }
+ );
+
+ Assert.ok(getRecsForItemStub.calledOnce);
+ Assert.equal(getRecsForItemStub.getCall(0).args[0], "1234");
+});
+
+test_runner(async function test_pktUI_getAndShowRecsForItem_off({ sandbox }) {
+ await SpecialPowers.pushPrefEnv({
+ set: [["extensions.pocket.onSaveRecs", false]],
+ });
+ pktUI.initPrefs();
+ const getRecsForItemStub = sandbox.stub(pktApi, "getRecsForItem");
+
+ pktUI.getAndShowRecsForItem(
+ {
+ resolved_id: "1234",
+ },
+ {
+ success() {},
+ }
+ );
+
+ Assert.ok(getRecsForItemStub.notCalled);
+});
+
+test_runner(async function test_pktUI_getAndShowRecsForItem_locale({
+ sandbox,
+}) {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["extensions.pocket.onSaveRecs", true],
+ ["extensions.pocket.onSaveRecs.locales", "de"],
+ ],
+ });
+ pktUI.initPrefs();
+ const getRecsForItemStub = sandbox.stub(pktApi, "getRecsForItem");
+
+ pktUI.getAndShowRecsForItem(
+ {
+ resolved_id: "1234",
+ },
+ {
+ success() {},
+ }
+ );
+
+ Assert.ok(getRecsForItemStub.notCalled);
+});
+
+test_runner(async function test_pktUI_showPanel({ sandbox }) {
+ const testFrame = {
+ setAttribute: sandbox.stub(),
+ style: { width: 0, height: 0 },
+ };
+ pktUI.setToolbarPanelFrame(testFrame);
+
+ pktUI.showPanel("about:pocket-saved", `saved`);
+
+ Assert.deepEqual(testFrame.setAttribute.args[0], [
+ "src",
+ `about:pocket-saved?utmSource=firefox_pocket_save_button&locale=${SpecialPowers.Services.locale.appLocaleAsBCP47}`,
+ ]);
+ Assert.deepEqual(testFrame.style, { width: "350px", height: "110px" });
+});
diff --git a/browser/components/pocket/test/unit/head.js b/browser/components/pocket/test/unit/head.js
new file mode 100644
index 0000000000..6699eb7a1c
--- /dev/null
+++ b/browser/components/pocket/test/unit/head.js
@@ -0,0 +1,12 @@
+ChromeUtils.defineESModuleGetters(this, {
+ pktApi: "chrome://pocket/content/pktApi.sys.mjs",
+});
+XPCOMUtils.defineLazyScriptGetter(
+ this,
+ "pktUI",
+ "chrome://pocket/content/pktUI.js"
+);
+
+const { sinon } = ChromeUtils.importESModule(
+ "resource://testing-common/Sinon.sys.mjs"
+);
diff --git a/browser/components/pocket/test/unit/panels/browser.ini b/browser/components/pocket/test/unit/panels/browser.ini
new file mode 100644
index 0000000000..66642d3745
--- /dev/null
+++ b/browser/components/pocket/test/unit/panels/browser.ini
@@ -0,0 +1,5 @@
+[DEFAULT]
+support-files =
+ head.js
+
+[browser_pocket_main.js]
diff --git a/browser/components/pocket/test/unit/panels/browser_pocket_main.js b/browser/components/pocket/test/unit/panels/browser_pocket_main.js
new file mode 100644
index 0000000000..c40ad4d5d4
--- /dev/null
+++ b/browser/components/pocket/test/unit/panels/browser_pocket_main.js
@@ -0,0 +1,49 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+function test_runner(test) {
+ let testTask = async () => {
+ // Before each
+ const sandbox = sinon.createSandbox();
+ try {
+ await test({
+ sandbox,
+ pktPanelMessaging: testGlobal.window.pktPanelMessaging,
+ });
+ } finally {
+ // After each
+ sandbox.restore();
+ }
+ };
+
+ // Copy the name of the test function to identify the test
+ Object.defineProperty(testTask, "name", { value: test.name });
+ add_task(testTask);
+}
+
+test_runner(async function test_clickHelper({ sandbox, pktPanelMessaging }) {
+ // Create a button to test the click helper with.
+ const button = document.createElement("button");
+ button.setAttribute("href", "http://example.com");
+
+ // Setup a stub for the click itself.
+ sandbox.stub(pktPanelMessaging, "sendMessage");
+
+ // Create the click helper and trigger the click.
+ pktPanelMessaging.clickHelper(button, { source: "test-click", position: 2 });
+ button.click();
+
+ Assert.ok(
+ pktPanelMessaging.sendMessage.calledOnce,
+ "Should fire sendMessage once with clickHelper click"
+ );
+ Assert.ok(
+ pktPanelMessaging.sendMessage.calledWith("PKT_openTabWithUrl", {
+ url: "http://example.com",
+ source: "test-click",
+ position: 2,
+ }),
+ "Should send expected values to sendMessage with clickHelper click"
+ );
+});
diff --git a/browser/components/pocket/test/unit/panels/head.js b/browser/components/pocket/test/unit/panels/head.js
new file mode 100644
index 0000000000..cc6ba440f1
--- /dev/null
+++ b/browser/components/pocket/test/unit/panels/head.js
@@ -0,0 +1,24 @@
+const { sinon } = ChromeUtils.importESModule(
+ "resource://testing-common/Sinon.sys.mjs"
+);
+
+const testGlobal = {
+ PKT_PANEL_OVERLAY: class {
+ create() {}
+ },
+ RPMRemoveMessageListener: () => {},
+ RPMAddMessageListener: () => {},
+ RPMSendAsyncMessage: () => {},
+ window: {},
+ self: {},
+};
+
+Services.scriptloader.loadSubScript(
+ "chrome://pocket/content/panels/js/vendor.bundle.js",
+ testGlobal
+);
+
+Services.scriptloader.loadSubScript(
+ "chrome://pocket/content/panels/js/main.bundle.js",
+ testGlobal
+);