| <!doctype html> |
| <html> |
| <head> |
| <title>Notifications: Property reflection in the "notificationclick" event and SWR.getNotifications().</title> |
| <script src="../resources/testharness.js"></script> |
| <script src="../resources/testharnessreport.js"></script> |
| <script src="../serviceworker/resources/test-helpers.js"></script> |
| <script src="resources/test-helpers.js"></script> |
| </head> |
| <body> |
| <script> |
| // Tests that the notification object in a) the "notificationclick" event in the |
| // Service Worker, and b) ServiceWorkerRegistration.getNotifications(), both |
| // accurately reflect the attributes with which the notification was created. |
| |
| // Serializes a trigger object to send via postMessage. |
| // If the given object is not a trigger, return the same object. |
| function serializeTrigger(trigger) { |
| if (trigger instanceof TimestampTrigger) |
| return { timestamp: trigger.timestamp }; |
| return trigger; |
| } |
| |
| // Serialize options to send via sendMessage and for deep comparison. |
| function serializeOptions(options) { |
| return { |
| ...options, |
| showTrigger: serializeTrigger(options.showTrigger), |
| }; |
| } |
| |
| // Checks that all the properties in expected also exist and are equal in actual. |
| function assert_object_is_superset(actual, expected, description) { |
| Object.keys(expected).forEach(function(key) { |
| // Serialize fields that are triggers so we can compare them. |
| var fieldActual = serializeTrigger(actual[key]); |
| var fieldExpected = serializeTrigger(expected[key]); |
| var fieldDescription = description + ' [field ' + key + ']'; |
| if (typeof fieldExpected == 'object') |
| assert_object_equals(fieldActual, fieldExpected, fieldDescription); |
| else |
| assert_equals(fieldActual, fieldExpected, fieldDescription); |
| }); |
| } |
| |
| async_test(function(test) { |
| var scope = 'resources/scope/' + location.pathname; |
| var script = 'instrumentation-service-worker.js'; |
| var port; |
| var registration; |
| |
| var options = { |
| title: scope, |
| dir: 'rtl', |
| lang: 'nl-NL', |
| body: 'Hello, world!', |
| tag: 'tag', |
| // FIXME: Relative URLs for the icon attribute currently get reflected as |
| // an absolute URL, which should probably be the given relative URL. |
| image: 'https://example/image.jpg', |
| icon: 'https://example/icon.png', |
| badge: 'https://example/badge.png', |
| vibrate: [100, 200, 300], |
| timestamp: 621046800000, |
| renotify: true, |
| silent: false, |
| requireInteraction: true, |
| data: [ |
| { property: 'foobar', |
| string: '\uDFFF\u0000\uDBFF', |
| scalar: true }, |
| 12.15 |
| ], |
| actions: [], |
| // Make sure the notification is immediately triggered by setting the timestamp in the past. |
| showTrigger: new TimestampTrigger(Date.now().valueOf() - 1000), |
| }; |
| // Deliberately add more actions than are supported. |
| for (var i = 0; i < 2 * Notification.maxActions; i++) { |
| options.actions.push({ |
| type: i % 2 == 0 ? 'button' : 'text', |
| action: 'a' + i, |
| title: 'Action ' + i, |
| icon: 'https://example/action_icon_' + i + '.png', |
| placeholder: i % 2 == 0 ? null : 'Type a reply...' |
| }); |
| } |
| |
| if (window.testRunner) { |
| testRunner.setPermission('notifications', 'granted', location.origin, location.origin); |
| } |
| getActiveServiceWorkerWithMessagePort(test, script, scope).then(function(info) { |
| port = info.port; |
| registration = info.registration; |
| // (1) Tell the Service Worker to display a Web Notification. |
| var showPromise = sendCommand(port, { |
| command: 'show', |
| |
| title: scope, |
| options: serializeOptions(options) |
| }); |
| |
| // Now limit actions to the number that we expect to be reflected on notifications. |
| options.actions = options.actions.slice(0, Notification.maxActions); |
| return showPromise; |
| }).then(function(data) { |
| // (2) Confirm that the service worker displayed the notification successfully. |
| assert_true(data.success, 'The notification must have been displayed.'); |
| return simulateNotificationClick(scope, -1 /* action_index */, port); |
| }).then(function(data) { |
| // (3) Confirm that all properties set on the cloned Notification object are as expected. |
| assert_object_is_superset(data.notification, options, 'The Notification object properties must be the same in notificationclick events.'); |
| return registration.getNotifications({includeTriggered: true}); |
| }).then(function(notifications) { |
| // (4) Check that the properties are also set correctly on the non-cloned Notification |
| // object from getNotifications. |
| assert_equals(notifications.length, 1); |
| assert_object_is_superset(notifications[0], options, 'The Notification object properties must be the same in getNotifications.'); |
| |
| notifications[0].actions.foo = 'bar'; |
| assert_throws_js(TypeError, () => notifications[0].actions.push({ title: 'Foo' })); |
| |
| if (notifications[0].actions.length) { |
| notifications[0].actions[0].title = 'Changed'; |
| notifications[0].actions[0].foo = 'bar'; |
| } |
| |
| assert_object_equals(notifications[0].actions, options.actions, 'The actions field should be immutable.'); |
| |
| assert_equals(notifications[0].actions, notifications[0].actions, '`actions` attribute equality'); |
| assert_equals(notifications[0].data, notifications[0].data, '`data` attribute equality'); |
| assert_equals(notifications[0].vibrate, notifications[0].vibrate, '`vibrate` attribute equality'); |
| assert_equals(notifications[0].showTrigger, notifications[0].showTrigger, '`showTrigger` attribute equality'); |
| test.done(); |
| }).catch(unreached_rejection(test)); |
| |
| }, 'Clicking on a notification displayed by a Service Worker the notificationclick event.'); |
| </script> |
| </body> |
| </html> |