mirror of
https://github.com/olehomelchenko/astrolabe.git
synced 2026-08-08 10:12:34 +00:00
78 lines
2.9 KiB
TypeScript
78 lines
2.9 KiB
TypeScript
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
|
|
import { act } from 'react';
|
|
import { createRoot, type Root } from 'react-dom/client';
|
|
import { createDataset } from '@core/dataset';
|
|
import { useChartBuilderStore } from '../stores/ChartBuilderStore';
|
|
import { useDatasetStore } from '../stores/DatasetStore';
|
|
import { useSnippetStore } from '../stores/SnippetStore';
|
|
import { ChartBuilderModal } from './ChartBuilderModal';
|
|
|
|
// The builder preview embeds a real Vega chart in an effect; stub the renderer so
|
|
// this render test stays a pure React/DOM check (the loop we guard against happens
|
|
// during commit, long before any chart is drawn).
|
|
vi.mock('../services/chart-renderer', () => ({
|
|
renderSpec: () => Promise.resolve({ destroy() {}, resize() {} }),
|
|
}));
|
|
|
|
// React 19 wants this flag set for act() to drive effects without warnings.
|
|
(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true;
|
|
|
|
const T = new Date('2026-06-01T00:00:00Z');
|
|
let container: HTMLDivElement;
|
|
let root: Root;
|
|
|
|
beforeEach(() => {
|
|
useChartBuilderStore.getState().reset();
|
|
useDatasetStore.getState().reset();
|
|
useSnippetStore.getState().reset();
|
|
container = document.createElement('div');
|
|
document.body.appendChild(container);
|
|
root = createRoot(container);
|
|
});
|
|
|
|
afterEach(() => {
|
|
act(() => root.unmount());
|
|
container.remove();
|
|
});
|
|
|
|
describe('ChartBuilderModal', () => {
|
|
test('renders without an infinite update loop when the config has warnings (regression)', async () => {
|
|
// Two numeric columns → default mark Point (clean). Switching to Bar makes it
|
|
// "two measures on a non-scatter" → a NON-EMPTY warnings array — the exact
|
|
// condition that previously looped because the warnings selector returned a
|
|
// fresh array of objects on every render. The fix derives warnings via useMemo
|
|
// over the stable `config` reference instead.
|
|
const ds = createDataset({
|
|
name: 'Nums',
|
|
data: [
|
|
{ a: 1, b: 2 },
|
|
{ a: 3, b: 4 },
|
|
],
|
|
format: 'json',
|
|
source: 'inline',
|
|
now: T,
|
|
});
|
|
useDatasetStore.getState().add(ds);
|
|
useChartBuilderStore.getState().init(ds.id);
|
|
useChartBuilderStore.getState().setMark('bar');
|
|
expect(useChartBuilderStore.getState().config.mark).toBe('bar');
|
|
|
|
// If the component looped, this act() would throw "Maximum update depth exceeded".
|
|
await act(async () => {
|
|
root.render(<ChartBuilderModal />);
|
|
await Promise.resolve();
|
|
});
|
|
|
|
expect(container.textContent).toContain('Building from');
|
|
expect(container.textContent).toContain('scatter'); // the guidance hint rendered
|
|
});
|
|
|
|
test('shows the empty state when no dataset is loaded', async () => {
|
|
await act(async () => {
|
|
root.render(<ChartBuilderModal />);
|
|
await Promise.resolve();
|
|
});
|
|
expect(container.textContent).toContain('No dataset loaded');
|
|
});
|
|
});
|