import React from 'react'; import { render } from '@testing-library/react'; import '@testing-library/jest-dom'; jest.mock('@times-components/image', () => ({ Placeholder: () =>
Placeholder
})); jest.mock('../../useFetchFeed', () => ({ useFetchFeed: jest.fn() })); import { useFetchFeed } from '../../useFetchFeed'; import { OptaMatchStatsWinProbability } from '../OptaMatchStatsWinProbability'; const mockUseFetchFeed = useFetchFeed as jest.Mock; // --------------------------------------------------------------------------- // Helpers // --------------------------------------------------------------------------- const createFeed = (overrides: Record = {}) => ({ matchInfo: { id: 'match-1', description: 'Home FC vs Away United', contestant: [ { id: 'team-home', name: 'Home FC', shortName: 'Home', officialName: 'Home Football Club', code: 'HFC', position: 'home' as const }, { id: 'team-away', name: 'Away United', shortName: 'Away', officialName: 'Away United FC', code: 'AUF', position: 'away' as const } ] }, liveData: { matchDetails: { matchStatus: 'Fixture' }, preMatchPredictions: [ { type: 'Win' as const, prediction: [ { type: 'Home' as const, probability: '59.622' }, { type: 'Away' as const, probability: '19.263' }, { type: 'Draw' as const, probability: '21.115' } ] } ] }, ...overrides }); const createLiveFeed = ( homePct: string, drawPct: string, awayPct: string, overrides: Record = {} ) => createFeed({ liveData: { matchDetails: { matchStatus: 'Playing', periodId: 1 }, overallLivePredictions: [ { type: 'Win' as const, prediction: [ { type: 'Home' as const, probability: homePct }, { type: 'Draw' as const, probability: drawPct }, { type: 'Away' as const, probability: awayPct } ] } ], ...overrides } }); // --------------------------------------------------------------------------- // Tests // --------------------------------------------------------------------------- describe('OptaMatchStatsWinProbability', () => { beforeEach(() => { mockUseFetchFeed.mockReturnValue({ data: undefined, loading: false, error: undefined }); }); afterEach(() => { jest.clearAllMocks(); }); // ── loading / error states ──────────────────────────────────────────────── it('shows placeholder skeleton while loading', () => { mockUseFetchFeed.mockReturnValue({ data: undefined, loading: true, error: undefined }); const { getByTestId } = render( ); expect(getByTestId('wp-loading')).toBeInTheDocument(); expect(getByTestId('wp-placeholder')).toBeInTheDocument(); }); it('shows "No data found." on error', () => { mockUseFetchFeed.mockReturnValue({ data: undefined, loading: false, error: 'Network error' }); const { getByTestId } = render( ); expect(getByTestId('wp-error')).toHaveTextContent('No data found.'); }); // ── data-driven states ─────────────────────────────────────────────────── it('renders nothing when no feed is provided and no URL given', () => { const { container } = render(); expect(container.firstChild).toBeNull(); }); it('renders the title', () => { const feed = createFeed(); const { getByText } = render( ); expect(getByText('Win Probability')).toBeInTheDocument(); }); it('renders pre-match probability values', () => { const feed = createFeed(); const { getByTestId } = render( ); expect(getByTestId('wp-label-home')).toHaveTextContent('59.6%'); expect(getByTestId('wp-label-draw')).toHaveTextContent('21.1%'); expect(getByTestId('wp-label-away')).toHaveTextContent('19.3%'); }); it('prefers live predictions over pre-match predictions', () => { const feed = createLiveFeed('46.036', '16.150', '37.814'); const { getByTestId } = render( ); expect(getByTestId('wp-label-home')).toHaveTextContent('46.0%'); expect(getByTestId('wp-label-draw')).toHaveTextContent('16.1%'); expect(getByTestId('wp-label-away')).toHaveTextContent('37.8%'); }); it('renders wrapper with correct test id', () => { const feed = createFeed(); const { getByTestId } = render( ); expect(getByTestId('win-probability-wrapper')).toBeInTheDocument(); }); it('renders three bar segments (home, draw, away)', () => { const feed = createFeed(); const { getByTestId } = render( ); expect(getByTestId('wp-bar-home')).toBeInTheDocument(); expect(getByTestId('wp-bar-draw')).toBeInTheDocument(); expect(getByTestId('wp-bar-away')).toBeInTheDocument(); }); it('renders nothing when contestants are missing', () => { const feed = { matchInfo: { id: 'match-1' }, liveData: { preMatchPredictions: [ { type: 'Win' as const, prediction: [ { type: 'Home' as const, probability: '50' }, { type: 'Away' as const, probability: '30' }, { type: 'Draw' as const, probability: '20' } ] } ] } }; const { container } = render( ); expect(container.firstChild).toBeNull(); }); it('renders nothing when predictions are absent', () => { const feed = { matchInfo: { id: 'match-1', contestant: [ { id: 'h', name: 'Home', shortName: 'Home', officialName: 'Home FC', code: 'HFC', position: 'home' as const }, { id: 'a', name: 'Away', shortName: 'Away', officialName: 'Away FC', code: 'AFC', position: 'away' as const } ] }, liveData: {} }; const { container } = render( ); expect(container.firstChild).toBeNull(); }); it('formats probabilities to 1 decimal place', () => { const feed = createLiveFeed('46.036', '16.150', '37.814'); const { getByTestId } = render( ); expect(getByTestId('wp-label-home').textContent).toMatch(/46\.0%/); expect(getByTestId('wp-label-draw').textContent).toMatch(/16\.1%/); expect(getByTestId('wp-label-away').textContent).toMatch(/37\.8%/); }); it('renders a snapshot for pre-match', () => { const feed = createFeed(); const { asFragment } = render( ); expect(asFragment()).toMatchSnapshot(); }); it('renders a snapshot for live match', () => { const feed = createLiveFeed('46.036', '16.150', '37.814'); const { asFragment } = render( ); expect(asFragment()).toMatchSnapshot(); }); });