@delt/tester-mcp 0.1.0
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/README.md +36 -0
- package/bin/tester-mcp.js +2 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +106 -0
- package/dist/config/loadConfig.d.ts +14 -0
- package/dist/config/loadConfig.js +20 -0
- package/dist/env/captureEnv.d.ts +8 -0
- package/dist/env/captureEnv.js +20 -0
- package/dist/guide/loadGuide.d.ts +6 -0
- package/dist/guide/loadGuide.js +23 -0
- package/dist/init.d.ts +17 -0
- package/dist/init.js +127 -0
- package/dist/result/parseExecutorResult.d.ts +11 -0
- package/dist/result/parseExecutorResult.js +29 -0
- package/dist/result/types.d.ts +44 -0
- package/dist/result/types.js +1 -0
- package/dist/result/writeResult.d.ts +3 -0
- package/dist/result/writeResult.js +22 -0
- package/dist/run/buildExecutorArgs.d.ts +6 -0
- package/dist/run/buildExecutorArgs.js +39 -0
- package/dist/run/buildPrompt.d.ts +7 -0
- package/dist/run/buildPrompt.js +84 -0
- package/dist/run/runScenario.d.ts +18 -0
- package/dist/run/runScenario.js +26 -0
- package/dist/run/runScenarios.d.ts +11 -0
- package/dist/run/runScenarios.js +47 -0
- package/dist/run/spawnExecutor.d.ts +34 -0
- package/dist/run/spawnExecutor.js +93 -0
- package/dist/run/streamParser.d.ts +17 -0
- package/dist/run/streamParser.js +51 -0
- package/dist/run/summarizeLine.d.ts +1 -0
- package/dist/run/summarizeLine.js +29 -0
- package/dist/scenario/actions.d.ts +5 -0
- package/dist/scenario/actions.js +33 -0
- package/dist/scenario/expandScenarioPaths.d.ts +1 -0
- package/dist/scenario/expandScenarioPaths.js +31 -0
- package/dist/scenario/parseScenario.d.ts +2 -0
- package/dist/scenario/parseScenario.js +37 -0
- package/dist/scenario/types.d.ts +47 -0
- package/dist/scenario/types.js +1 -0
- package/dist/secrets/loadSecretsFile.d.ts +1 -0
- package/dist/secrets/loadSecretsFile.js +10 -0
- package/dist/secrets/redactSecrets.d.ts +6 -0
- package/dist/secrets/redactSecrets.js +46 -0
- package/dist/secrets/resolveSecrets.d.ts +5 -0
- package/dist/secrets/resolveSecrets.js +16 -0
- package/dist/util/runId.d.ts +1 -0
- package/dist/util/runId.js +3 -0
- package/package.json +19 -0
- package/skills/tester-mcp/SKILL.md +29 -0
- package/skills/tester-mcp/document-guide.md +146 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// ${secrets.a.b} → secrets file (nested) first, else env SECRET_A_B; throw if missing.
|
|
2
|
+
export function resolveSecrets(value, opts = {}) {
|
|
3
|
+
const env = opts.env ?? process.env;
|
|
4
|
+
return value.replace(/\$\{secrets\.([\w.]+)\}/g, (_m, path) => {
|
|
5
|
+
const fromFile = path
|
|
6
|
+
.split(".")
|
|
7
|
+
.reduce((o, k) => (o == null ? undefined : o[k]), opts.secrets);
|
|
8
|
+
if (typeof fromFile === "string")
|
|
9
|
+
return fromFile;
|
|
10
|
+
const key = "SECRET_" + path.replace(/\./g, "_").toUpperCase();
|
|
11
|
+
const v = env[key];
|
|
12
|
+
if (v === undefined)
|
|
13
|
+
throw new Error(`시크릿 누락: ${path} (tester-mcp.secrets.yaml 의 ${path} 또는 env ${key})`);
|
|
14
|
+
return String(v);
|
|
15
|
+
});
|
|
16
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function makeRunId(now?: Date): string;
|
package/package.json
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@delt/tester-mcp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Opus(planner) + Haiku(executor) + Chrome 통합 테스트 오케스트레이터",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": { "tester-mcp": "bin/tester-mcp.js" },
|
|
7
|
+
"files": ["dist", "bin", "skills"],
|
|
8
|
+
"publishConfig": { "access": "public" },
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsc -p tsconfig.json",
|
|
11
|
+
"dev": "tsx src/cli.ts",
|
|
12
|
+
"test": "vitest run"
|
|
13
|
+
},
|
|
14
|
+
"engines": { "node": ">=20" },
|
|
15
|
+
"dependencies": { "commander": "^12.1.0", "yaml": "^2.5.0" },
|
|
16
|
+
"devDependencies": {
|
|
17
|
+
"typescript": "^5.5.0", "tsx": "^4.16.0", "vitest": "^2.0.0", "@types/node": "^20.14.0"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Run browser screen E2E tests. Use when the user asks to test a web screen or flow, verify a login or form, or check a UI after a change. Writes a scenario YAML, runs the tester-mcp CLI to drive a claude-in-chrome executor, and reports PASS / PARTIAL / FAIL / NOT_TESTED.
|
|
3
|
+
allowed-tools: Bash(tester-mcp *) Read Write Edit Glob Grep
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# tester-mcp — Screen E2E testing
|
|
7
|
+
|
|
8
|
+
Current CLI usage (always up to date):
|
|
9
|
+
|
|
10
|
+
!`tester-mcp --help`
|
|
11
|
+
|
|
12
|
+
> Prerequisites and the full scenario DSL (single source of truth): run `tester-mcp document-guide`.
|
|
13
|
+
|
|
14
|
+
## Workflow (document first)
|
|
15
|
+
|
|
16
|
+
1. **Write the scenario** — `scenarios/<area>/<id>.yaml`. **Resolve a stable `css` or `role` selector from the Vue/PrimeVue source and put it in the `target`** — don't rely on the executor to find elements by natural language (it tries once, then bails NOT_TESTED). `description`/`text` are last-resort fallbacks. Pin the language with `locale:`, reference secrets as `${secrets...}`. Full schema: `tester-mcp document-guide`.
|
|
17
|
+
2. **Run** — `tester-mcp run <scenarios...> -c <config>`. The CLI spawns the executor(s) and waits. Pass multiple scenarios (files or a directory) to run them in parallel; `--concurrency <1-10>` caps how many run at once (default `min(count, 10)`). Each executor creates its OWN browser tab (via tabs_create), so they run truly in parallel (~2-3× at 4-way). They still share one Chrome, so heavy scenarios contend somewhat — keep concurrency modest for heavy flows.
|
|
18
|
+
3. **Branch on the result label**:
|
|
19
|
+
- PASS / PARTIAL → report the evidence and screenshots.
|
|
20
|
+
- FAIL → present the contradicting evidence, screenshots, and `handoff_notes`, then move into a fix.
|
|
21
|
+
- NOT_TESTED → give the reason plus `pattern_inference` (assumed_ok/unknown); state the missing precondition, or hand off to a human after repeated failure. Read the result's executor_log (full tool trail) to diagnose, then fix the scenario's selectors.
|
|
22
|
+
|
|
23
|
+
## Secrets
|
|
24
|
+
|
|
25
|
+
The test account lives in the gitignored `tester-mcp.secrets.yaml` (or env `SECRET_*` in CI); reference it as `${secrets.tester.username}`. If it is missing, tell the user to fill it in. Details: `tester-mcp document-guide`.
|
|
26
|
+
|
|
27
|
+
## Discipline
|
|
28
|
+
|
|
29
|
+
Grep for references to any changed symbol and check runtime dependencies (storage, store, API params). Separate proof from inference — report "verified X, inferred Y by the same pattern", never absolute claims.
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# tester-mcp — Authoring Guide
|
|
2
|
+
|
|
3
|
+
This guide is printed by `tester-mcp document-guide`. It is the single source of
|
|
4
|
+
truth for prerequisites and the scenario DSL. Read it before writing scenarios.
|
|
5
|
+
|
|
6
|
+
## Prerequisites
|
|
7
|
+
|
|
8
|
+
- Node.js >= 20.
|
|
9
|
+
- The `claude` CLI must be installed and **logged in** (the executor runs
|
|
10
|
+
`claude -p --chrome`).
|
|
11
|
+
- The claude-in-chrome browser extension must be installed and connected. The
|
|
12
|
+
executor drives a **real, visible Chrome window** — it is NOT headless and it
|
|
13
|
+
keeps cookies/session. Chrome/Edge only.
|
|
14
|
+
- On **Windows**, the `--chrome` flag is required for claude-in-chrome to work in
|
|
15
|
+
PowerShell. **WSL is not supported.**
|
|
16
|
+
- Run `tester-mcp init` once per project to install the skill and scaffold
|
|
17
|
+
`tester-mcp.config.yaml` plus a secrets example.
|
|
18
|
+
|
|
19
|
+
## Running
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
tester-mcp run scenarios/<area>/<id>.yaml -c tester-mcp.config.yaml
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The CLI spawns the executor, waits, and writes a result to `runs/<runId>/`.
|
|
26
|
+
A hard timeout (default 5 min, `runner.timeout_ms` or `--timeout`) kills a stuck
|
|
27
|
+
executor and reports NOT_TESTED.
|
|
28
|
+
|
|
29
|
+
### Running multiple scenarios in parallel
|
|
30
|
+
|
|
31
|
+
Pass several scenario files (or a directory) to run them concurrently — each
|
|
32
|
+
scenario gets its own executor process and creates its OWN browser tab (via
|
|
33
|
+
tabs_create) inside the shared Chrome tab group, so they run in parallel
|
|
34
|
+
(~2-3× at 4-way; one Chrome still serializes part of the work, so expect some
|
|
35
|
+
contention and varied finish times, not a clean N×):
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
tester-mcp run scenarios/a.yaml scenarios/b.yaml --concurrency 3 -c tester-mcp.config.yaml
|
|
39
|
+
tester-mcp run scenarios/auth/ --concurrency 5 -c tester-mcp.config.yaml
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
- `--concurrency <1-10>`: how many run at once (default `min(count, 10)`, hard cap 10).
|
|
43
|
+
- Each scenario writes its own `runs/<runId>/<id>.json` + `.log`.
|
|
44
|
+
- Logins are per-tab isolated (auth in sessionStorage), so concurrent logins are safe.
|
|
45
|
+
- But `localStorage` (e.g. `languageType`) is shared across tabs — keep parallel scenarios on the **same locale**, or run different locales serially.
|
|
46
|
+
- Slow scenarios under contention may exceed the timeout — raise `--timeout` (e.g. 240000–300000) for parallel batches.
|
|
47
|
+
- Each scenario `id` must be unique across the batch (results/logs are keyed by `id`).
|
|
48
|
+
|
|
49
|
+
## Scenario file
|
|
50
|
+
|
|
51
|
+
A scenario is one YAML file. Fields:
|
|
52
|
+
|
|
53
|
+
- `id` (string, required) — stable identifier, used in the result filename.
|
|
54
|
+
- `title` (string, required) — human-readable summary.
|
|
55
|
+
- `steps` (list, required) — ordered actions (see below).
|
|
56
|
+
- `locale` (string, optional) — pins UI language: `kg` (Kyrgyz), `ru` (Russian),
|
|
57
|
+
`kr` (Korean). The executor switches the app to this language first.
|
|
58
|
+
- `login_as` (string, optional) — named login to perform before the steps.
|
|
59
|
+
- `on_failure` (string, optional) — `stop` (default) or `continue`.
|
|
60
|
+
- `optional` (bool, optional) — if true, a FAIL is downgraded to a soft signal.
|
|
61
|
+
- `defaults` (map, optional) — default values reused across steps.
|
|
62
|
+
- `precondition` (string, optional) — human note on required data/state.
|
|
63
|
+
|
|
64
|
+
## Actions (each item in `steps`)
|
|
65
|
+
|
|
66
|
+
- `navigate` — `{ action: navigate, url: "/path" }` (relative to `targets.frontend`).
|
|
67
|
+
- `fill` — `{ action: fill, target: <target>, value: "..." }`.
|
|
68
|
+
- `click` — `{ action: click, target: <target> }`.
|
|
69
|
+
- `wait_for` — `{ action: wait_for, target: <target> }`.
|
|
70
|
+
- `assert_visible` — `{ action: assert_visible, target: <target> }`.
|
|
71
|
+
- `screenshot` — `{ action: screenshot, name: "after-login" }`.
|
|
72
|
+
|
|
73
|
+
## Target (how to locate an element)
|
|
74
|
+
|
|
75
|
+
`target` is a multi-strategy object. Provide one or more; the executor tries them
|
|
76
|
+
in order of specificity. There is no testid in this project, so prefer stable
|
|
77
|
+
attributes and visible text.
|
|
78
|
+
|
|
79
|
+
- `css` — CSS selector.
|
|
80
|
+
- `placeholder` — input placeholder text.
|
|
81
|
+
- `label` — associated label text.
|
|
82
|
+
- `text` — visible text content.
|
|
83
|
+
- `role` — ARIA role (e.g. `button`).
|
|
84
|
+
- `description` — natural-language fallback ("the blue Login button").
|
|
85
|
+
|
|
86
|
+
Example:
|
|
87
|
+
```yaml
|
|
88
|
+
target:
|
|
89
|
+
placeholder: "Username"
|
|
90
|
+
description: "the username input on the login form"
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
**Authoring rule (selector-first).** Resolve a stable `css` or `role`+name from the
|
|
94
|
+
component source (Vue/PrimeVue) and put it in the target. `description`/`text` are
|
|
95
|
+
last-resort fallbacks, not the primary strategy. The executor tries the target's
|
|
96
|
+
strategies **once** and does NOT grope the page — on a first-attempt miss it bails
|
|
97
|
+
with NOT_TESTED and reports what it actually saw. A precise selector is what drives
|
|
98
|
+
pass rate and speed. For multi-step UI (filters, dropdowns, modals), script the
|
|
99
|
+
open→select sequence as explicit steps with `wait_for` between them.
|
|
100
|
+
|
|
101
|
+
## Secrets
|
|
102
|
+
|
|
103
|
+
Never inline credentials. Reference them as `${secrets.a.b}`:
|
|
104
|
+
```yaml
|
|
105
|
+
- { action: fill, target: { placeholder: "Username" }, value: "${secrets.tester.username}" }
|
|
106
|
+
- { action: fill, target: { placeholder: "Password" }, value: "${secrets.tester.password}" }
|
|
107
|
+
```
|
|
108
|
+
Resolution order: the file `tester-mcp.secrets.yaml` (gitignored) first, then the
|
|
109
|
+
environment variable `SECRET_A_B` (uppercased, dot → underscore). Secret values
|
|
110
|
+
are redacted to `***` in stored results.
|
|
111
|
+
|
|
112
|
+
## Ephemeral UI (toasts, snackbars)
|
|
113
|
+
|
|
114
|
+
Short-lived elements (e.g. PrimeVue toast, `life:3000`) vanish faster than tool
|
|
115
|
+
round-trips. To verify them reliably:
|
|
116
|
+
- Set `ephemeral: true` on the scenario.
|
|
117
|
+
- Assert immediately after the trigger with ONE fast text/DOM check — do NOT chain
|
|
118
|
+
fallbacks (JS → find → read_page); the element disappears mid-chain.
|
|
119
|
+
- Do NOT add a `screenshot` step for an ephemeral element — the assertion IS the
|
|
120
|
+
proof, and a screenshot of a vanished element causes retry loops.
|
|
121
|
+
- (Optional) In a test build, raise the toast `life` so it stays long enough.
|
|
122
|
+
|
|
123
|
+
## Result labels
|
|
124
|
+
|
|
125
|
+
- `PASS` — every assertion verified at runtime.
|
|
126
|
+
- `PARTIAL` — some verified, some not (each item carries proof or reason).
|
|
127
|
+
- `FAIL` — an assertion was contradicted at runtime.
|
|
128
|
+
- `NOT_TESTED` — could not run (timeout, missing data, blocked prerequisite).
|
|
129
|
+
|
|
130
|
+
On NOT_TESTED, read the run's `executor_log` (path is in the result JSON) to see the
|
|
131
|
+
tool sequence and errors — that's how the Planner diagnoses and fixes the scenario.
|
|
132
|
+
|
|
133
|
+
## Minimal example
|
|
134
|
+
|
|
135
|
+
```yaml
|
|
136
|
+
id: login-success
|
|
137
|
+
title: Login with valid credentials reaches the dashboard
|
|
138
|
+
locale: ru
|
|
139
|
+
steps:
|
|
140
|
+
- { action: navigate, url: "/" }
|
|
141
|
+
- { action: fill, target: { placeholder: "Username" }, value: "${secrets.tester.username}" }
|
|
142
|
+
- { action: fill, target: { placeholder: "Password" }, value: "${secrets.tester.password}" }
|
|
143
|
+
- { action: click, target: { text: "Login", role: "button" } }
|
|
144
|
+
- { action: assert_visible, target: { description: "the main dashboard after login" } }
|
|
145
|
+
- { action: screenshot, name: "dashboard" }
|
|
146
|
+
```
|