mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-10 18:36:15 -08:00
feat(playback): show an "Up next" card in the last minutes of an episode
Phase 4 of the "Hybrid" player controls redesign. Series hosts hand the episode after the playing one to the shared controls (`upNext` on PortalInlinePlayerComponent → WebPlayerViewComponent → the four engine hosts → app-player-controls). ControlsUpNext shows the glass card once at most eight minutes remain on a seekable episode with a next-episode capable engine; it yields to the settings panel and to hidden controls. A click emits nextEpisodeRequested, so the switch keeps fullscreen like the transport button. Compact docks get a smaller card. Also dedupes the web-player-view spec's local engine stubs in favour of the shared stub file its sibling specs already use. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
e2a846b430
commit
d0c1d49aee
51 files changed
+856
-96
No files matched your search
@@ -123,6 +123,54 @@ test.describe('@xtream inline series fullscreen', () => {
|
||||
expect(await fullscreenOwner()).not.toBeNull();
|
||||
});
|
||||
|
||||
test('shows the Up next card near the end and plays the next episode from it', async ({
|
||||
page,
|
||||
request,
|
||||
}) => {
|
||||
await routeEpisodeClip(page);
|
||||
await rewriteSeriesEpisodesToMp4(page);
|
||||
await selectWebPlayer(page, 'HTML5 video player');
|
||||
const { playerView, video } = await playFirstSeriesEpisode(
|
||||
page,
|
||||
request
|
||||
);
|
||||
await expect(playerView.locator('app-html-video-player')).toBeVisible({
|
||||
timeout: 15_000,
|
||||
});
|
||||
await expect
|
||||
.poll(() =>
|
||||
video.evaluate((el) => (el as HTMLVideoElement).readyState)
|
||||
)
|
||||
.toBeGreaterThanOrEqual(1);
|
||||
|
||||
// The 30 s fixture clip is inside the card's threshold from its
|
||||
// first frame, so the card is up as soon as the duration is known.
|
||||
const card = playerView.locator(
|
||||
'[data-test-id="player-controls-up-next"]'
|
||||
);
|
||||
await expect(card).toBeVisible();
|
||||
await expect(card).toContainText('Up next');
|
||||
await expect(card).toHaveAttribute('aria-label', /S01E02/);
|
||||
|
||||
// The settings panel takes the same corner: the card yields to it.
|
||||
await playerView.hover();
|
||||
await playerView
|
||||
.locator('[data-test-id="player-controls-settings-button"]')
|
||||
.click();
|
||||
await expect(card).toHaveCount(0);
|
||||
await page.keyboard.press('Escape');
|
||||
await expect(card).toBeVisible();
|
||||
|
||||
// A click plays the next episode through the host's own path.
|
||||
await card.click();
|
||||
await expect(
|
||||
page.locator('app-portal-inline-player .player-shell__episode-meta')
|
||||
).toContainText('S01E02', { timeout: 15_000 });
|
||||
await expect(card).toHaveAttribute('aria-label', /S01E03/, {
|
||||
timeout: 15_000,
|
||||
});
|
||||
});
|
||||
|
||||
test('switches episodes from the fullscreen episode panel without leaving fullscreen', async ({
|
||||
page,
|
||||
request,
|
||||
|
||||
Reference in new issue
Block a user