/** * Vitest 测试环境设置 * * 配置全局测试环境和工具 */ import { vi } from 'vitest' // Mock window.matchMedia Object.defineProperty(window, 'matchMedia', { writable: true, value: vi.fn().mockImplementation(query => ({ matches: false, media: query, onchange: null, addListener: vi.fn(), // Deprecated removeListener: vi.fn(), // Deprecated addEventListener: vi.fn(), removeEventListener: vi.fn(), dispatchEvent: vi.fn(), })), }) // Mock IntersectionObserver global.IntersectionObserver = class IntersectionObserver { constructor() {} disconnect() {} observe() {} takeRecords() { return [] } unobserve() {} } as any // Mock ResizeObserver global.ResizeObserver = class ResizeObserver { constructor() {} disconnect() {} observe() {} unobserve() {} } as any // Mock localStorage const localStorageMock = { getItem: (key: string) => null, setItem: (key: string, value: string) => {}, removeItem: (key: string) => {}, clear: () => {}, } Object.defineProperty(window, 'localStorage', { value: localStorageMock, }) // Mock sessionStorage const sessionStorageMock = { getItem: (key: string) => null, setItem: (key: string, value: string) => {}, removeItem: (key: string) => {}, clear: () => {}, } Object.defineProperty(window, 'sessionStorage', { value: sessionStorageMock, }) // Mock requestAnimationFrame global.requestAnimationFrame = (callback: FrameRequestCallback) => { return setTimeout(callback, 16) as unknown as number } global.cancelAnimationFrame = (id: number) => { clearTimeout(id) } // Mock console方法以减少测试输出 global.console = { ...console, // Uncomment to ignore console logs during tests // log: vi.fn(), // debug: vi.fn(), // info: vi.fn(), // warn: vi.fn(), // error: vi.fn(), } // 设置全局错误处理 window.addEventListener('error', event => { console.error('Global error:', event.error) }) // 设置未处理的Promise rejection window.addEventListener('unhandledrejection', event => { console.error('Unhandled promise rejection:', event.reason) }) // Mock URL.createObjectURL和URL.revokeObjectURL global.URL.createObjectURL = vi.fn(() => 'mock-url') global.URL.revokeObjectURL = vi.fn() // Mock navigator Object.defineProperty(window.navigator, 'userAgent', { value: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36', writable: true, }) // Mock scrollTo window.scrollTo = vi.fn() // Mock getComputedStyle window.getComputedStyle = vi.fn(() => ({ getPropertyValue: (property: string) => { const styles: Record = { display: 'block', width: '100px', height: '100px', } return styles[property] || '' }, })) // Mock DOMRect global.DOMRect = class DOMRect { constructor( public x = 0, public y = 0, public width = 0, public height = 0, ) {} toJSON() { return { x: this.x, y: this.y, top: this.y, left: this.x, bottom: this.y + this.height, right: this.x + this.width, width: this.width, height: this.height, } } static fromRect(rect?: DOMRectInit): DOMRect { return new DOMRect(rect?.x, rect?.y, rect?.width, rect?.height) } } as any // Element.prototype.getBoundingClientRect mock Element.prototype.getBoundingClientRect = new DOMRect(0, 0, 0, 0) as any // 每个测试前清理 beforeEach(() => { vi.clearAllMocks() }) // 导出测试工具 export const TestUtils = { /** * 等待组件更新 */ async flushPromises(): Promise { return new Promise(resolve => setTimeout(resolve, 0)) }, /** * 创建Mock响应 */ createMockResponse(data: T, status = 200): Response { return { ok: status >= 200 && status < 300, status, statusText: status === 200 ? 'OK' : 'Error', json: async () => data, data, } as any }, /** * 创建Mock API响应 */ createMockApiResponse(data: T, code = 200, message = 'success') { return { code, message, data, timestamp: Date.now(), } }, }