mirror of
https://github.com/olehomelchenko/astrolabe.git
synced 2026-08-08 02:02:33 +00:00
Implement M1 authoring loop: library, editor, live preview, persistence
M1 MVP from docs/IMPLEMENTATION-PLAN.md, with the /alignment pass applied. - core: Snippet model + factory; prepareSpecForRender (copy-not-mutate); per-theme Vega chart config - state/orchestration: SnippetStore with debounced auto-save; IndexedDB adapter + read-time migration; startup hydration + write-through persistence - ui: SnippetLibrary, SpecEditor (Monaco edcore.main — full editor features, JSON-only languages), LivePreview - build: Monaco/Vega manual chunks; raised PWA precache ceiling - alignment: flush a valid draft on snippet switch (+regression tests); TODO breadcrumbs for the preview render race and the window.confirm delete - housekeeping: gitignore .claude/projects/
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
import { describe, expect, test } from 'vitest';
|
||||
import { escapeVegaField, prepareSpecForRender } from './rendering';
|
||||
|
||||
describe('prepareSpecForRender', () => {
|
||||
test('returns a deep copy, never the same reference', () => {
|
||||
const spec = { mark: 'bar', encoding: { x: { field: 'a' } } };
|
||||
const out = prepareSpecForRender(spec);
|
||||
expect(out).not.toBe(spec);
|
||||
expect(out.encoding).not.toBe(spec.encoding);
|
||||
expect(out).toEqual(spec);
|
||||
});
|
||||
|
||||
test('never mutates the input spec (copy-not-mutate invariant)', () => {
|
||||
const spec = {
|
||||
data: { values: [{ a: 1 }] },
|
||||
mark: 'bar',
|
||||
encoding: { x: { field: 'a', type: 'quantitative' } },
|
||||
};
|
||||
const before = structuredClone(spec);
|
||||
const out = prepareSpecForRender(spec, { fitMode: 'width' });
|
||||
|
||||
// Mutating the output must not touch the input.
|
||||
(out as { mark: string }).mark = 'point';
|
||||
expect(spec).toEqual(before);
|
||||
});
|
||||
|
||||
test('M1 is a faithful pass-through of the spec content', () => {
|
||||
const spec = { $schema: 'x', mark: 'line', width: 200, height: 100 };
|
||||
expect(prepareSpecForRender(spec)).toEqual(spec);
|
||||
});
|
||||
});
|
||||
|
||||
describe('escapeVegaField', () => {
|
||||
test('escapes dots and brackets that VL treats as accessors', () => {
|
||||
expect(escapeVegaField('user.age')).toBe('user\\.age');
|
||||
expect(escapeVegaField('a[0]')).toBe('a\\[0\\]');
|
||||
});
|
||||
|
||||
test('leaves plain field names untouched', () => {
|
||||
expect(escapeVegaField('category')).toBe('category');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user