test(e2e): sweep the EPG ribbon to see every programme

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
4grayandClaude Opus 5.5 committed 2026-10-04 11:37:48 +02:00
1 parent 67e0133f12
commit 97d9847aa5
1 file changed
+41 -9
@@ -285,7 +285,7 @@ for (const timeZone of ['UTC', 'Europe/Berlin'] as const) {
// The timeline lays out the full multi-day window sorted by start
// time (no per-day filtering — it scrolls) and renders the blocks
// near the visible range: an ordered run around the current
// programme, and the last programme once scrolled to the end.
// programme, and every programme once the ribbon is swept.
const allTitles = [...fixture.fullEpg]
.sort((a, b) => a.startTimestamp - b.startTimestamp)
.map((listing) => listing.title);
@@ -298,14 +298,9 @@ for (const timeZone of ['UTC', 'Europe/Berlin'] as const) {
);
})
.toBe(true);
await app.mainWindow
.locator('app-epg-timeline .epg-timeline__ribbon')
.evaluate((ribbon) => {
ribbon.scrollLeft = ribbon.scrollWidth;
});
await expect
.poll(() => timelineBlockTitles(app.mainWindow))
.toContain(allTitles[allTitles.length - 1]);
expect(await sweepTimelineTitles(app.mainWindow)).toEqual(
new Set(allTitles)
);
await app.mainWindow
.locator('app-epg-timeline .epg-timeline__jump')
.click();
@@ -886,6 +881,43 @@ async function timelineBlockTitles(
.then((titles) => titles.map((title) => title.trim()).filter(Boolean));
}
/**
* Scroll the ribbon from its start to its end in half-viewport steps and
* collect every programme title rendered on the way.
*/
async function sweepTimelineTitles(
page: Parameters<typeof channelItemByTitle>[0]
): Promise<Set<string>> {
const ribbon = page.locator('app-epg-timeline .epg-timeline__ribbon');
const seen = new Set<string>();
let left = 0;
for (;;) {
const { scrollLeft, maxLeft, step } = await ribbon.evaluate(
(element, target) => {
element.scrollLeft = target;
return {
scrollLeft: element.scrollLeft,
maxLeft: element.scrollWidth - element.clientWidth,
step: Math.max(1, element.clientWidth / 2),
};
},
left
);
// Two frames: the scroll's measure, then the re-rendered blocks.
await page.evaluate(
() =>
new Promise((resolve) =>
requestAnimationFrame(() => requestAnimationFrame(resolve))
)
);
(await timelineBlockTitles(page)).forEach((title) => seen.add(title));
if (scrollLeft >= maxLeft) {
return seen;
}
left = scrollLeft + step;
}
}
/** Whether `run` is a non-empty, in-order slice of `all`. */
function isContiguousRun(run: string[], all: string[]): boolean {
if (run.length === 0) {