From c6f5f7f93a30ca4c69ef93a34bc8761165b6c51e Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Tue, 19 Dec 2023 22:56:20 +0900 Subject: [PATCH] fix(runner): handle fixture teardown error (#4683) --- packages/runner/src/fixture.ts | 12 +++++++++++- .../fixtures/test-extend/fixture-error.test.ts | 13 +++++++++++++ test/fails/test/__snapshots__/runner.test.ts.snap | 3 ++- 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/packages/runner/src/fixture.ts b/packages/runner/src/fixture.ts index bc18b23f4657..d0ec39152296 100644 --- a/packages/runner/src/fixture.ts +++ b/packages/runner/src/fixture.ts @@ -89,7 +89,9 @@ export function withFixtures(fn: Function, testContext?: TestContext) { if (fixture.isFn) { // wait for `use` call to extract fixture value const useFnArgPromise = createDefer() + let isUseFnArgResolved = false const fixtureReturn = fixture.value(context, async (useFnArg: unknown) => { + isUseFnArgResolved = true useFnArgPromise.resolve(useFnArg) // suspend fixture teardown until cleanup const useReturnPromise = createDefer() @@ -100,7 +102,15 @@ export function withFixtures(fn: Function, testContext?: TestContext) { await fixtureReturn }) await useReturnPromise - }).catch(useFnArgPromise.reject) // treat fixture function error (until `use` call) as test failure + }).catch((e: unknown) => { + // treat fixture setup error as test failure + if (!isUseFnArgResolved) { + useFnArgPromise.reject(e) + return + } + // otherwise re-throw to avoid silencing error during cleanup + throw e + }) resolvedValue = await useFnArgPromise } else { diff --git a/test/fails/fixtures/test-extend/fixture-error.test.ts b/test/fails/fixtures/test-extend/fixture-error.test.ts index 4f815b9cae8d..2ec15cf73b8f 100644 --- a/test/fails/fixtures/test-extend/fixture-error.test.ts +++ b/test/fails/fixtures/test-extend/fixture-error.test.ts @@ -50,3 +50,16 @@ describe('correctly fails when test times out', () => { expect(a).toBe(2) }, 20) }) + +describe('error thrown during fixture teardown', () => { + const myTest = test.extend<{ a: string }>({ + a: async ({}, use) => { + await use("hello"); + throw new Error('Error fixture teardown') + }, + }) + + myTest('no error in test', ({ a }) => { + expect(a).toBe("hello"); + }) +}) diff --git a/test/fails/test/__snapshots__/runner.test.ts.snap b/test/fails/test/__snapshots__/runner.test.ts.snap index b628660bd975..5ee589c515ee 100644 --- a/test/fails/test/__snapshots__/runner.test.ts.snap +++ b/test/fails/test/__snapshots__/runner.test.ts.snap @@ -43,7 +43,8 @@ TypeError: failure" exports[`should fail test-extend/circular-dependency.test.ts > test-extend/circular-dependency.test.ts 1`] = `"Error: Circular fixture dependency detected: a <- b <- a"`; exports[`should fail test-extend/fixture-error.test.ts > test-extend/fixture-error.test.ts 1`] = ` -"Error: Test timed out in 20ms. +"Error: Error fixture teardown +Error: Test timed out in 20ms. Error: Error thrown in test fixture Error: Error thrown in afterEach fixture Error: Error thrown in beforeEach fixture"