Testing and Release Operations
Purpose and Scope
This page documents the maintainer-facing test and release operations that are visible in the repository automation. SWR is a React Hooks data-fetching library, so its operational checks need to cover source correctness, built package behavior, TypeScript types, browser-like React behavior, and end-to-end application scenarios. The repository separates those concerns into Jest configuration, a build-test Jest variant, a Playwright-backed end-to-end job, React canary and React 17 compatibility workflows, and a manually triggered npm publish workflow.
The main operational pattern is to build before validating, then test both source-mapped modules and packaged output. The standard CI workflow runs on pushes to main and on pull requests, while the React 17 workflow also runs for version tags. React canary is not part of every pull request; it is scheduled daily and can also be started manually. Publishing is intentionally manual through GitHub Actions workflow dispatch and uses npm trusted publishing through OpenID Connect. Sources: .github/workflows/test.yml, .github/workflows/test-canary.yml, .github/workflows/test-legacy-react.yml, .github/workflows/trigger-release.yml
Use this page when changing test configuration, triaging a failed workflow, preparing a release, or deciding where a new compatibility check belongs. It is not a replacement for feature-level unit tests; instead, it explains how the existing automation composes those tests into repository-level confidence signals. When adding a hook, entrypoint, or behavior that depends on React version semantics, update the relevant unit tests first, then verify the normal, canary, and legacy jobs still exercise the intended path.
Relevant Source Files
e2e/site/README.md- documents the local Next.js application used as the end-to-end site and gives the development-server commands for manual inspection..github/workflows/test.yml- defines the primary CI workflow, including the normal lint, build, package, typing, Jest, build-test, and Playwright end-to-end jobs..github/workflows/test-canary.yml- defines the scheduled and manually triggered React canary compatibility workflow that upgrades React-related packages before testing..github/workflows/test-legacy-react.yml- defines the React 17 compatibility workflow and marks the job withTEST_REACT_LEGACY..github/workflows/trigger-release.yml- defines the manually triggered npm publish workflow, OIDC permissions, npm version setup, build step, dist-tag detection, and publish commands.jest.config.js- configures the main Jest environment, source module aliases, transform, coverage exclusions, setup file, and reporters.jest.config.build.js- imports the main Jest config but clears module aliases so tests run against built package files instead of source aliases.
Test Matrix Overview
The default Test workflow has two jobs. The test job checks the library through a source and package-oriented path: it cleans, builds, runs aggregate checks, creates an npm tarball, runs package analysis with pnpm attw, executes Jest, executes build-file tests, and runs TypeScript typing tests. This order matters because failures can reveal different classes of problems. A source unit test failure points at implementation behavior, while a build-test failure can indicate that the distributed files or package entrypoints behave differently after compilation. Sources: .github/workflows/test.yml, jest.config.js, jest.config.build.js
The same workflow also defines an e2e job that runs inside the Microsoft Playwright container image mcr.microsoft.com/playwright:v1.57.0-noble. That job performs a clean build, builds the end-to-end application, and runs pnpm test:e2e. It always uploads the playwright-report artifact, which is important for diagnosing failures that only reproduce in browser automation. Treat this job as the integration signal for behavior that requires a real Next.js site, routing, hydration, browser APIs, or user-observable rendering states. Sources: .github/workflows/test.yml, e2e/site/README.md
The React canary workflow exists to detect upcoming React compatibility issues before they land in stable React releases. It runs daily at midnight by cron and can be started with workflow_dispatch. Before testing, it upgrades react, react-dom, and use-sync-external-store to the canary channel, then runs the core clean, build, Jest, build-test, and typing sequence with TEST_REACT_CANARY set. A failure here does not necessarily mean a pull request broke stable users, but it is a strong signal that maintainers should inspect assumptions around scheduling, rendering, Suspense, transitions, or external-store behavior. Sources: .github/workflows/test-canary.yml
The React 17 workflow protects the older supported React compatibility surface. It runs on pull requests, pushes to main, and version tags matching v*, and it sets TEST_REACT_LEGACY while running clean, build, Jest, build-test, and typing checks. Because this job is present on release tags, maintainers should treat it as a release gate for APIs that claim compatibility with legacy React behavior. If a change depends on newer React semantics, it should either preserve the legacy path or intentionally update support expectations elsewhere in the project. Sources: .github/workflows/test-legacy-react.yml
Jest Configuration and Build Testing
The main Jest configuration runs tests in the jsdom environment, which is appropriate for React hook behavior that expects browser-like globals. It discovers TypeScript and TSX tests under test using the configured test regular expression, ignores node_modules and e2e, and excludes the examples directory from module path resolution. The setup file is test/jest-setup.ts, and source files are transformed through @swc/jest. These details mean local unit tests are optimized for the library source tree rather than for the example applications or Playwright site. Sources: jest.config.js
Module mapping is one of the most important parts of the default Jest setup. Imports such as swr, swr/infinite, swr/immutable, swr/subscription, swr/mutation, and swr/_internal resolve directly to files under src. That gives unit tests fast feedback against the TypeScript implementation while still using the same public package names that users import. When adding a new public subpath or changing an entrypoint, keep this source aliasing in mind, because tests may pass against source while package output needs separate validation. Sources: jest.config.js
The build-test configuration intentionally changes that behavior. jest.config.build.js imports the main Jest config and then overrides moduleNameMapper with an empty object. The comment says this is done to use build files. Operationally, that means pnpm test:build is not just a repeat of the source tests; it checks the package as resolved from built artifacts. If a failure appears only in test:build, inspect build output, exports, package metadata, and generated files before assuming the source test is wrong. Sources: jest.config.build.js, jest.config.js
End-to-End Site Operations
The end-to-end site is a Next.js app bootstrapped with create-next-app. Its README describes the manual development workflow: run the dev server with npm run dev, yarn dev, or pnpm dev, then open http://localhost:3000. The document also points maintainers to edit app/page.tsx for page changes and explains that the page auto-updates while editing. This is useful when an end-to-end failure needs interactive reproduction rather than only a Playwright artifact. Sources: e2e/site/README.md
The same README explains that API routes are available under http://localhost:3000/api/hello, with the endpoint edited in pages/api/hello.ts, and that the pages/api directory maps to /api/* rather than React pages. For SWR tests, this distinction is operationally important because many realistic data-fetching checks need both a React page and a server endpoint. When debugging browser-visible behavior, verify whether the failure originates in the page, the API route, or the SWR cache and revalidation behavior exercised between them. Sources: e2e/site/README.md
Release Workflow
Publishing is handled by the Publish workflow in .github/workflows/trigger-release.yml and is only started through workflow_dispatch. The workflow grants id-token: write and contents: read because it uses npm OIDC trusted publishing. A comment in the workflow states that the trusted publisher on npmjs.org is configured for this exact workflow filename, so the npm publish must live there. Maintainers should avoid moving publishing logic to a differently named workflow unless the npm trusted publisher configuration is updated at the same time. Sources: .github/workflows/trigger-release.yml
The release job checks out the repository, runs the shared install action, installs npm 11 globally because OIDC trusted publishing requires npm at least 11.5.1, then cleans and builds the package. After building, it derives the npm dist-tag from the version in package.json: prerelease identifiers matching alpha, beta, canary, or rc publish to that tag, while versions without those identifiers publish to latest. This keeps prerelease builds from accidentally replacing the stable npm channel. Sources: .github/workflows/trigger-release.yml
Operational Reference
| Area | Trigger | Key commands or settings | Primary signal |
|---|---|---|---|
| Standard CI | push to main, pull request | pnpm clean, pnpm build, pnpm run-all-checks, npm pack, pnpm attw, pnpm test, pnpm test:build, pnpm test-typing | Source, package, typing, and build validation |
| E2E CI | push to main, pull request | Playwright container, pnpm build:e2e, pnpm test:e2e, artifact upload | Browser and Next.js integration validation |
| React canary | daily cron, manual dispatch | upgrade React packages to canary, TEST_REACT_CANARY=1 | Future React compatibility |
| React 17 | push to main, version tags, pull request | TEST_REACT_LEGACY=1 | Legacy React compatibility |
| Publish | manual dispatch | npm 11, build, dist-tag detection, npm publish --access public --no-git-checks | npm package release |
When investigating a failure, start with the job name and environment variable rather than the failing command alone. A failing Jest test under TEST_REACT_CANARY can have a different priority and root cause than the same test under React 17. A failing package or build test should prompt entrypoint and distribution checks, while an E2E failure should prompt inspection of the Playwright report and the local Next.js reproduction path. For changes that affect public imports, run both source and build tests before release; for changes that affect rendering lifecycle behavior, pay special attention to the canary and legacy workflows.
Next Steps
Maintainers preparing a change should first make the relevant unit or integration tests pass locally, then use the CI matrix to validate the change across package output and React compatibility variants. For release preparation, confirm the package version encodes the intended npm channel before manually dispatching Publish. If the version is a prerelease with alpha, beta, canary, or rc, the workflow publishes to that tag; otherwise it publishes to latest. Related pages for deeper context include contributing-workflow, suspense-ssr-and-rsc, and install-and-package-entrypoints.