Skip to content

Commit 963f3ee

Browse files
committed
add component tests for chart-selectors, chart-buttons, date-range-picker
1 parent f60fb85 commit 963f3ee

3 files changed

Lines changed: 266 additions & 0 deletions

File tree

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import { ChartButtons } from '@/components/ui/chart-buttons';
2+
3+
describe('ChartButtons', () => {
4+
describe('without CSV export', () => {
5+
beforeEach(() => {
6+
cy.mount(
7+
<div style={{ position: 'relative', width: 400, height: 200 }}>
8+
<div id="test-chart">Chart content</div>
9+
<ChartButtons chartId="test-chart" analyticsPrefix="test" />
10+
</div>,
11+
);
12+
});
13+
14+
it('renders export button', () => {
15+
cy.get('[data-testid="export-button"]').should('be.visible');
16+
});
17+
18+
it('renders zoom reset button', () => {
19+
cy.get('[data-testid="zoom-reset-button"]').should('be.visible');
20+
});
21+
22+
it('zoom reset dispatches custom event', () => {
23+
cy.window().then((win) => {
24+
const handler = cy.stub().as('zoomReset');
25+
win.addEventListener('test_zoom_reset_test-chart', handler);
26+
});
27+
cy.get('[data-testid="zoom-reset-button"]').click();
28+
cy.get('@zoomReset').should('have.been.calledOnce');
29+
});
30+
});
31+
32+
describe('with CSV export', () => {
33+
it('shows dropdown with PNG and CSV options', () => {
34+
const onExportCsv = cy.stub().as('csvExport');
35+
cy.mount(
36+
<div style={{ position: 'relative', width: 400, height: 200 }}>
37+
<div id="test-chart">Chart content</div>
38+
<ChartButtons chartId="test-chart" analyticsPrefix="test" onExportCsv={onExportCsv} />
39+
</div>,
40+
);
41+
cy.get('[data-testid="export-button"]').click();
42+
cy.get('[data-testid="export-png-button"]').should('be.visible');
43+
cy.get('[data-testid="export-csv-button"]').should('be.visible');
44+
});
45+
46+
it('clicking CSV calls onExportCsv', () => {
47+
const onExportCsv = cy.stub().as('csvExport');
48+
cy.mount(
49+
<div style={{ position: 'relative', width: 400, height: 200 }}>
50+
<div id="test-chart">Chart content</div>
51+
<ChartButtons chartId="test-chart" analyticsPrefix="test" onExportCsv={onExportCsv} />
52+
</div>,
53+
);
54+
cy.get('[data-testid="export-button"]').click();
55+
cy.get('[data-testid="export-csv-button"]').click();
56+
cy.get('@csvExport').should('have.been.calledOnce');
57+
});
58+
});
59+
60+
describe('hideZoomReset', () => {
61+
it('hides zoom reset button when hideZoomReset is true', () => {
62+
cy.mount(
63+
<div style={{ position: 'relative', width: 400, height: 200 }}>
64+
<div id="test-chart">Chart content</div>
65+
<ChartButtons chartId="test-chart" analyticsPrefix="test" hideZoomReset />
66+
</div>,
67+
);
68+
cy.get('[data-testid="zoom-reset-button"]').should('not.exist');
69+
cy.get('[data-testid="export-button"]').should('be.visible');
70+
});
71+
});
72+
});
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import { useState } from 'react';
2+
3+
import { ModelSelector, SequenceSelector, PrecisionSelector } from '@/components/ui/chart-selectors';
4+
import { TooltipProvider } from '@/components/ui/tooltip';
5+
6+
function ModelSelectorHarness() {
7+
const [value, setValue] = useState('DeepSeek-R1-0528');
8+
return (
9+
<TooltipProvider>
10+
<ModelSelector
11+
value={value}
12+
onChange={setValue}
13+
availableModels={['DeepSeek-R1-0528', 'Llama-4-Maverick', 'Qwen3-235B']}
14+
data-testid="model-selector"
15+
/>
16+
</TooltipProvider>
17+
);
18+
}
19+
20+
function SequenceSelectorHarness() {
21+
const [value, setValue] = useState('1024_128');
22+
return (
23+
<TooltipProvider>
24+
<SequenceSelector
25+
value={value}
26+
onChange={setValue}
27+
availableSequences={['1024_128', '1024_8192', '8192_1024']}
28+
data-testid="sequence-selector"
29+
/>
30+
</TooltipProvider>
31+
);
32+
}
33+
34+
function PrecisionSelectorHarness() {
35+
const [value, setValue] = useState(['FP8']);
36+
return (
37+
<TooltipProvider>
38+
<PrecisionSelector
39+
value={value}
40+
onChange={setValue}
41+
availablePrecisions={['FP8', 'FP4', 'BF16']}
42+
data-testid="precision-multiselect"
43+
/>
44+
</TooltipProvider>
45+
);
46+
}
47+
48+
describe('Chart Selectors', () => {
49+
describe('ModelSelector', () => {
50+
beforeEach(() => {
51+
cy.mount(<ModelSelectorHarness />);
52+
});
53+
54+
it('renders with data-testid', () => {
55+
cy.get('[data-testid="model-selector"]').should('be.visible');
56+
});
57+
58+
it('shows options when clicked', () => {
59+
cy.get('[data-testid="model-selector"]').click();
60+
cy.get('[role="option"]').should('have.length.greaterThan', 0);
61+
});
62+
63+
it('selecting an option updates the displayed value', () => {
64+
cy.get('[data-testid="model-selector"]').click();
65+
cy.get('[role="option"]').contains('Qwen3-235B').click();
66+
cy.get('[data-testid="model-selector"]').should('contain', 'Qwen3-235B');
67+
});
68+
});
69+
70+
describe('SequenceSelector', () => {
71+
beforeEach(() => {
72+
cy.mount(<SequenceSelectorHarness />);
73+
});
74+
75+
it('renders with data-testid', () => {
76+
cy.get('[data-testid="sequence-selector"]').should('be.visible');
77+
});
78+
79+
it('shows options when clicked', () => {
80+
cy.get('[data-testid="sequence-selector"]').click();
81+
cy.get('[role="option"]').should('have.length', 3);
82+
});
83+
84+
it('selecting an option updates the displayed value', () => {
85+
cy.get('[data-testid="sequence-selector"]').click();
86+
cy.get('[role="option"]').last().click();
87+
cy.get('[data-testid="sequence-selector"]').should('not.contain', '1K / 128');
88+
});
89+
});
90+
91+
describe('PrecisionSelector', () => {
92+
beforeEach(() => {
93+
cy.mount(<PrecisionSelectorHarness />);
94+
});
95+
96+
it('renders with data-testid', () => {
97+
cy.get('[data-testid="precision-multiselect"]').should('be.visible');
98+
});
99+
100+
it('shows current selection', () => {
101+
cy.get('[data-testid="precision-multiselect"]').should('contain', 'FP8');
102+
});
103+
});
104+
});
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import { useState } from 'react';
2+
3+
import { DateRangePicker, DateRange } from '@/components/ui/date-range-picker';
4+
5+
function DateRangePickerHarness({
6+
initialRange = { startDate: '', endDate: '' },
7+
availableDates,
8+
}: {
9+
initialRange?: DateRange;
10+
availableDates?: string[];
11+
}) {
12+
const [range, setRange] = useState<DateRange>(initialRange);
13+
return (
14+
<div data-testid="date-range-wrapper">
15+
<DateRangePicker
16+
dateRange={range}
17+
onChange={setRange}
18+
availableDates={availableDates}
19+
placeholder="Select date range"
20+
/>
21+
<div data-testid="date-range-output">
22+
{range.startDate && range.endDate
23+
? `${range.startDate} to ${range.endDate}`
24+
: 'no range'}
25+
</div>
26+
</div>
27+
);
28+
}
29+
30+
describe('DateRangePicker', () => {
31+
it('renders with placeholder text', () => {
32+
cy.mount(<DateRangePickerHarness />);
33+
cy.get('[data-testid="date-range-wrapper"]').should('contain', 'Select date range');
34+
});
35+
36+
it('opens dialog when clicked', () => {
37+
cy.mount(<DateRangePickerHarness />);
38+
cy.contains('Select date range').click();
39+
cy.get('[role="dialog"]').should('be.visible');
40+
cy.contains('Select Date Range').should('be.visible');
41+
});
42+
43+
it('shows cancel and apply buttons in dialog', () => {
44+
cy.mount(<DateRangePickerHarness />);
45+
cy.contains('Select date range').click();
46+
cy.contains('button', 'Cancel').should('be.visible');
47+
cy.contains('button', 'Apply').should('be.visible');
48+
});
49+
50+
it('apply is disabled when no dates selected', () => {
51+
cy.mount(<DateRangePickerHarness />);
52+
cy.contains('Select date range').click();
53+
cy.contains('button', 'Apply').should('be.disabled');
54+
});
55+
56+
it('cancel closes the dialog', () => {
57+
cy.mount(<DateRangePickerHarness />);
58+
cy.contains('Select date range').click();
59+
cy.get('[role="dialog"]').should('be.visible');
60+
cy.contains('button', 'Cancel').click();
61+
cy.get('[role="dialog"]').should('not.exist');
62+
});
63+
64+
it('displays formatted range when dates are provided', () => {
65+
cy.mount(
66+
<DateRangePickerHarness initialRange={{ startDate: '2026-01-15', endDate: '2026-02-20' }} />,
67+
);
68+
cy.get('[data-testid="date-range-wrapper"]').should('contain', 'Jan 15, 2026');
69+
cy.get('[data-testid="date-range-wrapper"]').should('contain', 'Feb 20, 2026');
70+
});
71+
72+
it('shows quick select buttons when availableDates provided', () => {
73+
const dates = ['2025-12-01', '2025-12-15', '2026-01-01', '2026-02-01', '2026-03-01'];
74+
cy.mount(<DateRangePickerHarness availableDates={dates} />);
75+
cy.contains('Select date range').click();
76+
cy.contains('button', 'Max Range').should('be.visible');
77+
});
78+
79+
it('shows overlay when no available dates', () => {
80+
cy.mount(<DateRangePickerHarness availableDates={[]} />);
81+
cy.contains('Select date range').click();
82+
cy.contains('No available dates').should('be.visible');
83+
});
84+
85+
it('shows overlay when only 1 available date', () => {
86+
cy.mount(<DateRangePickerHarness availableDates={['2026-01-01']} />);
87+
cy.contains('Select date range').click();
88+
cy.contains('Only 1 date available').should('be.visible');
89+
});
90+
});

0 commit comments

Comments
 (0)