diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /dom/notification/test | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/notification/test')
20 files changed, 1722 insertions, 0 deletions
diff --git a/dom/notification/test/browser/browser.toml b/dom/notification/test/browser/browser.toml new file mode 100644 index 0000000000..426a79b940 --- /dev/null +++ b/dom/notification/test/browser/browser.toml @@ -0,0 +1,4 @@ +[DEFAULT] + +["browser_permission_dismiss.js"] +support-files = ["notification.html"] diff --git a/dom/notification/test/browser/browser_permission_dismiss.js b/dom/notification/test/browser/browser_permission_dismiss.js new file mode 100644 index 0000000000..72b2db2827 --- /dev/null +++ b/dom/notification/test/browser/browser_permission_dismiss.js @@ -0,0 +1,231 @@ +"use strict"; + +const { PermissionTestUtils } = ChromeUtils.importESModule( + "resource://testing-common/PermissionTestUtils.sys.mjs" +); + +const ORIGIN_URI = Services.io.newURI("https://example.com"); +const PERMISSION_NAME = "desktop-notification"; +const PROMPT_ALLOW_BUTTON = -1; +const PROMPT_NOT_NOW_BUTTON = 0; +const PROMPT_NEVER_BUTTON = 1; +const TEST_URL = + "https://example.com/browser/dom/notification/test/browser/notification.html"; + +/** + * Clicks the specified web-notifications prompt button. + * + * @param {Number} aButtonIndex Number indicating which button to click. + * See the constants in this file. + * @note modified from toolkit/components/passwordmgr/test/browser/head.js + */ +function clickDoorhangerButton(aButtonIndex, browser) { + let popup = PopupNotifications.getNotification("web-notifications", browser); + let notifications = popup.owner.panel.childNodes; + ok(notifications.length, "at least one notification displayed"); + ok(true, notifications.length + " notification(s)"); + let notification = notifications[0]; + + if (aButtonIndex == PROMPT_ALLOW_BUTTON) { + ok(true, "Triggering main action (allow the permission)"); + notification.button.doCommand(); + } else if (aButtonIndex == PROMPT_NEVER_BUTTON) { + ok(true, "Triggering secondary action (deny the permission permanently)"); + notification.menupopup.querySelector("menuitem").doCommand(); + } else { + ok(true, "Triggering secondary action (deny the permission temporarily)"); + notification.secondaryButton.doCommand(); + } +} + +/** + * Opens a tab which calls `Notification.requestPermission()` with a callback + * argument, calls the `task` function while the permission prompt is open, + * and verifies that the expected permission is set. + * + * @param {Function} task Task function to run to interact with the prompt. + * @param {String} permission Expected permission value. + * @return {Promise} resolving when the task function is done and the tab + * closes. + */ +function tabWithRequest( + task, + permission, + browser = window.gBrowser, + privateWindow = false +) { + clearPermission(ORIGIN_URI, PERMISSION_NAME, privateWindow); + + return BrowserTestUtils.withNewTab( + { + gBrowser: browser, + url: TEST_URL, + }, + async function (linkedBrowser) { + let requestPromise = SpecialPowers.spawn( + linkedBrowser, + [ + { + permission, + }, + ], + async function ({ permission }) { + function requestCallback(perm) { + is( + perm, + permission, + "Should call the legacy callback with the permission state" + ); + } + let perm = await content.window.Notification.requestPermission( + requestCallback + ); + is( + perm, + permission, + "Should resolve the promise with the permission state" + ); + } + ); + + await task(linkedBrowser); + await requestPromise; + } + ); +} + +function clearPermission(origin, permissionName, isPrivate) { + let principal = Services.scriptSecurityManager.createContentPrincipal( + origin, + isPrivate ? { privateBrowsingId: 1 } : {} /* attrs */ + ); + PermissionTestUtils.remove(principal, permissionName); +} + +add_setup(async function () { + Services.prefs.setBoolPref( + "dom.webnotifications.requireuserinteraction", + false + ); + Services.prefs.setBoolPref( + "permissions.desktop-notification.notNow.enabled", + true + ); + SimpleTest.registerCleanupFunction(() => { + Services.prefs.clearUserPref("dom.webnotifications.requireuserinteraction"); + Services.prefs.clearUserPref( + "permissions.desktop-notification.notNow.enabled" + ); + + clearPermission(ORIGIN_URI, PERMISSION_NAME, false /* private origin */); + clearPermission(ORIGIN_URI, PERMISSION_NAME, true /* private origin */); + }); +}); + +add_task(async function test_requestPermission_granted() { + await tabWithRequest(async function (linkedBrowser) { + await BrowserTestUtils.waitForEvent(PopupNotifications.panel, "popupshown"); + clickDoorhangerButton(PROMPT_ALLOW_BUTTON, linkedBrowser); + }, "granted"); + + ok( + !PopupNotifications.getNotification("web-notifications"), + "Should remove the doorhanger notification icon if granted" + ); + + is( + PermissionTestUtils.testPermission(ORIGIN_URI, PERMISSION_NAME), + Services.perms.ALLOW_ACTION, + "Check permission in perm. manager" + ); +}); + +add_task(async function test_requestPermission_denied_temporarily() { + await tabWithRequest(async function (linkedBrowser) { + await BrowserTestUtils.waitForEvent(PopupNotifications.panel, "popupshown"); + clickDoorhangerButton(PROMPT_NOT_NOW_BUTTON, linkedBrowser); + }, "default"); + + ok( + !PopupNotifications.getNotification("web-notifications"), + "Should remove the doorhanger notification icon if denied" + ); + + is( + PermissionTestUtils.testPermission(ORIGIN_URI, PERMISSION_NAME), + Services.perms.UNKNOWN_ACTION, + "Check permission in perm. manager" + ); +}); + +add_task(async function test_requestPermission_denied_permanently() { + await tabWithRequest(async function (linkedBrowser) { + await BrowserTestUtils.waitForEvent(PopupNotifications.panel, "popupshown"); + clickDoorhangerButton(PROMPT_NEVER_BUTTON, linkedBrowser); + }, "denied"); + + ok( + !PopupNotifications.getNotification("web-notifications"), + "Should remove the doorhanger notification icon if denied" + ); + + is( + PermissionTestUtils.testPermission(ORIGIN_URI, PERMISSION_NAME), + Services.perms.DENY_ACTION, + "Check permission in perm. manager" + ); +}); + +add_task( + async function test_requestPermission_defaultPrivateNotificationsPref() { + ok( + !SpecialPowers.getBoolPref( + "dom.webnotifications.privateBrowsing.enableDespiteLimitations" + ), + "Pref should be default disabled" + ); + } +); + +add_task(async function test_requestPermission_privateNotifications() { + async function run(perm) { + let privateWindow = await BrowserTestUtils.openNewBrowserWindow({ + private: true, + }); + + await tabWithRequest( + async linkedBrowser => { + if (perm != Services.perms.UNKNOWN_ACTION) { + await BrowserTestUtils.waitForEvent( + privateWindow.PopupNotifications.panel, + "popupshown" + ); + + clickDoorhangerButton(PROMPT_ALLOW_BUTTON, linkedBrowser); + } + }, + perm == Services.perms.ALLOW_ACTION ? "granted" : "denied", + privateWindow.gBrowser, + true /* privateWindow */ + ); + + ok( + !PopupNotifications.getNotification( + "web-notifications", + privateWindow.gBrowser + ), + "doorhanger should have been removed in all cases by now" + ); + + await BrowserTestUtils.closeWindow(privateWindow); + } + + await run(Services.perms.UNKNOWN_ACTION); + + await SpecialPowers.pushPrefEnv({ + set: [ + ["dom.webnotifications.privateBrowsing.enableDespiteLimitations", true], + ], + }); + await run(Services.perms.ALLOW_ACTION); +}); diff --git a/dom/notification/test/browser/notification.html b/dom/notification/test/browser/notification.html new file mode 100644 index 0000000000..0ceeb8ea46 --- /dev/null +++ b/dom/notification/test/browser/notification.html @@ -0,0 +1,11 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8"> + <title>Notifications test</title> + </head> + + <body> + + </body> +</html> diff --git a/dom/notification/test/chrome/chrome.toml b/dom/notification/test/chrome/chrome.toml new file mode 100644 index 0000000000..d4027438d2 --- /dev/null +++ b/dom/notification/test/chrome/chrome.toml @@ -0,0 +1,3 @@ +[DEFAULT] + +["test_notification_system_principal.xhtml"] diff --git a/dom/notification/test/chrome/test_notification_system_principal.xhtml b/dom/notification/test/chrome/test_notification_system_principal.xhtml new file mode 100644 index 0000000000..0700d59338 --- /dev/null +++ b/dom/notification/test/chrome/test_notification_system_principal.xhtml @@ -0,0 +1,76 @@ +<?xml version="1.0"?> +<?xml-stylesheet type="text/css" href="chrome://global/skin"?> +<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=874090 +--> +<window title="Mozilla Bug 874090" onload="runTests()" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> + <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/> + + <!-- test results are displayed in the html:body --> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=874090" + target="_blank">Mozilla Bug 874090</a> + </body> + + <!-- test code goes here --> + <script type="application/javascript"> + <![CDATA[ + /** Test for Bug 874090 **/ + const MOCK_CID = Components.ID("{2a0f83c4-8818-4914-a184-f1172b4eaaa7}"); + const ALERTS_SERVICE_CONTRACT_ID = "@mozilla.org/alerts-service;1"; + + var mockAlertsService = { + showAlert(alert, alertListener) { + ok(true, "System principal was granted permission and is able to call showAlert."); + unregisterMock(); + SimpleTest.finish(); + }, + + showAlertNotification(imageUrl, title, text, textClickable, + cookie, alertListener, name, dir, lang, data) { + this.showAlert(); + }, + + QueryInterface: ChromeUtils.generateQI(["nsIAlertsService"]), + + createInstance(aIID) { + return this.QueryInterface(aIID); + } + }; + + function registerMock() { + Components.manager.QueryInterface(Ci.nsIComponentRegistrar). + registerFactory(MOCK_CID, "alerts service", ALERTS_SERVICE_CONTRACT_ID, mockAlertsService); + } + + function unregisterMock() { + Components.manager.QueryInterface(Ci.nsIComponentRegistrar). + unregisterFactory(MOCK_CID, mockAlertsService); + } + + function runTests() { + registerMock(); + + is(Notification.permission, "granted", "System principal should be automatically granted permission."); + + Notification.requestPermission(function(permission) { + is(permission, "granted", "System principal should be granted permission when calling requestPermission."); + + if (permission == "granted") { + // Create a notification and make sure that it is able to call into + // the mock alert service to show the notification. + new Notification("Hello"); + } else { + unregisterMock(); + SimpleTest.finish(); + } + }); + } + + SimpleTest.waitForExplicitFinish(); + ]]> + </script> +</window> diff --git a/dom/notification/test/mochitest/MockServices.js b/dom/notification/test/mochitest/MockServices.js new file mode 100644 index 0000000000..4b2526c26a --- /dev/null +++ b/dom/notification/test/mochitest/MockServices.js @@ -0,0 +1,176 @@ +var MockServices = (function () { + "use strict"; + + const MOCK_ALERTS_CID = SpecialPowers.wrap(SpecialPowers.Components).ID( + "{48068bc2-40ab-4904-8afd-4cdfb3a385f3}" + ); + const ALERTS_SERVICE_CONTRACT_ID = "@mozilla.org/alerts-service;1"; + + const MOCK_SYSTEM_ALERTS_CID = SpecialPowers.wrap( + SpecialPowers.Components + ).ID("{e86d888c-e41b-4b78-9104-2f2742a532de}"); + const SYSTEM_ALERTS_SERVICE_CONTRACT_ID = + "@mozilla.org/system-alerts-service;1"; + + var registrar = SpecialPowers.wrap( + SpecialPowers.Components + ).manager.QueryInterface(SpecialPowers.Ci.nsIComponentRegistrar); + + var activeAlertNotifications = Object.create(null); + + var activeAppNotifications = Object.create(null); + + window.addEventListener("mock-notification-close-event", function (e) { + for (var alertName in activeAlertNotifications) { + var notif = activeAlertNotifications[alertName]; + if (notif.title === e.detail.title) { + notif.listener.observe(null, "alertfinished", null); + delete activeAlertNotifications[alertName]; + delete activeAppNotifications[alertName]; + return; + } + } + }); + + var mockAlertsService = { + showPersistentNotification(persistentData, alert, alertListener) { + this.showAlert(alert, alertListener); + }, + + showAlert(alert, alertListener) { + var listener = SpecialPowers.wrap(alertListener); + activeAlertNotifications[alert.name] = { + listener, + cookie: alert.cookie, + title: alert.title, + }; + + // fake async alert show event + if (listener) { + setTimeout(function () { + listener.observe(null, "alertshow", alert.cookie); + }, 100); + setTimeout(function () { + listener.observe(null, "alertclickcallback", alert.cookie); + }, 100); + } + }, + + showAlertNotification( + imageUrl, + title, + text, + textClickable, + cookie, + alertListener, + name + ) { + this.showAlert( + { + name, + cookie, + title, + }, + alertListener + ); + }, + + closeAlert(name) { + var alertNotification = activeAlertNotifications[name]; + if (alertNotification) { + if (alertNotification.listener) { + alertNotification.listener.observe( + null, + "alertfinished", + alertNotification.cookie + ); + } + delete activeAlertNotifications[name]; + } + + var appNotification = activeAppNotifications[name]; + if (appNotification) { + delete activeAppNotifications[name]; + } + }, + + QueryInterface(aIID) { + if ( + SpecialPowers.wrap(aIID).equals(SpecialPowers.Ci.nsISupports) || + SpecialPowers.wrap(aIID).equals(SpecialPowers.Ci.nsIAlertsService) + ) { + return this; + } + throw SpecialPowers.Components.results.NS_ERROR_NO_INTERFACE; + }, + + createInstance(aIID) { + return this.QueryInterface(aIID); + }, + }; + mockAlertsService = SpecialPowers.wrapCallbackObject(mockAlertsService); + + // MockServices API + return { + register() { + try { + this.originalAlertsCID = registrar.contractIDToCID( + ALERTS_SERVICE_CONTRACT_ID + ); + } catch (ex) { + this.originalAlertsCID = null; + } + try { + this.originalSystemAlertsCID = registrar.contractIDToCID( + SYSTEM_ALERTS_SERVICE_CONTRACT_ID + ); + } catch (ex) { + this.originalSystemAlertsCID = null; + } + + registrar.registerFactory( + MOCK_ALERTS_CID, + "alerts service", + ALERTS_SERVICE_CONTRACT_ID, + mockAlertsService + ); + + registrar.registerFactory( + MOCK_SYSTEM_ALERTS_CID, + "system alerts service", + SYSTEM_ALERTS_SERVICE_CONTRACT_ID, + mockAlertsService + ); + }, + + unregister() { + registrar.unregisterFactory(MOCK_ALERTS_CID, mockAlertsService); + registrar.unregisterFactory(MOCK_SYSTEM_ALERTS_CID, mockAlertsService); + + // Passing `null` for the factory re-maps the contract ID to the + // entry for its original CID. + + if (this.originalAlertsCID) { + registrar.registerFactory( + this.originalAlertsCID, + "alerts service", + ALERTS_SERVICE_CONTRACT_ID, + null + ); + } + + if (this.originalSystemAlertsCID) { + registrar.registerFactory( + this.originalSystemAlertsCID, + "system alerts service", + SYSTEM_ALERTS_SERVICE_CONTRACT_ID, + null + ); + } + }, + + activeAlertNotifications, + + activeAppNotifications, + }; +})(); diff --git a/dom/notification/test/mochitest/NotificationTest.js b/dom/notification/test/mochitest/NotificationTest.js new file mode 100644 index 0000000000..400ff56253 --- /dev/null +++ b/dom/notification/test/mochitest/NotificationTest.js @@ -0,0 +1,102 @@ +var NotificationTest = (function () { + "use strict"; + + function info(msg, name) { + SimpleTest.info("::Notification Tests::" + (name || ""), msg); + } + + function setup_testing_env() { + SimpleTest.waitForExplicitFinish(); + // turn on testing pref (used by notification.cpp, and mock the alerts + return SpecialPowers.setBoolPref("notification.prompt.testing", true); + } + + async function teardown_testing_env() { + await SpecialPowers.clearUserPref("notification.prompt.testing"); + await SpecialPowers.clearUserPref("notification.prompt.testing.allow"); + + SimpleTest.finish(); + } + + function executeTests(tests, callback) { + // context is `this` object in test functions + // it can be used to track data between tests + var context = {}; + + (function executeRemainingTests(remainingTests) { + if (!remainingTests.length) { + callback(); + return; + } + + var nextTest = remainingTests.shift(); + var finishTest = executeRemainingTests.bind(null, remainingTests); + var startTest = nextTest.call.bind(nextTest, context, finishTest); + + try { + startTest(); + // if no callback was defined for test function, + // we must manually invoke finish to continue + if (nextTest.length === 0) { + finishTest(); + } + } catch (e) { + ok(false, "Test threw exception!"); + finishTest(); + } + })(tests); + } + + // NotificationTest API + return { + run(tests, callback) { + let ready = setup_testing_env(); + + addLoadEvent(async function () { + await ready; + executeTests(tests, function () { + teardown_testing_env(); + callback && callback(); + }); + }); + }, + + allowNotifications() { + return SpecialPowers.setBoolPref( + "notification.prompt.testing.allow", + true + ); + }, + + denyNotifications() { + return SpecialPowers.setBoolPref( + "notification.prompt.testing.allow", + false + ); + }, + + clickNotification(notification) { + // TODO: how?? + }, + + fireCloseEvent(title) { + window.dispatchEvent( + new CustomEvent("mock-notification-close-event", { + detail: { + title, + }, + }) + ); + }, + + info, + + payload: { + body: "Body", + tag: "fakeTag", + icon: "icon.jpg", + lang: "en-US", + dir: "ltr", + }, + }; +})(); diff --git a/dom/notification/test/mochitest/blank.html b/dom/notification/test/mochitest/blank.html new file mode 100644 index 0000000000..1f9324523a --- /dev/null +++ b/dom/notification/test/mochitest/blank.html @@ -0,0 +1,4 @@ +<!DOCTYPE html> +<html> + <body></body> +</html> diff --git a/dom/notification/test/mochitest/create_notification.html b/dom/notification/test/mochitest/create_notification.html new file mode 100644 index 0000000000..b0387e4ffb --- /dev/null +++ b/dom/notification/test/mochitest/create_notification.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<html> +<head><meta charset=utf-8> + <title>Create a notification</title> +</head> +<body> +<script> + +var notification = new Notification("This is a title", { + body: "This is a notification body", + tag: "sometag", + }); + +</script> +</body> +</html> diff --git a/dom/notification/test/mochitest/mochitest.toml b/dom/notification/test/mochitest/mochitest.toml new file mode 100644 index 0000000000..19320326fc --- /dev/null +++ b/dom/notification/test/mochitest/mochitest.toml @@ -0,0 +1,30 @@ +[DEFAULT] + +support-files = [ + "blank.html", + "create_notification.html", + "MockServices.js", + "NotificationTest.js", +] + +["test_notification_basics.html"] +skip-if = ["xorigin"] # Bug 1792790 + +["test_notification_crossorigin_iframe.html"] +scheme = "https" +# This test needs to be run on HTTP (not HTTPS). + +["test_notification_insecure_context.html"] +skip-if = [ + "http3", + "http2", +] + +["test_notification_permissions.html"] +scheme = "https" + +["test_notification_tag.html"] +skip-if = [ + "http3", + "http2", +] diff --git a/dom/notification/test/mochitest/test_notification_basics.html b/dom/notification/test/mochitest/test_notification_basics.html new file mode 100644 index 0000000000..3dde839a96 --- /dev/null +++ b/dom/notification/test/mochitest/test_notification_basics.html @@ -0,0 +1,125 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Notification Basics</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="MockServices.js"></script> + <script type="text/javascript" src="NotificationTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"></div> +<pre id="test"></pre> +<script type="text/javascript"> + + var info = NotificationTest.info; + var options; + + SimpleTest.requestFlakyTimeout("untriaged"); + + var steps = [ + function() { + info("Test notification spec"); + ok(Notification, "Notification constructor exists"); + ok(Notification.permission, "Notification.permission exists"); + ok(Notification.requestPermission, "Notification.requestPermission exists"); + }, + + function() { + info("Test requestPermission without callback"); + Notification.requestPermission(); + }, + + async function(done) { + info("Test requestPermission deny"); + function assertPermissionDenied(perm) { + is(perm, "denied", "Permission should be denied."); + is(Notification.permission, "denied", "Permission should be denied."); + } + await NotificationTest.denyNotifications(); + Notification.requestPermission() + .then(assertPermissionDenied) + .then(_ => Notification.requestPermission(assertPermissionDenied)) + .catch(err => { + ok(!err, "requestPermission should not reject promise"); + }) + .then(done); + }, + + async function(done) { + info("Test requestPermission grant"); + function assertPermissionGranted(perm) { + is(perm, "granted", "Permission should be granted."); + is(Notification.permission, "granted", "Permission should be granted"); + } + await NotificationTest.allowNotifications(); + Notification.requestPermission() + .then(assertPermissionGranted) + .then(_ => Notification.requestPermission(assertPermissionGranted)) + .catch(err => { + ok(!err, "requestPermission should not reject promise"); + }) + .then(done); + }, + + function(done) { + info("Test invalid requestPermission"); + Notification.requestPermission({}) + .then(_ => { + ok(false, "Non callable arg to requestPermission should reject promise"); + }, err => { + ok(true, "Non callable arg to requestPermission should reject promise"); + }) + .then(done); + }, + + function(done) { + info("Test create notification"); + + options = NotificationTest.payload; + + var notification = new Notification("This is a title", options); + + ok(notification, "Notification exists"); + is(notification.onclick, null, "onclick() should be null"); + is(notification.onshow, null, "onshow() should be null"); + is(notification.onerror, null, "onerror() should be null"); + is(notification.onclose, null, "onclose() should be null"); + is(typeof notification.close, "function", "close() should exist"); + + is(notification.dir, options.dir, "auto should get set"); + is(notification.lang, options.lang, "lang should get set"); + is(notification.body, options.body, "body should get set"); + is(notification.tag, options.tag, "tag should get set"); + is(notification.icon, options.icon, "icon should get set"); + + // store notification in test context + this.notification = notification; + + notification.onshow = function() { + ok(true, "onshow handler should be called"); + done(); + }; + }, + + function(done) { + info("Test closing a notification"); + var notification = this.notification; + + notification.onclose = function() { + ok(true, "onclose handler should be called"); + done(); + }; + + notification.close(); + }, + ]; + + MockServices.register(); + NotificationTest.run(steps, function() { + MockServices.unregister(); + }); +</script> +</body> +</html> diff --git a/dom/notification/test/mochitest/test_notification_crossorigin_iframe.html b/dom/notification/test/mochitest/test_notification_crossorigin_iframe.html new file mode 100644 index 0000000000..0abfce3722 --- /dev/null +++ b/dom/notification/test/mochitest/test_notification_crossorigin_iframe.html @@ -0,0 +1,67 @@ +<!DOCTYPE HTML> +<html> +<!-- +Tests that Notification permissions are denied in cross-origin iframes. +https://bugzilla.mozilla.org/show_bug.cgi?id=1560741 +--> +<head> + <title>Notification permission in cross-origin iframes</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> + <p id="display"></p> + <div id="content" style="display: none"> + </div> + <pre id="test"> + <script class="testbody" type="text/javascript"> + SimpleTest.waitForExplicitFinish(); + + const kBlankURL = "https://example.org/tests/dom/notification/test/mochitest/blank.html"; + + (async function runTest() { + await SpecialPowers.pushPrefEnv({"set": [ + ["notification.prompt.testing", true], + ["notification.prompt.testing.allow", true], + ]}); + + let iframe = document.createElement("iframe"); + iframe.src = kBlankURL; + document.body.appendChild(iframe); + await new Promise(resolve => { + iframe.onload = resolve; + }); + + let checkRequest = async (expectedResponse, msg) => { + let response = await this.content.Notification.requestPermission(); + Assert.equal(response, expectedResponse, msg); + }; + + await SpecialPowers.spawn(iframe, + ["denied", "Denied permission in cross-origin iframe"], + checkRequest); + + let checkPermission = async (expectedPermission, msg) => { + let permission = this.content.Notification.permission; + Assert.equal(permission, expectedPermission, msg); + }; + + await SpecialPowers.spawn(iframe, + ["denied", "Permission is denied in cross-origin iframe"], + checkPermission); + + await SpecialPowers.pushPrefEnv({"set": [["dom.webnotifications.allowcrossoriginiframe", true]]}); + + await SpecialPowers.spawn(iframe, + ["granted", "Granted permission in cross-origin iframe with pref set"], + checkRequest); + await SpecialPowers.spawn(iframe, + ["granted", "Permission is granted in cross-origin iframe with pref set"], + checkPermission); + + SimpleTest.finish(); + })(); + </script> + </pre> +</body> +</html> diff --git a/dom/notification/test/mochitest/test_notification_insecure_context.html b/dom/notification/test/mochitest/test_notification_insecure_context.html new file mode 100644 index 0000000000..bb64db64a9 --- /dev/null +++ b/dom/notification/test/mochitest/test_notification_insecure_context.html @@ -0,0 +1,42 @@ +<!DOCTYPE HTML> +<html> +<!-- +Tests that Notification permissions are denied in insecure context. +https://bugzilla.mozilla.org/show_bug.cgi?id=1429432 +--> +<head> + <title>Notification permission in insecure context</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> + <p id="display"></p> + <div id="content" style="display: none"> + </div> + <pre id="test"> + <script class="testbody" type="text/javascript"> + SimpleTest.waitForExplicitFinish(); + + // Add an allow permission for the mochitest origin to test this. + let script = SpecialPowers.loadChromeScript(function() { + /* eslint-env mozilla/chrome-script */ + let principal = Services.scriptSecurityManager.createContentPrincipalFromOrigin("http://mochi.test:8888"); + Services.perms.addFromPrincipal(principal, "desktop-notification", Services.perms.ALLOW_ACTION); + addMessageListener("destroy", function() { + Services.perms.removeFromPrincipal(principal, "desktop-notification"); + }); + }); + + (async function runTest() { + let response = await Notification.requestPermission(); + is(response, "denied", "Denied permission in insecure context"); + + script.sendAsyncMessage("destroy"); + script.destroy(); + + SimpleTest.finish(); + })(); + </script> + </pre> +</body> +</html> diff --git a/dom/notification/test/mochitest/test_notification_permissions.html b/dom/notification/test/mochitest/test_notification_permissions.html new file mode 100644 index 0000000000..0290fb9c87 --- /dev/null +++ b/dom/notification/test/mochitest/test_notification_permissions.html @@ -0,0 +1,68 @@ +<!DOCTYPE HTML> +<html> +<!-- +Tests that the Notification.requestPermission and navigator.permissions.query +return values are consistent with the stored permission. +https://bugzilla.mozilla.org/show_bug.cgi?id=1589754 +--> +<head> + <title>Notification permissions and permissions API</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"> +</head> +<body> + <p id="display"></p> + <div id="content" style="display: none"> + </div> + <pre id="test"> +<script class="testbody"> + +add_task(async function test_notifications_permission() { + await SpecialPowers.clearUserPref("notification.prompt.testing"); + await SpecialPowers.pushPrefEnv({ + set: [ + // Automatically dismiss the permission request when it appears. + ["dom.webnotifications.requireuserinteraction", true], + ], + }); + + async function testPermissionInWindow(win) { + async function checkPermission(perm, expectedResult, expectedPermission) { + await SpecialPowers.pushPermissions([ + { + type: "desktop-notification", + allow: SpecialPowers.Ci.nsIPermissionManager[perm], + context: document, + }, + ]); + is( + await win.Notification.requestPermission(), + expectedResult, + `expected requestPermission() result for permission ${perm}` + ); + + let result = + await win.navigator.permissions.query({ name: "notifications" }); + is( + result.state, + expectedPermission, + `expected permissions API result for permission ${perm}` + ); + } + + await checkPermission("UNKNOWN_ACTION", "default", "prompt"); + await checkPermission("ALLOW_ACTION", "granted", "granted"); + await checkPermission("DENY_ACTION", "denied", "denied"); + await checkPermission("PROMPT_ACTION", "default", "prompt"); + } + + var win = window.open("blank.html"); + await new Promise(resolve => { win.onload = resolve; }); + await testPermissionInWindow(win); + win.close(); +}); + +</script> + </pre> +</body> +</html> diff --git a/dom/notification/test/mochitest/test_notification_tag.html b/dom/notification/test/mochitest/test_notification_tag.html new file mode 100644 index 0000000000..f4fc72bbe3 --- /dev/null +++ b/dom/notification/test/mochitest/test_notification_tag.html @@ -0,0 +1,169 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=782211 +--> +<head> + <title>Bug 782211</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=782211">Bug 782211</a> +<p id="display"></p> +<div id="content" style="display: none"> +</div> +<pre id="test"> +</pre> +<script type="text/javascript"> + /* eslint-disable mozilla/use-chromeutils-generateqi */ + + // The mock is not a general purpose mock, but is specific for this test. + // It is always registered in the parent process using LoadChromeScript by + // the MockAlertsService below, to allow this to work regardless of whether + // the frames from different origins live in the same process or in different + // processes (with Fission), since the default content-process alerts service + // relays messages to the parent process. + function mockServicesChromeScript() { + /* eslint-env mozilla/chrome-script */ + const MOCK_CID = Components.ID("{dbe37e64-d9a3-402c-8d8a-0826c619f7ad}"); + const ALERTS_SERVICE_CONTRACT_ID = "@mozilla.org/alerts-service;1"; + + var notificationsCreated = []; + + const mockAlertsService = { + showAlert(alert, alertListener) { + notificationsCreated.push(alert.name); + if (notificationsCreated.length == 3) { + // notifications created by the test1 origin + var test1notifications = []; + // notifications created by the test2 origin + var test2notifications = []; + for (var i = 0; i < notificationsCreated.length; i++) { + var notificationName = notificationsCreated[i]; + if (notificationName.includes("test1")) { + test1notifications.push(notificationsCreated[i]); + } else if (notificationName.includes("test2")) { + test2notifications.push(notificationsCreated[i]); + } + } + + is( + test1notifications.length, + 2, + "2 notifications should be created by test1.example.org:80 origin." + ); + is( + test1notifications[0], + test1notifications[1], + "notification names should be identical." + ); + is( + test2notifications.length, + 1, + "1 notification should be created by test2.example.org:80 origin." + ); + + // Register original alerts service. + registrar.unregisterFactory(MOCK_CID, this); + + sendAsyncMessage("mock-alert-service:unregistered"); + } + }, + + showAlertNotification( + imageUrl, + title, + text, + textClickable, + cookie, + alertListener, + name, + dir, + lang, + data + ) { + this.showAlert({ name }); + }, + + QueryInterface(aIID) { + if (aIID.equals(Ci.nsISupports) || aIID.equals(Ci.nsIAlertsService)) { + return this; + } + throw Components.Exception("", Cr.NS_ERROR_NO_INTERFACE); + }, + + createInstance(aIID) { + return this.QueryInterface(aIID); + }, + }; + + const registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar); + + registrar.registerFactory( + MOCK_CID, + "alerts service", + ALERTS_SERVICE_CONTRACT_ID, + mockAlertsService + ); + + const { sendAsyncMessage } = this; + + sendAsyncMessage("mock-alert-service:registered"); + } + + const MockAlertsService = { + async register() { + if (this._chromeScript) { + throw new Error("MockAlertsService already registered"); + } + this._chromeScript = SpecialPowers.loadChromeScript( + mockServicesChromeScript + ); + await this._chromeScript.promiseOneMessage("mock-alert-service:registered"); + }, + async unregistered() { + await this._chromeScript.promiseOneMessage( + "mock-alert-service:unregistered" + ); + }, + }; + + if (window.Notification) { + SimpleTest.waitForExplicitFinish(); + + async function showNotifications() { + await MockAlertsService.register(); + + // Load two frames with the same origin that create notification with the same tag. + // Both pages should generate notifications with the same name, and thus the second + // notification should replace the first. + let sameDomain = window.open("http://test1.example.org:80/tests/dom/notification/test/mochitest/create_notification.html"); + let anotherSameDomain = window.open("http://test1.example.org:80/tests/dom/notification/test/mochitest/create_notification.html"); + // Load a frame with a different origin that creates a notification with the same tag. + // The notification name should be different and thus no notifications should be replaced. + let crossDomain = window.open("http://test2.example.org:80/tests/dom/notification/test/mochitest/create_notification.html"); + + await MockAlertsService.unregistered(); + + sameDomain.close(); + anotherSameDomain.close(); + crossDomain.close(); + SimpleTest.finish(); + } + + SpecialPowers.pushPrefEnv( + { + set: [ + ["notification.prompt.testing", true], + ["notification.prompt.testing.allow", true], + ], + }, + showNotifications + ); + } else { + ok(true, "Notifications are not enabled on the platform."); + } +</script> +</body> +</html> diff --git a/dom/notification/test/unit/head_notificationdb.js b/dom/notification/test/unit/head_notificationdb.js new file mode 100644 index 0000000000..1b23d88729 --- /dev/null +++ b/dom/notification/test/unit/head_notificationdb.js @@ -0,0 +1,61 @@ +"use strict"; + +var { XPCOMUtils } = ChromeUtils.importESModule( + "resource://gre/modules/XPCOMUtils.sys.mjs" +); + +function getNotificationObject(app, id, tag, includeScope) { + const origin = `https://${app}.gaiamobile.org/`; + return { + origin, + id, + title: app + "Notification:" + Date.now(), + dir: "auto", + lang: "", + body: app + " notification body", + tag: tag || "", + icon: "icon.png", + serviceWorkerRegistrationScope: includeScope ? origin : undefined, + }; +} + +var systemNotification = getNotificationObject( + "system", + "{2bc883bf-2809-4432-b0f4-f54e10372764}" +); + +var calendarNotification = getNotificationObject( + "calendar", + "{d8d11299-a58e-429b-9a9a-57c562982fbf}" +); + +// Helper to start the NotificationDB +function startNotificationDB() { + ChromeUtils.importESModule("resource://gre/modules/NotificationDB.sys.mjs"); +} + +// Helper function to add a listener, send message and treat the reply +function addAndSend(msg, reply, callback, payload, runNext = true) { + let handler = { + receiveMessage(message) { + if (message.name === reply) { + Services.cpmm.removeMessageListener(reply, handler); + callback(message); + if (runNext) { + run_next_test(); + } + } + }, + }; + Services.cpmm.addMessageListener(reply, handler); + Services.cpmm.sendAsyncMessage(msg, payload); +} + +// helper fonction, comparing two notifications +function compareNotification(notif1, notif2) { + // retrieved notification should be the second one sent + for (let prop in notif1) { + // compare each property + Assert.equal(notif1[prop], notif2[prop]); + } +} diff --git a/dom/notification/test/unit/test_notificationdb.js b/dom/notification/test/unit/test_notificationdb.js new file mode 100644 index 0000000000..33397f87f3 --- /dev/null +++ b/dom/notification/test/unit/test_notificationdb.js @@ -0,0 +1,340 @@ +"use strict"; + +function run_test() { + do_get_profile(); + startNotificationDB(); + run_next_test(); +} + +// Get one notification, none exists +add_test(function test_get_none() { + let requestID = 0; + let msgReply = "Notification:GetAll:Return:OK"; + let msgHandler = function (message) { + Assert.equal(requestID, message.data.requestID); + Assert.equal(0, message.data.notifications.length); + }; + + addAndSend("Notification:GetAll", msgReply, msgHandler, { + origin: systemNotification.origin, + requestID, + }); +}); + +// Store one notification +add_test(function test_send_one() { + let requestID = 1; + let msgReply = "Notification:Save:Return:OK"; + let msgHandler = function (message) { + Assert.equal(requestID, message.data.requestID); + }; + + addAndSend("Notification:Save", msgReply, msgHandler, { + origin: systemNotification.origin, + notification: systemNotification, + requestID, + }); +}); + +// Get one notification, one exists +add_test(function test_get_one() { + let requestID = 2; + let msgReply = "Notification:GetAll:Return:OK"; + let msgHandler = function (message) { + Assert.equal(requestID, message.data.requestID); + Assert.equal(1, message.data.notifications.length); + // compare the content + compareNotification(systemNotification, message.data.notifications[0]); + }; + + addAndSend("Notification:GetAll", msgReply, msgHandler, { + origin: systemNotification.origin, + requestID, + }); +}); + +// Delete one notification +add_test(function test_delete_one() { + let requestID = 3; + let msgReply = "Notification:Delete:Return:OK"; + let msgHandler = function (message) { + Assert.equal(requestID, message.data.requestID); + }; + + addAndSend("Notification:Delete", msgReply, msgHandler, { + origin: systemNotification.origin, + id: systemNotification.id, + requestID, + }); +}); + +// Get one notification, none exists +add_test(function test_get_none_again() { + let requestID = 4; + let msgReply = "Notification:GetAll:Return:OK"; + let msgHandler = function (message) { + Assert.equal(requestID, message.data.requestID); + Assert.equal(0, message.data.notifications.length); + }; + + addAndSend("Notification:GetAll", msgReply, msgHandler, { + origin: systemNotification.origin, + requestID, + }); +}); + +// Delete one notification that do not exists anymore +add_test(function test_delete_one_nonexistent() { + let requestID = 5; + let msgReply = "Notification:Delete:Return:OK"; + let msgHandler = function (message) { + Assert.equal(requestID, message.data.requestID); + }; + + addAndSend("Notification:Delete", msgReply, msgHandler, { + origin: systemNotification.origin, + id: systemNotification.id, + requestID, + }); +}); + +// Store two notifications with the same id +add_test(function test_send_two_get_one() { + let requestID = 6; + let calls = 0; + + let msgGetReply = "Notification:GetAll:Return:OK"; + let msgGetHandler = function (message) { + Assert.equal(requestID + 2, message.data.requestID); + Assert.equal(1, message.data.notifications.length); + // compare the content + compareNotification(systemNotification, message.data.notifications[0]); + }; + + let msgSaveReply = "Notification:Save:Return:OK"; + let msgSaveHandler = function (message) { + calls += 1; + if (calls === 2) { + addAndSend("Notification:GetAll", msgGetReply, msgGetHandler, { + origin: systemNotification.origin, + requestID: requestID + 2, + }); + } + }; + + addAndSend( + "Notification:Save", + msgSaveReply, + msgSaveHandler, + { + origin: systemNotification.origin, + notification: systemNotification, + requestID, + }, + false + ); + + addAndSend( + "Notification:Save", + msgSaveReply, + msgSaveHandler, + { + origin: systemNotification.origin, + notification: systemNotification, + requestID: requestID + 1, + }, + false + ); +}); + +// Delete previous notification +add_test(function test_delete_previous() { + let requestID = 8; + let msgReply = "Notification:Delete:Return:OK"; + let msgHandler = function (message) { + Assert.equal(requestID, message.data.requestID); + }; + + addAndSend("Notification:Delete", msgReply, msgHandler, { + origin: systemNotification.origin, + id: systemNotification.id, + requestID, + }); +}); + +// Store two notifications from same origin with the same tag +add_test(function test_send_two_get_one() { + let requestID = 10; + let tag = "voicemail"; + + let systemNotification1 = getNotificationObject( + "system", + "{f271f9ee-3955-4c10-b1f2-af552fb270ee}", + tag + ); + let systemNotification2 = getNotificationObject( + "system", + "{8ef9a628-f0f4-44b4-820d-c117573c33e3}", + tag + ); + + let msgGetReply = "Notification:GetAll:Return:OK"; + let msgGetNotifHandler = { + receiveMessage(message) { + if (message.name === msgGetReply) { + Services.cpmm.removeMessageListener(msgGetReply, msgGetNotifHandler); + let notifications = message.data.notifications; + // same tag, so replaced + Assert.equal(1, notifications.length); + // compare the content + compareNotification(systemNotification2, notifications[0]); + run_next_test(); + } + }, + }; + + Services.cpmm.addMessageListener(msgGetReply, msgGetNotifHandler); + + let msgSaveReply = "Notification:Save:Return:OK"; + let msgSaveCalls = 0; + let msgSaveHandler = function (message) { + msgSaveCalls++; + // Once both request have been sent, trigger getall + if (msgSaveCalls === 2) { + Services.cpmm.sendAsyncMessage("Notification:GetAll", { + origin: systemNotification1.origin, + requestID: message.data.requestID + 2, // 12, 13 + }); + } + }; + + addAndSend( + "Notification:Save", + msgSaveReply, + msgSaveHandler, + { + origin: systemNotification1.origin, + notification: systemNotification1, + requestID, // 10 + }, + false + ); + + addAndSend( + "Notification:Save", + msgSaveReply, + msgSaveHandler, + { + origin: systemNotification2.origin, + notification: systemNotification2, + requestID: requestID + 1, // 11 + }, + false + ); +}); + +// Delete previous notification +add_test(function test_delete_previous() { + let requestID = 15; + let msgReply = "Notification:Delete:Return:OK"; + let msgHandler = function (message) { + Assert.equal(requestID, message.data.requestID); + }; + + addAndSend("Notification:Delete", msgReply, msgHandler, { + origin: systemNotification.origin, + id: "{8ef9a628-f0f4-44b4-820d-c117573c33e3}", + requestID, + }); +}); + +// Store two notifications from two origins with the same tag +add_test(function test_send_two_get_two() { + let requestID = 20; + let tag = "voicemail"; + + let systemNotification1 = systemNotification; + systemNotification1.tag = tag; + + let calendarNotification2 = calendarNotification; + calendarNotification2.tag = tag; + + let msgGetReply = "Notification:GetAll:Return:OK"; + let msgGetCalls = 0; + let msgGetHandler = { + receiveMessage(message) { + if (message.name === msgGetReply) { + msgGetCalls++; + let notifications = message.data.notifications; + + // one notification per origin + Assert.equal(1, notifications.length); + + // first call should be system notification + if (msgGetCalls === 1) { + compareNotification(systemNotification1, notifications[0]); + } + + // second and last call should be calendar notification + if (msgGetCalls === 2) { + Services.cpmm.removeMessageListener(msgGetReply, msgGetHandler); + compareNotification(calendarNotification2, notifications[0]); + run_next_test(); + } + } + }, + }; + Services.cpmm.addMessageListener(msgGetReply, msgGetHandler); + + let msgSaveReply = "Notification:Save:Return:OK"; + let msgSaveCalls = 0; + let msgSaveHandler = { + receiveMessage(message) { + if (message.name === msgSaveReply) { + msgSaveCalls++; + if (msgSaveCalls === 2) { + Services.cpmm.removeMessageListener(msgSaveReply, msgSaveHandler); + + // Trigger getall for each origin + Services.cpmm.sendAsyncMessage("Notification:GetAll", { + origin: systemNotification1.origin, + requestID: message.data.requestID + 1, // 22 + }); + + Services.cpmm.sendAsyncMessage("Notification:GetAll", { + origin: calendarNotification2.origin, + requestID: message.data.requestID + 2, // 23 + }); + } + } + }, + }; + Services.cpmm.addMessageListener(msgSaveReply, msgSaveHandler); + + Services.cpmm.sendAsyncMessage("Notification:Save", { + origin: systemNotification1.origin, + notification: systemNotification1, + requestID, // 20 + }); + + Services.cpmm.sendAsyncMessage("Notification:Save", { + origin: calendarNotification2.origin, + notification: calendarNotification2, + requestID: requestID + 1, // 21 + }); +}); + +// Cleanup previous notification +add_test(function test_delete_previous() { + let requestID = 25; + let msgReply = "Notification:Delete:Return:OK"; + let msgHandler = function (message) { + Assert.equal(requestID, message.data.requestID); + }; + + addAndSend("Notification:Delete", msgReply, msgHandler, { + origin: systemNotification.origin, + id: "{2bc883bf-2809-4432-b0f4-f54e10372764}", + requestID, + }); +}); diff --git a/dom/notification/test/unit/test_notificationdb_bug1024090.js b/dom/notification/test/unit/test_notificationdb_bug1024090.js new file mode 100644 index 0000000000..37dee0a639 --- /dev/null +++ b/dom/notification/test/unit/test_notificationdb_bug1024090.js @@ -0,0 +1,59 @@ +"use strict"; + +function run_test() { + do_get_profile(); + run_next_test(); +} + +// For bug 1024090: test edge case of notificationstore.json +add_test(function test_bug1024090_purge() { + const NOTIFICATION_STORE_PATH = PathUtils.join( + PathUtils.profileDir, + "notificationstore" + ); + let cleanup = IOUtils.remove(NOTIFICATION_STORE_PATH, { recursive: true }); + cleanup + .then( + function onSuccess() { + ok(true, "Notification database cleaned."); + }, + function onError(reason) { + ok(false, "Notification database error when cleaning: " + reason); + } + ) + .then(function next() { + info("Cleanup steps completed: " + NOTIFICATION_STORE_PATH); + startNotificationDB(); + run_next_test(); + }); +}); + +// Store one notification +add_test(function test_bug1024090_send_one() { + let requestID = 1; + let msgReply = "Notification:Save:Return:OK"; + let msgHandler = function (message) { + equal(requestID, message.data.requestID, "Checking requestID"); + }; + + addAndSend("Notification:Save", msgReply, msgHandler, { + origin: systemNotification.origin, + notification: systemNotification, + requestID, + }); +}); + +// Get one notification, one exists +add_test(function test_bug1024090_get_one() { + let requestID = 2; + let msgReply = "Notification:GetAll:Return:OK"; + let msgHandler = function (message) { + equal(requestID, message.data.requestID, "Checking requestID"); + equal(1, message.data.notifications.length, "One notification stored"); + }; + + addAndSend("Notification:GetAll", msgReply, msgHandler, { + origin: systemNotification.origin, + requestID, + }); +}); diff --git a/dom/notification/test/unit/test_notificationdb_migration.js b/dom/notification/test/unit/test_notificationdb_migration.js new file mode 100644 index 0000000000..2b38e8d43a --- /dev/null +++ b/dom/notification/test/unit/test_notificationdb_migration.js @@ -0,0 +1,129 @@ +"use strict"; + +const { AppConstants } = ChromeUtils.importESModule( + "resource://gre/modules/AppConstants.sys.mjs" +); + +const fooNotification = getNotificationObject( + "foo", + "a4f1d54a-98b7-4231-9120-5afc26545bad", + null, + true +); +const barNotification = getNotificationObject( + "bar", + "a4f1d54a-98b7-4231-9120-5afc26545bad", + "baz", + true +); +const msg = "Notification:GetAll"; +const msgReply = "Notification:GetAll:Return:OK"; + +do_get_profile(); +const OLD_STORE_PATH = PathUtils.join( + PathUtils.profileDir, + "notificationstore.json" +); + +let nextRequestID = 0; + +// Create the old datastore and populate it with data before we initialize +// the notification database so it has data to migrate. This is a setup step, +// not a test, but it seems like we need to do it in a test function +// rather than in run_test() because the test runner doesn't handle async steps +// in run_test(). +add_task( + { + skip_if: () => !AppConstants.MOZ_NEW_NOTIFICATION_STORE, + }, + async function test_create_old_datastore() { + const notifications = { + [fooNotification.origin]: { + [fooNotification.id]: fooNotification, + }, + [barNotification.origin]: { + [barNotification.id]: barNotification, + }, + }; + + await IOUtils.writeJSON(OLD_STORE_PATH, notifications); + + startNotificationDB(); + } +); + +add_test( + { + skip_if: () => !AppConstants.MOZ_NEW_NOTIFICATION_STORE, + }, + function test_get_system_notification() { + const requestID = nextRequestID++; + const msgHandler = function (message) { + Assert.equal(requestID, message.data.requestID); + Assert.equal(0, message.data.notifications.length); + }; + + addAndSend(msg, msgReply, msgHandler, { + origin: systemNotification.origin, + requestID, + }); + } +); + +add_test( + { + skip_if: () => !AppConstants.MOZ_NEW_NOTIFICATION_STORE, + }, + function test_get_foo_notification() { + const requestID = nextRequestID++; + const msgHandler = function (message) { + Assert.equal(requestID, message.data.requestID); + Assert.equal(1, message.data.notifications.length); + Assert.deepEqual( + fooNotification, + message.data.notifications[0], + "Notification data migrated" + ); + }; + + addAndSend(msg, msgReply, msgHandler, { + origin: fooNotification.origin, + requestID, + }); + } +); + +add_test( + { + skip_if: () => !AppConstants.MOZ_NEW_NOTIFICATION_STORE, + }, + function test_get_bar_notification() { + const requestID = nextRequestID++; + const msgHandler = function (message) { + Assert.equal(requestID, message.data.requestID); + Assert.equal(1, message.data.notifications.length); + Assert.deepEqual( + barNotification, + message.data.notifications[0], + "Notification data migrated" + ); + }; + + addAndSend(msg, msgReply, msgHandler, { + origin: barNotification.origin, + requestID, + }); + } +); + +add_task( + { + skip_if: () => !AppConstants.MOZ_NEW_NOTIFICATION_STORE, + }, + async function test_old_datastore_deleted() { + Assert.ok( + !(await IOUtils.exists(OLD_STORE_PATH)), + "old datastore no longer exists" + ); + } +); diff --git a/dom/notification/test/unit/xpcshell.toml b/dom/notification/test/unit/xpcshell.toml new file mode 100644 index 0000000000..178af95b1f --- /dev/null +++ b/dom/notification/test/unit/xpcshell.toml @@ -0,0 +1,9 @@ +[DEFAULT] +head = "head_notificationdb.js" +skip-if = ["os == 'android'"] + +["test_notificationdb.js"] + +["test_notificationdb_bug1024090.js"] + +["test_notificationdb_migration.js"] |