Skip to content

Commit 2094646

Browse files
author
DavertMik
committed
Merge branch '4.x' of github.com:codeceptjs/CodeceptJS into 4.x
2 parents 450ba82 + 20011d8 commit 2094646

9 files changed

Lines changed: 112 additions & 42 deletions

File tree

README.md

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -251,20 +251,22 @@ Add the `testomatio` plugin to your `codecept.conf.js`:
251251
plugins: {
252252
testomatio: {
253253
enabled: true,
254-
require: '@testomatio/reporter/lib/adapter/codecept',
254+
require: '@testomatio/reporter/codecept',
255+
html: true,
256+
reportDir: 'output/report',
255257
},
256258
}
257259
```
258260

259261
### Usage
260262

261-
Generate HTML reports by setting the `TESTOMATIO_HTML_REPORT_SAVE` environment variable:
263+
Run tests normally:
262264

263265
```sh
264-
TESTOMATIO_HTML_REPORT_SAVE=1 npx codeceptjs run
266+
npx codeceptjs run
265267
```
266268

267-
The report will be saved to `html-report/testomatio-report.html`.
269+
The report will be saved to `output/report/testomatio-report.html` by default. You can also keep using `TESTOMATIO_HTML_REPORT_SAVE=1` and related environment variables if you prefer env-based setup.
268270

269271
### Features
270272

@@ -278,14 +280,11 @@ The report will be saved to `html-report/testomatio-report.html`.
278280
### Customization
279281

280282
```sh
281-
# Custom output folder
282-
TESTOMATIO_HTML_REPORT_SAVE=1 TESTOMATIO_HTML_REPORT_FOLDER=./reports npx codeceptjs run
283-
284-
# Custom filename
285-
TESTOMATIO_HTML_REPORT_SAVE=1 TESTOMATIO_HTML_FILENAME=my-report.html npx codeceptjs run
283+
# Custom output folder in codecept.conf.js
284+
# reportDir: './reports'
286285

287286
# Integrate with Testomat.io cloud
288-
TESTOMATIO_HTML_REPORT_SAVE=1 TESTOMATIO=your_api_key npx codeceptjs run
287+
TESTOMATIO=your_api_key npx codeceptjs run
289288
```
290289

291290
## PageObjects
449 KB
Loading
162 KB
Loading

docs/migration-4.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -171,16 +171,17 @@ npm install --save-dev @testomatio/reporter
171171
plugins: {
172172
testomatio: {
173173
enabled: true,
174-
require: '@testomatio/reporter/lib/adapter/codecept',
174+
require: '@testomatio/reporter/codecept',
175+
html: true,
175176
},
176177
}
177178
```
178179

179180
```bash
180-
TESTOMATIO_DISABLE_UPLOAD=1 npx codeceptjs run
181+
npx codeceptjs run
181182
```
182183

183-
The HTML report is written to `output/reports/`. See [Reports HTML](/reports) for pipe options.
184+
The HTML report is written to `output/report/` by default. See [Reports > HTML](/reports) for pipe options.
184185

185186
Reporting notes:
186187

docs/plugins/retryFailedStep.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,9 @@ Run tests with plugin enabled:
2828
#### Configuration:
2929

3030
* `retries` - number of retries (by default 3),
31-
* `when` - function, when to perform a retry (accepts error as parameter)
3231
* `factor` - The exponential factor to use. Default is 1.5.
33-
* `minTimeout` - The number of milliseconds before starting the first retry. Default is 1000.
34-
* `maxTimeout` - The maximum number of milliseconds between two retries. Default is Infinity.
32+
* `minTimeout` - The number of milliseconds before starting the first retry. Default is 150.
33+
* `maxTimeout` - The maximum number of milliseconds between two retries. Default is 10000.
3534
* `randomize` - Randomizes the timeouts by multiplying with a factor from 1 to 2. Default is false.
3635
* `defaultIgnoredSteps` - an array of steps to be ignored for retry. Includes:
3736
* `amOnPage`

docs/reports.md

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,19 @@ plugins: {
2121
testomatio: {
2222
enabled: true,
2323
require: '@testomatio/reporter/codecept',
24+
html: true,
25+
markdown: true,
26+
csv: true,
27+
reportDir: 'output/report',
2428
},
2529
}
2630
```
2731

32+
The local reports above are enabled directly from CodeceptJS config. If `reportDir` is omitted, reports are written to `output/report` using the CodeceptJS `output` directory.
33+
2834
### Enable an output
2935

30-
Each output turns on when you set its environment variable. Run your tests as usual one run feeds every output you enabled.
36+
Each output can also be enabled with environment variables. Run your tests as usual and one run feeds every output you enabled.
3137

3238
| To get… | Set | Details |
3339
| --- | --- | --- |
@@ -63,7 +69,9 @@ The GitHub pipe also needs the job to grant `permissions: pull-requests: write`.
6369

6470
A single self-contained HTML file with the run summary and, per test, its steps, screenshots, logs, and error. It needs no API key and no service, so it works anywhere — open it locally or attach it to a CI build.
6571

66-
![HTML report](https://raw.githubusercontent.com/testomatio/reporter/master/docs/pipes/images/html-pipe.png)
72+
![HTML report](./images/testomatio-html-report.png)
73+
74+
- Preferred in CodeceptJS 4: enable `html: true` in `plugins.testomatio` and run `npx codeceptjs run`
6775

6876
- `TESTOMATIO_HTML_REPORT_SAVE=1` — enable the report
6977
- `TESTOMATIO_HTML_REPORT_FOLDER=output/reports` — keep it inside CodeceptJS's `output/` dir (default folder is `html-report`)
@@ -118,6 +126,10 @@ Posts a comment to the Pull Request with the same summary. Comments are created
118126

119127
### Markdown Report
120128

129+
- Preferred in CodeceptJS 4: enable `markdown: true` in `plugins.testomatio` and run `npx codeceptjs run`
130+
131+
![Markdown report](./images/testomatio-markdown-report.png)
132+
121133
A single self-contained Markdown file — renders in PR comments, CI job summaries, and Slack, and is convenient for AI agents reading test results. Needs no API key.
122134

123135
- `TESTOMATIO_MARKDOWN_REPORT_SAVE=1` — enable the report

docs/retry.md

Lines changed: 48 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -20,24 +20,36 @@ CodeceptJS provides flexible retry mechanisms to handle flaky tests. Use retries
2020

2121
## Helper Retries
2222

23-
Browser automation helpers (Playwright, Puppeteer, WebDriver) have **built-in retry mechanisms** for element interactions. When you call `I.click('Button')`, Playwright automatically waits for the element to exist, be visible, stable, and enabled — retrying for up to 5 seconds.
23+
Plawright has a built-in retry mechanism for element interactions. When you call `I.click('Button')`, after the element is located Playwright keeps retrying until it is actionable — up to `timeout` (default 5s).
2424

25-
Configure the timeout in your helper settings:
25+
> WebDriver has a different auto-retry option: [smartWait](/webdriver#smartwait)
26+
27+
Even though the handle exists (from `.all()`), Playwright still waits for it to become visible, stable (not mid-animation), enabled, not covered by an overlay/modal, and not rerendering.
2628

2729
```js
2830
helpers: {
2931
Playwright: {
30-
timeout: 5000, // retry actions for up to 5 seconds
31-
waitForAction: 100 // wait 100ms before each action
32+
timeout: 5000, // retry the action until the element is actionable
33+
waitForAction: 100 // fixed pause AFTER click/doubleClick/pressKey
3234
}
3335
}
3436
```
3537

36-
**Learn more:** [Playwright Helper](/helpers/Playwright), [Timeouts](/timeouts)
38+
What each setting does:
39+
40+
```
41+
find element (no wait — fails instantly if locator matches nothing)
42+
→ wait up to `timeout` for it to become actionable ← timeout
43+
→ perform action
44+
→ sleep `waitForAction` ms ← waitForAction (settle pause, not a wait)
45+
```
46+
47+
`timeout` covers the action. If the locator matches nothing yet, the step fails immediately. Use [Failed Step Retries](#failed-step-retries) to cover that gap.
48+
3749

3850
## Failed Step Retries
3951

40-
Automatically retry all failed steps without modifying test code:
52+
CodeceptJS retries all failed steps by default by using the `retryFailedStep` plugin.
4153

4254
```js
4355
plugins: {
@@ -66,18 +78,36 @@ Scenario('manual retries only', { disableRetryFailedStep: true }, ({ I }) => {
6678
})
6779
```
6880

69-
Full plugin options:
70-
71-
| Option | Default | Description |
72-
|--------|---------|-------------|
73-
| `retries` || Retries per step |
74-
| `minTimeout` || Milliseconds before first retry |
75-
| `maxTimeout` | `Infinity` | Max milliseconds between retries |
76-
| `factor` || Exponential backoff multiplier |
77-
| `randomize` | `false` | Randomize timeout intervals |
78-
| `ignoredSteps` | `[]` | Patterns/regex of steps to never retry |
79-
| `deferToScenarioRetries` | `true` | Disable step retries when scenario retries exist |
80-
| `when` | `() => true` | Function receiving error; return `true` to retry |
81+
Defaults: `minTimeout: 150`, `factor: 1.5`, `maxTimeout: 10000`.
82+
83+
84+
> See [plugin reference](/plugins/retry-failed-step) for more options
85+
86+
Retries are calculated via this formula:
87+
88+
```
89+
gap(N) = min(minTimeout × factor^(N-1), maxTimeout)
90+
```
91+
92+
Practically if step fails it will trigger a retry with increasing delay until `maxTimeout` is reached:
93+
94+
```
95+
retries: 2 => 0.15s-0.4s (150,225ms)
96+
retries: 3 => 0.15s-0.7s (150,225,338ms)
97+
retries: 3, minTimeout: 1000 => 1s-4.75s (1s,1.5s,2.25s)
98+
retries: 3, minTimeout: 1000, factor: 2 => 1s-7s (1s,2s,4s)
99+
retries: 5, minTimeout: 1000, factor: 2 => 1s-25s (1s,2s,4s,8s,10s)
100+
```
101+
102+
Playwright `timeout` adds to each attempt only when the element is found:
103+
104+
- `Playwright.timeout: 5000`
105+
- `retries: 2, minTimeout: 1000`
106+
107+
```
108+
element not found => 0 + (1s+1s) = 2s
109+
element found but not interactable => 3×5s + (1s+1s) = 17s
110+
```
81111

82112
## Manual Step Retries
83113

lib/plugin/retryFailedStep.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@ const debug = debugModule('codeceptjs:retryFailedStep')
88
const defaultConfig = {
99
retries: 3,
1010
defaultIgnoredSteps: ['amOnPage', 'wait*', 'send*', 'execute*', 'run*', 'have*'],
11+
minTimeout: 150,
12+
maxTimeout: 10000,
1113
factor: 1.5,
14+
randomize: false,
1215
ignoredSteps: [],
1316
deferToScenarioRetries: true,
1417
}
@@ -44,10 +47,9 @@ const RETRY_PRIORITIES = {
4447
* #### Configuration:
4548
*
4649
* * `retries` - number of retries (by default 3),
47-
* * `when` - function, when to perform a retry (accepts error as parameter)
4850
* * `factor` - The exponential factor to use. Default is 1.5.
49-
* * `minTimeout` - The number of milliseconds before starting the first retry. Default is 1000.
50-
* * `maxTimeout` - The maximum number of milliseconds between two retries. Default is Infinity.
51+
* * `minTimeout` - The number of milliseconds before starting the first retry. Default is 150.
52+
* * `maxTimeout` - The maximum number of milliseconds between two retries. Default is 10000.
5153
* * `randomize` - Randomizes the timeouts by multiplying with a factor from 1 to 2. Default is false.
5254
* * `defaultIgnoredSteps` - an array of steps to be ignored for retry. Includes:
5355
* * `amOnPage`
@@ -89,9 +91,8 @@ const RETRY_PRIORITIES = {
8991
*
9092
*/
9193
export default function (config) {
92-
config = Object.assign(defaultConfig, config)
94+
config = Object.assign({}, defaultConfig, config)
9395
config.ignoredSteps = config.ignoredSteps.concat(config.defaultIgnoredSteps)
94-
const customWhen = config.when
9596

9697
let enableRetry = false
9798

@@ -101,7 +102,6 @@ export default function (config) {
101102
if (!store.autoRetries) return false
102103
if (err && err.isTerminal) return false
103104
if (err && err.message && (err.message.includes('ERR_ABORTED') || err.message.includes('frame was detached') || err.message.includes('Target page, context or browser has been closed'))) return false
104-
if (customWhen) return customWhen(err)
105105
return true
106106
}
107107
config.when = when

test/unit/plugin/retryFailedStep_test.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,35 @@ describe('retryFailedStep', () => {
7676
expect(counter).to.equal(2)
7777
})
7878

79+
describe('config', () => {
80+
it('applies default retry timing', () => {
81+
retryFailedStep({})
82+
const cfg = recorder.retries.find(r => r.deferToScenarioRetries !== undefined)
83+
expect(cfg.retries).to.equal(3)
84+
expect(cfg.minTimeout).to.equal(150)
85+
expect(cfg.maxTimeout).to.equal(10000)
86+
expect(cfg.factor).to.equal(1.5)
87+
})
88+
89+
it('overrides retry timing from config', () => {
90+
retryFailedStep({ retries: 5, minTimeout: 1000, maxTimeout: 3000, factor: 2 })
91+
const cfg = recorder.retries.find(r => r.deferToScenarioRetries !== undefined)
92+
expect(cfg.retries).to.equal(5)
93+
expect(cfg.minTimeout).to.equal(1000)
94+
expect(cfg.maxTimeout).to.equal(3000)
95+
expect(cfg.factor).to.equal(2)
96+
})
97+
98+
it('does not leak config between instances', () => {
99+
retryFailedStep({ retries: 5, minTimeout: 1000 })
100+
recorder.retries = []
101+
retryFailedStep({})
102+
const cfg = recorder.retries.find(r => r.deferToScenarioRetries !== undefined)
103+
expect(cfg.retries).to.equal(3)
104+
expect(cfg.minTimeout).to.equal(150)
105+
})
106+
})
107+
79108
it('should not retry steps with wait*', async () => {
80109
retryFailedStep({ retries: 2, minTimeout: 1 })
81110
event.dispatcher.emit(event.test.before, createTest('test'))

0 commit comments

Comments
 (0)