Skip to content

Commit 409e2a5

Browse files
committed
🐛 修复 GM API E2E 测试 CI 兼容性
- Phase 1 添加 --headless=new 参数,修复 CI 无 X server 环境 - 添加 eslint-disable 注释消除 Playwright use() 的误报 - prettier 格式化修正
1 parent 9fe45b0 commit 409e2a5

2 files changed

Lines changed: 18 additions & 68 deletions

File tree

e2e/gm-api.spec.ts

Lines changed: 12 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,7 @@
11
import fs from "fs";
22
import path from "path";
33
import os from "os";
4-
import {
5-
test as base,
6-
expect,
7-
chromium,
8-
type BrowserContext,
9-
} from "@playwright/test";
4+
import { test as base, expect, chromium, type BrowserContext } from "@playwright/test";
105
import { installScriptByCode } from "./utils";
116

127
const test = base.extend<{
@@ -17,15 +12,12 @@ const test = base.extend<{
1712
context: async ({}, use) => {
1813
const pathToExtension = path.resolve(__dirname, "../dist/ext");
1914
const userDataDir = fs.mkdtempSync(path.join(os.tmpdir(), "pw-ext-"));
20-
const chromeArgs = [
21-
`--disable-extensions-except=${pathToExtension}`,
22-
`--load-extension=${pathToExtension}`,
23-
];
15+
const chromeArgs = [`--disable-extensions-except=${pathToExtension}`, `--load-extension=${pathToExtension}`];
2416

2517
// Phase 1: Enable user scripts permission
2618
const ctx1 = await chromium.launchPersistentContext(userDataDir, {
2719
headless: false,
28-
args: chromeArgs,
20+
args: ["--headless=new", ...chromeArgs],
2921
});
3022
let [bg] = ctx1.serviceWorkers();
3123
if (!bg) bg = await ctx1.waitForEvent("serviceworker");
@@ -48,6 +40,7 @@ const test = base.extend<{
4840
headless: false,
4941
args: ["--headless=new", ...chromeArgs],
5042
});
43+
// eslint-disable-next-line react-hooks/rules-of-hooks
5144
await use(context);
5245
await context.close();
5346
fs.rmSync(userDataDir, { recursive: true, force: true });
@@ -61,21 +54,16 @@ const test = base.extend<{
6154
await initPage.waitForLoadState("domcontentloaded");
6255
await initPage.evaluate(() => localStorage.setItem("firstUse", "false"));
6356
await initPage.close();
57+
// eslint-disable-next-line react-hooks/rules-of-hooks
6458
await use(extensionId);
6559
},
6660
});
6761

6862
/** Strip SRI hashes and replace slow CDN with faster alternative */
6963
function patchScriptCode(code: string): string {
7064
return code
71-
.replace(
72-
/^(\/\/\s*@(?:require|resource)\s+.*?)#sha(?:256|384|512)[=-][^\s]+/gm,
73-
"$1"
74-
)
75-
.replace(
76-
/https:\/\/cdn\.jsdelivr\.net\/npm\//g,
77-
"https://unpkg.com/"
78-
);
65+
.replace(/^(\/\/\s*@(?:require|resource)\s+.*?)#sha(?:256|384|512)[=-][^\s]+/gm, "$1")
66+
.replace(/https:\/\/cdn\.jsdelivr\.net\/npm\//g, "https://unpkg.com/");
7967
}
8068

8169
/**
@@ -121,10 +109,7 @@ async function runTestScript(
121109
targetUrl: string,
122110
timeoutMs: number
123111
): Promise<{ passed: number; failed: number; logs: string[] }> {
124-
let code = fs.readFileSync(
125-
path.join(__dirname, `../example/tests/${scriptFile}`),
126-
"utf-8"
127-
);
112+
let code = fs.readFileSync(path.join(__dirname, `../example/tests/${scriptFile}`), "utf-8");
128113
code = patchScriptCode(code);
129114

130115
await installScriptByCode(context, extensionId, code);
@@ -163,17 +148,8 @@ test.describe("GM API", () => {
163148
// Two-phase launch + script install + network fetches + permission dialogs
164149
test.setTimeout(300_000);
165150

166-
test("GM_ sync API tests (gm_api_test.js)", async ({
167-
context,
168-
extensionId,
169-
}) => {
170-
const { passed, failed, logs } = await runTestScript(
171-
context,
172-
extensionId,
173-
"gm_api_test.js",
174-
TARGET_URL,
175-
90_000
176-
);
151+
test("GM_ sync API tests (gm_api_test.js)", async ({ context, extensionId }) => {
152+
const { passed, failed, logs } = await runTestScript(context, extensionId, "gm_api_test.js", TARGET_URL, 90_000);
177153

178154
console.log(`[gm_api_test] passed=${passed}, failed=${failed}`);
179155
if (failed !== 0) {
@@ -183,10 +159,7 @@ test.describe("GM API", () => {
183159
expect(passed, "No test results found - script may not have run").toBeGreaterThan(0);
184160
});
185161

186-
test("GM.* async API tests (gm_api_async_test.js)", async ({
187-
context,
188-
extensionId,
189-
}) => {
162+
test("GM.* async API tests (gm_api_async_test.js)", async ({ context, extensionId }) => {
190163
const { passed, failed, logs } = await runTestScript(
191164
context,
192165
extensionId,
@@ -203,10 +176,7 @@ test.describe("GM API", () => {
203176
expect(passed, "No test results found - script may not have run").toBeGreaterThan(0);
204177
});
205178

206-
test("Content inject tests (inject_content_test.js)", async ({
207-
context,
208-
extensionId,
209-
}) => {
179+
test("Content inject tests (inject_content_test.js)", async ({ context, extensionId }) => {
210180
const { passed, failed, logs } = await runTestScript(
211181
context,
212182
extensionId,

e2e/utils.ts

Lines changed: 6 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,31 @@
11
import type { BrowserContext, Page } from "@playwright/test";
22

33
/** Open the options page and wait for it to load */
4-
export async function openOptionsPage(
5-
context: BrowserContext,
6-
extensionId: string
7-
): Promise<Page> {
4+
export async function openOptionsPage(context: BrowserContext, extensionId: string): Promise<Page> {
85
const page = await context.newPage();
96
await page.goto(`chrome-extension://${extensionId}/src/options.html`);
107
await page.waitForLoadState("domcontentloaded");
118
return page;
129
}
1310

1411
/** Open the popup page and wait for it to load */
15-
export async function openPopupPage(
16-
context: BrowserContext,
17-
extensionId: string
18-
): Promise<Page> {
12+
export async function openPopupPage(context: BrowserContext, extensionId: string): Promise<Page> {
1913
const page = await context.newPage();
2014
await page.goto(`chrome-extension://${extensionId}/src/popup.html`);
2115
await page.waitForLoadState("domcontentloaded");
2216
return page;
2317
}
2418

2519
/** Open the install page with a script URL parameter */
26-
export async function openInstallPage(
27-
context: BrowserContext,
28-
extensionId: string,
29-
scriptUrl: string
30-
): Promise<Page> {
20+
export async function openInstallPage(context: BrowserContext, extensionId: string, scriptUrl: string): Promise<Page> {
3121
const page = await context.newPage();
32-
await page.goto(
33-
`chrome-extension://${extensionId}/src/install.html?url=${encodeURIComponent(scriptUrl)}`
34-
);
22+
await page.goto(`chrome-extension://${extensionId}/src/install.html?url=${encodeURIComponent(scriptUrl)}`);
3523
await page.waitForLoadState("domcontentloaded");
3624
return page;
3725
}
3826

3927
/** Open the script editor page */
40-
export async function openEditorPage(
41-
context: BrowserContext,
42-
extensionId: string,
43-
params?: string
44-
): Promise<Page> {
28+
export async function openEditorPage(context: BrowserContext, extensionId: string, params?: string): Promise<Page> {
4529
const page = await context.newPage();
4630
const hash = params ? `#/script/editor?${params}` : "#/script/editor";
4731
await page.goto(`chrome-extension://${extensionId}/src/options.html${hash}`);
@@ -50,11 +34,7 @@ export async function openEditorPage(
5034
}
5135

5236
/** Install a script by injecting code into the Monaco editor and saving */
53-
export async function installScriptByCode(
54-
context: BrowserContext,
55-
extensionId: string,
56-
code: string
57-
): Promise<void> {
37+
export async function installScriptByCode(context: BrowserContext, extensionId: string, code: string): Promise<void> {
5838
const page = await openEditorPage(context, extensionId);
5939
// Wait for Monaco editor to be ready
6040
await page.locator(".monaco-editor").waitFor({ timeout: 30_000 });

0 commit comments

Comments
 (0)