diff options
Diffstat (limited to '')
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 +); |