Headless/Vite: @srhazi/gooey-test/vite plugin + optional vite peer (#2) #14

Closed
orchestrator wants to merge 5 commits from wip/issue-02 into main
Member

Adds a Vite integration so hosted + headless modes "just work", per #2.

Changes

  • New @srhazi/gooey-test/vite plugin: serves the built testrunner at a stable path (/@gooey-test/testrunner.html), discovers test files, injects them as window.__gooeyTest.files, exposes virtual:gooey-test/manifest, and appends the runner dir to server.fs.allow.
  • vite declared as an optional peerDependency (kept as a devDependency); ./vite subpath export; s/dist + tsconfig.vite.json build it.
  • Headless mode consumes the plugin: generates a per-run Vite config that merges the consumer's config with the plugin, and falls back to the old glob/#/... flow if unavailable.
  • src/testrunner/testrunner.tsx reads the injected file list (hash still wins for back-compat).
  • README (Setup / hosted mode / plugin API); smoke coverage including a define-global regression guard.

Verification

  • Self-test + #/... fallback pass; base: '/app/' passes.
  • Reference consumer gooey-identity: PASS 523/20 via the plugin path.
  • Dockerized ./s/smoke: green (incl. hosted entry + the define guard).

Note

Serving the sandbox iframe HTML through Vite's pipeline was required so Vite's define globals (e.g. TEST) reach test modules loaded in the sandbox iframes.

Closes #2

Adds a Vite integration so hosted + headless modes "just work", per #2. ## Changes - New `@srhazi/gooey-test/vite` plugin: serves the built testrunner at a stable path (`/@gooey-test/testrunner.html`), discovers test files, injects them as `window.__gooeyTest.files`, exposes `virtual:gooey-test/manifest`, and appends the runner dir to `server.fs.allow`. - `vite` declared as an **optional peerDependency** (kept as a devDependency); `./vite` subpath export; `s/dist` + `tsconfig.vite.json` build it. - Headless mode consumes the plugin: generates a per-run Vite config that merges the consumer's config with the plugin, and falls back to the old glob/`#/...` flow if unavailable. - `src/testrunner/testrunner.tsx` reads the injected file list (hash still wins for back-compat). - README (Setup / hosted mode / plugin API); smoke coverage including a `define`-global regression guard. ## Verification - Self-test + `#/...` fallback pass; `base: '/app/'` passes. - Reference consumer gooey-identity: **PASS 523/20** via the plugin path. - Dockerized `./s/smoke`: green (incl. hosted entry + the define guard). ## Note Serving the sandbox iframe HTML through Vite's pipeline was required so Vite's `define` globals (e.g. `TEST`) reach test modules loaded in the sandbox iframes. Closes #2
Own the gooey-test/Vite integration so hosted and headless modes both
'just work':

- New @srhazi/gooey-test/vite plugin serves the testrunner at a stable
  path (/@gooey-test/testrunner.html), discovers test files (same
  include/exclude defaults as headless) and injects them via
  window.__gooeyTest.files; also exposes virtual:gooey-test/manifest.
- testrunner reads the injected manifest, keeping #/... for back-compat.
- headless generates a throwaway Vite config that merges the consumer's
  config with the plugin and navigates to the stable entry; falls back to
  the glob-based #/... flow when the plugin can't be applied.
- Declare vite as an optional peerDependency (kept as a devDependency).
- Build/bundle/type the plugin via s/dist + tsconfig.vite.json.
- README: setup, hosted mode, plugin API. Minimal additive smoke checks.
If a consumer already registers gooeyTest() (as the README now suggests),
the merged plugin list had two instances and the consumer's discovery won,
silently ignoring headless's explicit/derived file list. Drop the
consumer's instance so headless's plugin (with the exact files) is the one
that serves.
- plugin: render base-independent root-absolute asset URLs (transformIndexHtml
  applies base once) and base-prefix injected file paths + /@fs/ fallbacks, so
  base:'/app/' works; previously assets double-prefixed to /app/app/... 404s.
- plugin: serve the runner's sibling files under the stable entry dir
  (/@gooey-test/<rest> -> <runner>/<rest>), so testsandbox.html is 200.
- plugin: escape '<' in the injected file list before interpolating it.
- headless: give each run a unique mkdtemp dir under node_modules/.gooey-test/
  and rm -r it (plus the empty parent) on cleanup; probe writability so an
  absent/read-only node_modules falls back without creating one.
- headless: readSummary returns zeros when .testrunner is absent (count() +
  try/catch + short timeout), so a never-mounting runner exits 2, not a crash.
- headless: only remove the generated config when Vite is torn down (keep it
  while --debug-failures leaves Vite running); use packageName() in
  pluginSpecifier.
- package.json/README: narrow vite peer to ^4 || ^5 || ^6, note tested on 4,
  and correct the optional-peer auto-install claim.
In Vite dev, 'define' is not applied by transforming modules; instead Vite
installs the define values as globals via /@vite/client, injected into each
HTML document. The plugin served the stable entry's sibling HTML (notably
testsandbox.html, which backs the sandbox iframes the test modules run in)
as a raw static file, so the iframe realm never loaded the client and bare
define globals (e.g. 'TEST') threw ReferenceError. 16/20 files in the
gooey-identity consumer failed to import.

Serve .html siblings through server.transformIndexHtml so the Vite client
(and consumer HTML plugins) are injected, matching the fallback/hosted
paths. Non-HTML siblings stay static.

Guard: smoke consumer defines SMOKE_DEFINE and src/define.test.ts asserts
it via a bare global, exercised through the headless/plugin path in
smoke/smoke.sh. Verified it fails when the HTML is served raw.

gooey-identity: 69/4 -> 523/20, exit 0.
Author
Member

Superseded by #23, which contains this change plus a simplification pass and is now based directly on main.

Closing this in favour of #23 so the simplified implementation is what lands. Summary of the simplification is in #23's description.

Superseded by #23, which contains this change **plus** a simplification pass and is now based directly on `main`. Closing this in favour of #23 so the simplified implementation is what lands. Summary of the simplification is in #23's description.
orchestrator closed this pull request 2026-10-13 13:54:15 +00:00

Pull request closed

Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
legend/gooey-test!14
No description provided.