diff --git a/frontend/src/component/impact-metrics/hooks/useImpactMetricsState.test.tsx b/frontend/src/component/impact-metrics/hooks/useImpactMetricsState.test.tsx
index c546ad4f32..f113df5347 100644
--- a/frontend/src/component/impact-metrics/hooks/useImpactMetricsState.test.tsx
+++ b/frontend/src/component/impact-metrics/hooks/useImpactMetricsState.test.tsx
@@ -1,123 +1,332 @@
+import { screen, waitFor } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
import { render } from 'utils/testRenderer';
+import { testServerRoute, testServerSetup } from 'utils/testServer';
import { useImpactMetricsState } from './useImpactMetricsState.ts';
-import { Route, Routes } from 'react-router-dom';
-import { createLocalStorage } from '../../../utils/createLocalStorage.ts';
import type { FC } from 'react';
import type { ImpactMetricsState } from '../types.ts';
-const TestComponent: FC = () => {
- const { charts, layout } = useImpactMetricsState();
+const server = testServerSetup();
+
+const TestComponent: FC<{
+ enableActions?: boolean;
+}> = ({ enableActions = false }) => {
+ const {
+ charts,
+ layout,
+ loading,
+ error,
+ addChart,
+ updateChart,
+ deleteChart,
+ } = useImpactMetricsState();
return (
{charts.length}
{layout.length}
+ {loading.toString()}
+ {error ? 'has-error' : 'no-error'}
+
+ {enableActions && (
+
+ )}
+
+ {enableActions && charts.length > 0 && (
+
+ )}
+
+ {enableActions && charts.length > 0 && (
+
+ )}
);
};
-const TestWrapper = () => (
-
- } />
-
-);
+const mockSettings: ImpactMetricsState = {
+ charts: [
+ {
+ id: 'test-chart',
+ selectedSeries: 'test-series',
+ selectedRange: 'day' as const,
+ beginAtZero: true,
+ showRate: false,
+ selectedLabels: {},
+ title: 'Test Chart',
+ },
+ ],
+ layout: [
+ {
+ i: 'test-chart',
+ x: 0,
+ y: 0,
+ w: 6,
+ h: 4,
+ minW: 4,
+ minH: 2,
+ maxW: 12,
+ maxH: 8,
+ },
+ ],
+};
+
+const emptySettings: ImpactMetricsState = {
+ charts: [],
+ layout: [],
+};
describe('useImpactMetricsState', () => {
beforeEach(() => {
- window.localStorage.clear();
+ testServerRoute(server, '/api/admin/ui-config', {});
});
- it('loads state from localStorage to the URL after opening page without URL state', async () => {
- const { setValue } = createLocalStorage(
- 'impact-metrics-state',
- {
- charts: [],
- layout: [],
- },
+ it('loads settings from API', async () => {
+ testServerRoute(
+ server,
+ '/api/admin/impact-metrics/settings',
+ mockSettings,
);
- setValue({
- charts: [
- {
- id: 'test-chart',
- selectedSeries: 'test-series',
- selectedRange: 'day' as const,
- beginAtZero: true,
- showRate: false,
- selectedLabels: {},
- title: 'Test Chart',
- },
- ],
- layout: [
- {
- i: 'test-chart',
- x: 0,
- y: 0,
- w: 6,
- h: 4,
- minW: 4,
- minH: 2,
- maxW: 12,
- maxH: 8,
- },
- ],
+ render();
+
+ await waitFor(() => {
+ expect(screen.getByTestId('charts-count')).toHaveTextContent('1');
+ expect(screen.getByTestId('layout-count')).toHaveTextContent('1');
+ expect(screen.getByTestId('loading')).toHaveTextContent('false');
+ expect(screen.getByTestId('error')).toHaveTextContent('no-error');
});
-
- render(, { route: '/impact-metrics' });
-
- expect(window.location.href).toContain('charts=');
- expect(window.location.href).toContain('layout=');
});
- it('does not modify URL when URL already contains data', async () => {
- const { setValue } = createLocalStorage(
- 'impact-metrics-state',
+ it('handles empty settings', async () => {
+ testServerRoute(
+ server,
+ '/api/admin/impact-metrics/settings',
+ emptySettings,
+ );
+
+ render();
+
+ await waitFor(() => {
+ expect(screen.getByTestId('charts-count')).toHaveTextContent('0');
+ expect(screen.getByTestId('layout-count')).toHaveTextContent('0');
+ expect(screen.getByTestId('loading')).toHaveTextContent('false');
+ expect(screen.getByTestId('error')).toHaveTextContent('no-error');
+ });
+ });
+
+ it('handles API errors', async () => {
+ testServerRoute(
+ server,
+ '/api/admin/impact-metrics/settings',
+ { message: 'Server error' },
+ 'get',
+ 500,
+ );
+
+ render();
+
+ await waitFor(() => {
+ expect(screen.getByTestId('error')).toHaveTextContent('has-error');
+ });
+ });
+
+ it('adds a chart successfully', async () => {
+ testServerRoute(
+ server,
+ '/api/admin/impact-metrics/settings',
+ emptySettings,
+ );
+
+ render();
+
+ await waitFor(() => {
+ expect(screen.getByTestId('charts-count')).toHaveTextContent('0');
+ });
+
+ testServerRoute(
+ server,
+ '/api/admin/impact-metrics/settings',
{
- charts: [],
- layout: [],
+ charts: [
+ {
+ id: 'new-chart-id',
+ selectedSeries: 'test-series',
+ selectedRange: 'day',
+ beginAtZero: true,
+ showRate: false,
+ selectedLabels: {},
+ title: 'Test Chart',
+ },
+ ],
+ layout: [
+ {
+ i: 'new-chart-id',
+ x: 0,
+ y: 0,
+ w: 6,
+ h: 4,
+ minW: 4,
+ minH: 2,
+ maxW: 12,
+ maxH: 8,
+ },
+ ],
},
+ 'put',
+ 200,
);
- setValue({
- charts: [
- {
- id: 'old-chart',
- selectedSeries: 'old-series',
- selectedRange: 'day' as const,
- beginAtZero: true,
- showRate: false,
- selectedLabels: {},
- title: 'Old Chart',
- },
- ],
- layout: [],
- });
-
- const urlCharts = btoa(
- JSON.stringify([
- {
- id: 'url-chart',
- selectedSeries: 'url-series',
- selectedRange: 'day',
- beginAtZero: true,
- showRate: false,
- selectedLabels: {},
- title: 'URL Chart',
- },
- ]),
+ testServerRoute(
+ server,
+ '/api/admin/impact-metrics/settings',
+ {
+ charts: [
+ {
+ id: 'new-chart-id',
+ selectedSeries: 'test-series',
+ selectedRange: 'day',
+ beginAtZero: true,
+ showRate: false,
+ selectedLabels: {},
+ title: 'Test Chart',
+ },
+ ],
+ layout: [
+ {
+ i: 'new-chart-id',
+ x: 0,
+ y: 0,
+ w: 6,
+ h: 4,
+ minW: 4,
+ minH: 2,
+ maxW: 12,
+ maxH: 8,
+ },
+ ],
+ },
+ 'get',
+ 200,
);
- render(, {
- route: `/impact-metrics?charts=${encodeURIComponent(urlCharts)}`,
+ const addButton = screen.getByTestId('add-chart');
+ await userEvent.click(addButton);
+
+ await waitFor(
+ () => {
+ expect(screen.getByTestId('charts-count')).toHaveTextContent(
+ '1',
+ );
+ },
+ { timeout: 5000 },
+ );
+ });
+
+ it('updates a chart successfully', async () => {
+ testServerRoute(
+ server,
+ '/api/admin/impact-metrics/settings',
+ mockSettings,
+ );
+
+ testServerRoute(
+ server,
+ '/api/admin/impact-metrics/settings',
+ {
+ charts: [
+ {
+ ...mockSettings.charts[0],
+ title: 'Updated Chart',
+ },
+ ],
+ layout: mockSettings.layout,
+ },
+ 'put',
+ 200,
+ );
+
+ render();
+
+ await waitFor(() => {
+ expect(screen.getByTestId('charts-count')).toHaveTextContent('1');
});
- const urlParams = new URLSearchParams(window.location.search);
- const chartsParam = urlParams.get('charts');
+ const updateButton = screen.getByTestId('update-chart');
+ await userEvent.click(updateButton);
- expect(chartsParam).toBeTruthy();
+ await waitFor(() => {
+ expect(screen.getByTestId('charts-count')).toHaveTextContent('1');
+ });
+ });
- const decodedCharts = JSON.parse(atob(chartsParam!));
- expect(decodedCharts[0].id).toBe('url-chart');
- expect(decodedCharts[0].id).not.toBe('old-chart');
+ it('deletes a chart successfully', async () => {
+ testServerRoute(
+ server,
+ '/api/admin/impact-metrics/settings',
+ mockSettings,
+ );
+
+ render();
+
+ await waitFor(() => {
+ expect(screen.getByTestId('charts-count')).toHaveTextContent('1');
+ });
+
+ testServerRoute(
+ server,
+ '/api/admin/impact-metrics/settings',
+ emptySettings,
+ 'put',
+ 200,
+ );
+
+ testServerRoute(
+ server,
+ '/api/admin/impact-metrics/settings',
+ emptySettings,
+ 'get',
+ 200,
+ );
+
+ const deleteButton = screen.getByTestId('delete-chart');
+ await userEvent.click(deleteButton);
+
+ await waitFor(
+ () => {
+ expect(screen.getByTestId('charts-count')).toHaveTextContent(
+ '0',
+ );
+ },
+ { timeout: 5000 },
+ );
});
});