test: Adds tests to dnd controls (#13650)

This commit is contained in:
Michael S. Molina 2021-04-01 13:11:33 -03:00 committed by GitHub
parent bb677b8ef1
commit 42c7e2cae6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 394 additions and 48 deletions

View File

@ -23,34 +23,41 @@ import { ThemeProvider, supersetTheme } from '@superset-ui/core';
import { Provider } from 'react-redux';
import { combineReducers, createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import { DndProvider } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend';
import reducerIndex from 'spec/helpers/reducerIndex';
type Options = Omit<RenderOptions, 'queries'> & {
useRedux?: boolean;
useDnd?: boolean;
initialState?: {};
reducers?: {};
};
function createWrapper(options?: Options) {
const { useRedux, initialState, reducers } = options || {};
const { useDnd, useRedux, initialState, reducers } = options || {};
if (useRedux) {
const store = createStore(
combineReducers(reducers || reducerIndex),
initialState || {},
compose(applyMiddleware(thunk)),
return ({ children }: { children?: ReactNode }) => {
let result = (
<ThemeProvider theme={supersetTheme}>{children}</ThemeProvider>
);
return ({ children }: { children?: ReactNode }) => (
<Provider store={store}>
<ThemeProvider theme={supersetTheme}>{children}</ThemeProvider>
</Provider>
);
}
if (useDnd) {
result = <DndProvider backend={HTML5Backend}>{result}</DndProvider>;
}
return ({ children }: { children?: ReactNode }) => (
<ThemeProvider theme={supersetTheme}>{children}</ThemeProvider>
);
if (useRedux) {
const store = createStore(
combineReducers(reducers || reducerIndex),
initialState || {},
compose(applyMiddleware(thunk)),
);
result = <Provider store={store}>{result}</Provider>;
}
return result;
};
}
const customRender = (ui: ReactElement, options?: Options) =>

View File

@ -0,0 +1,40 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import React from 'react';
import { render, screen } from 'spec/helpers/testing-library';
import { LabelProps } from 'src/explore/components/controls/DndColumnSelectControl/types';
import { DndColumnSelect } from 'src/explore/components/controls/DndColumnSelectControl/DndColumnSelect';
const defaultProps: LabelProps = {
name: 'Filter',
onChange: jest.fn(),
options: { string: { column_name: 'Column A' } },
};
test('renders with default props', () => {
render(<DndColumnSelect {...defaultProps} />, { useDnd: true });
expect(screen.getByText('Drop columns')).toBeInTheDocument();
});
test('renders with value', () => {
render(<DndColumnSelect {...defaultProps} value="string" />, {
useDnd: true,
});
expect(screen.getByText('Column A')).toBeInTheDocument();
});

View File

@ -19,12 +19,12 @@
import React, { useState } from 'react';
import { ColumnMeta, ColumnOption } from '@superset-ui/chart-controls';
import { isEmpty } from 'lodash';
import { LabelProps } from './types';
import DndSelectLabel from './DndSelectLabel';
import OptionWrapper from './components/OptionWrapper';
import { OptionSelector } from './utils';
import { DatasourcePanelDndItem } from '../../DatasourcePanel/types';
import { DndItemType } from '../../DndItemType';
import { LabelProps } from 'src/explore/components/controls/DndColumnSelectControl/types';
import DndSelectLabel from 'src/explore/components/controls/DndColumnSelectControl/DndSelectLabel';
import OptionWrapper from 'src/explore/components/controls/DndColumnSelectControl/OptionWrapper';
import { OptionSelector } from 'src/explore/components/controls/DndColumnSelectControl/utils';
import { DatasourcePanelDndItem } from 'src/explore/components/DatasourcePanel/types';
import { DndItemType } from 'src/explore/components/DndItemType';
export const DndColumnSelect = (props: LabelProps) => {
const { value, options } = props;

View File

@ -0,0 +1,83 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import React from 'react';
import { render, screen } from 'spec/helpers/testing-library';
import AdhocMetric from 'src/explore/components/controls/MetricControl/AdhocMetric';
import AdhocFilter, {
EXPRESSION_TYPES,
} from 'src/explore/components/controls/FilterControl/AdhocFilter';
import { DndFilterSelect } from 'src/explore/components/controls/DndColumnSelectControl/DndFilterSelect';
const defaultProps = {
name: 'Filter',
value: [],
columns: [],
datasource: {},
formData: {},
savedMetrics: [],
onChange: jest.fn(),
options: { string: { column_name: 'Column' } },
};
test('renders with default props', () => {
render(<DndFilterSelect {...defaultProps} />, { useDnd: true });
expect(screen.getByText('Drop columns or metrics')).toBeInTheDocument();
});
test('renders with value', () => {
const value = new AdhocFilter({
sqlExpression: 'COUNT(*)',
expressionType: EXPRESSION_TYPES.SQL,
});
render(<DndFilterSelect {...defaultProps} value={[value]} />, {
useDnd: true,
});
expect(screen.getByText('COUNT(*)')).toBeInTheDocument();
});
test('renders options with saved metric', () => {
render(<DndFilterSelect {...defaultProps} formData={['saved_metric']} />, {
useDnd: true,
});
expect(screen.getByText('Drop columns or metrics')).toBeInTheDocument();
});
test('renders options with column', () => {
render(
<DndFilterSelect
{...defaultProps}
columns={[{ id: 1, type: 'string', column_name: 'Column' }]}
/>,
{
useDnd: true,
},
);
expect(screen.getByText('Drop columns or metrics')).toBeInTheDocument();
});
test('renders options with adhoc metric', () => {
const adhocMetric = new AdhocMetric({
expression: 'AVG(birth_names.num)',
metric_name: 'avg__num',
});
render(<DndFilterSelect {...defaultProps} formData={[adhocMetric]} />, {
useDnd: true,
});
expect(screen.getByText('Drop columns or metrics')).toBeInTheDocument();
});

View File

@ -22,20 +22,23 @@ import { ColumnMeta } from '@superset-ui/chart-controls';
import { Tooltip } from 'src/common/components/Tooltip';
import { OPERATORS } from 'src/explore/constants';
import { OptionSortType } from 'src/explore/types';
import { DndFilterSelectProps, OptionValueType } from './types';
import AdhocFilterPopoverTrigger from '../FilterControl/AdhocFilterPopoverTrigger';
import OptionWrapper from './components/OptionWrapper';
import DndSelectLabel from './DndSelectLabel';
import {
DndFilterSelectProps,
OptionValueType,
} from 'src/explore/components/controls/DndColumnSelectControl/types';
import AdhocFilterPopoverTrigger from 'src/explore/components/controls/FilterControl/AdhocFilterPopoverTrigger';
import OptionWrapper from 'src/explore/components/controls/DndColumnSelectControl/OptionWrapper';
import DndSelectLabel from 'src/explore/components/controls/DndColumnSelectControl/DndSelectLabel';
import AdhocFilter, {
CLAUSES,
EXPRESSION_TYPES,
} from '../FilterControl/AdhocFilter';
import AdhocMetric from '../MetricControl/AdhocMetric';
} from 'src/explore/components/controls/FilterControl/AdhocFilter';
import AdhocMetric from 'src/explore/components/controls/MetricControl/AdhocMetric';
import {
DatasourcePanelDndItem,
DndItemValue,
} from '../../DatasourcePanel/types';
import { DndItemType } from '../../DndItemType';
} from 'src/explore/components/DatasourcePanel/types';
import { DndItemType } from 'src/explore/components/DndItemType';
const isDictionaryForAdhocFilter = (value: OptionValueType) =>
!(value instanceof AdhocFilter) && value?.expressionType;

View File

@ -0,0 +1,35 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import React from 'react';
import { render, screen } from 'spec/helpers/testing-library';
import { DndMetricSelect } from 'src/explore/components/controls/DndColumnSelectControl/DndMetricSelect';
const defaultProps = {
savedMetrics: [
{
metric_name: 'Metric A',
expression: 'Expression A',
},
],
};
test('renders with default props', () => {
render(<DndMetricSelect {...defaultProps} />, { useDnd: true });
expect(screen.getByText('Drop columns or metrics')).toBeInTheDocument();
});

View File

@ -22,14 +22,14 @@ import { ensureIsArray, Metric, t } from '@superset-ui/core';
import { ColumnMeta } from '@superset-ui/chart-controls';
import { isEqual } from 'lodash';
import { usePrevious } from 'src/common/hooks/usePrevious';
import AdhocMetric from '../MetricControl/AdhocMetric';
import AdhocMetricPopoverTrigger from '../MetricControl/AdhocMetricPopoverTrigger';
import MetricDefinitionValue from '../MetricControl/MetricDefinitionValue';
import { OptionValueType } from './types';
import { DatasourcePanelDndItem } from '../../DatasourcePanel/types';
import { DndItemType } from '../../DndItemType';
import DndSelectLabel from './DndSelectLabel';
import { savedMetricType } from '../MetricControl/types';
import AdhocMetric from 'src/explore/components/controls/MetricControl/AdhocMetric';
import AdhocMetricPopoverTrigger from 'src/explore/components/controls/MetricControl/AdhocMetricPopoverTrigger';
import MetricDefinitionValue from 'src/explore/components/controls/MetricControl/MetricDefinitionValue';
import { OptionValueType } from 'src/explore/components/controls/DndColumnSelectControl/types';
import { DatasourcePanelDndItem } from 'src/explore/components/DatasourcePanel/types';
import { DndItemType } from 'src/explore/components/DndItemType';
import DndSelectLabel from 'src/explore/components/controls/DndColumnSelectControl/DndSelectLabel';
import { savedMetricType } from 'src/explore/components/controls/MetricControl/types';
const isDictionaryForAdhocMetric = (value: any) =>
value && !(value instanceof AdhocMetric) && value.expressionType;

View File

@ -0,0 +1,55 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import React from 'react';
import { render, screen } from 'spec/helpers/testing-library';
import { DndItemType } from 'src/explore/components/DndItemType';
import DndSelectLabel from 'src/explore/components/controls/DndColumnSelectControl/DndSelectLabel';
const defaultProps = {
name: 'Column',
accept: 'Column' as DndItemType,
onDrop: jest.fn(),
canDrop: () => false,
valuesRenderer: () => <span />,
onChange: jest.fn(),
options: { string: { column_name: 'Column' } },
};
test('renders with default props', () => {
render(<DndSelectLabel {...defaultProps} />, { useDnd: true });
expect(screen.getByText('Drop columns')).toBeInTheDocument();
});
test('renders ghost button when empty', () => {
const ghostButtonText = 'Ghost button text';
render(
<DndSelectLabel {...defaultProps} ghostButtonText={ghostButtonText} />,
{ useDnd: true },
);
expect(screen.getByText(ghostButtonText)).toBeInTheDocument();
});
test('renders values', () => {
const values = 'Values';
const valuesRenderer = () => <span>{values}</span>;
render(<DndSelectLabel {...defaultProps} valuesRenderer={valuesRenderer} />, {
useDnd: true,
});
expect(screen.getByText(values)).toBeInTheDocument();
});

View File

@ -0,0 +1,56 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import React from 'react';
import { render, screen } from 'spec/helpers/testing-library';
import userEvent from '@testing-library/user-event';
import Option from 'src/explore/components/controls/DndColumnSelectControl/Option';
test('renders with default props', () => {
const { container } = render(
<Option index={1} clickClose={jest.fn()}>
Option
</Option>,
);
expect(container).toBeInTheDocument();
expect(screen.getByRole('img', { name: 'x-small' })).toBeInTheDocument();
expect(
screen.queryByRole('img', { name: 'caret-right' }),
).not.toBeInTheDocument();
});
test('renders with caret', () => {
render(
<Option index={1} clickClose={jest.fn()} withCaret>
Option
</Option>,
);
expect(screen.getByRole('img', { name: 'x-small' })).toBeInTheDocument();
expect(screen.getByRole('img', { name: 'caret-right' })).toBeInTheDocument();
});
test('triggers onClose', () => {
const clickClose = jest.fn();
render(
<Option index={1} clickClose={clickClose}>
Option
</Option>,
);
userEvent.click(screen.getByRole('img', { name: 'x-small' }));
expect(clickClose).toHaveBeenCalled();
});

View File

@ -25,7 +25,7 @@ import {
OptionControlContainer,
Label,
} from 'src/explore/components/controls/OptionControls';
import { OptionProps } from '../types';
import { OptionProps } from 'src/explore/components/controls/DndColumnSelectControl/types';
export default function Option(props: OptionProps) {
const theme = useTheme();

View File

@ -0,0 +1,67 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import React from 'react';
import { render, screen, fireEvent } from 'spec/helpers/testing-library';
import { DndItemType } from 'src/explore/components/DndItemType';
import OptionWrapper from 'src/explore/components/controls/DndColumnSelectControl/OptionWrapper';
test('renders with default props', () => {
const { container } = render(
<OptionWrapper
index={1}
clickClose={jest.fn()}
type={'Column' as DndItemType}
onShiftOptions={jest.fn()}
>
Option
</OptionWrapper>,
{ useDnd: true },
);
expect(container).toBeInTheDocument();
expect(screen.getByRole('img', { name: 'x-small' })).toBeInTheDocument();
});
test('triggers onShiftOptions on drop', () => {
const onShiftOptions = jest.fn();
render(
<>
<OptionWrapper
index={1}
clickClose={jest.fn()}
type={'Column' as DndItemType}
onShiftOptions={onShiftOptions}
>
Option 1
</OptionWrapper>
<OptionWrapper
index={2}
clickClose={jest.fn()}
type={'Column' as DndItemType}
onShiftOptions={onShiftOptions}
>
Option 2
</OptionWrapper>
</>,
{ useDnd: true },
);
fireEvent.dragStart(screen.getByText('Option 1'));
fireEvent.drop(screen.getByText('Option 2'));
expect(onShiftOptions).toHaveBeenCalled();
});

View File

@ -25,11 +25,17 @@ import {
} from 'react-dnd';
import { DragContainer } from 'src/explore/components/controls/OptionControls';
import { DndItemType } from 'src/explore/components/DndItemType';
import {
OptionProps,
OptionItemInterface,
} from 'src/explore/components/controls/DndColumnSelectControl/types';
import Option from './Option';
import { OptionProps, OptionItemInterface } from '../types';
export default function OptionWrapper(
props: OptionProps & { type: DndItemType },
props: OptionProps & {
type: DndItemType;
onShiftOptions: (dragIndex: number, hoverIndex: number) => void;
},
) {
const {
index,
@ -99,13 +105,8 @@ export default function OptionWrapper(
drag(drop(ref));
return (
<DragContainer ref={ref} {...props}>
<Option
index={index}
clickClose={clickClose}
onShiftOptions={onShiftOptions}
withCaret={withCaret}
>
<DragContainer ref={ref}>
<Option index={index} clickClose={clickClose} withCaret={withCaret}>
{children}
</Option>
</DragContainer>

View File

@ -26,7 +26,6 @@ export interface OptionProps {
children: ReactNode;
index: number;
clickClose: (index: number) => void;
onShiftOptions: (dragIndex: number, hoverIndex: number) => void;
withCaret?: boolean;
}