summaryrefslogtreecommitdiffstats
path: root/browser/components/newtab/test/browser/browser_trigger_listeners.js
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/newtab/test/browser/browser_trigger_listeners.js')
-rw-r--r--browser/components/newtab/test/browser/browser_trigger_listeners.js343
1 files changed, 343 insertions, 0 deletions
diff --git a/browser/components/newtab/test/browser/browser_trigger_listeners.js b/browser/components/newtab/test/browser/browser_trigger_listeners.js
new file mode 100644
index 0000000000..c7a502fdd0
--- /dev/null
+++ b/browser/components/newtab/test/browser/browser_trigger_listeners.js
@@ -0,0 +1,343 @@
+const { ASRouterTriggerListeners } = ChromeUtils.import(
+ "resource://activity-stream/lib/ASRouterTriggerListeners.jsm"
+);
+
+const mockIdleService = {
+ _observers: new Set(),
+ _fireObservers(state) {
+ for (let observer of this._observers.values()) {
+ observer.observe(this, state, null);
+ }
+ },
+ QueryInterface: ChromeUtils.generateQI(["nsIUserIdleService"]),
+ idleTime: 1200000,
+ addIdleObserver(observer, time) {
+ this._observers.add(observer);
+ },
+ removeIdleObserver(observer, time) {
+ this._observers.delete(observer);
+ },
+};
+
+const sleepMs = (ms = 0) => new Promise(resolve => setTimeout(resolve, ms)); // eslint-disable-line mozilla/no-arbitrary-setTimeout
+
+const inChaosMode = !!parseInt(Services.env.get("MOZ_CHAOSMODE"), 16);
+
+add_setup(async function () {
+ // Runtime increases in chaos mode on Mac.
+ if (inChaosMode && AppConstants.platform === "macosx") {
+ requestLongerTimeout(2);
+ }
+
+ registerCleanupFunction(() => {
+ const trigger = ASRouterTriggerListeners.get("openURL");
+ trigger.uninit();
+ });
+});
+
+add_task(async function test_openURL_visit_counter() {
+ const trigger = ASRouterTriggerListeners.get("openURL");
+ const stub = sinon.stub();
+ trigger.uninit();
+
+ trigger.init(stub, ["example.com"]);
+
+ await waitForUrlLoad("about:blank");
+ await waitForUrlLoad("https://example.com/");
+ await waitForUrlLoad("about:blank");
+ await waitForUrlLoad("http://example.com/");
+ await waitForUrlLoad("about:blank");
+ await waitForUrlLoad("http://example.com/");
+
+ Assert.equal(stub.callCount, 3, "Stub called 3 times for example.com host");
+ Assert.equal(
+ stub.firstCall.args[1].context.visitsCount,
+ 1,
+ "First call should have count 1"
+ );
+ Assert.equal(
+ stub.thirdCall.args[1].context.visitsCount,
+ 2,
+ "Third call should have count 2 for http://example.com"
+ );
+});
+
+add_task(async function test_openURL_visit_counter_withPattern() {
+ const trigger = ASRouterTriggerListeners.get("openURL");
+ const stub = sinon.stub();
+ trigger.uninit();
+
+ // Match any valid URL
+ trigger.init(stub, [], ["*://*/*"]);
+
+ await waitForUrlLoad("about:blank");
+ await waitForUrlLoad("https://example.com/");
+ await waitForUrlLoad("about:blank");
+ await waitForUrlLoad("http://example.com/");
+ await waitForUrlLoad("about:blank");
+ await waitForUrlLoad("http://example.com/");
+
+ Assert.equal(stub.callCount, 3, "Stub called 3 times for example.com host");
+ Assert.equal(
+ stub.firstCall.args[1].context.visitsCount,
+ 1,
+ "First call should have count 1"
+ );
+ Assert.equal(
+ stub.thirdCall.args[1].context.visitsCount,
+ 2,
+ "Third call should have count 2 for http://example.com"
+ );
+});
+
+add_task(async function test_captivePortalLogin() {
+ const stub = sinon.stub();
+ const captivePortalTrigger =
+ ASRouterTriggerListeners.get("captivePortalLogin");
+
+ captivePortalTrigger.init(stub);
+
+ Services.obs.notifyObservers(this, "captive-portal-login-success", {});
+
+ Assert.ok(stub.called, "Called after login event");
+
+ captivePortalTrigger.uninit();
+
+ Services.obs.notifyObservers(this, "captive-portal-login-success", {});
+
+ Assert.equal(stub.callCount, 1, "Not called after uninit");
+});
+
+add_task(async function test_preferenceObserver() {
+ const stub = sinon.stub();
+ const poTrigger = ASRouterTriggerListeners.get("preferenceObserver");
+
+ poTrigger.uninit();
+
+ poTrigger.init(stub, ["foo.bar", "bar.foo"]);
+
+ Services.prefs.setStringPref("foo.bar", "foo.bar");
+
+ Assert.ok(stub.calledOnce, "Called for pref foo.bar");
+ Assert.deepEqual(
+ stub.firstCall.args[1],
+ {
+ id: "preferenceObserver",
+ param: { type: "foo.bar" },
+ },
+ "Called with expected arguments"
+ );
+
+ Services.prefs.setStringPref("bar.foo", "bar.foo");
+ Assert.ok(stub.calledTwice, "Called again for second pref.");
+ Services.prefs.clearUserPref("foo.bar");
+ Assert.ok(stub.calledThrice, "Called when clearing the pref as well.");
+
+ stub.resetHistory();
+ poTrigger.uninit();
+
+ Services.prefs.clearUserPref("bar.foo");
+ Assert.ok(stub.notCalled, "Not called after uninit");
+});
+
+add_task(async function test_nthTabClosed() {
+ const handlerStub = sinon.stub();
+ const tabClosedTrigger = ASRouterTriggerListeners.get("nthTabClosed");
+ tabClosedTrigger.uninit();
+ tabClosedTrigger.init(handlerStub);
+
+ let tab1 = await BrowserTestUtils.openNewForegroundTab(gBrowser);
+ let tab2 = await BrowserTestUtils.openNewForegroundTab(gBrowser);
+
+ BrowserTestUtils.removeTab(tab1);
+ Assert.ok(handlerStub.calledOnce, "Called once after first tab closed");
+
+ BrowserTestUtils.removeTab(tab2);
+ Assert.ok(handlerStub.calledTwice, "Called twice after second tab closed");
+
+ handlerStub.resetHistory();
+ tabClosedTrigger.uninit();
+
+ Assert.ok(handlerStub.notCalled, "Not called after uninit");
+});
+
+add_task(async function test_cookieBannerDetected() {
+ const handlerStub = sinon.stub();
+ const bannerDetectedTrigger = ASRouterTriggerListeners.get(
+ "cookieBannerDetected"
+ );
+ bannerDetectedTrigger.uninit();
+ bannerDetectedTrigger.init(handlerStub);
+
+ const win = await BrowserTestUtils.openNewBrowserWindow();
+ let eventWait = BrowserTestUtils.waitForEvent(win, "cookiebannerdetected");
+ win.dispatchEvent(new Event("cookiebannerdetected"));
+ await eventWait;
+ let closeWindow = BrowserTestUtils.closeWindow(win);
+
+ Assert.ok(
+ handlerStub.called,
+ "Called after `cookiebannerdetected` event fires"
+ );
+
+ handlerStub.resetHistory();
+ bannerDetectedTrigger.uninit();
+
+ Assert.ok(handlerStub.notCalled, "Not called after uninit");
+ await closeWindow;
+});
+
+function getIdleTriggerMock() {
+ const idleTrigger = ASRouterTriggerListeners.get("activityAfterIdle");
+ idleTrigger.uninit();
+ const sandbox = sinon.createSandbox();
+ const handlerStub = sandbox.stub();
+ sandbox.stub(idleTrigger, "_triggerDelay").value(0);
+ sandbox.stub(idleTrigger, "_wakeDelay").value(30);
+ sandbox.stub(idleTrigger, "_idleService").value(mockIdleService);
+ let restored = false;
+ const restore = () => {
+ if (restored) return;
+ restored = true;
+ idleTrigger.uninit();
+ sandbox.restore();
+ };
+ registerCleanupFunction(restore);
+ idleTrigger.init(handlerStub);
+ return { idleTrigger, handlerStub, restore };
+}
+
+// Test that the trigger fires under normal conditions.
+add_task(async function test_activityAfterIdle() {
+ const { handlerStub, restore } = getIdleTriggerMock();
+ let firedOnActive = new Promise(resolve =>
+ handlerStub.callsFake(() => resolve(true))
+ );
+ mockIdleService._fireObservers("idle");
+ await TestUtils.waitForTick();
+ ok(handlerStub.notCalled, "Not called when idle");
+ mockIdleService._fireObservers("active");
+ ok(await firedOnActive, "Called once when active after idle");
+ restore();
+});
+
+// Test that the trigger does not fire when the active window is private.
+add_task(async function test_activityAfterIdlePrivateWindow() {
+ const { handlerStub, restore } = getIdleTriggerMock();
+ let privateWin = await BrowserTestUtils.openNewBrowserWindow({
+ private: true,
+ });
+ ok(PrivateBrowsingUtils.isWindowPrivate(privateWin), "Window is private");
+ await TestUtils.waitForTick();
+ mockIdleService._fireObservers("idle");
+ await TestUtils.waitForTick();
+ mockIdleService._fireObservers("active");
+ await TestUtils.waitForTick();
+ ok(handlerStub.notCalled, "Not called when active window is private");
+ await BrowserTestUtils.closeWindow(privateWin);
+ restore();
+});
+
+// Test that the trigger does not fire when the window is minimized, but does
+// fire after the window is restored.
+add_task(async function test_activityAfterIdleHiddenWindow() {
+ const { handlerStub, restore } = getIdleTriggerMock();
+ let firedOnRestore = new Promise(resolve =>
+ handlerStub.callsFake(() => resolve(true))
+ );
+ window.minimize();
+ await BrowserTestUtils.waitForCondition(
+ () => window.windowState === window.STATE_MINIMIZED,
+ "Window should be minimized"
+ );
+ mockIdleService._fireObservers("idle");
+ await TestUtils.waitForTick();
+ mockIdleService._fireObservers("active");
+ await TestUtils.waitForTick();
+ ok(handlerStub.notCalled, "Not called when window is minimized");
+ window.restore();
+ ok(await firedOnRestore, "Called once after restoring minimized window");
+ restore();
+});
+
+// Test that the trigger does not fire immediately after waking from sleep.
+add_task(async function test_activityAfterIdleWake() {
+ const { handlerStub, restore } = getIdleTriggerMock();
+ let firedAfterWake = new Promise(resolve =>
+ handlerStub.callsFake(() => resolve(true))
+ );
+ mockIdleService._fireObservers("wake_notification");
+ mockIdleService._fireObservers("idle");
+ await sleepMs(1);
+ mockIdleService._fireObservers("active");
+ await sleepMs(inChaosMode ? 32 : 300);
+ ok(handlerStub.notCalled, "Not called immediately after waking from sleep");
+
+ mockIdleService._fireObservers("idle");
+ await TestUtils.waitForTick();
+ mockIdleService._fireObservers("active");
+ ok(
+ await firedAfterWake,
+ "Called once after waiting for wake delay before firing idle"
+ );
+ restore();
+});
+
+add_task(async function test_formAutofillTrigger() {
+ const sandbox = sinon.createSandbox();
+ const handlerStub = sandbox.stub();
+ const formAutofillTrigger = ASRouterTriggerListeners.get("formAutofill");
+ sandbox.stub(formAutofillTrigger, "_triggerDelay").value(0);
+ formAutofillTrigger.uninit();
+ formAutofillTrigger.init(handlerStub);
+
+ function notifyCreditCardSaved() {
+ Services.obs.notifyObservers(
+ {
+ wrappedJSObject: { sourceSync: false, collectionName: "creditCards" },
+ },
+ formAutofillTrigger._topic,
+ "add"
+ );
+ }
+
+ // Saving credit cards for autofill currently fails for some hardware
+ // configurations, so mock the event instead of really adding a card.
+ notifyCreditCardSaved();
+ await sleepMs(1);
+ Assert.ok(handlerStub.called, "Called after event");
+
+ // Test that the trigger doesn't fire when the credit card manager is open.
+ handlerStub.resetHistory();
+ await BrowserTestUtils.withNewTab(
+ { gBrowser, url: "about:preferences#privacy" },
+ async browser => {
+ await SpecialPowers.spawn(browser, [], async () =>
+ (
+ await ContentTaskUtils.waitForCondition(
+ () => content.document.querySelector("#creditCardAutofill button"),
+ "Waiting for credit card manager button"
+ )
+ )?.click()
+ );
+ await BrowserTestUtils.waitForCondition(
+ () => browser.contentWindow?.gSubDialog?.dialogs.length
+ );
+ notifyCreditCardSaved();
+ await sleepMs(1);
+ Assert.ok(
+ handlerStub.notCalled,
+ "Not called when credit card manager is open"
+ );
+ }
+ );
+
+ formAutofillTrigger.uninit();
+ handlerStub.resetHistory();
+ notifyCreditCardSaved();
+ await sleepMs(1);
+ Assert.ok(handlerStub.notCalled, "Not called after uninit");
+
+ sandbox.restore();
+ formAutofillTrigger.uninit();
+});