mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-14 13:16:16 -08:00
fix(playlist): mount the saved engine for recognized M3U movies
WebPlayerViewComponent resolves the engine from its own asynchronous settings read and falls back to Video.js until it lands. The engine is part of the application token, so anyone whose saved player is not Video.js got a first application on the wrong engine and a swap a tick later. The M3U live layout never showed it because it passes an explicit playerOverride; the detail path goes through the shared inline player, which had no way to. PortalInlinePlayerComponent now accepts an optional playerOverride (null keeps the portals' behaviour) and the M3U detail chain forwards the value the live layout already uses. The E2E guard is a MutationObserver over engine mounts, not a polling locator: both engines leave the same final DOM, so only the record of what was ever attached can see the defect. Verified to fail without the fix. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
d0d29567be
commit
5b4079726e
12 files changed
+67
-137
No files matched your search
@@ -145,12 +145,45 @@ test('@web @m3u @tmdb recognized movies open the VOD detail view', async ({
|
||||
await enableTmdb(page);
|
||||
await importPlaylist(page);
|
||||
|
||||
// Record every engine that is ever attached. A polling assertion cannot
|
||||
// see the defect this guards: mounting Video.js first and correcting to
|
||||
// the saved engine a tick later leaves the same final DOM.
|
||||
await page.evaluate(() => {
|
||||
const seen = new Set<string>();
|
||||
(window as unknown as { __enginesSeen: Set<string> }).__enginesSeen =
|
||||
seen;
|
||||
const record = () => {
|
||||
for (const selector of [
|
||||
'app-vjs-player',
|
||||
'app-html-video-player',
|
||||
]) {
|
||||
if (document.querySelector(selector)) {
|
||||
seen.add(selector);
|
||||
}
|
||||
}
|
||||
};
|
||||
record();
|
||||
new MutationObserver(record).observe(document.body, {
|
||||
childList: true,
|
||||
subtree: true,
|
||||
});
|
||||
});
|
||||
|
||||
// Watch-first: activating the entry plays immediately inside the detail
|
||||
// shell, with no EPG zone in sight.
|
||||
await sidebarEntry(page, 'Dune (2021) 1080p').click();
|
||||
await expect(detail(page)).toBeVisible();
|
||||
await expect(inlineVideo(page)).toBeVisible();
|
||||
await expect(page.locator('app-epg-timeline')).toHaveCount(0);
|
||||
// The engine is part of the application token, so the saved player must
|
||||
// mount FIRST TIME — a late correction swaps the player mid-playback.
|
||||
await expect(detail(page).locator('app-html-video-player')).toHaveCount(1);
|
||||
expect(
|
||||
await page.evaluate(() => [
|
||||
...(window as unknown as { __enginesSeen: Set<string> })
|
||||
.__enginesSeen,
|
||||
])
|
||||
).toEqual(['app-html-video-player']);
|
||||
|
||||
// Metadata patches the mounted view asynchronously. The shell stamps the
|
||||
// host templates into BOTH the hero and the watch-state About block, so
|
||||
|
||||
Reference in new issue
Block a user