fix(portals): nineteenth review round and the web E2E line limit

- A queued Xtream episode launch checks the page owner before it closes
  the running player, so a viewer who left the series keeps playback.
- A Play or episode choice made while a forced launch settles starts
  once it settled instead of being dropped; only the latest choice runs.
- Both series menus disable the bulk watched actions while a start has
  not published its session yet.
- The series-menu E2E helpers are shared, which brings the Stalker web
  spec back under the line limit; the inline episode state derivation
  moves into ui/playback.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
4grayandClaude Fable 5.1 committed 2026-10-03 14:21:46 +02:00
1 parent 956e7c36b7
commit 5b2fbe296b
11 files changed
+133 -64

No files matched your search

+21
View File
@@ -286,3 +286,24 @@ export async function expectSeriesSurfacesInBothThemes(
.click();
}
}
/** Opens the series "…" menu (if closed) and returns the row with that test id. */
export async function seriesMenuRow(page: Page, rowTestId: string) {
const row = page.locator(`[data-test-id="${rowTestId}"]`);
if (!(await row.isVisible().catch(() => false))) {
await page.locator('[data-testid="series-more-menu"]').click();
}
await expect(row).toBeVisible({ timeout: 15_000 });
return row;
}
/** Closes an open series menu without choosing a row. */
export async function closeSeriesMenu(page: Page): Promise<void> {
// The panel, not the backdrop: a backdrop locator can match a second
// overlay and then fail the strict check, leaving the menu open.
const panel = page.locator('[data-test-id="vod-more-menu"]');
if (await panel.isVisible().catch(() => false)) {
await page.keyboard.press('Escape');
}
await expect(panel).toBeHidden({ timeout: 10_000 });
}
+6 -18
View File
@@ -1,5 +1,10 @@
import { type APIRequestContext, type Page } from '@playwright/test';
import { expectSeriesSurfacesInBothThemes, setInputValue } from './e2e-helpers';
import {
closeSeriesMenu,
expectSeriesSurfacesInBothThemes,
seriesMenuRow,
setInputValue,
} from './e2e-helpers';
import {
verifyStalkerCategorySearch,
verifyStalkerPlaybackCategoryReturn,
@@ -1136,23 +1141,6 @@ test('@stalker season watched toggle — embedded series marks and clears every
await closeSeriesMenu(page);
});
/** Opens the series "…" menu (if closed) and returns the row with that test id. */
async function seriesMenuRow(page: Page, rowTestId: string) {
const row = page.locator(`[data-test-id="${rowTestId}"]`);
if (!(await row.isVisible().catch(() => false))) {
await page.locator('[data-testid="series-more-menu"]').click();
}
await expect(row).toBeVisible({ timeout: 15_000 });
return row;
}
async function closeSeriesMenu(page: Page): Promise<void> {
if (await page.locator('.cdk-overlay-backdrop').isVisible().catch(() => false)) {
await page.keyboard.press('Escape');
}
}
test('@stalker series watched toggle — embedded series marks and clears from the header menu', async ({
page,
request,
+2 -20
View File
@@ -1,8 +1,10 @@
import type { APIRequestContext, Page } from '@playwright/test';
import { expect, test } from './fixtures';
import {
closeSeriesMenu,
pressTab,
rasterizedBorderContrast,
seriesMenuRow,
setInputValue,
surfaceContrast,
waitForScrollIdle,
@@ -802,26 +804,6 @@ test('@xtream season cover — shows the provider season cover and follows the s
// unwatch-all once the whole series is watched, and survives a reload.
// ---------------------------------------------------------------------------
/** Opens the series "…" menu (if closed) and returns the row with that test id. */
async function seriesMenuRow(page: Page, rowTestId: string) {
const row = page.locator(`[data-test-id="${rowTestId}"]`);
if (!(await row.isVisible().catch(() => false))) {
await page.locator('[data-testid="series-more-menu"]').click();
}
await expect(row).toBeVisible({ timeout: 15_000 });
return row;
}
async function closeSeriesMenu(page: Page): Promise<void> {
// The panel, not the backdrop: a backdrop locator can match a second
// overlay and then fail the strict check, leaving the menu open.
const panel = page.locator('[data-test-id="vod-more-menu"]');
if (await panel.isVisible().catch(() => false)) {
await page.keyboard.press('Escape');
}
await expect(panel).toBeHidden({ timeout: 10_000 });
}
test('@xtream series watched toggle — marks every season from the header menu, survives reload, and clears again', async ({
page,
request,