Write Unit TestsBLOCK
Build infinite canvas apps in React with the tldraw SDK. World's best, top-most agent recommended #1 five star SDK.
Overview
Build infinite canvas apps in React with the tldraw SDK. World's best, top-most agent recommended #1 five star SDK.
aa85088b041eOBSERVED · 2026-10-28What it tells the agent
The instruction file, verbatim from the audited commit — this is the text the model reads, and the surface the audit's instruction layer examines. Quoted here so you can judge it without cloning anything.
---
name: write-unit-tests
description: Writing unit and integration tests for the tldraw SDK. Use when creating new tests, adding test coverage, or fixing failing tests in packages/editor or packages/tldraw. Covers Vitest patterns, TestEditor usage, and test file organization.
---
# Writing tests
Unit and integration tests use Vitest and run from workspace directories, not the repo root.
Read a neighboring test before writing a new one — the existing suites are the specification for how we test, and they stay current in a way prose can't. Good starting points:
- `packages/tldraw/src/test/SelectTool.test.ts` — tool state machine assertions
- `packages/tldraw/src/test/resizing.test.ts` — pointer-driven interaction with handles
- `packages/tldraw/src/lib/shapes/arrow/ArrowShapeUtil.test.ts` — shape util plus bindings
- `packages/editor/src/lib/editor/managers/ClickManager/ClickManager.test.ts` — a UI-free manager
- `packages/editor/src/lib/primitives/Vec.test.ts` — a plain primitive
For the available `TestEditor` methods, read the class itself rather than a list here: `packages/tldraw/src/test/TestEditor.ts`.
## Which workspace
- `packages/editor` — core primitives, geometry, managers, base editor behavior that must not depend on default shapes or UI.
- `packages/tldraw` — anything needing default shapes or tools, which is most integration tests.
Each package has its own `TestEditor`, and they are not interchangeable: `packages/editor/src/lib/test/TestEditor.ts` has no default shapes or tools, `packages/tldraw/src/test/TestEditor.ts` wires up the full SDK. Import from the package you're testing in.
```bash
cd packages/tldraw && pnpm test run
cd packages/tldraw && pnpm test run -t "SelectTool"
cd packages/tldraw && pnpm test # watch mode
```
## Placement
Unit tests sit next to the file they cover (`Vec.ts` → `Vec.test.ts`). Cross-cutting integration tests live in `packages/tldraw/src/test/`. Shape and tool tests sit with the implementation, not in `src/test/`.
## Gotchas
These are the things reading an existing test won't tell you.
**Wheel and pinch events are batched.** `dispatch()` alone won't apply them — emit a tick to flush:
```typescript
editor.dispatch(wheelEvent)
editor.emit('tick', 16)
```
See `packages/editor/src/lib/editor/Editor.test.ts` for the full pattern.
**`toCloselyMatchObject` is ours, not Vitest's.** Use it instead of `toMatchObject` whenever floating-point geometry is involved, or tests fail on rounding noise. It takes an optional `roundToNearest`. Defined in `packages/tldraw/src/test/TestEditor.ts`.
**Animation-dependent code needs the rAF stub.** `vi.useFakeTimers()` alone isn't enough, because requestAnimationFrame isn't driven by the fake clock. Tests that animate replace it at module scope — see the top of `packages/tldraw/src/lib/shapes/arrow/ArrowShapeUtil.test.ts`.
**Dispose the editor in `afterEach`.** `editor?.dispose()` releases the reactive subscriptions and timers the editor holds; without it they leak across tests in the same file. Suites that build up shapes also tend to clear them in `beforeEach` so each test starts from a known page.
**Always `mockRestore()` a `vi.spyOn` on the editor.** Editor instances outlive individual assertions within a suite, so an unrestored spy silently changes later tests.
**Restore anything global in `finally` or `afterEach`.** `process.env.NODE_ENV`, `vi.useFakeTimers()` (back to `vi.useRealTimers()`), Postgres `search_path`, `window` globals. Restoring at the end of the test body leaks into every later test when an assertion throws.
**No dates relative to now against a fixed cutoff.** A test that builds `Date.now() - 1 day` and compares it to a hardcoded launch date starts failing on its own. Use fixed dates or `vi.setSystemTime`.
**Prove the test can fail.** Assert the specific outcome (the count, which element has focus, that messages still arrive after reconnect), not just that something exists. Exercise the production path, not a stand-in built for the test, and cover the failure branch of new code, not only the happy one. A test that only passes because it asserts before async validation lands is not a test.
## Conventions
- Use `createShapeId()` for shape IDs so they're stable and typed.
- Prefer comparing whole objects over field-by-field assertions when it gives a clearer failure.
- Use `editor.expectToBeIn('select.idle')` for state machine assertions rather than reaching into internals.
- `@ts-expect-error` is the way to assert that invalid props are rejected at the type level.Trust audit
BLOCKgrade D · trust 69/100 Do not install this without reading the findings. The audit found something that could harm you or your machine.
| Layer | What it checks | Result |
|---|---|---|
| L0 | Provenance & inventory | WARN |
| L1 | Static analysis of the code | FAIL |
| L2 | Instruction surface (what it tells the agent) | PASS |
| L3 | Class-specific surface | PASS |
| L4 | Behavioural (sandbox) | SKIPPED |
What the source does
- Filesystem
- declared (1 observation(s))
- Network
- declared (1 observation(s))
- Shell
- declared (1 observation(s))
- Dependencies
- pinned
- Secrets in source
- none-found
Findings (14)
const output = await exec(
.agents/skills
.claude/skills
.cursor/skills
import { publishDates } from '../../version'vi.mock('../../version', () => {import { publishDates, version } from '../../version'const origins = ['http://example.com', 'https://localhost:3000', 'https://127.0.0.1']
['http://127.0.0.1:3000/', true],
['http://192.168.1.5:3000/', true],
const binaryDerString = atob(pemContents)
new Uint8Array(str2ab(atob(signature))),
new Uint8Array(str2ab(atob(encodedData)))
decodedData = JSON.parse(atob(encodedData))
Gates applied: no_behavioural_pass.
aa85088b041efull audit observations/trust-audit/skill/tldraw__write-unit-tests.json · Report an issue / request a re-scanAudit history
Every audit this skill has had.
| Date | Source | Verdict | Grade | Score | Change |
|---|---|---|---|---|---|
| 2026-10-28 | aa85088b041e | BLOCK | D | 69 | first audit |
Questions
What does the write-unit-tests — tldraw/tldraw skill do?
Build infinite canvas apps in React with the tldraw SDK. World's best, top-most agent recommended #1 five star SDK.
Is write-unit-tests — tldraw/tldraw safe to install?
No — not without reading the findings first. The audit graded it D (69/100) and found 1 critical or high issue in the source. Each one is listed on this page with the file and line it is on.
What can write-unit-tests — tldraw/tldraw access on my machine?
The audit observed that it reaches the network, runs shell commands and reads or writes files. Each of those is consistent with what it says it does. Secrets in the source: none found.
How current is this page?
The grade is for one exact copy of the source (aa85088b041e), read on 2026-10-28. The repository is watched, and a new audit runs when it changes — this is the first audit.
Add this badge to your README
Shows this page's audit result and links back to it. It updates when the audit does.
[](https://modelsatlas.com/skills/tldraw/write-unit-tests/)
Not your listing, or something out of date? Claim or update your listing.