refactor(fp): WP-31 — shared store helpers (dedupe brief/org-template/stamdata)
Audit "apply high-value": extract four shared helpers into shared/application/ and rewire the editor stores (behaviour unchanged, existing specs are the gate): - action-state.ts: ActionState/SaveState (were duplicated in both brief stores). - history.ts: createHistory<T> (extracted from BriefStore's WP-27 undo/redo; WP-32 reuses). - debounced-save.ts: createDebouncedSave (the 600ms timer/PendingSave dance, was 2×+). - machine-remote-data.ts: machineRemoteData (the loading/failed/loaded→RemoteData switch, 3×). Each helper has a co-located spec. Deferred DDD findings (contracts/ inconsistency, a parse* traverse combinator, the 6× Seed boilerplate) are reported in the WP file, not built. npm run ci green; 323 tests (+13 helper specs); brief/org-template/stamdata specs unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { createHistory } from './history';
|
||||
|
||||
describe('createHistory', () => {
|
||||
it('starts empty; undo/redo are no-ops', () => {
|
||||
const h = createHistory<number>();
|
||||
expect(h.canUndo()).toBe(false);
|
||||
expect(h.canRedo()).toBe(false);
|
||||
expect(h.undo(1)).toBeUndefined();
|
||||
expect(h.redo(1)).toBeUndefined();
|
||||
});
|
||||
|
||||
it('records pre-edit snapshots, then undoes and redoes through them', () => {
|
||||
const h = createHistory<string>();
|
||||
// document went a -> b (record a) -> c (record b); current is 'c'
|
||||
h.record('a');
|
||||
h.record('b');
|
||||
expect(h.canUndo()).toBe(true);
|
||||
|
||||
expect(h.undo('c')).toBe('b'); // current 'c' pushed to redo
|
||||
expect(h.canRedo()).toBe(true);
|
||||
expect(h.undo('b')).toBe('a');
|
||||
expect(h.canUndo()).toBe(false);
|
||||
|
||||
expect(h.redo('a')).toBe('b');
|
||||
expect(h.redo('b')).toBe('c');
|
||||
expect(h.canRedo()).toBe(false);
|
||||
});
|
||||
|
||||
it('record() clears the redo stack (no dead redo after a fresh edit)', () => {
|
||||
const h = createHistory<string>();
|
||||
h.record('a');
|
||||
h.undo('b'); // redo now holds 'b'
|
||||
expect(h.canRedo()).toBe(true);
|
||||
h.record('x');
|
||||
expect(h.canRedo()).toBe(false);
|
||||
});
|
||||
|
||||
it('caps the stack depth', () => {
|
||||
const h = createHistory<number>(3);
|
||||
for (let i = 0; i < 5; i++) h.record(i);
|
||||
let undos = 0;
|
||||
let cur = 99;
|
||||
while (h.canUndo()) {
|
||||
cur = h.undo(cur)!;
|
||||
undos++;
|
||||
}
|
||||
expect(undos).toBe(3);
|
||||
});
|
||||
|
||||
it('clear() empties both stacks', () => {
|
||||
const h = createHistory<number>();
|
||||
h.record(1);
|
||||
h.undo(2);
|
||||
h.clear();
|
||||
expect(h.canUndo()).toBe(false);
|
||||
expect(h.canRedo()).toBe(false);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user