|
1 | 1 | import React from 'react'; |
2 | | -import { render, screen } from '@testing-library/react'; |
| 2 | +import { render, screen, fireEvent } from '@testing-library/react'; |
3 | 3 | import { describe, it, expect, vi } from 'vitest'; |
4 | 4 | import ChartContainer from '../ChartContainer'; |
5 | 5 |
|
6 | 6 | // Mock chart.js and react-chartjs-2 to avoid canvas requirements |
7 | 7 | vi.mock('chart.js', () => { |
8 | | - const Chart = { register: vi.fn() }; |
| 8 | + const Chart = { |
| 9 | + register: vi.fn(), |
| 10 | + defaults: { plugins: { legend: { labels: { generateLabels: vi.fn(() => []) } } } } |
| 11 | + }; |
9 | 12 | return { |
10 | 13 | Chart, |
11 | 14 | ChartJS: Chart, |
@@ -103,4 +106,63 @@ describe('ChartContainer', () => { |
103 | 106 | hover({}, [{ index: 0 }]); |
104 | 107 | expect(__charts[1].setActiveElements).toHaveBeenCalled(); |
105 | 108 | }); |
| 109 | + |
| 110 | + it('parses metrics, applies range and triggers callbacks', () => { |
| 111 | + const onXRangeChange = vi.fn(); |
| 112 | + const onMaxStepChange = vi.fn(); |
| 113 | + const files = [ |
| 114 | + { |
| 115 | + name: 'a.log', |
| 116 | + enabled: true, |
| 117 | + content: 'loss: 1\nloss: 2\nloss: 3\nacc: 4\nacc: 5', |
| 118 | + config: { dataRange: { start: 1, end: 3 } } |
| 119 | + }, |
| 120 | + { |
| 121 | + name: 'b.log', |
| 122 | + enabled: true, |
| 123 | + content: 'loss: 2\nloss: 4\nacc: 6\nacc: 8', |
| 124 | + config: { dataRange: { start: 1, end: 3 } } |
| 125 | + } |
| 126 | + ]; |
| 127 | + const metrics = [ |
| 128 | + { keyword: 'loss', mode: 'keyword' }, |
| 129 | + { regex: 'acc:(\\d+)', mode: 'regex' }, |
| 130 | + {} |
| 131 | + ]; |
| 132 | + |
| 133 | + render( |
| 134 | + <ChartContainer |
| 135 | + files={files} |
| 136 | + metrics={metrics} |
| 137 | + compareMode="relative" |
| 138 | + onXRangeChange={onXRangeChange} |
| 139 | + onMaxStepChange={onMaxStepChange} |
| 140 | + /> |
| 141 | + ); |
| 142 | + |
| 143 | + // metric titles |
| 144 | + expect(screen.getAllByText(/loss/)[0]).toBeTruthy(); |
| 145 | + screen.getByText(/metric2/); |
| 146 | + screen.getByText(/metric3/); |
| 147 | + |
| 148 | + // data range applied (start 1 end 3 => 2 points for loss) |
| 149 | + const currentProps = __lineProps.slice(-5); |
| 150 | + expect(currentProps[0].data.datasets[0].data).toHaveLength(2); |
| 151 | + |
| 152 | + // trigger container mouse leave |
| 153 | + const container = screen.getAllByTestId('line-chart')[0].parentElement; |
| 154 | + fireEvent.mouseLeave(container); |
| 155 | + |
| 156 | + // invoke legend and tooltip callbacks |
| 157 | + const opts = currentProps[0].options; |
| 158 | + opts.plugins.legend.labels.generateLabels({ data: { datasets: [{}, { borderDash: [5,5] }] } }); |
| 159 | + const tt = opts.plugins.tooltip.callbacks; |
| 160 | + tt.title([{ parsed: { x: 1 } }]); |
| 161 | + tt.label({ parsed: { y: 1.2345 } }); |
| 162 | + tt.labelColor({ dataset: { borderColor: '#fff' } }); |
| 163 | + |
| 164 | + // invoke zoom callbacks |
| 165 | + opts.plugins.zoom.pan.onPanComplete({ chart: { scales: { x: { min: 0, max: 10 } } } }); |
| 166 | + opts.plugins.zoom.zoom.onZoomComplete({ chart: { scales: { x: { min: 2, max: 4 } } } }); |
| 167 | + }); |
106 | 168 | }); |
0 commit comments