mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-08-27 18:36:08 +00:00
fix kpi data not showing in widget (#17594)
This commit is contained in:
parent
60ed221cf1
commit
7813d82b91
@ -22,6 +22,11 @@ test.use({ storageState: 'playwright/.auth/admin.json' });
|
|||||||
|
|
||||||
test.describe.configure({ mode: 'serial' });
|
test.describe.configure({ mode: 'serial' });
|
||||||
|
|
||||||
|
const DESCRIPTION_WITH_PERCENTAGE =
|
||||||
|
'playwright-description-with-percentage-percentage';
|
||||||
|
|
||||||
|
const DESCRIPTION_WITH_OWNER = 'playwright-owner-with-percentage-percentage';
|
||||||
|
|
||||||
test.describe('Data Insight Page', { tag: '@data-insight' }, () => {
|
test.describe('Data Insight Page', { tag: '@data-insight' }, () => {
|
||||||
test.beforeAll(async ({ browser }) => {
|
test.beforeAll(async ({ browser }) => {
|
||||||
const { apiContext } = await createNewPage(browser);
|
const { apiContext } = await createNewPage(browser);
|
||||||
@ -137,14 +142,10 @@ test.describe('Data Insight Page', { tag: '@data-insight' }, () => {
|
|||||||
|
|
||||||
await expect(page.getByTestId('kpi-card')).toBeVisible();
|
await expect(page.getByTestId('kpi-card')).toBeVisible();
|
||||||
await expect(
|
await expect(
|
||||||
page.locator(
|
page.locator(`[data-row-key=${DESCRIPTION_WITH_PERCENTAGE}]`)
|
||||||
'[data-row-key="playwright-description-with-percentage-percentage"]'
|
|
||||||
)
|
|
||||||
).toBeVisible();
|
).toBeVisible();
|
||||||
await expect(
|
await expect(
|
||||||
page.locator(
|
page.locator(`[data-row-key=${DESCRIPTION_WITH_OWNER}]`)
|
||||||
'[data-row-key="playwright-owner-with-percentage-percentage"]'
|
|
||||||
)
|
|
||||||
).toBeVisible();
|
).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -162,6 +163,20 @@ test.describe('Data Insight Page', { tag: '@data-insight' }, () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('Verify KPI widget in Landing page', async ({ page }) => {
|
||||||
|
const kpiResponse = page.waitForResponse('/api/v1/kpi/*/kpiResult?*');
|
||||||
|
|
||||||
|
await redirectToHomePage(page);
|
||||||
|
|
||||||
|
await kpiResponse;
|
||||||
|
|
||||||
|
expect(page.locator('[data-testid="kpi-widget"]')).toBeVisible();
|
||||||
|
|
||||||
|
// description and owner data to be visible
|
||||||
|
expect(page.getByTestId(DESCRIPTION_WITH_PERCENTAGE)).toBeVisible();
|
||||||
|
expect(page.getByTestId(DESCRIPTION_WITH_OWNER)).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
test('Delete Kpi', async ({ page }) => {
|
test('Delete Kpi', async ({ page }) => {
|
||||||
await page.waitForResponse(
|
await page.waitForResponse(
|
||||||
'/api/v1/kpi/playwright-owner-with-percentage-percentage/latestKpiResult'
|
'/api/v1/kpi/playwright-owner-with-percentage-percentage/latestKpiResult'
|
||||||
|
@ -33,12 +33,11 @@ import {
|
|||||||
} from '../../../../constants/constants';
|
} from '../../../../constants/constants';
|
||||||
import { DATA_INSIGHT_GRAPH_COLORS } from '../../../../constants/DataInsight.constants';
|
import { DATA_INSIGHT_GRAPH_COLORS } from '../../../../constants/DataInsight.constants';
|
||||||
import { DATA_INSIGHT_DOCS } from '../../../../constants/docs.constants';
|
import { DATA_INSIGHT_DOCS } from '../../../../constants/docs.constants';
|
||||||
import { SIZE } from '../../../../enums/common.enum';
|
import { ERROR_PLACEHOLDER_TYPE, SIZE } from '../../../../enums/common.enum';
|
||||||
import { WidgetWidths } from '../../../../enums/CustomizablePage.enum';
|
import { WidgetWidths } from '../../../../enums/CustomizablePage.enum';
|
||||||
import { TabSpecificField } from '../../../../enums/entity.enum';
|
import { TabSpecificField } from '../../../../enums/entity.enum';
|
||||||
import { Kpi, KpiResult } from '../../../../generated/dataInsight/kpi/kpi';
|
import { Kpi, KpiResult } from '../../../../generated/dataInsight/kpi/kpi';
|
||||||
import { UIKpiResult } from '../../../../interface/data-insight.interface';
|
import { UIKpiResult } from '../../../../interface/data-insight.interface';
|
||||||
import { useDataInsightProvider } from '../../../../pages/DataInsightPage/DataInsightProvider';
|
|
||||||
import { DataInsightCustomChartResult } from '../../../../rest/DataInsightAPI';
|
import { DataInsightCustomChartResult } from '../../../../rest/DataInsightAPI';
|
||||||
import {
|
import {
|
||||||
getLatestKpiResult,
|
getLatestKpiResult,
|
||||||
@ -46,42 +45,17 @@ import {
|
|||||||
getListKPIs,
|
getListKPIs,
|
||||||
} from '../../../../rest/KpiAPI';
|
} from '../../../../rest/KpiAPI';
|
||||||
import { Transi18next } from '../../../../utils/CommonUtils';
|
import { Transi18next } from '../../../../utils/CommonUtils';
|
||||||
import { customFormatDateTime } from '../../../../utils/date-time/DateTimeUtils';
|
import {
|
||||||
|
customFormatDateTime,
|
||||||
|
getCurrentMillis,
|
||||||
|
getEpochMillisForPastDays,
|
||||||
|
} from '../../../../utils/date-time/DateTimeUtils';
|
||||||
import { showErrorToast } from '../../../../utils/ToastUtils';
|
import { showErrorToast } from '../../../../utils/ToastUtils';
|
||||||
|
import ErrorPlaceHolder from '../../../common/ErrorWithPlaceholder/ErrorPlaceHolder';
|
||||||
import KPILatestResultsV1 from '../../../DataInsight/KPILatestResultsV1';
|
import KPILatestResultsV1 from '../../../DataInsight/KPILatestResultsV1';
|
||||||
import './kpi-widget.less';
|
import './kpi-widget.less';
|
||||||
import { KPIWidgetProps } from './KPIWidget.interface';
|
import { KPIWidgetProps } from './KPIWidget.interface';
|
||||||
|
|
||||||
const EmptyPlaceholder = () => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="flex-center flex-col h-full p-t-sm">
|
|
||||||
<KPIEmptyIcon width={SIZE.X_SMALL} />
|
|
||||||
<div className="m-t-xs text-center">
|
|
||||||
<Typography.Paragraph style={{ marginBottom: '0' }}>
|
|
||||||
{t('message.no-kpi')}
|
|
||||||
</Typography.Paragraph>
|
|
||||||
<Typography.Paragraph>
|
|
||||||
<Transi18next
|
|
||||||
i18nKey="message.refer-to-our-doc"
|
|
||||||
renderElement={
|
|
||||||
<Link
|
|
||||||
rel="noreferrer"
|
|
||||||
target="_blank"
|
|
||||||
to={{ pathname: DATA_INSIGHT_DOCS }}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
values={{
|
|
||||||
doc: t('label.doc-plural-lowercase'),
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Typography.Paragraph>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const KPIWidget = ({
|
const KPIWidget = ({
|
||||||
isEditView = false,
|
isEditView = false,
|
||||||
selectedDays = CHART_WIDGET_DAYS_DURATION,
|
selectedDays = CHART_WIDGET_DAYS_DURATION,
|
||||||
@ -98,12 +72,11 @@ const KPIWidget = ({
|
|||||||
const [kpiLatestResults, setKpiLatestResults] =
|
const [kpiLatestResults, setKpiLatestResults] =
|
||||||
useState<Record<string, UIKpiResult>>();
|
useState<Record<string, UIKpiResult>>();
|
||||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||||
const { chartFilter } = useDataInsightProvider();
|
|
||||||
|
|
||||||
const getKPIResult = async (kpi: Kpi) => {
|
const getKPIResult = async (kpi: Kpi) => {
|
||||||
const response = await getListKpiResult(kpi.fullyQualifiedName ?? '', {
|
const response = await getListKpiResult(kpi.fullyQualifiedName ?? '', {
|
||||||
startTs: chartFilter.startTs,
|
startTs: getEpochMillisForPastDays(selectedDays),
|
||||||
endTs: chartFilter.endTs,
|
endTs: getCurrentMillis(),
|
||||||
});
|
});
|
||||||
|
|
||||||
return { name: kpi.name, data: response.results };
|
return { name: kpi.name, data: response.results };
|
||||||
@ -253,7 +226,28 @@ const KPIWidget = ({
|
|||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
{isEmpty(kpiList) || isEmpty(kpiResults) ? (
|
{isEmpty(kpiList) || isEmpty(kpiResults) ? (
|
||||||
<EmptyPlaceholder />
|
<ErrorPlaceHolder
|
||||||
|
icon={<KPIEmptyIcon height={SIZE.X_SMALL} width={SIZE.X_SMALL} />}
|
||||||
|
type={ERROR_PLACEHOLDER_TYPE.CUSTOM}>
|
||||||
|
<Typography.Paragraph style={{ marginBottom: '0' }}>
|
||||||
|
{t('message.no-kpi')}
|
||||||
|
</Typography.Paragraph>
|
||||||
|
<Typography.Paragraph>
|
||||||
|
<Transi18next
|
||||||
|
i18nKey="message.refer-to-our-doc"
|
||||||
|
renderElement={
|
||||||
|
<Link
|
||||||
|
rel="noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
to={{ pathname: DATA_INSIGHT_DOCS }}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
values={{
|
||||||
|
doc: t('label.doc-plural-lowercase'),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Typography.Paragraph>
|
||||||
|
</ErrorPlaceHolder>
|
||||||
) : (
|
) : (
|
||||||
<Row className="p-t-md">
|
<Row className="p-t-md">
|
||||||
<Col span={isWidgetSizeMedium ? 14 : 24}>
|
<Col span={isWidgetSizeMedium ? 14 : 24}>
|
||||||
|
@ -0,0 +1,145 @@
|
|||||||
|
/*
|
||||||
|
* 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 { act } from 'react-test-renderer';
|
||||||
|
import { WidgetWidths } from '../../../../enums/CustomizablePage.enum';
|
||||||
|
import { MOCK_KPI_LIST_RESPONSE } from '../../../../pages/KPIPage/KPIMock.mock';
|
||||||
|
import { getListKPIs } from '../../../../rest/KpiAPI';
|
||||||
|
import KPIWidget from './KPIWidget.component';
|
||||||
|
|
||||||
|
jest.mock('../../../../constants/DataInsight.constants', () => ({
|
||||||
|
DATA_INSIGHT_GRAPH_COLORS: ['#E7B85D'],
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock('../../../../constants/constants', () => ({
|
||||||
|
CHART_WIDGET_DAYS_DURATION: 14,
|
||||||
|
GRAPH_BACKGROUND_COLOR: '#000000',
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock('../../../../utils/date-time/DateTimeUtils', () => ({
|
||||||
|
customFormatDateTime: jest.fn().mockReturnValue('Dec 05, 11:54'),
|
||||||
|
getCurrentMillis: jest.fn().mockReturnValue(1711583974000),
|
||||||
|
getEpochMillisForPastDays: jest.fn().mockReturnValue(1709424034000),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock('../../../../rest/DataInsightAPI', () => ({
|
||||||
|
DataInsightCustomChartResult: jest
|
||||||
|
.fn()
|
||||||
|
.mockImplementation(() => Promise.resolve()),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock('../../../../rest/KpiAPI', () => ({
|
||||||
|
getLatestKpiResult: jest.fn().mockImplementation(() =>
|
||||||
|
Promise.resolve({
|
||||||
|
timestamp: 1724760319723,
|
||||||
|
kpiFqn: 'description-percentage',
|
||||||
|
targetResult: [
|
||||||
|
{
|
||||||
|
value: '23.52941176470588',
|
||||||
|
targetMet: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
),
|
||||||
|
getListKpiResult: jest.fn().mockImplementation(() =>
|
||||||
|
Promise.resolve({
|
||||||
|
results: [
|
||||||
|
{
|
||||||
|
count: 23.52941176470588,
|
||||||
|
day: 1724716800000,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
),
|
||||||
|
getListKPIs: jest
|
||||||
|
.fn()
|
||||||
|
.mockImplementation(() => Promise.resolve(MOCK_KPI_LIST_RESPONSE)),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock('../../../../utils/ToastUtils', () => ({
|
||||||
|
showErrorToast: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock('../../../../utils/CommonUtils', () => ({
|
||||||
|
Transi18next: jest.fn().mockReturnValue('text'),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock('../../../DataInsight/KPILatestResultsV1', () =>
|
||||||
|
jest.fn().mockReturnValue(<p>KPILatestResultsV1.Component</p>)
|
||||||
|
);
|
||||||
|
|
||||||
|
jest.mock('../../../common/ErrorWithPlaceholder/ErrorPlaceHolder', () =>
|
||||||
|
jest.fn().mockReturnValue(<p>ErrorPlaceHolder.Component</p>)
|
||||||
|
);
|
||||||
|
|
||||||
|
const mockHandleRemoveWidget = jest.fn();
|
||||||
|
|
||||||
|
const widgetProps = {
|
||||||
|
selectedGridSize: WidgetWidths.medium,
|
||||||
|
isEditView: true,
|
||||||
|
widgetKey: 'testWidgetKey',
|
||||||
|
handleRemoveWidget: mockHandleRemoveWidget,
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('KPIWidget', () => {
|
||||||
|
it('should fetch kpi list api initially', async () => {
|
||||||
|
render(<KPIWidget {...widgetProps} />);
|
||||||
|
|
||||||
|
expect(getListKPIs).toHaveBeenCalledWith({ fields: 'dataInsightChart' });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle close click when in edit view', async () => {
|
||||||
|
await act(async () => {
|
||||||
|
render(<KPIWidget {...widgetProps} />);
|
||||||
|
});
|
||||||
|
|
||||||
|
fireEvent.click(screen.getByTestId('remove-widget-button'));
|
||||||
|
|
||||||
|
expect(mockHandleRemoveWidget).toHaveBeenCalledWith(widgetProps.widgetKey);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render charts and data if present', async () => {
|
||||||
|
await act(async () => {
|
||||||
|
render(<KPIWidget {...widgetProps} />);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(screen.getByText('label.kpi-title')).toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
screen.getByText('KPILatestResultsV1.Component')
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not render data if selectedGridSize is small', async () => {
|
||||||
|
await act(async () => {
|
||||||
|
render(
|
||||||
|
<KPIWidget {...widgetProps} selectedGridSize={WidgetWidths.small} />
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(screen.getByText('label.kpi-title')).toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
screen.queryByText('KPILatestResultsV1.Component')
|
||||||
|
).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render ErrorPlaceholder if no data there', async () => {
|
||||||
|
(getListKPIs as jest.Mock).mockImplementation(() => Promise.resolve());
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
render(<KPIWidget {...widgetProps} />);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(screen.getByText('ErrorPlaceHolder.Component')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
@ -442,3 +442,36 @@ export const DESCRIPTION_CHART = {
|
|||||||
href: 'http://localhost:8585/api/v1/analytics/dataInsights/charts/7dc794d3-1881-408c-92fc-6182aa453bc8',
|
href: 'http://localhost:8585/api/v1/analytics/dataInsights/charts/7dc794d3-1881-408c-92fc-6182aa453bc8',
|
||||||
deleted: false,
|
deleted: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const MOCK_KPI_LIST_RESPONSE = {
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
id: '17651e36-2350-414a-a68d-bea58cc96d02',
|
||||||
|
name: 'description-percentage',
|
||||||
|
displayName: 'description',
|
||||||
|
fullyQualifiedName: 'description-percentage',
|
||||||
|
description: 'this is description',
|
||||||
|
metricType: 'PERCENTAGE',
|
||||||
|
dataInsightChart: {
|
||||||
|
id: 'e10d1bef-0d6b-42cd-a215-5771f40803eb',
|
||||||
|
type: 'dataInsightCustomChart',
|
||||||
|
name: 'percentage_of_data_asset_with_description_kpi',
|
||||||
|
fullyQualifiedName: 'percentage_of_data_asset_with_description_kpi',
|
||||||
|
displayName: 'percentage_of_data_asset_with_description_kpi',
|
||||||
|
deleted: false,
|
||||||
|
href: 'http://localhost:8585/api/v1/analytics/dataInsights/system/charts/e10d1bef-0d6b-42cd-a215-5771f40803eb',
|
||||||
|
},
|
||||||
|
targetValue: 58,
|
||||||
|
startDate: 1724697000000,
|
||||||
|
endDate: 1725128999999,
|
||||||
|
version: 0.1,
|
||||||
|
updatedAt: 1724760086039,
|
||||||
|
updatedBy: 'admin',
|
||||||
|
href: 'http://localhost:8585/api/v1/kpi/17651e36-2350-414a-a68d-bea58cc96d02',
|
||||||
|
deleted: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
paging: {
|
||||||
|
total: 1,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user