You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
351 lines
11 KiB
351 lines
11 KiB
import {
|
|
render,
|
|
screen,
|
|
cleanup,
|
|
fireEvent,
|
|
waitFor,
|
|
} from "@testing-library/react";
|
|
import { describe, it, expect, vi, afterEach } from "vitest";
|
|
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
import { QuestionAnswersProvider } from "./question-answer-storage";
|
|
import { QuestionBirthplace } from "./question-birthplace";
|
|
import QuestionNumber from "./question-number";
|
|
import QuestionText from "./question-text";
|
|
|
|
vi.mock("@/translations/provider", () => ({
|
|
useI18n: vi.fn(() => ({ locale: "fa", dictionary: {} })),
|
|
}));
|
|
|
|
describe("UI Config based behavior", () => {
|
|
afterEach(() => {
|
|
cleanup();
|
|
});
|
|
|
|
const queryClient = new QueryClient({
|
|
defaultOptions: { queries: { retry: false } },
|
|
});
|
|
|
|
it("should trigger GeoIP only when ui_config.enable_geoip is true", () => {
|
|
// With totally random title but ui_config.enable_geoip = true
|
|
const qWithGeo = {
|
|
id: "q1",
|
|
title: "Random Title Here",
|
|
type: "birthplace",
|
|
order: 1,
|
|
required: true,
|
|
isVisible: true,
|
|
description: "",
|
|
tooltip: "",
|
|
extras: {},
|
|
options: [],
|
|
ui_config: { enable_geoip: true },
|
|
} as any;
|
|
|
|
const { rerender } = render(
|
|
<QueryClientProvider client={queryClient}>
|
|
<QuestionAnswersProvider slug="test" questions={[qWithGeo]}>
|
|
<QuestionBirthplace question={qWithGeo} />
|
|
</QuestionAnswersProvider>
|
|
</QueryClientProvider>,
|
|
);
|
|
|
|
// Auto button is present when GeoIP is active
|
|
expect(screen.getByText("خودکار")).toBeDefined();
|
|
|
|
// With title "residence" but no ui_config
|
|
const qWithoutGeo = {
|
|
id: "q2",
|
|
title: "residence test",
|
|
type: "birthplace",
|
|
order: 1,
|
|
required: true,
|
|
isVisible: true,
|
|
description: "",
|
|
tooltip: "",
|
|
extras: {},
|
|
options: [],
|
|
ui_config: {},
|
|
} as any;
|
|
|
|
rerender(
|
|
<QueryClientProvider client={queryClient}>
|
|
<QuestionAnswersProvider slug="test" questions={[qWithoutGeo]}>
|
|
<QuestionBirthplace question={qWithoutGeo} />
|
|
</QuestionAnswersProvider>
|
|
</QueryClientProvider>,
|
|
);
|
|
|
|
expect(screen.queryByText("خودکار")).toBeNull();
|
|
});
|
|
|
|
it("should auto-detect and display city and country when ui_config.enable_geoip is true", async () => {
|
|
const qWithGeo = {
|
|
id: "q_residence",
|
|
title: "Current Residence",
|
|
type: "birthplace",
|
|
order: 1,
|
|
required: true,
|
|
isVisible: true,
|
|
description: "",
|
|
tooltip: "",
|
|
extras: {},
|
|
options: [],
|
|
ui_config: { enable_geoip: true },
|
|
} as any;
|
|
|
|
const { setStoredUserGeoRegion } = await import("@/lib/geo-region");
|
|
const { marriageQueryKeys } = await import("@/hooks/marriage/query-keys");
|
|
setStoredUserGeoRegion({
|
|
city: "Tehran",
|
|
country: "Iran",
|
|
countryCode: "IR",
|
|
phoneCode: "+98",
|
|
});
|
|
|
|
queryClient.setQueryData(marriageQueryKeys.sectionData("test-geoip"), {
|
|
status: "success",
|
|
data: [],
|
|
version: 1,
|
|
});
|
|
|
|
render(
|
|
<QueryClientProvider client={queryClient}>
|
|
<QuestionAnswersProvider slug="test-geoip" questions={[qWithGeo]}>
|
|
<QuestionBirthplace question={qWithGeo} />
|
|
</QuestionAnswersProvider>
|
|
</QueryClientProvider>,
|
|
);
|
|
|
|
await waitFor(() => {
|
|
expect(screen.getByText(/(ایران|Iran).*Tehran/i)).toBeDefined();
|
|
});
|
|
});
|
|
|
|
it("should trigger currency behavior only when ui_config.currency_enabled is true", () => {
|
|
// Title is random, but currency_enabled is true
|
|
const qWithCurrency = {
|
|
id: "q1",
|
|
title: "Random Income",
|
|
type: "number",
|
|
order: 1,
|
|
required: true,
|
|
isVisible: true,
|
|
description: "",
|
|
tooltip: "",
|
|
extras: { placeHolder: "", range: [0, 0] },
|
|
options: [],
|
|
ui_config: { currency_enabled: true },
|
|
} as any;
|
|
|
|
const { rerender } = render(
|
|
<QueryClientProvider client={queryClient}>
|
|
<QuestionAnswersProvider slug="test" questions={[qWithCurrency]}>
|
|
<QuestionNumber question={qWithCurrency} />
|
|
</QuestionAnswersProvider>
|
|
</QueryClientProvider>,
|
|
);
|
|
|
|
// Dropdown arrow should be rendered in currency mode (path is in SVG)
|
|
expect(screen.getByRole("img", { name: "Dropdown chevron" })).toBeDefined();
|
|
|
|
// Title is "Monthly Income", but currency_enabled is false
|
|
const qWithoutCurrency = {
|
|
id: "q2",
|
|
title: "Monthly Income",
|
|
type: "number",
|
|
order: 1,
|
|
required: true,
|
|
isVisible: true,
|
|
description: "",
|
|
tooltip: "",
|
|
extras: { placeHolder: "", range: [0, 0] },
|
|
options: [],
|
|
ui_config: {},
|
|
} as any;
|
|
|
|
rerender(
|
|
<QueryClientProvider client={queryClient}>
|
|
<QuestionAnswersProvider slug="test" questions={[qWithoutCurrency]}>
|
|
<QuestionNumber question={qWithoutCurrency} />
|
|
</QuestionAnswersProvider>
|
|
</QueryClientProvider>,
|
|
);
|
|
|
|
expect(screen.queryByRole("img", { name: "Dropdown chevron" })).toBeNull();
|
|
});
|
|
|
|
it("should render error message from validation.errorMessage or fallback to locale", async () => {
|
|
// Title is random, type is email, custom error message
|
|
const qCustomError = {
|
|
id: "q1",
|
|
title: "Random Email",
|
|
type: "email",
|
|
order: 1,
|
|
required: true,
|
|
isVisible: true,
|
|
description: "",
|
|
tooltip: "",
|
|
extras: { placeHolder: "" },
|
|
options: [],
|
|
validation: { errorMessage: "Custom Backend Error" },
|
|
} as any;
|
|
|
|
const { rerender } = render(
|
|
<QueryClientProvider client={queryClient}>
|
|
<QuestionAnswersProvider slug="test" questions={[qCustomError]}>
|
|
<QuestionText question={qCustomError} />
|
|
</QuestionAnswersProvider>
|
|
</QueryClientProvider>,
|
|
);
|
|
|
|
const input = screen.getByRole("textbox");
|
|
fireEvent.change(input, { target: { value: "invalid_email" } });
|
|
|
|
await waitFor(() => {
|
|
expect(screen.getByText("Custom Backend Error")).toBeDefined();
|
|
});
|
|
|
|
const qFallbackError = {
|
|
id: "q2",
|
|
title: "Random Email",
|
|
type: "email",
|
|
order: 1,
|
|
required: true,
|
|
isVisible: true,
|
|
description: "",
|
|
tooltip: "",
|
|
extras: { placeHolder: "" },
|
|
options: [],
|
|
validation: {},
|
|
} as any;
|
|
|
|
rerender(
|
|
<QueryClientProvider client={queryClient}>
|
|
<QuestionAnswersProvider slug="test" questions={[qFallbackError]}>
|
|
<QuestionText question={qFallbackError} />
|
|
</QuestionAnswersProvider>
|
|
</QueryClientProvider>,
|
|
);
|
|
|
|
const input2 = screen.getByRole("textbox");
|
|
fireEvent.change(input2, { target: { value: "invalid_email_again" } });
|
|
|
|
await waitFor(() => {
|
|
expect(screen.getByText("یک آدرس ایمیل معتبر وارد کنید.")).toBeDefined();
|
|
});
|
|
});
|
|
|
|
it("should preserve city input when country is selected or changed in birthplace", async () => {
|
|
const qBirthplace = {
|
|
id: "birthplace_test",
|
|
title: "Birthplace",
|
|
type: "birthplace",
|
|
order: 1,
|
|
required: true,
|
|
isVisible: true,
|
|
description: "",
|
|
tooltip: "",
|
|
extras: { placeHolder: "City, region" },
|
|
options: [],
|
|
ui_config: {},
|
|
} as any;
|
|
|
|
render(
|
|
<QueryClientProvider client={queryClient}>
|
|
<QuestionAnswersProvider slug="test" questions={[qBirthplace]}>
|
|
<QuestionBirthplace question={qBirthplace} />
|
|
</QuestionAnswersProvider>
|
|
</QueryClientProvider>,
|
|
);
|
|
|
|
// Type city into input
|
|
const cityInput = screen.getByPlaceholderText("City, region");
|
|
expect(document.activeElement).not.toBe(cityInput);
|
|
fireEvent.change(cityInput, { target: { value: "Tehran" } });
|
|
expect((cityInput as HTMLInputElement).value).toBe("Tehran");
|
|
|
|
// Open country dropdown
|
|
const countryButton = screen.getByText("انتخاب کشور");
|
|
fireEvent.click(countryButton);
|
|
expect(document.body.classList.contains("dropdown-open")).toBe(true);
|
|
|
|
// Select Iran
|
|
const iranOption = screen.getByRole("button", { name: "ایران" });
|
|
fireEvent.click(iranOption);
|
|
|
|
// City should still be Tehran!
|
|
expect((cityInput as HTMLInputElement).value).toBe("Tehran");
|
|
await waitFor(() => {
|
|
expect(document.activeElement).toBe(cityInput);
|
|
expect(document.body.classList.contains("dropdown-open")).toBe(false);
|
|
});
|
|
});
|
|
|
|
it("should preserve country when typing city after selecting country", async () => {
|
|
const qBirthplace = {
|
|
id: "birthplace_select_first",
|
|
title: "محل تولد",
|
|
type: "birthplace",
|
|
order: 1,
|
|
required: true,
|
|
isVisible: true,
|
|
description: "",
|
|
tooltip: "",
|
|
extras: { placeHolder: "شهر، منطقه یا محله" },
|
|
options: [],
|
|
ui_config: {},
|
|
} as any;
|
|
|
|
render(
|
|
<QueryClientProvider client={queryClient}>
|
|
<QuestionAnswersProvider slug="test-select-first" questions={[qBirthplace]}>
|
|
<QuestionBirthplace question={qBirthplace} />
|
|
</QuestionAnswersProvider>
|
|
</QueryClientProvider>,
|
|
);
|
|
|
|
// 1. Open country dropdown first
|
|
const countryButton = screen.getByText("انتخاب کشور");
|
|
fireEvent.click(countryButton);
|
|
|
|
// 2. Select Germany (آلمان)
|
|
const germanyOption = screen.getByRole("button", { name: "آلمان" });
|
|
fireEvent.click(germanyOption);
|
|
|
|
await waitFor(() => {
|
|
expect(screen.queryByRole("dialog")).toBeNull();
|
|
});
|
|
|
|
// Country button should show Germany
|
|
expect(screen.getByText("آلمان")).toBeDefined();
|
|
|
|
// 3. Type city in the input field
|
|
const cityInput = screen.getByPlaceholderText("شهر، منطقه یا محله");
|
|
fireEvent.change(cityInput, { target: { value: "برلین" } });
|
|
|
|
// Country button MUST still show Germany and city must show Berlin
|
|
expect(screen.getByText("آلمان")).toBeDefined();
|
|
expect((cityInput as HTMLInputElement).value).toBe("برلین");
|
|
|
|
// 4. Type more characters into city field
|
|
fireEvent.change(cityInput, { target: { value: "برلین مرکزی" } });
|
|
expect(screen.getByText("آلمان")).toBeDefined();
|
|
expect((cityInput as HTMLInputElement).value).toBe("برلین مرکزی");
|
|
|
|
// 5. Change country to France (فرانسه)
|
|
const updatedCountryButton = screen.getByText("آلمان");
|
|
fireEvent.click(updatedCountryButton);
|
|
|
|
const franceOption = screen.getByRole("button", { name: "فرانسه" });
|
|
fireEvent.click(franceOption);
|
|
|
|
await waitFor(() => {
|
|
expect(screen.queryByRole("dialog")).toBeNull();
|
|
});
|
|
|
|
expect(screen.getByText("فرانسه")).toBeDefined();
|
|
expect((cityInput as HTMLInputElement).value).toBe("برلین مرکزی");
|
|
});
|
|
});
|
|
|
|
|