chore(dashboard): migrate enzyme to RTL (#26260)
This commit is contained in:
parent
918057e6c7
commit
f8c75ca50b
|
|
@ -17,77 +17,94 @@
|
|||
* under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import sinon from 'sinon';
|
||||
import { fireEvent, render } from 'spec/helpers/testing-library';
|
||||
|
||||
import DashboardComponent from 'src/dashboard/containers/DashboardComponent';
|
||||
import DashboardGrid from 'src/dashboard/components/DashboardGrid';
|
||||
import DragDroppable from 'src/dashboard/components/dnd/DragDroppable';
|
||||
import newComponentFactory from 'src/dashboard/util/newComponentFactory';
|
||||
|
||||
import { DASHBOARD_GRID_TYPE } from 'src/dashboard/util/componentTypes';
|
||||
import { GRID_COLUMN_COUNT } from 'src/dashboard/util/constants';
|
||||
|
||||
describe('DashboardGrid', () => {
|
||||
const props = {
|
||||
depth: 1,
|
||||
editMode: false,
|
||||
gridComponent: {
|
||||
...newComponentFactory(DASHBOARD_GRID_TYPE),
|
||||
children: ['a'],
|
||||
},
|
||||
handleComponentDrop() {},
|
||||
resizeComponent() {},
|
||||
width: 500,
|
||||
isComponentVisible: true,
|
||||
setDirectPathToChild() {},
|
||||
};
|
||||
const args = { id: 'id', widthMultiple: 1, heightMultiple: 3 };
|
||||
|
||||
function setup(overrideProps) {
|
||||
const wrapper = shallow(<DashboardGrid {...props} {...overrideProps} />);
|
||||
return wrapper;
|
||||
}
|
||||
jest.mock(
|
||||
'src/dashboard/containers/DashboardComponent',
|
||||
() =>
|
||||
({ onResizeStart, onResizeStop }) =>
|
||||
(
|
||||
<button
|
||||
type="button"
|
||||
data-test="mock-dashboard-component"
|
||||
onClick={() => onResizeStart()}
|
||||
onBlur={() => onResizeStop(args)}
|
||||
>
|
||||
Mock
|
||||
</button>
|
||||
),
|
||||
);
|
||||
|
||||
it('should render a div with class "dashboard-grid"', () => {
|
||||
const wrapper = setup();
|
||||
expect(wrapper.find('.dashboard-grid')).toExist();
|
||||
const props = {
|
||||
depth: 1,
|
||||
editMode: false,
|
||||
gridComponent: {
|
||||
...newComponentFactory(DASHBOARD_GRID_TYPE),
|
||||
children: ['a'],
|
||||
},
|
||||
handleComponentDrop() {},
|
||||
resizeComponent() {},
|
||||
width: 500,
|
||||
isComponentVisible: true,
|
||||
setDirectPathToChild() {},
|
||||
};
|
||||
|
||||
function setup(overrideProps) {
|
||||
return render(<DashboardGrid {...props} {...overrideProps} />, {
|
||||
useRedux: true,
|
||||
useDnd: true,
|
||||
});
|
||||
}
|
||||
|
||||
it('should render one DashboardComponent for each gridComponent child', () => {
|
||||
const wrapper = setup({
|
||||
gridComponent: { ...props.gridComponent, children: ['a', 'b'] },
|
||||
});
|
||||
expect(wrapper.find(DashboardComponent)).toHaveLength(2);
|
||||
test('should render a div with class "dashboard-grid"', () => {
|
||||
const { container } = setup();
|
||||
expect(container.querySelector('.dashboard-grid')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should render one DashboardComponent for each gridComponent child', () => {
|
||||
const { getAllByTestId } = setup({
|
||||
gridComponent: { ...props.gridComponent, children: ['a', 'b'] },
|
||||
});
|
||||
expect(getAllByTestId('mock-dashboard-component')).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('should render two empty DragDroppables in editMode to increase the drop target zone', () => {
|
||||
const viewMode = setup({ editMode: false });
|
||||
const editMode = setup({ editMode: true });
|
||||
expect(viewMode.find(DragDroppable)).toHaveLength(0);
|
||||
expect(editMode.find(DragDroppable)).toHaveLength(2);
|
||||
});
|
||||
test('should render two empty DragDroppables in editMode to increase the drop target zone', () => {
|
||||
const { queryAllByTestId } = setup({ editMode: false });
|
||||
expect(queryAllByTestId('dragdroppable-object').length).toEqual(0);
|
||||
const { getAllByTestId } = setup({ editMode: true });
|
||||
expect(getAllByTestId('dragdroppable-object').length).toEqual(2);
|
||||
});
|
||||
|
||||
it('should render grid column guides when resizing', () => {
|
||||
const wrapper = setup({ editMode: true });
|
||||
expect(wrapper.find('.grid-column-guide')).not.toExist();
|
||||
test('should render grid column guides when resizing', () => {
|
||||
const { container, getAllByTestId } = setup({ editMode: true });
|
||||
expect(container.querySelector('.grid-column-guide')).not.toBeInTheDocument();
|
||||
|
||||
wrapper.setState({ isResizing: true });
|
||||
// map handleResizeStart to the onClick prop of the mock DashboardComponent
|
||||
fireEvent.click(getAllByTestId('mock-dashboard-component')[0]);
|
||||
|
||||
expect(wrapper.find('.grid-column-guide')).toHaveLength(GRID_COLUMN_COUNT);
|
||||
});
|
||||
expect(container.querySelectorAll('.grid-column-guide')).toHaveLength(
|
||||
GRID_COLUMN_COUNT,
|
||||
);
|
||||
});
|
||||
|
||||
it('should call resizeComponent when a child DashboardComponent calls resizeStop', () => {
|
||||
const resizeComponent = sinon.spy();
|
||||
const args = { id: 'id', widthMultiple: 1, heightMultiple: 3 };
|
||||
const wrapper = setup({ resizeComponent });
|
||||
const dashboardComponent = wrapper.find(DashboardComponent).first();
|
||||
dashboardComponent.prop('onResizeStop')(args);
|
||||
test('should call resizeComponent when a child DashboardComponent calls resizeStop', () => {
|
||||
const resizeComponent = jest.fn();
|
||||
const { getAllByTestId } = setup({ resizeComponent });
|
||||
const dashboardComponent = getAllByTestId('mock-dashboard-component')[0];
|
||||
fireEvent.blur(dashboardComponent);
|
||||
|
||||
expect(resizeComponent.callCount).toBe(1);
|
||||
expect(resizeComponent.getCall(0).args[0]).toEqual({
|
||||
id: 'id',
|
||||
width: 1,
|
||||
height: 3,
|
||||
});
|
||||
expect(resizeComponent).toHaveBeenCalledTimes(1);
|
||||
expect(resizeComponent).toHaveBeenCalledWith({
|
||||
id: 'id',
|
||||
width: 1,
|
||||
height: 3,
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -17,11 +17,8 @@
|
|||
* under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import { Provider } from 'react-redux';
|
||||
import { Store } from 'redux';
|
||||
import * as SupersetUI from '@superset-ui/core';
|
||||
import { styledMount as mount } from 'spec/helpers/theming';
|
||||
import { render } from 'spec/helpers/testing-library';
|
||||
import {
|
||||
CHART_RENDERING_SUCCEEDED,
|
||||
CHART_UPDATE_SUCCEEDED,
|
||||
|
|
@ -36,123 +33,105 @@ import { sliceId } from 'spec/fixtures/mockChartQueries';
|
|||
import { dashboardFilters } from 'spec/fixtures/mockDashboardFilters';
|
||||
import { dashboardWithFilter } from 'spec/fixtures/mockDashboardLayout';
|
||||
|
||||
jest.mock(
|
||||
'src/dashboard/components/FiltersBadge/DetailsPanel',
|
||||
() =>
|
||||
({ children }: { children: React.ReactNode }) =>
|
||||
<div data-test="mock-details-panel">{children}</div>,
|
||||
);
|
||||
|
||||
const defaultStore = getMockStoreWithFilters();
|
||||
function setup(store: Store = defaultStore) {
|
||||
return mount(
|
||||
<Provider store={store}>
|
||||
<FiltersBadge chartId={sliceId} />
|
||||
</Provider>,
|
||||
);
|
||||
return render(<FiltersBadge chartId={sliceId} />, { store });
|
||||
}
|
||||
|
||||
describe('FiltersBadge', () => {
|
||||
// there's this bizarre "active filters" thing
|
||||
// that doesn't actually use any kind of state management.
|
||||
// Have to set variables in there.
|
||||
buildActiveFilters({
|
||||
dashboardFilters,
|
||||
components: dashboardWithFilter,
|
||||
// there's this bizarre "active filters" thing
|
||||
// that doesn't actually use any kind of state management.
|
||||
// Have to set variables in there.
|
||||
buildActiveFilters({
|
||||
dashboardFilters,
|
||||
components: dashboardWithFilter,
|
||||
});
|
||||
|
||||
describe('for dashboard filters', () => {
|
||||
test('does not show number when there are no active filters', () => {
|
||||
const store = getMockStoreWithFilters();
|
||||
// start with basic dashboard state, dispatch an event to simulate query completion
|
||||
store.dispatch({
|
||||
type: CHART_UPDATE_SUCCEEDED,
|
||||
key: sliceId,
|
||||
queriesResponse: [
|
||||
{
|
||||
status: 'success',
|
||||
applied_filters: [],
|
||||
rejected_filters: [],
|
||||
},
|
||||
],
|
||||
dashboardFilters,
|
||||
});
|
||||
const { queryByTestId } = setup(store);
|
||||
expect(queryByTestId('applied-filter-count')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
// shallow rendering in enzyme doesn't propagate contexts correctly,
|
||||
// so we have to mock the hook.
|
||||
// See https://medium.com/7shifts-engineering-blog/testing-usecontext-react-hook-with-enzyme-shallow-da062140fc83
|
||||
jest
|
||||
.spyOn(SupersetUI, 'useTheme')
|
||||
.mockImplementation(() => SupersetUI.supersetTheme);
|
||||
});
|
||||
|
||||
describe('for dashboard filters', () => {
|
||||
it("doesn't show number when there are no active filters", () => {
|
||||
const store = getMockStoreWithFilters();
|
||||
// start with basic dashboard state, dispatch an event to simulate query completion
|
||||
store.dispatch({
|
||||
type: CHART_UPDATE_SUCCEEDED,
|
||||
key: sliceId,
|
||||
queriesResponse: [
|
||||
{
|
||||
status: 'success',
|
||||
applied_filters: [],
|
||||
rejected_filters: [],
|
||||
},
|
||||
],
|
||||
dashboardFilters,
|
||||
});
|
||||
const wrapper = shallow(
|
||||
<Provider store={store}>
|
||||
<FiltersBadge chartId={sliceId} />,
|
||||
</Provider>,
|
||||
);
|
||||
expect(wrapper.find('[data-test="applied-filter-count"]')).not.toExist();
|
||||
});
|
||||
|
||||
it('shows the indicator when filters have been applied', () => {
|
||||
const store = getMockStoreWithFilters();
|
||||
// start with basic dashboard state, dispatch an event to simulate query completion
|
||||
store.dispatch({
|
||||
type: CHART_UPDATE_SUCCEEDED,
|
||||
key: sliceId,
|
||||
queriesResponse: [
|
||||
{
|
||||
status: 'success',
|
||||
applied_filters: [{ column: 'region' }],
|
||||
rejected_filters: [],
|
||||
},
|
||||
],
|
||||
dashboardFilters,
|
||||
});
|
||||
store.dispatch({ type: CHART_RENDERING_SUCCEEDED, key: sliceId });
|
||||
const wrapper = setup(store);
|
||||
expect(wrapper.find('DetailsPanelPopover')).toExist();
|
||||
expect(
|
||||
wrapper.find('[data-test="applied-filter-count"] .current'),
|
||||
).toHaveText('1');
|
||||
expect(wrapper.find('WarningFilled')).not.toExist();
|
||||
});
|
||||
});
|
||||
|
||||
describe('for native filters', () => {
|
||||
it("doesn't show number when there are no active filters", () => {
|
||||
const store = getMockStoreWithNativeFilters();
|
||||
// start with basic dashboard state, dispatch an event to simulate query completion
|
||||
store.dispatch({
|
||||
type: CHART_UPDATE_SUCCEEDED,
|
||||
key: sliceId,
|
||||
queriesResponse: [
|
||||
{
|
||||
status: 'success',
|
||||
applied_filters: [],
|
||||
rejected_filters: [],
|
||||
},
|
||||
],
|
||||
});
|
||||
store.dispatch({ type: CHART_RENDERING_SUCCEEDED, key: sliceId });
|
||||
const wrapper = setup(store);
|
||||
expect(wrapper.find('[data-test="applied-filter-count"]')).not.toExist();
|
||||
});
|
||||
|
||||
it('shows the indicator when filters have been applied', () => {
|
||||
const store = getMockStoreWithNativeFilters();
|
||||
// start with basic dashboard state, dispatch an event to simulate query completion
|
||||
store.dispatch({
|
||||
type: CHART_UPDATE_SUCCEEDED,
|
||||
key: sliceId,
|
||||
queriesResponse: [
|
||||
{
|
||||
status: 'success',
|
||||
applied_filters: [{ column: 'region' }],
|
||||
rejected_filters: [],
|
||||
},
|
||||
],
|
||||
});
|
||||
store.dispatch({ type: CHART_RENDERING_SUCCEEDED, key: sliceId });
|
||||
const wrapper = setup(store);
|
||||
expect(wrapper.find('DetailsPanelPopover')).toExist();
|
||||
expect(
|
||||
wrapper.find('[data-test="applied-filter-count"] .current'),
|
||||
).toHaveText('1');
|
||||
expect(wrapper.find('WarningFilled')).not.toExist();
|
||||
test('shows the indicator when filters have been applied', () => {
|
||||
const store = getMockStoreWithFilters();
|
||||
// start with basic dashboard state, dispatch an event to simulate query completion
|
||||
store.dispatch({
|
||||
type: CHART_UPDATE_SUCCEEDED,
|
||||
key: sliceId,
|
||||
queriesResponse: [
|
||||
{
|
||||
status: 'success',
|
||||
applied_filters: [{ column: 'region' }],
|
||||
rejected_filters: [],
|
||||
},
|
||||
],
|
||||
dashboardFilters,
|
||||
});
|
||||
store.dispatch({ type: CHART_RENDERING_SUCCEEDED, key: sliceId });
|
||||
const { getByTestId } = setup(store);
|
||||
expect(getByTestId('applied-filter-count')).toHaveTextContent('1');
|
||||
expect(getByTestId('mock-details-panel')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('for native filters', () => {
|
||||
test('does not show number when there are no active filters', () => {
|
||||
const store = getMockStoreWithNativeFilters();
|
||||
// start with basic dashboard state, dispatch an event to simulate query completion
|
||||
store.dispatch({
|
||||
type: CHART_UPDATE_SUCCEEDED,
|
||||
key: sliceId,
|
||||
queriesResponse: [
|
||||
{
|
||||
status: 'success',
|
||||
applied_filters: [],
|
||||
rejected_filters: [],
|
||||
},
|
||||
],
|
||||
});
|
||||
store.dispatch({ type: CHART_RENDERING_SUCCEEDED, key: sliceId });
|
||||
const { queryByTestId } = setup(store);
|
||||
expect(queryByTestId('applied-filter-count')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('shows the indicator when filters have been applied', () => {
|
||||
const store = getMockStoreWithNativeFilters();
|
||||
// start with basic dashboard state, dispatch an event to simulate query completion
|
||||
store.dispatch({
|
||||
type: CHART_UPDATE_SUCCEEDED,
|
||||
key: sliceId,
|
||||
queriesResponse: [
|
||||
{
|
||||
status: 'success',
|
||||
applied_filters: [{ column: 'region' }],
|
||||
rejected_filters: [],
|
||||
},
|
||||
],
|
||||
});
|
||||
store.dispatch({ type: CHART_RENDERING_SUCCEEDED, key: sliceId });
|
||||
const { getByTestId } = setup(store);
|
||||
expect(getByTestId('applied-filter-count')).toHaveTextContent('1');
|
||||
expect(getByTestId('mock-details-panel')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -17,15 +17,10 @@
|
|||
* under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import { Provider } from 'react-redux';
|
||||
import { render } from 'spec/helpers/testing-library';
|
||||
import fetchMock from 'fetch-mock';
|
||||
|
||||
import {
|
||||
supersetTheme,
|
||||
SupersetClient,
|
||||
ThemeProvider,
|
||||
} from '@superset-ui/core';
|
||||
import { SupersetClient } from '@superset-ui/core';
|
||||
|
||||
import Modal from 'src/components/Modal';
|
||||
import PropertiesModal from 'src/dashboard/components/PropertiesModal';
|
||||
|
|
@ -73,15 +68,10 @@ describe.skip('PropertiesModal', () => {
|
|||
};
|
||||
|
||||
function setup(overrideProps) {
|
||||
return mount(
|
||||
<Provider store={mockStore}>
|
||||
<PropertiesModal {...requiredProps} {...overrideProps} />
|
||||
</Provider>,
|
||||
{
|
||||
wrappingComponent: ThemeProvider,
|
||||
wrappingComponentProps: { theme: supersetTheme },
|
||||
},
|
||||
);
|
||||
return render(<PropertiesModal {...requiredProps} {...overrideProps} />, {
|
||||
useRedux: true,
|
||||
store: mockStore,
|
||||
});
|
||||
}
|
||||
|
||||
describe('onColorSchemeChange', () => {
|
||||
|
|
|
|||
|
|
@ -17,54 +17,12 @@
|
|||
* under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import { render, screen } from 'spec/helpers/testing-library';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import fetchMock from 'fetch-mock';
|
||||
|
||||
import ModalTrigger from 'src/components/ModalTrigger';
|
||||
import RefreshIntervalModal from 'src/dashboard/components/RefreshIntervalModal';
|
||||
import HeaderActionsDropdown from 'src/dashboard/components/Header/HeaderActionsDropdown';
|
||||
import Alert from 'src/components/Alert';
|
||||
import { supersetTheme, ThemeProvider } from '@superset-ui/core';
|
||||
|
||||
describe('RefreshIntervalModal - Enzyme', () => {
|
||||
const getMountWrapper = (props: any) =>
|
||||
mount(<RefreshIntervalModal {...props} />, {
|
||||
wrappingComponent: ThemeProvider,
|
||||
wrappingComponentProps: {
|
||||
theme: supersetTheme,
|
||||
},
|
||||
});
|
||||
const mockedProps = {
|
||||
triggerNode: <i className="fa fa-edit" />,
|
||||
refreshFrequency: 10,
|
||||
onChange: jest.fn(),
|
||||
editMode: true,
|
||||
refreshIntervalOptions: [],
|
||||
};
|
||||
it('should show warning message', () => {
|
||||
const props = {
|
||||
...mockedProps,
|
||||
refreshLimit: 3600,
|
||||
refreshWarning: 'Show warning',
|
||||
};
|
||||
|
||||
const wrapper = getMountWrapper(props);
|
||||
wrapper.find('span[role="button"]').simulate('click');
|
||||
|
||||
// @ts-ignore (for handleFrequencyChange)
|
||||
wrapper.instance().handleFrequencyChange(30);
|
||||
wrapper.update();
|
||||
expect(wrapper.find(ModalTrigger).find(Alert)).toExist();
|
||||
|
||||
// @ts-ignore (for handleFrequencyChange)
|
||||
wrapper.instance().handleFrequencyChange(3601);
|
||||
wrapper.update();
|
||||
expect(wrapper.find(ModalTrigger).find(Alert)).not.toExist();
|
||||
wrapper.unmount();
|
||||
});
|
||||
});
|
||||
|
||||
const createProps = () => ({
|
||||
addSuccessToast: jest.fn(),
|
||||
|
|
@ -150,103 +108,99 @@ const defaultRefreshIntervalModalProps = {
|
|||
refreshIntervalOptions: [],
|
||||
};
|
||||
|
||||
describe('RefreshIntervalModal - RTL', () => {
|
||||
it('is valid', () => {
|
||||
expect(
|
||||
React.isValidElement(
|
||||
<RefreshIntervalModal {...defaultRefreshIntervalModalProps} />,
|
||||
),
|
||||
).toBe(true);
|
||||
test('is valid', () => {
|
||||
expect(
|
||||
React.isValidElement(
|
||||
<RefreshIntervalModal {...defaultRefreshIntervalModalProps} />,
|
||||
),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
test('renders refresh interval modal', async () => {
|
||||
render(setup(editModeOnProps));
|
||||
await openRefreshIntervalModal();
|
||||
|
||||
// Assert that modal exists by checking for the modal title
|
||||
expect(screen.getByText('Refresh interval')).toBeVisible();
|
||||
});
|
||||
|
||||
test('renders refresh interval options', async () => {
|
||||
render(setup(editModeOnProps));
|
||||
await openRefreshIntervalModal();
|
||||
await displayOptions();
|
||||
|
||||
// Assert that both "Don't refresh" instances exist
|
||||
// - There will be two at this point, the default option and the dropdown option
|
||||
const dontRefreshInstances = screen.getAllByText(/don't refresh/i);
|
||||
expect(dontRefreshInstances).toHaveLength(2);
|
||||
dontRefreshInstances.forEach(option => {
|
||||
expect(option).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders refresh interval modal', async () => {
|
||||
render(setup(editModeOnProps));
|
||||
await openRefreshIntervalModal();
|
||||
|
||||
// Assert that modal exists by checking for the modal title
|
||||
expect(screen.getByText('Refresh interval')).toBeVisible();
|
||||
});
|
||||
|
||||
it('renders refresh interval options', async () => {
|
||||
render(setup(editModeOnProps));
|
||||
await openRefreshIntervalModal();
|
||||
await displayOptions();
|
||||
|
||||
// Assert that both "Don't refresh" instances exist
|
||||
// - There will be two at this point, the default option and the dropdown option
|
||||
const dontRefreshInstances = screen.getAllByText(/don't refresh/i);
|
||||
expect(dontRefreshInstances).toHaveLength(2);
|
||||
dontRefreshInstances.forEach(option => {
|
||||
expect(option).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// Assert that all the other options exist
|
||||
const options = [
|
||||
screen.getByText(/10 seconds/i),
|
||||
screen.getByText(/30 seconds/i),
|
||||
screen.getByText(/1 minute/i),
|
||||
screen.getByText(/5 minutes/i),
|
||||
screen.getByText(/30 minutes/i),
|
||||
screen.getByText(/1 hour/i),
|
||||
screen.getByText(/6 hours/i),
|
||||
screen.getByText(/12 hours/i),
|
||||
screen.getByText(/24 hours/i),
|
||||
];
|
||||
options.forEach(option => {
|
||||
expect(option).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('should change selected value', async () => {
|
||||
render(setup(editModeOnProps));
|
||||
await openRefreshIntervalModal();
|
||||
|
||||
// Initial selected value should be "Don't refresh"
|
||||
const selectedValue = screen.getByText(/don't refresh/i);
|
||||
expect(selectedValue.title).toMatch(/don't refresh/i);
|
||||
|
||||
// Display options and select "10 seconds"
|
||||
await displayOptions();
|
||||
userEvent.click(screen.getByText(/10 seconds/i));
|
||||
|
||||
// Selected value should now be "10 seconds"
|
||||
expect(selectedValue.title).toMatch(/10 seconds/i);
|
||||
expect(selectedValue.title).not.toMatch(/don't refresh/i);
|
||||
});
|
||||
|
||||
it('should save a newly-selected value', async () => {
|
||||
render(setup(editModeOnProps));
|
||||
await openRefreshIntervalModal();
|
||||
await displayOptions();
|
||||
|
||||
// Select a new interval and click save
|
||||
userEvent.click(screen.getByText(/10 seconds/i));
|
||||
userEvent.click(screen.getByRole('button', { name: /save/i }));
|
||||
|
||||
expect(editModeOnProps.setRefreshFrequency).toHaveBeenCalled();
|
||||
expect(editModeOnProps.setRefreshFrequency).toHaveBeenCalledWith(
|
||||
10,
|
||||
editModeOnProps.editMode,
|
||||
);
|
||||
expect(editModeOnProps.addSuccessToast).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should show warning message', async () => {
|
||||
// TODO (lyndsiWilliams): This test is incomplete
|
||||
const warningProps = {
|
||||
...editModeOnProps,
|
||||
refreshLimit: 3600,
|
||||
refreshWarning: 'Show warning',
|
||||
};
|
||||
|
||||
render(setup(warningProps));
|
||||
await openRefreshIntervalModal();
|
||||
await displayOptions();
|
||||
|
||||
userEvent.click(screen.getByText(/30 seconds/i));
|
||||
userEvent.click(screen.getByRole('button', { name: /save/i }));
|
||||
|
||||
// screen.debug(screen.getByRole('alert'));
|
||||
expect.anything();
|
||||
// Assert that all the other options exist
|
||||
const options = [
|
||||
screen.getByText(/10 seconds/i),
|
||||
screen.getByText(/30 seconds/i),
|
||||
screen.getByText(/1 minute/i),
|
||||
screen.getByText(/5 minutes/i),
|
||||
screen.getByText(/30 minutes/i),
|
||||
screen.getByText(/1 hour/i),
|
||||
screen.getByText(/6 hours/i),
|
||||
screen.getByText(/12 hours/i),
|
||||
screen.getByText(/24 hours/i),
|
||||
];
|
||||
options.forEach(option => {
|
||||
expect(option).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
test('should change selected value', async () => {
|
||||
render(setup(editModeOnProps));
|
||||
await openRefreshIntervalModal();
|
||||
|
||||
// Initial selected value should be "Don't refresh"
|
||||
const selectedValue = screen.getByText(/don't refresh/i);
|
||||
expect(selectedValue.title).toMatch(/don't refresh/i);
|
||||
|
||||
// Display options and select "10 seconds"
|
||||
await displayOptions();
|
||||
userEvent.click(screen.getByText(/10 seconds/i));
|
||||
|
||||
// Selected value should now be "10 seconds"
|
||||
expect(selectedValue.title).toMatch(/10 seconds/i);
|
||||
expect(selectedValue.title).not.toMatch(/don't refresh/i);
|
||||
});
|
||||
|
||||
test('should save a newly-selected value', async () => {
|
||||
render(setup(editModeOnProps));
|
||||
await openRefreshIntervalModal();
|
||||
await displayOptions();
|
||||
|
||||
// Select a new interval and click save
|
||||
userEvent.click(screen.getByText(/10 seconds/i));
|
||||
userEvent.click(screen.getByRole('button', { name: /save/i }));
|
||||
|
||||
expect(editModeOnProps.setRefreshFrequency).toHaveBeenCalled();
|
||||
expect(editModeOnProps.setRefreshFrequency).toHaveBeenCalledWith(
|
||||
10,
|
||||
editModeOnProps.editMode,
|
||||
);
|
||||
expect(editModeOnProps.addSuccessToast).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('should show warning message', async () => {
|
||||
const warningProps = {
|
||||
...editModeOnProps,
|
||||
refreshLimit: 3600,
|
||||
refreshWarning: 'Show warning',
|
||||
};
|
||||
|
||||
const { getByRole, queryByRole } = render(setup(warningProps));
|
||||
await openRefreshIntervalModal();
|
||||
await displayOptions();
|
||||
|
||||
userEvent.click(screen.getByText(/30 seconds/i));
|
||||
expect(getByRole('alert')).toBeInTheDocument();
|
||||
userEvent.click(screen.getByText(/6 hours/i));
|
||||
expect(queryByRole('alert')).not.toBeInTheDocument();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -17,12 +17,10 @@
|
|||
* under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import sinon from 'sinon';
|
||||
import { fireEvent, render } from 'spec/helpers/testing-library';
|
||||
import { FeatureFlag } from '@superset-ui/core';
|
||||
|
||||
import Chart from 'src/dashboard/components/gridComponents/Chart';
|
||||
import SliceHeader from 'src/dashboard/components/SliceHeader';
|
||||
import ChartContainer from 'src/components/Chart/ChartContainer';
|
||||
import * as exploreUtils from 'src/explore/exploreUtils';
|
||||
import { sliceEntitiesForChart as sliceEntities } from 'spec/fixtures/mockSliceEntities';
|
||||
import mockDatasource from 'spec/fixtures/mockDatasource';
|
||||
|
|
@ -30,146 +28,183 @@ import chartQueries, {
|
|||
sliceId as queryId,
|
||||
} from 'spec/fixtures/mockChartQueries';
|
||||
|
||||
describe('Chart', () => {
|
||||
const props = {
|
||||
id: queryId,
|
||||
width: 100,
|
||||
height: 100,
|
||||
updateSliceName() {},
|
||||
const props = {
|
||||
id: queryId,
|
||||
width: 100,
|
||||
height: 100,
|
||||
updateSliceName() {},
|
||||
|
||||
// from redux
|
||||
maxRows: 666,
|
||||
chart: chartQueries[queryId],
|
||||
formData: chartQueries[queryId].form_data,
|
||||
datasource: mockDatasource[sliceEntities.slices[queryId].datasource],
|
||||
slice: {
|
||||
...sliceEntities.slices[queryId],
|
||||
description_markeddown: 'markdown',
|
||||
owners: [],
|
||||
},
|
||||
sliceName: sliceEntities.slices[queryId].slice_name,
|
||||
timeout: 60,
|
||||
filters: {},
|
||||
refreshChart() {},
|
||||
toggleExpandSlice() {},
|
||||
addFilter() {},
|
||||
logEvent() {},
|
||||
handleToggleFullSize() {},
|
||||
changeFilter() {},
|
||||
setFocusedFilterField() {},
|
||||
unsetFocusedFilterField() {},
|
||||
addSuccessToast() {},
|
||||
addDangerToast() {},
|
||||
exportCSV() {},
|
||||
exportFullCSV() {},
|
||||
exportXLSX() {},
|
||||
exportFullXLSX() {},
|
||||
componentId: 'test',
|
||||
dashboardId: 111,
|
||||
editMode: false,
|
||||
isExpanded: false,
|
||||
supersetCanExplore: false,
|
||||
supersetCanCSV: false,
|
||||
};
|
||||
// from redux
|
||||
maxRows: 666,
|
||||
chart: chartQueries[queryId],
|
||||
formData: chartQueries[queryId].form_data,
|
||||
datasource: mockDatasource[sliceEntities.slices[queryId].datasource],
|
||||
slice: {
|
||||
...sliceEntities.slices[queryId],
|
||||
description_markeddown: 'markdown',
|
||||
owners: [],
|
||||
viz_type: 'table',
|
||||
},
|
||||
sliceName: sliceEntities.slices[queryId].slice_name,
|
||||
timeout: 60,
|
||||
filters: {},
|
||||
refreshChart() {},
|
||||
toggleExpandSlice() {},
|
||||
addFilter() {},
|
||||
logEvent() {},
|
||||
handleToggleFullSize() {},
|
||||
changeFilter() {},
|
||||
setFocusedFilterField() {},
|
||||
unsetFocusedFilterField() {},
|
||||
addSuccessToast() {},
|
||||
addDangerToast() {},
|
||||
exportCSV() {},
|
||||
exportFullCSV() {},
|
||||
exportXLSX() {},
|
||||
exportFullXLSX() {},
|
||||
componentId: 'test',
|
||||
dashboardId: 111,
|
||||
editMode: false,
|
||||
isExpanded: false,
|
||||
supersetCanExplore: false,
|
||||
supersetCanCSV: false,
|
||||
supersetCanShare: false,
|
||||
};
|
||||
|
||||
function setup(overrideProps) {
|
||||
const wrapper = shallow(
|
||||
<Chart.WrappedComponent {...props} {...overrideProps} />,
|
||||
);
|
||||
return wrapper;
|
||||
}
|
||||
function setup(overrideProps) {
|
||||
return render(<Chart.WrappedComponent {...props} {...overrideProps} />, {
|
||||
useRedux: true,
|
||||
useRouter: true,
|
||||
});
|
||||
}
|
||||
|
||||
it('should render a SliceHeader', () => {
|
||||
const wrapper = setup();
|
||||
expect(wrapper.find(SliceHeader)).toExist();
|
||||
});
|
||||
|
||||
it('should render a ChartContainer', () => {
|
||||
const wrapper = setup();
|
||||
expect(wrapper.find(ChartContainer)).toExist();
|
||||
});
|
||||
|
||||
it('should render a description if it has one and isExpanded=true', () => {
|
||||
const wrapper = setup();
|
||||
expect(wrapper.find('.slice_description')).not.toExist();
|
||||
wrapper.setProps({ ...props, isExpanded: true });
|
||||
expect(wrapper.find('.slice_description')).toExist();
|
||||
});
|
||||
|
||||
it('should calculate the description height if it has one and isExpanded=true', () => {
|
||||
const spy = jest.spyOn(
|
||||
Chart.WrappedComponent.prototype,
|
||||
'getDescriptionHeight',
|
||||
);
|
||||
const wrapper = setup({ isExpanded: true });
|
||||
|
||||
expect(wrapper.find('.slice_description')).toExist();
|
||||
expect(spy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call refreshChart when SliceHeader calls forceRefresh', () => {
|
||||
const refreshChart = sinon.spy();
|
||||
const wrapper = setup({ refreshChart });
|
||||
wrapper.instance().forceRefresh();
|
||||
expect(refreshChart.callCount).toBe(1);
|
||||
});
|
||||
|
||||
it('should call changeFilter when ChartContainer calls changeFilter', () => {
|
||||
const changeFilter = sinon.spy();
|
||||
const wrapper = setup({ changeFilter });
|
||||
wrapper.instance().changeFilter();
|
||||
expect(changeFilter.callCount).toBe(1);
|
||||
});
|
||||
it('should call exportChart when exportCSV is clicked', () => {
|
||||
const stubbedExportCSV = sinon
|
||||
.stub(exploreUtils, 'exportChart')
|
||||
.returns(() => {});
|
||||
const wrapper = setup();
|
||||
wrapper.instance().exportCSV(props.slice.sliceId);
|
||||
expect(stubbedExportCSV.calledOnce).toBe(true);
|
||||
expect(stubbedExportCSV.lastCall.args[0]).toEqual(
|
||||
expect.objectContaining({
|
||||
formData: expect.anything(),
|
||||
resultType: 'full',
|
||||
resultFormat: 'csv',
|
||||
}),
|
||||
);
|
||||
exploreUtils.exportChart.restore();
|
||||
});
|
||||
it('should call exportChart with row_limit props.maxRows when exportFullCSV is clicked', () => {
|
||||
const stubbedExportCSV = sinon
|
||||
.stub(exploreUtils, 'exportChart')
|
||||
.returns(() => {});
|
||||
const wrapper = setup();
|
||||
wrapper.instance().exportFullCSV(props.slice.sliceId);
|
||||
expect(stubbedExportCSV.calledOnce).toBe(true);
|
||||
expect(stubbedExportCSV.lastCall.args[0].formData.row_limit).toEqual(666);
|
||||
exploreUtils.exportChart.restore();
|
||||
});
|
||||
it('should call exportChart when exportXLSX is clicked', () => {
|
||||
const stubbedExportXLSX = sinon
|
||||
.stub(exploreUtils, 'exportChart')
|
||||
.returns(() => {});
|
||||
const wrapper = setup();
|
||||
wrapper.instance().exportXLSX(props.slice.sliceId);
|
||||
expect(stubbedExportXLSX.calledOnce).toBe(true);
|
||||
expect(stubbedExportXLSX.lastCall.args[0]).toEqual(
|
||||
expect.objectContaining({
|
||||
formData: expect.anything(),
|
||||
resultType: 'full',
|
||||
resultFormat: 'xlsx',
|
||||
}),
|
||||
);
|
||||
exploreUtils.exportChart.restore();
|
||||
});
|
||||
it('should call exportChart with row_limit props.maxRows when exportFullXLSX is clicked', () => {
|
||||
const stubbedExportXLSX = sinon
|
||||
.stub(exploreUtils, 'exportChart')
|
||||
.returns(() => {});
|
||||
const wrapper = setup();
|
||||
wrapper.instance().exportFullXLSX(props.slice.sliceId);
|
||||
expect(stubbedExportXLSX.calledOnce).toBe(true);
|
||||
expect(stubbedExportXLSX.lastCall.args[0].formData.row_limit).toEqual(666);
|
||||
exploreUtils.exportChart.restore();
|
||||
});
|
||||
test('should render a SliceHeader', () => {
|
||||
const { getByTestId, container } = setup();
|
||||
expect(getByTestId('slice-header')).toBeInTheDocument();
|
||||
expect(container.querySelector('.slice_description')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should render a ChartContainer', () => {
|
||||
const { getByTestId } = setup();
|
||||
expect(getByTestId('chart-container')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should render a description if it has one and isExpanded=true', () => {
|
||||
const { container } = setup({ isExpanded: true });
|
||||
expect(container.querySelector('.slice_description')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should calculate the description height if it has one and isExpanded=true', () => {
|
||||
const spy = jest.spyOn(
|
||||
Chart.WrappedComponent.prototype,
|
||||
'getDescriptionHeight',
|
||||
);
|
||||
const { container } = setup({ isExpanded: true });
|
||||
expect(container.querySelector('.slice_description')).toBeInTheDocument();
|
||||
expect(spy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('should call refreshChart when SliceHeader calls forceRefresh', () => {
|
||||
const refreshChart = jest.fn();
|
||||
const { getByText, getByRole } = setup({ refreshChart });
|
||||
fireEvent.click(getByRole('button', { name: 'More Options' }));
|
||||
fireEvent.click(getByText('Force refresh'));
|
||||
expect(refreshChart).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test.skip('should call changeFilter when ChartContainer calls changeFilter', () => {
|
||||
const changeFilter = jest.fn();
|
||||
const wrapper = setup({ changeFilter });
|
||||
wrapper.instance().changeFilter();
|
||||
expect(changeFilter.callCount).toBe(1);
|
||||
});
|
||||
|
||||
test('should call exportChart when exportCSV is clicked', async () => {
|
||||
const stubbedExportCSV = jest
|
||||
.spyOn(exploreUtils, 'exportChart')
|
||||
.mockImplementation(() => {});
|
||||
const { findByText, getByRole } = setup({ supersetCanCSV: true });
|
||||
fireEvent.click(getByRole('button', { name: 'More Options' }));
|
||||
fireEvent.mouseOver(getByRole('button', { name: 'Download' }));
|
||||
const exportAction = await findByText('Export to .CSV');
|
||||
fireEvent.click(exportAction);
|
||||
expect(stubbedExportCSV).toHaveBeenCalledTimes(1);
|
||||
expect(stubbedExportCSV).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
formData: expect.anything(),
|
||||
resultType: 'full',
|
||||
resultFormat: 'csv',
|
||||
}),
|
||||
);
|
||||
stubbedExportCSV.mockRestore();
|
||||
});
|
||||
|
||||
test('should call exportChart with row_limit props.maxRows when exportFullCSV is clicked', async () => {
|
||||
global.featureFlags = {
|
||||
[FeatureFlag.AllowFullCsvExport]: true,
|
||||
};
|
||||
const stubbedExportCSV = jest
|
||||
.spyOn(exploreUtils, 'exportChart')
|
||||
.mockImplementation(() => {});
|
||||
const { findByText, getByRole } = setup({ supersetCanCSV: true });
|
||||
fireEvent.click(getByRole('button', { name: 'More Options' }));
|
||||
fireEvent.mouseOver(getByRole('button', { name: 'Download' }));
|
||||
const exportAction = await findByText('Export to full .CSV');
|
||||
fireEvent.click(exportAction);
|
||||
expect(stubbedExportCSV).toHaveBeenCalledTimes(1);
|
||||
expect(stubbedExportCSV).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
formData: expect.objectContaining({
|
||||
row_limit: 666,
|
||||
}),
|
||||
resultType: 'full',
|
||||
resultFormat: 'csv',
|
||||
}),
|
||||
);
|
||||
stubbedExportCSV.mockRestore();
|
||||
});
|
||||
|
||||
test('should call exportChart when exportXLSX is clicked', async () => {
|
||||
const stubbedExportXLSX = jest
|
||||
.spyOn(exploreUtils, 'exportChart')
|
||||
.mockImplementation(() => {});
|
||||
const { findByText, getByRole } = setup({ supersetCanCSV: true });
|
||||
fireEvent.click(getByRole('button', { name: 'More Options' }));
|
||||
fireEvent.mouseOver(getByRole('button', { name: 'Download' }));
|
||||
const exportAction = await findByText('Export to Excel');
|
||||
fireEvent.click(exportAction);
|
||||
expect(stubbedExportXLSX).toHaveBeenCalledTimes(1);
|
||||
expect(stubbedExportXLSX).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
resultType: 'full',
|
||||
resultFormat: 'xlsx',
|
||||
}),
|
||||
);
|
||||
stubbedExportXLSX.mockRestore();
|
||||
});
|
||||
|
||||
test('should call exportChart with row_limit props.maxRows when exportFullXLSX is clicked', async () => {
|
||||
global.featureFlags = {
|
||||
[FeatureFlag.AllowFullCsvExport]: true,
|
||||
};
|
||||
const stubbedExportXLSX = jest
|
||||
.spyOn(exploreUtils, 'exportChart')
|
||||
.mockImplementation(() => {});
|
||||
const { findByText, getByRole } = setup({ supersetCanCSV: true });
|
||||
fireEvent.click(getByRole('button', { name: 'More Options' }));
|
||||
fireEvent.mouseOver(getByRole('button', { name: 'Download' }));
|
||||
const exportAction = await findByText('Export to full Excel');
|
||||
fireEvent.click(exportAction);
|
||||
expect(stubbedExportXLSX).toHaveBeenCalledTimes(1);
|
||||
expect(stubbedExportXLSX).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
formData: expect.objectContaining({
|
||||
row_limit: 666,
|
||||
}),
|
||||
resultType: 'full',
|
||||
resultFormat: 'xlsx',
|
||||
}),
|
||||
);
|
||||
|
||||
stubbedExportXLSX.mockRestore();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -16,158 +16,179 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { Provider } from 'react-redux';
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import sinon from 'sinon';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { supersetTheme, ThemeProvider } from '@superset-ui/core';
|
||||
import { DndProvider } from 'react-dnd';
|
||||
import { HTML5Backend } from 'react-dnd-html5-backend';
|
||||
import { fireEvent, render } from 'spec/helpers/testing-library';
|
||||
|
||||
import BackgroundStyleDropdown from 'src/dashboard/components/menu/BackgroundStyleDropdown';
|
||||
import Column from 'src/dashboard/components/gridComponents/Column';
|
||||
import DashboardComponent from 'src/dashboard/containers/DashboardComponent';
|
||||
import DeleteComponentButton from 'src/dashboard/components/DeleteComponentButton';
|
||||
import DragDroppable from 'src/dashboard/components/dnd/DragDroppable';
|
||||
import HoverMenu from 'src/dashboard/components/menu/HoverMenu';
|
||||
import IconButton from 'src/dashboard/components/IconButton';
|
||||
import ResizableContainer from 'src/dashboard/components/resizable/ResizableContainer';
|
||||
import WithPopoverMenu from 'src/dashboard/components/menu/WithPopoverMenu';
|
||||
|
||||
import { getMockStore } from 'spec/fixtures/mockStore';
|
||||
import { dashboardLayout as mockLayout } from 'spec/fixtures/mockDashboardLayout';
|
||||
import { initialState } from 'src/SqlLab/fixtures';
|
||||
|
||||
describe('Column', () => {
|
||||
const columnWithoutChildren = {
|
||||
...mockLayout.present.COLUMN_ID,
|
||||
children: [],
|
||||
};
|
||||
const props = {
|
||||
id: 'COLUMN_ID',
|
||||
parentId: 'ROW_ID',
|
||||
component: mockLayout.present.COLUMN_ID,
|
||||
parentComponent: mockLayout.present.ROW_ID,
|
||||
index: 0,
|
||||
depth: 2,
|
||||
editMode: false,
|
||||
availableColumnCount: 12,
|
||||
minColumnWidth: 2,
|
||||
columnWidth: 50,
|
||||
occupiedColumnCount: 6,
|
||||
onResizeStart() {},
|
||||
onResize() {},
|
||||
onResizeStop() {},
|
||||
handleComponentDrop() {},
|
||||
deleteComponent() {},
|
||||
updateComponents() {},
|
||||
};
|
||||
jest.mock(
|
||||
'src/dashboard/containers/DashboardComponent',
|
||||
() =>
|
||||
({ availableColumnCount, depth }) =>
|
||||
(
|
||||
<div data-test="mock-dashboard-component" depth={depth}>
|
||||
{availableColumnCount}
|
||||
</div>
|
||||
),
|
||||
);
|
||||
jest.mock(
|
||||
'src/dashboard/components/menu/WithPopoverMenu',
|
||||
() =>
|
||||
({ children }) =>
|
||||
<div data-test="mock-with-popover-menu">{children}</div>,
|
||||
);
|
||||
jest.mock(
|
||||
'src/dashboard/components/DeleteComponentButton',
|
||||
() =>
|
||||
({ onDelete }) =>
|
||||
(
|
||||
<button
|
||||
type="button"
|
||||
data-test="mock-delete-component-button"
|
||||
onClick={onDelete}
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
),
|
||||
);
|
||||
|
||||
function setup(overrideProps) {
|
||||
// We have to wrap provide DragDropContext for the underlying DragDroppable
|
||||
// otherwise we cannot assert on DragDroppable children
|
||||
const mockStore = getMockStore({
|
||||
...initialState,
|
||||
});
|
||||
const wrapper = mount(
|
||||
<Provider store={mockStore}>
|
||||
<MemoryRouter>
|
||||
<DndProvider backend={HTML5Backend}>
|
||||
<Column {...props} {...overrideProps} />
|
||||
</DndProvider>
|
||||
</MemoryRouter>
|
||||
</Provider>,
|
||||
{
|
||||
wrappingComponent: ThemeProvider,
|
||||
wrappingComponentProps: { theme: supersetTheme },
|
||||
},
|
||||
);
|
||||
return wrapper;
|
||||
}
|
||||
const columnWithoutChildren = {
|
||||
...mockLayout.present.COLUMN_ID,
|
||||
children: [],
|
||||
};
|
||||
const props = {
|
||||
id: 'COLUMN_ID',
|
||||
parentId: 'ROW_ID',
|
||||
component: mockLayout.present.COLUMN_ID,
|
||||
parentComponent: mockLayout.present.ROW_ID,
|
||||
index: 0,
|
||||
depth: 2,
|
||||
editMode: false,
|
||||
availableColumnCount: 12,
|
||||
minColumnWidth: 2,
|
||||
columnWidth: 50,
|
||||
occupiedColumnCount: 6,
|
||||
onResizeStart() {},
|
||||
onResize() {},
|
||||
onResizeStop() {},
|
||||
handleComponentDrop() {},
|
||||
deleteComponent() {},
|
||||
updateComponents() {},
|
||||
};
|
||||
|
||||
it('should render a DragDroppable', () => {
|
||||
// don't count child DragDroppables
|
||||
const wrapper = setup({ component: columnWithoutChildren });
|
||||
expect(wrapper.find(DragDroppable)).toExist();
|
||||
function setup(overrideProps) {
|
||||
// We have to wrap provide DragDropContext for the underlying DragDroppable
|
||||
// otherwise we cannot assert on DragDroppable children
|
||||
const mockStore = getMockStore({
|
||||
...initialState,
|
||||
});
|
||||
|
||||
it('should render a WithPopoverMenu', () => {
|
||||
// don't count child DragDroppables
|
||||
const wrapper = setup({ component: columnWithoutChildren });
|
||||
expect(wrapper.find(WithPopoverMenu)).toExist();
|
||||
return render(<Column {...props} {...overrideProps} />, {
|
||||
store: mockStore,
|
||||
useDnd: true,
|
||||
useRouter: true,
|
||||
});
|
||||
}
|
||||
|
||||
it('should render a ResizableContainer', () => {
|
||||
// don't count child DragDroppables
|
||||
const wrapper = setup({ component: columnWithoutChildren });
|
||||
expect(wrapper.find(ResizableContainer)).toExist();
|
||||
});
|
||||
|
||||
it('should render a HoverMenu in editMode', () => {
|
||||
let wrapper = setup({ component: columnWithoutChildren });
|
||||
expect(wrapper.find(HoverMenu)).not.toExist();
|
||||
|
||||
// we cannot set props on the Row because of the WithDragDropContext wrapper
|
||||
wrapper = setup({ component: columnWithoutChildren, editMode: true });
|
||||
expect(wrapper.find(HoverMenu)).toExist();
|
||||
});
|
||||
|
||||
it('should render a DeleteComponentButton in editMode', () => {
|
||||
let wrapper = setup({ component: columnWithoutChildren });
|
||||
expect(wrapper.find(DeleteComponentButton)).not.toExist();
|
||||
|
||||
// we cannot set props on the Row because of the WithDragDropContext wrapper
|
||||
wrapper = setup({ component: columnWithoutChildren, editMode: true });
|
||||
expect(wrapper.find(DeleteComponentButton)).toExist();
|
||||
});
|
||||
|
||||
it('should render a BackgroundStyleDropdown when focused', () => {
|
||||
let wrapper = setup({ component: columnWithoutChildren });
|
||||
expect(wrapper.find(BackgroundStyleDropdown)).not.toExist();
|
||||
|
||||
// we cannot set props on the Row because of the WithDragDropContext wrapper
|
||||
wrapper = setup({ component: columnWithoutChildren, editMode: true });
|
||||
wrapper
|
||||
.find(IconButton)
|
||||
.at(1) // first one is delete button
|
||||
.simulate('click');
|
||||
|
||||
expect(wrapper.find(BackgroundStyleDropdown)).toExist();
|
||||
});
|
||||
|
||||
it('should call deleteComponent when deleted', () => {
|
||||
const deleteComponent = sinon.spy();
|
||||
const wrapper = setup({ editMode: true, deleteComponent });
|
||||
wrapper.find(DeleteComponentButton).simulate('click');
|
||||
expect(deleteComponent.callCount).toBe(1);
|
||||
});
|
||||
|
||||
it('should pass its own width as availableColumnCount to children', () => {
|
||||
const wrapper = setup();
|
||||
const dashboardComponent = wrapper.find(DashboardComponent).first();
|
||||
expect(dashboardComponent.props().availableColumnCount).toBe(
|
||||
props.component.meta.width,
|
||||
);
|
||||
});
|
||||
|
||||
it('should pass appropriate dimensions to ResizableContainer', () => {
|
||||
const wrapper = setup({ component: columnWithoutChildren });
|
||||
const columnWidth = columnWithoutChildren.meta.width;
|
||||
const resizableProps = wrapper.find(ResizableContainer).props();
|
||||
expect(resizableProps.adjustableWidth).toBe(true);
|
||||
expect(resizableProps.adjustableHeight).toBe(false);
|
||||
expect(resizableProps.widthStep).toBe(props.columnWidth);
|
||||
expect(resizableProps.widthMultiple).toBe(columnWidth);
|
||||
expect(resizableProps.minWidthMultiple).toBe(props.minColumnWidth);
|
||||
expect(resizableProps.maxWidthMultiple).toBe(
|
||||
props.availableColumnCount + columnWidth,
|
||||
);
|
||||
});
|
||||
|
||||
it('should increment the depth of its children', () => {
|
||||
const wrapper = setup();
|
||||
const dashboardComponent = wrapper.find(DashboardComponent);
|
||||
expect(dashboardComponent.props().depth).toBe(props.depth + 1);
|
||||
});
|
||||
test('should render a DragDroppable', () => {
|
||||
// don't count child DragDroppables
|
||||
const { getByTestId } = setup({ component: columnWithoutChildren });
|
||||
expect(getByTestId('dragdroppable-object')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should skip rendering HoverMenu and DeleteComponentButton when not in editMode', () => {
|
||||
const { container, queryByTestId } = setup({
|
||||
component: columnWithoutChildren,
|
||||
});
|
||||
expect(container.querySelector('.hover-menu')).not.toBeInTheDocument();
|
||||
expect(queryByTestId('mock-delete-component-button')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should render a WithPopoverMenu', () => {
|
||||
// don't count child DragDroppables
|
||||
const { getByTestId } = setup({ component: columnWithoutChildren });
|
||||
expect(getByTestId('mock-with-popover-menu')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should render a ResizableContainer', () => {
|
||||
// don't count child DragDroppables
|
||||
const { container } = setup({ component: columnWithoutChildren });
|
||||
expect(container.querySelector('.resizable-container')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should render a HoverMenu in editMode', () => {
|
||||
// we cannot set props on the Row because of the WithDragDropContext wrapper
|
||||
const { container } = setup({
|
||||
component: columnWithoutChildren,
|
||||
editMode: true,
|
||||
});
|
||||
expect(container.querySelector('.hover-menu')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should render a DeleteComponentButton in editMode', () => {
|
||||
// we cannot set props on the Row because of the WithDragDropContext wrapper
|
||||
const { getByTestId } = setup({
|
||||
component: columnWithoutChildren,
|
||||
editMode: true,
|
||||
});
|
||||
expect(getByTestId('mock-delete-component-button')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test.skip('should render a BackgroundStyleDropdown when focused', () => {
|
||||
let wrapper = setup({ component: columnWithoutChildren });
|
||||
expect(wrapper.find(BackgroundStyleDropdown)).not.toExist();
|
||||
|
||||
// we cannot set props on the Row because of the WithDragDropContext wrapper
|
||||
wrapper = setup({ component: columnWithoutChildren, editMode: true });
|
||||
wrapper
|
||||
.find(IconButton)
|
||||
.at(1) // first one is delete button
|
||||
.simulate('click');
|
||||
|
||||
expect(wrapper.find(BackgroundStyleDropdown)).toExist();
|
||||
});
|
||||
|
||||
test('should call deleteComponent when deleted', () => {
|
||||
const deleteComponent = jest.fn();
|
||||
const { getByTestId } = setup({ editMode: true, deleteComponent });
|
||||
fireEvent.click(getByTestId('mock-delete-component-button'));
|
||||
expect(deleteComponent).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('should pass its own width as availableColumnCount to children', () => {
|
||||
const { getByTestId } = setup();
|
||||
expect(getByTestId('mock-dashboard-component')).toHaveTextContent(
|
||||
props.component.meta.width,
|
||||
);
|
||||
});
|
||||
|
||||
test.skip('should pass appropriate dimensions to ResizableContainer', () => {
|
||||
const { container } = setup({ component: columnWithoutChildren });
|
||||
const columnWidth = columnWithoutChildren.meta.width;
|
||||
|
||||
expect(container.querySelector('.resizable-container')).toEqual({
|
||||
columnWidth,
|
||||
});
|
||||
// const resizableProps = wrapper.find(ResizableContainer).props();
|
||||
// expect(resizableProps.adjustableWidth).toBe(true);
|
||||
// expect(resizableProps.adjustableHeight).toBe(false);
|
||||
// expect(resizableProps.widthStep).toBe(props.columnWidth);
|
||||
// expect(resizableProps.widthMultiple).toBe(columnWidth);
|
||||
// expect(resizableProps.minWidthMultiple).toBe(props.minColumnWidth);
|
||||
// expect(resizableProps.maxWidthMultiple).toBe(
|
||||
// props.availableColumnCount + columnWidth,
|
||||
// );
|
||||
});
|
||||
|
||||
test('should increment the depth of its children', () => {
|
||||
const { getByTestId } = setup();
|
||||
expect(getByTestId('mock-dashboard-component')).toHaveAttribute(
|
||||
'depth',
|
||||
`${props.depth + 1}`,
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -16,134 +16,153 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { Provider } from 'react-redux';
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import sinon from 'sinon';
|
||||
import { DndProvider } from 'react-dnd';
|
||||
import { HTML5Backend } from 'react-dnd-html5-backend';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { fireEvent, render } from 'spec/helpers/testing-library';
|
||||
|
||||
import BackgroundStyleDropdown from 'src/dashboard/components/menu/BackgroundStyleDropdown';
|
||||
import DashboardComponent from 'src/dashboard/containers/DashboardComponent';
|
||||
import DeleteComponentButton from 'src/dashboard/components/DeleteComponentButton';
|
||||
import DragDroppable from 'src/dashboard/components/dnd/DragDroppable';
|
||||
import HoverMenu from 'src/dashboard/components/menu/HoverMenu';
|
||||
import IconButton from 'src/dashboard/components/IconButton';
|
||||
import Row from 'src/dashboard/components/gridComponents/Row';
|
||||
import WithPopoverMenu from 'src/dashboard/components/menu/WithPopoverMenu';
|
||||
import { DASHBOARD_GRID_ID } from 'src/dashboard/util/constants';
|
||||
import { supersetTheme, ThemeProvider } from '@superset-ui/core';
|
||||
|
||||
import { getMockStore } from 'spec/fixtures/mockStore';
|
||||
import { dashboardLayout as mockLayout } from 'spec/fixtures/mockDashboardLayout';
|
||||
import { initialState } from 'src/SqlLab/fixtures';
|
||||
|
||||
describe('Row', () => {
|
||||
const rowWithoutChildren = { ...mockLayout.present.ROW_ID, children: [] };
|
||||
const props = {
|
||||
id: 'ROW_ID',
|
||||
parentId: DASHBOARD_GRID_ID,
|
||||
component: mockLayout.present.ROW_ID,
|
||||
parentComponent: mockLayout.present[DASHBOARD_GRID_ID],
|
||||
index: 0,
|
||||
depth: 2,
|
||||
editMode: false,
|
||||
availableColumnCount: 12,
|
||||
columnWidth: 50,
|
||||
occupiedColumnCount: 6,
|
||||
onResizeStart() {},
|
||||
onResize() {},
|
||||
onResizeStop() {},
|
||||
handleComponentDrop() {},
|
||||
deleteComponent() {},
|
||||
updateComponents() {},
|
||||
};
|
||||
jest.mock(
|
||||
'src/dashboard/containers/DashboardComponent',
|
||||
() =>
|
||||
({ availableColumnCount, depth }) =>
|
||||
(
|
||||
<div data-test="mock-dashboard-component" depth={depth}>
|
||||
{availableColumnCount}
|
||||
</div>
|
||||
),
|
||||
);
|
||||
|
||||
function setup(overrideProps) {
|
||||
// We have to wrap provide DragDropContext for the underlying DragDroppable
|
||||
// otherwise we cannot assert on DragDroppable children
|
||||
const mockStore = getMockStore({
|
||||
...initialState,
|
||||
});
|
||||
const wrapper = mount(
|
||||
<Provider store={mockStore}>
|
||||
<MemoryRouter>
|
||||
<DndProvider backend={HTML5Backend}>
|
||||
<Row {...props} {...overrideProps} />
|
||||
</DndProvider>
|
||||
</MemoryRouter>
|
||||
</Provider>,
|
||||
{
|
||||
wrappingComponent: ThemeProvider,
|
||||
wrappingComponentProps: { theme: supersetTheme },
|
||||
},
|
||||
);
|
||||
return wrapper;
|
||||
}
|
||||
jest.mock(
|
||||
'src/dashboard/components/menu/WithPopoverMenu',
|
||||
() =>
|
||||
({ children }) =>
|
||||
<div data-test="mock-with-popover-menu">{children}</div>,
|
||||
);
|
||||
|
||||
it('should render a DragDroppable', () => {
|
||||
// don't count child DragDroppables
|
||||
const wrapper = setup({ component: rowWithoutChildren });
|
||||
expect(wrapper.find(DragDroppable)).toExist();
|
||||
jest.mock(
|
||||
'src/dashboard/components/DeleteComponentButton',
|
||||
() =>
|
||||
({ onDelete }) =>
|
||||
(
|
||||
<button
|
||||
type="button"
|
||||
data-test="mock-delete-component-button"
|
||||
onClick={onDelete}
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
),
|
||||
);
|
||||
|
||||
const rowWithoutChildren = { ...mockLayout.present.ROW_ID, children: [] };
|
||||
const props = {
|
||||
id: 'ROW_ID',
|
||||
parentId: DASHBOARD_GRID_ID,
|
||||
component: mockLayout.present.ROW_ID,
|
||||
parentComponent: mockLayout.present[DASHBOARD_GRID_ID],
|
||||
index: 0,
|
||||
depth: 2,
|
||||
editMode: false,
|
||||
availableColumnCount: 12,
|
||||
columnWidth: 50,
|
||||
occupiedColumnCount: 6,
|
||||
onResizeStart() {},
|
||||
onResize() {},
|
||||
onResizeStop() {},
|
||||
handleComponentDrop() {},
|
||||
deleteComponent() {},
|
||||
updateComponents() {},
|
||||
};
|
||||
|
||||
function setup(overrideProps) {
|
||||
// We have to wrap provide DragDropContext for the underlying DragDroppable
|
||||
// otherwise we cannot assert on DragDroppable children
|
||||
const mockStore = getMockStore({
|
||||
...initialState,
|
||||
});
|
||||
|
||||
it('should render a WithPopoverMenu', () => {
|
||||
// don't count child DragDroppables
|
||||
const wrapper = setup({ component: rowWithoutChildren });
|
||||
expect(wrapper.find(WithPopoverMenu)).toExist();
|
||||
return render(<Row {...props} {...overrideProps} />, {
|
||||
store: mockStore,
|
||||
useDnd: true,
|
||||
useRouter: true,
|
||||
});
|
||||
}
|
||||
|
||||
it('should render a HoverMenu in editMode', () => {
|
||||
let wrapper = setup({ component: rowWithoutChildren });
|
||||
expect(wrapper.find(HoverMenu)).not.toExist();
|
||||
|
||||
// we cannot set props on the Row because of the WithDragDropContext wrapper
|
||||
wrapper = setup({ component: rowWithoutChildren, editMode: true });
|
||||
expect(wrapper.find(HoverMenu)).toExist();
|
||||
});
|
||||
|
||||
it('should render a DeleteComponentButton in editMode', () => {
|
||||
let wrapper = setup({ component: rowWithoutChildren });
|
||||
expect(wrapper.find(DeleteComponentButton)).not.toExist();
|
||||
|
||||
// we cannot set props on the Row because of the WithDragDropContext wrapper
|
||||
wrapper = setup({ component: rowWithoutChildren, editMode: true });
|
||||
expect(wrapper.find(DeleteComponentButton)).toExist();
|
||||
});
|
||||
|
||||
it('should render a BackgroundStyleDropdown when focused', () => {
|
||||
let wrapper = setup({ component: rowWithoutChildren });
|
||||
expect(wrapper.find(BackgroundStyleDropdown)).not.toExist();
|
||||
|
||||
// we cannot set props on the Row because of the WithDragDropContext wrapper
|
||||
wrapper = setup({ component: rowWithoutChildren, editMode: true });
|
||||
wrapper
|
||||
.find(IconButton)
|
||||
.at(1) // first one is delete button
|
||||
.simulate('click');
|
||||
|
||||
expect(wrapper.find(BackgroundStyleDropdown)).toExist();
|
||||
});
|
||||
|
||||
it('should call deleteComponent when deleted', () => {
|
||||
const deleteComponent = sinon.spy();
|
||||
const wrapper = setup({ editMode: true, deleteComponent });
|
||||
wrapper.find(DeleteComponentButton).simulate('click');
|
||||
expect(deleteComponent.callCount).toBe(1);
|
||||
});
|
||||
|
||||
it('should pass appropriate availableColumnCount to children', () => {
|
||||
const wrapper = setup();
|
||||
const dashboardComponent = wrapper.find(DashboardComponent).first();
|
||||
expect(dashboardComponent.props().availableColumnCount).toBe(
|
||||
props.availableColumnCount - props.occupiedColumnCount,
|
||||
);
|
||||
});
|
||||
|
||||
it('should increment the depth of its children', () => {
|
||||
const wrapper = setup();
|
||||
const dashboardComponent = wrapper.find(DashboardComponent).first();
|
||||
expect(dashboardComponent.props().depth).toBe(props.depth + 1);
|
||||
});
|
||||
test('should render a DragDroppable', () => {
|
||||
// don't count child DragDroppables
|
||||
const { getByTestId } = setup({ component: rowWithoutChildren });
|
||||
expect(getByTestId('dragdroppable-object')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should skip rendering HoverMenu and DeleteComponentButton when not in editMode', () => {
|
||||
const { container, queryByTestId } = setup({
|
||||
component: rowWithoutChildren,
|
||||
});
|
||||
expect(container.querySelector('.hover-menu')).not.toBeInTheDocument();
|
||||
expect(queryByTestId('mock-delete-component-button')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should render a WithPopoverMenu', () => {
|
||||
// don't count child DragDroppables
|
||||
const { getByTestId } = setup({ component: rowWithoutChildren });
|
||||
expect(getByTestId('mock-with-popover-menu')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should render a HoverMenu in editMode', () => {
|
||||
const { container } = setup({
|
||||
component: rowWithoutChildren,
|
||||
editMode: true,
|
||||
});
|
||||
expect(container.querySelector('.hover-menu')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should render a DeleteComponentButton in editMode', () => {
|
||||
const { getByTestId } = setup({
|
||||
component: rowWithoutChildren,
|
||||
editMode: true,
|
||||
});
|
||||
expect(getByTestId('mock-delete-component-button')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test.skip('should render a BackgroundStyleDropdown when focused', () => {
|
||||
let wrapper = setup({ component: rowWithoutChildren });
|
||||
expect(wrapper.find(BackgroundStyleDropdown)).not.toExist();
|
||||
|
||||
// we cannot set props on the Row because of the WithDragDropContext wrapper
|
||||
wrapper = setup({ component: rowWithoutChildren, editMode: true });
|
||||
wrapper
|
||||
.find(IconButton)
|
||||
.at(1) // first one is delete button
|
||||
.simulate('click');
|
||||
|
||||
expect(wrapper.find(BackgroundStyleDropdown)).toExist();
|
||||
});
|
||||
|
||||
test('should call deleteComponent when deleted', () => {
|
||||
const deleteComponent = jest.fn();
|
||||
const { getByTestId } = setup({ editMode: true, deleteComponent });
|
||||
fireEvent.click(getByTestId('mock-delete-component-button'));
|
||||
expect(deleteComponent).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('should pass appropriate availableColumnCount to children', () => {
|
||||
const { getByTestId } = setup();
|
||||
expect(getByTestId('mock-dashboard-component')).toHaveTextContent(
|
||||
props.availableColumnCount - props.occupiedColumnCount,
|
||||
);
|
||||
});
|
||||
|
||||
test('should increment the depth of its children', () => {
|
||||
const { getByTestId } = setup();
|
||||
expect(getByTestId('mock-dashboard-component')).toHaveAttribute(
|
||||
'depth',
|
||||
`${props.depth + 1}`,
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -16,20 +16,11 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { Provider } from 'react-redux';
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import sinon from 'sinon';
|
||||
import { DndProvider } from 'react-dnd';
|
||||
import { HTML5Backend } from 'react-dnd-html5-backend';
|
||||
import { fireEvent, render } from 'spec/helpers/testing-library';
|
||||
|
||||
import { LineEditableTabs } from 'src/components/Tabs';
|
||||
import { AntdModal } from 'src/components';
|
||||
import { styledMount as mount } from 'spec/helpers/theming';
|
||||
import DashboardComponent from 'src/dashboard/containers/DashboardComponent';
|
||||
import DeleteComponentButton from 'src/dashboard/components/DeleteComponentButton';
|
||||
import HoverMenu from 'src/dashboard/components/menu/HoverMenu';
|
||||
import DragDroppable from 'src/dashboard/components/dnd/DragDroppable';
|
||||
import fetchMock from 'fetch-mock';
|
||||
import { Tabs } from 'src/dashboard/components/gridComponents/Tabs';
|
||||
import { DASHBOARD_ROOT_ID } from 'src/dashboard/util/constants';
|
||||
import emptyDashboardLayout from 'src/dashboard/fixtures/emptyDashboardLayout';
|
||||
|
|
@ -38,177 +29,167 @@ import { getMockStore } from 'spec/fixtures/mockStore';
|
|||
import { nativeFilters } from 'spec/fixtures/mockNativeFilters';
|
||||
import { initialState } from 'src/SqlLab/fixtures';
|
||||
|
||||
describe('Tabs', () => {
|
||||
const props = {
|
||||
id: 'TABS_ID',
|
||||
parentId: DASHBOARD_ROOT_ID,
|
||||
component: dashboardLayoutWithTabs.present.TABS_ID,
|
||||
parentComponent: dashboardLayoutWithTabs.present[DASHBOARD_ROOT_ID],
|
||||
index: 0,
|
||||
depth: 1,
|
||||
renderTabContent: true,
|
||||
editMode: false,
|
||||
availableColumnCount: 12,
|
||||
columnWidth: 50,
|
||||
dashboardId: 1,
|
||||
onResizeStart() {},
|
||||
onResize() {},
|
||||
onResizeStop() {},
|
||||
createComponent() {},
|
||||
handleComponentDrop() {},
|
||||
onChangeTab() {},
|
||||
deleteComponent() {},
|
||||
updateComponents() {},
|
||||
logEvent() {},
|
||||
dashboardLayout: emptyDashboardLayout,
|
||||
nativeFilters: nativeFilters.filters,
|
||||
};
|
||||
jest.mock('src/dashboard/containers/DashboardComponent', () => ({ id }) => (
|
||||
<div data-test="mock-dashboard-component">{id}</div>
|
||||
));
|
||||
|
||||
const mockStore = getMockStore({
|
||||
...initialState,
|
||||
dashboardLayout: dashboardLayoutWithTabs,
|
||||
dashboardFilters: {},
|
||||
});
|
||||
jest.mock(
|
||||
'src/dashboard/components/DeleteComponentButton',
|
||||
() =>
|
||||
({ onDelete }) =>
|
||||
(
|
||||
<button
|
||||
type="button"
|
||||
data-test="mock-delete-component-button"
|
||||
onClick={onDelete}
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
),
|
||||
);
|
||||
|
||||
function setup(overrideProps) {
|
||||
// We have to wrap provide DragDropContext for the underlying DragDroppable
|
||||
// otherwise we cannot assert on DragDroppable children
|
||||
const wrapper = mount(
|
||||
<Provider store={mockStore}>
|
||||
<DndProvider backend={HTML5Backend}>
|
||||
<Tabs {...props} {...overrideProps} />
|
||||
</DndProvider>
|
||||
</Provider>,
|
||||
);
|
||||
return wrapper;
|
||||
}
|
||||
fetchMock.post('glob:*/r/shortener/', {});
|
||||
|
||||
it('should render a DragDroppable', () => {
|
||||
// test just Tabs with no children DragDroppables
|
||||
const wrapper = setup({ component: { ...props.component, children: [] } });
|
||||
expect(wrapper.find(DragDroppable)).toExist();
|
||||
});
|
||||
const props = {
|
||||
id: 'TABS_ID',
|
||||
parentId: DASHBOARD_ROOT_ID,
|
||||
component: dashboardLayoutWithTabs.present.TABS_ID,
|
||||
parentComponent: dashboardLayoutWithTabs.present[DASHBOARD_ROOT_ID],
|
||||
index: 0,
|
||||
depth: 1,
|
||||
renderTabContent: true,
|
||||
editMode: false,
|
||||
availableColumnCount: 12,
|
||||
columnWidth: 50,
|
||||
dashboardId: 1,
|
||||
onResizeStart() {},
|
||||
onResize() {},
|
||||
onResizeStop() {},
|
||||
createComponent() {},
|
||||
handleComponentDrop() {},
|
||||
onChangeTab() {},
|
||||
deleteComponent() {},
|
||||
updateComponents() {},
|
||||
logEvent() {},
|
||||
dashboardLayout: emptyDashboardLayout,
|
||||
nativeFilters: nativeFilters.filters,
|
||||
};
|
||||
|
||||
it('should render non-editable tabs', () => {
|
||||
const wrapper = setup();
|
||||
expect(wrapper.find(LineEditableTabs)).toExist();
|
||||
expect(wrapper.find('.ant-tabs-nav-add').exists()).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should render a tab pane for each child', () => {
|
||||
const wrapper = setup();
|
||||
expect(wrapper.find(LineEditableTabs.TabPane)).toHaveLength(
|
||||
props.component.children.length,
|
||||
);
|
||||
});
|
||||
|
||||
it('should render editable tabs in editMode', () => {
|
||||
const wrapper = setup({ editMode: true });
|
||||
expect(wrapper.find(LineEditableTabs)).toExist();
|
||||
expect(wrapper.find('.ant-tabs-nav-add')).toExist();
|
||||
});
|
||||
|
||||
it('should render a DashboardComponent for each child', () => {
|
||||
// note: this does not test Tab content
|
||||
const wrapper = setup({ renderTabContent: false });
|
||||
expect(wrapper.find(DashboardComponent)).toHaveLength(
|
||||
props.component.children.length,
|
||||
);
|
||||
});
|
||||
|
||||
it('should call createComponent if the (+) tab is clicked', () => {
|
||||
const createComponent = sinon.spy();
|
||||
const wrapper = setup({ editMode: true, createComponent });
|
||||
wrapper
|
||||
.find('[data-test="dashboard-component-tabs"] .ant-tabs-nav-add')
|
||||
.last()
|
||||
.simulate('click');
|
||||
|
||||
expect(createComponent.callCount).toBe(1);
|
||||
});
|
||||
|
||||
it('should call onChangeTab when a tab is clicked', () => {
|
||||
const onChangeTab = sinon.spy();
|
||||
const wrapper = setup({ editMode: true, onChangeTab });
|
||||
wrapper
|
||||
.find('[data-test="dashboard-component-tabs"] .ant-tabs-tab')
|
||||
.at(1) // will not call if it is already selected
|
||||
.simulate('click');
|
||||
|
||||
expect(onChangeTab.callCount).toBe(1);
|
||||
});
|
||||
|
||||
it('should not call onChangeTab when anchor link is clicked', () => {
|
||||
const onChangeTab = sinon.spy();
|
||||
const wrapper = setup({ editMode: true, onChangeTab });
|
||||
wrapper
|
||||
.find(
|
||||
'[data-test="dashboard-component-tabs"] .ant-tabs-tab [role="button"]',
|
||||
)
|
||||
.at(1) // will not call if it is already selected
|
||||
.simulate('click');
|
||||
|
||||
expect(onChangeTab.callCount).toBe(0);
|
||||
});
|
||||
|
||||
it('should render a HoverMenu in editMode', () => {
|
||||
let wrapper = setup();
|
||||
expect(wrapper.find(HoverMenu)).not.toExist();
|
||||
|
||||
wrapper = setup({ editMode: true });
|
||||
expect(wrapper.find(HoverMenu)).toExist();
|
||||
});
|
||||
|
||||
it('should render a DeleteComponentButton in editMode', () => {
|
||||
let wrapper = setup();
|
||||
expect(wrapper.find(DeleteComponentButton)).not.toExist();
|
||||
|
||||
wrapper = setup({ editMode: true });
|
||||
expect(wrapper.find(DeleteComponentButton)).toExist();
|
||||
});
|
||||
|
||||
it('should call deleteComponent when deleted', () => {
|
||||
const deleteComponent = sinon.spy();
|
||||
const wrapper = setup({ editMode: true, deleteComponent });
|
||||
wrapper.find(DeleteComponentButton).simulate('click');
|
||||
|
||||
expect(deleteComponent.callCount).toBe(1);
|
||||
});
|
||||
|
||||
it('should direct display direct-link tab', () => {
|
||||
let wrapper = shallow(<Tabs {...props} />);
|
||||
// default show first tab child
|
||||
expect(wrapper.state('tabIndex')).toBe(0);
|
||||
|
||||
// display child in directPathToChild list
|
||||
const directPathToChild =
|
||||
dashboardLayoutWithTabs.present.ROW_ID2.parents.slice();
|
||||
const directLinkProps = {
|
||||
...props,
|
||||
directPathToChild,
|
||||
};
|
||||
|
||||
wrapper = shallow(<Tabs {...directLinkProps} />);
|
||||
expect(wrapper.state('tabIndex')).toBe(1);
|
||||
});
|
||||
|
||||
it('should render Modal when clicked remove tab button', () => {
|
||||
const deleteComponent = sinon.spy();
|
||||
const modalMock = jest.spyOn(AntdModal, 'confirm');
|
||||
const wrapper = setup({ editMode: true, deleteComponent });
|
||||
wrapper.find('.ant-tabs-tab-remove').at(0).simulate('click');
|
||||
expect(modalMock.mock.calls).toHaveLength(1);
|
||||
expect(deleteComponent.callCount).toBe(0);
|
||||
});
|
||||
|
||||
it('should set new tab key if dashboardId was changed', () => {
|
||||
const wrapper = shallow(<Tabs {...props} />);
|
||||
expect(wrapper.state('activeKey')).toBe('TAB_ID');
|
||||
wrapper.setProps({
|
||||
...props,
|
||||
dashboardId: 2,
|
||||
component: dashboardLayoutWithTabs.present.TAB_ID,
|
||||
});
|
||||
expect(wrapper.state('activeKey')).toBe('ROW_ID');
|
||||
});
|
||||
const mockStore = getMockStore({
|
||||
...initialState,
|
||||
dashboardLayout: dashboardLayoutWithTabs,
|
||||
dashboardFilters: {},
|
||||
});
|
||||
|
||||
function setup(overrideProps) {
|
||||
return render(<Tabs {...props} {...overrideProps} />, {
|
||||
useDnd: true,
|
||||
useRouter: true,
|
||||
store: mockStore,
|
||||
});
|
||||
}
|
||||
|
||||
test('should render a DragDroppable', () => {
|
||||
// test just Tabs with no children DragDroppables
|
||||
const { getByTestId } = setup({
|
||||
component: { ...props.component, children: [] },
|
||||
});
|
||||
expect(getByTestId('dragdroppable-object')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should render non-editable tabs', () => {
|
||||
const { getAllByRole, container } = setup();
|
||||
expect(getAllByRole('tab')[0]).toBeInTheDocument();
|
||||
expect(container.querySelector('.ant-tabs-nav-add')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should render a tab pane for each child', () => {
|
||||
const { getAllByRole } = setup();
|
||||
expect(getAllByRole('tab')).toHaveLength(props.component.children.length);
|
||||
});
|
||||
|
||||
test('should render editable tabs in editMode', () => {
|
||||
const { getAllByRole, container } = setup({ editMode: true });
|
||||
expect(getAllByRole('tab')[0]).toBeInTheDocument();
|
||||
expect(container.querySelector('.ant-tabs-nav-add')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should render a DashboardComponent for each child', () => {
|
||||
// note: this does not test Tab content
|
||||
const { getAllByTestId } = setup({ renderTabContent: false });
|
||||
expect(getAllByTestId('mock-dashboard-component')).toHaveLength(
|
||||
props.component.children.length,
|
||||
);
|
||||
});
|
||||
|
||||
test('should call createComponent if the (+) tab is clicked', () => {
|
||||
const createComponent = jest.fn();
|
||||
const { getAllByRole } = setup({ editMode: true, createComponent });
|
||||
const addButtons = getAllByRole('button', { name: 'Add tab' });
|
||||
fireEvent.click(addButtons[0]);
|
||||
expect(createComponent).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('should call onChangeTab when a tab is clicked', () => {
|
||||
const onChangeTab = jest.fn();
|
||||
const { getByRole } = setup({ editMode: true, onChangeTab });
|
||||
const newTab = getByRole('tab', { selected: false });
|
||||
fireEvent.click(newTab);
|
||||
expect(onChangeTab).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('should not call onChangeTab when anchor link is clicked', () => {
|
||||
const onChangeTab = jest.fn();
|
||||
const { getByRole } = setup({ editMode: true, onChangeTab });
|
||||
const currentTab = getByRole('tab', { selected: true });
|
||||
fireEvent.click(currentTab);
|
||||
|
||||
expect(onChangeTab).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
test('should render a HoverMenu in editMode', () => {
|
||||
const { container } = setup({ editMode: true });
|
||||
expect(container.querySelector('.hover-menu')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should render a DeleteComponentButton in editMode', () => {
|
||||
const { getByTestId } = setup({ editMode: true });
|
||||
expect(getByTestId('mock-delete-component-button')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should call deleteComponent when deleted', () => {
|
||||
const deleteComponent = jest.fn();
|
||||
const { getByTestId } = setup({ editMode: true, deleteComponent });
|
||||
fireEvent.click(getByTestId('mock-delete-component-button'));
|
||||
expect(deleteComponent).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('should direct display direct-link tab', () => {
|
||||
// display child in directPathToChild list
|
||||
const directPathToChild =
|
||||
dashboardLayoutWithTabs.present.ROW_ID2.parents.slice();
|
||||
const directLinkProps = {
|
||||
...props,
|
||||
directPathToChild,
|
||||
};
|
||||
const { getByRole } = setup(directLinkProps);
|
||||
expect(getByRole('tab', { selected: true })).toHaveTextContent('TAB_ID2');
|
||||
});
|
||||
|
||||
test('should render Modal when clicked remove tab button', () => {
|
||||
const deleteComponent = jest.fn();
|
||||
const modalMock = jest.spyOn(AntdModal, 'confirm');
|
||||
const { container } = setup({ editMode: true, deleteComponent });
|
||||
fireEvent.click(container.querySelector('.ant-tabs-tab-remove'));
|
||||
expect(modalMock).toHaveBeenCalledTimes(1);
|
||||
expect(deleteComponent).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
test('should set new tab key if dashboardId was changed', () => {
|
||||
const { getByRole } = setup({
|
||||
...props,
|
||||
dashboardId: 2,
|
||||
component: dashboardLayoutWithTabs.present.TAB_ID,
|
||||
});
|
||||
expect(getByRole('tab', { selected: true })).toHaveTextContent('ROW_ID');
|
||||
});
|
||||
|
|
|
|||
|
|
@ -17,29 +17,32 @@
|
|||
* under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import { render } from 'spec/helpers/testing-library';
|
||||
|
||||
import DraggableNewComponent from 'src/dashboard/components/gridComponents/new/DraggableNewComponent';
|
||||
import NewColumn from 'src/dashboard/components/gridComponents/new/NewColumn';
|
||||
|
||||
import { NEW_COLUMN_ID } from 'src/dashboard/util/constants';
|
||||
import { COLUMN_TYPE } from 'src/dashboard/util/componentTypes';
|
||||
|
||||
describe('NewColumn', () => {
|
||||
function setup() {
|
||||
return shallow(<NewColumn />);
|
||||
}
|
||||
jest.mock(
|
||||
'src/dashboard/components/gridComponents/new/DraggableNewComponent',
|
||||
() =>
|
||||
({ type, id }) =>
|
||||
<div data-test="mock-draggable-new-component">{`${type}:${id}`}</div>,
|
||||
);
|
||||
|
||||
it('should render a DraggableNewComponent', () => {
|
||||
const wrapper = setup();
|
||||
expect(wrapper.find(DraggableNewComponent)).toExist();
|
||||
});
|
||||
function setup() {
|
||||
return render(<NewColumn />);
|
||||
}
|
||||
|
||||
it('should set appropriate type and id', () => {
|
||||
const wrapper = setup();
|
||||
expect(wrapper.find(DraggableNewComponent).props()).toMatchObject({
|
||||
type: COLUMN_TYPE,
|
||||
id: NEW_COLUMN_ID,
|
||||
});
|
||||
});
|
||||
test('should render a DraggableNewComponent', () => {
|
||||
const { getByTestId } = setup();
|
||||
expect(getByTestId('mock-draggable-new-component')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should set appropriate type and id', () => {
|
||||
const { getByTestId } = setup();
|
||||
expect(getByTestId('mock-draggable-new-component')).toHaveTextContent(
|
||||
`${COLUMN_TYPE}:${NEW_COLUMN_ID}`,
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -17,29 +17,32 @@
|
|||
* under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import { render } from 'spec/helpers/testing-library';
|
||||
|
||||
import DraggableNewComponent from 'src/dashboard/components/gridComponents/new/DraggableNewComponent';
|
||||
import NewDivider from 'src/dashboard/components/gridComponents/new/NewDivider';
|
||||
|
||||
import { NEW_DIVIDER_ID } from 'src/dashboard/util/constants';
|
||||
import { DIVIDER_TYPE } from 'src/dashboard/util/componentTypes';
|
||||
|
||||
describe('NewDivider', () => {
|
||||
function setup() {
|
||||
return shallow(<NewDivider />);
|
||||
}
|
||||
jest.mock(
|
||||
'src/dashboard/components/gridComponents/new/DraggableNewComponent',
|
||||
() =>
|
||||
({ type, id }) =>
|
||||
<div data-test="mock-draggable-new-component">{`${type}:${id}`}</div>,
|
||||
);
|
||||
|
||||
it('should render a DraggableNewComponent', () => {
|
||||
const wrapper = setup();
|
||||
expect(wrapper.find(DraggableNewComponent)).toExist();
|
||||
});
|
||||
function setup() {
|
||||
return render(<NewDivider />);
|
||||
}
|
||||
|
||||
it('should set appropriate type and id', () => {
|
||||
const wrapper = setup();
|
||||
expect(wrapper.find(DraggableNewComponent).props()).toMatchObject({
|
||||
type: DIVIDER_TYPE,
|
||||
id: NEW_DIVIDER_ID,
|
||||
});
|
||||
});
|
||||
test('should render a DraggableNewComponent', () => {
|
||||
const { getByTestId } = setup();
|
||||
expect(getByTestId('mock-draggable-new-component')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should set appropriate type and id', () => {
|
||||
const { getByTestId } = setup();
|
||||
expect(getByTestId('mock-draggable-new-component')).toHaveTextContent(
|
||||
`${DIVIDER_TYPE}:${NEW_DIVIDER_ID}`,
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -17,29 +17,32 @@
|
|||
* under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import { render } from 'spec/helpers/testing-library';
|
||||
|
||||
import DraggableNewComponent from 'src/dashboard/components/gridComponents/new/DraggableNewComponent';
|
||||
import NewHeader from 'src/dashboard/components/gridComponents/new/NewHeader';
|
||||
|
||||
import { NEW_HEADER_ID } from 'src/dashboard/util/constants';
|
||||
import { HEADER_TYPE } from 'src/dashboard/util/componentTypes';
|
||||
|
||||
describe('NewHeader', () => {
|
||||
function setup() {
|
||||
return shallow(<NewHeader />);
|
||||
}
|
||||
jest.mock(
|
||||
'src/dashboard/components/gridComponents/new/DraggableNewComponent',
|
||||
() =>
|
||||
({ type, id }) =>
|
||||
<div data-test="mock-draggable-new-component">{`${type}:${id}`}</div>,
|
||||
);
|
||||
|
||||
it('should render a DraggableNewComponent', () => {
|
||||
const wrapper = setup();
|
||||
expect(wrapper.find(DraggableNewComponent)).toExist();
|
||||
});
|
||||
function setup() {
|
||||
return render(<NewHeader />);
|
||||
}
|
||||
|
||||
it('should set appropriate type and id', () => {
|
||||
const wrapper = setup();
|
||||
expect(wrapper.find(DraggableNewComponent).props()).toMatchObject({
|
||||
type: HEADER_TYPE,
|
||||
id: NEW_HEADER_ID,
|
||||
});
|
||||
});
|
||||
test('should render a DraggableNewComponent', () => {
|
||||
const { getByTestId } = setup();
|
||||
expect(getByTestId('mock-draggable-new-component')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should set appropriate type and id', () => {
|
||||
const { getByTestId } = setup();
|
||||
expect(getByTestId('mock-draggable-new-component')).toHaveTextContent(
|
||||
`${HEADER_TYPE}:${NEW_HEADER_ID}`,
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -17,29 +17,32 @@
|
|||
* under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import { render } from 'spec/helpers/testing-library';
|
||||
|
||||
import DraggableNewComponent from 'src/dashboard/components/gridComponents/new/DraggableNewComponent';
|
||||
import NewRow from 'src/dashboard/components/gridComponents/new/NewRow';
|
||||
|
||||
import { NEW_ROW_ID } from 'src/dashboard/util/constants';
|
||||
import { ROW_TYPE } from 'src/dashboard/util/componentTypes';
|
||||
|
||||
describe('NewRow', () => {
|
||||
function setup() {
|
||||
return shallow(<NewRow />);
|
||||
}
|
||||
jest.mock(
|
||||
'src/dashboard/components/gridComponents/new/DraggableNewComponent',
|
||||
() =>
|
||||
({ type, id }) =>
|
||||
<div data-test="mock-draggable-new-component">{`${type}:${id}`}</div>,
|
||||
);
|
||||
|
||||
it('should render a DraggableNewComponent', () => {
|
||||
const wrapper = setup();
|
||||
expect(wrapper.find(DraggableNewComponent)).toExist();
|
||||
});
|
||||
function setup() {
|
||||
return render(<NewRow />);
|
||||
}
|
||||
|
||||
it('should set appropriate type and id', () => {
|
||||
const wrapper = setup();
|
||||
expect(wrapper.find(DraggableNewComponent).props()).toMatchObject({
|
||||
type: ROW_TYPE,
|
||||
id: NEW_ROW_ID,
|
||||
});
|
||||
});
|
||||
test('should render a DraggableNewComponent', () => {
|
||||
const { getByTestId } = setup();
|
||||
expect(getByTestId('mock-draggable-new-component')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should set appropriate type and id', () => {
|
||||
const { getByTestId } = setup();
|
||||
expect(getByTestId('mock-draggable-new-component')).toHaveTextContent(
|
||||
`${ROW_TYPE}:${NEW_ROW_ID}`,
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -17,29 +17,32 @@
|
|||
* under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import { render } from 'spec/helpers/testing-library';
|
||||
|
||||
import DraggableNewComponent from 'src/dashboard/components/gridComponents/new/DraggableNewComponent';
|
||||
import NewTabs from 'src/dashboard/components/gridComponents/new/NewTabs';
|
||||
|
||||
import { NEW_TABS_ID } from 'src/dashboard/util/constants';
|
||||
import { TABS_TYPE } from 'src/dashboard/util/componentTypes';
|
||||
|
||||
describe('NewTabs', () => {
|
||||
function setup() {
|
||||
return shallow(<NewTabs />);
|
||||
}
|
||||
jest.mock(
|
||||
'src/dashboard/components/gridComponents/new/DraggableNewComponent',
|
||||
() =>
|
||||
({ type, id }) =>
|
||||
<div data-test="mock-draggable-new-component">{`${type}:${id}`}</div>,
|
||||
);
|
||||
|
||||
it('should render a DraggableNewComponent', () => {
|
||||
const wrapper = setup();
|
||||
expect(wrapper.find(DraggableNewComponent)).toExist();
|
||||
});
|
||||
function setup() {
|
||||
return render(<NewTabs />);
|
||||
}
|
||||
|
||||
it('should set appropriate type and id', () => {
|
||||
const wrapper = setup();
|
||||
expect(wrapper.find(DraggableNewComponent).props()).toMatchObject({
|
||||
type: TABS_TYPE,
|
||||
id: NEW_TABS_ID,
|
||||
});
|
||||
});
|
||||
test('should render a DraggableNewComponent', () => {
|
||||
const { getByTestId } = setup();
|
||||
expect(getByTestId('mock-draggable-new-component')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should set appropriate type and id', () => {
|
||||
const { getByTestId } = setup();
|
||||
expect(getByTestId('mock-draggable-new-component')).toHaveTextContent(
|
||||
`${TABS_TYPE}:${NEW_TABS_ID}`,
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -17,13 +17,11 @@
|
|||
* under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import { render } from 'spec/helpers/testing-library';
|
||||
|
||||
import HoverMenu from 'src/dashboard/components/menu/HoverMenu';
|
||||
|
||||
describe('HoverMenu', () => {
|
||||
it('should render a div.hover-menu', () => {
|
||||
const wrapper = shallow(<HoverMenu />);
|
||||
expect(wrapper.find('.hover-menu')).toExist();
|
||||
});
|
||||
test('should render a div.hover-menu', () => {
|
||||
const { container } = render(<HoverMenu />);
|
||||
expect(container.querySelector('.hover-menu')).toBeInTheDocument();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -17,71 +17,68 @@
|
|||
* under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import { fireEvent, render } from 'spec/helpers/testing-library';
|
||||
|
||||
import WithPopoverMenu from 'src/dashboard/components/menu/WithPopoverMenu';
|
||||
|
||||
describe('WithPopoverMenu', () => {
|
||||
const props = {
|
||||
children: <div id="child" />,
|
||||
disableClick: false,
|
||||
menuItems: [<div id="menu1" />, <div id="menu2" />],
|
||||
onChangeFocus() {},
|
||||
shouldFocus: () => true, // needed for mock
|
||||
isFocused: false,
|
||||
editMode: false,
|
||||
};
|
||||
const props = {
|
||||
children: <div id="child" />,
|
||||
disableClick: false,
|
||||
menuItems: [<div id="menu1" />, <div id="menu2" />],
|
||||
onChangeFocus() {},
|
||||
shouldFocus: () => true, // needed for mock
|
||||
isFocused: false,
|
||||
editMode: false,
|
||||
};
|
||||
|
||||
function setup(overrideProps) {
|
||||
const wrapper = shallow(<WithPopoverMenu {...props} {...overrideProps} />);
|
||||
return wrapper;
|
||||
}
|
||||
function setup(overrideProps) {
|
||||
return render(<WithPopoverMenu {...props} {...overrideProps} />);
|
||||
}
|
||||
|
||||
it('should render a div with class "with-popover-menu"', () => {
|
||||
const wrapper = setup();
|
||||
expect(wrapper.find('.with-popover-menu')).toExist();
|
||||
});
|
||||
|
||||
it('should render the passed children', () => {
|
||||
const wrapper = setup();
|
||||
expect(wrapper.find('#child')).toExist();
|
||||
});
|
||||
|
||||
it('should focus on click in editMode', () => {
|
||||
const wrapper = setup();
|
||||
expect(wrapper.state('isFocused')).toBe(false);
|
||||
|
||||
wrapper.simulate('click');
|
||||
expect(wrapper.state('isFocused')).toBe(false);
|
||||
|
||||
wrapper.setProps({ ...props, editMode: true });
|
||||
wrapper.simulate('click');
|
||||
expect(wrapper.state('isFocused')).toBe(true);
|
||||
});
|
||||
|
||||
it('should render menuItems when focused', () => {
|
||||
const wrapper = setup({ editMode: true });
|
||||
expect(wrapper.find('#menu1')).not.toExist();
|
||||
expect(wrapper.find('#menu2')).not.toExist();
|
||||
|
||||
wrapper.simulate('click');
|
||||
expect(wrapper.find('#menu1')).toExist();
|
||||
expect(wrapper.find('#menu2')).toExist();
|
||||
});
|
||||
|
||||
it('should not focus when disableClick=true', () => {
|
||||
const wrapper = setup({ disableClick: true, editMode: true });
|
||||
expect(wrapper.state('isFocused')).toBe(false);
|
||||
|
||||
wrapper.simulate('click');
|
||||
expect(wrapper.state('isFocused')).toBe(false);
|
||||
});
|
||||
|
||||
it('should use the passed shouldFocus func to determine if it should focus', () => {
|
||||
const wrapper = setup({ editMode: true, shouldFocus: () => false });
|
||||
expect(wrapper.state('isFocused')).toBe(false);
|
||||
|
||||
wrapper.simulate('click');
|
||||
expect(wrapper.state('isFocused')).toBe(false);
|
||||
});
|
||||
test('should render a div with class "with-popover-menu"', () => {
|
||||
const { container } = setup();
|
||||
expect(container.querySelector('.with-popover-menu')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should render the passed children', () => {
|
||||
const { container } = setup();
|
||||
expect(container.querySelector('#child')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should focus on click in editMode', () => {
|
||||
const { container } = setup({ editMode: true });
|
||||
fireEvent.click(container.querySelector('.with-popover-menu'));
|
||||
expect(
|
||||
container.querySelector('.with-popover-menu--focused'),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should render menuItems when focused', () => {
|
||||
const { container } = setup({ editMode: true });
|
||||
expect(container.querySelector('#menu1')).not.toBeInTheDocument();
|
||||
expect(container.querySelector('#menu2')).not.toBeInTheDocument();
|
||||
|
||||
fireEvent.click(container.querySelector('.with-popover-menu'));
|
||||
expect(container.querySelector('#menu1')).toBeInTheDocument();
|
||||
expect(container.querySelector('#menu2')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should not focus when disableClick=true', () => {
|
||||
const { container } = setup({ disableClick: true, editMode: true });
|
||||
|
||||
fireEvent.click(container.querySelector('.with-popover-menu'));
|
||||
expect(
|
||||
container.querySelector('.with-popover-menu--focused'),
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should use the passed shouldFocus func to determine if it should focus', () => {
|
||||
const { container } = setup({ editMode: true, shouldFocus: () => false });
|
||||
expect(
|
||||
container.querySelector('.with-popover-menu--focused'),
|
||||
).not.toBeInTheDocument();
|
||||
fireEvent.click(container.querySelector('.with-popover-menu'));
|
||||
expect(
|
||||
container.querySelector('.with-popover-menu--focused'),
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -423,7 +423,7 @@ function FiltersConfigModal({
|
|||
)();
|
||||
resetForm(true);
|
||||
} else {
|
||||
configFormRef.current.changeTab('configuration');
|
||||
configFormRef.current?.changeTab?.('configuration');
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -16,18 +16,8 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { ReactWrapper } from 'enzyme';
|
||||
import React from 'react';
|
||||
import { DndProvider } from 'react-dnd';
|
||||
import { HTML5Backend } from 'react-dnd-html5-backend';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { Provider } from 'react-redux';
|
||||
import { mockStore } from 'spec/fixtures/mockStore';
|
||||
import { styledMount as mount } from 'spec/helpers/theming';
|
||||
import waitForComponentToPaint from 'spec/helpers/waitForComponentToPaint';
|
||||
import { AntdDropdown } from 'src/components';
|
||||
import { Menu } from 'src/components/Menu';
|
||||
import Alert from 'src/components/Alert';
|
||||
import { fireEvent, render } from 'spec/helpers/testing-library';
|
||||
import FiltersConfigModal from 'src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigModal';
|
||||
|
||||
Object.defineProperty(window, 'matchMedia', {
|
||||
|
|
@ -59,83 +49,56 @@ jest.mock('@superset-ui/core', () => ({
|
|||
}),
|
||||
}));
|
||||
|
||||
describe('FiltersConfigModal', () => {
|
||||
const mockedProps = {
|
||||
isOpen: true,
|
||||
initialFilterId: 'NATIVE_FILTER-1',
|
||||
createNewOnOpen: true,
|
||||
onCancel: jest.fn(),
|
||||
onSave: jest.fn(),
|
||||
};
|
||||
function setup(overridesProps?: any) {
|
||||
return mount(
|
||||
<Provider store={mockStore}>
|
||||
<DndProvider backend={HTML5Backend}>
|
||||
<FiltersConfigModal {...mockedProps} {...overridesProps} />
|
||||
</DndProvider>
|
||||
</Provider>,
|
||||
);
|
||||
}
|
||||
const mockedProps = {
|
||||
isOpen: true,
|
||||
initialFilterId: 'NATIVE_FILTER-1',
|
||||
createNewOnOpen: true,
|
||||
onCancel: jest.fn(),
|
||||
onSave: jest.fn(),
|
||||
};
|
||||
function setup(overridesProps?: any) {
|
||||
return render(<FiltersConfigModal {...mockedProps} {...overridesProps} />, {
|
||||
useDnd: true,
|
||||
useRedux: true,
|
||||
});
|
||||
}
|
||||
|
||||
it('should be a valid react element', () => {
|
||||
expect(React.isValidElement(<FiltersConfigModal {...mockedProps} />)).toBe(
|
||||
true,
|
||||
);
|
||||
test('should be a valid react element', () => {
|
||||
const { container } = setup();
|
||||
expect(container).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('the form validates required fields', async () => {
|
||||
const onSave = jest.fn();
|
||||
const { getByRole } = setup({ save: onSave });
|
||||
fireEvent.change(getByRole('textbox', { name: 'Description' }), {
|
||||
target: { value: 'test name' },
|
||||
});
|
||||
const saveButton = getByRole('button', { name: 'Save' });
|
||||
fireEvent.click(saveButton);
|
||||
expect(onSave).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
describe('createNewOnOpen', () => {
|
||||
test('does not show alert when there is no unsaved filters', async () => {
|
||||
const onCancel = jest.fn();
|
||||
const { getByRole } = setup({ onCancel, createNewOnOpen: false });
|
||||
fireEvent.click(getByRole('button', { name: 'Cancel' }));
|
||||
expect(onCancel).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('the form validates required fields', async () => {
|
||||
const onSave = jest.fn();
|
||||
const wrapper = setup({ save: onSave });
|
||||
act(() => {
|
||||
wrapper
|
||||
.find('input')
|
||||
.first()
|
||||
.simulate('change', { target: { value: 'test name' } });
|
||||
|
||||
wrapper.find('.ant-modal-footer button').at(1).simulate('click');
|
||||
});
|
||||
await waitForComponentToPaint(wrapper);
|
||||
expect(onSave.mock.calls).toHaveLength(0);
|
||||
});
|
||||
|
||||
describe('when click cancel', () => {
|
||||
let onCancel: jest.Mock;
|
||||
let wrapper: ReactWrapper;
|
||||
|
||||
beforeEach(() => {
|
||||
onCancel = jest.fn();
|
||||
wrapper = setup({ onCancel, createNewOnOpen: false });
|
||||
});
|
||||
|
||||
async function clickCancel() {
|
||||
act(() => {
|
||||
wrapper.find('.ant-modal-footer button').at(0).simulate('click');
|
||||
});
|
||||
await waitForComponentToPaint(wrapper);
|
||||
}
|
||||
|
||||
async function addFilter() {
|
||||
act(() => {
|
||||
wrapper.find(AntdDropdown).at(0).simulate('mouseEnter');
|
||||
});
|
||||
await waitForComponentToPaint(wrapper, 300);
|
||||
act(() => {
|
||||
wrapper.find(Menu.Item).at(0).simulate('click');
|
||||
});
|
||||
}
|
||||
|
||||
it('does not show alert when there is no unsaved filters', async () => {
|
||||
await clickCancel();
|
||||
expect(onCancel.mock.calls).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('shows correct alert message for unsaved filters', async () => {
|
||||
await addFilter();
|
||||
await clickCancel();
|
||||
expect(onCancel.mock.calls).toHaveLength(0);
|
||||
expect(wrapper.find(Alert).text()).toContain(
|
||||
'There are unsaved changes.',
|
||||
);
|
||||
test('shows correct alert message for unsaved filters', async () => {
|
||||
const onCancel = jest.fn();
|
||||
const { getByRole, getByTestId, findByRole } = setup({
|
||||
onCancel,
|
||||
createNewOnOpen: false,
|
||||
});
|
||||
fireEvent.mouseOver(getByTestId('new-dropdown-icon'));
|
||||
const addFilterButton = await findByRole('menuitem', { name: 'Filter' });
|
||||
fireEvent.click(addFilterButton);
|
||||
fireEvent.click(getByRole('button', { name: 'Cancel' }));
|
||||
expect(onCancel).toHaveBeenCalledTimes(0);
|
||||
expect(getByRole('alert')).toBeInTheDocument();
|
||||
expect(getByRole('alert')).toHaveTextContent('There are unsaved changes.');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue