From 36e1291d2c10303d5b3f4c210c6b172923c416c9 Mon Sep 17 00:00:00 2001 From: KoalaDev <6156589+Shik3i@users.noreply.github.com> Date: Fri, 14 Aug 2026 07:23:12 +0200 Subject: [PATCH] feat: rank player candidates by ordered signals, add browser E2E suite The weighted score summed incomparable units, so size could outvote traits that disqualify an element outright. Measured on a real page: a display:none preload reports its full 1080p intrinsic size and scored 2073600, beating a visible unmuted player at 509920. Selection now compares an ordered list of signals, highest priority first: has a source, is rendered, is not a silent background loop, rendered size bucket, is playing, has controls, duration. Rendered size replaces intrinsic resolution, and mute state is gone from the ranking entirely: it is a viewer preference, not evidence about which element is the player. It stays a ranking rather than a filter, so a page of only bad candidates still yields one and findVideo never returns null where a video exists. The new tests/e2e suite runs the shipped finder against real fixture pages and drives the packed extension for injection, reinjection and remote play/pause/seek into a first-party frame. All five scoring scenarios fail against the previous implementation. Co-Authored-By: Claude Opus 5 --- .github/workflows/ci.yml | 29 ++++ .gitignore | 5 + docs/CHANGELOG.md | 5 + docs/ROADMAP.md | 11 +- extension/content.js | 100 +++++++++--- package.json | 2 + scripts/test-content-video-finder.cjs | 28 +++- tests/e2e/README.md | 63 ++++++++ tests/e2e/detection.spec.mjs | 86 +++++++++++ tests/e2e/extension.spec.mjs | 143 ++++++++++++++++++ tests/e2e/fixture-server.mjs | 72 +++++++++ tests/e2e/fixtures/media/ad-360p-5s.mp4 | Bin 0 -> 3152 bytes tests/e2e/fixtures/media/loop-720p-3s.mp4 | Bin 0 -> 3203 bytes tests/e2e/fixtures/media/player-1080p-30s.mp4 | Bin 0 -> 26431 bytes tests/e2e/fixtures/media/player-480p-12s.mp4 | Bin 0 -> 5873 bytes tests/e2e/fixtures/pages/ad-frame.html | 7 + tests/e2e/fixtures/pages/background-loop.html | 13 ++ tests/e2e/fixtures/pages/frames/ad-frame.html | 7 + .../fixtures/pages/frames/player-frame.html | 5 + tests/e2e/fixtures/pages/hidden-preload.html | 9 ++ tests/e2e/fixtures/pages/iframe-player.html | 6 + tests/e2e/fixtures/pages/late-frame.html | 16 ++ tests/e2e/fixtures/pages/multi-player.html | 11 ++ tests/e2e/fixtures/pages/muted-player.html | 8 + tests/e2e/fixtures/pages/ready.js | 51 +++++++ tests/e2e/fixtures/pages/shadow-player.html | 19 +++ tests/e2e/fixtures/pages/simple-player.html | 6 + tests/e2e/fixtures/pages/sourceless.html | 9 ++ tests/e2e/helpers/content-source.mjs | 65 ++++++++ tests/e2e/playwright.config.mjs | 34 +++++ 30 files changed, 781 insertions(+), 29 deletions(-) create mode 100644 tests/e2e/README.md create mode 100644 tests/e2e/detection.spec.mjs create mode 100644 tests/e2e/extension.spec.mjs create mode 100644 tests/e2e/fixture-server.mjs create mode 100644 tests/e2e/fixtures/media/ad-360p-5s.mp4 create mode 100644 tests/e2e/fixtures/media/loop-720p-3s.mp4 create mode 100644 tests/e2e/fixtures/media/player-1080p-30s.mp4 create mode 100644 tests/e2e/fixtures/media/player-480p-12s.mp4 create mode 100644 tests/e2e/fixtures/pages/ad-frame.html create mode 100644 tests/e2e/fixtures/pages/background-loop.html create mode 100644 tests/e2e/fixtures/pages/frames/ad-frame.html create mode 100644 tests/e2e/fixtures/pages/frames/player-frame.html create mode 100644 tests/e2e/fixtures/pages/hidden-preload.html create mode 100644 tests/e2e/fixtures/pages/iframe-player.html create mode 100644 tests/e2e/fixtures/pages/late-frame.html create mode 100644 tests/e2e/fixtures/pages/multi-player.html create mode 100644 tests/e2e/fixtures/pages/muted-player.html create mode 100644 tests/e2e/fixtures/pages/ready.js create mode 100644 tests/e2e/fixtures/pages/shadow-player.html create mode 100644 tests/e2e/fixtures/pages/simple-player.html create mode 100644 tests/e2e/fixtures/pages/sourceless.html create mode 100644 tests/e2e/helpers/content-source.mjs create mode 100644 tests/e2e/playwright.config.mjs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8e4d203..8383631 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -46,3 +46,32 @@ jobs: - name: Run verification suite run: npm run verify + + e2e: + # Kept separate from `verify`: this job needs a downloaded browser, so a + # failure here should read as "the browser flow broke", not as a broken + # lint or unit run. `verify` stays usable offline and in the release job. + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v7 + + - name: Set up Node.js + uses: actions/setup-node@v6 + with: + node-version: '24' + cache: 'npm' + cache-dependency-path: package-lock.json + + - name: Install root dependencies + run: npm ci + + - name: Install Playwright Chromium + run: npx playwright install --with-deps chromium chromium-headless-shell + + # The extension specs load dist/chrome, so the artifact has to exist. + - name: Build the extension + run: npm run build:extension + + - name: Run extension E2E smoke tests + run: npm run test:e2e diff --git a/.gitignore b/.gitignore index e193446..f05187c 100644 --- a/.gitignore +++ b/.gitignore @@ -45,6 +45,11 @@ extension/shared/ website/www/ website/.avif-cache.json +# Playwright E2E artifacts (the fixtures themselves are committed) +test-results/ +playwright-report/ +blob-report/ + # Temporary scratch files scratch/ diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 99cd4b1..c8240aa 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -11,6 +11,11 @@ All notable changes to the KoalaSync browser extension and relay server. - **Extension: Editable Hide-Clutter list** — Adds a validated, deduplicated domain editor in Settings, prefilled with the shipped blacklist, with a defaults reset and per-device persistence in `chrome.storage.local`. The editor groups entries into your own and the shipped defaults, and only your changes are stored, so domains added to the shipped list in later versions still reach you without overriding what you removed or added. Lists saved by an earlier version are migrated automatically. - **Extension: Independent default audio boost** — Adds a configurable `0–20 dB` output gain in half-decibel steps. The boost works with or without the compressor and applies live to the selected video tab. +- **Testing: Browser E2E smoke suite** — Adds a local Playwright suite (`npm run test:e2e`) that runs the shipped video finder against real fixture pages and drives the packed extension end to end: injection into the target tab, reinjection after navigation, and remote play, pause and seek applied to a player inside a first-party frame. Runs as its own CI job so `npm run verify` stays browser-free. + +### Changed +- **Extension: Rebuilt player selection** — Replaces the weighted score with an ordered list of signals, so a video that cannot be the player (no source, not rendered) is never picked just for being large. Rendered size now decides instead of intrinsic resolution, a video that is actually playing wins between equally sized players, silent looping background videos are demoted, and mute state no longer influences the choice at all. + ### Fixed - **Extension: Google Drive tab selection** — A broad parent domain in the Hide-Clutter list no longer hides a host that has its own supported player path, so Drive videos stay selectable while the filter is on. An exact entry for the host itself still filters it. - **Extension: Debug report frame visibility** — Video counts now include players inside same-origin frames and the report states whether the selected video sits in a frame. diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index a16f712..083c602 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -57,13 +57,12 @@ - **Possible approach:** Add an opt-in frame bridge (`allFrames: true` injection) where child frames announce detected videos to the top frame, and the top frame routes remote play/pause/seek commands to the active child video. Needs a frame-election rule so ad frames cannot claim the session. - **Status:** Same-origin part completed; cross-origin frame bridge still open. Not needed for current Emby behavior. -### Local extension E2E smoke tests +### Sync a second video source per room -- **Priority:** P2 -- **Category:** Testing / Release Confidence -- **Background:** The release verification covers unit tests, server integration, syntax, lint, audits, and builds, but it does not currently run a real browser extension flow. A small local E2E smoke suite would catch regressions in content-script injection, tab navigation reinjection, remote seek handling, and iframe player support. -- **Possible approach:** Add a separate local-only Playwright smoke command that loads the unpacked extension, opens two controlled video pages, and verifies play/pause/seek through the actual extension path. Keep it outside `npm run verify` until it is stable enough for CI. -- **Status:** Backlog, recommended before larger content-script or frame-bridge changes. +- **Priority:** P3 +- **Category:** Compatibility / Player Selection +- **Background:** Detection picks exactly one `