Skip to content

Commit

Permalink
More tests
Browse files Browse the repository at this point in the history
  • Loading branch information
hariombalhara committed Aug 12, 2024
1 parent 7901b2e commit ba5d122
Show file tree
Hide file tree
Showing 9 changed files with 220 additions and 41 deletions.
Original file line number Diff line number Diff line change
@@ -1,34 +1,64 @@
import { TooltipProvider } from "@radix-ui/react-tooltip";
import { render, screen } from "@testing-library/react";
import { render, fireEvent, screen } from "@testing-library/react";
import * as React from "react";
import type { UseFormReturn } from "react-hook-form";
import { FormProvider, useForm } from "react-hook-form";
import { expect } from "vitest";

import { getBookingFieldsWithSystemFields } from "../../../lib/getBookingFields";
import { BookingFields } from "./BookingFields";

// eslint-disable-next-line @typescript-eslint/no-explicit-any
type FormMethods = UseFormReturn<any>;

const renderComponent = ({
props: props,
formDefaultValues,
}: {
props: Parameters<typeof BookingFields>[0];
// eslint-disable-next-line @typescript-eslint/no-explicit-any
formDefaultValues?: any;
}) => {
let formMethods: UseFormReturn | undefined;
const Wrapper = ({ children }: { children: React.ReactNode }) => {
const form = useForm({
defaultValues: formDefaultValues,
});
formMethods = form;
return (
<TooltipProvider>
<FormProvider {...form}>{children}</FormProvider>
</TooltipProvider>
);
};
return render(<BookingFields {...props} />, { wrapper: Wrapper });
const result = render(<BookingFields {...props} />, { wrapper: Wrapper });
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
return { result, formMethods: formMethods! };
};

describe("FormBuilderField", () => {
it("basic test", () => {
renderComponent({
describe("BookingFields", () => {
it("should correctly render with location fields", () => {
const AttendeePhoneNumberOption = {
label: "attendee_phone_number",
value: "phone",
};

const OrganizerLinkOption = {
label: "https://google.com",
value: "link",
};

const locations = [
{
type: AttendeePhoneNumberOption.value,
},
{
link: "https://google.com",
type: OrganizerLinkOption.value,
displayLocationPublicly: true,
},
];
const { formMethods } = renderComponent({
props: {
fields: getBookingFieldsWithSystemFields({
disableGuests: false,
Expand All @@ -37,22 +67,115 @@ describe("FormBuilderField", () => {
workflows: [],
customInputs: [],
}),
locations: [
{
type: "phone",
},
{
link: "https://google.com",
type: "link",
displayLocationPublicly: true,
},
],
locations,
isDynamicGroupBooking: false,
bookingData: null,
},
formDefaultValues: {},
});
screen.getByText("email_address");
screen.logTestingPlaygroundURL();

component.fillName({ value: "John Doe" });
component.fillEmail({ value: "john.doe@example.com" });
component.fillNotes({ value: "This is a note" });
expectScenarios.expectNameToBe({ value: "John Doe", formMethods });
expectScenarios.expectEmailToBe({ value: "john.doe@example.com", formMethods });
expectScenarios.expectNotesToBe({ value: "This is a note", formMethods });

component.fillRadioInputLocation({ label: AttendeePhoneNumberOption.label, inputValue: "+1234567890" });
expectScenarios.expectLocationToBe({
formMethods,
label: AttendeePhoneNumberOption.label,
toMatch: {
formattedValue: "+1 (234) 567-890",
value: { optionValue: "+1234567890", value: AttendeePhoneNumberOption.value },
},
});

component.fillRadioInputLocation({ label: OrganizerLinkOption.label });
expectScenarios.expectLocationToBe({
formMethods,
label: OrganizerLinkOption.label,
toMatch: {
formattedValue: "+1 (234) 567-890",
value: { optionValue: "", value: OrganizerLinkOption.value },
},
});
});
});

const component = {
getName: ({ label = "your_name" }: { label?: string } = {}) =>
screen.getByRole("textbox", {
name: new RegExp(label),
}) as HTMLInputElement,
getEmail: () => screen.getByRole("textbox", { name: /email/i }) as HTMLInputElement,
getLocationRadioOption: ({ label }: { label: string }) =>
screen.getByRole("radio", { name: new RegExp(label) }) as HTMLInputElement,
getLocationRadioInput: ({ placeholder }: { placeholder: string }) =>
screen.getByPlaceholderText(placeholder) as HTMLInputElement,
getNotes: () => screen.getByRole("textbox", { name: /additional_notes/i }) as HTMLInputElement,
getGuests: () => screen.getByLabelText("guests"),
fillName: ({ value }: { value: string }) => {
fireEvent.change(component.getName(), { target: { value } });
},
fillEmail: ({ value }: { value: string }) => {
fireEvent.change(component.getEmail(), { target: { value } });
},
fillRadioInputLocation: ({ label, inputValue }: { label: string; inputValue?: string }) => {
fireEvent.click(component.getLocationRadioOption({ label }));

if (inputValue) {
let placeholder = label;
if (label === "attendee_phone_number") {
placeholder = "enter_phone_number";
} else {
// radioInput doesn't have a label, so we need to identify by placeholder
throw new Error("Tell me how to identify the placeholder for this location input");
}
fireEvent.change(component.getLocationRadioInput({ placeholder }), {
target: { value: inputValue },
});
}
},
fillNotes: ({ value }: { value: string }) => {
fireEvent.change(component.getNotes(), { target: { value } });
},
};

const expectScenarios = {
expectNameToBe: ({ value, formMethods }: { value: string; formMethods: FormMethods }) => {
expect(component.getName().value).toEqual(value);
expect(formMethods.getValues("responses.name")).toEqual(value);
},
expectEmailToBe: ({ value, formMethods }: { value: string; formMethods: FormMethods }) => {
expect(component.getEmail().value).toEqual(value);
expect(formMethods.getValues("responses.email")).toEqual(value);
},
expectLocationToBe: ({
formMethods,
label,
toMatch: { formattedValue, value },
}: {
label: string;
toMatch: {
formattedValue?: string;
value: {
optionValue: string;
value: string;
};
};
formMethods: FormMethods;
}) => {
expect(component.getLocationRadioOption({ label }).checked).toBe(true);
if (value.optionValue) {
expect(component.getLocationRadioInput({ placeholder: "enter_phone_number" }).value).toEqual(
formattedValue
);
}
expect(formMethods.getValues("responses.location")).toEqual(value);
},
expectNotesToBe: ({ value, formMethods }: { value: string; formMethods: FormMethods }) => {
expect(component.getNotes().value).toEqual(value);
expect(formMethods.getValues("responses.notes")).toEqual(value);
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,6 @@ export const BookingFields = ({

// TODO: Instead of `getLocationOptionsForSelect` options should be retrieved from dataStore[field.getOptionsAt]. It would make it agnostic of the `name` of the field.
const options = getLocationOptionsForSelect(locations, t);
console.log({ options });
options.forEach((option) => {
const optionInput = optionsInputs[option.value as keyof typeof optionsInputs];
if (optionInput) {
Expand Down Expand Up @@ -133,7 +132,6 @@ export const BookingFields = ({
};
});
}
console.log({ field });

return (
<FormBuilderField className="mb-4" field={{ ...field, hidden }} readOnly={readOnly} key={index} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ export default function getLocationsOptionsForSelect(
locations: LocationObject[],
t: ReturnType<typeof useLocale>["t"]
) {
console.log({ locations: JSON.stringify(locations) });
return locations
.map((location) => {
const eventLocation = getEventLocationType(location.type);
Expand Down
14 changes: 8 additions & 6 deletions packages/features/form-builder/Components.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -97,16 +97,16 @@ type Component =
export const Components: Record<FieldType, Component> = {
text: {
propsType: propsTypes.text,
factory: (props) => <Widgets.TextWidget noLabel={true} {...props} />,
factory: (props) => <Widgets.TextWidget id={props.name} noLabel={true} {...props} />,
},
textarea: {
propsType: propsTypes.textarea,
// TODO: Make rows configurable in the form builder
factory: (props) => <Widgets.TextAreaWidget rows={3} {...props} />,
factory: (props) => <Widgets.TextAreaWidget id={props.name} rows={3} {...props} />,
},
number: {
propsType: propsTypes.number,
factory: (props) => <Widgets.NumberWidget noLabel={true} {...props} />,
factory: (props) => <Widgets.NumberWidget id={props.name} noLabel={true} {...props} />,
},
name: {
propsType: propsTypes.name,
Expand Down Expand Up @@ -211,14 +211,15 @@ export const Components: Record<FieldType, Component> = {
if (!props) {
return <div />;
}
return <Widgets.TextWidget type="email" noLabel={true} {...props} />;
return <Widgets.TextWidget type="email" id={props.name} noLabel={true} {...props} />;
},
},
address: {
propsType: propsTypes.address,
factory: (props) => {
return (
<AddressInput
id={props.name}
onChange={(val) => {
props.setValue(val);
}}
Expand Down Expand Up @@ -247,6 +248,7 @@ export const Components: Record<FieldType, Component> = {
{value.map((field, index) => (
<li key={index}>
<EmailField
id={`${props.name}.${index}`}
disabled={readOnly}
value={value[index]}
className={inputClassName}
Expand Down Expand Up @@ -318,7 +320,7 @@ export const Components: Record<FieldType, Component> = {
...props,
listValues: props.options.map((o) => ({ title: o.label, value: o.value })),
};
return <Widgets.MultiSelectWidget {...newProps} />;
return <Widgets.MultiSelectWidget id={props.name} {...newProps} />;
},
},
select: {
Expand All @@ -328,7 +330,7 @@ export const Components: Record<FieldType, Component> = {
...props,
listValues: props.options.map((o) => ({ title: o.label, value: o.value })),
};
return <Widgets.SelectWidget {...newProps} />;
return <Widgets.SelectWidget id={props.name} {...newProps} />;
},
},
checkbox: {
Expand Down
1 change: 0 additions & 1 deletion packages/features/form-builder/FormBuilder.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,6 @@ describe("FormBuilder", () => {
return field.name === "location" ? true : false;
},
},
// TODO: May be we should get this from getBookingFields directly which tests more practical cases
formDefaultValues: {
fields: [getLocationBookingField()],
},
Expand Down
56 changes: 50 additions & 6 deletions packages/features/form-builder/FormBuilderField.test.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { render } from "@testing-library/react";
import { render, screen, fireEvent } from "@testing-library/react";
import * as React from "react";
import { FormProvider, useForm } from "react-hook-form";
import type { UseFormReturn } from "react-hook-form";
import { vi } from "vitest";

import { FormBuilderField } from "./FormBuilderField";
import { getLocationBookingField } from "./testUtils";

vi.mock("@formkit/auto-animate/react", () => ({
useAutoAnimate: () => [null],
Expand All @@ -15,26 +15,70 @@ const renderComponent = ({
formDefaultValues,
}: {
props: Parameters<typeof FormBuilderField>[0];
// eslint-disable-next-line @typescript-eslint/no-explicit-any
formDefaultValues?: any;
}) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let formMethods: UseFormReturn<any> | undefined;
const Wrapper = ({ children }: { children: React.ReactNode }) => {
const form = useForm({
defaultValues: formDefaultValues,
});
formMethods = form;
return <FormProvider {...form}>{children}</FormProvider>;
};
return render(<FormBuilderField {...props} />, { wrapper: Wrapper });
render(<FormBuilderField {...props} />, { wrapper: Wrapper });
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
return { formMethods: formMethods! };
};

describe("FormBuilderField", () => {
it("basic test", () => {
renderComponent({
it("verify a text type input field", () => {
const { formMethods } = renderComponent({
props: {
field: getLocationBookingField(),
field: {
name: "textInput1",
type: "text",
label: "Text Input 1",
placeholder: "Enter text",
},
readOnly: false,
className: "",
},
formDefaultValues: {},
});
expect(component.getFieldInput({ label: "Text Input 1" }).value).toEqual("");
component.fillFieldInput({ label: "Text Input 1", value: "test" });
expectScenario.toHaveFieldValue({
label: "Text Input 1",
identifier: "textInput1",
value: "test",
formMethods,
});
});
});

const component = {
getFieldInput: ({ label }) => screen.getByRole("textbox", { name: label }) as HTMLInputElement,
fillFieldInput: ({ label, value }: { label: string; value: string }) => {
fireEvent.change(component.getFieldInput({ label }), { target: { value } });
},
};

const expectScenario = {
toHaveFieldValue: ({
identifier,
label,
value,
formMethods,
}: {
identifier: string;
label: string;
value: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
formMethods: UseFormReturn<any>;
}) => {
expect(component.getFieldInput({ label }).value).toEqual(value);
expect(formMethods.getValues(`responses.${identifier}`)).toEqual(value);
},
};
Loading

0 comments on commit ba5d122

Please sign in to comment.