feat: add calendar frontend
ci / gitleaks (push) Successful in 8s
ci / docker (push) Successful in 4m41s

This commit is contained in:
2026-09-20 13:21:47 +08:00
parent 583b6a8a9d
commit af83a68fe1
14 changed files with 221 additions and 28 deletions
+6 -1
View File
@@ -8,6 +8,11 @@ function bottomTab(page: Page, name: string) {
return page.getByRole('navigation', { name: '主要导航' }).getByRole('button', { name, exact: true })
}
async function openSidebarView(page: Page, name: string) {
await page.getByRole('button', { name: /展开菜单|收起菜单/ }).click()
await page.locator('.sidebar').getByRole('button', { name, exact: true }).click()
}
async function csrf(request: APIRequestContext, baseURL: string) {
const state = await request.storageState()
return state.cookies.find(cookie => cookie.name === 'dodo_csrf' && baseURL.includes(cookie.domain))?.value
@@ -50,7 +55,7 @@ test('complete ZIP backup preflights and replace-restores task, habit history, c
expect(countdownResponse.ok()).toBeTruthy()
await page.goto('/')
await bottomTab(page, '设置').click()
await openSidebarView(page, '设置')
const downloadPromise = page.waitForEvent('download')
await page.getByRole('button', { name: '导出 ZIP' }).click()
const download = await downloadPromise
+15 -12
View File
@@ -6,11 +6,6 @@ function bottomTab(page: Page, name: string) {
}
async function openSettings(page: Page) {
const mobileTab = bottomTab(page, '设置')
if (await mobileTab.isVisible()) {
await mobileTab.click()
return
}
const desktopSettings = page.getByRole('navigation', { name: '管理' }).getByRole('button', { name: '设置', exact: true })
const box = await desktopSettings.boundingBox()
if (box && box.x + box.width > 0 && box.y + box.height > 0 && box.x < (await page.viewportSize())!.width) await desktopSettings.click()
@@ -159,15 +154,21 @@ test('bottom navigation keeps its safe-area gap after dragging', async ({ page }
})
test('all bottom destinations expose one active page and desktop layout stays unchanged', async ({ page }) => {
await page.route('**/api/v1/calendar-subscriptions', route => route.fulfill({ json: [] }))
await page.route('**/api/v1/calendar-events?*', route => route.fulfill({ json: { events: [], sources: [] } }))
await page.goto('/')
const navigation = page.getByRole('navigation', { name: '主要导航' })
for (const label of ['今天', '习惯', '倒数日', '设置']) {
await bottomTab(page, label).click()
await expect(navigation.locator('[aria-current="page"]')).toHaveCount(1)
await expect(bottomTab(page, label)).toHaveAttribute('aria-current', 'page')
const mobile = (await page.viewportSize())!.width <= 930
if (mobile) {
for (const label of ['今天', '习惯', '倒数日', '备忘录', '日历']) {
await bottomTab(page, label).click()
await expect(navigation.locator('[aria-current="page"]')).toHaveCount(1)
await expect(bottomTab(page, label)).toHaveAttribute('aria-current', 'page')
}
await bottomTab(page, '今天').click()
} else {
await expect(navigation).toBeHidden()
}
await bottomTab(page, '今天').click()
await page.setViewportSize({ width: 1440, height: 900 })
const desktop = await page.locator('.shell').evaluate(element => {
const shell = getComputedStyle(element)
@@ -202,7 +203,9 @@ test('all bottom destinations expose one active page and desktop layout stays un
test('settings match the approved paper-ledger geometry and action hierarchy', async ({ page }) => {
await page.goto('/')
await openSettings(page)
if ((await page.viewportSize())!.width <= 720) await expect(bottomTab(page, '设置')).toHaveAttribute('aria-current', 'page')
if ((await page.viewportSize())!.width <= 720) {
await expect(page.getByRole('navigation', { name: '主要导航' }).getByRole('button', { name: '设置', exact: true })).toHaveCount(0)
}
const groups = page.locator('.settings-group')
await expect(groups).toHaveCount(4)
const layout = await page.locator('.settings-sections').evaluate(element => {
+1 -1
View File
@@ -130,7 +130,7 @@ test('task rows use the body for detail and Trash keeps distinct actions', async
test('Settings removes intro/empty danger and places mode-specific restore risk copy correctly', async ({ page }, testInfo) => {
await page.goto('/')
await bottomTab(page, '设置').click()
await openSidebarView(page, '设置')
expect(await page.locator('.view-intro').count()).toBe(0)
await expect(page.locator('.settings-group')).toHaveCount(4)
expect(await page.locator('.settings-danger').count()).toBe(0)