feat(frontend): hydrate + debounced-persist cross-subject plot config
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { render, screen, fireEvent } from '@testing-library/react';
|
||||
import { render, screen, fireEvent, act } from '@testing-library/react';
|
||||
|
||||
// recharts uses ResizeObserver (absent in jsdom); stub all used pieces as passthroughs.
|
||||
// Coverage boundary: with recharts mocked out, these tests cover only the sidebar
|
||||
@@ -59,3 +59,68 @@ it('unchecking a subject in the sidebar persists across re-render', () => {
|
||||
expect(screen.getByLabelText('Mouse-A1').checked).toBe(false);
|
||||
expect(screen.getByText('Show all')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('hydrates hidden subjects and sidebar-open from plotConfig', () => {
|
||||
render(
|
||||
<ExperimentAnalysisCharts
|
||||
animals={ANIMALS}
|
||||
experimentStatuses={STATUSES}
|
||||
subjectTemplate={SUBJECT_TEMPLATE}
|
||||
dailyTemplate={DAILY_TEMPLATE}
|
||||
plotConfig={{ hiddenSubjects: ['a1'], sidebarOpen: true }}
|
||||
/>,
|
||||
);
|
||||
expect(screen.getByLabelText('Mouse-A1').checked).toBe(false);
|
||||
expect(screen.getByLabelText('Mouse-B1').checked).toBe(true);
|
||||
});
|
||||
|
||||
describe('debounced auto-save', () => {
|
||||
beforeEach(() => jest.useFakeTimers());
|
||||
afterEach(() => jest.useRealTimers());
|
||||
|
||||
it('does not call onPersistConfig on initial render', () => {
|
||||
const onPersist = jest.fn();
|
||||
render(
|
||||
<ExperimentAnalysisCharts
|
||||
animals={ANIMALS} experimentStatuses={STATUSES}
|
||||
subjectTemplate={SUBJECT_TEMPLATE} dailyTemplate={DAILY_TEMPLATE}
|
||||
plotConfig={null} onPersistConfig={onPersist}
|
||||
/>,
|
||||
);
|
||||
act(() => { jest.advanceTimersByTime(1500); });
|
||||
expect(onPersist).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('calls onPersistConfig ~1s after a change, with the updated config', () => {
|
||||
const onPersist = jest.fn().mockResolvedValue({});
|
||||
render(
|
||||
<ExperimentAnalysisCharts
|
||||
animals={ANIMALS} experimentStatuses={STATUSES}
|
||||
subjectTemplate={SUBJECT_TEMPLATE} dailyTemplate={DAILY_TEMPLATE}
|
||||
plotConfig={{ sidebarOpen: true }} onPersistConfig={onPersist}
|
||||
/>,
|
||||
);
|
||||
fireEvent.click(screen.getByLabelText('Mouse-A1'));
|
||||
expect(onPersist).not.toHaveBeenCalled();
|
||||
act(() => { jest.advanceTimersByTime(1100); });
|
||||
expect(onPersist).toHaveBeenCalledTimes(1);
|
||||
expect(onPersist.mock.calls[0][0].hiddenSubjects).toContain('a1');
|
||||
});
|
||||
|
||||
it('does not save and clears the saving hint when a change is reverted before the debounce fires', () => {
|
||||
const onPersist = jest.fn().mockResolvedValue({});
|
||||
render(
|
||||
<ExperimentAnalysisCharts
|
||||
animals={ANIMALS} experimentStatuses={STATUSES}
|
||||
subjectTemplate={SUBJECT_TEMPLATE} dailyTemplate={DAILY_TEMPLATE}
|
||||
plotConfig={{ sidebarOpen: true }} onPersistConfig={onPersist}
|
||||
/>,
|
||||
);
|
||||
fireEvent.click(screen.getByLabelText('Mouse-A1')); // hide a1
|
||||
expect(screen.getByText('Saving…')).toBeInTheDocument();
|
||||
fireEvent.click(screen.getByLabelText('Mouse-A1')); // revert before the 1s timer
|
||||
act(() => { jest.advanceTimersByTime(1100); });
|
||||
expect(onPersist).not.toHaveBeenCalled();
|
||||
expect(screen.queryByText('Saving…')).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user