diff --git a/tests/browser/channel-opening.spec.mjs b/tests/browser/channel-opening.spec.mjs index 8d3a3a3d..add4ed87 100644 --- a/tests/browser/channel-opening.spec.mjs +++ b/tests/browser/channel-opening.spec.mjs @@ -33,8 +33,27 @@ test("cold opening bypasses held DM labels; warm switching paints within 100ms w filter.authors?.some((id) => app.participants.includes(id)), ); app.relay.holdProfiles(app.participants); + app.relay.holdEose("alpha"); + app.relay.holdEose("beta"); + // A visible pre-establishment head is not a warm, verified cache. A signed + // missed message proves the catch-up reached the UI, not merely the broker. + const establish = async (channel) => { + expect(heads(app, channel)).toHaveLength(1); + const missed = app.append( + "primary", + channel, + "Startup catch-up marker", + false, + ); + app.relay.releaseEose(channel); + await expect( + page.locator(`[data-message-id="${missed.id}"]`), + ).toBeVisible(); + expect(heads(app, channel)).toHaveLength(2); + }; try { await open(page, app); + await establish("alpha"); await expect.poll(() => labelReads().length).toBe(1); expect(labelReads()[0].filter.authors).toHaveLength(500); // The actual label hook has >1,000 missing participants. Keep its profile @@ -62,6 +81,7 @@ test("cold opening bypasses held DM labels; warm switching paints within 100ms w expect(heads(app, "beta").length).toBeGreaterThan(0); expect(app.report.profileHolds.some((held) => held.pending)).toBe(true); expect(app.report.profileHolds.some((held) => held.aborted)).toBe(false); + await establish("beta"); const before = submittedHeads.length; // Browser-clock click → first visible row → paint, excluding Playwright IPC. for (const name of ["Alpha", "Beta", "Alpha", "Beta"]) { @@ -138,6 +158,8 @@ test("cold opening bypasses held DM labels; warm switching paints within 100ms w expect(app.report.profileHolds.some((held) => held.pending)).toBe(true); expect(app.report.profileHolds.some((held) => held.aborted)).toBe(false); } finally { + app.relay.releaseEose("alpha"); + app.relay.releaseEose("beta"); app.relay.releaseProfiles(); } }); diff --git a/tests/browser/sidebar-unread.spec.mjs b/tests/browser/sidebar-unread.spec.mjs index 433c13bb..1cdd0b5b 100644 --- a/tests/browser/sidebar-unread.spec.mjs +++ b/tests/browser/sidebar-unread.spec.mjs @@ -92,7 +92,25 @@ test("edge pills follow scroll and reveal the nearest unread without selection o page, app, }, info) => { - await open(page, app); + // Visible rows can precede the post-establishment catch-up. Force that late + // ordering, then account for its head read before measuring cue-triggered work. + app.relay.holdEose("alpha"); + try { + await open(page, app); + const alphaHeads = () => + heads(app).filter( + ({ community, filter }) => + community === "primary" && + filter["#h"][0] === "alpha" && + filter.top_level === true && + filter.until === undefined, + ); + expect(alphaHeads()).toHaveLength(1); + app.relay.releaseEose("alpha"); + await expect.poll(() => alphaHeads().length).toBe(2); + } finally { + app.relay.releaseEose("alpha"); + } await expect(row(page, "dm-090").getByRole("img")).toHaveCount(1); await expect(cue(page, "below")).toBeVisible(); await expect(cue(page, "above")).toHaveCount(0);