diff --git a/superset-frontend/src/views/CRUD/data/database/DatabaseModal/index.test.jsx b/superset-frontend/src/views/CRUD/data/database/DatabaseModal/index.test.jsx index eec45618e7150..7b8d822cbcae5 100644 --- a/superset-frontend/src/views/CRUD/data/database/DatabaseModal/index.test.jsx +++ b/superset-frontend/src/views/CRUD/data/database/DatabaseModal/index.test.jsx @@ -18,13 +18,23 @@ */ import React from 'react'; import fetchMock from 'fetch-mock'; -// import userEvent from '@testing-library/user-event'; -import { render, screen } from 'spec/helpers/testing-library'; +import userEvent from '@testing-library/user-event'; +import { + render, + screen, + within, + cleanup, + act, +} from 'spec/helpers/testing-library'; +/* -- These imports are used for the mock functions that currently don't work +import { + testDatabaseConnection, + useSingleViewResource, +} from 'src/views/CRUD/hooks'; */ import DatabaseModal from './index'; const dbProps = { show: true, - databaseId: 10, database_name: 'my database', sqlalchemy_uri: 'postgres://superset:superset@something:1234/superset', }; @@ -46,276 +56,959 @@ fetchMock.get(DATABASE_FETCH_ENDPOINT, { fetchMock.mock(AVAILABLE_DB_ENDPOINT, { databases: [ { + available_drivers: ['psycopg2'], + default_driver: 'psycopg2', + engine: 'postgresql', + name: 'PostgreSQL', + parameters: { + properties: { + database: { + description: 'Database name', + type: 'string', + }, + encryption: { + description: 'Use an encrypted connection to the database', + type: 'boolean', + }, + host: { + description: 'Hostname or IP address', + type: 'string', + }, + password: { + description: 'Password', + nullable: true, + type: 'string', + }, + port: { + description: 'Database port', + format: 'int32', + maximum: 65536, + minimum: 0, + type: 'integer', + }, + query: { + additionalProperties: {}, + description: 'Additional parameters', + type: 'object', + }, + username: { + description: 'Username', + nullable: true, + type: 'string', + }, + }, + required: ['database', 'host', 'port', 'username'], + type: 'object', + }, + preferred: true, + sqlalchemy_uri_placeholder: + 'postgresql://user:password@host:port/dbname[?key=value&key=value...]', + }, + { + available_drivers: ['rest'], + engine: 'presto', + name: 'Presto', + preferred: true, + }, + { + available_drivers: ['mysqldb'], + default_driver: 'mysqldb', engine: 'mysql', name: 'MySQL', + parameters: { + properties: { + database: { + description: 'Database name', + type: 'string', + }, + encryption: { + description: 'Use an encrypted connection to the database', + type: 'boolean', + }, + host: { + description: 'Hostname or IP address', + type: 'string', + }, + password: { + description: 'Password', + nullable: true, + type: 'string', + }, + port: { + description: 'Database port', + format: 'int32', + maximum: 65536, + minimum: 0, + type: 'integer', + }, + query: { + additionalProperties: {}, + description: 'Additional parameters', + type: 'object', + }, + username: { + description: 'Username', + nullable: true, + type: 'string', + }, + }, + required: ['database', 'host', 'port', 'username'], + type: 'object', + }, + preferred: true, + sqlalchemy_uri_placeholder: + 'mysql://user:password@host:port/dbname[?key=value&key=value...]', + }, + { + available_drivers: ['pysqlite'], + engine: 'sqlite', + name: 'SQLite', + preferred: true, + }, + { + available_drivers: ['rest'], + engine: 'druid', + name: 'Apache Druid', + preferred: false, + }, + { + available_drivers: ['bigquery'], + default_driver: 'bigquery', + engine: 'bigquery', + name: 'Google BigQuery', + parameters: { + properties: { + credentials_info: { + description: 'Contents of BigQuery JSON credentials.', + type: 'string', + 'x-encrypted-extra': true, + }, + query: { + type: 'object', + }, + }, + type: 'object', + }, preferred: false, + sqlalchemy_uri_placeholder: 'bigquery://{project_id}', }, ], }); describe('DatabaseModal', () => { - afterEach(fetchMock.restore); - // describe('initial load', () => { - // it('hides the forms from the db when not selected', () => { - // render(, { useRedux: true }); - // // Select Advanced tab - // const advancedTab = screen.getByRole('tab', { - // name: /advanced/i, - // }); - // userEvent.click(advancedTab); - // // Select SQL Lab tab - // const sqlLabSettingsTab = screen.getByRole('tab', { - // name: /sql lab/i, - // }); - // userEvent.click(sqlLabSettingsTab); - - // const exposeInSqlLab = screen.getByText('Expose in SQL Lab'); - // const exposeChoicesForm = exposeInSqlLab.parentElement.nextSibling; - // const schemaField = screen.getByText('CTAS & CVAS SCHEMA').parentElement; - // expect(exposeChoicesForm).not.toHaveClass('open'); - // expect(schemaField).not.toHaveClass('open'); - // }); - // }); - // it('renders all settings when "Expose in SQL Lab" is checked', () => { - // render(, { useRedux: true }); - - // // Select Advanced tab - // const advancedTab = screen.getByRole('tab', { - // name: /advanced/i, - // }); - // userEvent.click(advancedTab); - - // // Select SQL Lab tab - // const sqlLabSettingsTab = screen.getByRole('tab', { - // name: /sql lab/i, - // }); - - // userEvent.click(sqlLabSettingsTab); - - // // Grab all SQL Lab settings by their labels - // // const exposeInSqlLab = screen.getByText('Expose in SQL Lab'); - // const exposeInSqlLab = screen.getByRole('checkbox', { - // name: /expose in sql lab/i, - // }); - - // expect(exposeInSqlLab).not.toBeChecked(); - // userEvent.click(exposeInSqlLab); - - // // While checked make sure all checkboxes are showing - // expect(exposeInSqlLab).toBeChecked(); - // const checkboxes = screen - // .getAllByRole('checkbox') - // .filter(checkbox => !checkbox.checked); - - // expect(checkboxes.length).toEqual(4); - // }); - - // it('renders the schema field when allowCTAS is checked', () => { - // render(, { useRedux: true }); - - // // Select Advanced tab - // const advancedTab = screen.getByRole('tab', { - // name: /advanced/i, - // }); - // userEvent.click(advancedTab); - - // // Select SQL Lab tab - // const sqlLabSettingsTab = screen.getByRole('tab', { - // name: /sql lab/i, - // }); - // userEvent.click(sqlLabSettingsTab); - // // Grab CTAS & schema field by their labels - // const allowCTAS = screen.getByLabelText('Allow CREATE TABLE AS'); - // const schemaField = screen.getByText('CTAS & CVAS SCHEMA').parentElement; - - // // While CTAS & CVAS are unchecked, schema field is not visible - // expect(schemaField).not.toHaveClass('open'); - - // // Check "Allow CTAS" to reveal schema field - // userEvent.click(allowCTAS); - // expect(schemaField).toHaveClass('open'); - - // // Uncheck "Allow CTAS" to hide schema field again - // userEvent.click(allowCTAS); - // expect(schemaField).not.toHaveClass('open'); - // }); - - // it('renders the schema field when allowCVAS is checked', () => { - // render(, { useRedux: true }); - - // // Select Advanced tab - // const advancedTab = screen.getByRole('tab', { - // name: /advanced/i, - // }); - // userEvent.click(advancedTab); - - // // Select SQL Lab tab - // const sqlLabSettingsTab = screen.getByRole('tab', { - // name: /sql lab/i, - // }); - // userEvent.click(sqlLabSettingsTab); - // // Grab CVAS by it's label & schema field - // const allowCVAS = screen.getByText('Allow CREATE VIEW AS'); - // const schemaField = screen.getByText('CTAS & CVAS SCHEMA').parentElement; - - // // While CTAS & CVAS are unchecked, schema field is not visible - // expect(schemaField).not.toHaveClass('open'); - - // // Check "Allow CVAS" to reveal schema field - // userEvent.click(allowCVAS); - // expect(schemaField).toHaveClass('open'); - - // // Uncheck "Allow CVAS" to hide schema field again - // userEvent.click(allowCVAS); - // expect(schemaField).not.toHaveClass('open'); - // }); - - // it('renders the schema field when both allowCTAS and allowCVAS are checked', () => { - // render(, { useRedux: true }); - - // // Select Advanced tab - // const advancedTab = screen.getByRole('tab', { - // name: /advanced/i, - // }); - // userEvent.click(advancedTab); - - // // Select SQL Lab tab - // const sqlLabSettingsTab = screen.getByRole('tab', { - // name: /sql lab/i, - // }); - // userEvent.click(sqlLabSettingsTab); - // // Grab CTAS and CVAS by their labels, & schema field - // const allowCTAS = screen.getByText('Allow CREATE TABLE AS'); - // const allowCVAS = screen.getByText('Allow CREATE VIEW AS'); - // const schemaField = screen.getByText('CTAS & CVAS SCHEMA').parentElement; - - // // While CTAS & CVAS are unchecked, schema field is not visible - // expect(schemaField).not.toHaveClass('open'); - - // // Check both "Allow CTAS" and "Allow CVAS" to reveal schema field - // userEvent.click(allowCTAS); - // userEvent.click(allowCVAS); - // expect(schemaField).toHaveClass('open'); - // // Uncheck both "Allow CTAS" and "Allow CVAS" to hide schema field again - // userEvent.click(allowCTAS); - // userEvent.click(allowCVAS); - - // // Both checkboxes go unchecked, so the field should no longer render - // expect(schemaField).not.toHaveClass('open'); - // }); - // TODO: rewrite when Modal is complete - // describe('create database', () => { - // 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(, { useRedux: true }); - // // it should have the correct header text - // const headerText = screen.getByText(/connect a database/i); - // expect(headerText).toBeVisible(); - - // await screen.findByText(/display name/i); - - // // it does not fetch any databases if no id is passed in - // 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(, { - // 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, - }, - ], + const renderAndWait = async () => { + const mounted = act(async () => { + render(, { + useRedux: true, + }); + }); + + return mounted; + }; + + beforeEach(async () => { + await renderAndWait(); + }); + + afterEach(cleanup); + + describe('Visual: New database connection', () => { + it('renders the initial load of Step 1 correctly', async () => { + // ---------- Components ---------- + // - AntD header + const closeButton = screen.getByRole('button', { name: /close/i }); + const step1Header = screen.getByRole('heading', { + name: /connect a database/i, + }); + // - Connection header + const step1Helper = screen.getByText(/step 1 of 3/i); + const selectDbHeader = screen.getByRole('heading', { + name: /select a database to connect/i, + }); + // - Preferred database buttons + const preferredDbButtonPostgreSQL = screen.getByRole('button', { + name: /default-database postgresql/i, + }); + const preferredDbTextPostgreSQL = within( + preferredDbButtonPostgreSQL, + ).getByText(/postgresql/i); + const preferredDbButtonPresto = screen.getByRole('button', { + name: /default-database presto/i, + }); + const preferredDbTextPresto = within(preferredDbButtonPresto).getByText( + /presto/i, + ); + const preferredDbButtonMySQL = screen.getByRole('button', { + name: /default-database mysql/i, + }); + const preferredDbTextMySQL = within(preferredDbButtonMySQL).getByText( + /mysql/i, + ); + const preferredDbButtonSQLite = screen.getByRole('button', { + name: /default-database sqlite/i, + }); + const preferredDbTextSQLite = within(preferredDbButtonSQLite).getByText( + /sqlite/i, + ); + // All dbs render with this icon in this testing environment, + // The Icon count should equal the count of databases rendered + const preferredDbIcon = screen.getAllByRole('img', { + name: /default-database/i, + }); + // renderAvailableSelector() =>