From f48a02cdb0a78a3a475c44862bb5fa091698546d Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Sun, 25 Aug 2024 11:42:41 +0900 Subject: [PATCH 01/10] fix(runner): auto await async assertion should timeout --- packages/runner/src/run.ts | 20 ++++++++++---------- packages/runner/src/suite.ts | 13 ++++++++++++- packages/vitest/LICENSE.md | 29 +++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 11 deletions(-) diff --git a/packages/runner/src/run.ts b/packages/runner/src/run.ts index d27ee69d5bd4..8e775b787a6d 100644 --- a/packages/runner/src/run.ts +++ b/packages/runner/src/run.ts @@ -223,16 +223,16 @@ export async function runTest(test: Test | Custom, runner: VitestRunner): Promis } await fn() } - // some async expect will be added to this array, in case user forget to await theme - if (test.promises) { - const result = await Promise.allSettled(test.promises) - const errors = result - .map(r => (r.status === 'rejected' ? r.reason : undefined)) - .filter(Boolean) - if (errors.length) { - throw errors - } - } + // // some async expect will be added to this array, in case user forget to await theme + // if (test.promises) { + // const result = await Promise.allSettled(test.promises) + // const errors = result + // .map(r => (r.status === 'rejected' ? r.reason : undefined)) + // .filter(Boolean) + // if (errors.length) { + // throw errors + // } + // } await runner.onAfterTryTask?.(test, { retry: retryCount, diff --git a/packages/runner/src/suite.ts b/packages/runner/src/suite.ts index 1572190d107f..1619da97050e 100644 --- a/packages/runner/src/suite.ts +++ b/packages/runner/src/suite.ts @@ -20,6 +20,7 @@ import type { SuiteHooks, Task, TaskCustomOptions, + TaskPopulated, Test, TestAPI, TestFunction, @@ -346,7 +347,7 @@ function createSuiteCollector( setFn( task, withTimeout( - withFixtures(handler, context), + withAwaitAsyncAssetions(withFixtures(handler, context), task), options?.timeout ?? runner.config.testTimeout, ), ) @@ -481,6 +482,16 @@ function createSuiteCollector( return collector } +function withAwaitAsyncAssetions any>(fn: T, task: TaskPopulated): T { + return (async (...args: any[]) => { + await fn(...args); + // some async expect will be added to this array, in case user forget to await them + if (task.promises) { + await Promise.all(task.promises); + } + }) as T +} + function createSuite() { function suiteFn( this: Record, diff --git a/packages/vitest/LICENSE.md b/packages/vitest/LICENSE.md index d54def96b44e..9f826da7d2d2 100644 --- a/packages/vitest/LICENSE.md +++ b/packages/vitest/LICENSE.md @@ -1235,6 +1235,35 @@ Repository: sindresorhus/p-locate --------------------------------------- +## package-manager-detector +License: MIT +By: Anthony Fu +Repository: git+https://github.com/antfu-collective/package-manager-detector.git + +> MIT License +> +> Copyright (c) 2020-PRESENT Anthony Fu +> +> Permission is hereby granted, free of charge, to any person obtaining a copy +> of this software and associated documentation files (the "Software"), to deal +> in the Software without restriction, including without limitation the rights +> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +> copies of the Software, and to permit persons to whom the Software is +> furnished to do so, subject to the following conditions: +> +> The above copyright notice and this permission notice shall be included in all +> copies or substantial portions of the Software. +> +> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +> SOFTWARE. + +--------------------------------------- + ## picomatch License: MIT By: Jon Schlinkert From 6722c0a748b86aef25545fcb2b6f7f9d40d41312 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Sun, 25 Aug 2024 12:04:15 +0900 Subject: [PATCH 02/10] chore: lint --- packages/runner/src/suite.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/runner/src/suite.ts b/packages/runner/src/suite.ts index 1619da97050e..3595c3fb9da0 100644 --- a/packages/runner/src/suite.ts +++ b/packages/runner/src/suite.ts @@ -484,10 +484,10 @@ function createSuiteCollector( function withAwaitAsyncAssetions any>(fn: T, task: TaskPopulated): T { return (async (...args: any[]) => { - await fn(...args); + await fn(...args) // some async expect will be added to this array, in case user forget to await them if (task.promises) { - await Promise.all(task.promises); + await Promise.all(task.promises) } }) as T } From 3e8902069128ace6fd52a428ed4ae3e5a6a6b331 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Sun, 25 Aug 2024 12:40:48 +0900 Subject: [PATCH 03/10] test: tweak --- packages/runner/src/context.ts | 2 +- test/cli/test/fails.test.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/runner/src/context.ts b/packages/runner/src/context.ts index f03b389c2484..6d0d954eed19 100644 --- a/packages/runner/src/context.ts +++ b/packages/runner/src/context.ts @@ -41,7 +41,7 @@ export function withTimeout any>( const { setTimeout, clearTimeout } = getSafeTimers() - return ((...args: T extends (...args: infer A) => any ? A : never) => { + return (function runWithTimeout(...args: T extends (...args: infer A) => any ? A : never) { return Promise.race([ fn(...args), new Promise((resolve, reject) => { diff --git a/test/cli/test/fails.test.ts b/test/cli/test/fails.test.ts index aee55dd286e8..20f94a7f8ced 100644 --- a/test/cli/test/fails.test.ts +++ b/test/cli/test/fails.test.ts @@ -17,7 +17,7 @@ it.each(files)('should fail %s', async (file) => { const msg = String(stderr) .split(/\n/g) .reverse() - .filter(i => i.includes('Error: ') && !i.includes('Command failed') && !i.includes('stackStr') && !i.includes('at runTest')) + .filter(i => i.includes('Error: ') && !i.includes('Command failed') && !i.includes('stackStr') && !i.includes('at runTest') && !i.includes('at runWithTimeout')) .map(i => i.trim().replace(root, ''), ).join('\n') expect(msg).toMatchSnapshot(file) From c8e96fd910ac2a2ea60415be7ef219652d493caf Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Sun, 25 Aug 2024 12:57:20 +0900 Subject: [PATCH 04/10] test: add test --- test/cli/fixtures/fails/test-timeout.test.ts | 6 +++++- test/cli/test/__snapshots__/fails.test.ts.snap | 3 ++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/test/cli/fixtures/fails/test-timeout.test.ts b/test/cli/fixtures/fails/test-timeout.test.ts index d06123b55778..010697ce7e51 100644 --- a/test/cli/fixtures/fails/test-timeout.test.ts +++ b/test/cli/fixtures/fails/test-timeout.test.ts @@ -1,4 +1,4 @@ -import { suite, test } from 'vitest' +import { expect, suite, test } from 'vitest' test('hi', async () => { await new Promise(resolve => setTimeout(resolve, 1000)) @@ -17,3 +17,7 @@ suite('suite timeout simple input', () => { await new Promise(resolve => setTimeout(resolve, 500)) }) }, 200) + +test('auto await async assertion', { timeout: 20 }, () => { + expect(new Promise(() => {})).resolves.toBe(0) +}) diff --git a/test/cli/test/__snapshots__/fails.test.ts.snap b/test/cli/test/__snapshots__/fails.test.ts.snap index a1fefe9ff820..766b8ddf67aa 100644 --- a/test/cli/test/__snapshots__/fails.test.ts.snap +++ b/test/cli/test/__snapshots__/fails.test.ts.snap @@ -85,7 +85,8 @@ exports[`should fail test-extend/test-rest-props.test.ts > test-extend/test-rest exports[`should fail test-extend/test-without-destructuring.test.ts > test-extend/test-without-destructuring.test.ts 1`] = `"Error: The first argument inside a fixture must use object destructuring pattern, e.g. ({ test } => {}). Instead, received "context"."`; exports[`should fail test-timeout.test.ts > test-timeout.test.ts 1`] = ` -"Error: Test timed out in 200ms. +"Error: Test timed out in 20ms. +Error: Test timed out in 200ms. Error: Test timed out in 100ms. Error: Test timed out in 10ms." `; From 029edf4200448348a78fdcb984248bd2127123d1 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Sun, 25 Aug 2024 12:57:29 +0900 Subject: [PATCH 05/10] chore: cleanup --- packages/runner/src/run.ts | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/packages/runner/src/run.ts b/packages/runner/src/run.ts index 8e775b787a6d..3ad026bbfd26 100644 --- a/packages/runner/src/run.ts +++ b/packages/runner/src/run.ts @@ -223,16 +223,6 @@ export async function runTest(test: Test | Custom, runner: VitestRunner): Promis } await fn() } - // // some async expect will be added to this array, in case user forget to await theme - // if (test.promises) { - // const result = await Promise.allSettled(test.promises) - // const errors = result - // .map(r => (r.status === 'rejected' ? r.reason : undefined)) - // .filter(Boolean) - // if (errors.length) { - // throw errors - // } - // } await runner.onAfterTryTask?.(test, { retry: retryCount, From 6f68621c475ab9867a30bdee14b2f3664e915a9c Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Sun, 25 Aug 2024 13:39:19 +0900 Subject: [PATCH 06/10] chore: comment --- packages/runner/src/context.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/runner/src/context.ts b/packages/runner/src/context.ts index 6d0d954eed19..f655a1d57acd 100644 --- a/packages/runner/src/context.ts +++ b/packages/runner/src/context.ts @@ -41,6 +41,7 @@ export function withTimeout any>( const { setTimeout, clearTimeout } = getSafeTimers() + // this function name is used to filter error in test/cli/test/fails.test.ts return (function runWithTimeout(...args: T extends (...args: infer A) => any ? A : never) { return Promise.race([ fn(...args), From 6b41abd6f2e00d5257b9f21082d188925d4351c0 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Wed, 28 Aug 2024 16:44:05 +0900 Subject: [PATCH 07/10] fix: await allSettled --- packages/runner/src/suite.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/runner/src/suite.ts b/packages/runner/src/suite.ts index 3595c3fb9da0..666f8fe7e155 100644 --- a/packages/runner/src/suite.ts +++ b/packages/runner/src/suite.ts @@ -487,7 +487,13 @@ function withAwaitAsyncAssetions any>(fn: T, task: await fn(...args) // some async expect will be added to this array, in case user forget to await them if (task.promises) { - await Promise.all(task.promises) + const result = await Promise.allSettled(task.promises) + const errors = result + .map(r => (r.status === 'rejected' ? r.reason : undefined)) + .filter(Boolean) + if (errors.length) { + throw errors + } } }) as T } From e18261cdd76ba2f3fa2dbd3f48a35e33ca380342 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Wed, 28 Aug 2024 16:46:50 +0900 Subject: [PATCH 08/10] test: add test --- test/cli/fixtures/fails/async-assertion.test.ts | 6 ++++++ test/cli/test/__snapshots__/fails.test.ts.snap | 7 +++++++ 2 files changed, 13 insertions(+) create mode 100644 test/cli/fixtures/fails/async-assertion.test.ts diff --git a/test/cli/fixtures/fails/async-assertion.test.ts b/test/cli/fixtures/fails/async-assertion.test.ts new file mode 100644 index 000000000000..aa88aff4f55d --- /dev/null +++ b/test/cli/fixtures/fails/async-assertion.test.ts @@ -0,0 +1,6 @@ +import { test, expect } from "vitest" + +test('multiple errors', () => { + expect(new Promise((r) => r("xx"))).resolves.toBe("yy"); + expect(new Promise((r) => setTimeout(() => r("xx"), 10))).resolves.toBe("zz"); +}) diff --git a/test/cli/test/__snapshots__/fails.test.ts.snap b/test/cli/test/__snapshots__/fails.test.ts.snap index 766b8ddf67aa..4cb5d4c511c1 100644 --- a/test/cli/test/__snapshots__/fails.test.ts.snap +++ b/test/cli/test/__snapshots__/fails.test.ts.snap @@ -2,6 +2,13 @@ exports[`should fail .dot-folder/dot-test.test.ts > .dot-folder/dot-test.test.ts 1`] = `"AssertionError: expected true to be false // Object.is equality"`; +exports[`should fail async-assertion.test.ts > async-assertion.test.ts 1`] = ` +"AssertionError: expected 'xx' to be 'zz' // Object.is equality +AssertionError: expected 'xx' to be 'yy' // Object.is equality +AssertionError: expected 'xx' to be 'zz' // Object.is equality +AssertionError: expected 'xx' to be 'yy' // Object.is equality" +`; + exports[`should fail concurrent-suite-deadlock.test.ts > concurrent-suite-deadlock.test.ts 1`] = `"Error: Test timed out in 500ms."`; exports[`should fail concurrent-test-deadlock.test.ts > concurrent-test-deadlock.test.ts 1`] = `"Error: Test timed out in 500ms."`; From 13ed041cdc3f9bab37d123801c1397d971113d2a Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Wed, 28 Aug 2024 16:55:40 +0900 Subject: [PATCH 09/10] chore: cleanup --- packages/vitest/LICENSE.md | 29 ----------------------------- 1 file changed, 29 deletions(-) diff --git a/packages/vitest/LICENSE.md b/packages/vitest/LICENSE.md index 9f826da7d2d2..d54def96b44e 100644 --- a/packages/vitest/LICENSE.md +++ b/packages/vitest/LICENSE.md @@ -1235,35 +1235,6 @@ Repository: sindresorhus/p-locate --------------------------------------- -## package-manager-detector -License: MIT -By: Anthony Fu -Repository: git+https://github.com/antfu-collective/package-manager-detector.git - -> MIT License -> -> Copyright (c) 2020-PRESENT Anthony Fu -> -> Permission is hereby granted, free of charge, to any person obtaining a copy -> of this software and associated documentation files (the "Software"), to deal -> in the Software without restriction, including without limitation the rights -> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -> copies of the Software, and to permit persons to whom the Software is -> furnished to do so, subject to the following conditions: -> -> The above copyright notice and this permission notice shall be included in all -> copies or substantial portions of the Software. -> -> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -> SOFTWARE. - ---------------------------------------- - ## picomatch License: MIT By: Jon Schlinkert From e7d5597f0463d62be81ca1566572db7ed6d226d2 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Fri, 30 Aug 2024 10:16:34 +0900 Subject: [PATCH 10/10] fix: no unhandled error --- packages/expect/src/jest-expect.ts | 4 ++-- test/cli/test/__snapshots__/fails.test.ts.snap | 2 -- test/core/test/jest-expect.test.ts | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/expect/src/jest-expect.ts b/packages/expect/src/jest-expect.ts index 0efba5b1af16..59e11c1968c8 100644 --- a/packages/expect/src/jest-expect.ts +++ b/packages/expect/src/jest-expect.ts @@ -1045,7 +1045,7 @@ export const JestChaiExpect: ChaiPlugin = (chai, utils) => { return result instanceof chai.Assertion ? proxy : result } - return async (...args: any[]) => { + return (...args: any[]) => { const promise = obj.then( (value: any) => { utils.flag(this, 'object', value) @@ -1107,7 +1107,7 @@ export const JestChaiExpect: ChaiPlugin = (chai, utils) => { return result instanceof chai.Assertion ? proxy : result } - return async (...args: any[]) => { + return (...args: any[]) => { const promise = wrapper.then( (value: any) => { const _error = new AssertionError( diff --git a/test/cli/test/__snapshots__/fails.test.ts.snap b/test/cli/test/__snapshots__/fails.test.ts.snap index 4cb5d4c511c1..391d0c063a0a 100644 --- a/test/cli/test/__snapshots__/fails.test.ts.snap +++ b/test/cli/test/__snapshots__/fails.test.ts.snap @@ -4,8 +4,6 @@ exports[`should fail .dot-folder/dot-test.test.ts > .dot-folder/dot-test.test.ts exports[`should fail async-assertion.test.ts > async-assertion.test.ts 1`] = ` "AssertionError: expected 'xx' to be 'zz' // Object.is equality -AssertionError: expected 'xx' to be 'yy' // Object.is equality -AssertionError: expected 'xx' to be 'zz' // Object.is equality AssertionError: expected 'xx' to be 'yy' // Object.is equality" `; diff --git a/test/core/test/jest-expect.test.ts b/test/core/test/jest-expect.test.ts index c057bb825c43..e001f2b3fe74 100644 --- a/test/core/test/jest-expect.test.ts +++ b/test/core/test/jest-expect.test.ts @@ -789,7 +789,7 @@ describe('async expect', () => { describe('promise auto queuing', () => { it.fails('fails', () => { - expect(() => new Promise((resolve, reject) => setTimeout(reject, 500))) + expect(new Promise((resolve, reject) => setTimeout(reject, 500))) .resolves .toBe('true') })