diff --git a/packages/playwright/src/runner/dispatcher.ts b/packages/playwright/src/runner/dispatcher.ts index 4e971f24757ac..4ced6088a2074 100644 --- a/packages/playwright/src/runner/dispatcher.ts +++ b/packages/playwright/src/runner/dispatcher.ts @@ -401,7 +401,7 @@ class JobDispatcher { // - there are no remaining // - we are here not because something failed // - no unrecoverable worker error - if (!this._remainingByTestId.size && !this._failedTests.size && !params.fatalErrors.length && !params.skipTestsDueToSetupFailure.length && !params.fatalUnknownTestIds && !params.unexpectedExitError) { + if (!this._remainingByTestId.size && !this._failedTests.size && !params.fatalErrors.length && !params.skipTestsDueToSetupFailure.length && !params.fatalUnknownTestIds && !params.unexpectedExitError && !process.env.NON_ISOLATED_TESTS) { this._finished({ didFail: false }); return; } diff --git a/packages/playwright/src/runner/testGroups.ts b/packages/playwright/src/runner/testGroups.ts index 5a70bc84cc0d7..fab717168ea2e 100644 --- a/packages/playwright/src/runner/testGroups.ts +++ b/packages/playwright/src/runner/testGroups.ts @@ -49,6 +49,7 @@ export function createTestGroups(projectSuite: Suite, workers: number): TestGrou // These should run as a serial group, each group is independent, key === serial suite. parallel: Map, parallelWithHooks: TestGroup, + parallelDescribes: Map>, }>>(); const createGroup = (test: TestCase): TestGroup => { @@ -73,6 +74,7 @@ export function createTestGroups(projectSuite: Suite, workers: number): TestGrou general: createGroup(test), parallel: new Map(), parallelWithHooks: createGroup(test), + parallelDescribes: new Map(), }; withWorkerHash.set(test._requireFile, withRequireFile); } @@ -81,16 +83,28 @@ export function createTestGroups(projectSuite: Suite, workers: number): TestGrou let insideParallel = false; let outerMostSequentialSuite: Suite | undefined; let hasAllHooks = false; + let hasDescribe = false; + let currentTitle = ''; for (let parent: Suite | undefined = test.parent; parent; parent = parent.parent) { if (parent._parallelMode === 'serial' || parent._parallelMode === 'default') outerMostSequentialSuite = parent; insideParallel = insideParallel || parent._parallelMode === 'parallel'; hasAllHooks = hasAllHooks || parent._hooks.some(hook => hook.type === 'beforeAll' || hook.type === 'afterAll'); + hasDescribe = hasDescribe || parent.type === 'describe'; + if (parent._type === 'describe') + currentTitle = parent.title; } if (insideParallel) { - if (hasAllHooks && !outerMostSequentialSuite) { + if ((hasAllHooks && !outerMostSequentialSuite && !process.env.NON_ISOLATED_TESTS) || (!hasDescribe && process.env.NON_ISOLATED_TESTS)) { withRequireFile.parallelWithHooks.tests.push(test); + } else if (hasDescribe && process.env.NON_ISOLATED_TESTS) { + if (!withRequireFile.parallelDescribes.get(currentTitle)) { + withRequireFile.parallelDescribes.set(currentTitle, [test]); + } else { + const value = withRequireFile.parallelDescribes.get(currentTitle); + value?.push(test); + } } else { const key = outerMostSequentialSuite || test; let group = withRequireFile.parallel.get(key); @@ -125,6 +139,20 @@ export function createTestGroups(projectSuite: Suite, workers: number): TestGrou } lastGroup.tests.push(test); } + if (process.env.NON_ISOLATED_TESTS) { + const describeGroups = Array.from(withRequireFile.parallelDescribes, ([name, value]) => ({ name, value })); + let lastDescribeGroup; + for (const testGroup of describeGroups) { + lastDescribeGroup = null; + for (const test of testGroup.value) { + if (!lastDescribeGroup) { + lastDescribeGroup = createGroup(test); + result.push(lastDescribeGroup); + } + lastDescribeGroup.tests.push(test); + } + } + } } } return result; diff --git a/packages/playwright/src/worker/workerMain.ts b/packages/playwright/src/worker/workerMain.ts index f180f3d08ba05..4adb6b70bc991 100644 --- a/packages/playwright/src/worker/workerMain.ts +++ b/packages/playwright/src/worker/workerMain.ts @@ -266,7 +266,7 @@ export class WorkerMain extends ProcessRunner { } }; - if (!this._isStopped) + if (!(this._isStopped && !process.env.NON_ISOLATED_TESTS)) this._fixtureRunner.setPool(test._pool!); const suites = getSuites(test); @@ -320,7 +320,7 @@ export class WorkerMain extends ProcessRunner { await testInfo._tracing.startIfNeeded(traceFixtureRegistration.fn); }); - if (this._isStopped || isSkipped) { + if ((this._isStopped && !process.env.NON_ISOLATED_TESTS) || isSkipped) { // Two reasons to get here: // - Last test is skipped, so we should not run the test, but run the cleanup. // - Worker is requested to stop, but was not able to run full cleanup yet. @@ -396,7 +396,7 @@ export class WorkerMain extends ProcessRunner { // In case of failure the worker will be stopped and we have to make sure that afterAll // hooks run before worker fixtures teardown. for (const suite of reversedSuites) { - if (!nextSuites.has(suite) || testInfo._isFailure()) { + if (!nextSuites.has(suite) || (testInfo._isFailure() && !process.env.NON_ISOLATED_TESTS)) { try { await this._runAfterAllHooksForSuite(suite, testInfo); } catch (error) { @@ -412,7 +412,7 @@ export class WorkerMain extends ProcessRunner { if (testInfo._isFailure()) this._isStopped = true; - if (this._isStopped) { + if (this._isStopped && !process.env.NON_ISOLATED_TESTS) { // Run all remaining "afterAll" hooks and teardown all fixtures when worker is shutting down. // Mark as "cleaned up" early to avoid running cleanup twice. this._didRunFullCleanup = true; @@ -534,14 +534,18 @@ export class WorkerMain extends ProcessRunner { } } } - if (firstError) + if (firstError) { + if (process.env.NON_ISOLATED_TESTS) + this._didRunFullCleanup = true; throw firstError; + } } private async _runAfterAllHooksForSuite(suite: Suite, testInfo: TestInfoImpl) { if (!this._activeSuites.has(suite)) return; - this._activeSuites.delete(suite); + if (!process.env.NON_ISOLATED_TESTS) + this._activeSuites.delete(suite); await this._runAllHooksForSuite(suite, testInfo, 'afterAll'); }