test(notifications): the center re-says its rows after a switch, a repeat coalesces by content; e2e helper fails with a cause

The center's rows (title, message, refusal, live button) were never rendered across a language
switch, and nothing held the structural compare that coalescing now needs. The channel-link helper
swallowed whatever ended its poll into a bare false: a link that goes away mid-look now counts as
not hit, and no hittable link fails with the poll's own message.
This commit is contained in:
2026-10-30 00:17:46 +01:00
parent 74f58ea001
commit 046dde44a4
5 changed files with 169 additions and 20 deletions
+1 -1
View File
@@ -196,7 +196,7 @@ test.describe("a bookmark reopens the view it was taken of", () => {
test("a channel page's own tab, over the page it was opened from", async ({ page, browser }) => {
await openApp(page, "/?page=feed");
await expect(page.getByTestId("video-card").first()).toBeVisible();
expect(await clickVisibleChannelLink(page)).toBe(true);
await clickVisibleChannelLink(page);
const title = await page.getByTestId("channel-title").textContent();
await tab(page, "About").click();
const fresh = await bookmarked(browser, page);
+1 -1
View File
@@ -48,7 +48,7 @@ test.describe("channel view", () => {
// `.first()` here would grab an overscan card ABOVE the viewport, and Playwright's scroll-into-
// view would move the feed before navigating, so Back would restore that moved position, not
// the one under test. (This is what made the test fail while the app was in fact correct.)
expect(await clickVisibleChannelLink(page)).toBe(true);
await clickVisibleChannelLink(page);
await expect(page.getByTestId("channel-title")).toBeVisible();
await page.getByTestId("channel-back").click();
await expect(page.getByTestId("video-card").first()).toBeVisible();
+24 -18
View File
@@ -33,35 +33,41 @@ export async function gotoPage(page: Page, id: string): Promise<void> {
* that then asserts on where the feed was. Being inside the viewport is not enough either: a card
* can sit under the page chrome above the scroller (the update banner, the toolbar band), and the
* covered click makes Playwright scroll and retry — the same move (webkit failed 3 in 24).
* So the link must be the element hit at its own centre. Returns false if none found.
* Looked for again until one is, not once: a fresh load is still settling when the first card shows
* (the links move down ~55px), and webkit can answer the hit test at a link's new centre with the
* card under it — every link missed in 1 of 30 runs.
* So the link must be the element hit at its own centre; none within the wait fails the test, with
* the poll's own message. Looked for again until one is, not once: a fresh load is still settling
* when the first card shows (the links move down ~55px), and webkit can answer the hit test at a
* link's new centre with the card under it — every link missed in 1 of 30 runs. A link that goes
* away mid-look (its evaluate waits for it, or throws) counts as not hit, so the next look runs.
*/
export async function clickVisibleChannelLink(page: Page): Promise<boolean> {
export async function clickVisibleChannelLink(page: Page): Promise<void> {
const links = page.getByTestId("video-card-channel").filter({ visible: true });
let index = -1;
const findHittable = async () => {
const count = await links.count();
for (let i = 0; i < count; i++) {
const hittable = await links.nth(i).evaluate((el) => {
const r = el.getBoundingClientRect();
const hit = document.elementFromPoint(r.left + r.width / 2, r.top + r.height / 2);
return hit !== null && el.contains(hit);
});
const hittable = await links
.nth(i)
.evaluate(
(el) => {
const r = el.getBoundingClientRect();
const hit = document.elementFromPoint(r.left + r.width / 2, r.top + r.height / 2);
return hit !== null && el.contains(hit);
},
undefined,
{ timeout: 1000 },
)
.catch(() => false);
if (hittable) return i;
}
return -1;
};
try {
await expect
.poll(async () => (index = await findHittable()), { timeout: 5000 })
.toBeGreaterThan(-1);
} catch {
return false;
}
await expect
.poll(async () => (index = await findHittable()), {
message: "a channel link hit at its own centre",
timeout: 5000,
})
.toBeGreaterThan(-1);
await links.nth(index).click();
return true;
}
/**