mirror of
https://github.com/Shik3i/KoalaSync.git
synced 2026-08-18 07:13:12 +00:00
ef1fdc89c3
The evidence for the frame-observation fixes was a reimplementation of the logic measured in a browser console, not the shipped code. Two extension specs now cover the observable behaviour end to end: a player frame that swaps its document, and the same one level deeper. The nested case fails against the pre-fix content.js and passes now. The top-level case already passed before the fix, so that fix removed dead observer registrations without changing what a user could see; recorded here so the distinction is not lost. Also adds bench-finder.mjs, which measures the extracted shipped finder instead of a transcription of it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
88 lines
4.5 KiB
JavaScript
88 lines
4.5 KiB
JavaScript
import { test, expect } from '@playwright/test';
|
|
import { buildVideoFinderScript } from './helpers/content-source.mjs';
|
|
|
|
/**
|
|
* Runs the shipped findVideo() against real pages in a real browser, where
|
|
* videoWidth, offsetParent, paused and duration all carry their true values.
|
|
* Each fixture marks the element that must win with data-expected.
|
|
*/
|
|
|
|
const SCENARIOS = [
|
|
{ page: 'simple-player.html', expected: 'player', what: 'the only visible player' },
|
|
{ page: 'iframe-player.html', expected: 'framed-player', what: 'a player inside a same-origin frame' },
|
|
{ page: 'nested-frame.html', expected: 'framed-player', what: 'a player two frame levels down' },
|
|
{ page: 'shadow-player.html', expected: 'shadow-player', what: 'a player inside a shadow root over a light-DOM teaser' },
|
|
{ page: 'muted-player.html', expected: 'player', what: 'the only player even when muted' },
|
|
{ page: 'hidden-preload.html', expected: 'player', what: 'the visible player over a hidden higher-resolution preload' },
|
|
{ page: 'ad-frame.html', expected: 'player', what: 'the real player over a muted ad in a first-party frame' },
|
|
{ page: 'background-loop.html', expected: 'player', what: 'the real player over a large looping background video' },
|
|
{ page: 'multi-player.html', expected: 'watched', what: 'the player that is actually playing' },
|
|
{ page: 'sourceless.html', expected: 'player', what: 'the real player over a large sourceless placeholder' }
|
|
];
|
|
|
|
test.describe('video detection', () => {
|
|
for (const { page: fixture, expected, what } of SCENARIOS) {
|
|
test(`picks ${what} (${fixture})`, async ({ page }) => {
|
|
await page.goto(`/pages/${fixture}`);
|
|
await page.waitForFunction(() => window.__fixtureReady === true);
|
|
|
|
// Assert the fixture is in the state it claims before judging the
|
|
// finder, so a broken fixture never reads as a scoring regression.
|
|
const preconditions = await page.evaluate(() => {
|
|
const videos = [];
|
|
const walk = (doc) => {
|
|
for (const v of doc.querySelectorAll('video')) videos.push(v);
|
|
for (const f of doc.querySelectorAll('iframe')) {
|
|
try { if (f.contentDocument) walk(f.contentDocument); } catch (_e) { /* cross-origin */ }
|
|
}
|
|
for (const el of doc.querySelectorAll('*')) if (el.shadowRoot) walk(el.shadowRoot);
|
|
};
|
|
walk(document);
|
|
return videos
|
|
.filter(v => v.dataset.autoplay !== undefined)
|
|
.map(v => ({ id: v.id, paused: v.paused, ended: v.ended }));
|
|
});
|
|
for (const video of preconditions) {
|
|
expect(video.paused, `fixture video ${video.id} should be playing`).toBe(false);
|
|
expect(video.ended, `fixture video ${video.id} should not have ended`).toBe(false);
|
|
}
|
|
|
|
await page.addScriptTag({ content: buildVideoFinderScript() });
|
|
|
|
const picked = await page.evaluate(() => {
|
|
const video = window.__koalaFindVideo();
|
|
if (!video) return null;
|
|
return {
|
|
id: video.id,
|
|
expected: video.dataset.expected !== undefined,
|
|
inFrame: video.ownerDocument !== document
|
|
};
|
|
});
|
|
|
|
expect(picked, 'a video must be found at all').not.toBeNull();
|
|
expect(picked.id).toBe(expected);
|
|
expect(picked.expected, `${picked.id} is not the element marked data-expected`).toBe(true);
|
|
});
|
|
}
|
|
|
|
test('finds a player frame that is attached later (late-frame.html)', async ({ page }) => {
|
|
await page.goto('/pages/late-frame.html');
|
|
await page.addScriptTag({ content: buildVideoFinderScript() });
|
|
|
|
// Nothing to find while the slot is still empty.
|
|
expect(await page.evaluate(() => window.__koalaFindVideo() === null)).toBe(true);
|
|
|
|
await page.waitForFunction(() => {
|
|
const video = window.__koalaFindVideo();
|
|
return !!video && video.readyState >= 1;
|
|
});
|
|
expect(await page.evaluate(() => window.__koalaFindVideo().id)).toBe('framed-player');
|
|
});
|
|
|
|
test('returns null on a page without any video', async ({ page }) => {
|
|
await page.setContent('<h1>no media here</h1>');
|
|
await page.addScriptTag({ content: buildVideoFinderScript() });
|
|
expect(await page.evaluate(() => window.__koalaFindVideo())).toBeNull();
|
|
});
|
|
});
|