diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/IncidentManager/TestCaseResultTab/TestCaseResultTab.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/IncidentManager/TestCaseResultTab/TestCaseResultTab.component.tsx index fbf96cf1e0f..a6b33af5377 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/IncidentManager/TestCaseResultTab/TestCaseResultTab.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/IncidentManager/TestCaseResultTab/TestCaseResultTab.component.tsx @@ -45,7 +45,11 @@ import testCaseResultTabClassBase from './TestCaseResultTabClassBase'; const TestCaseResultTab = () => { const { t } = useTranslation(); - const { testCase: testCaseData, setTestCase } = useTestCaseStore(); + const { + testCase: testCaseData, + setTestCase, + showAILearningBanner, + } = useTestCaseStore(); const additionalComponent = testCaseResultTabClassBase.getAdditionalComponents(); const [isDescriptionEdit, setIsDescriptionEdit] = useState(false); @@ -116,6 +120,11 @@ const TestCaseResultTab = () => { [] ); + const AlertComponent = useMemo( + () => testCaseResultTabClassBase.getAlertBanner(), + [] + ); + const testCaseParams = useMemo(() => { if (testCaseData?.useDynamicAssertion) { return ( @@ -224,6 +233,13 @@ const TestCaseResultTab = () => { ) : null} + {showAILearningBanner && + testCaseData?.useDynamicAssertion && + AlertComponent && ( + + + + )} {testCaseData && ( diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/IncidentManager/TestCaseResultTab/TestCaseResultTab.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/IncidentManager/TestCaseResultTab/TestCaseResultTab.test.tsx index 3c2a240440c..455e1b21626 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/IncidentManager/TestCaseResultTab/TestCaseResultTab.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/IncidentManager/TestCaseResultTab/TestCaseResultTab.test.tsx @@ -22,7 +22,7 @@ import { TestCase } from '../../../../generated/tests/testCase'; import { checkPermission } from '../../../../utils/PermissionsUtils'; import TestCaseResultTab from './TestCaseResultTab.component'; -const mockTestCaseData = { +const mockTestCaseData: TestCase = { id: '1b748634-d24b-4879-9791-289f2f90fc3c', name: 'table_column_count_equals', fullyQualifiedName: @@ -68,6 +68,7 @@ const mockTestCaseData = { const mockUseTestCaseStore = { testCase: mockTestCaseData, setTestCase: jest.fn(), + showAILearningBanner: false, }; jest.mock( @@ -76,6 +77,11 @@ jest.mock( useTestCaseStore: jest.fn().mockImplementation(() => mockUseTestCaseStore), }) ); +const mockBannerComponent = () =>
BannerComponent
; +jest.mock('./TestCaseResultTabClassBase', () => ({ + getAdditionalComponents: jest.fn().mockReturnValue([]), + getAlertBanner: jest.fn().mockImplementation(() => mockBannerComponent), +})); jest.mock('../../../common/EntityDescription/DescriptionV1', () => { return jest.fn().mockImplementation(() =>
DescriptionV1
); }); @@ -189,4 +195,32 @@ describe('TestCaseResultTab', () => { mockUseTestCaseStore.testCase.useDynamicAssertion = false; }); + + it('Should show banner if banner component is available, useDynamicAssertion and showAILearningBanner is true', async () => { + mockTestCaseData.useDynamicAssertion = true; + mockUseTestCaseStore.showAILearningBanner = true; + + render(); + + const bannerComponent = await screen.findByText('BannerComponent'); + + expect(bannerComponent).toBeInTheDocument(); + + mockTestCaseData.useDynamicAssertion = false; + mockUseTestCaseStore.showAILearningBanner = false; + }); + + it('Should not show banner if banner component is available, useDynamicAssertion is false and showAILearningBanner is true', async () => { + mockTestCaseData.useDynamicAssertion = false; + mockUseTestCaseStore.showAILearningBanner = true; + + render(); + + const bannerComponent = screen.queryByText('BannerComponent'); + + expect(bannerComponent).not.toBeInTheDocument(); + + mockTestCaseData.useDynamicAssertion = false; + mockUseTestCaseStore.showAILearningBanner = false; + }); }); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/IncidentManager/TestCaseResultTab/TestCaseResultTabClassBase.ts b/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/IncidentManager/TestCaseResultTab/TestCaseResultTabClassBase.ts index 7cd9c660e87..63190815aef 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/IncidentManager/TestCaseResultTab/TestCaseResultTabClassBase.ts +++ b/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/IncidentManager/TestCaseResultTab/TestCaseResultTabClassBase.ts @@ -21,6 +21,10 @@ class TestCaseResultTabClassBase { public getAdditionalComponents(): Array { return []; } + + public getAlertBanner(): React.FC | null { + return null; + } } const testCaseResultTabClassBase = new TestCaseResultTabClassBase(); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Database/Profiler/TestSummary/TestSummaryGraph.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Database/Profiler/TestSummary/TestSummaryGraph.test.tsx index 5b127ba2b2d..6fae93a4b21 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Database/Profiler/TestSummary/TestSummaryGraph.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Database/Profiler/TestSummary/TestSummaryGraph.test.tsx @@ -150,6 +150,15 @@ jest.mock( })), }) ); +const mockSetShowAILearningBanner = jest.fn(); +jest.mock( + '../../../../pages/IncidentManager/IncidentManagerDetailPage/useTestCase.store', + () => ({ + useTestCaseStore: jest.fn().mockImplementation(() => ({ + setShowAILearningBanner: mockSetShowAILearningBanner, + })), + }) +); describe('TestSummaryGraph', () => { it('should display error placeholder when the result data is empty', () => { @@ -214,4 +223,10 @@ describe('TestSummaryGraph', () => { expect(minLineChart).toBeInTheDocument(); expect(maxLineChart).toBeInTheDocument(); }); + + it('should call mockSetShowAILearningBanner', () => { + render(); + + expect(mockSetShowAILearningBanner).toHaveBeenCalledWith(false); + }); }); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Database/Profiler/TestSummary/TestSummaryGraph.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Database/Profiler/TestSummary/TestSummaryGraph.tsx index 3d64bb34c1a..0a1601b648b 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Database/Profiler/TestSummary/TestSummaryGraph.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Database/Profiler/TestSummary/TestSummaryGraph.tsx @@ -51,6 +51,7 @@ import { ThreadTaskStatus, } from '../../../../generated/entity/feed/thread'; import { TestCaseStatus } from '../../../../generated/tests/testCase'; +import { useTestCaseStore } from '../../../../pages/IncidentManager/IncidentManagerDetailPage/useTestCase.store'; import { axisTickFormatter, updateActiveChartFilter, @@ -72,6 +73,7 @@ function TestSummaryGraph({ }: Readonly) { const { t } = useTranslation(); const { entityThread = [] } = useActivityFeedProvider(); + const { setShowAILearningBanner } = useTestCaseStore(); const chartRef = useRef(null); const [chartMouseEvent, setChartMouseEvent] = useState(); @@ -91,11 +93,14 @@ function TestSummaryGraph({ }, [chartRef, chartMouseEvent]); const chartData = useMemo(() => { - return prepareChartData({ + const data = prepareChartData({ testCaseParameterValue: testCaseParameterValue ?? [], testCaseResults, entityThread, }); + setShowAILearningBanner(data.showAILearningBanner); + + return data; }, [testCaseResults, entityThread, testCaseParameterValue]); const incidentData = useMemo(() => { diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/IncidentManager/IncidentManagerDetailPage/IncidentManagerDetailPage.test.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/IncidentManager/IncidentManagerDetailPage/IncidentManagerDetailPage.test.tsx index 43b0075c566..094123f7e48 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/IncidentManager/IncidentManagerDetailPage/IncidentManagerDetailPage.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/IncidentManager/IncidentManagerDetailPage/IncidentManagerDetailPage.test.tsx @@ -76,6 +76,8 @@ const mockUseTestCase: UseTestCaseStoreInterface = { isLoading: false, setIsLoading: jest.fn(), reset: jest.fn(), + showAILearningBanner: false, + setShowAILearningBanner: jest.fn(), }; jest.mock('./useTestCase.store', () => ({ useTestCaseStore: jest.fn().mockImplementation(() => mockUseTestCase), diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/IncidentManager/IncidentManagerDetailPage/useTestCase.store.ts b/openmetadata-ui/src/main/resources/ui/src/pages/IncidentManager/IncidentManagerDetailPage/useTestCase.store.ts index bee6c127110..b4f671ca64c 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/IncidentManager/IncidentManagerDetailPage/useTestCase.store.ts +++ b/openmetadata-ui/src/main/resources/ui/src/pages/IncidentManager/IncidentManagerDetailPage/useTestCase.store.ts @@ -16,20 +16,26 @@ import { TestCase } from '../../../generated/tests/testCase'; export interface UseTestCaseStoreInterface { testCase: TestCase | undefined; isLoading: boolean; + showAILearningBanner: boolean; setTestCase: (testCase: TestCase) => void; setIsLoading: (isLoading: boolean) => void; + setShowAILearningBanner: (showBanner: boolean) => void; reset: () => void; } export const useTestCaseStore = create()((set) => ({ testCase: undefined, isLoading: true, + showAILearningBanner: false, setTestCase: (testCase: TestCase) => { set({ testCase }); }, setIsLoading: (isLoading: boolean) => { set({ isLoading }); }, + setShowAILearningBanner: (showAILearningBanner: boolean) => { + set({ showAILearningBanner }); + }, reset: () => { - set({ testCase: undefined, isLoading: true }); + set({ testCase: undefined, isLoading: true, showAILearningBanner: false }); }, })); diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/DataQuality/TestSummaryGraphUtils.test.ts b/openmetadata-ui/src/main/resources/ui/src/utils/DataQuality/TestSummaryGraphUtils.test.ts index 997508e400d..dc4657f515d 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/DataQuality/TestSummaryGraphUtils.test.ts +++ b/openmetadata-ui/src/main/resources/ui/src/utils/DataQuality/TestSummaryGraphUtils.test.ts @@ -96,6 +96,7 @@ describe('prepareChartData', () => { label: 'max', }, ], + showAILearningBanner: false, }); }); @@ -185,6 +186,7 @@ describe('prepareChartData', () => { label: 'max', }, ], + showAILearningBanner: true, }); }); @@ -230,6 +232,7 @@ describe('prepareChartData', () => { label: 'max', }, ], + showAILearningBanner: false, }); }); @@ -254,6 +257,7 @@ describe('prepareChartData', () => { expect(result).toEqual({ data: [], information: [], + showAILearningBanner: false, }); }); }); diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/DataQuality/TestSummaryGraphUtils.ts b/openmetadata-ui/src/main/resources/ui/src/utils/DataQuality/TestSummaryGraphUtils.ts index 16c40449fae..f2e77488673 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/DataQuality/TestSummaryGraphUtils.ts +++ b/openmetadata-ui/src/main/resources/ui/src/utils/DataQuality/TestSummaryGraphUtils.ts @@ -36,6 +36,7 @@ export const prepareChartData = ({ const yValues = params.reduce((acc, curr, i) => { return { ...acc, [`y${i + 1}`]: parseInt(curr.value ?? '') }; }, {}); + let showAILearningBanner = false; testCaseResults.forEach((result) => { const values = result.testResultValue?.reduce((acc, curr) => { const value = round(parseFloat(curr.value ?? ''), 2) || 0; @@ -59,6 +60,10 @@ export const prepareChartData = ({ const y2 = result?.maxBound ?? yValues.y2; const boundArea = isUndefined(y1) || isUndefined(y2) ? undefined : [y1, y2]; + if (isUndefined(boundArea)) { + showAILearningBanner = true; + } + dataPoints.push({ name: result.timestamp, status: result.testCaseStatus, @@ -81,5 +86,6 @@ export const prepareChartData = ({ color: COLORS[i], })) ?? [], data: dataPoints, + showAILearningBanner, }; };