feat: strengthen backup and mobile workflows
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
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)
|
||||
})
|
||||
Reference in New Issue
Block a user