import type { APIRequestContext, Page } from '@playwright/test' import { expect, test } from './fixtures' async function csrf(request: APIRequestContext) { const state = await request.storageState() return state.cookies.find(cookie => cookie.name === 'dodo_csrf')?.value ?? '' } async function createTask(request: APIRequestContext, baseURL: string, listId: string, title: string) { const response = await request.post('/api/v1/tasks', { data: { title, list_id: listId }, headers: { 'x-csrf-token': await csrf(request), origin: baseURL }, }) expect(response.ok(), await response.text()).toBeTruthy() } async function openInbox(page: Page) { const inbox = page.locator('.sidebar').getByRole('button', { name: '收集箱', exact: true }) if (await page.evaluate(() => window.innerWidth <= 930)) { await page.locator('main .topbar > button').first().click() } await inbox.click() } test('task pagination stays below the list and returns to the list start after navigation', async ({ page, request, baseURL }) => { const bootstrapResponse = await request.get('/api/v1/bootstrap') expect(bootstrapResponse.ok()).toBeTruthy() const inbox = (await bootstrapResponse.json()).lists.find((item: { is_inbox: boolean }) => item.is_inbox) expect(inbox).toBeTruthy() for (let index = 1; index <= 51; index += 1) { await createTask(request, baseURL!, inbox.id, `分页验收任务 ${String(index).padStart(2, '0')}`) } await page.goto('/') await openInbox(page) const taskList = page.locator('.task-list') const pager = page.locator('.pager') await expect(taskList.locator('.task-row')).toHaveCount(50) await expect(pager).toBeVisible() await expect(pager.locator('.pager-status')).toHaveText('1/ 2') const firstPageGeometry = await page.evaluate(() => { const list = document.querySelector('.task-list')!.getBoundingClientRect() const pagerElement = document.querySelector('.pager')! const pagerBox = pagerElement.getBoundingClientRect() const previous = pagerElement.querySelector('.pager-button--previous')! const next = pagerElement.querySelector('.pager-button--next')! const status = pagerElement.querySelector('.pager-status')! const previousBox = previous.getBoundingClientRect() const nextBox = next.getBoundingClientRect() const statusBox = status.getBoundingClientRect() const pagerCenter = pagerBox.left + pagerBox.width / 2 return { listBottom: list.bottom, pagerTop: pagerBox.top, previousWidth: previousBox.width, nextWidth: nextBox.width, previousHeight: previousBox.height, nextHeight: nextBox.height, statusCenterOffset: Math.abs(statusBox.left + statusBox.width / 2 - pagerCenter), rootScrollWidth: document.documentElement.scrollWidth, viewportWidth: innerWidth, } }) expect(firstPageGeometry.pagerTop).toBeGreaterThanOrEqual(firstPageGeometry.listBottom) expect(Math.abs(firstPageGeometry.previousWidth - firstPageGeometry.nextWidth)).toBeLessThanOrEqual(1) expect(firstPageGeometry.previousHeight).toBeGreaterThanOrEqual(44) expect(firstPageGeometry.nextHeight).toBeGreaterThanOrEqual(44) expect(firstPageGeometry.statusCenterOffset).toBeLessThanOrEqual(1) expect(firstPageGeometry.rootScrollWidth).toBeLessThanOrEqual(firstPageGeometry.viewportWidth) const nextButton = pager.getByRole('button', { name: '下一页' }) await page.locator('body').click({ position: { x: 1, y: 1 } }) for (let index = 0; index < 300; index += 1) { await page.keyboard.press('Tab') if (await nextButton.evaluate(element => element === document.activeElement)) break } await expect(nextButton).toBeFocused() const focusOutline = await nextButton.evaluate(element => getComputedStyle(element).outlineStyle) expect(focusOutline).not.toBe('none') await nextButton.click() await expect(pager.locator('.pager-status')).toHaveText('2/ 2') await expect(taskList.locator('.task-row')).toHaveCount(1) const secondPageGeometry = await page.evaluate(() => { const list = document.querySelector('.task-list')!.getBoundingClientRect() const pagerBox = document.querySelector('.pager')!.getBoundingClientRect() return { listTop: list.top, listBottom: list.bottom, pagerTop: pagerBox.top, viewportHeight: innerHeight } }) expect(secondPageGeometry.listTop).toBeGreaterThanOrEqual(-1) expect(secondPageGeometry.listTop).toBeLessThan(secondPageGeometry.viewportHeight / 2) expect(secondPageGeometry.pagerTop).toBeGreaterThanOrEqual(secondPageGeometry.listBottom) })