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( , ); // 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( , ); 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( , ); 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( , ); // 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( , ); 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( , ); 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( , ); 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( , ); // 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( , ); // 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("برلین مرکزی"); }); });