@motion-proto/live-tokens 0.3.6 → 0.3.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,328 +0,0 @@
1
- // @vitest-environment happy-dom
2
- import { beforeEach, describe, expect, it } from 'vitest';
3
- import { get } from 'svelte/store';
4
- import type { PaletteConfig } from './themeTypes';
5
- import {
6
- editorState,
7
- mutate,
8
- beginScope,
9
- commitScope,
10
- cancelScope,
11
- beginSliderGesture,
12
- transaction,
13
- undo,
14
- redo,
15
- setPaletteConfig,
16
- __resetForTests,
17
- __getHistoryLengths,
18
- __getPastAt,
19
- } from './editorStore';
20
-
21
- function makePaletteConfig(baseColor: string): PaletteConfig {
22
- return {
23
- baseColor,
24
- tintHue: 0,
25
- tintChroma: 0.04,
26
- lightnessCurve: [],
27
- saturationCurve: [],
28
- grayLightnessCurve: [],
29
- graySaturationCurve: [],
30
- scaleCurves: {},
31
- curveOffset: {},
32
- overrides: {},
33
- snappedScales: [],
34
- };
35
- }
36
-
37
- const txOpts = { label: 'tx', collapseToOne: true, clipUndoFloor: false } as const;
38
- const sessionOpts = { label: 'palette session', collapseToOne: true, clipUndoFloor: true } as const;
39
-
40
- beforeEach(() => {
41
- __resetForTests();
42
- });
43
-
44
- describe('editorStore — mutate() outside a scope', () => {
45
- it('pushes exactly one past[] entry per call and undo restores', () => {
46
- setPaletteConfig('Background', makePaletteConfig('#111111'));
47
- expect(__getHistoryLengths().past).toBe(1);
48
-
49
- setPaletteConfig('Background', makePaletteConfig('#222222'));
50
- expect(__getHistoryLengths().past).toBe(2);
51
-
52
- expect(get(editorState).palettes.Background.baseColor).toBe('#222222');
53
- expect(undo()).toBe(true);
54
- expect(get(editorState).palettes.Background.baseColor).toBe('#111111');
55
- expect(undo()).toBe(true);
56
- expect(get(editorState).palettes.Background).toBeUndefined();
57
- expect(undo()).toBe(false);
58
- });
59
- });
60
-
61
- describe('editorStore — non-clipping scopes group gestures', () => {
62
- it('beginScope + multiple mutate() + commitScope → one past entry equal to pre-gesture snapshot', () => {
63
- setPaletteConfig('Background', makePaletteConfig('#111111'));
64
- const baselinePast = __getHistoryLengths().past;
65
- const preGesture = structuredClone(get(editorState));
66
-
67
- const scope = beginScope({ label: 'drag hue', collapseToOne: true, clipUndoFloor: false });
68
- mutate('hue step 1', (s) => { s.palettes.Background.baseColor = '#222222'; });
69
- mutate('hue step 2', (s) => { s.palettes.Background.baseColor = '#333333'; });
70
- mutate('hue step 3', (s) => { s.palettes.Background.baseColor = '#444444'; });
71
- commitScope(scope);
72
-
73
- expect(__getHistoryLengths().past).toBe(baselinePast + 1);
74
- const lastEntry = __getPastAt(__getHistoryLengths().past - 1)!;
75
- expect(lastEntry.palettes.Background.baseColor).toBe(preGesture.palettes.Background.baseColor);
76
- expect(get(editorState).palettes.Background.baseColor).toBe('#444444');
77
-
78
- // One undo rolls the whole gesture back
79
- undo();
80
- expect(get(editorState).palettes.Background.baseColor).toBe('#111111');
81
- });
82
-
83
- it('beginSliderGesture opens a scope that groups updates into one entry', () => {
84
- setPaletteConfig('Background', makePaletteConfig('#111111'));
85
- const baselinePast = __getHistoryLengths().past;
86
-
87
- beginSliderGesture('drag');
88
- mutate('step', (s) => { s.palettes.Background.baseColor = '#222222'; });
89
- mutate('step', (s) => { s.palettes.Background.baseColor = '#333333'; });
90
- // Simulate pointerup
91
- window.dispatchEvent(new Event('pointerup'));
92
-
93
- expect(__getHistoryLengths().past).toBe(baselinePast + 1);
94
- expect(get(editorState).palettes.Background.baseColor).toBe('#333333');
95
- });
96
-
97
- it('empty scope (no mutate calls) does not push history', () => {
98
- const baselinePast = __getHistoryLengths().past;
99
- const scope = beginScope({ ...txOpts, label: 'unused' });
100
- commitScope(scope);
101
- expect(__getHistoryLengths().past).toBe(baselinePast);
102
- });
103
-
104
- it('cancelScope on a non-clipping scope restores pre-gesture state and does not push history', () => {
105
- setPaletteConfig('Background', makePaletteConfig('#111111'));
106
- const baselinePast = __getHistoryLengths().past;
107
-
108
- const scope = beginScope({ ...txOpts, label: 'drag' });
109
- mutate('step', (s) => { s.palettes.Background.baseColor = '#999999'; });
110
- cancelScope(scope, { silent: true });
111
-
112
- expect(__getHistoryLengths().past).toBe(baselinePast);
113
- expect(get(editorState).palettes.Background.baseColor).toBe('#111111');
114
- });
115
- });
116
-
117
- describe('editorStore — clipping scopes (palette edit sessions)', () => {
118
- it('beginScope with clipUndoFloor does not push history', () => {
119
- setPaletteConfig('Background', makePaletteConfig('#111111'));
120
- const before = __getHistoryLengths().past;
121
- beginScope({ ...sessionOpts });
122
- expect(__getHistoryLengths().past).toBe(before);
123
- });
124
-
125
- it('undo is clipped to the scope floor while open', () => {
126
- setPaletteConfig('Background', makePaletteConfig('#111111'));
127
- setPaletteConfig('Background', makePaletteConfig('#222222'));
128
- const floor = __getHistoryLengths().past;
129
-
130
- beginScope({ ...sessionOpts });
131
- setPaletteConfig('Background', makePaletteConfig('#333333'));
132
- setPaletteConfig('Background', makePaletteConfig('#444444'));
133
- expect(__getHistoryLengths().past).toBe(floor + 2);
134
-
135
- expect(undo()).toBe(true);
136
- expect(get(editorState).palettes.Background.baseColor).toBe('#333333');
137
- expect(undo()).toBe(true);
138
- expect(get(editorState).palettes.Background.baseColor).toBe('#222222');
139
-
140
- // Floor reached — further undo returns false, state unchanged
141
- expect(undo()).toBe(false);
142
- expect(get(editorState).palettes.Background.baseColor).toBe('#222222');
143
- });
144
-
145
- it('commitScope on a clipping scope collapses intra-scope history into one entry equal to the snapshot', () => {
146
- setPaletteConfig('Background', makePaletteConfig('#111111'));
147
- const preSessionPastLen = __getHistoryLengths().past;
148
-
149
- const session = beginScope({ ...sessionOpts });
150
- setPaletteConfig('Background', makePaletteConfig('#222222'));
151
- setPaletteConfig('Background', makePaletteConfig('#333333'));
152
- setPaletteConfig('Background', makePaletteConfig('#444444'));
153
- commitScope(session);
154
-
155
- expect(__getHistoryLengths().past).toBe(preSessionPastLen + 1);
156
-
157
- const committedEntry = __getPastAt(__getHistoryLengths().past - 1)!;
158
- expect(committedEntry.palettes.Background.baseColor).toBe('#111111');
159
- expect(get(editorState).palettes.Background.baseColor).toBe('#444444');
160
-
161
- // One undo restores pre-scope state
162
- undo();
163
- expect(get(editorState).palettes.Background.baseColor).toBe('#111111');
164
- });
165
-
166
- it('commitScope on a clipping scope with no net change pushes nothing', () => {
167
- setPaletteConfig('Background', makePaletteConfig('#111111'));
168
- const preSessionPastLen = __getHistoryLengths().past;
169
-
170
- const session = beginScope({ ...sessionOpts });
171
- // Mutate and revert to snapshot value
172
- setPaletteConfig('Background', makePaletteConfig('#222222'));
173
- setPaletteConfig('Background', makePaletteConfig('#111111'));
174
- commitScope(session);
175
-
176
- expect(__getHistoryLengths().past).toBe(preSessionPastLen);
177
- expect(get(editorState).palettes.Background.baseColor).toBe('#111111');
178
- });
179
-
180
- it('cancelScope on a clipping scope restores snapshot, drops intra-scope entries, clears future', () => {
181
- setPaletteConfig('Background', makePaletteConfig('#111111'));
182
- const preSessionPastLen = __getHistoryLengths().past;
183
-
184
- const session = beginScope({ ...sessionOpts });
185
- setPaletteConfig('Background', makePaletteConfig('#222222'));
186
- setPaletteConfig('Background', makePaletteConfig('#333333'));
187
- expect(__getHistoryLengths().past).toBe(preSessionPastLen + 2);
188
-
189
- cancelScope(session);
190
-
191
- expect(__getHistoryLengths().past).toBe(preSessionPastLen);
192
- expect(__getHistoryLengths().future).toBe(0);
193
- expect(get(editorState).palettes.Background.baseColor).toBe('#111111');
194
- });
195
-
196
- it('nested clipping beginScope auto-commits the prior scope', () => {
197
- setPaletteConfig('Background', makePaletteConfig('#111111'));
198
- setPaletteConfig('Accent', makePaletteConfig('#aaaaaa'));
199
- const preSessionPastLen = __getHistoryLengths().past;
200
-
201
- beginScope({ ...sessionOpts });
202
- setPaletteConfig('Background', makePaletteConfig('#222222'));
203
- const second = beginScope({ ...sessionOpts }); // auto-commits prior
204
- expect(__getHistoryLengths().past).toBe(preSessionPastLen + 1);
205
-
206
- setPaletteConfig('Accent', makePaletteConfig('#bbbbbb'));
207
- commitScope(second);
208
-
209
- // Two collapsed entries: prior Background scope, then Accent scope
210
- expect(__getHistoryLengths().past).toBe(preSessionPastLen + 2);
211
- // One undo: revert Accent to pre-scope value
212
- undo();
213
- expect(get(editorState).palettes.Accent.baseColor).toBe('#aaaaaa');
214
- expect(get(editorState).palettes.Background.baseColor).toBe('#222222');
215
- // Another undo: revert Background to pre-scope value
216
- undo();
217
- expect(get(editorState).palettes.Background.baseColor).toBe('#111111');
218
- });
219
-
220
- it('undo() with a pending non-clipping scope cancels it first (drag-in-flight is discarded)', () => {
221
- setPaletteConfig('Background', makePaletteConfig('#111111'));
222
- const pastLenBefore = __getHistoryLengths().past;
223
-
224
- beginScope({ ...txOpts, label: 'drag' });
225
- mutate('step', (s) => { s.palettes.Background.baseColor = '#ffffff'; });
226
- // An in-flight drag holds pre-drag state in the scope's snapshot;
227
- // `undo()` cancels it (restoring that snapshot) before consulting history.
228
- undo();
229
- // The cancelled in-flight change is gone; history count unchanged by the cancel.
230
- // (Current impl also consumes one history step after cancelling — the
231
- // cross-boundary behavior is a separate concern tracked in the plan.)
232
- expect(__getHistoryLengths().past).toBe(pastLenBefore - 1);
233
- // The pending mutation did not survive: '#ffffff' is not current.
234
- expect(get(editorState).palettes.Background?.baseColor).not.toBe('#ffffff');
235
- });
236
- });
237
-
238
- describe('editorStore — apply + undo matches spec end-to-end', () => {
239
- it('after Apply, one Cmd+Z restores to pre-session state', () => {
240
- setPaletteConfig('Background', makePaletteConfig('#8d7f74'));
241
- const preSessionState = structuredClone(get(editorState));
242
-
243
- const session = beginScope({ ...sessionOpts });
244
- // Simulate three slider drags during the session
245
- for (const hex of ['#702030', '#503090', '#205090']) {
246
- const drag = beginScope({ ...txOpts, label: `drag ${hex}` });
247
- setPaletteConfig('Background', makePaletteConfig(hex));
248
- commitScope(drag);
249
- }
250
- commitScope(session);
251
-
252
- expect(get(editorState).palettes.Background.baseColor).toBe('#205090');
253
-
254
- const undone = undo();
255
- expect(undone).toBe(true);
256
- expect(get(editorState).palettes.Background.baseColor).toBe(preSessionState.palettes.Background.baseColor);
257
- expect(JSON.stringify(get(editorState))).toBe(JSON.stringify(preSessionState));
258
- });
259
-
260
- it('after Cancel, Cmd+Z does not resurrect discarded drags', () => {
261
- setPaletteConfig('Background', makePaletteConfig('#8d7f74'));
262
- const preSessionState = structuredClone(get(editorState));
263
-
264
- const session = beginScope({ ...sessionOpts });
265
- for (const hex of ['#702030', '#503090', '#205090']) {
266
- const drag = beginScope({ ...txOpts, label: `drag ${hex}` });
267
- setPaletteConfig('Background', makePaletteConfig(hex));
268
- commitScope(drag);
269
- }
270
- cancelScope(session);
271
-
272
- // State is pre-session; no new history entry
273
- expect(JSON.stringify(get(editorState))).toBe(JSON.stringify(preSessionState));
274
- // One undo walks back to before the palette existed (setPaletteConfig before scope)
275
- undo();
276
- expect(get(editorState).palettes.Background).toBeUndefined();
277
- });
278
- });
279
-
280
- describe('editorStore — intra-session slider-drag tracking', () => {
281
- // Regression guard for the two-writer feedback loop fixed in the
282
- // PaletteEditor single-source-of-truth refactor: during a drag inside a
283
- // palette edit session, every per-tick mutation must be visible in the
284
- // store immediately (not pulled back to a stale pre-session value).
285
- it('store reflects every per-tick mutation during a slider-drag session', () => {
286
- setPaletteConfig('Background', makePaletteConfig('#8d7f74'));
287
-
288
- const session = beginScope({ ...sessionOpts });
289
- beginSliderGesture('drag base');
290
-
291
- const tickHexes = ['#8c7f73', '#8b7f72', '#8a7f71', '#897f70', '#887f6f'];
292
- for (const hex of tickHexes) {
293
- mutate('drag tick', (s) => { s.palettes.Background.baseColor = hex; });
294
- // Each tick must be visible on read — no stale pre-session value
295
- expect(get(editorState).palettes.Background.baseColor).toBe(hex);
296
- }
297
-
298
- window.dispatchEvent(new Event('pointerup'));
299
- commitScope(session);
300
-
301
- expect(get(editorState).palettes.Background.baseColor).toBe('#887f6f');
302
-
303
- // One undo after Apply restores to pre-session
304
- undo();
305
- expect(get(editorState).palettes.Background.baseColor).toBe('#8d7f74');
306
- });
307
-
308
- it('Cmd+Z during a session walks one tick back per press', () => {
309
- setPaletteConfig('Background', makePaletteConfig('#8d7f74'));
310
-
311
- beginScope({ ...sessionOpts });
312
- for (const hex of ['#702030', '#503090', '#205090']) {
313
- const drag = beginScope({ ...txOpts, label: `drag ${hex}` });
314
- mutate('tick', (s) => { s.palettes.Background.baseColor = hex; });
315
- commitScope(drag);
316
- }
317
-
318
- expect(get(editorState).palettes.Background.baseColor).toBe('#205090');
319
- undo();
320
- expect(get(editorState).palettes.Background.baseColor).toBe('#503090');
321
- undo();
322
- expect(get(editorState).palettes.Background.baseColor).toBe('#702030');
323
- undo();
324
- expect(get(editorState).palettes.Background.baseColor).toBe('#8d7f74');
325
- // Session floor reached — further undo no-ops
326
- expect(undo()).toBe(false);
327
- });
328
- });
@@ -1,54 +0,0 @@
1
- // @vitest-environment happy-dom
2
- import { describe, expect, it, vi, beforeEach } from 'vitest';
3
- import { get } from 'svelte/store';
4
- import { configureEditor, storageKey } from './editorConfig';
5
- import { route, navigate, init as initRouter } from './router';
6
-
7
- describe('M8 — lazy storage prefix resolution', () => {
8
- it('storageKey() reflects the prefix at call time, not at module-load time', () => {
9
- // The default prefix is `lt-`. configureEditor mutates it; subsequent
10
- // storageKey() calls must observe the new prefix.
11
- expect(storageKey('editor-state')).toBe('lt-editor-state');
12
-
13
- configureEditor({ storagePrefix: 'test-' });
14
- expect(storageKey('editor-state')).toBe('test-editor-state');
15
-
16
- // Reset for any later tests in the file.
17
- configureEditor({ storagePrefix: 'lt-' });
18
- expect(storageKey('editor-state')).toBe('lt-editor-state');
19
- });
20
-
21
- it('editorStore persistNow uses the configured prefix at write time', async () => {
22
- // Reconfigure BEFORE importing editorStore — but since these tests share
23
- // module state with other suites, we can only verify that getPersistKey
24
- // behaviour is lazy via the storageKey contract above. The functional
25
- // round-trip is covered by manual library-import tests.
26
- configureEditor({ storagePrefix: 'lazy-' });
27
- expect(storageKey('editor-state')).toBe('lazy-editor-state');
28
- configureEditor({ storagePrefix: 'lt-' });
29
- });
30
- });
31
-
32
- describe('m9 — navigate() emits exactly one route store update', () => {
33
- beforeEach(() => {
34
- // Pin a stable starting route. Router.init() is idempotent; calling it
35
- // here is safe even if some earlier test already initialised it.
36
- initRouter();
37
- // Reset history pushState side effects to a clean state by overwriting
38
- // the current route to '/'.
39
- history.replaceState(null, '', '/');
40
- route.set('/');
41
- });
42
-
43
- it('produces a single store write per navigate() call', () => {
44
- const sub = vi.fn();
45
- const unsub = route.subscribe(sub);
46
- sub.mockClear(); // drop the immediate-emit on subscribe
47
-
48
- navigate('/foo');
49
- expect(sub).toHaveBeenCalledTimes(1);
50
- expect(get(route)).toBe('/foo');
51
-
52
- unsub();
53
- });
54
- });