openspec-playwright 0.3.47 → 0.3.49

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
@@ -97,6 +97,23 @@ program
97
97
  await audit();
98
98
  await checkForUpdate(pkg.version);
99
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
+ });
100
117
  program
101
118
  .command("explore")
102
119
  .description("Explore routes in parallel with Playwright")
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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"}
@@ -58,6 +58,8 @@
58
58
 
59
59
  **搜索要全**:用 Grep 搜内容,用 Glob 搜文件名。两者缺一不可。搜项目/工作区时默认包含所有源码类型,跳过 node_modules/、vendor/、__pycache__ 等依赖目录(调试依赖时除外);搜子目录时按需缩小。重命名时覆盖调用、类型、字符串、`import`、barrel file、测试 mock,不得假设一次覆盖所有情况。
60
60
 
61
+ **联网调研优先 agent-reach**:需要查询/调研/搜索等联网操作时,优先使用 agent-reach skill,避免 WebFetch/WebSearch 直接抓页面导致反爬失败。
62
+
61
63
  **编辑要求**:编辑后重新读取文件确认变更正确应用。变更完成后,明确告知用户可能遗漏的区域(动态引用、测试 mock 等),提示人工复查。
62
64
 
63
65
  **禁止脚本改文件**:修改源码文件只能使用内置编辑工具(Read/Edit/Write),禁止用 sed/awk/node -e/python -c 等管道命令改文件。格式化工具(ruff fmt、prettier)除外。
@@ -70,14 +72,10 @@
70
72
 
71
73
  ## 5. 完整生产工作流
72
74
 
73
- 按需叠加的评审:`/plan-ceo-review`(产品战略)/ `/plan-eng-review`(架构)/ `/plan-design-review`(UX)。
75
+ **Superpowers(可选增强)**:安装 `/plugin install superpowers@claude-plugins-official` 后,Superpowers 在 propose → apply → verify 阶段提供更严谨的 AI 方法论(对话式 spec 探索、TDD、subagent 并行实现)。Superpowers **不改变 OpenSpec 主流程**,所有阶段仍由用户手动触发。
74
76
 
75
77
  阶段命令即触发器:`/opsx:propose` → `/opsx:apply` → `/opsx:verify` → e2e 命令(Claude Code 用 `/opsx:e2e`,OpenCode 用 `/opsx-e2e`)→ `/opsx:archive`。**所有阶段由用户手动触发,AI 不自动进入下一阶段**(详见 §2 阶段隔离)。
76
78
 
77
- 可用 `npx openspec --help` 查看更多 OpenSpec 命令。
78
-
79
- 方向不明时可用 `/office-hours` 做创意验证。**Healer 需要 Playwright 环境**;非 Node.js 项目请参考各自语言的 OpenSpec 测试集成。
80
-
81
79
  ## 6. 禁止编撰数据
82
80
 
83
81
  无论前后端一体还是纯前端项目,AI 助手**严禁**主动编撰任何数据填充代码,除非用户明确同意。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openspec-playwright",
3
- "version": "0.3.47",
3
+ "version": "0.3.49",
4
4
  "description": "OpenSpec + Playwright E2E verification setup tool for Claude Code",
5
5
  "type": "module",
6
6
  "bin": {
@@ -24,7 +24,7 @@
24
24
  > **Full regression is opt-in only.** `openspec-pw run <name>` → one spec file. Do NOT run `npx playwright test` (no file) or `--only-changed` unless user explicitly requests.
25
25
  > **Roles**: Planner (Steps 4–5) → test-plan.md; Generator (Step 6) → `.spec.ts` + Page Objects; Healer (Step 9) → repairs failures via MCP.
26
26
 
27
- Browser exploration is tool-agnostic: gstack (`/browse`), Playwright MCP, or `openspec-pw explore --parallel N`.
27
+ Browser exploration is tool-agnostic: Playwright MCP or `openspec-pw explore --parallel N`.
28
28
 
29
29
  ## Testing Principles
30
30
 
@@ -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>;