Testing a consumer #
This is about testing your app when it imports @dolphinspired/solid-stage
— not about the library's own suite. If your Vitest tests render a component
that imports the library (directly or transitively), you'll hit this on the
first run:
TypeError: Unknown file extension ".jsx" for …/@kobalte/core/dist/text-field/index.jsx
Why it happens #
The library is shipped as pre-compiled JS — it isn't the source of the
.jsx. The error comes from its dependency @kobalte/core (the headless field
primitives) and three packages Kobalte pulls in — @corvu/utils,
solid-presence, solid-prevent-scroll — which all ship .jsx in their
dist/. Node can't parse .jsx, and Vitest externalizes node_modules by
default (loads them through Node, untransformed), so the import blows up.
The fix is to tell Vitest to inline those packages so vite-plugin-solid
transforms their .jsx like your own source.
The config #
// vitest.config.ts
import { defineConfig } from 'vitest/config';
import solid from 'vite-plugin-solid';
export default defineConfig({
plugins: [solid()],
test: {
environment: 'happy-dom',
server: {
deps: {
// The Kobalte ecosystem ships .jsx in dist — inline so vite-plugin-solid
// transforms it. These four are the complete set (verified: @kobalte/utils
// ships plain JS and does NOT need inlining).
inline: [
/@kobalte\/core/,
/@corvu\/utils/,
/solid-presence/,
/solid-prevent-scroll/,
],
},
},
},
// Without this, the inlined deps pull a second copy of Solid and reactivity
// silently breaks across the boundary ("multiple instances of solid-js").
resolve: { dedupe: ['solid-js'] },
});
That's the whole fix. Each piece, and why:
| Piece | Why |
|---|---|
inline: [/@kobalte\/core/, /@corvu\/utils/, /solid-presence/, /solid-prevent-scroll/] |
The only .jsx-shipping packages in the tree. Inlining routes them through vite-plugin-solid. |
resolve.dedupe: ['solid-js'] |
Forces one Solid instance. Solid's reactivity is instance-global; two copies means signals from one don't track in the other. |
environment: 'happy-dom' (or jsdom) |
The library's components reference browser globals (document, getBoundingClientRect, guarded ResizeObserver/IntersectionObserver). A test that imports them needs a DOM. |
You do not need to inline
@dolphinspired/solid-stageitself. It ships pre-compiled JS, so Node loads it fine and the dedupedsolid-jsreaches it. Inlining it too is harmless (one rule for the whole library surface) but redundant — the.jsxonly ever comes from the Kobalte-ecosystem deps above.
Developing against a local
link:ed checkout of the library (not the published package)? The samesolid-jsdedupe applies to your dev server and build, plus a couple more link-only steps — see Developing against a local checkout.
Per-file environment instead of global #
If you'd rather not set environment globally, drop a docblock at the top of
each test file that touches the library (this is what the library's own suite
does):
// @vitest-environment happy-dom
A minimal smoke test #
// src/MyToolbar.test.tsx
// @vitest-environment happy-dom
import { render } from '@solidjs/testing-library';
import { expect, it } from 'vitest';
import { MyToolbar } from './MyToolbar'; // imports TextField from the library
it('renders', () => {
const { container } = render(() => <MyToolbar />);
expect(container.querySelector('.stage-field-control')).not.toBeNull();
});
If this runs green, your config is correct and the rest of your suite will
import the library without the .jsx error.
Driving Kobalte primitives in a test is fiddly — the event that commits a value differs per primitive (text/number fields fire on
input; switch / checkbox / slider fire onchange; open a Select/Color overlay withpointerdown+pointerup, not.click()). The library's own*.test.tsxfiles next to each control are the reference for the exact gesture.Driving a section drag in a test works with real
PointerEvents dispatched on the section's.stage-section-drag-handle:pointerdown, thenpointermovepast 4px, thenpointerupat the drop point. happy-dom has no layout, so stubgetBoundingClientRecton the docks and cards involved.sectionDrag.test.tsxin the library is the reference. A synthetic drag proves the logic, not the feel: judge smoothness by eye with a real pointer.