Skip to content

Commit

Permalink
test(FormBuilder): add tests to FormBuilder elements
Browse files Browse the repository at this point in the history
  • Loading branch information
ribeirojose committed Apr 28, 2024
1 parent 8f93804 commit 659b55a
Show file tree
Hide file tree
Showing 12 changed files with 439 additions and 1 deletion.
47 changes: 47 additions & 0 deletions tests/components/FormBuilder/CheckboxField.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import React from "react";
import { describe, it, expect } from "vitest";
import { render, screen, fireEvent, renderHook } from "@testing-library/react";
import { FormProvider, useForm, UseFormReturn } from "react-hook-form";

import { CheckboxField, CheckboxFieldProps } from "#/components";

const TestForm = ({
field,
form,
}: {
field: CheckboxFieldProps;
form: UseFormReturn<any>;
}) => (
<FormProvider {...form}>
<CheckboxField form={form} field={field} />
</FormProvider>
);

describe("CheckboxField", () => {
it("renders a checkbox element", () => {
const field: CheckboxFieldProps = {
type: "checkbox",
name: "test",
value: "",
label: "Test Checkbox",
};
const { result } = renderHook(() => useForm());
render(<TestForm field={field} form={result.current} />);
const checkboxElement = screen.getByRole("checkbox");
expect(checkboxElement).toBeInTheDocument();
});

it("updates the form state correctly when interacted with", () => {
const field: CheckboxFieldProps = {
type: "checkbox",
name: "test",
value: "",
label: "Test Checkbox",
};
const { result } = renderHook(() => useForm());
render(<TestForm field={field} form={result.current} />);
const checkboxElement = screen.getByRole("checkbox");
fireEvent.click(checkboxElement);
expect(result.current.getValues("test")).toBe(true);
});
});
40 changes: 40 additions & 0 deletions tests/components/FormBuilder/FormField.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// FormField.test.tsx
import React from "react";
import { describe, it, expect, vi } from "vitest";
import { render, renderHook, screen } from "@testing-library/react";
import { FormProvider, useForm } from "react-hook-form";
import { FormField } from "#/components";

describe("FormField", () => {
const renderFormField = (props) => {
const { result } = renderHook(() => useForm());
return (
<FormProvider {...result.current}>
<FormField {...props} />
</FormProvider>
);
};

it("renders the appropriate field component based on the render prop", () => {
render(
renderFormField({
name: "test",
render: ({ field }) => <input {...field} placeholder="Enter text" />,
})
);
const inputElement = screen.getByPlaceholderText("Enter text");
expect(inputElement).toBeInTheDocument();
});

it("passes the correct props to the render function", () => {
const renderMock = vi.fn();
render(renderFormField({ name: "test", render: renderMock }));
expect(renderMock).toHaveBeenCalledWith(
expect.objectContaining({
field: expect.any(Object),
fieldState: expect.any(Object),
formState: expect.any(Object),
})
);
});
});
41 changes: 41 additions & 0 deletions tests/components/FormBuilder/FormFieldProvider.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// import * as React from "react";
// import { describe, it, expect } from "vitest";
// import { render, screen, renderHook } from "@testing-library/react";
// import {
// FormFieldProvider,
// useFormFieldState,
// useFormFieldUpdater,
// } from "#/components";

// describe("FormFieldProvider", () => {
// it("correctly initializes the field state with the provided name", () => {
// render(
// <FormFieldProvider
// name="test"
// render={({ field }) => <div {...field} data-testid="child" />}
// />
// );
// const childElement = screen.getByTestId("child");
// expect(childElement).toBeInTheDocument();
// });

// it("provides the field state and updater function to its children", () => {
// const { result } = renderHook(
// () => {
// const fieldState = useFormFieldState();
// const setFieldState = useFormFieldUpdater();
// return { fieldState, setFieldState };
// },
// {
// wrapper: ({ children }) => (
// <FormFieldProvider
// name="test"
// render={({ field }) => <div>{children}</div>}
// />
// ),
// }
// );
// expect(result.current.fieldState.name).toBe("test");
// expect(typeof result.current.setFieldState).toBe("function");
// });
// });
49 changes: 49 additions & 0 deletions tests/components/FormBuilder/InputField.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import React from "react";
import { describe, it, expect } from "vitest";
import { render, screen, fireEvent, renderHook } from "@testing-library/react";
import { FormProvider, useForm } from "react-hook-form";
import { InputField } from "#/components";

const TestForm = ({ field }) => {
const form = useForm();
return (
<FormProvider {...form}>
<InputField form={form} field={field} />
</FormProvider>
);
};

describe("InputField", () => {
it("renders an input element", () => {
const field = { type: "input", name: "test" };
render(<TestForm field={field} />);
const inputElement = screen.getByRole("textbox");
expect(inputElement).toBeInTheDocument();
});

// it("applies the appropriate validation rules", () => {
// const field = { type: "input", name: "test", required: true };
// render(<TestForm field={field} />);
// const inputElement = screen.getByRole("textbox");
// fireEvent.change(inputElement, { target: { value: "" } });
// expect(inputElement).toBeInvalid();
// });

it("updates the form state correctly when interacted with", () => {
const field = {
type: "input",
name: "test",
value: "",
mode: "text",
} as const;
const { result } = renderHook(() => useForm());
render(
<FormProvider {...result.current}>
<InputField form={result.current} field={field} />
</FormProvider>
);
const inputElement = screen.getByRole("textbox");
fireEvent.change(inputElement, { target: { value: "Test value" } });
expect(result.current.getValues("test")).toBe("Test value");
});
});
44 changes: 44 additions & 0 deletions tests/components/FormBuilder/TextAreaField.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import React from "react";
import { describe, it, expect } from "vitest";
import { render, screen, fireEvent, renderHook } from "@testing-library/react";
import { FormProvider, useForm } from "react-hook-form";
import { TextAreaField } from "#/components";

const TestForm = ({ field }) => {
const form = useForm();
return (
<FormProvider {...form}>
<TextAreaField form={form} field={field} />
</FormProvider>
);
};

describe("TextAreaField", () => {
it("renders a textarea element", () => {
const field = { type: "textarea", name: "test" };
render(<TestForm field={field} />);
const textareaElement = screen.getByRole("textbox");
expect(textareaElement).toBeInTheDocument();
});

// it("applies the appropriate validation rules", () => {
// const field = { type: "textarea", name: "test", required: true };
// render(<TestForm field={field} />);
// const textareaElement = screen.getByRole("textbox");
// fireEvent.change(textareaElement, { target: { value: "" } });
// expect(textareaElement).toBeInvalid();
// });

it("updates the form state correctly when interacted with", () => {
const field = { type: "textarea", name: "test", value: "" } as const;
const { result } = renderHook(() => useForm());
render(
<FormProvider {...result.current}>
<TextAreaField form={result.current} field={field} />
</FormProvider>
);
const textareaElement = screen.getByRole("textbox");
fireEvent.change(textareaElement, { target: { value: "Test value" } });
expect(result.current.getValues("test")).toBe("Test value");
});
});
36 changes: 36 additions & 0 deletions tests/components/FormBuilder/buildForm.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// buildForm.test.tsx
import { describe, it, expect } from "vitest";
import { renderHook } from "@testing-library/react";
import { useForm } from "react-hook-form";
import { buildForm } from "#/components/FormBuilder/buildForm";
import { InputField } from "#/components/FormBuilder/fields/InputField";

describe("buildForm", () => {
const fields = [
{ type: "input", name: "firstName", value: "", mode: "text" },
{ type: "textarea", name: "description", value: "" },
];
const { result: renderHookResult } = renderHook(() => useForm());
const form = renderHookResult.current;

it("returns an array of form elements", () => {
const result = buildForm(fields, form);
expect(Array.isArray(result)).toBe(true);
});

it("throws an error when an invalid field type is provided", () => {
const invalidFields = [{ type: "invalid", name: "test", value: "" }];
expect(() => buildForm(invalidFields, form)).toThrowError(
"Invalid field type: invalid"
);
});

it("uses custom components when provided", () => {
const customFields = [{ type: "joana", name: "description", value: "" }];
const customComponents = {
joana: InputField,
} as unknown as Parameters<typeof buildForm>[3];
const result = buildForm(customFields, form, 0, customComponents);
expect(result[0].type).toBe(InputField);
});
});
39 changes: 39 additions & 0 deletions tests/components/FormBuilder/useFormField.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import React from "react";
import { describe, it, expect } from "vitest";
import { renderHook } from "@testing-library/react";
import { FormProvider, useForm } from "react-hook-form";
import {
FormFieldProvider,
FormItemProvider,
useFormField,
} from "#/components";

describe("useFormField", () => {
const wrapper = ({ children }) => {
const methods = useForm();
return (
<FormProvider {...methods}>
<FormFieldProvider name="test" render={() => <div />}>
<FormItemProvider>{children}</FormItemProvider>
</FormFieldProvider>
</FormProvider>
);
};
it("returns the correct field state and item state", () => {
const { result } = renderHook(() => useFormField(), { wrapper });
expect(result.current.name).toBe("test");
expect(result.current.id).toBeTruthy();
});

it("returns the field state from the form context", () => {
const { result } = renderHook(() => useFormField(), { wrapper });
expect(result.current.name).toBe("test");
expect(result.current.isTouched).toBe(false);
});

it("throws an error when used outside of FormField", () => {
expect(() => renderHook(() => useFormField())).toThrowError(
/useFormFieldState must be used within a FormFieldProvider/
);
});
});
41 changes: 41 additions & 0 deletions tests/components/FormBuilder/withConditional.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import * as React from "react";
import { describe, it, expect } from "vitest";
import { render, renderHook, screen } from "@testing-library/react";
import { useForm } from "react-hook-form";
import { withConditional } from "#/components";

describe("withConditional", () => {
const TestComponent = () => <div>Test</div>;
const ConditionalComponent = withConditional(TestComponent);

it("renders the wrapped component when conditions are satisfied", () => {
const field = {
name: "test",
type: "input",
value: "",
conditions: [{ name: "test", value: "value" }],
};
const form = renderHook(() =>
useForm({
defaultValues: { test: "value" },
})
).result.current;
// @ts-expect-error TS(2345) FIXME: Argument of type 'React.ComponentType<CommonFieldProps<BaseField>>' is not assignable to parameter of type 'React.ComponentType<CommonFieldProps<BaseField>>'.
render(<ConditionalComponent field={field} form={form} />);
const testElement = screen.getByText("Test");
expect(testElement).toBeInTheDocument();
});

it("does not render the wrapped component when conditions are not satisfied", () => {
const field = {
name: "test",
type: "input",
value: "",
conditions: [{ name: "test", value: "value" }],
};
const form = renderHook(() => useForm()).result.current;
render(<ConditionalComponent field={field} form={form} />);
const testElement = screen.queryByText("Test");
expect(testElement).not.toBeInTheDocument();
});
});
Loading

0 comments on commit 659b55a

Please sign in to comment.