Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: move Dashboard.test.jsx from enzyme to react testing lib #32022

Closed
wants to merge 4 commits into from
Closed
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
280 changes: 159 additions & 121 deletions superset-frontend/src/dashboard/components/Dashboard.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@
* 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';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please import from spec/helpers/testing-library


import Dashboard from 'src/dashboard/components/Dashboard';
import { CHART_TYPE } from 'src/dashboard/util/componentTypes';
Expand Down Expand Up @@ -48,7 +49,9 @@ describe('Dashboard', () => {
removeSliceFromDashboard() {},
triggerQuery() {},
logEvent() {},
clearDataMaskState() {},
},
dashboardId: 1,
dashboardState,
dashboardInfo,
charts: chartQueries,
Expand All @@ -66,203 +69,238 @@ describe('Dashboard', () => {

const ChildrenComponent = () => <div>Test</div>;

function setup(overrideProps) {
const wrapper = shallow(
<Dashboard {...props} {...overrideProps}>
<ChildrenComponent />
</Dashboard>,
);
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] },
'3_region': { values: [], scope: [1] },
'3_country_name': { values: ['USA'], scope: [] },
};

it('should render the children component', () => {
const wrapper = setup();
expect(wrapper.find(ChildrenComponent)).toExist();
});
function renderDashboard(override = {}) {
// Helper to render the Dashboard and return the testing utils
return render(
<Dashboard {...props} {...override}>
<ChildrenComponent />
</Dashboard>,
);
}

describe('UNSAFE_componentWillReceiveProps', () => {
describe('UNSAFE_componentWillReceiveProps (simulated via re-render)', () => {
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 addSliceToDashboardMock = jest.spyOn(
props.actions,
'addSliceToDashboard',
);
const { rerender } = renderDashboard();
rerender(
<Dashboard {...props} layout={layoutWithExtraChart}>
<ChildrenComponent />
</Dashboard>,
);
expect(addSliceToDashboardMock).toHaveBeenCalledTimes(1);
addSliceToDashboardMock.mockRestore();
});

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 removeSliceFromDashboardMock = jest.spyOn(
props.actions,
'removeSliceFromDashboard',
);
// First, render with an extra slice
const { rerender } = renderDashboard({ layout: layoutWithExtraChart });
// Then re-render with that slice removed
const nextLayout = { ...layoutWithExtraChart };
delete nextLayout[1001];

wrapper.instance().UNSAFE_componentWillReceiveProps({
...props,
layout: nextLayout,
});
spy.restore();
expect(spy.callCount).toBe(1);
rerender(
<Dashboard {...props} layout={nextLayout}>
<ChildrenComponent />
</Dashboard>,
);

expect(removeSliceFromDashboardMock).toHaveBeenCalledTimes(1);
removeSliceFromDashboardMock.mockRestore();
});
});

describe('componentDidUpdate', () => {
let wrapper;
let prevProps;
describe('componentDidUpdate (simulated via re-render)', () => {
// We'll spy on Dashboard.prototype to ensure refreshCharts gets called
let refreshSpy;

beforeEach(() => {
wrapper = setup({ activeFilters: OVERRIDE_FILTERS });
wrapper.instance().appliedFilters = OVERRIDE_FILTERS;
prevProps = wrapper.instance().props;
refreshSpy = sinon.spy(wrapper.instance(), 'refreshCharts');
refreshSpy = jest.spyOn(Dashboard.prototype, 'refreshCharts');
});

afterEach(() => {
refreshSpy.restore();
refreshSpy.mockRestore();
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);
it('should not call refresh when in editMode', () => {
const { rerender } = renderDashboard({ activeFilters: OVERRIDE_FILTERS });
rerender(
<Dashboard
{...props}
activeFilters={OVERRIDE_FILTERS}
dashboardState={{ ...dashboardState, editMode: true }}
>
<ChildrenComponent />
</Dashboard>,
);
expect(refreshSpy).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);
it('should not call refresh when there is no change in activeFilters', () => {
const { rerender } = renderDashboard({ activeFilters: OVERRIDE_FILTERS });
// Re-render with the exact same activeFilters
rerender(
<Dashboard {...props} activeFilters={OVERRIDE_FILTERS}>
<ChildrenComponent />
</Dashboard>,
);
expect(refreshSpy).not.toHaveBeenCalled();
});

it('should call refresh when native filters changed', () => {
it('should call refresh when native filters changed (example: new filter added)', () => {
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({
const filtersWithNative = {
...OVERRIDE_FILTERS,
[NATIVE_FILTER_ID]: {
scope: [230],
values: extraFormData,
filterType: 'filter_select',
targets: [
{
datasetId: 13,
column: {
name: 'ethnic_minority',
},
},
],
},
});
...getAllActiveFilters({
dataMask: dataMaskWith1Filter,
nativeFilters: singleNativeFiltersState.filters,
allSliceIds: [227, 229, 230],
}),
};
const { rerender } = renderDashboard({ activeFilters: OVERRIDE_FILTERS });
rerender(
<Dashboard {...props} activeFilters={filtersWithNative}>
<ChildrenComponent />
</Dashboard>,
);
expect(refreshSpy).toHaveBeenCalledTimes(1);
});

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(
<Dashboard {...props} activeFilters={newFilter}>
<ChildrenComponent />
</Dashboard>,
);
expect(refreshSpy).toHaveBeenCalledTimes(1);
});

it('should call refresh if a filter is removed', () => {
getRelatedCharts.mockReturnValue([]);
wrapper.setProps({
activeFilters: {},
});
expect(refreshSpy.callCount).toBe(1);
expect(wrapper.instance().appliedFilters).toEqual({});
const { rerender } = renderDashboard({ activeFilters: OVERRIDE_FILTERS });
rerender(
<Dashboard {...props} activeFilters={{}}>
<ChildrenComponent />
</Dashboard>,
);
expect(refreshSpy).toHaveBeenCalledTimes(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(
<Dashboard {...props} activeFilters={newFilters}>
<ChildrenComponent />
</Dashboard>,
);
expect(refreshSpy).toHaveBeenCalledTimes(1);
expect(refreshSpy).toHaveBeenCalledWith([1]);
});

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(
<Dashboard {...props} activeFilters={newFilters}>
<ChildrenComponent />
</Dashboard>,
);
expect(refreshSpy).toHaveBeenCalledTimes(1);
expect(refreshSpy).toHaveBeenCalledWith([1, 2]);
});

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(
<Dashboard {...props} activeFilters={newFilters}>
<ChildrenComponent />
</Dashboard>,
);
expect(refreshSpy).toHaveBeenCalledTimes(1);
expect(refreshSpy).toHaveBeenCalledWith([2]);
});

it('should call refresh with empty [] if a filter is changed but scope is not applicable', () => {
it('should call refresh with empty array if a filter is changed but scope is not applicable', () => {
getRelatedCharts.mockReturnValue([]);
const { rerender } = renderDashboard({ activeFilters: OVERRIDE_FILTERS });
const newFilters = {
...OVERRIDE_FILTERS,
'3_country_name': { values: ['CHINA'], scope: [] },
};
rerender(
<Dashboard {...props} activeFilters={newFilters}>
<ChildrenComponent />
</Dashboard>,
);
expect(refreshSpy).toHaveBeenCalledTimes(1);
expect(refreshSpy).toHaveBeenCalledWith([]);
});

wrapper.setProps({
activeFilters: newFilters,
});
expect(refreshSpy.callCount).toBe(1);
expect(refreshSpy.getCall(0).args[0]).toEqual([]);
it('should call refresh when a native filter is added (more comprehensive case)', () => {
getRelatedCharts.mockReturnValue([230]);
const initialFilters = {
...OVERRIDE_FILTERS,
};
const { rerender } = renderDashboard({ activeFilters: initialFilters });
rerender(
<Dashboard
{...props}
activeFilters={{
...OVERRIDE_FILTERS,
[NATIVE_FILTER_ID]: {
scope: [230],
values: extraFormData,
filterType: 'filter_select',
targets: [
{
datasetId: 13,
column: { name: 'ethnic_minority' },
},
],
},
}}
>
<ChildrenComponent />
</Dashboard>,
);
expect(refreshSpy).toHaveBeenCalledTimes(1);
});
});
});
Loading