You've already forked domainstack.io
mirror of
https://github.com/jakejarvis/domainstack.io.git
synced 2025-12-02 19:33:48 -05:00
refactor: update test imports to use custom test-utils for consistent testing setup
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
/* @vitest-environment jsdom */
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { CreateIssueButton } from "@/components/create-issue-button";
|
||||
import { render, screen } from "@/lib/test-utils";
|
||||
|
||||
describe("CreateIssueButton", () => {
|
||||
it("renders with icon and label", () => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* @vitest-environment jsdom */
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { render, screen } from "@/lib/test-utils";
|
||||
import { CertificatesSection, equalHostname } from "./certificates-section";
|
||||
|
||||
vi.mock("@/components/domain/favicon", () => ({
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* @vitest-environment jsdom */
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { DnsRecordList } from "@/components/domain/dns/dns-record-list";
|
||||
import { render, screen } from "@/lib/test-utils";
|
||||
|
||||
vi.mock("@/components/domain/favicon", () => ({
|
||||
Favicon: ({ domain }: { domain: string }) => <div>icon:{domain}</div>,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* @vitest-environment jsdom */
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { render, screen } from "@/lib/test-utils";
|
||||
import { DnsSection } from "./dns-section";
|
||||
|
||||
vi.mock("@/components/domain/dns/dns-group", () => ({
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/* @vitest-environment jsdom */
|
||||
import { render, screen, waitFor } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { DomainSearch } from "@/components/domain/domain-search";
|
||||
import { render, screen, waitFor } from "@/lib/test-utils";
|
||||
|
||||
const nav = vi.hoisted(() => ({
|
||||
push: vi.fn(),
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/* @vitest-environment jsdom */
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { createElement } from "react";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { DomainSuggestionsClient } from "@/components/domain/domain-suggestions-client";
|
||||
import { HomeSearchProvider } from "@/components/layout/home-search-context";
|
||||
import { render, screen } from "@/lib/test-utils";
|
||||
|
||||
vi.mock("@/hooks/use-router", () => ({
|
||||
useRouter: () => ({ push: vi.fn() }),
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
/* @vitest-environment jsdom */
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import { createElement } from "react";
|
||||
import type { Mock } from "vitest";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { render, screen, waitFor } from "@/lib/test-utils";
|
||||
import { Favicon } from "./favicon";
|
||||
|
||||
vi.mock("next/image", () => ({
|
||||
@@ -27,64 +26,65 @@ vi.mock("next/image", () => ({
|
||||
}),
|
||||
}));
|
||||
|
||||
// Mock the tRPC client to return a mock queryOptions function
|
||||
const mockQueryOptions = vi.fn();
|
||||
|
||||
vi.mock("@/lib/trpc/client", () => ({
|
||||
useTRPC: () => ({
|
||||
domain: {
|
||||
getFavicon: {
|
||||
queryOptions: (vars: unknown) => ({
|
||||
queryKey: ["getFavicon", vars],
|
||||
}),
|
||||
queryOptions: mockQueryOptions,
|
||||
},
|
||||
},
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock("@tanstack/react-query", async () => {
|
||||
const actual = await vi.importActual<typeof import("@tanstack/react-query")>(
|
||||
"@tanstack/react-query",
|
||||
);
|
||||
return {
|
||||
...actual,
|
||||
useQuery: vi.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
const { useQuery } = await import("@tanstack/react-query");
|
||||
|
||||
describe("Favicon", () => {
|
||||
beforeEach(() => {
|
||||
(useQuery as unknown as Mock).mockReset();
|
||||
mockQueryOptions.mockClear();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
it("shows skeleton while loading (after mount)", () => {
|
||||
(useQuery as unknown as Mock).mockReturnValue({
|
||||
data: undefined,
|
||||
isPending: true,
|
||||
isPlaceholderData: false,
|
||||
});
|
||||
it("shows skeleton while loading (after mount)", async () => {
|
||||
// Configure mock to return queryOptions that React Query will use
|
||||
mockQueryOptions.mockImplementation(({ domain }: { domain: string }) => ({
|
||||
queryKey: ["getFavicon", { domain }],
|
||||
queryFn: () => new Promise(() => {}), // Never resolves to keep loading state
|
||||
}));
|
||||
|
||||
render(<Favicon domain="example.com" size={16} />);
|
||||
|
||||
// While loading, should show skeleton
|
||||
const skeletons = document.querySelectorAll('[data-slot="skeleton"]');
|
||||
expect(skeletons.length).toBeGreaterThan(0);
|
||||
// Initial render shows skeleton due to not mounted
|
||||
expect(
|
||||
document.querySelectorAll('[data-slot="skeleton"]').length,
|
||||
).toBeGreaterThan(0);
|
||||
|
||||
// Wait for mount effect
|
||||
await waitFor(() => {
|
||||
const skeletons = document.querySelectorAll('[data-slot="skeleton"]');
|
||||
expect(skeletons.length).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
|
||||
it("shows letter avatar when no url (after mount)", () => {
|
||||
(useQuery as unknown as Mock).mockReturnValue({
|
||||
data: { url: null },
|
||||
isPending: false,
|
||||
isPlaceholderData: false,
|
||||
});
|
||||
it("shows letter avatar when no url (after mount)", async () => {
|
||||
// Configure mock to return queryOptions with null URL
|
||||
mockQueryOptions.mockImplementation(({ domain }: { domain: string }) => ({
|
||||
queryKey: ["getFavicon", { domain }],
|
||||
queryFn: async () => ({ url: null }),
|
||||
}));
|
||||
|
||||
render(<Favicon domain="example.com" size={16} />);
|
||||
|
||||
// After mount, when no favicon URL, should show letter avatar
|
||||
// Wait for query to complete and component to mount
|
||||
await waitFor(() => {
|
||||
const avatar = screen.queryByText("E");
|
||||
expect(avatar).toBeInTheDocument();
|
||||
});
|
||||
|
||||
const avatar = screen.getByText("E");
|
||||
expect(avatar).toBeInTheDocument();
|
||||
expect(avatar).toHaveAttribute("data-favicon", "example.com");
|
||||
expect(avatar).toHaveAttribute("role", "img");
|
||||
|
||||
@@ -92,14 +92,19 @@ describe("Favicon", () => {
|
||||
expect(document.querySelector('[data-slot="image"]')).toBeNull();
|
||||
});
|
||||
|
||||
it("shows domain letter avatar when no url and not loading", () => {
|
||||
(useQuery as unknown as Mock).mockReturnValue({
|
||||
data: { url: null },
|
||||
isPending: false,
|
||||
isPlaceholderData: false,
|
||||
});
|
||||
it("shows domain letter avatar when no url and not loading", async () => {
|
||||
mockQueryOptions.mockImplementation(({ domain }: { domain: string }) => ({
|
||||
queryKey: ["getFavicon", { domain }],
|
||||
queryFn: async () => ({ url: null }),
|
||||
}));
|
||||
|
||||
render(<Favicon domain="example.com" size={16} />);
|
||||
|
||||
// Wait for query to complete
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByText("E")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// Should show letter 'E' for example.com
|
||||
const avatar = screen.getByText("E");
|
||||
expect(avatar).toBeInTheDocument();
|
||||
@@ -110,36 +115,52 @@ describe("Favicon", () => {
|
||||
expect(document.querySelector('[data-slot="image"]')).toBeNull();
|
||||
});
|
||||
|
||||
it("generates consistent colors for same domain", () => {
|
||||
(useQuery as unknown as Mock).mockReturnValue({
|
||||
data: { url: null },
|
||||
isPending: false,
|
||||
isPlaceholderData: false,
|
||||
});
|
||||
it("generates consistent colors for same domain", async () => {
|
||||
mockQueryOptions.mockImplementation(({ domain }: { domain: string }) => ({
|
||||
queryKey: ["getFavicon", { domain }],
|
||||
queryFn: async () => ({ url: null }),
|
||||
}));
|
||||
|
||||
const { unmount } = render(<Favicon domain="example.com" size={16} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByText("E")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
const firstAvatar = screen.getByText("E");
|
||||
const firstClass = firstAvatar.className;
|
||||
const firstStyle = firstAvatar.getAttribute("style");
|
||||
unmount();
|
||||
|
||||
render(<Favicon domain="example.com" size={16} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByText("E")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
const secondAvatar = screen.getByText("E");
|
||||
expect(secondAvatar.className).toBe(firstClass);
|
||||
expect(secondAvatar.getAttribute("style")).toBe(firstStyle);
|
||||
});
|
||||
|
||||
it("renders Image when url present", () => {
|
||||
(useQuery as unknown as Mock).mockReturnValue({
|
||||
data: {
|
||||
url: "https://test-store.public.blob.vercel-storage.com/abcdef0123456789abcdef0123456789/32x32.webp",
|
||||
},
|
||||
isPending: false,
|
||||
isPlaceholderData: false,
|
||||
});
|
||||
it("renders Image when url present", async () => {
|
||||
const faviconUrl =
|
||||
"https://test-store.public.blob.vercel-storage.com/abcdef0123456789abcdef0123456789/32x32.webp";
|
||||
|
||||
mockQueryOptions.mockImplementation(({ domain }: { domain: string }) => ({
|
||||
queryKey: ["getFavicon", { domain }],
|
||||
queryFn: async () => ({ url: faviconUrl }),
|
||||
}));
|
||||
|
||||
render(<Favicon domain="example.com" size={16} />);
|
||||
|
||||
// Wait for the image to be rendered
|
||||
await waitFor(() => {
|
||||
const img = screen.queryByRole("img", { name: /icon/i });
|
||||
expect(img).toBeInTheDocument();
|
||||
});
|
||||
|
||||
const img = screen.getByRole("img", { name: /icon/i });
|
||||
expect(img).toHaveAttribute(
|
||||
"src",
|
||||
"https://test-store.public.blob.vercel-storage.com/abcdef0123456789abcdef0123456789/32x32.webp",
|
||||
);
|
||||
expect(img).toHaveAttribute("src", faviconUrl);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* @vitest-environment jsdom */
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { render, screen } from "@/lib/test-utils";
|
||||
import { HeadersSection } from "./headers-section";
|
||||
|
||||
// Keep TooltipContent empty in unit tests to avoid text duplication issues.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* @vitest-environment jsdom */
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { render, screen } from "@/lib/test-utils";
|
||||
import { HostingSection } from "./hosting-section";
|
||||
|
||||
vi.mock("next/dynamic", () => ({
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* @vitest-environment jsdom */
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { render, screen } from "@/lib/test-utils";
|
||||
import { KeyValue } from "./key-value";
|
||||
|
||||
// Mock CopyButton - we're testing KeyValue, not clipboard functionality
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* @vitest-environment jsdom */
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { render, screen } from "@/lib/test-utils";
|
||||
import { ProviderValue } from "./provider-value";
|
||||
|
||||
vi.mock("@/components/domain/favicon", () => ({
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* @vitest-environment jsdom */
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { render, screen } from "@/lib/test-utils";
|
||||
import { RegistrationSection } from "./registration-section";
|
||||
|
||||
vi.mock("@/components/domain/favicon", () => ({
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/* @vitest-environment jsdom */
|
||||
import { fireEvent, render, screen } from "@testing-library/react";
|
||||
import type { Mock } from "vitest";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { fireEvent, render, screen, waitFor } from "@/lib/test-utils";
|
||||
import { ScreenshotTooltip } from "./screenshot-tooltip";
|
||||
|
||||
vi.mock("@/components/ui/tooltip", () => ({
|
||||
@@ -26,74 +25,78 @@ vi.mock("next/image", () => ({
|
||||
),
|
||||
}));
|
||||
|
||||
// Mock the tRPC client to return a mock queryOptions function
|
||||
const mockQueryOptions = vi.fn();
|
||||
|
||||
vi.mock("@/lib/trpc/client", () => ({
|
||||
useTRPC: () => ({
|
||||
domain: {
|
||||
getScreenshot: {
|
||||
queryOptions: (vars: unknown) => ({
|
||||
queryKey: ["getScreenshot", vars],
|
||||
}),
|
||||
queryOptions: mockQueryOptions,
|
||||
},
|
||||
},
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock("@tanstack/react-query", async () => {
|
||||
const actual = await vi.importActual<typeof import("@tanstack/react-query")>(
|
||||
"@tanstack/react-query",
|
||||
);
|
||||
return {
|
||||
...actual,
|
||||
useQuery: vi.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
const { useQuery } = await import("@tanstack/react-query");
|
||||
|
||||
describe("ScreenshotTooltip", () => {
|
||||
beforeEach(() => {
|
||||
(useQuery as unknown as Mock).mockReset();
|
||||
mockQueryOptions.mockClear();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
it("fetches on open and shows loading UI", () => {
|
||||
(useQuery as unknown as Mock).mockReturnValue({
|
||||
data: undefined,
|
||||
isLoading: true,
|
||||
isFetching: false,
|
||||
});
|
||||
it("fetches on open and shows loading UI", async () => {
|
||||
// Configure mock to return queryOptions that keep loading state
|
||||
mockQueryOptions.mockImplementation(({ domain }: { domain: string }) => ({
|
||||
queryKey: ["getScreenshot", { domain }],
|
||||
queryFn: () => new Promise(() => {}), // Never resolves to keep loading state
|
||||
}));
|
||||
|
||||
render(
|
||||
<ScreenshotTooltip domain="example.com">
|
||||
<span>hover me</span>
|
||||
</ScreenshotTooltip>,
|
||||
);
|
||||
|
||||
// Simulate open by clicking the trigger
|
||||
fireEvent.click(screen.getByText("hover me"));
|
||||
expect(screen.getByText(/taking screenshot/i)).toBeInTheDocument();
|
||||
|
||||
// Should show loading state
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText(/taking screenshot/i)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it("renders image when loaded", () => {
|
||||
(useQuery as unknown as Mock).mockReturnValue({
|
||||
data: {
|
||||
url: "https://test-store.public.blob.vercel-storage.com/abcdef0123456789abcdef0123456789/1200x630.webp",
|
||||
},
|
||||
isLoading: false,
|
||||
isFetching: false,
|
||||
});
|
||||
it("renders image when loaded", async () => {
|
||||
const screenshotUrl =
|
||||
"https://test-store.public.blob.vercel-storage.com/abcdef0123456789abcdef0123456789/1200x630.webp";
|
||||
|
||||
mockQueryOptions.mockImplementation(({ domain }: { domain: string }) => ({
|
||||
queryKey: ["getScreenshot", { domain }],
|
||||
queryFn: async () => ({ url: screenshotUrl }),
|
||||
}));
|
||||
|
||||
render(
|
||||
<ScreenshotTooltip domain="example.com">
|
||||
<span>hover me</span>
|
||||
</ScreenshotTooltip>,
|
||||
);
|
||||
|
||||
fireEvent.click(screen.getByText("hover me"));
|
||||
|
||||
// Wait for the image to be rendered
|
||||
await waitFor(() => {
|
||||
const img = screen.queryByRole("img", {
|
||||
name: /homepage preview of example.com/i,
|
||||
});
|
||||
expect(img).toBeInTheDocument();
|
||||
});
|
||||
|
||||
const img = screen.getByRole("img", {
|
||||
name: /homepage preview of example.com/i,
|
||||
});
|
||||
expect(img).toHaveAttribute(
|
||||
"src",
|
||||
"https://test-store.public.blob.vercel-storage.com/abcdef0123456789abcdef0123456789/1200x630.webp",
|
||||
);
|
||||
expect(img).toHaveAttribute("src", screenshotUrl);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
/* @vitest-environment jsdom */
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import { createElement } from "react";
|
||||
import type { Mock } from "vitest";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { render, screen, waitFor } from "@/lib/test-utils";
|
||||
import { Screenshot } from "./screenshot";
|
||||
|
||||
vi.mock("next/image", () => ({
|
||||
@@ -27,73 +26,79 @@ vi.mock("next/image", () => ({
|
||||
}),
|
||||
}));
|
||||
|
||||
// Mock the tRPC client to return a mock queryOptions function
|
||||
const mockQueryOptions = vi.fn();
|
||||
|
||||
vi.mock("@/lib/trpc/client", () => ({
|
||||
useTRPC: () => ({
|
||||
domain: {
|
||||
getScreenshot: {
|
||||
queryOptions: (vars: unknown) => ({
|
||||
queryKey: ["getScreenshot", vars],
|
||||
}),
|
||||
queryOptions: mockQueryOptions,
|
||||
},
|
||||
},
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock("@tanstack/react-query", async () => {
|
||||
const actual = await vi.importActual<typeof import("@tanstack/react-query")>(
|
||||
"@tanstack/react-query",
|
||||
);
|
||||
return {
|
||||
...actual,
|
||||
useQuery: vi.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
const { useQuery } = await import("@tanstack/react-query");
|
||||
|
||||
describe("Screenshot", () => {
|
||||
beforeEach(() => {
|
||||
(useQuery as unknown as Mock).mockReset();
|
||||
mockQueryOptions.mockClear();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
it("shows loading UI during fetch", () => {
|
||||
(useQuery as unknown as Mock).mockReturnValue({
|
||||
data: undefined,
|
||||
isLoading: true,
|
||||
isFetching: false,
|
||||
});
|
||||
it("shows loading UI during fetch", async () => {
|
||||
// Configure mock to return queryOptions that keep loading state
|
||||
mockQueryOptions.mockImplementation(({ domain }: { domain: string }) => ({
|
||||
queryKey: ["getScreenshot", { domain }],
|
||||
queryFn: () => new Promise(() => {}), // Never resolves to keep loading state
|
||||
}));
|
||||
|
||||
render(<Screenshot domain="example.com" />);
|
||||
expect(screen.getByText(/taking screenshot/i)).toBeInTheDocument();
|
||||
|
||||
// Should show loading state
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText(/taking screenshot/i)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it("renders image when url present", () => {
|
||||
(useQuery as unknown as Mock).mockReturnValue({
|
||||
data: {
|
||||
url: "https://test-store.public.blob.vercel-storage.com/abcdef0123456789abcdef0123456789/1200x630.webp",
|
||||
},
|
||||
isLoading: false,
|
||||
isFetching: false,
|
||||
});
|
||||
it("renders image when url present", async () => {
|
||||
const screenshotUrl =
|
||||
"https://test-store.public.blob.vercel-storage.com/abcdef0123456789abcdef0123456789/1200x630.webp";
|
||||
|
||||
mockQueryOptions.mockImplementation(({ domain }: { domain: string }) => ({
|
||||
queryKey: ["getScreenshot", { domain }],
|
||||
queryFn: async () => ({ url: screenshotUrl }),
|
||||
}));
|
||||
|
||||
render(<Screenshot domain="example.com" />);
|
||||
|
||||
// Wait for the image to be rendered
|
||||
await waitFor(() => {
|
||||
const img = screen.queryByRole("img", {
|
||||
name: /homepage preview of example.com/i,
|
||||
});
|
||||
expect(img).toBeInTheDocument();
|
||||
});
|
||||
|
||||
const img = screen.getByRole("img", {
|
||||
name: /homepage preview of example.com/i,
|
||||
});
|
||||
expect(img).toHaveAttribute(
|
||||
"src",
|
||||
"https://test-store.public.blob.vercel-storage.com/abcdef0123456789abcdef0123456789/1200x630.webp",
|
||||
);
|
||||
expect(img).toHaveAttribute("src", screenshotUrl);
|
||||
});
|
||||
|
||||
it("shows fallback when no url and not loading", () => {
|
||||
(useQuery as unknown as Mock).mockReturnValue({
|
||||
data: { url: null },
|
||||
isLoading: false,
|
||||
isFetching: false,
|
||||
});
|
||||
it("shows fallback when no url and not loading", async () => {
|
||||
mockQueryOptions.mockImplementation(({ domain }: { domain: string }) => ({
|
||||
queryKey: ["getScreenshot", { domain }],
|
||||
queryFn: async () => ({ url: null }),
|
||||
}));
|
||||
|
||||
render(<Screenshot domain="example.com" />);
|
||||
expect(screen.getByText(/unable to take/i)).toBeInTheDocument();
|
||||
|
||||
// Wait for query to complete
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText(/unable to take/i)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,14 +2,13 @@
|
||||
|
||||
import { Ban } from "lucide-react";
|
||||
import posthog from "posthog-js";
|
||||
import type { ReactNode } from "react";
|
||||
import { Component } from "react";
|
||||
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { createLogger } from "@/lib/logger/client";
|
||||
|
||||
interface Props {
|
||||
children: ReactNode;
|
||||
children: React.ReactNode;
|
||||
sectionName: string;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* @vitest-environment jsdom */
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { render, screen } from "@/lib/test-utils";
|
||||
import { MetaTagsGrid } from "./meta-tags-grid";
|
||||
|
||||
describe("MetaTagsGrid", () => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* @vitest-environment jsdom */
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import type { SeoResponse } from "@/lib/schemas";
|
||||
import { render, screen } from "@/lib/test-utils";
|
||||
import { RobotsSummary } from "./robots-summary";
|
||||
|
||||
vi.mock("@/components/ui/tooltip", () => ({
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* @vitest-environment jsdom */
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import type { SeoResponse } from "@/lib/schemas";
|
||||
import { render, screen } from "@/lib/test-utils";
|
||||
|
||||
// Mock child components to isolate main component testing
|
||||
vi.mock("@/components/domain/seo/meta-tags-grid", () => ({
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* @vitest-environment jsdom */
|
||||
import { render, screen, within } from "@testing-library/react";
|
||||
import { userEvent } from "@testing-library/user-event";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { render, screen, within } from "@/lib/test-utils";
|
||||
import { SocialPreviewTabs } from "./social-preview-tabs";
|
||||
|
||||
describe("SocialPreviewTabs", () => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* @vitest-environment jsdom */
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import { createElement } from "react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { render, screen } from "@/lib/test-utils";
|
||||
import { SocialPreview } from "./social-preview";
|
||||
|
||||
// Mock next/image with a plain img for JSDOM
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* @vitest-environment jsdom */
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { render, screen } from "@/lib/test-utils";
|
||||
|
||||
// Mock the video player components to avoid media-chrome dependencies
|
||||
vi.mock("@/components/ui/video-player", () => ({
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* @vitest-environment jsdom */
|
||||
import { render, screen, waitFor } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { render, screen, waitFor } from "@/lib/test-utils";
|
||||
import { HeaderSearch } from "./header-search";
|
||||
import { HeaderSearchProvider } from "./header-search-context";
|
||||
|
||||
|
||||
89
lib/test-utils.tsx
Normal file
89
lib/test-utils.tsx
Normal file
@@ -0,0 +1,89 @@
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
import { type RenderOptions, render } from "@testing-library/react";
|
||||
|
||||
/**
|
||||
* Creates a QueryClient configured for testing.
|
||||
*
|
||||
* Configuration follows TanStack Query testing best practices:
|
||||
* - retry: false - Prevents test timeouts on failed queries
|
||||
* - gcTime: Infinity - Prevents "Jest did not exit" warnings
|
||||
*
|
||||
* @see https://tanstack.com/query/latest/docs/framework/react/guides/testing
|
||||
*/
|
||||
export function createTestQueryClient(): QueryClient {
|
||||
return new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
// Turn off retries to prevent test timeouts
|
||||
retry: false,
|
||||
// Set gcTime to Infinity to prevent cleanup warnings
|
||||
gcTime: Number.POSITIVE_INFINITY,
|
||||
},
|
||||
mutations: {
|
||||
// Turn off retries for mutations too
|
||||
retry: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Test wrapper that provides QueryClientProvider with a fresh client for each test.
|
||||
* Ensures test isolation by creating a new QueryClient instance per render.
|
||||
*/
|
||||
function createWrapper(queryClient: QueryClient) {
|
||||
return function Wrapper({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
interface CustomRenderOptions extends Omit<RenderOptions, "wrapper"> {
|
||||
/**
|
||||
* Optional QueryClient instance. If not provided, a new one will be created
|
||||
* using createTestQueryClient().
|
||||
*/
|
||||
queryClient?: QueryClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom render function that wraps components with QueryClientProvider.
|
||||
*
|
||||
* Usage:
|
||||
* ```tsx
|
||||
* import { render, screen } from '@/lib/test-utils'
|
||||
*
|
||||
* it('renders component with React Query', () => {
|
||||
* render(<MyComponent />)
|
||||
* expect(screen.getByText('Hello')).toBeInTheDocument()
|
||||
* })
|
||||
*
|
||||
* // With custom QueryClient
|
||||
* it('uses prefilled cache', () => {
|
||||
* const queryClient = createTestQueryClient()
|
||||
* queryClient.setQueryData(['key'], { data: 'value' })
|
||||
* render(<MyComponent />, { queryClient })
|
||||
* })
|
||||
* ```
|
||||
*
|
||||
* @see https://tanstack.com/query/latest/docs/framework/react/guides/testing
|
||||
*/
|
||||
function customRender(ui: React.ReactElement, options?: CustomRenderOptions) {
|
||||
const { queryClient = createTestQueryClient(), ...renderOptions } =
|
||||
options ?? {};
|
||||
|
||||
return {
|
||||
...render(ui, {
|
||||
wrapper: createWrapper(queryClient),
|
||||
...renderOptions,
|
||||
}),
|
||||
queryClient,
|
||||
};
|
||||
}
|
||||
|
||||
// Re-export everything from @testing-library/react
|
||||
export * from "@testing-library/react";
|
||||
|
||||
// Override render with our custom version
|
||||
export { customRender as render };
|
||||
Reference in New Issue
Block a user