request-iframe 0.0.1 → 0.0.3
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/README.CN.md +271 -12
- package/README.md +268 -11
- package/library/__tests__/channel.test.ts +420 -0
- package/library/__tests__/debug.test.ts +588 -0
- package/library/__tests__/dispatcher.test.ts +481 -0
- package/library/__tests__/interceptors.test.ts +22 -0
- package/library/__tests__/requestIframe.test.ts +2317 -99
- package/library/__tests__/server.test.ts +738 -0
- package/library/api/client.d.js +5 -0
- package/library/api/client.d.ts.map +1 -1
- package/library/api/client.js +11 -6
- package/library/api/server.d.js +5 -0
- package/library/api/server.d.ts +4 -3
- package/library/api/server.d.ts.map +1 -1
- package/library/api/server.js +25 -7
- package/library/constants/index.d.js +36 -0
- package/library/constants/index.d.ts +14 -4
- package/library/constants/index.d.ts.map +1 -1
- package/library/constants/index.js +15 -7
- package/library/constants/messages.d.js +5 -0
- package/library/constants/messages.d.ts +35 -0
- package/library/constants/messages.d.ts.map +1 -1
- package/library/constants/messages.js +36 -1
- package/library/core/client-server.d.ts +101 -0
- package/library/core/client-server.d.ts.map +1 -0
- package/library/core/client-server.js +266 -0
- package/library/core/client.d.js +5 -0
- package/library/core/client.d.ts +38 -6
- package/library/core/client.d.ts.map +1 -1
- package/library/core/client.js +198 -24
- package/library/core/request.d.js +5 -0
- package/library/core/response.d.js +5 -0
- package/library/core/response.d.ts +5 -1
- package/library/core/response.d.ts.map +1 -1
- package/library/core/response.js +85 -70
- package/library/core/server-client.d.js +5 -0
- package/library/core/server-client.d.ts +3 -1
- package/library/core/server-client.d.ts.map +1 -1
- package/library/core/server-client.js +19 -9
- package/library/core/server.d.js +5 -0
- package/library/core/server.d.ts +11 -3
- package/library/core/server.d.ts.map +1 -1
- package/library/core/server.js +112 -54
- package/library/index.d.ts +1 -1
- package/library/index.js +2 -2
- package/library/interceptors/index.d.js +5 -0
- package/library/interceptors/index.d.ts +4 -0
- package/library/interceptors/index.d.ts.map +1 -1
- package/library/interceptors/index.js +7 -0
- package/library/message/channel.d.js +5 -0
- package/library/message/channel.d.ts +3 -1
- package/library/message/channel.d.ts.map +1 -1
- package/library/message/dispatcher.d.js +5 -0
- package/library/message/dispatcher.d.ts +7 -2
- package/library/message/dispatcher.d.ts.map +1 -1
- package/library/message/dispatcher.js +47 -2
- package/library/message/index.d.js +25 -0
- package/library/stream/file-stream.d.js +4 -0
- package/library/stream/file-stream.d.ts +5 -0
- package/library/stream/file-stream.d.ts.map +1 -1
- package/library/stream/file-stream.js +41 -12
- package/library/stream/index.d.js +58 -0
- package/library/stream/readable-stream.d.js +5 -0
- package/library/stream/readable-stream.d.ts.map +1 -1
- package/library/stream/readable-stream.js +32 -30
- package/library/stream/types.d.js +5 -0
- package/library/stream/types.d.ts +18 -0
- package/library/stream/types.d.ts.map +1 -1
- package/library/stream/writable-stream.d.js +5 -0
- package/library/stream/writable-stream.d.ts +1 -0
- package/library/stream/writable-stream.d.ts.map +1 -1
- package/library/stream/writable-stream.js +7 -2
- package/library/types/index.d.js +5 -0
- package/library/types/index.d.ts +79 -19
- package/library/types/index.d.ts.map +1 -1
- package/library/utils/cache.d.js +5 -0
- package/library/utils/cache.d.ts +24 -0
- package/library/utils/cache.d.ts.map +1 -1
- package/library/utils/cache.js +76 -0
- package/library/utils/cookie.d.js +5 -0
- package/library/utils/debug.d.js +5 -0
- package/library/utils/debug.d.ts.map +1 -1
- package/library/utils/debug.js +382 -20
- package/library/utils/index.d.js +94 -0
- package/library/utils/index.d.ts +5 -0
- package/library/utils/index.d.ts.map +1 -1
- package/library/utils/index.js +14 -1
- package/library/utils/path-match.d.js +5 -0
- package/library/utils/protocol.d.js +5 -0
- package/package.json +16 -2
- package/react/library/__tests__/index.test.d.ts +2 -0
- package/react/library/__tests__/index.test.d.ts.map +1 -0
- package/react/library/__tests__/index.test.tsx +770 -0
- package/react/library/index.d.ts +118 -0
- package/react/library/index.d.ts.map +1 -0
- package/react/library/index.js +232 -0
|
@@ -0,0 +1,738 @@
|
|
|
1
|
+
import { requestIframeServer, clearRequestIframeServerCache } from '../api/server';
|
|
2
|
+
import { MessageType, MessageRole, HttpStatus, ErrorCode, Messages } from '../constants';
|
|
3
|
+
import { PostMessageData } from '../types';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Create test iframe
|
|
7
|
+
*/
|
|
8
|
+
function createTestIframe(origin: string): HTMLIFrameElement {
|
|
9
|
+
const iframe = document.createElement('iframe');
|
|
10
|
+
iframe.src = `${origin}/test.html`;
|
|
11
|
+
document.body.appendChild(iframe);
|
|
12
|
+
return iframe;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Cleanup test iframe
|
|
17
|
+
*/
|
|
18
|
+
function cleanupIframe(iframe: HTMLIFrameElement): void {
|
|
19
|
+
if (iframe.parentNode) {
|
|
20
|
+
iframe.parentNode.removeChild(iframe);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Create a mock window object that can be used as MessageEvent source
|
|
26
|
+
* This ensures the source is recognized as a Window object by the message channel
|
|
27
|
+
*/
|
|
28
|
+
function createMockWindow(): { postMessage: jest.Mock } & Window {
|
|
29
|
+
const mockPostMessage = jest.fn();
|
|
30
|
+
// Create a mock window that extends the real window prototype
|
|
31
|
+
const mockWindow = Object.create(window) as any;
|
|
32
|
+
mockWindow.postMessage = mockPostMessage;
|
|
33
|
+
return mockWindow;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
describe('RequestIframeServer', () => {
|
|
37
|
+
beforeEach(() => {
|
|
38
|
+
clearRequestIframeServerCache();
|
|
39
|
+
// Clear all iframes
|
|
40
|
+
document.querySelectorAll('iframe').forEach((iframe) => {
|
|
41
|
+
if (iframe.parentNode) {
|
|
42
|
+
iframe.parentNode.removeChild(iframe);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
afterEach(() => {
|
|
48
|
+
// Clear all caches
|
|
49
|
+
clearRequestIframeServerCache();
|
|
50
|
+
// Clear all iframes
|
|
51
|
+
document.querySelectorAll('iframe').forEach((iframe) => {
|
|
52
|
+
if (iframe.parentNode) {
|
|
53
|
+
iframe.parentNode.removeChild(iframe);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
describe('constructor', () => {
|
|
59
|
+
it('should create server with default options', () => {
|
|
60
|
+
const server = requestIframeServer();
|
|
61
|
+
expect(server).toBeDefined();
|
|
62
|
+
expect(server.isOpen).toBe(true);
|
|
63
|
+
server.destroy();
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('should create server with secretKey', () => {
|
|
67
|
+
const server = requestIframeServer({ secretKey: 'test-key' });
|
|
68
|
+
expect(server.secretKey).toBe('test-key');
|
|
69
|
+
server.destroy();
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('should create server with custom ackTimeout', () => {
|
|
73
|
+
const server = requestIframeServer({ ackTimeout: 1000 });
|
|
74
|
+
expect(server).toBeDefined();
|
|
75
|
+
server.destroy();
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('should create server with autoOpen false', () => {
|
|
79
|
+
const server = requestIframeServer({ autoOpen: false });
|
|
80
|
+
expect(server.isOpen).toBe(false);
|
|
81
|
+
server.open();
|
|
82
|
+
expect(server.isOpen).toBe(true);
|
|
83
|
+
server.destroy();
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('should create server with custom versionValidator', () => {
|
|
87
|
+
const versionValidator = jest.fn(() => true);
|
|
88
|
+
const server = requestIframeServer({ versionValidator } as any);
|
|
89
|
+
expect(server).toBeDefined();
|
|
90
|
+
server.destroy();
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
describe('open and close', () => {
|
|
95
|
+
it('should open server', () => {
|
|
96
|
+
const server = requestIframeServer({ autoOpen: false });
|
|
97
|
+
expect(server.isOpen).toBe(false);
|
|
98
|
+
server.open();
|
|
99
|
+
expect(server.isOpen).toBe(true);
|
|
100
|
+
server.destroy();
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('should not open if already open', () => {
|
|
104
|
+
const server = requestIframeServer();
|
|
105
|
+
const originalOpen = server.open;
|
|
106
|
+
server.open = jest.fn();
|
|
107
|
+
server.open();
|
|
108
|
+
expect(server.open).toHaveBeenCalledTimes(1);
|
|
109
|
+
server.destroy();
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('should close server', () => {
|
|
113
|
+
const server = requestIframeServer();
|
|
114
|
+
expect(server.isOpen).toBe(true);
|
|
115
|
+
server.close();
|
|
116
|
+
expect(server.isOpen).toBe(false);
|
|
117
|
+
server.destroy();
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('should not close if already closed', () => {
|
|
121
|
+
const server = requestIframeServer();
|
|
122
|
+
server.close();
|
|
123
|
+
const originalClose = server.close;
|
|
124
|
+
server.close = jest.fn();
|
|
125
|
+
server.close();
|
|
126
|
+
expect(server.close).toHaveBeenCalledTimes(1);
|
|
127
|
+
server.destroy();
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
describe('on and off', () => {
|
|
132
|
+
it('should register handler', async () => {
|
|
133
|
+
const server = requestIframeServer();
|
|
134
|
+
const handler = jest.fn();
|
|
135
|
+
|
|
136
|
+
server.on('test', handler);
|
|
137
|
+
|
|
138
|
+
const origin = 'https://example.com';
|
|
139
|
+
const iframe = createTestIframe(origin);
|
|
140
|
+
const mockWindow = createMockWindow();
|
|
141
|
+
|
|
142
|
+
window.dispatchEvent(
|
|
143
|
+
new MessageEvent('message', {
|
|
144
|
+
data: {
|
|
145
|
+
__requestIframe__: 1,
|
|
146
|
+
timestamp: Date.now(),
|
|
147
|
+
type: 'request',
|
|
148
|
+
requestId: 'req123',
|
|
149
|
+
path: 'test',
|
|
150
|
+
body: { param: 'value' },
|
|
151
|
+
role: MessageRole.CLIENT
|
|
152
|
+
},
|
|
153
|
+
origin,
|
|
154
|
+
source: mockWindow
|
|
155
|
+
})
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
159
|
+
expect(handler).toHaveBeenCalled();
|
|
160
|
+
server.destroy();
|
|
161
|
+
cleanupIframe(iframe);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it('should unregister handler', async () => {
|
|
165
|
+
const server = requestIframeServer();
|
|
166
|
+
const handler = jest.fn();
|
|
167
|
+
|
|
168
|
+
server.on('test', handler);
|
|
169
|
+
server.off('test');
|
|
170
|
+
|
|
171
|
+
const origin = 'https://example.com';
|
|
172
|
+
const iframe = createTestIframe(origin);
|
|
173
|
+
const mockWindow = createMockWindow();
|
|
174
|
+
|
|
175
|
+
window.dispatchEvent(
|
|
176
|
+
new MessageEvent('message', {
|
|
177
|
+
data: {
|
|
178
|
+
__requestIframe__: 1,
|
|
179
|
+
timestamp: Date.now(),
|
|
180
|
+
type: 'request',
|
|
181
|
+
requestId: 'req123',
|
|
182
|
+
path: 'test',
|
|
183
|
+
role: MessageRole.CLIENT
|
|
184
|
+
},
|
|
185
|
+
origin,
|
|
186
|
+
source: mockWindow
|
|
187
|
+
})
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
191
|
+
|
|
192
|
+
expect(handler).not.toHaveBeenCalled();
|
|
193
|
+
server.destroy();
|
|
194
|
+
cleanupIframe(iframe);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it('should handle multiple handlers for same path', async () => {
|
|
198
|
+
const server = requestIframeServer();
|
|
199
|
+
const handler1 = jest.fn((req, res) => {
|
|
200
|
+
res.send({ result: 'handler1' });
|
|
201
|
+
});
|
|
202
|
+
const handler2 = jest.fn((req, res) => {
|
|
203
|
+
res.send({ result: 'handler2' });
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
server.on('test', handler1);
|
|
207
|
+
server.on('test', handler2);
|
|
208
|
+
|
|
209
|
+
// Only the last registered handler should be called
|
|
210
|
+
const origin = 'https://example.com';
|
|
211
|
+
const iframe = createTestIframe(origin);
|
|
212
|
+
const mockContentWindow = {
|
|
213
|
+
postMessage: jest.fn()
|
|
214
|
+
};
|
|
215
|
+
Object.defineProperty(iframe, 'contentWindow', {
|
|
216
|
+
value: mockContentWindow,
|
|
217
|
+
writable: true
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
const mockWindow = createMockWindow();
|
|
221
|
+
|
|
222
|
+
window.dispatchEvent(
|
|
223
|
+
new MessageEvent('message', {
|
|
224
|
+
data: {
|
|
225
|
+
__requestIframe__: 1,
|
|
226
|
+
timestamp: Date.now(),
|
|
227
|
+
type: 'request',
|
|
228
|
+
requestId: 'req123',
|
|
229
|
+
path: 'test',
|
|
230
|
+
role: MessageRole.CLIENT
|
|
231
|
+
},
|
|
232
|
+
origin,
|
|
233
|
+
source: mockWindow
|
|
234
|
+
})
|
|
235
|
+
);
|
|
236
|
+
|
|
237
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
238
|
+
expect(handler1).not.toHaveBeenCalled();
|
|
239
|
+
expect(handler2).toHaveBeenCalled();
|
|
240
|
+
server.destroy();
|
|
241
|
+
cleanupIframe(iframe);
|
|
242
|
+
});
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
describe('map', () => {
|
|
246
|
+
it('should register multiple handlers at once', () => {
|
|
247
|
+
const server = requestIframeServer();
|
|
248
|
+
const handler1 = jest.fn((req, res) => res.send({ path: '1' }));
|
|
249
|
+
const handler2 = jest.fn((req, res) => res.send({ path: '2' }));
|
|
250
|
+
const handler3 = jest.fn((req, res) => res.send({ path: '3' }));
|
|
251
|
+
|
|
252
|
+
server.map({
|
|
253
|
+
path1: handler1,
|
|
254
|
+
path2: handler2,
|
|
255
|
+
path3: handler3
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
expect(server).toBeDefined();
|
|
259
|
+
server.destroy();
|
|
260
|
+
});
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
describe('use (middleware)', () => {
|
|
264
|
+
it('should register global middleware', async () => {
|
|
265
|
+
const server = requestIframeServer();
|
|
266
|
+
const middleware = jest.fn((req, res, next) => next());
|
|
267
|
+
|
|
268
|
+
server.use(middleware);
|
|
269
|
+
|
|
270
|
+
server.on('test', (req, res) => {
|
|
271
|
+
res.send({ result: 'success' });
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
const origin = 'https://example.com';
|
|
275
|
+
const iframe = createTestIframe(origin);
|
|
276
|
+
const mockWindow = createMockWindow();
|
|
277
|
+
|
|
278
|
+
window.dispatchEvent(
|
|
279
|
+
new MessageEvent('message', {
|
|
280
|
+
data: {
|
|
281
|
+
__requestIframe__: 1,
|
|
282
|
+
timestamp: Date.now(),
|
|
283
|
+
type: 'request',
|
|
284
|
+
requestId: 'req123',
|
|
285
|
+
path: 'test',
|
|
286
|
+
role: MessageRole.CLIENT
|
|
287
|
+
},
|
|
288
|
+
origin,
|
|
289
|
+
source: mockWindow
|
|
290
|
+
})
|
|
291
|
+
);
|
|
292
|
+
|
|
293
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
294
|
+
expect(middleware).toHaveBeenCalled();
|
|
295
|
+
server.destroy();
|
|
296
|
+
cleanupIframe(iframe);
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
it('should register path-specific middleware', async () => {
|
|
300
|
+
const server = requestIframeServer();
|
|
301
|
+
const middleware = jest.fn((req, res, next) => next());
|
|
302
|
+
|
|
303
|
+
server.use('/api/*', middleware);
|
|
304
|
+
|
|
305
|
+
server.on('/api/test', (req, res) => {
|
|
306
|
+
res.send({ result: 'success' });
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
const origin = 'https://example.com';
|
|
310
|
+
const iframe = createTestIframe(origin);
|
|
311
|
+
const mockWindow = createMockWindow();
|
|
312
|
+
|
|
313
|
+
window.dispatchEvent(
|
|
314
|
+
new MessageEvent('message', {
|
|
315
|
+
data: {
|
|
316
|
+
__requestIframe__: 1,
|
|
317
|
+
timestamp: Date.now(),
|
|
318
|
+
type: 'request',
|
|
319
|
+
requestId: 'req123',
|
|
320
|
+
path: '/api/test',
|
|
321
|
+
role: MessageRole.CLIENT
|
|
322
|
+
},
|
|
323
|
+
origin,
|
|
324
|
+
source: mockWindow
|
|
325
|
+
})
|
|
326
|
+
);
|
|
327
|
+
|
|
328
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
329
|
+
expect(middleware).toHaveBeenCalled();
|
|
330
|
+
server.destroy();
|
|
331
|
+
cleanupIframe(iframe);
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
it('should execute middleware in order', async () => {
|
|
335
|
+
const server = requestIframeServer();
|
|
336
|
+
const order: number[] = [];
|
|
337
|
+
|
|
338
|
+
server.use((req, res, next) => {
|
|
339
|
+
order.push(1);
|
|
340
|
+
next();
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
server.use((req, res, next) => {
|
|
344
|
+
order.push(2);
|
|
345
|
+
next();
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
server.on('test', (req, res) => {
|
|
349
|
+
order.push(3);
|
|
350
|
+
res.send({ result: 'success' });
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
const origin = 'https://example.com';
|
|
354
|
+
const iframe = createTestIframe(origin);
|
|
355
|
+
const mockWindow = createMockWindow();
|
|
356
|
+
|
|
357
|
+
window.dispatchEvent(
|
|
358
|
+
new MessageEvent('message', {
|
|
359
|
+
data: {
|
|
360
|
+
__requestIframe__: 1,
|
|
361
|
+
timestamp: Date.now(),
|
|
362
|
+
type: 'request',
|
|
363
|
+
requestId: 'req123',
|
|
364
|
+
path: 'test',
|
|
365
|
+
role: MessageRole.CLIENT
|
|
366
|
+
},
|
|
367
|
+
origin,
|
|
368
|
+
source: mockWindow
|
|
369
|
+
})
|
|
370
|
+
);
|
|
371
|
+
|
|
372
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
373
|
+
expect(order).toEqual([1, 2, 3]);
|
|
374
|
+
server.destroy();
|
|
375
|
+
cleanupIframe(iframe);
|
|
376
|
+
});
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
describe('error handling', () => {
|
|
380
|
+
it('should send METHOD_NOT_FOUND error when handler not found', async () => {
|
|
381
|
+
const server = requestIframeServer();
|
|
382
|
+
const origin = 'https://example.com';
|
|
383
|
+
const iframe = createTestIframe(origin);
|
|
384
|
+
const mockContentWindow = {
|
|
385
|
+
postMessage: jest.fn()
|
|
386
|
+
};
|
|
387
|
+
Object.defineProperty(iframe, 'contentWindow', {
|
|
388
|
+
value: mockContentWindow,
|
|
389
|
+
writable: true
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
window.dispatchEvent(
|
|
393
|
+
new MessageEvent('message', {
|
|
394
|
+
data: {
|
|
395
|
+
__requestIframe__: 1,
|
|
396
|
+
timestamp: Date.now(),
|
|
397
|
+
type: 'request',
|
|
398
|
+
requestId: 'req123',
|
|
399
|
+
path: 'nonexistent',
|
|
400
|
+
role: MessageRole.CLIENT
|
|
401
|
+
},
|
|
402
|
+
origin,
|
|
403
|
+
source: mockContentWindow as any
|
|
404
|
+
})
|
|
405
|
+
);
|
|
406
|
+
|
|
407
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
408
|
+
|
|
409
|
+
expect(mockContentWindow.postMessage).toHaveBeenCalledWith(
|
|
410
|
+
expect.objectContaining({
|
|
411
|
+
type: 'error',
|
|
412
|
+
requestId: 'req123',
|
|
413
|
+
error: expect.objectContaining({
|
|
414
|
+
message: Messages.METHOD_NOT_FOUND,
|
|
415
|
+
code: ErrorCode.METHOD_NOT_FOUND
|
|
416
|
+
}),
|
|
417
|
+
status: HttpStatus.NOT_FOUND
|
|
418
|
+
}),
|
|
419
|
+
origin
|
|
420
|
+
);
|
|
421
|
+
|
|
422
|
+
server.destroy();
|
|
423
|
+
cleanupIframe(iframe);
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
it('should handle handler errors', async () => {
|
|
427
|
+
const server = requestIframeServer();
|
|
428
|
+
const origin = 'https://example.com';
|
|
429
|
+
const iframe = createTestIframe(origin);
|
|
430
|
+
const mockContentWindow = {
|
|
431
|
+
postMessage: jest.fn()
|
|
432
|
+
};
|
|
433
|
+
Object.defineProperty(iframe, 'contentWindow', {
|
|
434
|
+
value: mockContentWindow,
|
|
435
|
+
writable: true
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
server.on('test', () => {
|
|
439
|
+
throw new Error('Handler error');
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
window.dispatchEvent(
|
|
443
|
+
new MessageEvent('message', {
|
|
444
|
+
data: {
|
|
445
|
+
__requestIframe__: 1,
|
|
446
|
+
timestamp: Date.now(),
|
|
447
|
+
type: 'request',
|
|
448
|
+
requestId: 'req123',
|
|
449
|
+
path: 'test',
|
|
450
|
+
role: MessageRole.CLIENT,
|
|
451
|
+
targetId: server.id
|
|
452
|
+
},
|
|
453
|
+
origin,
|
|
454
|
+
source: mockContentWindow as any
|
|
455
|
+
})
|
|
456
|
+
);
|
|
457
|
+
|
|
458
|
+
await new Promise(resolve => setTimeout(resolve, 150));
|
|
459
|
+
|
|
460
|
+
expect(mockContentWindow.postMessage).toHaveBeenCalledWith(
|
|
461
|
+
expect.objectContaining({
|
|
462
|
+
type: 'error',
|
|
463
|
+
requestId: 'req123',
|
|
464
|
+
status: HttpStatus.INTERNAL_SERVER_ERROR
|
|
465
|
+
}),
|
|
466
|
+
origin
|
|
467
|
+
);
|
|
468
|
+
|
|
469
|
+
server.destroy();
|
|
470
|
+
cleanupIframe(iframe);
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
it('should handle async handler errors', async () => {
|
|
474
|
+
const server = requestIframeServer();
|
|
475
|
+
const origin = 'https://example.com';
|
|
476
|
+
const iframe = createTestIframe(origin);
|
|
477
|
+
const mockContentWindow = {
|
|
478
|
+
postMessage: jest.fn()
|
|
479
|
+
};
|
|
480
|
+
Object.defineProperty(iframe, 'contentWindow', {
|
|
481
|
+
value: mockContentWindow,
|
|
482
|
+
writable: true
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
server.on('test', async () => {
|
|
486
|
+
throw new Error('Async handler error');
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
window.dispatchEvent(
|
|
490
|
+
new MessageEvent('message', {
|
|
491
|
+
data: {
|
|
492
|
+
__requestIframe__: 1,
|
|
493
|
+
timestamp: Date.now(),
|
|
494
|
+
type: 'request',
|
|
495
|
+
requestId: 'req123',
|
|
496
|
+
path: 'test',
|
|
497
|
+
role: MessageRole.CLIENT,
|
|
498
|
+
targetId: server.id
|
|
499
|
+
},
|
|
500
|
+
origin,
|
|
501
|
+
source: mockContentWindow as any
|
|
502
|
+
})
|
|
503
|
+
);
|
|
504
|
+
|
|
505
|
+
await new Promise(resolve => setTimeout(resolve, 200));
|
|
506
|
+
|
|
507
|
+
expect(mockContentWindow.postMessage).toHaveBeenCalledWith(
|
|
508
|
+
expect.objectContaining({
|
|
509
|
+
type: 'error',
|
|
510
|
+
requestId: 'req123',
|
|
511
|
+
status: HttpStatus.INTERNAL_SERVER_ERROR
|
|
512
|
+
}),
|
|
513
|
+
origin
|
|
514
|
+
);
|
|
515
|
+
|
|
516
|
+
server.destroy();
|
|
517
|
+
cleanupIframe(iframe);
|
|
518
|
+
});
|
|
519
|
+
});
|
|
520
|
+
|
|
521
|
+
describe('async handlers', () => {
|
|
522
|
+
it('should send ASYNC notification for async handlers', async () => {
|
|
523
|
+
const server = requestIframeServer();
|
|
524
|
+
const origin = 'https://example.com';
|
|
525
|
+
const iframe = createTestIframe(origin);
|
|
526
|
+
const mockContentWindow = {
|
|
527
|
+
postMessage: jest.fn()
|
|
528
|
+
};
|
|
529
|
+
Object.defineProperty(iframe, 'contentWindow', {
|
|
530
|
+
value: mockContentWindow,
|
|
531
|
+
writable: true
|
|
532
|
+
});
|
|
533
|
+
|
|
534
|
+
server.on('test', async (req, res) => {
|
|
535
|
+
await new Promise(resolve => setTimeout(resolve, 10));
|
|
536
|
+
res.send({ result: 'success' });
|
|
537
|
+
});
|
|
538
|
+
|
|
539
|
+
window.dispatchEvent(
|
|
540
|
+
new MessageEvent('message', {
|
|
541
|
+
data: {
|
|
542
|
+
__requestIframe__: 1,
|
|
543
|
+
timestamp: Date.now(),
|
|
544
|
+
type: 'request',
|
|
545
|
+
requestId: 'req123',
|
|
546
|
+
path: 'test',
|
|
547
|
+
role: MessageRole.CLIENT,
|
|
548
|
+
targetId: server.id
|
|
549
|
+
},
|
|
550
|
+
origin,
|
|
551
|
+
source: mockContentWindow as any
|
|
552
|
+
})
|
|
553
|
+
);
|
|
554
|
+
|
|
555
|
+
await new Promise(resolve => setTimeout(resolve, 150));
|
|
556
|
+
|
|
557
|
+
expect(mockContentWindow.postMessage).toHaveBeenCalledWith(
|
|
558
|
+
expect.objectContaining({
|
|
559
|
+
type: 'async',
|
|
560
|
+
requestId: 'req123'
|
|
561
|
+
}),
|
|
562
|
+
origin
|
|
563
|
+
);
|
|
564
|
+
|
|
565
|
+
server.destroy();
|
|
566
|
+
cleanupIframe(iframe);
|
|
567
|
+
});
|
|
568
|
+
|
|
569
|
+
it('should handle async handler without response', async () => {
|
|
570
|
+
const server = requestIframeServer();
|
|
571
|
+
const origin = 'https://example.com';
|
|
572
|
+
const iframe = createTestIframe(origin);
|
|
573
|
+
const mockContentWindow = {
|
|
574
|
+
postMessage: jest.fn()
|
|
575
|
+
};
|
|
576
|
+
Object.defineProperty(iframe, 'contentWindow', {
|
|
577
|
+
value: mockContentWindow,
|
|
578
|
+
writable: true
|
|
579
|
+
});
|
|
580
|
+
|
|
581
|
+
server.on('test', async () => {
|
|
582
|
+
// No response sent
|
|
583
|
+
});
|
|
584
|
+
|
|
585
|
+
window.dispatchEvent(
|
|
586
|
+
new MessageEvent('message', {
|
|
587
|
+
data: {
|
|
588
|
+
__requestIframe__: 1,
|
|
589
|
+
timestamp: Date.now(),
|
|
590
|
+
type: 'request',
|
|
591
|
+
requestId: 'req123',
|
|
592
|
+
path: 'test',
|
|
593
|
+
role: MessageRole.CLIENT,
|
|
594
|
+
targetId: server.id
|
|
595
|
+
},
|
|
596
|
+
origin,
|
|
597
|
+
source: mockContentWindow as any
|
|
598
|
+
})
|
|
599
|
+
);
|
|
600
|
+
|
|
601
|
+
await new Promise(resolve => setTimeout(resolve, 200));
|
|
602
|
+
|
|
603
|
+
expect(mockContentWindow.postMessage).toHaveBeenCalledWith(
|
|
604
|
+
expect.objectContaining({
|
|
605
|
+
type: 'error',
|
|
606
|
+
requestId: 'req123'
|
|
607
|
+
}),
|
|
608
|
+
origin
|
|
609
|
+
);
|
|
610
|
+
|
|
611
|
+
server.destroy();
|
|
612
|
+
cleanupIframe(iframe);
|
|
613
|
+
});
|
|
614
|
+
});
|
|
615
|
+
|
|
616
|
+
describe('ping/pong', () => {
|
|
617
|
+
it('should handle ping messages', async () => {
|
|
618
|
+
const server = requestIframeServer();
|
|
619
|
+
const origin = 'https://example.com';
|
|
620
|
+
const iframe = createTestIframe(origin);
|
|
621
|
+
const mockContentWindow = {
|
|
622
|
+
postMessage: jest.fn()
|
|
623
|
+
};
|
|
624
|
+
Object.defineProperty(iframe, 'contentWindow', {
|
|
625
|
+
value: mockContentWindow,
|
|
626
|
+
writable: true
|
|
627
|
+
});
|
|
628
|
+
|
|
629
|
+
window.dispatchEvent(
|
|
630
|
+
new MessageEvent('message', {
|
|
631
|
+
data: {
|
|
632
|
+
__requestIframe__: 1,
|
|
633
|
+
timestamp: Date.now(),
|
|
634
|
+
type: 'ping',
|
|
635
|
+
requestId: 'req123',
|
|
636
|
+
role: MessageRole.CLIENT
|
|
637
|
+
},
|
|
638
|
+
origin,
|
|
639
|
+
source: mockContentWindow as any
|
|
640
|
+
})
|
|
641
|
+
);
|
|
642
|
+
|
|
643
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
644
|
+
|
|
645
|
+
expect(mockContentWindow.postMessage).toHaveBeenCalledWith(
|
|
646
|
+
expect.objectContaining({
|
|
647
|
+
type: 'pong',
|
|
648
|
+
requestId: 'req123'
|
|
649
|
+
}),
|
|
650
|
+
origin
|
|
651
|
+
);
|
|
652
|
+
|
|
653
|
+
server.destroy();
|
|
654
|
+
cleanupIframe(iframe);
|
|
655
|
+
});
|
|
656
|
+
});
|
|
657
|
+
|
|
658
|
+
describe('received acknowledgment', () => {
|
|
659
|
+
it('should handle received acknowledgment', async () => {
|
|
660
|
+
const server = requestIframeServer();
|
|
661
|
+
const origin = 'https://example.com';
|
|
662
|
+
const iframe = createTestIframe(origin);
|
|
663
|
+
const mockWindow = createMockWindow();
|
|
664
|
+
|
|
665
|
+
let ackResolve: (value: boolean) => void;
|
|
666
|
+
const ackPromise = new Promise<boolean>(resolve => {
|
|
667
|
+
ackResolve = resolve;
|
|
668
|
+
});
|
|
669
|
+
|
|
670
|
+
server.on('test', (req, res) => {
|
|
671
|
+
res.send({ result: 'success' }, { requireAck: true }).then(ackResolve);
|
|
672
|
+
});
|
|
673
|
+
|
|
674
|
+
window.dispatchEvent(
|
|
675
|
+
new MessageEvent('message', {
|
|
676
|
+
data: {
|
|
677
|
+
__requestIframe__: 1,
|
|
678
|
+
timestamp: Date.now(),
|
|
679
|
+
type: 'request',
|
|
680
|
+
requestId: 'req123',
|
|
681
|
+
path: 'test',
|
|
682
|
+
role: MessageRole.CLIENT,
|
|
683
|
+
targetId: server.id
|
|
684
|
+
},
|
|
685
|
+
origin,
|
|
686
|
+
source: mockWindow
|
|
687
|
+
})
|
|
688
|
+
);
|
|
689
|
+
|
|
690
|
+
await new Promise(resolve => setTimeout(resolve, 150));
|
|
691
|
+
|
|
692
|
+
// Send received acknowledgment
|
|
693
|
+
window.dispatchEvent(
|
|
694
|
+
new MessageEvent('message', {
|
|
695
|
+
data: {
|
|
696
|
+
__requestIframe__: 1,
|
|
697
|
+
timestamp: Date.now(),
|
|
698
|
+
type: 'received',
|
|
699
|
+
requestId: 'req123',
|
|
700
|
+
role: MessageRole.CLIENT
|
|
701
|
+
},
|
|
702
|
+
origin,
|
|
703
|
+
source: mockWindow
|
|
704
|
+
})
|
|
705
|
+
);
|
|
706
|
+
|
|
707
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
708
|
+
|
|
709
|
+
const received = await Promise.race([
|
|
710
|
+
ackPromise,
|
|
711
|
+
new Promise<boolean>(resolve => setTimeout(() => resolve(false), 2000))
|
|
712
|
+
]);
|
|
713
|
+
expect(received).toBe(true);
|
|
714
|
+
|
|
715
|
+
server.destroy();
|
|
716
|
+
cleanupIframe(iframe);
|
|
717
|
+
}, 10000);
|
|
718
|
+
});
|
|
719
|
+
|
|
720
|
+
describe('destroy', () => {
|
|
721
|
+
it('should destroy server and clean up', () => {
|
|
722
|
+
const server = requestIframeServer();
|
|
723
|
+
server.on('test', (req, res) => res.send({}));
|
|
724
|
+
|
|
725
|
+
expect(server.isOpen).toBe(true);
|
|
726
|
+
server.destroy();
|
|
727
|
+
expect(server.isOpen).toBe(false);
|
|
728
|
+
});
|
|
729
|
+
});
|
|
730
|
+
|
|
731
|
+
describe('messageDispatcher access', () => {
|
|
732
|
+
it('should provide access to messageDispatcher', () => {
|
|
733
|
+
const server = requestIframeServer();
|
|
734
|
+
expect((server as any).messageDispatcher).toBeDefined();
|
|
735
|
+
server.destroy();
|
|
736
|
+
});
|
|
737
|
+
});
|
|
738
|
+
});
|