Test suits for home page (#571)

* Adding test-suits for UI

* fixed broken tests

* fixed all test fail files

* removed parsing errors from files

* resolved test-suits error

* fiexed all the failing test

* fixed tabledatacard title padding

* fixed popOver test

* fixed eslint issue in file

* added eslint disable on top of file

* add test for searchData and MyDataHeader

* add addtional test for teams page

Co-authored-by: darth-coder00 <aashit@getcollate.io>
This commit is contained in:
Shailesh Parmar 2021-09-27 11:16:48 +05:30 committed by GitHub
parent 627481f181
commit 1bac212d5e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 587 additions and 14 deletions

View File

@ -19,14 +19,14 @@ import { getAllByTestId, render } from '@testing-library/react';
import { TableDetail } from 'Models';
import React from 'react';
import { MemoryRouter } from 'react-router-dom';
import { Column, ColumnDataType } from '../../generated/entity/data/table';
import { ColumnDataType } from '../../generated/entity/data/table';
import SchemaTable from './SchemaTable';
jest.mock('../common/rich-text-editor/RichTextEditorPreviewer', () => {
return jest.fn().mockReturnValue(<p>RichTextEditorPreviewer</p>);
});
const mockColumns: Column[] = [
const mockColumns = [
{
name: 'testId',
columnDataType: ColumnDataType.String,

View File

@ -0,0 +1,163 @@
/*
* 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 { getByTestId, getByText, render } from '@testing-library/react';
import React from 'react';
import { MemoryRouter } from 'react-router';
import MyDataHeader from './MyDataHeader';
describe('Test MyDataHeader Component', () => {
it('Component should render', () => {
const { container } = render(
<MyDataHeader
countAssets={3}
countServices={193}
entityCounts={{
tableCount: 40,
topicCount: 13,
dashboardCount: 10,
}}
/>,
{
wrapper: MemoryRouter,
}
);
const myDataHeader = getByTestId(container, 'data-header-container');
expect(myDataHeader).toBeInTheDocument();
});
it('Should have main title', () => {
const { container } = render(
<MyDataHeader
countAssets={3}
countServices={193}
entityCounts={{
tableCount: 40,
topicCount: 13,
dashboardCount: 10,
}}
/>,
{
wrapper: MemoryRouter,
}
);
const mainTitle = getByTestId(container, 'main-title');
expect(mainTitle).toBeInTheDocument();
});
it('Should have 3 state box', () => {
const { container } = render(
<MyDataHeader
countAssets={3}
countServices={193}
entityCounts={{
tableCount: 40,
topicCount: 13,
dashboardCount: 10,
}}
/>,
{
wrapper: MemoryRouter,
}
);
const stateBox = getByTestId(container, 'states-box-container');
expect(stateBox.childElementCount).toBe(3);
expect(getByText(container, /Explore Assets/i)).toBeInTheDocument();
expect(getByText(container, /Register Services/i)).toBeInTheDocument();
expect(getByText(container, /Knowledgebase/i)).toBeInTheDocument();
});
it('Should have 6 data summary details', () => {
const { container } = render(
<MyDataHeader
countAssets={3}
countServices={193}
entityCounts={{
tableCount: 40,
topicCount: 13,
dashboardCount: 10,
}}
/>,
{
wrapper: MemoryRouter,
}
);
const dataSummary = getByTestId(container, 'data-summary-container');
expect(dataSummary.childElementCount).toBe(6);
});
it('Should display same count as provided by props', () => {
const { container } = render(
<MyDataHeader
countAssets={193}
countServices={4}
entityCounts={{
tableCount: 40,
topicCount: 13,
dashboardCount: 10,
}}
/>,
{
wrapper: MemoryRouter,
}
);
expect(getByText(container, /40 tables/i)).toBeInTheDocument();
expect(getByText(container, /13 topics/i)).toBeInTheDocument();
expect(getByText(container, /10 dashboards/i)).toBeInTheDocument();
expect(getByText(container, /4 of services/i)).toBeInTheDocument();
expect(getByText(container, /193 assets/i)).toBeInTheDocument();
});
it('OnClick it should redirect to respective page', () => {
const { container } = render(
<MyDataHeader
countAssets={193}
countServices={4}
entityCounts={{
tableCount: 40,
topicCount: 13,
dashboardCount: 10,
}}
/>,
{
wrapper: MemoryRouter,
}
);
const tables = getByTestId(container, 'tables');
const topics = getByTestId(container, 'topics');
const dashboards = getByTestId(container, 'dashboards');
const service = getByTestId(container, 'service');
const user = getByTestId(container, 'user');
const terms = getByTestId(container, 'terms');
expect(tables).toHaveAttribute('href', '/explore/tables');
expect(topics).toHaveAttribute('href', '/explore/topics');
expect(dashboards).toHaveAttribute('href', '/explore/dashboards');
expect(service).toHaveAttribute('href', '/services');
expect(user).toHaveAttribute('href', '/teams');
expect(terms).toHaveAttribute('href', '/teams');
});
});

View File

@ -1,3 +1,20 @@
/*
* 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 classNames from 'classnames';
import { observer } from 'mobx-react';
import React, { FunctionComponent, useEffect, useState } from 'react';
@ -19,6 +36,7 @@ type Summary = {
icon: string;
data: string;
link?: string;
dataTestId?: string;
};
const LANDING_STATES = [
@ -46,7 +64,7 @@ const MyDataHeader: FunctionComponent<Props> = ({
const [dataSummary, setdataSummary] = useState<Record<string, Summary>>({});
const getFormattedDescription = (description: string) => {
return description.replaceAll('{countAssets}', countAssets.toString());
return description.replace(/{countAssets}/g, countAssets.toString());
};
const getSummarydata = () => {
@ -55,31 +73,37 @@ const MyDataHeader: FunctionComponent<Props> = ({
icon: Icons.TABLE_GREY,
data: `${entityCounts.tableCount} Tables`,
link: `/explore/tables`,
dataTestId: 'tables',
},
topics: {
icon: Icons.TOPIC_GREY,
data: `${entityCounts.topicCount} Topics`,
link: `/explore/topics`,
dataTestId: 'topics',
},
dashboards: {
icon: Icons.DASHBOARD_GREY,
data: `${entityCounts.dashboardCount} Dashboards`,
link: `/explore/dashboards`,
dataTestId: 'dashboards',
},
service: {
icon: Icons.SERVICE,
data: `${countServices} of Services`,
link: `/services`,
dataTestId: 'service',
},
user: {
icon: Icons.USERS,
data: `${users.length} of Users`,
link: `/teams`,
dataTestId: 'user',
},
terms: {
icon: Icons.TERMS,
data: `${userTeams.length} of Teams`,
link: `/teams`,
dataTestId: 'terms',
},
};
};
@ -95,12 +119,14 @@ const MyDataHeader: FunctionComponent<Props> = ({
}, [userTeams, users, countServices]);
return (
<section className="tw-flex tw-flex-col tw-items-center tw-py-7">
<h3 className="tw-mb-7 tw-font-semibold ">
<section
className="tw-flex tw-flex-col tw-items-center tw-py-7"
data-testid="data-header-container">
<h3 className="tw-mb-7 tw-font-semibold " data-testid="main-title">
<span className="tw-text-primary-II">Open</span>
<span className="tw-text-primary">Metadata</span>
</h3>
<div className="tw-flex tw-mb-7">
<div className="tw-flex tw-mb-7" data-testid="data-summary-container">
{Object.values(dataSummary).map((data, index) => (
<div
className={classNames('tw-flex tw-items-center', {
@ -109,7 +135,10 @@ const MyDataHeader: FunctionComponent<Props> = ({
key={index}>
<SVGIcons alt="icon" className="tw-h-4 tw-w-4" icon={data.icon} />
{data.link ? (
<Link className="tw-font-medium tw-pl-2" to={data.link}>
<Link
className="tw-font-medium tw-pl-2"
data-testid={data.dataTestId}
to={data.link}>
<button className="tw-text-grey-body hover:tw-text-primary-hover hover:tw-underline">
{data.data}
</button>
@ -121,7 +150,7 @@ const MyDataHeader: FunctionComponent<Props> = ({
))}
</div>
<div className="tw-flex">
<div className="tw-flex" data-testid="states-box-container">
{LANDING_STATES.map((d, i) => (
<div
className={classNames(

View File

@ -0,0 +1,184 @@
/*
* 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 {
getAllByTestId,
getByTestId,
getByText,
render,
} from '@testing-library/react';
import React from 'react';
import { MemoryRouter } from 'react-router';
import SearchedData from './SearchedData';
const mockData = [
{
id: 'id1',
name: 'name1',
description: 'description1',
fullyQualifiedName: 'fullyQualifiedName1',
owner: 'owner1',
tags: ['tags1', 'tags2', 'tags3'],
tier: 'tier1',
index: 'index1',
},
{
id: 'id2',
name: 'name2',
description: 'description2',
fullyQualifiedName: 'fullyQualifiedName2',
owner: 'owner2',
tags: ['tags1', 'tags2', 'tags3'],
tier: 'tier2',
index: 'index1',
},
{
id: 'id3',
name: 'name3',
description: 'description3',
fullyQualifiedName: 'fullyQualifiedName3',
owner: 'owner3',
tags: ['tags1', 'tags2', 'tags3'],
tier: 'tier3',
index: 'index1',
},
];
const mockPaginate = jest.fn();
jest.mock('../common/table-data-card/TableDataCard', () => {
return jest
.fn()
.mockReturnValue(<p data-testid="table-data-card">TableDataCard</p>);
});
jest.mock('../Pagination', () => {
return jest.fn().mockReturnValue(<p>Pagination</p>);
});
jest.mock('../onboarding/Onboarding', () => {
return jest.fn().mockReturnValue(<p>Onboarding</p>);
});
jest.mock('../common/error-with-placeholder/ErrorPlaceHolderES', () => {
return jest.fn().mockReturnValue(<p>ErrorPlaceHolderES</p>);
});
describe('Test SearchedData Component', () => {
it('Component should render', () => {
const { container } = render(
<SearchedData
currentPage={0}
data={mockData}
paginate={mockPaginate}
totalValue={10}
/>,
{
wrapper: MemoryRouter,
}
);
const searchedDataContainer = getByTestId(container, 'fluid-container');
expect(searchedDataContainer).toBeInTheDocument();
});
it('Should display table card according to data provided in props', () => {
const { container } = render(
<SearchedData
currentPage={0}
data={mockData}
paginate={mockPaginate}
totalValue={10}
/>,
{
wrapper: MemoryRouter,
}
);
const searchedDataContainer = getAllByTestId(container, 'table-data-card');
expect(searchedDataContainer.length).toBe(3);
});
it('If children is provided it should display', () => {
const { container } = render(
<SearchedData
currentPage={0}
data={mockData}
paginate={mockPaginate}
totalValue={10}>
<p>hello world</p>
</SearchedData>,
{
wrapper: MemoryRouter,
}
);
expect(getByText(container, /hello world/i)).toBeInTheDocument();
});
it('Pagination Should be there if data is more than 10 count', () => {
const { container } = render(
<SearchedData
currentPage={0}
data={mockData}
paginate={mockPaginate}
totalValue={11}>
<p>hello world</p>
</SearchedData>,
{
wrapper: MemoryRouter,
}
);
expect(getByText(container, /Pagination/i)).toBeInTheDocument();
});
it('Onboarding component should display if there is showOnboardingTemplate is true', () => {
const { container } = render(
<SearchedData
showOnboardingTemplate
currentPage={0}
data={[]}
paginate={mockPaginate}
totalValue={0}
/>,
{
wrapper: MemoryRouter,
}
);
expect(getByText(container, /Onboarding/i)).toBeInTheDocument();
});
it('ErrorPlaceHolderES component should display if there is no data', () => {
const { container } = render(
<SearchedData
currentPage={0}
data={[]}
paginate={mockPaginate}
totalValue={0}
/>,
{
wrapper: MemoryRouter,
}
);
expect(getByText(container, /ErrorPlaceHolderES/i)).toBeInTheDocument();
});
});

View File

@ -15,10 +15,85 @@
* limitations under the License.
*/
import { findByTestId, render } from '@testing-library/react';
import { findByTestId, fireEvent, render } from '@testing-library/react';
import React, { ReactNode } from 'react';
import TeamsPage from './index';
const mockTeamsData = [
{
description: '',
displayName: 'Cloud Infra',
href: 'href1',
id: 'id1',
name: 'Cloud_Infra',
},
{
description: '',
displayName: 'Cloud Infra',
href: 'href1',
id: 'id1',
name: 'Cloud_Infra',
},
{
description: '',
displayName: 'Cloud Infra',
href: 'href1',
id: 'id1',
name: 'Cloud_Infra',
},
];
const mockDataTeamByName = {
description: '',
displayName: 'Customer Support',
href: 'http://localhost:8585/api/v1/teams/f6b906cc-005b-4d68-b7f7-62d591a8d9dd',
id: 'f6b906cc-005b-4d68-b7f7-62d591a8d9dd',
name: 'Customer_Support',
owns: [
{
description: 'Robert Mitchell',
href: 'href',
id: 'id1',
name: 'robert_mitchell6',
type: 'user',
},
{
description: 'Shane Davis',
href: 'href',
id: 'id2',
name: 'shane_davis8',
type: 'user',
},
],
users: [
{
description: 'Robert Mitchell',
href: 'href',
id: 'id1',
name: 'robert_mitchell6',
type: 'user',
},
{
description: 'Shane Davis',
href: 'href',
id: 'id2',
name: 'shane_davis8',
type: 'user',
},
],
};
jest.mock('../../axiosAPIs/teamsAPI', () => ({
createTeam: jest.fn(),
getTeamByName: jest
.fn()
.mockImplementation(() => Promise.resolve({ data: mockDataTeamByName })),
getTeams: jest
.fn()
.mockImplementation(() => Promise.resolve({ data: mockTeamsData })),
patchTeamDetail: jest.fn(),
}));
jest.mock(
'../../components/common/rich-text-editor/RichTextEditorPreviewer',
() => {
@ -26,6 +101,10 @@ jest.mock(
}
);
jest.mock('../../components/Modals/FormModal', () => {
return jest.fn().mockReturnValue(<p data-testid="form-modal">FormModal</p>);
});
jest.mock(
'../../components/containers/PageContainer',
() =>
@ -44,6 +123,27 @@ jest.mock(
)
);
jest.mock('./AddUsersModal', () => {
return jest
.fn()
.mockReturnValue(<p data-testid="add-user-modal">AddUsersModal</p>);
});
jest.mock('../../components/common/non-admin-action/NonAdminAction', () => {
return jest
.fn()
.mockImplementation(({ children }: { children: ReactNode }) => (
<div>{children}</div>
));
});
jest.mock(
'../../components/Modals/ModalWithMarkdownEditor/ModalWithMarkdownEditor',
() => ({
ModalWithMarkdownEditor: jest.fn(),
})
);
describe('Test Teams page', () => {
it('Check for heading', async () => {
const { container } = render(<TeamsPage />);
@ -52,8 +152,90 @@ describe('Test Teams page', () => {
container,
'left-panel-content'
);
const header = await findByTestId(container, 'header');
const userCard = await findByTestId(container, 'user-card-container');
expect(teamComponent).toBeInTheDocument();
expect(leftPanelContent).toBeInTheDocument();
expect(header).toBeInTheDocument();
expect(userCard).toBeInTheDocument();
});
it('OnClick of assets tab, assets tab should display', async () => {
const { container } = render(<TeamsPage />);
const assets = await findByTestId(container, 'assets');
fireEvent.click(
assets,
new MouseEvent('click', {
bubbles: true,
cancelable: true,
})
);
expect(await findByTestId(container, 'dataset-card')).toBeInTheDocument();
});
it('OnClick of add new user, AddUsersModal should display', async () => {
const { container } = render(<TeamsPage />);
const addNewUser = await findByTestId(container, 'add-new-user-button');
expect(addNewUser).toBeInTheDocument();
fireEvent.click(
addNewUser,
new MouseEvent('click', {
bubbles: true,
cancelable: true,
})
);
expect(await findByTestId(container, 'add-user-modal')).toBeInTheDocument();
});
it('Should have 2 tabs in the page', async () => {
const { container } = render(<TeamsPage />);
const tabs = await findByTestId(container, 'tabs');
const user = await findByTestId(container, 'users');
const asstes = await findByTestId(container, 'assets');
expect(tabs.childElementCount).toBe(2);
expect(user).toBeInTheDocument();
expect(asstes).toBeInTheDocument();
});
it('Description should be in document', async () => {
const { container } = render(<TeamsPage />);
const descriptionContainer = await findByTestId(
container,
'description-container'
);
const description = await findByTestId(container, 'description');
const addDescription = await findByTestId(container, 'add-description');
expect(descriptionContainer).toBeInTheDocument();
expect(addDescription).toBeInTheDocument();
expect(description).toBeInTheDocument();
});
it('onClick of add team button, FormModal should open', async () => {
const { container } = render(<TeamsPage />);
const addButton = await findByTestId(container, 'add-teams');
expect(addButton).toBeInTheDocument();
fireEvent.click(
addButton,
new MouseEvent('click', {
bubbles: true,
cancelable: true,
})
);
expect(await findByTestId(container, 'form-modal')).toBeInTheDocument();
});
});

View File

@ -149,9 +149,12 @@ const TeamsPage = () => {
const getTabs = () => {
return (
<div className="tw-mb-3 ">
<nav className="tw-flex tw-flex-row tw-gh-tabs-container tw-px-4">
<nav
className="tw-flex tw-flex-row tw-gh-tabs-container tw-px-4"
data-testid="tabs">
<button
className={`tw-pb-2 tw-px-4 tw-gh-tabs ${getActiveTabClass(1)}`}
data-testid="users"
onClick={() => {
setCurrentTab(1);
}}>
@ -160,6 +163,7 @@ const TeamsPage = () => {
</button>
<button
className={`tw-pb-2 tw-px-4 tw-gh-tabs ${getActiveTabClass(2)}`}
data-testid="assets"
onClick={() => {
setCurrentTab(2);
}}>
@ -193,7 +197,9 @@ const TeamsPage = () => {
return (
<>
<div className="tw-grid xl:tw-grid-cols-4 md:tw-grid-cols-2 tw-gap-4">
<div
className="tw-grid xl:tw-grid-cols-4 md:tw-grid-cols-2 tw-gap-4"
data-testid="user-card-container">
{currentTeam?.users.map((user, index) => {
const User = {
description: user.description,
@ -236,7 +242,9 @@ const TeamsPage = () => {
return (
<>
<div className="tw-grid xl:tw-grid-cols-4 md:tw-grid-cols-2 tw-gap-4">
<div
className="tw-grid xl:tw-grid-cols-4 md:tw-grid-cols-2 tw-gap-4"
data-testid="dataset-card">
{' '}
{currentTeam?.owns.map((dataset, index) => {
const Dataset = { description: dataset.name, name: dataset.type };
@ -257,6 +265,7 @@ const TeamsPage = () => {
<NonAdminAction position="bottom" title={TITLE_FOR_NON_ADMIN_ACTION}>
<Button
className="tw-h-7 tw-px-2"
data-testid="add-teams"
size="small"
theme="primary"
variant="contained"
@ -349,7 +358,9 @@ const TeamsPage = () => {
<div
className="container-fluid tw-pt-1 tw-pb-3"
data-testid="team-container">
<div className="tw-flex tw-justify-between tw-pl-1">
<div
className="tw-flex tw-justify-between tw-pl-1"
data-testid="header">
<div className="tw-heading tw-text-link tw-text-base">
{currentTeam?.displayName}
</div>
@ -358,6 +369,7 @@ const TeamsPage = () => {
title={TITLE_FOR_NON_ADMIN_ACTION}>
<Button
className="tw-h-8 tw-rounded tw-mb-2"
data-testid="add-new-user-button"
size="small"
theme="primary"
variant="contained"
@ -366,7 +378,9 @@ const TeamsPage = () => {
</Button>
</NonAdminAction>
</div>
<div className="tw-flex tw-flex-col tw-border tw-rounded-md tw-mb-3 tw-min-h-32 tw-bg-white">
<div
className="tw-flex tw-flex-col tw-border tw-rounded-md tw-mb-3 tw-min-h-32 tw-bg-white"
data-testid="description-container">
<div className="tw-flex tw-items-center tw-px-3 tw-py-1 tw-border-b">
<span className="tw-flex-1 tw-leading-8 tw-m-0 tw-font-normal">
Description
@ -377,6 +391,7 @@ const TeamsPage = () => {
title={TITLE_FOR_NON_ADMIN_ACTION}>
<button
className="focus:tw-outline-none"
data-testid="add-description"
onClick={onDescriptionEdit}>
<SVGIcons
alt="edit"