/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ function test() { waitForExplicitFinish(); ok(PopupNotifications, "PopupNotifications object exists"); ok(PopupNotifications.panel, "PopupNotifications panel exists"); setup(); } var gNotification; var tests = [ // panel updates should fire the showing and shown callbacks again. { id: "Test#1", run() { this.notifyObj = new BasicNotification(this.id); this.notification = showNotification(this.notifyObj); }, onShown(popup) { checkPopup(popup, this.notifyObj); this.notifyObj.showingCallbackTriggered = false; this.notifyObj.shownCallbackTriggered = false; // Force an update of the panel. This is typically called // automatically when receiving 'activate' or 'TabSelect' events, // but from a setTimeout, which is inconvenient for the test. PopupNotifications._update(); checkPopup(popup, this.notifyObj); this.notification.remove(); }, onHidden() {}, }, // A first dismissed notification shouldn't stop _update from showing a second notification { id: "Test#2", run() { this.notifyObj1 = new BasicNotification(this.id); this.notifyObj1.id += "_1"; this.notifyObj1.anchorID = "default-notification-icon"; this.notifyObj1.options.dismissed = true; this.notification1 = showNotification(this.notifyObj1); this.notifyObj2 = new BasicNotification(this.id); this.notifyObj2.id += "_2"; this.notifyObj2.anchorID = "geo-notification-icon"; this.notifyObj2.options.dismissed = true; this.notification2 = showNotification(this.notifyObj2); this.notification2.dismissed = false; PopupNotifications._update(); }, onShown(popup) { checkPopup(popup, this.notifyObj2); this.notification1.remove(); this.notification2.remove(); }, onHidden(popup) {}, }, // The anchor icon should be shown for notifications in background windows. { id: "Test#3", async run() { let notifyObj = new BasicNotification(this.id); notifyObj.options.dismissed = true; let win = await BrowserTestUtils.openNewBrowserWindow(); // Open the notification in the original window, now in the background. let notification = showNotification(notifyObj); let anchor = document.getElementById("default-notification-icon"); is(anchor.getAttribute("showing"), "true", "the anchor is shown"); notification.remove(); await BrowserTestUtils.closeWindow(win); await waitForWindowReadyForPopupNotifications(window); goNext(); }, }, // Test that persistent doesn't allow the notification to persist after // navigation. { id: "Test#4", async run() { this.oldSelectedTab = gBrowser.selectedTab; await BrowserTestUtils.openNewForegroundTab( gBrowser, // eslint-disable-next-line @microsoft/sdl/no-insecure-url "http://example.com/" ); this.notifyObj = new BasicNotification(this.id); this.notifyObj.addOptions({ persistent: true, }); this.notification = showNotification(this.notifyObj); }, async onShown(popup) { this.complete = false; // eslint-disable-next-line @microsoft/sdl/no-insecure-url await promiseTabLoadEvent(gBrowser.selectedTab, "http://example.org/"); // eslint-disable-next-line @microsoft/sdl/no-insecure-url await promiseTabLoadEvent(gBrowser.selectedTab, "http://example.com/"); // This code should not be executed. ok(false, "Should have removed the notification after navigation"); // Properly dismiss and cleanup in case the unthinkable happens. this.complete = true; triggerSecondaryCommand(popup, 0); }, onHidden(popup) { ok( !this.complete, "Should have hidden the notification after navigation" ); this.notification.remove(); gBrowser.removeTab(gBrowser.selectedTab); gBrowser.selectedTab = this.oldSelectedTab; }, }, // Test that persistent allows the notification to persist until explicitly // dismissed. { id: "Test#5", async run() { this.oldSelectedTab = gBrowser.selectedTab; await BrowserTestUtils.openNewForegroundTab( gBrowser, // eslint-disable-next-line @microsoft/sdl/no-insecure-url "http://example.com/" ); this.notifyObj = new BasicNotification(this.id); this.notifyObj.addOptions({ persistent: true, }); this.notification = showNotification(this.notifyObj); }, async onShown(popup) { this.complete = false; // Notification should persist after attempt to dismiss by clicking on the // content area. let browser = gBrowser.selectedBrowser; await BrowserTestUtils.synthesizeMouseAtCenter("body", {}, browser); // Notification should be hidden after dismissal via Don't Allow. this.complete = true; triggerSecondaryCommand(popup, 0); }, onHidden(popup) { ok( this.complete, "Should have hidden the notification after clicking Not Now" ); this.notification.remove(); gBrowser.removeTab(gBrowser.selectedTab); gBrowser.selectedTab = this.oldSelectedTab; }, }, // Test that persistent panels are still open after switching to another tab // and back. { id: "Test#6a", run() { this.notifyObj = new BasicNotification(this.id); this.notifyObj.options.persistent = true; gNotification = showNotification(this.notifyObj); }, async onShown(popup) { this.oldSelectedTab = gBrowser.selectedTab; await BrowserTestUtils.openNewForegroundTab( gBrowser, // eslint-disable-next-line @microsoft/sdl/no-insecure-url "http://example.com/" ); }, onHidden(popup) { ok(true, "Should have hidden the notification after tab switch"); gBrowser.removeTab(gBrowser.selectedTab); gBrowser.selectedTab = this.oldSelectedTab; }, }, // Second part of the previous test that compensates for the limitation in // runNextTest that expects a single onShown/onHidden invocation per test. { id: "Test#6b", run() { let id = PopupNotifications.panel.firstElementChild.getAttribute("popupid"); ok( id.endsWith("Test#6a"), "Should have found the notification from Test6a" ); ok( PopupNotifications.isPanelOpen, "Should have shown the popup again after getting back to the tab" ); gNotification.remove(); gNotification = null; goNext(); }, }, // Test that persistent panels are still open after switching to another // window and back. { id: "Test#7", async run() { this.oldSelectedTab = gBrowser.selectedTab; await BrowserTestUtils.openNewForegroundTab( gBrowser, // eslint-disable-next-line @microsoft/sdl/no-insecure-url "http://example.com/" ); let firstTab = gBrowser.selectedTab; await BrowserTestUtils.openNewForegroundTab( gBrowser, // eslint-disable-next-line @microsoft/sdl/no-insecure-url "http://example.com/" ); let shown = waitForNotificationPanel(); let notifyObj = new BasicNotification(this.id); notifyObj.options.persistent = true; this.notification = showNotification(notifyObj); await shown; ok( notifyObj.shownCallbackTriggered, "Should have triggered the shown event" ); ok( notifyObj.showingCallbackTriggered, "Should have triggered the showing event" ); // Reset to false so that we can ensure these are not fired a second time. notifyObj.shownCallbackTriggered = false; notifyObj.showingCallbackTriggered = false; let timeShown = this.notification.timeShown; let promiseWin = BrowserTestUtils.waitForNewWindow(); gBrowser.replaceTabWithWindow(firstTab); let win = await promiseWin; let anchor = win.document.getElementById("default-notification-icon"); win.PopupNotifications._reshowNotifications(anchor); ok( !win.PopupNotifications.panel.children.length, "no notification displayed in new window" ); await BrowserTestUtils.closeWindow(win); await waitForWindowReadyForPopupNotifications(window); let id = PopupNotifications.panel.firstElementChild.getAttribute("popupid"); ok( id.endsWith("Test#7"), "Should have found the notification from Test7" ); ok( PopupNotifications.isPanelOpen, "Should have kept the popup on the first window" ); ok( !notifyObj.dismissalCallbackTriggered, "Should not have triggered a dismissed event" ); ok( !notifyObj.shownCallbackTriggered, "Should not have triggered a second shown event" ); ok( !notifyObj.showingCallbackTriggered, "Should not have triggered a second showing event" ); ok( this.notification.timeShown > timeShown, "should have updated timeShown to restart the security delay" ); this.notification.remove(); gBrowser.removeTab(gBrowser.selectedTab); gBrowser.selectedTab = this.oldSelectedTab; goNext(); }, }, // Test that only the first persistent notification is shown on update { id: "Test#8", run() { this.notifyObj1 = new BasicNotification(this.id); this.notifyObj1.id += "_1"; this.notifyObj1.anchorID = "default-notification-icon"; this.notifyObj1.options.persistent = true; this.notification1 = showNotification(this.notifyObj1); this.notifyObj2 = new BasicNotification(this.id); this.notifyObj2.id += "_2"; this.notifyObj2.anchorID = "geo-notification-icon"; this.notifyObj2.options.persistent = true; this.notification2 = showNotification(this.notifyObj2); PopupNotifications._update(); }, onShown(popup) { checkPopup(popup, this.notifyObj1); this.notification1.remove(); this.notification2.remove(); }, onHidden(popup) {}, }, // Test that persistent notifications are shown stacked by anchor on update { id: "Test#9", run() { this.notifyObj1 = new BasicNotification(this.id); this.notifyObj1.id += "_1"; this.notifyObj1.anchorID = "default-notification-icon"; this.notifyObj1.options.persistent = true; this.notification1 = showNotification(this.notifyObj1); this.notifyObj2 = new BasicNotification(this.id); this.notifyObj2.id += "_2"; this.notifyObj2.anchorID = "geo-notification-icon"; this.notifyObj2.options.persistent = true; this.notification2 = showNotification(this.notifyObj2); this.notifyObj3 = new BasicNotification(this.id); this.notifyObj3.id += "_3"; this.notifyObj3.anchorID = "default-notification-icon"; this.notifyObj3.options.persistent = true; this.notification3 = showNotification(this.notifyObj3); PopupNotifications._update(); }, onShown(popup) { let notifications = popup.children; is(notifications.length, 2, "two notifications displayed"); let [notification1, notification2] = notifications; is( notification1.id, this.notifyObj1.id + "-notification", "id 1 matches" ); is( notification2.id, this.notifyObj3.id + "-notification", "id 2 matches" ); this.notification1.remove(); this.notification2.remove(); this.notification3.remove(); }, onHidden(popup) {}, }, // Test that on closebutton click, only the persistent notification // that contained the closebutton loses its persistent status. { id: "Test#10", run() { this.notifyObj1 = new BasicNotification(this.id); this.notifyObj1.id += "_1"; this.notifyObj1.anchorID = "geo-notification-icon"; this.notifyObj1.options.persistent = true; this.notifyObj1.options.hideClose = false; this.notification1 = showNotification(this.notifyObj1); this.notifyObj2 = new BasicNotification(this.id); this.notifyObj2.id += "_2"; this.notifyObj2.anchorID = "geo-notification-icon"; this.notifyObj2.options.persistent = true; this.notifyObj2.options.hideClose = false; this.notification2 = showNotification(this.notifyObj2); this.notifyObj3 = new BasicNotification(this.id); this.notifyObj3.id += "_3"; this.notifyObj3.anchorID = "geo-notification-icon"; this.notifyObj3.options.persistent = true; this.notifyObj3.options.hideClose = false; this.notification3 = showNotification(this.notifyObj3); PopupNotifications._update(); }, onShown(popup) { let notifications = popup.children; is(notifications.length, 3, "three notifications displayed"); EventUtils.synthesizeMouseAtCenter(notifications[1].closebutton, {}); }, onHidden(popup) { let notifications = popup.children; is(notifications.length, 2, "two notifications displayed"); ok(this.notification1.options.persistent, "notification 1 is persistent"); ok( !this.notification2.options.persistent, "notification 2 is not persistent" ); ok(this.notification3.options.persistent, "notification 3 is persistent"); this.notification1.remove(); this.notification2.remove(); this.notification3.remove(); }, }, // Test clicking the anchor icon. // Clicking the anchor of an already visible persistent notification should // focus the main action button, but not cause additional showing/shown event // callback calls. // Clicking the anchor of a dismissed notification should show it, even when // the currently displayed notification is a persistent one. { id: "Test#11", async run() { await SpecialPowers.pushPrefEnv({ set: [["accessibility.tabfocus", 7]] }); function clickAnchor(notifyObj) { let anchor = document.getElementById(notifyObj.anchorID); EventUtils.synthesizeMouseAtCenter(anchor, {}); } let popup = PopupNotifications.panel; let notifyObj1 = new BasicNotification(this.id); notifyObj1.id += "_1"; notifyObj1.anchorID = "default-notification-icon"; notifyObj1.options.persistent = true; let shown = waitForNotificationPanel(); let notification1 = showNotification(notifyObj1); await shown; checkPopup(popup, notifyObj1); ok( !notifyObj1.dismissalCallbackTriggered, "Should not have dismissed the notification" ); notifyObj1.shownCallbackTriggered = false; notifyObj1.showingCallbackTriggered = false; // Click the anchor. This should focus the closebutton // (because it's the first focusable element), but not // call event callbacks on the notification object. clickAnchor(notifyObj1); is(document.activeElement, popup.children[0].closebutton); ok( !notifyObj1.dismissalCallbackTriggered, "Should not have dismissed the notification" ); ok( !notifyObj1.shownCallbackTriggered, "Should have triggered the shown event again" ); ok( !notifyObj1.showingCallbackTriggered, "Should have triggered the showing event again" ); // Add another notification. let notifyObj2 = new BasicNotification(this.id); notifyObj2.id += "_2"; notifyObj2.anchorID = "geo-notification-icon"; notifyObj2.options.dismissed = true; let notification2 = showNotification(notifyObj2); // Click the anchor of the second notification, this should dismiss the // first notification. shown = waitForNotificationPanel(); clickAnchor(notifyObj2); await shown; checkPopup(popup, notifyObj2); ok( notifyObj1.dismissalCallbackTriggered, "Should have dismissed the first notification" ); // Click the anchor of the first notification, it should be shown again. shown = waitForNotificationPanel(); clickAnchor(notifyObj1); await shown; checkPopup(popup, notifyObj1); ok( notifyObj2.dismissalCallbackTriggered, "Should have dismissed the second notification" ); // Cleanup. notification1.remove(); notification2.remove(); goNext(); }, }, ];