* build: typecheck the test suites
Tests were in no TypeScript program: no tsconfig included tests/** or
packages/*/tests/**, and bun strips types without checking them, so a
fixture could drop a required field and keep passing until something
read it.
@types/bun moves to the root because it was installed per package only,
and #cli-tests/* joins the paths the root config already carries.
* test: fix the type errors the test suites were hiding
Typechecking the tests turned up 1123 errors. Most were ordinary
strictness, but some were real: `NodeChange` bound to Figma's plugin
typings rather than the Kiwi codec in thirteen .fig tests,
materializeInstance was called with six arguments against five so the
blobs and source children were dropped, CanvasKit pixels were written
to a plain object that never reached WASM, and assertions were made
through accessors that do not exist, so they asserted nothing.
Fixtures that had quietly lost a required field now carry it, nullable
results are narrowed through the existing expectDefined helper rather
than assumed, and stand-ins for CanvasKit and the editor go through one
named helper instead of an unexplained cast at each site.
No test was deleted, skipped, or weakened, and no `any`, non-null
assertion, or ts-expect-error was introduced.
* docs: record what typechecking the tests established
Pins the app program's global types with an assertion rather than a
note, since an unpinned types list lets any root @types package decide
which platform src/** is judged against.
The two environment faults that look like code regressions — Vite's
dependency pre-bundle outliving a package rebuild, and heavy .fig
suites failing under load — go to the development docs, where an
explanation belongs.
* fix: align @types/bun and keep node types resolvable when extended
The root manifest declared a newer @types/bun than every package, which
check:monorepo rejects, and pinning the app program's types left them
unresolvable from a config that extends this one out of tree.
* fix: fail the test typecheck when the compiler itself fails
The gate matched diagnostics by substring, so a compiler or config
failure that named no test file printed a pass while having checked
nothing. Diagnostics are now split by whether they name a file: an
unscoped one is the run failing and stops the gate, a test file's is a
finding, and a source file's stays out by design.
Also drops the parameter planComponentConstruction never read, and
makes the inner-shadow verification script exit non-zero when it
renders no image instead of logging and succeeding.
* chore: merge master into tests-typecheck
- useEditorSetup() / useEditorSetupWithClear() in tests/e2e/fixtures.ts
- Migrated 17 specs to shared fixture, removing ~300 lines of boilerplate
- Moved getSelectedNode/getSelectedNodes to shared store helpers
- Replaced inline getSelectedNode in 4 specs with shared import
- Test duplication: 329 → 308 clones (10.12% → 9.08%)
- Replace simple no-op test callbacks with explicit undefined returns
- Remove no-empty-function exceptions for autosave, stroke picker, font settings, pen, tauri, AI adapter, and undo helper tests
- Validate check plus affected engine and E2E tests
- Expose window.openPencil.getStore() instead of a direct store property
- Update E2E helpers and specs to use the bridge getter
- Add lint coverage preventing direct window.openPencil.store access
- Move test-only write-count, mock-handle, and saved-open globals under window.openPencil.test
- Keep external Tauri and Vite injected globals separate from app-owned browser bridge hooks
- Validate check plus autosave and chat E2E coverage
- Route browser globals through src/app/window-api.ts instead of private __OPEN_PENCIL fields
- Move E2E tests to the public window.openPencil test API
- Add an oxlint rule banning direct window.__OPEN_PENCIL* access
- Replace low-count E2E non-null assertions with store guards and optional expectations
- Use expectDefined for tool adapter and XPath node lookups
- Validate affected engine and E2E tests
- Remove stale imports and unused locals from split engine and e2e tests
- Drop the targeted no-unused-vars test allowances
- Keep check and affected test suites warning-free
- Configure oxfmt custom import groups for workspace, app, package, and test aliases
- Keep type imports grouped with their matching source category instead of one global tail group
- Expand the format script to cover formatter config, Vite files, and scripts
- Merge structural lint rules into oxlint.json and remove the second config file
- Add #tests/* path aliases and rewrite deep test helper imports
- Ban new deep parent-relative imports through the shared lint config
- Enforce explicit-any and TypeScript suppression bans in tests
- Remove remaining broad any casts from engine and e2e coverage
- Split markdown ambient declarations from typed browser globals
- Extend the as-unknown-as structural rule beyond app and Vue code
- Remove existing broad double casts from core and tests
- Replace the vector base64 helper and XPath facade casts with precise code paths
9 app-menu tests: menu visibility, all 6 submenus content, Undo/Duplicate/
Zoom to fit via menu actions.
2 autosave tests: write triggers after scene change with mock file handle,
no write without file handle.