import React from 'react'; import { render, screen, fireEvent, waitFor, act } from '@testing-library/react'; import ApkList from '../ApkList'; import useSWR from 'swr'; jest.mock('swr'); // Mock fetch for config API const mockFetch = jest.fn(); global.fetch = mockFetch; describe('ApkList', () => { const mockApks = [ { name: 'ft_dev_0_42_57ef3a60d.apk', environment: 'dev', rawEnvironment: 'dev', commit: '57ef3a60d', size: 15728640, modifiedAt: '2026-03-04T14:30:00.000Z', downloadUrl: '/api/download?project=FT&version=0_42&filename=ft_dev_0_42_57ef3a60d.apk', }, { name: 'ft_0_42_ff9ff3441.apk', environment: 'product', rawEnvironment: 'product', commit: 'ff9ff3441', size: 15728640, modifiedAt: '2026-03-04T14:30:00.000Z', downloadUrl: '/api/download?project=FT&version=0_42&filename=ft_0_42_ff9ff3441.apk', }, { name: 'ft_sandbox_0_42_abc123456.apk', environment: 'sandbox', rawEnvironment: 'sandbox', commit: 'abc123456', size: 15728640, modifiedAt: '2026-03-04T14:30:00.000Z', downloadUrl: '/api/download?project=FT&version=0_42&filename=ft_sandbox_0_42_abc123456.apk', }, { name: 'ft_timeshift_0_42_xyz987654.apk', environment: 'other', rawEnvironment: 'timeshift', commit: 'xyz987654', size: 15728640, modifiedAt: '2026-03-04T14:30:00.000Z', downloadUrl: '/api/download?project=FT&version=0_42&filename=ft_timeshift_0_42_xyz987654.apk', }, ]; beforeEach(() => { (useSWR as jest.Mock).mockReturnValue({ data: mockApks, isLoading: false, error: null, }); // Mock config API response mockFetch.mockResolvedValue({ json: () => Promise.resolve({ tabs: ['dev', 'sandbox'] }), }); }); afterEach(() => { mockFetch.mockClear(); }); it('should render all 4 tabs', async () => { await act(async () => { render(); }); await waitFor(() => { // Use getAllByText since text appears in both tab and badge expect(screen.getAllByText(/dev/).length).toBeGreaterThan(0); expect(screen.getAllByText(/product/).length).toBeGreaterThan(0); expect(screen.getAllByText(/sandbox/).length).toBeGreaterThan(0); expect(screen.getAllByText(/other/).length).toBeGreaterThan(0); }); }); it('should switch tabs correctly', async () => { await act(async () => { render(); }); await waitFor(() => { expect(screen.getByText(/dev/)).toBeInTheDocument(); }); const devTab = screen.getByText(/dev/); fireEvent.click(devTab); expect(screen.getByText('57ef3a60d')).toBeInTheDocument(); }); it('should show loading state', async () => { (useSWR as jest.Mock).mockReturnValue({ data: null, isLoading: true, error: null, }); await act(async () => { render(); }); expect(screen.getByText('加载中...')).toBeInTheDocument(); }); it('should show error state', async () => { (useSWR as jest.Mock).mockReturnValue({ data: null, isLoading: false, error: new Error('API error'), }); await act(async () => { render(); }); expect(screen.getByText('加载失败,请重试')).toBeInTheDocument(); }); });