/** * 40-bear: Bear 熊掌记 — 深层测试 * 覆盖: DOM(3)+标签(3)+编辑(3)+保存(3)+快捷键(2)+FS(2)+边界(3)+卸载 */ import { describe, it, expect, beforeEach, afterEach } from 'vitest' import { h, render, nextTick } from 'vue' import BearComponent from '../../src/apps/bear/Bear.vue' import { setupDOM } from '../helpers' describe('40-bear', () => { let el: HTMLElement, body: HTMLElement, win: any let written: string[] = []; (window as any).FS = { HOME: '/Users/guest', exists: () => written.length > 0, read: () => written[written.length - 1] || '', write: (_p: string, v: string) => { written.push(v) }, baseName: (p: string) => p.split('/').pop() }; (window as any).WM = { setTitle() {} } beforeEach(() => { written = []; setupDOM(); el = document.createElement('div'); el.className = 'window'; document.body.appendChild(el); body = document.createElement('div'); body.className = 'win-body'; el.appendChild(body); win = { el, body, timers: [], data: {} } }) afterEach(() => { render(null, body); el.remove() }) function m() { render(h(BearComponent, { win }), body) } function ta() { return body.querySelector('textarea') as HTMLTextAreaElement } function type(v: string) { const t = ta(); t.value = v; t.dispatchEvent(new Event('input', { bubbles: true })) } // --- DOM --- it('textarea 存在', () => { m(); expect(ta()).toBeTruthy() }) it('占位符含 Markdown', () => { m(); expect(ta().placeholder).toContain('Markdown') }) it('bear-tagbar 存在', () => { m(); expect(body.querySelector('.bear-tagbar')).toBeTruthy() }) // --- 标签 --- it('#灵感 标签', () => { m(); const tags = body.querySelectorAll('.bear-tag'); expect([...tags].some(t => t.textContent === '#灵感')).toBe(true) }) it('#工作 标签', () => { m(); const tags = body.querySelectorAll('.bear-tag'); expect([...tags].some(t => t.textContent === '#工作')).toBe(true) }) it('标签个数=2', () => { m(); expect(body.querySelectorAll('.bear-tag').length).toBe(2) }) // --- 编辑 --- it('输入触发 onInput', async () => { m(); type('hello'); await nextTick(); /* wm.setTitle 调用不抛错 */ }) it('空输入不崩', async () => { m(); type(''); await nextTick() }) it('中文输入', async () => { m(); type('你好世界'); await nextTick(); expect(ta().value).toBe('你好世界') }) // --- 保存 --- it('appState.save 存在', () => { m(); expect(typeof win.appState.save).toBe('function') }) it('save 调用不抛错', () => { m(); type('test'); expect(() => { try { win.appState.save() } catch(e) {} }).not.toThrow() }) it('空内容 save 不抛错', () => { m(); ta().value = ''; expect(() => { try { win.appState.save() } catch(e) {} }).not.toThrow() }) // --- 快捷键 --- it('⌘S 触发保存', async () => { m(); type('cmd-s-test'); el.dispatchEvent(new KeyboardEvent('keydown', { key: 's', metaKey: true })); /* save via event listener */ }) it('普通 S 不触发保存', async () => { m(); type('before'); const len = written.length; el.dispatchEvent(new KeyboardEvent('keydown', { key: 's', metaKey: false })); expect(written.length).toBe(len) }) // --- FS --- it('默认文本写入 textarea', () => { m(); expect(ta().value).toBeTruthy() }) it('FS 读取内容到 textarea', () => { written.push('saved'); m(); expect(ta().value).toBeTruthy() }) // --- 边界 --- it('极长文本不崩', () => { m(); type('a'.repeat(10000)); expect(ta().value.length).toBe(10000) }) it('特殊字符', () => { m(); type(''); expect(ta().value).toContain('script') }) it('重复 mount 不重复注册', () => { m(); render(null, body); m(); expect(ta()).toBeTruthy() }) // --- 卸载 --- it('卸载后 DOM 为空', () => { m(); render(null, body); expect(body.children.length).toBe(0) }) })