Skip to content
File

Blob: src/workerd/api/tests/messageport-test.js

javascript152 lines
1// Copyright (c) 2025 Cloudflare, Inc.
2// Licensed under the Apache 2.0 license found in the LICENSE file or at:
3// https://opensource.org/licenses/Apache-2.0
4import { ok, strictEqual, throws } from 'node:assert';
5 
6import { mock } from 'node:test';
7 
8export const simple1 = {
9 async test() {
10 const { port1, port2 } = new MessageChannel();
11 ok(port1 instanceof MessagePort);
12 ok(port2 instanceof MessagePort);
13 port1.postMessage(1);
14 port2.postMessage(1);
15 const { promise, resolve } = Promise.withResolvers();
16 const handler = mock.fn((event) => {
17 strictEqual(event.data, 1);
18 strictEqual(event.isTrusted, true);
19 resolve();
20 });
21 port2.onmessage = handler;
22 port1.onmessage = handler;
23 await promise;
24 strictEqual(handler.mock.callCount(), 2);
25 },
26};
27 
28export const simple2 = {
29 async test() {
30 const { port1, port2 } = new MessageChannel();
31 ok(port1 instanceof MessagePort);
32 ok(port2 instanceof MessagePort);
33 const { promise, resolve } = Promise.withResolvers();
34 const handler = mock.fn((event) => {
35 strictEqual(event.data, 1);
36 resolve();
37 });
38 port2.onmessage = handler;
39 port1.onmessage = handler;
40 port1.postMessage(1);
41 port2.postMessage(1);
42 await promise;
43 strictEqual(handler.mock.callCount(), 2);
44 },
45};
46 
47export const simple3 = {
48 async test() {
49 const { port1, port2 } = new MessageChannel();
50 
51 const closeHandler = mock.fn();
52 port1.onclose = closeHandler;
53 port2.onclose = closeHandler;
54 
55 port1.close();
56 port2.onmessage = () => {
57 throw new Error('should not be called');
58 };
59 port1.postMessage('nope');
60 await scheduler.wait(10);
61 strictEqual(closeHandler.mock.callCount(), 2);
62 },
63};
64 
65export const simple4 = {
66 async test() {
67 const { port1, port2 } = new MessageChannel();
68 port2.close();
69 port2.onmessage = () => {
70 throw new Error('should not be called');
71 };
72 port1.onmessage = () => {
73 throw new Error('should not be called');
74 };
75 port1.postMessage('nope');
76 port2.postMessage('nope');
77 await scheduler.wait(10);
78 },
79};
80 
81export const simple5 = {
82 async test() {
83 const { port1, port2 } = new MessageChannel();
84 throws(() => port1.postMessage(1, [1]), {
85 message: 'Transfer list is not supported',
86 });
87 throws(() => port1.postMessage(1, { transfer: [1] }), {
88 message: 'Transfer list is not supported',
89 });
90 // If the lists are empty it is ok.
91 port1.postMessage(1, []);
92 port1.postMessage(1, { transfer: [] });
93 
94 const handler = mock.fn((event) => {
95 strictEqual(event.data, 1);
96 });
97 port2.onmessage = handler;
98 await scheduler.wait(10);
99 strictEqual(handler.mock.callCount(), 2);
100 },
101};
102 
103// The following are a selected subset of web platform tests for MessageChannel and MessagePort
104// that we know we pass. We don't support the full MessagePort spec so we're not going to run
105// the full WPT's for these yet.
106// Refs: https://github.com/web-platform-tests/wpt/blob/master/webmessaging/Channel_postMessage_Blob.any.js
107// Refs: https://github.com/web-platform-tests/wpt/blob/master/webmessaging/Channel_postMessage_DataCloneErr.any.js
108 
109export const postMessageBlob = {
110 test() {
111 // Per the spec, Blob is a serializable object, but we don't implement it as such currently.
112 // So attempting to post a blob should throw an error.
113 const { port1 } = new MessageChannel();
114 throws(() => port1.postMessage(new Blob([''])), {
115 code: 25, // DATA_CLONE_ERR,
116 name: 'DataCloneError',
117 message: /Could not serialize/,
118 });
119 },
120};
121 
122export const postMessageRpcTarget = {
123 async test() {
124 const { RpcTarget } = await import('cloudflare:workers');
125 class Foo extends RpcTarget {}
126 
127 const { port1 } = new MessageChannel();
128 throws(() => port1.postMessage(new Foo()), {
129 code: 25, // DATA_CLONE_ERR,
130 name: 'DataCloneError',
131 });
132 },
133};
134 
135// Subset of the Web Platform Tests we know we don't pass, listed for future reference:
136// Most the web messaging WPT's are set up to require a full implementation of MessagePort
137// with web workers and most of the tests are in html files. We'll come back to these
138// later.
139// * https://github.com/web-platform-tests/wpt/blob/master/webmessaging/Channel_postMessage_clone_port.any.js
140// * https://github.com/web-platform-tests/wpt/blob/master/webmessaging/Channel_postMessage_clone_port_error.any.js
141// * https://github.com/web-platform-tests/wpt/blob/master/webmessaging/Channel_postMessage_ports_readonly_array.any.js
142// * https://github.com/web-platform-tests/wpt/blob/master/webmessaging/Channel_postMessage_transfer_xsite_incoming_messages.window.js
143// * https://github.com/web-platform-tests/wpt/blob/master/webmessaging/Channel_postMessage_transfer_xsite_incoming_messages.window.js
144// * https://github.com/web-platform-tests/wpt/blob/master/webmessaging/Channel_postMessage_with_transfer_incoming_messages.any.js
145// * https://github.com/web-platform-tests/wpt/blob/master/webmessaging/Channel_postMessage_with_transfer_outgoing_messages.any.js
146 
147// This one is a bit special, per the spec we're supposed to fire off the close event
148// on an entangled port when the other port is garbage collected. We don't do that yet
149// and we might not ever. Need to investigate this further but it's not blocking us
150// right now.
151// * https://github.com/web-platform-tests/wpt/blob/master/webmessaging/message-channels/close-event/garbage-collected.tentative.any.js