* feat:add prefilcheck check box in the field creation * chore:formatted the li8 translations * feat:add e2e test case for this feature * refactor:reanmed the key from prefilledCheck to disableOnPrefill and moved the readOnly logic to formbuilderField * chore:fixed the type check * fix:if prefilled has error. do not disabble it * chore:removed the e2e tests for prefill in e2e * chore:revert back to old changes * chore:remove unwanted code frome2e * Support name field as well * refactor:handled the feedback changes. still unit test cases pending * feat: add unit tests for disable field on prefill logic * chore: resolved the type check and typos * chore:renamed the files and type clean up * Apply suggestions from code review * chore:renaming test files and moving the disableonprefill hook to formbuilderFeild. * refactor: add unit test for serach params and form values check and minor code formatting and file name changes * fix: validating the input value partially at the intialising phase. so that form response values and from ui values are same and making sure invalid data not sent to backend on submit. * chore:fix the type check issue * fix: field value valdiation at initilisation and add the unit test cases for that change * chore:add comments to the tests * chore:resolve type check * chore: moved the tests to the formBuilder * Simplify codebase by ensuring select and multiselect changes the value when no option is found * Relocate and refactor tests * handle special cases * Dont disable for dirty fields * Update schema.prisma --------- Co-authored-by: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com> Co-authored-by: Hariom <hariombalhara@gmail.com>
555 lines
16 KiB
TypeScript
555 lines
16 KiB
TypeScript
import type { Control } from "react-hook-form";
|
|
import { useFormContext } from "react-hook-form";
|
|
import { describe, expect, vi, beforeEach, test } from "vitest";
|
|
|
|
import { useRouterQuery } from "@calcom/lib/hooks/useRouterQuery";
|
|
|
|
import { useShouldBeDisabledDueToPrefill } from "./useShouldBeDisabledDueToPrefill";
|
|
|
|
vi.mock("@calcom/lib/hooks/useRouterQuery", () => ({
|
|
useRouterQuery: vi.fn(),
|
|
}));
|
|
|
|
// Mock `react-hook-form` module
|
|
vi.mock("react-hook-form", async () => {
|
|
const actual: any = await vi.importActual("react-hook-form");
|
|
return {
|
|
...actual,
|
|
useFormContext: vi.fn(),
|
|
};
|
|
});
|
|
|
|
const defaultField = {
|
|
name: "test-field",
|
|
type: "text" as const,
|
|
};
|
|
|
|
type FormState = {
|
|
dirtyFields: {
|
|
responses: Record<string, boolean>;
|
|
};
|
|
};
|
|
|
|
const buildFormState = (formState?: FormState) => {
|
|
formState = formState || {
|
|
dirtyFields: {
|
|
responses: {},
|
|
},
|
|
};
|
|
return {
|
|
...formState,
|
|
};
|
|
};
|
|
|
|
const buildFormStateWithErrorForField = ({
|
|
fieldName,
|
|
formState,
|
|
}: {
|
|
fieldName: string;
|
|
formState?: FormState;
|
|
}) => {
|
|
return {
|
|
...buildFormState(formState),
|
|
errors: {
|
|
responses: {
|
|
message: `{${fieldName}}Some error in the field`,
|
|
},
|
|
},
|
|
};
|
|
};
|
|
|
|
const buildFormStateWithNoErrors = (formState?: FormState) => {
|
|
return buildFormState(formState);
|
|
};
|
|
|
|
const getMockFormContext = ({
|
|
formState,
|
|
responses,
|
|
}: {
|
|
formState: FormState;
|
|
responses: Record<string, string | string[]>;
|
|
}) => {
|
|
return {
|
|
formState,
|
|
control: {} as Control,
|
|
getValues: () => ({
|
|
responses,
|
|
}),
|
|
};
|
|
};
|
|
|
|
function mockFormContext({ formState, responses }: { formState: any; responses: any }) {
|
|
(vi.mocked(useFormContext) as any).mockReturnValue(getMockFormContext({ formState, responses }));
|
|
}
|
|
|
|
function mockScenario({
|
|
formState,
|
|
responses,
|
|
searchParams,
|
|
}: {
|
|
formState: any;
|
|
responses: any;
|
|
searchParams: any;
|
|
}) {
|
|
mockFormContext({ formState, responses });
|
|
vi.mocked(useRouterQuery).mockReturnValue(searchParams);
|
|
}
|
|
|
|
describe("useShouldBeDisabledDueToPrefill", () => {
|
|
beforeEach(() => {
|
|
// Reset mocks before each test
|
|
vi.resetAllMocks();
|
|
});
|
|
|
|
describe("When disableOnPrefill is true", () => {
|
|
test("should return `false` for a field if there is an error for the field", () => {
|
|
const field = {
|
|
...defaultField,
|
|
disableOnPrefill: true,
|
|
};
|
|
mockScenario({
|
|
formState: buildFormStateWithErrorForField({ fieldName: field.name }),
|
|
responses: {
|
|
[field.name]: "TestValue",
|
|
},
|
|
searchParams: {
|
|
[field.name]: "TestValue",
|
|
},
|
|
});
|
|
const shouldBeDisabled = useShouldBeDisabledDueToPrefill(field);
|
|
|
|
expect(shouldBeDisabled).toBe(false);
|
|
});
|
|
|
|
test("should return `true` for a field if there is an error but not for that field", () => {
|
|
const field = {
|
|
...defaultField,
|
|
disableOnPrefill: true,
|
|
};
|
|
mockScenario({
|
|
formState: buildFormStateWithErrorForField({ fieldName: "some-other-field" }),
|
|
responses: {
|
|
[field.name]: "TestValue",
|
|
},
|
|
searchParams: {
|
|
[field.name]: "TestValue",
|
|
},
|
|
});
|
|
const shouldBeDisabled = useShouldBeDisabledDueToPrefill(field);
|
|
|
|
expect(shouldBeDisabled).toBe(true);
|
|
});
|
|
|
|
test("should return `true` for a field if there are no errors", () => {
|
|
const field = {
|
|
...defaultField,
|
|
disableOnPrefill: true,
|
|
};
|
|
mockScenario({
|
|
formState: buildFormStateWithNoErrors(),
|
|
responses: {
|
|
[field.name]: "TestValue",
|
|
},
|
|
searchParams: {
|
|
[field.name]: "TestValue",
|
|
},
|
|
});
|
|
const shouldBeDisabled = useShouldBeDisabledDueToPrefill(field);
|
|
|
|
expect(shouldBeDisabled).toBe(true);
|
|
});
|
|
|
|
test("should return `false` when the responses and searchParams both are empty(handles the undefined === undefined case)", () => {
|
|
const field = {
|
|
...defaultField,
|
|
disableOnPrefill: true,
|
|
};
|
|
mockScenario({
|
|
formState: buildFormStateWithNoErrors(),
|
|
responses: {},
|
|
searchParams: {},
|
|
});
|
|
const shouldBeDisabled = useShouldBeDisabledDueToPrefill(field);
|
|
|
|
expect(shouldBeDisabled).toBe(false);
|
|
});
|
|
|
|
describe("Special handling of radioInput and variantsConfig type fields", () => {
|
|
describe("radioInput type field", () => {
|
|
test(`should return true for radioInput type field when the searchParams value is set and response is also set even though it doesn't match`, () => {
|
|
const field = {
|
|
...defaultField,
|
|
type: "radioInput" as const,
|
|
disableOnPrefill: true,
|
|
optionsInputs: {
|
|
phone: {
|
|
type: "phone" as const,
|
|
},
|
|
},
|
|
};
|
|
mockScenario({
|
|
formState: buildFormStateWithNoErrors(),
|
|
searchParams: {
|
|
[field.name]: "TestValue1",
|
|
},
|
|
responses: {
|
|
[field.name]: {
|
|
value: "phone",
|
|
optionValue: "TestValue2",
|
|
},
|
|
},
|
|
});
|
|
const shouldBeDisabled = useShouldBeDisabledDueToPrefill(field);
|
|
expect(shouldBeDisabled).toBe(true);
|
|
});
|
|
|
|
test(`should return false for radioInput type field when the searchParams value is set and response is also set but it isn't valid`, () => {
|
|
const field = {
|
|
...defaultField,
|
|
type: "radioInput" as const,
|
|
disableOnPrefill: true,
|
|
optionsInputs: {
|
|
phone: {
|
|
type: "phone" as const,
|
|
},
|
|
},
|
|
};
|
|
mockScenario({
|
|
formState: buildFormStateWithNoErrors(),
|
|
searchParams: {
|
|
[field.name]: "TestValue1",
|
|
},
|
|
responses: {
|
|
[field.name]: "TestValue2",
|
|
},
|
|
});
|
|
const shouldBeDisabled = useShouldBeDisabledDueToPrefill(field);
|
|
expect(shouldBeDisabled).toBe(false);
|
|
});
|
|
|
|
test(`should return false for radioInput type field when the searchParams value is set and response is also set but it radioInput value isn't completely filled(optionsInputs case)`, () => {
|
|
const field = {
|
|
...defaultField,
|
|
type: "radioInput" as const,
|
|
disableOnPrefill: true,
|
|
optionsInputs: {
|
|
phone: {
|
|
type: "phone" as const,
|
|
},
|
|
},
|
|
};
|
|
mockScenario({
|
|
formState: buildFormStateWithNoErrors(),
|
|
searchParams: {
|
|
[field.name]: "TestValue1",
|
|
},
|
|
responses: {
|
|
[field.name]: {
|
|
value: "phone",
|
|
// Not filled
|
|
optionValue: "",
|
|
},
|
|
},
|
|
});
|
|
const shouldBeDisabled = useShouldBeDisabledDueToPrefill(field);
|
|
expect(shouldBeDisabled).toBe(false);
|
|
});
|
|
|
|
test(`should return false for radioInput type field when the searchParams value is set and response is also set but it radioInput value isn't completely filled`, () => {
|
|
const field = {
|
|
...defaultField,
|
|
type: "radioInput" as const,
|
|
disableOnPrefill: true,
|
|
optionsInputs: {
|
|
not_phone: {
|
|
type: "phone" as const,
|
|
},
|
|
},
|
|
};
|
|
mockScenario({
|
|
formState: buildFormStateWithNoErrors(),
|
|
searchParams: {
|
|
[field.name]: "TestValue1",
|
|
},
|
|
responses: {
|
|
[field.name]: {
|
|
value: "phone",
|
|
// Not filled but doesn't matter because phone type doesn't need optionValue as per the optionsInputs
|
|
optionValue: "",
|
|
},
|
|
},
|
|
});
|
|
const shouldBeDisabled = useShouldBeDisabledDueToPrefill(field);
|
|
expect(shouldBeDisabled).toBe(true);
|
|
});
|
|
|
|
test(`should return false for radioInput type field if some other field is set in the searchParams`, () => {
|
|
const field = {
|
|
...defaultField,
|
|
type: "radioInput" as const,
|
|
disableOnPrefill: true,
|
|
};
|
|
mockScenario({
|
|
formState: buildFormStateWithNoErrors(),
|
|
searchParams: {
|
|
["some-other-field"]: "TestValue1",
|
|
},
|
|
responses: {},
|
|
});
|
|
const shouldBeDisabled = useShouldBeDisabledDueToPrefill(field);
|
|
expect(shouldBeDisabled).toBe(false);
|
|
});
|
|
});
|
|
|
|
test(`should return true for field with variantsConfig when the searchParams value is set and response is also set even though it doesn't match`, () => {
|
|
const field = {
|
|
...defaultField,
|
|
type: "text" as const,
|
|
variantsConfig: {
|
|
variants: {
|
|
TestValue1: {
|
|
fields: [],
|
|
},
|
|
},
|
|
},
|
|
disableOnPrefill: true,
|
|
};
|
|
mockScenario({
|
|
formState: buildFormStateWithNoErrors(),
|
|
searchParams: {
|
|
[field.name]: "TestValue1",
|
|
},
|
|
responses: {
|
|
[field.name]: "TestValue2",
|
|
},
|
|
});
|
|
const shouldBeDisabled = useShouldBeDisabledDueToPrefill(field);
|
|
expect(shouldBeDisabled).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe("for array type value", () => {
|
|
test(`should return 'true' when there is complete match of values b/w the searchParams and the responses prefilled`, () => {
|
|
const field = {
|
|
...defaultField,
|
|
disableOnPrefill: true,
|
|
};
|
|
mockScenario({
|
|
formState: buildFormStateWithNoErrors(),
|
|
responses: {
|
|
[field.name]: ["TestValue1", "TestValue2"],
|
|
},
|
|
searchParams: {
|
|
[field.name]: ["TestValue1", "TestValue2"],
|
|
},
|
|
});
|
|
const shouldBeDisabled = useShouldBeDisabledDueToPrefill(field);
|
|
expect(shouldBeDisabled).toBe(true);
|
|
});
|
|
|
|
test(`should return 'true' even when searchParams value partially exists in the responses`, () => {
|
|
const field = {
|
|
...defaultField,
|
|
disableOnPrefill: true,
|
|
};
|
|
mockScenario({
|
|
formState: buildFormStateWithNoErrors(),
|
|
responses: {
|
|
[field.name]: ["TestValue1", "TestValue2"],
|
|
},
|
|
searchParams: {
|
|
[field.name]: ["TestValue1"],
|
|
},
|
|
});
|
|
const shouldBeDisabled = useShouldBeDisabledDueToPrefill(field);
|
|
expect(shouldBeDisabled).toBe(true);
|
|
});
|
|
|
|
test(`should return 'true' even when responses value partially exists in the searchParams`, () => {
|
|
const field = {
|
|
...defaultField,
|
|
disableOnPrefill: true,
|
|
};
|
|
mockScenario({
|
|
formState: buildFormStateWithNoErrors(),
|
|
responses: {
|
|
[field.name]: ["TestValue1"],
|
|
},
|
|
searchParams: {
|
|
[field.name]: ["TestValue1", "TestValue2"],
|
|
},
|
|
});
|
|
const shouldBeDisabled = useShouldBeDisabledDueToPrefill(field);
|
|
expect(shouldBeDisabled).toBe(true);
|
|
});
|
|
|
|
test(`should return 'false' when there is a mismatch`, () => {
|
|
const field = {
|
|
...defaultField,
|
|
disableOnPrefill: true,
|
|
};
|
|
mockScenario({
|
|
formState: buildFormStateWithNoErrors(),
|
|
responses: {
|
|
[field.name]: ["TestValue3"],
|
|
},
|
|
searchParams: {
|
|
[field.name]: ["TestValue1", "TestValue2"],
|
|
},
|
|
});
|
|
const shouldBeDisabled = useShouldBeDisabledDueToPrefill(field);
|
|
expect(shouldBeDisabled).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe("for number type value", () => {
|
|
test(`should return 'true' when number type value is prefilled`, () => {
|
|
const field = {
|
|
...defaultField,
|
|
disableOnPrefill: true,
|
|
};
|
|
mockScenario({
|
|
formState: buildFormStateWithNoErrors(),
|
|
responses: {
|
|
[field.name]: 123,
|
|
},
|
|
searchParams: {
|
|
[field.name]: 123,
|
|
},
|
|
});
|
|
const shouldBeDisabled = useShouldBeDisabledDueToPrefill(field);
|
|
expect(shouldBeDisabled).toBe(true);
|
|
});
|
|
|
|
test(`should return 'true' when when the searchParams value is a string and the responses value is a number`, () => {
|
|
const field = {
|
|
...defaultField,
|
|
disableOnPrefill: true,
|
|
};
|
|
mockScenario({
|
|
formState: buildFormStateWithNoErrors(),
|
|
responses: {
|
|
[field.name]: "123",
|
|
},
|
|
searchParams: {
|
|
[field.name]: 123,
|
|
},
|
|
});
|
|
const shouldBeDisabled = useShouldBeDisabledDueToPrefill(field);
|
|
expect(shouldBeDisabled).toBe(true);
|
|
});
|
|
|
|
test(`should return 'true' when when the searchParams value is a number and the responses value is a string`, () => {
|
|
const field = {
|
|
...defaultField,
|
|
disableOnPrefill: true,
|
|
};
|
|
mockScenario({
|
|
formState: buildFormStateWithNoErrors(),
|
|
responses: {
|
|
[field.name]: 123,
|
|
},
|
|
searchParams: {
|
|
[field.name]: "123",
|
|
},
|
|
});
|
|
const shouldBeDisabled = useShouldBeDisabledDueToPrefill(field);
|
|
expect(shouldBeDisabled).toBe(true);
|
|
});
|
|
});
|
|
|
|
test("should return `false` for a field even if there are no errors and it is prefilled but the field is dirty", () => {
|
|
const field = {
|
|
...defaultField,
|
|
disableOnPrefill: true,
|
|
};
|
|
mockScenario({
|
|
formState: buildFormStateWithNoErrors({
|
|
dirtyFields: {
|
|
responses: {
|
|
[field.name]: true,
|
|
},
|
|
},
|
|
}),
|
|
responses: {
|
|
[field.name]: "TestValue",
|
|
},
|
|
searchParams: {
|
|
[field.name]: "TestValue",
|
|
},
|
|
});
|
|
const shouldBeDisabled = useShouldBeDisabledDueToPrefill(field);
|
|
|
|
expect(shouldBeDisabled).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe("When disableOnPrefill is false", () => {
|
|
test(`should return 'false' for the field`, () => {
|
|
const field = {
|
|
...defaultField,
|
|
disableOnPrefill: false,
|
|
};
|
|
|
|
mockScenario({
|
|
formState: buildFormStateWithNoErrors(),
|
|
responses: {},
|
|
searchParams: {},
|
|
});
|
|
|
|
const shouldBeDisabled = useShouldBeDisabledDueToPrefill(field);
|
|
expect(shouldBeDisabled).toBe(false);
|
|
});
|
|
|
|
test(`should return 'false' for the field even when the field is prefilled`, () => {
|
|
const field = {
|
|
...defaultField,
|
|
disableOnPrefill: false,
|
|
};
|
|
|
|
mockScenario({
|
|
formState: buildFormStateWithNoErrors(),
|
|
responses: {
|
|
[field.name]: "TestValue",
|
|
},
|
|
searchParams: {
|
|
[field.name]: "TestValue",
|
|
},
|
|
});
|
|
|
|
const shouldBeDisabled = useShouldBeDisabledDueToPrefill(field);
|
|
expect(shouldBeDisabled).toBe(false);
|
|
});
|
|
|
|
test(`should return 'false' for field having variantsConfig even when the field is prefilled`, () => {
|
|
const field = {
|
|
...defaultField,
|
|
variantsConfig: {
|
|
variants: {
|
|
TestValue1: {
|
|
fields: [],
|
|
},
|
|
TestValue2: {
|
|
fields: [],
|
|
},
|
|
},
|
|
},
|
|
disableOnPrefill: false,
|
|
};
|
|
|
|
mockScenario({
|
|
formState: buildFormStateWithNoErrors(),
|
|
responses: {
|
|
[field.name]: "TestValue1",
|
|
},
|
|
searchParams: {
|
|
[field.name]: "TestValue",
|
|
},
|
|
});
|
|
|
|
const shouldBeDisabled = useShouldBeDisabledDueToPrefill(field);
|
|
expect(shouldBeDisabled).toBe(false);
|
|
});
|
|
});
|
|
});
|