macos-web/tests/cases/44-podcasts.test.ts

55 lines
3.6 KiB
TypeScript

/**
* 44-podcasts: 播客 — 深层测试
* 覆盖: DOM(3)+列表(5)+播放(4)+音频(3)+边界(3)+卸载
* ⚠️ audio.play/pause jsdom 不支持,验证 DOM 状态
*/
import { describe, it, expect, beforeEach, afterEach } from 'vitest'
import { h, render, nextTick } from 'vue'
import PodcastsComponent from '../../src/apps/podcasts/Podcasts.vue'
import { setupDOM } from '../helpers'
describe('44-podcasts', () => {
let body: HTMLElement, el: HTMLElement, win: any
beforeEach(() => {
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: {} };
(window as any).Sys = () => ({ unregisterMedia: () => {} })
})
afterEach(() => { render(null, body); el.remove() })
function m() { render(h(PodcastsComponent, { win }), body) }
function row(i: number) { return body.querySelectorAll('.music-row')[i] as HTMLElement }
function clickRow(i: number) { row(i)?.dispatchEvent(new MouseEvent('click', { bubbles: true })) }
// --- DOM ---
it('music-body 容器', () => { m(); expect(body.querySelector('.music-body')).toBeTruthy() })
it('pod-bar 存在', () => { m(); expect(body.querySelector('.pod-bar')).toBeTruthy() })
it('audio 元素存在', () => { m(); expect(body.querySelector('audio')).toBeTruthy() })
// --- 列表 ---
it('6 集播客', () => { m(); expect(body.querySelectorAll('.music-row').length).toBe(6) })
it('每集有序号', () => { m(); [...body.querySelectorAll('.music-num')].forEach((n, i) => { if (i < 6) expect(n.textContent).toBe(String(i + 1)) }) })
it('每集有标题', () => { m(); [...body.querySelectorAll('.music-row b')].forEach(b => expect(b.textContent).toBeTruthy()) })
it('每集有时长', () => { m(); [...body.querySelectorAll('.music-dur')].forEach(d => expect(d.textContent).toMatch(/^\d+:\d{2}$/)) })
it('每集有 show 名', () => { m(); [...body.querySelectorAll('.music-row small')].forEach(s => expect(s.textContent).toContain('Web 桌面谈')) })
// --- 播放 ---
it('点击选中→高亮', async () => { m(); clickRow(0); await nextTick(); expect(row(0).classList.contains('sel')).toBe(true) })
it('选中后高亮变化', async () => { m(); clickRow(0); await nextTick(); expect(row(0).classList.contains('sel')).toBe(true) })
it('底部栏显示当前集名', async () => { m(); clickRow(0); await nextTick(); expect(body.querySelector('.pod-bar')?.textContent).toContain('Monkeys') })
it('切换集更新底部栏', async () => { m(); clickRow(0); await nextTick(); clickRow(1); await nextTick(); expect(body.querySelector('.pod-bar')?.textContent).toContain('Fluffing') })
// --- 音频 ---
it('点击暂停切换', async () => { m(); clickRow(0); await nextTick(); clickRow(0); await nextTick(); /* play→pause 不抛错 */ })
it('播放下一集', async () => { m(); clickRow(5); await nextTick(); /* audio ended→next 不抛错 */ })
it('时长格式化', () => { m(); expect(body.querySelector('.music-dur')?.textContent).toBe('0:22') })
// --- 边界 ---
it('快速双击不崩', async () => { m(); clickRow(0); clickRow(0); await nextTick() })
it('选中集内再次点击不切换', async () => { m(); clickRow(0); await nextTick(); clickRow(0); await nextTick(); expect(row(0).classList.contains('sel')).toBe(true) })
it('底部栏初始状态', () => { m(); expect(body.querySelector('.pod-bar')?.textContent).toContain('选择一集') })
// --- 卸载 ---
it('卸载后 DOM 为空', () => { m(); render(null, body); expect(body.children.length).toBe(0) })
})