diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/DataContracts.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/DataContracts.spec.ts
index 5ce33233bc0..c192566fad6 100644
--- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/DataContracts.spec.ts
+++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/DataContracts.spec.ts
@@ -54,7 +54,6 @@ const test = base.extend<{ page: Page }>({
test.describe('Data Contracts', () => {
const table = new TableClass();
- const table2 = new TableClass();
const testClassification = new ClassificationClass();
const testTag = new TagClass({
classification: testClassification.data.name,
@@ -68,7 +67,6 @@ test.describe('Data Contracts', () => {
const { apiContext, afterAction } = await performAdminLogin(browser);
await table.create(apiContext);
- await table2.create(apiContext);
await testClassification.create(apiContext);
await testTag.create(apiContext);
await testGlossary.create(apiContext);
@@ -111,7 +109,6 @@ test.describe('Data Contracts', () => {
const { apiContext, afterAction } = await performAdminLogin(browser);
await table.delete(apiContext);
- await table2.delete(apiContext);
await testClassification.delete(apiContext);
await testTag.delete(apiContext);
await testGlossary.delete(apiContext);
@@ -541,7 +538,7 @@ test.describe('Data Contracts', () => {
});
});
- test('Contract Status badge should not be visible if Contract Tab is hidden by Person', async ({
+ test('Contract Status badge should be visible on condition if Contract Tab is present/hidden by Persona', async ({
page,
}) => {
test.slow(true);
@@ -549,7 +546,7 @@ test.describe('Data Contracts', () => {
await test.step(
'Create Data Contract in Table and validate it fails',
async () => {
- await table2.visitEntityPage(page);
+ await table.visitEntityPage(page);
// Open contract section and start adding contract
await page.click('[data-testid="contract"]');
@@ -679,6 +676,35 @@ test.describe('Data Contracts', () => {
await personaResponse;
});
+ await test.step(
+ 'Verify Contract tab and status badge are visible if persona is set',
+ async () => {
+ await redirectToHomePage(page);
+ await table.visitEntityPage(page);
+ await page.waitForLoadState('networkidle');
+ await page.waitForSelector('[data-testid="loader"]', {
+ state: 'detached',
+ });
+
+ // Verify Contract tab is not visible (should be hidden by persona customization)
+ await expect(page.getByTestId('contract')).toBeVisible();
+
+ // Verify Contract status badge is not visible in header
+ await expect(
+ page.getByTestId('data-contract-latest-result-btn')
+ ).toBeVisible();
+
+ // Additional verification: Check that other tabs are still visible
+ await expect(page.getByTestId('schema')).toBeVisible();
+ await expect(page.getByTestId('activity_feed')).toBeVisible();
+ await expect(page.getByTestId('sample_data')).toBeVisible();
+ await expect(page.getByTestId('table_queries')).toBeVisible();
+ await expect(page.getByTestId('profiler')).toBeVisible();
+ await expect(page.getByTestId('lineage')).toBeVisible();
+ await expect(page.getByTestId('custom_properties')).toBeVisible();
+ }
+ );
+
await test.step('Customize Table page to hide Contract tab', async () => {
await settingClick(page, GlobalSettingOptions.PERSONA);
await page.waitForLoadState('networkidle');
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataAssets/DataAssetsHeader/DataAssetsHeader.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataAssets/DataAssetsHeader/DataAssetsHeader.component.tsx
index 455f76c3b8d..91c9a918166 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/DataAssets/DataAssetsHeader/DataAssetsHeader.component.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/DataAssets/DataAssetsHeader/DataAssetsHeader.component.tsx
@@ -429,14 +429,14 @@ export const DataAssetsHeader = ({
]);
const dataContractLatestResultButton = useMemo(() => {
- const entityContainContractTab =
- isUndefined(customizedPage?.tabs) ??
+ const entityContainContractTabVisible =
+ isUndefined(customizedPage?.tabs) ||
Boolean(
customizedPage?.tabs?.find((item) => item.id === EntityTabs.CONTRACT)
);
if (
- entityContainContractTab &&
+ entityContainContractTabVisible &&
dataContract?.latestResult?.status &&
[
ContractExecutionStatus.Aborted,
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataAssets/DataAssetsHeader/DataAssetsHeader.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataAssets/DataAssetsHeader/DataAssetsHeader.test.tsx
index 3c6eb242fa8..1eefa0fb096 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/DataAssets/DataAssetsHeader/DataAssetsHeader.test.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/DataAssets/DataAssetsHeader/DataAssetsHeader.test.tsx
@@ -12,21 +12,26 @@
*/
import { act, fireEvent, render, screen } from '@testing-library/react';
import { AUTO_PILOT_APP_NAME } from '../../../constants/Applications.constant';
-import { EntityType } from '../../../enums/entity.enum';
+import { EntityTabs, EntityType } from '../../../enums/entity.enum';
import { ServiceCategory } from '../../../enums/service.enum';
import {
Container,
StorageServiceType,
} from '../../../generated/entity/data/container';
+import { ContractExecutionStatus } from '../../../generated/entity/data/dataContract';
import { DatabaseServiceType } from '../../../generated/entity/services/databaseService';
import { LabelType, State, TagSource } from '../../../generated/tests/testCase';
import { AssetCertification } from '../../../generated/type/assetCertification';
+import { useCustomPages } from '../../../hooks/useCustomPages';
+import { MOCK_DATA_CONTRACT } from '../../../mocks/DataContract.mock';
import { MOCK_TIER_DATA } from '../../../mocks/TableData.mock';
import { triggerOnDemandApp } from '../../../rest/applicationAPI';
import { getDataQualityLineage } from '../../../rest/lineageAPI';
import { getContainerByName } from '../../../rest/storageAPI';
import { ExtraInfoLink } from '../../../utils/DataAssetsHeader.utils';
+import { getDataContractStatusIcon } from '../../../utils/DataContract/DataContractUtils';
import { DEFAULT_ENTITY_PERMISSION } from '../../../utils/PermissionsUtils';
+import { getEntityDetailsPath } from '../../../utils/RouterUtils';
import { useRequiredParams } from '../../../utils/useRequiredParams';
import { DataAssetsHeader } from './DataAssetsHeader.component';
import { DataAssetsHeaderProps } from './DataAssetsHeader.interface';
@@ -58,9 +63,11 @@ const mockProps: DataAssetsHeaderProps = {
onOwnerUpdate: jest.fn(),
};
+const mockNavigate = jest.fn();
+
jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
- useNavigate: jest.fn().mockReturnValue(jest.fn()),
+ useNavigate: jest.fn().mockImplementation(() => mockNavigate),
}));
jest.mock('../../../utils/useRequiredParams', () => ({
@@ -179,6 +186,18 @@ jest.mock('../../../rest/lineageAPI', () => ({
getDataQualityLineage: jest.fn(),
}));
+jest.mock('../../../utils/DataContract/DataContractUtils', () => ({
+ getDataContractStatusIcon: jest.fn(),
+}));
+
+jest.mock('../../../hooks/useCustomPages', () => ({
+ useCustomPages: jest.fn().mockReturnValue({ customizedPage: null }),
+}));
+
+jest.mock('../../../utils/RouterUtils', () => ({
+ getEntityDetailsPath: jest.fn(),
+}));
+
describe('ExtraInfoLink component', () => {
const mockProps = {
label: 'myLabel',
@@ -475,4 +494,220 @@ describe('DataAssetsHeader component', () => {
expect(button).toBeEnabled();
});
+
+ describe('dataContractLatestResultButton', () => {
+ const mockGetDataContractStatusIcon =
+ getDataContractStatusIcon as jest.Mock;
+ const mockUseCustomPages = useCustomPages as jest.Mock;
+ const mockGetEntityDetailsPath = getEntityDetailsPath as jest.Mock;
+
+ it('should render data contract button when contract tab is visible and status is in allowed list', () => {
+ mockUseCustomPages.mockReturnValue({
+ customizedPage: {
+ tabs: [{ id: EntityTabs.CONTRACT }],
+ },
+ });
+
+ render(
+
The customer lifetime value must always be greater or equal to zero
', + }, + { + id: 'e57ffd73-b8f1-4f5f-91b1-7ebe614dc26e', + type: 'testCase', + name: 'Customer ID To Be Unique', + }, + { + id: '484e016e-ed87-4f6c-ae77-7d5b16f07ad0', + type: 'testCase', + name: 'Table Row Count To Equal', + }, + ], + reviewers: [], + changeDescription: { + fieldsAdded: [ + { + name: 'latestResult', + newValue: { + status: ContractExecutionStatus.Failed, + resultId: '04551202-c2b9-4d7b-aecf-6c9c8fe22c1c', + timestamp: 1756944000095, + }, + }, + ], + fieldsUpdated: [], + fieldsDeleted: [], + previousVersion: 0.1, + changeSummary: {}, + }, + incrementalChangeDescription: { + fieldsAdded: [], + fieldsUpdated: [], + fieldsDeleted: [], + previousVersion: 0.2, + }, + deleted: false, + latestResult: { + timestamp: 1756944000095, + status: ContractExecutionStatus.Failed, + resultId: '04551202-c2b9-4d7b-aecf-6c9c8fe22c1c', + }, +};