1
1
import * as net from "node:net" ;
2
2
import * as os from "node:os" ;
3
3
4
+ import { ElectronVersions , SemVer } from "@electron/fiddle-core" ;
4
5
import * as vscode from "vscode" ;
5
6
6
7
import { setupSpecRunner } from "../electron/spec-runner" ;
@@ -54,10 +55,26 @@ enum DebugMode {
54
55
NATIVE_AND_JS ,
55
56
}
56
57
57
- export function createTestController (
58
+ const FilterReleasesQuickInputButton : vscode . QuickInputButton = {
59
+ iconPath : new vscode . ThemeIcon ( "filter" ) ,
60
+ tooltip : "Filter" ,
61
+ } ;
62
+
63
+ const FilteredReleasesQuickInputButton : vscode . QuickInputButton = {
64
+ iconPath : new vscode . ThemeIcon ( "filter-filled" ) ,
65
+ tooltip : "Filter" ,
66
+ } ;
67
+
68
+ const RefreshReleasesQuickInputButton : vscode . QuickInputButton = {
69
+ iconPath : new vscode . ThemeIcon ( "refresh" ) ,
70
+ tooltip : "Refresh" ,
71
+ } ;
72
+
73
+ export async function createTestController (
58
74
context : vscode . ExtensionContext ,
59
75
electronRoot : vscode . Uri ,
60
76
) {
77
+ const electronVersions = await ElectronVersions . create ( ) ;
61
78
const runProfileData = new WeakMap < vscode . TestRunProfile , string > ( ) ;
62
79
63
80
const testController = vscode . tests . createTestController (
@@ -88,7 +105,7 @@ export function createTestController(
88
105
async function runTests (
89
106
request : vscode . TestRunRequest ,
90
107
token : vscode . CancellationToken ,
91
- debug ?: DebugMode ,
108
+ options : { debug ?: DebugMode ; version ?: string } = { } ,
92
109
) {
93
110
const extraArgs = request . profile
94
111
? runProfileData . get ( request . profile )
@@ -157,11 +174,15 @@ export function createTestController(
157
174
command += ` ${ extraArgs } ` ;
158
175
}
159
176
160
- if ( debug !== undefined ) {
177
+ if ( options . version ) {
178
+ command += ` --electronVersion=${ options . version } ` ;
179
+ }
180
+
181
+ if ( options . debug !== undefined ) {
161
182
command += ` --inspect-brk` ;
162
183
}
163
184
164
- if ( debug === DebugMode . NATIVE_AND_JS ) {
185
+ if ( options . debug === DebugMode . NATIVE_AND_JS ) {
165
186
command += ` --wait-for-debugger` ;
166
187
env . ELECTRON_TEST_PID_DUMP_PATH = processIdFilename . fsPath ;
167
188
}
@@ -255,7 +276,7 @@ export function createTestController(
255
276
}
256
277
} ) ;
257
278
258
- if ( debug === DebugMode . NATIVE_AND_JS ) {
279
+ if ( options . debug === DebugMode . NATIVE_AND_JS ) {
259
280
// Directory may not exist so create it first
260
281
await vscode . workspace . fs . createDirectory ( context . storageUri ! ) ;
261
282
@@ -326,7 +347,7 @@ export function createTestController(
326
347
}
327
348
}
328
349
329
- if ( testRunError === undefined && debug !== undefined ) {
350
+ if ( testRunError === undefined && options . debug !== undefined ) {
330
351
const debuggingSession = await vscode . debug . startDebugging (
331
352
undefined ,
332
353
{
@@ -369,6 +390,209 @@ export function createTestController(
369
390
}
370
391
}
371
392
393
+ const includeNightliesStateKey = "testPrebuiltElectron.includeNightlies" ;
394
+ const includeAlphasStateKey = "testPrebuiltElectron.includeAlphas" ;
395
+ const includeBetasStateKey = "testPrebuiltElectron.includeBetas" ;
396
+
397
+ let includeNightlies =
398
+ context . globalState . get < boolean > ( includeNightliesStateKey ) ?? false ;
399
+ let includeAlphas =
400
+ context . globalState . get < boolean > ( includeAlphasStateKey ) ?? false ;
401
+ let includeBetas =
402
+ context . globalState . get < boolean > ( includeBetasStateKey ) ?? false ;
403
+
404
+ async function showElectronVersionQuickPick ( ) {
405
+ const getVersions = async ( ) => {
406
+ const majors = [
407
+ ...electronVersions . supportedMajors ,
408
+ ...electronVersions . prereleaseMajors ,
409
+ ] ;
410
+
411
+ const versions : vscode . QuickPickItem [ ] = [ ...electronVersions . versions ]
412
+ . reverse ( )
413
+ . filter ( ( version ) => {
414
+ if ( version . prerelease . length > 0 ) {
415
+ return (
416
+ ( includeNightlies && version . prerelease [ 0 ] === "nightly" ) ||
417
+ ( includeAlphas && version . prerelease [ 0 ] === "alpha" ) ||
418
+ ( includeBetas && version . prerelease [ 0 ] === "beta" )
419
+ ) ;
420
+ }
421
+
422
+ return true ;
423
+ } )
424
+ . map ( ( version ) => ( { label : `v${ version } ` , version } ) ) ;
425
+
426
+ // Add a separator after the last stable major version
427
+ versions . splice (
428
+ ( versions as unknown as { version : SemVer } [ ] ) . findIndex (
429
+ ( { version } ) => ! majors . includes ( version . major ) ,
430
+ ) ,
431
+ 0 ,
432
+ {
433
+ label : "Unsupported Releases" ,
434
+ kind : vscode . QuickPickItemKind . Separator ,
435
+ } ,
436
+ ) ;
437
+
438
+ return versions ;
439
+ } ;
440
+
441
+ const versions = await getVersions ( ) ;
442
+
443
+ return new Promise < string | undefined > ( ( resolve ) => {
444
+ const title = "Electron Version" ;
445
+ const placeholder = "Select an Electron version" ;
446
+
447
+ const filtersApplied = includeNightlies || includeAlphas || includeBetas ;
448
+
449
+ const quickPick = vscode . window . createQuickPick ( ) ;
450
+ const setupEventListeners = ( ) => {
451
+ return vscode . Disposable . from (
452
+ quickPick . onDidHide ( ( ) => {
453
+ quickPick . dispose ( ) ;
454
+ resolve ( undefined ) ;
455
+ } ) ,
456
+ quickPick . onDidAccept ( ( ) => {
457
+ resolve ( quickPick . selectedItems [ 0 ] . label . slice ( 1 ) ) ;
458
+ quickPick . dispose ( ) ;
459
+ } ) ,
460
+ ) ;
461
+ } ;
462
+ quickPick . title = title ;
463
+ quickPick . placeholder = placeholder ;
464
+ quickPick . buttons = [
465
+ RefreshReleasesQuickInputButton ,
466
+ filtersApplied
467
+ ? FilteredReleasesQuickInputButton
468
+ : FilterReleasesQuickInputButton ,
469
+ ] ;
470
+ quickPick . items = versions ;
471
+ let eventListeners = setupEventListeners ( ) ;
472
+ quickPick . onDidTriggerButton ( async ( button ) => {
473
+ if ( button === RefreshReleasesQuickInputButton ) {
474
+ quickPick . busy = true ;
475
+ quickPick . items = [ ] ;
476
+ await electronVersions . fetch ( ) ;
477
+ quickPick . items = await getVersions ( ) ;
478
+ quickPick . busy = false ;
479
+ } else if (
480
+ button === FilterReleasesQuickInputButton ||
481
+ button === FilteredReleasesQuickInputButton
482
+ ) {
483
+ const includeNightliesLabel = "Include Nightlies" ;
484
+ const includeAlphasLabel = "Include Alphas" ;
485
+ const includeBetasLabel = "Include Betas" ;
486
+
487
+ // Temporarily transform this quickpick into the filter quickpick
488
+ quickPick . enabled = false ;
489
+ quickPick . busy = true ;
490
+ eventListeners . dispose ( ) ;
491
+ quickPick . buttons = [ ] ;
492
+ quickPick . title = "Electron Version Filters" ;
493
+ quickPick . placeholder = "Select Electron version filters" ;
494
+ const items = [
495
+ { label : includeNightliesLabel } ,
496
+ { label : includeAlphasLabel } ,
497
+ { label : includeBetasLabel } ,
498
+ ] ;
499
+ const selectedItems : vscode . QuickPickItem [ ] = [ ] ;
500
+ if ( includeNightlies ) {
501
+ selectedItems . push (
502
+ items . find ( ( { label } ) => label === includeNightliesLabel ) ! ,
503
+ ) ;
504
+ }
505
+ if ( includeAlphas ) {
506
+ selectedItems . push (
507
+ items . find ( ( { label } ) => label === includeAlphasLabel ) ! ,
508
+ ) ;
509
+ }
510
+ if ( includeBetas ) {
511
+ selectedItems . push (
512
+ items . find ( ( { label } ) => label === includeBetasLabel ) ! ,
513
+ ) ;
514
+ }
515
+ quickPick . canSelectMany = true ;
516
+ quickPick . items = items ;
517
+ quickPick . selectedItems = selectedItems ;
518
+ quickPick . busy = false ;
519
+ quickPick . enabled = true ;
520
+
521
+ const filters = await new Promise <
522
+ readonly vscode . QuickPickItem [ ] | undefined
523
+ > ( ( resolve ) => {
524
+ const eventListeners = vscode . Disposable . from (
525
+ quickPick . onDidHide ( ( ) => {
526
+ eventListeners . dispose ( ) ;
527
+ resolve ( undefined ) ;
528
+ } ) ,
529
+ quickPick . onDidAccept ( ( ) => {
530
+ quickPick . enabled = false ;
531
+ quickPick . busy = true ;
532
+ eventListeners . dispose ( ) ;
533
+ resolve ( quickPick . selectedItems ) ;
534
+ } ) ,
535
+ ) ;
536
+ } ) ;
537
+
538
+ if ( filters ) {
539
+ includeNightlies =
540
+ filters . find (
541
+ ( filter ) => filter . label === includeNightliesLabel ,
542
+ ) !== undefined ;
543
+ includeAlphas =
544
+ filters . find ( ( filter ) => filter . label === includeAlphasLabel ) !==
545
+ undefined ;
546
+ includeBetas =
547
+ filters . find ( ( filter ) => filter . label === includeBetasLabel ) !==
548
+ undefined ;
549
+
550
+ await Promise . all ( [
551
+ context . globalState . update (
552
+ includeNightliesStateKey ,
553
+ includeNightlies ,
554
+ ) ,
555
+ context . globalState . update ( includeAlphasStateKey , includeAlphas ) ,
556
+ context . globalState . update ( includeBetasStateKey , includeBetas ) ,
557
+ ] ) ;
558
+
559
+ //
560
+ // Restore the original quickpick
561
+ //
562
+ // NOTE - We have to hide and show it again otherwise
563
+ // changing `canSelectMany` will make it disappear
564
+ quickPick . hide ( ) ;
565
+ quickPick . selectedItems = [ ] ;
566
+ quickPick . items = [ ] ;
567
+ quickPick . canSelectMany = false ;
568
+ quickPick . title = title ;
569
+ quickPick . placeholder = placeholder ;
570
+ quickPick . items = await getVersions ( ) ;
571
+ quickPick . buttons = [
572
+ RefreshReleasesQuickInputButton ,
573
+ filters . length > 0
574
+ ? FilteredReleasesQuickInputButton
575
+ : FilterReleasesQuickInputButton ,
576
+ ] ;
577
+ quickPick . busy = false ;
578
+ quickPick . enabled = true ;
579
+ // Only restore the event listeners after the change takes effect
580
+ const eventListener = quickPick . onDidChangeActive ( ( ) => {
581
+ eventListener . dispose ( ) ;
582
+ eventListeners = setupEventListeners ( ) ;
583
+ } ) ;
584
+ quickPick . show ( ) ;
585
+ } else {
586
+ // User cancelled the filter quickpick
587
+ resolve ( undefined ) ;
588
+ quickPick . dispose ( ) ;
589
+ }
590
+ }
591
+ } ) ;
592
+ quickPick . show ( ) ;
593
+ } ) ;
594
+ }
595
+
372
596
const profiles = [
373
597
testController . createRunProfile (
374
598
"Run" ,
@@ -382,13 +606,49 @@ export function createTestController(
382
606
} ,
383
607
true ,
384
608
) ,
609
+ testController . createRunProfile (
610
+ "Run (Prebuilt)" ,
611
+ vscode . TestRunProfileKind . Run ,
612
+ async ( request , token ) => {
613
+ const version = await showElectronVersionQuickPick ( ) ;
614
+
615
+ if ( ! version ) {
616
+ return ;
617
+ }
618
+
619
+ return ExtensionState . runOperation (
620
+ ExtensionOperation . RUN_TESTS ,
621
+ ( ) => runTests ( request , token , { version } ) ,
622
+ ExtensionState . isOperationRunning ( ExtensionOperation . RUN_TESTS ) ,
623
+ ) ;
624
+ } ,
625
+ false ,
626
+ ) ,
385
627
testController . createRunProfile (
386
628
"Debug" ,
387
629
vscode . TestRunProfileKind . Debug ,
388
630
async ( request , token ) => {
389
631
return ExtensionState . runOperation (
390
632
ExtensionOperation . RUN_TESTS ,
391
- ( ) => runTests ( request , token , DebugMode . JS ) ,
633
+ ( ) => runTests ( request , token , { debug : DebugMode . JS } ) ,
634
+ ExtensionState . isOperationRunning ( ExtensionOperation . RUN_TESTS ) ,
635
+ ) ;
636
+ } ,
637
+ false ,
638
+ ) ,
639
+ testController . createRunProfile (
640
+ "Debug (Prebuilt)" ,
641
+ vscode . TestRunProfileKind . Debug ,
642
+ async ( request , token ) => {
643
+ const version = await showElectronVersionQuickPick ( ) ;
644
+
645
+ if ( ! version ) {
646
+ return ;
647
+ }
648
+
649
+ return ExtensionState . runOperation (
650
+ ExtensionOperation . RUN_TESTS ,
651
+ ( ) => runTests ( request , token , { debug : DebugMode . JS , version } ) ,
392
652
ExtensionState . isOperationRunning ( ExtensionOperation . RUN_TESTS ) ,
393
653
) ;
394
654
} ,
@@ -420,7 +680,7 @@ export function createTestController(
420
680
421
681
return ExtensionState . runOperation (
422
682
ExtensionOperation . RUN_TESTS ,
423
- ( ) => runTests ( request , token , DebugMode . NATIVE_AND_JS ) ,
683
+ ( ) => runTests ( request , token , { debug : DebugMode . NATIVE_AND_JS } ) ,
424
684
ExtensionState . isOperationRunning ( ExtensionOperation . RUN_TESTS ) ,
425
685
) ;
426
686
} ,
0 commit comments