specweave 1.0.239 → 1.0.240
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.md +32 -30
- package/README.md +1 -1
- package/bin/specweave.js +16 -0
- package/dist/plugins/specweave-testing/lib/playwright-cli-detector.d.ts +1 -0
- package/dist/plugins/specweave-testing/lib/playwright-cli-detector.d.ts.map +1 -1
- package/dist/plugins/specweave-testing/lib/playwright-cli-detector.js +7 -3
- package/dist/plugins/specweave-testing/lib/playwright-cli-detector.js.map +1 -1
- package/dist/plugins/specweave-testing/lib/playwright-cli-runner.d.ts.map +1 -1
- package/dist/plugins/specweave-testing/lib/playwright-cli-runner.js +27 -19
- package/dist/plugins/specweave-testing/lib/playwright-cli-runner.js.map +1 -1
- package/dist/src/cli/commands/init.d.ts.map +1 -1
- package/dist/src/cli/commands/init.js +8 -0
- package/dist/src/cli/commands/init.js.map +1 -1
- package/dist/src/cli/commands/team.d.ts +20 -0
- package/dist/src/cli/commands/team.d.ts.map +1 -0
- package/dist/src/cli/commands/team.js +98 -0
- package/dist/src/cli/commands/team.js.map +1 -0
- package/dist/src/cli/helpers/init/claude-settings-env.d.ts +16 -0
- package/dist/src/cli/helpers/init/claude-settings-env.d.ts.map +1 -0
- package/dist/src/cli/helpers/init/claude-settings-env.js +44 -0
- package/dist/src/cli/helpers/init/claude-settings-env.js.map +1 -0
- package/dist/src/sync/engine.d.ts.map +1 -1
- package/dist/src/sync/engine.js +2 -0
- package/dist/src/sync/engine.js.map +1 -1
- package/dist/src/sync/providers/ado.d.ts.map +1 -1
- package/dist/src/sync/providers/ado.js +4 -2
- package/dist/src/sync/providers/ado.js.map +1 -1
- package/dist/src/sync/providers/github.d.ts.map +1 -1
- package/dist/src/sync/providers/github.js +11 -0
- package/dist/src/sync/providers/github.js.map +1 -1
- package/dist/src/sync/providers/jira.d.ts.map +1 -1
- package/dist/src/sync/providers/jira.js +14 -2
- package/dist/src/sync/providers/jira.js.map +1 -1
- package/package.json +2 -2
- package/plugins/specweave/hooks/user-prompt-submit.sh +16 -0
- package/plugins/specweave/scripts/read-grill-context.sh +149 -0
- package/plugins/specweave-testing/commands/e2e-setup.md +12 -12
- package/plugins/specweave-testing/commands/ui-automate.md +3 -3
- package/plugins/specweave-testing/commands/ui-inspect.md +3 -3
- package/plugins/specweave-testing/lib/playwright-cli-detector.js +8 -3
- package/plugins/specweave-testing/lib/playwright-cli-detector.ts +8 -3
- package/plugins/specweave-testing/lib/playwright-cli-runner.js +25 -20
- package/plugins/specweave-testing/lib/playwright-cli-runner.ts +24 -19
- package/plugins/specweave-testing/skills/e2e-testing/SKILL.md +29 -28
- package/plugins/specweave-testing/skills/qa-engineer/SKILL.md +2 -0
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# read-grill-context.sh - Load increment context for /sw:grill code review
|
|
3
|
+
#
|
|
4
|
+
# Executed by UserPromptSubmit hook when /sw:grill is detected.
|
|
5
|
+
# Outputs structured context + review instructions that get injected
|
|
6
|
+
# into the LLM's conversation via additionalContext.
|
|
7
|
+
#
|
|
8
|
+
# Usage: bash read-grill-context.sh [incrementId]
|
|
9
|
+
#
|
|
10
|
+
# If no incrementId given, auto-detects from active-increment.json.
|
|
11
|
+
# Supports partial ID matching (e.g., "0042" matches "0042-auth-feature").
|
|
12
|
+
#
|
|
13
|
+
# Compatible with bash 3.x (macOS default)
|
|
14
|
+
|
|
15
|
+
set -e
|
|
16
|
+
|
|
17
|
+
INCREMENT_ID="${1:-}"
|
|
18
|
+
|
|
19
|
+
# Find project root
|
|
20
|
+
PROJECT_ROOT="$PWD"
|
|
21
|
+
while [[ "$PROJECT_ROOT" != "/" ]] && [[ ! -d "$PROJECT_ROOT/.specweave" ]]; do
|
|
22
|
+
PROJECT_ROOT=$(dirname "$PROJECT_ROOT")
|
|
23
|
+
done
|
|
24
|
+
|
|
25
|
+
if [[ ! -d "$PROJECT_ROOT/.specweave" ]]; then
|
|
26
|
+
echo "No SpecWeave project found (missing .specweave/)"
|
|
27
|
+
exit 1
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
INCREMENTS_DIR="$PROJECT_ROOT/.specweave/increments"
|
|
31
|
+
STATE_DIR="$PROJECT_ROOT/.specweave/state"
|
|
32
|
+
|
|
33
|
+
# Auto-detect active increment if no ID given
|
|
34
|
+
if [[ -z "$INCREMENT_ID" ]]; then
|
|
35
|
+
if [[ -f "$STATE_DIR/active-increment.json" ]] && command -v jq >/dev/null 2>&1; then
|
|
36
|
+
INCREMENT_ID=$(jq -r '.ids[0] // empty' "$STATE_DIR/active-increment.json" 2>/dev/null)
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
if [[ -z "$INCREMENT_ID" ]]; then
|
|
40
|
+
echo "No increment ID provided and no active increment found."
|
|
41
|
+
echo "Usage: /sw:grill <incrementId>"
|
|
42
|
+
exit 1
|
|
43
|
+
fi
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
# Find increment folder (exact or partial match)
|
|
47
|
+
FOUND_DIR=""
|
|
48
|
+
if [[ -d "$INCREMENTS_DIR/$INCREMENT_ID" ]]; then
|
|
49
|
+
FOUND_DIR="$INCREMENTS_DIR/$INCREMENT_ID"
|
|
50
|
+
else
|
|
51
|
+
# Partial match
|
|
52
|
+
for dir in "$INCREMENTS_DIR"/[0-9]*/; do
|
|
53
|
+
dirname=$(basename "$dir")
|
|
54
|
+
if [[ "$dirname" == "$INCREMENT_ID"* ]]; then
|
|
55
|
+
FOUND_DIR="${dir%/}" # Strip trailing slash from glob
|
|
56
|
+
INCREMENT_ID="$dirname"
|
|
57
|
+
break
|
|
58
|
+
fi
|
|
59
|
+
done
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
if [[ -z "$FOUND_DIR" ]] || [[ ! -d "$FOUND_DIR" ]]; then
|
|
63
|
+
echo "Increment not found: ${1:-$INCREMENT_ID}"
|
|
64
|
+
exit 1
|
|
65
|
+
fi
|
|
66
|
+
|
|
67
|
+
# Read metadata
|
|
68
|
+
STATUS="unknown"
|
|
69
|
+
TYPE="feature"
|
|
70
|
+
PRIORITY="P1"
|
|
71
|
+
if [[ -f "$FOUND_DIR/metadata.json" ]] && command -v jq >/dev/null 2>&1; then
|
|
72
|
+
STATUS=$(jq -r '.status // "unknown"' "$FOUND_DIR/metadata.json" 2>/dev/null)
|
|
73
|
+
TYPE=$(jq -r '.type // "feature"' "$FOUND_DIR/metadata.json" 2>/dev/null)
|
|
74
|
+
PRIORITY=$(jq -r '.priority // "P1"' "$FOUND_DIR/metadata.json" 2>/dev/null)
|
|
75
|
+
fi
|
|
76
|
+
|
|
77
|
+
# Count tasks
|
|
78
|
+
# NOTE: grep -c exits 1 on zero matches but still outputs "0".
|
|
79
|
+
# Using || true prevents set -e from killing the script without doubling output.
|
|
80
|
+
TASK_TOTAL=0
|
|
81
|
+
TASK_COMPLETED=0
|
|
82
|
+
TASK_PCT=0
|
|
83
|
+
if [[ -f "$FOUND_DIR/tasks.md" ]]; then
|
|
84
|
+
TASK_TOTAL=$(grep -c '### T-[0-9]' "$FOUND_DIR/tasks.md" 2>/dev/null) || true
|
|
85
|
+
TASK_COMPLETED=$(grep -ci '\*\*Status\*\*:\s*\[x\]' "$FOUND_DIR/tasks.md" 2>/dev/null) || true
|
|
86
|
+
[[ -z "$TASK_TOTAL" ]] && TASK_TOTAL=0
|
|
87
|
+
[[ -z "$TASK_COMPLETED" ]] && TASK_COMPLETED=0
|
|
88
|
+
if [[ "$TASK_TOTAL" -gt 0 ]]; then
|
|
89
|
+
TASK_PCT=$((TASK_COMPLETED * 100 / TASK_TOTAL))
|
|
90
|
+
fi
|
|
91
|
+
fi
|
|
92
|
+
|
|
93
|
+
# Count ACs from spec.md
|
|
94
|
+
AC_TOTAL=0
|
|
95
|
+
AC_COMPLETED=0
|
|
96
|
+
AC_PCT=0
|
|
97
|
+
if [[ -f "$FOUND_DIR/spec.md" ]]; then
|
|
98
|
+
AC_TOTAL=$(grep -cE '^\s*- \[[ x]\] \*\*AC-' "$FOUND_DIR/spec.md" 2>/dev/null) || true
|
|
99
|
+
AC_COMPLETED=$(grep -cE '^\s*- \[x\] \*\*AC-' "$FOUND_DIR/spec.md" 2>/dev/null) || true
|
|
100
|
+
[[ -z "$AC_TOTAL" ]] && AC_TOTAL=0
|
|
101
|
+
[[ -z "$AC_COMPLETED" ]] && AC_COMPLETED=0
|
|
102
|
+
if [[ "$AC_TOTAL" -gt 0 ]]; then
|
|
103
|
+
AC_PCT=$((AC_COMPLETED * 100 / AC_TOTAL))
|
|
104
|
+
fi
|
|
105
|
+
fi
|
|
106
|
+
|
|
107
|
+
# Output structured grill context
|
|
108
|
+
cat <<GRILL_CONTEXT
|
|
109
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
110
|
+
GRILL MODE ACTIVATED — ${INCREMENT_ID}
|
|
111
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
112
|
+
|
|
113
|
+
CONTEXT:
|
|
114
|
+
Status: ${STATUS} | Type: ${TYPE} | Priority: ${PRIORITY}
|
|
115
|
+
Tasks: ${TASK_COMPLETED}/${TASK_TOTAL} (${TASK_PCT}%)
|
|
116
|
+
ACs: ${AC_COMPLETED}/${AC_TOTAL} (${AC_PCT}%)
|
|
117
|
+
|
|
118
|
+
INCREMENT FILES:
|
|
119
|
+
spec.md: ${FOUND_DIR}/spec.md
|
|
120
|
+
tasks.md: ${FOUND_DIR}/tasks.md
|
|
121
|
+
plan.md: ${FOUND_DIR}/plan.md
|
|
122
|
+
|
|
123
|
+
INSTRUCTIONS — You are now in GRILL MODE. Act as a demanding senior engineer:
|
|
124
|
+
|
|
125
|
+
1. Read the increment's spec.md to understand WHAT should have been built
|
|
126
|
+
2. Read tasks.md to understand WHAT was done
|
|
127
|
+
3. Find all changed files:
|
|
128
|
+
git diff --name-only \$(git merge-base HEAD main 2>/dev/null || git merge-base HEAD develop 2>/dev/null || echo HEAD~10)..HEAD 2>/dev/null
|
|
129
|
+
If git is not available, read the tasks for file references.
|
|
130
|
+
4. For EACH significant changed file, check:
|
|
131
|
+
- Correctness: Does it satisfy the ACs? Edge cases? Null handling?
|
|
132
|
+
- Security: Injection? XSS? Auth bypass? Secrets in code?
|
|
133
|
+
- Performance: O(n²)? N+1 queries? Memory leaks? Blocking ops?
|
|
134
|
+
- Maintainability: God functions? Magic numbers? Inconsistent patterns?
|
|
135
|
+
5. Categorize every issue found:
|
|
136
|
+
BLOCKER — Production will break, MUST fix
|
|
137
|
+
CRITICAL — Security/data risk, MUST fix
|
|
138
|
+
MAJOR — Significant gap, should fix
|
|
139
|
+
MINOR — Code quality, can fix later
|
|
140
|
+
SUGGESTION — Nice to have improvement
|
|
141
|
+
|
|
142
|
+
OUTPUT FORMAT:
|
|
143
|
+
For each issue: [{SEVERITY}] Title | File:line | Problem | Fix
|
|
144
|
+
End with: VERDICT: PASS (no blockers/criticals) or FAIL (has blockers/criticals)
|
|
145
|
+
|
|
146
|
+
If FAIL: List blocking issues and stop. Do NOT proceed with /sw:done.
|
|
147
|
+
If PASS: Code is ready for closure.
|
|
148
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
149
|
+
GRILL_CONTEXT
|
|
@@ -8,22 +8,22 @@ Set up comprehensive Playwright E2E testing with best practices, page objects, a
|
|
|
8
8
|
|
|
9
9
|
You are an expert E2E testing engineer who implements production-ready Playwright test suites.
|
|
10
10
|
|
|
11
|
-
## CLI
|
|
11
|
+
## CLI-First Rule (MANDATORY)
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
**ALWAYS use Playwright CLI** (`npx playwright test`, `npx playwright codegen`) for all test setup, execution, and debugging. **DO NOT** use MCP Playwright tools (`browser_click`, `browser_snapshot`, `browser_navigate`, etc.) — they bypass playwright.config.ts, ignore fixtures/reporters, and waste tokens.
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
- **@playwright/mcp** (for interactive exploration): Rich inline snapshots, good for debugging
|
|
15
|
+
MCP Playwright is only acceptable for `/sw-testing:ui-inspect` (interactive DOM inspection). For everything else, use the CLI via Bash.
|
|
17
16
|
|
|
18
|
-
|
|
17
|
+
```bash
|
|
18
|
+
# Install Playwright
|
|
19
|
+
npm init playwright@latest
|
|
19
20
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}
|
|
21
|
+
# Run tests
|
|
22
|
+
npx playwright test
|
|
23
|
+
|
|
24
|
+
# Debug
|
|
25
|
+
npx playwright test --debug
|
|
26
|
+
npx playwright test --ui
|
|
27
27
|
```
|
|
28
28
|
|
|
29
29
|
## Your Task
|
|
@@ -19,9 +19,9 @@ Create and execute automated browser workflows using Playwright. Generate script
|
|
|
19
19
|
3. **Error Handling**: Add retry logic, timeouts, and fallbacks
|
|
20
20
|
4. **Output Collection**: Capture screenshots, data, and validation results
|
|
21
21
|
|
|
22
|
-
> **
|
|
23
|
-
|
|
24
|
-
>
|
|
22
|
+
> **Code-First, CLI-First (MANDATORY)**: Generate Playwright scripts as `.ts` files and run them via CLI (`npx playwright test` or `npx tsx script.ts`). **DO NOT** use MCP Playwright tools (`browser_click`, `browser_fill_form`, `browser_navigate`, etc.) for automation — they produce ephemeral tool calls instead of reusable code, consume 20x more tokens, and cannot be committed or run in CI.
|
|
23
|
+
>
|
|
24
|
+
> Anthropic research confirms [code execution beats MCP tool calls](https://www.anthropic.com/engineering/code-execution-with-mcp) with 98% token reduction.
|
|
25
25
|
|
|
26
26
|
## Workflow Types
|
|
27
27
|
|
|
@@ -65,11 +65,11 @@ Provides:
|
|
|
65
65
|
|
|
66
66
|
## Browser Mode
|
|
67
67
|
|
|
68
|
-
This command
|
|
68
|
+
This command is the **only SpecWeave testing command where MCP Playwright is appropriate**. It uses MCP's `browser_snapshot` for rich DOM introspection — the AI can reason about the accessibility tree inline and find optimal selectors.
|
|
69
69
|
|
|
70
|
-
If MCP is unavailable, falls back to `@playwright/cli snapshot` which saves the accessibility tree to a file (`.playwright-cli/*.yml`).
|
|
70
|
+
If MCP is unavailable, falls back to `@playwright/cli snapshot` which saves the accessibility tree to a file (`.playwright-cli/*.yml`).
|
|
71
71
|
|
|
72
|
-
**
|
|
72
|
+
**Important**: MCP Playwright is appropriate here for interactive inspection. For all other testing tasks (running tests, writing test code, automation scripts), always use the Playwright CLI via Bash instead. See `/sw-testing:e2e-setup` for details.
|
|
73
73
|
|
|
74
74
|
## Requirements
|
|
75
75
|
|
|
@@ -1,14 +1,18 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { execFileSync } from "child_process";
|
|
2
2
|
let cachedResult = null;
|
|
3
|
+
function clearCache() {
|
|
4
|
+
cachedResult = null;
|
|
5
|
+
}
|
|
3
6
|
function detectPlaywrightCli(options = {}) {
|
|
4
7
|
const { useCache = false } = options;
|
|
5
8
|
if (useCache && cachedResult) {
|
|
6
9
|
return cachedResult;
|
|
7
10
|
}
|
|
11
|
+
const whichCmd = process.platform === "win32" ? "where" : "which";
|
|
8
12
|
let path;
|
|
9
13
|
let version;
|
|
10
14
|
try {
|
|
11
|
-
path =
|
|
15
|
+
path = execFileSync(whichCmd, ["playwright-cli"], {
|
|
12
16
|
encoding: "utf-8",
|
|
13
17
|
timeout: 5e3
|
|
14
18
|
}).trim();
|
|
@@ -18,7 +22,7 @@ function detectPlaywrightCli(options = {}) {
|
|
|
18
22
|
return result2;
|
|
19
23
|
}
|
|
20
24
|
try {
|
|
21
|
-
version =
|
|
25
|
+
version = execFileSync("playwright-cli", ["--version"], {
|
|
22
26
|
encoding: "utf-8",
|
|
23
27
|
timeout: 5e3
|
|
24
28
|
}).trim();
|
|
@@ -29,5 +33,6 @@ function detectPlaywrightCli(options = {}) {
|
|
|
29
33
|
return result;
|
|
30
34
|
}
|
|
31
35
|
export {
|
|
36
|
+
clearCache,
|
|
32
37
|
detectPlaywrightCli
|
|
33
38
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { execFileSync } from 'child_process';
|
|
2
2
|
|
|
3
3
|
export interface CliDetectionResult {
|
|
4
4
|
installed: boolean;
|
|
@@ -12,6 +12,10 @@ export interface DetectOptions {
|
|
|
12
12
|
|
|
13
13
|
let cachedResult: CliDetectionResult | null = null;
|
|
14
14
|
|
|
15
|
+
export function clearCache(): void {
|
|
16
|
+
cachedResult = null;
|
|
17
|
+
}
|
|
18
|
+
|
|
15
19
|
export function detectPlaywrightCli(options: DetectOptions = {}): CliDetectionResult {
|
|
16
20
|
const { useCache = false } = options;
|
|
17
21
|
|
|
@@ -19,11 +23,12 @@ export function detectPlaywrightCli(options: DetectOptions = {}): CliDetectionRe
|
|
|
19
23
|
return cachedResult;
|
|
20
24
|
}
|
|
21
25
|
|
|
26
|
+
const whichCmd = process.platform === 'win32' ? 'where' : 'which';
|
|
22
27
|
let path: string | undefined;
|
|
23
28
|
let version: string | undefined;
|
|
24
29
|
|
|
25
30
|
try {
|
|
26
|
-
path =
|
|
31
|
+
path = execFileSync(whichCmd, ['playwright-cli'], {
|
|
27
32
|
encoding: 'utf-8',
|
|
28
33
|
timeout: 5_000,
|
|
29
34
|
}).trim();
|
|
@@ -34,7 +39,7 @@ export function detectPlaywrightCli(options: DetectOptions = {}): CliDetectionRe
|
|
|
34
39
|
}
|
|
35
40
|
|
|
36
41
|
try {
|
|
37
|
-
version =
|
|
42
|
+
version = execFileSync('playwright-cli', ['--version'], {
|
|
38
43
|
encoding: 'utf-8',
|
|
39
44
|
timeout: 5_000,
|
|
40
45
|
}).trim();
|
|
@@ -1,21 +1,24 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { execFileSync } from "child_process";
|
|
2
2
|
class PlaywrightCliRunner {
|
|
3
3
|
constructor(config = {}) {
|
|
4
4
|
this.config = {
|
|
5
5
|
headed: config.headed ?? false,
|
|
6
6
|
browser: config.browser ?? "chrome",
|
|
7
|
-
session: config.session,
|
|
8
7
|
timeout: config.timeout ?? 3e4,
|
|
9
|
-
|
|
8
|
+
session: config.session
|
|
10
9
|
};
|
|
11
10
|
}
|
|
12
|
-
exec(
|
|
13
|
-
const
|
|
14
|
-
|
|
11
|
+
exec(args) {
|
|
12
|
+
const fullArgs = [];
|
|
13
|
+
if (this.config.session) {
|
|
14
|
+
fullArgs.push(`-s=${this.config.session}`);
|
|
15
|
+
}
|
|
16
|
+
fullArgs.push(...args);
|
|
15
17
|
try {
|
|
16
|
-
const output =
|
|
18
|
+
const output = execFileSync("playwright-cli", fullArgs, {
|
|
17
19
|
encoding: "utf-8",
|
|
18
|
-
timeout: this.config.timeout
|
|
20
|
+
timeout: this.config.timeout,
|
|
21
|
+
maxBuffer: 1024 * 1024
|
|
19
22
|
}).trim();
|
|
20
23
|
return { ok: true, output };
|
|
21
24
|
} catch (e) {
|
|
@@ -23,34 +26,36 @@ class PlaywrightCliRunner {
|
|
|
23
26
|
}
|
|
24
27
|
}
|
|
25
28
|
open(url) {
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
+
const args = ["open"];
|
|
30
|
+
if (url) args.push(url);
|
|
31
|
+
if (this.config.headed) args.push("--headed");
|
|
32
|
+
return this.exec(args);
|
|
29
33
|
}
|
|
30
34
|
navigate(url) {
|
|
31
|
-
return this.exec(
|
|
35
|
+
return this.exec(["goto", url]);
|
|
32
36
|
}
|
|
33
37
|
snapshot() {
|
|
34
|
-
return this.exec("snapshot");
|
|
38
|
+
return this.exec(["snapshot"]);
|
|
35
39
|
}
|
|
36
40
|
screenshot(filename) {
|
|
37
|
-
const
|
|
38
|
-
|
|
41
|
+
const args = ["screenshot"];
|
|
42
|
+
if (filename) args.push("--filename", filename);
|
|
43
|
+
return this.exec(args);
|
|
39
44
|
}
|
|
40
45
|
close() {
|
|
41
|
-
return this.exec("close");
|
|
46
|
+
return this.exec(["close"]);
|
|
42
47
|
}
|
|
43
48
|
click(ref) {
|
|
44
|
-
return this.exec(
|
|
49
|
+
return this.exec(["click", ref]);
|
|
45
50
|
}
|
|
46
51
|
type(text) {
|
|
47
|
-
return this.exec(
|
|
52
|
+
return this.exec(["type", text]);
|
|
48
53
|
}
|
|
49
54
|
fill(ref, text) {
|
|
50
|
-
return this.exec(
|
|
55
|
+
return this.exec(["fill", ref, text]);
|
|
51
56
|
}
|
|
52
57
|
evaluate(fn) {
|
|
53
|
-
return this.exec(
|
|
58
|
+
return this.exec(["eval", fn]);
|
|
54
59
|
}
|
|
55
60
|
}
|
|
56
61
|
export {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { execFileSync } from 'child_process';
|
|
2
2
|
|
|
3
3
|
export interface CliRunnerConfig {
|
|
4
4
|
headed?: boolean;
|
|
@@ -19,19 +19,22 @@ export class PlaywrightCliRunner {
|
|
|
19
19
|
this.config = {
|
|
20
20
|
headed: config.headed ?? false,
|
|
21
21
|
browser: config.browser ?? 'chrome',
|
|
22
|
-
session: config.session,
|
|
23
22
|
timeout: config.timeout ?? 30_000,
|
|
24
|
-
|
|
23
|
+
session: config.session,
|
|
25
24
|
};
|
|
26
25
|
}
|
|
27
26
|
|
|
28
|
-
private exec(
|
|
29
|
-
const
|
|
30
|
-
|
|
27
|
+
private exec(args: string[]): CliResult {
|
|
28
|
+
const fullArgs: string[] = [];
|
|
29
|
+
if (this.config.session) {
|
|
30
|
+
fullArgs.push(`-s=${this.config.session}`);
|
|
31
|
+
}
|
|
32
|
+
fullArgs.push(...args);
|
|
31
33
|
try {
|
|
32
|
-
const output =
|
|
34
|
+
const output = execFileSync('playwright-cli', fullArgs, {
|
|
33
35
|
encoding: 'utf-8',
|
|
34
36
|
timeout: this.config.timeout,
|
|
37
|
+
maxBuffer: 1024 * 1024,
|
|
35
38
|
}).trim();
|
|
36
39
|
return { ok: true, output };
|
|
37
40
|
} catch (e: unknown) {
|
|
@@ -40,41 +43,43 @@ export class PlaywrightCliRunner {
|
|
|
40
43
|
}
|
|
41
44
|
|
|
42
45
|
open(url?: string): CliResult {
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
+
const args = ['open'];
|
|
47
|
+
if (url) args.push(url);
|
|
48
|
+
if (this.config.headed) args.push('--headed');
|
|
49
|
+
return this.exec(args);
|
|
46
50
|
}
|
|
47
51
|
|
|
48
52
|
navigate(url: string): CliResult {
|
|
49
|
-
return this.exec(
|
|
53
|
+
return this.exec(['goto', url]);
|
|
50
54
|
}
|
|
51
55
|
|
|
52
56
|
snapshot(): CliResult {
|
|
53
|
-
return this.exec('snapshot');
|
|
57
|
+
return this.exec(['snapshot']);
|
|
54
58
|
}
|
|
55
59
|
|
|
56
60
|
screenshot(filename?: string): CliResult {
|
|
57
|
-
const
|
|
58
|
-
|
|
61
|
+
const args = ['screenshot'];
|
|
62
|
+
if (filename) args.push('--filename', filename);
|
|
63
|
+
return this.exec(args);
|
|
59
64
|
}
|
|
60
65
|
|
|
61
66
|
close(): CliResult {
|
|
62
|
-
return this.exec('close');
|
|
67
|
+
return this.exec(['close']);
|
|
63
68
|
}
|
|
64
69
|
|
|
65
70
|
click(ref: string): CliResult {
|
|
66
|
-
return this.exec(
|
|
71
|
+
return this.exec(['click', ref]);
|
|
67
72
|
}
|
|
68
73
|
|
|
69
74
|
type(text: string): CliResult {
|
|
70
|
-
return this.exec(
|
|
75
|
+
return this.exec(['type', text]);
|
|
71
76
|
}
|
|
72
77
|
|
|
73
78
|
fill(ref: string, text: string): CliResult {
|
|
74
|
-
return this.exec(
|
|
79
|
+
return this.exec(['fill', ref, text]);
|
|
75
80
|
}
|
|
76
81
|
|
|
77
82
|
evaluate(fn: string): CliResult {
|
|
78
|
-
return this.exec(
|
|
83
|
+
return this.exec(['eval', fn]);
|
|
79
84
|
}
|
|
80
85
|
}
|
|
@@ -230,43 +230,44 @@ e2e/
|
|
|
230
230
|
└── playwright.config.ts
|
|
231
231
|
```
|
|
232
232
|
|
|
233
|
-
## Browser Automation
|
|
233
|
+
## Browser Automation: CLI-First Rule
|
|
234
234
|
|
|
235
|
-
|
|
235
|
+
**MANDATORY**: Always use Playwright CLI (`npx playwright test`, Bash tool) for test execution, automation scripts, and CI/CD workflows. **DO NOT** use MCP Playwright tools (`browser_click`, `browser_snapshot`, `browser_navigate`, etc.) for these tasks.
|
|
236
236
|
|
|
237
|
-
|
|
238
|
-
|------|------|----------|------------|
|
|
239
|
-
| **CLI** | `@playwright/cli` (Bash) | Test execution, automation, CI/CD | ~250 chars/action |
|
|
240
|
-
| **MCP** | Playwright MCP plugin | Interactive inspection, self-healing | ~5K+ chars/action |
|
|
237
|
+
MCP Playwright tools consume ~20x more tokens per action and bypass your test configuration (playwright.config.ts, fixtures, reporters). They are the wrong tool for testing.
|
|
241
238
|
|
|
242
|
-
### When to Use
|
|
243
|
-
- Running E2E test suites (`npx playwright test`)
|
|
244
|
-
- Generating automation scripts
|
|
245
|
-
- CI/CD pipelines (headless by default)
|
|
246
|
-
- Token-constrained sessions
|
|
247
|
-
- Network mocking and auth state management
|
|
239
|
+
### When to Use What
|
|
248
240
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
241
|
+
| Task | Tool | Why |
|
|
242
|
+
|------|------|-----|
|
|
243
|
+
| Run tests | `npx playwright test` (Bash) | Uses project config, parallel execution, reporters |
|
|
244
|
+
| Write test code | Write/Edit tools | Produces committable, CI-runnable `.spec.ts` files |
|
|
245
|
+
| Generate tests | `npx playwright codegen` (Bash) | Records user actions as code |
|
|
246
|
+
| Debug failures | `npx playwright test --debug` or `--ui` (Bash) | Full trace viewer, time-travel debugging |
|
|
247
|
+
| **Exception**: inspect live DOM | MCP `browser_snapshot` | Only when you need to reason about current page structure interactively |
|
|
253
248
|
|
|
254
|
-
###
|
|
249
|
+
### Why CLI Over MCP for Testing
|
|
250
|
+
|
|
251
|
+
1. **Headless control**: CLI respects `playwright.config.ts` headless settings; MCP always opens a visible browser
|
|
252
|
+
2. **Config integration**: CLI uses your fixtures, projects, retries, reporters; MCP ignores them all
|
|
253
|
+
3. **Token efficiency**: CLI returns concise pass/fail output (~250 chars); MCP returns full DOM trees (~5K+ chars per action)
|
|
254
|
+
4. **Reproducibility**: CLI tests are deterministic `.spec.ts` files; MCP interactions are ephemeral tool calls
|
|
255
|
+
5. **CI/CD ready**: CLI output works directly in GitHub Actions, Jenkins, etc.
|
|
256
|
+
|
|
257
|
+
### Install Playwright CLI
|
|
255
258
|
```bash
|
|
256
|
-
npm
|
|
259
|
+
npm init playwright@latest
|
|
260
|
+
# or for existing projects:
|
|
261
|
+
npx playwright install
|
|
257
262
|
```
|
|
258
263
|
|
|
259
|
-
###
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
"playwright": { "preferCli": true }
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
```
|
|
264
|
+
### The Only MCP Exception
|
|
265
|
+
|
|
266
|
+
Use MCP Playwright tools **only** for:
|
|
267
|
+
- `/sw-testing:ui-inspect` — interactive element inspection requiring DOM reasoning
|
|
268
|
+
- One-off page exploration when you need to see what's on a page right now
|
|
268
269
|
|
|
269
|
-
|
|
270
|
+
For everything else — writing tests, running tests, generating tests, debugging tests — use the CLI via Bash.
|
|
270
271
|
|
|
271
272
|
## Related Skills
|
|
272
273
|
|
|
@@ -158,6 +158,8 @@ You are an expert QA engineer with deep knowledge of testing strategies, test au
|
|
|
158
158
|
|
|
159
159
|
### 6. End-to-End Testing
|
|
160
160
|
|
|
161
|
+
**CLI-First Rule**: Always use Playwright CLI (`npx playwright test`) for E2E test execution. DO NOT use MCP Playwright tools (`browser_click`, `browser_snapshot`, etc.) for running or writing tests — they bypass playwright.config.ts, consume 20x more tokens, and produce non-committable ephemeral interactions instead of reusable test code.
|
|
162
|
+
|
|
161
163
|
**Playwright Excellence**:
|
|
162
164
|
- Page Object Model (POM)
|
|
163
165
|
- Fixtures for setup/teardown
|