diff --git a/CHANGELOG.md b/CHANGELOG.md index e1eabc73a3f9..ca32e2a69b4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ - `[babel-plugin-jest-hoist]` Use `denylist` instead of the deprecated `blacklist` for Babel 8 support ([#14109](https://github.com/jestjs/jest/pull/14109)) - `[expect]` Check error instance type for `toThrow/toThrowError` ([#14576](https://github.com/jestjs/jest/pull/14576)) - `[jest-circus]` [**BREAKING**] Prevent false test failures caused by promise rejections handled asynchronously ([#14315](https://github.com/jestjs/jest/pull/14315)) +- `[jest-circus]` Replace recursive `makeTestResults` implementation with iterative one ([#14760](https://github.com/jestjs/jest/pull/14760)) - `[jest-circus, jest-expect, jest-snapshot]` Pass `test.failing` tests when containing failing snapshot matchers ([#14313](https://github.com/jestjs/jest/pull/14313)) - `[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)) diff --git a/packages/jest-circus/src/__tests__/utils.test.ts b/packages/jest-circus/src/__tests__/utils.test.ts new file mode 100644 index 000000000000..aeaa15fe6210 --- /dev/null +++ b/packages/jest-circus/src/__tests__/utils.test.ts @@ -0,0 +1,24 @@ +/** + * 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 {runTest} from '../__mocks__/testUtils'; + +test('makeTestResults does not thrown a stack overflow exception', () => { + let testString = 'describe("top level describe", () => {'; + const numberOfTestBlocks = 150_000; + let currentTestIndex = 0; + + while (currentTestIndex < numberOfTestBlocks) { + testString += `test("should do something #${currentTestIndex++}", () => {});`; + } + + testString += '})'; + + const {stdout} = runTest(testString); + + expect(stdout.split('\n')).toHaveLength(900_010); +}); diff --git a/packages/jest-circus/src/utils.ts b/packages/jest-circus/src/utils.ts index 786b010b7fb7..12e6e9595293 100644 --- a/packages/jest-circus/src/utils.ts +++ b/packages/jest-circus/src/utils.ts @@ -391,17 +391,22 @@ export const makeSingleTestResult = ( const makeTestResults = ( describeBlock: Circus.DescribeBlock, ): Circus.TestResults => { - const testResults: Circus.TestResults = []; + const testResults = []; + const stack: [[Circus.DescribeBlock, number]] = [[describeBlock, 0]]; - for (const child of describeBlock.children) { - switch (child.type) { - case 'describeBlock': { - testResults.push(...makeTestResults(child)); + while (stack.length > 0) { + const [currentBlock, childIndex] = stack.pop()!; + + for (let i = childIndex; i < currentBlock.children.length; i++) { + const child = currentBlock.children[i]; + + if (child.type === 'describeBlock') { + stack.push([currentBlock, i + 1]); + stack.push([child, 0]); break; } - case 'test': { + if (child.type === 'test') { testResults.push(makeSingleTestResult(child)); - break; } } }