Files
dodo/frontend/e2e/mobile-journeys.spec.ts
T
bboysoul 6c234d7d82
ci / gitleaks (push) Successful in 1m19s
ci / docker (push) Successful in 5m48s
feat: strengthen backup and mobile workflows
2026-09-16 21:12:52 +08:00

113 lines
5.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { Locator, Page } from '@playwright/test'
import { allowExpectedError, expect, test } from './fixtures'
async function bottomTab(page: Page, name: string) {
return page.getByRole('navigation', { name: '主要导航' }).getByRole('button', { name, exact: true })
}
function taskRow(page: Page, title: string) {
return page.locator('.task-row').filter({ has: page.locator('strong', { hasText: title }) })
}
async function assertInsideViewport(locator: Locator, page: Page) {
const box = await locator.boundingBox()
const viewport = page.viewportSize()
expect(box).not.toBeNull()
expect(viewport).not.toBeNull()
expect(box!.x).toBeGreaterThanOrEqual(0)
expect(box!.y).toBeGreaterThanOrEqual(0)
expect(box!.x + box!.width).toBeLessThanOrEqual(viewport!.width + 1)
expect(box!.y + box!.height).toBeLessThanOrEqual(viewport!.height + 1)
}
test('Today task persists through detail, completion and reopen', async ({ page }, testInfo) => {
const title = `E2E 今日任务 ${testInfo.project.name}`
await page.goto('/')
await expect(await bottomTab(page, '今天')).toHaveAttribute('aria-current', 'page')
await page.getByRole('button', { name: '添加任务' }).click()
await page.getByLabel('任务名称').fill(title)
await page.getByRole('button', { name: '添加任务', exact: true }).click()
const row = taskRow(page, title)
await expect(row).toHaveCount(1)
const rowMain = row.locator('.task-main')
await rowMain.click()
const detail = page.getByRole('dialog', { name: '任务详情' })
await expect(detail).toBeVisible()
await assertInsideViewport(detail, page)
await page.getByRole('button', { name: '关闭详情' }).click()
await expect(rowMain).toBeFocused()
await row.getByRole('button', { name: `完成${title}` }).click()
await expect(row).toHaveClass(/done/)
await page.reload()
const persisted = taskRow(page, title)
await expect(persisted).toHaveCount(1)
await expect(persisted.getByRole('button', { name: `重新打开${title}` })).toBeVisible()
await persisted.getByRole('button', { name: `重新打开${title}` }).click()
await expect(persisted.getByRole('button', { name: `完成${title}` })).toBeVisible()
})
test('numeric habit records history, edits target, and continues', async ({ page }, testInfo) => {
const name = `E2E 数量习惯 ${testInfo.project.name}`
await page.goto('/')
await (await bottomTab(page, '习惯')).click()
await expect(await bottomTab(page, '习惯')).toHaveAttribute('aria-current', 'page')
await page.getByRole('button', { name: '添加习惯' }).click()
await page.getByLabel('新习惯名称').fill(name)
await page.getByLabel('习惯类型').selectOption('numeric')
await page.getByLabel('目标值').fill('2')
await page.getByRole('button', { name: '添加习惯', exact: true }).click()
const row = page.locator('.habit-row').filter({ hasText: name })
await expect(row).toHaveCount(1)
const check = row.getByRole('button', { name: `完成${name}一次` })
await check.click()
await expect(row).toContainText('1 / 2')
await check.click()
await expect(row).toContainText('2 / 2')
await row.getByRole('button', { name: `查看习惯详情:${name}` }).click()
const detail = page.getByRole('dialog', { name: name })
await expect(detail.getByRole('heading', { name: '历史记录' })).toBeVisible()
await expect(detail.locator('.habit-history__row')).toContainText('2 / 2')
await detail.getByRole('button', { name: '编辑习惯' }).click()
await page.getByLabel('目标值').fill('3')
await page.getByRole('button', { name: '保存修改' }).click()
await expect(row).toContainText('2 / 3')
await row.getByRole('button', { name: `完成${name}一次` }).click()
await expect(row).toContainText('3 / 3')
await page.reload()
const persistedRow = page.locator('.habit-row').filter({ hasText: name })
await expect(persistedRow).toContainText('3 / 3')
await persistedRow.getByRole('button', { name: `查看习惯详情:${name}` }).click()
const persistedDetail = page.getByRole('dialog', { name })
await expect(persistedDetail.locator('.habit-history__row')).toContainText('3 / 3')
})
test('countdown archive and restore keeps one entity after refresh', async ({ page }, testInfo) => {
const title = `E2E 倒数日 ${testInfo.project.name}`
await page.goto('/')
await (await bottomTab(page, '倒数日')).click()
await page.getByRole('button', { name: '添加倒数日' }).click()
await page.getByLabel('倒数日名称').fill(title)
await page.getByRole('button', { name: '保存', exact: true }).click()
const item = page.getByRole('button').filter({ hasText: title })
await expect(item).toHaveCount(1)
await item.click()
const detail = page.getByRole('dialog', { name: title })
await expect(detail).toBeVisible()
// The UI intentionally aborts its DELETE fetch after the 204 response while closing the detail sheet.
allowExpectedError(page, 'requestfailed: DELETE http://127.0.0.1:5173/api/v1/countdowns/')
await Promise.all([
page.waitForResponse(response => response.url().includes(`/api/v1/countdowns/`) && response.request().method() === 'DELETE' && response.status() === 204),
detail.getByRole('button', { name: '归档' }).click(),
])
await page.getByRole('button', { name: /已归档(1/ }).click()
const archived = page.locator('.archived-countdowns article').filter({ hasText: title })
await expect(archived).toHaveCount(1)
await archived.getByRole('button', { name: '恢复' }).click()
await page.reload()
await expect(page.getByText(title, { exact: true })).toHaveCount(1)
})