test: 增强 mail 8→26 + fix
This commit is contained in:
parent
521d70b5a0
commit
e89bf9010b
@ -1,27 +1,79 @@
|
||||
/**
|
||||
* 37-mail: Mail Vue 组件化测试
|
||||
* 37-mail: Mail Vue 组件化 — 深层测试
|
||||
* 覆盖: 三个邮箱渲染、未读计数、邮件列表/排序、搜索过滤、选中/已读标记、
|
||||
* 星标切换、删除/移入废纸篓、写邮件弹窗/关闭、发送/存储、
|
||||
* 回复引用、compose appState、卸载
|
||||
*/
|
||||
import { describe, it, expect, beforeEach, afterEach } from 'vitest'
|
||||
import { h, render, nextTick } from 'vue'
|
||||
import MailComponent from '../../src/apps/mail/Mail.vue'
|
||||
import { store } from '../../src/composables/useStore'
|
||||
import { setSysForNotify } from '../../src/composables/useNotify'
|
||||
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 mkWin() { 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: {} } }
|
||||
|
||||
describe('37-mail — Mail Vue 组件化', () => {
|
||||
describe('37-mail — Mail Vue 深层测试', () => {
|
||||
let win: any
|
||||
beforeEach(() => { localStorage.clear(); store.set('mail', null); setupDOM(); win = makeMockWin() })
|
||||
beforeEach(() => { localStorage.clear(); store.set('mail', null); setupDOM(); win = mkWin();
|
||||
setSysForNotify({ settings: { notificationsEnabled: true, notifAllow: {} as any, focus: false } }) })
|
||||
afterEach(() => { render(null, win.body); win.el.remove() })
|
||||
function mount() { const v = h(MailComponent, { win }); render(v, win.body) }
|
||||
|
||||
it('显示 3 个邮箱', () => { mount(); expect(win.body.querySelectorAll('.mail-side .fb-side-item').length).toBe(3) })
|
||||
it('收件箱有邮件列表', () => { mount(); expect(win.body.querySelectorAll('.mail-row').length).toBeGreaterThanOrEqual(1) })
|
||||
it('默认选中收件箱', () => { mount(); expect(win.body.querySelector('.mail-side .fb-side-item.sel')?.textContent).toContain('收件箱') })
|
||||
it('搜索框存在', () => { mount(); expect(win.body.querySelector('.notes-search')).toBeTruthy() })
|
||||
it('新邮件按钮存在', () => { mount(); expect(win.body.querySelector('.fb-btn')).toBeTruthy() })
|
||||
it('点击邮件显示详情', async () => { mount(); const row = win.body.querySelector('.mail-row') as HTMLElement; row.dispatchEvent(new MouseEvent('click', { bubbles: true })); await nextTick(); expect(win.body.querySelector('.mail-detail-subject')).toBeTruthy() })
|
||||
it('点击新邮件打开弹窗', async () => { mount(); (win.body.querySelector('.fb-btn') as HTMLElement).dispatchEvent(new MouseEvent('click', { bubbles: true })); await nextTick(); expect(win.body.querySelector('.mail-compose')).toBeTruthy() })
|
||||
it('切换邮箱清除选中', async () => { mount(); const row = win.body.querySelector('.mail-row') as HTMLElement; row.dispatchEvent(new MouseEvent('click', { bubbles: true })); await nextTick(); const side = win.body.querySelectorAll('.mail-side .fb-side-item')[1] as HTMLElement; side.dispatchEvent(new MouseEvent('click', { bubbles: true })); await nextTick(); expect(win.body.querySelector('.mail-detail .empty-state')).toBeTruthy() })
|
||||
it('卸载', () => { mount(); render(null, win.body); expect(win.body.children.length).toBe(0) })
|
||||
// ==================== 邮箱导航 ====================
|
||||
describe('邮箱导航', () => {
|
||||
it('3 个邮箱:收件箱/已发送/废纸篓', () => { mount(); const items = win.body.querySelectorAll('.mail-side .fb-side-item'); expect(items.length).toBe(3) })
|
||||
it('默认选中收件箱', () => { mount(); expect(win.body.querySelector('.mail-side .fb-side-item.sel')?.textContent).toContain('收件箱') })
|
||||
it('收件箱有未读计数 badge', () => { mount(); expect(win.body.querySelector('.mail-side .badge')).toBeTruthy() })
|
||||
it('切换邮箱清除当前选中邮件', async () => { mount(); const row = win.body.querySelector('.mail-row') as HTMLElement; row.dispatchEvent(new MouseEvent('click', { bubbles: true })); await nextTick(); (win.body.querySelectorAll('.mail-side .fb-side-item')[1] as HTMLElement).dispatchEvent(new MouseEvent('click', { bubbles: true })); await nextTick(); expect(win.body.querySelector('.mail-detail .empty-state')).toBeTruthy() })
|
||||
it('已发送无邮件显示提示', async () => { mount(); (win.body.querySelectorAll('.mail-side .fb-side-item')[1] as HTMLElement).dispatchEvent(new MouseEvent('click', { bubbles: true })); await nextTick(); expect(win.body.querySelector('.empty-state')?.textContent).toContain('没有邮件') })
|
||||
})
|
||||
|
||||
// ==================== 邮件列表 ====================
|
||||
describe('邮件列表', () => {
|
||||
it('收件箱有 4 封预设邮件', () => { mount(); expect(win.body.querySelectorAll('.mail-row').length).toBe(4) })
|
||||
it('邮件显示发件人和主题', () => { mount(); const first = win.body.querySelector('.mail-row') as HTMLElement; expect(first.querySelector('b')?.textContent).toBeTruthy(); expect(first.querySelector('.mail-subject')?.textContent).toBeTruthy() })
|
||||
it('未读邮件有 unread 类', () => { mount(); expect(win.body.querySelector('.mail-row.unread')).toBeTruthy() })
|
||||
it('已读邮件无 unread 类', () => { mount(); expect(win.body.querySelectorAll('.mail-row:not(.unread)').length).toBeGreaterThanOrEqual(1) })
|
||||
it('星标邮件显示 ★', () => { mount(); expect(win.body.querySelector('.mail-star')).toBeTruthy() })
|
||||
it('按时间倒序排列', () => { mount(); const subjects = [...win.body.querySelectorAll('.mail-subject')].map((e: any) => e.textContent?.trim()); expect(subjects[0]).toContain('欢迎使用') }) // 最新
|
||||
})
|
||||
|
||||
// ==================== 选中邮件 ====================
|
||||
describe('选中邮件', () => {
|
||||
it('点击未读邮件→标记已读+显示详情', async () => { mount(); const unread = win.body.querySelector('.mail-row.unread') as HTMLElement; unread.dispatchEvent(new MouseEvent('click', { bubbles: true })); await nextTick(); expect(unread.classList.contains('unread')).toBe(false); expect(win.body.querySelector('.mail-detail-body')).toBeTruthy() })
|
||||
it('详情显示主题/发件人/时间', async () => { mount(); (win.body.querySelector('.mail-row') as HTMLElement).dispatchEvent(new MouseEvent('click', { bubbles: true })); await nextTick(); expect(win.body.querySelector('.mail-detail-subject')).toBeTruthy(); expect(win.body.querySelector('.mail-detail-meta')?.textContent).toContain('<') })
|
||||
it('详情有星标/删除/回复按钮', async () => { mount(); (win.body.querySelector('.mail-row') as HTMLElement).dispatchEvent(new MouseEvent('click', { bubbles: true })); await nextTick(); const btns = win.body.querySelectorAll('.mail-detail-actions .fb-btn, .mail-detail-actions .btn'); expect(btns.length).toBeGreaterThanOrEqual(2) })
|
||||
})
|
||||
|
||||
// ==================== 星标 ====================
|
||||
describe('星标', () => {
|
||||
it('toggle 星标切换', async () => { mount(); (win.body.querySelector('.mail-row') as HTMLElement).dispatchEvent(new MouseEvent('click', { bubbles: true })); await nextTick(); const starBtn = win.body.querySelector('.mail-detail-actions .fb-btn') as HTMLElement; const before = starBtn.textContent; starBtn.dispatchEvent(new MouseEvent('click', { bubbles: true })); await nextTick(); expect(starBtn.textContent).not.toBe(before) })
|
||||
})
|
||||
|
||||
// ==================== 删除 ====================
|
||||
describe('删除', () => {
|
||||
it('删除后从列表消失', async () => { mount(); (win.body.querySelector('.mail-row') as HTMLElement).dispatchEvent(new MouseEvent('click', { bubbles: true })); await nextTick(); const delBtn = win.body.querySelectorAll('.mail-detail-actions .fb-btn')[1] as HTMLElement; delBtn.dispatchEvent(new MouseEvent('click', { bubbles: true })); await nextTick(); expect(win.body.querySelector('.mail-detail .empty-state')).toBeTruthy() })
|
||||
})
|
||||
|
||||
// ==================== 搜索 ====================
|
||||
describe('搜索', () => {
|
||||
async function search(text: string) { const input = win.body.querySelector('.notes-search') as HTMLInputElement; input.value = text; input.dispatchEvent(new Event('input', { bubbles: true })); await new Promise(r => setTimeout(r, 250)); await nextTick() }
|
||||
it('搜索"Apple"过滤出一封', async () => { mount(); await search('Apple'); expect(win.body.querySelectorAll('.mail-row').length).toBe(1) })
|
||||
it('搜索无匹配显示"没有邮件"', async () => { mount(); await search('zzz_no_match'); expect(win.body.querySelector('.empty-state')?.textContent).toContain('没有邮件') })
|
||||
})
|
||||
|
||||
// ==================== 写邮件 ====================
|
||||
describe('写邮件', () => {
|
||||
it('✏️ 按钮打开弹窗', async () => { mount(); (win.body.querySelector('.fb-btn') as HTMLElement).dispatchEvent(new MouseEvent('click', { bubbles: true })); await nextTick(); expect(win.body.querySelector('.mail-compose')).toBeTruthy() })
|
||||
it('弹窗有收件人/主题/正文', async () => { mount(); (win.body.querySelector('.fb-btn') as HTMLElement).dispatchEvent(new MouseEvent('click', { bubbles: true })); await nextTick(); expect(win.body.querySelectorAll('.dlg-input').length).toBeGreaterThanOrEqual(2) })
|
||||
it('点击取消关闭弹窗', async () => { mount(); (win.body.querySelector('.fb-btn') as HTMLElement).dispatchEvent(new MouseEvent('click', { bubbles: true })); await nextTick(); const cancel = win.body.querySelector('.dlg-btns .btn') as HTMLElement; cancel.dispatchEvent(new MouseEvent('click', { bubbles: true })); await nextTick(); expect(win.body.querySelector('.mail-compose')).toBeNull() })
|
||||
it('点击背景关闭弹窗', async () => { mount(); (win.body.querySelector('.fb-btn') as HTMLElement).dispatchEvent(new MouseEvent('click', { bubbles: true })); await nextTick(); (win.body.querySelector('.modal-mask') as HTMLElement).dispatchEvent(new MouseEvent('click', { bubbles: true })); await nextTick(); expect(win.body.querySelector('.mail-compose')).toBeNull() })
|
||||
it('空收件人发送被拒', async () => { mount(); (win.body.querySelector('.fb-btn') as HTMLElement).dispatchEvent(new MouseEvent('click', { bubbles: true })); await nextTick(); (win.body.querySelector('.btn.primary') as HTMLElement).dispatchEvent(new MouseEvent('click', { bubbles: true })); await nextTick(); expect(win.body.querySelector('.mail-compose')).toBeTruthy() }) // 弹窗仍在
|
||||
it('填写后发送→已发送+1', async () => { mount(); (win.body.querySelector('.fb-btn') as HTMLElement).dispatchEvent(new MouseEvent('click', { bubbles: true })); await nextTick(); const inputs = win.body.querySelectorAll('.dlg-input'); (inputs[0] as HTMLInputElement).value = 'test@test.com'; inputs[0].dispatchEvent(new Event('input', { bubbles: true })); (win.body.querySelector('.btn.primary') as HTMLElement).dispatchEvent(new MouseEvent('click', { bubbles: true })); await nextTick(); expect(win.body.querySelector('.mail-compose')).toBeNull(); const data = store.get('mail', {}); expect(data.sent.length).toBe(1) })
|
||||
it('compose 函数通过 appState 暴露', () => { mount(); expect(typeof win.appState.compose).toBe('function') })
|
||||
})
|
||||
|
||||
// ==================== 卸载 ====================
|
||||
it('卸载后 DOM 为空', () => { mount(); render(null, win.body); expect(win.body.children.length).toBe(0) })
|
||||
})
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user