Something went wrong. Try again.
[READ-ONLY] Mirror of https://github.com/vitest-dev/vitest. Next generation testing framework powered by Vite. vitest.dev
test testing-tools vite
Something went wrong. Try again.
TypeScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140import type { Test } from '../../vitest/src/runtime/runner/types'import type { Assertion } from './types'import { processError } from '@vitest/utils/error'import { noop } from '@vitest/utils/helpers'
export function createAssertionMessage( util: Chai.ChaiUtils, assertion: Chai.Assertion, hasArgs: boolean,) { const soft = util.flag(assertion, 'soft') ? '.soft' : '' const not = util.flag(assertion, 'negate') ? 'not.' : '' const name = `${util.flag(assertion, '_name')}(${hasArgs ? 'expected' : ''})` const promiseName = util.flag(assertion, 'promise') const promise = promiseName ? `.${promiseName}` : '' return `expect${soft}(actual)${promise}.${not}${name}`}
export function recordAsyncExpect( _test: any, promise: Promise<any>, assertion: string, error: Error, isSoft?: boolean,): Promise<any> { const test = _test as Test | undefined // record promise for test, that resolves before test ends if (test && promise instanceof Promise) { // if promise is explicitly awaited, remove it from the list promise = promise.finally(() => { if (!test.promises) { return } const index = test.promises.indexOf(promise) if (index !== -1) { test.promises.splice(index, 1) } })
// record promise if (!test.promises) { test.promises = [] } // setup `expect.soft` handler here instead of `wrapAssertion` // to avoid double error tracking while keeping non-await promise detection. if (isSoft) { promise = promise.then(noop, (err) => { handleTestError(test, err) }) } test.promises.push(promise)
let resolved = false test.onFinished ??= [] test.onFinished.push(() => { if (!resolved) { const awaitError = new Error( `Promise returned by \`${assertion}\` was not awaited. This assertion is asynchronous and must be awaited; otherwise, it is not guaranteed to complete before the test finishes:\n\nawait ${assertion}\n`, ) if (error.stack) { awaitError.stack = error.stack.replace(error.message, awaitError.message) } throw awaitError } })
return { then(onFulfilled, onRejected) { resolved = true return promise.then(onFulfilled, onRejected) }, catch(onRejected) { resolved = true return promise.catch(onRejected) }, finally(onFinally) { resolved = true return promise.finally(onFinally) }, [Symbol.toStringTag]: 'Promise', } satisfies Promise<any> }
return promise}
function handleTestError(test: Test, err: unknown) { test.result ||= { state: 'fail' } test.result.state = 'fail' test.result.errors ||= [] test.result.errors.push(processError(err))}
/** wrap assertion function to support `expect.soft` and provide assertion name as `_name` */export function wrapAssertion( utils: Chai.ChaiUtils, name: string, fn: (this: Chai.AssertionStatic & Assertion, ...args: any[]) => void | PromiseLike<void>,) { return function ( this: Chai.AssertionStatic & Assertion, ...args: any[] ): void | PromiseLike<void> { // private if (name !== 'withTest') { utils.flag(this, '_name', name) }
if (!utils.flag(this, 'soft')) { // avoid WebKit's proper tail call to preserve stacktrace offset for inline snapshot // https://webkit.org/blog/6240/ecmascript-6-proper-tail-calls-in-webkit try { return fn.apply(this, args) } finally { // no lint } }
const test: Test = utils.flag(this, 'vitest-test')
if (!test) { throw new Error('expect.soft() can only be used inside a test') }
try { const result = fn.apply(this, args)
if (result && typeof result === 'object' && typeof result.then === 'function') { return result.then(noop, (err) => { handleTestError(test, err) }) }
return result } catch (err) { handleTestError(test, err) } }}