import { expect, test } from "vitest"; import { render, waitFor, waitForElementToBeRemoved, } from "@testing-library/react"; import { Field, FieldArray, FieldArrayItem, Form } from "houseform"; import React, { useState } from "react"; import { z } from "zod"; test("Field array item should submit with values in tact", async () => { const SubmitValues = () => { const [values, setValues] = useState(null); if (values) return

{values}

; return (
setValues(JSON.stringify(values))}> {({ submit }) => (
initialValue={[{ thing: 1 }]} name={"people"} > {({ value }) => ( <> {value.map((person, i) => ( key={`people[${i}].thing`} name={`people[${i}].thing`} > {({ value, setValue }) => (

Value: {value}

)} ))} )}
)}
); }; const { getByText, container } = render(); expect(getByText("Value: 1")).toBeInTheDocument(); await user.click(getByText("Set value")); expect(getByText("Value: 2")).toBeInTheDocument(); await user.click(getByText("Submit")); await waitFor(() => expect(container).toMatchInlineSnapshot(`

{"people":[{"thing":2}]}

`) ); }); test("Field array item be able to set a value", async () => { const { getByText } = render(
{() => ( initialValue={[{ thing: 1 }]} name={"people"} > {({ value }) => ( <> {value.map((person, i) => ( key={`people[${i}].thing`} name={`people[${i}].thing`} > {({ value, setValue }) => (

Value: {value}

)} ))} )} )} ); expect(getByText("Value: 1")).toBeInTheDocument(); await user.click(getByText("Set value")); expect(getByText("Value: 2")).toBeInTheDocument(); }); test("field array item should track `isDirty`", async () => { const { queryByText, getByText } = render(
{() => ( initialValue={[{ thing: 1 }]} name={"people"} > {({ value }) => ( <> {value.map((_, i) => ( key={`people[${i}].thing`} name={`people[${i}].thing`} > {({ setValue, isDirty }) => (
{isDirty &&

Is dirty

}
)} ))} )} )} ); expect(queryByText("Is dirty")).not.toBeInTheDocument(); await user.click(getByText("Set value")); expect(getByText("Is dirty")).toBeInTheDocument(); }); test("field array item should track `isTouched`", async () => { const { queryByText, getByText } = render(
{() => ( initialValue={[{ thing: 1 }]} name={"people"} > {({ value }) => ( <> {value.map((_, i) => ( key={`people[${i}].thing`} name={`people[${i}].thing`} > {({ isTouched, onBlur }) => (
{isTouched &&

Is touched

}
)} ))} )} )} ); expect(queryByText("Is touched")).not.toBeInTheDocument(); await user.click(getByText("Blur")); expect(getByText("Is touched")).toBeInTheDocument(); }); test("field array item should validate onChange", async () => { const { queryByText, getByText } = render(
{() => ( initialValue={[{ thing: 1 }]} name={"people"} > {({ value }) => ( <> {value.map((_, i) => ( key={`people[${i}].thing`} name={`people[${i}].thing`} onChangeValidate={z.number().min(3, "Must be at least 3")} > {({ setValue, errors }) => (
{errors.map((error) => (

{error}

))}
)} ))} )} )} ); expect(queryByText("Must be at least 3")).not.toBeInTheDocument(); await user.click(getByText("Set value")); expect(getByText("Must be at least 3")).toBeInTheDocument(); }); test("field array item should validate onBlur", async () => { const { queryByText, getByText } = render(
{() => ( initialValue={[{ thing: 1 }]} name={"people"} > {({ value }) => ( <> {value.map((_, i) => ( key={`people[${i}].thing`} name={`people[${i}].thing`} onBlurValidate={z.number().min(3, "Must be at least 3")} > {({ onBlur, errors }) => (
{errors.map((error) => (

{error}

))}
)} ))} )} )} ); expect(queryByText("Must be at least 3")).not.toBeInTheDocument(); await user.click(getByText("Blur")); expect(getByText("Must be at least 3")).toBeInTheDocument(); }); test("field array item should validate onSubmit", async () => { const { queryByText, getByText } = render(
{({ submit }) => (
initialValue={[{ thing: 1 }]} name={"people"} > {({ value }) => ( <> {value.map((_, i) => ( key={`people[${i}].thing`} name={`people[${i}].thing`} onSubmitValidate={z.number().min(3, "Must be at least 3")} > {({ errors }) => (
{errors.map((error) => (

{error}

))}
)} ))} )}
)}
); expect(queryByText("Must be at least 3")).not.toBeInTheDocument(); await user.click(getByText("Submit")); expect(getByText("Must be at least 3")).toBeInTheDocument(); }); test("field array item should work with listenTo as the subject", async () => { const { queryByText, getByText } = render(
{() => (
name={"test"} listenTo={[`people[0].thing`]} onChangeValidate={z.string().min(3, "Must be at least 3")} initialValue={"T"} > {({ errors }) => (
{errors.map((error) => (

{error}

))}
)} initialValue={[{ thing: 1 }]} name={"people"} > {({ value }) => ( <> {value.map((_, i) => ( key={`people[${i}].thing`} name={`people[${i}].thing`} > {({ setValue }) => ( )} ))} )}
)}
); expect(queryByText("Must be at least 3")).not.toBeInTheDocument(); await user.click(getByText("Set value")); expect(getByText("Must be at least 3")).toBeInTheDocument(); }); test("field array item should work with listenTo as the listener", async () => { const { queryByText, getByText } = render(
{() => (
name={"test"} initialValue={"T"}> {({ setValue }) => ( )} initialValue={[{ thing: 1 }]} name={"people"} > {({ value }) => ( <> {value.map((_, i) => ( key={`people[${i}].thing`} name={`people[${i}].thing`} onChangeValidate={z.number().min(3, "Must be at least 3")} listenTo={[`test`]} > {({ errors }) => (
{errors.map((error) => (

{error}

))}
)} ))} )}
)}
); expect(queryByText("Must be at least 3")).not.toBeInTheDocument(); await user.click(getByText("Set value")); expect(getByText("Must be at least 3")).toBeInTheDocument(); }); test("field array item should provide an error when improperly prefixed", async () => { expect(() => render(
{() => (
name={"test"} initialValue={"T"}> {({ setValue }) => ( )} initialValue={[{ thing: 1 }]} name={"people"} > {({ value }) => ( <> {value.map((_, i) => ( key={`person[${i}].thing`} name={`person[${i}].thing`} onChangeValidate={z.number().min(3, "Must be at least 3")} listenTo={[`test`]} > {({ errors }) => (
{errors.map((error) => (

{error}

))}
)} ))} )}
)}
) ).toThrowError(); }); test("field array item should set isValidating with onChange validation", async () => { const { queryByText, getByText } = render(
{() => ( initialValue={[{ thing: 1 }]} name={"people"} > {({ value }) => ( <> {value.map((_, i) => ( key={`people[${i}].thing`} name={`people[${i}].thing`} onChangeValidate={() => new Promise((resolve) => setTimeout(() => resolve(true), 50) ) } > {({ setValue, isValidating }) => (
{isValidating &&

Validating

}
)} ))} )} )} ); expect(queryByText("Validating")).not.toBeInTheDocument(); await user.click(getByText("Set value")); expect(getByText("Validating")).toBeInTheDocument(); await waitForElementToBeRemoved(() => queryByText("Validating")); }); test("field array item should set isValidating with onBlur validator", async () => { const { queryByText, getByText } = render(
{() => ( initialValue={[{ thing: 1 }]} name={"people"} > {({ value }) => ( <> {value.map((_, i) => ( key={`people[${i}].thing`} name={`people[${i}].thing`} onBlurValidate={() => new Promise((resolve) => setTimeout(() => resolve(true), 50) ) } > {({ onBlur, isValidating }) => (
{isValidating &&

Validating

}
)} ))} )} )} ); expect(queryByText("Validating")).not.toBeInTheDocument(); await user.click(getByText("Blur")); expect(getByText("Validating")).toBeInTheDocument(); await waitForElementToBeRemoved(() => queryByText("Validating")); }); test("field array item should set isValidating with onSubmit validation", async () => { const { queryByText, getByText } = render(
{({ submit }) => (
initialValue={[{ thing: 1 }]} name={"people"} > {({ value }) => ( <> {value.map((_, i) => ( key={`people[${i}].thing`} name={`people[${i}].thing`} onSubmitValidate={() => new Promise((resolve) => setTimeout(() => resolve(true), 50) ) } > {({ isValidating }) => (
{isValidating &&

Validating

}
)} ))} )}
)}
); expect(queryByText("Validating")).not.toBeInTheDocument(); await user.click(getByText("Submit")); expect(getByText("Validating")).toBeInTheDocument(); await waitForElementToBeRemoved(() => queryByText("Validating")); }); test("Field array item should not be dirty when form reset", async () => { const { getByText } = render(
{({ reset }) => ( <> initialValue={[{ thing: 1 }]} name={"people"} > {({ value }) => ( <> {value.map((person, i) => ( key={`people[${i}].thing`} name={`people[${i}].thing`} > {({ setValue, isDirty }) => (

{isDirty ? "Dirty" : "Clean"}

)} ))} )} )} ); expect(getByText("Clean")).toBeInTheDocument(); await user.click(getByText("Set value")); expect(getByText("Dirty")).toBeInTheDocument(); await user.click(getByText("Reset")); expect(getByText("Clean")).toBeInTheDocument(); });