import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest' import { act, fireEvent, render, screen } from '@testing-library/react' import type { ECharts } from 'echarts' import { BrushToggle } from './brush-toggle' import { Provider } from '../../provider/widget-provider' import { applyTransforms, clearAllWidgetStores, getWidgetStore, setEchartInstance, } from '../../stores' interface ChartHandlers { finished?: () => void } interface MockChart { dispatchAction: ReturnType on: (ev: string, cb: () => void) => void off: (ev: string, cb: () => void) => void __handlers: ChartHandlers } function makeMockChart(): MockChart { const handlers: ChartHandlers = {} return { dispatchAction: vi.fn(), on: (ev, cb) => { if (ev === 'finished') handlers.finished = cb }, off: (ev) => { if (ev === 'finished') handlers.finished = undefined }, __handlers: handlers, } } beforeEach(() => clearAllWidgetStores()) afterEach(() => clearAllWidgetStores()) describe('', () => { it('starts disabled by default', () => { render( , ) expect(screen.getByLabelText('Enable brush selection')).toBeTruthy() }) it('respects initialEnabled=true', () => { render( , ) expect(screen.getByLabelText('Disable brush selection')).toBeTruthy() }) it('clicking enables the transform — brush config appears in the post-pipeline option', () => { render( , ) fireEvent.click(screen.getByLabelText('Enable brush selection')) // configTransforms now apply inside ; replay the // pipeline locally over a blank structural option. const configTransforms = getWidgetStore('bt-3').getState().configTransforms const opts = applyTransforms({}, configTransforms) as { brush?: { xAxisIndex?: string } } expect(opts.brush).toBeTruthy() expect(opts.brush?.xAxisIndex).toBe('all') }) it('dispatches takeGlobalCursor with brush config when enabled and a chart is registered', () => { const chart = makeMockChart() setEchartInstance('bt-4', chart as unknown as ECharts) render( , ) expect(chart.dispatchAction).toHaveBeenCalledWith({ type: 'takeGlobalCursor', key: 'brush', brushOption: { brushType: 'lineX', brushMode: 'multiple' }, }) }) it("re-dispatches takeGlobalCursor on the chart's 'finished' event", () => { const chart = makeMockChart() setEchartInstance('bt-5', chart as unknown as ECharts) render( , ) chart.dispatchAction.mockClear() act(() => chart.__handlers.finished?.()) expect(chart.dispatchAction).toHaveBeenCalledWith({ type: 'takeGlobalCursor', key: 'brush', brushOption: { brushType: 'lineX', brushMode: 'multiple' }, }) }) it('on disable: clears the global cursor and wipes drawn areas', () => { const chart = makeMockChart() setEchartInstance('bt-6', chart as unknown as ECharts) render( , ) chart.dispatchAction.mockClear() fireEvent.click(screen.getByLabelText('Disable brush selection')) expect(chart.dispatchAction).toHaveBeenCalledWith({ type: 'takeGlobalCursor', }) expect(chart.dispatchAction).toHaveBeenCalledWith({ type: 'brush', areas: [], }) }) it('honors brushType=rect and brushMode=single', () => { const chart = makeMockChart() setEchartInstance('bt-7', chart as unknown as ECharts) render( , ) expect(chart.dispatchAction).toHaveBeenCalledWith({ type: 'takeGlobalCursor', key: 'brush', brushOption: { brushType: 'rect', brushMode: 'single' }, }) }) it('wipes drawn brush areas when an external selection is cleared while enabled', () => { const chart = makeMockChart() setEchartInstance('bt-9', chart as unknown as ECharts) const { rerender } = render( , ) chart.dispatchAction.mockClear() rerender( , ) expect(chart.dispatchAction).toHaveBeenCalledWith({ type: 'brush', areas: [], }) }) it('attaches the cursor reactively when the chart arrives *after* the toggle mounts', () => { const chart = makeMockChart() render( , ) // Chart not yet registered — no dispatch should have happened. expect(chart.dispatchAction).not.toHaveBeenCalled() // Simulate the bridge mounting and publishing the instance. act(() => setEchartInstance('bt-8', chart as unknown as ECharts)) expect(chart.dispatchAction).toHaveBeenCalledWith({ type: 'takeGlobalCursor', key: 'brush', brushOption: { brushType: 'lineX', brushMode: 'multiple' }, }) }) })