Files
buzz/desktop/tests/e2e/message-performance.spec.ts
Wes b3a83e0334 test(desktop): pin thread page and anchor contracts
Exercise the fail-closed authoritative bounds parser directly and extend the
large-thread release smoke to preserve both bottom and later-page targets while
older pages prepend.

Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
Signed-off-by: Wes <wesbillman@users.noreply.github.com>
2026-08-13 12:36:47 -06:00

614 lines
21 KiB
TypeScript

import { expect, test, type Locator, type Page } from "@playwright/test";
import { installMockBridge } from "../helpers/bridge";
const GENERAL_CHANNEL_ID = "9a1657ac-f7aa-5db0-b632-d8bbeb6dfb50";
const RANDOM_CHANNEL_ID = "9dae0116-799b-5071-a0a8-fdd30a91a35d";
const DEEP_HISTORY_CHANNEL_ID = "feedf00d-0000-4000-8000-000000000007";
type MessageRequest = {
command: "get_channel_window" | "get_thread_replies";
endedAt: number | null;
payload: unknown;
requestId: number;
startedAt: number;
};
async function clearMessageRequests(page: Page) {
await page.evaluate(() => {
window.__BUZZ_E2E_MESSAGE_REQUEST_LOG__ = [];
});
}
async function readMessageRequests(
page: Page,
command: MessageRequest["command"],
): Promise<MessageRequest[]> {
return page.evaluate(
(targetCommand) =>
(window.__BUZZ_E2E_MESSAGE_REQUEST_LOG__ ?? []).filter(
(entry) => entry.command === targetCommand,
),
command,
);
}
async function openThread(root: Locator, page: Page) {
const timelineRoot = root.first();
await timelineRoot.hover();
await timelineRoot.getByRole("button", { name: "Reply" }).click();
const panel = page.getByTestId("message-thread-panel");
await expect(panel).toBeVisible();
return panel;
}
async function installChannelRevisitPaintTracking(page: Page) {
await page.evaluate(() => {
const tracking = {
cachedRowSeen: false,
cachedRowRemovedAfterPaint: false,
disconnect: () => {},
};
const inspect = () => {
const cachedRow = document.querySelector(
'[data-message-id^="mock-deep-history-"]',
);
if (cachedRow && !tracking.cachedRowSeen) {
tracking.cachedRowSeen = true;
}
if (tracking.cachedRowSeen && !cachedRow) {
tracking.cachedRowRemovedAfterPaint = true;
}
};
const observer = new MutationObserver(inspect);
observer.observe(document.body, { childList: true, subtree: true });
tracking.disconnect = () => observer.disconnect();
inspect();
(
window as typeof window & {
__MESSAGE_REVISIT_PAINT__?: typeof tracking;
}
).__MESSAGE_REVISIT_PAINT__ = tracking;
});
}
async function readChannelRevisitPaintTracking(page: Page) {
return page.evaluate(() => {
const tracking = (
window as typeof window & {
__MESSAGE_REVISIT_PAINT__?: {
cachedRowSeen: boolean;
cachedRowRemovedAfterPaint: boolean;
disconnect: () => void;
};
}
).__MESSAGE_REVISIT_PAINT__;
if (!tracking) throw new Error("revisit paint observer was not installed");
tracking.disconnect();
return tracking;
});
}
test("cold channel load keeps its skeleton; fresh and stale revisits keep cached rows painted", async ({
page,
}) => {
await installMockBridge(page, { initialChannelWindowDelayMs: 700 });
await page.goto("/");
await page.getByTestId("channel-general").click();
await expect(page.getByTestId("chat-title")).toHaveText("general");
await expect(
page.locator('[data-message-id="mock-general-welcome"]'),
).toBeVisible();
await clearMessageRequests(page);
await page.getByTestId("channel-deep-history").click();
await expect(page.getByTestId("chat-title")).toHaveText("deep-history");
// Negative guard: a genuinely cold query still holds the timeline skeleton.
await expect(page.getByTestId("message-timeline-loading")).toBeVisible();
await expect
.poll(() => readMessageRequests(page, "get_channel_window"))
.toHaveLength(1);
await expect(
page.locator('[data-message-id^="mock-deep-history-"]').first(),
).toBeVisible();
await expect(page.getByTestId("message-timeline-loading")).toHaveCount(0);
await page.getByTestId("channel-general").click();
await expect(page.getByTestId("chat-title")).toHaveText("general");
await clearMessageRequests(page);
await installChannelRevisitPaintTracking(page);
// A fresh warm revisit remains painted across the required
// post-subscription reconciliation.
await page.getByTestId("channel-deep-history").click();
await expect(
page.locator('[data-message-id^="mock-deep-history-"]').first(),
).toBeVisible();
await expect
.poll(() => readMessageRequests(page, "get_channel_window"))
.toHaveLength(1);
await expect
.poll(async () =>
(await readMessageRequests(page, "get_channel_window")).every(
(request) => request.endedAt !== null,
),
)
.toBe(true);
await expect(
page.locator('[data-message-id^="mock-deep-history-"]').first(),
).toBeVisible();
const freshRevisit = await readChannelRevisitPaintTracking(page);
expect(freshRevisit.cachedRowSeen).toBe(true);
expect(freshRevisit.cachedRowRemovedAfterPaint).toBe(false);
await page.getByTestId("channel-general").click();
await expect(page.getByTestId("chat-title")).toHaveText("general");
// Make the now-inactive message cache older than its five-minute staleTime
// while retaining the authoritative channel-window proof.
await page.evaluate((channelId) => {
const queryClient = window.__BUZZ_E2E_QUERY_CLIENT__ as unknown as {
getQueryData: (key: readonly unknown[]) => unknown;
removeQueries: (filters: { queryKey: readonly unknown[] }) => void;
setQueryData: (
key: readonly unknown[],
data: unknown,
options?: { updatedAt?: number },
) => void;
};
const key = ["channel-messages", channelId] as const;
const cachedMessages = queryClient.getQueryData(key);
queryClient.removeQueries({ queryKey: key });
queryClient.setQueryData(key, cachedMessages, {
updatedAt: Date.now() - 10 * 60 * 1_000,
});
}, DEEP_HISTORY_CHANNEL_ID);
await clearMessageRequests(page);
await installChannelRevisitPaintTracking(page);
const staleRevisitStartedAt = await page.evaluate(() => performance.now());
// A >5-minute stale revisit keeps cached rows painted while one delayed
// initial-window reconciliation runs behind it.
await page.getByTestId("channel-deep-history").click();
await expect(
page.locator('[data-message-id^="mock-deep-history-"]').first(),
).toBeVisible();
const staleRevisit = await readChannelRevisitPaintTracking(page);
expect(staleRevisit.cachedRowSeen).toBe(true);
expect(staleRevisit.cachedRowRemovedAfterPaint).toBe(false);
await expect
.poll(async () => {
const requests = await readMessageRequests(page, "get_channel_window");
return requests.some(
(request) =>
request.startedAt >= staleRevisitStartedAt &&
request.endedAt === null,
);
})
.toBe(true);
const staleRevalidation = (
await readMessageRequests(page, "get_channel_window")
).find(
(request) =>
request.startedAt >= staleRevisitStartedAt && request.endedAt === null,
);
expect(staleRevalidation).toBeDefined();
expect(staleRevalidation?.endedAt).toBeNull();
await expect
.poll(
async () =>
(await readMessageRequests(page, "get_channel_window")).find(
(request) => request.requestId === staleRevalidation?.requestId,
)?.endedAt,
)
.not.toBeNull();
});
test("thread cache paints on fresh reopen and while a stale cache revalidates", async ({
page,
}) => {
await installMockBridge(page, { threadRepliesDelayMs: 700 });
await page.goto("/");
await page.getByTestId("channel-general").click();
await expect(page.getByTestId("chat-title")).toHaveText("general");
const root = page.locator('[data-message-id="mock-general-welcome"]');
await expect(root).toBeVisible();
const reply = await page.evaluate((parentEventId) => {
const emit = window.__BUZZ_E2E_EMIT_MOCK_MESSAGE__;
if (!emit) throw new Error("mock message emitter is unavailable");
return emit({
channelName: "general",
content: "Cached thread reply",
parentEventId,
});
}, "mock-general-welcome");
let panel = await openThread(root, page);
await expect(panel.locator(`[data-message-id="${reply.id}"]`)).toContainText(
"Cached thread reply",
);
await expect
.poll(async () => {
const requests = await readMessageRequests(page, "get_thread_replies");
return requests.length === 1 && requests[0].endedAt !== null;
})
.toBe(true);
await panel.getByTestId("auxiliary-panel-close").click();
await expect(panel).toHaveCount(0);
// Every reopen paints the cache immediately and revalidates in background.
await clearMessageRequests(page);
panel = await openThread(root, page);
await expect(panel.locator(`[data-message-id="${reply.id}"]`)).toBeVisible();
await expect(panel.getByTestId("message-thread-replies-loading")).toHaveCount(
0,
);
await expect
.poll(() => readMessageRequests(page, "get_thread_replies"))
.toHaveLength(1);
const [freshRevalidation] = await readMessageRequests(
page,
"get_thread_replies",
);
expect(freshRevalidation.endedAt).toBeNull();
await expect
.poll(
async () =>
(await readMessageRequests(page, "get_thread_replies"))[0]?.endedAt,
)
.not.toBeNull();
// Negative guard: once genuinely stale, the same cached row remains painted
// while a delayed background reconciliation is in flight.
await panel.getByTestId("auxiliary-panel-close").click();
await expect(panel).toHaveCount(0);
await page.evaluate(
async ({ channelId, rootId }) => {
const queryClient = window.__BUZZ_E2E_QUERY_CLIENT__ as unknown as {
getQueryData: (key: readonly unknown[]) => unknown;
removeQueries: (filters: { queryKey: readonly unknown[] }) => void;
setQueryData: (
key: readonly unknown[],
data: unknown,
options?: { updatedAt?: number },
) => void;
};
const key = ["thread-replies", channelId, rootId] as const;
const cachedReplies = queryClient.getQueryData(key);
queryClient.removeQueries({ queryKey: key });
queryClient.setQueryData(key, cachedReplies, {
updatedAt: Date.now() - 10 * 60 * 1_000,
});
},
{ channelId: GENERAL_CHANNEL_ID, rootId: "mock-general-welcome" },
);
await clearMessageRequests(page);
await page.evaluate((replyId) => {
const tracking = {
cachedReplyFrameAt: null as number | null,
disconnect: () => {},
};
let framePending = false;
const inspect = () => {
if (tracking.cachedReplyFrameAt !== null || framePending) return;
if (!document.querySelector(`[data-message-id="${replyId}"]`)) return;
framePending = true;
requestAnimationFrame(() => {
tracking.cachedReplyFrameAt = performance.now();
framePending = false;
});
};
const observer = new MutationObserver(inspect);
observer.observe(document.body, { childList: true, subtree: true });
tracking.disconnect = () => observer.disconnect();
inspect();
(
window as typeof window & {
__THREAD_CACHE_PAINT__?: typeof tracking;
}
).__THREAD_CACHE_PAINT__ = tracking;
}, reply.id);
panel = await openThread(root, page);
await expect(panel.locator(`[data-message-id="${reply.id}"]`)).toBeVisible();
await expect
.poll(() =>
page.evaluate(
() =>
(
window as typeof window & {
__THREAD_CACHE_PAINT__?: { cachedReplyFrameAt: number | null };
}
).__THREAD_CACHE_PAINT__?.cachedReplyFrameAt ?? null,
),
)
.not.toBeNull();
const cachedPaintedAt = await page.evaluate(() => {
const tracking = (
window as typeof window & {
__THREAD_CACHE_PAINT__?: {
cachedReplyFrameAt: number | null;
disconnect: () => void;
};
}
).__THREAD_CACHE_PAINT__;
if (!tracking?.cachedReplyFrameAt) {
throw new Error("cached thread reply paint was not observed");
}
tracking.disconnect();
return tracking.cachedReplyFrameAt;
});
await expect(panel.getByTestId("message-thread-replies-loading")).toHaveCount(
0,
);
await expect
.poll(() => readMessageRequests(page, "get_thread_replies"))
.toHaveLength(1);
await expect
.poll(
async () =>
(await readMessageRequests(page, "get_thread_replies"))[0]?.endedAt,
)
.not.toBeNull();
const [revalidation] = await readMessageRequests(page, "get_thread_replies");
expect(revalidation.startedAt).toBeLessThanOrEqual(cachedPaintedAt);
expect(revalidation.endedAt ?? 0).toBeGreaterThanOrEqual(cachedPaintedAt);
await expect(panel.locator(`[data-message-id="${reply.id}"]`)).toBeVisible();
});
test("401-reply thread paints page 1, preserves a live arrival, and converges", async ({
page,
}) => {
await installMockBridge(page, {
holdThreadRepliesPage2: true,
threadRepliesDelayMs: 25,
});
await page.goto("/");
await page.getByTestId("channel-general").click();
await expect(page.getByTestId("chat-title")).toHaveText("general");
const seeded = await page.evaluate(() => {
const emit = window.__BUZZ_E2E_EMIT_MOCK_MESSAGE__;
if (!emit) throw new Error("mock message emitter is unavailable");
const root = emit({ channelName: "random", content: "Large thread root" });
const replyIds: string[] = [];
for (let index = 0; index < 401; index += 1) {
replyIds.push(
emit({
channelName: "random",
content: `Large thread reply ${index}`,
createdAt: 1_700_000_000 + index,
parentEventId: root.id,
}).id,
);
}
return { rootId: root.id, replyIds };
});
await page.getByTestId("channel-random").click();
await expect(page.getByTestId("chat-title")).toHaveText("random");
const root = page.locator(`[data-message-id="${seeded.rootId}"]`);
await expect(root).toBeVisible();
await clearMessageRequests(page);
const panel = await openThread(root, page);
// Page 2 is deliberately held. The newest page must already be useful; a
// skeleton or wait-for-convergence implementation cannot satisfy this.
await expect
.poll(() => readMessageRequests(page, "get_thread_replies"))
.toHaveLength(2);
const pageTwoPending = await readMessageRequests(page, "get_thread_replies");
expect(pageTwoPending[0].endedAt).not.toBeNull();
expect(pageTwoPending[1].endedAt).toBeNull();
expect((pageTwoPending[0].payload as { limit?: number }).limit).toBe(200);
expect(
(pageTwoPending[0].payload as { threadOrder?: string }).threadOrder,
).toBe("newest");
await expect(panel).toContainText("Large thread reply 400");
await expect(panel).not.toContainText("Large thread reply 0");
await expect(panel.getByTestId("message-thread-replies-loading")).toHaveCount(
0,
);
const threadBody = panel.getByTestId("message-thread-body");
await expect
.poll(() =>
threadBody.evaluate(
(element) =>
element.scrollHeight - element.clientHeight - element.scrollTop,
),
)
.toBeLessThanOrEqual(2);
const liveReply = await page.evaluate((parentEventId) => {
const emit = window.__BUZZ_E2E_EMIT_MOCK_MESSAGE__;
if (!emit) throw new Error("mock message emitter is unavailable");
return emit({
channelName: "random",
content: "Live reply during pagination",
createdAt: 1_700_000_500,
parentEventId,
});
}, seeded.rootId);
await expect(
panel.locator(`[data-message-id="${liveReply.id}"]`),
).toBeVisible();
await page.evaluate(() => {
const release = window.__BUZZ_E2E_RELEASE_THREAD_PAGE_2__;
if (!release) throw new Error("thread page 2 was not held");
release();
});
await expect
.poll(async () => {
const requests = await readMessageRequests(page, "get_thread_replies");
return (
requests.length === 3 &&
requests.every((request) => request.endedAt !== null)
);
})
.toBe(true);
const convergence = await page.evaluate(
({ channelId, rootId, expectedIds, liveId }) => {
const queryClient = window.__BUZZ_E2E_QUERY_CLIENT__ as unknown as {
getQueryData: (key: readonly unknown[]) =>
| Array<{
created_at: number;
id: string;
}>
| undefined;
};
const replies =
queryClient.getQueryData(["thread-replies", channelId, rootId]) ?? [];
const ids = replies.map((reply) => reply.id);
return {
chronological: replies.every(
(reply, index) =>
index === 0 ||
replies[index - 1].created_at < reply.created_at ||
(replies[index - 1].created_at === reply.created_at &&
replies[index - 1].id.localeCompare(reply.id) <= 0),
),
exactSeedSet: expectedIds.every((id) => ids.includes(id)),
liveCount: ids.filter((id) => id === liveId).length,
total: ids.length,
unique: new Set(ids).size,
};
},
{
channelId: RANDOM_CHANNEL_ID,
rootId: seeded.rootId,
expectedIds: seeded.replyIds,
liveId: liveReply.id,
},
);
expect(convergence).toEqual({
chronological: true,
exactSeedSet: true,
liveCount: 1,
total: 402,
unique: 402,
});
await expect
.poll(() =>
threadBody.evaluate(
(element) =>
element.scrollHeight - element.clientHeight - element.scrollTop,
),
)
.toBeLessThanOrEqual(2);
});
test("later-page thread target settles and survives subsequent prepends", async ({
page,
}) => {
await installMockBridge(page, {
holdThreadRepliesPage2: true,
holdThreadRepliesPage3: true,
threadRepliesDelayMs: 25,
});
await page.goto("/");
await page.getByTestId("channel-general").click();
await expect(page.getByTestId("chat-title")).toHaveText("general");
const seeded = await page.evaluate(() => {
const emit = window.__BUZZ_E2E_EMIT_MOCK_MESSAGE__;
if (!emit) throw new Error("mock message emitter is unavailable");
const root = emit({ channelName: "random", content: "Target thread root" });
const replies = Array.from({ length: 401 }, (_, index) =>
emit({
channelName: "random",
content: `Target thread reply ${index}`,
createdAt: 1_700_100_000 + index,
parentEventId: root.id,
}),
);
return { rootId: root.id, targetId: replies[150].id };
});
await page.getByTestId("channel-random").click();
await expect(page.getByTestId("chat-title")).toHaveText("random");
const root = page.locator(`[data-message-id="${seeded.rootId}"]`);
await expect(root).toBeVisible();
const panel = await openThread(root, page);
await expect
.poll(() => readMessageRequests(page, "get_thread_replies"))
.toHaveLength(2);
await expect(
panel.locator(`[data-message-id="${seeded.targetId}"]`),
).toHaveCount(0);
await page.evaluate(() => window.__BUZZ_E2E_RELEASE_THREAD_PAGE_2__?.());
await expect
.poll(async () => {
const requests = await readMessageRequests(page, "get_thread_replies");
return requests.length === 3 && requests[2]?.endedAt === null;
})
.toBe(true);
// Aim at the old reply only after page 2 makes it available. This exercises
// the same route target state used by deep links while page 3 is still held.
await page.evaluate(({ rootId, targetId }) => {
const hash = window.location.hash.replace(/^#/, "") || "/";
const [path, query = ""] = hash.split("?");
const params = new URLSearchParams(query);
params.set("messageId", targetId);
params.set("thread", rootId);
window.history.pushState(
{},
"",
`${window.location.pathname}#${path}?${params}`,
);
window.dispatchEvent(new HashChangeEvent("hashchange"));
window.dispatchEvent(new PopStateEvent("popstate"));
}, seeded);
const target = panel.locator(`[data-message-id="${seeded.targetId}"]`);
const body = panel.getByTestId("message-thread-body");
await expect
.poll(async () => {
if (!(await target.count())) return false;
return body.evaluate((element, targetId) => {
const row = element.querySelector(
`[data-message-id="${CSS.escape(targetId)}"]`,
);
if (!row) return false;
const bodyRect = element.getBoundingClientRect();
const rowRect = row.getBoundingClientRect();
return rowRect.top >= bodyRect.top && rowRect.bottom <= bodyRect.bottom;
}, seeded.targetId);
})
.toBe(true);
// Page 3 is now held after page 2 exposed and settled the target. Releasing
// it prepends more history; the target must remain the visible anchor.
await expect
.poll(async () => {
const requests = await readMessageRequests(page, "get_thread_replies");
return requests.length === 3 && requests[2]?.endedAt === null;
})
.toBe(true);
await page.evaluate(() => window.__BUZZ_E2E_RELEASE_THREAD_PAGE_3__?.());
await expect
.poll(async () => {
const requests = await readMessageRequests(page, "get_thread_replies");
return (
requests.length === 3 &&
requests.every((request) => request.endedAt !== null)
);
})
.toBe(true);
await expect
.poll(() =>
body.evaluate((element, targetId) => {
const row = element.querySelector(
`[data-message-id="${CSS.escape(targetId)}"]`,
);
if (!row) return false;
const bodyRect = element.getBoundingClientRect();
const rowRect = row.getBoundingClientRect();
return rowRect.top >= bodyRect.top && rowRect.bottom <= bodyRect.bottom;
}, seeded.targetId),
)
.toBe(true);
});