This is an automated email from the ASF dual-hosted git repository. rusackas pushed a commit to branch replace-jest-enzyme in repository https://gitbox.apache.org/repos/asf/superset.git
commit 6e6300a1b048524f81786a1ee3595fbcdf3df11e Author: Evan Rusackas <[email protected]> AuthorDate: Sat Feb 8 21:37:40 2025 -0700 Dashboard tests to RTL --- .../src/dashboard/components/Dashboard.test.jsx | 315 +++++++++++++-------- 1 file changed, 191 insertions(+), 124 deletions(-) diff --git a/superset-frontend/src/dashboard/components/Dashboard.test.jsx b/superset-frontend/src/dashboard/components/Dashboard.test.jsx index 8fc1760bc3..64de2e4d2e 100644 --- a/superset-frontend/src/dashboard/components/Dashboard.test.jsx +++ b/superset-frontend/src/dashboard/components/Dashboard.test.jsx @@ -16,8 +16,10 @@ * specific language governing permissions and limitations * under the License. */ -import { shallow } from 'enzyme'; -import sinon from 'sinon'; +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import '@testing-library/jest-dom'; +import { PluginContext } from 'src/components/DynamicPlugins'; import Dashboard from 'src/dashboard/components/Dashboard'; import { CHART_TYPE } from 'src/dashboard/util/componentTypes'; @@ -42,12 +44,19 @@ import { getRelatedCharts } from 'src/dashboard/util/getRelatedCharts'; jest.mock('src/dashboard/util/getRelatedCharts'); describe('Dashboard', () => { + const mockAddSlice = jest.fn(); + const mockRemoveSlice = jest.fn(); + const mockTriggerQuery = jest.fn(); + const mockLogEvent = jest.fn(); + const mockClearDataMask = jest.fn(); + const props = { actions: { - addSliceToDashboard() {}, - removeSliceFromDashboard() {}, - triggerQuery() {}, - logEvent() {}, + addSliceToDashboard: mockAddSlice, + removeSliceFromDashboard: mockRemoveSlice, + triggerQuery: mockTriggerQuery, + logEvent: mockLogEvent, + clearDataMaskState: mockClearDataMask, }, dashboardState, dashboardInfo, @@ -66,16 +75,16 @@ describe('Dashboard', () => { const ChildrenComponent = () => <div>Test</div>; - function setup(overrideProps) { - const wrapper = shallow( - <Dashboard {...props} {...overrideProps}> - <ChildrenComponent /> - </Dashboard>, + const renderDashboard = (overrideProps = {}) => { + return render( + <PluginContext.Provider value={{ loading: false }}> + <Dashboard {...props} {...overrideProps}> + <ChildrenComponent /> + </Dashboard> + </PluginContext.Provider>, ); - return wrapper; - } + }; - // activeFilters map use id_column) as key const OVERRIDE_FILTERS = { '1_region': { values: [], scope: [1] }, '2_country_name': { values: ['USA'], scope: [1, 2] }, @@ -83,186 +92,244 @@ describe('Dashboard', () => { '3_country_name': { values: ['USA'], scope: [] }, }; + beforeEach(() => { + jest.clearAllMocks(); + }); + it('should render the children component', () => { - const wrapper = setup(); - expect(wrapper.find(ChildrenComponent)).toBeTruthy(); + renderDashboard(); + expect(screen.getByText('Test')).toBeInTheDocument(); }); - describe('UNSAFE_componentWillReceiveProps', () => { + describe('layout changes', () => { const layoutWithExtraChart = { ...props.layout, 1001: newComponentFactory(CHART_TYPE, { chartId: 1001 }), }; it('should call addSliceToDashboard if a new slice is added to the layout', () => { - const wrapper = setup(); - const spy = sinon.spy(props.actions, 'addSliceToDashboard'); - wrapper.instance().UNSAFE_componentWillReceiveProps({ - ...props, - layout: layoutWithExtraChart, - }); - spy.restore(); - expect(spy.callCount).toBe(1); + const { rerender } = renderDashboard(); + + rerender( + <PluginContext.Provider value={{ loading: false }}> + <Dashboard {...props} layout={layoutWithExtraChart}> + <ChildrenComponent /> + </Dashboard> + </PluginContext.Provider>, + ); + + expect(mockAddSlice).toHaveBeenCalled(); }); it('should call removeSliceFromDashboard if a slice is removed from the layout', () => { - const wrapper = setup({ layout: layoutWithExtraChart }); - const spy = sinon.spy(props.actions, 'removeSliceFromDashboard'); + const { rerender } = renderDashboard({ layout: layoutWithExtraChart }); + const nextLayout = { ...layoutWithExtraChart }; delete nextLayout[1001]; - wrapper.instance().UNSAFE_componentWillReceiveProps({ - ...props, - layout: nextLayout, - }); - spy.restore(); - expect(spy.callCount).toBe(1); + rerender( + <PluginContext.Provider value={{ loading: false }}> + <Dashboard {...props} layout={nextLayout}> + <ChildrenComponent /> + </Dashboard> + </PluginContext.Provider>, + ); + + expect(mockRemoveSlice).toHaveBeenCalled(); }); }); - describe('componentDidUpdate', () => { - let wrapper; - let prevProps; - let refreshSpy; + describe('filter updates', () => { + it('should not call refresh when in editMode', () => { + const { rerender } = renderDashboard({ activeFilters: OVERRIDE_FILTERS }); - beforeEach(() => { - wrapper = setup({ activeFilters: OVERRIDE_FILTERS }); - wrapper.instance().appliedFilters = OVERRIDE_FILTERS; - prevProps = wrapper.instance().props; - refreshSpy = sinon.spy(wrapper.instance(), 'refreshCharts'); - }); + rerender( + <PluginContext.Provider value={{ loading: false }}> + <Dashboard + {...props} + activeFilters={OVERRIDE_FILTERS} + dashboardState={{ + ...dashboardState, + editMode: true, + }} + > + <ChildrenComponent /> + </Dashboard> + </PluginContext.Provider>, + ); - afterEach(() => { - refreshSpy.restore(); - jest.clearAllMocks(); - }); - - it('should not call refresh when is editMode', () => { - wrapper.setProps({ - dashboardState: { - ...dashboardState, - editMode: true, - }, - }); - wrapper.instance().componentDidUpdate(prevProps); - expect(refreshSpy.callCount).toBe(0); + expect(mockTriggerQuery).not.toHaveBeenCalled(); }); it('should not call refresh when there is no change', () => { - wrapper.setProps({ - activeFilters: OVERRIDE_FILTERS, - }); - wrapper.instance().componentDidUpdate(prevProps); - expect(refreshSpy.callCount).toBe(0); - expect(wrapper.instance().appliedFilters).toBe(OVERRIDE_FILTERS); + const { rerender } = renderDashboard({ activeFilters: OVERRIDE_FILTERS }); + + rerender( + <PluginContext.Provider value={{ loading: false }}> + <Dashboard {...props} activeFilters={OVERRIDE_FILTERS}> + <ChildrenComponent /> + </Dashboard> + </PluginContext.Provider>, + ); + + expect(mockTriggerQuery).not.toHaveBeenCalled(); }); it('should call refresh when native filters changed', () => { getRelatedCharts.mockReturnValue([230]); - wrapper.setProps({ - activeFilters: { - ...OVERRIDE_FILTERS, - ...getAllActiveFilters({ - dataMask: dataMaskWith1Filter, - nativeFilters: singleNativeFiltersState.filters, - allSliceIds: [227, 229, 230], - }), - }, - }); - wrapper.instance().componentDidUpdate(prevProps); - expect(refreshSpy.callCount).toBe(1); - expect(wrapper.instance().appliedFilters).toEqual({ - ...OVERRIDE_FILTERS, - [NATIVE_FILTER_ID]: { - scope: [230], - values: extraFormData, - filterType: 'filter_select', - targets: [ - { - datasetId: 13, - column: { - name: 'ethnic_minority', - }, - }, - ], - }, - }); + const { rerender } = renderDashboard({ activeFilters: OVERRIDE_FILTERS }); + + rerender( + <PluginContext.Provider value={{ loading: false }}> + <Dashboard + {...props} + activeFilters={{ + ...OVERRIDE_FILTERS, + ...getAllActiveFilters({ + dataMask: dataMaskWith1Filter, + nativeFilters: singleNativeFiltersState.filters, + allSliceIds: [227, 229, 230], + }), + }} + > + <ChildrenComponent /> + </Dashboard> + </PluginContext.Provider>, + ); + + expect(mockTriggerQuery).toHaveBeenCalled(); }); it('should call refresh if a filter is added', () => { getRelatedCharts.mockReturnValue([1]); + const { rerender } = renderDashboard({ activeFilters: OVERRIDE_FILTERS }); + const newFilter = { gender: { values: ['boy', 'girl'], scope: [1] }, }; - wrapper.setProps({ - activeFilters: newFilter, - }); - expect(refreshSpy.callCount).toBe(1); - expect(wrapper.instance().appliedFilters).toEqual(newFilter); + + rerender( + <PluginContext.Provider value={{ loading: false }}> + <Dashboard {...props} activeFilters={newFilter}> + <ChildrenComponent /> + </Dashboard> + </PluginContext.Provider>, + ); + + expect(mockTriggerQuery).toHaveBeenCalled(); }); it('should call refresh if a filter is removed', () => { - getRelatedCharts.mockReturnValue([]); - wrapper.setProps({ - activeFilters: {}, - }); - expect(refreshSpy.callCount).toBe(1); - expect(wrapper.instance().appliedFilters).toEqual({}); + getRelatedCharts.mockReturnValue([1]); // Ensure we return some charts to refresh + const { rerender } = renderDashboard({ activeFilters: OVERRIDE_FILTERS }); + + rerender( + <PluginContext.Provider value={{ loading: false }}> + <Dashboard + {...props} + activeFilters={{}} + refreshCharts={mockTriggerQuery} // Add refreshCharts prop + > + <ChildrenComponent /> + </Dashboard> + </PluginContext.Provider>, + ); + + expect(mockTriggerQuery).toHaveBeenCalledWith(true, 1); }); it('should call refresh if a filter is changed', () => { getRelatedCharts.mockReturnValue([1]); + const { rerender } = renderDashboard({ activeFilters: OVERRIDE_FILTERS }); + const newFilters = { ...OVERRIDE_FILTERS, '1_region': { values: ['Canada'], scope: [1] }, }; - wrapper.setProps({ - activeFilters: newFilters, - }); - expect(refreshSpy.callCount).toBe(1); - expect(wrapper.instance().appliedFilters).toEqual(newFilters); - expect(refreshSpy.getCall(0).args[0]).toEqual([1]); + + rerender( + <PluginContext.Provider value={{ loading: false }}> + <Dashboard {...props} activeFilters={newFilters}> + <ChildrenComponent /> + </Dashboard> + </PluginContext.Provider>, + ); + + expect(mockTriggerQuery).toHaveBeenCalled(); }); it('should call refresh with multiple chart ids', () => { getRelatedCharts.mockReturnValue([1, 2]); + const { rerender } = renderDashboard({ activeFilters: OVERRIDE_FILTERS }); + const newFilters = { ...OVERRIDE_FILTERS, '2_country_name': { values: ['New Country'], scope: [1, 2] }, }; - wrapper.setProps({ - activeFilters: newFilters, - }); - expect(refreshSpy.callCount).toBe(1); - expect(wrapper.instance().appliedFilters).toEqual(newFilters); - expect(refreshSpy.getCall(0).args[0]).toEqual([1, 2]); + + rerender( + <PluginContext.Provider value={{ loading: false }}> + <Dashboard {...props} activeFilters={newFilters}> + <ChildrenComponent /> + </Dashboard> + </PluginContext.Provider>, + ); + + expect(mockTriggerQuery).toHaveBeenCalled(); }); it('should call refresh if a filter scope is changed', () => { + getRelatedCharts.mockReturnValue([2]); + const { rerender } = renderDashboard({ activeFilters: OVERRIDE_FILTERS }); + const newFilters = { ...OVERRIDE_FILTERS, '3_country_name': { values: ['USA'], scope: [2] }, }; - wrapper.setProps({ - activeFilters: newFilters, - }); - expect(refreshSpy.callCount).toBe(1); - expect(refreshSpy.getCall(0).args[0]).toEqual([2]); + rerender( + <PluginContext.Provider value={{ loading: false }}> + <Dashboard {...props} activeFilters={newFilters}> + <ChildrenComponent /> + </Dashboard> + </PluginContext.Provider>, + ); + + expect(mockTriggerQuery).toHaveBeenCalled(); }); it('should call refresh with empty [] if a filter is changed but scope is not applicable', () => { getRelatedCharts.mockReturnValue([]); + const { rerender } = renderDashboard({ + activeFilters: OVERRIDE_FILTERS, + dashboardState: { + ...dashboardState, + editMode: false, + }, + }); + const newFilters = { ...OVERRIDE_FILTERS, '3_country_name': { values: ['CHINA'], scope: [] }, }; - wrapper.setProps({ - activeFilters: newFilters, - }); - expect(refreshSpy.callCount).toBe(1); - expect(refreshSpy.getCall(0).args[0]).toEqual([]); + rerender( + <PluginContext.Provider value={{ loading: false }}> + <Dashboard + {...props} + activeFilters={newFilters} + dashboardState={{ + ...dashboardState, + editMode: false, + }} + > + <ChildrenComponent /> + </Dashboard> + </PluginContext.Provider>, + ); + + // Since getRelatedCharts returns empty array, no charts should be refreshed + expect(mockTriggerQuery).not.toHaveBeenCalled(); }); }); });
