Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion plugins/draw/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,14 @@ Build the UI from its own lockfile:
```sh
cd ui
npm ci
npx tsc --noEmit
npm run build
cd ..
npm run test:integration
```

The integration tests connect the UI Socket.IO client to the Draw server over loopback WebSocket and polling transports. They verify room joins, scene updates, cursor updates, state requests, and disconnects.
The integration tests connect the UI Socket.IO client to the Draw server over loopback WebSocket and polling transports. They verify room joins, scene updates, cursor updates, state requests, and disconnects. They also check that the built UI serves its Excalidraw stylesheet.

The unit tests exercise parser compatibility, rejection of malformed binary headers, Nano ID's six-character session suffixes and Draw storage in temporary directories. They do not open a browser or sharing tunnel. CI runs these checks on Node.js 20, 22, and 24 alongside the repository's Bats and Shellcheck jobs.

The UI overrides vulnerable versions pinned by Excalidraw and its Mermaid converter: Nano ID, lodash-es, and Sass. The Sass update also removes the old Chokidar/Braces dependency chain. Keep these overrides until the upstream dependency ranges include patched releases, and preserve Node.js 20 compatibility when updating them. Browser checks should cover drawing and Mermaid-to-Excalidraw conversion as well as the build.
28 changes: 28 additions & 0 deletions plugins/draw/test/integration/ui.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import assert from 'node:assert/strict';
import { once } from 'node:events';
import { test } from 'node:test';
import { createHttpServer } from '../../dist/server/http.js';

test('built UI serves the Excalidraw stylesheet', async (t) => {
const server = createHttpServer({ getElements: () => [], saveElements: () => {} });
t.after(() => new Promise((resolve) => server.close(resolve)));
server.listen(0, '127.0.0.1');
await once(server, 'listening');
const origin = `http://127.0.0.1:${server.address().port}`;
const response = await fetch(origin);
assert.equal(response.status, 200);
const html = await response.text();
const stylesheets = [...html.matchAll(/<link\b[^>]*rel="stylesheet"[^>]*>/g)]
.map(([tag]) => tag.match(/href="([^"]+)"/)?.[1]);
assert.ok(stylesheets.length > 0, 'the UI must load its canvas stylesheet');

let css = '';
for (const href of stylesheets) {
assert.ok(href, 'stylesheet link must have an href');
const stylesheet = await fetch(new URL(href, origin));
assert.equal(stylesheet.status, 200);
assert.match(stylesheet.headers.get('content-type'), /^text\/css/);
css += await stylesheet.text();
}
assert.match(css, /\.excalidraw\b/, 'the served CSS must include Excalidraw styles');
});
Loading
Loading