Open-Source Wikis

/

Tailwind CSS

/

How to contribute

/

Testing

tailwindlabs/tailwindcss

Testing

The project has four test surfaces.

1. Vitest unit tests

Most testing happens here. The root vitest.config.ts defines a workspace of two projects: a node project and a jsdom project (browser-like environment for the browser build).

Common entry points:

File Coverage
packages/tailwindcss/src/index.test.ts End-to-end compiler behavior (~3,000 lines)
packages/tailwindcss/src/utilities.test.ts Built-in utilities (largest test file in the repo)
packages/tailwindcss/src/variants.test.ts All variants
packages/tailwindcss/src/candidate.test.ts parseCandidate
packages/tailwindcss/src/css-parser.test.ts The CSS parser
packages/tailwindcss/src/canonicalize-candidates.test.ts Class-list normalization
packages/tailwindcss/src/source-maps/source-map.test.ts Source map generation
packages/tailwindcss/src/compat/config.test.ts v3 config bridging
packages/tailwindcss/src/compat/plugin-api.test.ts v3 plugin API
packages/@tailwindcss-postcss/src/index.test.ts PostCSS plugin behavior
packages/@tailwindcss-upgrade/src/index.test.ts Upgrade tool top-level
packages/@tailwindcss-upgrade/src/codemods/**/*.test.ts Per-codemod tests

Run all of them:

pnpm test           # cargo test + vitest run --hideSkippedTests
pnpm tdd            # vitest in watch mode

Run one file:

pnpm vitest run packages/tailwindcss/src/utilities.test.ts
pnpm vitest packages/tailwindcss/src/utilities.test.ts -- -t 'background-color'

Inline snapshots

The compiler's tests use expect(css).toMatchInlineSnapshot('...') heavily. To regenerate after a change:

pnpm vitest run -- --update

2. Cargo tests

The Rust crates ship in-line #[cfg(test)] mod tests { ... } blocks plus dedicated test files in crates/<crate>/tests/.

cargo test                          # all crates in the workspace
cargo test -p tailwindcss-oxide     # just the scanner
cargo test --release                # opt-level 3, slower compile, faster runtime

Notable Rust test files:

  • crates/oxide/tests/scanner.rs — ~1,800 lines exercising auto source detection, ignore behavior, and re-scan logic.
  • crates/oxide/src/extractor/mod.rs — large #[cfg(test)] block at the bottom that drives the extractor against fixture strings.

3. Integration tests (integrations/)

These are real builds, not mocks. Each subdirectory exercises one bundler against the freshly-packed dist tarballs from pnpm build:

integrations/
├── cli/
├── postcss/
├── vite/
├── webpack/
├── upgrade/
└── oxide/

The shared harness is integrations/utils.ts (~720 lines). It:

  • Spawns the right package manager (npm/yarn/pnpm/bun depending on the test).
  • Installs the locally packed *.tgz files.
  • Runs build/dev commands.
  • Asserts on output CSS, file content, and process behavior.

Run them all:

pnpm build
pnpm test:integrations

Run a subset:

pnpm test:integrations -- vite
pnpm test:integrations -- --grep "watch"

Integration tests are slow because each one starts a fresh package install. Reserve them for changes that cross package boundaries (e.g. a fix for @tailwindcss/postcss consuming a new compiler API).

4. Playwright UI tests (pnpm test:ui)

Two Playwright suites:

  • packages/tailwindcss/playwright.config.ts — runs tests/ui.spec.ts (~2,000 lines) against built CSS in real browsers. Tests browser-specific resolution: color-mix, @property, custom properties under @supports.
  • packages/@tailwindcss-browser/playwright.config.ts — drives the in-browser bundle and verifies it builds CSS from real DOM mutations.
pnpm build
pnpm test:ui

Benchmarks

Files matching *.bench.ts use Vitest's bench runner. Useful for verifying performance work:

pnpm bench

The compiler ships several benches: ast.bench.ts, candidate.bench.ts, css-parser.bench.ts, intellisense.bench.ts, index.bench.ts, plus per-utility benchmarks under packages/tailwindcss/src/utils/.

CI

Tests are wired up in .github/workflows/ci.yml (unit + lint) and .github/workflows/integration-tests.yml (integrations). Adding [ci-all] to a PR description fans the CI matrix out across all platforms (otherwise it runs on Linux only).

What "good test coverage" looks like here

Patterns to follow when adding tests:

  • Pin the bug. Reproduce the failing case first; commit only when it passes.
  • Use snapshot tests for output CSS. They are the project's preferred way to assert on compiler output. They also catch incidental output changes.
  • Use small dedent-style fixture inputs. Most compiler tests construct a minimal CSS input string inline and assert against snapshotted output.
  • Test the public API. For new compiler features, add an index.test.ts case that goes through compile(...).
  • Test extractor changes against pre-existing fixtures. Don't invent a one-off test string; the extractor tests already cover dozens of language quirks.

Local QA screenshots

These screenshots came from local QA runs and are kept here as supporting test evidence rather than overview material.

WEB-1 Browser bundle initial compile

WEB-2 Browser bundle DOM/style mutation rebuild

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Testing – Tailwind CSS wiki | Factory