Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .changeset/no-jsdom-default.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
'@solidjs/vite-plugin': patch
---

The plugin no longer sets `test.environment: 'jsdom'` for Vitest. Set the
environment yourself (`jsdom` or `happy-dom` for DOM component tests); Vitest's
own default is `node`. The default also applied to Node-only suites that loaded
the plugin through a shared config, which then ran under jsdom and failed in
ways that were hard to trace. The client test posture (browser conditions, DOM
codegen) is unchanged, and `environment: 'node'` still selects the server
posture.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1152,7 +1152,7 @@ For other breaking changes, check [the migration guide of vite](https://vitejs.d

If you are using [vitest](https://vitest.dev/), this plugin already injects the necessary configuration for you. It even automatically detects if you have `@testing-library/jest-dom` installed in your project and automatically adds it to the `setupFiles`. All you need to add (if you want) is `globals`, `coverage`, and other testing configuration of your choice. If you can live without those, enjoy using vitest without the need to configure it yourself.

Tests default to the client posture, regardless of the app's `ssr` flag: DOM codegen, non-hydratable (nothing hydrates in a test), browser export conditions, and a `jsdom` default `test.environment`. `solid-js`, `@solidjs/web` and the packages that import them (such as `@solidjs/testing-library`) are inlined, so their imports resolve the browser build through Vite on any host runtime, Deno included. A server-rendered app needs no `ssr: mode !== 'test'` workaround — DOM component tests just work.
Tests default to the client posture, regardless of the app's `ssr` flag: DOM codegen, non-hydratable (nothing hydrates in a test), and browser export conditions. The plugin does not set `test.environment`: set `environment: 'jsdom'` (or `'happy-dom'`) for DOM component tests, as Vitest's own default is `node`. `solid-js`, `@solidjs/web` and the packages that import them (such as `@solidjs/testing-library`) are inlined, so their imports resolve the browser build through Vite on any host runtime, Deno included. A server-rendered app needs no `ssr: mode !== 'test'` workaround — DOM component tests just work.

Server-runtime unit tests (server functions, sessions, `renderToString` — anything that needs `isServer` to be `true` and the real server build of the framework) opt out per [vitest project](https://vitest.dev/guide/projects) by setting `test.environment: 'node'` (or `'edge-runtime'`) explicitly. That project gets the server posture end to end: server export conditions, ssr codegen, and the framework inlined so the whole graph — request-event storage included — resolves into one server-build instance. No inline/alias configuration needed. Both postures coexist in one workspace:

Expand Down
6 changes: 3 additions & 3 deletions examples/start-ssr/src/posture.test.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// Vitest posture regression test (vitest mode in test/run.mjs): this app is
// `ssr: true`, but tests must compile and resolve with the CLIENT posture —
// dom codegen, non-hydratable, browser export conditions, jsdom by default
// (the plugin injects all of that in test mode; this example's config has no
// `test` block and no `ssr: mode !== 'test'` workaround). Before the fix this
// dom codegen, non-hydratable, browser export conditions (the plugin injects
// all of that in test mode; this example's config only sets `environment:
// 'jsdom'` and has no `ssr: mode !== 'test'` workaround). Before the fix this
// file would compile hydratable (or with server semantics under a node
// environment) and DOM component tests broke.
import { expect, it } from 'vitest';
Expand Down
29 changes: 12 additions & 17 deletions examples/start-ssr/test/run.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -6318,7 +6318,7 @@ async function runNodeMode() {

// Vitest posture: even though this app is `ssr: true`, tests compile and
// resolve with the client posture — dom codegen, non-hydratable, browser
// conditions, jsdom default — with no `test` block and no
// conditions — with only `environment: 'jsdom'` and no
// `ssr: mode !== 'test'` workaround in the config. src/posture.test.tsx
// carries the actual assertions (isServer false, non-hydratable codegen, a
// DOM component rendering and updating under jsdom); this mode just runs it.
Expand Down Expand Up @@ -6424,17 +6424,15 @@ async function runVitestMode() {
projectsPass ? undefined : projectsOut.slice(-2000),
);

// Browser-mode projects must NOT get the jsdom default: vitest 4 probes
// for the environment's package at startup (getEnvPackageName →
// ensureInstalled) and sets a failing exit code when jsdom isn't
// installed — even though the suite itself runs (and passes) in the real
// browser. The gate mirrors the plugin's jest-dom one (`browser.enabled`
// skips the injection); everything else keeps the jsdom default. Asserted
// at the config-resolution level: jsdom IS installed in this example, so
// a full browser run couldn't reproduce the missing-package probe.
// The plugin injects no `test.environment`, in any project shape: a
// default applied to Node-only suites that loaded the plugin too, and in
// browser mode or a projects root vitest probes for the environment's
// package at startup and fails the run when it isn't installed (#205).
// Resolved without the example's config file, which sets jsdom itself.
const { default: solid } = await import('@solidjs/vite-plugin');
const resolveTestEnvironment = async (testBlock) => {
const resolved = await resolveConfig(
{ root: exampleDir, mode: 'test', test: testBlock },
{ root: exampleDir, mode: 'test', configFile: false, plugins: [solid()], test: testBlock },
'serve',
);
return resolved.test?.environment;
Expand All @@ -6450,20 +6448,17 @@ async function runVitestMode() {
} catch (e) {
envError = String(e);
}
const browserEnvPass = !envError && browserEnv === undefined && defaultEnv === 'jsdom';
const browserEnvPass = !envError && browserEnv === undefined && defaultEnv === undefined;
record(
mode,
'browser-env',
'browser-mode project gets no injected jsdom environment (non-browser keeps the jsdom default)',
'no injected test environment (browser mode or a plain test block)',
browserEnvPass,
browserEnvPass ? undefined : envError || `browser: ${browserEnv}, default: ${defaultEnv}`,
);

// A root config that defines `test.projects` runs no tests itself — each
// project controls its own environment — so it must NOT get the jsdom
// default either: vitest probes for the root environment's package at
// startup and prompts for jsdom even when every project runs under node
// or in the browser (solidjs/solid-vite-plugin#205, forward-port of #323).
// A root config that defines `test.projects` runs no tests itself; it
// gets no environment either (solidjs/solid-vite-plugin#205).
let projectsEnv;
let projectsEnvError = '';
try {
Expand Down
7 changes: 3 additions & 4 deletions examples/start-ssr/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,9 +111,8 @@ export default defineConfig({
// DOM component tests under jsdom get the client posture (browser
// conditions, dom codegen), while the node project gets the server
// posture end to end (server conditions, isServer true, ssr codegen)
// just by writing `environment: 'node'`. Without the knob the config has
// no `test` block at all: the zero-config client-posture default stays
// under test too.
// just by writing `environment: 'node'`. Without the knob the config only
// picks jsdom: the client posture needs no other `test` options.
...(process.env.VITEST_PROJECTS
? {
test: {
Expand All @@ -137,7 +136,7 @@ export default defineConfig({
],
},
}
: {}),
: { test: { environment: 'jsdom' } }),
...(process.env.EXTRA_CLIENT_INPUT
? {
environments: {
Expand Down
34 changes: 7 additions & 27 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1435,14 +1435,14 @@ export default function solidPlugin(options: Partial<Options> = {}): Plugin[] {
domTestRuntimeInlined = false;
forceProduction = options.dev === false && command === 'serve' && !isTestMode;
// Per-vitest-project posture: the client posture (browser conditions,
// dom codegen, jsdom default) is right for DOM component tests but
// dom codegen) is right for DOM component tests but
// wrong for server-runtime unit tests. A project that explicitly opts
// into a server runtime — `test: { environment: 'node' }` (or
// 'edge-runtime') — gets the server posture end to end: no browser
// condition injection, so the framework resolves its real server
// build (isServer true) with no inline/alias workarounds. DOM
// environments (the jsdom default, happy-dom, browser mode) keep the
// client posture. Each vitest project resolves its own config, so the
// environments (jsdom, happy-dom, browser mode) and an unset
// environment keep the client posture. Each vitest project resolves its own config, so the
// hooks below see the posture of the project they serve.
serverTestPosture =
isTestMode &&
Expand Down Expand Up @@ -1554,30 +1554,10 @@ export default function solidPlugin(options: Partial<Options> = {}): Plugin[] {
? [userTest.setupFiles]
: userTest.setupFiles || [];

// Regardless of the app's `ssr` flag: tests run with the client
// posture (DOM component tests are the norm), so the default test
// environment is a DOM. Node-environment tests opt in explicitly.
// Browser-mode projects get the real browser DOM, so don't default
// them to jsdom — vitest probes for the environment's package at
// startup and fails the run if jsdom isn't installed. They fall
// back to vitest's own node default (no package probe).
// A root config that defines `test.projects` (or the pre-vitest-4
// `test.workspace`) doesn't run tests itself: each project controls
// its own environment, so the root gets no jsdom default either —
// otherwise vitest probes for jsdom at the root on startup even when
// every project runs under node or in the browser. Note that an
// inline project with `extends: true` inherits the root file's
// `test.projects` and opts out too: projects declare their
// environment explicitly, as in vitest's own projects guide.
// https://github.com/solidjs/solid-vite-plugin/issues/205
if (
!userTest.environment &&
!userTest.browser?.enabled &&
!userTest.workspace &&
!userTest.projects
) {
test.environment = 'jsdom';
}
// No `test.environment` is injected: the project picks its own
// (`jsdom` or `happy-dom` for DOM component tests). A default here
// also applied to every Node-only test suite that happened to load
// the plugin, which then ran under a DOM it never asked for.

// Vitest 5 defaults `test.sharedViteServer` to true: inline projects
// then reuse the root Vite server and take their `test` options from
Expand Down
Loading