This is an automated email from the ASF dual-hosted git repository. lyndsi pushed a commit to branch lyndsi/sql-lab-new-explore-button-functionality-and-move-save-dataset-to-split-save-button in repository https://gitbox.apache.org/repos/asf/superset.git
commit 237ca9644580e629a8e669d3deae69bf2982280d Author: lyndsiWilliams <[email protected]> AuthorDate: Tue Jun 28 20:10:10 2022 -0500 Added testing and converted SaveQuery test to RTL --- .../SaveDatasetActionButton.test.tsx | 62 +++++++ .../SqlLab/components/SaveQuery/SaveQuery.test.jsx | 193 +++++++++++++++------ .../src/SqlLab/components/SaveQuery/index.tsx | 39 ++--- 3 files changed, 220 insertions(+), 74 deletions(-) diff --git a/superset-frontend/src/SqlLab/components/SaveDatasetActionButton/SaveDatasetActionButton.test.tsx b/superset-frontend/src/SqlLab/components/SaveDatasetActionButton/SaveDatasetActionButton.test.tsx new file mode 100644 index 0000000000..ec129d5a6a --- /dev/null +++ b/superset-frontend/src/SqlLab/components/SaveDatasetActionButton/SaveDatasetActionButton.test.tsx @@ -0,0 +1,62 @@ +/** + * 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 { Menu } from 'src/components/Menu'; +import SaveDatasetActionButton from 'src/SqlLab/components/SaveDatasetActionButton'; + +const overlayMenu = ( + <Menu> + <Menu.Item>Save dataset</Menu.Item> + </Menu> +); + +describe('SaveDatasetActionButton', () => { + it('renders a split save button', () => { + render( + <SaveDatasetActionButton + toggleSave={() => {}} + overlayMenu={overlayMenu} + />, + ); + + const saveBtn = screen.getByRole('button', { name: /save/i }); + const caretBtn = screen.getByRole('button', { name: /caret-down/i }); + + expect(saveBtn).toBeVisible(); + expect(caretBtn).toBeVisible(); + }); + + it('renders a "save dataset" dropdown menu item when user clicks caret button', () => { + render( + <SaveDatasetActionButton + toggleSave={() => {}} + overlayMenu={overlayMenu} + />, + ); + + const caretBtn = screen.getByRole('button', { name: /caret-down/i }); + userEvent.click(caretBtn); + + const saveDatasetMenuItem = screen.getByText(/save dataset/i); + + expect(saveDatasetMenuItem).toBeInTheDocument(); + }); +}); diff --git a/superset-frontend/src/SqlLab/components/SaveQuery/SaveQuery.test.jsx b/superset-frontend/src/SqlLab/components/SaveQuery/SaveQuery.test.jsx index 76f6ca8260..5d9237dec8 100644 --- a/superset-frontend/src/SqlLab/components/SaveQuery/SaveQuery.test.jsx +++ b/superset-frontend/src/SqlLab/components/SaveQuery/SaveQuery.test.jsx @@ -17,60 +17,87 @@ * under the License. */ import React from 'react'; -import { shallow } from 'enzyme'; -import * as sinon from 'sinon'; +import { render, screen } from 'spec/helpers/testing-library'; +import userEvent from '@testing-library/user-event'; import SaveQuery from 'src/SqlLab/components/SaveQuery'; -import Modal from 'src/components/Modal'; -import Button from 'src/components/Button'; -import { FormItem } from 'src/components/Form'; +import { databases } from 'src/SqlLab/fixtures'; + +const mockedProps = { + query: { + dbId: 1, + schema: 'main', + sql: 'SELECT * FROM t', + }, + defaultLabel: 'untitled', + animation: false, + database: databases.result[0], + onUpdate: () => {}, + onSave: () => {}, +}; + +const splitSaveBtnProps = { + ...mockedProps, + database: { + ...mockedProps.database, + allows_virtual_table_explore: true, + }, +}; describe('SavedQuery', () => { - const mockedProps = { - query: { - dbId: 1, - schema: 'main', - sql: 'SELECT * FROM t', - }, - defaultLabel: 'untitled', - animation: false, - }; - it('is valid', () => { - expect(React.isValidElement(<SaveQuery />)).toBe(true); - }); - it('is valid with props', () => { - expect(React.isValidElement(<SaveQuery {...mockedProps} />)).toBe(true); - }); - it('has a Modal', () => { - const wrapper = shallow(<SaveQuery {...mockedProps} />); - expect(wrapper.find(Modal)).toExist(); - }); - // TODO: eschutho convert test to RTL - // eslint-disable-next-line jest/no-disabled-tests - it.skip('has a cancel button', () => { - const wrapper = shallow(<SaveQuery {...mockedProps} />); - const modal = wrapper.find(Modal); + it('renders a non-split save button when allows_virtual_table_explore is not enabled', () => { + render(<SaveQuery {...mockedProps} />, { useRedux: true }); + + const saveBtn = screen.getByRole('button', { name: /save/i }); - expect(modal.find('[data-test="cancel-query"]')).toHaveLength(1); + expect(saveBtn).toBeVisible(); }); - it('has 2 FormItem', () => { - const wrapper = shallow(<SaveQuery {...mockedProps} />); - const modal = wrapper.find(Modal); - expect(modal.find(FormItem)).toHaveLength(2); + it('renders a save query modal when user clicks save button', () => { + render(<SaveQuery {...mockedProps} />, { useRedux: true }); + + const saveBtn = screen.getByRole('button', { name: /save/i }); + userEvent.click(saveBtn); + + const saveQueryModalHeader = screen.getByRole('heading', { + name: /save query/i, + }); + + expect(saveQueryModalHeader).toBeVisible(); }); - // eslint-disable-next-line jest/no-disabled-tests - it.skip('has a save button if this is a new query', () => { - const saveSpy = sinon.spy(); - const wrapper = shallow(<SaveQuery {...mockedProps} onSave={saveSpy} />); - const modal = wrapper.find(Modal); - - expect(modal.find(Button)).toHaveLength(2); - modal.find(Button).at(0).simulate('click'); - expect(saveSpy.calledOnce).toBe(true); + + it('renders the save query modal UI', () => { + render(<SaveQuery {...mockedProps} />, { useRedux: true }); + + const saveBtn = screen.getByRole('button', { name: /save/i }); + userEvent.click(saveBtn); + + const closeBtn = screen.getByRole('button', { name: /close/i }); + const saveQueryModalHeader = screen.getByRole('heading', { + name: /save query/i, + }); + const nameLabel = screen.getByText(/name/i); + const descriptionLabel = screen.getByText(/description/i); + const textBoxes = screen.getAllByRole('textbox'); + const nameTextbox = textBoxes[0]; + const descriptionTextbox = textBoxes[1]; + // There are now two save buttons, the initial save button and the modal save button + const saveBtns = screen.getAllByRole('button', { name: /save/i }); + const cancelBtn = screen.getByRole('button', { name: /cancel/i }); + + expect(closeBtn).toBeVisible(); + expect(saveQueryModalHeader).toBeVisible(); + expect(nameLabel).toBeVisible(); + expect(descriptionLabel).toBeVisible(); + expect(textBoxes.length).toBe(2); + expect(nameTextbox).toBeVisible(); + expect(descriptionTextbox).toBeVisible(); + expect(saveBtns.length).toBe(2); + expect(saveBtns[0]).toBeVisible(); + expect(saveBtns[1]).toBeVisible(); + expect(cancelBtn).toBeVisible(); }); - // eslint-disable-next-line jest/no-disabled-tests - it.skip('has an update button if this is an existing query', () => { - const updateSpy = sinon.spy(); + + it('renders a "save as new" and "update" button if query already exists', () => { const props = { ...mockedProps, query: { @@ -78,11 +105,75 @@ describe('SavedQuery', () => { remoteId: '42', }, }; - const wrapper = shallow(<SaveQuery {...props} onUpdate={updateSpy} />); - const modal = wrapper.find(Modal); + render(<SaveQuery {...props} />, { useRedux: true }); + + const saveBtn = screen.getByRole('button', { name: /save/i }); + userEvent.click(saveBtn); + + const saveAsNewBtn = screen.getByRole('button', { name: /save as new/i }); + const updateBtn = screen.getByRole('button', { name: /update/i }); + + expect(saveAsNewBtn).toBeVisible(); + expect(updateBtn).toBeVisible(); + }); + + it('renders a split save button when allows_virtual_table_explore is enabled', () => { + render(<SaveQuery {...splitSaveBtnProps} />, { useRedux: true }); + + const saveBtn = screen.getByRole('button', { name: /save/i }); + const caretBtn = screen.getByRole('button', { name: /caret-down/i }); + + expect(saveBtn).toBeVisible(); + expect(caretBtn).toBeVisible(); + }); + + it('renders a save dataset modal when user clicks "save dataset" menu item', () => { + render(<SaveQuery {...splitSaveBtnProps} />, { useRedux: true }); + + const caretBtn = screen.getByRole('button', { name: /caret-down/i }); + userEvent.click(caretBtn); + + const saveDatasetMenuItem = screen.getByText(/save dataset/i); + userEvent.click(saveDatasetMenuItem); + + const saveDatasetHeader = screen.getByText(/save or overwrite dataset/i); + + expect(saveDatasetHeader).toBeVisible(); + }); + + it('renders the save dataset modal UI', () => { + render(<SaveQuery {...splitSaveBtnProps} />, { useRedux: true }); + + const caretBtn = screen.getByRole('button', { name: /caret-down/i }); + userEvent.click(caretBtn); + + const saveDatasetMenuItem = screen.getByText(/save dataset/i); + userEvent.click(saveDatasetMenuItem); + + const closeBtn = screen.getByRole('button', { name: /close/i }); + const saveDatasetHeader = screen.getByText(/save or overwrite dataset/i); + const saveRadio = screen.getByRole('radio', { + name: /save as new undefined/i, + }); + const saveLabel = screen.getByText(/save as new/i); + const saveTextbox = screen.getByRole('textbox'); + const overwriteRadio = screen.getByRole('radio', { + name: /overwrite existing select or type dataset name/i, + }); + const overwriteLabel = screen.getByText(/overwrite existing/i); + const overwriteCombobox = screen.getByRole('combobox'); + const overwritePlaceholderText = screen.getByText( + /select or type dataset name/i, + ); - expect(modal.find(Button)).toHaveLength(3); - modal.find(Button).at(0).simulate('click'); - expect(updateSpy.calledOnce).toBe(true); + expect(saveDatasetHeader).toBeVisible(); + expect(closeBtn).toBeVisible(); + expect(saveRadio).toBeVisible(); + expect(saveLabel).toBeVisible(); + expect(saveTextbox).toBeVisible(); + expect(overwriteRadio).toBeVisible(); + expect(overwriteLabel).toBeVisible(); + expect(overwriteCombobox).toBeVisible(); + expect(overwritePlaceholderText).toBeVisible(); }); }); diff --git a/superset-frontend/src/SqlLab/components/SaveQuery/index.tsx b/superset-frontend/src/SqlLab/components/SaveQuery/index.tsx index 467eb6a185..aa0c10de4c 100644 --- a/superset-frontend/src/SqlLab/components/SaveQuery/index.tsx +++ b/superset-frontend/src/SqlLab/components/SaveQuery/index.tsx @@ -19,7 +19,7 @@ import React, { useState, useEffect } from 'react'; import { Row, Col } from 'src/components'; import { Input, TextArea } from 'src/components/Input'; -import { t, styled, useTheme } from '@superset-ui/core'; +import { t, styled } from '@superset-ui/core'; import Button from 'src/components/Button'; import { Menu } from 'src/components/Menu'; import { Form, FormItem } from 'src/components/Form'; @@ -62,6 +62,18 @@ type QueryPayload = { title: string; }; +const Styles = styled.span` + span[role='img'] { + display: flex; + margin: 0; + color: ${({ theme }) => theme.colors.grayscale.base}; + svg { + vertical-align: -${({ theme }) => theme.gridUnit * 1.25}px; + margin: 0; + } + } +`; + export default function SaveQuery({ query, defaultLabel = t('Undefined'), @@ -77,21 +89,8 @@ export default function SaveQuery({ const [showSave, setShowSave] = useState<boolean>(false); const [showSaveDatasetModal, setShowSaveDatasetModal] = useState(false); const isSaved = !!query.remoteId; - const theme = useTheme(); const canExploreDatabase = !!database?.allows_virtual_table_explore; - const Styles = styled.span` - span[role='img'] { - display: flex; - margin: 0; - color: ${theme.colors.grayscale.base}; - svg { - vertical-align: -${theme.gridUnit * 1.25}px; - margin: 0; - } - } - `; - const overlayMenu = ( <Menu> <Menu.Item onClick={() => setShowSaveDatasetModal(true)}> @@ -107,14 +106,10 @@ export default function SaveQuery({ }); useEffect(() => { - if (!isSaved) { - setLabel(defaultLabel); - } + if (!isSaved) setLabel(defaultLabel); }, [defaultLabel]); - const close = () => { - setShowSave(false); - }; + const close = () => setShowSave(false); const onSaveWrapper = () => { onSave(queryPayload()); @@ -134,9 +129,7 @@ export default function SaveQuery({ setDescription(e.target.value); }; - const toggleSave = () => { - setShowSave(!showSave); - }; + const toggleSave = () => setShowSave(!showSave); const renderModalBody = () => ( <Form layout="vertical">
