mirror of
https://github.com/Matysh/houseplan-card
synced 2026-10-02 12:49:56 +00:00
test(stairs): take the AC5 reference after the previous write is adopted (#708)
untouchedDialogSaveKeepsSizes compared the stair before and after an untouched dialog Save byte for byte, but took the reference while the previous frame gesture's debounced save (500 ms) was still pending. That write adopts the canonical record it sends, so under load it landed between the two reads: the reference had x: 0.21699999999999997, the read after Save had x: 0.217, and the check went red although the dialog wrote nothing. The smoke now waits for the card's own "config writes idle" condition (no debounced save pending, no write in flight; read-only) before taking the reference, so both sides are the same canonical stair. The exact JSON comparison, the >1 m field and the no-history check are unchanged; a reference that never goes idle within 5 s fails the check instead of racing. Issue: #708 User-Visible: no Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018qZfe7YS4rqEMKoVeS3GKd
This commit is contained in:
+18
-2
@@ -17,6 +17,18 @@ const out = await page.evaluate(async () => {
|
||||
await new Promise((resolve) => requestAnimationFrame(resolve));
|
||||
await settled();
|
||||
};
|
||||
// A gesture's save is debounced, and the write adopts the canonical record
|
||||
// it sends (`x: 0.21699999999999997` becomes `0.217`). The card's own
|
||||
// «writes idle» — no debounced save pending, nothing in flight — marks the
|
||||
// moment the stored stair is final (#708).
|
||||
const writesIdle = async () => {
|
||||
const busy = () => card._saveConfigDebounced.pending() || card._writesPending > 0;
|
||||
const deadline = performance.now() + 5000;
|
||||
while (busy() && performance.now() < deadline)
|
||||
await new Promise((resolve) => setTimeout(resolve, 16));
|
||||
await settled();
|
||||
return !busy();
|
||||
};
|
||||
const spaceCfg = (id) => card._serverCfg.spaces.find((space) => space.id === id);
|
||||
const stairs = (id = 'f1') => spaceCfg(id)?.stairs || [];
|
||||
const stairNode = (id) => root().querySelector(`[data-hp="stair"][data-id="${id}"]`);
|
||||
@@ -346,13 +358,17 @@ const out = await page.evaluate(async () => {
|
||||
await hp.setTool('stairs');
|
||||
|
||||
// Saving the properties dialog untouched changes nothing (#676 AC5):
|
||||
// sizes above one metre survive and no history entry is written.
|
||||
// sizes above one metre survive and no history entry is written. The
|
||||
// reference is the record after the previous gesture's write is adopted:
|
||||
// both sides of the comparison are then the same canonical stair (#708).
|
||||
const idleBeforeUntouched = await writesIdle();
|
||||
const untouched = JSON.stringify(stairs().find((stair) => stair.id === straight.id));
|
||||
const historyBefore = card._geometryHistory.size;
|
||||
dialog = await openStairDialog(straight.id);
|
||||
const fieldsShown = [...dialog.querySelectorAll('input[type="number"]')].map((input) => input.value);
|
||||
await saveDialog(dialog);
|
||||
result.untouchedDialogSaveKeepsSizes = JSON.stringify(stairs().find((stair) => stair.id === straight.id)) === untouched
|
||||
result.untouchedDialogSaveKeepsSizes = idleBeforeUntouched
|
||||
&& JSON.stringify(stairs().find((stair) => stair.id === straight.id)) === untouched
|
||||
&& Number(fieldsShown[0]) > 100;
|
||||
result.untouchedDialogSaveWritesNoHistory = card._geometryHistory.size === historyBefore;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user