login-visual-regression.test.tsx 2.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. import { describe, expect, it, vi, beforeEach, afterEach } from "vitest";
  2. import { createRoot } from "react-dom/client";
  3. import { act } from "react";
  4. import LoginPage from "@/app/[locale]/login/page";
  5. // Mocks
  6. const mockPush = vi.hoisted(() => vi.fn());
  7. const mockRefresh = vi.hoisted(() => vi.fn());
  8. const mockUseRouter = vi.hoisted(() => vi.fn(() => ({ push: mockPush, refresh: mockRefresh })));
  9. const mockUseSearchParams = vi.hoisted(() => vi.fn(() => ({ get: vi.fn(() => null) })));
  10. const mockUseTranslations = vi.hoisted(() => vi.fn(() => (key: string) => `t:${key}`));
  11. const mockUseLocale = vi.hoisted(() => vi.fn(() => "en"));
  12. const mockUsePathname = vi.hoisted(() => vi.fn(() => "/login"));
  13. vi.mock("next/navigation", () => ({
  14. useSearchParams: mockUseSearchParams,
  15. useRouter: mockUseRouter,
  16. usePathname: mockUsePathname,
  17. }));
  18. vi.mock("next-intl", () => ({
  19. useTranslations: mockUseTranslations,
  20. useLocale: mockUseLocale,
  21. }));
  22. vi.mock("@/i18n/routing", () => ({
  23. Link: ({ children, ...props }: any) => <a {...props}>{children}</a>,
  24. useRouter: mockUseRouter,
  25. usePathname: mockUsePathname,
  26. }));
  27. vi.mock("next-themes", () => ({
  28. useTheme: vi.fn(() => ({ theme: "system", setTheme: vi.fn() })),
  29. }));
  30. describe("LoginPage Visual Regression", () => {
  31. let container: HTMLDivElement;
  32. let root: ReturnType<typeof createRoot>;
  33. beforeEach(() => {
  34. container = document.createElement("div");
  35. document.body.appendChild(container);
  36. root = createRoot(container);
  37. vi.clearAllMocks();
  38. global.fetch = vi.fn().mockResolvedValue({
  39. ok: true,
  40. json: async () => ({}),
  41. });
  42. });
  43. afterEach(() => {
  44. act(() => {
  45. root.unmount();
  46. });
  47. document.body.removeChild(container);
  48. });
  49. const render = async () => {
  50. await act(async () => {
  51. root.render(<LoginPage />);
  52. });
  53. };
  54. it("renders key structural elements", async () => {
  55. await render();
  56. const mainContainer = container.querySelector("div.min-h-screen");
  57. expect(mainContainer).not.toBeNull();
  58. const className = mainContainer?.className || "";
  59. expect(className).toContain("bg-gradient-to");
  60. const langSwitcher = container.querySelector(".fixed.top-4.right-4");
  61. expect(langSwitcher).not.toBeNull();
  62. const card = container.querySelector('[data-slot="card"]');
  63. expect(card).not.toBeNull();
  64. const form = container.querySelector("form");
  65. expect(form).not.toBeNull();
  66. const input = container.querySelector("input#apiKey");
  67. expect(input).not.toBeNull();
  68. const button = container.querySelector('button[type="submit"]');
  69. expect(button).not.toBeNull();
  70. });
  71. it("has mobile responsive classes", async () => {
  72. await render();
  73. const wrapper = container.querySelector(".max-w-lg");
  74. expect(wrapper).not.toBeNull();
  75. const card = wrapper?.querySelector('[data-slot="card"]');
  76. expect(card).not.toBeNull();
  77. expect(card?.className).toContain("w-full");
  78. });
  79. });