mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-09-19 01:34:27 -05:00
159 lines
4.7 KiB
TypeScript
159 lines
4.7 KiB
TypeScript
import * as v from "valibot";
|
|
import { describe, expect, test } from "vitest";
|
|
import { preprocess } from "~/utils/schema";
|
|
import { getNestedSchema, getNestedValue, setNestedValue } from "./utils";
|
|
|
|
describe("getNestedValue", () => {
|
|
test("returns value at simple path", () => {
|
|
expect(getNestedValue({ name: "test" }, "name")).toBe("test");
|
|
});
|
|
|
|
test("returns value at nested path", () => {
|
|
expect(getNestedValue({ config: { name: "test" } }, "config.name")).toBe(
|
|
"test",
|
|
);
|
|
});
|
|
|
|
test("returns undefined for missing path", () => {
|
|
expect(getNestedValue({ config: {} }, "config.name")).toBe(undefined);
|
|
});
|
|
|
|
test("returns undefined when parent is null", () => {
|
|
expect(getNestedValue({ config: null }, "config.name")).toBe(undefined);
|
|
});
|
|
|
|
test("handles deeply nested paths", () => {
|
|
const obj = { a: { b: { c: { d: "deep" } } } };
|
|
expect(getNestedValue(obj, "a.b.c.d")).toBe("deep");
|
|
});
|
|
});
|
|
|
|
describe("setNestedValue", () => {
|
|
test("sets value at simple path", () => {
|
|
expect(setNestedValue({}, "name", "test")).toEqual({ name: "test" });
|
|
});
|
|
|
|
test("sets value at nested path", () => {
|
|
expect(setNestedValue({}, "config.name", "test")).toEqual({
|
|
config: { name: "test" },
|
|
});
|
|
});
|
|
|
|
test("preserves existing sibling values", () => {
|
|
const obj = { config: { existing: "keep" } };
|
|
expect(setNestedValue(obj, "config.name", "test")).toEqual({
|
|
config: { existing: "keep", name: "test" },
|
|
});
|
|
});
|
|
|
|
test("is immutable - does not modify original", () => {
|
|
const obj = { config: { name: "old" } };
|
|
setNestedValue(obj, "config.name", "new");
|
|
expect(obj.config.name).toBe("old");
|
|
});
|
|
|
|
test("handles deeply nested paths", () => {
|
|
expect(setNestedValue({}, "a.b.c.d", "deep")).toEqual({
|
|
a: { b: { c: { d: "deep" } } },
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("getNestedSchema", () => {
|
|
test("returns schema for simple path", () => {
|
|
const schema = v.object({ name: v.string() });
|
|
const result = getNestedSchema(schema, "name");
|
|
expect(result?.type).toBe("string");
|
|
});
|
|
|
|
test("returns schema for nested path", () => {
|
|
const schema = v.object({ config: v.object({ name: v.string() }) });
|
|
const result = getNestedSchema(schema, "config.name");
|
|
expect(result?.type).toBe("string");
|
|
});
|
|
|
|
test("unwraps nullable wrapper", () => {
|
|
const schema = v.object({
|
|
config: v.nullable(v.object({ name: v.string() })),
|
|
});
|
|
const result = getNestedSchema(schema, "config.name");
|
|
expect(result?.type).toBe("string");
|
|
});
|
|
|
|
test("unwraps optional wrapper", () => {
|
|
const schema = v.object({
|
|
config: v.optional(v.object({ name: v.string() })),
|
|
});
|
|
const result = getNestedSchema(schema, "config.name");
|
|
expect(result?.type).toBe("string");
|
|
});
|
|
|
|
test("returns undefined for invalid path", () => {
|
|
const schema = v.object({ name: v.string() });
|
|
expect(getNestedSchema(schema, "missing.path")).toBe(undefined);
|
|
});
|
|
|
|
test("returns undefined when path goes through non-object", () => {
|
|
const schema = v.object({ name: v.string() });
|
|
expect(getNestedSchema(schema, "name.invalid")).toBe(undefined);
|
|
});
|
|
|
|
test("returns schema for array element path", () => {
|
|
const schema = v.object({
|
|
items: v.array(v.object({ name: v.string() })),
|
|
});
|
|
const result = getNestedSchema(schema, "items[0].name");
|
|
expect(result?.type).toBe("string");
|
|
});
|
|
|
|
test("returns schema for array element path with min/max", () => {
|
|
const schema = v.object({
|
|
items: v.pipe(
|
|
v.array(v.object({ name: v.string() })),
|
|
v.minLength(1),
|
|
v.maxLength(10),
|
|
),
|
|
});
|
|
const result = getNestedSchema(schema, "items[0].name");
|
|
expect(result?.type).toBe("string");
|
|
});
|
|
|
|
test("drills through a preprocess pipe into a nested object", () => {
|
|
const schema = v.object({
|
|
config: preprocess((val) => val, v.object({ name: v.string() })),
|
|
});
|
|
const result = getNestedSchema(schema, "config.name");
|
|
expect(result?.type).toBe("string");
|
|
});
|
|
|
|
test("drills through a preprocess pipe into a nested array", () => {
|
|
const schema = v.object({
|
|
items: preprocess((val) => val, v.array(v.object({ name: v.string() }))),
|
|
});
|
|
const result = getNestedSchema(schema, "items[0].name");
|
|
expect(result?.type).toBe("string");
|
|
});
|
|
|
|
test("drills through a preprocess pipe inside an array item", () => {
|
|
const schema = v.object({
|
|
items: v.array(preprocess((val) => val, v.object({ name: v.string() }))),
|
|
});
|
|
const result = getNestedSchema(schema, "items[0].name");
|
|
expect(result?.type).toBe("string");
|
|
});
|
|
|
|
test("drills through a preprocess pipe wrapping a validated object", () => {
|
|
const schema = v.object({
|
|
config: preprocess(
|
|
(val) => val,
|
|
v.pipe(
|
|
v.object({ name: v.string() }),
|
|
v.check((val) => val.name.length > 0),
|
|
),
|
|
),
|
|
});
|
|
const result = getNestedSchema(schema, "config.name");
|
|
expect(result?.type).toBe("string");
|
|
});
|
|
});
|