blob: e3e515bf40036198349fa842632c3e7689c2b9ac [file] [log] [blame]
<!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>