mirror of
https://github.com/apache/superset.git
synced 2024-09-17 11:09:47 -04:00
database modal should close on connect with tab layout (#14771)
This commit is contained in:
parent
e9657afe4b
commit
fbe6f16052
@ -44,6 +44,7 @@ const SqlAlchemyTab = ({
|
||||
<input
|
||||
type="text"
|
||||
name="database_name"
|
||||
data-test="database-name-input"
|
||||
value={db?.database_name || ''}
|
||||
placeholder={t('Name your database')}
|
||||
onChange={onInputChange}
|
||||
@ -62,6 +63,7 @@ const SqlAlchemyTab = ({
|
||||
<input
|
||||
type="text"
|
||||
name="sqlalchemy_uri"
|
||||
data-test="sqlalchemy-uri-input"
|
||||
value={db?.sqlalchemy_uri || ''}
|
||||
autoComplete="off"
|
||||
placeholder={t(
|
||||
|
@ -17,28 +17,11 @@
|
||||
* under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import thunk from 'redux-thunk';
|
||||
import configureStore from 'redux-mock-store';
|
||||
import * as redux from 'react-redux';
|
||||
import fetchMock from 'fetch-mock';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { Provider } from 'react-redux';
|
||||
|
||||
import waitForComponentToPaint from 'spec/helpers/waitForComponentToPaint';
|
||||
import { initialState } from 'spec/javascripts/sqllab/fixtures';
|
||||
import { styledMount as mount } from 'spec/helpers/theming';
|
||||
import { render, screen } from 'spec/helpers/testing-library';
|
||||
|
||||
import Modal from 'src/components/Modal';
|
||||
import Tabs from 'src/components/Tabs';
|
||||
import { render, screen, waitFor } from 'spec/helpers/testing-library';
|
||||
import DatabaseModal from './index';
|
||||
|
||||
// store needed for withToasts(DatabaseModal)
|
||||
const mockStore = configureStore([thunk]);
|
||||
const store = mockStore({});
|
||||
const mockedProps = {
|
||||
show: true,
|
||||
};
|
||||
const dbProps = {
|
||||
show: true,
|
||||
databaseId: 10,
|
||||
@ -46,10 +29,11 @@ const dbProps = {
|
||||
sqlalchemy_uri: 'postgres://superset:superset@something:1234/superset',
|
||||
};
|
||||
|
||||
const DATABASE_ENDPOINT = 'glob:*/api/v1/database/*';
|
||||
const AVAILABLE_DB_ENDPOINT = 'glob:*/api/v1/database/available/*';
|
||||
const DATABASE_FETCH_ENDPOINT = 'glob:*/api/v1/database/10';
|
||||
const DATABASE_POST_ENDPOINT = 'glob:*/api/v1/database/';
|
||||
const AVAILABLE_DB_ENDPOINT = 'glob:*/api/v1/database/available*';
|
||||
fetchMock.config.overwriteRoutes = true;
|
||||
fetchMock.get(DATABASE_ENDPOINT, {
|
||||
fetchMock.get(DATABASE_FETCH_ENDPOINT, {
|
||||
result: {
|
||||
id: 10,
|
||||
database_name: 'my database',
|
||||
@ -59,7 +43,7 @@ fetchMock.get(DATABASE_ENDPOINT, {
|
||||
configuration_method: 'sqlalchemy_form',
|
||||
},
|
||||
});
|
||||
fetchMock.get(AVAILABLE_DB_ENDPOINT, {
|
||||
fetchMock.mock(AVAILABLE_DB_ENDPOINT, {
|
||||
databases: [
|
||||
{
|
||||
engine: 'mysql',
|
||||
@ -70,53 +54,7 @@ fetchMock.get(AVAILABLE_DB_ENDPOINT, {
|
||||
});
|
||||
|
||||
describe('DatabaseModal', () => {
|
||||
afterEach(fetchMock.reset);
|
||||
describe('enzyme', () => {
|
||||
let wrapper;
|
||||
let spyOnUseSelector;
|
||||
beforeAll(() => {
|
||||
spyOnUseSelector = jest.spyOn(redux, 'useSelector');
|
||||
spyOnUseSelector.mockReturnValue(initialState.common.conf);
|
||||
});
|
||||
beforeEach(() => {
|
||||
wrapper = mount(
|
||||
<Provider store={store}>
|
||||
<DatabaseModal store={store} {...mockedProps} />
|
||||
</Provider>,
|
||||
);
|
||||
});
|
||||
afterEach(() => {
|
||||
wrapper.unmount();
|
||||
});
|
||||
it('renders', () => {
|
||||
expect(wrapper.find(DatabaseModal)).toExist();
|
||||
});
|
||||
it('renders a Modal', () => {
|
||||
expect(wrapper.find(Modal)).toExist();
|
||||
});
|
||||
it('renders "Connect a database" header when no database is included', () => {
|
||||
expect(wrapper.find('h4').text()).toEqual('Connect a database');
|
||||
});
|
||||
it('renders "Edit database" header when database prop is included', () => {
|
||||
const editWrapper = mount(<DatabaseModal store={store} {...dbProps} />);
|
||||
waitForComponentToPaint(editWrapper);
|
||||
expect(editWrapper.find('h4').text()).toEqual('Edit database');
|
||||
editWrapper.unmount();
|
||||
});
|
||||
it('renders a Tabs menu', () => {
|
||||
expect(wrapper.find(Tabs)).toExist();
|
||||
});
|
||||
it('renders two TabPanes', () => {
|
||||
expect(wrapper.find('.ant-tabs-tab')).toExist();
|
||||
expect(wrapper.find('.ant-tabs-tab')).toHaveLength(2);
|
||||
});
|
||||
it('renders input elements for Connection section', () => {
|
||||
expect(wrapper.find('input[name="database_name"]')).toExist();
|
||||
expect(wrapper.find('input[name="sqlalchemy_uri"]')).toExist();
|
||||
});
|
||||
});
|
||||
|
||||
describe('RTL', () => {
|
||||
afterEach(fetchMock.restore);
|
||||
describe('initial load', () => {
|
||||
it('hides the forms from the db when not selected', () => {
|
||||
render(<DatabaseModal show databaseId={1} />, { useRedux: true });
|
||||
@ -133,8 +71,7 @@ describe('DatabaseModal', () => {
|
||||
|
||||
const exposeInSqlLab = screen.getByText('Expose in SQL Lab');
|
||||
const exposeChoicesForm = exposeInSqlLab.parentElement.nextSibling;
|
||||
const schemaField = screen.getByText('CTAS & CVAS SCHEMA')
|
||||
.parentElement;
|
||||
const schemaField = screen.getByText('CTAS & CVAS SCHEMA').parentElement;
|
||||
expect(exposeChoicesForm).not.toHaveClass('open');
|
||||
expect(schemaField).not.toHaveClass('open');
|
||||
});
|
||||
@ -268,13 +205,27 @@ describe('DatabaseModal', () => {
|
||||
});
|
||||
|
||||
describe('create database', () => {
|
||||
it('should show a form when dynamic_form is selected', async () => {
|
||||
beforeEach(() => {
|
||||
fetchMock.post(DATABASE_POST_ENDPOINT, {
|
||||
id: 10,
|
||||
});
|
||||
fetchMock.mock(AVAILABLE_DB_ENDPOINT, {
|
||||
databases: [
|
||||
{
|
||||
engine: 'mysql',
|
||||
name: 'MySQL',
|
||||
preferred: false,
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
const props = {
|
||||
...dbProps,
|
||||
databaseId: null,
|
||||
database_name: null,
|
||||
sqlalchemy_uri: null,
|
||||
};
|
||||
it('should show a form when dynamic_form is selected', async () => {
|
||||
render(<DatabaseModal {...props} />, { useRedux: true });
|
||||
// it should have the correct header text
|
||||
const headerText = screen.getByText(/connect a database/i);
|
||||
@ -283,14 +234,50 @@ describe('DatabaseModal', () => {
|
||||
await screen.findByText(/display name/i);
|
||||
|
||||
// it does not fetch any databases if no id is passed in
|
||||
expect(fetchMock.calls().length).toEqual(0);
|
||||
expect(fetchMock.calls(DATABASE_FETCH_ENDPOINT).length).toEqual(0);
|
||||
|
||||
// todo we haven't hooked this up to load dynamically yet so
|
||||
// we can't currently test it
|
||||
});
|
||||
it('should close the modal on save if using the sqlalchemy form', async () => {
|
||||
const onHideMock = jest.fn();
|
||||
render(<DatabaseModal {...props} onHide={onHideMock} />, {
|
||||
useRedux: true,
|
||||
});
|
||||
// button should be disabled by default
|
||||
const submitButton = screen.getByTestId('modal-confirm-button');
|
||||
expect(submitButton).toBeDisabled();
|
||||
|
||||
const displayName = screen.getByTestId('database-name-input');
|
||||
userEvent.type(displayName, 'MyTestDB');
|
||||
expect(displayName.value).toBe('MyTestDB');
|
||||
const sqlalchemyInput = screen.getByTestId('sqlalchemy-uri-input');
|
||||
userEvent.type(sqlalchemyInput, 'some_url');
|
||||
expect(sqlalchemyInput.value).toBe('some_url');
|
||||
|
||||
// button should not be disabled now
|
||||
expect(submitButton).toBeEnabled();
|
||||
|
||||
await waitFor(() => {
|
||||
userEvent.click(submitButton);
|
||||
});
|
||||
expect(fetchMock.calls(DATABASE_POST_ENDPOINT)).toHaveLength(1);
|
||||
expect(onHideMock).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('edit database', () => {
|
||||
beforeEach(() => {
|
||||
fetchMock.mock(AVAILABLE_DB_ENDPOINT, {
|
||||
databases: [
|
||||
{
|
||||
engine: 'mysql',
|
||||
name: 'MySQL',
|
||||
preferred: false,
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
it('renders the sqlalchemy form when the sqlalchemy_form configuration method is set', async () => {
|
||||
render(<DatabaseModal {...dbProps} />, { useRedux: true });
|
||||
|
||||
@ -307,7 +294,7 @@ describe('DatabaseModal', () => {
|
||||
// todo add more when this form is built out
|
||||
});
|
||||
it('renders the dynamic form when the dynamic_form configuration method is set', async () => {
|
||||
fetchMock.get(DATABASE_ENDPOINT, {
|
||||
fetchMock.get(DATABASE_FETCH_ENDPOINT, {
|
||||
result: {
|
||||
id: 10,
|
||||
database_name: 'my database',
|
||||
@ -333,5 +320,4 @@ describe('DatabaseModal', () => {
|
||||
expect(todoText[0]).toBeVisible();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -202,7 +202,7 @@ const DatabaseModal: FunctionComponent<DatabaseModalProps> = ({
|
||||
const isEditMode = !!databaseId;
|
||||
const useSqlAlchemyForm =
|
||||
db?.configuration_method === CONFIGURATION_METHOD.SQLALCHEMY_URI;
|
||||
|
||||
const useTabLayout = isEditMode || useSqlAlchemyForm;
|
||||
// Database fetch logic
|
||||
const {
|
||||
state: { loading: dbLoading, resource: dbFetched },
|
||||
@ -240,7 +240,7 @@ const DatabaseModal: FunctionComponent<DatabaseModalProps> = ({
|
||||
onHide();
|
||||
};
|
||||
|
||||
const onSave = () => {
|
||||
const onSave = async () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const { id, ...update } = db || {};
|
||||
if (db?.id) {
|
||||
@ -258,14 +258,18 @@ const DatabaseModal: FunctionComponent<DatabaseModalProps> = ({
|
||||
});
|
||||
} else if (db) {
|
||||
// Create
|
||||
createResource(update as DatabaseObject).then(dbId => {
|
||||
const dbId = await createResource(update as DatabaseObject);
|
||||
if (dbId) {
|
||||
setHasConnectedDb(true);
|
||||
if (onDatabaseAdd) {
|
||||
onDatabaseAdd();
|
||||
}
|
||||
if (useTabLayout) {
|
||||
// tab layout only has one step
|
||||
// so it should close immediately on save
|
||||
onClose();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@ -336,7 +340,7 @@ const DatabaseModal: FunctionComponent<DatabaseModalProps> = ({
|
||||
FALSY_FORM_VALUES.includes(db?.parameters?.[field]),
|
||||
).length);
|
||||
|
||||
return isEditMode || useSqlAlchemyForm ? (
|
||||
return useTabLayout ? (
|
||||
<Modal
|
||||
css={(theme: SupersetTheme) => [
|
||||
antDTabsStyles,
|
||||
@ -346,6 +350,7 @@ const DatabaseModal: FunctionComponent<DatabaseModalProps> = ({
|
||||
]}
|
||||
name="database"
|
||||
disablePrimaryButton={disableSave}
|
||||
data-test="database-modal"
|
||||
height="600px"
|
||||
onHandledPrimaryAction={onSave}
|
||||
onHide={onClose}
|
||||
|
Loading…
Reference in New Issue
Block a user