Files
siftlode/frontend/e2e/player.spec.ts
T
peter 00886d92fe fix(player): the YouTube player keeps its size on a short window and when maximised
Two layout bugs since the player grew a side panel, both reported from prod.

Maximised (F) on a wide window, the video was a thin line across the screen: the side-panel layout
aligns the stage `self-start`, and Chrome applies a flex item's self-alignment to an absolutely
positioned box too, so the fixed stage took its content's height — 0 px. The maximised stage now
stretches between its insets whatever the layout asks of it.

On a short window under 1280 px (a laptop at 100%, a 1080p screen at 175%) only the side strips were
left: the card narrows so the chrome under the video fits, but that chrome grows as the card narrows
(its text wraps), and the loop ran the card down to 2 px. The video now never goes narrower than
36rem to make room; past that the card scrolls.

Measured before/after (localdev, real YouTube iframe): 1097×520 open 0×0 → 610×343; 1920×911 maximised
1920×0 → 1920×911; 1440×760 and 1366×650 maximised fill the window; leaving maximise restores the card.
Held by an e2e test at both sizes (red on the old code: 0 px wide, and 911 ≠ 0), green on Chrome,
WebKit and iPad.
2026-10-05 23:40:13 +02:00

67 lines
2.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { test, expect, type Page } from "@playwright/test";
import { openApp, openFirstVideo } from "./helpers";
// Locks the in-app player's open/close contract. The player is a portaled overlay whose
// inconsistent mounting caused a regression — exactly the kind of thing the refactor's
// overlay-root convention must not break. We assert the modal shell, not YouTube playback.
test.describe("player modal", () => {
test.beforeEach(async ({ page }) => {
// Hermetic: don't actually load the YouTube iframe/player.
await page.route(/youtube\.com/, (route) => route.abort());
});
test("opens from a card and closes via the close button", async ({ page }) => {
await openApp(page);
await openFirstVideo(page);
await page.getByTestId("player-close").click();
await expect(page.getByTestId("player-modal")).toHaveCount(0);
});
test("closes on Escape", async ({ page }) => {
await openApp(page);
await openFirstVideo(page);
await page.keyboard.press("Escape");
await expect(page.getByTestId("player-modal")).toHaveCount(0);
});
});
// The video's size, in the windows the owner meets: layout bugs no DOM test can see. Each viewport
// is a real one — 1920×911 is a 1080p screen at 100% (the side-panel layout), 1097×520 the same
// screen at 175% or a small laptop at 100% (the stacked layout, on a short window).
test.describe("player modal sizing", () => {
test.beforeEach(async ({ page }) => {
await page.route(/youtube\.com/, (route) => route.abort());
});
const box = async (page: Page, selector: string) =>
(await page.locator(selector).first().boundingBox())!;
for (const [w, h] of [
[1920, 911],
[1097, 520],
] as const) {
test(`the video keeps a real size at ${w}×${h}, and maximised fills the window`, async ({
page,
}) => {
await page.setViewportSize({ width: w, height: h });
await openApp(page);
await openFirstVideo(page);
// A short window narrows the card so the chrome under the video fits — but the chrome grows
// as the card narrows (its text wraps), and that loop once ran the card down to 2 px.
const stage = await box(page, ".player-stage");
expect(stage.width).toBeGreaterThanOrEqual(Math.min(w * 0.5, 560));
expect(stage.height).toBeGreaterThan(stage.width * 0.5);
// Maximise (F): the stage is fixed to the viewport. In the side-panel layout its `self-start`
// once aligned the fixed box too, and it shrank to its content's height — 0 px, a thin line.
await page.getByTestId("player-modal").press("f");
await expect(page.locator(".player-stage--max")).toHaveCount(1);
const max = await box(page, ".player-stage--max");
expect([Math.round(max.width), Math.round(max.height)]).toEqual([w, h]);
});
}
});