From 8e385bb09eb1585a8410bf08c8e54596b08cc119 Mon Sep 17 00:00:00 2001 From: Han Date: Fri, 16 Jun 2023 17:53:49 +0800 Subject: [PATCH] feat(expect): support `expect.unreachable` (#3556) Co-authored-by: Vladimir --- docs/api/expect.md | 47 ++++++++++++++++++- packages/expect/src/types.ts | 1 + .../vitest/src/integrations/chai/index.ts | 4 ++ .../fails/fixtures/expect-unreachable.test.ts | 5 ++ .../test/__snapshots__/runner.test.ts.snap | 2 + 5 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 test/fails/fixtures/expect-unreachable.test.ts diff --git a/docs/api/expect.md b/docs/api/expect.md index d2a83597a459..62aae90bcd23 100644 --- a/docs/api/expect.md +++ b/docs/api/expect.md @@ -42,7 +42,7 @@ type Awaitable = T | PromiseLike }) // At the end of the test, the above errors will be output. ``` - + It can also be used with `expect`. if `expect` assertion fails, the test will be terminated and all errors will be displayed. ```ts @@ -54,7 +54,7 @@ type Awaitable = T | PromiseLike expect.soft(1 + 2).toBe(4) // do not run }) ``` - + ::: warning `expect.soft` can only be used inside the [`test`](/api/#test) function. ::: @@ -1136,6 +1136,49 @@ If the value in the error message is too truncated, you can increase [chaiConfig }) ``` +## expect.unreachable + +- **Type:** `(message?: string) => never` + + This method is used to asserting that a line should never be reached. + + For example, if we want to test that `build()` throws due to receiving directories having no `src` folder, and also handle each error separately, we could do this: + + ```ts + import { expect, test } from 'vitest' + + async function build(dir) { + if (dir.includes('no-src')) + throw new Error(`${dir}/src does not exist`) + } + + const errorDirs = [ + 'no-src-folder', + // ... + ] + + test.each(errorDirs)('build fails with "%s"', async (dir) => { + try { + await build(dir) + expect.unreachable('Should not pass build') + } + catch (err: any) { + expect(err).toBeInstanceOf(Error) + expect(err.stack).toContain('build') + + switch (dir) { + case 'no-src-folder': + expect(err.message).toBe(`${dir}/src does not exist`) + break + default: + // to exhaust all error tests + expect.unreachable('All error test must be handled') + break + } + } + }) + ``` + ## expect.anything diff --git a/packages/expect/src/types.ts b/packages/expect/src/types.ts index b4aa42789e55..f530c8b80f8c 100644 --- a/packages/expect/src/types.ts +++ b/packages/expect/src/types.ts @@ -80,6 +80,7 @@ export type MatchersObject = Record(actual: T, message?: string): Assertion + unreachable(message?: string): never soft(actual: T, message?: string): Assertion extend(expects: MatchersObject): void assertions(expected: number): void diff --git a/packages/vitest/src/integrations/chai/index.ts b/packages/vitest/src/integrations/chai/index.ts index 941374d19e97..93d3c6d8094a 100644 --- a/packages/vitest/src/integrations/chai/index.ts +++ b/packages/vitest/src/integrations/chai/index.ts @@ -53,6 +53,10 @@ export function createExpect(test?: Test) { return assert } + expect.unreachable = (message?: string) => { + chai.assert.fail(`expected${message ? ` "${message}" ` : ' '}not to be reached`) + } + function assertions(expected: number) { const errorGen = () => new Error(`expected number of assertions to be ${expected}, but got ${expect.getState().assertionCalls}`) if (Error.captureStackTrace) diff --git a/test/fails/fixtures/expect-unreachable.test.ts b/test/fails/fixtures/expect-unreachable.test.ts new file mode 100644 index 000000000000..ecb8abf9d3a2 --- /dev/null +++ b/test/fails/fixtures/expect-unreachable.test.ts @@ -0,0 +1,5 @@ +import { expect, test } from 'vitest' + +test('', () => { + expect.unreachable('hi') +}) diff --git a/test/fails/test/__snapshots__/runner.test.ts.snap b/test/fails/test/__snapshots__/runner.test.ts.snap index 0dca905eb9e7..68475f764dcb 100644 --- a/test/fails/test/__snapshots__/runner.test.ts.snap +++ b/test/fails/test/__snapshots__/runner.test.ts.snap @@ -10,6 +10,8 @@ exports[`should fail expect.test.ts > expect.test.ts 1`] = `"AssertionError: exp exports[`should fail expect-soft.test.ts > expect-soft.test.ts 1`] = `"Error: expect.soft() can only be used inside a test"`; +exports[`should fail expect-unreachable.test.ts > expect-unreachable.test.ts 1`] = `"AssertionError: expected \\"hi\\" not to be reached"`; + exports[`should fail hook-timeout.test.ts > hook-timeout.test.ts 1`] = `"Error: Hook timed out in 10ms."`; exports[`should fail hooks-called.test.ts > hooks-called.test.ts 1`] = `