mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-10-27 08:44:49 +00:00
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:
parent
18ee80d441
commit
5ca757f5e8
@ -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();
|
||||
});
|
||||
});
|
||||
|
||||
@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@ -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();
|
||||
});
|
||||
});
|
||||
|
||||
@ -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}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
icon = (
|
||||
<SVGIcons
|
||||
alt="fail"
|
||||
className="tw-w-5"
|
||||
data-testid="fail-icon"
|
||||
icon={Icons.FAIL_BADGE}
|
||||
/>
|
||||
);
|
||||
}
|
||||
const isAirflowPlatform = useMemo(
|
||||
() => platform === PIPELINE_SERVICE_PLATFORM,
|
||||
[platform]
|
||||
);
|
||||
|
||||
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 }}>
|
||||
{isUndefined(successMessage) ? (
|
||||
<span>
|
||||
<span className="tw-mr-1 tw-font-semibold">
|
||||
{`"${name || 'demo_mysql'}"`}
|
||||
<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="m-r-xss font-semibold">
|
||||
{`"${name || 'demo_mysql'}"`}
|
||||
</span>
|
||||
{suffix && <span className="m-r-xss">{suffix}</span>}
|
||||
<span>{t('message.has-been-created-successfully')}</span>
|
||||
</span>
|
||||
{suffix && <span className="tw-mr-1">{suffix}</span>}
|
||||
<span>{t('message.has-been-created-successfully')}</span>
|
||||
</span>
|
||||
) : (
|
||||
successMessage
|
||||
)}
|
||||
</Typography.Paragraph>
|
||||
</div>
|
||||
|
||||
{!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>
|
||||
) : (
|
||||
successMessage
|
||||
)}
|
||||
</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>
|
||||
</Typography.Paragraph>
|
||||
</Space>
|
||||
</Card>
|
||||
{!isAirflowAvailable && (
|
||||
<>
|
||||
<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}>
|
||||
|
||||
@ -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;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user