Files
dodo/frontend/e2e/today-plain-list.spec.ts
T
bboysoul 66589dd458
ci / gitleaks (push) Successful in 10s
ci / docker (push) Successful in 7m27s
feat: simplify today into plain list
2026-09-17 18:04:40 +08:00

96 lines
5.5 KiB
TypeScript

import { expect, test } from './fixtures'
const widths = [721, 720, 390, 375]
test('Today plain-list layout matches the approved responsive geometry', async ({ page }) => {
await page.goto('/')
for (const width of widths) {
await page.setViewportSize({ width, height: width <= 390 ? 844 : 900 })
await expect(page.locator('.today-environment')).toBeVisible()
await expect(page.getByRole('button', { name: '刷新当前页面' })).toBeVisible()
await expect(page.getByRole('switch', { name: '显示已完成' })).toHaveCount(1)
await expect(page.locator('.today-environment__gold')).toContainText('Au99.99')
const metrics = await page.evaluate(() => {
const rect = (selector: string) => document.querySelector<HTMLElement>(selector)!.getBoundingClientRect()
const environment = document.querySelector<HTMLElement>('.today-environment')!
const calendar = document.querySelector<HTMLElement>('.today-environment__calendar')!
const weather = document.querySelector<HTMLElement>('.today-environment__weather')!
const gold = document.querySelector<HTMLElement>('.today-environment__gold')!
const title = document.querySelector<HTMLElement>('.today-page-title')!
const remaining = document.querySelector<HTMLElement>('.today-remaining')!
const filter = document.querySelector<HTMLElement>('.today-inline-filter')!
const firstSection = document.querySelector<HTMLElement>('.today-section-toggle')!
const main = document.querySelector<HTMLElement>('main.today-main')!
const content = rect('.today-context')
const orderedElements = [environment, title, remaining, filter, firstSection]
const ordered = orderedElements.map((element) => {
const box = element.getBoundingClientRect()
return { left: box.left, right: box.right, top: box.top, bottom: box.bottom, width: box.width, height: box.height }
})
const tolerance = 1
const orderedPairs = ordered.slice(0, -1).map((current, index) => ({
previousBottom: current.bottom,
nextTop: ordered[index + 1].top,
separated: current.bottom <= ordered[index + 1].top + tolerance,
}))
const positiveGeometry = ordered.every(box => box.width > 0 && box.height > 0)
const insideContent = ordered.every(box => box.left >= content.left - tolerance && box.right <= content.right + tolerance)
const visible = (element: HTMLElement) => {
const style = getComputedStyle(element)
return style.visibility !== 'hidden' && style.display !== 'none' && element.getBoundingClientRect().width > 0
}
const rows = [...environment.children]
.filter((node): node is HTMLElement => node instanceof HTMLElement && visible(node) && !node.classList.contains('sr-only'))
.map(node => Math.round(node.getBoundingClientRect().top))
const uniqueRows = [...new Set(rows)]
const collisions = [calendar, weather, gold].some((a, index, items) => items.slice(index + 1).some(b => {
const ar = a.getBoundingClientRect(); const br = b.getBoundingClientRect()
return ar.left < br.right && ar.right > br.left && ar.top < br.bottom && ar.bottom > br.top
}))
const secondary = [...environment.querySelectorAll<HTMLElement>('small')].map(node => ({ scrollWidth: node.scrollWidth, clientWidth: node.clientWidth, visible: visible(node) }))
return {
viewport: innerWidth,
documentOverflow: document.documentElement.scrollWidth - document.documentElement.clientWidth,
mainOverflow: main.scrollWidth - main.clientWidth,
content: { left: content.left, right: content.right, width: content.width },
environment: { ...environment.getBoundingClientRect().toJSON(), scrollWidth: environment.scrollWidth, clientWidth: environment.clientWidth },
directions: { calendar: getComputedStyle(calendar).flexDirection, weather: getComputedStyle(weather).flexDirection, gold: getComputedStyle(gold).flexDirection },
uniqueRows,
collisions,
secondary,
ordered,
orderedPairs,
positiveGeometry,
insideContent,
weatherText: weather.innerText,
goldText: gold.innerText,
goldLabel: gold.querySelector('.today-environment__gold-date')?.getAttribute('aria-label'),
}
})
console.log(`[today-geometry][${width}] ${JSON.stringify(metrics)}`)
expect(metrics.documentOverflow).toBe(0)
expect(metrics.mainOverflow).toBeLessThanOrEqual(0)
expect(metrics.environment.scrollWidth).toBeLessThanOrEqual(metrics.environment.clientWidth)
expect(metrics.collisions).toBeFalsy()
expect(metrics.positiveGeometry).toBeTruthy()
expect(metrics.insideContent).toBeTruthy()
expect(metrics.orderedPairs.every(pair => pair.separated)).toBeTruthy()
expect(metrics.goldText).toContain('Au99.99')
expect(metrics.goldLabel).toMatch(/市场日期 \d{4}-\d{2}-\d{2}/)
expect(metrics.secondary.every(line => line.visible && line.scrollWidth <= line.clientWidth)).toBeTruthy()
if (width === 721) {
expect(metrics.uniqueRows).toHaveLength(1)
expect(metrics.environment.height).toBeLessThan(80)
} else {
expect(metrics.uniqueRows).toHaveLength(2)
expect(metrics.directions.calendar).toBe('row')
expect(metrics.directions.weather).toBe('column')
expect(metrics.directions.gold).toBe('column')
}
if (width >= 721) {
expect(metrics.content.width).toBeLessThanOrEqual(721)
expect(Math.abs(metrics.content.left - (width - metrics.content.width) / 2)).toBeLessThanOrEqual(2)
}
}
})