-
Add timeout and abort signal support to
@remix-run/test.Tests and lifecycle hooks can now pass
{ timeout, signal }. Timed-out tests fail and abortt.signal, so async work that accepts anAbortSignalcan cancel promptly. Tests and suites can also use stringskip/todoreasons, and reporters display those reasons when a pending result is reported.it('loads data', { timeout: 5_000 }, async (t) => { let response = await fetch('/api/data', { signal: t.signal }) assert.equal(response.status, 200) }) it('depends on external credentials', { skip: 'requires API credentials' }, () => {})
- Ignore browser-cancelled script requests in
remix-testbrowser runs so iframe navigation can finish cleanly on Windows while still reporting real script load failures.
-
Fix browser tests so bare package imports resolve with browser and ESM conditions, matching the asset server and avoiding CommonJS entries for packages like
clsx(see #11478). -
Update the optional
playwrightpeer dependency range to match the workspace Playwright catalog version. -
Run browser and E2E tests sequentially within each Playwright project so
remix-testavoids launching extra browsers at the same time and reduces timing flakes on constrained CI runners. -
Report
beforeAll,afterEach, andafterAllhook failures as failed test results soremix-testexits non-zero when lifecycle hooks throw. -
Use OS-assigned ports for browser test servers so parallel
remix-testruns do not fail when the fixed port window is exhausted.
- Bumped
@remix-run/*dependencies:
-
Migrated
@remix-run/testfrom thetsxpackage to Remix's internal@remix-run/node-tsxmodule loader.BREAKING CHANGE:
.ts,.tsx, and.jsxmodule loading in@remix-run/testnow uses Remix's internal@remix-run/node-tsxloader. Test modules are still transformed before execution, including JSX and TypeScript syntax that requires JavaScript output, but the loader is now maintained inside Remix.
-
Fix browser test runs so large suites can exceed the per-file timeout as long as individual test files keep reporting progress.
-
Fix
describe.skipanddescribe.onlyso they propagate to nesteddescribeblocks. Previously the skipped/focused state was set only on the outer suite, so tests inside nested describes still ran (or were incorrectly skipped underonly). -
Bumped
@remix-run/*dependencies:
-
Add
FakeTimers#advanceAsync(ms)tot.useFakeTimers(). Likeadvance, it walks pending timers in time order and fires them, but yields to microtasks between each firing so promise continuations (and any timers they schedule) can settle before the next firing is processed. Use it when a fake-timer-driven callback awaits work that itself depends on the fake clock. -
Accept arrays for
glob.{test,browser,e2e,exclude},project,type, andcoverage.{include,exclude}config fields- The matching CLI flags (
--glob.test,--project,--type, etc.) can be repeated - Positional arguments after
remix-testnow collect intoglob.test, soremix-test "src/**/*.test.ts" "tests/**/*.test.tsx"works. type's default is now["server", "browser", "e2e"]instead of"server,browser,e2e".
- The matching CLI flags (
-
Include the total number of test files/suites in the end-of-run summary for all test reporters
-
Load Playwright only when browser or E2E tests run, allowing test help and server-only test runs without Playwright installed. Browser and E2E test runs now report a clearer error when Playwright is missing.
-
Run server and E2E test files in forked child processes by default, add
pool: 'threads'/--pool threadsto preserve the previous worker-thread behavior, and clean up leaked test worker resources after results are reported.
-
Add
glob.excludeconfig for filtering paths during test discovery (defaults tonode_modules/**) -
Add code coverage reporting to
remix-test- You can enable coverage with default settings vis
remix-test --coverageor settingcoverage:truein yourremix-test.config.ts - Or you can specify individual coverage settings via the following config fields:
coverage.dir: Directory to store coverage information (default.coverage)coverage.include: Array of globs for files to include in coveragecoverage.exclude: Array of globs for files to exclude from coveragecoverage.statements: Percentage threshold for statement coveragecoverage.lines: Percentage threshold for line coveragecoverage.branches: Percentage threshold for branch coveragecoverage.functions: Percentage threshold for function coverage
- You can enable coverage with default settings vis
-
Export
runRemixTestfrom@remix-run/test/cliso other tools can run the Remix test runner programmatically without exiting the host process. The function returns an exit code so callers can decide how to terminate. Theremix-testexecutable now declares Node.js 24.3.0 or later in package metadata.
-
Internal refactor to test discovery to better support test execution in
bun.- Unlike Node, Bun's
fs.promises.globfollows symbolic links and does not prune traversal via theexcludeoption, which can cause the test runner to enternode_modulessymlink cycles in pnpm workspaces - Refactored the internal test discovery logic to detect and use Bun's native
Globclass when running under the Bun runtime. Bun'sGlob#scandoes not follow symlinks by default, avoiding the cycle. - The Node runtime continues to use
fs.promises.glob
- Unlike Node, Bun's
-
Use native dynamic
import()in Bun to load.tsand.tsxfiles in the test runner -
Bumped
@remix-run/*dependencies:
- Initial release of
@remix-run/test, a test framework for Remix applications.describe/ittest structure withbefore/after/beforeEach/afterEachhooksTestContext(t) per test:t.mock.fn(),t.mock.method(),t.after()for cleanup- Playwright E2E testing via
t.serve() - CLI (
remix-test) with flags for all config options - Watch mode (
--watch) - Config file support (
remix-test.config.ts) globalSetup/globalTeardownhooks via thesetupmodule, called once before/after the entire test run
- Bumped
@remix-run/*dependencies: