Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions tests/browser/local-agent-activity.spec.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,11 @@ test("opens an active message's empty thread without losing its activity preview
await expect(
thread.getByRole("textbox", { name: "Reply to thread", exact: true }),
).toBeFocused();
// The composer takes focus before the rows finish positioning, and the
// rows stay inert until then, so a press on one would land in the composer.
await expect(
thread.getByRole("region", { name: "Thread messages", exact: true }),
).toHaveAttribute("aria-busy", "false");
await activity.press("Enter");
await expect(
page.getByRole("tab", { name: "Activity", selected: true }),
Expand Down Expand Up @@ -607,6 +612,11 @@ for (const focusOn of ["bubble", "stop", "composer"]) {
name: /^View agent activity:/,
});
await expect(bubble).toBeVisible();
// Rows stay inert while the thread positions its first page, so focus()
// on a control inside one is a no-op until the region is no longer busy.
await expect(
thread.getByRole("region", { name: "Thread messages", exact: true }),
).toHaveAttribute("aria-busy", "false");
// Finish entrance and hold only the exit so the intermediate state is testable.
await animationsSettled(message.locator("section"), { subtree: true });
// Hold the actual exit animation before its first frame, rather than racing
Expand Down
32 changes: 28 additions & 4 deletions tests/browser/message-navigation.spec.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -847,6 +847,11 @@ test.describe("fractional row geometry", () => {
readTest(
"exact reveal uses ordinary dwell rather than marking read at open",
async ({ page, app }) => {
// The revealed target starts its 300 ms dwell as navigation completes, so
// only a paused clock keeps "nothing read at open" from racing that dwell.
// Pause at a fixed offset, not the page's already-ticking clock.
const base = Date.now();
await page.clock.install({ time: base });

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 [P2] Hold startup reading focus before opening the app

page.clock.install() leaves time running until pauseAt below. During open(page, app), Messages initially autofocuses the channel composer, which qualifies for the normal 300 ms reading dwell. If a slow runner leaves that focus in place before the search palette takes over, Alpha can already save a frontier. Pausing afterward cannot undo it, so the empty-journal check and later target-only equality fail despite correct behavior. unread.spec.mjs:130–133 documents and guards this same startup ordering. Reuse holdReadingFocus(page) before open() and releaseReadingFocus(page) after pauseAt, as the neighboring retained-thread test does. This is a source-derived ordering defect; it was not reproduced in this review.

await open(page, app);
await page.evaluate(() =>
window.fixtureRelay.snapshot().session.unread.ensure(),
Expand All @@ -872,23 +877,42 @@ readTest(
await route.continue().catch(() => {});
});
const before = app.report.readPublications.length;
const frontiers = async () =>
(await readJournal(page))?.state.frontiers ?? {};
await page.clock.pauseAt(base + 20_000);
try {
expect(await openTarget(page, target(app))).toEqual({ status: "opened" });
const opening = openTarget(page, target(app));
// Exact reveal requires real focus for two animation frames. Advance one
// frame at a time until its owner completes, without earning reading dwell.
await expect
.poll(async () => {
await page.clock.runFor(16);
return status(page);
})
.toBe("opened");
expect(await opening).toEqual({ status: "opened" });
await seen;
// Opening marks nothing read locally, so nothing can have been published.
expect(await frontiers()).toEqual({});
expect(app.report.readPublications.length).toBe(before);
await expect(
thread(page).locator(`[data-message-id="${app.exact.target.id}"]`),
).toBeFocused();
// Dwell then marks the target, the only row shown while its context is
// held, read locally before the publish debounce even starts; the
// debounced, signed publication itself is covered by unread.spec.mjs.
await page.clock.runFor(300);
await expect.poll(frontiers).toEqual({
[`msg:${app.exact.target.id}`]: app.exact.target.created_at,
});
} finally {
release();
}
await page.clock.resume();
await expect(thread(page).locator("[data-message-id]")).toHaveCount(81);
await expect(
thread(page).locator(`[data-message-id="${app.exact.target.id}"]`),
).toBeFocused();
await expect
.poll(() => app.report.readPublications.length)
.toBeGreaterThan(before);
},
);

Expand Down
Loading