@matware/e2e-runner 1.0.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 ADDED
@@ -0,0 +1,396 @@
1
+ <p align="center">
2
+ <img src="https://img.shields.io/npm/v/@matware/e2e-runner?color=blue" alt="npm version" />
3
+ <img src="https://img.shields.io/node/v/@matware/e2e-runner" alt="node version" />
4
+ <img src="https://img.shields.io/npm/l/@matware/e2e-runner" alt="license" />
5
+ <img src="https://img.shields.io/badge/MCP-compatible-green" alt="MCP compatible" />
6
+ </p>
7
+
8
+ # @matware/e2e-runner
9
+
10
+ JSON-driven E2E test runner. Define browser tests as simple JSON action arrays, run them in parallel against a Chrome pool. No JavaScript test files, no complex setup.
11
+
12
+ ```json
13
+ [
14
+ {
15
+ "name": "login-flow",
16
+ "actions": [
17
+ { "type": "goto", "value": "/login" },
18
+ { "type": "type", "selector": "#email", "value": "user@test.com" },
19
+ { "type": "type", "selector": "#password", "value": "secret" },
20
+ { "type": "click", "text": "Sign In" },
21
+ { "type": "assert_text", "text": "Welcome back" },
22
+ { "type": "screenshot", "value": "logged-in.png" }
23
+ ]
24
+ }
25
+ ]
26
+ ```
27
+
28
+ ---
29
+
30
+ ## Why
31
+
32
+ - **No code** -- Tests are JSON files. QA, product, and devs can all write them.
33
+ - **Parallel** -- Run N tests simultaneously against a shared Chrome pool.
34
+ - **Portable** -- Chrome runs in Docker, tests run anywhere.
35
+ - **CI-ready** -- JUnit XML output, exit code 1 on failure, error screenshots.
36
+ - **AI-native** -- Built-in MCP server for Claude Code integration.
37
+
38
+ ## Quick Start
39
+
40
+ ```bash
41
+ # Install
42
+ npm install @matware/e2e-runner
43
+
44
+ # Scaffold project structure
45
+ npx e2e-runner init
46
+
47
+ # Start Chrome pool (requires Docker)
48
+ npx e2e-runner pool start
49
+
50
+ # Run all tests
51
+ npx e2e-runner run --all
52
+ ```
53
+
54
+ The `init` command creates:
55
+
56
+ ```
57
+ e2e/
58
+ tests/
59
+ 01-sample.json # Sample test suite
60
+ screenshots/ # Reports and error screenshots
61
+ e2e.config.js # Configuration file
62
+ ```
63
+
64
+ ## Test Format
65
+
66
+ Each `.json` file in `e2e/tests/` contains an array of tests. Each test has a `name` and sequential `actions`:
67
+
68
+ ```json
69
+ [
70
+ {
71
+ "name": "homepage-loads",
72
+ "actions": [
73
+ { "type": "goto", "value": "/" },
74
+ { "type": "wait", "selector": ".hero" },
75
+ { "type": "assert_text", "text": "Welcome" },
76
+ { "type": "assert_url", "value": "/" },
77
+ { "type": "screenshot", "value": "homepage.png" }
78
+ ]
79
+ }
80
+ ]
81
+ ```
82
+
83
+ Suite files can have numeric prefixes for ordering (`01-auth.json`, `02-dashboard.json`). The `--suite` flag matches with or without the prefix, so `--suite auth` finds `01-auth.json`.
84
+
85
+ ### Available Actions
86
+
87
+ | Action | Fields | Description |
88
+ |--------|--------|-------------|
89
+ | `goto` | `value` | Navigate to URL (relative to `baseUrl` or absolute) |
90
+ | `click` | `selector` or `text` | Click by CSS selector or visible text content |
91
+ | `type` / `fill` | `selector`, `value` | Clear field and type text |
92
+ | `wait` | `selector`, `text`, or `value` (ms) | Wait for element, text, or fixed delay |
93
+ | `assert_text` | `text` | Assert text exists on the page |
94
+ | `assert_url` | `value` | Assert current URL contains value |
95
+ | `assert_visible` | `selector` | Assert element is visible |
96
+ | `assert_count` | `selector`, `value` | Assert element count matches |
97
+ | `screenshot` | `value` (filename) | Capture a screenshot |
98
+ | `select` | `selector`, `value` | Select a dropdown option |
99
+ | `clear` | `selector` | Clear an input field |
100
+ | `press` | `value` | Press a keyboard key (e.g. `Enter`, `Tab`) |
101
+ | `scroll` | `selector` or `value` (px) | Scroll to element or by pixel amount |
102
+ | `hover` | `selector` | Hover over an element |
103
+ | `evaluate` | `value` | Execute JavaScript in the browser context |
104
+
105
+ ### Click by Text
106
+
107
+ When `click` uses `text` instead of `selector`, it searches across interactive elements:
108
+
109
+ ```
110
+ button, a, [role="button"], [role="tab"], [role="menuitem"], div[class*="cursor"], span
111
+ ```
112
+
113
+ ```json
114
+ { "type": "click", "text": "Sign In" }
115
+ ```
116
+
117
+ ## CLI
118
+
119
+ ```bash
120
+ # Run tests
121
+ npx e2e-runner run --all # All suites
122
+ npx e2e-runner run --suite auth # Single suite
123
+ npx e2e-runner run --tests path/to.json # Specific file
124
+ npx e2e-runner run --inline '<json>' # Inline JSON
125
+
126
+ # Pool management
127
+ npx e2e-runner pool start # Start Chrome container
128
+ npx e2e-runner pool stop # Stop Chrome container
129
+ npx e2e-runner pool status # Check pool health
130
+
131
+ # Other
132
+ npx e2e-runner list # List available suites
133
+ npx e2e-runner init # Scaffold project
134
+ ```
135
+
136
+ ### CLI Options
137
+
138
+ | Flag | Default | Description |
139
+ |------|---------|-------------|
140
+ | `--base-url <url>` | `http://host.docker.internal:3000` | Application base URL |
141
+ | `--pool-url <ws>` | `ws://localhost:3333` | Chrome pool WebSocket URL |
142
+ | `--tests-dir <dir>` | `e2e/tests` | Tests directory |
143
+ | `--screenshots-dir <dir>` | `e2e/screenshots` | Screenshots/reports directory |
144
+ | `--concurrency <n>` | `3` | Parallel test workers |
145
+ | `--timeout <ms>` | `10000` | Default action timeout |
146
+ | `--retries <n>` | `0` | Retry failed tests N times |
147
+ | `--retry-delay <ms>` | `1000` | Delay between retries |
148
+ | `--test-timeout <ms>` | `60000` | Per-test timeout |
149
+ | `--output <format>` | `json` | Report format: `json`, `junit`, `both` |
150
+ | `--env <name>` | `default` | Environment profile |
151
+ | `--pool-port <port>` | `3333` | Chrome pool port |
152
+ | `--max-sessions <n>` | `10` | Max concurrent Chrome sessions |
153
+
154
+ ## Configuration
155
+
156
+ Create `e2e.config.js` (or `e2e.config.json`) in your project root:
157
+
158
+ ```js
159
+ export default {
160
+ baseUrl: 'http://host.docker.internal:3000',
161
+ concurrency: 4,
162
+ retries: 2,
163
+ testTimeout: 30000,
164
+ outputFormat: 'both',
165
+
166
+ hooks: {
167
+ beforeEach: [{ type: 'goto', value: '/' }],
168
+ afterEach: [{ type: 'screenshot', value: 'after-test.png' }],
169
+ },
170
+
171
+ environments: {
172
+ staging: { baseUrl: 'https://staging.example.com' },
173
+ production: { baseUrl: 'https://example.com', concurrency: 5 },
174
+ },
175
+ };
176
+ ```
177
+
178
+ ### Config Priority (highest wins)
179
+
180
+ 1. CLI flags (`--base-url`, `--concurrency`, ...)
181
+ 2. Environment variables (`BASE_URL`, `CONCURRENCY`, ...)
182
+ 3. Config file (`e2e.config.js` or `e2e.config.json`)
183
+ 4. Defaults
184
+
185
+ When `--env <name>` is set, the matching profile from `environments` overrides everything.
186
+
187
+ ### Environment Variables
188
+
189
+ | Variable | Maps to |
190
+ |----------|---------|
191
+ | `BASE_URL` | `baseUrl` |
192
+ | `CHROME_POOL_URL` | `poolUrl` |
193
+ | `TESTS_DIR` | `testsDir` |
194
+ | `SCREENSHOTS_DIR` | `screenshotsDir` |
195
+ | `CONCURRENCY` | `concurrency` |
196
+ | `DEFAULT_TIMEOUT` | `defaultTimeout` |
197
+ | `POOL_PORT` | `poolPort` |
198
+ | `MAX_SESSIONS` | `maxSessions` |
199
+ | `RETRIES` | `retries` |
200
+ | `RETRY_DELAY` | `retryDelay` |
201
+ | `TEST_TIMEOUT` | `testTimeout` |
202
+ | `OUTPUT_FORMAT` | `outputFormat` |
203
+ | `E2E_ENV` | `env` |
204
+
205
+ ## Hooks
206
+
207
+ Hooks run actions at lifecycle points. Define them globally in config or per-suite in the JSON file:
208
+
209
+ ```json
210
+ {
211
+ "hooks": {
212
+ "beforeAll": [{ "type": "goto", "value": "/login" }],
213
+ "beforeEach": [{ "type": "goto", "value": "/" }],
214
+ "afterEach": [],
215
+ "afterAll": []
216
+ },
217
+ "tests": [
218
+ { "name": "test-1", "actions": [...] }
219
+ ]
220
+ }
221
+ ```
222
+
223
+ Suite-level hooks override global hooks per key (non-empty array wins). The plain array format (`[{ name, actions }]`) is still supported.
224
+
225
+ ## Retries and Timeouts
226
+
227
+ Override globally or per-test:
228
+
229
+ ```json
230
+ {
231
+ "name": "flaky-test",
232
+ "retries": 3,
233
+ "timeout": 15000,
234
+ "actions": [...]
235
+ }
236
+ ```
237
+
238
+ - **Retries**: Each attempt gets its own fresh timeout. Tests that pass after retry are flagged as "flaky" in the report.
239
+ - **Timeout**: Applied via `Promise.race()`. Defaults to 60s.
240
+
241
+ ## CI/CD
242
+
243
+ ### JUnit XML
244
+
245
+ ```bash
246
+ npx e2e-runner run --all --output junit
247
+ # or: --output both (JSON + XML)
248
+ ```
249
+
250
+ Output saved to `e2e/screenshots/junit.xml`.
251
+
252
+ ### GitHub Actions
253
+
254
+ ```yaml
255
+ jobs:
256
+ e2e:
257
+ runs-on: ubuntu-latest
258
+ steps:
259
+ - uses: actions/checkout@v4
260
+ - uses: actions/setup-node@v4
261
+ with:
262
+ node-version: 20
263
+ - run: npm ci
264
+ - run: npx e2e-runner pool start
265
+ - run: npx e2e-runner run --all --output junit
266
+ - uses: mikepenz/action-junit-report@v4
267
+ if: always()
268
+ with:
269
+ report_paths: e2e/screenshots/junit.xml
270
+ ```
271
+
272
+ ### Exit Codes
273
+
274
+ | Code | Meaning |
275
+ |------|---------|
276
+ | `0` | All tests passed |
277
+ | `1` | One or more tests failed |
278
+
279
+ ## Programmatic API
280
+
281
+ ```js
282
+ import { createRunner } from '@matware/e2e-runner';
283
+
284
+ const runner = await createRunner({ baseUrl: 'http://localhost:3000' });
285
+
286
+ // Run all suites
287
+ const report = await runner.runAll();
288
+
289
+ // Run a specific suite
290
+ const report = await runner.runSuite('auth');
291
+
292
+ // Run a specific file
293
+ const report = await runner.runFile('e2e/tests/login.json');
294
+
295
+ // Run inline test objects
296
+ const report = await runner.runTests([
297
+ {
298
+ name: 'quick-check',
299
+ actions: [
300
+ { type: 'goto', value: '/' },
301
+ { type: 'assert_text', text: 'Hello' },
302
+ ],
303
+ },
304
+ ]);
305
+ ```
306
+
307
+ ### Lower-Level Exports
308
+
309
+ ```js
310
+ import {
311
+ loadConfig,
312
+ waitForPool, connectToPool, getPoolStatus, startPool, stopPool,
313
+ runTest, runTestsParallel, loadTestFile, loadTestSuite, loadAllSuites, listSuites,
314
+ generateReport, generateJUnitXML, saveReport, printReport,
315
+ executeAction,
316
+ } from '@matware/e2e-runner';
317
+ ```
318
+
319
+ ## Claude Code Integration (MCP)
320
+
321
+ The package includes a built-in [MCP server](https://modelcontextprotocol.io/) that gives Claude Code native access to the test runner. Install once and it's available in every project:
322
+
323
+ ```bash
324
+ claude mcp add --transport stdio --scope user e2e-runner \
325
+ -- npx -y -p @matware/e2e-runner e2e-runner-mcp
326
+ ```
327
+
328
+ ### MCP Tools
329
+
330
+ | Tool | Description |
331
+ |------|-------------|
332
+ | `e2e_run` | Run tests (all suites, by suite name, or by file path) |
333
+ | `e2e_list` | List available test suites with test names and counts |
334
+ | `e2e_create_test` | Create a new test JSON file |
335
+ | `e2e_pool_status` | Check Chrome pool availability and capacity |
336
+ | `e2e_pool_start` | Start the Chrome pool Docker container |
337
+ | `e2e_pool_stop` | Stop the Chrome pool |
338
+
339
+ Once installed, Claude Code can run tests, analyze failures, create new test files, and manage the Chrome pool as part of its normal workflow. Just ask:
340
+
341
+ > "Run all E2E tests"
342
+ > "Create a test that verifies the checkout flow"
343
+ > "What's the status of the Chrome pool?"
344
+
345
+ ### Verify Installation
346
+
347
+ ```bash
348
+ claude mcp list
349
+ # e2e-runner: ... - Connected
350
+ ```
351
+
352
+ ## Architecture
353
+
354
+ ```
355
+ bin/cli.js CLI entry point (manual argv parsing)
356
+ bin/mcp-server.js MCP server entry point (stdio transport)
357
+ src/config.js Config cascade: defaults -> file -> env -> CLI -> profile
358
+ src/pool.js Chrome pool: Docker Compose lifecycle + WebSocket
359
+ src/runner.js Parallel test executor with retries and timeouts
360
+ src/actions.js Action engine: maps JSON actions to Puppeteer calls
361
+ src/reporter.js JSON reports, JUnit XML, console output
362
+ src/mcp-server.js MCP server: exposes tools for Claude Code
363
+ src/logger.js ANSI colored logger
364
+ src/index.js Programmatic API (createRunner)
365
+ templates/ Scaffolding templates for init command
366
+ ```
367
+
368
+ ### How It Works
369
+
370
+ 1. **Pool**: A Docker container running [browserless/chrome](https://github.com/browserless/browserless) provides shared Chrome instances via WebSocket.
371
+ 2. **Runner**: Spawns N parallel workers. Each worker connects to the pool, opens a new page, and executes actions sequentially.
372
+ 3. **Actions**: Each JSON action maps to a Puppeteer call (`page.goto`, `page.click`, `page.type`, etc.).
373
+ 4. **Reports**: Results are collected, aggregated into a report, and saved as JSON and/or JUnit XML.
374
+
375
+ The `baseUrl` defaults to `http://host.docker.internal:3000` because Chrome runs inside Docker and needs to reach the host machine.
376
+
377
+ ## Requirements
378
+
379
+ - **Node.js** >= 20
380
+ - **Docker** (for the Chrome pool)
381
+
382
+ ## License
383
+
384
+ Copyright 2025 Matias Aguirre (fastslack)
385
+
386
+ Licensed under the Apache License, Version 2.0 (the "License");
387
+ you may not use this file except in compliance with the License.
388
+ You may obtain a copy of the License at
389
+
390
+ http://www.apache.org/licenses/LICENSE-2.0
391
+
392
+ Unless required by applicable law or agreed to in writing, software
393
+ distributed under the License is distributed on an "AS IS" BASIS,
394
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
395
+ See the License for the specific language governing permissions and
396
+ limitations under the License.