Skip to content

Commit d05d663

Browse files
lishaducktim-smart
andauthored
feat: add it.*.fails method to @effect/vitest (#3973)
Co-authored-by: Tim <hello@timsmart.co>
1 parent 8cd7319 commit d05d663

File tree

6 files changed

+38
-3
lines changed

6 files changed

+38
-3
lines changed

.changeset/cyan-pans-tan.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@effect/vitest": patch
3+
---
4+
5+
Add it.*.fails method to @effect/vitest

.vscode/settings.json

+2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
"typescript.tsdk": "node_modules/typescript/lib",
33
"typescript.preferences.importModuleSpecifier": "relative",
44
"typescript.enablePromptUseWorkspaceTsdk": true,
5+
"deno.enable": false,
6+
"biome.enabled": false,
57
"editor.formatOnSave": true,
68
"eslint.format.enable": true,
79
"[json]": {

packages/vitest/README.md

+24
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,30 @@ it.effect.only("test failure as Exit", () =>
212212
)
213213
```
214214

215+
## Expecting Tests to Fail
216+
217+
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.
218+
219+
**Example** (Asserting one test fails)
220+
221+
```ts
222+
import { it } from "@effect/vitest"
223+
import { Effect, Exit } from "effect"
224+
225+
function divide(a: number, b: number): number {
226+
if (b === 0) return Effect.fail("Cannot divide by zero")
227+
return Effect.succeed(a / b)
228+
}
229+
230+
// Temporarily assert that the test for dividing by zero fails.
231+
it.effect.fails("dividing by zero special cases", ({ expect }) =>
232+
Effect.gen(function* () {
233+
const result = yield* Effect.exit(divide(4, 0))
234+
expect(result).toStrictEqual(0)
235+
})
236+
)
237+
```
238+
215239
## Logging
216240

217241
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.

packages/vitest/src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ export namespace Vitest {
5656
each: <T>(
5757
cases: ReadonlyArray<T>
5858
) => <A, E>(name: string, self: TestFunction<A, E, R, Array<T>>, timeout?: number | V.TestOptions) => void
59+
fails: Vitest.Test<R>
5960

6061
/**
6162
* @since 1.0.0

packages/vitest/src/internal.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,9 @@ const makeTester = <R>(
9999
(args, ctx) => run(ctx, [args], self) as any
100100
)
101101

102+
const fails: Vitest.Vitest.Tester<R>["fails"] = (name, self, timeout) =>
103+
V.it.fails(name, (ctx) => run(ctx, [ctx], self), timeout)
104+
102105
const prop: Vitest.Vitest.Tester<R>["prop"] = (name, arbitraries, self, timeout) => {
103106
if (Array.isArray(arbitraries)) {
104107
const arbs = arbitraries.map((arbitrary) => Schema.isSchema(arbitrary) ? Arbitrary.make(arbitrary) : arbitrary)
@@ -136,7 +139,7 @@ const makeTester = <R>(
136139
)
137140
}
138141

139-
return Object.assign(f, { skip, skipIf, runIf, only, each, prop })
142+
return Object.assign(f, { skip, skipIf, runIf, only, each, fails, prop })
140143
}
141144

142145
export const prop: Vitest.Vitest.Methods["prop"] = (name, arbitraries, self, timeout) => {

packages/vitest/test/index.test.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ it.effect.runIf(false)("effect runIf (false)", () => Effect.die("not run anyway"
6868

6969
// The following test is expected to fail because it simulates a test timeout.
7070
// Be aware that eventual "failure" of the test is only logged out.
71-
it.scopedLive("interrupts on timeout", (ctx) =>
71+
it.scopedLive.fails("interrupts on timeout", (ctx) =>
7272
Effect.gen(function*() {
7373
let acquired = false
7474

@@ -84,7 +84,7 @@ it.scopedLive("interrupts on timeout", (ctx) =>
8484
() => Effect.sync(() => acquired = false)
8585
)
8686
yield* Effect.sleep(1000)
87-
}), { timeout: 100, fails: true })
87+
}), 1)
8888

8989
class Foo extends Context.Tag("Foo")<Foo, "foo">() {
9090
static Live = Layer.succeed(Foo, "foo")

0 commit comments

Comments
 (0)