openspec-playwright 0.1.59 → 0.1.61

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.
@@ -5,7 +5,7 @@ license: MIT
5
5
  compatibility: Requires openspec CLI, Playwright (with browsers installed), and @playwright/mcp (globally installed via `claude mcp add playwright npx @playwright/mcp@latest`).
6
6
  metadata:
7
7
  author: openspec-playwright
8
- version: "2.9"
8
+ version: "2.11"
9
9
  ---
10
10
 
11
11
  ## Input
@@ -32,6 +32,14 @@ Two modes, same pipeline:
32
32
 
33
33
  Both modes update `app-knowledge.md` and `app-exploration.md`. All `.spec.ts` files run together as regression suite.
34
34
 
35
+ **Role mapping** (Playwright Test Agents terminology):
36
+
37
+ | Role | This SKILL | What it does |
38
+ |------|-----------|--------------|
39
+ | Planner | Step 4–5 | Explores app via Playwright MCP → produces test-plan.md |
40
+ | Generator | Step 6 | Transforms test-plan.md → `.spec.ts` with verified selectors |
41
+ | Healer | Step 9 | Executes tests, repairs failures via Playwright MCP |
42
+
35
43
  ## Testing principles
36
44
 
37
45
  **UI first** — Test every user flow through the browser UI. E2E validates that users can accomplish tasks in the real interface, not just that the backend responds correctly.
@@ -98,7 +106,7 @@ Run the seed test before generating tests:
98
106
  npx playwright test tests/playwright/seed.spec.ts --project=chromium
99
107
  ```
100
108
 
101
- This validates: app server reachable, auth fixtures initialized, Playwright working.
109
+ Seed test initializes the `page` context — it runs all fixtures, hooks, and globalSetup. Not just a smoke check: it also validates that auth setup, BASE_URL, and Playwright are fully functional.
102
110
 
103
111
  **If seed test fails**: Stop and report. Fix the environment before proceeding.
104
112
 
@@ -203,6 +211,7 @@ After writing `app-exploration.md`, extract **project-level shared knowledge** a
203
211
  | Common Selector Patterns | New patterns discovered that apply across routes |
204
212
  | SPA Routing | SPA framework, routing behavior |
205
213
  | Project Conventions | BASE_URL, auth method, multi-user roles |
214
+ | Selector Fixes | Healed selectors (see Step 9) — route, old selector, new selector, reason |
206
215
 
207
216
  Append only new/changed items — preserve existing content.
208
217
 
@@ -357,8 +366,6 @@ The CLI handles: server lifecycle, port mismatch, report generation.
357
366
  If tests fail → use Playwright MCP tools to inspect UI, fix selectors, re-run.
358
367
 
359
368
  **Healer MCP tools** (in order of use):
360
- <!-- MCP_VERSION: 0.0.68 -->
361
-
362
369
  <!-- MCP_VERSION: 0.0.70 -->
363
370
 
364
371
  | Tool | Purpose |
@@ -381,7 +388,7 @@ If tests fail → use Playwright MCP tools to inspect UI, fix selectors, re-run.
381
388
  | **Timing issue** | `waitFor`/`page.evaluate` timeout | Switch to `request` API or add `waitFor` → re-run |
382
389
  | **page.evaluate with fetch** | `fetch` in browser context, CORS errors | Switch to `page.request` API → re-run |
383
390
 
384
- 3. **Heal** (≤3 attempts): snapshot → fix → re-run
391
+ 3. **Heal** (≤3 attempts): snapshot → fix → re-run. If healed successfully → append to `app-knowledge.md` → **Selector Fixes** table: route, old selector → new selector, reason.
385
392
  4. **After 3 failures**: collect evidence checklist → `test.skip()` if app bug, report recommendation if test bug
386
393
 
387
394
  ### 10. False Pass Detection
@@ -21,6 +21,13 @@ export function getStoredMcpVersion(skillContent) {
21
21
  const end = skillContent.indexOf(' -->', idx);
22
22
  return skillContent.slice(idx + MCP_VERSION_MARKER.length, end).trim();
23
23
  }
24
+ /** Remove all existing MCP_VERSION comment lines from content */
25
+ function removeMcpVersionMarkers(content) {
26
+ return content
27
+ .split('\n')
28
+ .filter(line => !line.trim().startsWith(MCP_VERSION_MARKER))
29
+ .join('\n');
30
+ }
24
31
  /** Build the Healer tools table markdown */
25
32
  function buildHealerTable(version, tools) {
26
33
  const rows = tools.map(t => `| \`${t.name}\` | ${t.purpose} |`).join('\n');
@@ -28,14 +35,15 @@ function buildHealerTable(version, tools) {
28
35
  }
29
36
  /** Replace the Healer tools table in SKILL.md */
30
37
  export function updateHealerTable(skillContent, version, tools) {
31
- const start = skillContent.indexOf('| Tool | Purpose |');
38
+ const noMarkers = removeMcpVersionMarkers(skillContent);
39
+ const start = noMarkers.indexOf('| Tool | Purpose |');
32
40
  if (start === -1)
33
41
  return skillContent;
34
- let end = skillContent.indexOf('\n\n', start);
42
+ let end = noMarkers.indexOf('\n\n', start);
35
43
  if (end === -1)
36
- end = skillContent.length;
37
- const before = skillContent.slice(0, start);
38
- const after = skillContent.slice(end);
44
+ end = noMarkers.length;
45
+ const before = noMarkers.slice(0, start);
46
+ const after = noMarkers.slice(end);
39
47
  return before + buildHealerTable(version, tools) + after;
40
48
  }
41
49
  /** Fetch latest @playwright/mcp version from npm registry */
@@ -1 +1 @@
1
- {"version":3,"file":"mcpSync.js","sourceRoot":"","sources":["../../src/commands/mcpSync.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"mcpSync.js","sourceRoot":"","sources":["../../src/commands/mcpSync.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ /** Record of tool name -> purpose description */
2
+ export interface McpTool {
3
+ name: string;
4
+ title: string;
5
+ description: string;
6
+ purpose: string;
7
+ }
8
+ /**
9
+ * Download and parse the @playwright/mcp README to extract all browser_* tools.
10
+ */
11
+ export declare function fetchMcpTools(): McpTool[];
12
+ /**
13
+ * Update the Healer MCP tools table in SKILL.md.
14
+ * Only syncs the 5 core Healer tools; all other MCP tools are
15
+ * general-purpose automation not specific to healing.
16
+ * Deduplicates by tool name.
17
+ */
18
+ export declare function updateSkillHealerTable(skillPath: string, tools: McpTool[]): void;
19
+ /**
20
+ * Main entry point: fetch tools and update SKILL.md.
21
+ * Call this after installing the skill in init.ts and update.ts.
22
+ */
23
+ export declare function syncMcpTools(skillPath: string): void;
@@ -0,0 +1,130 @@
1
+ /**
2
+ * Shared utilities for Playwright MCP tool synchronization.
3
+ * Parses @playwright/mcp README to extract browser_* tool names and
4
+ * updates the Healer MCP tools table in SKILL.md.
5
+ */
6
+ import { execSync } from 'child_process';
7
+ import { existsSync, readFileSync, writeFileSync } from 'fs';
8
+ import { join } from 'path';
9
+ import chalk from 'chalk';
10
+ /** Cached directory for npm temp files */
11
+ const TMP_DIR = '/tmp/openspec-pw-mcp';
12
+ /**
13
+ * Get the latest @playwright/mcp version from npm registry.
14
+ * Falls back to cached README if network fails.
15
+ */
16
+ function getLatestVersion() {
17
+ try {
18
+ const json = execSync('npm show @playwright/mcp version --json', { stdio: 'pipe', encoding: 'utf-8' }).trim();
19
+ return JSON.parse(json);
20
+ }
21
+ catch {
22
+ return '0.0.68'; // fallback to known version
23
+ }
24
+ }
25
+ /**
26
+ * Download and parse the @playwright/mcp README to extract all browser_* tools.
27
+ */
28
+ export function fetchMcpTools() {
29
+ const version = getLatestVersion();
30
+ const tmpPkg = join(TMP_DIR, 'package');
31
+ // Clean and prepare temp dir
32
+ execSync(`rm -rf ${TMP_DIR} && mkdir -p ${TMP_DIR}`, { stdio: 'pipe' });
33
+ // Download tarball
34
+ execSync(`cd ${TMP_DIR} && npm pack @playwright/mcp@${version} --pack-destination ${TMP_DIR}`, { stdio: 'pipe' });
35
+ const tarball = execSync(`ls -t ${TMP_DIR}/playwright-mcp-*.tgz | head -1`, { encoding: 'utf-8' }).trim();
36
+ execSync(`tar -xzf "${tarball}" -C ${TMP_DIR} --strip-components=1`, { stdio: 'pipe' });
37
+ const readme = readFileSync(join(TMP_DIR, 'README.md'), 'utf-8');
38
+ return parseToolsFromReadme(readme);
39
+ }
40
+ /**
41
+ * Parse browser_* tools from the MCP README markdown.
42
+ * Format:
43
+ * - **browser_navigate**
44
+ * - Title: Navigate to a URL
45
+ * - Description: Navigate to a URL
46
+ */
47
+ function parseToolsFromReadme(readme) {
48
+ const tools = [];
49
+ // Match tool blocks: **browser_name** followed by Title and Description
50
+ const blockRegex = /- \*\*([a-z_]+)\*\*[\s\S]*?- Title: ([^\n]+)\n[\s\S]*?- Description: ([^\n]+)/g;
51
+ let match;
52
+ while ((match = blockRegex.exec(readme)) !== null) {
53
+ const name = match[1];
54
+ const title = match[2].trim();
55
+ const description = match[3].trim();
56
+ // Map tool name to Healer purpose
57
+ const purpose = mapToolToPurpose(name, title, description);
58
+ tools.push({ name, title, description, purpose });
59
+ }
60
+ return tools;
61
+ }
62
+ /** Map a browser_* tool to its Healer purpose in the SKILL */
63
+ function mapToolToPurpose(name, title, description) {
64
+ const map = {
65
+ browser_navigate: 'Go to the failing test\'s page',
66
+ browser_snapshot: 'Get page structure to find equivalent selectors',
67
+ browser_console_messages: 'Diagnose JS errors that may cause failures',
68
+ browser_take_screenshot: 'Visually compare before/after fixes',
69
+ browser_run_code: 'Execute custom fix logic (optional)',
70
+ };
71
+ return map[name] || title;
72
+ }
73
+ /**
74
+ * Update the Healer MCP tools table in SKILL.md.
75
+ * Only syncs the 5 core Healer tools; all other MCP tools are
76
+ * general-purpose automation not specific to healing.
77
+ * Deduplicates by tool name.
78
+ */
79
+ export function updateSkillHealerTable(skillPath, tools) {
80
+ if (!existsSync(skillPath))
81
+ return;
82
+ const content = readFileSync(skillPath, 'utf-8');
83
+ // Only the 5 core Healer tools
84
+ const healerToolNames = [
85
+ 'browser_navigate',
86
+ 'browser_snapshot',
87
+ 'browser_console_messages',
88
+ 'browser_take_screenshot',
89
+ 'browser_run_code',
90
+ ];
91
+ // Filter to healer tools, deduplicate by name
92
+ const healerTools = tools.filter(t => healerToolNames.includes(t.name));
93
+ const seen = new Set();
94
+ const unique = healerTools.filter(t => {
95
+ if (seen.has(t.name))
96
+ return false;
97
+ seen.add(t.name);
98
+ return true;
99
+ });
100
+ // Build new table rows
101
+ const rows = unique.map(t => `| \`${t.name}\` | ${t.purpose} |`).join('\n');
102
+ // Replace the existing table
103
+ const tableHeader = '| Tool | Purpose |';
104
+ const tableSep = '|------|---------|';
105
+ const start = content.indexOf(tableHeader);
106
+ const end = content.indexOf(tableSep, start + 1);
107
+ if (start === -1 || end === -1)
108
+ return;
109
+ const newSection = tableHeader + '\n' + tableSep + '\n' + rows;
110
+ const newContent = content.substring(0, start) +
111
+ newSection +
112
+ content.substring(end + tableSep.length);
113
+ writeFileSync(skillPath, newContent, 'utf-8');
114
+ }
115
+ /**
116
+ * Main entry point: fetch tools and update SKILL.md.
117
+ * Call this after installing the skill in init.ts and update.ts.
118
+ */
119
+ export function syncMcpTools(skillPath) {
120
+ try {
121
+ const tools = fetchMcpTools();
122
+ updateSkillHealerTable(skillPath, tools);
123
+ console.log(chalk.green(` ✓ Healer tools synced from @playwright/mcp`));
124
+ }
125
+ catch (err) {
126
+ console.log(chalk.yellow(` ⚠ Failed to sync MCP tools: ${err}`));
127
+ console.log(chalk.gray(' SKILL.md MCP tools table may be stale'));
128
+ }
129
+ }
130
+ //# sourceMappingURL=mcp-tools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-tools.js","sourceRoot":"","sources":["../../src/utils/mcp-tools.ts"],"names":[],"mappings":"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"}
@@ -1,84 +1,42 @@
1
1
  # Claude Code Employee-Grade Configuration
2
2
 
3
- > 员工级行为规范,适用于所有项目。
4
- > 本项目遵循 OpenSpec 规范驱动开发流程(详见 /openspec/)。
3
+ > 员工级行为规范,适用于 OpenSpec 项目。
4
+ > 遵循 OpenSpec 规范驱动开发流程(详见 /openspec/)。
5
5
 
6
6
  ---
7
7
 
8
8
  ## 一、代码质量
9
9
 
10
- ### 强制验证
10
+ **验证后才能报告成功**。运行项目的 linter 和 type checker:查看 `package.json`/`pyproject.toml`/`go.mod`/`Cargo.toml` 中的 scripts,使用对应的 `lint`/`typecheck` 命令。不存在时,明确告知用户,不得假装成功。
11
11
 
12
- 禁止在验证完成前报告成功。每次文件修改后必须运行:
12
+ **拒绝'够用就行'**。架构缺陷、状态重复、模式不一致——说出来并修复。高级工程师在 code review 中会拒绝什么?全部修掉。
13
13
 
14
- ```bash
15
- npx tsc --noEmit && npx eslint . --quiet
16
- ```
17
-
18
- 如项目无类型检查,明确告知用户,不得假装成功。
19
-
20
- ### 高级工程师标准
21
-
22
- 不接受"最简单方案"和"不超出需求"作为偷懒的借口。如果架构有缺陷、状态重复、模式不一致——说出来并修复。问自己:**高级工程师在 code review 中会拒绝什么?** 把那些全部修掉。
14
+ **安全防护**:防范 XSS、SQL 注入、命令注入(OWASP Top 10)。
23
15
 
24
16
  ---
25
17
 
26
18
  ## 二、上下文管理
27
19
 
28
- ### Step 0 规则
29
-
30
- 任何结构性重构(>300 LOC)之前,先删除所有死代码(未使用 import/export/prop/console.log)。单独提交清理,再做重构。
31
- 死代码会提前消耗 token budget,触发 auto-compaction——每次失败会浪费大量 API 调用。保持代码干净直接节省成本。
32
-
33
- ### 文件读取分段
20
+ **文件读取完整**:超过 500 行的文件,不要假设单次读取覆盖了完整文件——根据需要分次读取相关段落,或编辑前重新读取完整文件。超过 10 条消息后,编辑任何文件前强制重新读取。
34
21
 
35
- 超过 500 行的文件,不要假设单次读取覆盖了完整文件。根据需要分次读取相关段落,或编辑前重新读取完整文件以确保上下文最新。
36
-
37
- ### 上下文衰减
38
-
39
- 超过 10 条消息后,编辑任何文件前必须重新读取。
22
+ **重构前清死代码**:未使用的 import/export/prop/console.log 先删掉,单独提交,再做重构。
40
23
 
41
24
  ---
42
25
 
43
- ## 三、大规模任务
44
-
45
- ### 子 Agent 并行化
46
-
47
- 任务涉及超过 5 个独立文件时,启动并行子 agent(每个 5-8 个文件),每个拥有独立 token budget。
26
+ ## 三,大规模任务
48
27
 
49
- ### 分阶段执行
28
+ **子 Agent 并行化**:任务涉及超过 5 个独立文件时,启动并行子 agent(每个 5-8 个文件),每个拥有独立 token budget。
50
29
 
51
- 不要在单次响应中完成多文件重构。每个阶段不超过 5 个文件,完成后验证,等待用户批准再继续。
30
+ **分阶段执行**:每个阶段不超过 5 个文件,完成后验证,等待用户批准再继续。
52
31
 
53
32
  ---
54
33
 
55
34
  ## 四、工具限制
56
35
 
57
- ### 工具结果截断(50,000 字符)
58
-
59
- 搜索结果异常少时,用更窄范围重新运行。怀疑截断时告知用户。
60
-
61
- ### Grep 不是 AST
62
-
63
- 重命名函数/类型/变量时,必须分开搜索:
64
-
65
- - 直接调用和引用
66
- - 类型级引用(interface、泛型)
67
- - 字符串字面量(动态引用)
68
- - `require()` / `import`
69
- - barrel file 和 re-export
70
- - 测试文件和 mock
71
-
72
- 不得假设一次 grep 覆盖所有情况。
36
+ **搜索维度完整**:重命名函数/类型/变量时,必须覆盖:直接调用、类型级引用、字符串字面量、`require()`/`import`、barrel file、测试 mock。不得假设一次 grep 覆盖所有情况。
73
37
 
74
38
  ---
75
39
 
76
40
  ## 五、编辑安全
77
41
 
78
- ### 编辑完整性
79
-
80
- 每次编辑前重新读取文件,编辑后再次读取确认变更正确应用。同一文件连续编辑不超过 3 次,中间必须验证读取。
81
-
82
- ### 变更完整性
83
-
84
- 重命名或签名变更完成后,明确告知用户可能遗漏的区域(动态引用、测试 mock 等),提示人工复查。
42
+ **验证后才算完成**:编辑后再次读取确认变更正确应用。同一文件连续编辑不超过 3 次,中间必须验证。变更完成后,明确告知用户可能遗漏的区域(动态引用、测试 mock 等),提示人工复查。
@@ -0,0 +1,56 @@
1
+ name: playwright-e2e
2
+ version: 1
3
+ description: Playwright E2E verification for an OpenSpec change
4
+
5
+ artifacts:
6
+ - id: test-plan
7
+ generates: specs/playwright/test-plan.md
8
+ description: Test plan derived from change specs
9
+ template: test-plan.md
10
+ instruction: |
11
+ Generate a test plan from the change specs. Each functional requirement becomes
12
+ one or more test cases.
13
+
14
+ For each test case, specify:
15
+ - **Test name** (kebab-case): what it verifies
16
+ - **Route/Page**: which URL or UI component it targets
17
+ - **Prerequisites**: auth role needed (user/admin/guest/none)
18
+ - **Happy path**: main user flow to verify
19
+ - **Error paths**: key error conditions to verify
20
+
21
+ Mark tests with role tags: `@role(user)`, `@role(admin)`, `@role(guest)`, `@role(none)`.
22
+ Mark auth requirement: `@auth(required)` or `@auth(none)`.
23
+
24
+ - id: e2e-test
25
+ generates: tests/playwright/<change>.spec.ts
26
+ description: Playwright E2E test suite
27
+ template: e2e-test.ts
28
+ instruction: |
29
+ Generate Playwright tests from the test-plan.
30
+
31
+ Rules:
32
+ - Follow the page object pattern from seed.spec.ts
33
+ - Prefer `data-testid` selectors, fall back to semantic selectors
34
+ - Each test maps to one test case from the test-plan
35
+ - Use `@project(user)` / `@project(admin)` for role-specific tests
36
+ - Cover happy path AND key error paths
37
+ - Name tests descriptively: `test('shows error on invalid input')`
38
+
39
+ - id: playwright-config
40
+ generates: playwright.config.ts
41
+ description: Playwright config with webServer and auth projects
42
+ template: playwright.config.ts
43
+ instruction: |
44
+ Configure Playwright for E2E testing.
45
+
46
+ - Set webServer command and url from BASE_URL
47
+ - Set baseURL for all tests
48
+ - Add auth setup project if credentials are configured
49
+ - Preserve any existing config fields (browsers, retries, etc.)
50
+ - Do NOT overwrite existing projects unless they conflict
51
+
52
+ apply:
53
+ requires: [test-plan, e2e-test]
54
+ instruction: |
55
+ Run tests via: openspec-pw run <change-name>
56
+ Analyze results and generate report at openspec/reports/playwright-e2e-<change>-<timestamp>.md
@@ -0,0 +1,55 @@
1
+ import { test, expect, Page } from '@playwright/test';
2
+
3
+ // ──────────────────────────────────────────────
4
+ // Test plan: <change-name>
5
+ // Generated from: openspec/changes/<change-name>/specs/playwright/test-plan.md
6
+ // ──────────────────────────────────────────────
7
+
8
+ const BASE_URL = process.env.BASE_URL || 'http://localhost:3000';
9
+
10
+ /**
11
+ * Page Object Pattern - add selectors for your app's pages
12
+ */
13
+ class AppPage {
14
+ constructor(private page: Page) {}
15
+
16
+ async goto(path: string = '/') {
17
+ await this.page.goto(`${BASE_URL}${path}`);
18
+ }
19
+
20
+ async getByTestId(id: string) {
21
+ return this.page.locator(`[data-testid="${id}"]`);
22
+ }
23
+
24
+ async waitForToast(message?: string) {
25
+ if (message) {
26
+ await this.page.getByText(message, { state: 'visible' }).waitFor();
27
+ }
28
+ }
29
+ }
30
+
31
+ function createPage(page: Page): AppPage {
32
+ return new AppPage(page);
33
+ }
34
+
35
+ // ──────────────────────────────────────────────
36
+ // Tests - generated from test-plan.md
37
+ // Customize selectors and assertions to match your app
38
+ // ──────────────────────────────────────────────
39
+
40
+ test.describe('<change-name>: E2E verification', () => {
41
+
42
+ test.beforeEach(async ({ page }) => {
43
+ const app = createPage(page);
44
+ await app.goto('/');
45
+ });
46
+
47
+ // TODO: Add test cases from specs/playwright/test-plan.md
48
+ // Example:
49
+ // test('shows expected content on page load', async ({ page }) => {
50
+ // const app = createPage(page);
51
+ // await app.goto('/');
52
+ // await expect(app.getByTestId('main-heading')).toBeVisible();
53
+ // });
54
+
55
+ });
@@ -0,0 +1,52 @@
1
+ import { defineConfig, devices } from '@playwright/test';
2
+ import { readFileSync, existsSync } from 'fs';
3
+ import { join } from 'path';
4
+
5
+ // ─── BASE_URL: prefer env, then seed.spec.ts, then default ───
6
+ const seedSpec = join(__dirname, '../tests/playwright/seed.spec.ts');
7
+ let baseUrl = process.env.BASE_URL || 'http://localhost:3000';
8
+ if (existsSync(seedSpec)) {
9
+ const content = readFileSync(seedSpec, 'utf-8');
10
+ const m = content.match(/BASE_URL\s*=\s*process\.env\.BASE_URL\s*\|\|\s*['"]([^'"]+)['"]/);
11
+ if (m) baseUrl = m[1];
12
+ }
13
+
14
+ // ─── Dev command: detect from package.json scripts ───
15
+ const pkgPath = join(__dirname, '../package.json');
16
+ let devCmd = 'npm run dev';
17
+ if (existsSync(pkgPath)) {
18
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
19
+ const scripts = pkg.scripts ?? {};
20
+ // Prefer in order: dev, start, serve, preview
21
+ devCmd = scripts.dev ?? scripts.start ?? scripts.serve ?? scripts.preview ?? devCmd;
22
+ }
23
+
24
+ export default defineConfig({
25
+ testDir: '../tests/playwright',
26
+ // Keep test artifacts inside tests/playwright/ instead of project root
27
+ outputDir: '../tests/playwright/test-results',
28
+ fullyParallel: true,
29
+ forbidOnly: !!process.env.CI,
30
+ retries: process.env.CI ? 2 : 0,
31
+ workers: process.env.CI ? 1 : undefined,
32
+ reporter: 'list',
33
+
34
+ use: {
35
+ baseURL: baseUrl,
36
+ trace: 'on-first-retry',
37
+ },
38
+
39
+ // Dev server lifecycle - Playwright starts/stops automatically
40
+ webServer: {
41
+ command: devCmd,
42
+ url: baseUrl,
43
+ timeout: 120000,
44
+ reuseExistingServer: true,
45
+ },
46
+
47
+ // Setup project for authentication (configured by openspec-pw run)
48
+ projects: [
49
+ { name: 'setup', testMatch: /.*\.setup\.ts/ },
50
+ { name: 'chromium', use: { ...devices['Desktop Chrome'] } },
51
+ ],
52
+ });
@@ -0,0 +1,27 @@
1
+ # E2E Verify Report: <change-name>
2
+
3
+ **Change**: `<change-name>`
4
+ **Generated**: `<timestamp>`
5
+ **Auth**: `<required|none>`
6
+
7
+ ## Summary
8
+
9
+ | Check | Status |
10
+ |-------|--------|
11
+ | Tests Run | <N> |
12
+ | Passed | <X> |
13
+ | Failed | <Y> |
14
+ | Auto-heals | <Z> |
15
+ | Final Status | <PASS|FAIL> |
16
+
17
+ ## Test Results
18
+
19
+ <!-- List each test with pass/fail status -->
20
+
21
+ ## Auto-Heal Log
22
+
23
+ <!-- If heals were performed, document each attempt -->
24
+
25
+ ## Recommendations
26
+
27
+ <!-- For failed tests, specific file:line references and fixes -->
@@ -0,0 +1,24 @@
1
+ # Test Plan: <change-name>
2
+
3
+ Generated from: `openspec/changes/<change-name>/specs/`
4
+
5
+ ## Auth Requirements
6
+
7
+ <!-- Mark auth requirements based on specs analysis: -->
8
+ - Auth required: **yes / no**
9
+ - Roles needed: none / user / admin / user+admin
10
+
11
+ ## Test Cases
12
+
13
+ ### <test-name>
14
+
15
+ - **Route**: `/<page>`
16
+ - **Role**: `@role(<role>)`
17
+ - **Auth**: `@auth(required|none)`
18
+
19
+ **Happy path:**
20
+ - Step 1: ...
21
+ - Step 2: ...
22
+
23
+ **Error paths:**
24
+ - ...
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openspec-playwright",
3
- "version": "0.1.59",
3
+ "version": "0.1.61",
4
4
  "description": "OpenSpec + Playwright E2E verification setup tool for Claude Code",
5
5
  "type": "module",
6
6
  "bin": {
@@ -14,9 +14,10 @@
14
14
  "release": "npm version patch && npm run build && git push && git push --tags && npm publish"
15
15
  },
16
16
  "dependencies": {
17
+ "@types/tar": "^6.1.13",
17
18
  "chalk": "^5.3.0",
18
19
  "commander": "^12.1.0",
19
- "tar": "^7.0.0"
20
+ "tar": "^7.5.13"
20
21
  },
21
22
  "devDependencies": {
22
23
  "@types/node": "^22.0.0",
package/release-notes.md CHANGED
@@ -1,5 +1,5 @@
1
1
  ## What's Changed
2
2
 
3
- - chore: bump version to 0.1.59
3
+ - refactor(employee-standards): remove Claude Code system prompt duplicates
4
4
 
5
- **Full Changelog**: https://github.com/wxhou/openspec-playwright/releases/tag/v0.1.59
5
+ **Full Changelog**: https://github.com/wxhou/openspec-playwright/releases/tag/v0.1.61
@@ -70,6 +70,16 @@ Priority: `[data-testid]` > `getByRole` > `getByLabel` > `getByText` > CSS
70
70
  | auth method | API / UI | See auth.setup.ts |
71
71
  | multi-user roles | `<roles>` | e.g. admin, user, guest |
72
72
 
73
+ ## Selector Fixes (Healer memory)
74
+
75
+ Persists selector repairs across sessions. Prevents the same selector from being healed repeatedly.
76
+
77
+ | Date | Route | Old Selector | New Selector | Reason |
78
+ |------|-------|-------------|-------------|--------|
79
+ | | | | | |
80
+
81
+ ---
82
+
73
83
  ## Changelog
74
84
 
75
85
  | Date | Change | By |
@@ -78,4 +88,4 @@ Priority: `[data-testid]` > `getByRole` > `getByLabel` > `getByText` > CSS
78
88
 
79
89
  ---
80
90
 
81
- > **Updating this file**: After each E2E exploration (Step 4), extract new shared patterns and update this file. Generator (Step 6) reads this before writing tests.
91
+ > **Updating this file**: After each E2E exploration (Step 4), extract new shared patterns and update this file. Generator (Step 6) reads this before writing tests. After Healer repairs (Step 9), append the fix here.
@@ -24,6 +24,14 @@ export function getStoredMcpVersion(skillContent: string): string | null {
24
24
  return skillContent.slice(idx + MCP_VERSION_MARKER.length, end).trim();
25
25
  }
26
26
 
27
+ /** Remove all existing MCP_VERSION comment lines from content */
28
+ function removeMcpVersionMarkers(content: string): string {
29
+ return content
30
+ .split('\n')
31
+ .filter(line => !line.trim().startsWith(MCP_VERSION_MARKER))
32
+ .join('\n');
33
+ }
34
+
27
35
  /** Build the Healer tools table markdown */
28
36
  function buildHealerTable(version: string, tools: Array<{ name: string; purpose: string }>): string {
29
37
  const rows = tools.map(t => `| \`${t.name}\` | ${t.purpose} |`).join('\n');
@@ -36,13 +44,14 @@ export function updateHealerTable(
36
44
  version: string,
37
45
  tools: Array<{ name: string; purpose: string }>
38
46
  ): string {
39
- const start = skillContent.indexOf('| Tool | Purpose |');
47
+ const noMarkers = removeMcpVersionMarkers(skillContent);
48
+ const start = noMarkers.indexOf('| Tool | Purpose |');
40
49
  if (start === -1) return skillContent;
41
- let end = skillContent.indexOf('\n\n', start);
42
- if (end === -1) end = skillContent.length;
50
+ let end = noMarkers.indexOf('\n\n', start);
51
+ if (end === -1) end = noMarkers.length;
43
52
 
44
- const before = skillContent.slice(0, start);
45
- const after = skillContent.slice(end);
53
+ const before = noMarkers.slice(0, start);
54
+ const after = noMarkers.slice(end);
46
55
  return before + buildHealerTable(version, tools) + after;
47
56
  }
48
57
 
@@ -3,6 +3,7 @@
3
3
  // Customize the page object and base URL for your application
4
4
 
5
5
  import { test, expect, Page } from '@playwright/test';
6
+ import { existsSync } from 'fs';
6
7
 
7
8
  // Customize these for your application
8
9
  const BASE_URL = process.env.BASE_URL || 'http://localhost:3000';
@@ -63,6 +64,27 @@ test.describe('Application smoke tests', () => {
63
64
  });
64
65
  });
65
66
 
67
+ // ──────────────────────────────────────────────
68
+ // Environment validation (runs in Step 3)
69
+ // ──────────────────────────────────────────────
70
+
71
+ test.describe('Environment validation', () => {
72
+ test('BASE_URL responds 200', async ({ page }) => {
73
+ const res = await page.request.get(`${BASE_URL}/`);
74
+ expect(res.status(), `BASE_URL ${BASE_URL} returned ${res.status()}`).toBeLessThan(500);
75
+ });
76
+
77
+ test('auth storageState exists if credentials provided', async () => {
78
+ if (!process.env.E2E_USERNAME) return; // skip if no credentials configured
79
+ const authPath = 'playwright/.auth/user.json';
80
+ if (!existsSync(authPath)) {
81
+ throw new Error(
82
+ `Auth not configured. Run:\n npx playwright test --project=setup\nThen re-run /opsx:e2e.`
83
+ );
84
+ }
85
+ });
86
+ });
87
+
66
88
  // ──────────────────────────────────────────────
67
89
  // Example: Role-based tests with @tag
68
90
  // Use tags (@admin, @user) for permission filtering instead of
Binary file