-
Notifications
You must be signed in to change notification settings - Fork 98
Expand file tree
/
Copy pathbaseTest.ts
More file actions
191 lines (170 loc) · 7.94 KB
/
baseTest.ts
File metadata and controls
191 lines (170 loc) · 7.94 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
/**
* Playwright test fixture that launches VS Code via Electron,
* opens a temporary copy of a test project, and tears everything
* down after the test.
*
* Usage in test files:
*
* import { test, expect } from "../fixtures/baseTest";
*
* test("my test", async ({ page }) => {
* // `page` is a Playwright Page attached to VS Code
* });
*/
import { _electron, test as base, type Page } from "@playwright/test";
import { downloadAndUnzipVSCode } from "@vscode/test-electron";
import * as fs from "fs-extra";
import * as os from "os";
import * as path from "path";
export { expect } from "@playwright/test";
// Root of the extension source tree
const EXTENSION_ROOT = path.join(__dirname, "..", "..", "..");
// Root of the test data projects
const TEST_DATA_ROOT = path.join(EXTENSION_ROOT, "test");
export type TestOptions = {
/** VS Code version to download, default "stable" */
vscodeVersion: string;
/** Relative path under `test/` to the project to open (e.g. "maven") */
testProjectDir: string;
};
type TestFixtures = TestOptions & {
/** Playwright Page connected to the VS Code Electron window */
page: Page;
};
export const test = base.extend<TestFixtures>({
vscodeVersion: [process.env.VSCODE_VERSION || "stable", { option: true }],
testProjectDir: ["maven", { option: true }],
page: async ({ vscodeVersion, testProjectDir }, use, testInfo) => {
// 1. Create a temp directory and copy the test project into it.
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "java-dep-e2e-"));
const projectName = path.basename(testProjectDir);
const projectDir = path.join(tmpDir, projectName);
fs.copySync(path.join(TEST_DATA_ROOT, testProjectDir), projectDir);
// Write VS Code settings to suppress telemetry prompts and notification noise
const vscodeDir = path.join(projectDir, ".vscode");
fs.ensureDirSync(vscodeDir);
const settingsPath = path.join(vscodeDir, "settings.json");
let existingSettings: Record<string, unknown> = {};
if (fs.existsSync(settingsPath)) {
// settings.json may contain JS-style comments (JSONC), strip them before parsing
const raw = fs.readFileSync(settingsPath, "utf-8");
const stripped = raw.replace(/\/\/.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "");
try {
existingSettings = JSON.parse(stripped);
} catch {
// If still invalid, start fresh — our injected settings are more important
existingSettings = {};
}
}
const mergedSettings = {
...existingSettings,
"telemetry.telemetryLevel": "off",
"redhat.telemetry.enabled": false,
"workbench.colorTheme": "Default Dark Modern",
"update.mode": "none",
"extensions.ignoreRecommendations": true,
};
fs.writeFileSync(settingsPath, JSON.stringify(mergedSettings, null, 4));
// 2. Resolve VS Code executable.
const vscodePath = await downloadAndUnzipVSCode(vscodeVersion);
// resolveCliArgsFromVSCodeExecutablePath returns CLI-specific args
// (e.g. --ms-enable-electron-run-as-node) that are unsuitable for
// Electron UI launch. Extract only --extensions-dir and --user-data-dir.
const vscodeTestDir = path.join(EXTENSION_ROOT, ".vscode-test");
const extensionsDir = path.join(vscodeTestDir, "extensions");
const userDataDir = path.join(vscodeTestDir, "user-data");
// 3. Launch VS Code as an Electron app.
const electronApp = await _electron.launch({
executablePath: vscodePath,
env: { ...process.env, NODE_ENV: "development" },
args: [
"--no-sandbox",
"--disable-gpu-sandbox",
"--disable-updates",
"--skip-welcome",
"--skip-release-notes",
"--disable-workspace-trust",
"--password-store=basic",
// Suppress notifications that block UI interactions
"--disable-telemetry",
`--extensions-dir=${extensionsDir}`,
`--user-data-dir=${userDataDir}`,
`--extensionDevelopmentPath=${EXTENSION_ROOT}`,
projectDir,
],
});
const page = await electronApp.firstWindow();
// Auto-dismiss Electron native dialogs (e.g. redhat.java refactoring
// confirmation, delete file confirmation). These dialogs are outside
// the renderer DOM and cannot be handled via Playwright Page API.
// Monkey-patch dialog.showMessageBox to find and click the confirm
// button by label, falling back to the first button.
await electronApp.evaluate(({ dialog }) => {
const confirmLabels = /^(OK|Delete|Move to Recycle Bin|Move to Trash)$/i;
dialog.showMessageBox = async (_win: any, opts: any) => {
const options = opts || _win;
const buttons: string[] = options?.buttons || [];
let idx = buttons.findIndex((b: string) => confirmLabels.test(b));
if (idx < 0) idx = 0;
return { response: idx, checkboxChecked: true };
};
dialog.showMessageBoxSync = (_win: any, opts: any) => {
const options = opts || _win;
const buttons: string[] = options?.buttons || [];
let idx = buttons.findIndex((b: string) => confirmLabels.test(b));
if (idx < 0) idx = 0;
return idx;
};
});
// Dismiss any startup notifications/dialogs before handing off to tests
await page.waitForTimeout(3_000);
await dismissAllNotifications(page);
// Tracing is handled by Playwright's built-in `use.trace` config
// (see playwright.config.ts). No manual tracing.start/stop needed.
// ---- hand off to the test ----
await use(page);
// ---- teardown ----
// Trace saving is handled automatically by Playwright's `use.trace`
// config — no manual tracing.stop() needed here.
await electronApp.close();
// Clean up temp directory
try {
fs.rmSync(tmpDir, { force: true, recursive: true });
} catch (e) {
console.warn(`Warning: failed to clean up ${tmpDir}: ${e}`);
}
},
});
/**
* Dismiss all VS Code notification toasts (telemetry prompts, theme suggestions, etc.).
* These notifications can steal focus and block Quick Open / Command Palette interactions.
*/
async function dismissAllNotifications(page: Page): Promise<void> {
try {
// Click "Clear All Notifications" if the notification center button is visible
const clearAll = page.locator(".notifications-toasts .codicon-notifications-clear-all, .notification-toast .codicon-close");
let count = await clearAll.count().catch(() => 0);
while (count > 0) {
await clearAll.first().click();
await page.waitForTimeout(500);
count = await clearAll.count().catch(() => 0);
}
// Also try the command palette approach as a fallback
const notificationToasts = page.locator(".notification-toast");
if (await notificationToasts.count().catch(() => 0) > 0) {
// Use keyboard shortcut to clear all notifications
await page.keyboard.press("Control+Shift+P");
const input = page.locator(".quick-input-widget input.input");
if (await input.isVisible({ timeout: 3_000 }).catch(() => false)) {
await input.fill("Notifications: Clear All Notifications");
await page.waitForTimeout(500);
await input.press("Enter");
await page.waitForTimeout(500);
}
}
} catch {
// Best effort
}
}