frontend/e2e/want-resources.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, settle, visit, VIEWPORTS } from './harness.ts';
describe('JAV 入库作品卡', () => {
let browser: Browser;
before(async () => { browser = await launch(); });
after(async () => { await browser?.close(); });
for (const viewport of VIEWPORTS) {
it(`资源链接与操作完整可见(${viewport.name})`, { timeout: 60_000 }, async () => {
const opened = await visit(browser, '/jav', viewport);
const page = opened.page;
try {
await settle(page);
await page.route('**/api/wants', route => route.fulfill({ json: { items: [{
id: 9001, code: 'DEMO-001', title: '演示作品'.repeat(30), origin: 'code', phase: 'searching',
link: null, release_date: '2025-01-01', studio: '演示厂牌', performers: '演示人物',
fresh: false, search_count: 0, cover: null, scrape_error: null,
}], counts: { searching: 1 }, scraping: false } }));
let resourceRequests = 0;
await page.route('**/api/wants/magnets?*', route => {
resourceRequests += 1;
return route.fulfill({ json: {
state: 'ready', error: '', checked_at: null, warnings: [], source_url: 'https://example.test/work', items: [{
id: 'magnet:abc', protocol: 'magnet', info_hash: 'abc', uri: 'magnet:?xt=urn:btih:abc',
name: 'DEMO-001'.repeat(24), size: '4 GB', files: '2 個文件', date: '2026-10-01',
attributes: ['字幕', '高清'], source: 'JavDB', source_url: 'https://example.test/resources',
origins: ['https://example.test/resources'],
}, {
id: 'ed2k:abc', protocol: 'ed2k', info_hash: '', uri: 'ed2k://|file|demo|1|abc|/', name: '评论补充文件',
size: '27,311,519,717 B', files: '', date: '2026-10-02', attributes: [], source: 'JavDB 评论',
source_url: 'https://example.test/comment', origins: ['https://example.test/comment'],
}, ...Array.from({ length: 6 }, (_, index) => ({
id: `magnet:extra-${index}`, protocol: 'magnet', info_hash: `extra-${index}`, uri: `magnet:?xt=urn:btih:extra-${index}`,
name: `DEMO-001 资源 ${index}`, size: '2 GB', files: '1 個文件', date: '2025-01-01',
attributes: [], source: 'JavDB', source_url: 'https://example.test/resources', origins: [],
}))],
} }); });
await page.route('**/api/downloads?*', route => route.fulfill({ json: { available: true,
providers: [{ key: '115', label: '115', configured: true, target: '/下载' }], tasks: [] } }));
await page.getByRole('tab', { name: '想要', exact: true }).click();
await page.locator('[data-want-magnets]').scrollIntoViewIfNeeded();
await page.getByRole('heading', { name: '资源链接 · 8' }).waitFor();
await settle(page);
const card = page.locator('[data-want-id="9001"]');
assert.equal(await page.locator('#stats').getByRole('button', { name: '查找资源', exact: true }).count(), 2, '通用入口与作品入口分别保留一处');
assert.equal(await page.getByRole('combobox', { name: '下载到', exact: true }).count(), 0, '下载目标在资源下载面板中选择');
const source = card.getByRole('link', { name: '来源页', exact: true });
assert.deepEqual(await source.evaluate(node => [...node.parentElement!.querySelectorAll('button, a')].map(action => action.textContent)), ['来源页', '移除', '查找资源']);
const style = await card.evaluate(el => ({ radius: getComputedStyle(el).borderRadius,
border: getComputedStyle(el).borderTopWidth }));
assert.notEqual(style.radius, '0px');
assert.equal(style.border, '0px', '想要卡还带着外框,页面上的卡只靠填充底');
assert.equal(await source.evaluate(el => el.getBoundingClientRect().height), 32);
assert.equal(await source.evaluate(el => getComputedStyle(el).borderTopWidth), '1px');
assert.equal(await source.getAttribute('href'), 'https://example.test/work');
assert.equal(await card.locator('[data-resource-protocol]:visible').count(), 3);
assert.equal((await card.textContent())!.includes('個'), false);
const ed2k = card.locator('[data-resource-protocol="ed2k"]');
assert.ok((await ed2k.textContent())!.includes('ED2K'));
assert.ok((await ed2k.textContent())!.includes('25.4 GB'));
assert.equal(await ed2k.locator('span').first().locator('svg').count(), 0);
const magnetStyle = await card.locator('[data-resource-protocol="magnet"]').first().locator('span').first().evaluate(el => getComputedStyle(el).backgroundColor);
const ed2kStyle = await card.locator('[data-resource-protocol="ed2k"] span').first().evaluate(el => getComputedStyle(el).backgroundColor);
assert.notEqual(magnetStyle, ed2kStyle);
const add = card.getByRole('button', { name: '添加下载', exact: true }).first();
const copy = card.getByRole('button', { name: '复制链接', exact: true }).first();
assert.notEqual(await add.evaluate(el => getComputedStyle(el).backgroundColor), await copy.evaluate(el => getComputedStyle(el).backgroundColor));
assert.equal(await add.locator('img').count(), 1, '下载渠道使用对应的网盘图标');
if (viewport.name === 'desktop') {
const centers = await add.evaluate(el => {
const row = el.closest('[data-resource-protocol]')!.getBoundingClientRect();
const button = el.getBoundingClientRect();
return { row: row.top + row.height / 2, button: button.top + button.height / 2 };
});
assert.ok(Math.abs(centers.row - centers.button) <= 1, JSON.stringify(centers));
}
const geometry = await layout(page);
assert.ok(geometry.scrollWidth <= geometry.viewportWidth + 1, JSON.stringify(geometry));
assert.deepEqual(geometry.offenders, []);
await card.screenshot({ path: `../build/want-resources-${viewport.name}.png` });
await card.locator('summary').click();
await page.waitForFunction(() => document.querySelectorAll('[data-resource-protocol]').length === 8 &&
!document.querySelector('details [inert=""]'));
assert.equal(await card.locator('[data-resource-protocol]:visible').count(), 8);
const phase = page.locator('[data-want-phase="searching"]');
await phase.locator('label:has(input[value="available"])').click();
assert.equal(await phase.locator('[data-want-id]').count(), 1);
await phase.locator('label:has(input[value="missing"])').click();
assert.equal(await phase.locator('[data-want-id]').count(), 0);
await phase.locator('label:has(input[value="available"])').click();
await phase.getByRole('heading', { name: '资源链接 · 8' }).waitFor();
assert.equal(resourceRequests, 1, '切换磁链筛选重复查询已有资源');
await phase.screenshot({ path: `../build/want-filter-${viewport.name}.png` });
await page.getByRole('tab', { name: '馆藏', exact: true }).click();
await page.getByRole('tab', { name: '想要', exact: true }).click();
await page.getByRole('heading', { name: '资源链接 · 8' }).waitFor();
assert.equal(resourceRequests, 1, '返回页面重复查询资源');
await page.reload();
await page.getByRole('tab', { name: '想要', exact: true }).click();
await page.getByRole('heading', { name: '资源链接 · 8' }).waitFor();
assert.equal(resourceRequests, 1, '刷新页面未复用持久缓存');
assert.deepEqual(opened.problems, []);
} finally { await opened.close(); }
});
}
});