chore(shared components): Migrate enzyme to RTL (#26258)

This commit is contained in:
JUST.in DO IT 2024-08-22 08:34:51 +09:00 committed by GitHub
parent 8e2f81816f
commit 1a1548da3b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 497 additions and 628 deletions

View File

@ -16,10 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
import { isValidElement } from 'react';
import { ReactWrapper } from 'enzyme';
import { styledMount as mount } from 'spec/helpers/theming';
import { fireEvent, render } from 'spec/helpers/testing-library';
import Button from '.';
import {
ButtonGallery,
@ -27,36 +24,27 @@ import {
STYLES as buttonStyles,
} from './Button.stories';
describe('Button', () => {
let wrapper: ReactWrapper;
// test the basic component
it('renders the base component', () => {
expect(isValidElement(<Button />)).toBe(true);
});
it('works with an onClick handler', () => {
const mockAction = jest.fn();
wrapper = mount(<Button onClick={mockAction} />);
wrapper.find('Button').first().simulate('click');
expect(mockAction).toHaveBeenCalled();
});
it('does not handle onClicks when disabled', () => {
const mockAction = jest.fn();
wrapper = mount(<Button onClick={mockAction} disabled />);
wrapper.find('Button').first().simulate('click');
expect(mockAction).toHaveBeenCalledTimes(0);
});
// test stories from the storybook!
it('All the sorybook gallery variants mount', () => {
wrapper = mount(<ButtonGallery />);
const permutationCount =
Object.values(buttonStyles.options).filter(o => o).length *
Object.values(buttonSizes.options).length;
expect(wrapper.find(Button).length).toEqual(permutationCount);
});
test('works with an onClick handler', () => {
const mockAction = jest.fn();
const { getByRole } = render(<Button onClick={mockAction} />);
fireEvent.click(getByRole('button'));
expect(mockAction).toHaveBeenCalled();
});
test('does not handle onClicks when disabled', () => {
const mockAction = jest.fn();
const { getByRole } = render(<Button onClick={mockAction} disabled />);
fireEvent.click(getByRole('button'));
expect(mockAction).toHaveBeenCalledTimes(0);
});
// test stories from the storybook!
test('All the sorybook gallery variants mount', () => {
const { getAllByRole } = render(<ButtonGallery />);
const permutationCount =
Object.values(buttonStyles.options).filter(o => o).length *
Object.values(buttonSizes.options).length;
expect(getAllByRole('button')).toHaveLength(permutationCount);
});

View File

@ -16,11 +16,22 @@
* specific language governing permissions and limitations
* under the License.
*/
import { shallow } from 'enzyme';
import { SuperChart } from '@superset-ui/core';
import { render } from 'spec/helpers/testing-library';
import ChartRenderer from 'src/components/Chart/ChartRenderer';
jest.mock('@superset-ui/core', () => ({
...jest.requireActual('@superset-ui/core'),
SuperChart: ({ formData }) => (
<div data-test="mock-super-chart">{JSON.stringify(formData)}</div>
),
}));
jest.mock(
'src/components/Chart/ChartContextMenu/ChartContextMenu',
() => () => <div data-test="mock-chart-context-menu" />,
);
const requiredProps = {
chartId: 1,
datasource: {},
@ -31,18 +42,18 @@ const requiredProps = {
vizType: 'table',
};
describe('ChartRenderer', () => {
it('should render SuperChart', () => {
const wrapper = shallow(
<ChartRenderer {...requiredProps} chartIsStale={false} />,
);
expect(wrapper.find(SuperChart)).toExist();
});
it('should use latestQueryFormData instead of formData when chartIsStale is true', () => {
const wrapper = shallow(<ChartRenderer {...requiredProps} chartIsStale />);
expect(wrapper.find(SuperChart).prop('formData')).toEqual({
testControl: 'bar',
});
});
test('should render SuperChart', () => {
const { getByTestId } = render(
<ChartRenderer {...requiredProps} chartIsStale={false} />,
);
expect(getByTestId('mock-super-chart')).toBeInTheDocument();
});
test('should use latestQueryFormData instead of formData when chartIsStale is true', () => {
const { getByTestId } = render(
<ChartRenderer {...requiredProps} chartIsStale />,
);
expect(getByTestId('mock-super-chart')).toHaveTextContent(
JSON.stringify({ testControl: 'bar' }),
);
});

View File

@ -16,62 +16,46 @@
* specific language governing permissions and limitations
* under the License.
*/
import { fireEvent, render } from 'spec/helpers/testing-library';
import { isValidElement } from 'react';
import { ReactWrapper } from 'enzyme';
import {
styledMount as mount,
styledShallow as shallow,
} from 'spec/helpers/theming';
import Checkbox from 'src/components/Checkbox';
import Checkbox, {
CheckboxChecked,
CheckboxUnchecked,
} from 'src/components/Checkbox';
jest.mock('src/components/Checkbox/CheckboxIcons', () => ({
CheckboxChecked: () => <div data-test="mock-CheckboxChecked" />,
CheckboxUnchecked: () => <div data-test="mock-CheckboxUnchecked" />,
}));
describe('Checkbox', () => {
let wrapper: ReactWrapper;
it('renders the base component', () => {
expect(
isValidElement(
<Checkbox style={{}} checked={false} onChange={() => true} />,
),
).toBe(true);
});
describe('when unchecked', () => {
it('renders the unchecked component', () => {
const shallowWrapper = shallow(
<Checkbox style={{}} checked={false} onChange={() => true} />,
);
expect(shallowWrapper.dive().find(CheckboxUnchecked)).toExist();
});
});
describe('when checked', () => {
it('renders the checked component', () => {
const shallowWrapper = shallow(
<Checkbox style={{}} checked onChange={() => true} />,
);
expect(shallowWrapper.dive().find(CheckboxChecked)).toExist();
});
});
it('works with an onChange handler', () => {
const mockAction = jest.fn();
wrapper = mount(
<Checkbox style={{}} checked={false} onChange={mockAction} />,
describe('when unchecked', () => {
test('renders the unchecked component', () => {
const { getByTestId } = render(
<Checkbox style={{}} checked={false} onChange={() => true} />,
);
wrapper.find('Checkbox').first().simulate('click');
expect(mockAction).toHaveBeenCalled();
});
it('renders custom Checkbox styles without melting', () => {
wrapper = mount(
<Checkbox onChange={() => true} checked={false} style={{ opacity: 1 }} />,
);
expect(wrapper.find('Checkbox')).toExist();
expect(wrapper.find('Checkbox')).toHaveStyle({ opacity: 1 });
expect(getByTestId('mock-CheckboxUnchecked')).toBeInTheDocument();
});
});
describe('when checked', () => {
test('renders the checked component', () => {
const { getByTestId } = render(
<Checkbox style={{}} checked onChange={() => true} />,
);
expect(getByTestId('mock-CheckboxChecked')).toBeInTheDocument();
});
});
test('works with an onChange handler', () => {
const mockAction = jest.fn();
const { getByRole } = render(
<Checkbox style={{}} checked={false} onChange={mockAction} />,
);
fireEvent.click(getByRole('checkbox'));
expect(mockAction).toHaveBeenCalled();
});
test('renders custom Checkbox styles without melting', () => {
const { getByRole } = render(
<Checkbox onChange={() => true} checked={false} style={{ opacity: 1 }} />,
);
expect(getByRole('checkbox')).toBeInTheDocument();
expect(getByRole('checkbox')).toHaveStyle({ opacity: 1 });
});

View File

@ -16,48 +16,51 @@
* specific language governing permissions and limitations
* under the License.
*/
import { mount } from 'enzyme';
import { fireEvent, render, waitFor } from 'spec/helpers/testing-library';
import Button from 'src/components/Button';
import { act } from 'react-dom/test-utils';
import { supersetTheme, ThemeProvider } from '@superset-ui/core';
import ConfirmStatusChange from 'src/components/ConfirmStatusChange';
import Modal from 'src/components/Modal';
describe('ConfirmStatusChange', () => {
const mockedProps = {
title: 'please confirm',
description: 'are you sure?',
onConfirm: jest.fn(),
};
const wrapper = mount(
const mockedProps = {
title: 'please confirm',
description: 'are you sure?',
onConfirm: jest.fn(),
};
test('opens a confirm modal', () => {
const { getByTestId } = render(
<ConfirmStatusChange {...mockedProps}>
{confirm => (
<>
<Button id="btn1" onClick={confirm} />
<Button data-test="btn1" onClick={confirm} />
</>
)}
</ConfirmStatusChange>,
{
wrappingComponent: ThemeProvider,
wrappingComponentProps: { theme: supersetTheme },
},
);
it('opens a confirm modal', () => {
act(() => {
wrapper.find('#btn1').first().props().onClick('foo');
});
fireEvent.click(getByTestId('btn1'));
wrapper.update();
expect(wrapper.find(Modal)).toExist();
});
it('calls the function on confirm', () => {
act(() => {
wrapper.find(Button).last().props().onClick();
});
expect(mockedProps.onConfirm).toHaveBeenCalledWith('foo');
});
expect(getByTestId(`${mockedProps.title}-modal`)).toBeInTheDocument();
});
test('calls the function on confirm', async () => {
const { getByTestId, getByRole } = render(
<ConfirmStatusChange {...mockedProps}>
{confirm => (
<>
<Button data-test="btn1" onClick={() => confirm('foo')} />
</>
)}
</ConfirmStatusChange>,
);
fireEvent.click(getByTestId('btn1'));
const confirmInput = getByTestId('delete-modal-input');
fireEvent.change(confirmInput, { target: { value: 'DELETE' } });
const confirmButton = getByRole('button', { name: 'delete' });
fireEvent.click(confirmButton);
await waitFor(() => expect(mockedProps.onConfirm).toHaveBeenCalledTimes(1));
expect(mockedProps.onConfirm).toHaveBeenCalledWith('foo');
});

View File

@ -16,16 +16,12 @@
* specific language governing permissions and limitations
* under the License.
*/
import { mount } from 'enzyme';
import { waitFor, render, fireEvent } from 'spec/helpers/testing-library';
import configureStore from 'redux-mock-store';
import fetchMock from 'fetch-mock';
import thunk from 'redux-thunk';
import { act } from 'react-dom/test-utils';
import sinon from 'sinon';
import { supersetTheme, ThemeProvider } from '@superset-ui/core';
import Modal from 'src/components/Modal';
import { ChangeDatasourceModal } from 'src/components/Datasource';
import waitForComponentToPaint from 'spec/helpers/waitForComponentToPaint';
import mockDatasource from 'spec/fixtures/mockDatasource';
const mockStore = configureStore([thunk]);
@ -57,60 +53,40 @@ fetchMock.get(DATASOURCES_ENDPOINT, { result: [mockDatasource['7__table']] });
fetchMock.get(DATASOURCE_ENDPOINT, DATASOURCE_PAYLOAD);
fetchMock.get(INFO_ENDPOINT, {});
async function mountAndWait(props = mockedProps) {
const mounted = mount(<ChangeDatasourceModal store={store} {...props} />, {
wrappingComponent: ThemeProvider,
wrappingComponentProps: { theme: supersetTheme },
});
await waitForComponentToPaint(mounted);
return mounted;
}
describe('ChangeDatasourceModal', () => {
let wrapper;
beforeEach(async () => {
wrapper = await mountAndWait();
});
it('renders', () => {
expect(wrapper.find(ChangeDatasourceModal)).toHaveLength(1);
});
it('renders a Modal', () => {
expect(wrapper.find(Modal)).toExist();
});
it('fetches datasources', async () => {
expect(fetchMock.calls(INFO_ENDPOINT)).toHaveLength(3);
});
it('renders confirmation message', async () => {
await waitForComponentToPaint(wrapper, 1000);
act(() => {
wrapper.find('[data-test="datasource-link"]').at(0).props().onClick();
});
await waitForComponentToPaint(wrapper);
expect(wrapper.find('.proceed-btn')).toExist();
});
it('changes the datasource', async () => {
await waitForComponentToPaint(wrapper, 1000);
act(() => {
wrapper.find('[data-test="datasource-link"]').at(0).props().onClick();
});
await waitForComponentToPaint(wrapper);
act(() => {
wrapper.find('.proceed-btn').at(0).props().onClick(datasourceData);
});
await waitForComponentToPaint(wrapper);
expect(fetchMock.calls(/api\/v1\/dataset\/7/)).toHaveLength(1);
});
afterEach(() => {
fetchMock.resetHistory();
});
const setup = (props = mockedProps) =>
render(<ChangeDatasourceModal {...props} />, {
useRedux: true,
store,
});
test('renders', () => {
const { getByTestId } = setup();
expect(getByTestId('Swap dataset-modal')).toBeInTheDocument();
});
test('fetches datasources', async () => {
setup();
await waitFor(() => expect(fetchMock.calls(INFO_ENDPOINT)).toHaveLength(1));
});
test('renders confirmation message', async () => {
const { findByTestId, getByRole } = setup();
const confirmLink = await findByTestId('datasource-link');
fireEvent.click(confirmLink);
expect(getByRole('button', { name: 'Proceed' })).toBeInTheDocument();
});
test('changes the datasource', async () => {
const { findByTestId, getByRole } = setup();
const confirmLink = await findByTestId('datasource-link');
fireEvent.click(confirmLink);
const proceedButton = getByRole('button', { name: 'Proceed' });
fireEvent.click(proceedButton);
await waitFor(() =>
expect(fetchMock.calls(/api\/v1\/dataset\/7/)).toHaveLength(1),
);
});

View File

@ -16,8 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
import { isValidElement } from 'react';
import { shallow } from 'enzyme';
import { render } from 'spec/helpers/testing-library';
import mockDatasource from 'spec/fixtures/mockDatasource';
import CollectionTable from './CollectionTable';
@ -27,22 +26,13 @@ const props = {
tableColumns: ['column_name', 'type', 'groupby'],
};
describe('CollectionTable', () => {
let wrapper;
let el;
beforeEach(() => {
el = <CollectionTable {...props} />;
wrapper = shallow(el);
});
it('is valid', () => {
expect(isValidElement(el)).toBe(true);
});
it('renders a table', () => {
const { length } = mockDatasource['7__table'].columns;
expect(wrapper.find('table')).toExist();
expect(wrapper.find('tbody tr.row')).toHaveLength(length);
});
test('renders a table', () => {
const { length } = mockDatasource['7__table'].columns;
const { getByRole } = render(<CollectionTable {...props} />);
expect(getByRole('table')).toBeInTheDocument();
expect(
getByRole('table')
.getElementsByTagName('tbody')[0]
.getElementsByClassName('row'),
).toHaveLength(length);
});

View File

@ -16,40 +16,34 @@
* specific language governing permissions and limitations
* under the License.
*/
import { fireEvent, render, screen } from 'spec/helpers/testing-library';
import { render, screen } from 'spec/helpers/testing-library';
import { shallow } from 'enzyme';
import TextAreaControl from 'src/explore/components/controls/TextAreaControl';
import Field from './Field';
describe('Field', () => {
const defaultProps = {
fieldKey: 'mock',
value: '',
label: 'mock',
description: 'description',
control: <TextAreaControl />,
onChange: jest.fn(),
compact: false,
inline: false,
};
const defaultProps = {
fieldKey: 'mock',
value: '',
label: 'mock',
description: 'description',
control: <input type="text" data-test="mock-text-control" />,
onChange: jest.fn(),
compact: false,
inline: false,
};
it('should render', () => {
const { container } = render(<Field {...defaultProps} />);
expect(container).toBeInTheDocument();
});
it('should call onChange', () => {
const wrapper = shallow(<Field {...defaultProps} />);
const textArea = wrapper.find(TextAreaControl);
textArea.simulate('change', { target: { value: 'x' } });
expect(defaultProps.onChange).toHaveBeenCalled();
});
it('should render compact', () => {
render(<Field {...defaultProps} compact />);
expect(
screen.queryByText(defaultProps.description),
).not.toBeInTheDocument();
});
test('should render', () => {
const { container } = render(<Field {...defaultProps} />);
expect(container).toBeInTheDocument();
});
test('should call onChange', () => {
const { getByTestId } = render(<Field {...defaultProps} />);
const textArea = getByTestId('mock-text-control');
fireEvent.change(textArea, { target: { value: 'x' } });
expect(defaultProps.onChange).toHaveBeenCalled();
});
test('should render compact', () => {
render(<Field {...defaultProps} compact />);
expect(screen.queryByText(defaultProps.description)).not.toBeInTheDocument();
});

View File

@ -16,88 +16,98 @@
* specific language governing permissions and limitations
* under the License.
*/
import { isValidElement } from 'react';
import { shallow } from 'enzyme';
import sinon from 'sinon';
import { fireEvent, getByRole, render } from 'spec/helpers/testing-library';
import EditableTable from 'src/components/EditableTitle';
describe('EditableTitle', () => {
const callback = sinon.spy();
const mockProps = {
title: 'my title',
canEdit: true,
onSaveTitle: callback,
};
const mockEvent = {
target: {
value: 'new title',
},
};
let editableWrapper = shallow(<EditableTable {...mockProps} />);
const notEditableWrapper = shallow(
<EditableTable title="my title" onSaveTitle={callback} />,
const mockEvent = {
target: {
value: 'new title',
},
};
const mockProps = {
title: 'my title',
canEdit: true,
onSaveTitle: jest.fn(),
};
test('should render title', () => {
const { getByRole } = render(<EditableTable {...mockProps} />);
expect(getByRole('button')).toBeInTheDocument();
expect(getByRole('button')).toHaveValue(mockProps.title);
});
test('should not render an input if it is not editable', () => {
const { queryByRole } = render(
<EditableTable title="my title" onSaveTitle={jest.fn()} />,
);
it('is valid', () => {
expect(isValidElement(<EditableTable {...mockProps} />)).toBe(true);
});
it('should render title', () => {
const titleElement = editableWrapper.find('input');
expect(titleElement.props().value).toBe('my title');
expect(titleElement.props().type).toBe('button');
});
it('should not render an input if it is not editable', () => {
expect(notEditableWrapper.find('input')).not.toExist();
});
expect(queryByRole('button')).not.toBeInTheDocument();
});
describe('should handle click', () => {
it('should change title', () => {
editableWrapper.find('input').simulate('click');
expect(editableWrapper.find('input').props().type).toBe('text');
});
});
describe('should handle change', () => {
afterEach(() => {
editableWrapper = shallow(<EditableTable {...mockProps} />);
});
it('should change title', () => {
editableWrapper.find('input').simulate('change', mockEvent);
expect(editableWrapper.find('input').props().value).toBe('new title');
});
});
describe('should handle blur', () => {
beforeEach(() => {
editableWrapper.find('input').simulate('click');
});
afterEach(() => {
callback.resetHistory();
editableWrapper = shallow(<EditableTable {...mockProps} />);
});
it('default input type should be text', () => {
expect(editableWrapper.find('input').props().type).toBe('text');
});
it('should trigger callback', () => {
editableWrapper.find('input').simulate('change', mockEvent);
editableWrapper.find('input').simulate('blur');
expect(editableWrapper.find('input').props().type).toBe('button');
expect(callback.callCount).toBe(1);
expect(callback.getCall(0).args[0]).toBe('new title');
});
it('should not trigger callback', () => {
editableWrapper.find('input').simulate('blur');
expect(editableWrapper.find('input').props().type).toBe('button');
// no change
expect(callback.callCount).toBe(0);
});
it('should not save empty title', () => {
editableWrapper.find('input').simulate('blur');
expect(editableWrapper.find('input').props().type).toBe('button');
expect(editableWrapper.find('input').props().value).toBe('my title');
expect(callback.callCount).toBe(0);
});
describe('should handle click', () => {
test('should change title', () => {
const { getByRole, container } = render(<EditableTable {...mockProps} />);
fireEvent.click(getByRole('button'));
expect(container.querySelector('input')?.getAttribute('type')).toEqual(
'text',
);
});
});
describe('should handle change', () => {
test('should change title', () => {
const { getByTestId, container } = render(
<EditableTable {...mockProps} editing />,
);
fireEvent.change(getByTestId('editable-title-input'), mockEvent);
expect(container.querySelector('input')).toHaveValue('new title');
});
});
describe('should handle blur', () => {
const setup = (overrides: Partial<typeof mockProps> = {}) => {
const selectors = render(<EditableTable {...mockProps} {...overrides} />);
fireEvent.click(selectors.getByRole('button'));
return selectors;
};
test('default input type should be text', () => {
const { container } = setup();
expect(container.querySelector('input')?.getAttribute('type')).toEqual(
'text',
);
});
test('should trigger callback', () => {
const callback = jest.fn();
const { getByTestId, container } = setup({ onSaveTitle: callback });
fireEvent.change(getByTestId('editable-title-input'), mockEvent);
fireEvent.blur(getByTestId('editable-title-input'));
expect(callback).toHaveBeenCalledTimes(1);
expect(callback).toHaveBeenCalledWith('new title');
expect(container.querySelector('input')?.getAttribute('type')).toEqual(
'button',
);
});
test('should not trigger callback', () => {
const callback = jest.fn();
const { getByTestId, container } = setup({ onSaveTitle: callback });
fireEvent.blur(getByTestId('editable-title-input'));
expect(container.querySelector('input')?.getAttribute('type')).toEqual(
'button',
);
// no change
expect(callback).not.toHaveBeenCalled();
});
test('should not save empty title', () => {
const callback = jest.fn();
const { getByTestId, container } = setup({ onSaveTitle: callback });
fireEvent.blur(getByTestId('editable-title-input'));
expect(container.querySelector('input')?.getAttribute('type')).toEqual(
'button',
);
expect(getByRole(container, 'button')).toHaveValue(mockProps.title);
expect(callback).not.toHaveBeenCalled();
});
});

View File

@ -16,42 +16,44 @@
* specific language governing permissions and limitations
* under the License.
*/
import { shallow } from 'enzyme';
import { render } from 'spec/helpers/testing-library';
import { InfoTooltipWithTrigger } from '@superset-ui/chart-controls';
import { Row, Col } from 'src/components';
import TextControl from 'src/explore/components/controls/TextControl';
import FormRow from 'src/components/FormRow';
jest.mock('@superset-ui/chart-controls', () => ({
...jest.requireActual('@superset-ui/chart-controls'),
InfoTooltipWithTrigger: () => <div data-test="mock-info-tooltip" />,
}));
jest.mock('src/components', () => ({
...jest.requireActual('src/components'),
Row: ({ children }) => <div data-test="mock-row">{children}</div>,
Col: ({ children }) => <div data-test="mock-col">{children}</div>,
}));
const defaultProps = {
label: 'Hello',
tooltip: 'A tooltip',
control: <TextControl label="test_cbox" />,
};
describe('FormRow', () => {
let wrapper;
const getWrapper = (overrideProps = {}) => {
const props = {
...defaultProps,
...overrideProps,
};
return shallow(<FormRow {...props} />);
const setup = (overrideProps = {}) => {
const props = {
...defaultProps,
...overrideProps,
};
return render(<FormRow {...props} />);
};
beforeEach(() => {
wrapper = getWrapper();
});
it('renders an InfoTooltipWithTrigger only if needed', () => {
expect(wrapper.find(InfoTooltipWithTrigger)).toExist();
wrapper = getWrapper({ tooltip: null });
expect(wrapper.find(InfoTooltipWithTrigger)).not.toExist();
});
it('renders a Row and 2 Cols', () => {
expect(wrapper.find(Row)).toExist();
expect(wrapper.find(Col)).toHaveLength(2);
});
test('renders an InfoTooltipWithTrigger only if needed', () => {
const { getByTestId, queryByTestId, rerender } = setup();
expect(getByTestId('mock-info-tooltip')).toBeInTheDocument();
rerender(<FormRow {...defaultProps} tooltip={null} />);
expect(queryByTestId('mock-info-tooltip')).not.toBeInTheDocument();
});
test('renders a Row and 2 Cols', () => {
const { getByTestId, getAllByTestId } = setup();
expect(getByTestId('mock-row')).toBeInTheDocument();
expect(getAllByTestId('mock-col')).toHaveLength(2);
});

View File

@ -16,25 +16,29 @@
* specific language governing permissions and limitations
* under the License.
*/
import { isValidElement } from 'react';
import { shallow } from 'enzyme';
import { Tooltip } from 'src/components/Tooltip';
import { render } from 'spec/helpers/testing-library';
import { IconTooltip } from 'src/components/IconTooltip';
describe('IconTooltip', () => {
const mockedProps = {
tooltip: 'This is a tooltip',
};
it('renders', () => {
expect(isValidElement(<IconTooltip>TEST</IconTooltip>)).toBe(true);
});
it('renders with props', () => {
expect(
isValidElement(<IconTooltip {...mockedProps}>TEST</IconTooltip>),
).toBe(true);
});
it('renders a tooltip', () => {
const wrapper = shallow(<IconTooltip {...mockedProps}>TEST</IconTooltip>);
expect(wrapper.find(Tooltip)).toExist();
});
jest.mock('src/components/Tooltip', () => ({
Tooltip: () => <div data-test="mock-tooltip" />,
}));
const mockedProps = {
tooltip: 'This is a tooltip',
};
test('renders', () => {
const { container } = render(<IconTooltip>TEST</IconTooltip>);
expect(container).toBeInTheDocument();
});
test('renders with props', () => {
const { container } = render(
<IconTooltip {...mockedProps}>TEST</IconTooltip>,
);
expect(container).toBeInTheDocument();
});
test('renders a tooltip', () => {
const { getByTestId } = render(
<IconTooltip {...mockedProps}>TEST</IconTooltip>,
);
expect(getByTestId('mock-tooltip')).toBeInTheDocument();
});

View File

@ -16,18 +16,14 @@
* specific language governing permissions and limitations
* under the License.
*/
import { act } from 'react-dom/test-utils';
import thunk from 'redux-thunk';
import configureStore from 'redux-mock-store';
import { styledMount as mount } from 'spec/helpers/theming';
import { ReactWrapper } from 'enzyme';
import { fireEvent, render, waitFor } from 'spec/helpers/testing-library';
import fetchMock from 'fetch-mock';
import waitForComponentToPaint from 'spec/helpers/waitForComponentToPaint';
import { Upload } from 'src/components';
import Button from 'src/components/Button';
import { ImportResourceName } from 'src/views/CRUD/types';
import ImportModelsModal from 'src/components/ImportModal';
import Modal from 'src/components/Modal';
import ImportModelsModal, {
ImportModelsModalProps,
} from 'src/components/ImportModal';
const mockStore = configureStore([thunk]);
const store = mockStore({});
@ -48,148 +44,98 @@ const requiredProps = {
onHide: () => {},
};
describe('ImportModelsModal', () => {
let wrapper: ReactWrapper;
afterEach(() => {
jest.clearAllMocks();
});
beforeEach(() => {
wrapper = mount(<ImportModelsModal {...requiredProps} />, {
context: { store },
});
});
const setup = (overrides: Partial<ImportModelsModalProps> = {}) =>
render(<ImportModelsModal {...requiredProps} {...overrides} />, { store });
afterEach(() => {
jest.clearAllMocks();
});
test('renders', () => {
const { container } = setup();
expect(container).toBeInTheDocument();
});
it('renders', () => {
expect(wrapper.find(ImportModelsModal)).toExist();
});
test('renders a Modal', () => {
const { getByTestId } = setup();
expect(getByTestId('model-modal')).toBeInTheDocument();
});
it('renders a Modal', () => {
expect(wrapper.find(Modal)).toExist();
});
test('renders "Import database" header', () => {
const { getByText } = setup();
expect(getByText('Import database')).toBeInTheDocument();
});
it('renders "Import database" header', () => {
expect(wrapper.find('h4').text()).toEqual('Import database');
});
test('renders a file input field', () => {
setup();
expect(document.querySelector('input[type="file"]')).toBeInTheDocument();
});
it('renders a file input field', () => {
expect(wrapper.find('input[type="file"]')).toExist();
});
test('should render the close, file, import and cancel buttons', () => {
setup();
expect(document.querySelectorAll('button')).toHaveLength(4);
});
it('should render the close, file, import and cancel buttons', () => {
expect(wrapper.find('button')).toHaveLength(4);
});
test('should render the import button initially disabled', () => {
const { getByRole } = setup();
expect(getByRole('button', { name: 'Import' })).toBeDisabled();
});
it('should render the import button initially disabled', () => {
expect(wrapper.find(Button).at(2).prop('disabled')).toBe(true);
});
it('should render the import button enabled when a file is selected', () => {
const file = new File([new ArrayBuffer(1)], 'model_export.zip');
act(() => {
const handler = wrapper.find(Upload).prop('onChange');
if (handler) {
handler({
fileList: [],
file: {
name: 'model_export.zip',
originFileObj: file,
uid: '-1',
size: 0,
type: 'zip',
},
});
}
});
wrapper.update();
expect(wrapper.find(Button).at(2).prop('disabled')).toBe(false);
});
it('should POST with request header `Accept: application/json`', async () => {
const file = new File([new ArrayBuffer(1)], 'model_export.zip');
act(() => {
const handler = wrapper.find(Upload).prop('onChange');
if (handler) {
handler({
fileList: [],
file: {
name: 'model_export.zip',
originFileObj: file,
uid: '-1',
size: 0,
type: 'zip',
},
});
}
});
wrapper.update();
wrapper.find(Button).at(2).simulate('click');
await waitForComponentToPaint(wrapper);
expect(fetchMock.calls(DATABASE_IMPORT_URL)[0][1]?.headers).toStrictEqual({
Accept: 'application/json',
'X-CSRFToken': '1234',
});
});
it('should render password fields when needed for import', () => {
const wrapperWithPasswords = mount(
<ImportModelsModal
{...requiredProps}
passwordFields={['databases/examples.yaml']}
/>,
{
context: { store },
test('should render the import button enabled when a file is selected', async () => {
const file = new File([new ArrayBuffer(1)], 'model_export.zip');
const { getByTestId, getByRole } = setup();
await waitFor(() =>
fireEvent.change(getByTestId('model-file-input'), {
target: {
files: [file],
},
);
expect(wrapperWithPasswords.find('input[type="password"]')).toExist();
});
}),
);
expect(getByRole('button', { name: 'Import' })).toBeEnabled();
});
it('should render ssh_tunnel password fields when needed for import', () => {
const wrapperWithPasswords = mount(
<ImportModelsModal
{...requiredProps}
sshTunnelPasswordFields={['databases/examples.yaml']}
/>,
{
context: { store },
test('should POST with request header `Accept: application/json`', async () => {
const file = new File([new ArrayBuffer(1)], 'model_export.zip');
const { getByTestId, getByRole } = setup();
await waitFor(() =>
fireEvent.change(getByTestId('model-file-input'), {
target: {
files: [file],
},
);
expect(
wrapperWithPasswords.find('[data-test="ssh_tunnel_password"]'),
).toExist();
});
it('should render ssh_tunnel private_key fields when needed for import', () => {
const wrapperWithPasswords = mount(
<ImportModelsModal
{...requiredProps}
sshTunnelPrivateKeyFields={['databases/examples.yaml']}
/>,
{
context: { store },
},
);
expect(
wrapperWithPasswords.find('[data-test="ssh_tunnel_private_key"]'),
).toExist();
});
it('should render ssh_tunnel private_key_password fields when needed for import', () => {
const wrapperWithPasswords = mount(
<ImportModelsModal
{...requiredProps}
sshTunnelPrivateKeyPasswordFields={['databases/examples.yaml']}
/>,
{
context: { store },
},
);
expect(
wrapperWithPasswords.find(
'[data-test="ssh_tunnel_private_key_password"]',
),
).toExist();
}),
);
fireEvent.click(getByRole('button', { name: 'Import' }));
await waitFor(() =>
expect(fetchMock.calls(DATABASE_IMPORT_URL)).toHaveLength(1),
);
expect(fetchMock.calls(DATABASE_IMPORT_URL)[0][1]?.headers).toStrictEqual({
Accept: 'application/json',
'X-CSRFToken': '1234',
});
});
test('should render password fields when needed for import', () => {
setup({ passwordFields: ['databases/examples.yaml'] });
expect(document.querySelector('input[type="password"]')).toBeInTheDocument();
});
test('should render ssh_tunnel password fields when needed for import', () => {
const { getByTestId } = setup({
sshTunnelPasswordFields: ['databases/examples.yaml'],
});
expect(getByTestId('ssh_tunnel_password')).toBeInTheDocument();
});
test('should render ssh_tunnel private_key fields when needed for import', () => {
const { getByTestId } = setup({
sshTunnelPrivateKeyFields: ['databases/examples.yaml'],
});
expect(getByTestId('ssh_tunnel_private_key')).toBeInTheDocument();
});
test('should render ssh_tunnel private_key_password fields when needed for import', () => {
const { getByTestId } = setup({
sshTunnelPrivateKeyPasswordFields: ['databases/examples.yaml'],
});
expect(getByTestId('ssh_tunnel_private_key_password')).toBeInTheDocument();
});

View File

@ -16,42 +16,28 @@
* specific language governing permissions and limitations
* under the License.
*/
import { fireEvent, render } from 'spec/helpers/testing-library';
import { isValidElement } from 'react';
import { ReactWrapper } from 'enzyme';
import { styledMount as mount } from 'spec/helpers/theming';
import Label from '.';
import { LabelGallery, options } from './Label.stories';
describe('Label', () => {
let wrapper: ReactWrapper;
// test the basic component
it('renders the base component (no onClick)', () => {
expect(isValidElement(<Label />)).toBe(true);
});
it('renders with role=undefined when onClick is not present', () => {
wrapper = mount(<Label />);
expect(wrapper.find('span').prop('role')).toBeUndefined();
});
it('renders with role="button" when onClick is present', () => {
const mockAction = jest.fn();
wrapper = mount(<Label onClick={mockAction} />);
expect(wrapper.find('span').prop('role')).toBe('button');
});
it('works with an onClick handler', () => {
const mockAction = jest.fn();
wrapper = mount(<Label onClick={mockAction} />);
wrapper.find(Label).simulate('click');
expect(mockAction).toHaveBeenCalled();
});
// test stories from the storybook!
it('renders all the storybook gallery variants', () => {
wrapper = mount(<LabelGallery />);
expect(wrapper.find(Label).length).toEqual(options.length * 2);
});
// test the basic component
test('renders the base component (no onClick)', () => {
const { container } = render(<Label />);
expect(container).toBeInTheDocument();
});
test('works with an onClick handler', () => {
const mockAction = jest.fn();
const { getByText } = render(<Label onClick={mockAction}>test</Label>);
fireEvent.click(getByText('test'));
expect(mockAction).toHaveBeenCalled();
});
// test stories from the storybook!
test('renders all the storybook gallery variants', () => {
const { container } = render(<LabelGallery />);
expect(container.querySelectorAll('.ant-tag')).toHaveLength(
options.length * 2,
);
});

View File

@ -16,30 +16,22 @@
* specific language governing permissions and limitations
* under the License.
*/
import { fireEvent, render } from 'spec/helpers/testing-library';
import { MouseEvent } from 'react';
import { ReactWrapper } from 'enzyme';
import { styledMount as mount } from 'spec/helpers/theming';
import waitForComponentToPaint from 'spec/helpers/waitForComponentToPaint';
import LastUpdated from '.';
describe('LastUpdated', () => {
let wrapper: ReactWrapper;
const updatedAt = new Date('Sat Dec 12 2020 00:00:00 GMT-0800');
const updatedAt = new Date('Sat Dec 12 2020 00:00:00 GMT-0800');
it('renders the base component (no refresh)', () => {
const wrapper = mount(<LastUpdated updatedAt={updatedAt} />);
expect(/^Last Updated .+$/.test(wrapper.text())).toBe(true);
});
it('renders a refresh action', async () => {
const mockAction = jest.fn();
wrapper = mount(<LastUpdated updatedAt={updatedAt} update={mockAction} />);
await waitForComponentToPaint(wrapper);
const props = wrapper.find('[aria-label="refresh"]').first().props();
if (props.onClick) {
props.onClick({} as MouseEvent);
}
expect(mockAction).toHaveBeenCalled();
});
test('renders the base component (no refresh)', () => {
const { getByText } = render(<LastUpdated updatedAt={updatedAt} />);
expect(getByText(/^Last Updated .+$/)).toBeInTheDocument();
});
test('renders a refresh action', async () => {
const mockAction = jest.fn();
const { getByLabelText } = render(
<LastUpdated updatedAt={updatedAt} update={mockAction} />,
);
fireEvent.click(getByLabelText('refresh'));
expect(mockAction).toHaveBeenCalled();
});

View File

@ -16,10 +16,8 @@
* specific language governing permissions and limitations
* under the License.
*/
import { mount } from 'enzyme';
import { ThemeProvider, supersetTheme } from '@superset-ui/core';
import { fireEvent, render, waitFor } from 'spec/helpers/testing-library';
import Toast from 'src/components/MessageToasts/Toast';
import { act } from 'react-dom/test-utils';
import mockMessageToasts from './mockMessageToasts';
const props = {
@ -27,35 +25,22 @@ const props = {
onCloseToast() {},
};
const setup = overrideProps =>
mount(<Toast {...props} {...overrideProps} />, {
wrappingComponent: ThemeProvider,
wrappingComponentProps: { theme: supersetTheme },
});
const setup = overrideProps => render(<Toast {...props} {...overrideProps} />);
describe('Toast', () => {
it('should render', () => {
const wrapper = setup();
expect(wrapper.find('[data-test="toast-container"]')).toExist();
});
it('should render toastText within the div', () => {
const wrapper = setup();
const container = wrapper.find('[data-test="toast-container"]');
expect(container.hostNodes().childAt(1).text()).toBe(props.toast.text);
});
it('should call onCloseToast upon toast dismissal', async () =>
act(
() =>
new Promise(done => {
const onCloseToast = id => {
expect(id).toBe(props.toast.id);
done();
};
const wrapper = setup({ onCloseToast });
wrapper.find('[data-test="close-button"]').props().onClick();
}),
));
test('should render', () => {
const { getByTestId } = setup();
expect(getByTestId('toast-container')).toBeInTheDocument();
});
test('should render toastText within the div', () => {
const { getByTestId } = setup();
expect(getByTestId('toast-container')).toHaveTextContent(props.toast.text);
});
test('should call onCloseToast upon toast dismissal', async () => {
const onCloseToast = jest.fn();
const { getByTestId } = setup({ onCloseToast });
fireEvent.click(getByTestId('close-button'));
await waitFor(() => expect(onCloseToast).toHaveBeenCalledTimes(1));
expect(onCloseToast).toHaveBeenCalledWith(props.toast.id);
});

View File

@ -16,35 +16,33 @@
* specific language governing permissions and limitations
* under the License.
*/
import { shallow } from 'enzyme';
import Toast from 'src/components/MessageToasts/Toast';
import { fireEvent, render, waitFor } from 'spec/helpers/testing-library';
import ToastPresenter from 'src/components/MessageToasts/ToastPresenter';
import mockMessageToasts from './mockMessageToasts';
describe('ToastPresenter', () => {
const props = {
toasts: mockMessageToasts,
removeToast() {},
};
const props = {
toasts: mockMessageToasts,
removeToast() {},
};
function setup(overrideProps) {
const wrapper = shallow(<ToastPresenter {...props} {...overrideProps} />);
return wrapper;
}
function setup(overrideProps) {
return render(<ToastPresenter {...props} {...overrideProps} />);
}
it('should render a div with id toast-presenter', () => {
const wrapper = setup();
expect(wrapper.find('#toast-presenter')).toExist();
});
it('should render a Toast for each toast object', () => {
const wrapper = setup();
expect(wrapper.find(Toast)).toHaveLength(props.toasts.length);
});
it('should pass removeToast to the Toast component', () => {
const removeToast = () => {};
const wrapper = setup({ removeToast });
expect(wrapper.find(Toast).first().prop('onCloseToast')).toBe(removeToast);
});
test('should render a div with id toast-presenter', () => {
const { container } = setup();
expect(container.querySelector('#toast-presenter')).toBeInTheDocument();
});
test('should render a Toast for each toast object', () => {
const { getAllByRole } = setup();
expect(getAllByRole('alert')).toHaveLength(props.toasts.length);
});
test('should pass removeToast to the Toast component', async () => {
const removeToast = jest.fn();
const { getAllByTestId } = setup({ removeToast });
fireEvent.click(getAllByTestId('close-button')[0]);
await waitFor(() => expect(removeToast).toHaveBeenCalledTimes(1));
});