Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions tests/src/form.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
/** biome-ignore-all lint/a11y/useAriaPropsSupportedByRole: pre aria-label */

import type { JSXElement } from "solid-js";
import { type CreateFormReturn, createForm, type FormValues, type SubmitHandler } from "./import";
import {
type CreateFormReturn,
createForm,
type FormValues,
type SubmitErrorHandler,
type SubmitHandler
} from "./import";

type FormProps<F extends FormValues> = {
defaultValues: F;
Expand All @@ -10,6 +16,7 @@ type FormProps<F extends FormValues> = {
render: (form: CreateFormReturn<F>) => JSXElement;
submitButton?: (form: CreateFormReturn<F>) => JSXElement;
onSubmit: SubmitHandler<F>;
onError?: SubmitErrorHandler<F>;
onReset?(form: CreateFormReturn<F>): void;
};

Expand All @@ -27,7 +34,7 @@ export const Form = <F extends FormValues>(props: FormProps<F>) => {
<div>
<form
style={{ display: "flex", "flex-direction": "column", gap: "32px" }}
onSubmit={handleSubmit(props.onSubmit)}
onSubmit={handleSubmit(props.onSubmit, props.onError)}
>
{props.render(form)}

Expand Down
90 changes: 90 additions & 0 deletions tests/src/getValues/getValues.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { render } from "vitest-browser-solid";
import { Form } from "../form";
import { Input } from "../input";

const onSubmit = vi.fn(() => {});
const onGetValue = vi.fn();
const onGetAllValues = vi.fn();

beforeEach(() => {
vi.resetAllMocks();
});

describe("getValues", () => {
it("should get single field value by name", async () => {
const page = render(() => (
<Form
defaultValues={{
email: "",
password: ""
}}
render={({ getValues, register }) => (
<>
<Input {...register("email")} />
<Input {...register("password")} />
<button type="button" onClick={() => onGetValue(getValues("email"))}>
Get email value
</button>
</>
)}
onSubmit={onSubmit}
/>
));

const emailInput = page.getByRole("textbox", { name: "email" });
const getButton = page.getByRole("button", { name: "Get email value" });

await getButton.click();
expect(onGetValue).toHaveBeenCalledWith("");

await emailInput.fill("user@example.com");
await getButton.click();
expect(onGetValue).toHaveBeenCalledWith("user@example.com");

await emailInput.fill("123");
await getButton.click();
expect(onGetValue).toHaveBeenCalledWith("123");
});

it("should get all fields values", async () => {
const page = render(() => (
<Form
defaultValues={{
email: "",
password: ""
}}
render={({ getValues, register }) => (
<>
<Input {...register("email")} />
<Input {...register("password")} />
<button type="button" onClick={() => onGetAllValues(getValues())}>
Get all values
</button>
</>
)}
onSubmit={onSubmit}
/>
));

const emailInput = page.getByRole("textbox", { name: "email" });
const passwordInput = page.getByRole("textbox", { name: "password" });
const getButton = page.getByRole("button", { name: "Get all values" });

await getButton.click();
expect(onGetAllValues).toHaveBeenCalledWith({ email: "", password: "" });

await emailInput.fill("user@example.com");
await getButton.click();
expect(onGetAllValues).toHaveBeenCalledWith({
email: "user@example.com",
password: ""
});

await passwordInput.fill("secret");
await getButton.click();
expect(onGetAllValues).toHaveBeenCalledWith({
email: "user@example.com",
password: "secret"
});
});
});
94 changes: 94 additions & 0 deletions tests/src/setValue/setValue.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { render } from "vitest-browser-solid";
import { Form } from "../form";
import { Input } from "../input";

const onSubmit = vi.fn(() => {});
const errorMessage = "This field is required";
const validValue = "valid@example.com";

beforeEach(() => {
vi.resetAllMocks();
});

describe("setValue", () => {
it("should setValue", async () => {
const page = render(() => (
<Form
defaultValues={{
email: ""
}}
render={({ errors, register, setValue }) => (
<>
<Input {...register("email", { required: errorMessage })} error={errors.email} />

<button type="button" onClick={() => setValue("email", "")}>
Set invalid
</button>
<button type="button" onClick={() => setValue("email", validValue)}>
Set valid
</button>
</>
)}
onSubmit={onSubmit}
/>
));

const input = page.getByRole("textbox", { name: "email" });
const setInvalidButton = page.getByRole("button", { name: "Set invalid" });
const setValidButton = page.getByRole("button", { name: "Set valid" });

expect(input).toHaveValue("");
expect(input).toHaveAttribute("aria-invalid", "false");

await setInvalidButton.click();
expect(input).toHaveAttribute("aria-invalid", "false");
expect(input).not.toHaveAccessibleErrorMessage();

await setValidButton.click();
expect(input).toHaveValue(validValue);
expect(input).toHaveAttribute("aria-invalid", "false");
expect(input).not.toHaveAccessibleErrorMessage();
});

it("should setValue and validate", async () => {
const page = render(() => (
<Form
defaultValues={{
email: ""
}}
render={({ errors, register, setValue }) => (
<>
<Input {...register("email", { required: errorMessage })} error={errors.email} />

<button type="button" onClick={() => setValue("email", "", { shouldValidate: true })}>
Set invalid
</button>
<button
type="button"
onClick={() => setValue("email", validValue, { shouldValidate: true })}
>
Set valid
</button>
</>
)}
onSubmit={onSubmit}
/>
));

const input = page.getByRole("textbox", { name: "email" });
const setInvalidButton = page.getByRole("button", { name: "Set invalid" });
const setValidButton = page.getByRole("button", { name: "Set valid" });

expect(input).toHaveValue("");
expect(input).toHaveAttribute("aria-invalid", "false");

await setInvalidButton.click();
expect(input).toHaveAttribute("aria-invalid", "true");
expect(input).toHaveAccessibleErrorMessage(errorMessage);

await setValidButton.click();
expect(input).toHaveValue(validValue);
expect(input).toHaveAttribute("aria-invalid", "false");
expect(input).not.toHaveAccessibleErrorMessage();
});
});
27 changes: 27 additions & 0 deletions tests/src/submit/submit.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Form } from "../form";
import { Input } from "../input";

const onSubmit = vi.fn(() => {});
const onError = vi.fn(() => {});

beforeEach(() => {
vi.resetAllMocks();
Expand Down Expand Up @@ -63,4 +64,30 @@ describe("submit", () => {
expect(onSubmit).toHaveBeenCalled();
expect(submitCount).toHaveTextContent("2");
});

it("should call onError", async () => {
const page = render(() => (
<Form
defaultValues={{
email: ""
}}
render={({ register }) => (
<Input {...register("email", { required: "Email is required" })} />
)}
onSubmit={onSubmit}
onError={onError}
/>
));

const submitButton = page.getByRole("button", { name: "Submit" });
await submitButton.click();

expect(onError).toHaveBeenCalledTimes(1);
expect(onError).toHaveBeenCalledWith(
expect.objectContaining({
email: expect.objectContaining({ message: "Email is required" })
})
);
expect(onSubmit).not.toHaveBeenCalled();
});
});