openspec-playwright 0.3.46 → 0.3.48

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,311 @@
1
+ import { readFileSync, existsSync } from "fs";
2
+ import { join } from "path";
3
+ import chalk from "chalk";
4
+ import { SHARED_FILE_NAMES } from "../shared/index.js";
5
+ import { collectSpecFiles } from "./coverage.js";
6
+ // ─── Main Entry Point ─────────────────────────────────────────────────
7
+ export async function flake(changeName, options) {
8
+ const projectRoot = process.cwd();
9
+ const testsDir = join(projectRoot, "tests", "playwright");
10
+ if (!existsSync(testsDir)) {
11
+ console.log(chalk.yellow(" tests/playwright/ not found. Run `openspec-pw init` first.\n"));
12
+ return;
13
+ }
14
+ console.log(chalk.blue("\n🔍 OpenSpec Playwright: Flake Detection\n"));
15
+ // Scope filtering
16
+ let specFiles;
17
+ if (changeName) {
18
+ const changeTestDir = join(testsDir, "changes", changeName);
19
+ if (!existsSync(changeTestDir)) {
20
+ console.log(chalk.yellow(` No tests found for change "${changeName}".\n`));
21
+ return;
22
+ }
23
+ specFiles = collectSpecFiles(changeTestDir);
24
+ }
25
+ else {
26
+ specFiles = collectSpecFiles(testsDir);
27
+ }
28
+ // Filter out shared files
29
+ const SHARED_FILES = new Set([
30
+ ...SHARED_FILE_NAMES,
31
+ "auth.setup.ts",
32
+ "global.teardown.ts",
33
+ "seed.spec.ts",
34
+ ]);
35
+ specFiles = specFiles.filter((f) => {
36
+ const name = f.split("/").pop() ?? "";
37
+ return !SHARED_FILES.has(name);
38
+ });
39
+ if (specFiles.length === 0) {
40
+ console.log(chalk.yellow(" No spec files found to analyze.\n"));
41
+ return;
42
+ }
43
+ // Parse config
44
+ const configHasStorageState = getConfigStorageState(projectRoot);
45
+ // Run all detectors
46
+ const findings = [];
47
+ for (const file of specFiles) {
48
+ const content = readFileSync(file, "utf-8");
49
+ const relPath = file.replace(testsDir + "/", "");
50
+ findings.push(...detectNetworkIdle(content, relPath));
51
+ findings.push(...detectRouteAfterGoto(content, relPath));
52
+ findings.push(...detectStorageLeakage(content, relPath, configHasStorageState));
53
+ findings.push(...detectTestUseScope(content, relPath, configHasStorageState));
54
+ }
55
+ // Build report
56
+ const report = buildReport(findings);
57
+ // Render
58
+ if (options?.json) {
59
+ console.log(JSON.stringify(report, null, 2));
60
+ }
61
+ else {
62
+ renderReport(report);
63
+ }
64
+ // Gate
65
+ if (options?.gate) {
66
+ const exitCode = computeGateExitCode(report, options.gate);
67
+ if (exitCode !== 0)
68
+ process.exit(exitCode);
69
+ }
70
+ }
71
+ // ─── Config Parsing ───────────────────────────────────────────────────
72
+ export function getConfigStorageState(projectRoot) {
73
+ for (const name of [
74
+ "playwright.config.ts",
75
+ "playwright.config.js",
76
+ "playwright.config.mjs",
77
+ ]) {
78
+ const configPath = join(projectRoot, name);
79
+ if (!existsSync(configPath))
80
+ continue;
81
+ const content = readFileSync(configPath, "utf-8");
82
+ if (/storageState/.test(content) || /storageState\s*:/.test(content)) {
83
+ return true;
84
+ }
85
+ }
86
+ return false;
87
+ }
88
+ // ─── Pattern Detectors ────────────────────────────────────────────────
89
+ export function detectNetworkIdle(content, filePath) {
90
+ const findings = [];
91
+ const regex = /waitForLoadState\s*\(\s*['"]networkidle['"]/g;
92
+ let match;
93
+ while ((match = regex.exec(content)) !== null) {
94
+ const line = content.substring(0, match.index).split("\n").length;
95
+ findings.push({
96
+ pattern: "networkidle",
97
+ file: filePath,
98
+ line,
99
+ message: "waitForLoadState('networkidle') in SPA — use a specific response wait instead",
100
+ severity: "high",
101
+ });
102
+ }
103
+ return findings;
104
+ }
105
+ export function detectRouteAfterGoto(content, filePath) {
106
+ const findings = [];
107
+ // Find all test( positions to isolate test blocks
108
+ const testPositions = [];
109
+ const testRegex = /test\s*\(/g;
110
+ let tm;
111
+ while ((tm = testRegex.exec(content)) !== null) {
112
+ testPositions.push(tm.index);
113
+ }
114
+ for (let i = 0; i < testPositions.length; i++) {
115
+ const testStart = testPositions[i];
116
+ const testEnd = i + 1 < testPositions.length ? testPositions[i + 1] : content.length;
117
+ const block = content.substring(testStart, testEnd);
118
+ // Collect page.goto() line numbers
119
+ const gotoCalls = [];
120
+ const gotoRegex = /page\.goto\s*\(/g;
121
+ let gm;
122
+ while ((gm = gotoRegex.exec(block)) !== null) {
123
+ const line = content.substring(0, testStart + gm.index).split("\n").length;
124
+ gotoCalls.push(line);
125
+ }
126
+ // Collect page.route() line numbers (excluding page.context().route())
127
+ const routeCalls = [];
128
+ const routeRegex = /page\.route\s*\(/g;
129
+ let rm;
130
+ while ((rm = routeRegex.exec(block)) !== null) {
131
+ // Skip if this is page.context().route()
132
+ const before = block.substring(Math.max(0, rm.index - 20), rm.index);
133
+ if (before.includes(".context("))
134
+ continue;
135
+ const line = content.substring(0, testStart + rm.index).split("\n").length;
136
+ routeCalls.push(line);
137
+ }
138
+ // If any route call appears after any goto call → finding
139
+ if (routeCalls.length > 0 && gotoCalls.length > 0) {
140
+ const earliestGoto = Math.min(...gotoCalls);
141
+ for (const routeLine of routeCalls) {
142
+ if (routeLine > earliestGoto) {
143
+ findings.push({
144
+ pattern: "route-after-goto",
145
+ file: filePath,
146
+ line: routeLine,
147
+ message: "page.route() registered after page.goto() — move route() before goto()",
148
+ severity: "high",
149
+ });
150
+ }
151
+ }
152
+ }
153
+ }
154
+ return findings;
155
+ }
156
+ export function detectStorageLeakage(content, filePath, configHasStorageState) {
157
+ // Condition 1: file references storageState
158
+ const hasStorageStateRef = content.includes("storageState") || configHasStorageState;
159
+ // Condition 2: page.goto() targeting protected routes
160
+ const protectedRoutes = [
161
+ "/dashboard",
162
+ "/profile",
163
+ "/admin",
164
+ "/settings",
165
+ "/account",
166
+ ];
167
+ const gotoRegex = /page\.goto\s*\(\s*['"`]([^'"`]*)['"`]/g;
168
+ let hasProtectedGoto = false;
169
+ let firstProtectedGotoLine = 0;
170
+ let gm;
171
+ while ((gm = gotoRegex.exec(content)) !== null) {
172
+ const url = gm[1];
173
+ if (protectedRoutes.some((route) => url.includes(route))) {
174
+ hasProtectedGoto = true;
175
+ firstProtectedGotoLine =
176
+ content.substring(0, gm.index).split("\n").length;
177
+ break;
178
+ }
179
+ }
180
+ // Condition 3: no browser.newContext()
181
+ const hasNewContext = content.includes("browser.newContext(");
182
+ if (hasStorageStateRef && hasProtectedGoto && !hasNewContext) {
183
+ return [
184
+ {
185
+ pattern: "storage-leakage",
186
+ file: filePath,
187
+ line: firstProtectedGotoLine,
188
+ message: "Potential storageState leakage — authenticated state may bleed into unauthenticated tests",
189
+ severity: "medium",
190
+ },
191
+ ];
192
+ }
193
+ return [];
194
+ }
195
+ export function detectTestUseScope(content, filePath, configHasStorageState) {
196
+ const findings = [];
197
+ if (!configHasStorageState)
198
+ return findings;
199
+ // Find all test.describe( positions
200
+ const describePositions = [];
201
+ const descRegex = /test\.describe\s*\(/g;
202
+ let dm;
203
+ while ((dm = descRegex.exec(content)) !== null) {
204
+ describePositions.push(dm.index);
205
+ }
206
+ // Find all test.use({ with storageState
207
+ const useRegex = /test\.use\s*\(/g;
208
+ let um;
209
+ while ((um = useRegex.exec(content)) !== null) {
210
+ const usePos = um.index;
211
+ const line = content.substring(0, usePos).split("\n").length;
212
+ // Check if storageState appears nearby (within the test.use config block)
213
+ // Search from usePos to the next test( or test.describe( or end of content
214
+ let searchEnd = content.length;
215
+ const nextTest = content.indexOf("test(", usePos + 1);
216
+ const nextDescribe = content.indexOf("test.describe(", usePos + 1);
217
+ if (nextTest > 0)
218
+ searchEnd = Math.min(searchEnd, nextTest);
219
+ if (nextDescribe > 0)
220
+ searchEnd = Math.min(searchEnd, nextDescribe);
221
+ const context = content.substring(usePos, Math.min(usePos + 500, searchEnd));
222
+ if (!context.includes("storageState"))
223
+ continue;
224
+ // Determine if inside a describe block
225
+ const isInsideDescribe = describePositions.some((dp) => dp < usePos);
226
+ if (isInsideDescribe) {
227
+ // Sub-check 1: test.use({ storageState }) inside test.describe(
228
+ findings.push({
229
+ pattern: "test-use-scope",
230
+ file: filePath,
231
+ line,
232
+ message: "Conflicting test.use({ storageState }) scope — may silently break test isolation",
233
+ severity: "medium",
234
+ });
235
+ }
236
+ else {
237
+ // Sub-check 2: top-level test.use({ storageState }) with untagged tests
238
+ const testTagRegex = /test\(['"`]([^'"`]*)['"`]/g;
239
+ let hasUntaggedTest = false;
240
+ let ttm;
241
+ while ((ttm = testTagRegex.exec(content)) !== null) {
242
+ if (!/@unauthenticated|@public/i.test(ttm[1])) {
243
+ hasUntaggedTest = true;
244
+ break;
245
+ }
246
+ }
247
+ if (hasUntaggedTest) {
248
+ findings.push({
249
+ pattern: "test-use-scope",
250
+ file: filePath,
251
+ line,
252
+ message: "Conflicting test.use({ storageState }) scope — may silently break test isolation",
253
+ severity: "medium",
254
+ });
255
+ }
256
+ }
257
+ }
258
+ return findings;
259
+ }
260
+ // ─── Report Helpers ───────────────────────────────────────────────────
261
+ export function buildReport(findings) {
262
+ const patternCounts = {};
263
+ for (const f of findings) {
264
+ patternCounts[f.pattern] = (patternCounts[f.pattern] || 0) + 1;
265
+ }
266
+ const totalPatterns = Object.keys(patternCounts).length;
267
+ return {
268
+ findings,
269
+ patternCounts,
270
+ summaryText: `${findings.length} finding(s) across ${totalPatterns} pattern(s)`,
271
+ };
272
+ }
273
+ function renderReport(report) {
274
+ if (report.findings.length === 0) {
275
+ console.log(" ✅ No flake patterns detected.\n");
276
+ return;
277
+ }
278
+ // Group findings by pattern
279
+ const grouped = {};
280
+ for (const f of report.findings) {
281
+ if (!grouped[f.pattern])
282
+ grouped[f.pattern] = [];
283
+ grouped[f.pattern].push(f);
284
+ }
285
+ for (const [pattern, patternFindings] of Object.entries(grouped)) {
286
+ console.log(`─── ${pattern} ───`);
287
+ for (const f of patternFindings) {
288
+ console.log(` ⚠ ${f.file}:${f.line}`);
289
+ console.log(` ${f.message}`);
290
+ }
291
+ console.log();
292
+ }
293
+ console.log(`Summary: ${report.summaryText}\n`);
294
+ }
295
+ export function computeGateExitCode(report, gate) {
296
+ const level = gate.toUpperCase();
297
+ if (level === "HIGH") {
298
+ return report.findings.some((f) => f.severity === "high") ? 1 : 0;
299
+ }
300
+ if (level === "MEDIUM") {
301
+ return report.findings.some((f) => f.severity === "high" || f.severity === "medium")
302
+ ? 1
303
+ : 0;
304
+ }
305
+ if (level === "ALL") {
306
+ return report.findings.length > 0 ? 1 : 0;
307
+ }
308
+ // Default to HIGH
309
+ return report.findings.some((f) => f.severity === "high") ? 1 : 0;
310
+ }
311
+ //# sourceMappingURL=flake.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flake.js","sourceRoot":"","sources":["../../src/commands/flake.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -7,15 +7,6 @@ import { readFileSync } from "fs";
7
7
  import { join, dirname } from "path";
8
8
  import { fileURLToPath } from "url";
9
9
  import { Command } from "commander";
10
- import { init } from "./commands/init.js";
11
- import { update } from "./commands/update.js";
12
- import { doctor } from "./commands/doctor.js";
13
- import { run } from "./commands/run.js";
14
- import { migrate } from "./commands/migrate.js";
15
- import { uninstall } from "./commands/uninstall.js";
16
- import { audit } from "./commands/audit.js";
17
- import { explore } from "./commands/explore.js";
18
- import { checkForUpdate } from "./shared/version-check.js";
19
10
  const __dirname = dirname(fileURLToPath(import.meta.url));
20
11
  const pkg = JSON.parse(readFileSync(join(__dirname, "../package.json"), "utf-8"));
21
12
  const program = new Command();
@@ -31,6 +22,8 @@ program
31
22
  .option("--seed", "force regenerate seed.spec.ts (overwrite if exists)")
32
23
  .option("--ci", "generate GitHub Actions CI workflow")
33
24
  .action(async (opts) => {
25
+ const { init } = await import("./commands/init.js");
26
+ const { checkForUpdate } = await import("./shared/version-check.js");
34
27
  await init(opts);
35
28
  await checkForUpdate(pkg.version);
36
29
  });
@@ -39,6 +32,8 @@ program
39
32
  .description("Check if all prerequisites are installed")
40
33
  .option("--json", "Output results as JSON")
41
34
  .action(async (opts) => {
35
+ const { doctor } = await import("./commands/doctor.js");
36
+ const { checkForUpdate } = await import("./shared/version-check.js");
42
37
  await doctor(opts);
43
38
  await checkForUpdate(pkg.version);
44
39
  });
@@ -48,6 +43,7 @@ program
48
43
  .option("--no-cli", "skip CLI update")
49
44
  .option("--no-skill", "skip command update")
50
45
  .action(async (opts) => {
46
+ const { update } = await import("./commands/update.js");
51
47
  await update(opts);
52
48
  // No version check needed after update — user just updated
53
49
  });
@@ -67,6 +63,8 @@ program
67
63
  .option("--headed", "Show browser during test run (default: headless)")
68
64
  .option("--update-snapshots", "Update screenshot baselines before running tests")
69
65
  .action(async (changeName, opts) => {
66
+ const { run } = await import("./commands/run.js");
67
+ const { checkForUpdate } = await import("./shared/version-check.js");
70
68
  await run(changeName, opts);
71
69
  await checkForUpdate(pkg.version);
72
70
  });
@@ -76,6 +74,8 @@ program
76
74
  .option("-n, --dry-run", "Show what would be migrated without moving files")
77
75
  .option("-f, --force", "Overwrite existing files at the new location")
78
76
  .action(async (opts) => {
77
+ const { migrate } = await import("./commands/migrate.js");
78
+ const { checkForUpdate } = await import("./shared/version-check.js");
79
79
  await migrate(opts);
80
80
  await checkForUpdate(pkg.version);
81
81
  });
@@ -83,6 +83,8 @@ program
83
83
  .command("uninstall")
84
84
  .description("Remove OpenSpec + Playwright E2E integration from the current project")
85
85
  .action(async () => {
86
+ const { uninstall } = await import("./commands/uninstall.js");
87
+ const { checkForUpdate } = await import("./shared/version-check.js");
86
88
  await uninstall();
87
89
  await checkForUpdate(pkg.version);
88
90
  });
@@ -90,15 +92,36 @@ program
90
92
  .command("audit")
91
93
  .description("Audit test files for orphaned specs, missing auth, sitemap issues")
92
94
  .action(async () => {
95
+ const { audit } = await import("./commands/audit.js");
96
+ const { checkForUpdate } = await import("./shared/version-check.js");
93
97
  await audit();
94
98
  await checkForUpdate(pkg.version);
95
99
  });
100
+ program
101
+ .command("coverage [change-name]")
102
+ .description("Analyze spec–test coverage for OpenSpec changes")
103
+ .option("--json", "Output results as JSON")
104
+ .action(async (changeName, opts) => {
105
+ const { coverage } = await import("./commands/coverage.js");
106
+ await coverage(changeName, opts);
107
+ });
108
+ program
109
+ .command("flake [change-name]")
110
+ .description("Detect static flake patterns in Playwright test files")
111
+ .option("--json", "Output results as JSON")
112
+ .option("--gate <severity>", "Exit non-zero if findings meet severity (HIGH|MEDIUM|ALL)")
113
+ .action(async (changeName, opts) => {
114
+ const { flake } = await import("./commands/flake.js");
115
+ await flake(changeName, opts);
116
+ });
96
117
  program
97
118
  .command("explore")
98
119
  .description("Explore routes in parallel with Playwright")
99
120
  .option("-p, --parallel <n>", "Number of parallel workers", (v) => parseInt(v, 10), 4)
100
121
  .option("-n, --dry-run", "Show what would be explored without running")
101
122
  .action(async (opts) => {
123
+ const { explore } = await import("./commands/explore.js");
124
+ const { checkForUpdate } = await import("./shared/version-check.js");
102
125
  await explore(opts);
103
126
  await checkForUpdate(pkg.version);
104
127
  });
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -91,3 +91,10 @@
91
91
  4. 用户拒绝提供 → 用 stub / `throw` / `return null` 让代码显式失败,**禁止**静默编造值
92
92
 
93
93
  **纯前端项目**:若存在 OpenAPI / 接口文档 / MCP 暴露的接口,必须查阅真实定义后引用,并标注来源(例如 `// 来源: docs/api/openapi.yaml#/paths/...`),禁止凭印象编造 endpoint / path / 字段名。
94
+
95
+ ## 7. 临时文件管理
96
+
97
+ 所有工具(Chrome DevTools MCP、截图、日志、heapdump 等)产生的非源码临时文件必须放项目根目录 `tmp/` 下:
98
+ - 平铺,不分子目录
99
+ - 文件名含时间戳(避免覆盖)
100
+ - `tmp/` 中超过 24 小时的文件可以在 commit 前删除
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openspec-playwright",
3
- "version": "0.3.46",
3
+ "version": "0.3.48",
4
4
  "description": "OpenSpec + Playwright E2E verification setup tool for Claude Code",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,35 +0,0 @@
1
- /**
2
- * Vision check command: Analyze screenshots for layout anomalies using Ollama VLM.
3
- *
4
- * Modes:
5
- * --screenshots "..." Analyze existing files (single mode)
6
- * --url ... --viewport ... Capture from URL at multiple viewports
7
- * --baseline Save as baseline (with --screenshots or --url+viewport)
8
- * --diff Compare against baseline
9
- * --report <path> Generate HTML report
10
- *
11
- * Exit codes:
12
- * 0 = Check completed (with or without anomalies)
13
- * 1 = Ollama not available (skipped)
14
- * 2 = Configuration missing or invalid (skipped)
15
- */
16
- export interface VisionCheckOptions {
17
- screenshots: string;
18
- config?: string;
19
- parallel?: number;
20
- output?: string;
21
- dryRun?: boolean;
22
- severity?: string;
23
- json?: boolean;
24
- viewport?: string;
25
- url?: string;
26
- baseline?: boolean;
27
- diff?: boolean;
28
- report?: string;
29
- threshold?: number;
30
- noCache?: boolean;
31
- }
32
- /**
33
- * Main vision check command.
34
- */
35
- export declare function visionCheck(options: VisionCheckOptions): Promise<void>;