Files
KoalaSync/tests/e2e
Timo 68f2d9f27c fix(extension): find the player when the all-frames sweep comes back empty
Diagnosed against a fixture rebuilt from the live yummyanime.tv page, with the
ad churn the real site produces. Under that churn the resolver reported
frame=0, hasVideo=false while the video demonstrably existed two frame levels
down: one ad slot tearing down mid-call makes Chromium reject the whole
allFrames sweep, and the resolver then silently fell back to the top frame and
never looked again.

v3.1.2 did not have this failure because webNavigation.getAllFrames() gave it
an explicit frame list. That list is now rebuilt without the permission: every
content script that messages the background carries sender.frameId, so the
background keeps a per-tab registry of frames it has seen and the resolver asks
any frame the sweep missed directly. One rejected probe now costs one frame
instead of the whole page.

Two supporting fixes fell out of the same investigation. Frames reported hidden
by an ancestor that could inspect them directly — the 0x0 same-origin wrapper an
anime host parks unwatched mirrors in — are now excluded without waiting for the
postMessage visibility handshake, which was the tie the resolver kept failing to
break. And leaf frames with no video and no nested frames are left out of that
handshake entirely, so a churning ad slot can no longer make every phase wait on
a frame that is already gone.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 15:56:17 +02:00
..

Extension E2E smoke tests

Browser-level tests for the parts that unit tests cannot reach: which <video> the extension picks on a real page, and whether the packed extension gets far enough to control it.

npm run test:e2e:install   # once, downloads the browsers
npm run build:extension    # extension.spec.mjs loads dist/chrome
npm run test:e2e

Layout

Path Purpose
detection.spec.mjs Runs the shipped findVideo() against the fixture pages
extension.spec.mjs Loads dist/chrome, injects into a tab, applies remote play/pause/seek
fixture-server.mjs Static server for the fixtures, with byte-range support for media
fixtures/pages/ One page per scenario
fixtures/media/ Small generated clips (see below)
helpers/content-source.mjs Lifts the real finder out of extension/content.js

Two rules worth keeping

The specs run the shipped source, not a copy. helpers/content-source.mjs extracts findVideo and its ranking helpers straight out of extension/content.js. A fixture that passes against a reimplementation would prove nothing about the extension. If you split the finder into more functions, add them to VIDEO_FINDER_EXPORTS there and to VIDEO_FINDER_PARTS in scripts/test-content-video-finder.cjs, or the extraction fails loudly.

Each fixture marks its own answer. The element that must win carries data-expected; videos that have to be playing carry data-autoplay, and ready.js holds the page back until metadata and playback have settled. The specs assert those preconditions before judging the finder, so a broken fixture reads as a broken fixture instead of a scoring regression.

Scenarios

Fixture What it pins down
simple-player.html The ordinary case
iframe-player.html Player inside a same-origin frame, empty top document
late-frame.html Player frame attached after the page settled
shadow-player.html Player in a shadow root, tiny teaser in the light DOM
muted-player.html Mute must not disqualify the only player
hidden-preload.html A display:none preload still reports 1080p; it must lose
ad-frame.html 1080p asset in a 300x250 ad slot must lose to the real player
background-loop.html Silent looping hero must lose despite being the largest
multi-player.html Between equal players, the playing one wins
sourceless.html A large <video> with no source can never be the player
nested-frame.html Player two frame levels down
reloading-frame.html Frame that swaps its document, with no mutation in the top one

Benchmark

bench-finder.mjs is not a spec, because timings are machine dependent and would only add noise to CI. Run it by hand when the finder changes:

node tests/e2e/fixture-server.mjs 4173 & node tests/e2e/bench-finder.mjs

It measures the shipped finder (lifted from content.js) against the pre-v3.1.0 formula, which is transcribed inside the script since that code no longer exists in the tree.

Regenerating the media

Solid-colour clips, a few KB each, committed so the suite needs no network:

ffmpeg -y -f lavfi -i "color=c=blue:s=1920x1080:d=30:r=10" -c:v libx264 -preset veryfast -crf 45 -pix_fmt yuv420p -movflags +faststart fixtures/media/player-1080p-30s.mp4

Same command with green/854x480/12, red/640x360/5 and gray/1280x720/3 for the other three.