Testing skill for shadcn/ui and Radix UI component libraries covering accessible component testing, dialog and popover testing, form validation testing, data table testing, command palette testing, and theme switching verification.
Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.
Quelldateien prüfen
Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.
Mit Codex oder Claude installieren Kopieren Sie diesen Prompt, fügen Sie ihn in Codex, Claude oder einen anderen Assistant ein und lassen Sie die Skill-Seite prüfen und installieren.
Ein direkter Befehl überspringt den Prüf-Prompt. Prüfen Sie die Quelle, bevor Sie ihn ausführen.
Testing skill for shadcn/ui and Radix UI component libraries covering accessible component testing, dialog and popover testing, form validation testing, data table testing, command palette testing, and theme switching verification.
You are an expert software engineer specializing in testing shadcn/ui and Radix UI component libraries. When the user asks you to write, review, or debug tests for shadcn/ui components including Dialog, Popover, Form, DataTable, Command palette, and theme switching, follow these detailed instructions.
Core Principles
Test user interactions, not Radix internals -- Radix primitives are well-tested; focus on your composition and customization.
Use accessible queries -- Prefer getByRole, getByLabelText, and getByText over CSS selectors to ensure ARIA compliance.
Test keyboard navigation -- shadcn/ui components support full keyboard interaction; verify tab order, arrow keys, and escape.
Verify portal rendering -- Dialogs, popovers, and dropdowns render in portals; use screen queries, not container queries.
Test form integration -- shadcn/ui forms use react-hook-form + zod; test validation messages and submission behavior.
Assert on visual states -- Test open/closed, disabled, loading, and error states explicitly.
Test theme switching -- Verify components render correctly in both light and dark modes.
// src/components/__tests__/accordion.test.tsximport { describe, it, expect } from'vitest';
import { render, screen } from'../test-utils/render-with-providers';
import userEvent from'@testing-library/user-event';
import {
Accordion,
AccordionItem,
AccordionTrigger,
AccordionContent,
} from'@/components/ui/accordion';
functionFAQ() {
return (
<Accordiontype="single"collapsible><AccordionItemvalue="item-1"><AccordionTrigger>What is shadcn/ui?</AccordionTrigger><AccordionContent>
A collection of reusable components built with Radix UI and Tailwind CSS.
</AccordionContent></AccordionItem><AccordionItemvalue="item-2"><AccordionTrigger>Is it accessible?</AccordionTrigger><AccordionContent>
Yes, it follows WAI-ARIA design patterns.
</AccordionContent></AccordionItem></Accordion>
);
}
describe('Accordion', () => {
it('should render all triggers', () => {
render(<FAQ />);
expect(screen.getByRole('button', { name: /what is shadcn/i })).toBeInTheDocument();
expect(screen.getByRole('button', { name: /is it accessible/i })).toBeInTheDocument();
});
it('should expand content when trigger is clicked', async () => {
const user = userEvent.setup();
render(<FAQ />);
await user.click(screen.getByRole('button', { name: /what is shadcn/i }));
expect(screen.getByText(/reusable components/i)).toBeVisible();
});
it('should collapse when clicking the same trigger again', async () => {
const user = userEvent.setup();
render(<FAQ />);
const trigger = screen.getByRole('button', { name: /what is shadcn/i });
await user.click(trigger);
expect(screen.getByText(/reusable components/i)).toBeVisible();
await user.click(trigger);
// Content should be hidden (aria-hidden or removed)expect(trigger).toHaveAttribute('aria-expanded', 'false');
});
it('should close previous item when opening another (single mode)', async () => {
const user = userEvent.setup();
render(<FAQ />);
await user.click(screen.getByRole('button', { name: /what is shadcn/i }));
expect(screen.getByText(/reusable components/i)).toBeVisible();
await user.click(screen.getByRole('button', { name: /is it accessible/i }));
expect(screen.getByText(/WAI-ARIA/i)).toBeVisible();
// First item should be collapsedconst firstTrigger = screen.getByRole('button', { name: /what is shadcn/i });
expect(firstTrigger).toHaveAttribute('aria-expanded', 'false');
});
it('should support keyboard navigation', async () => {
const user = userEvent.setup();
render(<FAQ />);
const firstTrigger = screen.getByRole('button', { name: /what is shadcn/i });
firstTrigger.focus();
// Space should toggleawait user.keyboard(' ');
expect(firstTrigger).toHaveAttribute('aria-expanded', 'true');
// Enter should also toggleawait user.keyboard('{Enter}');
expect(firstTrigger).toHaveAttribute('aria-expanded', 'false');
});
});
E2E Tests with Playwright
// e2e/components.spec.tsimport { test, expect } from'@playwright/test';
test.describe('shadcn/ui Components E2E', () => {
test('dialog should open and close with keyboard', async ({ page }) => {
await page.goto('/components/dialog-demo');
await page.getByRole('button', { name: /open dialog/i }).click();
awaitexpect(page.getByRole('dialog')).toBeVisible();
// Close with Escapeawait page.keyboard.press('Escape');
awaitexpect(page.getByRole('dialog')).not.toBeVisible();
});
test('command palette should open with Cmd+K', async ({ page }) => {
await page.goto('/dashboard');
// Open command palette with keyboard shortcutawait page.keyboard.press('Meta+k');
awaitexpect(page.getByPlaceholder(/type a command/i)).toBeVisible();
// Search and selectawait page.getByPlaceholder(/type a command/i).fill('settings');
await page.keyboard.press('Enter');
awaitexpect(page).toHaveURL(/\/settings/);
});
test('data table should sort and filter', async ({ page }) => {
await page.goto('/dashboard/users');
// Sort by nameawait page.getByRole('columnheader', { name: /name/i }).click();
const firstRow = page.getByRole('row').nth(1);
awaitexpect(firstRow.getByRole('cell').first()).toHaveText(/^A/);
// Filterawait page.getByPlaceholder(/filter/i).fill('admin');
const rows = page.getByRole('row');
awaitexpect(rows).toHaveCount(3); // Header + 2 admin rows
});
test('form should show validation errors and submit', async ({ page }) => {
await page.goto('/profile/edit');
// Submit empty formawait page.getByRole('button', { name: /save/i }).click();
awaitexpect(page.getByText(/at least 3 characters/i)).toBeVisible();
// Fill valid dataawait page.getByLabel(/username/i).fill('testuser');
await page.getByLabel(/email/i).fill('test@example.com');
await page.getByRole('button', { name: /save/i }).click();
awaitexpect(page.getByText(/saved successfully/i)).toBeVisible();
});
});
Best Practices
Use getByRole over getByTestId -- Role queries test accessibility for free; test IDs skip it.
Always use userEvent over fireEvent -- userEvent simulates real browser interactions including focus.
Wrap state changes in waitFor -- Radix components use animations; state changes may be async.
Test portal-rendered content with screen -- Dialogs and popovers render outside the component tree.
Run axe audits on interactive states -- Test accessibility of both closed and open states.
Mock next-themes for predictable theme testing -- Avoid relying on browser/OS theme preferences.
Test disabled states explicitly -- Verify that disabled buttons, inputs, and menu items are not interactive.
Use within() for scoped queries -- When testing tables or lists, scope queries to a specific row or item.
Test the complete form lifecycle -- Empty submit, validation errors, correction, successful submit.
Keep component tests focused -- Test one component behavior per test; compose for integration tests.
Anti-Patterns to Avoid
Testing Radix internal state -- Do not assert on data-state attributes; test visible behavior instead.
Using CSS selectors for queries -- querySelector('.shadcn-button') is fragile; use ARIA roles.
Forgetting to wait for animations -- Radix uses enter/exit animations; wrap assertions in waitFor.
Testing styled-component class names -- Tailwind classes are implementation details; test visual output.
Skipping keyboard interaction tests -- Many users rely on keyboard navigation; it must work.
Mocking Radix primitives -- Never mock the component library; test the real components.
Testing only the open state -- Verify that closed/collapsed states also render correctly.
Ignoring focus management -- After a dialog closes, focus should return to the trigger element.
Not testing with screen readers -- Run automated ARIA checks and manual VoiceOver/NVDA testing.
Hardcoding animation durations in tests -- Use waitFor instead of setTimeout for animation timing.
Running Tests
# Run all component tests
npx vitest run src/components/__tests__/
# Run a specific component test
npx vitest run src/components/__tests__/dialog.test.tsx
# Run with coverage
npx vitest run src/components/__tests__/ --coverage
# Watch mode for development
npx vitest watch src/components/__tests__/
# Run E2E tests
npx playwright test e2e/components.spec.ts
# Run E2E with UI mode
npx playwright test --ui
# Run accessibility audit
npx vitest run src/components/__tests__/ --reporter=verbose
# Debug a failing test
npx vitest run src/components/__tests__/form.test.tsx --reporter=verbose