/** * 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 fetchMock from 'fetch-mock'; import userEvent from '@testing-library/user-event'; import { render, screen, within, cleanup, act, } from 'spec/helpers/testing-library'; import * as hooks from 'src/views/CRUD/hooks'; import { DatabaseObject, CONFIGURATION_METHOD, } from 'src/views/CRUD/data/database/types'; import DatabaseModal, { dbReducer, DBReducerActionType, ActionType, } from './index'; const dbProps = { show: true, database_name: 'my database', sqlalchemy_uri: 'postgres://superset:superset@something:1234/superset', onHide: () => {}, }; const DATABASE_FETCH_ENDPOINT = 'glob:*/api/v1/database/10'; const AVAILABLE_DB_ENDPOINT = 'glob:*/api/v1/database/available*'; const VALIDATE_PARAMS_ENDPOINT = 'glob:*/api/v1/database/validate_parameters*'; fetchMock.config.overwriteRoutes = true; fetchMock.get(DATABASE_FETCH_ENDPOINT, { result: { id: 10, database_name: 'my database', expose_in_sqllab: false, allow_ctas: false, allow_cvas: false, configuration_method: 'sqlalchemy_form', }, }); 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...]', engine_information: { supports_file_upload: true, }, }, { available_drivers: ['rest'], engine: 'presto', name: 'Presto', preferred: true, engine_information: { supports_file_upload: 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...]', engine_information: { supports_file_upload: true, }, }, { available_drivers: ['pysqlite'], engine: 'sqlite', name: 'SQLite', preferred: true, engine_information: { supports_file_upload: true, }, }, { available_drivers: ['rest'], engine: 'druid', name: 'Apache Druid', preferred: false, engine_information: { supports_file_upload: true, }, }, { 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}', engine_information: { supports_file_upload: true, }, }, { available_drivers: ['rest'], default_driver: 'apsw', engine: 'gsheets', name: 'Google Sheets', preferred: false, engine_information: { supports_file_upload: false, }, }, { available_drivers: ['connector'], default_driver: 'connector', engine: 'databricks', name: 'Databricks', parameters: { properties: { access_token: { type: 'string', }, database: { type: 'string', }, host: { type: 'string', }, http_path: { type: 'string', }, port: { format: 'int32', type: 'integer', }, }, required: ['access_token', 'database', 'host', 'http_path', 'port'], type: 'object', }, preferred: true, sqlalchemy_uri_placeholder: 'databricks+connector://token:{access_token}@{host}:{port}/{database_name}', }, ], }); fetchMock.post(VALIDATE_PARAMS_ENDPOINT, { message: 'OK', }); const databaseFixture: DatabaseObject = { backend: 'postgres', configuration_method: CONFIGURATION_METHOD.DYNAMIC_FORM, database_name: 'Postgres', name: 'PostgresDB', is_managed_externally: false, driver: 'psycopg2', }; describe('DatabaseModal', () => { const renderAndWait = async () => { const mounted = act(async () => { render(, { useRedux: true, }); }); return mounted; }; beforeEach(async () => { await renderAndWait(); }); afterEach(cleanup); describe('Visual: New database connection', () => { test('renders the initial load of Step 1 correctly', () => { // ---------- Components ---------- // - AntD header const closeButton = screen.getByLabelText('Close'); 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: /postgresql/i, }); const preferredDbTextPostgreSQL = within( preferredDbButtonPostgreSQL, ).getByText(/postgresql/i); const preferredDbButtonPresto = screen.getByRole('button', { name: /presto/i, }); const preferredDbTextPresto = within(preferredDbButtonPresto).getByText( /presto/i, ); const preferredDbButtonMySQL = screen.getByRole('button', { name: /mysql/i, }); const preferredDbTextMySQL = within(preferredDbButtonMySQL).getByText( /mysql/i, ); const preferredDbButtonSQLite = screen.getByRole('button', { name: /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-icon/i, }); // renderAvailableSelector() =>