From 3b8d23941035fd80dc757f8982519480c40d0a30 Mon Sep 17 00:00:00 2001 From: HugoXK Date: Fri, 30 Oct 2026 14:23:14 +0000 Subject: [PATCH] test(room-invite-preview): freeze the clock the render reads The card's deadline case rendered with Date.now() inside inviteCountdown and then compared against inviteCountdownAt(..., Date.now()) - two independent reads, so a second boundary between them made the expectation one second ahead of the drawn line. It was seen once as "37958984:53 left to join" against ":52" and never reproduced in 60 single-file runs (#1839). Read the clock once, freeze it for the whole case and drive both the render and the expectation from that instant; restore the real clock in finally. No tolerance and no production change: the assertion still compares exact text, so an off-by-one inside the countdown would still fail. Docs-not-needed: test-only; no subsystem behaviour or owned path changes. Fixes #1839 --- core/test/room-invite-preview.test.js | 28 +++++++++++++------ .../files/test/room-invite-preview.test.js | 28 +++++++++++++------ 2 files changed, 38 insertions(+), 18 deletions(-) diff --git a/core/test/room-invite-preview.test.js b/core/test/room-invite-preview.test.js index 73e99e9f6..860d10914 100644 --- a/core/test/room-invite-preview.test.js +++ b/core/test/room-invite-preview.test.js @@ -19,15 +19,25 @@ const byKind = (node, kind) => walk(node).filter(n => n.props?.['data-flybot'] = const preview = { name: 'Saturday room', lead: 'Chief', people: '3 / 8 in the room', expiresAt: '2099-01-01T00:00:00.000Z', clockOffsetMs: 0 } test('#876: the card names the room, its lead, its people and the deadline', () => { - const tree = RoomInvitePreview({ h, preview, onJoin() {}, onClear() {} }) - assert.equal(tree.props['data-flybot'], 'face-link-preview') - assert.equal(tree.children[0].children.join(''), 'Saturday room') - assert.equal(tree.children[1].children.join(''), 'Lead: Chief') - assert.equal(tree.children[2].children.join(''), '3 / 8 in the room') - // The deadline line is the one countdown text, not a per-surface copy of the rule. - assert.equal(tree.children[3].children.join(''), inviteCountdownAt(preview.expiresAt, 0, Date.now())) - const foot = tree.children[4] - assert.deepEqual(foot.children.map(button => button.children[0]), ['Clear', 'Join']) + // The render reads the clock itself, so freeze that clock for the whole case: one instant drives + // both the rendered line and the expectation. A second reading can land on the far side of a + // second boundary and expect one second less than the card drew (#1839). + const realNow = Date.now + const frozen = realNow() + Date.now = () => frozen + try { + const tree = RoomInvitePreview({ h, preview, onJoin() {}, onClear() {} }) + assert.equal(tree.props['data-flybot'], 'face-link-preview') + assert.equal(tree.children[0].children.join(''), 'Saturday room') + assert.equal(tree.children[1].children.join(''), 'Lead: Chief') + assert.equal(tree.children[2].children.join(''), '3 / 8 in the room') + // The deadline line is the one countdown text, not a per-surface copy of the rule. + assert.equal(tree.children[3].children.join(''), inviteCountdownAt(preview.expiresAt, 0, frozen)) + const foot = tree.children[4] + assert.deepEqual(foot.children.map(button => button.children[0]), ['Clear', 'Join']) + } finally { + Date.now = realNow + } }) test('#876: `kind` only renames the hooks, so both surfaces draw this one tree', () => { diff --git a/patches/ds-bot/files/test/room-invite-preview.test.js b/patches/ds-bot/files/test/room-invite-preview.test.js index 73e99e9f6..860d10914 100644 --- a/patches/ds-bot/files/test/room-invite-preview.test.js +++ b/patches/ds-bot/files/test/room-invite-preview.test.js @@ -19,15 +19,25 @@ const byKind = (node, kind) => walk(node).filter(n => n.props?.['data-flybot'] = const preview = { name: 'Saturday room', lead: 'Chief', people: '3 / 8 in the room', expiresAt: '2099-01-01T00:00:00.000Z', clockOffsetMs: 0 } test('#876: the card names the room, its lead, its people and the deadline', () => { - const tree = RoomInvitePreview({ h, preview, onJoin() {}, onClear() {} }) - assert.equal(tree.props['data-flybot'], 'face-link-preview') - assert.equal(tree.children[0].children.join(''), 'Saturday room') - assert.equal(tree.children[1].children.join(''), 'Lead: Chief') - assert.equal(tree.children[2].children.join(''), '3 / 8 in the room') - // The deadline line is the one countdown text, not a per-surface copy of the rule. - assert.equal(tree.children[3].children.join(''), inviteCountdownAt(preview.expiresAt, 0, Date.now())) - const foot = tree.children[4] - assert.deepEqual(foot.children.map(button => button.children[0]), ['Clear', 'Join']) + // The render reads the clock itself, so freeze that clock for the whole case: one instant drives + // both the rendered line and the expectation. A second reading can land on the far side of a + // second boundary and expect one second less than the card drew (#1839). + const realNow = Date.now + const frozen = realNow() + Date.now = () => frozen + try { + const tree = RoomInvitePreview({ h, preview, onJoin() {}, onClear() {} }) + assert.equal(tree.props['data-flybot'], 'face-link-preview') + assert.equal(tree.children[0].children.join(''), 'Saturday room') + assert.equal(tree.children[1].children.join(''), 'Lead: Chief') + assert.equal(tree.children[2].children.join(''), '3 / 8 in the room') + // The deadline line is the one countdown text, not a per-surface copy of the rule. + assert.equal(tree.children[3].children.join(''), inviteCountdownAt(preview.expiresAt, 0, frozen)) + const foot = tree.children[4] + assert.deepEqual(foot.children.map(button => button.children[0]), ['Clear', 'Join']) + } finally { + Date.now = realNow + } }) test('#876: `kind` only renames the hooks, so both surfaces draw this one tree', () => {