1+ import { TestUtils } from '@deephaven/utils' ;
12import {
23 makeMessage ,
34 makeResponse ,
@@ -7,76 +8,113 @@ import {
78
89it ( 'Throws an exception if called on a window without parent' , async ( ) => {
910 await expect ( requestParentResponse ( 'request' ) ) . rejects . toThrow (
10- 'window.opener is null, unable to send request.'
11+ 'window parent is null, unable to send request.'
1112 ) ;
1213} ) ;
1314
14- describe ( 'requestParentResponse' , ( ) => {
15- let addListenerSpy : jest . SpyInstance ;
16- let removeListenerSpy : jest . SpyInstance ;
17- let listenerCallback ;
18- let messageId ;
19- const mockPostMessage = jest . fn ( ( data : Message < unknown > ) => {
20- messageId = data . id ;
21- } ) ;
15+ /**
16+ * Set up the mock for window.parent or window.opener, and return a cleanup function.
17+ * @param type Whether to mock window.parent or window.opener
18+ * @param mockPostMessage The mock postMessage function to use
19+ * @returns Cleanup function
20+ */
21+ function setupWindowParentMock (
22+ type : string ,
23+ mockPostMessage : jest . Mock
24+ ) : ( ) => void {
25+ if ( type !== 'parent' && type !== 'opener' ) {
26+ throw new Error ( `Invalid type ${ type } ` ) ;
27+ }
28+ if ( type === 'parent' ) {
29+ const windowParentSpy = jest . spyOn ( window , 'parent' , 'get' ) . mockReturnValue (
30+ TestUtils . createMockProxy < Window > ( {
31+ postMessage : mockPostMessage ,
32+ } )
33+ ) ;
34+ return ( ) => {
35+ windowParentSpy . mockRestore ( ) ;
36+ } ;
37+ }
38+
2239 const originalWindowOpener = window . opener ;
23- beforeEach ( ( ) => {
24- addListenerSpy = jest
25- . spyOn ( window , 'addEventListener' )
26- . mockImplementation ( ( event , cb ) => {
27- listenerCallback = cb ;
28- } ) ;
29- removeListenerSpy = jest . spyOn ( window , 'removeEventListener' ) ;
30- window . opener = { postMessage : mockPostMessage } ;
31- } ) ;
32- afterEach ( ( ) => {
33- addListenerSpy . mockRestore ( ) ;
34- removeListenerSpy . mockRestore ( ) ;
35- mockPostMessage . mockClear ( ) ;
40+ window . opener = { postMessage : mockPostMessage } ;
41+ return ( ) => {
3642 window . opener = originalWindowOpener ;
37- messageId = undefined ;
38- } ) ;
43+ } ;
44+ }
3945
40- it ( 'Posts message to parent and subscribes to response' , async ( ) => {
41- requestParentResponse ( 'request' ) ;
42- expect ( mockPostMessage ) . toHaveBeenCalledWith (
43- expect . objectContaining ( makeMessage ( 'request' , messageId ) ) ,
44- '*'
45- ) ;
46- expect ( addListenerSpy ) . toHaveBeenCalledWith (
47- 'message' ,
48- expect . any ( Function )
49- ) ;
50- } ) ;
46+ describe . each ( [ [ 'parent' ] , [ 'opener' ] ] ) (
47+ `requestParentResponse with %s` ,
48+ type => {
49+ let parentCleanup : ( ) => void ;
50+ let addListenerSpy : jest . SpyInstance ;
51+ let removeListenerSpy : jest . SpyInstance ;
52+ let listenerCallback ;
53+ let messageId ;
54+ const mockPostMessage = jest . fn ( ( data : Message < unknown > ) => {
55+ messageId = data . id ;
56+ } ) ;
57+ beforeEach ( ( ) => {
58+ addListenerSpy = jest
59+ . spyOn ( window , 'addEventListener' )
60+ . mockImplementation ( ( event , cb ) => {
61+ listenerCallback = cb ;
62+ } ) ;
63+ removeListenerSpy = jest . spyOn ( window , 'removeEventListener' ) ;
64+ parentCleanup = setupWindowParentMock ( type , mockPostMessage ) ;
65+ } ) ;
66+ afterEach ( ( ) => {
67+ addListenerSpy . mockRestore ( ) ;
68+ removeListenerSpy . mockRestore ( ) ;
69+ mockPostMessage . mockClear ( ) ;
70+ parentCleanup ( ) ;
71+ messageId = undefined ;
72+ } ) ;
5173
52- it ( 'Resolves with the payload from the parent window response and unsubscribes' , async ( ) => {
53- const PAYLOAD = 'PAYLOAD' ;
54- const promise = requestParentResponse ( 'request' ) ;
55- listenerCallback ( {
56- data : makeResponse ( messageId , PAYLOAD ) ,
74+ it ( 'Posts message to parent and subscribes to response' , async ( ) => {
75+ requestParentResponse ( 'request' ) ;
76+ expect ( mockPostMessage ) . toHaveBeenCalledWith (
77+ expect . objectContaining ( makeMessage ( 'request' , messageId ) ) ,
78+ '*'
79+ ) ;
80+ expect ( addListenerSpy ) . toHaveBeenCalledWith (
81+ 'message' ,
82+ expect . any ( Function )
83+ ) ;
5784 } ) ;
58- const result = await promise ;
59- expect ( result ) . toBe ( PAYLOAD ) ;
60- expect ( removeListenerSpy ) . toHaveBeenCalledWith ( 'message' , listenerCallback ) ;
61- } ) ;
6285
63- it ( 'Ignores unrelated response, rejects on timeout' , async ( ) => {
64- jest . useFakeTimers ( ) ;
65- const promise = requestParentResponse ( 'request' ) ;
66- listenerCallback ( {
67- data : makeMessage ( 'wrong-id' ) ,
86+ it ( 'Resolves with the payload from the parent window response and unsubscribes' , async ( ) => {
87+ const PAYLOAD = 'PAYLOAD' ;
88+ const promise = requestParentResponse ( 'request' ) ;
89+ listenerCallback ( {
90+ data : makeResponse ( messageId , PAYLOAD ) ,
91+ } ) ;
92+ const result = await promise ;
93+ expect ( result ) . toBe ( PAYLOAD ) ;
94+ expect ( removeListenerSpy ) . toHaveBeenCalledWith (
95+ 'message' ,
96+ listenerCallback
97+ ) ;
6898 } ) ;
69- jest . runOnlyPendingTimers ( ) ;
70- await expect ( promise ) . rejects . toThrow ( 'Request timed out' ) ;
71- jest . useRealTimers ( ) ;
72- } ) ;
7399
74- it ( 'Times out if no response' , async ( ) => {
75- jest . useFakeTimers ( ) ;
76- const promise = requestParentResponse ( 'request' ) ;
77- jest . runOnlyPendingTimers ( ) ;
78- expect ( removeListenerSpy ) . toHaveBeenCalled ( ) ;
79- await expect ( promise ) . rejects . toThrow ( 'Request timed out' ) ;
80- jest . useRealTimers ( ) ;
81- } ) ;
82- } ) ;
100+ it ( 'Ignores unrelated response, rejects on timeout' , async ( ) => {
101+ jest . useFakeTimers ( ) ;
102+ const promise = requestParentResponse ( 'request' ) ;
103+ listenerCallback ( {
104+ data : makeMessage ( 'wrong-id' ) ,
105+ } ) ;
106+ jest . runOnlyPendingTimers ( ) ;
107+ await expect ( promise ) . rejects . toThrow ( 'Request timed out' ) ;
108+ jest . useRealTimers ( ) ;
109+ } ) ;
110+
111+ it ( 'Times out if no response' , async ( ) => {
112+ jest . useFakeTimers ( ) ;
113+ const promise = requestParentResponse ( 'request' ) ;
114+ jest . runOnlyPendingTimers ( ) ;
115+ expect ( removeListenerSpy ) . toHaveBeenCalled ( ) ;
116+ await expect ( promise ) . rejects . toThrow ( 'Request timed out' ) ;
117+ jest . useRealTimers ( ) ;
118+ } ) ;
119+ }
120+ ) ;
0 commit comments