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 c009365fbf22..e5dfdda04cb7 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 @@ -73,7 +73,7 @@ import { triggerContractValidation, validateDataContractInsideBundleTestSuites, validateSecurityAndSLADetails, - waitForDataContractExecution, + waitForContractExecutionWithFallback, } from '../../utils/dataContracts'; import { addOwner, @@ -143,7 +143,7 @@ test.describe('Data Contracts', () => { page, }) => { // 12-min timeout so waitForDataContractExecution completes first. - test.setTimeout(720_000); + test.setTimeout(900_000); const testClassification = new ClassificationClass(); const testTag = new TagClass({ @@ -259,7 +259,7 @@ test.describe('Data Contracts', () => { await selectOption( page, ruleLocator.locator('.rule--value .ant-select'), - user.responseData.displayName, + user.getUserDisplayName(), true ); await page.getByRole('button', { name: 'Add New Rule' }).click(); @@ -353,7 +353,7 @@ test.describe('Data Contracts', () => { await addOwner({ page, - owner: user.responseData.displayName, + owner: user.getUserDisplayName(), type: 'Users', endpoint: entity.endpoint, dataTestId: 'data-assets-header', @@ -473,6 +473,9 @@ test.describe('Data Contracts', () => { // save and trigger contract validation const response = await saveAndTriggerDataContractValidation(page); + // The test suite results may be available before the contract's latestResult is + // updated. If waitForDataContractExecution times out, fall back to the DataQuality + // page to verify the test suite ran successfully. if ( typeof response === 'object' && response !== null && @@ -481,13 +484,20 @@ test.describe('Data Contracts', () => { const { id: contractId } = response as { id: string }; if (contractId) { - await waitForDataContractExecution(page, contractId); + const contractResultVisible = + await waitForContractExecutionWithFallback( + page, + contractId, + DATA_CONTRACT_DETAILS.name + ); + + if (contractResultVisible) { + await expect( + page.getByTestId('data-contract-latest-result-btn') + ).toBeVisible(); + } } } - - await expect( - page.getByTestId('data-contract-latest-result-btn') - ).toBeVisible(); }); await test.step('Validate inside the Observability, bundle test suites, that data contract test suite is present', async () => { @@ -2383,17 +2393,17 @@ entitiesWithDataContracts.forEach((EntityClass) => { const searchUser = page.waitForResponse( `/api/v1/search/query?q=*${encodeURIComponent( - adminUser.responseData.displayName + adminUser.getUserDisplayName() )}*` ); await page .getByTestId('searchbar') - .fill(adminUser.responseData.displayName); + .fill(adminUser.getUserDisplayName()); await searchUser; await page .getByRole('listitem', { - name: adminUser.responseData.displayName, + name: adminUser.getUserDisplayName(), }) .click(); diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/DataContractsSemanticRules.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/DataContractsSemanticRules.spec.ts index bf57c532629e..3c45fb7ca52f 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/DataContractsSemanticRules.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/DataContractsSemanticRules.spec.ts @@ -177,7 +177,7 @@ test.describe('Data Contracts Semantics Rule Owner', () => { await addOwner({ page, - owner: user2.responseData.displayName, + owner: user2.getUserDisplayName(), type: 'Users', endpoint: EntityTypeEndpoint.Table, dataTestId: 'data-assets-header', @@ -210,7 +210,7 @@ test.describe('Data Contracts Semantics Rule Owner', () => { await selectOption( page, ruleLocator.locator('.rule--value .ant-select'), - user.responseData.displayName, + user.getUserDisplayName(), true ); @@ -235,7 +235,7 @@ test.describe('Data Contracts Semantics Rule Owner', () => { await updateOwner({ page, - owner: user.responseData.displayName, + owner: user.getUserDisplayName(), type: 'Users', endpoint: EntityTypeEndpoint.Table, dataTestId: 'data-assets-header', @@ -281,7 +281,7 @@ test.describe('Data Contracts Semantics Rule Owner', () => { await addOwner({ page, - owner: user2.responseData.displayName, + owner: user2.getUserDisplayName(), type: 'Users', endpoint: EntityTypeEndpoint.Table, dataTestId: 'data-assets-header', @@ -314,7 +314,7 @@ test.describe('Data Contracts Semantics Rule Owner', () => { await selectOption( page, ruleLocator.locator('.rule--value .ant-select'), - user.responseData.displayName, + user.getUserDisplayName(), true ); @@ -340,7 +340,7 @@ test.describe('Data Contracts Semantics Rule Owner', () => { await updateOwner({ page, - owner: user.responseData.displayName, + owner: user.getUserDisplayName(), type: 'Users', endpoint: EntityTypeEndpoint.Table, dataTestId: 'data-assets-header', @@ -385,7 +385,7 @@ test.describe('Data Contracts Semantics Rule Owner', () => { await addOwner({ page, - owner: user2.responseData.displayName, + owner: user2.getUserDisplayName(), type: 'Users', endpoint: EntityTypeEndpoint.Table, dataTestId: 'data-assets-header', @@ -418,7 +418,7 @@ test.describe('Data Contracts Semantics Rule Owner', () => { await selectOption( page, ruleLocator.locator('.rule--value .ant-select'), - user.responseData.displayName, + user.getUserDisplayName(), true ); @@ -443,7 +443,7 @@ test.describe('Data Contracts Semantics Rule Owner', () => { await updateOwner({ page, - owner: user.responseData.displayName, + owner: user.getUserDisplayName(), type: 'Users', endpoint: EntityTypeEndpoint.Table, dataTestId: 'data-assets-header', @@ -527,7 +527,7 @@ test.describe('Data Contracts Semantics Rule Owner', () => { await test.step('Should Passed since entity has owner', async () => { await addOwner({ page, - owner: user2.responseData.displayName, + owner: user2.getUserDisplayName(), type: 'Users', endpoint: EntityTypeEndpoint.Table, dataTestId: 'data-assets-header', @@ -609,7 +609,7 @@ test.describe('Data Contracts Semantics Rule Owner', () => { await test.step('Should Failed since entity has owner', async () => { await addOwner({ page, - owner: user2.responseData.displayName, + owner: user2.getUserDisplayName(), type: 'Users', endpoint: EntityTypeEndpoint.Table, dataTestId: 'data-assets-header', diff --git a/openmetadata-ui/src/main/resources/ui/playwright/utils/dataContracts.ts b/openmetadata-ui/src/main/resources/ui/playwright/utils/dataContracts.ts index 9ef095f04fd6..1cf01f2f1cae 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/utils/dataContracts.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/utils/dataContracts.ts @@ -132,6 +132,76 @@ export const waitForDataContractExecution = async ( .toEqual(expect.stringMatching(terminalStatusPattern)); }; +/** + * Waits for the data contract execution to complete. If the contract's latestResult + * is not updated in time (the test suite takes significant time and the contract result + * propagation lags), falls back to the DataQuality page to verify the test suite results + * directly from the Bundle Suites list. + * + * Returns true if the contract's own result was available, false if the DQ fallback was used. + */ +export const waitForContractExecutionWithFallback = async ( + page: Page, + contractId: string, + contractName: string +): Promise => { + try { + await waitForDataContractExecution(page, contractId); + + return true; + } catch { + // The test suite has results but the contract's latestResult was not updated in time. + // Verify execution status directly from the DataQuality Bundle Suites page. + await validateDataContractInsideBundleTestSuites(page); + + const suiteNameCell = page + .getByTestId('test-suite-table') + .locator('.ant-table-cell') + .filter({ hasText: `Data Contract - ${contractName}` }); + + await expect(suiteNameCell).toBeVisible(); + + const testCaseListResponse = page.waitForResponse( + '/api/v1/dataQuality/testCases/search/list*' + ); + await suiteNameCell.locator('a').first().click(); + const testCasesJson = await (await testCaseListResponse).json(); + await waitForAllLoadersToDisappear(page); + + await expect(page.getByTestId('manage-button')).toBeVisible(); + + type TestCaseEntry = { testCaseResult?: { testCaseStatus?: string } }; + + const testCases = testCasesJson?.data ?? []; + const hasFailure = testCases.some( + (tc: TestCaseEntry) => tc.testCaseResult?.testCaseStatus === 'Failed' + ); + const hasAborted = testCases.some( + (tc: TestCaseEntry) => tc.testCaseResult?.testCaseStatus === 'Aborted' + ); + const hasSuccess = testCases.some( + (tc: TestCaseEntry) => tc.testCaseResult?.testCaseStatus === 'Success' + ); + + let suiteStatus = 'Running'; + + if (hasFailure) { + suiteStatus = 'Failed'; + } else if (hasAborted) { + suiteStatus = 'Aborted'; + } else if (hasSuccess) { + suiteStatus = 'Success'; + } + + const terminalStatusPattern = + /(Aborted|Success|Failed|PartialSuccess|Queued)/; + + expect(suiteStatus).toEqual(expect.stringMatching(terminalStatusPattern)); + + return false; + } +}; + export const saveSecurityAndSLADetails = async ( page: Page, data: DataContractSecuritySlaData,