From 672611eac3afe5b2264aaaa0c2664dbe29ccc1c6 Mon Sep 17 00:00:00 2001 From: geido Date: Thu, 25 Mar 2021 20:26:09 +0200 Subject: [PATCH 1/2] Refactor and enhance tests --- .../components/DatasourcePanel_spec.jsx | 127 --------------- .../DatasourcePanel/DatasourcePanel.test.tsx | 152 ++++++++++++++++++ .../components/DatasourcePanel/index.ts | 19 --- .../{DatasourcePanel.tsx => index.tsx} | 2 +- 4 files changed, 153 insertions(+), 147 deletions(-) delete mode 100644 superset-frontend/spec/javascripts/explore/components/DatasourcePanel_spec.jsx create mode 100644 superset-frontend/src/explore/components/DatasourcePanel/DatasourcePanel.test.tsx delete mode 100644 superset-frontend/src/explore/components/DatasourcePanel/index.ts rename superset-frontend/src/explore/components/DatasourcePanel/{DatasourcePanel.tsx => index.tsx} (99%) diff --git a/superset-frontend/spec/javascripts/explore/components/DatasourcePanel_spec.jsx b/superset-frontend/spec/javascripts/explore/components/DatasourcePanel_spec.jsx deleted file mode 100644 index c0ddb5b3db848..0000000000000 --- a/superset-frontend/spec/javascripts/explore/components/DatasourcePanel_spec.jsx +++ /dev/null @@ -1,127 +0,0 @@ -/** - * 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 { HTML5Backend } from 'react-dnd-html5-backend'; -import { DndProvider } from 'react-dnd'; -import { render, screen } from 'spec/helpers/testing-library'; -import userEvent from '@testing-library/user-event'; -import DatasourcePanel from 'src/explore/components/DatasourcePanel'; -import { columns, metrics } from 'spec/javascripts/datasource/fixtures'; - -describe('datasourcepanel', () => { - const datasource = { - name: 'birth_names', - type: 'table', - uid: '1__table', - id: 1, - columns, - metrics, - database: { - backend: 'mysql', - name: 'main', - }, - }; - const props = { - datasource, - controls: { - datasource: { - validationErrors: null, - mapStateToProps: () => null, - type: 'DatasourceControl', - label: 'hello', - datasource, - }, - }, - actions: {}, - }; - - const setup = props => ( - - - - ); - - function search(value, input) { - userEvent.clear(input); - userEvent.type(input, value); - } - - it('should render', () => { - const { container } = render(setup(props)); - expect(container).toBeVisible(); - }); - - it('should display items in controls', () => { - render(setup(props)); - expect(screen.getByText('birth_names')).toBeTruthy(); - expect(screen.getByText('Columns')).toBeTruthy(); - expect(screen.getByText('Metrics')).toBeTruthy(); - expect(screen.queryByTestId('warning')).not.toBeInTheDocument(); - }); - - it('should render search results', () => { - const { container } = render(setup(props)); - const c = container.getElementsByClassName('option-label'); - - expect(c).toHaveLength(5); - }); - - it('should render 0 search results', () => { - const { container } = render(setup(props)); - const c = container.getElementsByClassName('option-label'); - const searchInput = screen.getByPlaceholderText('Search Metrics & Columns'); - - search('sssssssss', searchInput); - setTimeout(() => { - expect(c).toHaveLength(0); - }, 201); - }); - - it('should render and sort search results', () => { - const { container } = render(setup(props)); - const c = container.getElementsByClassName('option-label'); - const searchInput = screen.getByPlaceholderText('Search Metrics & Columns'); - - search('end', searchInput); - setTimeout(() => { - expect(c).toHaveLength(4); - expect(c[0].value).toBe('metric_end_certified'); - }, 201); - }); - - it('should render a warning', () => { - const deprecatedDatasource = { - ...datasource, - extra: JSON.stringify({ warning_markdown: 'This is a warning.' }), - }; - render( - setup({ - ...props, - datasource: deprecatedDatasource, - controls: { - datasource: { - ...props.controls.datasource, - datasource: deprecatedDatasource, - }, - }, - }), - ); - expect(screen.getByTestId('alert-solid')).toBeTruthy(); - }); -}); diff --git a/superset-frontend/src/explore/components/DatasourcePanel/DatasourcePanel.test.tsx b/superset-frontend/src/explore/components/DatasourcePanel/DatasourcePanel.test.tsx new file mode 100644 index 0000000000000..ddeada5cc0eb4 --- /dev/null +++ b/superset-frontend/src/explore/components/DatasourcePanel/DatasourcePanel.test.tsx @@ -0,0 +1,152 @@ +/** + * 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 { HTML5Backend } from 'react-dnd-html5-backend'; +import { DndProvider } from 'react-dnd'; +import { render, screen, waitFor } from 'spec/helpers/testing-library'; +import userEvent from '@testing-library/user-event'; +import DatasourcePanel, { Props as DatasourcePanelProps } from 'src/explore/components/DatasourcePanel'; +import { columns, metrics } from 'spec/javascripts/datasource/fixtures'; +import { DatasourceType } from '@superset-ui/core/lib/query/types/Datasource'; +import DatasourceControl from 'src/explore/components/controls/DatasourceControl'; +import { FeatureFlag } from 'src/featureFlags'; + +const datasource = { + id: 1, + type: DatasourceType.Table, + name: 'birth_names', + columns, + metrics, + uid: '1__table', + database: { + backend: 'mysql', + name: 'main', + }, + column_format: {"ratio": ".2%"}, + verbose_map: {"__timestamp": "Time"}, + main_dttm_col: "None", + datasource_name: "table1", + description: "desc" + }; + const props = { + datasource, + controls: { + datasource: { + validationErrors: null, + mapStateToProps: () => ({ value: undefined }), + type: DatasourceControl, + label: 'hello', + datasource, + }, + }, + actions: { + setControlValue: () => ({ type: 'type', controlName: 'control', value: 'val', validationErrors: [] }) + }, + }; + + const setup = (props: DatasourcePanelProps) => ( + + + + ); + + function search(value: string, input: HTMLElement) { + userEvent.clear(input); + userEvent.type(input, value); + } + + test('should render', () => { + const { container } = render(setup(props)); + expect(container).toBeVisible(); + }); + + test('should display items in controls', () => { + render(setup(props)); + expect(screen.getByText('birth_names')).toBeInTheDocument(); + expect(screen.getByText('Metrics')).toBeInTheDocument(); + expect(screen.getByText('Columns')).toBeInTheDocument(); + }); + + test('should render the metrics', () => { + render(setup(props)); + const metricsNum = metrics.length; + metrics.forEach(metric => expect(screen.getByText(metric.metric_name)).toBeInTheDocument()); + expect(screen.getByText(`Showing ${metricsNum} of ${metricsNum}`)).toBeInTheDocument(); + }); + + test('should render the columns', () => { + render(setup(props)); + const columnsNum = columns.length; + columns.forEach(col => expect(screen.getByText(col.column_name)).toBeInTheDocument()); + expect(screen.getByText(`Showing ${columnsNum} of ${columnsNum}`)).toBeInTheDocument(); + }); + + test('should render 0 search results', async () => { + render(setup(props)); + const searchInput = screen.getByPlaceholderText('Search Metrics & Columns'); + + search('nothing', searchInput); + expect(await screen.findByText('Showing 0 of 0')).toBeInTheDocument(); + }); + + test('should search and render matching columns', async () => { + render(setup(props)); + const searchInput = screen.getByPlaceholderText('Search Metrics & Columns'); + + search(columns[0].column_name, searchInput); + + await waitFor(() => { + expect(screen.getByText(columns[0].column_name)).toBeInTheDocument(); + expect(screen.queryByText(columns[1].column_name)).not.toBeInTheDocument(); + }) + + }); + + test('should search and render matching metrics', async () => { + render(setup(props)); + const searchInput = screen.getByPlaceholderText('Search Metrics & Columns'); + + search(metrics[0].metric_name, searchInput); + + await waitFor(() => { + expect(screen.getByText(metrics[0].metric_name)).toBeInTheDocument(); + expect(screen.queryByText(metrics[1].metric_name)).not.toBeInTheDocument(); + }) + + }); + + test('should render a warning', () => { + const deprecatedDatasource = { + ...datasource, + extra: JSON.stringify({ warning_markdown: 'This is a warning.' }), + }; + render( + setup({ + ...props, + datasource: deprecatedDatasource, + controls: { + datasource: { + ...props.controls.datasource, + datasource: deprecatedDatasource, + }, + }, + }), + ); + expect(screen.getByTestId('alert-solid')).toBeInTheDocument(); + }); diff --git a/superset-frontend/src/explore/components/DatasourcePanel/index.ts b/superset-frontend/src/explore/components/DatasourcePanel/index.ts deleted file mode 100644 index 82a2573159641..0000000000000 --- a/superset-frontend/src/explore/components/DatasourcePanel/index.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * 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. - */ -export { default } from './DatasourcePanel'; diff --git a/superset-frontend/src/explore/components/DatasourcePanel/DatasourcePanel.tsx b/superset-frontend/src/explore/components/DatasourcePanel/index.tsx similarity index 99% rename from superset-frontend/src/explore/components/DatasourcePanel/DatasourcePanel.tsx rename to superset-frontend/src/explore/components/DatasourcePanel/index.tsx index cd195d63d3f98..475601ac49d7d 100644 --- a/superset-frontend/src/explore/components/DatasourcePanel/DatasourcePanel.tsx +++ b/superset-frontend/src/explore/components/DatasourcePanel/index.tsx @@ -38,7 +38,7 @@ interface DatasourceControl extends ControlConfig { datasource?: DatasourceMeta; } -interface Props { +export interface Props { datasource: DatasourceMeta; controls: { datasource: DatasourceControl; From 9454092a39f8c698803b3d093e6d59556eb67961 Mon Sep 17 00:00:00 2001 From: geido Date: Fri, 26 Mar 2021 21:04:53 +0200 Subject: [PATCH 2/2] Fix linting issues --- .../DatasourcePanel/DatasourcePanel.test.tsx | 232 +++++++++--------- 1 file changed, 122 insertions(+), 110 deletions(-) diff --git a/superset-frontend/src/explore/components/DatasourcePanel/DatasourcePanel.test.tsx b/superset-frontend/src/explore/components/DatasourcePanel/DatasourcePanel.test.tsx index ddeada5cc0eb4..c8f61ad3eb2bd 100644 --- a/superset-frontend/src/explore/components/DatasourcePanel/DatasourcePanel.test.tsx +++ b/superset-frontend/src/explore/components/DatasourcePanel/DatasourcePanel.test.tsx @@ -21,132 +21,144 @@ import { HTML5Backend } from 'react-dnd-html5-backend'; import { DndProvider } from 'react-dnd'; import { render, screen, waitFor } from 'spec/helpers/testing-library'; import userEvent from '@testing-library/user-event'; -import DatasourcePanel, { Props as DatasourcePanelProps } from 'src/explore/components/DatasourcePanel'; +import DatasourcePanel, { + Props as DatasourcePanelProps, +} from 'src/explore/components/DatasourcePanel'; import { columns, metrics } from 'spec/javascripts/datasource/fixtures'; import { DatasourceType } from '@superset-ui/core/lib/query/types/Datasource'; import DatasourceControl from 'src/explore/components/controls/DatasourceControl'; -import { FeatureFlag } from 'src/featureFlags'; const datasource = { - id: 1, - type: DatasourceType.Table, - name: 'birth_names', - columns, - metrics, - uid: '1__table', - database: { - backend: 'mysql', - name: 'main', - }, - column_format: {"ratio": ".2%"}, - verbose_map: {"__timestamp": "Time"}, - main_dttm_col: "None", - datasource_name: "table1", - description: "desc" - }; - const props = { - datasource, - controls: { - datasource: { - validationErrors: null, - mapStateToProps: () => ({ value: undefined }), - type: DatasourceControl, - label: 'hello', - datasource, - }, - }, - actions: { - setControlValue: () => ({ type: 'type', controlName: 'control', value: 'val', validationErrors: [] }) + id: 1, + type: DatasourceType.Table, + name: 'birth_names', + columns, + metrics, + uid: '1__table', + database: { + backend: 'mysql', + name: 'main', + }, + column_format: { ratio: '.2%' }, + verbose_map: { __timestamp: 'Time' }, + main_dttm_col: 'None', + datasource_name: 'table1', + description: 'desc', +}; +const props = { + datasource, + controls: { + datasource: { + validationErrors: null, + mapStateToProps: () => ({ value: undefined }), + type: DatasourceControl, + label: 'hello', + datasource, }, - }; - - const setup = (props: DatasourcePanelProps) => ( - - - + }, + actions: { + setControlValue: () => ({ + type: 'type', + controlName: 'control', + value: 'val', + validationErrors: [], + }), + }, +}; + +const setup = (props: DatasourcePanelProps) => ( + + + +); + +function search(value: string, input: HTMLElement) { + userEvent.clear(input); + userEvent.type(input, value); +} + +test('should render', () => { + const { container } = render(setup(props)); + expect(container).toBeVisible(); +}); + +test('should display items in controls', () => { + render(setup(props)); + expect(screen.getByText('birth_names')).toBeInTheDocument(); + expect(screen.getByText('Metrics')).toBeInTheDocument(); + expect(screen.getByText('Columns')).toBeInTheDocument(); +}); + +test('should render the metrics', () => { + render(setup(props)); + const metricsNum = metrics.length; + metrics.forEach(metric => + expect(screen.getByText(metric.metric_name)).toBeInTheDocument(), ); + expect( + screen.getByText(`Showing ${metricsNum} of ${metricsNum}`), + ).toBeInTheDocument(); +}); + +test('should render the columns', () => { + render(setup(props)); + const columnsNum = columns.length; + columns.forEach(col => + expect(screen.getByText(col.column_name)).toBeInTheDocument(), + ); + expect( + screen.getByText(`Showing ${columnsNum} of ${columnsNum}`), + ).toBeInTheDocument(); +}); - function search(value: string, input: HTMLElement) { - userEvent.clear(input); - userEvent.type(input, value); - } - - test('should render', () => { - const { container } = render(setup(props)); - expect(container).toBeVisible(); - }); - - test('should display items in controls', () => { - render(setup(props)); - expect(screen.getByText('birth_names')).toBeInTheDocument(); - expect(screen.getByText('Metrics')).toBeInTheDocument(); - expect(screen.getByText('Columns')).toBeInTheDocument(); - }); - - test('should render the metrics', () => { - render(setup(props)); - const metricsNum = metrics.length; - metrics.forEach(metric => expect(screen.getByText(metric.metric_name)).toBeInTheDocument()); - expect(screen.getByText(`Showing ${metricsNum} of ${metricsNum}`)).toBeInTheDocument(); - }); - - test('should render the columns', () => { - render(setup(props)); - const columnsNum = columns.length; - columns.forEach(col => expect(screen.getByText(col.column_name)).toBeInTheDocument()); - expect(screen.getByText(`Showing ${columnsNum} of ${columnsNum}`)).toBeInTheDocument(); - }); - - test('should render 0 search results', async () => { - render(setup(props)); - const searchInput = screen.getByPlaceholderText('Search Metrics & Columns'); - - search('nothing', searchInput); - expect(await screen.findByText('Showing 0 of 0')).toBeInTheDocument(); - }); +test('should render 0 search results', async () => { + render(setup(props)); + const searchInput = screen.getByPlaceholderText('Search Metrics & Columns'); - test('should search and render matching columns', async () => { - render(setup(props)); - const searchInput = screen.getByPlaceholderText('Search Metrics & Columns'); + search('nothing', searchInput); + expect(await screen.findByText('Showing 0 of 0')).toBeInTheDocument(); +}); - search(columns[0].column_name, searchInput); +test('should search and render matching columns', async () => { + render(setup(props)); + const searchInput = screen.getByPlaceholderText('Search Metrics & Columns'); - await waitFor(() => { - expect(screen.getByText(columns[0].column_name)).toBeInTheDocument(); - expect(screen.queryByText(columns[1].column_name)).not.toBeInTheDocument(); - }) + search(columns[0].column_name, searchInput); + await waitFor(() => { + expect(screen.getByText(columns[0].column_name)).toBeInTheDocument(); + expect(screen.queryByText(columns[1].column_name)).not.toBeInTheDocument(); }); +}); - test('should search and render matching metrics', async () => { - render(setup(props)); - const searchInput = screen.getByPlaceholderText('Search Metrics & Columns'); +test('should search and render matching metrics', async () => { + render(setup(props)); + const searchInput = screen.getByPlaceholderText('Search Metrics & Columns'); - search(metrics[0].metric_name, searchInput); - - await waitFor(() => { - expect(screen.getByText(metrics[0].metric_name)).toBeInTheDocument(); - expect(screen.queryByText(metrics[1].metric_name)).not.toBeInTheDocument(); - }) + search(metrics[0].metric_name, searchInput); + await waitFor(() => { + expect(screen.getByText(metrics[0].metric_name)).toBeInTheDocument(); + expect(screen.queryByText(metrics[1].metric_name)).not.toBeInTheDocument(); }); +}); - test('should render a warning', () => { - const deprecatedDatasource = { - ...datasource, - extra: JSON.stringify({ warning_markdown: 'This is a warning.' }), - }; - render( - setup({ - ...props, - datasource: deprecatedDatasource, - controls: { - datasource: { - ...props.controls.datasource, - datasource: deprecatedDatasource, - }, +test('should render a warning', () => { + const deprecatedDatasource = { + ...datasource, + extra: JSON.stringify({ warning_markdown: 'This is a warning.' }), + }; + render( + setup({ + ...props, + datasource: deprecatedDatasource, + controls: { + datasource: { + ...props.controls.datasource, + datasource: deprecatedDatasource, }, - }), - ); - expect(screen.getByTestId('alert-solid')).toBeInTheDocument(); - }); + }, + }), + ); + expect(screen.getByTestId('alert-solid')).toBeInTheDocument(); +});