frontend/e2e/external-playback.test.ts
Rama master · réplica de lectura del repositorio original.
import assert from 'node:assert/strict';
import { after, before, describe, it } from 'node:test';
import type { Browser } from 'playwright-core';
import { launch, layout, VIEWPORTS } from './harness.ts';
import { ITEM, openItemPage } from './item-fixture.ts';
describe('外部播放器', () => {
let browser: Browser;
before(async () => { browser = await launch() });
after(async () => { await browser.close() });
for (const viewport of [VIEWPORTS.find((one) => !one.mobile)!, VIEWPORTS.find((one) => one.mobile)!]) {
it(`${viewport.mobile ? '手机' : '桌面'}详情共用播放器选择与打开按钮`, { timeout: 60_000 }, async () => {
const opened = await openItemPage(browser, `/assets/${ITEM.plain}`, viewport);
try {
const page = opened.page;
await page.route('**/api/local-players', route => route.fulfill({ json: { players: [
{ id: 'default', label: '系统默认播放器' }, { id: 'potplayer', label: 'PotPlayer' },
] } }));
let selected = '';
let failOpen = false;
await page.route('**/api/local-playback?*', route => {
selected = new URL(route.request().url()).searchParams.get('player') || '';
if (failOpen) return route.fulfill({ status: 503, json: { detail: '无法打开播放器' } });
return route.fulfill({ json: { opened: true } });
});
await page.reload();
const chooser = page.getByRole('button', { name: '外部播放器', exact: true });
await chooser.waitFor();
assert.equal(await page.locator('#stage details[open]').count(), 0);
assert.equal(await page.getByText('外部播放器 / VR', { exact: true }).count(), 0);
assert.equal(await page.getByRole('button', { name: '使用外部播放器打开', exact: true }).count(), 1);
await chooser.click();
await page.getByRole('option', { name: 'PotPlayer', exact: true }).click();
await page.getByRole('button', { name: '使用外部播放器打开', exact: true }).click();
await page.waitForFunction(() => document.body.textContent?.includes('已请求打开本地播放器'));
assert.equal(selected, 'potplayer');
failOpen = true;
await page.getByRole('button', { name: '使用外部播放器打开', exact: true }).click();
await page.locator('[data-local-playback] [role="alert"]').waitFor();
const geometry = await page.locator('[data-local-playback]').evaluate(host => {
const row = host.querySelector('[data-local-playback-row]')!.getBoundingClientRect();
const error = host.querySelector('[role="alert"]')!.getBoundingClientRect();
const select = host.querySelector('[aria-label="外部播放器"]')!.getBoundingClientRect();
const button = host.querySelector('[aria-label="使用外部播放器打开"]')!.getBoundingClientRect();
return { gap: error.top - row.bottom, left: error.left - row.left,
sameTop: Math.abs(select.top - button.top), sameHeight: Math.abs(select.height - button.height) };
});
assert.equal(geometry.gap, 8);
assert.equal(geometry.left, 0);
assert.equal(geometry.sameTop, 0);
assert.equal(geometry.sameHeight, 0);
assert.deepEqual((await layout(page)).offenders, []);
} finally { await opened.close() }
});
it(`${viewport.mobile ? '手机' : '桌面'}按需生成可复制的网络地址`, { timeout: 60_000 }, async () => {
const opened = await openItemPage(browser, `/assets/${ITEM.plain}`, viewport, { ready: '#stage[open] [data-item-side]' });
try {
const page = opened.page;
let requested = 0;
await page.route('**/api/external-playback?*', route => {
requested++;
return route.fulfill({ json: { url: `/external-stream/${ITEM.plain}?ticket=${'a'.repeat(90)}`,
playlist: `/external-playlist/${ITEM.plain}.m3u?ticket=${'a'.repeat(90)}` } });
});
await page.getByRole('button', { name: '外部播放器', exact: true }).click();
await page.getByRole('option', { name: '网络播放地址 / VR', exact: true }).click();
assert.equal(requested, 0);
await page.getByRole('button', { name: '使用外部播放器打开', exact: true }).click();
await page.getByRole('textbox', { name: '网络播放地址' }).waitFor();
assert.equal(requested, 1);
assert.match(await page.getByRole('textbox', { name: '网络播放地址' }).inputValue(), /\/external-stream\/11\?ticket=/);
assert.deepEqual((await layout(page)).offenders, []);
} finally { await opened.close() }
});
}
});