Files
wehub-resource-sync 070959e133
landing-page-staging / Deploy landing page to staging (push) Has been skipped
landing-page-ci / Validate landing page (push) Failing after 4s
visual-baseline / Capture visual baselines (push) Has been cancelled
bake-plugin-previews / Bake plugin previews (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:00:47 +08:00

145 lines
4.3 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import {
buildExcalidrawSketchDocument,
buildSketchDocument,
computeSketchBounds,
parseSketchDocument,
parseSketchWorkspaceDocument,
} from '../../src/components/sketch-model';
describe('sketch-model', () => {
it('tolerates malformed text items from sketch json when computing bounds', () => {
const items = parseSketchDocument(JSON.stringify({
version: 1,
items: [
{ kind: 'text', x: 0, y: 0, size: 16, color: '#111' },
],
}));
expect(() => computeSketchBounds(items)).not.toThrow();
expect(computeSketchBounds(items)).toEqual({
minX: -4,
minY: -20,
maxX: 20,
maxY: 7.2,
});
});
it('drops malformed non-text items while preserving normalized text items', () => {
const items = parseSketchDocument(JSON.stringify({
version: 1,
items: [
{ kind: 'pen' },
{ kind: 'rect' },
{ kind: 'arrow' },
{ kind: 'text', x: 0, y: 0, size: 16, color: '#111' },
],
}));
expect(items).toEqual([
{
kind: 'text',
x: 0,
y: 0,
text: '',
color: '#111',
size: 16,
},
]);
expect(() => computeSketchBounds(items)).not.toThrow();
expect(computeSketchBounds(items)).toEqual({
minX: -4,
minY: -20,
maxX: 20,
maxY: 7.2,
});
});
it('preserves unsupported raw items and version when rebuilding a workspace sketch document', () => {
const parsed = parseSketchWorkspaceDocument(JSON.stringify({
version: 3,
items: [
{ kind: 'pen', points: [{ x: 1, y: 2 }], color: '#111', size: 2 },
{ kind: 'ellipse', cx: 80, cy: 60, rx: 24, ry: 12, color: '#0af', size: 3 },
{ kind: 'text', x: 20, y: 32, text: 'hello', color: '#222', size: 16 },
],
}));
const rebuilt = buildSketchDocument(parsed.version, parsed.rawItems, parsed.items);
expect(rebuilt).toEqual({
version: 3,
items: [
{ kind: 'pen', points: [{ x: 1, y: 2 }], color: '#111', size: 2 },
{ kind: 'ellipse', cx: 80, cy: 60, rx: 24, ry: 12, color: '#0af', size: 3 },
{ kind: 'text', x: 20, y: 32, text: 'hello', color: '#222', size: 16 },
],
});
});
it('drops unsupported raw items when rebuilding from an explicit clear state', () => {
const rebuilt = buildSketchDocument(3, [], []);
expect(rebuilt).toEqual({
version: 3,
items: [],
});
});
it('parses Excalidraw sketch documents as scenes', () => {
const parsed = parseSketchWorkspaceDocument(JSON.stringify({
type: 'excalidraw',
version: 2,
source: 'https://open-design.ai/sketch',
elements: [
{ id: 'box', type: 'rectangle', x: 10, y: 20, width: 80, height: 40 },
],
appState: { viewBackgroundColor: '#ffffff' },
files: {},
libraryItems: [
{
id: 'lib-box',
status: 'unpublished',
created: 1710000000000,
elements: [{ id: 'box-template', type: 'rectangle', isDeleted: false }],
},
],
}));
expect(parsed.format).toBe('excalidraw');
expect(parsed.items).toEqual([]);
expect(parsed.rawItems).toEqual([]);
expect(parsed.scene.elements).toHaveLength(1);
expect(parsed.scene.appState).toEqual({ viewBackgroundColor: '#ffffff' });
expect(parsed.scene).not.toHaveProperty('libraryItems');
});
it('serializes Excalidraw scenes without transient app state', () => {
const document = buildExcalidrawSketchDocument({
elements: [{ id: 'box', type: 'rectangle', isDeleted: false }],
appState: {
name: 'scratch.sketch.json',
viewBackgroundColor: '#ffffff',
openMenu: 'canvas',
collaborators: new Map(),
},
files: {},
}, 'scratch.sketch.json');
expect(document).toMatchObject({
type: 'excalidraw',
version: 2,
source: 'https://open-design.ai/sketch',
elements: [{ id: 'box', type: 'rectangle', isDeleted: false }],
appState: {
name: 'scratch.sketch.json',
viewBackgroundColor: '#ffffff',
},
files: {},
});
expect(document).not.toHaveProperty('libraryItems');
expect(document.appState).not.toHaveProperty('openMenu');
expect(document.appState).not.toHaveProperty('collaborators');
});
});