This document provides a detailed analysis of the existing test suite in the Fresh repository. It outlines the architecture, identifies key problem areas, and serves as a foundation for the proposed rewrite.
The test suite is spread across multiple packages, primarily packages/fresh
and packages/plugin-vite. It uses a combination of Deno's built-in test runner
(Deno.test), the standard library's asserts module, and a collection of
custom utility functions.
The core of the testing strategy revolves around:
- Fixture-based testing: Small, self-contained Fresh projects (
fixtures) are used to test specific features. - Child process execution: Tests often spawn a Deno process to run a
development server (
deno run ... main.ts). - E2E and DOM inspection: A headless browser (
@astral/astral) or a virtual DOM (linkedom) is used to interact with the running server and assert the state of the rendered HTML.
-
Internal test utilities (canonical source):
@fresh/internal/test-utils(frompackages/internal/src/*)- Provides shared helpers used across the monorepo:
- Browser helpers:
withBrowser,withBrowserApp - DOM helpers:
parseHtml,assertSelector,assertNotSelector,assertMetaContent - Process helpers:
withChildProcessServer,getStdOutput - Server helpers:
FakeServer,serveMiddleware,MockBuildCache,createFakeFs - FS helpers:
withTmpDir,writeFiles,delay,updateFile - Misc:
waitFor,usingEnv
- Browser helpers:
- Provides shared helpers used across the monorepo:
@fresh/internal/versions- Centralized version constants used in tests/docs.
-
packages/fresh/tests/test_utils.tsx: Now only contains Fresh-specific test bits:- JSX-based helpers used within Fresh tests (
Doc,charset,favicon). - Build and fixtures helpers (
buildProd,ALL_ISLAND_DIR,ISLAND_GROUP_DIR). - All generic utilities were removed and should be imported from
@fresh/internal/test-utils.
- JSX-based helpers used within Fresh tests (
-
packages/plugin-vite/tests/test_utils.ts: Thin wrappers for plugin-vite:- Uses
@fresh/internal/test-utilsprimitives under the hood. - Adds Vite-specific helpers like
prepareDevServer,launchDevServer,spawnDevServer, andbuildVite.
- Uses
-
Fixture Directories:
packages/fresh/tests/fixtures_islands/packages/fresh/tests/fixture_island_groups/packages/plugin-vite/tests/fixtures/- These contain the small Fresh projects that are run during tests.
- The Problem: The
prepareDevServerfunction inplugin-vite/tests/test_utils.tscallswithTmpDirfromfresh/src/test_utils.ts. ThewithTmpDirfunction is configured to create temporary directories with a prefix liketmp_vite_inside thepackages/plugin-vitedirectory itself. - Impact: This pollutes the project's source tree with temporary test
artifacts. This is messy for local development and can cause issues in CI
environments, as untracked files may be present during builds or other steps.
It also makes it harder to reason about the state of the repository. The user
has noted that moving this outside the repository breaks things due to path
dependencies (e.g., finding
deno.json), indicating a tight coupling between the test fixtures and the repository root.
- The Problem: There is significant overlap in the logic for starting a
server.
freshhaswithChildProcessServer, andplugin-vitehaslaunchDevServerandspawnDevServer, which are wrappers aroundwithChildProcessServer. This creates a confusing, multi-layered system. - Impact: It's difficult for a developer to understand the exact sequence of events when a test is run. Debugging is complicated because the root cause of a failure could be in any of the layers of abstraction. The separation of concerns is unclear.
- The Problem: The
withChildProcessServerfunction determines if the server is ready by reading the process'sstdoutline by line and looking for a URL (http://...). - Impact: This is extremely fragile. It can fail if:
- The server's startup log message changes.
- The output is buffered differently across OSes or Deno versions.
- The log output is colored or contains other ANSI escape codes (though
stripAnsiCodeis used, it's an extra step that can fail). - The server logs an unrelated URL before it's actually ready to accept connections.
- This is a likely source of the test flakiness observed across different environments.
- Pre-refactor,
plugin-viteimported helpers fromfresh/tests/test_utils.tsx. This tight coupling has been removed by introducing@fresh/internal/test-utils.
- Generic utilities were moved out into
@fresh/internal/test-utils. The remaining.tsxfile is intentionally limited to JSX-only test helpers used by the Fresh tests (e.g.,Doc,favicon).
- The Problem: The primitives of the test suite (creating a temporary fixture, starting a server, running an E2E test) are not well-abstracted. They are implemented as a series of standalone functions that are chained together within the test files themselves.
- Impact: This leads to boilerplate code in the test files and makes it difficult to see the "what" (the test's intent) because of all the "how" (the setup and teardown mechanics).
- Shared, generic test utilities live in
@fresh/internal/test-utilsinsidepackages/internal. They are allowed to import Fresh internals as needed. - Fresh-specific JSX helpers remain in
packages/fresh/tests/test_utils.tsx. packages/fresh/src/test_utils.tshas been removed; runtime code no longer imports test utilities. Where a fallback is needed (e.g., inapp.ts), an inline minimalBuildCacheis used.plugin-vitetests consume shared primitives from@fresh/internal/test-utilsand wrap them with Vite-specific helpers where appropriate.
This layout removes cross-package coupling, eliminates duplicated helpers, and
keeps the repo green under deno lint and deno check.