fix(ui): show message based on pipeline service platform (#12446)

* fix(ui): show message based on pipeline service platform

* chore: only show the message if airflow is not available

* chore: remove tailwind classes

* fix: unit test

* test: add changes unit test

* fix: unit test
This commit is contained in:
Sachin Chaurasiya 2023-07-17 20:34:25 +05:30 committed by GitHub
parent 18ee80d441
commit 5ca757f5e8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 218 additions and 174 deletions

View File

@ -11,14 +11,21 @@
* limitations under the License.
*/
import { getByTestId, render } from '@testing-library/react';
import {
getByTestId,
render,
screen,
waitForElementToBeRemoved,
} from '@testing-library/react';
import React from 'react';
import ErrorPlaceHolderIngestion from './ErrorPlaceHolderIngestion';
describe('Test Error placeholder ingestion Component', () => {
it('Component should render', () => {
it('Component should render', async () => {
const { container } = render(<ErrorPlaceHolderIngestion />);
await waitForElementToBeRemoved(() => screen.getByTestId('loader'));
expect(getByTestId(container, 'error-steps')).toBeInTheDocument();
});
});

View File

@ -13,6 +13,7 @@
import { Card, Space, Typography } from 'antd';
import { ReactComponent as IconCollateSupport } from 'assets/svg/ic-collate-support.svg';
import Loader from 'components/Loader/Loader';
import { AIRFLOW_DOCS } from 'constants/docs.constants';
import { PIPELINE_SERVICE_PLATFORM } from 'constants/Services.constant';
import { useAirflowStatus } from 'hooks/useAirflowStatus';
@ -21,7 +22,7 @@ import React from 'react';
import AirflowMessageBanner from '../AirflowMessageBanner/AirflowMessageBanner';
const ErrorPlaceHolderIngestion = () => {
const { platform } = useAirflowStatus();
const { platform, isFetchingStatus } = useAirflowStatus();
const isAirflowPlatform = platform === PIPELINE_SERVICE_PLATFORM;
@ -68,7 +69,7 @@ const ErrorPlaceHolderIngestion = () => {
return (
<div className="tw-mt-5 tw-text-base tw-font-medium">
{airflowSetupGuide()}
{isFetchingStatus ? <Loader /> : airflowSetupGuide()}
</div>
);
};

View File

@ -11,96 +11,141 @@
* limitations under the License.
*/
import { findByTestId, queryByTestId, render } from '@testing-library/react';
import { render, screen } from '@testing-library/react';
import { PIPELINE_SERVICE_PLATFORM } from 'constants/Services.constant';
import { useAirflowStatus } from 'hooks/useAirflowStatus';
import React from 'react';
import { FormSubmitType } from '../../../enums/form.enum';
import { useAirflowStatus } from '../../../hooks/useAirflowStatus';
import SuccessScreen from './SuccessScreen';
import SuccessScreen, { SuccessScreenProps } from './SuccessScreen';
jest.mock('../../../hooks/useAirflowStatus', () => ({
useAirflowStatus: jest.fn().mockImplementation(() => ({
isAirflowAvailable: true,
fetchAirflowStatus: jest.fn(),
isFetchingStatus: false,
platform: PIPELINE_SERVICE_PLATFORM,
})),
}));
const mockViewService = jest.fn();
const mockDeployService = jest.fn();
const mockIngestService = jest.fn();
const mockProps: SuccessScreenProps = {
name: 'newService',
suffix: 'suffix',
successMessage: 'this is success message',
showIngestionButton: true,
showDeployButton: true,
state: FormSubmitType.ADD,
viewServiceText: 'View New Service',
handleViewServiceClick: mockViewService,
handleDeployClick: mockDeployService,
handleIngestionClick: mockIngestService,
};
describe('Test SuccessScreen component', () => {
it('SuccessScreen component should render', async () => {
const { container } = render(
<SuccessScreen
showIngestionButton
handleViewServiceClick={jest.fn()}
name="NewService"
state={FormSubmitType.ADD}
successMessage={<span>title</span>}
/>
);
render(<SuccessScreen {...mockProps} />);
const succsessScreenContainer = await findByTestId(
container,
const successScreenContainer = await screen.findByTestId(
'success-screen-container'
);
const successIcon = await findByTestId(container, 'success-icon');
const successLine = await findByTestId(container, 'success-line');
const viewServiceBtn = await findByTestId(container, 'view-service-button');
const addIngestionBtn = await findByTestId(
container,
'add-ingestion-button'
);
const statusMsg = queryByTestId(container, 'airflow-status-msg');
const airflowDoc = queryByTestId(container, 'airflow-doc-link');
const statusCheck = queryByTestId(container, 'airflow-status-check');
const successIcon = await screen.findByTestId('success-icon');
const successLine = await screen.findByTestId('success-line');
const viewServiceBtn = await screen.findByTestId('view-service-button');
const addIngestionBtn = await screen.findByTestId('add-ingestion-button');
const deployButton = await screen.findByTestId('deploy-ingestion-button');
const statusMsg = screen.queryByTestId('airflow-platform-message');
expect(successScreenContainer).toBeInTheDocument();
expect(succsessScreenContainer).toBeInTheDocument();
expect(successIcon).toBeInTheDocument();
expect(successLine).toBeInTheDocument();
expect(viewServiceBtn).toBeInTheDocument();
expect(addIngestionBtn).toBeInTheDocument();
expect(deployButton).toBeInTheDocument();
expect(statusMsg).not.toBeInTheDocument();
expect(airflowDoc).not.toBeInTheDocument();
expect(statusCheck).not.toBeInTheDocument();
});
it('SuccessScreen component should render with airflow helper text', async () => {
(useAirflowStatus as jest.Mock).mockImplementation(() => ({
it('Should Render airflow message if pipeline service client is not available and platform is airflow', () => {
(useAirflowStatus as jest.Mock).mockImplementationOnce(() => ({
isAirflowAvailable: false,
fetchAirflowStatus: jest.fn(),
isFetchingStatus: false,
platform: PIPELINE_SERVICE_PLATFORM,
}));
render(<SuccessScreen {...mockProps} />);
const { container } = render(
<SuccessScreen
showIngestionButton
handleViewServiceClick={jest.fn()}
name="NewService"
state={FormSubmitType.ADD}
successMessage={<span>title</span>}
/>
const airflowPlatformMessage = screen.getByTestId(
'airflow-platform-message'
);
const succsessScreenContainer = await findByTestId(
container,
'success-screen-container'
);
const successIcon = await findByTestId(container, 'success-icon');
const successLine = await findByTestId(container, 'success-line');
const viewServiceBtn = await findByTestId(container, 'view-service-button');
const addIngestionBtn = await findByTestId(
container,
'add-ingestion-button'
);
const statusMsg = await findByTestId(container, 'airflow-status-msg');
const airflowDoc = await findByTestId(container, 'airflow-doc-link');
const statusCheck = await findByTestId(container, 'airflow-status-check');
expect(airflowPlatformMessage).toBeInTheDocument();
expect(succsessScreenContainer).toBeInTheDocument();
expect(successIcon).toBeInTheDocument();
expect(successLine).toBeInTheDocument();
expect(viewServiceBtn).toBeInTheDocument();
expect(addIngestionBtn).toBeInTheDocument();
expect(statusMsg).toBeInTheDocument();
expect(airflowDoc).toBeInTheDocument();
expect(statusCheck).toBeInTheDocument();
expect(
screen.getByText('message.manage-airflow-api-failed')
).toBeInTheDocument();
expect(
screen.getByText('message.airflow-guide-message')
).toBeInTheDocument();
expect(
screen.getByText('label.install-airflow-api >>')
).toBeInTheDocument();
});
it('Should Render pipeline scheduler message if pipeline service client is not available and platform is argo', () => {
(useAirflowStatus as jest.Mock).mockImplementationOnce(() => ({
isAirflowAvailable: false,
fetchAirflowStatus: jest.fn(),
isFetchingStatus: false,
platform: 'Argo',
}));
render(<SuccessScreen {...mockProps} />);
const argoPlatformMessage = screen.getByTestId('argo-platform-message');
expect(argoPlatformMessage).toBeInTheDocument();
expect(
screen.getByText('message.pipeline-scheduler-message')
).toBeInTheDocument();
expect(screen.getByTestId('collate-support')).toBeInTheDocument();
});
it('Should not render any message if pipeline service client is available with any platform', () => {
(useAirflowStatus as jest.Mock).mockImplementationOnce(() => ({
isAirflowAvailable: true,
fetchAirflowStatus: jest.fn(),
isFetchingStatus: false,
platform: PIPELINE_SERVICE_PLATFORM,
}));
render(<SuccessScreen {...mockProps} />);
const airflowPlatformMessage = screen.queryByTestId(
'airflow-platform-message'
);
const argoPlatformMessage = screen.queryByTestId('argo-platform-message');
expect(airflowPlatformMessage).not.toBeInTheDocument();
expect(argoPlatformMessage).not.toBeInTheDocument();
});
it('Should render the loader if status is fetching', () => {
(useAirflowStatus as jest.Mock).mockImplementationOnce(() => ({
isAirflowAvailable: false,
fetchAirflowStatus: jest.fn(),
isFetchingStatus: true,
platform: PIPELINE_SERVICE_PLATFORM,
}));
render(<SuccessScreen {...mockProps} />);
expect(screen.getByTestId('loader')).toBeInTheDocument();
});
});

View File

@ -11,22 +11,23 @@
* limitations under the License.
*/
import { Button, Typography } from 'antd';
import classNames from 'classnames';
import { Button, Card, Space, Typography } from 'antd';
import { ReactComponent as IconCollateSupport } from 'assets/svg/ic-collate-support.svg';
import { ReactComponent as IconSuccessBadge } from 'assets/svg/success-badge.svg';
import Loader from 'components/Loader/Loader';
import { AIRFLOW_DOCS } from 'constants/docs.constants';
import { PIPELINE_SERVICE_PLATFORM } from 'constants/Services.constant';
import { isUndefined } from 'lodash';
import React from 'react';
import React, { ReactNode, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { Transi18next } from 'utils/CommonUtils';
import { FormSubmitType } from '../../../enums/form.enum';
import { useAirflowStatus } from '../../../hooks/useAirflowStatus';
import SVGIcons, { Icons } from '../../../utils/SvgUtils';
import Loader from '../../Loader/Loader';
import AirflowMessageBanner from '../AirflowMessageBanner/AirflowMessageBanner';
type SuccessScreenProps = {
export type SuccessScreenProps = {
name: string;
suffix?: string;
successMessage?: JSX.Element;
successMessage?: ReactNode;
showIngestionButton: boolean;
showDeployButton?: boolean;
state: FormSubmitType;
@ -41,7 +42,6 @@ const SuccessScreen = ({
suffix,
showIngestionButton,
showDeployButton = false,
handleIngestionClick,
handleViewServiceClick,
handleDeployClick,
@ -49,111 +49,86 @@ const SuccessScreen = ({
viewServiceText,
}: SuccessScreenProps) => {
const { t } = useTranslation();
const { isAirflowAvailable, fetchAirflowStatus, isFetchingStatus } =
useAirflowStatus();
const { isAirflowAvailable, platform, isFetchingStatus } = useAirflowStatus();
const getAirflowStatusIcon = () => {
let icon;
if (isFetchingStatus) {
icon = <Loader size="small" type="default" />;
} else if (isAirflowAvailable) {
icon = (
<SVGIcons
alt="success"
className="tw-w-5"
data-testid="success-icon"
icon={Icons.SUCCESS_BADGE}
/>
const isAirflowPlatform = useMemo(
() => platform === PIPELINE_SERVICE_PLATFORM,
[platform]
);
} else {
icon = (
<SVGIcons
alt="fail"
className="tw-w-5"
data-testid="fail-icon"
icon={Icons.FAIL_BADGE}
/>
);
}
return icon;
};
const messageElement = useMemo(
() =>
isAirflowPlatform ? (
<div data-testid="airflow-platform-message">
<div>
<h6 className="text-base text-grey-body font-medium">
{t('message.manage-airflow-api-failed')}
</h6>
<p className="text-grey-body text-sm m-b-md">
{t('message.airflow-guide-message')}
</p>
</div>
<p>
<a href={AIRFLOW_DOCS} rel="noopener noreferrer" target="_blank">
{`${t('label.install-airflow-api')} >>`}
</a>
</p>
</div>
) : (
<Space
align="center"
className="justify-center w-full m-t-sm"
data-testid="argo-platform-message"
direction="vertical"
size={16}>
<IconCollateSupport
data-testid="collate-support"
height={100}
width={100}
/>
<Typography>{t('message.pipeline-scheduler-message')}</Typography>
</Space>
),
[isAirflowPlatform]
);
return (
<div
className="d-flex flex-col tw-mt-14 tw-mb-24 tw-mx-8 tw-px-1"
className="d-flex flex-col mt-14 mb-24 mx-8 p-x-xss"
data-testid="success-screen-container">
<div className="d-flex tw-border tw-border-main tw-rounded tw-shadow tw-p-3">
<div className="tw-mr-2">
<SVGIcons
alt="success"
className="tw-w-5"
data-testid="success-icon"
icon={Icons.SUCCESS_BADGE}
/>
</div>
<Typography.Paragraph data-testid="success-line" ellipsis={{ rows: 3 }}>
<Card>
<Space>
<IconSuccessBadge data-testid="success-icon" width="20px" />
<Typography.Paragraph
className="m-b-0"
data-testid="success-line"
ellipsis={{ rows: 3 }}>
{isUndefined(successMessage) ? (
<span>
<span className="tw-mr-1 tw-font-semibold">
<span className="m-r-xss font-semibold">
{`"${name || 'demo_mysql'}"`}
</span>
{suffix && <span className="tw-mr-1">{suffix}</span>}
{suffix && <span className="m-r-xss">{suffix}</span>}
<span>{t('message.has-been-created-successfully')}</span>
</span>
) : (
successMessage
)}
</Typography.Paragraph>
</div>
</Space>
</Card>
{!isAirflowAvailable && (
<div
className="tw-border tw-border-main tw-rounded tw-shadow tw-mt-7 tw-p-3"
data-testid="airflow-status-msg">
<div className="d-flex tw-justify-between items-center">
<div className="d-flex tw-mt-0.5">
<div className="flex-none tw-mr-2">{getAirflowStatusIcon()}</div>
<h6 className="tw-text-base tw-font-medium tw-mb-0.5">
{isAirflowAvailable
? t('message.manage-airflow-api')
: t('message.manage-airflow-api-failed')}
</h6>
</div>
{!isUndefined(fetchAirflowStatus) && (
<div className="flex-none">
<Button
ghost
data-testid="airflow-status-check"
loading={isFetchingStatus}
size="small"
type="primary"
onClick={fetchAirflowStatus}>
{t('label.check-status')}
</Button>
</div>
)}
</div>
{!isAirflowAvailable && (
<Transi18next
i18nKey="message.configure-airflow"
renderElement={
<a
data-testid="airflow-doc-link"
href={AIRFLOW_DOCS}
rel="noopener noreferrer"
target="_blank"
/>
}
values={{
text: t('label.documentation-lowercase'),
}}
/>
)}
</div>
<>
<AirflowMessageBanner className="m-t-sm" />
<Card className="m-t-sm">
{isFetchingStatus ? <Loader /> : <>{messageElement}</>}
</Card>
</>
)}
<div className="tw-mt-7 tw-text-center">
<div className="mt-7 text-center">
<Button
ghost
data-testid="view-service-button"
@ -167,9 +142,7 @@ const SuccessScreen = ({
{showIngestionButton && (
<Button
className={classNames('tw-ml-3.5', {
'tw-opacity-40 tw-pointer-events-none': !isAirflowAvailable,
})}
className="m-l-3.5"
data-testid="add-ingestion-button"
disabled={!isAirflowAvailable}
type="primary"
@ -182,10 +155,8 @@ const SuccessScreen = ({
{showDeployButton && (
<Button
className={classNames('tw-ml-3.5', {
'tw-opacity-40 tw-pointer-events-none': !isAirflowAvailable,
})}
data-testid="add-ingestion-button"
className="m-l-3.5"
data-testid="deploy-ingestion-button"
disabled={!isAirflowAvailable}
type="primary"
onClick={handleDeployClick}>

View File

@ -118,6 +118,11 @@
.m--l-md {
margin-left: -@margin-md;
}
.m-l-3\.5 {
margin-left: 0.875rem;
}
.m-l-0 {
margin-left: 0 !important;
}
@ -170,6 +175,10 @@
margin-top: @margin-xlg;
}
.mt-7 {
margin-top: 1.75rem;
}
.mt-44 {
margin-top: 11rem /* 176px */;
}
@ -285,12 +294,18 @@
.mt-12 {
margin-top: 3rem;
}
.mt-14 {
margin-top: 3.5rem;
}
.mb-10 {
margin-bottom: 2.5rem;
}
.mb-12 {
margin-bottom: 3rem;
}
.mb-24 {
margin-bottom: 6rem;
}
.mt-20 {
margin-top: 5rem;
}
@ -307,6 +322,11 @@
margin: 13rem;
}
.mx-8 {
margin-left: 2rem;
margin-right: 2rem;
}
.my-4 {
margin-top: 1rem;
margin-bottom: 1rem;