/** * @vitest-environment happy-dom * * BatchKeySection: canLoginWebUi toggle semantics alignment test * Verifies that the toggle uses inverted logic to match add/edit forms: * - Switch ON => canLoginWebUi=false (independent personal usage page) * - Switch OFF => canLoginWebUi=true (restricted Web UI) */ import type { ReactNode } from "react"; import { act } from "react"; import { createRoot } from "react-dom/client"; import { beforeEach, describe, expect, test, vi } from "vitest"; import { BatchKeySection, type BatchKeySectionState, } from "@/app/[locale]/dashboard/_components/user/batch-edit/batch-key-section"; function render(node: ReactNode) { const container = document.createElement("div"); document.body.appendChild(container); const root = createRoot(container); act(() => { root.render(node); }); return { container, unmount: () => { act(() => root.unmount()); container.remove(); }, }; } const createInitialState = (): BatchKeySectionState => ({ providerGroupEnabled: false, providerGroup: "", limit5hUsdEnabled: false, limit5hUsd: "", limitDailyUsdEnabled: false, limitDailyUsd: "", limitWeeklyUsdEnabled: false, limitWeeklyUsd: "", limitMonthlyUsdEnabled: false, limitMonthlyUsd: "", canLoginWebUiEnabled: true, // Enable the field for testing canLoginWebUi: false, // Default: independent page enabled (switch ON) isEnabledEnabled: false, isEnabled: true, }); const createTranslations = () => ({ title: "Key Settings", affected: "Will affect {count} keys", enableFieldAria: "Enable {title} field", fields: { providerGroup: "Group (providerGroup)", limit5h: "5h Limit (USD)", limitDaily: "Daily Limit (USD)", limitWeekly: "Weekly Limit (USD)", limitMonthly: "Monthly Limit (USD)", canLoginWebUi: "Independent Personal Usage Page", // Updated label keyEnabled: "Key Enabled Status", }, placeholders: { groupPlaceholder: "Leave empty to clear", emptyNoLimit: "Leave empty for no limit", }, targetValue: "Target Value", }); // Helper to find the inner value switch (not the enable/disable switch) // The inner switch has aria-label starting with "Target Value:" function findCanLoginWebUiValueSwitch(): HTMLButtonElement | null { const switches = document.body.querySelectorAll('button[role="switch"]'); return Array.from(switches).find((el) => el.getAttribute("aria-label")?.startsWith("Target Value: Independent Personal Usage Page") ) as HTMLButtonElement | null; } describe("BatchKeySection: canLoginWebUi toggle semantics alignment", () => { beforeEach(() => { while (document.body.firstChild) { document.body.removeChild(document.body.firstChild); } }); test("should use 'Independent Personal Usage Page' label for canLoginWebUi field", () => { const state = createInitialState(); const translations = createTranslations(); const onChange = vi.fn(); const { unmount } = render( ); // Find the field card title for canLoginWebUi const fieldCards = document.body.querySelectorAll(".text-sm.font-medium"); const canLoginWebUiCard = Array.from(fieldCards).find( (el) => el.textContent === "Independent Personal Usage Page" ); expect(canLoginWebUiCard).toBeTruthy(); unmount(); }); test("switch should be ON (checked) when canLoginWebUi=false (inverted logic)", () => { const state = createInitialState(); state.canLoginWebUi = false; // Independent page enabled const translations = createTranslations(); const onChange = vi.fn(); const { unmount } = render( ); const canLoginWebUiSwitch = findCanLoginWebUiValueSwitch(); expect(canLoginWebUiSwitch).toBeTruthy(); // With inverted logic: canLoginWebUi=false should show switch as ON (checked) expect(canLoginWebUiSwitch?.getAttribute("data-state")).toBe("checked"); unmount(); }); test("switch should be OFF (unchecked) when canLoginWebUi=true (inverted logic)", () => { const state = createInitialState(); state.canLoginWebUi = true; // Restricted Web UI const translations = createTranslations(); const onChange = vi.fn(); const { unmount } = render( ); const canLoginWebUiSwitch = findCanLoginWebUiValueSwitch(); expect(canLoginWebUiSwitch).toBeTruthy(); // With inverted logic: canLoginWebUi=true should show switch as OFF (unchecked) expect(canLoginWebUiSwitch?.getAttribute("data-state")).toBe("unchecked"); unmount(); }); test("toggling switch ON should set canLoginWebUi=false (inverted logic)", async () => { const state = createInitialState(); state.canLoginWebUi = true; // Start with restricted Web UI (switch OFF) const translations = createTranslations(); const onChange = vi.fn(); const { unmount } = render( ); const canLoginWebUiSwitch = findCanLoginWebUiValueSwitch(); expect(canLoginWebUiSwitch).toBeTruthy(); // Click to toggle ON await act(async () => { canLoginWebUiSwitch?.click(); await new Promise((r) => setTimeout(r, 50)); }); // With inverted logic: clicking switch ON should set canLoginWebUi=false expect(onChange).toHaveBeenCalledWith({ canLoginWebUi: false }); unmount(); }); test("toggling switch OFF should set canLoginWebUi=true (inverted logic)", async () => { const state = createInitialState(); state.canLoginWebUi = false; // Start with independent page (switch ON) const translations = createTranslations(); const onChange = vi.fn(); const { unmount } = render( ); const canLoginWebUiSwitch = findCanLoginWebUiValueSwitch(); expect(canLoginWebUiSwitch).toBeTruthy(); // Click to toggle OFF await act(async () => { canLoginWebUiSwitch?.click(); await new Promise((r) => setTimeout(r, 50)); }); // With inverted logic: clicking switch OFF should set canLoginWebUi=true expect(onChange).toHaveBeenCalledWith({ canLoginWebUi: true }); unmount(); }); });