Skip to content

Run the browser-dependent unit tests in a DOM-capable test environment #148

Description

@CarsonDavis

Run the browser-dependent unit tests in a DOM-capable test environment

Motivation

Many unit tests — the map-engine adapters (Leaflet and deck.gl), the map-engine registry, and the panel manager — do not actually run. Every one of these specs errors while loading, before a single assertion executes, so the suite reports them as failures and they provide zero protection today even though the tests are written and look comprehensive (well over 200 cases).

This was invisible for a long time because the test job failed earlier, during dependency install, and never reached the tests at all. Now that install is fixed, the test step runs and surfaces this second, pre-existing layer of breakage. The same specs fail on a developer's machine and in CI, so npm test is red for everyone and a green-looking suite would be misleading.

The root cause is environmental, not a product bug: these specs exercise code that expects a browser (a window, a document, browser element types), but the unit suite runs in a plain Node process with no browser environment. CI compounds it by running on an older Node than the project officially supports.

How it should work

  • Running the unit suite — locally and in CI — executes all unit specs to completion, with no load/import errors.
  • The browser-dependent specs (map-engine adapters including deck.gl, the engine registry, and the panel manager) execute. The adapters and registry — written against current behavior — pass; the panel-manager specs (and one validator case) surface as real failures once they run, and fixing those stale assertions is tracked separately in Fix the stale unit tests revealed by the jsdom test-environment migration #149.
  • CI runs the unit suite on the Node version the project officially supports.
  • A developer can run the unit suite and the browser-dependent tests are actually enforced, not silently skipped. The suite is intentionally red until Fix the stale unit tests revealed by the jsdom test-environment migration #149 lands the stale-test fixes; once both land, a green result honestly means the covered code works.
  • The end-to-end suite is unaffected and continues to run as before.

Done when

  • The unit suite runs every unit spec without import/load errors, both locally and in CI.
  • The previously non-running specs (map-engine adapters incl. deck.gl, engine registry, panel manager) execute.
  • CI runs the unit suite on the project's supported Node version (20+).
  • A failing unit test fails the CI check (the suite is actually gating again).
  • The end-to-end test suite still runs and is unchanged in behavior.

Out of scope

  • Writing new tests or changing what the existing tests assert — this is purely about making the already-written tests run.
  • Any rework of the end-to-end suite.
  • The separate lean "served as a live server" gating gap (the deferred two-axis / capability-table work) — unrelated to the test environment.
Draft implementation plan — written as of 42b9e0a on 2026-06-16. Rough guide; re-verify against latest code.

Current behavior

Unit specs run through Playwright's test runner (playwright test tests/unit) in a Node worker with no DOM. Nine specs fail at import:

  • tests/unit/LeafletAdapter.spec.js
  • tests/unit/deckGLAdapter.spec.js
  • tests/unit/deckGLHelpers.spec.js
  • tests/unit/mapEngineRegistry.spec.js
  • tests/unit/panelManager/panelManager.{layout,queries,registration,stateManagement,toolManagement}.spec.js

Two stacked causes:

  1. No DOM at import. These specs import browser-coupled modules (the Leaflet/ deck.gl adapters; PanelManager_TimeControl) that read window / screen / HTMLAnchorElement at module-load time. The repo's existing "shim window in beforeEach" pattern is too late — the failure happens during import.
  2. Node can't require() ESM. Those modules transitively require() ESM-only packages (@mapbox/tiny-sdf via deck.gl, d3-time-format via TimeControl). CI runs Node 18 (.github/workflows/playwright-tests.yml, node-version: "18"), which throws ERR_REQUIRE_ESM — even though AGENTS.md states the project requires Node 20+.

The split (verified by running locally on Node 22 vs CI on Node 18): deckGLHelpers fails only on the ESM-require issue — it passes once Node is 20+. The other eight need a DOM regardless of Node version.

Where the change lands & rough plan

Move the unit specs to a DOM-capable runner instead of trying to bolt a DOM onto Playwright's runner. Vitest with environment: 'jsdom' is verified to work on a throwaway probe: deckGLAdapter's 28 tests all pass under it (no live WebGL is needed — the adapter tests drive _viewState and the layer registry), and panelManager.layout clears the window/ESM blockers under jsdom.

Rough steps:

  • Add vitest + jsdom as dev dependencies.
  • Add a vitest.config with test.environment = 'jsdom' covering tests/unit.
  • Swap the unit specs' test import from @playwright/test to vitest (the test/expect APIs are compatible, so only the import line changes).
  • Add a small Vitest setup file to stub the few remaining browser globals — the probe showed panelManager still needs one (a Tooltip-providing library referenced at import); identify and stub those.
  • Keep Playwright for tests/e2e; point the CI "unit" step at Vitest.
  • Bump the CI workflow Node from 18 → 20.

⚠️ Gotcha: do not try to give Playwright's own runner a DOM via NODE_OPTIONS=--require <jsdom-setup>. It triggers ERR_METHOD_NOT_IMPLEMENTED: resolveSync — Playwright's ESM loader conflicts with require()-ing ESM under a preload. A separate runner (Vitest) is the path; patching Playwright is a dead end.

⚠️ Gotcha: the specs import .ts source files directly, so the runner must transpile TypeScript on the fly. Vitest does this natively; a plain Jest setup would need extra config.

References

  • .github/workflows/playwright-tests.ymlnode-version: "18", npx playwright test tests/unit
  • playwright.config.js — current unit+e2e config
  • tests/unit/panelManager/testHelpers.js — existing (too-late) window shim pattern
  • src/essence/Basics/Layers_/LayerConstructors.jslet L = window.L at module load (one of the import-time window triggers)

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions