@@ -12,6 +12,16 @@ interface CodeServerProcess {
12
12
address : string
13
13
}
14
14
15
+ class CancelToken {
16
+ private _canceled = false
17
+ public canceled ( ) : boolean {
18
+ return this . _canceled
19
+ }
20
+ public cancel ( ) : void {
21
+ this . _canceled = true
22
+ }
23
+ }
24
+
15
25
/**
16
26
* Class for spawning and managing code-server.
17
27
*/
@@ -247,11 +257,27 @@ export class CodeServerPage {
247
257
* trying.
248
258
*/
249
259
async navigateMenus ( menus : string [ ] ) {
250
- const navigate = async ( ) => {
251
- await this . page . waitForSelector ( `${ menuSelector } :focus-within` )
260
+ const navigate = async ( cancelToken : CancelToken ) => {
261
+ const steps : Array < ( ) => Promise < unknown > > = [ ( ) => this . page . waitForSelector ( `${ menuSelector } :focus-within` ) ]
252
262
for ( const menu of menus ) {
253
- await this . page . hover ( `text=${ menu } ` )
254
- await this . page . click ( `text=${ menu } ` )
263
+ // Normally these will wait for the item to be visible and then execute
264
+ // the action. The problem is that if the menu closes these will still
265
+ // be waiting and continue to execute once the menu is visible again,
266
+ // potentially conflicting with the new set of navigations (for example
267
+ // if the old promise clicks logout before the new one can). By
268
+ // splitting them into two steps each we can cancel before running the
269
+ // action.
270
+ steps . push ( ( ) => this . page . hover ( `text=${ menu } ` , { trial : true } ) )
271
+ steps . push ( ( ) => this . page . hover ( `text=${ menu } ` , { force : true } ) )
272
+ steps . push ( ( ) => this . page . click ( `text=${ menu } ` , { trial : true } ) )
273
+ steps . push ( ( ) => this . page . click ( `text=${ menu } ` , { force : true } ) )
274
+ }
275
+ for ( const step of steps ) {
276
+ await step ( )
277
+ if ( cancelToken . canceled ( ) ) {
278
+ this . codeServer . logger . debug ( "menu navigation canceled" )
279
+ return false
280
+ }
255
281
}
256
282
return true
257
283
}
@@ -266,10 +292,15 @@ export class CodeServerPage {
266
292
// TODO: Starting in 1.57 something closes the menu after opening it if we
267
293
// open it too soon. To counter that we'll watch for when the menu loses
268
294
// focus and when/if it does we'll try again.
295
+ // I tried using the classic menu but it doesn't show up at all for some
296
+ // reason. I also tried toggle but the menu disappears after toggling.
269
297
let retryCount = 0
270
- while ( ! ( await Promise . race ( [ open ( ) , navigate ( ) ] ) ) ) {
298
+ let cancelToken = new CancelToken ( )
299
+ while ( ! ( await Promise . race ( [ open ( ) , navigate ( cancelToken ) ] ) ) ) {
271
300
this . codeServer . logger . debug ( "menu was closed, retrying" )
272
301
++ retryCount
302
+ cancelToken . cancel ( )
303
+ cancelToken = new CancelToken ( )
273
304
}
274
305
275
306
this . codeServer . logger . debug ( `menu navigation retries: ${ retryCount } ` )
0 commit comments