openspec-playwright 0.1.40 → 0.1.43
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.
- package/.claude/skills/openspec-e2e/SKILL.md +83 -208
- package/README.md +20 -13
- package/README.zh-CN.md +16 -9
- package/dist/commands/editors.d.ts +2 -1
- package/dist/commands/editors.js +20 -20
- package/dist/commands/editors.js.map +1 -1
- package/openspec-playwright-0.1.43.tgz +0 -0
- package/package.json +5 -2
- package/release-notes.md +2 -2
- package/src/commands/editors.ts +20 -20
- package/templates/auth.setup.ts +17 -4
- package/templates/credentials.yaml +6 -6
- package/tests/editors.test.ts +180 -0
- package/vitest.config.ts +9 -0
- package/openspec-playwright-0.1.40.tgz +0 -0
|
@@ -3,11 +3,9 @@ name: openspec-e2e
|
|
|
3
3
|
description: Run Playwright E2E verification for an OpenSpec change. Use when the user wants to validate that the implementation works end-to-end by running Playwright tests generated from the specs.
|
|
4
4
|
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
|
-
|
|
7
|
-
**Architecture**: Uses CLI + SKILLs (not `init-agents`). This follows Playwright's recommended approach for coding agents — CLI is more token-efficient than loading MCP tool schemas into context. MCP is used only for Healer (UI inspection on failure).
|
|
8
6
|
metadata:
|
|
9
7
|
author: openspec-playwright
|
|
10
|
-
version: "2.
|
|
8
|
+
version: "2.8"
|
|
11
9
|
---
|
|
12
10
|
|
|
13
11
|
## Input
|
|
@@ -25,13 +23,9 @@ metadata:
|
|
|
25
23
|
|
|
26
24
|
## Architecture
|
|
27
25
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
- **🎭 Planner** (Step 4): Consumes OpenSpec specs (`specs/*.md`) and produces `test-plan.md` — combines OpenSpec's structured requirements with LLM编排.
|
|
31
|
-
- **🎭 Generator** (Step 5): Transforms the Markdown test-plan into real Playwright `.spec.ts` files using LLM code generation.
|
|
32
|
-
- **🎭 Healer** (Step 8): Executes the test suite and automatically repairs failing selectors via Playwright MCP tools.
|
|
26
|
+
Pipeline: **Planner** (Step 4) → **Generator** (Step 5) → **Healer** (Step 8).
|
|
33
27
|
|
|
34
|
-
Uses CLI + SKILLs (not `init-agents`).
|
|
28
|
+
Uses CLI + SKILLs (not `init-agents`). CLI is ~4x more token-efficient than loading MCP tool schemas. MCP is used only for Healer (UI inspection on failure).
|
|
35
29
|
|
|
36
30
|
**Schema owns templates. CLI handles execution. Skill handles cognitive work.**
|
|
37
31
|
|
|
@@ -56,7 +50,7 @@ If `openspec/changes/<name>/specs/` is empty, inform the user and stop. E2E requ
|
|
|
56
50
|
|
|
57
51
|
Read all files from `openspec/changes/<name>/specs/*.md`. Extract functional requirements.
|
|
58
52
|
|
|
59
|
-
Detect if auth is required
|
|
53
|
+
Detect if auth is required only when BOTH conditions are met:
|
|
60
54
|
|
|
61
55
|
**Condition A — Explicit markers**: "login", "signin", "logout", "authenticate", "protected", "authenticated", "session", "unauthorized"
|
|
62
56
|
|
|
@@ -71,169 +65,101 @@ Detect if auth is required. Mark as **auth required** only when BOTH conditions
|
|
|
71
65
|
|
|
72
66
|
### 3. Validate environment
|
|
73
67
|
|
|
74
|
-
|
|
75
|
-
|
|
68
|
+
Run the seed test before generating tests:
|
|
76
69
|
```bash
|
|
77
70
|
npx playwright test tests/playwright/seed.spec.ts --project=chromium
|
|
78
71
|
```
|
|
79
72
|
|
|
80
|
-
|
|
81
|
-
- App server is reachable (BASE_URL accessible)
|
|
82
|
-
- Auth fixtures (`storageState`) are initialized if auth is required
|
|
83
|
-
- Playwright browser and config are working
|
|
84
|
-
|
|
85
|
-
**If seed test fails**: Stop and report the failure. User must fix the environment before proceeding.
|
|
73
|
+
This validates: app server reachable, auth fixtures initialized, Playwright working.
|
|
86
74
|
|
|
87
|
-
**If seed test
|
|
75
|
+
**If seed test fails**: Stop and report. Fix the environment before proceeding.
|
|
88
76
|
|
|
89
77
|
### 4. Generate test plan
|
|
90
78
|
|
|
91
|
-
Create `openspec/changes/<name>/specs/playwright/test-plan.md
|
|
92
|
-
-
|
|
93
|
-
-
|
|
94
|
-
-
|
|
95
|
-
-
|
|
79
|
+
Create `openspec/changes/<name>/specs/playwright/test-plan.md`:
|
|
80
|
+
- List each functional requirement as a test case
|
|
81
|
+
- Mark with `@role(user|admin|guest|none)` and `@auth(required|none)`
|
|
82
|
+
- Include happy path AND error paths
|
|
83
|
+
- Reference the route/page each test targets
|
|
96
84
|
|
|
97
|
-
**Idempotency**: If test-plan.md already exists → read it, use it, do NOT regenerate
|
|
85
|
+
**Idempotency**: If test-plan.md already exists → read it, use it, do NOT regenerate.
|
|
98
86
|
|
|
99
|
-
### 5. Generate test file
|
|
87
|
+
### 5. Generate test file
|
|
100
88
|
|
|
101
|
-
|
|
89
|
+
Create `tests/playwright/<name>.spec.ts`:
|
|
102
90
|
|
|
103
91
|
**Read inputs first**:
|
|
104
|
-
- `openspec/changes/<name>/specs/playwright/test-plan.md`
|
|
105
|
-
- `tests/playwright/seed.spec.ts`
|
|
106
|
-
|
|
107
|
-
**Generate Playwright code
|
|
108
|
-
- Follow
|
|
109
|
-
- Prefer `data-testid
|
|
110
|
-
- Include
|
|
111
|
-
- Use `test.describe(...)` for grouping
|
|
92
|
+
- `openspec/changes/<name>/specs/playwright/test-plan.md`
|
|
93
|
+
- `tests/playwright/seed.spec.ts`
|
|
94
|
+
|
|
95
|
+
**Generate** Playwright code for each test case:
|
|
96
|
+
- Follow `seed.spec.ts` structure
|
|
97
|
+
- Prefer `data-testid`; fallback to `getByRole`, `getByLabel`, `getByText`
|
|
98
|
+
- Include happy path AND error/edge cases
|
|
99
|
+
- Use `test.describe(...)` for grouping
|
|
112
100
|
- Each test: `test('描述性名称', async ({ page }) => { ... })`
|
|
113
|
-
- Add `@project(user)` / `@project(admin)` on role-specific tests
|
|
114
101
|
|
|
115
|
-
|
|
102
|
+
#### Anti-Pattern Warnings
|
|
116
103
|
|
|
117
|
-
**🚫
|
|
104
|
+
**🚫 False Pass** — never silently skip when element is missing:
|
|
118
105
|
```typescript
|
|
119
|
-
// WRONG
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
await btn.click();
|
|
123
|
-
await expect(page.getByText('成功')).toBeVisible();
|
|
124
|
-
}
|
|
125
|
-
// ✅ CORRECT: Use assertion — test fails if element is missing
|
|
106
|
+
// WRONG
|
|
107
|
+
if (await btn.isVisible().catch(() => false)) { ... }
|
|
108
|
+
// ✅ CORRECT
|
|
126
109
|
await expect(page.getByRole('button', { name: '取消' })).toBeVisible();
|
|
127
|
-
await page.getByRole('button', { name: '取消' }).click();
|
|
128
|
-
await expect(page.getByText('成功')).toBeVisible();
|
|
129
110
|
```
|
|
130
111
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
**🚫 NEVER rely on Playwright projects for permission filtering:**
|
|
112
|
+
**🚫 Permission filtering** — use `@tag` with `--grep`, not projects:
|
|
134
113
|
```typescript
|
|
135
|
-
|
|
136
|
-
projects: [{ name: 'admin' }, { name: 'user' }]
|
|
137
|
-
|
|
138
|
-
// ✅ CORRECT: Use @tag for permission-based test filtering
|
|
139
|
-
test('admin only - activate subscription', { tag: '@admin' }, async ({ page }) => { ... });
|
|
140
|
-
test('user only - view subscription', { tag: '@user' }, async ({ page }) => { ... });
|
|
114
|
+
test('admin only', { tag: '@admin' }, async ({ page }) => { ... });
|
|
141
115
|
// Run with: npx playwright test --grep "@admin"
|
|
142
116
|
```
|
|
143
117
|
|
|
144
|
-
**🚫
|
|
145
|
-
The auth guard is a **critical security feature**. Skipping it leaves a gap in coverage.
|
|
118
|
+
**🚫 Auth guard** — test with FRESH browser context (no inherited cookies):
|
|
146
119
|
```typescript
|
|
147
|
-
// ✅ CORRECT: Test auth guard with a FRESH browser context (no cookies, no storage)
|
|
148
120
|
test('redirects unauthenticated user to login', async ({ browser }) => {
|
|
149
|
-
const
|
|
150
|
-
const freshPage = await freshContext.newPage();
|
|
121
|
+
const freshPage = await browser.newContext().newPage();
|
|
151
122
|
await freshPage.goto(`${BASE_URL}/dashboard`);
|
|
152
123
|
await expect(freshPage).toHaveURL(/login|auth/);
|
|
153
|
-
await freshContext.close();
|
|
154
|
-
});
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
**Always include error path tests** (not just happy paths):
|
|
158
|
-
- API returns 500 → UI error message displayed?
|
|
159
|
-
- API returns 404 → graceful "not found" handling?
|
|
160
|
-
- Network timeout → retry or error UX?
|
|
161
|
-
- Invalid input → validation message shown?
|
|
162
|
-
|
|
163
|
-
**Example pattern** (from seed.spec.ts):
|
|
164
|
-
```typescript
|
|
165
|
-
import { test, expect } from '@playwright/test';
|
|
166
|
-
|
|
167
|
-
const BASE_URL = process.env.BASE_URL || 'http://localhost:3000';
|
|
168
|
-
|
|
169
|
-
test.describe('Feature Name', () => {
|
|
170
|
-
test('happy path - action succeeds', async ({ page }) => {
|
|
171
|
-
await page.goto(`${BASE_URL}/path`);
|
|
172
|
-
await page.getByTestId('element').click();
|
|
173
|
-
await expect(page.getByTestId('result')).toBeVisible();
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
test('error case - invalid input shows message', async ({ page }) => {
|
|
177
|
-
await page.goto(`${BASE_URL}/path`);
|
|
178
|
-
await page.getByTestId('input').fill('invalid');
|
|
179
|
-
await page.getByTestId('submit').click();
|
|
180
|
-
await expect(page.getByTestId('error')).toContainText('Error text');
|
|
181
|
-
});
|
|
182
124
|
});
|
|
183
125
|
```
|
|
184
126
|
|
|
185
|
-
**
|
|
127
|
+
**Always include error path tests** — API 500, 404, network timeout, invalid input.
|
|
186
128
|
|
|
187
|
-
|
|
129
|
+
If the file exists → diff against test-plan, add only missing test cases.
|
|
188
130
|
|
|
189
131
|
### 6. Configure auth (if required)
|
|
190
132
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
**
|
|
133
|
+
- **API login**: Generate `auth.setup.ts` using `E2E_USERNAME`/`E2E_PASSWORD` + POST to login endpoint
|
|
134
|
+
- **UI login**: Generate `auth.setup.ts` using browser form fill. Update selectors to match your login page
|
|
135
|
+
- **Multi-user**: Separate `storageState` paths per role
|
|
194
136
|
|
|
195
|
-
**
|
|
196
|
-
|
|
197
|
-
**Multi-user**: If specs mention multiple roles (admin + user) → generate separate `auth.setup.ts` blocks for each role with different `storageState` paths.
|
|
198
|
-
|
|
199
|
-
**Prompt user** with:
|
|
137
|
+
**Prompt user**:
|
|
200
138
|
```
|
|
201
|
-
Auth required. To set up
|
|
202
|
-
|
|
203
|
-
1. Customize tests/playwright/credentials.yaml with your test user
|
|
139
|
+
Auth required. To set up:
|
|
140
|
+
1. Customize tests/playwright/credentials.yaml
|
|
204
141
|
2. Export: export E2E_USERNAME=xxx E2E_PASSWORD=yyy
|
|
205
142
|
3. Run auth: npx playwright test --project=setup
|
|
206
|
-
4.
|
|
143
|
+
4. Re-run /opsx:e2e to execute tests
|
|
207
144
|
```
|
|
208
145
|
|
|
209
146
|
**Idempotency**: If `auth.setup.ts` already exists → verify format, update only if stale.
|
|
210
147
|
|
|
211
|
-
### 7. Configure playwright.config.ts
|
|
148
|
+
### 7. Configure playwright.config.ts
|
|
212
149
|
|
|
213
|
-
If
|
|
214
|
-
- **BASE_URL**: from `process.env.BASE_URL`, falling back to `tests/playwright/seed.spec.ts` → `BASE_URL` value, then `http://localhost:3000`
|
|
215
|
-
- **Dev command**: from `package.json` → scripts in order: `dev` → `start` → `serve` → `preview` → `npm run dev`
|
|
150
|
+
If missing → generate from `openspec/schemas/playwright-e2e/templates/playwright.config.ts`. Auto-detects BASE_URL and dev command from `package.json`.
|
|
216
151
|
|
|
217
|
-
If
|
|
152
|
+
If exists → READ first, preserve ALL existing fields, add only missing `webServer` block.
|
|
218
153
|
|
|
219
|
-
### 8. Execute tests
|
|
154
|
+
### 8. Execute tests
|
|
220
155
|
|
|
221
156
|
```bash
|
|
222
157
|
openspec-pw run <name> --project=<role>
|
|
223
158
|
```
|
|
224
159
|
|
|
225
|
-
|
|
226
|
-
```bash
|
|
227
|
-
npx playwright test tests/playwright/<name>.spec.ts --grep "@<role>"
|
|
228
|
-
```
|
|
229
|
-
The `--project` approach runs ALL tests under each project's credentials — use `@tag` with `--grep` for precise filtering.
|
|
230
|
-
|
|
231
|
-
The CLI handles:
|
|
232
|
-
- Server lifecycle (start → wait for HTTP → test → stop)
|
|
233
|
-
- Port mismatch detection
|
|
234
|
-
- Report generation at `openspec/reports/playwright-e2e-<name>-<timestamp>.md`
|
|
160
|
+
The CLI handles: server lifecycle, port mismatch, report generation.
|
|
235
161
|
|
|
236
|
-
If tests fail →
|
|
162
|
+
If tests fail → use Playwright MCP tools to inspect UI, fix selectors, re-run.
|
|
237
163
|
|
|
238
164
|
**Healer MCP tools** (in order of use):
|
|
239
165
|
<!-- MCP_VERSION: 0.0.68 -->
|
|
@@ -247,77 +173,41 @@ If tests fail → analyze failures, use **Playwright MCP tools** to inspect UI s
|
|
|
247
173
|
| `browser_run_code` | Execute custom fix logic (optional) |
|
|
248
174
|
|
|
249
175
|
**Healer workflow**:
|
|
250
|
-
1. Read the failing test → identify failure type
|
|
251
|
-
2. Classify
|
|
176
|
+
1. Read the failing test → identify failure type
|
|
177
|
+
2. Classify:
|
|
252
178
|
|
|
253
179
|
| Failure type | Signal | Action |
|
|
254
180
|
|-------------|--------|--------|
|
|
255
|
-
| **Network/backend** | `fetch failed`, `net::ERR`, 5xx
|
|
256
|
-
| **Selector changed** | Element not found
|
|
257
|
-
| **Assertion mismatch** |
|
|
258
|
-
| **Timing issue** | `waitFor` timeout
|
|
259
|
-
|
|
260
|
-
3. **Attempt heal** (up to 3 times):
|
|
261
|
-
- Apply fix using `browser_snapshot` (prefer `getByRole`, `getByLabel`, `getByText`)
|
|
262
|
-
- Re-run: `openspec-pw run <name> --project=<role>`
|
|
263
|
-
|
|
264
|
-
4. **After 3 failed attempts**, collect evidence:
|
|
181
|
+
| **Network/backend** | `fetch failed`, `net::ERR`, 5xx | `browser_console_messages` → `test.skip()` |
|
|
182
|
+
| **Selector changed** | Element not found | `browser_snapshot` → fix selector → re-run |
|
|
183
|
+
| **Assertion mismatch** | Wrong content/value | `browser_snapshot` → fix assertion → re-run |
|
|
184
|
+
| **Timing issue** | `waitFor` timeout | Adjust wait strategy → re-run |
|
|
265
185
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|-------|--------|----------|
|
|
269
|
-
| `browser_console_messages` | ERROR-level messages present | App bug → `test.skip()` + report "console error" |
|
|
270
|
-
| `browser_snapshot` | Target element missing from DOM | App bug → `test.skip()` + report "element missing" |
|
|
271
|
-
| `browser_snapshot` | Element exists, no errors | Test bug → report recommendation |
|
|
272
|
-
|
|
273
|
-
- **App bug**: `test.skip('app bug — reason: <signal>')` + detailed report entry
|
|
274
|
-
- **Test bug**: report with "likely selector change, verify manually at file:line"
|
|
275
|
-
- Do NOT retry after evidence checklist — evidence is conclusive
|
|
186
|
+
3. **Attempt heal** (≤3 times): snapshot → fix → re-run
|
|
187
|
+
4. **After 3 failures**: collect evidence checklist → `test.skip()` if app bug, report recommendation if test bug
|
|
276
188
|
|
|
277
189
|
### 9. False Pass Detection
|
|
278
190
|
|
|
279
|
-
Run
|
|
280
|
-
|
|
281
|
-
**Indicator A — Conditional test logic:**
|
|
282
|
-
Look for patterns in the test file:
|
|
283
|
-
```typescript
|
|
284
|
-
if (await locator.isVisible().catch(() => false)) { ... }
|
|
285
|
-
```
|
|
286
|
-
→ If test passes, the locator might not exist → check with `browser_snapshot`
|
|
287
|
-
→ Report: "Test passed but may have skipped — conditional visibility check detected"
|
|
191
|
+
Run after test suite completes (even if all pass):
|
|
288
192
|
|
|
289
|
-
**
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
→ Report: "Test duration suspiciously short — verify test logic was executed"
|
|
193
|
+
- **Conditional logic**: Look for `if (locator.isVisible().catch(() => false))` — if test passes, locator may not exist
|
|
194
|
+
- **Too fast**: < 200ms for a complex flow is suspicious
|
|
195
|
+
- **No fresh auth context**: Protected routes without `browser.newContext()`
|
|
293
196
|
|
|
294
|
-
|
|
295
|
-
If specs mention "protected route" or "redirect to login" but no test uses a fresh browser context:
|
|
296
|
-
→ Report: "Auth guard not verified — test uses authenticated context (cookies/storage inherited)"
|
|
297
|
-
→ Recommendation: Add a test with `browser.newContext()` (no storageState) to verify the guard
|
|
298
|
-
|
|
299
|
-
If any false-pass indicator is found → add a **⚠️ Coverage Gap** section to the report.
|
|
197
|
+
Report any gaps in a **⚠️ Coverage Gap** section.
|
|
300
198
|
|
|
301
199
|
### 10. Report results
|
|
302
200
|
|
|
303
|
-
Read
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
-
|
|
307
|
-
- Any auto-heal notes
|
|
308
|
-
- Recommendations for failed tests (with specific `file:line` references)
|
|
201
|
+
Read report at `openspec/reports/playwright-e2e-<name>-<timestamp>.md`. Present:
|
|
202
|
+
- Summary table (tests, passed, failed, duration, status)
|
|
203
|
+
- Auto-heal notes
|
|
204
|
+
- Recommendations with `file:line` references
|
|
309
205
|
|
|
310
|
-
**Update tasks.md**
|
|
311
|
-
- Read `openspec/changes/<name>/tasks.md`
|
|
312
|
-
- Find tasks related to E2E testing (look for `[ ]` items mentioning "test", "e2e", "playwright", "verify")
|
|
313
|
-
- Append a verification note: e.g. `✅ Verified via Playwright E2E (<timestamp>)`
|
|
314
|
-
- Write the updated content back using the Edit tool
|
|
206
|
+
**Update tasks.md** if all tests pass: find E2E-related items, append `✅ Verified via Playwright E2E (<timestamp>)`.
|
|
315
207
|
|
|
316
|
-
##
|
|
208
|
+
## Report Structure
|
|
317
209
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
```
|
|
210
|
+
```markdown
|
|
321
211
|
# Playwright E2E Report — <name>
|
|
322
212
|
|
|
323
213
|
## Summary
|
|
@@ -326,7 +216,6 @@ Read the report at `openspec/reports/playwright-e2e-<name>-<timestamp>.md`.
|
|
|
326
216
|
| N | N | N | Xm Xs | ✅/❌ |
|
|
327
217
|
|
|
328
218
|
## Results
|
|
329
|
-
|
|
330
219
|
### Passed
|
|
331
220
|
| Test | Duration | Notes |
|
|
332
221
|
|------|----------|-------|
|
|
@@ -345,49 +234,35 @@ Read the report at `openspec/reports/playwright-e2e-<name>-<timestamp>.md`.
|
|
|
345
234
|
- [ ] Requirement 2 (unverified)
|
|
346
235
|
|
|
347
236
|
## ⚠️ Coverage Gaps
|
|
348
|
-
> Tests passed but coverage gaps
|
|
237
|
+
> Tests passed but coverage gaps detected.
|
|
349
238
|
|
|
350
239
|
| Test | Gap | Recommendation |
|
|
351
240
|
|------|-----|----------------|
|
|
352
|
-
| ... | Conditional visibility check
|
|
353
|
-
| ... | Auth guard uses inherited session | Add fresh context test
|
|
354
|
-
| ... | Suspiciously fast execution (<200ms) | Verify test logic
|
|
355
|
-
|
|
356
|
-
### Updated tasks.md
|
|
357
|
-
```
|
|
358
|
-
- [x] Implement feature X ✅ Verified via Playwright E2E (2026-03-28)
|
|
241
|
+
| ... | Conditional visibility check | file:line — use `expect().toBeVisible()` |
|
|
242
|
+
| ... | Auth guard uses inherited session | Add fresh context test |
|
|
243
|
+
| ... | Suspiciously fast execution (<200ms) | Verify test logic executed |
|
|
359
244
|
```
|
|
360
245
|
|
|
361
246
|
## Graceful Degradation
|
|
362
247
|
|
|
363
248
|
| Scenario | Behavior |
|
|
364
249
|
|----------|----------|
|
|
365
|
-
| No specs
|
|
366
|
-
| Seed test fails | Stop
|
|
367
|
-
| No auth required | Skip auth setup
|
|
368
|
-
| test-plan.md exists | Read and use
|
|
369
|
-
| auth.setup.ts exists | Verify format
|
|
370
|
-
| playwright.config.ts exists |
|
|
371
|
-
| Test fails (
|
|
372
|
-
| Test fails (selector) | Healer: snapshot → fix
|
|
373
|
-
|
|
|
374
|
-
|
|
|
375
|
-
| False pass detected | Report coverage gap → add to "⚠️ Coverage Gap" section in report |
|
|
376
|
-
|
|
377
|
-
## Verification Heuristics
|
|
378
|
-
|
|
379
|
-
- **Coverage**: Every functional requirement → at least one test
|
|
380
|
-
- **Selector robustness**: Prefer `data-testid`, fallback to semantic selectors
|
|
381
|
-
- **False positives**: If test fails due to test bug (not app bug) → fix the test
|
|
382
|
-
- **Actionability**: Every failed test needs a specific recommendation
|
|
383
|
-
- **No false passes**: Every passing test must actually execute its test logic — verify absence of `if (isVisible())` conditional patterns
|
|
384
|
-
- **Auth guard verified**: Protected routes must have a test using a fresh browser context (no inherited cookies)
|
|
250
|
+
| No specs | Stop — E2E requires specs |
|
|
251
|
+
| Seed test fails | Stop — fix environment |
|
|
252
|
+
| No auth required | Skip auth setup |
|
|
253
|
+
| test-plan.md exists | Read and use (never regenerate) |
|
|
254
|
+
| auth.setup.ts exists | Verify format (update only if stale) |
|
|
255
|
+
| playwright.config.ts exists | Preserve all fields (add only missing) |
|
|
256
|
+
| Test fails (backend) | `test.skip()` + report |
|
|
257
|
+
| Test fails (selector/assertion) | Healer: snapshot → fix → re-run (≤3) |
|
|
258
|
+
| 3 heals failed | Evidence checklist → app bug: `test.skip()`; unclear: report |
|
|
259
|
+
| False pass detected | Add "⚠️ Coverage Gap" to report |
|
|
385
260
|
|
|
386
261
|
## Guardrails
|
|
387
262
|
|
|
388
263
|
- Read specs from `openspec/changes/<name>/specs/` as source of truth
|
|
389
264
|
- Do NOT generate tests that contradict the specs
|
|
390
|
-
- **DO generate real, runnable Playwright test code** — not placeholders or
|
|
391
|
-
- Do NOT overwrite files outside: `specs/playwright/`, `tests/playwright/`, `openspec/reports/`, `playwright.config.ts
|
|
265
|
+
- **DO generate real, runnable Playwright test code** — not placeholders or TODOs
|
|
266
|
+
- Do NOT overwrite files outside: `specs/playwright/`, `tests/playwright/`, `openspec/reports/`, `playwright.config.ts`
|
|
392
267
|
- Cap auto-heal at 3 attempts
|
|
393
268
|
- If no change specified → always ask user to select
|
package/README.md
CHANGED
|
@@ -20,17 +20,24 @@ openspec-pw init # Install Playwright E2E integration
|
|
|
20
20
|
|
|
21
21
|
## Supported AI Coding Assistants
|
|
22
22
|
|
|
23
|
-
Auto-detects and installs commands for
|
|
24
|
-
|
|
25
|
-
| Editor |
|
|
26
|
-
|
|
27
|
-
| Claude Code |
|
|
28
|
-
| Cursor |
|
|
29
|
-
| Windsurf |
|
|
30
|
-
| Cline |
|
|
31
|
-
| Continue |
|
|
32
|
-
|
|
33
|
-
|
|
23
|
+
Auto-detects and installs commands for all 24 editors OpenSpec supports:
|
|
24
|
+
|
|
25
|
+
| Editor | Path | Editor | Path |
|
|
26
|
+
|--------|------|--------|------|
|
|
27
|
+
| Claude Code | `.claude/` | Gemini CLI | `.gemini/` |
|
|
28
|
+
| Cursor | `.cursor/` | GitHub Copilot | `.github/` |
|
|
29
|
+
| Windsurf | `.windsurf/` | Kiro | `.kiro/` |
|
|
30
|
+
| Cline | `.clinerules/` | Kilo Code | `.kilocode/` |
|
|
31
|
+
| Continue | `.continue/` | iFlow | `.iflow/` |
|
|
32
|
+
| Amazon Q | `.amazonq/` | CoStrict | `.cospec/` |
|
|
33
|
+
| Antigravity | `.agent/` | OpenCode | `.opencode/` |
|
|
34
|
+
| Auggie | `.augment/` | Factory | `.factory/` |
|
|
35
|
+
| CodeBuddy | `.codebuddy/` | Pi | `.pi/` |
|
|
36
|
+
| Codex | `~/.codex/` (global) | Qoder | `.qoder/` |
|
|
37
|
+
| Qwen Code | `.qwen/` | RooCode | `.roo/` |
|
|
38
|
+
| Crush | `.crush/` | | |
|
|
39
|
+
|
|
40
|
+
`openspec-pw init` auto-detects editors in your project and installs the right command files. Claude Code gets the full experience (skill + command + Playwright MCP). Other editors get command/workflow files with the complete E2E workflow.
|
|
34
41
|
|
|
35
42
|
## Usage
|
|
36
43
|
|
|
@@ -76,12 +83,12 @@ openspec-pw doctor # Check prerequisites
|
|
|
76
83
|
|
|
77
84
|
1. **Node.js >= 20**
|
|
78
85
|
2. **OpenSpec** initialized: `npm install -g @fission-ai/openspec && openspec init`
|
|
79
|
-
3. **One of**: Claude Code, Cursor, Windsurf, Cline,
|
|
86
|
+
3. **One of 24 editors**: Claude Code, Cursor, Windsurf, Cline, Continue, Amazon Q, Gemini CLI, GitHub Copilot, Kiro, Kilo Code, iFlow, CoStrict, OpenCode, Auggie, Factory, CodeBuddy, Codex, Pi, Qoder, Qwen Code, RooCode, Crush, Antigravity (auto-detected)
|
|
80
87
|
4. **Claude Code only**: Playwright MCP — `claude mcp add playwright npx @playwright/mcp@latest`
|
|
81
88
|
|
|
82
89
|
## What `openspec-pw init` Does
|
|
83
90
|
|
|
84
|
-
1. Detects installed AI coding assistants (
|
|
91
|
+
1. Detects installed AI coding assistants (all 24 supported editors)
|
|
85
92
|
2. Installs E2E command/workflow files for each detected editor
|
|
86
93
|
3. Installs `/openspec-e2e` skill for Claude Code
|
|
87
94
|
4. Installs Playwright MCP globally for Claude Code (via `claude mcp add`)
|
package/README.zh-CN.md
CHANGED
|
@@ -20,15 +20,22 @@ openspec-pw init # 安装 Playwright E2E 集成
|
|
|
20
20
|
|
|
21
21
|
## 支持的 AI 编码助手
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
| 编辑器 |
|
|
26
|
-
|
|
27
|
-
| Claude Code |
|
|
28
|
-
| Cursor |
|
|
29
|
-
| Windsurf |
|
|
30
|
-
| Cline |
|
|
31
|
-
| Continue |
|
|
23
|
+
自动检测并安装 OpenSpec 支持的全部 24 个编辑器的命令文件:
|
|
24
|
+
|
|
25
|
+
| 编辑器 | 路径 | 编辑器 | 路径 |
|
|
26
|
+
|--------|------|--------|------|
|
|
27
|
+
| Claude Code | `.claude/` | Gemini CLI | `.gemini/` |
|
|
28
|
+
| Cursor | `.cursor/` | GitHub Copilot | `.github/` |
|
|
29
|
+
| Windsurf | `.windsurf/` | Kiro | `.kiro/` |
|
|
30
|
+
| Cline | `.clinerules/` | Kilo Code | `.kilocode/` |
|
|
31
|
+
| Continue | `.continue/` | iFlow | `.iflow/` |
|
|
32
|
+
| Amazon Q | `.amazonq/` | CoStrict | `.cospec/` |
|
|
33
|
+
| Antigravity | `.agent/` | OpenCode | `.opencode/` |
|
|
34
|
+
| Auggie | `.augment/` | Factory | `.factory/` |
|
|
35
|
+
| CodeBuddy | `.codebuddy/` | Pi | `.pi/` |
|
|
36
|
+
| Codex | `~/.codex/` (全局) | Qoder | `.qoder/` |
|
|
37
|
+
| Qwen Code | `.qwen/` | RooCode | `.roo/` |
|
|
38
|
+
| Crush | `.crush/` | | |
|
|
32
39
|
|
|
33
40
|
`openspec-pw init` 会检测项目中安装了哪些编辑器并安装对应文件。Claude Code 获得完整体验(skill + 命令 + Playwright MCP)。其他编辑器获得包含完整 E2E 工作流的命令/工作流文件。
|
|
34
41
|
|
|
@@ -20,6 +20,7 @@ export interface EditorAdapter {
|
|
|
20
20
|
}
|
|
21
21
|
/** Claude Code: .claude/commands/opsx/<id>.md + SKILL.md */
|
|
22
22
|
declare const claudeAdapter: EditorAdapter;
|
|
23
|
+
declare const ALL_ADAPTERS: EditorAdapter[];
|
|
23
24
|
/** Detect which editors are installed by checking their config directories */
|
|
24
25
|
export declare function detectEditors(projectRoot: string): EditorAdapter[];
|
|
25
26
|
/** Detect Codex by checking if CODEX_HOME or ~/.codex exists */
|
|
@@ -30,4 +31,4 @@ export declare function buildCommandMeta(body: string): CommandMeta;
|
|
|
30
31
|
export declare function installForAllEditors(body: string, adapters: EditorAdapter[], projectRoot: string): void;
|
|
31
32
|
/** Install SKILL.md only for Claude Code */
|
|
32
33
|
export declare function installSkill(projectRoot: string, skillContent: string): void;
|
|
33
|
-
export { claudeAdapter };
|
|
34
|
+
export { claudeAdapter, ALL_ADAPTERS };
|
package/dist/commands/editors.js
CHANGED
|
@@ -117,7 +117,7 @@ const amazonqAdapter = {
|
|
|
117
117
|
getCommandPath(id) { return join('.amazonq', 'prompts', `opsx-${id}.md`); },
|
|
118
118
|
formatCommand(meta) {
|
|
119
119
|
return `---
|
|
120
|
-
description: ${
|
|
120
|
+
description: ${meta.description}
|
|
121
121
|
---
|
|
122
122
|
|
|
123
123
|
${meta.body}
|
|
@@ -132,7 +132,7 @@ const antigravityAdapter = {
|
|
|
132
132
|
getCommandPath(id) { return join('.agent', 'workflows', `opsx-${id}.md`); },
|
|
133
133
|
formatCommand(meta) {
|
|
134
134
|
return `---
|
|
135
|
-
description: ${
|
|
135
|
+
description: ${meta.description}
|
|
136
136
|
---
|
|
137
137
|
|
|
138
138
|
${meta.body}
|
|
@@ -147,7 +147,7 @@ const auggieAdapter = {
|
|
|
147
147
|
getCommandPath(id) { return join('.augment', 'commands', `opsx-${id}.md`); },
|
|
148
148
|
formatCommand(meta) {
|
|
149
149
|
return `---
|
|
150
|
-
description: ${
|
|
150
|
+
description: ${meta.description}
|
|
151
151
|
argument-hint: command arguments
|
|
152
152
|
---
|
|
153
153
|
|
|
@@ -163,7 +163,7 @@ const codebuddyAdapter = {
|
|
|
163
163
|
getCommandPath(id) { return join('.codebuddy', 'commands', 'opsx', `${id}.md`); },
|
|
164
164
|
formatCommand(meta) {
|
|
165
165
|
return `---
|
|
166
|
-
name: ${
|
|
166
|
+
name: ${meta.name}
|
|
167
167
|
description: "${meta.description}"
|
|
168
168
|
argument-hint: "[command arguments]"
|
|
169
169
|
---
|
|
@@ -183,7 +183,7 @@ const codexAdapter = {
|
|
|
183
183
|
},
|
|
184
184
|
formatCommand(meta) {
|
|
185
185
|
return `---
|
|
186
|
-
description: ${
|
|
186
|
+
description: ${meta.description}
|
|
187
187
|
argument-hint: command arguments
|
|
188
188
|
---
|
|
189
189
|
|
|
@@ -208,16 +208,16 @@ ${meta.body}
|
|
|
208
208
|
},
|
|
209
209
|
};
|
|
210
210
|
// ─── crush ────────────────────────────────────────────────────────────────
|
|
211
|
-
/** Crush: .crush/commands/opsx/<id>.md —
|
|
211
|
+
/** Crush: .crush/commands/opsx/<id>.md — raw values, no escaping */
|
|
212
212
|
const crushAdapter = {
|
|
213
213
|
toolId: 'crush',
|
|
214
214
|
hasSkill: false,
|
|
215
215
|
getCommandPath(id) { return join('.crush', 'commands', 'opsx', `${id}.md`); },
|
|
216
216
|
formatCommand(meta) {
|
|
217
217
|
return `---
|
|
218
|
-
name: ${
|
|
219
|
-
description: ${
|
|
220
|
-
category: ${
|
|
218
|
+
name: ${meta.name}
|
|
219
|
+
description: ${meta.description}
|
|
220
|
+
category: ${meta.category}
|
|
221
221
|
tags: ${formatTagsPlain(meta.tags)}
|
|
222
222
|
---
|
|
223
223
|
|
|
@@ -233,7 +233,7 @@ const factoryAdapter = {
|
|
|
233
233
|
getCommandPath(id) { return join('.factory', 'commands', `opsx-${id}.md`); },
|
|
234
234
|
formatCommand(meta) {
|
|
235
235
|
return `---
|
|
236
|
-
description: ${
|
|
236
|
+
description: ${meta.description}
|
|
237
237
|
argument-hint: command arguments
|
|
238
238
|
---
|
|
239
239
|
|
|
@@ -264,7 +264,7 @@ const githubcopilotAdapter = {
|
|
|
264
264
|
getCommandPath(id) { return join('.github', 'prompts', `opsx-${id}.prompt.md`); },
|
|
265
265
|
formatCommand(meta) {
|
|
266
266
|
return `---
|
|
267
|
-
description: ${
|
|
267
|
+
description: ${meta.description}
|
|
268
268
|
---
|
|
269
269
|
|
|
270
270
|
${meta.body}
|
|
@@ -281,8 +281,8 @@ const iflowAdapter = {
|
|
|
281
281
|
return `---
|
|
282
282
|
name: /opsx-${meta.id}
|
|
283
283
|
id: opsx-${meta.id}
|
|
284
|
-
category: ${
|
|
285
|
-
description: ${
|
|
284
|
+
category: ${meta.category}
|
|
285
|
+
description: ${meta.description}
|
|
286
286
|
---
|
|
287
287
|
|
|
288
288
|
${meta.body}
|
|
@@ -308,7 +308,7 @@ const kiroAdapter = {
|
|
|
308
308
|
getCommandPath(id) { return join('.kiro', 'prompts', `opsx-${id}.prompt.md`); },
|
|
309
309
|
formatCommand(meta) {
|
|
310
310
|
return `---
|
|
311
|
-
description: ${
|
|
311
|
+
description: ${meta.description}
|
|
312
312
|
---
|
|
313
313
|
|
|
314
314
|
${meta.body}
|
|
@@ -324,7 +324,7 @@ const opencodeAdapter = {
|
|
|
324
324
|
formatCommand(meta) {
|
|
325
325
|
const transformed = transformToHyphenCommands(meta.body);
|
|
326
326
|
return `---
|
|
327
|
-
description: ${
|
|
327
|
+
description: ${meta.description}
|
|
328
328
|
---
|
|
329
329
|
|
|
330
330
|
${transformed}
|
|
@@ -347,16 +347,16 @@ ${meta.body}
|
|
|
347
347
|
},
|
|
348
348
|
};
|
|
349
349
|
// ─── qoder ────────────────────────────────────────────────────────────────
|
|
350
|
-
/** Qoder: .qoder/commands/opsx/<id>.md —
|
|
350
|
+
/** Qoder: .qoder/commands/opsx/<id>.md — raw values, no escaping */
|
|
351
351
|
const qoderAdapter = {
|
|
352
352
|
toolId: 'qoder',
|
|
353
353
|
hasSkill: false,
|
|
354
354
|
getCommandPath(id) { return join('.qoder', 'commands', 'opsx', `${id}.md`); },
|
|
355
355
|
formatCommand(meta) {
|
|
356
356
|
return `---
|
|
357
|
-
name: ${
|
|
358
|
-
description: ${
|
|
359
|
-
category: ${
|
|
357
|
+
name: ${meta.name}
|
|
358
|
+
description: ${meta.description}
|
|
359
|
+
category: ${meta.category}
|
|
360
360
|
tags: ${formatTagsPlain(meta.tags)}
|
|
361
361
|
---
|
|
362
362
|
|
|
@@ -486,5 +486,5 @@ export function installSkill(projectRoot, skillContent) {
|
|
|
486
486
|
writeFileSync(join(skillDir, 'SKILL.md'), skillContent);
|
|
487
487
|
console.log(chalk.green(` ✓ claude: .claude/skills/openspec-e2e/SKILL.md`));
|
|
488
488
|
}
|
|
489
|
-
export { claudeAdapter };
|
|
489
|
+
export { claudeAdapter, ALL_ADAPTERS };
|
|
490
490
|
//# sourceMappingURL=editors.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"editors.js","sourceRoot":"","sources":["../../src/commands/editors.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"editors.js","sourceRoot":"","sources":["../../src/commands/editors.ts"],"names":[],"mappings":"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"}
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openspec-playwright",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.43",
|
|
4
4
|
"description": "OpenSpec + Playwright E2E verification setup tool for Claude Code",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
10
|
"build": "tsc",
|
|
11
|
+
"test": "vitest",
|
|
12
|
+
"test:run": "vitest run",
|
|
11
13
|
"prepublishOnly": "npm run build",
|
|
12
14
|
"release": "npm version patch && npm run build && git push && git push --tags && npm publish"
|
|
13
15
|
},
|
|
@@ -18,7 +20,8 @@
|
|
|
18
20
|
},
|
|
19
21
|
"devDependencies": {
|
|
20
22
|
"@types/node": "^22.0.0",
|
|
21
|
-
"typescript": "^5.6.0"
|
|
23
|
+
"typescript": "^5.6.0",
|
|
24
|
+
"vitest": "^4.1.2"
|
|
22
25
|
},
|
|
23
26
|
"engines": {
|
|
24
27
|
"node": ">=20"
|
package/release-notes.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
## What's Changed
|
|
2
2
|
|
|
3
|
-
-
|
|
3
|
+
- chore: regenerate package-lock.json with vitest dev dependency
|
|
4
4
|
|
|
5
|
-
**Full Changelog**: https://github.com/wxhou/openspec-playwright/releases/tag/v0.1.
|
|
5
|
+
**Full Changelog**: https://github.com/wxhou/openspec-playwright/releases/tag/v0.1.43
|
package/src/commands/editors.ts
CHANGED
|
@@ -151,7 +151,7 @@ const amazonqAdapter: EditorAdapter = {
|
|
|
151
151
|
getCommandPath(id) { return join('.amazonq', 'prompts', `opsx-${id}.md`); },
|
|
152
152
|
formatCommand(meta) {
|
|
153
153
|
return `---
|
|
154
|
-
description: ${
|
|
154
|
+
description: ${meta.description}
|
|
155
155
|
---
|
|
156
156
|
|
|
157
157
|
${meta.body}
|
|
@@ -168,7 +168,7 @@ const antigravityAdapter: EditorAdapter = {
|
|
|
168
168
|
getCommandPath(id) { return join('.agent', 'workflows', `opsx-${id}.md`); },
|
|
169
169
|
formatCommand(meta) {
|
|
170
170
|
return `---
|
|
171
|
-
description: ${
|
|
171
|
+
description: ${meta.description}
|
|
172
172
|
---
|
|
173
173
|
|
|
174
174
|
${meta.body}
|
|
@@ -185,7 +185,7 @@ const auggieAdapter: EditorAdapter = {
|
|
|
185
185
|
getCommandPath(id) { return join('.augment', 'commands', `opsx-${id}.md`); },
|
|
186
186
|
formatCommand(meta) {
|
|
187
187
|
return `---
|
|
188
|
-
description: ${
|
|
188
|
+
description: ${meta.description}
|
|
189
189
|
argument-hint: command arguments
|
|
190
190
|
---
|
|
191
191
|
|
|
@@ -203,7 +203,7 @@ const codebuddyAdapter: EditorAdapter = {
|
|
|
203
203
|
getCommandPath(id) { return join('.codebuddy', 'commands', 'opsx', `${id}.md`); },
|
|
204
204
|
formatCommand(meta) {
|
|
205
205
|
return `---
|
|
206
|
-
name: ${
|
|
206
|
+
name: ${meta.name}
|
|
207
207
|
description: "${meta.description}"
|
|
208
208
|
argument-hint: "[command arguments]"
|
|
209
209
|
---
|
|
@@ -225,7 +225,7 @@ const codexAdapter: EditorAdapter = {
|
|
|
225
225
|
},
|
|
226
226
|
formatCommand(meta) {
|
|
227
227
|
return `---
|
|
228
|
-
description: ${
|
|
228
|
+
description: ${meta.description}
|
|
229
229
|
argument-hint: command arguments
|
|
230
230
|
---
|
|
231
231
|
|
|
@@ -254,16 +254,16 @@ ${meta.body}
|
|
|
254
254
|
|
|
255
255
|
// ─── crush ────────────────────────────────────────────────────────────────
|
|
256
256
|
|
|
257
|
-
/** Crush: .crush/commands/opsx/<id>.md —
|
|
257
|
+
/** Crush: .crush/commands/opsx/<id>.md — raw values, no escaping */
|
|
258
258
|
const crushAdapter: EditorAdapter = {
|
|
259
259
|
toolId: 'crush',
|
|
260
260
|
hasSkill: false,
|
|
261
261
|
getCommandPath(id) { return join('.crush', 'commands', 'opsx', `${id}.md`); },
|
|
262
262
|
formatCommand(meta) {
|
|
263
263
|
return `---
|
|
264
|
-
name: ${
|
|
265
|
-
description: ${
|
|
266
|
-
category: ${
|
|
264
|
+
name: ${meta.name}
|
|
265
|
+
description: ${meta.description}
|
|
266
|
+
category: ${meta.category}
|
|
267
267
|
tags: ${formatTagsPlain(meta.tags)}
|
|
268
268
|
---
|
|
269
269
|
|
|
@@ -281,7 +281,7 @@ const factoryAdapter: EditorAdapter = {
|
|
|
281
281
|
getCommandPath(id) { return join('.factory', 'commands', `opsx-${id}.md`); },
|
|
282
282
|
formatCommand(meta) {
|
|
283
283
|
return `---
|
|
284
|
-
description: ${
|
|
284
|
+
description: ${meta.description}
|
|
285
285
|
argument-hint: command arguments
|
|
286
286
|
---
|
|
287
287
|
|
|
@@ -316,7 +316,7 @@ const githubcopilotAdapter: EditorAdapter = {
|
|
|
316
316
|
getCommandPath(id) { return join('.github', 'prompts', `opsx-${id}.prompt.md`); },
|
|
317
317
|
formatCommand(meta) {
|
|
318
318
|
return `---
|
|
319
|
-
description: ${
|
|
319
|
+
description: ${meta.description}
|
|
320
320
|
---
|
|
321
321
|
|
|
322
322
|
${meta.body}
|
|
@@ -335,8 +335,8 @@ const iflowAdapter: EditorAdapter = {
|
|
|
335
335
|
return `---
|
|
336
336
|
name: /opsx-${meta.id}
|
|
337
337
|
id: opsx-${meta.id}
|
|
338
|
-
category: ${
|
|
339
|
-
description: ${
|
|
338
|
+
category: ${meta.category}
|
|
339
|
+
description: ${meta.description}
|
|
340
340
|
---
|
|
341
341
|
|
|
342
342
|
${meta.body}
|
|
@@ -366,7 +366,7 @@ const kiroAdapter: EditorAdapter = {
|
|
|
366
366
|
getCommandPath(id) { return join('.kiro', 'prompts', `opsx-${id}.prompt.md`); },
|
|
367
367
|
formatCommand(meta) {
|
|
368
368
|
return `---
|
|
369
|
-
description: ${
|
|
369
|
+
description: ${meta.description}
|
|
370
370
|
---
|
|
371
371
|
|
|
372
372
|
${meta.body}
|
|
@@ -384,7 +384,7 @@ const opencodeAdapter: EditorAdapter = {
|
|
|
384
384
|
formatCommand(meta) {
|
|
385
385
|
const transformed = transformToHyphenCommands(meta.body);
|
|
386
386
|
return `---
|
|
387
|
-
description: ${
|
|
387
|
+
description: ${meta.description}
|
|
388
388
|
---
|
|
389
389
|
|
|
390
390
|
${transformed}
|
|
@@ -411,16 +411,16 @@ ${meta.body}
|
|
|
411
411
|
|
|
412
412
|
// ─── qoder ────────────────────────────────────────────────────────────────
|
|
413
413
|
|
|
414
|
-
/** Qoder: .qoder/commands/opsx/<id>.md —
|
|
414
|
+
/** Qoder: .qoder/commands/opsx/<id>.md — raw values, no escaping */
|
|
415
415
|
const qoderAdapter: EditorAdapter = {
|
|
416
416
|
toolId: 'qoder',
|
|
417
417
|
hasSkill: false,
|
|
418
418
|
getCommandPath(id) { return join('.qoder', 'commands', 'opsx', `${id}.md`); },
|
|
419
419
|
formatCommand(meta) {
|
|
420
420
|
return `---
|
|
421
|
-
name: ${
|
|
422
|
-
description: ${
|
|
423
|
-
category: ${
|
|
421
|
+
name: ${meta.name}
|
|
422
|
+
description: ${meta.description}
|
|
423
|
+
category: ${meta.category}
|
|
424
424
|
tags: ${formatTagsPlain(meta.tags)}
|
|
425
425
|
---
|
|
426
426
|
|
|
@@ -570,4 +570,4 @@ export function installSkill(projectRoot: string, skillContent: string): void {
|
|
|
570
570
|
console.log(chalk.green(` ✓ claude: .claude/skills/openspec-e2e/SKILL.md`));
|
|
571
571
|
}
|
|
572
572
|
|
|
573
|
-
export { claudeAdapter };
|
|
573
|
+
export { claudeAdapter, ALL_ADAPTERS };
|
package/templates/auth.setup.ts
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
// For multi-user testing, add more setup blocks with different storageState paths.
|
|
14
14
|
|
|
15
15
|
import { test as setup, expect } from '@playwright/test';
|
|
16
|
+
import { existsSync } from 'fs';
|
|
16
17
|
|
|
17
18
|
// ─── API Login (preferred) ───────────────────────────────────────────────────
|
|
18
19
|
// If your app has a login API, configure it in tests/playwright/credentials.yaml:
|
|
@@ -25,8 +26,10 @@ setup('authenticate via API', async ({ page }) => {
|
|
|
25
26
|
const password = process.env.E2E_PASSWORD;
|
|
26
27
|
|
|
27
28
|
if (!username || !password) {
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
throw new Error(
|
|
30
|
+
'E2E_USERNAME and E2E_PASSWORD environment variables are required.\n' +
|
|
31
|
+
'Set them with: export E2E_USERNAME=xxx E2E_PASSWORD=yyy'
|
|
32
|
+
);
|
|
30
33
|
}
|
|
31
34
|
|
|
32
35
|
const res = await page.request.post(`${baseUrl}/api/auth/login`, {
|
|
@@ -38,6 +41,10 @@ setup('authenticate via API', async ({ page }) => {
|
|
|
38
41
|
}
|
|
39
42
|
|
|
40
43
|
await page.context().storageState({ path: './playwright/.auth/user.json' });
|
|
44
|
+
|
|
45
|
+
if (!existsSync('./playwright/.auth/user.json')) {
|
|
46
|
+
throw new Error('Auth setup failed: storageState file was not created');
|
|
47
|
+
}
|
|
41
48
|
});
|
|
42
49
|
|
|
43
50
|
// ─── UI Login (fallback) ─────────────────────────────────────────────────────
|
|
@@ -49,8 +56,10 @@ setup('authenticate via UI', async ({ page }) => {
|
|
|
49
56
|
const password = process.env.E2E_PASSWORD;
|
|
50
57
|
|
|
51
58
|
if (!username || !password) {
|
|
52
|
-
|
|
53
|
-
|
|
59
|
+
throw new Error(
|
|
60
|
+
'E2E_USERNAME and E2E_PASSWORD environment variables are required.\n' +
|
|
61
|
+
'Set them with: export E2E_USERNAME=xxx E2E_PASSWORD=yyy'
|
|
62
|
+
);
|
|
54
63
|
}
|
|
55
64
|
|
|
56
65
|
// TODO: Update these selectors to match your login page
|
|
@@ -74,4 +83,8 @@ setup('authenticate via UI', async ({ page }) => {
|
|
|
74
83
|
await expect(page).not.toHaveURL(/.*login.*|.*signin.*/);
|
|
75
84
|
|
|
76
85
|
await page.context().storageState({ path: './playwright/.auth/user.json' });
|
|
86
|
+
|
|
87
|
+
if (!existsSync('./playwright/.auth/user.json')) {
|
|
88
|
+
throw new Error('Auth setup failed: storageState file was not created');
|
|
89
|
+
}
|
|
77
90
|
});
|
|
@@ -21,13 +21,13 @@ api: /api/auth/login # Leave empty for UI login
|
|
|
21
21
|
|
|
22
22
|
users:
|
|
23
23
|
- name: user
|
|
24
|
-
username:
|
|
25
|
-
password:
|
|
24
|
+
username: CHANGE_ME@example.com
|
|
25
|
+
password: CHANGE_ME_PASSWORD
|
|
26
26
|
|
|
27
27
|
# Multi-user example (uncomment and configure for role-based tests):
|
|
28
28
|
# - name: admin
|
|
29
|
-
# username: admin
|
|
30
|
-
# password:
|
|
29
|
+
# username: CHANGE_ME@admin.com
|
|
30
|
+
# password: CHANGE_ME_ADMIN_PASSWORD
|
|
31
31
|
# - name: premium
|
|
32
|
-
# username: premium
|
|
33
|
-
# password:
|
|
32
|
+
# username: CHANGE_ME@premium.com
|
|
33
|
+
# password: CHANGE_ME_PREMIUM_PASSWORD
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import {
|
|
3
|
+
escapeYamlValue,
|
|
4
|
+
formatTagsArray,
|
|
5
|
+
buildCommandMeta,
|
|
6
|
+
detectEditors,
|
|
7
|
+
detectCodex,
|
|
8
|
+
ALL_ADAPTERS,
|
|
9
|
+
} from '../src/commands/editors.js';
|
|
10
|
+
|
|
11
|
+
// ─── escapeYamlValue ──────────────────────────────────────────────────────────
|
|
12
|
+
|
|
13
|
+
describe('escapeYamlValue', () => {
|
|
14
|
+
it('returns raw value for plain strings', () => {
|
|
15
|
+
expect(escapeYamlValue('hello world')).toBe('hello world');
|
|
16
|
+
expect(escapeYamlValue('simple')).toBe('simple');
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('quotes strings with colons', () => {
|
|
20
|
+
expect(escapeYamlValue('hello: world')).toBe('"hello: world"');
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('quotes strings starting with whitespace', () => {
|
|
24
|
+
expect(escapeYamlValue(' hello')).toBe('" hello"');
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('quotes strings ending with whitespace', () => {
|
|
28
|
+
expect(escapeYamlValue('hello ')).toBe('"hello "');
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('quotes and escapes strings containing quotes', () => {
|
|
32
|
+
expect(escapeYamlValue('say "hello"')).toBe('"say \\"hello\\""');
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('quotes and escapes strings containing newlines', () => {
|
|
36
|
+
expect(escapeYamlValue('line1\nline2')).toBe('"line1\\nline2"');
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('quotes strings with special YAML chars', () => {
|
|
40
|
+
expect(escapeYamlValue('key: value')).toBe('"key: value"');
|
|
41
|
+
expect(escapeYamlValue('# comment')).toBe('"# comment"');
|
|
42
|
+
expect(escapeYamlValue('a{b}c')).toBe('"a{b}c"');
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('quotes strings with array-like chars', () => {
|
|
46
|
+
expect(escapeYamlValue('[a, b]')).toBe('"[a, b]"');
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// ─── formatTagsArray ─────────────────────────────────────────────────────────
|
|
51
|
+
|
|
52
|
+
describe('formatTagsArray', () => {
|
|
53
|
+
it('formats empty tags', () => {
|
|
54
|
+
expect(formatTagsArray([])).toBe('[]');
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('formats single tag', () => {
|
|
58
|
+
expect(formatTagsArray(['openspec'])).toBe('[openspec]');
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('formats multiple tags', () => {
|
|
62
|
+
expect(formatTagsArray(['openspec', 'playwright', 'e2e'])).toBe(
|
|
63
|
+
'[openspec, playwright, e2e]'
|
|
64
|
+
);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('escapes tags with colons using YAML quoting', () => {
|
|
68
|
+
// escapeYamlValue quotes 'tag:colon' → "tag:colon", so array has quoted element
|
|
69
|
+
expect(formatTagsArray(['tag:colon'])).toBe('["tag:colon"]');
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// ─── buildCommandMeta ─────────────────────────────────────────────────────────
|
|
74
|
+
|
|
75
|
+
describe('buildCommandMeta', () => {
|
|
76
|
+
it('creates meta with correct id', () => {
|
|
77
|
+
const meta = buildCommandMeta('test body');
|
|
78
|
+
expect(meta.id).toBe('e2e');
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('creates meta with correct fields', () => {
|
|
82
|
+
const meta = buildCommandMeta('test body');
|
|
83
|
+
expect(meta.name).toBe('OPSX: E2E');
|
|
84
|
+
expect(meta.description).toBe('Run Playwright E2E verification for an OpenSpec change');
|
|
85
|
+
expect(meta.category).toBe('OpenSpec');
|
|
86
|
+
expect(meta.tags).toEqual(['openspec', 'playwright', 'e2e', 'testing']);
|
|
87
|
+
expect(meta.body).toBe('test body');
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
// ─── detectEditors ────────────────────────────────────────────────────────────
|
|
92
|
+
|
|
93
|
+
describe('detectEditors', () => {
|
|
94
|
+
it('returns empty array for non-existent directory', () => {
|
|
95
|
+
const result = detectEditors('/tmp/nonexistent-project-xyz-123');
|
|
96
|
+
expect(result).toEqual([]);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('detects Claude Code when .claude directory exists', () => {
|
|
100
|
+
const result = detectEditors('/Users/wxhou/Documents/openspec-playwright');
|
|
101
|
+
const names = result.map(a => a.toolId);
|
|
102
|
+
expect(names).toContain('claude');
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
// ─── detectCodex ─────────────────────────────────────────────────────────────
|
|
107
|
+
|
|
108
|
+
describe('detectCodex', () => {
|
|
109
|
+
it('returns null when CODEX_HOME is set to non-existent path', () => {
|
|
110
|
+
const original = process.env.CODEX_HOME;
|
|
111
|
+
process.env.CODEX_HOME = '/tmp/this-codex-dir-does-not-exist-xyz';
|
|
112
|
+
const result = detectCodex();
|
|
113
|
+
expect(result).toBeNull();
|
|
114
|
+
if (original !== undefined) {
|
|
115
|
+
process.env.CODEX_HOME = original;
|
|
116
|
+
} else {
|
|
117
|
+
delete process.env.CODEX_HOME;
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// ─── Adapter format correctness ───────────────────────────────────────────────
|
|
123
|
+
|
|
124
|
+
describe('Adapter format correctness', () => {
|
|
125
|
+
it('every adapter has a toolId', () => {
|
|
126
|
+
for (const adapter of ALL_ADAPTERS) {
|
|
127
|
+
expect(adapter.toolId).toBeTruthy();
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it('every adapter has formatCommand that returns non-empty string', () => {
|
|
132
|
+
const meta = buildCommandMeta('test body content');
|
|
133
|
+
for (const adapter of ALL_ADAPTERS) {
|
|
134
|
+
const output = adapter.formatCommand(meta);
|
|
135
|
+
expect(output.length).toBeGreaterThan(0);
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it('every adapter getCommandPath returns a path with the id', () => {
|
|
140
|
+
const meta = buildCommandMeta('test');
|
|
141
|
+
for (const adapter of ALL_ADAPTERS) {
|
|
142
|
+
const path = adapter.getCommandPath(meta.id);
|
|
143
|
+
expect(path).toContain('opsx');
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('Claude adapter output has YAML frontmatter', () => {
|
|
148
|
+
const meta = buildCommandMeta('body');
|
|
149
|
+
const output = ALL_ADAPTERS[0].formatCommand(meta);
|
|
150
|
+
expect(output).toContain('---');
|
|
151
|
+
expect(output).toContain('name:');
|
|
152
|
+
expect(output).toContain('description:');
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it('TOML adapters (gemini, qwen) do not use YAML frontmatter', () => {
|
|
156
|
+
const meta = buildCommandMeta('body');
|
|
157
|
+
const adapters = ALL_ADAPTERS.filter(
|
|
158
|
+
a => a.toolId === 'gemini' || a.toolId === 'qwen'
|
|
159
|
+
);
|
|
160
|
+
for (const adapter of adapters) {
|
|
161
|
+
const output = adapter.formatCommand(meta);
|
|
162
|
+
expect(output).not.toContain('---');
|
|
163
|
+
expect(output).toContain('description =');
|
|
164
|
+
expect(output).toContain('prompt =');
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it('crush and qoder use raw values without escaping in name/description', () => {
|
|
169
|
+
const meta = buildCommandMeta('body');
|
|
170
|
+
const adapters = ALL_ADAPTERS.filter(
|
|
171
|
+
a => a.toolId === 'crush' || a.toolId === 'qoder'
|
|
172
|
+
);
|
|
173
|
+
for (const adapter of adapters) {
|
|
174
|
+
const output = adapter.formatCommand(meta);
|
|
175
|
+
// Should NOT have escaped quotes in the raw-value fields
|
|
176
|
+
expect(output).not.toContain('\\\\"');
|
|
177
|
+
expect(output).toContain('name: OPSX: E2E');
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
});
|
package/vitest.config.ts
ADDED
|
Binary file
|