@adobe/uix-core 0.6.5 → 0.7.1-nightly.20230114
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__helpers__/jest.messagechannel.d.cts +2 -0
- package/dist/__helpers__/jest.messagechannel.d.cts.map +1 -0
- package/dist/__mocks__/mock-finalization-registry.d.ts +11 -0
- package/dist/__mocks__/mock-finalization-registry.d.ts.map +1 -0
- package/dist/__mocks__/mock-weak-ref.d.ts +7 -0
- package/dist/__mocks__/mock-weak-ref.d.ts.map +1 -0
- package/dist/constants.d.ts +8 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/cross-realm-object.d.ts +44 -0
- package/dist/cross-realm-object.d.ts.map +1 -0
- package/dist/debuglog.d.ts +11 -0
- package/dist/debuglog.d.ts.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +906 -7
- package/dist/index.js.map +1 -1
- package/dist/message-wrapper.d.ts +9 -0
- package/dist/message-wrapper.d.ts.map +1 -0
- package/dist/object-simulator.d.ts +28 -0
- package/dist/object-simulator.d.ts.map +1 -0
- package/dist/object-simulator.test.d.ts +2 -0
- package/dist/object-simulator.test.d.ts.map +1 -0
- package/dist/object-walker.d.ts +30 -0
- package/dist/object-walker.d.ts.map +1 -0
- package/dist/promises/index.d.ts +3 -0
- package/dist/promises/index.d.ts.map +1 -0
- package/dist/promises/promise-wrappers.test.d.ts +2 -0
- package/dist/promises/promise-wrappers.test.d.ts.map +1 -0
- package/dist/promises/timed.d.ts +15 -0
- package/dist/promises/timed.d.ts.map +1 -0
- package/dist/promises/wait.d.ts +7 -0
- package/dist/promises/wait.d.ts.map +1 -0
- package/dist/remote-subject.d.ts +70 -0
- package/dist/remote-subject.d.ts.map +1 -0
- package/dist/rpc/call-receiver.d.ts +4 -0
- package/dist/rpc/call-receiver.d.ts.map +1 -0
- package/dist/rpc/call-receiver.test.d.ts +2 -0
- package/dist/rpc/call-receiver.test.d.ts.map +1 -0
- package/dist/rpc/call-sender.d.ts +4 -0
- package/dist/rpc/call-sender.d.ts.map +1 -0
- package/dist/rpc/call-sender.test.d.ts +2 -0
- package/dist/rpc/call-sender.test.d.ts.map +1 -0
- package/dist/rpc/index.d.ts +3 -0
- package/dist/rpc/index.d.ts.map +1 -0
- package/dist/tickets.d.ts +34 -0
- package/dist/tickets.d.ts.map +1 -0
- package/dist/tunnel/index.d.ts +2 -0
- package/dist/tunnel/index.d.ts.map +1 -0
- package/dist/tunnel/tunnel-messenger.d.ts +25 -0
- package/dist/tunnel/tunnel-messenger.d.ts.map +1 -0
- package/dist/tunnel/tunnel-messenger.test.d.ts +2 -0
- package/dist/tunnel/tunnel-messenger.test.d.ts.map +1 -0
- package/dist/tunnel/tunnel.d.ts +62 -0
- package/dist/tunnel/tunnel.d.ts.map +1 -0
- package/dist/tunnel/tunnel.test.d.ts +2 -0
- package/dist/tunnel/tunnel.test.d.ts.map +1 -0
- package/dist/types.d.ts +1 -4
- package/dist/types.d.ts.map +1 -1
- package/dist/value-assertions.d.ts +13 -0
- package/dist/value-assertions.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/__helpers__/jest.messagechannel.cjs +3 -0
- package/src/__mocks__/mock-finalization-registry.ts +13 -0
- package/src/__mocks__/mock-weak-ref.ts +10 -0
- package/src/constants.ts +10 -0
- package/src/cross-realm-object.ts +117 -0
- package/src/debuglog.ts +1 -1
- package/src/index.ts +4 -1
- package/src/message-wrapper.ts +35 -0
- package/src/object-simulator.test.ts +328 -0
- package/src/object-simulator.ts +145 -0
- package/src/object-walker.ts +132 -0
- package/src/promises/index.ts +2 -0
- package/src/promises/promise-wrappers.test.ts +63 -0
- package/src/promises/timed.ts +41 -0
- package/src/promises/wait.ts +10 -0
- package/src/remote-subject.ts +185 -0
- package/src/rpc/call-receiver.test.ts +90 -0
- package/src/rpc/call-receiver.ts +29 -0
- package/src/rpc/call-sender.test.ts +73 -0
- package/src/rpc/call-sender.ts +72 -0
- package/src/rpc/index.ts +2 -0
- package/src/tickets.ts +71 -0
- package/src/tunnel/index.ts +1 -0
- package/src/tunnel/tunnel-messenger.test.ts +183 -0
- package/src/tunnel/tunnel-messenger.ts +99 -0
- package/src/tunnel/tunnel.test.ts +211 -0
- package/src/tunnel/tunnel.ts +322 -0
- package/src/types.ts +3 -5
- package/src/value-assertions.ts +58 -0
- package/tsconfig.json +2 -6
- package/dist/timeout-promise.d.ts +0 -12
- package/dist/timeout-promise.d.ts.map +0 -1
- package/src/timeout-promise.ts +0 -36
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { NS_ROOT, VERSION } from "../constants";
|
|
2
|
+
import { TunnelMessenger } from "./tunnel-messenger";
|
|
3
|
+
|
|
4
|
+
const fakeConsole = {
|
|
5
|
+
error: jest.fn(),
|
|
6
|
+
warn: jest.fn(),
|
|
7
|
+
} as unknown as jest.Mocked<Console>;
|
|
8
|
+
|
|
9
|
+
describe("tunnel negotiation message factory", () => {
|
|
10
|
+
let messenger: TunnelMessenger;
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
messenger = new TunnelMessenger({
|
|
13
|
+
myOrigin: "https://me",
|
|
14
|
+
targetOrigin: "https://you",
|
|
15
|
+
logger: fakeConsole,
|
|
16
|
+
});
|
|
17
|
+
jest.clearAllMocks();
|
|
18
|
+
});
|
|
19
|
+
afterAll(() => {
|
|
20
|
+
jest.restoreAllMocks();
|
|
21
|
+
});
|
|
22
|
+
it("makeAccepted", () => {
|
|
23
|
+
expect(messenger.makeAccepted("test1")).toMatchObject({
|
|
24
|
+
[NS_ROOT]: {
|
|
25
|
+
accepts: "test1",
|
|
26
|
+
version: VERSION,
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
it("makeOffered", () => {
|
|
31
|
+
expect(messenger.makeOffered("test1")).toMatchObject({
|
|
32
|
+
[NS_ROOT]: {
|
|
33
|
+
offers: "test1",
|
|
34
|
+
version: VERSION,
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
it("isHandshakeOffer", () => {
|
|
39
|
+
expect(
|
|
40
|
+
messenger.isHandshakeOffer({
|
|
41
|
+
[NS_ROOT]: {
|
|
42
|
+
offers: "test2",
|
|
43
|
+
version: VERSION,
|
|
44
|
+
},
|
|
45
|
+
})
|
|
46
|
+
).toBeTruthy();
|
|
47
|
+
expect(
|
|
48
|
+
messenger.isHandshakeOffer({
|
|
49
|
+
[NS_ROOT]: {
|
|
50
|
+
accepts: "test2",
|
|
51
|
+
version: VERSION,
|
|
52
|
+
},
|
|
53
|
+
})
|
|
54
|
+
).toBeFalsy();
|
|
55
|
+
expect(messenger.isHandshakeOffer({})).toBeFalsy();
|
|
56
|
+
});
|
|
57
|
+
it("isHandshakeAccepting(message, id) matches on id", () => {
|
|
58
|
+
expect(
|
|
59
|
+
messenger.isHandshakeAccepting(
|
|
60
|
+
{
|
|
61
|
+
[NS_ROOT]: {
|
|
62
|
+
accepts: "test3",
|
|
63
|
+
version: VERSION,
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
"test3"
|
|
67
|
+
)
|
|
68
|
+
).toBeTruthy();
|
|
69
|
+
expect(
|
|
70
|
+
messenger.isHandshakeAccepting(
|
|
71
|
+
{
|
|
72
|
+
accepts: "test3",
|
|
73
|
+
version: VERSION,
|
|
74
|
+
},
|
|
75
|
+
"mismatch"
|
|
76
|
+
)
|
|
77
|
+
).toBeFalsy();
|
|
78
|
+
expect(messenger.isHandshakeAccepting({}, "test3")).toBeFalsy();
|
|
79
|
+
});
|
|
80
|
+
describe("isHandshake rejects malformed messages", () => {
|
|
81
|
+
it("non-plain-objects", () => {
|
|
82
|
+
expect(messenger.isHandshake([])).toBeFalsy();
|
|
83
|
+
expect(messenger.isHandshake("")).toBeFalsy();
|
|
84
|
+
expect(messenger.isHandshake(true)).toBeFalsy();
|
|
85
|
+
expect(fakeConsole.error).toHaveBeenCalledTimes(3);
|
|
86
|
+
expect(fakeConsole.error.mock.calls.map(([msg]) => msg))
|
|
87
|
+
.toMatchInlineSnapshot(`
|
|
88
|
+
[
|
|
89
|
+
"Malformed tunnel message sent from SDK at https://you to https://me:
|
|
90
|
+
[]
|
|
91
|
+
Message must be an object with "_$pg" property, which must be an object with a "version" string and an either an "accepts" or "offers" property containing an ID string.",
|
|
92
|
+
"Malformed tunnel message sent from SDK at https://you to https://me:
|
|
93
|
+
""
|
|
94
|
+
Message must be an object with "_$pg" property, which must be an object with a "version" string and an either an "accepts" or "offers" property containing an ID string.",
|
|
95
|
+
"Malformed tunnel message sent from SDK at https://you to https://me:
|
|
96
|
+
true
|
|
97
|
+
Message must be an object with "_$pg" property, which must be an object with a "version" string and an either an "accepts" or "offers" property containing an ID string.",
|
|
98
|
+
]
|
|
99
|
+
`);
|
|
100
|
+
});
|
|
101
|
+
it("without a sub-object at the expected root property", () => {
|
|
102
|
+
expect(
|
|
103
|
+
messenger.isHandshakeOffer({
|
|
104
|
+
someOtherRoot: false,
|
|
105
|
+
})
|
|
106
|
+
).toBeFalsy();
|
|
107
|
+
expect(
|
|
108
|
+
messenger.isHandshake({
|
|
109
|
+
[NS_ROOT]: 5,
|
|
110
|
+
})
|
|
111
|
+
).toBeFalsy();
|
|
112
|
+
expect(fakeConsole.error.mock.calls.map(([msg]) => msg))
|
|
113
|
+
.toMatchInlineSnapshot(`
|
|
114
|
+
[
|
|
115
|
+
"Malformed tunnel message sent from SDK at https://you to https://me:
|
|
116
|
+
{
|
|
117
|
+
"someOtherRoot": false
|
|
118
|
+
}
|
|
119
|
+
Message must be an object with "_$pg" property, which must be an object with a "version" string and an either an "accepts" or "offers" property containing an ID string.",
|
|
120
|
+
"Malformed tunnel message sent from SDK at https://you to https://me:
|
|
121
|
+
{
|
|
122
|
+
"_$pg": 5
|
|
123
|
+
}
|
|
124
|
+
Message must be an object with "_$pg" property, which must be an object with a "version" string and an either an "accepts" or "offers" property containing an ID string.",
|
|
125
|
+
]
|
|
126
|
+
`);
|
|
127
|
+
});
|
|
128
|
+
it("without accept or offers properties", () => {
|
|
129
|
+
expect(
|
|
130
|
+
messenger.isHandshake({
|
|
131
|
+
[NS_ROOT]: {
|
|
132
|
+
version: VERSION,
|
|
133
|
+
},
|
|
134
|
+
})
|
|
135
|
+
).toBeFalsy();
|
|
136
|
+
});
|
|
137
|
+
it("with no version string", () => {
|
|
138
|
+
expect(
|
|
139
|
+
messenger.isHandshake({
|
|
140
|
+
[NS_ROOT]: {
|
|
141
|
+
offers: "test4",
|
|
142
|
+
},
|
|
143
|
+
})
|
|
144
|
+
).toBeFalsy();
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
describe("version mismatch handling", () => {
|
|
148
|
+
const withVersion = (version: string) => ({
|
|
149
|
+
[NS_ROOT]: {
|
|
150
|
+
offers: "test5",
|
|
151
|
+
version,
|
|
152
|
+
},
|
|
153
|
+
});
|
|
154
|
+
it("warns in console, once for each version", () => {
|
|
155
|
+
expect(messenger.isHandshake(withVersion("bad-version"))).toBeTruthy();
|
|
156
|
+
expect(messenger.isHandshake(withVersion("worse-version"))).toBeTruthy();
|
|
157
|
+
expect(messenger.isHandshake(withVersion("bad-version"))).toBeTruthy();
|
|
158
|
+
expect(fakeConsole.warn).toHaveBeenCalledTimes(2);
|
|
159
|
+
expect(fakeConsole.warn.mock.calls.map(([msg]) => msg))
|
|
160
|
+
.toMatchInlineSnapshot(`
|
|
161
|
+
[
|
|
162
|
+
"SDK version mismatch. https://me is using v0.0.1-test, but received message from https://you using SDK vbad-version. Extensions may be broken or unresponsive.",
|
|
163
|
+
"SDK version mismatch. https://me is using v0.0.1-test, but received message from https://you using SDK vworse-version. Extensions may be broken or unresponsive.",
|
|
164
|
+
]
|
|
165
|
+
`);
|
|
166
|
+
});
|
|
167
|
+
it("resetWarnings() resets seen version warnings so they'll log again", () => {
|
|
168
|
+
messenger.resetWarnings();
|
|
169
|
+
expect(
|
|
170
|
+
messenger.isHandshake(withVersion("same-bad-version"))
|
|
171
|
+
).toBeTruthy();
|
|
172
|
+
expect(
|
|
173
|
+
messenger.isHandshake(withVersion("same-bad-version"))
|
|
174
|
+
).toBeTruthy();
|
|
175
|
+
expect(fakeConsole.warn).toHaveBeenCalledTimes(1);
|
|
176
|
+
messenger.resetWarnings();
|
|
177
|
+
expect(
|
|
178
|
+
messenger.isHandshake(withVersion("same-bad-version"))
|
|
179
|
+
).toBeTruthy();
|
|
180
|
+
expect(fakeConsole.warn).toHaveBeenCalledTimes(2);
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
});
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { NS_ROOT, VERSION } from "../constants";
|
|
2
|
+
import { isPlainObject } from "../value-assertions";
|
|
3
|
+
import { WrappedMessage, isWrapped, wrap, unwrap } from "../message-wrapper";
|
|
4
|
+
import { HandshakeAcceptedTicket, HandshakeOfferedTicket } from "../tickets";
|
|
5
|
+
|
|
6
|
+
type Handshake = HandshakeAcceptedTicket | HandshakeOfferedTicket;
|
|
7
|
+
type HandshakeAccepted = WrappedMessage<HandshakeAcceptedTicket>;
|
|
8
|
+
type HandshakeOffered = WrappedMessage<HandshakeOfferedTicket>;
|
|
9
|
+
type HandshakeMessage = HandshakeAccepted | HandshakeOffered;
|
|
10
|
+
|
|
11
|
+
export class TunnelMessenger {
|
|
12
|
+
private myOrigin: string;
|
|
13
|
+
private remoteOrigin: string;
|
|
14
|
+
private logger: Console;
|
|
15
|
+
private versionWarnings = new Set<string>();
|
|
16
|
+
constructor(opts: {
|
|
17
|
+
myOrigin: string;
|
|
18
|
+
targetOrigin: string;
|
|
19
|
+
logger: Console;
|
|
20
|
+
}) {
|
|
21
|
+
this.myOrigin = opts.myOrigin;
|
|
22
|
+
this.remoteOrigin =
|
|
23
|
+
opts.targetOrigin === "*" ? "remote document" : opts.targetOrigin;
|
|
24
|
+
this.logger = opts.logger;
|
|
25
|
+
}
|
|
26
|
+
resetWarnings() {
|
|
27
|
+
this.versionWarnings.clear();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
makeAccepted(id: string): HandshakeAccepted {
|
|
31
|
+
return wrap({
|
|
32
|
+
accepts: id,
|
|
33
|
+
version: VERSION,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
makeOffered(id: string): HandshakeOffered {
|
|
37
|
+
return wrap({
|
|
38
|
+
offers: id,
|
|
39
|
+
version: VERSION,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
isHandshakeAccepting(
|
|
43
|
+
message: unknown,
|
|
44
|
+
id: string
|
|
45
|
+
): message is HandshakeAccepted {
|
|
46
|
+
return (
|
|
47
|
+
this.isHandshake(message) &&
|
|
48
|
+
unwrap(message as HandshakeAccepted).accepts === id
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
isHandshakeOffer(message: unknown): message is HandshakeOffered {
|
|
52
|
+
return (
|
|
53
|
+
this.isHandshake(message) &&
|
|
54
|
+
typeof unwrap(message as HandshakeOffered).offers === "string"
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
isHandshake(message: unknown): message is HandshakeMessage {
|
|
58
|
+
if (!isWrapped(message)) {
|
|
59
|
+
this.logMalformed(message);
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
const tunnelData: Handshake = unwrap<Handshake>(
|
|
63
|
+
message as HandshakeMessage
|
|
64
|
+
);
|
|
65
|
+
if (
|
|
66
|
+
!isPlainObject(tunnelData) ||
|
|
67
|
+
typeof tunnelData.version !== "string" ||
|
|
68
|
+
!(Reflect.has(tunnelData, "accepts") || Reflect.has(tunnelData, "offers"))
|
|
69
|
+
) {
|
|
70
|
+
this.logMalformed(message);
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
const { version } = tunnelData;
|
|
74
|
+
if (version !== VERSION && !this.versionWarnings.has(version)) {
|
|
75
|
+
this.versionWarnings.add(version);
|
|
76
|
+
this.logger.warn(
|
|
77
|
+
`SDK version mismatch. ${this.myOrigin} is using v${VERSION}, but received message from ${this.remoteOrigin} using SDK v${version}. Extensions may be broken or unresponsive.`
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
private logMalformed(message: unknown) {
|
|
83
|
+
let inspectedMessage: string;
|
|
84
|
+
try {
|
|
85
|
+
inspectedMessage = JSON.stringify(message, null, 2);
|
|
86
|
+
} catch (_) {
|
|
87
|
+
try {
|
|
88
|
+
inspectedMessage = message.toString();
|
|
89
|
+
} catch (e) {
|
|
90
|
+
inspectedMessage = Object.prototype.toString.call(message);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
this.logger.error(
|
|
94
|
+
`Malformed tunnel message sent from SDK at ${this.remoteOrigin} to ${this.myOrigin}:
|
|
95
|
+
${inspectedMessage}
|
|
96
|
+
Message must be an object with "${NS_ROOT}" property, which must be an object with a "version" string and an either an "accepts" or "offers" property containing an ID string.`
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { fireEvent } from "@testing-library/dom";
|
|
2
|
+
import { wait } from "../promises/wait";
|
|
3
|
+
import { Tunnel } from "./tunnel";
|
|
4
|
+
import { TunnelMessenger } from "./tunnel-messenger";
|
|
5
|
+
|
|
6
|
+
const fakeConsole = {
|
|
7
|
+
error: jest.fn(),
|
|
8
|
+
warn: jest.fn(),
|
|
9
|
+
} as unknown as jest.Mocked<Console>;
|
|
10
|
+
|
|
11
|
+
const defaultTunnelConfig = {
|
|
12
|
+
targetOrigin: "*",
|
|
13
|
+
timeout: 4000,
|
|
14
|
+
logger: fakeConsole,
|
|
15
|
+
};
|
|
16
|
+
type TunnelHarness = { tunnel: Tunnel; port: MessagePort };
|
|
17
|
+
const openPorts: MessagePort[] = [];
|
|
18
|
+
function tunnelHarness(
|
|
19
|
+
port: MessagePort,
|
|
20
|
+
config = defaultTunnelConfig
|
|
21
|
+
): TunnelHarness {
|
|
22
|
+
const tunnel = new Tunnel(config);
|
|
23
|
+
tunnel.connect(port);
|
|
24
|
+
openPorts.push(port);
|
|
25
|
+
return {
|
|
26
|
+
tunnel,
|
|
27
|
+
port,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async function testEventExchange(local: Tunnel, remote: Tunnel) {
|
|
32
|
+
const replyHandler = jest.fn();
|
|
33
|
+
remote.on("outgoing", replyHandler);
|
|
34
|
+
local.on("incoming", (data) => {
|
|
35
|
+
local.emit("outgoing", {
|
|
36
|
+
reply: `${data.greeting} It is I!`,
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
remote.emit("incoming", { greeting: "Who goes there?" });
|
|
40
|
+
await wait(10);
|
|
41
|
+
expect(replyHandler).toHaveBeenCalledTimes(1);
|
|
42
|
+
expect(replyHandler.mock.lastCall[0]).toMatchObject({
|
|
43
|
+
reply: "Who goes there? It is I!",
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
describe("an EventEmitter dispatching and receiving from a MessagePort", () => {
|
|
48
|
+
let local: TunnelHarness;
|
|
49
|
+
let remote: TunnelHarness;
|
|
50
|
+
beforeEach(() => {
|
|
51
|
+
const channel = new MessageChannel();
|
|
52
|
+
local = tunnelHarness(channel.port1);
|
|
53
|
+
remote = tunnelHarness(channel.port2);
|
|
54
|
+
});
|
|
55
|
+
afterEach(() => {
|
|
56
|
+
while (openPorts.length > 0) {
|
|
57
|
+
openPorts.pop().close();
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
it("receives MessageEvents and emits local events to listeners", async () => {
|
|
61
|
+
const test1Handler = jest.fn();
|
|
62
|
+
local.tunnel.on("test1", test1Handler);
|
|
63
|
+
remote.port.postMessage({
|
|
64
|
+
type: "test1",
|
|
65
|
+
payload: {
|
|
66
|
+
test1Payload: true,
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
await wait(100);
|
|
70
|
+
expect(test1Handler).toHaveBeenCalled();
|
|
71
|
+
expect(test1Handler.mock.lastCall[0]).toMatchObject({ test1Payload: true });
|
|
72
|
+
});
|
|
73
|
+
it("exchanges connect events", async () => {
|
|
74
|
+
const localConnectHandler = jest.fn();
|
|
75
|
+
const remoteConnectHandler = jest.fn();
|
|
76
|
+
local.tunnel.on("connected", localConnectHandler);
|
|
77
|
+
remote.tunnel.on("connected", remoteConnectHandler);
|
|
78
|
+
await wait(100);
|
|
79
|
+
expect(localConnectHandler).toHaveBeenCalledTimes(1);
|
|
80
|
+
expect(remoteConnectHandler).toHaveBeenCalledTimes(1);
|
|
81
|
+
});
|
|
82
|
+
it("#emitRemote() sends remote events after connect", async () => {
|
|
83
|
+
const messageListener = jest.fn();
|
|
84
|
+
remote.port.addEventListener("message", messageListener);
|
|
85
|
+
local.tunnel.emit("test2", { test2Payload: true });
|
|
86
|
+
local.tunnel.emit("test3", { test3Payload: true });
|
|
87
|
+
await wait(10);
|
|
88
|
+
expect(messageListener).toHaveBeenCalledTimes(3);
|
|
89
|
+
const connectMessageEvent = messageListener.mock.calls[0][0];
|
|
90
|
+
expect(connectMessageEvent).toHaveProperty("data", {
|
|
91
|
+
type: "connected",
|
|
92
|
+
});
|
|
93
|
+
const test2MessageEvent = messageListener.mock.calls[1][0];
|
|
94
|
+
expect(test2MessageEvent).toHaveProperty("data", {
|
|
95
|
+
type: "test2",
|
|
96
|
+
payload: {
|
|
97
|
+
test2Payload: true,
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
const test3MessageEvent = messageListener.mock.calls[2][0];
|
|
101
|
+
expect(test3MessageEvent).toHaveProperty("data", {
|
|
102
|
+
type: "test3",
|
|
103
|
+
payload: {
|
|
104
|
+
test3Payload: true,
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
it("exchanges events between two emitters sharing ports", async () => {
|
|
109
|
+
await testEventExchange(local.tunnel, remote.tunnel);
|
|
110
|
+
});
|
|
111
|
+
it("#connect(port) accepts a new messageport", async () => {
|
|
112
|
+
const connectHandler = jest.fn();
|
|
113
|
+
local.tunnel.on("connected", connectHandler);
|
|
114
|
+
remote.tunnel.on("reconnect", connectHandler);
|
|
115
|
+
const confirmHandler = jest.fn();
|
|
116
|
+
local.tunnel.on("confirm", confirmHandler);
|
|
117
|
+
const dispelHandler = jest.fn();
|
|
118
|
+
remote.tunnel.on("dispel", dispelHandler);
|
|
119
|
+
local.tunnel.emit("dispel", { dispelled: 1 });
|
|
120
|
+
remote.tunnel.emit("confirm", { confirmed: 1 });
|
|
121
|
+
await wait(10);
|
|
122
|
+
expect(confirmHandler).toHaveBeenCalledTimes(1);
|
|
123
|
+
expect(dispelHandler).toHaveBeenCalledTimes(1);
|
|
124
|
+
|
|
125
|
+
const replacementChannel = new MessageChannel();
|
|
126
|
+
local.tunnel.connect(replacementChannel.port2);
|
|
127
|
+
|
|
128
|
+
// this event should wait until remote connects port1;
|
|
129
|
+
local.tunnel.emit("dispel", { dispelled: 2 });
|
|
130
|
+
|
|
131
|
+
// this event fires on the dead port, since remote.tunnel still has it
|
|
132
|
+
remote.tunnel.emit("confirm", { confirmed: 2 });
|
|
133
|
+
await wait(10);
|
|
134
|
+
// so neither is called
|
|
135
|
+
expect(confirmHandler).toHaveBeenCalledTimes(1);
|
|
136
|
+
expect(dispelHandler).toHaveBeenCalledTimes(1);
|
|
137
|
+
|
|
138
|
+
remote.tunnel.connect(replacementChannel.port1);
|
|
139
|
+
await wait(10);
|
|
140
|
+
// dispel handler fired when port1 was opened by #reconnect
|
|
141
|
+
expect(dispelHandler).toHaveBeenCalledTimes(2);
|
|
142
|
+
|
|
143
|
+
// this dispel event should work now
|
|
144
|
+
remote.tunnel.emit("confirm", { confirmed: 3 });
|
|
145
|
+
await wait(10);
|
|
146
|
+
|
|
147
|
+
expect(confirmHandler).toHaveBeenCalledTimes(2);
|
|
148
|
+
expect(confirmHandler.mock.calls[1][0]).toMatchObject({ confirmed: 3 });
|
|
149
|
+
|
|
150
|
+
expect(connectHandler).toHaveBeenCalledTimes(2);
|
|
151
|
+
replacementChannel.port1.close();
|
|
152
|
+
replacementChannel.port2.close();
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
describe("static Tunnel.toIframe(iframe, options)", () => {
|
|
156
|
+
let localTunnel: Tunnel;
|
|
157
|
+
let remoteTunnel: Tunnel;
|
|
158
|
+
afterEach(() => {
|
|
159
|
+
localTunnel && localTunnel.destroy();
|
|
160
|
+
remoteTunnel && remoteTunnel.destroy();
|
|
161
|
+
});
|
|
162
|
+
/**
|
|
163
|
+
* skipped in unit tests because JSDOM's iframe and postMessage don't
|
|
164
|
+
* implement proper MessageEvents as of 2022/11/30. See
|
|
165
|
+
* https://github.com/jsdom/jsdom/blob/22f7c3c51829a6f14387f7a99e5cdf087f72e685/lib/jsdom/living/post-message.js#L31-L37
|
|
166
|
+
*/
|
|
167
|
+
describe.skip("creates a Tunnel connected to an iframe", () => {
|
|
168
|
+
it.only("listens for handshakes from the frame window", async () => {
|
|
169
|
+
let remoteTunnel: Tunnel;
|
|
170
|
+
const connectMessageHandler = jest.fn();
|
|
171
|
+
const acceptListener = jest.fn();
|
|
172
|
+
const targetOrigin = "https://example.com:4001";
|
|
173
|
+
const loadedFrame = document.createElement("iframe");
|
|
174
|
+
loadedFrame.src = targetOrigin;
|
|
175
|
+
document.body.appendChild(loadedFrame);
|
|
176
|
+
loadedFrame.contentWindow.addEventListener("message", acceptListener);
|
|
177
|
+
const localTunnel = Tunnel.toIframe(loadedFrame, {
|
|
178
|
+
targetOrigin,
|
|
179
|
+
timeout: 9999,
|
|
180
|
+
});
|
|
181
|
+
const messenger = new TunnelMessenger({
|
|
182
|
+
myOrigin: "https://example.com",
|
|
183
|
+
targetOrigin,
|
|
184
|
+
logger: fakeConsole,
|
|
185
|
+
});
|
|
186
|
+
localTunnel.on("connected", connectMessageHandler);
|
|
187
|
+
await wait(100);
|
|
188
|
+
fireEvent(
|
|
189
|
+
window,
|
|
190
|
+
new MessageEvent("message", {
|
|
191
|
+
data: messenger.makeOffered("iframe-test-1"),
|
|
192
|
+
origin: loadedFrame.src,
|
|
193
|
+
source: loadedFrame.contentWindow,
|
|
194
|
+
})
|
|
195
|
+
);
|
|
196
|
+
await wait(100);
|
|
197
|
+
expect(acceptListener).toHaveBeenCalled();
|
|
198
|
+
const acceptEvent = acceptListener.mock.lastCall[0];
|
|
199
|
+
expect(acceptEvent).toHaveProperty(
|
|
200
|
+
"data",
|
|
201
|
+
messenger.makeAccepted("iframe-test-1")
|
|
202
|
+
);
|
|
203
|
+
expect(acceptEvent.ports).toHaveLength(1);
|
|
204
|
+
remoteTunnel = new Tunnel(defaultTunnelConfig);
|
|
205
|
+
remoteTunnel.connect(acceptEvent.ports[0]);
|
|
206
|
+
await wait(100);
|
|
207
|
+
expect(connectMessageHandler).toHaveBeenCalledTimes(1);
|
|
208
|
+
await testEventExchange(localTunnel, remoteTunnel);
|
|
209
|
+
});
|
|
210
|
+
});
|
|
211
|
+
});
|