Skip to content

Commit 5a2da88

Browse files
committed
feat: test profiles to run against prebuilt Electron versions
1 parent da93166 commit 5a2da88

File tree

4 files changed

+697
-22
lines changed

4 files changed

+697
-22
lines changed

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -948,7 +948,7 @@
948948
"vscode:prepublish": "run-p \"esbuild:* -- {1}\" -- --minify",
949949
"esbuild": "run-p \"esbuild:* -- {1}\" -- --sourcemap",
950950
"watch": "run-p \"esbuild:* -- {1} {2}\" -- --sourcemap --watch",
951-
"esbuild:extension": "esbuild ./src/extension.ts --bundle --outfile=out/main.js --external:vscode --format=cjs --platform=node",
951+
"esbuild:extension": "esbuild ./src/extension.ts --bundle --outfile=out/main.js --external:vscode --external:original-fs --format=cjs --platform=node",
952952
"esbuild:list-tests": "esbuild ./electron/listMochaTests.ts --bundle --outfile=out/electron/listMochaTests.js --external:electron --format=cjs --platform=node",
953953
"esbuild:mocha-interface": "esbuild ./electron/mocha-interface.ts --bundle --outfile=out/electron/mocha-interface.js --format=cjs --platform=node",
954954
"esbuild:mocha-reporter": "esbuild ./electron/mocha-reporter.ts --bundle --outfile=out/electron/mocha-reporter.js --format=cjs --platform=node",
@@ -990,6 +990,7 @@
990990
},
991991
"dependencies": {
992992
"@electron/docs-parser": "^2.0.0",
993+
"@electron/fiddle-core": "^1.4.0",
993994
"@octokit/rest": "^19.0.4",
994995
"@vscode/prompt-tsx": "^0.3.0-alpha.20",
995996
"diff": "^5.0.0",

src/extension.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -394,7 +394,7 @@ export async function activate(context: vscode.ExtensionContext) {
394394
if (electronRoot !== undefined) {
395395
await setContext("active", true);
396396

397-
const testController = createTestController(context, electronRoot);
397+
const testController = await createTestController(context, electronRoot);
398398

399399
const patchesConfig = getPatchesConfigFile(electronRoot);
400400
const patchesProvider = new ElectronPatchesProvider(

src/tests.ts

Lines changed: 268 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as net from "node:net";
22
import * as os from "node:os";
33

4+
import { ElectronVersions, SemVer } from "@electron/fiddle-core";
45
import * as vscode from "vscode";
56

67
import { setupSpecRunner } from "../electron/spec-runner";
@@ -54,10 +55,26 @@ enum DebugMode {
5455
NATIVE_AND_JS,
5556
}
5657

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(
5874
context: vscode.ExtensionContext,
5975
electronRoot: vscode.Uri,
6076
) {
77+
const electronVersions = await ElectronVersions.create();
6178
const runProfileData = new WeakMap<vscode.TestRunProfile, string>();
6279

6380
const testController = vscode.tests.createTestController(
@@ -88,7 +105,7 @@ export function createTestController(
88105
async function runTests(
89106
request: vscode.TestRunRequest,
90107
token: vscode.CancellationToken,
91-
debug?: DebugMode,
108+
options: { debug?: DebugMode; version?: string } = {},
92109
) {
93110
const extraArgs = request.profile
94111
? runProfileData.get(request.profile)
@@ -157,11 +174,15 @@ export function createTestController(
157174
command += ` ${extraArgs}`;
158175
}
159176

160-
if (debug !== undefined) {
177+
if (options.version) {
178+
command += ` --electronVersion=${options.version}`;
179+
}
180+
181+
if (options.debug !== undefined) {
161182
command += ` --inspect-brk`;
162183
}
163184

164-
if (debug === DebugMode.NATIVE_AND_JS) {
185+
if (options.debug === DebugMode.NATIVE_AND_JS) {
165186
command += ` --wait-for-debugger`;
166187
env.ELECTRON_TEST_PID_DUMP_PATH = processIdFilename.fsPath;
167188
}
@@ -255,7 +276,7 @@ export function createTestController(
255276
}
256277
});
257278

258-
if (debug === DebugMode.NATIVE_AND_JS) {
279+
if (options.debug === DebugMode.NATIVE_AND_JS) {
259280
// Directory may not exist so create it first
260281
await vscode.workspace.fs.createDirectory(context.storageUri!);
261282

@@ -326,7 +347,7 @@ export function createTestController(
326347
}
327348
}
328349

329-
if (testRunError === undefined && debug !== undefined) {
350+
if (testRunError === undefined && options.debug !== undefined) {
330351
const debuggingSession = await vscode.debug.startDebugging(
331352
undefined,
332353
{
@@ -369,6 +390,209 @@ export function createTestController(
369390
}
370391
}
371392

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+
372596
const profiles = [
373597
testController.createRunProfile(
374598
"Run",
@@ -382,13 +606,49 @@ export function createTestController(
382606
},
383607
true,
384608
),
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+
),
385627
testController.createRunProfile(
386628
"Debug",
387629
vscode.TestRunProfileKind.Debug,
388630
async (request, token) => {
389631
return ExtensionState.runOperation(
390632
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 }),
392652
ExtensionState.isOperationRunning(ExtensionOperation.RUN_TESTS),
393653
);
394654
},
@@ -420,7 +680,7 @@ export function createTestController(
420680

421681
return ExtensionState.runOperation(
422682
ExtensionOperation.RUN_TESTS,
423-
() => runTests(request, token, DebugMode.NATIVE_AND_JS),
683+
() => runTests(request, token, { debug: DebugMode.NATIVE_AND_JS }),
424684
ExtensionState.isOperationRunning(ExtensionOperation.RUN_TESTS),
425685
);
426686
},

0 commit comments

Comments
 (0)