@@ -12,6 +12,16 @@ interface CodeServerProcess {
1212 address : string
1313}
1414
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+
1525/**
1626 * Class for spawning and managing code-server.
1727 */
@@ -247,11 +257,27 @@ export class CodeServerPage {
247257 * trying.
248258 */
249259 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` ) ]
252262 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+ }
255281 }
256282 return true
257283 }
@@ -266,10 +292,15 @@ export class CodeServerPage {
266292 // TODO: Starting in 1.57 something closes the menu after opening it if we
267293 // open it too soon. To counter that we'll watch for when the menu loses
268294 // 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.
269297 let retryCount = 0
270- while ( ! ( await Promise . race ( [ open ( ) , navigate ( ) ] ) ) ) {
298+ let cancelToken = new CancelToken ( )
299+ while ( ! ( await Promise . race ( [ open ( ) , navigate ( cancelToken ) ] ) ) ) {
271300 this . codeServer . logger . debug ( "menu was closed, retrying" )
272301 ++ retryCount
302+ cancelToken . cancel ( )
303+ cancelToken = new CancelToken ( )
273304 }
274305
275306 this . codeServer . logger . debug ( `menu navigation retries: ${ retryCount } ` )
0 commit comments