// Licensed to Cloudera, Inc. under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. Cloudera, Inc. 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, fireEvent, screen, waitFor } from '@testing-library/react';
import '@testing-library/jest-dom';
import DestinationSettings from './DestinationSettings';
import { DestinationConfig } from '../../types';
import { useDataCatalog } from '../../../../utils/hooks/useDataCatalog/useDataCatalog';
const mockUseDataCatalog = {
loading: {
connector: false,
compute: false,
database: false,
table: false
},
databases: ['database1', 'database2'],
database: 'database1',
connectors: [
{ id: 'connector1', displayName: 'Connector 1' },
{ id: 'connector2', displayName: 'Connector 2' }
],
connector: { id: 'connector1', displayName: 'Connector 1' },
computes: [
{ id: 'compute1', name: 'Compute 1' },
{ id: 'compute2', name: 'Compute 2' }
],
compute: { id: 'compute1', name: 'Compute 1' },
tables: [],
setCompute: jest.fn(),
setConnector: jest.fn(),
setDatabase: jest.fn()
};
jest.mock('../../../../utils/hooks/useDataCatalog/useDataCatalog', () => ({
useDataCatalog: jest.fn()
}));
const defaultProps = {
defaultValues: {
database: 'database1',
tableName: 'test_table',
connectorId: 'connector1',
computeId: 'compute1'
} as DestinationConfig,
onChange: jest.fn()
};
describe('DestinationSettings Component', () => {
beforeEach(() => {
jest.clearAllMocks();
(useDataCatalog as jest.Mock).mockReturnValue(mockUseDataCatalog);
});
afterEach(() => {
jest.clearAllMocks();
});
it('should render all form fields correctly', () => {
render();
expect(screen.getByLabelText('Engine')).toBeInTheDocument();
expect(screen.getByLabelText('Compute')).toBeInTheDocument();
expect(screen.getByLabelText('Database')).toBeInTheDocument();
expect(screen.getByLabelText('Table Name')).toBeInTheDocument();
});
it('should display correct initial values', () => {
render();
expect(screen.getByDisplayValue('test_table')).toBeInTheDocument();
});
it('should not have compute field when only one compute is available', async () => {
const mockUseDataCatalogSingleCompute = {
...mockUseDataCatalog,
computes: [{ id: 'compute1', name: 'Compute 1' }],
compute: { id: 'compute1', name: 'Compute 1' }
};
(useDataCatalog as jest.Mock).mockReturnValue(mockUseDataCatalogSingleCompute);
render();
await waitFor(() => {
expect(screen.getByLabelText('Engine')).toBeInTheDocument();
expect(screen.getByLabelText('Database')).toBeInTheDocument();
expect(screen.getByLabelText('Table Name')).toBeInTheDocument();
expect(screen.queryByLabelText('Compute')).not.toBeInTheDocument();
});
});
it('should show compute field when multiple computes are available', async () => {
render();
expect(screen.getByLabelText('Compute')).toBeInTheDocument();
});
it('should call onChange when engine dropdown changes', async () => {
render();
const engineSelect = screen.getByLabelText('Engine');
fireEvent.mouseDown(engineSelect);
await waitFor(() => {
const option = screen.getByText('Connector 2');
fireEvent.click(option);
});
expect(mockUseDataCatalog.setConnector).toHaveBeenCalledWith({
id: 'connector2',
displayName: 'Connector 2'
});
expect(defaultProps.onChange).toHaveBeenCalledWith('connectorId', 'connector2');
});
it('should call onChange when database dropdown changes', async () => {
render();
const databaseSelect = screen.getByLabelText('Database');
fireEvent.mouseDown(databaseSelect);
await waitFor(() => {
const options = screen.getAllByText('database2');
const option = options.find(el => el.closest('.ant-select-item'));
if (option) {
fireEvent.click(option);
}
});
expect(mockUseDataCatalog.setDatabase).toHaveBeenCalledWith('database2');
expect(defaultProps.onChange).toHaveBeenCalledWith('database', 'database2');
});
it('should call onChange when compute dropdown changes', async () => {
render();
const computeSelect = screen.getByLabelText('Compute');
fireEvent.mouseDown(computeSelect);
await waitFor(() => {
const option = screen.getByText('Compute 2');
fireEvent.click(option);
});
expect(mockUseDataCatalog.setCompute).toHaveBeenCalledWith({
id: 'compute2',
name: 'Compute 2'
});
expect(defaultProps.onChange).toHaveBeenCalledWith('computeId', 'compute2');
});
it('should update table name input and call onChange when input changes', async () => {
render();
const tableNameInput = screen.getByLabelText('Table Name');
fireEvent.change(tableNameInput, { target: { value: 'new_table_name' } });
expect(screen.getByDisplayValue('new_table_name')).toBeInTheDocument();
expect(defaultProps.onChange).toHaveBeenCalledWith('tableName', 'new_table_name');
});
it('should set default values from props on component mount', async () => {
const mockSetConnector = jest.fn();
const mockSetDatabase = jest.fn();
const mockSetCompute = jest.fn();
const mockUseDataCatalogWithSetters = {
...mockUseDataCatalog,
setConnector: mockSetConnector,
setDatabase: mockSetDatabase,
setCompute: mockSetCompute
};
(useDataCatalog as jest.Mock).mockReturnValueOnce(mockUseDataCatalogWithSetters);
render();
expect(mockSetConnector).toHaveBeenCalledWith({
id: 'connector1',
displayName: 'Connector 1'
});
expect(mockSetDatabase).toHaveBeenCalledWith('database1');
expect(mockSetCompute).toHaveBeenCalledWith({
id: 'compute1',
name: 'Compute 1'
});
});
it('should not call setters when no matching items found in defaultValues', async () => {
const mockSetConnector = jest.fn();
const mockSetDatabase = jest.fn();
const mockSetCompute = jest.fn();
const mockUseDataCatalogNoMatch = {
...mockUseDataCatalog,
connectors: [{ id: 'different_connector', displayName: 'Different Connector' }],
databases: ['different_database'],
computes: [{ id: 'different_compute', name: 'Different Compute' }],
setConnector: mockSetConnector,
setDatabase: mockSetDatabase,
setCompute: mockSetCompute
};
(useDataCatalog as jest.Mock).mockReturnValueOnce(mockUseDataCatalogNoMatch);
render();
expect(mockSetConnector).not.toHaveBeenCalled();
expect(mockSetDatabase).not.toHaveBeenCalled();
expect(mockSetCompute).not.toHaveBeenCalled();
});
it('should show alert when table name already exists', async () => {
const mockUseDataCatalogWithExistingTables = {
...mockUseDataCatalog,
tables: [{ name: 'existing_table' }, { name: 'another_table' }]
};
(useDataCatalog as jest.Mock).mockReturnValue(mockUseDataCatalogWithExistingTables);
render();
const tableNameInput = screen.getByLabelText('Table Name');
fireEvent.change(tableNameInput, { target: { value: 'existing_table' } });
await waitFor(() => {
expect(screen.getByRole('alert')).toBeInTheDocument();
expect(screen.getByText('Table name already exists in the database')).toBeInTheDocument();
});
});
it('should hide alert when table name is unique', async () => {
const mockUseDataCatalogWithExistingTables = {
...mockUseDataCatalog,
tables: [{ name: 'existing_table' }, { name: 'another_table' }]
};
(useDataCatalog as jest.Mock).mockReturnValue(mockUseDataCatalogWithExistingTables);
render();
const tableNameInput = screen.getByLabelText('Table Name');
fireEvent.change(tableNameInput, { target: { value: 'existing_table' } });
await waitFor(() => {
expect(screen.getByRole('alert')).toBeInTheDocument();
});
fireEvent.change(tableNameInput, { target: { value: 'unique_table_name' } });
await waitFor(() => {
expect(screen.queryByRole('alert')).not.toBeInTheDocument();
});
});
});