Add more unit tests

This commit is contained in:
adilallo
2025-08-29 09:26:27 -06:00
parent 09603d1b54
commit 54227d1930
12 changed files with 1715 additions and 58 deletions
+185
View File
@@ -0,0 +1,185 @@
import { test, expect } from "@playwright/test";
const breakpoints = [
{ name: "xs", width: 360, height: 700 },
{ name: "sm", width: 640, height: 700 },
{ name: "md", width: 768, height: 700 },
{ name: "lg", width: 1024, height: 700 },
{ name: "xl", width: 1280, height: 700 },
];
for (const bp of breakpoints) {
test.describe(`Header responsive behavior at ${bp.name} breakpoint`, () => {
test.beforeEach(async ({ page }) => {
await page.setViewportSize({ width: bp.width, height: bp.height });
await page.goto("/");
});
test(`header layout at ${bp.name}`, async ({ page }) => {
const nav = page.getByRole("navigation", { name: /main navigation/i });
await expect(nav).toBeVisible();
// Check that header banner is visible
const header = page.getByRole("banner", {
name: /main navigation header/i,
});
await expect(header).toBeVisible();
});
test(`navigation items visibility at ${bp.name}`, async ({ page }) => {
// All breakpoints should have navigation items
await expect(
page.getByRole("link", { name: /use cases/i })
).toBeVisible();
await expect(page.getByRole("link", { name: /learn/i })).toBeVisible();
await expect(page.getByRole("link", { name: /about/i })).toBeVisible();
});
test(`authentication elements visibility at ${bp.name}`, async ({
page,
}) => {
// All breakpoints should have login button
await expect(
page.getByRole("link", { name: /log in to your account/i })
).toBeVisible();
// All breakpoints should have create rule button
await expect(
page.getByRole("button", {
name: /create a new rule with avatar decoration/i,
})
).toBeVisible();
});
test(`logo visibility at ${bp.name}`, async ({ page }) => {
// Logo should be visible at all breakpoints
const logo = page.locator('[data-testid="logo-wrapper"]').first();
await expect(logo).toBeVisible();
});
// Breakpoint-specific tests
if (bp.name === "xs") {
test("xs breakpoint specific behavior", async ({ page }) => {
// At xs, navigation items should be in the right section
const authXs = page.locator('[data-testid="auth-xs"]');
await expect(authXs).toBeVisible();
// Navigation items should be in the auth section at xs
const useCasesLink = page.getByRole("link", { name: /use cases/i });
await expect(useCasesLink).toBeVisible();
// Login button should be in the auth section
const loginButton = page.getByRole("link", {
name: /log in to your account/i,
});
await expect(loginButton).toBeVisible();
// Create rule button should be visible
const createRuleButton = page.getByRole("button", {
name: /create a new rule with avatar decoration/i,
});
await expect(createRuleButton).toBeVisible();
});
}
if (bp.name === "sm") {
test("sm breakpoint specific behavior", async ({ page }) => {
// At sm, navigation should be in the center section
const navSm = page.locator('[data-testid="nav-sm"]');
await expect(navSm).toBeVisible();
// Auth section should only have create rule button
const authSm = page.locator('[data-testid="auth-sm"]');
await expect(authSm).toBeVisible();
});
}
if (bp.name === "md") {
test("md breakpoint specific behavior", async ({ page }) => {
// At md, navigation should be in the center section
const navMd = page.locator('[data-testid="nav-md"]');
await expect(navMd).toBeVisible();
// Auth section should have login and create rule button
const authMd = page.locator('[data-testid="auth-md"]');
await expect(authMd).toBeVisible();
});
}
if (bp.name === "lg") {
test("lg breakpoint specific behavior", async ({ page }) => {
// At lg, navigation should be in the center section
const navLg = page.locator('[data-testid="nav-lg"]');
await expect(navLg).toBeVisible();
// Auth section should have login and create rule button
const authLg = page.locator('[data-testid="auth-lg"]');
await expect(authLg).toBeVisible();
});
}
if (bp.name === "xl") {
test("xl breakpoint specific behavior", async ({ page }) => {
// At xl, navigation should be in the center section
const navXl = page.locator('[data-testid="nav-xl"]');
await expect(navXl).toBeVisible();
// Auth section should have login and create rule button
const authXl = page.locator('[data-testid="auth-xl"]');
await expect(authXl).toBeVisible();
});
}
});
}
// Additional responsive behavior tests
test.describe("Header responsive behavior", () => {
test("header maintains proper layout across breakpoints", async ({
page,
}) => {
// Test that header doesn't break at edge cases
const edgeCases = [
{ width: 320, height: 700 }, // Very small
{ width: 1920, height: 700 }, // Very large
];
for (const viewport of edgeCases) {
await page.setViewportSize(viewport);
await page.goto("/");
const header = page.getByRole("banner", {
name: /main navigation header/i,
});
await expect(header).toBeVisible();
const nav = page.getByRole("navigation", { name: /main navigation/i });
await expect(nav).toBeVisible();
}
});
test("header elements are properly accessible across breakpoints", async ({
page,
}) => {
// Test accessibility at different breakpoints
for (const bp of breakpoints) {
await page.setViewportSize({ width: bp.width, height: bp.height });
await page.goto("/");
// Check that all interactive elements are accessible
const interactiveElements = [
page.getByRole("link", { name: /use cases/i }),
page.getByRole("link", { name: /learn/i }),
page.getByRole("link", { name: /about/i }),
page.getByRole("link", { name: /log in to your account/i }),
page.getByRole("button", {
name: /create a new rule with avatar decoration/i,
}),
];
for (const element of interactiveElements) {
await expect(element).toBeVisible();
await expect(element).toBeEnabled();
}
}
});
});
+253
View File
@@ -0,0 +1,253 @@
import { describe, test, expect } from "vitest";
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import Footer from "../../app/components/Footer";
describe("Footer", () => {
test("renders footer with correct structure", () => {
render(<Footer />);
const footers = screen.getAllByRole("contentinfo");
expect(footers.length).toBeGreaterThan(0);
const footer = footers[0];
expect(footer).toBeInTheDocument();
expect(footer).toHaveClass("bg-[var(--color-surface-default-primary)]");
expect(footer).toHaveClass("w-full");
});
test("renders schema markup for organization information", () => {
render(<Footer />);
const script = document.querySelector('script[type="application/ld+json"]');
expect(script).toBeInTheDocument();
const schemaData = JSON.parse(script.textContent);
expect(schemaData["@type"]).toBe("Organization");
expect(schemaData.name).toBe("Media Economies Design Lab");
expect(schemaData.email).toBe("medlab@colorado.edu");
expect(schemaData.url).toBe("https://communityrule.com");
expect(schemaData.sameAs).toContain(
"https://bsky.app/profile/medlabboulder"
);
expect(schemaData.sameAs).toContain("https://gitlab.com/medlabboulder");
});
test("renders organization name and contact information", () => {
render(<Footer />);
expect(
screen.getAllByText("Media Economies Design Lab").length
).toBeGreaterThan(0);
const emailLinks = screen.getAllByRole("link", {
name: "medlab@colorado.edu",
});
expect(emailLinks.length).toBeGreaterThan(0);
const emailLink = emailLinks[0];
expect(emailLink).toBeInTheDocument();
expect(emailLink).toHaveAttribute("href", "mailto:medlab@colorado.edu");
});
test("renders social media links with correct accessibility", () => {
render(<Footer />);
// Check Bluesky link
const blueskyLinks = screen.getAllByRole("link", {
name: "Follow us on Bluesky",
});
expect(blueskyLinks.length).toBeGreaterThan(0);
const blueskyLink = blueskyLinks[0];
expect(blueskyLink).toBeInTheDocument();
expect(screen.getAllByText("medlabboulder").length).toBeGreaterThan(0);
// Check GitLab link
const gitlabLinks = screen.getAllByRole("link", {
name: "Follow us on GitLab",
});
expect(gitlabLinks.length).toBeGreaterThan(0);
const gitlabLink = gitlabLinks[0];
expect(gitlabLink).toBeInTheDocument();
// Check social media images
const blueskyImages = screen.getAllByAltText("Bluesky");
expect(blueskyImages.length).toBeGreaterThan(0);
const blueskyImage = blueskyImages[0];
expect(blueskyImage).toBeInTheDocument();
expect(blueskyImage).toHaveAttribute("src", "assets/Bluesky_Logo.svg");
const gitlabImages = screen.getAllByAltText("GitLab");
expect(gitlabImages.length).toBeGreaterThan(0);
const gitlabImage = gitlabImages[0];
expect(gitlabImage).toBeInTheDocument();
expect(gitlabImage).toHaveAttribute("src", "assets/GitLab_Icon.png");
});
test("renders navigation links", () => {
render(<Footer />);
expect(
screen.getAllByRole("link", { name: "Use cases" }).length
).toBeGreaterThan(0);
expect(
screen.getAllByRole("link", { name: "Learn" }).length
).toBeGreaterThan(0);
expect(
screen.getAllByRole("link", { name: "About" }).length
).toBeGreaterThan(0);
});
test("renders legal links", () => {
render(<Footer />);
expect(
screen.getAllByRole("link", { name: "Privacy Policy" }).length
).toBeGreaterThan(0);
expect(
screen.getAllByRole("link", { name: "Terms of Service" }).length
).toBeGreaterThan(0);
expect(
screen.getAllByRole("link", { name: "Cookies Settings" }).length
).toBeGreaterThan(0);
});
test("renders copyright information", () => {
render(<Footer />);
expect(screen.getAllByText("© All right reserved").length).toBeGreaterThan(
0
);
});
test("renders responsive logo configurations", () => {
render(<Footer />);
// Check that logo containers exist for different breakpoints
const logoContainers = document.querySelectorAll(
'[class*="block sm:hidden"], [class*="hidden sm:block lg:hidden"], [class*="hidden lg:block"]'
);
expect(logoContainers.length).toBeGreaterThan(0);
});
test("has correct CSS classes for responsive design", () => {
render(<Footer />);
const footers = screen.getAllByRole("contentinfo");
expect(footers.length).toBeGreaterThan(0);
const footer = footers[0];
const mainContainer = footer.querySelector("div");
expect(mainContainer).toHaveClass("flex");
expect(mainContainer).toHaveClass("flex-col");
expect(mainContainer).toHaveClass("items-start");
expect(mainContainer).toHaveClass("mx-auto");
});
test("renders separator component", () => {
render(<Footer />);
// The Separator component should be rendered (it uses a div with border, not hr)
const separator = document.querySelector(
".bg-\\[var\\(--border-color-default-secondary\\)\\]"
);
expect(separator).toBeInTheDocument();
});
test("social media links have hover and focus states", () => {
render(<Footer />);
const blueskyLinks = screen.getAllByRole("link", {
name: "Follow us on Bluesky",
});
expect(blueskyLinks.length).toBeGreaterThan(0);
expect(blueskyLinks[0]).toHaveClass("hover:opacity-80");
expect(blueskyLinks[0]).toHaveClass("active:opacity-60");
expect(blueskyLinks[0]).toHaveClass("focus:opacity-80");
expect(blueskyLinks[0]).toHaveClass("transition-opacity");
const gitlabLinks = screen.getAllByRole("link", {
name: "Follow us on GitLab",
});
expect(gitlabLinks.length).toBeGreaterThan(0);
expect(gitlabLinks[0]).toHaveClass("hover:opacity-80");
expect(gitlabLinks[0]).toHaveClass("active:opacity-60");
expect(gitlabLinks[0]).toHaveClass("focus:opacity-80");
expect(gitlabLinks[0]).toHaveClass("transition-opacity");
});
test("navigation links have hover and focus states", () => {
render(<Footer />);
const useCasesLinks = screen.getAllByRole("link", { name: "Use cases" });
expect(useCasesLinks.length).toBeGreaterThan(0);
expect(useCasesLinks[0]).toHaveClass("hover:opacity-80");
expect(useCasesLinks[0]).toHaveClass("active:opacity-60");
expect(useCasesLinks[0]).toHaveClass("focus:opacity-80");
expect(useCasesLinks[0]).toHaveClass("transition-opacity");
});
test("legal links have hover and focus states", () => {
render(<Footer />);
const privacyLinks = screen.getAllByRole("link", {
name: "Privacy Policy",
});
expect(privacyLinks.length).toBeGreaterThan(0);
expect(privacyLinks[0]).toHaveClass("hover:opacity-80");
expect(privacyLinks[0]).toHaveClass("active:opacity-60");
expect(privacyLinks[0]).toHaveClass("focus:opacity-80");
expect(privacyLinks[0]).toHaveClass("transition-opacity");
});
test("email link has hover and focus states", () => {
render(<Footer />);
const emailLinks = screen.getAllByRole("link", {
name: "medlab@colorado.edu",
});
expect(emailLinks.length).toBeGreaterThan(0);
expect(emailLinks[0]).toHaveClass("hover:opacity-80");
expect(emailLinks[0]).toHaveClass("active:opacity-60");
expect(emailLinks[0]).toHaveClass("focus:opacity-80");
expect(emailLinks[0]).toHaveClass("transition-opacity");
});
test("social media images have hover effects", () => {
render(<Footer />);
const blueskyImages = screen.getAllByAltText("Bluesky");
expect(blueskyImages.length).toBeGreaterThan(0);
expect(blueskyImages[0]).toHaveClass("group-hover:scale-110");
expect(blueskyImages[0]).toHaveClass("transition-transform");
const gitlabImages = screen.getAllByAltText("GitLab");
expect(gitlabImages.length).toBeGreaterThan(0);
expect(gitlabImages[0]).toHaveClass("group-hover:scale-110");
expect(gitlabImages[0]).toHaveClass("transition-transform");
expect(gitlabImages[0]).toHaveClass("grayscale");
});
test("renders multiple instances of navigation links for responsive design", () => {
render(<Footer />);
// Should have navigation links in the footer
const useCasesLinks = screen.getAllByText("Use cases");
const learnLinks = screen.getAllByText("Learn");
const aboutLinks = screen.getAllByText("About");
expect(useCasesLinks.length).toBeGreaterThan(0);
expect(learnLinks.length).toBeGreaterThan(0);
expect(aboutLinks.length).toBeGreaterThan(0);
});
test("has proper focus management for accessibility", () => {
render(<Footer />);
const links = screen.getAllByRole("link");
links.forEach((link) => {
expect(link).toHaveClass("focus:outline-none");
expect(link).toHaveClass("focus:ring-2");
expect(link).toHaveClass("focus:ring-offset-2");
});
});
});
+344
View File
@@ -0,0 +1,344 @@
import { describe, test, expect, vi, beforeEach } from "vitest";
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import Header, {
navigationItems,
avatarImages,
logoConfig,
} from "../../app/components/Header.js";
describe("Header", () => {
const mockOnToggle = vi.fn();
beforeEach(() => {
mockOnToggle.mockClear();
// Clear any existing rendered content
document.body.innerHTML = "";
});
describe("Accessibility and Landmarks", () => {
test("renders header with correct structure and accessibility attributes", () => {
const { container } = render(<Header onToggle={mockOnToggle} />);
// Check main header structure - use container to scope the search
const header = container.querySelector(
'[role="banner"][aria-label="Main navigation header"]'
);
expect(header).toBeInTheDocument();
expect(header).toHaveAttribute("aria-label", "Main navigation header");
// Check navigation - use container to scope the search
const nav = container.querySelector(
'[role="navigation"][aria-label="Main navigation"]'
);
expect(nav).toBeInTheDocument();
expect(nav).toHaveAttribute("aria-label", "Main navigation");
});
test("renders all navigation items with proper accessibility", () => {
render(<Header onToggle={mockOnToggle} />);
// Check all navigation items have proper aria-labels - use menuitem role since they're in a menubar
expect(
screen.getAllByRole("menuitem", { name: "Navigate to Use cases page" })
.length
).toBeGreaterThan(0);
expect(
screen.getAllByRole("menuitem", { name: "Navigate to Learn page" })
.length
).toBeGreaterThan(0);
expect(
screen.getAllByRole("menuitem", { name: "Navigate to About page" })
.length
).toBeGreaterThan(0);
});
});
describe("Schema Markup", () => {
test("renders schema markup for site navigation", () => {
render(<Header onToggle={mockOnToggle} />);
const script = document.querySelector(
'script[type="application/ld+json"]'
);
expect(script).toBeInTheDocument();
const schemaData = JSON.parse(script.textContent);
expect(schemaData["@type"]).toBe("WebSite");
expect(schemaData.name).toBe("CommunityRule");
expect(schemaData.url).toBe("https://communityrule.com");
expect(schemaData.potentialAction["@type"]).toBe("SearchAction");
});
});
describe("Configuration Data", () => {
test("navigationItems has correct structure and count", () => {
expect(navigationItems).toHaveLength(3);
expect(navigationItems[0]).toEqual({
href: "#",
text: "Use cases",
extraPadding: true,
});
expect(navigationItems[1]).toEqual({
href: "#",
text: "Learn",
});
expect(navigationItems[2]).toEqual({
href: "#",
text: "About",
});
});
test("avatarImages has correct structure and count", () => {
expect(avatarImages).toHaveLength(3);
expect(avatarImages[0]).toEqual({
src: "assets/Avatar_1.png",
alt: "Avatar 1",
});
expect(avatarImages[1]).toEqual({
src: "assets/Avatar_2.png",
alt: "Avatar 2",
});
expect(avatarImages[2]).toEqual({
src: "assets/Avatar_3.png",
alt: "Avatar 3",
});
});
test("logoConfig has correct structure and count", () => {
expect(logoConfig).toHaveLength(5);
// Check first config (xs)
expect(logoConfig[0]).toEqual({
breakpoint: "block sm:hidden",
size: "header",
showText: false,
});
// Check last config (xl+)
expect(logoConfig[4]).toEqual({
breakpoint: "hidden xl:block",
size: "headerXl",
showText: true,
});
});
});
describe("Logo Configuration", () => {
test("renders correct number of logo variants", () => {
render(<Header onToggle={mockOnToggle} />);
const logoWrappers = screen.getAllByTestId("logo-wrapper");
expect(logoWrappers).toHaveLength(logoConfig.length);
});
test("logo wrappers include expected breakpoint classes", () => {
render(<Header onToggle={mockOnToggle} />);
const logoWrappers = screen.getAllByTestId("logo-wrapper");
// Check first logo variant (xs only)
expect(logoWrappers[0]).toHaveClass("block", "sm:hidden");
// Check second logo variant (sm only)
expect(logoWrappers[1]).toHaveClass("hidden", "sm:block", "md:hidden");
// Check third logo variant (md only)
expect(logoWrappers[2]).toHaveClass("hidden", "md:block", "lg:hidden");
// Check fourth logo variant (lg only)
expect(logoWrappers[3]).toHaveClass("hidden", "lg:block", "xl:hidden");
// Check fifth logo variant (xl+)
expect(logoWrappers[4]).toHaveClass("hidden", "xl:block");
});
});
describe("Navigation Structure", () => {
test("renders all breakpoint-specific navigation containers", () => {
render(<Header onToggle={mockOnToggle} />);
expect(screen.getByTestId("nav-xs")).toBeInTheDocument();
expect(screen.getByTestId("nav-sm")).toBeInTheDocument();
expect(screen.getByTestId("nav-md")).toBeInTheDocument();
expect(screen.getByTestId("nav-lg")).toBeInTheDocument();
expect(screen.getByTestId("nav-xl")).toBeInTheDocument();
});
test("navigation containers use expected breakpoint classes", () => {
render(<Header onToggle={mockOnToggle} />);
// XSmall navigation
const navXs = screen.getByTestId("nav-xs");
expect(navXs).toHaveClass("block", "sm:hidden");
// Small navigation
const navSm = screen.getByTestId("nav-sm");
expect(navSm).toHaveClass("hidden", "sm:block", "md:hidden");
// Medium navigation
const navMd = screen.getByTestId("nav-md");
expect(navMd).toHaveClass("hidden", "md:block", "lg:hidden");
// Large navigation
const navLg = screen.getByTestId("nav-lg");
expect(navLg).toHaveClass("hidden", "lg:block", "xl:hidden");
// XLarge navigation
const navXl = screen.getByTestId("nav-xl");
expect(navXl).toHaveClass("hidden", "xl:block");
});
test("renders navigation items with correct text and links", () => {
render(<Header onToggle={mockOnToggle} />);
// Check navigation items
expect(screen.getAllByText("Use cases").length).toBeGreaterThan(0);
expect(screen.getAllByText("Learn").length).toBeGreaterThan(0);
expect(screen.getAllByText("About").length).toBeGreaterThan(0);
});
test("renders multiple instances of navigation items for responsive design", () => {
render(<Header onToggle={mockOnToggle} />);
// Should have multiple instances of navigation items for different breakpoints
const useCasesLinks = screen.getAllByText("Use cases");
const learnLinks = screen.getAllByText("Learn");
const aboutLinks = screen.getAllByText("About");
expect(useCasesLinks.length).toBeGreaterThan(1);
expect(learnLinks.length).toBeGreaterThan(1);
expect(aboutLinks.length).toBeGreaterThan(1);
});
});
describe("Authentication Structure", () => {
test("renders all breakpoint-specific auth containers", () => {
render(<Header onToggle={mockOnToggle} />);
expect(screen.getByTestId("auth-xs")).toBeInTheDocument();
expect(screen.getByTestId("auth-sm")).toBeInTheDocument();
expect(screen.getByTestId("auth-md")).toBeInTheDocument();
expect(screen.getByTestId("auth-lg")).toBeInTheDocument();
expect(screen.getByTestId("auth-xl")).toBeInTheDocument();
});
test("auth containers use expected breakpoint classes", () => {
render(<Header onToggle={mockOnToggle} />);
// XSmall auth
const authXs = screen.getByTestId("auth-xs");
expect(authXs).toHaveClass("block", "sm:hidden");
// Small auth
const authSm = screen.getByTestId("auth-sm");
expect(authSm).toHaveClass("hidden", "sm:block", "md:hidden");
// Medium auth
const authMd = screen.getByTestId("auth-md");
expect(authMd).toHaveClass("hidden", "md:block", "lg:hidden");
// Large auth
const authLg = screen.getByTestId("auth-lg");
expect(authLg).toHaveClass("hidden", "lg:block", "xl:hidden");
// XLarge auth
const authXl = screen.getByTestId("auth-xl");
expect(authXl).toHaveClass("hidden", "xl:block");
});
test("renders login button with correct accessibility", () => {
render(<Header onToggle={mockOnToggle} />);
const loginLinks = screen.getAllByRole("menuitem", {
name: "Log in to your account",
});
expect(loginLinks.length).toBeGreaterThan(0);
expect(screen.getAllByText("Log in").length).toBeGreaterThan(0);
});
test("renders multiple login buttons for responsive design", () => {
render(<Header onToggle={mockOnToggle} />);
// Should have multiple login buttons for different breakpoints
const loginButtons = screen.getAllByText("Log in");
expect(loginButtons.length).toBeGreaterThan(1);
});
test("renders create rule button with avatar decoration", () => {
render(<Header onToggle={mockOnToggle} />);
const createRuleButtons = screen.getAllByRole("button", {
name: "Create a new rule with avatar decoration",
});
expect(createRuleButtons.length).toBeGreaterThan(0);
expect(screen.getAllByText("Create rule").length).toBeGreaterThan(0);
});
test("renders multiple create rule buttons for responsive design", () => {
render(<Header onToggle={mockOnToggle} />);
// Should have multiple create rule buttons for different breakpoints
const createRuleButtons = screen.getAllByText("Create rule");
expect(createRuleButtons.length).toBeGreaterThan(1);
});
});
describe("Avatar Images", () => {
test("renders avatar images with correct attributes", () => {
render(<Header onToggle={mockOnToggle} />);
const avatars = screen.getAllByRole("img");
expect(avatars.length).toBeGreaterThan(0);
// Check for avatar images
const avatarImages = avatars.filter(
(img) =>
img.alt === "Avatar 1" ||
img.alt === "Avatar 2" ||
img.alt === "Avatar 3"
);
expect(avatarImages.length).toBeGreaterThan(0);
});
});
describe("User Interactions", () => {
test("calls onToggle when navigation items are clicked", async () => {
const user = userEvent.setup();
render(<Header onToggle={mockOnToggle} />);
// Find and click a navigation item - use menuitem role since they're in a menubar
const useCasesLinks = screen.getAllByRole("menuitem", {
name: "Navigate to Use cases page",
});
expect(useCasesLinks.length).toBeGreaterThan(0);
const useCasesLink = useCasesLinks[0];
await user.click(useCasesLink);
expect(mockOnToggle).toHaveBeenCalledTimes(1);
});
});
describe("CSS Classes and Styling", () => {
test("has correct CSS classes for styling", () => {
const { container } = render(<Header onToggle={mockOnToggle} />);
const header = container.querySelector(
'[role="banner"][aria-label="Main navigation header"]'
);
expect(header).toHaveClass("bg-[var(--color-surface-default-primary)]");
expect(header).toHaveClass("w-full");
expect(header).toHaveClass("border-b");
expect(header).toHaveClass(
"border-[var(--border-color-default-tertiary)]"
);
const nav = container.querySelector(
'[role="navigation"][aria-label="Main navigation"]'
);
expect(nav).toHaveClass("flex");
expect(nav).toHaveClass("items-center");
expect(nav).toHaveClass("justify-between");
});
});
});
+183
View File
@@ -0,0 +1,183 @@
import { describe, test, expect, vi } from "vitest";
import { render, screen } from "@testing-library/react";
import RootLayout from "../../app/layout";
// Mock the font imports since they're Next.js specific
vi.mock("next/font/google", () => ({
Inter: vi.fn(() => ({
variable: "--font-inter",
style: { fontFamily: "Inter" },
})),
Bricolage_Grotesque: vi.fn(() => ({
variable: "--font-bricolage-grotesque",
style: { fontFamily: "Bricolage Grotesque" },
})),
Space_Grotesk: vi.fn(() => ({
variable: "--font-space-grotesk",
style: { fontFamily: "Space Grotesk" },
})),
}));
describe("RootLayout", () => {
test("renders HTML structure with correct attributes", () => {
render(
<RootLayout>
<div>Test content</div>
</RootLayout>
);
const html = document.querySelector("html");
expect(html).toBeInTheDocument();
expect(html).toHaveAttribute("lang", "en");
expect(html).toHaveClass("font-sans");
});
test("renders body with font variables", () => {
render(
<RootLayout>
<div>Test content</div>
</RootLayout>
);
const body = document.querySelector("body");
expect(body).toBeInTheDocument();
expect(body).toHaveClass("--font-inter");
expect(body).toHaveClass("--font-bricolage-grotesque");
expect(body).toHaveClass("--font-space-grotesk");
});
test("renders main layout structure", () => {
render(
<RootLayout>
<div>Test content</div>
</RootLayout>
);
const mainContainer = document.querySelector(".min-h-screen.flex.flex-col");
expect(mainContainer).toBeInTheDocument();
});
test("renders HomeHeader component", () => {
render(
<RootLayout>
<div>Test content</div>
</RootLayout>
);
// The HomeHeader component should be rendered
// We can check for its presence by looking for elements that would be in the header
const header = document.querySelector("header");
expect(header).toBeInTheDocument();
});
test("renders main content area", () => {
render(
<RootLayout>
<div>Test content</div>
</RootLayout>
);
const main = document.querySelector("main");
expect(main).toBeInTheDocument();
expect(main).toHaveClass("flex-1");
expect(main).toHaveTextContent("Test content");
});
test("renders Footer component", () => {
render(
<RootLayout>
<div>Test content</div>
</RootLayout>
);
// The Footer component should be rendered
const footer = document.querySelector("footer");
expect(footer).toBeInTheDocument();
});
test("renders children content correctly", () => {
const testContent = "This is test content";
render(
<RootLayout>
<div>{testContent}</div>
</RootLayout>
);
expect(screen.getByText(testContent)).toBeInTheDocument();
});
test("has correct CSS classes for layout structure", () => {
render(
<RootLayout>
<div>Test content</div>
</RootLayout>
);
const mainContainer = document.querySelector(".min-h-screen.flex.flex-col");
expect(mainContainer).toBeInTheDocument();
expect(mainContainer).toHaveClass("min-h-screen");
expect(mainContainer).toHaveClass("flex");
expect(mainContainer).toHaveClass("flex-col");
});
test("main element has correct flex properties", () => {
render(
<RootLayout>
<div>Test content</div>
</RootLayout>
);
const main = document.querySelector("main");
expect(main).toHaveClass("flex-1");
});
test("renders complete page structure", () => {
render(
<RootLayout>
<div>Test content</div>
</RootLayout>
);
// Check for all major structural elements
expect(document.querySelector("html")).toBeInTheDocument();
expect(document.querySelector("body")).toBeInTheDocument();
expect(document.querySelector("header")).toBeInTheDocument();
expect(document.querySelector("main")).toBeInTheDocument();
expect(document.querySelector("footer")).toBeInTheDocument();
});
test("maintains proper document structure", () => {
render(
<RootLayout>
<div>Test content</div>
</RootLayout>
);
// Check that the document has proper structure
const html = document.querySelector("html");
const body = html.querySelector("body");
const header = body.querySelector("header");
const main = body.querySelector("main");
const footer = body.querySelector("footer");
expect(html).toBeInTheDocument();
expect(body).toBeInTheDocument();
expect(header).toBeInTheDocument();
expect(main).toBeInTheDocument();
expect(footer).toBeInTheDocument();
});
test("renders multiple children correctly", () => {
render(
<RootLayout>
<div>First child</div>
<div>Second child</div>
<div>Third child</div>
</RootLayout>
);
expect(screen.getByText("First child")).toBeInTheDocument();
expect(screen.getByText("Second child")).toBeInTheDocument();
expect(screen.getByText("Third child")).toBeInTheDocument();
});
});
+248
View File
@@ -0,0 +1,248 @@
import { describe, test, expect } from "vitest";
import { render, screen } from "@testing-library/react";
import Page from "../../app/page";
describe("Page", () => {
test("renders all main sections", () => {
render(<Page />);
// Check that all main sections are rendered (using getAllByText since there are multiple instances)
expect(screen.getAllByText("Collaborate").length).toBeGreaterThan(0);
expect(screen.getAllByText("with clarity").length).toBeGreaterThan(0);
expect(
screen.getAllByText(
"Help your community make important decisions in a way that reflects its unique values."
).length
).toBeGreaterThan(0);
// Check numbered cards section (using getAllByText since there are multiple instances)
expect(
screen.getAllByText("How CommunityRule works").length
).toBeGreaterThan(0);
expect(
screen.getAllByText(
"Here's a quick overview of the process, from start to finish."
).length
).toBeGreaterThan(0);
// Check feature grid section (using getAllByText since there are multiple instances)
expect(
screen.getAllByText("We've got your back, every step of the way").length
).toBeGreaterThan(0);
expect(
screen.getAllByText(
"Use our toolkit to improve, document, and evolve your organization."
).length
).toBeGreaterThan(0);
// Check ask organizer section (using getAllByText since there are multiple instances)
expect(screen.getAllByText("Still have questions?").length).toBeGreaterThan(
0
);
expect(
screen.getAllByText("Get answers from an experienced organizer").length
).toBeGreaterThan(0);
});
test("renders hero banner with correct data", () => {
render(<Page />);
// Check hero banner content (using getAllByText since there are multiple instances)
expect(screen.getAllByText("Collaborate").length).toBeGreaterThan(0);
expect(screen.getAllByText("with clarity").length).toBeGreaterThan(0);
expect(
screen.getAllByText(
"Help your community make important decisions in a way that reflects its unique values."
).length
).toBeGreaterThan(0);
expect(
screen.getAllByText("Learn how CommunityRule works").length
).toBeGreaterThan(0);
});
test("renders numbered cards with correct data", () => {
render(<Page />);
// Check numbered cards content (using getAllByText since there are multiple instances)
expect(
screen.getAllByText("How CommunityRule works").length
).toBeGreaterThan(0);
expect(
screen.getAllByText(
"Here's a quick overview of the process, from start to finish."
).length
).toBeGreaterThan(0);
// Check individual card content (using getAllByText since there are multiple instances)
expect(
screen.getAllByText("Document how your community makes decisions").length
).toBeGreaterThan(0);
expect(
screen.getAllByText(
"Build an operating manual for a successful community"
).length
).toBeGreaterThan(0);
expect(
screen.getAllByText(
"Get a link to your manual for your group to review and evolve"
).length
).toBeGreaterThan(0);
});
test("renders feature grid with correct data", () => {
render(<Page />);
// Check feature grid content (using getAllByText since there are multiple instances)
expect(
screen.getAllByText("We've got your back, every step of the way").length
).toBeGreaterThan(0);
expect(
screen.getAllByText(
"Use our toolkit to improve, document, and evolve your organization."
).length
).toBeGreaterThan(0);
});
test("renders ask organizer section with correct data", () => {
render(<Page />);
// Check ask organizer content (using getAllByText since there are multiple instances)
expect(screen.getAllByText("Still have questions?").length).toBeGreaterThan(
0
);
expect(
screen.getAllByText("Get answers from an experienced organizer").length
).toBeGreaterThan(0);
expect(screen.getAllByText("Ask an organizer").length).toBeGreaterThan(0);
});
test("renders all component sections", () => {
render(<Page />);
// Check that all major components are present by looking for their content
// HeroBanner
expect(screen.getAllByText("Collaborate").length).toBeGreaterThan(0);
// LogoWall - should be present (even if just the component structure)
// NumberedCards
expect(
screen.getAllByText("How CommunityRule works").length
).toBeGreaterThan(0);
// RuleStack - should be present
// FeatureGrid
expect(
screen.getAllByText("We've got your back, every step of the way").length
).toBeGreaterThan(0);
// QuoteBlock - should be present
// AskOrganizer
expect(screen.getAllByText("Still have questions?").length).toBeGreaterThan(
0
);
});
test("has correct page structure", () => {
render(<Page />);
const mainContainer = screen.getAllByText("Collaborate")[0].closest("div");
expect(mainContainer).toBeInTheDocument();
});
test("renders call-to-action elements", () => {
render(<Page />);
// Check CTA button in hero banner
expect(
screen.getAllByText("Learn how CommunityRule works").length
).toBeGreaterThan(0);
// Check CTA button in ask organizer section
expect(screen.getAllByText("Ask an organizer").length).toBeGreaterThan(0);
});
test("renders descriptive text content", () => {
render(<Page />);
// Check main description (using getAllByText since there are multiple instances)
expect(
screen.getAllByText(
"Help your community make important decisions in a way that reflects its unique values."
).length
).toBeGreaterThan(0);
// Check numbered cards description (using getAllByText since there are multiple instances)
expect(
screen.getAllByText(
"Here's a quick overview of the process, from start to finish."
).length
).toBeGreaterThan(0);
// Check feature grid description (using getAllByText since there are multiple instances)
expect(
screen.getAllByText(
"Use our toolkit to improve, document, and evolve your organization."
).length
).toBeGreaterThan(0);
// Check ask organizer description (using getAllByText since there are multiple instances)
expect(
screen.getAllByText("Get answers from an experienced organizer").length
).toBeGreaterThan(0);
});
test("renders section titles correctly", () => {
render(<Page />);
// Check all section titles (using getAllByText since there are multiple instances)
expect(screen.getAllByText("Collaborate").length).toBeGreaterThan(0);
expect(
screen.getAllByText("How CommunityRule works").length
).toBeGreaterThan(0);
expect(
screen.getAllByText("We've got your back, every step of the way").length
).toBeGreaterThan(0);
expect(screen.getAllByText("Still have questions?").length).toBeGreaterThan(
0
);
});
test("renders numbered card items with correct content", () => {
render(<Page />);
// Check all three numbered card items (using getAllByText since there are multiple instances)
expect(
screen.getAllByText("Document how your community makes decisions").length
).toBeGreaterThan(0);
expect(
screen.getAllByText(
"Build an operating manual for a successful community"
).length
).toBeGreaterThan(0);
expect(
screen.getAllByText(
"Get a link to your manual for your group to review and evolve"
).length
).toBeGreaterThan(0);
});
test("renders subtitle content correctly", () => {
render(<Page />);
// Check subtitles (using getAllByText since there are multiple instances)
expect(screen.getAllByText("with clarity").length).toBeGreaterThan(0);
expect(
screen.getAllByText(
"Here's a quick overview of the process, from start to finish."
).length
).toBeGreaterThan(0);
expect(
screen.getAllByText(
"Use our toolkit to improve, document, and evolve your organization."
).length
).toBeGreaterThan(0);
expect(
screen.getAllByText("Get answers from an experienced organizer").length
).toBeGreaterThan(0);
});
});
-12
View File
@@ -1,12 +0,0 @@
import { describe, it, expect } from "vitest";
import React from "react";
function Thing() {
return <div>ok</div>;
}
describe("jsx in .js", () => {
it("parses", () => {
expect(Thing).toBeTypeOf("function");
});
});
-7
View File
@@ -1,7 +0,0 @@
import { describe, test, expect } from 'vitest';
describe('Simple Test', () => {
test('should work', () => {
expect(1 + 1).toBe(2);
});
});