diff --git a/tests/coercion/schema/intersectAsync.test.ts b/tests/coercion/schema/intersectAsync.test.ts new file mode 100644 index 0000000..3b409d3 --- /dev/null +++ b/tests/coercion/schema/intersectAsync.test.ts @@ -0,0 +1,113 @@ +import { describe, expect, test } from "vitest"; +import { + literal, + objectAsync, + string, + intersectAsync, + checkAsync, + pipeAsync, +} from "valibot"; +import { parseWithValibot } from "../../../parse"; +import { createFormData } from "../../helpers/FormData"; + +describe("intersectAsync", () => { + test("should pass only intersect values", async () => { + const schema1 = objectAsync({ + intersect: intersectAsync([string(), literal("test")]), + }); + const input1 = createFormData("intersect", "test"); + const output1 = await parseWithValibot(input1, { schema: schema1 }); + expect(output1).toMatchObject({ + status: "success", + value: { intersect: "test" }, + }); + + const errorInput1 = createFormData("intersect", "foo"); + const errorOutput1 = await parseWithValibot(errorInput1, { + schema: schema1, + }); + expect(errorOutput1).toMatchObject({ + error: { + intersect: ['Invalid type: Expected "test" but received "foo"'], + }, + }); + const errorInput2 = createFormData("intersect", ""); + const errorOutput2 = await parseWithValibot(errorInput2, { + schema: schema1, + }); + expect(errorOutput2).toMatchObject({ + error: { + intersect: [ + "Invalid type: Expected string but received undefined", + 'Invalid type: Expected "test" but received undefined', + ], + }, + }); + + const schema2 = intersectAsync([ + objectAsync({ foo: string() }), + objectAsync({ bar: string() }), + ]); + const input2 = createFormData("foo", "test"); + input2.append("bar", "test"); + const output2 = await parseWithValibot(input2, { schema: schema2 }); + expect(output2).toMatchObject({ + status: "success", + value: { foo: "test", bar: "test" }, + }); + const errorInput3 = createFormData("foo", "test"); + const errorOutput3 = await parseWithValibot(errorInput3, { + schema: schema2, + }); + expect(errorOutput3).toMatchObject({ + error: { bar: ["Invalid type: Expected string but received undefined"] }, + }); + const errorInput4 = createFormData("bar", "test"); + const errorOutput4 = await parseWithValibot(errorInput4, { + schema: schema2, + }); + expect(errorOutput4).toMatchObject({ + error: { foo: ["Invalid type: Expected string but received undefined"] }, + }); + }); + + test("should pass only intersect values with pipe", async () => { + const schema = objectAsync({ + intersect: pipeAsync( + intersectAsync([string()]), + checkAsync( + async (value) => value === "test", + "intersect must be equal to test", + ), + ), + }); + const input1 = createFormData("intersect", "test"); + const output1 = await parseWithValibot(input1, { schema }); + expect(output1).toMatchObject({ + status: "success", + value: { intersect: "test" }, + }); + + const errorInput1 = createFormData("intersect", "foo"); + const errorOutput1 = await parseWithValibot(errorInput1, { schema }); + expect(errorOutput1).toMatchObject({ + error: { + intersect: ["intersect must be equal to test"], + }, + }); + }); + + test("should throw only first issue", async () => { + const schema = objectAsync({ + intersect: intersectAsync([string(), literal("test")]), + }); + const errorInput = createFormData("intersect", ""); + const info = { abortEarly: true }; + const errorOutput = await parseWithValibot(errorInput, { schema, info }); + expect(errorOutput).toMatchObject({ + error: { + intersect: ["Invalid type: Expected string but received undefined"], + }, + }); + }); +}); diff --git a/tests/coercion/schema/nonNullishAsync.test.ts b/tests/coercion/schema/nonNullishAsync.test.ts new file mode 100644 index 0000000..999fd60 --- /dev/null +++ b/tests/coercion/schema/nonNullishAsync.test.ts @@ -0,0 +1,105 @@ +import { describe, expect, test } from "vitest"; +import { parseWithValibot } from "../../../parse"; +import { + objectAsync, + number, + optionalAsync, + nonNullishAsync, + unionAsync, + undefined_, + checkAsync, + pipeAsync, +} from "valibot"; +import { createFormData } from "../../helpers/FormData"; + +describe("nonOptionalAsync", () => { + test("should not pass undefined", async () => { + const schema1 = objectAsync({ + item: nonNullishAsync(optionalAsync(number())), + }); + const input1 = createFormData("item", "1"); + const output1 = await parseWithValibot(input1, { schema: schema1 }); + expect(output1).toMatchObject({ status: "success", value: { item: 1 } }); + expect( + await parseWithValibot(createFormData("item", "non Number"), { + schema: schema1, + }), + ).toMatchObject({ + error: { item: ["Invalid type: Expected number but received NaN"] }, + }); + expect( + await parseWithValibot(createFormData("item2", "non Param"), { + schema: schema1, + }), + ).toMatchObject({ + error: { + item: [ + "Invalid type: Expected !null & !undefined but received undefined", + ], + }, + }); + + const schema2 = objectAsync({ + item: nonNullishAsync(unionAsync([number(), undefined_()])), + }); + const output2 = await parseWithValibot(input1, { schema: schema2 }); + expect(output2).toMatchObject({ status: "success", value: { item: 1 } }); + expect( + await parseWithValibot(createFormData("item", "non Number"), { + schema: schema2, + }), + ).toMatchObject({ + error: { + item: [ + 'Invalid type: Expected number | undefined but received "non Number"', + ], + }, + }); + expect( + await parseWithValibot(createFormData("item2", "non Param"), { + schema: schema2, + }), + ).toMatchObject({ + error: { + item: [ + "Invalid type: Expected !null & !undefined but received undefined", + ], + }, + }); + }); + + test("should pass nonNullish with pipe", async () => { + const schema = objectAsync({ + age: pipeAsync( + nonNullishAsync(optionalAsync(number())), + checkAsync((value) => value > 0, "age must be greater than 0"), + ), + }); + + const output1 = await parseWithValibot(createFormData("age", ""), { + schema, + }); + expect(output1).toMatchObject({ + error: { + age: [ + "Invalid type: Expected !null & !undefined but received undefined", + ], + }, + }); + + const output2 = await parseWithValibot(createFormData("age", "20"), { + schema, + }); + expect(output2).toMatchObject({ + status: "success", + value: { age: 20 }, + }); + + const errorOutput = await parseWithValibot(createFormData("age", "0"), { + schema, + }); + expect(errorOutput).toMatchObject({ + error: { age: ["age must be greater than 0"] }, + }); + }); +}); diff --git a/tests/coercion/schema/nonOptionalAsync.test.ts b/tests/coercion/schema/nonOptionalAsync.test.ts new file mode 100644 index 0000000..72552f9 --- /dev/null +++ b/tests/coercion/schema/nonOptionalAsync.test.ts @@ -0,0 +1,96 @@ +import { describe, expect, test } from "vitest"; +import { parseWithValibot } from "../../../parse"; +import { + objectAsync, + number, + optionalAsync, + nonOptionalAsync, + unionAsync, + undefined_, + checkAsync, + pipeAsync, +} from "valibot"; +import { createFormData } from "../../helpers/FormData"; + +describe("nonOptionalAsync", () => { + test("should not pass undefined", async () => { + const schema1 = objectAsync({ + item: nonOptionalAsync(optionalAsync(number())), + }); + const input1 = createFormData("item", "1"); + const output1 = await parseWithValibot(input1, { schema: schema1 }); + expect(output1).toMatchObject({ status: "success", value: { item: 1 } }); + expect( + await parseWithValibot(createFormData("item", "non Number"), { + schema: schema1, + }), + ).toMatchObject({ + error: { item: ["Invalid type: Expected number but received NaN"] }, + }); + expect( + await parseWithValibot(createFormData("item2", "non Param"), { + schema: schema1, + }), + ).toMatchObject({ + error: { + item: ["Invalid type: Expected !undefined but received undefined"], + }, + }); + + const schema2 = objectAsync({ + item: nonOptionalAsync(unionAsync([number(), undefined_()])), + }); + const output2 = await parseWithValibot(input1, { schema: schema2 }); + expect(output2).toMatchObject({ status: "success", value: { item: 1 } }); + expect( + await parseWithValibot(createFormData("item", "non Number"), { + schema: schema2, + }), + ).toMatchObject({ + error: { + item: [ + 'Invalid type: Expected number | undefined but received "non Number"', + ], + }, + }); + expect( + await parseWithValibot(createFormData("item2", "non Param"), { + schema: schema2, + }), + ).toMatchObject({ + error: { + item: ["Invalid type: Expected !undefined but received undefined"], + }, + }); + }); + + test("should pass nonOptional with pipe", async () => { + const schema = objectAsync({ + age: pipeAsync( + nonOptionalAsync(optionalAsync(number())), + checkAsync(async (value) => value > 0, "age must be greater than 0"), + ), + }); + + const output1 = await parseWithValibot(createFormData("age", "1"), { + schema, + }); + expect(output1).toMatchObject({ status: "success", value: { age: 1 } }); + + const output2 = await parseWithValibot(createFormData("age", "0"), { + schema, + }); + expect(output2).toMatchObject({ + error: { age: ["age must be greater than 0"] }, + }); + + const output3 = await parseWithValibot(createFormData("age", ""), { + schema, + }); + expect(output3).toMatchObject({ + error: { + age: ["Invalid type: Expected !undefined but received undefined"], + }, + }); + }); +}); diff --git a/tests/coercion/schema/nullable.ts b/tests/coercion/schema/nullable.test.ts similarity index 74% rename from tests/coercion/schema/nullable.ts rename to tests/coercion/schema/nullable.test.ts index 61b0483..c2c2a13 100644 --- a/tests/coercion/schema/nullable.ts +++ b/tests/coercion/schema/nullable.test.ts @@ -1,26 +1,30 @@ import { describe, expect, test } from "vitest"; -import { string, number, object, nullable, check, pipe } from "valibot"; +import { + string, + number, + object, + nullable, + optional, + check, + pipe, +} from "valibot"; import { parseWithValibot } from "../../../parse"; import { createFormData } from "../../helpers/FormData"; describe("nullable", () => { test("should pass also undefined", () => { const schema = object({ age: nullable(number()) }); - const output = parseWithValibot(createFormData("age", ""), { schema }); + const output = parseWithValibot(createFormData("age", "20"), { schema }); expect(output).toMatchObject({ - status: "success", - value: { age: undefined }, - }); - expect( - parseWithValibot(createFormData("age", "20"), { schema }), - ).toMatchObject({ status: "success", value: { age: 20 }, }); expect( parseWithValibot(createFormData("age", "non number"), { schema }), - ).toMatchObject({ error: { age: ["Invalid type"] } }); + ).toMatchObject({ + error: { age: ["Invalid type: Expected number but received NaN"] }, + }); }); test("should pass nullable with pipe", () => { @@ -34,12 +38,6 @@ describe("nullable", () => { ), }); - const output1 = parseWithValibot(createFormData("age", ""), { schema }); - expect(output1).toMatchObject({ - status: "success", - value: { age: undefined }, - }); - const output2 = parseWithValibot(createFormData("age", "20"), { schema }); expect(output2).toMatchObject({ status: "success", @@ -57,7 +55,9 @@ describe("nullable", () => { test("should use default if required", () => { const default_ = "default"; - const schema1 = object({ name: nullable(string(), default_) }); + const schema1 = object({ + name: optional(nullable(string(), default_), null), + }); const output1 = parseWithValibot(createFormData("name", ""), { schema: schema1, }); diff --git a/tests/coercion/schema/nullableAsync.test.ts b/tests/coercion/schema/nullableAsync.test.ts new file mode 100644 index 0000000..a4b33a8 --- /dev/null +++ b/tests/coercion/schema/nullableAsync.test.ts @@ -0,0 +1,84 @@ +import { describe, expect, test } from "vitest"; +import { + string, + number, + objectAsync, + nullableAsync, + optionalAsync, + checkAsync, + pipeAsync, +} from "valibot"; +import { parseWithValibot } from "../../../parse"; +import { createFormData } from "../../helpers/FormData"; + +describe("nullableAsync", () => { + test("should pass also undefined", async () => { + const schema = objectAsync({ age: nullableAsync(number()) }); + const output = await parseWithValibot(createFormData("age", "20"), { + schema, + }); + + expect(output).toMatchObject({ + status: "success", + value: { age: 20 }, + }); + expect( + await parseWithValibot(createFormData("age", "non number"), { schema }), + ).toMatchObject({ + error: { age: ["Invalid type: Expected number but received NaN"] }, + }); + }); + + test("should pass nullable with pipe", async () => { + const schema = objectAsync({ + age: pipeAsync( + nullableAsync(number()), + checkAsync( + async (value) => value == null || value > 0, + "age must be greater than 0", + ), + ), + }); + + const output2 = await parseWithValibot(createFormData("age", "20"), { + schema, + }); + expect(output2).toMatchObject({ + status: "success", + value: { age: 20 }, + }); + + const errorOutput = await parseWithValibot(createFormData("age", "0"), { + schema, + }); + expect(errorOutput).toMatchObject({ + error: { age: ["age must be greater than 0"] }, + }); + }); + + test("should use default if required", async () => { + const default_ = "default"; + + const schema1 = objectAsync({ + name: optionalAsync(nullableAsync(string(), default_), null), + }); + const output1 = await parseWithValibot(createFormData("name", ""), { + schema: schema1, + }); + expect(output1).toMatchObject({ + status: "success", + value: { name: "default" }, + }); + + const schema2 = objectAsync({ + name: nullableAsync(string(), () => default_), + }); + const output2 = await parseWithValibot(createFormData("name", ""), { + schema: schema1, + }); + expect(output2).toMatchObject({ + status: "success", + value: { name: "default" }, + }); + }); +}); diff --git a/tests/coercion/schema/nullishAsync.test.ts b/tests/coercion/schema/nullishAsync.test.ts new file mode 100644 index 0000000..7965400 --- /dev/null +++ b/tests/coercion/schema/nullishAsync.test.ts @@ -0,0 +1,95 @@ +import { describe, expect, test } from "vitest"; +import { + string, + number, + objectAsync, + nullishAsync, + checkAsync, + pipeAsync, +} from "valibot"; +import { parseWithValibot } from "../../../parse"; +import { createFormData } from "../../helpers/FormData"; + +describe("nullishAsync", () => { + test("should pass also undefined", async () => { + const schema = objectAsync({ age: nullishAsync(number()) }); + const output = await parseWithValibot(createFormData("age", ""), { + schema, + }); + + expect(output).toMatchObject({ + status: "success", + value: { age: undefined }, + }); + expect( + await parseWithValibot(createFormData("age", "20"), { schema }), + ).toMatchObject({ + status: "success", + value: { age: 20 }, + }); + expect( + await parseWithValibot(createFormData("age", "non number"), { schema }), + ).toMatchObject({ + error: { age: ["Invalid type: Expected number but received NaN"] }, + }); + }); + + test("should pass nullish with pipe", async () => { + const schema = objectAsync({ + age: pipeAsync( + nullishAsync(number()), + checkAsync( + async (value) => value == null || value > 0, + "age must be greater than 0", + ), + ), + }); + + const output1 = await parseWithValibot(createFormData("age", ""), { + schema, + }); + expect(output1).toMatchObject({ + status: "success", + value: { age: undefined }, + }); + + const output2 = await parseWithValibot(createFormData("age", "20"), { + schema, + }); + expect(output2).toMatchObject({ + status: "success", + value: { age: 20 }, + }); + + const errorOutput = await parseWithValibot(createFormData("age", "0"), { + schema, + }); + expect(errorOutput).toMatchObject({ + error: { age: ["age must be greater than 0"] }, + }); + }); + + test("should use default if required", async () => { + const default_ = "default"; + + const schema1 = objectAsync({ name: nullishAsync(string(), default_) }); + const output1 = await parseWithValibot(createFormData("name", ""), { + schema: schema1, + }); + expect(output1).toMatchObject({ + status: "success", + value: { name: "default" }, + }); + + const schema2 = objectAsync({ + name: nullishAsync(string(), () => default_), + }); + const output2 = await parseWithValibot(createFormData("name", ""), { + schema: schema2, + }); + expect(output2).toMatchObject({ + status: "success", + value: { name: "default" }, + }); + }); +}); diff --git a/tests/coercion/schema/objectAsync.test.ts b/tests/coercion/schema/objectAsync.test.ts index 7c31773..dd89faa 100644 --- a/tests/coercion/schema/objectAsync.test.ts +++ b/tests/coercion/schema/objectAsync.test.ts @@ -3,15 +3,15 @@ import { string, objectAsync, pipeAsync, - check, - forward, + checkAsync, + forwardAsync, } from "valibot"; import { describe, expect, test } from "vitest"; import { parseWithValibot } from "../../../parse"; import { createFormData } from "../../helpers/FormData"; describe("objectAsync", () => { - test("should pass only async objects", async () => { + test("should pass only objects", async () => { const schema1 = objectAsync({ key1: string(), key2: number() }); const input1 = createFormData("key1", "test"); input1.append("key2", "123"); @@ -47,13 +47,13 @@ describe("objectAsync", () => { }); }); - test("should pass async objects with pipe", async () => { + test("should pass objects with pipe", async () => { const schema = pipeAsync( objectAsync({ key: string(), }), - forward( - check(({ key }) => key !== "error name", "key is error"), + forwardAsync( + checkAsync(async ({ key }) => key !== "error name", "key is error"), ["key"], ), ); diff --git a/tests/coercion/schema/optionalAsync.test.ts b/tests/coercion/schema/optionalAsync.test.ts new file mode 100644 index 0000000..a8c252e --- /dev/null +++ b/tests/coercion/schema/optionalAsync.test.ts @@ -0,0 +1,95 @@ +import { describe, expect, test } from "vitest"; +import { + string, + number, + objectAsync, + optionalAsync, + checkAsync, + pipeAsync, +} from "valibot"; +import { parseWithValibot } from "../../../parse"; +import { createFormData } from "../../helpers/FormData"; + +describe("optionalAsync", () => { + test("should pass also undefined", async () => { + const schema = objectAsync({ age: optionalAsync(number()) }); + const output = await parseWithValibot(createFormData("age", ""), { + schema, + }); + + expect(output).toMatchObject({ + status: "success", + value: { age: undefined }, + }); + expect( + await parseWithValibot(createFormData("age", "20"), { schema }), + ).toMatchObject({ + status: "success", + value: { age: 20 }, + }); + expect( + await parseWithValibot(createFormData("age", "non number"), { schema }), + ).toMatchObject({ + error: { age: ["Invalid type: Expected number but received NaN"] }, + }); + }); + + test("should pass optional with pipe", async () => { + const schema = objectAsync({ + age: pipeAsync( + optionalAsync(number()), + checkAsync( + async (value) => value == null || value > 0, + "age must be greater than 0", + ), + ), + }); + + const output1 = await parseWithValibot(createFormData("age", ""), { + schema, + }); + expect(output1).toMatchObject({ + status: "success", + value: { age: undefined }, + }); + + const output2 = await parseWithValibot(createFormData("age", "20"), { + schema, + }); + expect(output2).toMatchObject({ + status: "success", + value: { age: 20 }, + }); + + const errorOutput = await parseWithValibot(createFormData("age", "0"), { + schema, + }); + expect(errorOutput).toMatchObject({ + error: { age: ["age must be greater than 0"] }, + }); + }); + + test("should use default if required", async () => { + const default_ = "default"; + + const schema1 = objectAsync({ name: optionalAsync(string(), default_) }); + const output1 = await parseWithValibot(createFormData("name", ""), { + schema: schema1, + }); + expect(output1).toMatchObject({ + status: "success", + value: { name: "default" }, + }); + + const schema2 = objectAsync({ + name: optionalAsync(string(), () => default_), + }); + const output2 = await parseWithValibot(createFormData("name", ""), { + schema: schema2, + }); + expect(output2).toMatchObject({ + status: "success", + value: { name: "default" }, + }); + }); +}); diff --git a/tests/coercion/schema/tupleAsync.test.ts b/tests/coercion/schema/tupleAsync.test.ts new file mode 100644 index 0000000..e7b42d7 --- /dev/null +++ b/tests/coercion/schema/tupleAsync.test.ts @@ -0,0 +1,70 @@ +import { describe, expect, test } from "vitest"; +import { parseWithValibot } from "../../../parse"; +import { + number, + objectAsync, + string, + tupleAsync, + checkAsync, + pipeAsync, +} from "valibot"; +import { createFormData } from "../../helpers/FormData"; + +describe("tupleAsync", () => { + test("should pass only tuples", async () => { + const schema1 = objectAsync({ tuple: tupleAsync([number(), string()]) }); + const input1 = createFormData("tuple", "1"); + input1.append("tuple", "test"); + const output1 = await parseWithValibot(input1, { schema: schema1 }); + expect(output1).toMatchObject({ + status: "success", + value: { tuple: [1, "test"] }, + }); + const input2 = createFormData("tuple", "1"); + input2.append("tuple", "test"); + input2.append("tuple", ""); + const output2 = await parseWithValibot(input2, { schema: schema1 }); + expect(output2).toMatchObject({ + status: "success", + value: { tuple: [1, "test"] }, + }); + + const errorInput1 = createFormData("tuple", "1"); + expect( + await parseWithValibot(errorInput1, { schema: schema1 }), + ).toMatchObject({ + error: { tuple: ['Invalid type: Expected Array but received "1"'] }, + }); + const errorInput2 = createFormData("tuple", "123"); + expect( + await parseWithValibot(errorInput2, { schema: schema1 }), + ).toMatchObject({ + error: { tuple: ['Invalid type: Expected Array but received "123"'] }, + }); + }); + + test("should pass only tuples with pipe", async () => { + const schema = objectAsync({ + tuple: pipeAsync( + tupleAsync([number(), string()]), + checkAsync(async ([num, str]) => num > 0, "num is not greater than 0"), + ), + }); + + const input = createFormData("tuple", "1"); + input.append("tuple", "test"); + const output = await parseWithValibot(input, { + schema, + }); + expect(output).toMatchObject({ + status: "success", + value: { tuple: [1, "test"] }, + }); + + const errorInput = createFormData("tuple", "0"); + errorInput.append("tuple", "test"); + expect(await parseWithValibot(errorInput, { schema })).toMatchObject({ + error: { tuple: ["num is not greater than 0"] }, + }); + }); +}); diff --git a/tests/coercion/schema/tupleWithRest.test.ts b/tests/coercion/schema/tupleWithRest.test.ts index 11bb9ca..f8ece57 100644 --- a/tests/coercion/schema/tupleWithRest.test.ts +++ b/tests/coercion/schema/tupleWithRest.test.ts @@ -16,24 +16,24 @@ describe("tupleWithRest", () => { const schema1 = object({ tuple: tupleWithRest([string()], optional(number())), }); - const input3 = createFormData("tuple[]", "test"); + const input2 = createFormData("tuple[]", "test"); + const output2 = parseWithValibot(input2, { schema: schema1 }); + expect(output2).toMatchObject({ + status: "success", + value: { tuple: ["test"] }, + }); + const input3 = createFormData("tuple", "test"); + input3.append("tuple", ""); const output3 = parseWithValibot(input3, { schema: schema1 }); expect(output3).toMatchObject({ status: "success", - value: { tuple: ["test"] }, + value: { tuple: ["test", undefined] }, }); const input4 = createFormData("tuple", "test"); - input4.append("tuple", ""); + input4.append("tuple", "1"); + input4.append("tuple", "2"); const output4 = parseWithValibot(input4, { schema: schema1 }); expect(output4).toMatchObject({ - status: "success", - value: { tuple: ["test", undefined] }, - }); - const input5 = createFormData("tuple", "test"); - input5.append("tuple", "1"); - input5.append("tuple", "2"); - const output5 = parseWithValibot(input5, { schema: schema1 }); - expect(output5).toMatchObject({ status: "success", value: { tuple: ["test", 1, 2] }, }); diff --git a/tests/coercion/schema/tupleWithRestAsync.test.ts b/tests/coercion/schema/tupleWithRestAsync.test.ts new file mode 100644 index 0000000..a07ca9f --- /dev/null +++ b/tests/coercion/schema/tupleWithRestAsync.test.ts @@ -0,0 +1,67 @@ +import { describe, expect, test } from "vitest"; +import { parseWithValibot } from "../../../parse"; +import { + optionalAsync, + number, + objectAsync, + string, + tupleWithRestAsync, + checkAsync, + pipeAsync, +} from "valibot"; +import { createFormData } from "../../helpers/FormData"; + +describe("tupleWithRestAsync", () => { + test("should pass only tuples", async () => { + const schema1 = objectAsync({ + tuple: tupleWithRestAsync([string()], optionalAsync(number())), + }); + const input2 = createFormData("tuple[]", "test"); + const output2 = await parseWithValibot(input2, { schema: schema1 }); + expect(output2).toMatchObject({ + status: "success", + value: { tuple: ["test"] }, + }); + const input3 = createFormData("tuple", "test"); + input3.append("tuple", ""); + const output3 = await parseWithValibot(input3, { schema: schema1 }); + expect(output3).toMatchObject({ + status: "success", + value: { tuple: ["test", undefined] }, + }); + const input4 = createFormData("tuple", "test"); + input4.append("tuple", "1"); + input4.append("tuple", "2"); + const output4 = await parseWithValibot(input4, { schema: schema1 }); + expect(output4).toMatchObject({ + status: "success", + value: { tuple: ["test", 1, 2] }, + }); + }); + + test("should pass tuples with pipe", async () => { + const schema = objectAsync({ + tuple: pipeAsync( + tupleWithRestAsync([string()], optionalAsync(number())), + checkAsync( + async (v) => v.length > 2, + "tuple must have more than 1 element", + ), + ), + }); + const input = createFormData("tuple", "test"); + input.append("tuple", "1"); + input.append("tuple", "2"); + const output = await parseWithValibot(input, { schema }); + expect(output).toMatchObject({ + status: "success", + value: { tuple: ["test", 1, 2] }, + }); + + const errorInput = createFormData("tuple", "test"); + errorInput.append("tuple", "1"); + expect(await parseWithValibot(errorInput, { schema })).toMatchObject({ + error: { tuple: ["tuple must have more than 1 element"] }, + }); + }); +}); diff --git a/tests/coercion/schema/variantAsync.test.ts b/tests/coercion/schema/variantAsync.test.ts new file mode 100644 index 0000000..e6bbc2f --- /dev/null +++ b/tests/coercion/schema/variantAsync.test.ts @@ -0,0 +1,116 @@ +import { describe, expect, test } from "vitest"; +import { parseWithValibot } from "../../../parse"; +import { + object, + variantAsync, + literal, + number, + string, + checkAsync, + pipeAsync, + forwardAsync, +} from "valibot"; +import { createFormData } from "../../helpers/FormData"; + +describe("variantAsync", () => { + test("should pass only variant values", async () => { + const schema1 = variantAsync("type", [ + object({ type: literal("a"), a: string() }), + object({ type: literal("b"), b: number() }), + ]); + const input1 = createFormData("type", "a"); + input1.append("a", "hello"); + const output1 = await parseWithValibot(input1, { schema: schema1 }); + expect(output1).toMatchObject({ + status: "success", + value: { type: "a", a: "hello" }, + }); + const input2 = createFormData("type", "b"); + input2.append("b", "123"); + const output2 = await parseWithValibot(input2, { schema: schema1 }); + expect(output2).toMatchObject({ + status: "success", + value: { type: "b", b: 123 }, + }); + + const schema2 = variantAsync("type", [ + schema1, + object({ type: literal("c"), foo: literal("foo") }), + object({ type: literal("c"), bar: literal("bar") }), + ]); + const input3 = createFormData("type", "b"); + input3.append("b", "123"); + const output3 = await parseWithValibot(input3, { schema: schema2 }); + expect(output3).toMatchObject({ + status: "success", + value: { type: "b", b: 123 }, + }); + const input4 = createFormData("type", "c"); + input4.append("foo", "foo"); + const output4 = await parseWithValibot(input4, { schema: schema2 }); + expect(output4).toMatchObject({ + status: "success", + value: { type: "c", foo: "foo" }, + }); + const input5 = createFormData("type", "c"); + input5.append("bar", "bar"); + const output5 = await parseWithValibot(input5, { schema: schema2 }); + expect(output5).toMatchObject({ + status: "success", + value: { type: "c", bar: "bar" }, + }); + + const errorInput1 = createFormData("type", "x"); + expect( + await parseWithValibot(errorInput1, { schema: schema2 }), + ).toMatchObject({ + error: { type: ['Invalid type: Expected "a" | "b" but received "x"'] }, + }); + const errorInput2 = createFormData("type2", "a"); + expect( + await parseWithValibot(errorInput2, { schema: schema2 }), + ).toMatchObject({ + error: { + type: ['Invalid type: Expected "a" | "b" | "c" but received undefined'], + }, + }); + }); + + test("should pass only variant values with pipe", async () => { + const schema = pipeAsync( + variantAsync("type", [ + object({ type: literal("a"), a: string() }), + object({ type: literal("b"), b: number() }), + ]), + forwardAsync( + checkAsync( + async ({ type, ...other }) => + type === "a" || (type === "b" && "b" in other && other.b !== 0), + "b must be non-zero", + ), + ["b"], + ), + ); + const input1 = createFormData("type", "a"); + input1.append("a", "hello"); + const output1 = await parseWithValibot(input1, { schema }); + expect(output1).toMatchObject({ + status: "success", + value: { type: "a", a: "hello" }, + }); + const input2 = createFormData("type", "b"); + input2.append("b", "123"); + const output2 = await parseWithValibot(input2, { schema }); + expect(output2).toMatchObject({ + status: "success", + value: { type: "b", b: 123 }, + }); + + const errorInput1 = createFormData("type", "b"); + errorInput1.append("b", "0"); + const errorOutput1 = await parseWithValibot(errorInput1, { schema }); + expect(errorOutput1).toMatchObject({ + error: { b: ["b must be non-zero"] }, + }); + }); +});