diff --git a/tests/cases/36-reminders.test.ts b/tests/cases/36-reminders.test.ts index e7d6f53..546cd77 100644 --- a/tests/cases/36-reminders.test.ts +++ b/tests/cases/36-reminders.test.ts @@ -1,5 +1,5 @@ /** - * 36-reminders: Reminders Vue 组件化测试 + * 36-reminders: Reminders Vue 组件化 — 深层测试 */ import { describe, it, expect, beforeEach, afterEach } from 'vitest' import { h, render, nextTick } from 'vue' @@ -7,22 +7,34 @@ import RemindersComponent from '../../src/apps/reminders/Reminders.vue' import { store } from '../../src/composables/useStore' import { setupDOM } from '../helpers' -function makeMockWin() { const el = document.createElement('div'); el.className = 'window'; document.getElementById('window-layer')?.appendChild(el); const body = document.createElement('div'); body.className = 'win-body'; el.appendChild(body); return { el, body, timers: [] as any[], data: {} } } +function mk() { const e = document.createElement('div'); e.className = 'window'; document.getElementById('window-layer')?.appendChild(e); const b = document.createElement('div'); b.className = 'win-body'; e.appendChild(b); return { el: e, body: b, timers: [] as any[], data: {} } } -describe('36-reminders — Reminders Vue 组件化', () => { - let win: any - beforeEach(() => { localStorage.clear(); store.set('reminders', null); setupDOM(); win = makeMockWin() }) - afterEach(() => { render(null, win.body); win.el.remove() }) - function mount() { const v = h(RemindersComponent, { win }); render(v, win.body) } +describe('36-reminders', () => { + let w: any + beforeEach(() => { localStorage.clear(); store.set('reminders', null); setupDOM(); w = mk() }) + afterEach(() => { render(null, w.body); w.el.remove() }) + function m() { render(h(RemindersComponent, { win: w }), w.body) } - it('显示 3 个列表', () => { mount(); expect(win.body.querySelectorAll('.fb-side-item').length).toBe(3) }) - it('列表有颜色标识', () => { mount(); expect(win.body.querySelector('.rem-color')).toBeTruthy() }) - it('显示未完成计数', () => { mount(); expect(win.body.querySelector('.rem-count')?.textContent).toBeTruthy() }) - it('切换列表', async () => { mount(); const items = win.body.querySelectorAll('.fb-side-item'); (items[1] as HTMLElement).dispatchEvent(new MouseEvent('click', { bubbles: true })); await nextTick(); expect(items[1].classList.contains('sel')).toBe(true) }) - it('显示提醒事项', () => { mount(); expect(win.body.querySelector('.rem-title')).toBeTruthy() }) - // ⚠️ jsdom 中 toggleItem 触发后 class 更新可能需额外 nextTick - it('勾选标记完成', async () => { mount(); const cb = win.body.querySelector('.rem-check') as HTMLElement; expect(cb).toBeTruthy(); const wasDone = cb.classList.contains('done'); cb.dispatchEvent(new MouseEvent('click', { bubbles: true })); await nextTick(); await nextTick(); const cb2 = win.body.querySelector('.rem-check') as HTMLElement; expect(cb2.classList.contains('done')).toBe(!wasDone) }) - it('Enter 添加新事项', async () => { mount(); const input = win.body.querySelector('.rem-new') as HTMLInputElement; input.value = '新提醒'; input.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', bubbles: true })); await nextTick(); expect(input.value).toBe('') }) - it('空列表显示提示', () => { store.set('reminders', { lists: [{ id: 'e1', name: '空', color: '#ff9f0a' }], items: [] }); mount(); expect(win.body.querySelector('.empty-state')?.textContent).toContain('此列表为空') }) - it('卸载', () => { mount(); render(null, win.body); expect(win.body.children.length).toBe(0) }) + it('3个默认列表', () => { m(); expect(w.body.querySelectorAll('.fb-side-item').length).toBe(3) }) + it('颜色标识', () => { m(); expect(w.body.querySelector('.rem-color')).toBeTruthy() }) + it('列表名称', () => { m(); expect(w.body.querySelector('.rem-lname')?.textContent).toBeTruthy() }) + it('未完成计数', () => { m(); expect(w.body.querySelector('.rem-count')?.textContent).toBeTruthy() }) + it('默认选中首列表', () => { m(); expect(w.body.querySelector('.fb-side-item.sel')).toBeTruthy() }) + it('切换列表sel转移', async () => { m(); const items = w.body.querySelectorAll('.fb-side-item'); click(items[1]); await nextTick(); expect(items[1].classList.contains('sel')).toBe(true) }) + it('checkbox存在', () => { m(); expect(w.body.querySelector('.rem-check')).toBeTruthy() }) + it('标题输入存在', () => { m(); expect(w.body.querySelector('.rem-title')).toBeTruthy() }) + it('日期输入存在', () => { m(); expect(w.body.querySelector('.rem-due')).toBeTruthy() }) + it('默认数据含3个列表', () => { m(); expect(w.body.querySelectorAll('.fb-side-item').length).toBe(3) }) + + it('勾选未完成变done', async () => { m(); const cb = w.body.querySelector('.rem-check:not(.done)') as HTMLElement; click(cb); await nextTick(); await nextTick(); expect(w.body.querySelectorAll('.rem-check.done').length).toBeGreaterThanOrEqual(1) }) + it('勾选后store持久化', async () => { m(); const cb = w.body.querySelector('.rem-check:not(.done)') as HTMLElement; click(cb); await nextTick(); await nextTick(); expect(store.get('reminders', {}).items.some((i: any) => i.done)).toBe(true) }) + it('Enter新建清空输入', async () => { m(); const inp = w.body.querySelector('.rem-new') as HTMLInputElement; inp.value = '新提醒'; inp.dispatchEvent(new Event('input', { bubbles: true })); inp.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', bubbles: true })); await nextTick(); expect((w.body.querySelector('.rem-new') as HTMLInputElement).value).toBe('') }) + it('空输入不新建', async () => { m(); const before = w.body.querySelectorAll('.rem-row').length; (w.body.querySelector('.rem-new') as HTMLInputElement).dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', bubbles: true })); await nextTick(); expect(w.body.querySelectorAll('.rem-row').length).toBe(before) }) + it('标题change更新', async () => { m(); const ti = w.body.querySelector('.rem-title') as HTMLInputElement; ti.value = '改了'; ti.dispatchEvent(new Event('change', { bubbles: true })); await nextTick(); expect(store.get('reminders', {}).items.some((i: any) => i.title === '改了')).toBe(true) }) + it('日期change更新', async () => { m(); const di = w.body.querySelector('.rem-due') as HTMLInputElement; di.value = '2026-12-25'; di.dispatchEvent(new Event('change', { bubbles: true })); await nextTick(); expect(store.get('reminders', {}).items.some((i: any) => i.due === '2026-12-25')).toBe(true) }) + it('空列表提示', () => { store.set('reminders', { lists: [{ id:'e1', name:'空', color:'#ff9f0a' }], items: [] }); m(); expect(w.body.querySelector('.empty-state')?.textContent).toContain('此列表为空') }) + it('store加载预设', () => { store.set('reminders', { lists: [{ id:'x1', name:'自定义', color:'#ff453a' }], items: [] }); m(); expect(w.body.querySelector('.rem-lname')?.textContent).toBe('自定义') }) + it('卸载', () => { m(); render(null, w.body); expect(w.body.children.length).toBe(0) }) }) + +function click(el: Element | null) { el?.dispatchEvent(new MouseEvent('click', { bubbles: true })) }