Testing guide
Comprehensive testing documentation for the nix-config project.
Overview
Section titled “Overview”This project uses a robust testing stack with type-safe patterns and functional programming principles:
- Vitest 3.x: Fast unit and component testing with Astro integration
- Playwright: Multi-browser E2E testing with type-safe APIs
- Astro Container API: Component rendering and testing
- Nix: Reproducible test environments with browser binaries
Quick Start
Section titled “Quick Start”# Enter nix development environment (manages all dependencies)nix develop
# Install npm dependenciesbun install
# Run all testsjust test
# Run tests in watch modejust test-watch
# Run E2E tests with UIjust test-ui
# Generate coverage reportjust test-coverageTest structure
Section titled “Test structure”packages/docs/├── src/│ ├── components/│ │ ├── Card.astro│ │ └── Card.test.ts # Component tests (co-located)│ └── utils/│ ├── formatters.ts│ └── formatters.test.ts # Unit tests (co-located)├── e2e/│ └── homepage.spec.ts # E2E tests├── tests/│ ├── fixtures/ # Shared test data│ │ ├── components.ts│ │ └── README.md│ └── types/ # Test type definitions│ ├── test-helpers.ts│ └── README.md├── vitest.config.ts # Vitest configuration├── playwright.config.ts # Playwright configuration└── coverage/ # Coverage reports (generated)Unit Testing with Vitest
Section titled “Unit Testing with Vitest”Writing Unit Tests
Section titled “Writing Unit Tests”Unit tests use Vitest’s global API and are co-located with source files:
import { describe, expect, it } from "vitest";import { capitalizeFirst } from "./formatters";
describe("capitalizeFirst", () => { it("capitalizes the first letter", () => { expect(capitalizeFirst("hello")).toBe("Hello"); });
it("handles empty strings", () => { expect(capitalizeFirst("")).toBe(""); });});Running Unit Tests
Section titled “Running Unit Tests”# Run all unit testsjust test-unit
# Watch mode for developmentjust test-watch
# With coveragejust test-coverageTest Patterns
Section titled “Test Patterns”Follow these patterns for unit tests:
- Pure functions: Test inputs and outputs without side effects
- Type safety: No
anytypes, leverage TypeScript inference - Descriptive names: Use
describeblocks for organization - Edge cases: Test empty, null, and boundary conditions
- Co-location: Place tests next to source files
Component Testing with Astro Container API
Section titled “Component Testing with Astro Container API”Writing Component Tests
Section titled “Writing Component Tests”Use the Astro Container API to test Astro components:
import { experimental_AstroContainer as AstroContainer } from "astro/container";import { describe, expect, it } from "vitest";import Card from "./Card.astro";
describe("Card component", () => { it("renders with props and slots", async () => { const container = await AstroContainer.create(); const result = await container.renderToString(Card, { props: { title: "Test Card" }, slots: { default: "Content" }, });
expect(result).toContain("Test Card"); expect(result).toContain("Content"); });});Container API Methods
Section titled “Container API Methods”AstroContainer.create(): Create container instancecontainer.renderToString(Component, options): Render to HTML stringcontainer.renderToResponse(Component, options): Render to Response object
Component Test Patterns
Section titled “Component Test Patterns”- Props validation: Test all prop variations
- Slot rendering: Verify slot content appears correctly
- Conditional rendering: Test dynamic element rendering
- Class application: Verify CSS class logic
- Type safety: Use proper prop types from component
E2E Testing with Playwright
Section titled “E2E Testing with Playwright”Writing E2E Tests
Section titled “Writing E2E Tests”E2E tests use Playwright’s test runner and live in the e2e/ directory:
import { expect, test } from "@playwright/test";
test.describe("Homepage", () => { test("has correct title", async ({ page }) => { await page.goto("/"); await expect(page).toHaveTitle(/My Docs/); });
test("navigates to guide", async ({ page }) => { await page.goto("/"); const link = page.getByRole("link", { name: /example guide/i }); await link.click(); await expect(page).toHaveURL(/\/guides\/example/); });});Running E2E Tests
Section titled “Running E2E Tests”# Run E2E testsjust test-e2e
# Run with UI (interactive mode)just test-ui
# Run specific test filebun run test:e2e e2e/homepage.spec.ts
# Run in specific browserbun run test:e2e --project=firefoxE2E Test Patterns
Section titled “E2E Test Patterns”- Page objects: Use locators for maintainability
- Accessibility: Use
getByRolefor semantic selectors - Assertions: Use Playwright’s async assertions
- Multi-browser: Tests run in Chromium, Firefox, WebKit
- Visual regression: Use screenshots for visual testing
Test Configuration
Section titled “Test Configuration”Vitest Configuration
Section titled “Vitest Configuration”Key configuration in vitest.config.ts:
- Environment:
nodefor SSR component testing - Globals:
truefor auto-imported test APIs - Coverage: v8 provider with text, JSON, HTML, LCOV reporters
- Include:
src/**/*.{test,spec}.{ts,tsx},tests/**/*.{test,spec}.{ts,tsx} - Exclude:
node_modules,dist,.astro,e2e
Playwright Configuration
Section titled “Playwright Configuration”Key configuration in playwright.config.ts:
- Test directory:
./e2e - Browsers: Chromium, Firefox, WebKit
- Workers: 1 in CI, unlimited locally
- Retries: 2 in CI, 0 locally
- Base URL:
http://localhost:4321 - Web server: Starts Astro preview automatically
Nix Integration
Section titled “Nix Integration”Playwright browsers are managed by Nix for reproducibility:
packages = [ playwright-driver.browsers ];
shellHook = '' export PLAYWRIGHT_BROWSERS_PATH=${pkgs.playwright-driver.browsers} export PLAYWRIGHT_SKIP_VALIDATE_HOST_REQUIREMENTS=true'';CI/CD Integration
Section titled “CI/CD Integration”Tests run automatically in GitHub Actions on pull requests and pushes to main.
CI Test Job
Section titled “CI Test Job”The test job runs after nixci and before build:
- Install Nix and dependencies
- Run unit tests with Vitest
- Install Playwright browsers
- Build site for E2E testing
- Run E2E tests across all browsers
- Upload test results and coverage artifacts
Viewing CI Results
Section titled “Viewing CI Results”# Check workflow statusjust gh-workflow-status
# View logs for latest runjust gh-logs
# Download test artifacts from GitHubgh run downloadWriting New Tests
Section titled “Writing New Tests”Unit Test Checklist
Section titled “Unit Test Checklist”- Create test file next to source file (e.g.,
foo.test.tsnext tofoo.ts) - Use
describeblocks to organize related tests - Test happy path and edge cases
- Use type-safe assertions
- Avoid
anytypes - Run tests in watch mode during development
Component Test Checklist
Section titled “Component Test Checklist”- Create test file next to component (e.g.,
Card.test.tsnext toCard.astro) - Create container with
AstroContainer.create() - Test all prop variations
- Test slot content rendering
- Verify conditional rendering logic
- Check CSS class application
E2E Test Checklist
Section titled “E2E Test Checklist”- Create test file in
e2e/directory - Use
test.describefor grouping - Use accessible selectors (
getByRole,getByLabel) - Test user workflows, not implementation details
- Add assertions for page state changes
- Consider responsive design (mobile viewports)
Troubleshooting
Section titled “Troubleshooting”Vitest Issues
Section titled “Vitest Issues”Tests not found
- Check file patterns in
vitest.config.ts - Ensure test files match
*.test.tsor*.spec.ts - Verify files are not in exclude list
Import errors
- Check TypeScript configuration
- Ensure dependencies are installed with
bun install - Verify Astro integration with
getViteConfig()
Playwright Issues
Section titled “Playwright Issues”Browsers not installed
just playwright-install# orbunx playwright install --with-depsPort already in use
- Stop other development servers on port 4321
- Or change
baseURLinplaywright.config.ts
Nix environment issues
# Rebuild nix shellnix develop --rebuild
# Verify environment variablesecho $PLAYWRIGHT_BROWSERS_PATHCoverage Issues
Section titled “Coverage Issues”Low coverage warnings
- Add tests for uncovered files
- Check coverage thresholds in
vitest.config.ts - Review coverage report in
coverage/index.html
Coverage not generated
# Ensure coverage dependency is installedbun install @vitest/coverage-v8
# Run with coverage flagjust test-coverageBest Practices
Section titled “Best Practices”Type Safety
Section titled “Type Safety”- No
anytypes: Use proper TypeScript types everywhere - Strict mode: Project uses Astro’s strict tsconfig
- Type imports: Import types explicitly when needed
Functional Patterns
Section titled “Functional Patterns”- Pure functions: Test functions without side effects
- Immutable data: Use const and readonly where appropriate
- Composition: Build complex tests from simple, reusable pieces
Test Organization
Section titled “Test Organization”- Co-location: Keep tests near source code
- Fixtures: Share test data in
tests/fixtures/ - Types: Define test types in
tests/types/ - DRY: Extract common patterns to shared utilities
Performance
Section titled “Performance”- Parallel execution: Tests run in parallel by default
- Watch mode: Use for rapid feedback during development
- CI optimization: Tests run with retries and proper workers in CI