refactor: unify task and habit list rows
This commit is contained in:
@@ -10,6 +10,24 @@ async function mutate(request: APIRequestContext, baseURL: string, path: string,
|
||||
return request.fetch(path, { ...options, headers: { ...options?.headers, 'x-csrf-token': await csrf(request), origin: baseURL } })
|
||||
}
|
||||
|
||||
async function createHabit(request: APIRequestContext, baseURL: string, name: string, extra: Record<string, unknown> = {}) {
|
||||
const response = await mutate(request, baseURL, '/api/v1/habits', {
|
||||
method: 'POST',
|
||||
data: { name, kind: 'boolean', schedule_type: 'daily', ...extra },
|
||||
})
|
||||
expect(response.ok(), await response.text()).toBeTruthy()
|
||||
return response.json() as Promise<{ id: string }>
|
||||
}
|
||||
|
||||
function shanghaiToday() {
|
||||
const parts = new Intl.DateTimeFormat('en-CA', {
|
||||
timeZone: 'Asia/Shanghai', year: 'numeric', month: '2-digit', day: '2-digit', weekday: 'short',
|
||||
}).formatToParts(new Date())
|
||||
const value = (type: Intl.DateTimeFormatPartTypes) => parts.find(part => part.type === type)!.value
|
||||
const weekdays: Record<string, number> = { Mon: 0, Tue: 1, Wed: 2, Thu: 3, Fri: 4, Sat: 5, Sun: 6 }
|
||||
return { day: `${value('year')}-${value('month')}-${value('day')}`, weekday: weekdays[value('weekday')] }
|
||||
}
|
||||
|
||||
function bottomTab(page: Page, name: string) {
|
||||
return page.getByRole('navigation', { name: '主要导航' }).getByRole('button', { name, exact: true })
|
||||
}
|
||||
@@ -39,8 +57,55 @@ async function expectNotClipped(locator: Locator) {
|
||||
expect(metrics.scrollHeight).toBeLessThanOrEqual(metrics.clientHeight)
|
||||
}
|
||||
|
||||
test('boolean habits complete in place while paused and unscheduled rows explain why they are read-only', async ({ page, request, baseURL }, testInfo) => {
|
||||
const suffix = `${testInfo.project.name}-${testInfo.workerIndex}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
|
||||
const today = shanghaiToday()
|
||||
const booleanName = `布尔习惯-${suffix}`
|
||||
const pausedName = `暂停习惯-${suffix}`
|
||||
const unscheduledName = `未安排习惯-${suffix}`
|
||||
await createHabit(request, baseURL!, booleanName)
|
||||
const paused = await createHabit(request, baseURL!, pausedName)
|
||||
await createHabit(request, baseURL!, unscheduledName, { schedule_type: 'weekly', weekdays: [(today.weekday + 1) % 7] })
|
||||
const pause = await mutate(request, baseURL!, `/api/v1/habits/${paused.id}/pauses`, {
|
||||
method: 'POST', data: { start_date: today.day, end_date: today.day },
|
||||
})
|
||||
expect(pause.ok(), await pause.text()).toBeTruthy()
|
||||
|
||||
await page.goto('/')
|
||||
await bottomTab(page, '习惯').click()
|
||||
const booleanRow = page.locator('.habit-row').filter({ hasText: booleanName })
|
||||
const booleanCheck = booleanRow.getByRole('button', { name: `完成${booleanName}一次` })
|
||||
const checkGeometry = await booleanCheck.evaluate((element) => {
|
||||
const control = element.getBoundingClientRect()
|
||||
const row = element.closest('.habit-row')!.getBoundingClientRect()
|
||||
return {
|
||||
visibleWidth: control.width - Math.max(row.left - control.left, 0) - Math.max(control.right - row.right, 0),
|
||||
height: control.height,
|
||||
}
|
||||
})
|
||||
expect(checkGeometry.visibleWidth).toBeGreaterThanOrEqual(44)
|
||||
expect(checkGeometry.height).toBeGreaterThanOrEqual(44)
|
||||
await booleanCheck.click()
|
||||
await expect(booleanRow).toHaveClass(/done/)
|
||||
await expect(booleanRow.getByRole('button', { name: `减少${booleanName}一次` })).toHaveAttribute('aria-pressed', 'true')
|
||||
await page.reload()
|
||||
const persistedBooleanRow = page.locator('.habit-row').filter({ hasText: booleanName })
|
||||
await expect(persistedBooleanRow).toHaveClass(/done/)
|
||||
await persistedBooleanRow.locator('.habit-main').focus()
|
||||
await persistedBooleanRow.locator('.habit-main').press('Enter')
|
||||
await expect(page.getByRole('dialog', { name: booleanName })).toBeVisible()
|
||||
await page.getByRole('button', { name: '关闭习惯详情' }).click()
|
||||
|
||||
const pausedRow = page.locator('.habit-row').filter({ hasText: pausedName })
|
||||
await expect(pausedRow).toContainText('今天已暂停')
|
||||
await expect(pausedRow.getByRole('button', { name: '今天已暂停' })).toBeDisabled()
|
||||
const unscheduledRow = page.locator('.habit-row').filter({ hasText: unscheduledName })
|
||||
await expect(unscheduledRow).toContainText('今天未安排')
|
||||
await expect(unscheduledRow.getByRole('button', { name: '今天未安排' })).toBeDisabled()
|
||||
})
|
||||
|
||||
test('approved polish keeps search state, dense rows, title-only memos, and unique detail titles', async ({ page, request, baseURL }, testInfo) => {
|
||||
const suffix = testInfo.project.name
|
||||
const suffix = `${testInfo.project.name}-${testInfo.workerIndex}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
|
||||
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)
|
||||
@@ -60,7 +125,7 @@ test('approved polish keeps search state, dense rows, title-only memos, and uniq
|
||||
})
|
||||
expect(memoResponse.ok(), await memoResponse.text()).toBeTruthy()
|
||||
|
||||
const day = new Date().toLocaleDateString('sv-SE')
|
||||
const day = shanghaiToday().day
|
||||
const countdownTitle = `紧凑倒数-${suffix}`
|
||||
const countdownResponse = await mutate(request, baseURL!, '/api/v1/countdowns', {
|
||||
method: 'POST',
|
||||
@@ -78,8 +143,16 @@ test('approved polish keeps search state, dense rows, title-only memos, and uniq
|
||||
await searchInput.fill(`搜索保留-${suffix}`)
|
||||
const taskRow = page.locator('.task-row').filter({ hasText: taskTitle })
|
||||
await expect(taskRow).toHaveCount(1)
|
||||
await expectHeightInRange(taskRow, 64, 68)
|
||||
await expectHeightInRange(taskRow, 57, 59)
|
||||
await expectNotClipped(taskRow)
|
||||
const transparent = 'rgba(0, 0, 0, 0)'
|
||||
await expect(taskRow).toHaveCSS('background-color', transparent)
|
||||
await taskRow.hover()
|
||||
await expect(taskRow).toHaveCSS('background-color', transparent)
|
||||
await taskRow.locator('.task-main').click()
|
||||
await expect(taskRow).toHaveClass(/selected/)
|
||||
await expect(taskRow).toHaveCSS('background-color', transparent)
|
||||
await page.keyboard.press('Escape')
|
||||
await searchInput.press('Escape')
|
||||
const collapsedToggle = page.getByRole('button', { name: '展开搜索任务' })
|
||||
await expect(collapsedToggle).toBeFocused()
|
||||
@@ -96,7 +169,7 @@ test('approved polish keeps search state, dense rows, title-only memos, and uniq
|
||||
await page.getByRole('button', { name: '添加习惯', exact: true }).click()
|
||||
const habitRow = page.locator('.habit-row').filter({ hasText: habitName })
|
||||
await expect(habitRow).toHaveCount(1)
|
||||
await expectHeightInRange(habitRow, 84, 88)
|
||||
await expectHeightInRange(habitRow, 57, 59)
|
||||
await expectNotClipped(habitRow)
|
||||
await habitRow.getByRole('button', { name: `查看习惯详情:${habitName}` }).click()
|
||||
const habitDetail = page.getByRole('dialog', { name: habitName })
|
||||
|
||||
Reference in New Issue
Block a user