99 */
1010
1111import type { JsonPagesListResponse } from '../inspector-proxy/types' ;
12- import type { DevToolLauncher } from '../types/DevToolLauncher' ;
12+ import type {
13+ DebuggerShellPreparationResult ,
14+ DevToolLauncher ,
15+ } from '../types/DevToolLauncher' ;
1316
1417import { fetchJson , requestLocal } from './FetchUtils' ;
1518import { createDeviceMock } from './InspectorDeviceUtils' ;
@@ -21,112 +24,212 @@ const PAGES_POLLING_DELAY = 2100;
2124
2225jest . useFakeTimers ( ) ;
2326
24- describe ( 'enableStandaloneFuseboxShell experiment' , ( ) => {
27+ async function setupDevice (
28+ serverRef : { + serverBaseWsUrl : string , ...} ,
29+ signal : AbortSignal ,
30+ ) {
31+ const device = await createDeviceMock (
32+ `${ serverRef . serverBaseWsUrl } /inspector/device?device=device1&name=foo&app=bar` ,
33+ signal ,
34+ ) ;
35+ device . getPages . mockImplementation ( ( ) => [
36+ {
37+ app : 'bar-app' ,
38+ id : 'page1' ,
39+ title : 'bar-title' ,
40+ vm : 'bar-vm' ,
41+ capabilities : {
42+ nativePageReloads : true ,
43+ } ,
44+ } ,
45+ ] ) ;
46+ jest . advanceTimersByTime ( PAGES_POLLING_DELAY ) ;
47+
48+ return device ;
49+ }
50+
51+ function setupToolLauncherWithFuseboxShell (
52+ prepareDebuggerShell : ( ) = > Promise < DebuggerShellPreparationResult > ,
53+ ) {
2554 const ToolLauncherWithFuseboxShell : DevToolLauncher = {
2655 launchDebuggerAppWindow : async ( url : string ) => { } ,
2756 launchDebuggerShell : ( ) => {
2857 throw new Error ( 'Not implemented' ) ;
2958 } ,
30- prepareDebuggerShell : async ( ) => {
31- return { code : 'not_implemented' } ;
32- } ,
59+ prepareDebuggerShell,
3360 } ;
34- const serverRef = withServerForEachTest ( {
35- logger : undefined ,
36- unstable_toolLauncher : ToolLauncherWithFuseboxShell ,
37- unstable_experiments : {
38- enableStandaloneFuseboxShell : true ,
39- } ,
40- } ) ;
61+
62+ const prepareDebuggerShellSpy = jest . spyOn (
63+ ToolLauncherWithFuseboxShell ,
64+ 'prepareDebuggerShell' ,
65+ ) ;
66+
67+ return {
68+ ToolLauncherWithFuseboxShell,
69+ prepareDebuggerShellSpy,
70+ } ;
71+ }
72+
73+ describe ( 'enableStandaloneFuseboxShell experiment' , ( ) => {
4174 const autoCleanup = withAbortSignalForEachTest ( ) ;
75+
4276 afterEach ( ( ) => {
4377 jest . clearAllMocks ( ) ;
4478 } ) ;
4579
4680 describe ( '/open-debugger endpoint' , ( ) => {
47- test ( 'launches the shell with a frontend URL and stable window key' , async ( ) => {
48- // Connect a device to use when opening the debugger
49- const device = await createDeviceMock (
50- `${ serverRef . serverBaseWsUrl } /inspector/device?device=device1&name=foo&app=bar` ,
51- autoCleanup . signal ,
52- ) ;
53- device . getPages . mockImplementation ( ( ) => [
54- {
55- app : 'bar-app' ,
56- id : 'page1' ,
57- title : 'bar-title' ,
58- vm : 'bar-vm' ,
59- capabilities : {
60- // Ensure the device target can be found when launching the debugger
61- nativePageReloads : true ,
62- } ,
63- } ,
64- ] ) ;
65- jest . advanceTimersByTime ( PAGES_POLLING_DELAY ) ;
66-
67- const launchDebuggerAppWindowSpy = jest
68- . spyOn ( ToolLauncherWithFuseboxShell , 'launchDebuggerAppWindow' )
69- . mockResolvedValue ( ) ;
70- const showFuseboxShellSpy = jest
71- . spyOn ( ToolLauncherWithFuseboxShell , 'launchDebuggerShell' )
72- . mockResolvedValue ( ) ;
73-
74- try {
75- // Fetch the target information for the device
76- const pageListResponse = await fetchJson < JsonPagesListResponse > (
77- `${ serverRef . serverBaseUrl } /json` ,
78- ) ;
79- // Select the first target from the page list response
80- expect ( pageListResponse . length ) . toBeGreaterThanOrEqual ( 1 ) ;
81- const firstPage = pageListResponse [ 0 ] ;
82-
83- // Build the URL for the debugger
84- const openUrl = new URL ( '/open-debugger' , serverRef . serverBaseUrl ) ;
85- openUrl . searchParams . set ( 'launchId' , 'launch1' ) ;
86- openUrl . searchParams . set (
87- 'device' ,
88- firstPage . reactNative . logicalDeviceId ,
81+ describe ( 'success' , ( ) => {
82+ const { ToolLauncherWithFuseboxShell, prepareDebuggerShellSpy} =
83+ setupToolLauncherWithFuseboxShell ( ( ) =>
84+ Promise . resolve ( { code : 'success' } ) ,
8985 ) ;
90- openUrl . searchParams . set ( 'target' , firstPage . id ) ;
91- // Request to open the debugger for the first device
92- {
86+ const server = withServerForEachTest ( {
87+ logger : undefined ,
88+ unstable_toolLauncher : ToolLauncherWithFuseboxShell ,
89+ unstable_experiments : {
90+ enableStandaloneFuseboxShell : true ,
91+ } ,
92+ } ) ;
93+
94+ test ( 'launches the shell with a frontend URL and stable window key' , async ( ) => {
95+ // Connect a device to use when opening the debugger
96+ const device = await setupDevice ( server , autoCleanup . signal ) ;
97+
98+ const launchDebuggerAppWindowSpy = jest
99+ . spyOn ( ToolLauncherWithFuseboxShell , 'launchDebuggerAppWindow' )
100+ . mockResolvedValue ( ) ;
101+ const showFuseboxShellSpy = jest
102+ . spyOn ( ToolLauncherWithFuseboxShell , 'launchDebuggerShell' )
103+ . mockResolvedValue ( ) ;
104+
105+ try {
106+ // Fetch the target information for the device
107+ const pageListResponse = await fetchJson < JsonPagesListResponse > (
108+ `${ server . serverBaseUrl } /json` ,
109+ ) ;
110+ // Select the first target from the page list response
111+ expect ( pageListResponse . length ) . toBeGreaterThanOrEqual ( 1 ) ;
112+ const firstPage = pageListResponse [ 0 ] ;
113+
114+ // Build the URL for the debugger
115+ const openUrl = new URL ( '/open-debugger' , server . serverBaseUrl ) ;
116+ openUrl . searchParams . set ( 'launchId' , 'launch1' ) ;
117+ openUrl . searchParams . set (
118+ 'device' ,
119+ firstPage . reactNative . logicalDeviceId ,
120+ ) ;
121+ openUrl . searchParams . set ( 'target' , firstPage . id ) ;
122+ // Request to open the debugger for the first device
93123 const response = await requestLocal ( openUrl . toString ( ) , {
94124 method : 'POST' ,
95125 } ) ;
126+
96127 // Ensure the request was handled properly
97128 expect ( response . statusCode ) . toBe ( 200 ) ;
129+
130+ // Debugger was launched but will fail to prepare standalone shell
131+ expect ( prepareDebuggerShellSpy ) . toHaveBeenCalled ( ) ;
132+
133+ // Ensure the debugger was launched using the standalone shell API
134+ expect ( showFuseboxShellSpy ) . toHaveBeenCalledWith (
135+ expect . any ( String ) ,
136+ expect . any ( String ) ,
137+ ) ;
138+
139+ // No call to the regular browser launcher since standalone shell should be used
140+ expect ( launchDebuggerAppWindowSpy ) . not . toHaveBeenCalled ( ) ;
141+
142+ const firstWindowKey = showFuseboxShellSpy . mock . calls [ 0 ] [ 1 ] ;
143+
144+ showFuseboxShellSpy . mockClear ( ) ;
145+ openUrl . searchParams . set ( 'launchId' , 'launch2' ) ;
146+
147+ const anotherResponse = await requestLocal ( openUrl . toString ( ) , {
148+ method : 'POST' ,
149+ } ) ;
150+
151+ // Ensure the request was handled properly
152+ expect ( anotherResponse . statusCode ) . toBe ( 200 ) ;
153+
154+ // Ensure the debugger was launched using the standalone shell API and the same window key
155+ expect ( showFuseboxShellSpy ) . toHaveBeenCalledWith (
156+ expect . any ( String ) ,
157+ firstWindowKey ,
158+ ) ;
159+
160+ // Ensure the debugger preparation function was called, just one time, during middleware initialization
161+ expect ( showFuseboxShellSpy ) . toHaveBeenCalledTimes ( 1 ) ;
162+
163+ // No fallback needed
164+ expect ( launchDebuggerAppWindowSpy ) . not . toHaveBeenCalled ( ) ;
165+ } finally {
166+ device . close ( ) ;
98167 }
99- openUrl . searchParams . set ( 'launchId' , 'launch1' ) ;
168+ } ) ;
169+ } ) ;
100170
101- // Ensure the debugger was launched using the standalone shell API
102- expect ( showFuseboxShellSpy ) . toHaveBeenCalledWith (
103- expect . any ( String ) ,
104- expect . any ( String ) ,
171+ describe ( 'prepareDebuggerShell failures' , ( ) => {
172+ const { ToolLauncherWithFuseboxShell , prepareDebuggerShellSpy } =
173+ setupToolLauncherWithFuseboxShell ( ( ) =>
174+ Promise . resolve ( { code : 'platform_not_supported' } ) ,
105175 ) ;
106- const firstWindowKey = showFuseboxShellSpy . mock . calls [ 0 ] [ 1 ] ;
176+ const server = withServerForEachTest ( {
177+ logger : undefined ,
178+ unstable_toolLauncher : ToolLauncherWithFuseboxShell ,
179+ unstable_experiments : {
180+ enableStandaloneFuseboxShell : true ,
181+ } ,
182+ } ) ;
183+
184+ test ( 'falls back to browser window when preparation fails' , async ( ) => {
185+ // Connect a device to use when opening the debugger
186+ const device = await setupDevice ( server , autoCleanup . signal ) ;
187+
188+ const launchDebuggerAppWindowSpy = jest
189+ . spyOn ( ToolLauncherWithFuseboxShell , 'launchDebuggerAppWindow' )
190+ . mockResolvedValue ( ) ;
191+ const showFuseboxShellSpy = jest
192+ . spyOn ( ToolLauncherWithFuseboxShell , 'launchDebuggerShell' )
193+ . mockResolvedValue ( ) ;
107194
108- showFuseboxShellSpy . mockClear ( ) ;
109- openUrl . searchParams . set ( 'launchId' , 'launch2' ) ;
195+ try {
196+ // Fetch the target information for the device
197+ const pageListResponse = await fetchJson < JsonPagesListResponse > (
198+ `${ server . serverBaseUrl } /json` ,
199+ ) ;
200+ // Select the first target from the page list response
201+ expect ( pageListResponse . length ) . toBeGreaterThanOrEqual ( 1 ) ;
202+ const firstPage = pageListResponse [ 0 ] ;
110203
111- {
204+ // Build the URL for the debugger
205+ const openUrl = new URL ( '/open-debugger' , server . serverBaseUrl ) ;
206+ openUrl . searchParams . set ( 'launchId' , 'launch1' ) ;
207+ openUrl . searchParams . set (
208+ 'device' ,
209+ firstPage . reactNative . logicalDeviceId ,
210+ ) ;
211+ openUrl . searchParams . set ( 'target' , firstPage . id ) ;
212+
213+ // Request to open the debugger for the first device
112214 const response = await requestLocal ( openUrl . toString ( ) , {
113215 method : 'POST' ,
114216 } ) ;
217+
115218 // Ensure the request was handled properly
116219 expect ( response . statusCode ) . toBe ( 200 ) ;
117- }
118- // Ensure the debugger was launched using the standalone shell API and the same window key
119- expect ( showFuseboxShellSpy ) . toHaveBeenCalledWith (
120- expect . any ( String ) ,
121- firstWindowKey ,
122- ) ;
123220
124- expect ( launchDebuggerAppWindowSpy ) . not . toHaveBeenCalled ( ) ;
125- } finally {
126- device . close ( ) ;
127- }
128- } ) ;
221+ // Debugger was launched but will fail to prepare standalone shell
222+ expect ( prepareDebuggerShellSpy ) . toHaveBeenCalled ( ) ;
223+
224+ // Debugger is not launched with standalone shell since preparation failed
225+ expect ( showFuseboxShellSpy ) . not . toHaveBeenCalled ( ) ;
129226
130- // TODO(moti): Add tests around prepareDebuggerShell
227+ // Debugger fallback
228+ expect ( launchDebuggerAppWindowSpy ) . toHaveBeenCalled ( ) ;
229+ } finally {
230+ device . close ( ) ;
231+ }
232+ } ) ;
233+ } ) ;
131234 } ) ;
132235} ) ;
0 commit comments