Skip to content

Commit 4226c5a

Browse files
DavertMikclaude
andcommitted
feat: add pauseOn plugin with multiple debug modes and CLI plugin arguments
Extend plugin system to support colon-separated CLI arguments (-p pluginName:arg1:arg2). Add pauseOn plugin with 4 modes: fail, step, file (breakpoint), and url. Add dedicated debugging documentation page. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 909c877 commit 4226c5a

File tree

3 files changed

+485
-2
lines changed

3 files changed

+485
-2
lines changed

docs/debugging.md

Lines changed: 302 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,302 @@
1+
---
2+
permalink: /debugging
3+
title: Debugging Tests
4+
---
5+
6+
# Debugging Tests
7+
8+
CodeceptJS provides powerful tools for debugging your tests at every level: from verbose output to interactive breakpoints with step-by-step execution.
9+
10+
## Output Verbosity
11+
12+
When something goes wrong, start by increasing the output level:
13+
14+
```bash
15+
npx codeceptjs run --steps # print each step
16+
npx codeceptjs run --debug # print steps + debug info
17+
npx codeceptjs run --verbose # print everything
18+
```
19+
20+
| Flag | What you see |
21+
|------|-------------|
22+
| `--steps` | Each executed step (`I click`, `I see`, etc.) |
23+
| `--debug` | Steps + helper/plugin info, URLs, loaded config |
24+
| `--verbose` | Everything above + internal promise queue, retries, timeouts |
25+
26+
We recommend **always using `--debug`** when developing tests.
27+
28+
For full internal logs, use the `DEBUG` environment variable:
29+
30+
```bash
31+
DEBUG=codeceptjs:* npx codeceptjs run
32+
```
33+
34+
You can narrow it down to specific modules:
35+
36+
```bash
37+
DEBUG=codeceptjs:recorder npx codeceptjs run # promise chain only
38+
DEBUG=codeceptjs:pause npx codeceptjs run # pause session only
39+
```
40+
41+
## Interactive Pause
42+
43+
The most powerful debugging tool in CodeceptJS is **interactive pause**. It stops test execution and opens a live shell where you can run steps directly in the browser.
44+
45+
Add `pause()` anywhere in your test:
46+
47+
```js
48+
Scenario('debug checkout', ({ I }) => {
49+
I.amOnPage('/products')
50+
I.click('Add to Cart')
51+
pause() // <-- test stops here, shell opens
52+
I.click('Checkout')
53+
})
54+
```
55+
56+
When the shell opens, you'll see:
57+
58+
```
59+
Interactive shell started
60+
Use JavaScript syntax to try steps in action
61+
- Press ENTER to run the next step
62+
- Press TAB twice to see all available commands
63+
- Type exit + Enter to exit the interactive shell
64+
- Prefix => to run js commands
65+
```
66+
67+
### Available Commands
68+
69+
| Input | What it does |
70+
|-------|-------------|
71+
| `click('Login')` | Runs `I.click('Login')` — the `I.` prefix is added automatically |
72+
| `see('Welcome')` | Runs `I.see('Welcome')` |
73+
| `grabCurrentUrl()` | Runs a grab method and prints the result |
74+
| `=> myVar` | Evaluates JavaScript expression |
75+
| *ENTER* (empty) | Runs the next test step and pauses again |
76+
| `exit` or `resume` | Exits the shell and continues the test |
77+
| *TAB TAB* | Shows all available I.* methods |
78+
79+
You can also pass variables into the shell:
80+
81+
```js
82+
const userData = { name: 'John', email: 'john@test.com' }
83+
pause({ userData })
84+
// in shell: => userData.name
85+
```
86+
87+
### Using Pause as a Stop Point
88+
89+
`pause()` works as a **breakpoint** in your test. Place it before a failing step to inspect the page state:
90+
91+
```js
92+
Scenario('fix login bug', ({ I }) => {
93+
I.amOnPage('/login')
94+
I.fillField('Email', 'user@test.com')
95+
I.fillField('Password', 'secret')
96+
pause() // stop here, inspect the form before submitting
97+
I.click('Sign In')
98+
I.see('Dashboard')
99+
})
100+
```
101+
102+
You can also add it in hooks:
103+
104+
```js
105+
After(({ I }) => {
106+
pause() // pause after every test to inspect final state
107+
})
108+
```
109+
110+
## Pause On Plugin
111+
112+
For automated debugging without modifying test code, use the `pauseOn` plugin. It pauses tests based on different triggers, controlled entirely from the command line.
113+
114+
### Pause on Failure
115+
116+
Automatically enters interactive pause when a step fails:
117+
118+
```bash
119+
npx codeceptjs run -p pauseOn:fail
120+
```
121+
122+
This is the most common debug workflow — run your tests, and when one fails, you land in the interactive shell with the browser in the exact state of the failure. You can inspect elements, try different selectors, and figure out what went wrong.
123+
124+
> The older `pauseOnFail` plugin still works: `npx codeceptjs run -p pauseOnFail`
125+
126+
### Pause on Every Step
127+
128+
Enters interactive pause at the start of the test. Use *ENTER* to advance step by step:
129+
130+
```bash
131+
npx codeceptjs run -p pauseOn:step
132+
```
133+
134+
This gives you full step-by-step execution. After each step, you're back in the interactive shell where you can inspect the page before pressing ENTER to continue.
135+
136+
### Pause on File (Breakpoint)
137+
138+
Pauses when execution reaches a specific file:
139+
140+
```bash
141+
npx codeceptjs run -p pauseOn:file:tests/login_test.js
142+
```
143+
144+
With a specific line number:
145+
146+
```bash
147+
npx codeceptjs run -p pauseOn:file:tests/login_test.js:43
148+
```
149+
150+
This works like a breakpoint — the test runs normally until it hits a step defined at that file and line, then opens the interactive shell.
151+
152+
### Pause on URL
153+
154+
Pauses when the browser navigates to a matching URL:
155+
156+
```bash
157+
npx codeceptjs run -p pauseOn:url:/users/1
158+
```
159+
160+
Supports `*` wildcards:
161+
162+
```bash
163+
npx codeceptjs run -p pauseOn:url:/api/*/edit
164+
npx codeceptjs run -p pauseOn:url:/checkout/*
165+
```
166+
167+
This is useful when you want to inspect a specific page regardless of which test step navigates there.
168+
169+
### Plugin Arguments Syntax
170+
171+
The `-p` flag supports passing arguments to any plugin using colon-separated syntax:
172+
173+
```
174+
-p <pluginName>:<arg1>:<arg2>:<arg3>
175+
```
176+
177+
Multiple plugins can be enabled together:
178+
179+
```bash
180+
npx codeceptjs run -p pauseOn:fail,retryFailedStep
181+
```
182+
183+
## IDE Debugging
184+
185+
### VS Code
186+
187+
You can use the built-in Node.js debugger in VS Code to set breakpoints in test files.
188+
189+
Add this configuration to `.vscode/launch.json`:
190+
191+
```json
192+
{
193+
"type": "node",
194+
"request": "launch",
195+
"name": "codeceptjs",
196+
"args": ["run", "--grep", "@your_test_tag", "--debug"],
197+
"program": "${workspaceFolder}/node_modules/codeceptjs/bin/codecept.js"
198+
}
199+
```
200+
201+
Set breakpoints in your test files, then press F5 to start debugging. You'll be able to step through code, inspect variables, and use the VS Code debug console — all while the browser is open and controlled by the test.
202+
203+
Combine with `pause()` for the best experience: set a VS Code breakpoint to inspect JavaScript state, then add `pause()` to interact with the browser.
204+
205+
### WebStorm
206+
207+
```bash
208+
node $NODE_DEBUG_OPTION ./node_modules/.bin/codeceptjs run
209+
```
210+
211+
### Node.js Inspector
212+
213+
```bash
214+
node --inspect ./node_modules/.bin/codeceptjs run
215+
node --inspect-brk ./node_modules/.bin/codeceptjs run # break on first line
216+
```
217+
218+
## Debugging on Failure
219+
220+
Several built-in plugins capture information when tests fail. These are most useful on CI where you can't use interactive debugging.
221+
222+
### Screenshots on Failure
223+
224+
Enabled by default. Saves a screenshot when a test fails:
225+
226+
```js
227+
plugins: {
228+
screenshotOnFail: {
229+
enabled: true,
230+
uniqueScreenshotNames: true,
231+
fullPageScreenshots: true,
232+
}
233+
}
234+
```
235+
236+
Screenshots are saved in the `output` directory.
237+
238+
### Page Info on Failure
239+
240+
Captures URL, HTML errors, and browser console logs on failure:
241+
242+
```js
243+
plugins: {
244+
pageInfo: {
245+
enabled: true,
246+
}
247+
}
248+
```
249+
250+
### Step-by-Step Report
251+
252+
Generates a slideshow of screenshots taken after every step — a visual replay of what the test did:
253+
254+
```js
255+
plugins: {
256+
stepByStepReport: {
257+
enabled: true,
258+
deleteSuccessful: true, // keep only failed tests
259+
fullPageScreenshots: true,
260+
}
261+
}
262+
```
263+
264+
```bash
265+
npx codeceptjs run -p stepByStepReport
266+
```
267+
268+
After the run, open `output/records.html` to browse through the slideshows.
269+
270+
## AI-Powered Debugging
271+
272+
### AI in Interactive Pause
273+
274+
When AI is configured, the interactive pause shell accepts natural language commands:
275+
276+
```
277+
AI is enabled! (experimental) Write what you want and make AI run it
278+
279+
I.$ fill the login form with test credentials and submit
280+
```
281+
282+
AI reads the current page HTML and generates CodeceptJS steps. See [Testing with AI](/ai) for setup.
283+
284+
### AI Trace Plugin
285+
286+
The `aiTrace` plugin captures rich execution traces for AI analysis — screenshots, HTML snapshots, ARIA trees, browser logs, and network requests at every step:
287+
288+
```js
289+
plugins: {
290+
aiTrace: {
291+
enabled: true,
292+
}
293+
}
294+
```
295+
296+
After a test run, trace files are generated in `output/trace_*/trace.md`. Feed these to an AI assistant (like Claude Code) for automated failure analysis. See [AI Trace Plugin](/aitrace) for details.
297+
298+
### MCP Server
299+
300+
CodeceptJS includes an [MCP server](/mcp) that allows AI agents to control tests programmatically — list tests, run them step by step, capture artifacts, and analyze results. This enables AI-driven debugging workflows where an agent can investigate failures autonomously.
301+
302+
> AI agent integration and MCP server will be covered in detail on a dedicated page.

lib/container.js

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -690,14 +690,28 @@ async function loadPluginFallback(modulePath, config) {
690690
async function createPlugins(config, options = {}) {
691691
const plugins = {}
692692

693-
const enabledPluginsByOptions = (options.plugins || '').split(',')
693+
const pluginOptionMap = new Map()
694+
for (const token of (options.plugins || '').split(',').filter(Boolean)) {
695+
const parts = token.split(':')
696+
pluginOptionMap.set(parts[0], parts.slice(1))
697+
}
698+
699+
for (const [name] of pluginOptionMap) {
700+
if (!config[name]) config[name] = {}
701+
}
702+
694703
for (const pluginName in config) {
695704
if (!config[pluginName]) config[pluginName] = {}
696705
const pluginConfig = config[pluginName]
697-
if (!pluginConfig.enabled && enabledPluginsByOptions.indexOf(pluginName) < 0) {
706+
const enabledByCli = pluginOptionMap.has(pluginName)
707+
if (!pluginConfig.enabled && !enabledByCli) {
698708
continue // plugin is disabled
699709
}
700710

711+
if (enabledByCli && pluginOptionMap.get(pluginName).length > 0) {
712+
pluginConfig._args = pluginOptionMap.get(pluginName)
713+
}
714+
701715
// Generic workers gate:
702716
// - runInWorker / runInWorkers controls plugin execution inside worker threads.
703717
// - runInParent / runInMain can disable plugin in workers parent process.

0 commit comments

Comments
 (0)