mirror of
https://github.com/Shik3i/KoalaSync.git
synced 2026-09-03 22:45:21 +00:00
160 lines
7.5 KiB
JavaScript
160 lines
7.5 KiB
JavaScript
import { test, expect } from './helpers/extension-fixture.mjs';
|
|
|
|
async function withExtensionPage(context, extensionId, fn) {
|
|
const page = await context.newPage();
|
|
await page.goto(`chrome-extension://${extensionId}/audio-options.html`);
|
|
try {
|
|
return await fn(page);
|
|
} finally {
|
|
await page.close();
|
|
}
|
|
}
|
|
|
|
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}`);
|
|
await chrome.runtime.sendMessage({ type: 'SET_TARGET_TAB', tabId: tab.id });
|
|
return tab.id;
|
|
}, pageUrl));
|
|
}
|
|
|
|
async function extensionMessage(context, extensionId, message) {
|
|
return withExtensionPage(context, extensionId, page => page.evaluate(
|
|
payload => chrome.runtime.sendMessage(payload),
|
|
message
|
|
));
|
|
}
|
|
|
|
async function prepareEpisodePage(page, title, currentTime) {
|
|
await page.waitForFunction(() => window.__fixtureReady === true);
|
|
await page.evaluate(async ({ title, currentTime }) => {
|
|
navigator.mediaSession.metadata = new window.MediaMetadata({ title });
|
|
const video = document.querySelector('#player');
|
|
video.muted = true;
|
|
video.playbackRate = 0.1;
|
|
video.currentTime = currentTime;
|
|
await video.play();
|
|
}, { title, currentTime });
|
|
}
|
|
|
|
async function contentLogs(context, extensionId) {
|
|
const logs = await extensionMessage(context, extensionId, { type: 'GET_LOGS' });
|
|
return logs.filter(entry => entry.message.includes('[Content]')).map(entry => entry.message);
|
|
}
|
|
|
|
test('keeps ordinary play/pause outside an episode boundary on the immediate path @episode-transition', async ({ context, extensionId, baseURL }) => {
|
|
const url = `${baseURL}/pages/simple-player.html`;
|
|
const page = await context.newPage();
|
|
await page.goto(url);
|
|
await prepareEpisodePage(page, 'Series S01E05', 3);
|
|
await selectTargetTab(context, extensionId, url);
|
|
await expect.poll(() => page.locator('#player').getAttribute('data-koala-attached')).toBe('true');
|
|
|
|
const before = await extensionMessage(context, extensionId, { type: 'GET_HISTORY' });
|
|
await page.locator('#player').evaluate(video => video.pause());
|
|
await expect.poll(async () => {
|
|
const history = await extensionMessage(context, extensionId, { type: 'GET_HISTORY' });
|
|
return history.length;
|
|
}, { timeout: 750 }).toBe(before.length + 1);
|
|
});
|
|
|
|
test('quarantines a suspicious boundary pause, then relays the final intent when no episode changes @episode-transition', async ({ context, extensionId, baseURL }) => {
|
|
const url = `${baseURL}/pages/simple-player.html`;
|
|
const page = await context.newPage();
|
|
await page.goto(url);
|
|
await prepareEpisodePage(page, 'Series S01E05', 9.25);
|
|
await selectTargetTab(context, extensionId, url);
|
|
await expect.poll(() => page.locator('#player').getAttribute('data-koala-attached')).toBe('true');
|
|
|
|
const before = await extensionMessage(context, extensionId, { type: 'GET_HISTORY' });
|
|
await page.locator('#player').evaluate(video => video.pause());
|
|
|
|
await page.waitForTimeout(500);
|
|
expect(await extensionMessage(context, extensionId, { type: 'GET_HISTORY' })).toEqual(before);
|
|
await expect.poll(async () => {
|
|
const history = await extensionMessage(context, extensionId, { type: 'GET_HISTORY' });
|
|
return history.length;
|
|
}, { timeout: 4000 }).toBe(before.length + 1);
|
|
const after = await extensionMessage(context, extensionId, { type: 'GET_HISTORY' });
|
|
expect(after[0]).toMatchObject({ action: 'pause' });
|
|
});
|
|
|
|
test('relays a deliberate boundary pause immediately @episode-transition', async ({ context, extensionId, baseURL }) => {
|
|
const url = `${baseURL}/pages/simple-player.html`;
|
|
const page = await context.newPage();
|
|
await page.goto(url);
|
|
await prepareEpisodePage(page, 'Series S01E05', 9.25);
|
|
await selectTargetTab(context, extensionId, url);
|
|
await expect.poll(() => page.locator('#player').getAttribute('data-koala-attached')).toBe('true');
|
|
|
|
const before = await extensionMessage(context, extensionId, { type: 'GET_HISTORY' });
|
|
await page.locator('#player').dispatchEvent('pointerdown');
|
|
await page.locator('#player').evaluate(video => video.pause());
|
|
await expect.poll(async () => {
|
|
const history = await extensionMessage(context, extensionId, { type: 'GET_HISTORY' });
|
|
return history.length;
|
|
}, { timeout: 750 }).toBe(before.length + 1);
|
|
});
|
|
|
|
test('does not consume title-before-loadeddata or loadeddata-before-title transitions @episode-transition', async ({ context, extensionId, baseURL }) => {
|
|
const runOrdering = async ordering => {
|
|
const url = `${baseURL}/pages/simple-player.html?ordering=${ordering}-${Date.now()}`;
|
|
const page = await context.newPage();
|
|
await page.goto(url);
|
|
await prepareEpisodePage(page, 'Series S01E05', 1);
|
|
await selectTargetTab(context, extensionId, url);
|
|
await expect.poll(() => page.locator('#player').getAttribute('data-koala-attached')).toBe('true');
|
|
|
|
if (ordering === 'title-first') {
|
|
await page.evaluate(() => {
|
|
navigator.mediaSession.metadata = new window.MediaMetadata({ title: 'Series S01E06' });
|
|
document.querySelector('#player').dispatchEvent(new window.Event('loadeddata'));
|
|
});
|
|
} else {
|
|
await page.evaluate(() => document.querySelector('#player').dispatchEvent(new window.Event('loadeddata')));
|
|
await page.waitForTimeout(150);
|
|
await page.evaluate(() => {
|
|
navigator.mediaSession.metadata = new window.MediaMetadata({ title: 'Series S01E06' });
|
|
});
|
|
}
|
|
|
|
await expect.poll(async () => {
|
|
const logs = await contentLogs(context, extensionId);
|
|
return logs.some(line => line.includes('Episode transition detected: "Series S01E06"'));
|
|
}, { timeout: 3000 }).toBe(true);
|
|
await page.close();
|
|
await extensionMessage(context, extensionId, { type: 'CLEAR_LOGS' });
|
|
};
|
|
|
|
await runOrdering('title-first');
|
|
await runOrdering('loadeddata-first');
|
|
});
|
|
|
|
test('discards source-swap pause/play churn after the episode is confirmed @episode-transition', async ({ context, extensionId, baseURL }) => {
|
|
const url = `${baseURL}/pages/simple-player.html`;
|
|
const page = await context.newPage();
|
|
await page.goto(url);
|
|
await prepareEpisodePage(page, 'Series S01E05', 11);
|
|
await selectTargetTab(context, extensionId, url);
|
|
await expect.poll(() => page.locator('#player').getAttribute('data-koala-attached')).toBe('true');
|
|
const before = await extensionMessage(context, extensionId, { type: 'GET_HISTORY' });
|
|
|
|
await page.evaluate(async () => {
|
|
const video = document.querySelector('#player');
|
|
video.pause();
|
|
navigator.mediaSession.metadata = new window.MediaMetadata({ title: 'Series S01E06' });
|
|
video.src = '../media/player-1080p-30s.mp4';
|
|
video.load();
|
|
await new Promise(resolve => video.addEventListener('loadeddata', resolve, { once: true }));
|
|
await video.play();
|
|
});
|
|
|
|
await expect.poll(async () => {
|
|
const logs = await contentLogs(context, extensionId);
|
|
return logs.some(line => line.includes('Episode transition detected: "Series S01E06"'));
|
|
}).toBe(true);
|
|
await page.waitForTimeout(2300);
|
|
expect(await extensionMessage(context, extensionId, { type: 'GET_HISTORY' })).toEqual(before);
|
|
});
|