login-visual-regression.test.tsx 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  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.bg-gradient-to-br");
  57. expect(mainContainer).not.toBeNull();
  58. const className = mainContainer?.className || "";
  59. expect(className).toContain("min-h-[var(--cch-viewport-height,100vh)]");
  60. expect(className).toContain("bg-gradient-to");
  61. const langSwitcher = container.querySelector(".fixed.top-4.right-4");
  62. expect(langSwitcher).not.toBeNull();
  63. const card = container.querySelector('[data-slot="card"]');
  64. expect(card).not.toBeNull();
  65. const form = container.querySelector("form");
  66. expect(form).not.toBeNull();
  67. const input = container.querySelector("input#apiKey");
  68. expect(input).not.toBeNull();
  69. const button = container.querySelector('button[type="submit"]');
  70. expect(button).not.toBeNull();
  71. });
  72. it("has mobile responsive classes", async () => {
  73. await render();
  74. const wrapper = container.querySelector(".max-w-lg");
  75. expect(wrapper).not.toBeNull();
  76. const card = wrapper?.querySelector('[data-slot="card"]');
  77. expect(card).not.toBeNull();
  78. expect(card?.className).toContain("w-full");
  79. });
  80. });