Files
iptvnator/apps/web-e2e/src/stalker-open-in-playlist.fixture.ts
T
4gray f80a21beef feat(collections): open a Stalker live favorite inside its portal (#1639)
Live channels in Favorites and Recently viewed now offer "Open in <playlist>"
for Stalker portals, the counterpart of the VOD "View in portal" action. The
chip in the programme panel and the channel's context menu jump to the channel
inside its portal's Live TV, with its genre selected and the channel playing.
Radio stays hidden: it is a separate legacy-paged section with no
open-on-arrival contract.

The handoff refuses to guess. The store records which genre the rendered ITV
list belongs to, so the deferred play waits for a list that can actually serve
the channel instead of inferring it from array identity. An id claimed by one
channel as its `id` and another as its `stream_id` is ambiguous, so neither is
played and the user still lands in the right genre. The handoff is abandoned
when the user changes genre, search, portal or section first.

Two pre-existing defects surfaced during review and are fixed here:

- A blank provider id shadowed a valid one. `a ?? b` keeps an empty string, so
  a channel with a blank `stream_id` was stored with no identity at all and
  could not be selected, played or found again. Six writers had that shape and
  three private copies of the skip-the-blank rule; all now go through one
  `firstNonBlankStalkerId` helper.
- Route-session readiness could publish before the store held the portal's
  row. The constructor starts one sync and the first navigation starts another,
  and the second skipped the bootstrap because the playlist id was claimed
  before the awaits that install it. Arrivals are now serialized, the id is
  claimed only after the store write resolves, and only the newest sync
  publishes readiness.

Both fixes carry regression tests that fail on the old behavior.
2026-09-20 22:55:29 +02:00

71 lines
2.9 KiB
TypeScript

import { expect, Page } from '@playwright/test';
/**
* Favorites → "Open in <portal>" → the same channel selected and playing in
* the portal's ITV section (the live counterpart of "View in portal").
*
* Runs against the PWA, where the collection tab renders no EPG panel and
* therefore no toolbar chip, so the row context menu is the entry point. The
* mock portal answers `get_all_channels`, so the layout locates the channel
* through the full ITV list and lands in its genre.
*
* Expects the portal to be imported already (`addStalkerPortal`).
*/
export async function verifyStalkerOpenInPlaylist(
page: Page,
portalName: string
): Promise<void> {
await page.getByRole('link', { name: /live|itv/i }).click();
await page.waitForURL(/stalker.*itv/);
const portalUrl = new URL(page.url());
const itvPath = portalUrl.pathname;
// Favorite the first channel of the second category (the first row is
// "All"), remembering its name for the round-trip assertion.
const categories = page.locator('.category-item');
await expect(categories.nth(1)).toBeVisible({ timeout: 10_000 });
await categories.nth(1).click();
const channels = page.locator('[data-test-id="channel-item"]');
await expect(channels.first()).toBeVisible({ timeout: 20_000 });
const channelName = (
await channels.first().locator('.channel-name').innerText()
).trim();
expect(channelName).not.toBe('');
await channels.first().locator('.favorite-button').click();
await expect(
channels.first().locator('.favorite-button mat-icon')
).toHaveText('star', { timeout: 10_000 });
// Global favorites, all playlists: the row offers the jump.
await page.goto('/workspace/global-favorites');
const allPlaylists = page
.locator('.scope-toggle')
.getByRole('radio', { name: 'All playlists' });
if (await allPlaylists.count()) {
await allPlaylists.click();
}
const favoriteRow = page
.locator('app-global-favorites-list [data-test-id="channel-item"]')
.filter({ hasText: channelName })
.first();
await expect(favoriteRow).toBeVisible({ timeout: 20_000 });
await favoriteRow.click({ button: 'right' });
const openInPlaylist = page.locator(
'[data-testid="channel-open-in-playlist"]'
);
await expect(openInPlaylist).toBeVisible({ timeout: 10_000 });
await expect(openInPlaylist).toContainText(portalName);
await openInPlaylist.click();
// Lands inside the portal's ITV section with that channel selected and
// playing, in its own genre.
await page.waitForURL((url) => url.pathname === itvPath, {
timeout: 20_000,
});
const activeRow = page.locator('[data-test-id="channel-item"].active');
await expect(activeRow).toContainText(channelName, { timeout: 30_000 });
await expect(page.locator('app-web-player-view')).toBeVisible({
timeout: 20_000,
});
}