# 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 ```ts // 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-stage` itself.** It ships > pre-compiled JS, so Node loads it fine and the deduped `solid-js` reaches it. > Inlining it too is harmless (one rule for the whole library surface) but > redundant — the `.jsx` only ever comes from the Kobalte-ecosystem deps above. > **Developing against a local `link:`ed checkout** of the library (not the > published package)? The same `solid-js` dedupe applies to your dev server and > build, plus a couple more link-only steps — see > [Developing against a local checkout](./local-development.md). ### 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): ```tsx // @vitest-environment happy-dom ``` ## A minimal smoke test ```tsx // 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(() => ); 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 on `change`; open a Select/Color overlay with > `pointerdown` + `pointerup`, not `.click()`). The library's own `*.test.tsx` > files next to each control are the reference for the exact gesture. > > **Driving a section drag in a test** works with real `PointerEvent`s dispatched on > the section's `.stage-section-drag-handle`: `pointerdown`, then `pointermove` past > 4px, then `pointerup` at the drop point. happy-dom has no layout, so stub > `getBoundingClientRect` on the docks and cards involved. `sectionDrag.test.tsx` in > the library is the reference. A synthetic drag proves the logic, not the feel: > judge smoothness by eye with a real pointer.