import assert from 'node:assert/strict'; import { after, before, describe, it } from 'node:test'; import type { Browser } from 'playwright-core'; import { launch, VIEWPORTS } from './harness.ts'; import { openFollowFeed } from './follow-fixture.ts'; import { openItemPage } from './item-fixture.ts'; describe('卡片信息与收藏入口', () => { let browser: Browser; before(async () => { browser = await launch() }); after(async () => { await browser.close() }); for (const viewport of VIEWPORTS) { it(`${viewport.name} 标题与署名相邻,标签有间距,${viewport.mobile ? '卡片按内容定高' : '同排卡片等高'}`, async () => { const opened = await openFollowFeed(browser, '/follow', viewport); try { assert.equal(await opened.page.locator('[data-follow-filter-glass]').getByRole('button', { name: '我的收藏', exact: true }).count(), 1); const payload = await opened.page.evaluate(async () => (await fetch('/api/follow')).json()); payload.groups[0].primary.title = '这是一个很长的作品标题'.repeat(8); payload.groups[1].primary.title = '短标题'; await opened.page.route(url => url.pathname === '/api/follow' && !url.searchParams.has('item'), route => route.fulfill({ json: payload })); await opened.page.reload(); await opened.page.locator('[data-follow-list] [data-follow-item]').first().waitFor(); const inspect = () => opened.page.locator('[data-follow-list] [data-follow-item]').evaluateAll(cards => cards.slice(0, 6).map(card => { const box = (selector: string) => card.querySelector(selector)!.getBoundingClientRect(); const title = box('[data-media-title]'), byline = box('[data-follow-byline]'), labels = box('[data-follow-labels]'); return { gap: byline.top - title.bottom, labelGap: labels.top + 4 - byline.bottom, height: card.getBoundingClientRect().height, title: title.height, line: parseFloat(getComputedStyle(card.querySelector('[data-media-title]')!).lineHeight) }; })); const rows = await inspect(); assert(rows[0].title >= rows[0].line * 1.9 && rows[1].title < rows[1].line * 1.5, JSON.stringify(rows)); assert(rows.every(row => row.gap >= 4 && row.gap <= 8 && row.labelGap >= 4 && row.labelGap <= 8), JSON.stringify(rows)); if (viewport.mobile) { assert(Math.abs((rows[0].height - rows[1].height) - (rows[0].title - rows[1].title)) <= 1, JSON.stringify(rows)); } else { assert(Math.max(...rows.map(row => row.height)) - Math.min(...rows.map(row => row.height)) <= 1, JSON.stringify(rows)); } await opened.page.evaluate(() => { document.body.dataset.density = 'dense' }); const dense = await inspect(); assert(dense.every(row => row.title < row.line * (viewport.mobile ? 2.1 : 1.5) && row.labelGap >= 2 && row.labelGap <= 8), JSON.stringify(dense)); if (viewport.mobile) { assert(dense[0].title >= dense[0].line * 1.9, JSON.stringify(dense)); assert(Math.abs((dense[0].height - dense[1].height) - (dense[0].title - dense[1].title)) <= 1, JSON.stringify(dense)); } } finally { await opened.close() } }); it(`${viewport.name} 收藏入口是紧凑图标按钮,播放媒体没有侧箭头`, async () => { const opened = await openItemPage(browser, '/', viewport, { ready: '[data-catalog-frame]' }); try { const geometry = await opened.page.locator('[data-shortcuts-trigger]').evaluate(node => { const button = node.getBoundingClientRect(), readout = document.querySelector('[data-catalog-readout]')!.getBoundingClientRect(); return { width: button.width, height: button.height, text: node.textContent?.trim(), glide: !!node.parentElement?.querySelector('[data-view-glide]'), gap: button.left - readout.right, parent: node.closest('[data-entity-sorts]') !== null }; }); assert.equal(geometry.parent, false); assert(geometry.width >= 30 && geometry.width <= 40 && geometry.width === geometry.height && geometry.gap >= 0 && geometry.gap <= 16, JSON.stringify(geometry)); assert.equal(geometry.text, ''); assert.equal(geometry.glide, false); await opened.page.goto(new URL('/item/31', opened.page.url()).href); await opened.page.locator('[data-carousel-video-active]').waitFor(); assert.equal(await opened.page.locator('[data-carousel-video-active] [data-follow-image-step]:visible').count(), 0); await opened.page.locator('[data-follow-image-item="-1"]').click(); assert.equal(await opened.page.locator('[data-follow-image-step]:visible').count(), 2); } finally { await opened.close() } }); } });