Initial testing framework

This commit is contained in:
adilallo
2025-08-28 21:17:27 -06:00
parent f9ff2d0c08
commit 165f7f8179
15 changed files with 9400 additions and 60 deletions
+5
View File
@@ -0,0 +1,5 @@
import { injectAxe, checkA11y } from '@axe-core/playwright';
export async function runA11y(page, options = {}) {
await injectAxe(page);
await checkA11y(page, undefined, { detailedReport: true, detailedReportOptions: { html: true }, ...options });
}
+95
View File
@@ -0,0 +1,95 @@
import { test, expect } from '@playwright/test';
import { runA11y } from './axe';
test('home loads, nav works, basic a11y passes', async ({ page }) => {
await page.goto('/');
// Check that the page loads
await expect(page).toHaveTitle(/Community Rule/);
// Check for main navigation elements
await expect(page.getByRole('banner')).toBeVisible();
// Check for main content
await expect(page.getByRole('main')).toBeVisible();
// Check for footer
await expect(page.getByRole('contentinfo')).toBeVisible();
});
test('keyboard navigation works', async ({ page }) => {
await page.goto('/');
// Tab through the page
await page.keyboard.press('Tab');
// Check that focus is visible
await expect(page.locator(':focus')).toBeVisible();
// Continue tabbing to test navigation
for (let i = 0; i < 5; i++) {
await page.keyboard.press('Tab');
await expect(page.locator(':focus')).toBeVisible();
}
});
test('accessibility standards are met', async ({ page }) => {
await page.goto('/');
// Run automated a11y checks
await runA11y(page, {
axeOptions: {
runOnly: ['wcag2a', 'wcag2aa']
}
});
});
test('responsive design works', async ({ page }) => {
// Test mobile viewport
await page.setViewportSize({ width: 375, height: 667 });
await page.goto('/');
// Check that content is visible on mobile
await expect(page.getByRole('main')).toBeVisible();
// Test tablet viewport
await page.setViewportSize({ width: 768, height: 1024 });
await expect(page.getByRole('main')).toBeVisible();
// Test desktop viewport
await page.setViewportSize({ width: 1440, height: 900 });
await expect(page.getByRole('main')).toBeVisible();
});
test('images have alt text', async ({ page }) => {
await page.goto('/');
// Get all images
const images = page.locator('img');
const imageCount = await images.count();
// Check that all images have alt attributes
for (let i = 0; i < imageCount; i++) {
const image = images.nth(i);
const alt = await image.getAttribute('alt');
expect(alt).toBeTruthy();
}
});
test('links are accessible', async ({ page }) => {
await page.goto('/');
// Get all links
const links = page.locator('a');
const linkCount = await links.count();
// Check that all links have either text content or aria-label
for (let i = 0; i < linkCount; i++) {
const link = links.nth(i);
const text = await link.textContent();
const ariaLabel = await link.getAttribute('aria-label');
// Link should have either text content or aria-label
expect(text?.trim() || ariaLabel).toBeTruthy();
}
});
@@ -0,0 +1,156 @@
import { render, screen, cleanup } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { vi, describe, test, expect, afterEach } from "vitest";
import ContentLockup from "../../app/components/ContentLockup";
afterEach(() => {
cleanup();
});
describe("ContentLockup Integration", () => {
test("renders hero variant with all content", () => {
render(
<ContentLockup
variant="hero"
title="Welcome"
subtitle="Get Started"
description="This is a description"
ctaText="Get Started"
/>
);
expect(
screen.getByRole("heading", { name: "Welcome" })
).toBeInTheDocument();
expect(
screen.getByRole("heading", { name: "Get Started" })
).toBeInTheDocument();
expect(screen.getByText("This is a description")).toBeInTheDocument();
expect(screen.getAllByRole("button", { name: "Get Started" })).toHaveLength(
3
);
});
test("renders feature variant with link", () => {
render(
<ContentLockup
variant="feature"
title="Feature Title"
subtitle="Feature Subtitle"
description="Feature description"
linkText="Learn More"
linkHref="/learn"
/>
);
expect(
screen.getByRole("heading", { name: "Feature Title" })
).toBeInTheDocument();
expect(
screen.getByRole("link", { name: "Learn More" })
).toBeInTheDocument();
expect(screen.getByRole("link", { name: "Learn More" })).toHaveAttribute(
"href",
"/learn"
);
});
test("renders ask variant with simplified structure", () => {
render(
<ContentLockup
variant="ask"
title="Ask Question"
subtitle="Ask subtitle"
/>
);
expect(
screen.getByRole("heading", { name: "Ask Question" })
).toBeInTheDocument();
expect(
screen.getByRole("heading", { name: "Ask subtitle" })
).toBeInTheDocument();
// Ask variant should not have description or CTA
expect(screen.queryByRole("button")).not.toBeInTheDocument();
});
test("handles left alignment", () => {
render(
<ContentLockup
variant="ask"
title="Left Aligned"
subtitle="Subtitle"
alignment="left"
/>
);
const container = screen
.getByRole("heading", { name: "Left Aligned" })
.closest("div");
expect(container).toHaveClass("justify-start");
});
test("renders responsive buttons correctly", () => {
render(
<ContentLockup variant="hero" title="Responsive" ctaText="Click Me" />
);
// Should render all three button variants for different breakpoints
const buttons = screen.getAllByRole("button", { name: "Click Me" });
expect(buttons).toHaveLength(3);
});
test("applies custom button className", () => {
render(
<ContentLockup
variant="hero"
title="Custom Button"
ctaText="Custom"
buttonClassName="custom-button-class"
/>
);
const buttons = screen.getAllByRole("button", { name: "Custom" });
// The large button (md breakpoint) should have the custom class
expect(buttons[1]).toHaveClass("custom-button-class");
});
test("handles missing optional props gracefully", () => {
render(<ContentLockup variant="hero" title="Minimal" />);
expect(
screen.getByRole("heading", { name: "Minimal" })
).toBeInTheDocument();
// Should not crash without subtitle, description, or CTA
expect(screen.queryByRole("button")).not.toBeInTheDocument();
});
test("renders decorative shape for hero variant", () => {
render(<ContentLockup variant="hero" title="Hero with Shape" />);
const shape = screen.getByAltText("Decorative shapes");
expect(shape).toBeInTheDocument();
expect(shape).toHaveAttribute("src", "assets/Shapes_1.svg");
});
test("does not render shape for non-hero variants", () => {
render(<ContentLockup variant="feature" title="Feature without Shape" />);
expect(screen.queryByAltText("Decorative shapes")).not.toBeInTheDocument();
});
test("link has proper accessibility attributes", () => {
render(
<ContentLockup
variant="feature"
title="Accessible"
linkText="Accessible Link"
linkHref="/accessible"
/>
);
const link = screen.getByRole("link", { name: "Accessible Link" });
expect(link).toHaveAttribute("href", "/accessible");
expect(link).toHaveClass("focus:outline-none", "focus:ring-2");
});
});
+2
View File
@@ -0,0 +1,2 @@
import { setupServer } from 'msw/node';
export const server = setupServer(); // add handlers per test as needed
+112
View File
@@ -0,0 +1,112 @@
import { render, screen, cleanup } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { vi, describe, test, expect, afterEach } from "vitest";
import Button from "../../app/components/Button";
afterEach(() => {
cleanup();
});
describe("Button Component", () => {
test("renders button with children", () => {
render(<Button>Click me</Button>);
const button = screen.getByRole("button", { name: "Click me" });
expect(button).toBeInTheDocument();
});
test("handles click events", async () => {
const user = userEvent.setup();
const onClick = vi.fn();
render(<Button onClick={onClick}>Click me</Button>);
const button = screen.getByRole("button", { name: "Click me" });
await user.click(button);
expect(onClick).toHaveBeenCalledTimes(1);
});
test("renders as link when href is provided", () => {
render(<Button href="/test">Link Button</Button>);
const link = screen.getByRole("link", { name: "Link Button" });
expect(link).toBeInTheDocument();
expect(link).toHaveAttribute("href", "/test");
});
test("applies different variants", () => {
const { rerender } = render(<Button variant="default">Default</Button>);
let button = screen.getByRole("button", { name: "Default" });
expect(button.className).toContain(
"bg-[var(--color-surface-inverse-primary)]"
);
rerender(<Button variant="secondary">Secondary</Button>);
button = screen.getByRole("button", { name: "Secondary" });
expect(button.className).toContain("bg-transparent");
});
test("applies different sizes", () => {
const { rerender } = render(<Button size="xsmall">Small</Button>);
let button = screen.getByRole("button", { name: "Small" });
expect(button.className).toContain("px-[var(--spacing-scale-006)]");
rerender(<Button size="large">Large</Button>);
button = screen.getByRole("button", { name: "Large" });
expect(button.className).toContain("px-[var(--spacing-scale-012)]");
});
test("handles disabled state", () => {
render(<Button disabled>Disabled</Button>);
const button = screen.getByRole("button", { name: "Disabled" });
expect(button).toBeDisabled();
expect(button).toHaveAttribute("aria-disabled", "true");
expect(button).toHaveAttribute("tabindex", "-1");
});
test("applies custom className", () => {
render(<Button className="custom-class">Custom</Button>);
const button = screen.getByRole("button", { name: "Custom" });
expect(button.className).toContain("custom-class");
});
test("applies aria-label for accessibility", () => {
render(<Button aria-label="Custom label">Button</Button>);
const button = screen.getByRole("button", { name: "Custom label" });
expect(button).toHaveAttribute("aria-label", "Custom label");
});
test("renders with design tokens", () => {
render(<Button>Token Test</Button>);
const button = screen.getByRole("button", { name: "Token Test" });
// Check that design tokens are applied
expect(button.className).toContain(
"rounded-[var(--radius-measures-radius-full)]"
);
expect(button.className).toContain(
"bg-[var(--color-surface-inverse-primary)]"
);
});
test("handles keyboard navigation", async () => {
const user = userEvent.setup();
const onClick = vi.fn();
render(<Button onClick={onClick}>Keyboard</Button>);
const button = screen.getByRole("button", { name: "Keyboard" });
button.focus();
expect(button).toHaveFocus();
await user.keyboard("{Enter}");
expect(onClick).toHaveBeenCalledTimes(1);
});
test("does not render as link when disabled and href provided", () => {
render(
<Button href="/test" disabled>
Disabled Link
</Button>
);
const button = screen.getByRole("button", { name: "Disabled Link" });
expect(button).toBeInTheDocument();
expect(button).toBeDisabled();
});
});
+12
View File
@@ -0,0 +1,12 @@
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
@@ -0,0 +1,7 @@
import { describe, test, expect } from 'vitest';
describe('Simple Test', () => {
test('should work', () => {
expect(1 + 1).toBe(2);
});
});