/** * OurImpact Component Tests * * 測試目標: * 1. 元件能正確渲染 * 2. 元件能正確呼叫廠商 ourImpact.js * 3. 元件卸載時能正確 cleanup */ import React from 'react'; import { render, waitFor, cleanup as rtlCleanup } from '@testing-library/react'; import OurImpact from '@/components/aia/our-impact'; import * as vendorOurImpact from '@vendor-web/js/components/ourImpact.js'; // Mock 廠商 ourImpact.js jest.mock('@vendor-web/js/components/ourImpact.js', () => ({ OurImpact: jest.fn(), })); describe('OurImpact Component', () => { let mockCleanup: jest.Mock; beforeEach(() => { mockCleanup = jest.fn(); (vendorOurImpact.OurImpact as jest.Mock).mockReturnValue(mockCleanup); }); afterEach(() => { rtlCleanup(); jest.clearAllMocks(); }); it('應該渲染 ourImpact 結構', () => { const { container } = render(); expect(container.querySelector('.ourImpact')).toBeInTheDocument(); expect(container.querySelector('.ourImpact-heading')).toBeInTheDocument(); expect(container.querySelector('.ourImpact-container')).toBeInTheDocument(); }); it('應該呼叫廠商 OurImpact 初始化函式', async () => { render(); await waitFor(() => { expect(vendorOurImpact.OurImpact).toHaveBeenCalledTimes(1); expect(vendorOurImpact.OurImpact).toHaveBeenCalledWith( expect.any(HTMLElement) ); }); }); it('應該在元件卸載時呼叫 cleanup 函式', async () => { const { unmount } = render(); await waitFor(() => { expect(vendorOurImpact.OurImpact).toHaveBeenCalled(); }); unmount(); expect(mockCleanup).toHaveBeenCalledTimes(1); }); it('應該能處理廠商 JS 未返回 cleanup 的情況', async () => { // Suppress console.warn for this test const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(); (vendorOurImpact.OurImpact as jest.Mock).mockReturnValue(undefined); const { unmount } = render(); // 不應該拋出錯誤 expect(() => unmount()).not.toThrow(); // Should log warning expect(consoleWarnSpy).toHaveBeenCalledWith( '[OurImpact] No cleanup function provided by vendor JS' ); consoleWarnSpy.mockRestore(); }); it('應該根據 dataEnd prop 設定不同 class', () => { const { container: container1 } = render(); expect(container1.querySelector('.ourImpact')).toHaveAttribute('data-end', 'false'); rtlCleanup(); const { container: container2 } = render(); expect(container2.querySelector('.ourImpact')).toHaveAttribute('data-end', 'true'); }); });