fix: address review feedback for airport-editor pure history module

Harden createHistory maxDepth (floor then positive finite) and cover
redo-path undo stack trim in unit tests.
This commit is contained in:
Reese Norris
2026-07-27 16:33:55 -04:00
parent 7be290f709
commit 9328d32380
2 changed files with 35 additions and 5 deletions

View File

@@ -54,10 +54,9 @@ function deepClone(value) {
* @returns {HistoryStack}
*/
export function createHistory(opts = {}) {
const n = Math.floor(Number(opts.maxDepth));
const maxDepth =
typeof opts.maxDepth === 'number' && opts.maxDepth > 0
? Math.floor(opts.maxDepth)
: DEFAULT_HISTORY_MAX_DEPTH;
Number.isFinite(n) && n > 0 ? n : DEFAULT_HISTORY_MAX_DEPTH;
return {
undoStack: [],
redoStack: [],

View File

@@ -86,8 +86,15 @@ test('createHistory defaults and custom maxDepth', () => {
const h2 = createHistory({ maxDepth: 3 });
assert.equal(h2.maxDepth, 3);
const h3 = createHistory({ maxDepth: 0 });
assert.equal(h3.maxDepth, DEFAULT_HISTORY_MAX_DEPTH);
// Invalid / non-positive / non-finite → default
assert.equal(createHistory({ maxDepth: 0 }).maxDepth, DEFAULT_HISTORY_MAX_DEPTH);
assert.equal(createHistory({ maxDepth: 0.5 }).maxDepth, DEFAULT_HISTORY_MAX_DEPTH);
assert.equal(createHistory({ maxDepth: -2 }).maxDepth, DEFAULT_HISTORY_MAX_DEPTH);
assert.equal(createHistory({ maxDepth: Infinity }).maxDepth, DEFAULT_HISTORY_MAX_DEPTH);
assert.equal(createHistory({ maxDepth: NaN }).maxDepth, DEFAULT_HISTORY_MAX_DEPTH);
assert.equal(createHistory({ maxDepth: 'nope' }).maxDepth, DEFAULT_HISTORY_MAX_DEPTH);
// Floor then accept: 2.9 → 2
assert.equal(createHistory({ maxDepth: 2.9 }).maxDepth, 2);
});
// ---------------------------------------------------------------------------
@@ -269,6 +276,30 @@ test('maxDepth drops oldest on overflow', () => {
assert.equal(h.undoStack[2]._nextSurfaceId, 5);
});
test('maxDepth trims undo on redo path when stack would exceed max', () => {
// Normal undo/redo from a max-sized stack never exceeds max (undo shrinks
// undo as it grows redo). Cover the defensive trim in redo by pre-filling
// undo to maxDepth with a pending redo entry so redo would push past max.
const h = createHistory({ maxDepth: 2 });
const doc = createEmptyDocument();
doc._nextSurfaceId = 1;
h.undoStack.push(captureSnapshot(doc));
doc._nextSurfaceId = 2;
h.undoStack.push(captureSnapshot(doc));
doc._nextSurfaceId = 99;
h.redoStack.push(captureSnapshot(doc));
doc._nextSurfaceId = 3; // live present; redo captures this onto undo
const r = redo(h, doc);
assert.equal(r.ok, true);
assert.equal(doc._nextSurfaceId, 99);
assert.equal(h.undoStack.length, 2); // not 3 — oldest dropped
assert.equal(h.undoStack[0]._nextSurfaceId, 2);
assert.equal(h.undoStack[1]._nextSurfaceId, 3);
assert.equal(h.redoStack.length, 0);
});
// ---------------------------------------------------------------------------
// gestures
// ---------------------------------------------------------------------------