Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions frontend/src/components/LandingPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,13 @@ export const LandingPage: React.FC<LandingPageProps> = ({ className }) => {
<h2 id="statistics-heading">Platform Statistics</h2>
<div className="error-message" role="alert">
<p>Unable to load statistics at this time. Please try again later.</p>
<button
className="retry-button"
onClick={() => window.location.reload()}
aria-label="Retry loading statistics"
>
Retry
</button>
</div>
</section>
}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ describe('LandingPage with ErrorBoundary', () => {
render(<LandingPage />);

expect(screen.getByText('Unable to load statistics at this time. Please try again later.')).toBeInTheDocument();
expect(screen.getByRole('button', { name: /retry loading statistics/i })).toBeInTheDocument();
});

it('should display error message with role alert', () => {
Expand Down
90 changes: 89 additions & 1 deletion frontend/src/components/__tests__/Statistics.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react';
import { render, screen, waitFor, fireEvent } from '@testing-library/react';
import { Statistics } from '../Statistics';
import { ErrorBoundary } from '../ErrorBoundary';
import { api } from '../../lib/api/client';

// Mock the API
Expand Down Expand Up @@ -86,4 +87,91 @@ describe('Statistics', () => {

expect(screen.getByRole('region', { name: /platform statistics/i })).toBeInTheDocument();
});
});
});

// Component that unconditionally throws to simulate a rendering exception in Statistics
const ThrowingStatistics: React.FC = () => {
throw new Error('Statistics rendering exception');
};

describe('Statistics wrapped in ErrorBoundary', () => {
beforeEach(() => {
jest.clearAllMocks();
// Suppress console.error for expected error boundary output
jest.spyOn(console, 'error').mockImplementation(() => {});
});

afterEach(() => {
jest.restoreAllMocks();
});

it('catches a rendering exception thrown inside Statistics and renders the fallback', () => {
const fallback = (
<section className="statistics" aria-labelledby="statistics-heading">
<h2 id="statistics-heading">Platform Statistics</h2>
<div className="error-message" role="alert">
<p>Unable to load statistics at this time. Please try again later.</p>
<button className="retry-button" onClick={() => window.location.reload()} aria-label="Retry loading statistics">
Retry
</button>
</div>
</section>
);

render(
<ErrorBoundary section="statistics" fallback={fallback}>
<ThrowingStatistics />
</ErrorBoundary>
);

// Fallback heading and message should be visible
expect(screen.getByRole('heading', { name: /platform statistics/i })).toBeInTheDocument();
expect(screen.getByRole('alert')).toBeInTheDocument();
expect(screen.getByText(/unable to load statistics at this time/i)).toBeInTheDocument();
});

it('renders a retry button in the fallback when Statistics throws', () => {
const fallback = (
<section className="statistics" aria-labelledby="statistics-heading">
<h2 id="statistics-heading">Platform Statistics</h2>
<div className="error-message" role="alert">
<p>Unable to load statistics at this time. Please try again later.</p>
<button className="retry-button" onClick={() => window.location.reload()} aria-label="Retry loading statistics">
Retry
</button>
</div>
</section>
);

render(
<ErrorBoundary section="statistics" fallback={fallback}>
<ThrowingStatistics />
</ErrorBoundary>
);

expect(screen.getByRole('button', { name: /retry loading statistics/i })).toBeInTheDocument();
});

it('does not render the Statistics content when an error is thrown', () => {
const fallback = (
<div role="alert">
<p>Unable to load statistics at this time. Please try again later.</p>
<button className="retry-button" aria-label="Retry loading statistics">Retry</button>
</div>
);

render(
<ErrorBoundary section="statistics" fallback={fallback}>
<ThrowingStatistics />
</ErrorBoundary>
);

// Normal statistics content should not be present
expect(screen.queryByText('Total Markets')).not.toBeInTheDocument();
expect(screen.queryByText('Total Volume')).not.toBeInTheDocument();
expect(screen.queryByText('Active Users')).not.toBeInTheDocument();

// Fallback should be visible instead
expect(screen.getByRole('alert')).toBeInTheDocument();
});
});