fix: refine mobile navigation and settings
ci / gitleaks (push) Successful in 7s
ci / docker (push) Successful in 3m27s

This commit is contained in:
2026-09-10 10:38:39 +08:00
parent 125e6d3a61
commit 7c05ec2a85
6 changed files with 136 additions and 9 deletions
+35 -2
View File
@@ -1,5 +1,5 @@
import { describe, expect, it } from 'vitest'
import { calendarModeLabel, changedHabitFields, clampFabPosition, countdownDayText, countdownKindLabel, dateKey, defaultView, formatHabitApiError, habitActionState, habitButtonNotice, habitButtonValue, habitWeek, isFabDrag, isHabitComplete, isHabitScheduledToday, isTaskView, nextHabitSwipeValue, nextTotalAfterLocalTaskAdd, normalizeRequiredName, numericHabitInputValue, previousHabitSwipeValue, quickTaskFields, readCountdownCache, readHabitGridCache, readStoredBoolean, readStoredNavigation, shouldToggleRowSwipe, validateHabitForm, writeCountdownCache, writeHabitGridCache, writeStoredBoolean, writeStoredNavigation } from './mvp-utils'
import { calendarModeLabel, changedHabitFields, clampFabPosition, countdownDayText, countdownKindLabel, dateKey, defaultView, formatAuditAction, formatAuditEntity, formatHabitApiError, formatLocalShortDateTime, formatUserAgent, habitActionState, habitButtonNotice, habitButtonValue, habitWeek, isFabDrag, isHabitComplete, isHabitScheduledToday, isTaskView, nextHabitSwipeValue, nextTotalAfterLocalTaskAdd, normalizeRequiredName, numericHabitInputValue, previousHabitSwipeValue, quickTaskFields, readCountdownCache, readHabitGridCache, readStoredBoolean, readStoredNavigation, shouldToggleRowSwipe, validateHabitForm, writeCountdownCache, writeHabitGridCache, writeStoredBoolean, writeStoredNavigation } from './mvp-utils'
describe('MVP view utilities', () => {
it('formats a local date as YYYY-MM-DD', () => {
@@ -171,7 +171,40 @@ describe('MVP view utilities', () => {
expect(habitActionState({ scheduled: true, paused: false }, true)).toEqual({ writable: false, reason: '该习惯已归档' })
})
it('distinguishes tapping the add button from dragging it', () => {
it('formats conservative device and browser summaries without leaking unknown user agents', () => {
expect(formatUserAgent('Mozilla/5.0 (iPhone; CPU iPhone OS 18_0 like Mac OS X) AppleWebKit/605.1.15 Version/18.0 Mobile/15E148 Safari/604.1')).toBe('iPhone · Safari')
expect(formatUserAgent('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 Chrome/128.0 Safari/537.36')).toBe('Mac · Chrome')
expect(formatUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/128.0 Safari/537.36 Edg/128.0')).toBe('Windows · Edge')
expect(formatUserAgent('Mozilla/5.0 (Linux; Android 14; Pixel 8) AppleWebKit/537.36 Chrome/128.0 Mobile Safari/537.36')).toBe('Android 手机 · Chrome')
expect(formatUserAgent('Mozilla/5.0 (Linux; Android 14; SM-X710) AppleWebKit/537.36 Chrome/128.0 Safari/537.36')).toBe('Android 平板 · Chrome')
expect(formatUserAgent('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15) AppleWebKit/605.1.15 Version/17.0 Mobile/15E148 Safari/604.1')).toBe('iPad · Safari')
expect(formatUserAgent('curl/8.7.1')).toBe('未知设备 · 未知浏览器')
expect(formatUserAgent(null)).toBe('未知设备 · 未知浏览器')
})
it('formats zoned and timezone-less ISO values in the selected local timezone', () => {
expect(formatLocalShortDateTime('2026-12-31T23:30:00Z', { timeZone: 'Asia/Shanghai' })).toMatch(/2027\/1\/1\s+07:30/)
expect(formatLocalShortDateTime('2026-09-10T14:08:00+02:00', { timeZone: 'Asia/Shanghai' })).toMatch(/2026\/9\/10\s+20:08/)
expect(formatLocalShortDateTime('2026-09-10T14:08:00', { timeZone: 'UTC' })).toMatch(/2026\/9\/10\s+14:08/)
expect(formatLocalShortDateTime('not-a-date')).toBe('未知时间')
expect(formatLocalShortDateTime(null)).toBe('未知时间')
})
it('rejects calendar dates that Date would otherwise normalize', () => {
expect(formatLocalShortDateTime('2026-02-30T12:00:00', { timeZone: 'UTC' })).toBe('未知时间')
expect(formatLocalShortDateTime('2026-02-30T12:00:00Z', { timeZone: 'UTC' })).toBe('未知时间')
expect(formatLocalShortDateTime('2026-04-31T12:00:00+08:00', { timeZone: 'UTC' })).toBe('未知时间')
})
it('localizes known audit actions and entities and hides unknown actions', () => {
expect(['create', 'update', 'complete', 'delete', 'archive', 'restore', 'move', 'import'].map(formatAuditAction)).toEqual(['创建', '更新', '完成', '删除', '归档', '恢复', '移动', '导入'])
expect(['task', 'list', 'folder', 'countdown', 'backup'].map(formatAuditEntity)).toEqual(['任务', '清单', '文件夹', '倒数日', '备份'])
expect(formatAuditAction('launch')).toBe('其他操作')
expect(formatAuditAction(null)).toBe('其他操作')
expect(formatAuditEntity('unknown')).toBe('内容')
})
it('distinguishes tapping the mobile add button from dragging it', () => {
expect(isFabDrag(3, 4)).toBe(false)
expect(isFabDrag(8, 0)).toBe(true)
expect(isFabDrag(6, 7)).toBe(true)