wrolpi/app/src/components/StatisticsPage.test.js
lrnselfreliance 306386e801
app: Flasher saved configs and Statistics distinguish pending from empty (#733)
The Flasher's saved configurations started as [] with no loading
flag, and the Saved Firmwares tab is the default tab, so every load
painted "No saved configurations yet." until the fetch returned.  The
api helper returned [] on a non-OK response too, so a failed fetch
read as an empty list.

useStatistics used useState() with no initial value, so the
Statistics page's first render saw undefined and flashed "Failed to
fetch statistics" for one frame before the effect set {} and the
spinner appeared.

Invariants now enforced and tested:
* null = pending, undefined = fetch failed, [] / object = loaded.  The
  api helper returns undefined (and toasts) on a non-OK response.
* The Saved Firmwares tab shows a placeholder while pending, an error
  on failure, and "No saved configurations yet." only once the server
  confirmed there are none.  Only the newest configs request may write
  state, since save and delete refetch.
* The Statistics page shows its loader while pending and its error
  only for a failed fetch.

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-26 17:05:02 -06:00

49 lines
1.7 KiB
JavaScript

import React from 'react';
import {act, render, screen} from '../test-utils';
import {StatisticsPage} from './Apps';
import {getStatistics} from '../api';
// What the Statistics page shows while the numbers are still loading, when the fetch failed, and
// once they arrive. "Failed to fetch statistics" may only appear for a failed fetch.
jest.mock('../api', () => ({
...jest.requireActual('../api'),
getStatistics: jest.fn(),
}));
const FAILED = /Failed to fetch statistics/;
const STATS = {
global_statistics: {db_size: 1024},
file_statistics: {
archive_count: 1, audio_count: 2, ebook_count: 3, image_count: 4, pdf_count: 5, total_count: 21,
video_count: 6, zip_count: 0, total_size: 4096, tagged_files: 7, tagged_zims: 0, tags_count: 2,
},
};
beforeEach(() => getStatistics.mockReset());
test('pending: shows a loader, not the error', () => {
getStatistics.mockReturnValue(new Promise(() => {
}));
const {container} = render(<StatisticsPage/>);
expect(container.querySelector('.wrolpi-loading')).toBeInTheDocument();
expect(screen.queryByText(FAILED)).not.toBeInTheDocument();
});
test('failed: shows the error', async () => {
// The api helper toasts and returns undefined on a non-OK response.
getStatistics.mockResolvedValue(undefined);
render(<StatisticsPage/>);
await act(async () => {
});
expect(screen.getByText(FAILED)).toBeInTheDocument();
});
test('loaded: shows the numbers', async () => {
getStatistics.mockResolvedValue(STATS);
render(<StatisticsPage/>);
await act(async () => {
});
expect(screen.getByText('All Files')).toBeInTheDocument();
expect(screen.queryByText(FAILED)).not.toBeInTheDocument();
});