feat: add local pomodoro focus timer
Frontend-only 25m focus / 5m break timer with pause-resume-reset, deadline-based absolute timing, versioned localStorage persistence, cross-tab conflict protection, midnight-completion attribution, accessible stage controls, and reduced-motion support. Adds 专注 entry to desktop sidebar; mobile bottom nav unchanged; no notifications, task association, history, or cross-device sync. Also fixes mobile E2E task isolation and the stale calendar 390px overflow assertion.
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
describe('pomodoro integration', () => {
|
||||
const app = readFileSync('src/App.vue', 'utf8')
|
||||
const css = readFileSync('src/style.css', 'utf8')
|
||||
const panel = readFileSync('src/PomodoroPanel.vue', 'utf8')
|
||||
|
||||
it('adds focus to desktop navigation and keeps the five-item mobile navigation unchanged', () => {
|
||||
const primary = app.slice(app.indexOf('<nav class="primary-nav">'), app.indexOf('</nav>', app.indexOf('<nav class="primary-nav">')))
|
||||
const bottom = app.slice(app.indexOf('<nav class="bottom"'), app.indexOf('</nav>', app.indexOf('<nav class="bottom"')))
|
||||
|
||||
expect(primary).toContain("switchView('focus')")
|
||||
expect(primary.indexOf("switchView('focus')")).toBeGreaterThan(primary.indexOf("switchView('habits')"))
|
||||
expect(bottom).not.toContain("switchView('focus')")
|
||||
expect(bottom.match(/aria-current=/g)).toHaveLength(5)
|
||||
})
|
||||
|
||||
it('renders the focus panel without the shared FAB and includes accessible stage controls', () => {
|
||||
expect(app).toContain("<PomodoroPanel v-else-if=\"activeView==='focus'\"")
|
||||
expect(app).not.toContain("'focus'].includes(activeView)")
|
||||
expect(css).toContain('.pomodoro-controls button{min-height:44px')
|
||||
expect(css).toContain('@media(prefers-reduced-motion:reduce)')
|
||||
})
|
||||
|
||||
it('coordinates writes across tabs with unique writers and a localStorage conflict check', () => {
|
||||
expect(panel).toContain('const WRITER_ID = crypto.randomUUID()')
|
||||
expect(panel).toContain('const stored = loadPomodoroState(window.localStorage, STORAGE_KEY)')
|
||||
expect(panel).toContain('shouldAcceptPomodoroState(stored, next) ? stored : next')
|
||||
expect(panel).toContain('Date.now(), WRITER_ID')
|
||||
expect(panel).toContain('savePomodoroState(window.localStorage, STORAGE_KEY, state.value)')
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user