Skip to content

Commit 2879fc7

Browse files
committed
feat: flaky tests implemented
1 parent f87b725 commit 2879fc7

11 files changed

Lines changed: 805 additions & 223 deletions

File tree

doc/api/test.md

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,56 @@ it.todo('should do the thing', { expectFailure: true }, () => {
275275
});
276276
```
277277

278+
## Flaky tests
279+
280+
<!-- YAML
281+
added:
282+
- REPLACEME
283+
-->
284+
285+
This flag causes a test or suite to be re-run a number of times until it
286+
either passes or has not passed after the final re-try.
287+
288+
When `flaky` is `true`, the test harness re-tries the test up to the default
289+
number of times (20), inclusive.
290+
291+
When `flaky` is a positive integer, the test harness re-tries the test up to
292+
the specified number of times, inclusive.
293+
294+
When `flaky` is falsy (the default), the test harness does not re-try the test.
295+
296+
When both a suite and an included test specify the `flaky` flag, the
297+
test's `flaky` value wins.
298+
299+
```js
300+
it.flaky('should do something', () => {
301+
// This test will be retried up to 20 times if it fails
302+
});
303+
304+
it('may take several times', { flaky: true }, () => {
305+
// Also retries up to 20 times
306+
});
307+
308+
it('may also take several times', { flaky: 5 }, () => {
309+
// Retries up to 5 times
310+
});
311+
312+
describe.flaky('flaky suite', () => {
313+
it('inherits flaky from suite', () => {
314+
// Retried up to 20 times (inherited from suite)
315+
});
316+
317+
it('not flaky', { flaky: false }, () => {
318+
// Not retried, overrides suite setting
319+
});
320+
});
321+
```
322+
323+
When a test marked `flaky` passes after retries, the number of re-tries taken
324+
is reported with that test.
325+
326+
`skip` and `todo` take precedence over `flaky`.
327+
278328
## `describe()` and `it()` aliases
279329

280330
Suites and tests can also be written using the `describe()` and `it()`
@@ -1649,6 +1699,16 @@ added:
16491699
Shorthand for marking a suite as `only`. This is the same as
16501700
[`suite([name], { only: true }[, fn])`][suite options].
16511701

1702+
## `suite.flaky([name][, options][, fn])`
1703+
1704+
<!-- YAML
1705+
added:
1706+
- REPLACEME
1707+
-->
1708+
1709+
Shorthand for marking a suite as flaky. This is the same as
1710+
[`suite([name], { flaky: true }[, fn])`][suite options].
1711+
16521712
## `test([name][, options][, fn])`
16531713

16541714
<!-- YAML
@@ -1684,6 +1744,11 @@ changes:
16841744
thread. If `false`, only one test runs at a time.
16851745
If unspecified, subtests inherit this value from their parent.
16861746
**Default:** `false`.
1747+
* `flaky` {boolean|number} If truthy, the test is re-tried up to the
1748+
specified number of times (or `20` if `true`) until it passes. If the test
1749+
passes after retries, the number of retries taken is reported. When both a
1750+
suite and an included test specify the `flaky` flag, the test's value wins.
1751+
**Default:** `false`.
16871752
* `only` {boolean} If truthy, and the test context is configured to run
16881753
`only` tests, then this test will be run. Otherwise, the test is skipped.
16891754
**Default:** `false`.
@@ -1755,6 +1820,16 @@ same as [`test([name], { todo: true }[, fn])`][it options].
17551820
Shorthand for marking a test as `only`,
17561821
same as [`test([name], { only: true }[, fn])`][it options].
17571822

1823+
## `test.flaky([name][, options][, fn])`
1824+
1825+
<!-- YAML
1826+
added:
1827+
- REPLACEME
1828+
-->
1829+
1830+
Shorthand for marking a test as flaky,
1831+
same as [`test([name], { flaky: true }[, fn])`][it options].
1832+
17581833
## `describe([name][, options][, fn])`
17591834

17601835
Alias for [`suite()`][].
@@ -1782,6 +1857,16 @@ added:
17821857
Shorthand for marking a suite as `only`. This is the same as
17831858
[`describe([name], { only: true }[, fn])`][describe options].
17841859

1860+
## `describe.flaky([name][, options][, fn])`
1861+
1862+
<!-- YAML
1863+
added:
1864+
- REPLACEME
1865+
-->
1866+
1867+
Shorthand for marking a suite as flaky. This is the same as
1868+
[`describe([name], { flaky: true }[, fn])`][describe options].
1869+
17851870
## `it([name][, options][, fn])`
17861871

17871872
<!-- YAML
@@ -1821,6 +1906,16 @@ added:
18211906
Shorthand for marking a test as `only`,
18221907
same as [`it([name], { only: true }[, fn])`][it options].
18231908

1909+
## `it.flaky([name][, options][, fn])`
1910+
1911+
<!-- YAML
1912+
added:
1913+
- REPLACEME
1914+
-->
1915+
1916+
Shorthand for marking a test as flaky,
1917+
same as [`it([name], { flaky: true }[, fn])`][it options].
1918+
18241919
## `before([fn][, options])`
18251920

18261921
<!-- YAML
@@ -3342,6 +3437,8 @@ Emitted when a test is enqueued for execution.
33423437
* `testNumber` {number} The ordinal number of the test.
33433438
* `todo` {string|boolean|undefined} Present if [`context.todo`][] is called
33443439
* `skip` {string|boolean|undefined} Present if [`context.skip`][] is called
3440+
* `flakyRetriedCount` {number|undefined} The number of retries taken for a
3441+
flaky test. Present when a test is marked as flaky.
33453442

33463443
Emitted when a test fails.
33473444
This event is guaranteed to be emitted in the same order as the tests are
@@ -3370,6 +3467,8 @@ The corresponding execution ordered event is `'test:complete'`.
33703467
* `testNumber` {number} The ordinal number of the test.
33713468
* `todo` {string|boolean|undefined} Present if [`context.todo`][] is called
33723469
* `skip` {string|boolean|undefined} Present if [`context.skip`][] is called
3470+
* `flakyRetriedCount` {number|undefined} The number of retries taken for a
3471+
flaky test. Present when a test is marked as flaky and passed after retries.
33733472

33743473
Emitted when a test passes.
33753474
This event is guaranteed to be emitted in the same order as the tests are
@@ -3983,6 +4082,9 @@ changes:
39834082
If `false`, it would only run one test at a time.
39844083
If unspecified, subtests inherit this value from their parent.
39854084
**Default:** `null`.
4085+
* `flaky` {boolean|number} If truthy, the test is re-tried up to the
4086+
specified number of times (or `20` if `true`) until it passes.
4087+
**Default:** `false`.
39864088
* `only` {boolean} If truthy, and the test context is configured to run
39874089
`only` tests, then this test will be run. Otherwise, the test is skipped.
39884090
**Default:** `false`.

lib/internal/test_runner/reporter/dot.js

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
'use strict';
2-
const {
3-
ArrayPrototypePush,
4-
MathMax,
5-
} = primordials;
2+
const { ArrayPrototypePush, MathMax } = primordials;
63
const colors = require('internal/util/colors');
74
const { formatTestReport } = require('internal/test_runner/reporter/utils');
85

@@ -12,7 +9,11 @@ module.exports = async function* dot(source) {
129
const failedTests = [];
1310
for await (const { type, data } of source) {
1411
if (type === 'test:pass') {
15-
yield `${colors.green}.${colors.reset}`;
12+
if (data.flakyRetriedCount > 0) {
13+
yield `${colors.yellow}F${colors.reset}`;
14+
} else {
15+
yield `${colors.green}.${colors.reset}`;
16+
}
1617
}
1718
if (type === 'test:fail') {
1819
yield `${colors.red}X${colors.reset}`;

0 commit comments

Comments
 (0)