frontend/e2e/jav-browser-settings.test.ts
Rama master · réplica de lectura del repositorio original.
import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
import { after, before, describe, it } from 'node:test';
import type { Browser } from 'playwright-core';
import { launch, layout, settle, visit, VIEWPORTS } from './harness.ts';
const SITES = JSON.parse(readFileSync(new URL('../../web/integrations/jav-sites.json', import.meta.url), 'utf8')).sites;
const SCRIPT = readFileSync(new URL('../../web/integrations/peach-jav.user.js', import.meta.url), 'utf8');
const RELEASE = JSON.parse(readFileSync(new URL('../../web/integrations/jav-userscript-release.json', import.meta.url), 'utf8'));
const USERSCRIPT = { version: RELEASE.version, minimum_version: RELEASE.bridge_min_version, install_url: RELEASE.install_url };
describe('浏览器添加 JAV 设置', () => {
let browser: Browser;
before(async () => { browser = await launch(); });
after(async () => { await browser?.close(); });
for (const viewport of VIEWPORTS) {
it(`${viewport.name}:启用、轮换与关闭,令牌只在签发响应后显示`, async () => {
const opened = await visit(browser, '/settings/library?section=' + encodeURIComponent('下载渠道'), viewport);
const { page } = opened;
try {
let enabled = false;
const posted: Record<string, unknown>[] = [];
await page.route('**/api/configuration/jav-browser', async route => {
const request = route.request();
const state = { enabled, provider: '115', available: true, userscript: USERSCRIPT, sites: SITES, providers: [
{ key: '115', label: '115', configured: true, target: '/115/Movies' },
{ key: 'pikpak', label: 'PikPak', configured: true, target: '/Movies' },
] };
if (request.method() === 'POST') {
const body = request.postDataJSON();
posted.push(body);
enabled = body.action !== 'disable';
await route.fulfill({ json: { ...state, enabled,
...(enabled ? { token: 'test-issued-token-' + posted.length } : {}) } });
} else await route.fulfill({ json: state });
});
await page.reload();
const region = page.getByRole('region', { name: '浏览器添加 JAV', exact: true });
const install = region.getByRole('link', { name: '安装 Peach 用户脚本' });
await install.waitFor();
const channel = region.locator('button[aria-haspopup="listbox"]');
const installBox = await region.locator('[data-userscript-status]').boundingBox();
const channelBox = await channel.boundingBox();
assert.ok(installBox && channelBox && Math.abs(installBox.x - channelBox.x) <= 1,
JSON.stringify({ installBox, channelBox }));
assert.equal(await channel.locator('img').count(), 1);
const status = region.locator('[data-userscript-status]').getByText('未启用', { exact: true }).or(
region.locator('[data-userscript-status]').getByText('已启用', { exact: true }));
assert.equal(await status.innerText(), '未启用');
const statusColor = () => status.evaluate(node => getComputedStyle(node).color);
const disabledColor = await statusColor();
const themeColor = (token: string) => page.evaluate(token => {
const sample = document.createElement('span');
sample.style.color = `var(${token})`;
document.querySelector('.peach-react')!.append(sample);
const color = getComputedStyle(sample).color;
sample.remove();
return color;
}, token);
assert.equal(disabledColor, await themeColor('--color-text-secondary'));
await channel.scrollIntoViewIfNeeded();
await page.evaluate(() => new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(resolve))));
await channel.click();
const pikpak = page.getByRole('option', { name: 'PikPak', exact: true });
await pikpak.waitFor();
const overflow = await page.getByRole('listbox').evaluate(node => ({ width: node.clientWidth, scroll: node.scrollWidth }));
assert.ok(overflow.scroll <= overflow.width + 1, JSON.stringify(overflow));
assert.equal(await pikpak.locator('img').count(), 1);
const cloud115 = page.getByRole('option', { name: '115', exact: true });
assert.equal(await cloud115.locator('img').count(), 1);
await cloud115.click();
await region.getByRole('button', { name: '启用并生成令牌', exact: true }).click();
const token = region.getByLabel('浏览器添加专用令牌', { exact: true });
await token.waitFor();
assert.equal(await status.innerText(), '已启用');
assert.notEqual(await statusColor(), disabledColor);
assert.equal(await statusColor(), await themeColor('--color-status-lime-text'));
const channelLabelBox = await region.getByText('浏览器添加的下载渠道', { exact: true }).boundingBox();
const channelFieldBox = await channel.boundingBox();
assert.ok(channelLabelBox && channelFieldBox);
const channelGap = channelFieldBox.y - channelLabelBox.y - channelLabelBox.height;
const inputGaps = await region.locator('[data-input-size]').evaluateAll(fields => fields.map(field => {
const label = field.querySelector('label')!;
const control = label.nextElementSibling!;
return control.getBoundingClientRect().top - label.getBoundingClientRect().bottom;
}));
assert.equal(inputGaps.length, 2);
assert.ok(inputGaps.every(gap => Math.abs(gap - channelGap) <= 0.5),
JSON.stringify({ channelGap, inputGaps }));
assert.equal(await token.inputValue(), 'test-issued-token-1');
assert.equal(await token.getAttribute('type'), 'password');
const refreshed = page.waitForResponse(response => response.url().endsWith('/api/configuration/jav-browser')
&& response.request().method() === 'GET');
await page.evaluate(() => window.dispatchEvent(new Event('visibilitychange')));
await refreshed;
assert.equal(await token.inputValue(), 'test-issued-token-1');
await page.evaluate(() => {
const copied: string[] = [];
Object.assign(window, { pairingClipboard: copied });
Object.defineProperty(navigator, 'clipboard', { configurable: true,
value: { writeText: async (value: string) => { copied.push(value); } } });
});
await region.getByRole('button', { name: '复制配对信息', exact: true }).click();
const copied = await page.evaluate(() => (window as Window & { pairingClipboard: string[] }).pairingClipboard);
assert.equal(copied.length, 1);
assert.deepEqual(JSON.parse(copied[0]!), { peach_pairing: 1, base: new URL(page.url()).origin, token: 'test-issued-token-1' });
assert.equal(await region.getByRole('link', { name: '安装 Peach 用户脚本' }).getAttribute('href'),
USERSCRIPT.install_url);
assert.equal(await region.getByLabel('Peach 地址', { exact: true }).inputValue(), new URL(page.url()).origin);
await region.getByRole('button', { name: '更新渠道与令牌', exact: true }).click();
await page.waitForFunction(() => document.querySelector<HTMLInputElement>(
'input[type="password"][value="test-issued-token-2"]') !== null);
await region.getByRole('button', { name: '关闭浏览器添加', exact: true }).click();
await token.waitFor({ state: 'detached' });
assert.equal(await region.getByRole('button', { name: '复制配对信息', exact: true }).count(), 0);
assert.equal(await status.innerText(), '未启用');
assert.equal(await statusColor(), disabledColor);
assert.deepEqual(posted.map(body => body.action), ['enable', 'rotate', 'disable']);
assert.ok(posted.every(body => body.provider === '115'));
await settle(page);
assert.ok((await layout(page)).scrollWidth <= viewport.width + 1);
assert.deepEqual(opened.problems, []);
await region.screenshot({ path: `../build/jav-browser-settings-${viewport.name}.png` });
} finally { await opened.close(); }
});
}
it('桥接页延迟加载仍能检测脚本,未知来源消息不改变检测结果', async () => {
const opened = await visit(browser, '/settings/library?section=' + encodeURIComponent('下载渠道'), VIEWPORTS[0]!);
const { page } = opened;
try {
await page.route('**/api/configuration/jav-browser', route => route.fulfill({ json: {
enabled: true, provider: '115', available: true, userscript: USERSCRIPT,
providers: [{ key: '115', label: '115', configured: true, target: '/Movies' }],
} }));
let installed = true;
await page.route('**/integrations/peach-userscript-bridge', async route => {
if (installed) await new Promise(resolve => setTimeout(resolve, 3500));
await route.fulfill({ contentType: 'text/html', body: '<!doctype html><meta name="peach-userscript-bridge" content="1">'
+ (installed ? '<script>window.GM_getValue=(key,fallback)=>({"peach.jav.base":location.origin,"peach.jav.token":"test-only-stored-token"})[key]??fallback;</script><script>'
+ SCRIPT.replaceAll('</script', '<\\/script') + '</script>' : '') });
});
await page.reload();
const region = page.getByRole('region', { name: '浏览器添加 JAV', exact: true });
const connection = region.locator('[data-userscript-status] [data-connection-tone]');
await connection.getByText('已连接', { exact: true }).waitFor();
assert.equal(await connection.getAttribute('data-connection-tone'), 'success');
await region.getByText(`用户脚本 v${RELEASE.version}`, { exact: true }).waitFor();
assert.equal(await region.getByRole('link', { name: '安装 Peach 用户脚本' }).count(), 0);
assert.equal(await region.getByLabel('浏览器添加专用令牌', { exact: true }).count(), 0);
installed = false;
await region.getByRole('button', { name: '重新检测', exact: true }).click();
await connection.getByText('未检测到', { exact: true }).waitFor();
assert.equal(await connection.getAttribute('data-connection-tone'), 'neutral');
await page.evaluate(() => window.postMessage({ type: 'peach-userscript:ready', protocol: 1,
version: '9.0.0', paired: true, nonce: crypto.randomUUID() }, location.origin));
assert.equal(await connection.getByText('未检测到', { exact: true }).count(), 1);
assert.equal(await region.getByRole('link', { name: '安装 Peach 用户脚本' }).count(), 1);
assert.ok((await region.innerText()).includes('允许用户脚本'));
assert.equal(await region.getByRole('link', { name: 'Tampermonkey 权限设置说明' }).getAttribute('href'),
'https://www.tampermonkey.net/faq.php#Q209');
assert.deepEqual(opened.problems, []);
} finally { await opened.close(); }
});
});