diff --git a/.changeset/cyan-pans-tan.md b/.changeset/cyan-pans-tan.md new file mode 100644 index 00000000000..180f3167321 --- /dev/null +++ b/.changeset/cyan-pans-tan.md @@ -0,0 +1,5 @@ +--- +"@effect/vitest": patch +--- + +Add fails method. diff --git a/.vscode/settings.json b/.vscode/settings.json index 393a3c822e7..a5826e77e08 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,6 +2,8 @@ "typescript.tsdk": "node_modules/typescript/lib", "typescript.preferences.importModuleSpecifier": "relative", "typescript.enablePromptUseWorkspaceTsdk": true, + "deno.enable": false, + "biome.enabled": false, "editor.formatOnSave": true, "eslint.format.enable": true, "[json]": { diff --git a/packages/vitest/README.md b/packages/vitest/README.md index 75c9d5b6dd4..cb875cc20bb 100644 --- a/packages/vitest/README.md +++ b/packages/vitest/README.md @@ -212,6 +212,30 @@ it.effect.only("test failure as Exit", () => ) ``` +## Expecting Tests to Fail + +When adding new failing tests, you might not be able to fix them right away. Instead of skipping them, you may want to assert it fails, so that when you fix them, you'll know and can re-enable them before it regresses. + +**Example** (Asserting one test fails) + +```ts +import { it } from "@effect/vitest" +import { Effect, Exit } from "effect" + +function divide(a: number, b: number): number { + if (b === 0) return Effect.fail("Cannot divide by zero") + return Effect.succeed(a / b) +} + +// Temporarily assert that the test for dividing by zero fails. +it.effect.fails("dividing by zero special cases", ({ expect }) => + Effect.gen(function* () { + const result = yield* Effect.exit(divide(4, 0)) + expect(result).toStrictEqual(0) + }) +) +``` + ## Logging By default, `it.effect` suppresses log output, which can be useful for keeping test results clean. However, if you want to enable logging during tests, you can use `it.live` or provide a custom logger to control the output. diff --git a/packages/vitest/src/index.ts b/packages/vitest/src/index.ts index 046ec9f3fb9..5103e3acaf1 100644 --- a/packages/vitest/src/index.ts +++ b/packages/vitest/src/index.ts @@ -56,6 +56,7 @@ export namespace Vitest { each: ( cases: ReadonlyArray ) => (name: string, self: TestFunction>, timeout?: number | V.TestOptions) => void + fails: Vitest.Test /** * @since 1.0.0 diff --git a/packages/vitest/src/internal.ts b/packages/vitest/src/internal.ts index ca179255235..64bda543ed5 100644 --- a/packages/vitest/src/internal.ts +++ b/packages/vitest/src/internal.ts @@ -99,6 +99,9 @@ const makeTester = ( (args, ctx) => run(ctx, [args], self) as any ) + const fails: Vitest.Vitest.Tester["fails"] = (name, self, timeout) => + V.it.fails(name, (ctx) => run(ctx, [ctx], self), timeout) + const prop: Vitest.Vitest.Tester["prop"] = (name, arbitraries, self, timeout) => { if (Array.isArray(arbitraries)) { const arbs = arbitraries.map((arbitrary) => Schema.isSchema(arbitrary) ? Arbitrary.make(arbitrary) : arbitrary) @@ -136,7 +139,7 @@ const makeTester = ( ) } - return Object.assign(f, { skip, skipIf, runIf, only, each, prop }) + return Object.assign(f, { skip, skipIf, runIf, only, each, fails, prop }) } export const prop: Vitest.Vitest.Methods["prop"] = (name, arbitraries, self, timeout) => { diff --git a/packages/vitest/test/index.test.ts b/packages/vitest/test/index.test.ts index 40c72e7f20e..fa3d4f19265 100644 --- a/packages/vitest/test/index.test.ts +++ b/packages/vitest/test/index.test.ts @@ -68,7 +68,7 @@ it.effect.runIf(false)("effect runIf (false)", () => Effect.die("not run anyway" // The following test is expected to fail because it simulates a test timeout. // Be aware that eventual "failure" of the test is only logged out. -it.scopedLive("interrupts on timeout", (ctx) => +it.scopedLive.fails("interrupts on timeout", (ctx) => Effect.gen(function*() { let acquired = false @@ -84,7 +84,7 @@ it.scopedLive("interrupts on timeout", (ctx) => () => Effect.sync(() => acquired = false) ) yield* Effect.sleep(1000) - }), { timeout: 100, fails: true }) + }), 1) class Foo extends Context.Tag("Foo")() { static Live = Layer.succeed(Foo, "foo")