Skip to content

Commit

Permalink
Add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
hariombalhara committed Aug 9, 2024
1 parent a7d8dce commit cc2fa98
Show file tree
Hide file tree
Showing 7 changed files with 431 additions and 187 deletions.
2 changes: 1 addition & 1 deletion apps/web/components/eventtype/EventAdvancedTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupProps,
},
}}
shouldConsiderRequired={(field: BookingField) => {
return field.name === "location" ? true : !!field.required;
return field.name === "location" ? true : field.required;
}}
/>
</div>
Expand Down
177 changes: 177 additions & 0 deletions packages/features/form-builder/FormBuilder.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
import { TooltipProvider } from "@radix-ui/react-tooltip";
import { render } from "@testing-library/react";
import type { ReactNode } from "react";
import { React } from "react";
import { FormProvider, useForm } from "react-hook-form";
import { vi } from "vitest";

import { FormBuilder } from "./FormBuilder";
import {
mockProps,
verifier,
setMockIntersectionObserver,
setMockMatchMedia,
pageObject,
expectScenario,
} from "./testUtils";

vi.mock("@formkit/auto-animate/react", () => ({
useAutoAnimate: () => [null],
}));

const renderComponent = ({
formBuilderProps: formBuilderProps,
formDefaultValues: formDefaultValues,
}: {
formBuilderProps: Parameters<typeof FormBuilder>[0];
formDefaultValues;
}) => {
const Wrapper = ({ children }: { children: ReactNode }) => {
const form = useForm({
defaultValues: formDefaultValues,
});
return (
<TooltipProvider>
<FormProvider {...form}>{children}</FormProvider>
</TooltipProvider>
);
};

return render(<FormBuilder {...formBuilderProps} />, { wrapper: Wrapper });
};

describe("FormBuilder", () => {
beforeAll(() => {
setMockMatchMedia();
setMockIntersectionObserver();
});

describe("Basic Tests", () => {
const fieldTypes = [
{ fieldType: "email", label: "Email Field" },
{ fieldType: "phone", label: "Phone Field" },
{ fieldType: "address", label: "Address Field" },
{ fieldType: "text", label: "Short Text Field" },
{ fieldType: "number", label: "Number Field" },
{ fieldType: "textarea", label: "LongText Field" },
{ fieldType: "select", label: "Select Field" },
{ fieldType: "multiselect", label: "MultiSelect Field" },
{ fieldType: "multiemail", label: "Multiple Emails Field" },
{ fieldType: "checkbox", label: "CheckBox Group Field" },
{ fieldType: "radio", label: "Radio Group Field" },
{ fieldType: "boolean", label: "Checkbox Field" },
];
beforeEach(() => {
renderComponent({ formBuilderProps: mockProps, formDefaultValues: {} });
});

for (const { fieldType, label } of fieldTypes) {
it(`Should add new field of type ${fieldType} `, async () => {
const defaultIdentifier = `${fieldType}-id`;
const newIdentifier = `${defaultIdentifier}-edited`;

await verifier.verifyFieldAddition({
fieldType,
identifier: defaultIdentifier,
label,
});

await verifier.verifyIdentifierChange({
newIdentifier: newIdentifier,
existingIdentifier: defaultIdentifier,
});

await verifier.verifyThatFieldCanBeMarkedOptional({
identifier: newIdentifier,
});

await verifier.verifyFieldToggle({ identifier: newIdentifier });

await verifier.verifyFieldDeletion({ identifier: newIdentifier });
});
}
});

describe("radioInput type field with options available in dataStore", () => {
test('Should add new field of type "radioInput" and select option from dataStore', async () => {
const field = {
identifier: "location",
type: "radioInput",
};
renderComponent({
formBuilderProps: {
...mockProps,
dataStore: {
options: {
locations: {
value: [
{
label: "Attendee Phone",
value: "phone",
inputPlaceholder: "Phone Number",
},
],
source: { label: "Location" },
},
},
},
// eslint-disable-next-line @typescript-eslint/no-unused-vars
shouldConsiderRequired: ({ field: any }) => {
field.name === "location" ? true : false;
},
},
// TODO: May be we should get this from getBookingFields directly which tests more practical cases
formDefaultValues: {
fields: [
{
defaultLabel: "location",
type: field.type,
name: field.identifier,
editable: "system",
hideWhenJustOneOption: true,
required: false,
getOptionsAt: "locations",
optionsInputs: {
attendeeInPerson: {
type: "address",
required: true,
placeholder: "",
},
phone: {
type: "phone",
required: true,
placeholder: "",
},
},
},
],
},
});

// editable:'system' field can't be deleted
expect(pageObject.queryDeleteButton({ identifier: field.identifier })).toBeNull();
// editable:'system' field can't be toggled
expect(pageObject.queryToggleButton({ identifier: field.identifier })).toBeNull();

const newFieldDialog = pageObject.openAddFieldDialog();

// radioInput type field isn't selectable by the user.
expect(
pageObject.dialog.fieldTypeDropdown.queryOptionForFieldType({
dialog: newFieldDialog,
fieldType: "radioInput",
})
).toBeNull();

pageObject.dialog.close({ dialog: newFieldDialog });

const dialog = pageObject.openEditFieldDialog({ identifier: field.identifier });

expectScenario.toHaveFieldTypeDropdownDisabled({ dialog });
expectScenario.toHaveIdentifierChangeDisabled({ dialog });
expectScenario.toHaveRequirablityToggleDisabled({ dialog });
expectScenario.toHaveLabelChangeAllowed({ dialog });
expect(pageObject.dialog.isFieldShowingAsRequired({ dialog })).toBe(true);
});
});
});
13 changes: 9 additions & 4 deletions packages/features/form-builder/FormBuilder.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,11 @@ export const FormBuilder = function FormBuilder({
}
>;
};
shouldConsiderRequired?: (field: RhfFormField) => boolean;
/**
* This is kind of a hack to allow certain fields to be just shown as required when they might not be required in a strict sense
* e.g. Location field has a default value at backend so API can send no location but formBuilder in UI doesn't allow it.
*/
shouldConsiderRequired?: (field: RhfFormField) => boolean | undefined;
}) {
// I would have liked to give Form Builder it's own Form but nested Forms aren't something that browsers support.
// So, this would reuse the same Form as the parent form.
Expand Down Expand Up @@ -322,6 +326,7 @@ export const FormBuilder = function FormBuilder({
data: null,
});
}}
shouldConsiderRequired={shouldConsiderRequired}
/>
)}
</div>
Expand Down Expand Up @@ -424,7 +429,7 @@ function FieldEditDialog({
dialog: { isOpen: boolean; fieldIndex: number; data: RhfFormField | null };
onOpenChange: (isOpen: boolean) => void;
handleSubmit: SubmitHandler<RhfFormField>;
shouldConsiderRequired?: (field: RhfFormField) => boolean;
shouldConsiderRequired?: (field: RhfFormField) => boolean | undefined;
}) {
const { t } = useLocale();
const fieldForm = useForm<RhfFormField>({
Expand Down Expand Up @@ -530,10 +535,10 @@ function FieldEditDialog({
<Controller
name="required"
control={fieldForm.control}
render={({ field: { onChange } }) => {
render={({ field: { value, onChange } }) => {
const isRequired = shouldConsiderRequired
? shouldConsiderRequired(fieldForm.getValues())
: fieldForm.getValues("required");
: value;
return (
<BooleanToggleGroupField
data-testid="field-required"
Expand Down
Loading

0 comments on commit cc2fa98

Please sign in to comment.