From 602acb217bfa39b20e4fd48a30e70f55123dcc1b Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Thu, 17 Feb 2022 14:22:51 +0100 Subject: [PATCH] test: run webmessaging/broadcastchannel WPT Refs: https://github.com/nodejs/node/issues/38803 PR-URL: https://github.com/nodejs/node/pull/41962 Reviewed-By: Colin Ihrig Reviewed-By: Benjamin Gruenbaum Reviewed-By: Mestery --- .../broadcastchannel/basics.any.js | 120 ++++++ .../webmessaging/broadcastchannel/blobs.html | 83 ++++ .../broadcastchannel/cross-origin.html | 38 ++ .../cross-partition.https.tentative.html | 72 ++++ .../broadcastchannel/detached-iframe.html | 174 ++++++++ .../broadcastchannel/interface.any.js | 65 +++ .../broadcastchannel/opaque-origin.html | 35 ++ .../broadcastchannel/ordering.html | 116 ++++++ .../broadcastchannel/origin.window.js | 10 + .../resources/cross-origin.html | 15 + .../broadcastchannel/resources/ordering.html | 78 ++++ .../broadcastchannel/resources/origin.html | 8 + .../broadcastchannel/resources/sandboxed.html | 10 + .../resources/service-worker.js | 15 + .../broadcastchannel/resources/worker.js | 37 ++ .../broadcastchannel/sandbox.html | 16 + .../service-worker.https.html | 47 +++ .../broadcastchannel/workers.html | 375 ++++++++++++++++++ .../status/webmessaging/broadcastchannel.json | 26 ++ test/wpt/test-broadcastchannel.js | 13 + 20 files changed, 1353 insertions(+) create mode 100644 test/fixtures/wpt/webmessaging/broadcastchannel/basics.any.js create mode 100644 test/fixtures/wpt/webmessaging/broadcastchannel/blobs.html create mode 100644 test/fixtures/wpt/webmessaging/broadcastchannel/cross-origin.html create mode 100644 test/fixtures/wpt/webmessaging/broadcastchannel/cross-partition.https.tentative.html create mode 100644 test/fixtures/wpt/webmessaging/broadcastchannel/detached-iframe.html create mode 100644 test/fixtures/wpt/webmessaging/broadcastchannel/interface.any.js create mode 100644 test/fixtures/wpt/webmessaging/broadcastchannel/opaque-origin.html create mode 100644 test/fixtures/wpt/webmessaging/broadcastchannel/ordering.html create mode 100644 test/fixtures/wpt/webmessaging/broadcastchannel/origin.window.js create mode 100644 test/fixtures/wpt/webmessaging/broadcastchannel/resources/cross-origin.html create mode 100644 test/fixtures/wpt/webmessaging/broadcastchannel/resources/ordering.html create mode 100644 test/fixtures/wpt/webmessaging/broadcastchannel/resources/origin.html create mode 100644 test/fixtures/wpt/webmessaging/broadcastchannel/resources/sandboxed.html create mode 100644 test/fixtures/wpt/webmessaging/broadcastchannel/resources/service-worker.js create mode 100644 test/fixtures/wpt/webmessaging/broadcastchannel/resources/worker.js create mode 100644 test/fixtures/wpt/webmessaging/broadcastchannel/sandbox.html create mode 100644 test/fixtures/wpt/webmessaging/broadcastchannel/service-worker.https.html create mode 100644 test/fixtures/wpt/webmessaging/broadcastchannel/workers.html create mode 100644 test/wpt/status/webmessaging/broadcastchannel.json create mode 100644 test/wpt/test-broadcastchannel.js diff --git a/test/fixtures/wpt/webmessaging/broadcastchannel/basics.any.js b/test/fixtures/wpt/webmessaging/broadcastchannel/basics.any.js new file mode 100644 index 00000000000000..68b4706028f5aa --- /dev/null +++ b/test/fixtures/wpt/webmessaging/broadcastchannel/basics.any.js @@ -0,0 +1,120 @@ +async_test(t => { + let c1 = new BroadcastChannel('eventType'); + let c2 = new BroadcastChannel('eventType'); + + c2.onmessage = t.step_func(e => { + assert_true(e instanceof MessageEvent); + assert_equals(e.target, c2); + assert_equals(e.type, 'message'); + assert_equals(e.origin, location.origin, 'origin'); + assert_equals(e.data, 'hello world'); + assert_equals(e.source, null, 'source'); + t.done(); + }); + c1.postMessage('hello world'); + }, 'postMessage results in correct event'); + +async_test(t => { + let c1 = new BroadcastChannel('order'); + let c2 = new BroadcastChannel('order'); + let c3 = new BroadcastChannel('order'); + + let events = []; + let doneCount = 0; + let handler = t.step_func(e => { + events.push(e); + if (e.data == 'done') { + doneCount++; + if (doneCount == 2) { + assert_equals(events.length, 6); + assert_equals(events[0].target, c2, 'target for event 0'); + assert_equals(events[0].data, 'from c1'); + assert_equals(events[1].target, c3, 'target for event 1'); + assert_equals(events[1].data, 'from c1'); + assert_equals(events[2].target, c1, 'target for event 2'); + assert_equals(events[2].data, 'from c3'); + assert_equals(events[3].target, c2, 'target for event 3'); + assert_equals(events[3].data, 'from c3'); + assert_equals(events[4].target, c1, 'target for event 4'); + assert_equals(events[4].data, 'done'); + assert_equals(events[5].target, c3, 'target for event 5'); + assert_equals(events[5].data, 'done'); + t.done(); + } + } + }); + c1.onmessage = handler; + c2.onmessage = handler; + c3.onmessage = handler; + + c1.postMessage('from c1'); + c3.postMessage('from c3'); + c2.postMessage('done'); + }, 'messages are delivered in port creation order'); + +async_test(t => { + let c1 = new BroadcastChannel('closed'); + let c2 = new BroadcastChannel('closed'); + let c3 = new BroadcastChannel('closed'); + + c2.onmessage = t.unreached_func(); + c2.close(); + c3.onmessage = t.step_func(() => t.done()); + c1.postMessage('test'); + }, 'messages aren\'t delivered to a closed port'); + + async_test(t => { + let c1 = new BroadcastChannel('closed'); + let c2 = new BroadcastChannel('closed'); + let c3 = new BroadcastChannel('closed'); + + c2.onmessage = t.unreached_func(); + c3.onmessage = t.step_func(() => t.done()); + c1.postMessage('test'); + c2.close(); +}, 'messages aren\'t delivered to a port closed after calling postMessage.'); + +async_test(t => { + let c1 = new BroadcastChannel('create-in-onmessage'); + let c2 = new BroadcastChannel('create-in-onmessage'); + + c2.onmessage = t.step_func(e => { + assert_equals(e.data, 'first'); + c2.close(); + let c3 = new BroadcastChannel('create-in-onmessage'); + c3.onmessage = t.step_func(event => { + assert_equals(event.data, 'done'); + t.done(); + }); + c1.postMessage('done'); + }); + c1.postMessage('first'); + c2.postMessage('second'); + }, 'closing and creating channels during message delivery works correctly'); + +async_test(t => { + let c1 = new BroadcastChannel('close-in-onmessage'); + let c2 = new BroadcastChannel('close-in-onmessage'); + let c3 = new BroadcastChannel('close-in-onmessage'); + let events = []; + c1.onmessage = e => events.push('c1: ' + e.data); + c2.onmessage = e => events.push('c2: ' + e.data); + c3.onmessage = e => events.push('c3: ' + e.data); + + // c2 closes itself when it receives the first message + c2.addEventListener('message', e => { + c2.close(); + }); + + c3.addEventListener('message', t.step_func(e => { + if (e.data == 'done') { + assert_array_equals(events, [ + 'c2: first', + 'c3: first', + 'c3: done']); + t.done(); + } + })); + c1.postMessage('first'); + c1.postMessage('done'); + }, 'Closing a channel in onmessage prevents already queued tasks from firing onmessage events'); diff --git a/test/fixtures/wpt/webmessaging/broadcastchannel/blobs.html b/test/fixtures/wpt/webmessaging/broadcastchannel/blobs.html new file mode 100644 index 00000000000000..1f0e2f1f1d6050 --- /dev/null +++ b/test/fixtures/wpt/webmessaging/broadcastchannel/blobs.html @@ -0,0 +1,83 @@ + + + + + diff --git a/test/fixtures/wpt/webmessaging/broadcastchannel/cross-origin.html b/test/fixtures/wpt/webmessaging/broadcastchannel/cross-origin.html new file mode 100644 index 00000000000000..ee4b2f21c8e3fe --- /dev/null +++ b/test/fixtures/wpt/webmessaging/broadcastchannel/cross-origin.html @@ -0,0 +1,38 @@ + + + + + + + + + + diff --git a/test/fixtures/wpt/webmessaging/broadcastchannel/cross-partition.https.tentative.html b/test/fixtures/wpt/webmessaging/broadcastchannel/cross-partition.https.tentative.html new file mode 100644 index 00000000000000..163e6c00a93a95 --- /dev/null +++ b/test/fixtures/wpt/webmessaging/broadcastchannel/cross-partition.https.tentative.html @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + diff --git a/test/fixtures/wpt/webmessaging/broadcastchannel/detached-iframe.html b/test/fixtures/wpt/webmessaging/broadcastchannel/detached-iframe.html new file mode 100644 index 00000000000000..b9b06c3a46463b --- /dev/null +++ b/test/fixtures/wpt/webmessaging/broadcastchannel/detached-iframe.html @@ -0,0 +1,174 @@ + + + + + + + + + + diff --git a/test/fixtures/wpt/webmessaging/broadcastchannel/interface.any.js b/test/fixtures/wpt/webmessaging/broadcastchannel/interface.any.js new file mode 100644 index 00000000000000..35e09d34b418d3 --- /dev/null +++ b/test/fixtures/wpt/webmessaging/broadcastchannel/interface.any.js @@ -0,0 +1,65 @@ +test(() => assert_throws_js(TypeError, () => new BroadcastChannel()), + 'Should throw if no name is provided'); + +test(() => { + let c = new BroadcastChannel(null); + assert_equals(c.name, 'null'); + }, 'Null name should not throw'); + +test(() => { + let c = new BroadcastChannel(undefined); + assert_equals(c.name, 'undefined'); + }, 'Undefined name should not throw'); + +test(() => { + let c = new BroadcastChannel('fooBar'); + assert_equals(c.name, 'fooBar'); + }, 'Non-empty name should not throw'); + +test(() => { + let c = new BroadcastChannel(123); + assert_equals(c.name, '123'); + }, 'Non-string name should not throw'); + +test(() => { + let c = new BroadcastChannel(''); + assert_throws_js(TypeError, () => c.postMessage()); + }, 'postMessage without parameters should throw'); + +test(() => { + let c = new BroadcastChannel(''); + c.postMessage(null); + }, 'postMessage with null should not throw'); + +test(() => { + let c = new BroadcastChannel(''); + c.close(); + }, 'close should not throw'); + +test(() => { + let c = new BroadcastChannel(''); + c.close(); + c.close(); + }, 'close should not throw when called multiple times'); + +test(() => { + let c = new BroadcastChannel(''); + c.close(); + assert_throws_dom('InvalidStateError', () => c.postMessage('')); + }, 'postMessage after close should throw'); + +test(() => { + let c = new BroadcastChannel(''); + assert_not_equals(c.onmessage, undefined); + }, 'BroadcastChannel should have an onmessage event'); + +test(() => { + let c = new BroadcastChannel(''); + assert_throws_dom('DataCloneError', () => c.postMessage(Symbol())); + }, 'postMessage should throw with uncloneable data'); + +test(() => { + let c = new BroadcastChannel(''); + c.close(); + assert_throws_dom('InvalidStateError', () => c.postMessage(Symbol())); + }, 'postMessage should throw InvalidStateError after close, even with uncloneable data'); diff --git a/test/fixtures/wpt/webmessaging/broadcastchannel/opaque-origin.html b/test/fixtures/wpt/webmessaging/broadcastchannel/opaque-origin.html new file mode 100644 index 00000000000000..c10e0cb22256a0 --- /dev/null +++ b/test/fixtures/wpt/webmessaging/broadcastchannel/opaque-origin.html @@ -0,0 +1,35 @@ + + + + + + + + + + + + diff --git a/test/fixtures/wpt/webmessaging/broadcastchannel/ordering.html b/test/fixtures/wpt/webmessaging/broadcastchannel/ordering.html new file mode 100644 index 00000000000000..2d521b9e0ccf94 --- /dev/null +++ b/test/fixtures/wpt/webmessaging/broadcastchannel/ordering.html @@ -0,0 +1,116 @@ + + + + + + + + + diff --git a/test/fixtures/wpt/webmessaging/broadcastchannel/origin.window.js b/test/fixtures/wpt/webmessaging/broadcastchannel/origin.window.js new file mode 100644 index 00000000000000..7e9d602af194b7 --- /dev/null +++ b/test/fixtures/wpt/webmessaging/broadcastchannel/origin.window.js @@ -0,0 +1,10 @@ +async_test(t => { + const crossOriginURL = new URL("resources/origin.html", self.location.href).href.replace("://", "://天気の良い日."), + frame = document.createElement("iframe"); + frame.src = crossOriginURL; + document.body.appendChild(frame); + t.add_cleanup(() => frame.remove()); + self.onmessage = t.step_func_done(e => { + assert_equals(e.data, self.origin.replace("://", "://xn--n8j6ds53lwwkrqhv28a.")); + }); +}, "Serialization of BroadcastChannel origin"); diff --git a/test/fixtures/wpt/webmessaging/broadcastchannel/resources/cross-origin.html b/test/fixtures/wpt/webmessaging/broadcastchannel/resources/cross-origin.html new file mode 100644 index 00000000000000..5078b6fc8e46f5 --- /dev/null +++ b/test/fixtures/wpt/webmessaging/broadcastchannel/resources/cross-origin.html @@ -0,0 +1,15 @@ + + diff --git a/test/fixtures/wpt/webmessaging/broadcastchannel/resources/ordering.html b/test/fixtures/wpt/webmessaging/broadcastchannel/resources/ordering.html new file mode 100644 index 00000000000000..b7f12d865ad2bf --- /dev/null +++ b/test/fixtures/wpt/webmessaging/broadcastchannel/resources/ordering.html @@ -0,0 +1,78 @@ + + diff --git a/test/fixtures/wpt/webmessaging/broadcastchannel/resources/origin.html b/test/fixtures/wpt/webmessaging/broadcastchannel/resources/origin.html new file mode 100644 index 00000000000000..f57d582bbb878c --- /dev/null +++ b/test/fixtures/wpt/webmessaging/broadcastchannel/resources/origin.html @@ -0,0 +1,8 @@ + diff --git a/test/fixtures/wpt/webmessaging/broadcastchannel/resources/sandboxed.html b/test/fixtures/wpt/webmessaging/broadcastchannel/resources/sandboxed.html new file mode 100644 index 00000000000000..e32962cdfd4456 --- /dev/null +++ b/test/fixtures/wpt/webmessaging/broadcastchannel/resources/sandboxed.html @@ -0,0 +1,10 @@ + + + diff --git a/test/fixtures/wpt/webmessaging/broadcastchannel/resources/service-worker.js b/test/fixtures/wpt/webmessaging/broadcastchannel/resources/service-worker.js new file mode 100644 index 00000000000000..a3d17b9c650adb --- /dev/null +++ b/test/fixtures/wpt/webmessaging/broadcastchannel/resources/service-worker.js @@ -0,0 +1,15 @@ +let promise_func = null; +let promise = new Promise(resolve => promise_func = resolve); + +const SERVICE_WORKER_TEST_CHANNEL_NAME = 'service worker'; +const bc3 = new BroadcastChannel(SERVICE_WORKER_TEST_CHANNEL_NAME); +bc3.onmessage = e => { + bc3.postMessage('done'); + promise_func(); +}; +bc3.postMessage('from worker'); + +// Ensure that the worker stays alive for the duration of the test +self.addEventListener('install', evt => { + evt.waitUntil(promise); +}); diff --git a/test/fixtures/wpt/webmessaging/broadcastchannel/resources/worker.js b/test/fixtures/wpt/webmessaging/broadcastchannel/resources/worker.js new file mode 100644 index 00000000000000..df23072bc99f95 --- /dev/null +++ b/test/fixtures/wpt/webmessaging/broadcastchannel/resources/worker.js @@ -0,0 +1,37 @@ +var c; + +function handler(e, reply) { + if (e.data.ping) { + c.postMessage(e.data.ping); + return; + } + if (e.data.blob) { + (() => { + c.postMessage({blob: new Blob(e.data.blob)}); + })(); + // TODO(https://github.com/web-platform-tests/wpt/issues/7899): Change to + // some sort of cross-browser GC trigger. + if (self.gc) self.gc(); + } + c = new BroadcastChannel(e.data.channel); + let messages = []; + c.onmessage = e => { + if (e.data === 'ready') { + // Ignore any 'ready' messages from the other thread since there could + // be some race conditions between this BroadcastChannel instance + // being created / ready to receive messages and the message being sent. + return; + } + messages.push(e.data); + if (e.data == 'done') + reply(messages); + }; + c.postMessage('from worker'); +} + +onmessage = e => handler(e, postMessage); + +onconnect = e => { + let port = e.ports[0]; + port.onmessage = e => handler(e, msg => port.postMessage(msg)); +}; diff --git a/test/fixtures/wpt/webmessaging/broadcastchannel/sandbox.html b/test/fixtures/wpt/webmessaging/broadcastchannel/sandbox.html new file mode 100644 index 00000000000000..aedf3c0d6fe64d --- /dev/null +++ b/test/fixtures/wpt/webmessaging/broadcastchannel/sandbox.html @@ -0,0 +1,16 @@ + + +Creating BroadcastChannel in an opaque origin + + + + + + diff --git a/test/fixtures/wpt/webmessaging/broadcastchannel/service-worker.https.html b/test/fixtures/wpt/webmessaging/broadcastchannel/service-worker.https.html new file mode 100644 index 00000000000000..d605434ae1ccb0 --- /dev/null +++ b/test/fixtures/wpt/webmessaging/broadcastchannel/service-worker.https.html @@ -0,0 +1,47 @@ + + + + + diff --git a/test/fixtures/wpt/webmessaging/broadcastchannel/workers.html b/test/fixtures/wpt/webmessaging/broadcastchannel/workers.html new file mode 100644 index 00000000000000..8b55492f3cffb3 --- /dev/null +++ b/test/fixtures/wpt/webmessaging/broadcastchannel/workers.html @@ -0,0 +1,375 @@ + + + + + diff --git a/test/wpt/status/webmessaging/broadcastchannel.json b/test/wpt/status/webmessaging/broadcastchannel.json new file mode 100644 index 00000000000000..1cc693407a2359 --- /dev/null +++ b/test/wpt/status/webmessaging/broadcastchannel.json @@ -0,0 +1,26 @@ +{ + "basics.any.js": { + "fail": { + "unexpected": [ + "assert_equals: origin expected \"https://example.com\" but got \"\"", + "assert_equals: target for event 0 expected object \"[object EventTarget]\" but got object \"[object EventTarget]\"" + ] + } + }, + "interface.any.js": { + "fail": { + "unexpected": [ + "assert_throws_dom: function \"() => c.postMessage('')\" threw object \"Error: BroadcastChannel is closed.\" that is not a DOMException InvalidStateError: property \"code\" is equal to 0, expected 11", + "assert_not_equals: got disallowed value undefined", + "assert_throws_dom: function \"() => c.postMessage(new Symbol())\" threw object \"Error: BroadcastChannel is closed.\" that is not a DOMException InvalidStateError: property \"code\" is equal to 0, expected 11" + ] + } + }, + "origin.window.js": { + "fail": { + "expected": [ + "document is not defined" + ] + } + } +} diff --git a/test/wpt/test-broadcastchannel.js b/test/wpt/test-broadcastchannel.js new file mode 100644 index 00000000000000..988a6270a936d6 --- /dev/null +++ b/test/wpt/test-broadcastchannel.js @@ -0,0 +1,13 @@ +'use strict'; + +require('../common'); +const { WPTRunner } = require('../common/wpt'); + +const runner = new WPTRunner('webmessaging/broadcastchannel'); + +runner.setInitScript(` + const { BroadcastChannel } = require('worker_threads'); + global.BroadcastChannel = BroadcastChannel; +`); + +runner.runJsTests();