support unit test for addDomainForm and minor cleanup (#15470)

This commit is contained in:
Ashish Gupta 2024-03-07 11:15:50 +05:30 committed by GitHub
parent 182f3a39f5
commit d7c21ae440
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 225 additions and 251 deletions

View File

@ -1,73 +0,0 @@
/*
* Copyright 2023 Collate.
* Licensed 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 { Button, Modal } from 'antd';
import { useForm } from 'antd/lib/form/Form';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { CreateDataProduct } from '../../../generated/api/domains/createDataProduct';
import { CreateDomain } from '../../../generated/api/domains/createDomain';
import AddDomainForm from '../AddDomainForm/AddDomainForm.component';
import { DomainFormType } from '../DomainPage.interface';
import { AddDataProductModalProps } from './AddDataProductModal.interface';
const AddDataProductModal = ({
open,
onSubmit,
onCancel,
}: AddDataProductModalProps) => {
const { t } = useTranslation();
const [form] = useForm();
const handleFormSubmit = async (
formData: CreateDomain | CreateDataProduct
) => {
onSubmit(formData);
};
return (
<Modal
centered
cancelText={t('label.cancel')}
className="add-data-product-modal"
closable={false}
footer={[
<Button key="cancel-btn" type="link" onClick={onCancel}>
{t('label.cancel')}
</Button>,
<Button
data-testid="save-data-product"
key="save-btn"
type="primary"
onClick={() => form.submit()}>
{t('label.save')}
</Button>,
]}
maskClosable={false}
okText={t('label.submit')}
open={open}
title={t('label.add-entity', { entity: t('label.data-product') })}
width={750}
onCancel={onCancel}>
<AddDomainForm
isFormInDialog
formRef={form}
loading={false}
type={DomainFormType.DATA_PRODUCT}
onCancel={onCancel}
onSubmit={handleFormSubmit}
/>
</Modal>
);
};
export default AddDataProductModal;

View File

@ -1,22 +0,0 @@
/*
* Copyright 2023 Collate.
* Licensed 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 { CreateDataProduct } from '../../../generated/api/domains/createDataProduct';
import { CreateDomain } from '../../../generated/api/domains/createDomain';
import { Domain } from '../../../generated/entity/domains/domain';
export interface AddDataProductModalProps {
open: boolean;
data?: Domain;
onCancel: () => void;
onSubmit: (data: CreateDomain | CreateDataProduct) => Promise<void>;
}

View File

@ -1,83 +0,0 @@
/*
* Copyright 2024 Collate.
* Licensed 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 { act, fireEvent, render, screen } from '@testing-library/react';
import React from 'react';
import AddDataProductModal from './AddDataProductModal.component';
const formData = {
description: 'test-description',
name: 'test-name',
};
const mockSubmit = jest.fn();
const mockCancel = jest.fn();
const mockSave = jest.fn();
const mockProps = {
open: true,
onSubmit: mockSubmit,
onCancel: mockCancel,
};
jest.mock('../AddDomainForm/AddDomainForm.component', () => {
return jest.fn().mockImplementation(({ onSubmit }) => (
<div>
AddDomainForm
<button data-testid="submit-button" onClick={() => onSubmit(formData)}>
Submit
</button>
</div>
));
});
jest.mock('antd/lib/form/Form', () => ({
useForm: () => [
{
submit: mockSave,
},
],
}));
describe('Test AddDataProductModal Component', () => {
it('Should Render Add Data Product Modal Component', async () => {
render(<AddDataProductModal {...mockProps} />);
expect(await screen.findByText('label.add-entity')).toBeInTheDocument();
expect(await screen.findByText('AddDomainForm')).toBeInTheDocument();
expect(await screen.findByText('label.cancel')).toBeInTheDocument();
expect(await screen.findByText('label.save')).toBeInTheDocument();
});
it('Should call onSubmit function', async () => {
render(<AddDataProductModal {...mockProps} />);
const submitButton = await screen.findByTestId('submit-button');
await act(async () => {
fireEvent.click(submitButton);
});
expect(mockSubmit).toHaveBeenCalledWith(formData);
});
it('should call form.submit when save button is clicked', async () => {
await act(async () => {
render(<AddDataProductModal {...mockProps} />);
});
const button = await screen.findByTestId('save-data-product');
await act(async () => {
fireEvent.click(button);
});
expect(mockSave).toHaveBeenCalled();
});
});

View File

@ -11,7 +11,6 @@
* limitations under the License. * limitations under the License.
*/ */
import { Typography } from 'antd'; import { Typography } from 'antd';
import { useForm } from 'antd/lib/form/Form';
import { AxiosError } from 'axios'; import { AxiosError } from 'axios';
import React, { useCallback, useState } from 'react'; import React, { useCallback, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
@ -33,7 +32,6 @@ import './add-domain.less';
const AddDomain = () => { const AddDomain = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const history = useHistory(); const history = useHistory();
const [form] = useForm();
const [isLoading, setIsLoading] = useState<boolean>(false); const [isLoading, setIsLoading] = useState<boolean>(false);
const { refreshDomains } = useDomainProvider(); const { refreshDomains } = useDomainProvider();
@ -114,7 +112,6 @@ const AddDomain = () => {
})} })}
</Typography.Title> </Typography.Title>
<AddDomainForm <AddDomainForm
formRef={form}
isFormInDialog={false} isFormInDialog={false}
loading={isLoading} loading={isLoading}
type={DomainFormType.DOMAIN} type={DomainFormType.DOMAIN}

View File

@ -49,10 +49,11 @@ const AddDomainForm = ({
loading, loading,
onCancel, onCancel,
onSubmit, onSubmit,
formRef: form, formRef,
type, type,
}: AddDomainFormProps) => { }: AddDomainFormProps) => {
const { t } = useTranslation(); const { t } = useTranslation();
const [form] = Form.useForm(formRef);
const { permissions } = usePermissionProvider(); const { permissions } = usePermissionProvider();
const domainTypeArray = Object.keys(DomainType).map((key) => ({ const domainTypeArray = Object.keys(DomainType).map((key) => ({
@ -235,67 +236,64 @@ const AddDomainForm = ({
}; };
return ( return (
<> <Form
<div data-testid="add-domain"> data-testid="add-domain"
<Form form={form} layout="vertical" onFinish={handleFormSubmit}> form={form}
{generateFormFields(formFields)} layout="vertical"
<div className="m-t-xss"> onFinish={handleFormSubmit}>
{getField(ownerField)} {generateFormFields(formFields)}
{selectedOwner && ( <div className="m-t-xss">
<div className="m-b-sm" data-testid="owner-container"> {getField(ownerField)}
<UserTag {selectedOwner && (
id={selectedOwner.name ?? selectedOwner.id} <div className="m-b-sm" data-testid="owner-container">
isTeam={selectedOwner.type === UserTeam.Team} <UserTag
name={getEntityName(selectedOwner)} id={selectedOwner.name ?? selectedOwner.id}
size={UserTagSize.small} isTeam={selectedOwner.type === UserTeam.Team}
/> name={getEntityName(selectedOwner)}
</div> size={UserTagSize.small}
)} />
</div> </div>
<div className="m-t-xss"> )}
{getField(expertsField)}
{Boolean(expertsList.length) && (
<Space
wrap
className="m-b-xs"
data-testid="experts-container"
size={[8, 8]}>
{expertsList.map((d) => (
<UserTag
id={d.name ?? d.id}
key={'expert' + d.id}
name={getEntityName(d)}
size={UserTagSize.small}
/>
))}
</Space>
)}
</div>
{!isFormInDialog && (
<Space
className="w-full justify-end"
data-testid="cta-buttons"
size={16}>
<Button
data-testid="cancel-domain"
type="link"
onClick={onCancel}>
{t('label.cancel')}
</Button>
<Button
data-testid="save-domain"
disabled={!createPermission}
htmlType="submit"
loading={loading}
type="primary">
{t('label.save')}
</Button>
</Space>
)}
</Form>
</div> </div>
</> <div className="m-t-xss">
{getField(expertsField)}
{Boolean(expertsList.length) && (
<Space
wrap
className="m-b-xs"
data-testid="experts-container"
size={[8, 8]}>
{expertsList.map((d) => (
<UserTag
id={d.name ?? d.id}
key={'expert' + d.id}
name={getEntityName(d)}
size={UserTagSize.small}
/>
))}
</Space>
)}
</div>
{!isFormInDialog && (
<Space
className="w-full justify-end"
data-testid="cta-buttons"
size={16}>
<Button data-testid="cancel-domain" type="link" onClick={onCancel}>
{t('label.cancel')}
</Button>
<Button
data-testid="save-domain"
disabled={!createPermission}
htmlType="submit"
loading={loading}
type="primary">
{t('label.save')}
</Button>
</Space>
)}
</Form>
); );
}; };

View File

@ -19,7 +19,7 @@ export interface AddDomainFormProps {
isFormInDialog: boolean; isFormInDialog: boolean;
onCancel: () => void; onCancel: () => void;
onSubmit: (data: CreateDomain | CreateDataProduct) => Promise<void>; onSubmit: (data: CreateDomain | CreateDataProduct) => Promise<void>;
formRef: FormInstance<CreateDomain | CreateDataProduct>; formRef?: FormInstance<CreateDomain | CreateDataProduct>;
loading: boolean; loading: boolean;
type: DomainFormType; type: DomainFormType;
} }

View File

@ -0,0 +1,122 @@
/*
* Copyright 2024 Collate.
* Licensed 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 { fireEvent, render, screen } from '@testing-library/react';
import React from 'react';
import { DomainFormType } from '../DomainPage.interface';
import AddDomainForm from './AddDomainForm.component';
jest.mock('antd', () => ({
...jest.requireActual('antd'),
Button: jest
.fn()
.mockImplementation(({ loading, children, ...rest }) => (
<button {...rest}>{loading ? 'Loader.Button' : children}</button>
)),
}));
jest.mock('../../../components/common/UserTag/UserTag.component', () => ({
UserTag: jest.fn().mockImplementation(() => <p>UserTag</p>),
}));
jest.mock('../../../context/PermissionProvider/PermissionProvider', () => ({
usePermissionProvider: jest.fn().mockReturnValue({
permissions: {
glossary: {
Create: true,
},
},
}),
}));
jest.mock('../../../utils/EntityUtils', () => ({
getEntityName: jest.fn().mockReturnValue('getEntityName'),
}));
jest.mock('../../../utils/DomainUtils', () => ({
domainTypeTooltipDataRender: jest
.fn()
.mockReturnValue(<p>domainTypeTooltipDataRender</p>),
}));
jest.mock('../../../utils/PermissionsUtils', () => ({
checkPermission: jest.fn().mockReturnValue(true),
}));
const mockOnCancel = jest.fn();
const mockOnSubmit = jest.fn();
const mockProps = {
loading: false,
isFormInDialog: false,
onCancel: mockOnCancel,
onSubmit: mockOnSubmit,
type: DomainFormType.DOMAIN,
};
describe('Test Add Domain component', () => {
it('Should render content of form', async () => {
render(<AddDomainForm {...mockProps} />);
expect(screen.getByText('label.name')).toBeInTheDocument();
expect(screen.getByTestId('name')).toBeInTheDocument();
expect(screen.getByText('label.display-name')).toBeInTheDocument();
expect(screen.getByTestId('display-name')).toBeInTheDocument();
expect(screen.getByText('label.description')).toBeInTheDocument();
expect(screen.getByTestId('editor')).toBeInTheDocument();
expect(screen.getByText('label.icon-url')).toBeInTheDocument();
expect(screen.getByTestId('icon-url')).toBeInTheDocument();
expect(screen.getByText('label.color')).toBeInTheDocument();
expect(screen.getByTestId('color-picker')).toBeInTheDocument();
expect(screen.getByTestId('color-input')).toBeInTheDocument();
expect(screen.getByText('label.domain-type')).toBeInTheDocument();
expect(screen.getAllByTestId('helper-icon')).toHaveLength(2);
expect(screen.getByText('label.owner')).toBeInTheDocument();
expect(screen.getByTestId('add-owner')).toBeInTheDocument();
expect(screen.getByText('label.expert-plural')).toBeInTheDocument();
expect(screen.getByTestId('add-experts')).toBeInTheDocument();
expect(screen.getByTestId('cancel-domain')).toBeInTheDocument();
expect(screen.getByTestId('save-domain')).toBeInTheDocument();
});
it('Should show loading button', async () => {
render(<AddDomainForm {...mockProps} loading />);
expect(screen.getByText('Loader.Button')).toBeInTheDocument();
});
it('Should trigger onCancel', async () => {
render(<AddDomainForm {...mockProps} />);
fireEvent.click(screen.getByText('label.cancel'));
expect(mockOnCancel).toHaveBeenCalled();
});
it('Should not show footer button if form is in dialog box', async () => {
render(<AddDomainForm {...mockProps} isFormInDialog />);
expect(screen.queryByTestId('cta-buttons')).not.toBeInTheDocument();
});
it('Should not trigger onSubmit if required element are not filled', async () => {
render(<AddDomainForm {...mockProps} />);
fireEvent.click(screen.getByTestId('save-domain'));
expect(mockOnSubmit).not.toHaveBeenCalled();
});
it('Should not show domain type if type is not DOMAIN', async () => {
render(<AddDomainForm {...mockProps} type={DomainFormType.DATA_PRODUCT} />);
expect(screen.queryByText('label.domain-type')).not.toBeInTheDocument();
});
});

View File

@ -15,6 +15,7 @@ import {
Button, Button,
Col, Col,
Dropdown, Dropdown,
Modal,
Row, Row,
Space, Space,
Tabs, Tabs,
@ -22,6 +23,7 @@ import {
Typography, Typography,
} from 'antd'; } from 'antd';
import ButtonGroup from 'antd/lib/button/button-group'; import ButtonGroup from 'antd/lib/button/button-group';
import { useForm } from 'antd/lib/form/Form';
import { ItemType } from 'antd/lib/menu/hooks/useItems'; import { ItemType } from 'antd/lib/menu/hooks/useItems';
import { AxiosError } from 'axios'; import { AxiosError } from 'axios';
import classNames from 'classnames'; import classNames from 'classnames';
@ -65,7 +67,6 @@ import {
import { EntityType } from '../../../enums/entity.enum'; import { EntityType } from '../../../enums/entity.enum';
import { SearchIndex } from '../../../enums/search.enum'; import { SearchIndex } from '../../../enums/search.enum';
import { CreateDataProduct } from '../../../generated/api/domains/createDataProduct'; import { CreateDataProduct } from '../../../generated/api/domains/createDataProduct';
import { CreateDomain } from '../../../generated/api/domains/createDomain';
import { DataProduct } from '../../../generated/entity/domains/dataProduct'; import { DataProduct } from '../../../generated/entity/domains/dataProduct';
import { Domain } from '../../../generated/entity/domains/domain'; import { Domain } from '../../../generated/entity/domains/domain';
import { ChangeDescription } from '../../../generated/entity/type'; import { ChangeDescription } from '../../../generated/entity/type';
@ -94,9 +95,9 @@ import TabsLabel from '../../common/TabsLabel/TabsLabel.component';
import { AssetSelectionModal } from '../../DataAssets/AssetsSelectionModal/AssetSelectionModal'; import { AssetSelectionModal } from '../../DataAssets/AssetsSelectionModal/AssetSelectionModal';
import { EntityDetailsObjectInterface } from '../../Explore/ExplorePage.interface'; import { EntityDetailsObjectInterface } from '../../Explore/ExplorePage.interface';
import StyleModal from '../../Modals/StyleModal/StyleModal.component'; import StyleModal from '../../Modals/StyleModal/StyleModal.component';
import AddDataProductModal from '../AddDataProductModal/AddDataProductModal.component'; import AddDomainForm from '../AddDomainForm/AddDomainForm.component';
import '../domain.less'; import '../domain.less';
import { DomainTabs } from '../DomainPage.interface'; import { DomainFormType, DomainTabs } from '../DomainPage.interface';
import DataProductsTab from '../DomainTabs/DataProductsTab/DataProductsTab.component'; import DataProductsTab from '../DomainTabs/DataProductsTab/DataProductsTab.component';
import { DataProductsTabRef } from '../DomainTabs/DataProductsTab/DataProductsTab.interface'; import { DataProductsTabRef } from '../DomainTabs/DataProductsTab/DataProductsTab.interface';
import DocumentationTab from '../DomainTabs/DocumentationTab/DocumentationTab.component'; import DocumentationTab from '../DomainTabs/DocumentationTab/DocumentationTab.component';
@ -109,6 +110,7 @@ const DomainDetailsPage = ({
isVersionsView = false, isVersionsView = false,
}: DomainDetailsPageProps) => { }: DomainDetailsPageProps) => {
const { t } = useTranslation(); const { t } = useTranslation();
const [form] = useForm();
const { getEntityPermission } = usePermissionProvider(); const { getEntityPermission } = usePermissionProvider();
const history = useHistory(); const history = useHistory();
const { tab: activeTab, version } = const { tab: activeTab, version } =
@ -345,6 +347,11 @@ const DomainDetailsPage = ({
setPreviewAsset(asset); setPreviewAsset(asset);
}, []); }, []);
const handleCloseDataProductModal = useCallback(
() => setShowAddDataProductModal(false),
[]
);
const manageButtonContent: ItemType[] = [ const manageButtonContent: ItemType[] = [
...(editDisplayNamePermission ...(editDisplayNamePermission
? ([ ? ([
@ -639,13 +646,41 @@ const DomainDetailsPage = ({
</Row> </Row>
{showAddDataProductModal && ( {showAddDataProductModal && (
<AddDataProductModal <Modal
centered
cancelText={t('label.cancel')}
className="add-data-product-modal"
closable={false}
footer={[
<Button
key="cancel-btn"
type="link"
onClick={handleCloseDataProductModal}>
{t('label.cancel')}
</Button>,
<Button
data-testid="save-data-product"
key="save-btn"
type="primary"
onClick={() => form.submit()}>
{t('label.save')}
</Button>,
]}
maskClosable={false}
okText={t('label.submit')}
open={showAddDataProductModal} open={showAddDataProductModal}
onCancel={() => setShowAddDataProductModal(false)} title={t('label.add-entity', { entity: t('label.data-product') })}
onSubmit={(data: CreateDomain | CreateDataProduct) => width={750}
addDataProduct(data as CreateDataProduct) onCancel={handleCloseDataProductModal}>
} <AddDomainForm
/> isFormInDialog
formRef={form}
loading={false}
type={DomainFormType.DATA_PRODUCT}
onCancel={handleCloseDataProductModal}
onSubmit={addDataProduct}
/>
</Modal>
)} )}
{assetModalVisible && ( {assetModalVisible && (
<AssetSelectionModal <AssetSelectionModal