mirror of
https://github.com/Shik3i/KoalaSync.git
synced 2026-08-18 15:23:13 +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>
165 lines
7.1 KiB
JavaScript
165 lines
7.1 KiB
JavaScript
import { test, expect } from './helpers/extension-fixture.mjs';
|
|
|
|
/**
|
|
* Drives the packed extension itself: real background service worker, real
|
|
* chrome.scripting injection, real runtime messaging. The detection specs cover
|
|
* which element gets picked; this file covers whether the extension ever gets
|
|
* far enough to pick one.
|
|
*/
|
|
|
|
/** Runs code in an extension page, where the privileged chrome.* APIs exist. */
|
|
async function withExtensionPage(context, extensionId, fn) {
|
|
const page = await context.newPage();
|
|
await page.goto(`chrome-extension://${extensionId}/popup.html`);
|
|
const result = await fn(page);
|
|
await page.close();
|
|
return result;
|
|
}
|
|
|
|
async function selectTargetTab(context, extensionId, pageUrl) {
|
|
return withExtensionPage(context, extensionId, page => page.evaluate(async (url) => {
|
|
const [tab] = await chrome.tabs.query({ url });
|
|
if (!tab) throw new Error(`no tab matched ${url}`);
|
|
const response = await chrome.runtime.sendMessage({ type: 'SET_TARGET_TAB', tabId: tab.id });
|
|
return { tabId: tab.id, response };
|
|
}, pageUrl));
|
|
}
|
|
|
|
async function sendServerCommand(context, extensionId, tabId, action, payload) {
|
|
return withExtensionPage(context, extensionId, page => page.evaluate(async ({ tabId, action, payload }) => {
|
|
return chrome.tabs.sendMessage(tabId, {
|
|
type: 'SERVER_COMMAND',
|
|
action,
|
|
payload,
|
|
actionTimestamp: Date.now(),
|
|
commandSenderId: 'e2e'
|
|
});
|
|
}, { tabId, action, payload }));
|
|
}
|
|
|
|
test('injects into the target tab and attaches to a same-origin frame player', async ({ context, extensionId, baseURL }) => {
|
|
const url = `${baseURL}/pages/iframe-player.html`;
|
|
const page = await context.newPage();
|
|
await page.goto(url);
|
|
await page.waitForFunction(() => window.__fixtureReady === true);
|
|
|
|
const { response } = await selectTargetTab(context, extensionId, url);
|
|
expect(response?.status, 'SET_TARGET_TAB should not report a failure').not.toBe('error');
|
|
|
|
await expect.poll(
|
|
() => page.evaluate(() => {
|
|
const video = document.querySelector('iframe').contentDocument.querySelector('video');
|
|
return video ? video.dataset.koalaAttached : null;
|
|
}),
|
|
{ message: 'content script should attach to the video inside the frame' }
|
|
).toBe('true');
|
|
});
|
|
|
|
test('applies remote play, pause and seek to the framed player', async ({ context, extensionId, baseURL }) => {
|
|
const url = `${baseURL}/pages/iframe-player.html`;
|
|
const page = await context.newPage();
|
|
await page.goto(url);
|
|
await page.waitForFunction(() => window.__fixtureReady === true);
|
|
|
|
const { tabId } = await selectTargetTab(context, extensionId, url);
|
|
await expect.poll(() => page.evaluate(() => {
|
|
const video = document.querySelector('iframe').contentDocument.querySelector('video');
|
|
return video ? video.dataset.koalaAttached : null;
|
|
})).toBe('true');
|
|
|
|
await sendServerCommand(context, extensionId, tabId, 'play');
|
|
await expect.poll(() => page.evaluate(FRAMED_VIDEO_PAUSED), { message: 'remote play should start playback' }).toBe(false);
|
|
|
|
await sendServerCommand(context, extensionId, tabId, 'pause');
|
|
await expect.poll(() => page.evaluate(FRAMED_VIDEO_PAUSED), { message: 'remote pause should stop playback' }).toBe(true);
|
|
|
|
await sendServerCommand(context, extensionId, tabId, 'seek', { targetTime: 6 });
|
|
await expect.poll(
|
|
() => page.evaluate(() => document.querySelector('iframe').contentDocument.querySelector('video').currentTime),
|
|
{ message: 'remote seek should move the framed player' }
|
|
).toBeGreaterThan(5);
|
|
});
|
|
|
|
test('reinjects after the target tab navigates', async ({ context, extensionId, baseURL }) => {
|
|
const first = `${baseURL}/pages/iframe-player.html`;
|
|
const page = await context.newPage();
|
|
await page.goto(first);
|
|
await page.waitForFunction(() => window.__fixtureReady === true);
|
|
await selectTargetTab(context, extensionId, first);
|
|
await expect.poll(() => page.evaluate(() => {
|
|
const video = document.querySelector('iframe').contentDocument.querySelector('video');
|
|
return video ? video.dataset.koalaAttached : null;
|
|
})).toBe('true');
|
|
|
|
await page.goto(`${baseURL}/pages/simple-player.html`);
|
|
await page.waitForFunction(() => window.__fixtureReady === true);
|
|
|
|
await expect.poll(
|
|
() => page.evaluate(() => {
|
|
const video = document.getElementById('player');
|
|
return video ? video.dataset.koalaAttached : null;
|
|
}),
|
|
{ message: 'the content script should come back after a navigation' }
|
|
).toBe('true');
|
|
});
|
|
|
|
test('re-attaches after the player frame swaps its document', async ({ context, extensionId, baseURL }) => {
|
|
const url = `${baseURL}/pages/reloading-frame.html`;
|
|
const page = await context.newPage();
|
|
await page.goto(url);
|
|
await page.waitForFunction(() => window.__fixtureReady === true);
|
|
await selectTargetTab(context, extensionId, url);
|
|
|
|
await expect.poll(() => page.evaluate(() => {
|
|
const video = document.querySelector('iframe').contentDocument.getElementById('framed-player');
|
|
return video ? video.dataset.koalaAttached : null;
|
|
})).toBe('true');
|
|
|
|
// Navigating the frame replaces its document without touching the top one,
|
|
// so nothing but a load hook on the frame can notice the new player.
|
|
await page.evaluate(() => {
|
|
document.querySelector('iframe').src = 'frames/player-frame-2.html';
|
|
});
|
|
|
|
await expect.poll(
|
|
() => page.evaluate(() => {
|
|
const video = document.querySelector('iframe').contentDocument.getElementById('framed-player-2');
|
|
return video ? video.dataset.koalaAttached : null;
|
|
}),
|
|
{ message: 'the content script should follow the frame to its new document' }
|
|
).toBe('true');
|
|
});
|
|
|
|
test('re-attaches when a nested player frame swaps its document', async ({ context, extensionId, baseURL }) => {
|
|
const url = `${baseURL}/pages/nested-frame.html`;
|
|
const page = await context.newPage();
|
|
await page.goto(url);
|
|
await page.waitForFunction(() => window.__fixtureReady === true);
|
|
await selectTargetTab(context, extensionId, url);
|
|
|
|
const innerVideo = (id) => page.evaluate((videoId) => {
|
|
const outer = document.querySelector('iframe').contentDocument;
|
|
const inner = outer.querySelector('iframe').contentDocument;
|
|
const video = inner.getElementById(videoId);
|
|
return video ? video.dataset.koalaAttached : null;
|
|
}, id);
|
|
|
|
await expect.poll(() => innerVideo('framed-player')).toBe('true');
|
|
|
|
// The reloading frame sits at depth two. A load hook that only covers
|
|
// top-level frames would never fire for it.
|
|
await page.evaluate(() => {
|
|
const outer = document.querySelector('iframe').contentDocument;
|
|
outer.querySelector('iframe').src = 'player-frame-2.html';
|
|
});
|
|
|
|
await expect.poll(
|
|
() => innerVideo('framed-player-2'),
|
|
{ message: 'a frame two levels down should be watched for reloads too' }
|
|
).toBe('true');
|
|
});
|
|
|
|
function FRAMED_VIDEO_PAUSED() {
|
|
return document.querySelector('iframe').contentDocument.querySelector('video').paused;
|
|
}
|