mirror of https://github.com/facebook/jest.git
fix(jest-circus) correct concurrent event ordering (#15381)
This commit is contained in:
parent
95f21e4f82
commit
fba7764631
|
@ -16,6 +16,12 @@
|
||||||
"windows": {
|
"windows": {
|
||||||
"program": "${workspaceFolder}/node_modules/jest/bin/jest"
|
"program": "${workspaceFolder}/node_modules/jest/bin/jest"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Attach to jest",
|
||||||
|
"type": "node",
|
||||||
|
"request": "attach",
|
||||||
|
"port": 9229
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,6 +62,7 @@
|
||||||
- `[jest-circus]` Replace recursive `makeTestResults` implementation with iterative one ([#14760](https://github.com/jestjs/jest/pull/14760))
|
- `[jest-circus]` Replace recursive `makeTestResults` implementation with iterative one ([#14760](https://github.com/jestjs/jest/pull/14760))
|
||||||
- `[jest-circus]` Omit `expect.hasAssertions()` errors if a test already has errors ([#14866](https://github.com/jestjs/jest/pull/14866))
|
- `[jest-circus]` Omit `expect.hasAssertions()` errors if a test already has errors ([#14866](https://github.com/jestjs/jest/pull/14866))
|
||||||
- `[jest-circus, jest-expect, jest-snapshot]` Pass `test.failing` tests when containing failing snapshot matchers ([#14313](https://github.com/jestjs/jest/pull/14313))
|
- `[jest-circus, jest-expect, jest-snapshot]` Pass `test.failing` tests when containing failing snapshot matchers ([#14313](https://github.com/jestjs/jest/pull/14313))
|
||||||
|
- `[jest-circus]` Concurrent tests now emit jest circus events at the correct point and in the expected order. ([#15381](https://github.com/jestjs/jest/pull/15381))
|
||||||
- `[jest-cli]` [**BREAKING**] Validate CLI flags that require arguments receives them ([#14783](https://github.com/jestjs/jest/pull/14783))
|
- `[jest-cli]` [**BREAKING**] Validate CLI flags that require arguments receives them ([#14783](https://github.com/jestjs/jest/pull/14783))
|
||||||
- `[jest-config]` Make sure to respect `runInBand` option ([#14578](https://github.com/jestjs/jest/pull/14578))
|
- `[jest-config]` Make sure to respect `runInBand` option ([#14578](https://github.com/jestjs/jest/pull/14578))
|
||||||
- `[jest-config]` Support `testTimeout` in project config ([#14697](https://github.com/jestjs/jest/pull/14697))
|
- `[jest-config]` Support `testTimeout` in project config ([#14697](https://github.com/jestjs/jest/pull/14697))
|
||||||
|
|
|
@ -0,0 +1,230 @@
|
||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`all passing runs the tests in the correct order 1`] = `
|
||||||
|
" console.log
|
||||||
|
beforeAll
|
||||||
|
|
||||||
|
at log (__tests__/concurrent.test.js:15:11)
|
||||||
|
|
||||||
|
console.log
|
||||||
|
START "one"
|
||||||
|
|
||||||
|
at log (__tests__/concurrent.test.js:15:11)
|
||||||
|
|
||||||
|
console.log
|
||||||
|
START "two"
|
||||||
|
|
||||||
|
at log (__tests__/concurrent.test.js:15:11)
|
||||||
|
|
||||||
|
console.log
|
||||||
|
START "three"
|
||||||
|
|
||||||
|
at log (__tests__/concurrent.test.js:15:11)
|
||||||
|
|
||||||
|
console.log
|
||||||
|
START "four"
|
||||||
|
|
||||||
|
at log (__tests__/concurrent.test.js:15:11)
|
||||||
|
|
||||||
|
console.log
|
||||||
|
START "five"
|
||||||
|
|
||||||
|
at log (__tests__/concurrent.test.js:15:11)
|
||||||
|
|
||||||
|
console.log
|
||||||
|
END: "three"
|
||||||
|
|
||||||
|
at log (__tests__/concurrent.test.js:15:11)
|
||||||
|
|
||||||
|
console.log
|
||||||
|
START "six"
|
||||||
|
|
||||||
|
at log (__tests__/concurrent.test.js:15:11)
|
||||||
|
|
||||||
|
console.log
|
||||||
|
END: "one"
|
||||||
|
|
||||||
|
at log (__tests__/concurrent.test.js:15:11)
|
||||||
|
|
||||||
|
console.log
|
||||||
|
START "seven"
|
||||||
|
|
||||||
|
at log (__tests__/concurrent.test.js:15:11)
|
||||||
|
|
||||||
|
console.log
|
||||||
|
END: "two"
|
||||||
|
|
||||||
|
at log (__tests__/concurrent.test.js:15:11)
|
||||||
|
|
||||||
|
console.log
|
||||||
|
START "eight"
|
||||||
|
|
||||||
|
at log (__tests__/concurrent.test.js:15:11)
|
||||||
|
|
||||||
|
console.log
|
||||||
|
END: "four"
|
||||||
|
|
||||||
|
at log (__tests__/concurrent.test.js:15:11)
|
||||||
|
|
||||||
|
console.log
|
||||||
|
START "nine"
|
||||||
|
|
||||||
|
at log (__tests__/concurrent.test.js:15:11)
|
||||||
|
|
||||||
|
console.log
|
||||||
|
END: "nine"
|
||||||
|
|
||||||
|
at log (__tests__/concurrent.test.js:15:11)
|
||||||
|
|
||||||
|
console.log
|
||||||
|
START "ten"
|
||||||
|
|
||||||
|
at log (__tests__/concurrent.test.js:15:11)
|
||||||
|
|
||||||
|
console.log
|
||||||
|
END: "five"
|
||||||
|
|
||||||
|
at log (__tests__/concurrent.test.js:15:11)
|
||||||
|
|
||||||
|
console.log
|
||||||
|
END: "six"
|
||||||
|
|
||||||
|
at log (__tests__/concurrent.test.js:15:11)
|
||||||
|
|
||||||
|
console.log
|
||||||
|
END: "seven"
|
||||||
|
|
||||||
|
at log (__tests__/concurrent.test.js:15:11)
|
||||||
|
|
||||||
|
console.log
|
||||||
|
END: "ten"
|
||||||
|
|
||||||
|
at log (__tests__/concurrent.test.js:15:11)
|
||||||
|
|
||||||
|
console.log
|
||||||
|
END: "eight"
|
||||||
|
|
||||||
|
at log (__tests__/concurrent.test.js:15:11)
|
||||||
|
|
||||||
|
console.log
|
||||||
|
afterAll
|
||||||
|
|
||||||
|
at log (__tests__/concurrent.test.js:15:11)
|
||||||
|
"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`with only runs the tests in the correct order 1`] = `
|
||||||
|
" console.log
|
||||||
|
beforeAll
|
||||||
|
|
||||||
|
at log (__tests__/concurrent-only.test.js:15:11)
|
||||||
|
|
||||||
|
console.log
|
||||||
|
START "four"
|
||||||
|
|
||||||
|
at log (__tests__/concurrent-only.test.js:15:11)
|
||||||
|
|
||||||
|
console.log
|
||||||
|
START "six"
|
||||||
|
|
||||||
|
at log (__tests__/concurrent-only.test.js:15:11)
|
||||||
|
|
||||||
|
console.log
|
||||||
|
START "nine"
|
||||||
|
|
||||||
|
at log (__tests__/concurrent-only.test.js:15:11)
|
||||||
|
|
||||||
|
console.log
|
||||||
|
END: "nine"
|
||||||
|
|
||||||
|
at log (__tests__/concurrent-only.test.js:15:11)
|
||||||
|
|
||||||
|
console.log
|
||||||
|
END: "six"
|
||||||
|
|
||||||
|
at log (__tests__/concurrent-only.test.js:15:11)
|
||||||
|
|
||||||
|
console.log
|
||||||
|
END: "four"
|
||||||
|
|
||||||
|
at log (__tests__/concurrent-only.test.js:15:11)
|
||||||
|
|
||||||
|
console.log
|
||||||
|
afterAll
|
||||||
|
|
||||||
|
at log (__tests__/concurrent-only.test.js:15:11)
|
||||||
|
"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`with skip runs the tests in the correct order 1`] = `
|
||||||
|
" console.log
|
||||||
|
beforeAll
|
||||||
|
|
||||||
|
at log (__tests__/concurrent-skip.test.js:15:11)
|
||||||
|
|
||||||
|
console.log
|
||||||
|
START "one"
|
||||||
|
|
||||||
|
at log (__tests__/concurrent-skip.test.js:15:11)
|
||||||
|
|
||||||
|
console.log
|
||||||
|
START "two"
|
||||||
|
|
||||||
|
at log (__tests__/concurrent-skip.test.js:15:11)
|
||||||
|
|
||||||
|
console.log
|
||||||
|
START "four"
|
||||||
|
|
||||||
|
at log (__tests__/concurrent-skip.test.js:15:11)
|
||||||
|
|
||||||
|
console.log
|
||||||
|
START "seven"
|
||||||
|
|
||||||
|
at log (__tests__/concurrent-skip.test.js:15:11)
|
||||||
|
|
||||||
|
console.log
|
||||||
|
START "eight"
|
||||||
|
|
||||||
|
at log (__tests__/concurrent-skip.test.js:15:11)
|
||||||
|
|
||||||
|
console.log
|
||||||
|
END: "one"
|
||||||
|
|
||||||
|
at log (__tests__/concurrent-skip.test.js:15:11)
|
||||||
|
|
||||||
|
console.log
|
||||||
|
START "ten"
|
||||||
|
|
||||||
|
at log (__tests__/concurrent-skip.test.js:15:11)
|
||||||
|
|
||||||
|
console.log
|
||||||
|
END: "two"
|
||||||
|
|
||||||
|
at log (__tests__/concurrent-skip.test.js:15:11)
|
||||||
|
|
||||||
|
console.log
|
||||||
|
END: "seven"
|
||||||
|
|
||||||
|
at log (__tests__/concurrent-skip.test.js:15:11)
|
||||||
|
|
||||||
|
console.log
|
||||||
|
END: "four"
|
||||||
|
|
||||||
|
at log (__tests__/concurrent-skip.test.js:15:11)
|
||||||
|
|
||||||
|
console.log
|
||||||
|
END: "eight"
|
||||||
|
|
||||||
|
at log (__tests__/concurrent-skip.test.js:15:11)
|
||||||
|
|
||||||
|
console.log
|
||||||
|
END: "ten"
|
||||||
|
|
||||||
|
at log (__tests__/concurrent-skip.test.js:15:11)
|
||||||
|
|
||||||
|
console.log
|
||||||
|
afterAll
|
||||||
|
|
||||||
|
at log (__tests__/concurrent-skip.test.js:15:11)
|
||||||
|
"
|
||||||
|
`;
|
|
@ -94,21 +94,27 @@ exports[`works with all statuses 1`] = `
|
||||||
exports[`works with concurrent and only mode 1`] = `
|
exports[`works with concurrent and only mode 1`] = `
|
||||||
"FAIL __tests__/worksWithConcurrentOnlyMode.test.js
|
"FAIL __tests__/worksWithConcurrentOnlyMode.test.js
|
||||||
block with concurrent
|
block with concurrent
|
||||||
✕ failing passes = fails
|
✕ .only.failing() should fail
|
||||||
✕ .add(1, 1)
|
✓ .only.failing() should pass
|
||||||
✕ .add(1, 2)
|
✕ .add(1, 1) .only.failing.each() should fail
|
||||||
✕ .add(2, 1)
|
✕ .add(1, 2) .only.failing.each() should fail
|
||||||
✓ failing fails = passes
|
✕ .add(2, 1) .only.failing.each() should fail
|
||||||
|
✓ .add(1, 1) .only.failing.each() should pass
|
||||||
|
✓ .add(1, 2) .only.failing.each() should pass
|
||||||
|
✓ .add(2, 1) .only.failing.each() should pass
|
||||||
○ skipped skipped failing test
|
○ skipped skipped failing test
|
||||||
|
○ skipped .add(1, 1) skipped each
|
||||||
|
○ skipped .add(1, 2) skipped each
|
||||||
|
○ skipped .add(2, 1) skipped each
|
||||||
○ skipped skipped failing fails
|
○ skipped skipped failing fails
|
||||||
|
|
||||||
● block with concurrent › failing passes = fails
|
● block with concurrent › .only.failing() should fail
|
||||||
|
|
||||||
Failing test passed even though it was supposed to fail. Remove \`.failing\` to remove error.
|
Failing test passed even though it was supposed to fail. Remove \`.failing\` to remove error.
|
||||||
|
|
||||||
11 | });
|
11 | });
|
||||||
12 |
|
12 |
|
||||||
> 13 | it.concurrent.only.failing('failing passes = fails', () => {
|
> 13 | it.concurrent.only.failing('.only.failing() should fail', () => {
|
||||||
| ^
|
| ^
|
||||||
14 | expect(10).toBe(10);
|
14 | expect(10).toBe(10);
|
||||||
15 | });
|
15 | });
|
||||||
|
@ -117,64 +123,67 @@ exports[`works with concurrent and only mode 1`] = `
|
||||||
at failing (__tests__/worksWithConcurrentOnlyMode.test.js:13:22)
|
at failing (__tests__/worksWithConcurrentOnlyMode.test.js:13:22)
|
||||||
at Object.describe (__tests__/worksWithConcurrentOnlyMode.test.js:8:1)
|
at Object.describe (__tests__/worksWithConcurrentOnlyMode.test.js:8:1)
|
||||||
|
|
||||||
● block with concurrent › .add(1, 1)
|
● block with concurrent › .add(1, 1) .only.failing.each() should fail
|
||||||
|
|
||||||
Failing test passed even though it was supposed to fail. Remove \`.failing\` to remove error.
|
Failing test passed even though it was supposed to fail. Remove \`.failing\` to remove error.
|
||||||
|
|
||||||
15 | });
|
19 | });
|
||||||
16 |
|
20 |
|
||||||
> 17 | test.concurrent.only.failing.each([
|
> 21 | test.concurrent.only.failing.each([
|
||||||
| ^
|
| ^
|
||||||
18 | {a: 1, b: 1, expected: 2},
|
22 | {a: 1, b: 1, expected: 2},
|
||||||
19 | {a: 1, b: 2, expected: 3},
|
23 | {a: 1, b: 2, expected: 3},
|
||||||
20 | {a: 2, b: 1, expected: 3},
|
24 | {a: 2, b: 1, expected: 3},
|
||||||
|
|
||||||
at each (__tests__/worksWithConcurrentOnlyMode.test.js:17:32)
|
at each (__tests__/worksWithConcurrentOnlyMode.test.js:21:32)
|
||||||
at Object.describe (__tests__/worksWithConcurrentOnlyMode.test.js:8:1)
|
at Object.describe (__tests__/worksWithConcurrentOnlyMode.test.js:8:1)
|
||||||
|
|
||||||
● block with concurrent › .add(1, 2)
|
● block with concurrent › .add(1, 2) .only.failing.each() should fail
|
||||||
|
|
||||||
Failing test passed even though it was supposed to fail. Remove \`.failing\` to remove error.
|
Failing test passed even though it was supposed to fail. Remove \`.failing\` to remove error.
|
||||||
|
|
||||||
15 | });
|
19 | });
|
||||||
16 |
|
20 |
|
||||||
> 17 | test.concurrent.only.failing.each([
|
> 21 | test.concurrent.only.failing.each([
|
||||||
| ^
|
| ^
|
||||||
18 | {a: 1, b: 1, expected: 2},
|
22 | {a: 1, b: 1, expected: 2},
|
||||||
19 | {a: 1, b: 2, expected: 3},
|
23 | {a: 1, b: 2, expected: 3},
|
||||||
20 | {a: 2, b: 1, expected: 3},
|
24 | {a: 2, b: 1, expected: 3},
|
||||||
|
|
||||||
at each (__tests__/worksWithConcurrentOnlyMode.test.js:17:32)
|
at each (__tests__/worksWithConcurrentOnlyMode.test.js:21:32)
|
||||||
at Object.describe (__tests__/worksWithConcurrentOnlyMode.test.js:8:1)
|
at Object.describe (__tests__/worksWithConcurrentOnlyMode.test.js:8:1)
|
||||||
|
|
||||||
● block with concurrent › .add(2, 1)
|
● block with concurrent › .add(2, 1) .only.failing.each() should fail
|
||||||
|
|
||||||
Failing test passed even though it was supposed to fail. Remove \`.failing\` to remove error.
|
Failing test passed even though it was supposed to fail. Remove \`.failing\` to remove error.
|
||||||
|
|
||||||
15 | });
|
19 | });
|
||||||
16 |
|
20 |
|
||||||
> 17 | test.concurrent.only.failing.each([
|
> 21 | test.concurrent.only.failing.each([
|
||||||
| ^
|
| ^
|
||||||
18 | {a: 1, b: 1, expected: 2},
|
22 | {a: 1, b: 1, expected: 2},
|
||||||
19 | {a: 1, b: 2, expected: 3},
|
23 | {a: 1, b: 2, expected: 3},
|
||||||
20 | {a: 2, b: 1, expected: 3},
|
24 | {a: 2, b: 1, expected: 3},
|
||||||
|
|
||||||
at each (__tests__/worksWithConcurrentOnlyMode.test.js:17:32)
|
at each (__tests__/worksWithConcurrentOnlyMode.test.js:21:32)
|
||||||
at Object.describe (__tests__/worksWithConcurrentOnlyMode.test.js:8:1)"
|
at Object.describe (__tests__/worksWithConcurrentOnlyMode.test.js:8:1)"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`works with concurrent mode 1`] = `
|
exports[`works with concurrent mode 1`] = `
|
||||||
"FAIL __tests__/worksWithConcurrentMode.test.js
|
"FAIL __tests__/worksWithConcurrentMode.test.js
|
||||||
block with concurrent
|
block with concurrent
|
||||||
✕ failing test
|
✕ test should fail
|
||||||
✕ failing passes = fails
|
✕ .failing() should fail
|
||||||
✕ .add(1, 1)
|
✓ .failing() should pass
|
||||||
✕ .add(1, 2)
|
✕ .add(1, 1) .failing.each() should fail
|
||||||
✕ .add(2, 1)
|
✕ .add(1, 2) .failing.each() should fail
|
||||||
✓ failing fails = passes
|
✕ .add(2, 1) .failing.each() should fail
|
||||||
|
✓ .add(1, 1) .failing.each() should pass
|
||||||
|
✓ .add(1, 2) .failing.each() should pass
|
||||||
|
✓ .add(2, 1) .failing.each() should pass
|
||||||
○ skipped skipped failing fails
|
○ skipped skipped failing fails
|
||||||
|
|
||||||
● block with concurrent › failing test
|
● block with concurrent › test should fail
|
||||||
|
|
||||||
expect(received).toBe(expected) // Object.is equality
|
expect(received).toBe(expected) // Object.is equality
|
||||||
|
|
||||||
|
@ -182,22 +191,22 @@ exports[`works with concurrent mode 1`] = `
|
||||||
Received: 10
|
Received: 10
|
||||||
|
|
||||||
8 | describe('block with concurrent', () => {
|
8 | describe('block with concurrent', () => {
|
||||||
9 | it('failing test', () => {
|
9 | it('test should fail', () => {
|
||||||
> 10 | expect(10).toBe(101);
|
> 10 | expect(10).toBe(101);
|
||||||
| ^
|
| ^
|
||||||
11 | });
|
11 | });
|
||||||
12 |
|
12 |
|
||||||
13 | it.concurrent.failing('failing passes = fails', () => {
|
13 | it.concurrent.failing('.failing() should fail', () => {
|
||||||
|
|
||||||
at Object.toBe (__tests__/worksWithConcurrentMode.test.js:10:16)
|
at Object.toBe (__tests__/worksWithConcurrentMode.test.js:10:16)
|
||||||
|
|
||||||
● block with concurrent › failing passes = fails
|
● block with concurrent › .failing() should fail
|
||||||
|
|
||||||
Failing test passed even though it was supposed to fail. Remove \`.failing\` to remove error.
|
Failing test passed even though it was supposed to fail. Remove \`.failing\` to remove error.
|
||||||
|
|
||||||
11 | });
|
11 | });
|
||||||
12 |
|
12 |
|
||||||
> 13 | it.concurrent.failing('failing passes = fails', () => {
|
> 13 | it.concurrent.failing('.failing() should fail', () => {
|
||||||
| ^
|
| ^
|
||||||
14 | expect(10).toBe(10);
|
14 | expect(10).toBe(10);
|
||||||
15 | });
|
15 | });
|
||||||
|
@ -206,49 +215,49 @@ exports[`works with concurrent mode 1`] = `
|
||||||
at failing (__tests__/worksWithConcurrentMode.test.js:13:17)
|
at failing (__tests__/worksWithConcurrentMode.test.js:13:17)
|
||||||
at Object.describe (__tests__/worksWithConcurrentMode.test.js:8:1)
|
at Object.describe (__tests__/worksWithConcurrentMode.test.js:8:1)
|
||||||
|
|
||||||
● block with concurrent › .add(1, 1)
|
● block with concurrent › .add(1, 1) .failing.each() should fail
|
||||||
|
|
||||||
Failing test passed even though it was supposed to fail. Remove \`.failing\` to remove error.
|
Failing test passed even though it was supposed to fail. Remove \`.failing\` to remove error.
|
||||||
|
|
||||||
15 | });
|
19 | });
|
||||||
16 |
|
20 |
|
||||||
> 17 | test.concurrent.failing.each([
|
> 21 | test.concurrent.failing.each([
|
||||||
| ^
|
| ^
|
||||||
18 | {a: 1, b: 1, expected: 2},
|
22 | {a: 1, b: 1, expected: 2},
|
||||||
19 | {a: 1, b: 2, expected: 3},
|
23 | {a: 1, b: 2, expected: 3},
|
||||||
20 | {a: 2, b: 1, expected: 3},
|
24 | {a: 2, b: 1, expected: 3},
|
||||||
|
|
||||||
at each (__tests__/worksWithConcurrentMode.test.js:17:27)
|
at each (__tests__/worksWithConcurrentMode.test.js:21:27)
|
||||||
at Object.describe (__tests__/worksWithConcurrentMode.test.js:8:1)
|
at Object.describe (__tests__/worksWithConcurrentMode.test.js:8:1)
|
||||||
|
|
||||||
● block with concurrent › .add(1, 2)
|
● block with concurrent › .add(1, 2) .failing.each() should fail
|
||||||
|
|
||||||
Failing test passed even though it was supposed to fail. Remove \`.failing\` to remove error.
|
Failing test passed even though it was supposed to fail. Remove \`.failing\` to remove error.
|
||||||
|
|
||||||
15 | });
|
19 | });
|
||||||
16 |
|
20 |
|
||||||
> 17 | test.concurrent.failing.each([
|
> 21 | test.concurrent.failing.each([
|
||||||
| ^
|
| ^
|
||||||
18 | {a: 1, b: 1, expected: 2},
|
22 | {a: 1, b: 1, expected: 2},
|
||||||
19 | {a: 1, b: 2, expected: 3},
|
23 | {a: 1, b: 2, expected: 3},
|
||||||
20 | {a: 2, b: 1, expected: 3},
|
24 | {a: 2, b: 1, expected: 3},
|
||||||
|
|
||||||
at each (__tests__/worksWithConcurrentMode.test.js:17:27)
|
at each (__tests__/worksWithConcurrentMode.test.js:21:27)
|
||||||
at Object.describe (__tests__/worksWithConcurrentMode.test.js:8:1)
|
at Object.describe (__tests__/worksWithConcurrentMode.test.js:8:1)
|
||||||
|
|
||||||
● block with concurrent › .add(2, 1)
|
● block with concurrent › .add(2, 1) .failing.each() should fail
|
||||||
|
|
||||||
Failing test passed even though it was supposed to fail. Remove \`.failing\` to remove error.
|
Failing test passed even though it was supposed to fail. Remove \`.failing\` to remove error.
|
||||||
|
|
||||||
15 | });
|
19 | });
|
||||||
16 |
|
20 |
|
||||||
> 17 | test.concurrent.failing.each([
|
> 21 | test.concurrent.failing.each([
|
||||||
| ^
|
| ^
|
||||||
18 | {a: 1, b: 1, expected: 2},
|
22 | {a: 1, b: 1, expected: 2},
|
||||||
19 | {a: 1, b: 2, expected: 3},
|
23 | {a: 1, b: 2, expected: 3},
|
||||||
20 | {a: 2, b: 1, expected: 3},
|
24 | {a: 2, b: 1, expected: 3},
|
||||||
|
|
||||||
at each (__tests__/worksWithConcurrentMode.test.js:17:27)
|
at each (__tests__/worksWithConcurrentMode.test.js:21:27)
|
||||||
at Object.describe (__tests__/worksWithConcurrentMode.test.js:8:1)"
|
at Object.describe (__tests__/worksWithConcurrentMode.test.js:8:1)"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ exports[`throws an error about unsupported modifier 1`] = `
|
||||||
at Object.failing (__tests__/statuses.test.js:22:4)
|
at Object.failing (__tests__/statuses.test.js:22:4)
|
||||||
|
|
||||||
FAIL __tests__/worksWithConcurrentMode.test.js
|
FAIL __tests__/worksWithConcurrentMode.test.js
|
||||||
● block with concurrent › failing test
|
● block with concurrent › test should fail
|
||||||
|
|
||||||
expect(received).toBe(expected) // Object.is equality
|
expect(received).toBe(expected) // Object.is equality
|
||||||
|
|
||||||
|
@ -25,12 +25,12 @@ FAIL __tests__/worksWithConcurrentMode.test.js
|
||||||
Received: 10
|
Received: 10
|
||||||
|
|
||||||
8 | describe('block with concurrent', () => {
|
8 | describe('block with concurrent', () => {
|
||||||
9 | it('failing test', () => {
|
9 | it('test should fail', () => {
|
||||||
> 10 | expect(10).toBe(101);
|
> 10 | expect(10).toBe(101);
|
||||||
| ^
|
| ^
|
||||||
11 | });
|
11 | });
|
||||||
12 |
|
12 |
|
||||||
13 | it.concurrent.failing('failing passes = fails', () => {
|
13 | it.concurrent.failing('.failing() should fail', () => {
|
||||||
|
|
||||||
at Object.toBe (__tests__/worksWithConcurrentMode.test.js:10:16)
|
at Object.toBe (__tests__/worksWithConcurrentMode.test.js:10:16)
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ FAIL __tests__/worksWithConcurrentMode.test.js
|
||||||
|
|
||||||
11 | });
|
11 | });
|
||||||
12 |
|
12 |
|
||||||
> 13 | it.concurrent.failing('failing passes = fails', () => {
|
> 13 | it.concurrent.failing('.failing() should fail', () => {
|
||||||
| ^
|
| ^
|
||||||
14 | expect(10).toBe(10);
|
14 | expect(10).toBe(10);
|
||||||
15 | });
|
15 | });
|
||||||
|
@ -64,7 +64,7 @@ FAIL __tests__/worksWithConcurrentOnlyMode.test.js
|
||||||
| ^
|
| ^
|
||||||
11 | });
|
11 | });
|
||||||
12 |
|
12 |
|
||||||
13 | it.concurrent.only.failing('failing passes = fails', () => {
|
13 | it.concurrent.only.failing('.only.failing() should fail', () => {
|
||||||
|
|
||||||
at Object.toBe (__tests__/worksWithConcurrentOnlyMode.test.js:10:16)
|
at Object.toBe (__tests__/worksWithConcurrentOnlyMode.test.js:10:16)
|
||||||
|
|
||||||
|
@ -74,7 +74,7 @@ FAIL __tests__/worksWithConcurrentOnlyMode.test.js
|
||||||
|
|
||||||
11 | });
|
11 | });
|
||||||
12 |
|
12 |
|
||||||
> 13 | it.concurrent.only.failing('failing passes = fails', () => {
|
> 13 | it.concurrent.only.failing('.only.failing() should fail', () => {
|
||||||
| ^
|
| ^
|
||||||
14 | expect(10).toBe(10);
|
14 | expect(10).toBe(10);
|
||||||
15 | });
|
15 | });
|
||||||
|
|
|
@ -1,5 +1,80 @@
|
||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`Concurrent Test Retries with flag retryImmediately retry immediately after failed test 1`] = `
|
||||||
|
"LOGGING RETRY ERRORS retryable test 1
|
||||||
|
RETRY 1
|
||||||
|
|
||||||
|
expect(received).toBeFalsy()
|
||||||
|
|
||||||
|
Received: true
|
||||||
|
|
||||||
|
15 | expect(true).toBeTruthy();
|
||||||
|
16 | } else {
|
||||||
|
> 17 | expect(true).toBeFalsy();
|
||||||
|
| ^
|
||||||
|
18 | }
|
||||||
|
19 | });
|
||||||
|
20 |
|
||||||
|
|
||||||
|
at Object.toBeFalsy (__tests__/retryImmediatelyConcurrent.test.js:17:18)
|
||||||
|
|
||||||
|
RETRY 2
|
||||||
|
|
||||||
|
expect(received).toBeFalsy()
|
||||||
|
|
||||||
|
Received: true
|
||||||
|
|
||||||
|
15 | expect(true).toBeTruthy();
|
||||||
|
16 | } else {
|
||||||
|
> 17 | expect(true).toBeFalsy();
|
||||||
|
| ^
|
||||||
|
18 | }
|
||||||
|
19 | });
|
||||||
|
20 |
|
||||||
|
|
||||||
|
at Object.toBeFalsy (__tests__/retryImmediatelyConcurrent.test.js:17:18)
|
||||||
|
at async Promise.all (index 0)
|
||||||
|
|
||||||
|
LOGGING RETRY ERRORS retryable test 2
|
||||||
|
RETRY 1
|
||||||
|
|
||||||
|
expect(received).toBeFalsy()
|
||||||
|
|
||||||
|
Received: true
|
||||||
|
|
||||||
|
26 | expect(true).toBeTruthy();
|
||||||
|
27 | } else {
|
||||||
|
> 28 | expect(true).toBeFalsy();
|
||||||
|
| ^
|
||||||
|
29 | }
|
||||||
|
30 | });
|
||||||
|
31 | it.concurrent('truthy test', () => {
|
||||||
|
|
||||||
|
at Object.toBeFalsy (__tests__/retryImmediatelyConcurrent.test.js:28:18)
|
||||||
|
|
||||||
|
RETRY 2
|
||||||
|
|
||||||
|
expect(received).toBeFalsy()
|
||||||
|
|
||||||
|
Received: true
|
||||||
|
|
||||||
|
26 | expect(true).toBeTruthy();
|
||||||
|
27 | } else {
|
||||||
|
> 28 | expect(true).toBeFalsy();
|
||||||
|
| ^
|
||||||
|
29 | }
|
||||||
|
30 | });
|
||||||
|
31 | it.concurrent('truthy test', () => {
|
||||||
|
|
||||||
|
at Object.toBeFalsy (__tests__/retryImmediatelyConcurrent.test.js:28:18)
|
||||||
|
at async Promise.all (index 1)
|
||||||
|
|
||||||
|
PASS __tests__/retryImmediatelyConcurrent.test.js
|
||||||
|
✓ retryable test 1
|
||||||
|
✓ retryable test 2
|
||||||
|
✓ truthy test"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`Test Retries logs error(s) before retry 1`] = `
|
exports[`Test Retries logs error(s) before retry 1`] = `
|
||||||
"LOGGING RETRY ERRORS retryTimes set
|
"LOGGING RETRY ERRORS retryTimes set
|
||||||
RETRY 1
|
RETRY 1
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
import exp = require('constants');
|
||||||
|
import {skipSuiteOnJasmine} from '@jest/test-utils';
|
||||||
|
import runJest, {json as runWithJson} from '../runJest';
|
||||||
|
|
||||||
|
skipSuiteOnJasmine();
|
||||||
|
|
||||||
|
describe('all passing', () => {
|
||||||
|
it('runs the correct number of tests', () => {
|
||||||
|
const {json, exitCode} = runWithJson('circus-concurrent', [
|
||||||
|
'concurrent.test.js',
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect(exitCode).toBe(0);
|
||||||
|
expect(json.numTotalTests).toBe(10);
|
||||||
|
expect(json.numPassedTests).toBe(10);
|
||||||
|
expect(json.numFailedTests).toBe(0);
|
||||||
|
expect(json.numPendingTests).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('runs the tests in the correct order', () => {
|
||||||
|
const {stdout} = runJest('circus-concurrent', ['concurrent.test.js']);
|
||||||
|
expect(stdout).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('with skip', () => {
|
||||||
|
it('runs the correct number of tests', () => {
|
||||||
|
const {json, exitCode} = runWithJson('circus-concurrent', [
|
||||||
|
'concurrent-skip.test.js',
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect(exitCode).toBe(0);
|
||||||
|
expect(json.numTotalTests).toBe(10);
|
||||||
|
expect(json.numPassedTests).toBe(6);
|
||||||
|
expect(json.numFailedTests).toBe(0);
|
||||||
|
expect(json.numPendingTests).toBe(4);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('runs the tests in the correct order', () => {
|
||||||
|
const {stdout} = runJest('circus-concurrent', ['concurrent-skip.test.js']);
|
||||||
|
expect(stdout).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('with only', () => {
|
||||||
|
it('runs the correct number of tests', () => {
|
||||||
|
const {json, exitCode} = runWithJson('circus-concurrent', [
|
||||||
|
'concurrent-only.test.js',
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect(exitCode).toBe(0);
|
||||||
|
expect(json.numTotalTests).toBe(10);
|
||||||
|
expect(json.numPassedTests).toBe(3);
|
||||||
|
expect(json.numFailedTests).toBe(0);
|
||||||
|
expect(json.numPendingTests).toBe(7);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('runs the tests in the correct order', () => {
|
||||||
|
const {stdout} = runJest('circus-concurrent', ['concurrent-only.test.js']);
|
||||||
|
expect(stdout).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
});
|
|
@ -173,3 +173,149 @@ describe('Test Retries', () => {
|
||||||
expect(jsonResult.testResults[0].testResults[0].invocations).toBe(1);
|
expect(jsonResult.testResults[0].testResults[0].invocations).toBe(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Concurrent Test Retries', () => {
|
||||||
|
const outputFileName = 'retries.result.json';
|
||||||
|
const outputFilePath = path.join(
|
||||||
|
process.cwd(),
|
||||||
|
'e2e/test-retries/',
|
||||||
|
outputFileName,
|
||||||
|
);
|
||||||
|
const logErrorsBeforeRetryErrorMessage = 'LOGGING RETRY ERRORS';
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
fs.unlinkSync(outputFilePath);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('retries failed tests', () => {
|
||||||
|
const result = runJest('test-retries', ['e2eConcurrent.test.js']);
|
||||||
|
|
||||||
|
expect(result.exitCode).toBe(0);
|
||||||
|
expect(result.failed).toBe(false);
|
||||||
|
expect(result.stderr).not.toContain(logErrorsBeforeRetryErrorMessage);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('with flag retryImmediately retry immediately after failed test', () => {
|
||||||
|
const logMessage = `console.log
|
||||||
|
FIRST TRUTHY TEST
|
||||||
|
|
||||||
|
at Object.log (__tests__/retryImmediatelyConcurrent.test.js:32:11)
|
||||||
|
|
||||||
|
console.log
|
||||||
|
SECOND TRUTHY TEST
|
||||||
|
|
||||||
|
at Object.log (__tests__/retryImmediatelyConcurrent.test.js:14:13)
|
||||||
|
at async Promise.all (index 0)
|
||||||
|
|
||||||
|
console.log
|
||||||
|
THIRD TRUTHY TEST
|
||||||
|
|
||||||
|
at Object.log (__tests__/retryImmediatelyConcurrent.test.js:25:13)
|
||||||
|
at async Promise.all (index 1)`;
|
||||||
|
|
||||||
|
const result = runJest('test-retries', [
|
||||||
|
'retryImmediatelyConcurrent.test.js',
|
||||||
|
]);
|
||||||
|
const stdout = result.stdout.trim();
|
||||||
|
expect(result.exitCode).toBe(0);
|
||||||
|
expect(result.failed).toBe(false);
|
||||||
|
expect(result.stderr).toContain(logErrorsBeforeRetryErrorMessage);
|
||||||
|
expect(stdout).toBe(logMessage);
|
||||||
|
expect(extractSummary(result.stderr).rest).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('reporter shows more than 1 invocation if test is retried', () => {
|
||||||
|
let jsonResult;
|
||||||
|
|
||||||
|
const reporterConfig = {
|
||||||
|
reporters: [
|
||||||
|
['<rootDir>/reporters/RetryReporter.js', {output: outputFilePath}],
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
runJest('test-retries', [
|
||||||
|
'--config',
|
||||||
|
JSON.stringify(reporterConfig),
|
||||||
|
'__tests__/retryConcurrent.test.js',
|
||||||
|
]);
|
||||||
|
|
||||||
|
const testOutput = fs.readFileSync(outputFilePath, 'utf8');
|
||||||
|
|
||||||
|
try {
|
||||||
|
jsonResult = JSON.parse(testOutput);
|
||||||
|
} catch (error: any) {
|
||||||
|
throw new Error(
|
||||||
|
`Can't parse the JSON result from ${outputFileName}, ${error.toString()}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(jsonResult.numPassedTests).toBe(1);
|
||||||
|
expect(jsonResult.numFailedTests).toBe(1);
|
||||||
|
expect(jsonResult.numPendingTests).toBe(0);
|
||||||
|
expect(jsonResult.testResults[0].testResults[0].invocations).toBe(4);
|
||||||
|
expect(jsonResult.testResults[0].testResults[1].invocations).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('reporter shows 1 invocation if tests are not retried', () => {
|
||||||
|
let jsonResult;
|
||||||
|
|
||||||
|
const reporterConfig = {
|
||||||
|
reporters: [
|
||||||
|
['<rootDir>/reporters/RetryReporter.js', {output: outputFilePath}],
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
runJest('test-retries', [
|
||||||
|
'--config',
|
||||||
|
JSON.stringify(reporterConfig),
|
||||||
|
'controlConcurrent.test.js',
|
||||||
|
]);
|
||||||
|
|
||||||
|
const testOutput = fs.readFileSync(outputFilePath, 'utf8');
|
||||||
|
|
||||||
|
try {
|
||||||
|
jsonResult = JSON.parse(testOutput);
|
||||||
|
} catch (error: any) {
|
||||||
|
throw new Error(
|
||||||
|
`Can't parse the JSON result from ${outputFileName}, ${error.toString()}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(jsonResult.numPassedTests).toBe(0);
|
||||||
|
expect(jsonResult.numFailedTests).toBe(1);
|
||||||
|
expect(jsonResult.numPendingTests).toBe(0);
|
||||||
|
expect(jsonResult.testResults[0].testResults[0].invocations).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('tests are not retried if beforeAll hook failure occurs', () => {
|
||||||
|
let jsonResult;
|
||||||
|
|
||||||
|
const reporterConfig = {
|
||||||
|
reporters: [
|
||||||
|
['<rootDir>/reporters/RetryReporter.js', {output: outputFilePath}],
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
runJest('test-retries', [
|
||||||
|
'--config',
|
||||||
|
JSON.stringify(reporterConfig),
|
||||||
|
'beforeAllFailureConcurrent.test.js',
|
||||||
|
]);
|
||||||
|
|
||||||
|
const testOutput = fs.readFileSync(outputFilePath, 'utf8');
|
||||||
|
|
||||||
|
try {
|
||||||
|
jsonResult = JSON.parse(testOutput);
|
||||||
|
} catch (error: any) {
|
||||||
|
throw new Error(
|
||||||
|
`Can't parse the JSON result from ${outputFileName}, ${error.toString()}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(jsonResult.numPassedTests).toBe(0);
|
||||||
|
expect(jsonResult.numFailedTests).toBe(2);
|
||||||
|
expect(jsonResult.numPendingTests).toBe(0);
|
||||||
|
expect(jsonResult.testResults[0].testResults[0].invocations).toBe(1);
|
||||||
|
expect(jsonResult.testResults[0].testResults[1].invocations).toBe(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const {setTimeout} = require('timers/promises');
|
||||||
|
|
||||||
|
let delta = Date.now();
|
||||||
|
const includeDelta = false;
|
||||||
|
const marker = s => {
|
||||||
|
console.log(s, includeDelta ? `+${Date.now() - delta}ms` : '');
|
||||||
|
delta = Date.now();
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeAll(() => marker('beforeAll'));
|
||||||
|
afterAll(() => marker('afterAll'));
|
||||||
|
|
||||||
|
beforeEach(() => marker('beforeEach'));
|
||||||
|
afterEach(() => marker('afterEach'));
|
||||||
|
|
||||||
|
const testFn = (name, delay, fail) => {
|
||||||
|
return async () => {
|
||||||
|
marker(`START "${name}"`);
|
||||||
|
await setTimeout(delay);
|
||||||
|
if (fail) {
|
||||||
|
throw new Error(`${name} failed`);
|
||||||
|
}
|
||||||
|
expect(name).toBe(name);
|
||||||
|
expect.assertions(1);
|
||||||
|
marker(`END: "${name}"`);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
it.concurrent('one', testFn('one', 85));
|
||||||
|
it('two (sequential)', testFn('two (sequential)', 100));
|
||||||
|
|
||||||
|
describe('level 1', () => {
|
||||||
|
beforeEach(() => marker('beforeEach level 1'));
|
||||||
|
afterEach(() => marker('afterEach level 1'));
|
||||||
|
|
||||||
|
it.concurrent('three', testFn('three', 70));
|
||||||
|
|
||||||
|
it('four (sequential)', testFn('four (sequential)', 120));
|
||||||
|
|
||||||
|
describe('level 2', () => {
|
||||||
|
beforeEach(() => marker('beforeEach level 2'));
|
||||||
|
afterEach(() => marker('afterEach level 2'));
|
||||||
|
it.concurrent('five', testFn('five', 160));
|
||||||
|
|
||||||
|
it('six (sequential)', testFn('six (sequential)', 100));
|
||||||
|
});
|
||||||
|
|
||||||
|
it.concurrent('seven', testFn('seven', 100));
|
||||||
|
it.concurrent('eight', testFn('eight', 120));
|
||||||
|
});
|
||||||
|
|
||||||
|
it.concurrent('nine', testFn('nine', 20));
|
||||||
|
|
||||||
|
it.concurrent('ten', testFn('ten', 50));
|
|
@ -0,0 +1,63 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const {setTimeout} = require('timers/promises');
|
||||||
|
|
||||||
|
let delta = Date.now();
|
||||||
|
const includeDelta = false;
|
||||||
|
const marker = s => {
|
||||||
|
console.log(s, includeDelta ? `+${Date.now() - delta}ms` : '');
|
||||||
|
delta = Date.now();
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeAll(() => marker('beforeAll'));
|
||||||
|
afterAll(() => marker('afterAll'));
|
||||||
|
|
||||||
|
beforeEach(() => marker('beforeEach'));
|
||||||
|
afterEach(() => marker('afterEach'));
|
||||||
|
|
||||||
|
const testFn = (name, delay, fail) => {
|
||||||
|
return async () => {
|
||||||
|
marker(`START "${name}"`);
|
||||||
|
await setTimeout(delay);
|
||||||
|
if (fail) {
|
||||||
|
throw new Error(`${name} failed`);
|
||||||
|
}
|
||||||
|
expect(name).toBe(name);
|
||||||
|
expect.assertions(1);
|
||||||
|
marker(`END: "${name}"`);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
it.concurrent('one', testFn('one', 85));
|
||||||
|
it.concurrent('two', testFn('two', 100, true));
|
||||||
|
|
||||||
|
describe('level 1', () => {
|
||||||
|
beforeEach(() => marker('beforeEach level 1'));
|
||||||
|
afterEach(() => marker('afterEach level 1'));
|
||||||
|
|
||||||
|
it.concurrent('three', testFn('three', 70));
|
||||||
|
|
||||||
|
it.concurrent.only('four', testFn('four', 120));
|
||||||
|
|
||||||
|
describe('level 2', () => {
|
||||||
|
beforeEach(() => marker('beforeEach level 2'));
|
||||||
|
afterEach(() => marker('afterEach level 2'));
|
||||||
|
it.concurrent('five', testFn('five', 160, true));
|
||||||
|
|
||||||
|
it.concurrent.only('six', testFn('six', 100));
|
||||||
|
});
|
||||||
|
|
||||||
|
it.concurrent('seven', testFn('seven', 100));
|
||||||
|
it.concurrent('eight', testFn('eight', 120));
|
||||||
|
});
|
||||||
|
|
||||||
|
it.concurrent.only('nine', testFn('nine', 20));
|
||||||
|
|
||||||
|
it.concurrent('ten', testFn('ten', 50));
|
|
@ -0,0 +1,63 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const {setTimeout} = require('timers/promises');
|
||||||
|
|
||||||
|
let delta = Date.now();
|
||||||
|
const includeDelta = false;
|
||||||
|
const marker = s => {
|
||||||
|
console.log(s, includeDelta ? `+${Date.now() - delta}ms` : '');
|
||||||
|
delta = Date.now();
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeAll(() => marker('beforeAll'));
|
||||||
|
afterAll(() => marker('afterAll'));
|
||||||
|
|
||||||
|
beforeEach(() => marker('beforeEach'));
|
||||||
|
afterEach(() => marker('afterEach'));
|
||||||
|
|
||||||
|
const testFn = (name, delay, fail) => {
|
||||||
|
return async () => {
|
||||||
|
marker(`START "${name}"`);
|
||||||
|
await setTimeout(delay);
|
||||||
|
if (fail) {
|
||||||
|
throw new Error(`${name} failed`);
|
||||||
|
}
|
||||||
|
expect(name).toBe(name);
|
||||||
|
expect.assertions(1);
|
||||||
|
marker(`END: "${name}"`);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
it.concurrent('one', testFn('one', 85));
|
||||||
|
it.concurrent('two', testFn('two', 100));
|
||||||
|
|
||||||
|
describe('level 1', () => {
|
||||||
|
beforeEach(() => marker('beforeEach level 1'));
|
||||||
|
afterEach(() => marker('afterEach level 1'));
|
||||||
|
|
||||||
|
it.concurrent.skip('skipped three', testFn('three', 70));
|
||||||
|
|
||||||
|
it.concurrent('four', testFn('four', 120));
|
||||||
|
|
||||||
|
describe('level 2', () => {
|
||||||
|
beforeEach(() => marker('beforeEach level 2'));
|
||||||
|
afterEach(() => marker('afterEach level 2'));
|
||||||
|
it.concurrent.skip('five (skipped)', testFn('five', 160));
|
||||||
|
|
||||||
|
it.concurrent.skip('six (skipped)', testFn('six', 100));
|
||||||
|
});
|
||||||
|
|
||||||
|
it.concurrent('seven', testFn('seven', 100));
|
||||||
|
it.concurrent('eight', testFn('eight', 120));
|
||||||
|
});
|
||||||
|
|
||||||
|
it.concurrent.skip('nine (skipped)', testFn('nine', 20));
|
||||||
|
|
||||||
|
it.concurrent('ten', testFn('ten', 50));
|
|
@ -0,0 +1,63 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const {setTimeout} = require('timers/promises');
|
||||||
|
|
||||||
|
let delta = Date.now();
|
||||||
|
const includeDelta = false;
|
||||||
|
const marker = s => {
|
||||||
|
console.log(s, includeDelta ? `+${Date.now() - delta}ms` : '');
|
||||||
|
delta = Date.now();
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeAll(() => marker('beforeAll'));
|
||||||
|
afterAll(() => marker('afterAll'));
|
||||||
|
|
||||||
|
beforeEach(() => marker('beforeEach'));
|
||||||
|
afterEach(() => marker('afterEach'));
|
||||||
|
|
||||||
|
const testFn = (name, delay, fail) => {
|
||||||
|
return async () => {
|
||||||
|
marker(`START "${name}"`);
|
||||||
|
await setTimeout(delay);
|
||||||
|
if (fail) {
|
||||||
|
throw new Error(`${name} failed`);
|
||||||
|
}
|
||||||
|
expect(name).toBe(name);
|
||||||
|
expect.assertions(1);
|
||||||
|
marker(`END: "${name}"`);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
it.concurrent('one', testFn('one', 85));
|
||||||
|
it.concurrent('two', testFn('two', 100));
|
||||||
|
|
||||||
|
describe('level 1', () => {
|
||||||
|
beforeEach(() => marker('beforeEach level 1'));
|
||||||
|
afterEach(() => marker('afterEach level 1'));
|
||||||
|
|
||||||
|
it.concurrent('three', testFn('three', 70));
|
||||||
|
|
||||||
|
it.concurrent('four', testFn('four', 120));
|
||||||
|
|
||||||
|
describe('level 2', () => {
|
||||||
|
beforeEach(() => marker('beforeEach level 2'));
|
||||||
|
afterEach(() => marker('afterEach level 2'));
|
||||||
|
it.concurrent('five', testFn('five', 160));
|
||||||
|
|
||||||
|
it.concurrent('six', testFn('six', 100));
|
||||||
|
});
|
||||||
|
|
||||||
|
it.concurrent('seven', testFn('seven', 100));
|
||||||
|
it.concurrent('eight', testFn('eight', 120));
|
||||||
|
});
|
||||||
|
|
||||||
|
it.concurrent('nine', testFn('nine', 20));
|
||||||
|
|
||||||
|
it.concurrent('ten', testFn('ten', 50));
|
|
@ -6,24 +6,32 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
describe('block with concurrent', () => {
|
describe('block with concurrent', () => {
|
||||||
it('failing test', () => {
|
it('test should fail', () => {
|
||||||
expect(10).toBe(101);
|
expect(10).toBe(101);
|
||||||
});
|
});
|
||||||
|
|
||||||
it.concurrent.failing('failing passes = fails', () => {
|
it.concurrent.failing('.failing() should fail', () => {
|
||||||
expect(10).toBe(10);
|
expect(10).toBe(10);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it.concurrent.failing('.failing() should pass', () => {
|
||||||
|
expect(10).toBe(101);
|
||||||
|
});
|
||||||
|
|
||||||
test.concurrent.failing.each([
|
test.concurrent.failing.each([
|
||||||
{a: 1, b: 1, expected: 2},
|
{a: 1, b: 1, expected: 2},
|
||||||
{a: 1, b: 2, expected: 3},
|
{a: 1, b: 2, expected: 3},
|
||||||
{a: 2, b: 1, expected: 3},
|
{a: 2, b: 1, expected: 3},
|
||||||
])('.add($a, $b)', ({a, b, expected}) => {
|
])('.add($a, $b) .failing.each() should fail', ({a, b, expected}) => {
|
||||||
expect(a + b).toBe(expected);
|
expect(a + b).toBe(expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
it.concurrent.failing('failing fails = passes', () => {
|
test.concurrent.failing.each([
|
||||||
expect(10).toBe(101);
|
{a: 1, b: 1, expected: 2},
|
||||||
|
{a: 1, b: 2, expected: 3},
|
||||||
|
{a: 2, b: 1, expected: 3},
|
||||||
|
])('.add($a, $b) .failing.each() should pass', ({a, b, expected}) => {
|
||||||
|
expect(a + b).toBe(expected + 10);
|
||||||
});
|
});
|
||||||
|
|
||||||
it.concurrent.skip.failing('skipped failing fails', () => {
|
it.concurrent.skip.failing('skipped failing fails', () => {
|
||||||
|
|
|
@ -10,20 +10,36 @@ describe('block with concurrent', () => {
|
||||||
expect(10).toBe(101);
|
expect(10).toBe(101);
|
||||||
});
|
});
|
||||||
|
|
||||||
it.concurrent.only.failing('failing passes = fails', () => {
|
it.concurrent.only.failing('.only.failing() should fail', () => {
|
||||||
expect(10).toBe(10);
|
expect(10).toBe(10);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it.concurrent.only.failing('.only.failing() should pass', () => {
|
||||||
|
expect(10).toBe(101);
|
||||||
|
});
|
||||||
|
|
||||||
test.concurrent.only.failing.each([
|
test.concurrent.only.failing.each([
|
||||||
{a: 1, b: 1, expected: 2},
|
{a: 1, b: 1, expected: 2},
|
||||||
{a: 1, b: 2, expected: 3},
|
{a: 1, b: 2, expected: 3},
|
||||||
{a: 2, b: 1, expected: 3},
|
{a: 2, b: 1, expected: 3},
|
||||||
])('.add($a, $b)', ({a, b, expected}) => {
|
])('.add($a, $b) .only.failing.each() should fail', ({a, b, expected}) => {
|
||||||
expect(a + b).toBe(expected);
|
expect(a + b).toBe(expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
it.concurrent.only.failing('failing fails = passes', () => {
|
test.concurrent.only.failing.each([
|
||||||
expect(10).toBe(101);
|
{a: 1, b: 1, expected: 2},
|
||||||
|
{a: 1, b: 2, expected: 3},
|
||||||
|
{a: 2, b: 1, expected: 3},
|
||||||
|
])('.add($a, $b) .only.failing.each() should pass', ({a, b, expected}) => {
|
||||||
|
expect(a + b).toBe(expected + 10);
|
||||||
|
});
|
||||||
|
|
||||||
|
test.concurrent.failing.each([
|
||||||
|
{a: 1, b: 1, expected: 2},
|
||||||
|
{a: 1, b: 2, expected: 3},
|
||||||
|
{a: 2, b: 1, expected: 3},
|
||||||
|
])('.add($a, $b) skipped each', ({a, b, expected}) => {
|
||||||
|
expect(a + b).toBe(expected + 10);
|
||||||
});
|
});
|
||||||
|
|
||||||
it.concurrent.failing('skipped failing fails', () => {
|
it.concurrent.failing('skipped failing fails', () => {
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
jest.retryTimes(3);
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
throw new Error('Failure in beforeAll');
|
||||||
|
});
|
||||||
|
|
||||||
|
it.concurrent('should not be retried because hook failure occurred', () => {
|
||||||
|
throw new Error('should not be invoked');
|
||||||
|
});
|
||||||
|
|
||||||
|
it.concurrent('should fail due to the beforeAll', () => {
|
||||||
|
expect(10).toBe(10);
|
||||||
|
});
|
|
@ -0,0 +1,11 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
it('retryTimes not set', () => {
|
||||||
|
expect(true).toBeFalsy();
|
||||||
|
});
|
|
@ -0,0 +1,29 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
const countPath = path.join(__dirname, '.tries');
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
fs.writeFileSync(countPath, '0', 'utf8');
|
||||||
|
});
|
||||||
|
|
||||||
|
jest.retryTimes(3);
|
||||||
|
|
||||||
|
it.concurrent('retries', () => {
|
||||||
|
const tries = Number.parseInt(fs.readFileSync(countPath, 'utf8'), 10);
|
||||||
|
fs.writeFileSync(countPath, `${tries + 1}`, 'utf8');
|
||||||
|
expect(tries).toBe(3);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
// cleanup
|
||||||
|
fs.unlinkSync(countPath);
|
||||||
|
});
|
|
@ -0,0 +1,17 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
jest.retryTimes(3);
|
||||||
|
|
||||||
|
it.concurrent('retryTimes set', () => {
|
||||||
|
expect(true).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it.concurrent('truthy test', () => {
|
||||||
|
expect(true).toBeTruthy();
|
||||||
|
});
|
|
@ -0,0 +1,34 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
jest.retryTimes(3, {logErrorsBeforeRetry: true, retryImmediately: true});
|
||||||
|
let i1 = 0;
|
||||||
|
it.concurrent('retryable test 1', () => {
|
||||||
|
i1++;
|
||||||
|
if (i1 === 3) {
|
||||||
|
console.log('SECOND TRUTHY TEST');
|
||||||
|
expect(true).toBeTruthy();
|
||||||
|
} else {
|
||||||
|
expect(true).toBeFalsy();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let i2 = 0;
|
||||||
|
it.concurrent('retryable test 2', () => {
|
||||||
|
i2++;
|
||||||
|
if (i2 === 3) {
|
||||||
|
console.log('THIRD TRUTHY TEST');
|
||||||
|
expect(true).toBeTruthy();
|
||||||
|
} else {
|
||||||
|
expect(true).toBeFalsy();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
it.concurrent('truthy test', () => {
|
||||||
|
console.log('FIRST TRUTHY TEST');
|
||||||
|
expect(true).toBeTruthy();
|
||||||
|
});
|
|
@ -1,5 +1,77 @@
|
||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`concurrent 1`] = `
|
||||||
|
"start_describe_definition: describe
|
||||||
|
add_hook: beforeEach
|
||||||
|
add_hook: afterEach
|
||||||
|
add_test: one
|
||||||
|
add_test: two
|
||||||
|
add_test: three
|
||||||
|
finish_describe_definition: describe
|
||||||
|
run_start
|
||||||
|
run_describe_start: ROOT_DESCRIBE_BLOCK
|
||||||
|
run_describe_start: describe
|
||||||
|
test_start: one
|
||||||
|
test_start: two
|
||||||
|
test_start: three
|
||||||
|
test_started: one
|
||||||
|
test_started: two
|
||||||
|
test_started: three
|
||||||
|
test_fn_start: one
|
||||||
|
test_fn_start: two
|
||||||
|
test_fn_start: three
|
||||||
|
hello one
|
||||||
|
hello two
|
||||||
|
hello three
|
||||||
|
test_fn_failure: one
|
||||||
|
test_fn_success: two
|
||||||
|
test_fn_success: three
|
||||||
|
test_done: one
|
||||||
|
test_done: two
|
||||||
|
test_done: three
|
||||||
|
run_describe_finish: describe
|
||||||
|
run_describe_finish: ROOT_DESCRIBE_BLOCK
|
||||||
|
run_finish
|
||||||
|
|
||||||
|
unhandledErrors: 0"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`concurrent.each 1`] = `
|
||||||
|
"start_describe_definition: describe
|
||||||
|
add_hook: beforeEach
|
||||||
|
add_hook: afterEach
|
||||||
|
add_test: one
|
||||||
|
add_test: two
|
||||||
|
add_test: three
|
||||||
|
finish_describe_definition: describe
|
||||||
|
run_start
|
||||||
|
run_describe_start: ROOT_DESCRIBE_BLOCK
|
||||||
|
run_describe_start: describe
|
||||||
|
test_start: one
|
||||||
|
test_start: two
|
||||||
|
test_start: three
|
||||||
|
test_started: one
|
||||||
|
test_started: two
|
||||||
|
test_started: three
|
||||||
|
test_fn_start: one
|
||||||
|
test_fn_start: two
|
||||||
|
test_fn_start: three
|
||||||
|
hello one
|
||||||
|
hello two
|
||||||
|
hello three
|
||||||
|
test_fn_success: one
|
||||||
|
test_fn_success: two
|
||||||
|
test_fn_success: three
|
||||||
|
test_done: one
|
||||||
|
test_done: two
|
||||||
|
test_done: three
|
||||||
|
run_describe_finish: describe
|
||||||
|
run_describe_finish: ROOT_DESCRIBE_BLOCK
|
||||||
|
run_finish
|
||||||
|
|
||||||
|
unhandledErrors: 0"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`failures 1`] = `
|
exports[`failures 1`] = `
|
||||||
"start_describe_definition: describe
|
"start_describe_definition: describe
|
||||||
add_hook: beforeEach
|
add_hook: beforeEach
|
||||||
|
|
|
@ -42,3 +42,44 @@ test('failures', () => {
|
||||||
|
|
||||||
expect(stdout).toMatchSnapshot();
|
expect(stdout).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('concurrent', () => {
|
||||||
|
const {stdout} = runTest(`
|
||||||
|
describe('describe', () => {
|
||||||
|
beforeEach(() => {});
|
||||||
|
afterEach(() => { throw new Error('banana')});
|
||||||
|
test.concurrent('one', () => {
|
||||||
|
console.log('hello one');
|
||||||
|
throw new Error('kentucky')
|
||||||
|
});
|
||||||
|
test.concurrent('two', () => {
|
||||||
|
console.log('hello two');
|
||||||
|
});
|
||||||
|
test.concurrent('three', async () => {
|
||||||
|
console.log('hello three');
|
||||||
|
await Promise.resolve();
|
||||||
|
});
|
||||||
|
})
|
||||||
|
`);
|
||||||
|
|
||||||
|
expect(stdout).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('concurrent.each', () => {
|
||||||
|
const {stdout} = runTest(`
|
||||||
|
describe('describe', () => {
|
||||||
|
beforeEach(() => {});
|
||||||
|
afterEach(() => { throw new Error('banana')});
|
||||||
|
test.concurrent.each([
|
||||||
|
['one'],
|
||||||
|
['two'],
|
||||||
|
['three'],
|
||||||
|
])('%s', async (name) => {
|
||||||
|
console.log('hello %s', name);
|
||||||
|
await Promise.resolve();
|
||||||
|
});
|
||||||
|
})
|
||||||
|
`);
|
||||||
|
|
||||||
|
expect(stdout).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
|
@ -30,6 +30,7 @@ const {setTimeout} = globalThis;
|
||||||
|
|
||||||
type ConcurrentTestEntry = Omit<Circus.TestEntry, 'fn'> & {
|
type ConcurrentTestEntry = Omit<Circus.TestEntry, 'fn'> & {
|
||||||
fn: Circus.ConcurrentTestFn;
|
fn: Circus.ConcurrentTestFn;
|
||||||
|
done: Promise<void>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const run = async (): Promise<Circus.RunResult> => {
|
const run = async (): Promise<Circus.RunResult> => {
|
||||||
|
@ -63,7 +64,7 @@ const _runTestsForDescribeBlock = async (
|
||||||
if (isRootBlock) {
|
if (isRootBlock) {
|
||||||
const concurrentTests = collectConcurrentTests(describeBlock);
|
const concurrentTests = collectConcurrentTests(describeBlock);
|
||||||
if (concurrentTests.length > 0) {
|
if (concurrentTests.length > 0) {
|
||||||
startTestsConcurrently(concurrentTests);
|
startTestsConcurrently(concurrentTests, isSkipped);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,7 +82,7 @@ const _runTestsForDescribeBlock = async (
|
||||||
const retryImmediately: boolean =
|
const retryImmediately: boolean =
|
||||||
((globalThis as Global.Global)[RETRY_IMMEDIATELY] as any) || false;
|
((globalThis as Global.Global)[RETRY_IMMEDIATELY] as any) || false;
|
||||||
|
|
||||||
const deferredRetryTests = [];
|
const deferredRetryTests: Array<Circus.TestEntry> = [];
|
||||||
|
|
||||||
if (rng) {
|
if (rng) {
|
||||||
describeBlock.children = shuffleArray(describeBlock.children, rng);
|
describeBlock.children = shuffleArray(describeBlock.children, rng);
|
||||||
|
@ -103,6 +104,27 @@ const _runTestsForDescribeBlock = async (
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleRetry = async (
|
||||||
|
test: Circus.TestEntry,
|
||||||
|
hasErrorsBeforeTestRun: boolean,
|
||||||
|
hasRetryTimes: boolean,
|
||||||
|
) => {
|
||||||
|
// no retry if the test passed or had errors before the test ran
|
||||||
|
if (test.errors.length === 0 || hasErrorsBeforeTestRun || !hasRetryTimes) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!retryImmediately) {
|
||||||
|
deferredRetryTests.push(test);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If immediate retry is set, we retry the test immediately after the first run
|
||||||
|
await rerunTest(test);
|
||||||
|
};
|
||||||
|
|
||||||
|
const concurrentTests = [];
|
||||||
|
|
||||||
for (const child of describeBlock.children) {
|
for (const child of describeBlock.children) {
|
||||||
switch (child.type) {
|
switch (child.type) {
|
||||||
case 'describeBlock': {
|
case 'describeBlock': {
|
||||||
|
@ -112,29 +134,24 @@ const _runTestsForDescribeBlock = async (
|
||||||
case 'test': {
|
case 'test': {
|
||||||
const hasErrorsBeforeTestRun = child.errors.length > 0;
|
const hasErrorsBeforeTestRun = child.errors.length > 0;
|
||||||
const hasRetryTimes = retryTimes > 0;
|
const hasRetryTimes = retryTimes > 0;
|
||||||
await _runTest(child, isSkipped);
|
if (child.concurrent) {
|
||||||
|
concurrentTests.push(
|
||||||
// If immediate retry is set, we retry the test immediately after the first run
|
(child as ConcurrentTestEntry).done.then(() =>
|
||||||
if (
|
handleRetry(child, hasErrorsBeforeTestRun, hasRetryTimes),
|
||||||
retryImmediately &&
|
),
|
||||||
hasErrorsBeforeTestRun === false &&
|
);
|
||||||
hasRetryTimes
|
} else {
|
||||||
) {
|
await _runTest(child, isSkipped);
|
||||||
await rerunTest(child);
|
await handleRetry(child, hasErrorsBeforeTestRun, hasRetryTimes);
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
hasErrorsBeforeTestRun === false &&
|
|
||||||
hasRetryTimes &&
|
|
||||||
!retryImmediately
|
|
||||||
) {
|
|
||||||
deferredRetryTests.push(child);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// wait for concurrent tests to finish
|
||||||
|
await Promise.all(concurrentTests);
|
||||||
|
|
||||||
// Re-run failed tests n-times if configured
|
// Re-run failed tests n-times if configured
|
||||||
for (const test of deferredRetryTests) {
|
for (const test of deferredRetryTests) {
|
||||||
await rerunTest(test);
|
await rerunTest(test);
|
||||||
|
@ -155,23 +172,23 @@ function collectConcurrentTests(
|
||||||
if (describeBlock.mode === 'skip') {
|
if (describeBlock.mode === 'skip') {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
const {hasFocusedTests, testNamePattern} = getState();
|
|
||||||
return describeBlock.children.flatMap(child => {
|
return describeBlock.children.flatMap(child => {
|
||||||
switch (child.type) {
|
switch (child.type) {
|
||||||
case 'describeBlock':
|
case 'describeBlock':
|
||||||
return collectConcurrentTests(child);
|
return collectConcurrentTests(child);
|
||||||
case 'test':
|
case 'test':
|
||||||
const skip =
|
if (child.concurrent) {
|
||||||
!child.concurrent ||
|
return [child as ConcurrentTestEntry];
|
||||||
child.mode === 'skip' ||
|
}
|
||||||
(hasFocusedTests && child.mode !== 'only') ||
|
return [];
|
||||||
(testNamePattern && !testNamePattern.test(getTestID(child)));
|
|
||||||
return skip ? [] : [child as ConcurrentTestEntry];
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function startTestsConcurrently(concurrentTests: Array<ConcurrentTestEntry>) {
|
function startTestsConcurrently(
|
||||||
|
concurrentTests: Array<ConcurrentTestEntry>,
|
||||||
|
parentSkipped: boolean,
|
||||||
|
) {
|
||||||
const mutex = pLimit(getState().maxConcurrency);
|
const mutex = pLimit(getState().maxConcurrency);
|
||||||
const testNameStorage = new AsyncLocalStorage<string>();
|
const testNameStorage = new AsyncLocalStorage<string>();
|
||||||
jestExpect.setState({
|
jestExpect.setState({
|
||||||
|
@ -179,13 +196,16 @@ function startTestsConcurrently(concurrentTests: Array<ConcurrentTestEntry>) {
|
||||||
});
|
});
|
||||||
for (const test of concurrentTests) {
|
for (const test of concurrentTests) {
|
||||||
try {
|
try {
|
||||||
const testFn = test.fn;
|
const promise = mutex(() =>
|
||||||
const promise = mutex(() => testNameStorage.run(getTestID(test), testFn));
|
testNameStorage.run(getTestID(test), () =>
|
||||||
|
_runTest(test, parentSkipped),
|
||||||
|
),
|
||||||
|
);
|
||||||
// Avoid triggering the uncaught promise rejection handler in case the
|
// Avoid triggering the uncaught promise rejection handler in case the
|
||||||
// test fails before being awaited on.
|
// test fails before being awaited on.
|
||||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||||
promise.catch(() => {});
|
promise.catch(() => {});
|
||||||
test.fn = () => promise;
|
test.done = promise;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
test.fn = () => {
|
test.fn = () => {
|
||||||
throw error;
|
throw error;
|
||||||
|
|
Loading…
Reference in New Issue