From 62f12d82443c4cfc2ae1937738f00aada4655669 Mon Sep 17 00:00:00 2001 From: Chris Wilkinson Date: Sun, 22 Dec 2024 21:02:44 +0000 Subject: [PATCH] Support native arbitraries in @effect/vitest (#4173) --- .changeset/shiny-timers-agree.md | 5 +++++ packages/vitest/src/index.ts | 26 ++++++++++++++++---------- packages/vitest/src/internal.ts | 21 +++++++++++---------- packages/vitest/test/index.test.ts | 22 ++++++++++++++++++---- 4 files changed, 50 insertions(+), 24 deletions(-) create mode 100644 .changeset/shiny-timers-agree.md diff --git a/.changeset/shiny-timers-agree.md b/.changeset/shiny-timers-agree.md new file mode 100644 index 00000000000..705c5480235 --- /dev/null +++ b/.changeset/shiny-timers-agree.md @@ -0,0 +1,5 @@ +--- +"@effect/vitest": minor +--- + +Support native arbitraries in @effect/vitest diff --git a/packages/vitest/src/index.ts b/packages/vitest/src/index.ts index dbed927fddc..19aa02735ab 100644 --- a/packages/vitest/src/index.ts +++ b/packages/vitest/src/index.ts @@ -3,6 +3,7 @@ */ import type * as Duration from "effect/Duration" import type * as Effect from "effect/Effect" +import type * as FastCheck from "effect/FastCheck" import type * as Layer from "effect/Layer" import type * as Schema from "effect/Schema" import type * as Scope from "effect/Scope" @@ -40,7 +41,9 @@ export namespace Vitest { /** * @since 1.0.0 */ - export type SchemaObj = Array | { [K in string]: Schema.Schema.Any } + export type Arbitraries = + | Array> + | { [K in string]: Schema.Schema.Any | FastCheck.Arbitrary } /** * @since 1.0.0 @@ -57,14 +60,17 @@ export namespace Vitest { /** * @since 1.0.0 */ - prop: ( + prop: ( name: string, - schemas: S, + arbitraries: Arbs, self: TestFunction< A, E, R, - [{ [K in keyof S]: Schema.Schema.Type }, V.TaskContext> & V.TestContext] + [ + { [K in keyof Arbs]: Arbs[K] extends FastCheck.Arbitrary ? T : Schema.Schema.Type }, + V.TaskContext> & V.TestContext + ] >, timeout?: number | V.TestOptions ) => void @@ -92,11 +98,11 @@ export namespace Vitest { /** * @since 1.0.0 */ - readonly prop: ( + readonly prop: ( name: string, - schemas: S, + arbitraries: Arbs, self: ( - schemas: { [K in keyof S]: Schema.Schema.Type }, + properties: { [K in keyof Arbs]: K extends FastCheck.Arbitrary ? T : Schema.Schema.Type }, ctx: V.TaskContext> & V.TestContext ) => void, timeout?: number | V.TestOptions @@ -188,11 +194,11 @@ export const flakyTest: ( /** * @since 1.0.0 */ -export const prop: ( +export const prop: ( name: string, - schemas: S, + arbitraries: Arbs, self: ( - schemas: { [K in keyof S]: Schema.Schema.Type }, + properties: { [K in keyof Arbs]: K extends FastCheck.Arbitrary ? T : Schema.Schema.Type }, ctx: V.TaskContext> & V.TestContext ) => void, timeout?: number | V.TestOptions diff --git a/packages/vitest/src/internal.ts b/packages/vitest/src/internal.ts index c12b4b657b9..9ad39a88812 100644 --- a/packages/vitest/src/internal.ts +++ b/packages/vitest/src/internal.ts @@ -13,6 +13,7 @@ import { flow, identity, pipe } from "effect/Function" import * as Layer from "effect/Layer" import * as Logger from "effect/Logger" import * as Schedule from "effect/Schedule" +import * as Schema from "effect/Schema" import * as Scope from "effect/Scope" import * as TestEnvironment from "effect/TestContext" import type * as TestServices from "effect/TestServices" @@ -97,9 +98,9 @@ const makeTester = ( (args, ctx) => run(ctx, [args], self) as any ) - const prop: Vitest.Vitest.Tester["prop"] = (name, schemaObj, self, timeout) => { - if (Array.isArray(schemaObj)) { - const arbs = schemaObj.map((schema) => Arbitrary.make(schema)) + 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) return V.it( name, // @ts-ignore @@ -109,8 +110,8 @@ const makeTester = ( } const arbs = fc.record( - Object.keys(schemaObj).reduce(function(result, key) { - result[key] = Arbitrary.make(schemaObj[key]) + Object.keys(arbitraries).reduce(function(result, key) { + result[key] = Schema.isSchema(arbitraries[key]) ? Arbitrary.make(arbitraries[key]) : arbitraries[key] return result }, {} as Record>) ) @@ -126,9 +127,9 @@ const makeTester = ( return Object.assign(f, { skip, skipIf, runIf, only, each, prop }) } -export const prop: Vitest.Vitest.Methods["prop"] = (name, schemaObj, self, timeout) => { - if (Array.isArray(schemaObj)) { - const arbs = schemaObj.map((schema) => Arbitrary.make(schema)) +export const prop: Vitest.Vitest.Methods["prop"] = (name, arbitraries, self, timeout) => { + if (Array.isArray(arbitraries)) { + const arbs = arbitraries.map((arbitrary) => Schema.isSchema(arbitrary) ? Arbitrary.make(arbitrary) : arbitrary) return V.it( name, // @ts-ignore @@ -138,8 +139,8 @@ export const prop: Vitest.Vitest.Methods["prop"] = (name, schemaObj, self, timeo } const arbs = fc.record( - Object.keys(schemaObj).reduce(function(result, key) { - result[key] = Arbitrary.make(schemaObj[key]) + Object.keys(arbitraries).reduce(function(result, key) { + result[key] = Schema.isSchema(arbitraries[key]) ? Arbitrary.make(arbitraries[key]) : arbitraries[key] return result }, {} as Record>) ) diff --git a/packages/vitest/test/index.test.ts b/packages/vitest/test/index.test.ts index 4c58da1defd..21e956abb56 100644 --- a/packages/vitest/test/index.test.ts +++ b/packages/vitest/test/index.test.ts @@ -1,5 +1,5 @@ import { afterAll, describe, expect, it, layer } from "@effect/vitest" -import { Context, Effect, Layer, Schema } from "effect" +import { Context, Effect, FastCheck, Layer, Schema } from "effect" it.live( "live %s", @@ -160,17 +160,31 @@ layer(Foo.Live)("layer", (it) => { const realNumber = Schema.Finite.pipe(Schema.nonNaN()) -it.prop("symmetry", [realNumber, realNumber], ([a, b]) => a + b === b + a) +it.prop("symmetry", [realNumber, FastCheck.integer()], ([a, b]) => a + b === b + a) -it.effect.prop("symmetry", [realNumber, realNumber], ([a, b]) => +it.prop( + "symmetry with object", + { a: realNumber, b: FastCheck.integer() }, + ({ a, b }) => a + b === b + a +) + +it.effect.prop("symmetry", [realNumber, FastCheck.integer()], ([a, b]) => Effect.gen(function*() { yield* Effect.void + + return a + b === b + a + })) + +it.effect.prop("symmetry with object", { a: realNumber, b: FastCheck.integer() }, ({ a, b }) => + Effect.gen(function*() { + yield* Effect.void + return a + b === b + a })) it.scoped.prop( "should detect the substring", - { a: Schema.String, b: Schema.String, c: Schema.String }, + { a: Schema.String, b: Schema.String, c: FastCheck.string() }, ({ a, b, c }) => Effect.gen(function*() { yield* Effect.scope