bun-effect.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Show hidden characters
/**
-
Minimal Effect test wrapper for Bun's native test runner.
-
This provides
it.effect()andit.scoped()that work withbun test, bun's native test runner, -
similar to
@effect/vitest. When the official@effect/bun-testpackage -
is released, replace this with that package.
-
@see https://github.com/Effect-TS/effect/pull/5973
-
@example
-
-
import { describe, expect, it } from "./bun-effect"
-
import { Effect, Layer } from "effect"
-
describe("my test", () => {
-
it.effect("runs an effect", () =>
-
Effect.gen(function* () { -
const result = yield* someEffect -
expect(result).toBe(expected) -
}).pipe(Effect.provide(TestLayer)) -
)
-
it.scoped("runs a scoped effect", () =>
-
Effect.gen(function* () { -
const resource = yield* acquireResource -
expect(resource).toBeDefined() -
}) -
)
-
it.effect.skip("skipped test", () => Effect.succeed(1))
-
it.effect.only("only this test", () => Effect.succeed(1))
-
})
-
*/
import { afterAll, beforeAll, describe, expect, test } from "bun:test"
import { Effect, TestServices } from "effect"
import type { Scope } from "effect"
export { afterAll, beforeAll, describe, expect }
type TestOptions = { timeout?: number }
const runTest = <E, A>(
effect: Effect.Effect<A, E, TestServices.TestServices>,
) => Effect.runPromise(effect.pipe(Effect.provide(TestServices.liveServices)))
const runTestScoped = <E, A>(
effect: Effect.Effect<A, E, TestServices.TestServices | Scope.Scope>,
) =>
Effect.runPromise(
effect.pipe(Effect.scoped, Effect.provide(TestServices.liveServices)),
)
type EffectFn<A, E, R> = () => Effect.Effect<A, E, R>
type EffectTester = {
<A, E>(
name: string,
fn: EffectFn<A, E, TestServices.TestServices>,
options?: number | TestOptions,
): void
skip: <A, E>(
name: string,
fn: EffectFn<A, E, TestServices.TestServices>,
options?: number | TestOptions,
) => void
only: <A, E>(
name: string,
fn: EffectFn<A, E, TestServices.TestServices>,
options?: number | TestOptions,
) => void
}
type ScopedTester = {
<A, E>(
name: string,
fn: EffectFn<A, E, TestServices.TestServices | Scope.Scope>,
options?: number | TestOptions,
): void
skip: <A, E>(
name: string,
fn: EffectFn<A, E, TestServices.TestServices | Scope.Scope>,
options?: number | TestOptions,
) => void
only: <A, E>(
name: string,
fn: EffectFn<A, E, TestServices.TestServices | Scope.Scope>,
options?: number | TestOptions,
) => void
}
const makeEffectTest =
(runner: typeof test) =>
<A, E>(
name: string,
fn: EffectFn<A, E, TestServices.TestServices>,
options?: number | TestOptions,
) => {
const timeout = typeof options === "number" ? options : options?.timeout
runner(name, () => runTest(fn()), timeout ? { timeout } : undefined)
}
const makeScopedTest =
(runner: typeof test) =>
<A, E>(
name: string,
fn: EffectFn<A, E, TestServices.TestServices | Scope.Scope>,
options?: number | TestOptions,
) => {
const timeout = typeof options === "number" ? options : options?.timeout
runner(name, () => runTestScoped(fn()), timeout ? { timeout } : undefined)
}
export const effect: EffectTester = Object.assign(makeEffectTest(test), {
skip: makeEffectTest(test.skip),
only: makeEffectTest(test.only),
})
export const scoped: ScopedTester = Object.assign(makeScopedTest(test), {
skip: makeScopedTest(test.skip),
only: makeScopedTest(test.only),
})
export const it = Object.assign(test, { effect, scoped })