test(ui): enhance data contract playwright test (#22795)

* enhance data contract test

* minor fix
This commit is contained in:
Pranita Fulsundar 2025-08-06 22:57:25 +05:30 committed by GitHub
parent 1cc1abcd27
commit 6358446c0e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 216 additions and 113 deletions

View File

@ -44,9 +44,9 @@ export const DATA_CONTRACT_SEMANTICS2 = {
}; };
export const NEW_TABLE_TEST_CASE = { export const NEW_TABLE_TEST_CASE = {
name: `table_column_name_to_exist_in_id_${uuid()}`, name: `table_row_count_to_equal_in_id_${uuid()}`,
label: 'Table Column Name To Exist', label: 'Table Row Count To Equal',
type: 'tableColumnNameToExist', type: 'tableRowCountToEqual',
field: 'testCase', value: '100000',
description: 'New table test case for TableColumnNameToExist', description: 'New table test case for TableRowCountToEqual',
}; };

View File

@ -31,6 +31,10 @@ import {
redirectToHomePage, redirectToHomePage,
toastNotification, toastNotification,
} from '../../utils/common'; } from '../../utils/common';
import {
saveAndTriggerDataContractValidation,
validateDataContractInsideBundleTestSuites,
} from '../../utils/dataContracts';
import { addOwner } from '../../utils/entity'; import { addOwner } from '../../utils/entity';
test.use({ storageState: 'playwright/.auth/admin.json' }); test.use({ storageState: 'playwright/.auth/admin.json' });
@ -230,90 +234,9 @@ test.describe('Data Contracts', () => {
).not.toBeVisible(); ).not.toBeVisible();
}); });
await test.step('Fill Contract Quality form', async () => { await test.step('Save contract and validate for semantics', async () => {
await page.getByRole('button', { name: 'Quality' }).click(); // save and trigger contract validation
await saveAndTriggerDataContractValidation(page);
// Fill Contract Quality form
await page.getByTestId('add-test-button').click();
await expect(page.getByRole('dialog')).toBeVisible();
await page.fill(
'[data-testid="test-case-name"]',
NEW_TABLE_TEST_CASE.name
);
await page.click('#testCaseFormV1_testTypeId');
await page.waitForSelector(`text=${NEW_TABLE_TEST_CASE.label}`);
await page.click(`text=${NEW_TABLE_TEST_CASE.label}`);
await page.fill(
'#testCaseFormV1_params_columnName',
NEW_TABLE_TEST_CASE.field
);
await page.click('[data-testid="tags-selector"] input');
await page.fill('[data-testid="tags-selector"] input', testTag.data.name);
await page
.getByTestId(`tag-${testTag.responseData.fullyQualifiedName}`)
.click();
await clickOutside(page);
await page.click('[data-testid="glossary-terms-selector"] input');
await page.fill(
'[data-testid="glossary-terms-selector"] input',
testGlossaryTerm.data.name
);
await page
.getByTestId(`tag-${testGlossaryTerm.responseData.fullyQualifiedName}`)
.click();
await clickOutside(page);
const testCaseResponse = page.waitForResponse(
'/api/v1/dataQuality/testCases'
);
await page.click('[data-testid="create-btn"]');
await testCaseResponse;
await page.waitForTimeout(100);
await expect(
page
.locator('.ant-table-cell')
.filter({ hasText: NEW_TABLE_TEST_CASE.name })
).toBeVisible();
});
await test.step('Save contract and validate', async () => {
const saveContractResponse = page.waitForResponse(
'/api/v1/dataContracts'
);
await page.getByTestId('save-contract-btn').click();
await saveContractResponse;
await toastNotification(page, 'Data contract saved successfully');
await expect(
page
.getByTestId('contract-card-title-container')
.filter({ hasText: 'Contract Status' })
).not.toBeVisible();
const runNowResponse = page.waitForResponse(
'/api/v1/dataContracts/*/validate'
);
await page.getByTestId('contract-run-now-button').click();
await runNowResponse;
await toastNotification(
page,
'Contract validation trigger successfully.'
);
await page.reload();
await expect( await expect(
page.getByTestId('contract-card-title-container').filter({ page.getByTestId('contract-card-title-container').filter({
@ -335,6 +258,10 @@ test.describe('Data Contracts', () => {
dataTestId: 'data-assets-header', dataTestId: 'data-assets-header',
}); });
const runNowResponse = page.waitForResponse(
'/api/v1/dataContracts/*/validate'
);
await page.getByTestId('contract-run-now-button').click(); await page.getByTestId('contract-run-now-button').click();
await runNowResponse; await runNowResponse;
@ -350,38 +277,153 @@ test.describe('Data Contracts', () => {
).toContainText('Passed'); ).toContainText('Passed');
}); });
await test.step('Edit contract and validate', async () => { await test.step(
await page.getByTestId('contract-edit-button').click(); 'Add table test case and validate for quality',
async () => {
await page.getByTestId('contract-edit-button').click();
await page.getByRole('tab', { name: 'Quality' }).click(); await page.getByRole('tab', { name: 'Quality' }).click();
await page await page.getByTestId('add-test-button').click();
.locator('input[type="checkbox"][aria-label="Select all"]')
.check();
await expect( await expect(page.getByRole('dialog')).toBeVisible();
page.getByRole('checkbox', { name: 'Select all' })
).toBeChecked();
await page.getByTestId('save-contract-btn').click(); await page.fill(
'[data-testid="test-case-name"]',
NEW_TABLE_TEST_CASE.name
);
await toastNotification(page, 'Data contract saved successfully'); await page.locator('#testCaseFormV1_testTypeId').click();
const runNowResponse = page.waitForResponse( const dropdown = page.locator('.rc-virtual-list-holder-inner');
'/api/v1/dataContracts/*/validate'
);
await page.getByTestId('contract-run-now-button').click();
await runNowResponse;
await toastNotification( await expect(dropdown).toBeVisible();
page,
'Contract validation trigger successfully.'
);
await page.reload(); for (let i = 0; i < 20; i++) {
}); const optionVisible = await dropdown
.getByText(NEW_TABLE_TEST_CASE.label)
.isVisible();
if (optionVisible) {
break;
}
await dropdown.press('ArrowDown');
}
await dropdown.getByText(NEW_TABLE_TEST_CASE.label).click();
await page.click(`text=${NEW_TABLE_TEST_CASE.label}`);
await page.fill(
'#testCaseFormV1_params_value',
NEW_TABLE_TEST_CASE.value
);
await page.click('[data-testid="tags-selector"] input');
await page.fill(
'[data-testid="tags-selector"] input',
testTag.data.name
);
await page
.getByTestId(`tag-${testTag.responseData.fullyQualifiedName}`)
.click();
await clickOutside(page);
await page.click('[data-testid="glossary-terms-selector"] input');
await page.fill(
'[data-testid="glossary-terms-selector"] input',
testGlossaryTerm.data.name
);
await page
.getByTestId(
`tag-${testGlossaryTerm.responseData.fullyQualifiedName}`
)
.click();
await clickOutside(page);
const testCaseResponse = page.waitForResponse(
'/api/v1/dataQuality/testCases'
);
await page.click('[data-testid="create-btn"]');
await testCaseResponse;
await page.waitForTimeout(100);
await expect(
page
.locator('.ant-table-cell')
.filter({ hasText: NEW_TABLE_TEST_CASE.name })
).toBeVisible();
await page
.locator('input[type="checkbox"][aria-label="Select all"]')
.check();
await expect(
page.getByRole('checkbox', { name: 'Select all' })
).toBeChecked();
// save and trigger contract validation
await saveAndTriggerDataContractValidation(page);
await expect(page.getByTestId('alert-bar')).toBeVisible();
await expect(
page.locator('.anticon-exclamation-circle[role="img"]')
).toBeVisible();
}
);
await test.step(
'Validate inside the Observability, bundle test suites, that data contract test suite is present',
async () => {
await validateDataContractInsideBundleTestSuites(page);
await expect(
page
.getByTestId('test-suite-table')
.locator('.ant-table-cell')
.filter({
hasText: `${DATA_CONTRACT_DETAILS.name} - Data Contract Expectations`,
})
).toBeVisible();
}
);
await test.step(
'Edit quality expectations from the data contract and validate',
async () => {
await table.visitEntityPage(page);
await page.getByTestId('contract').click();
await page.getByTestId('contract-edit-button').click();
await page.getByRole('tab', { name: 'Quality' }).click();
await page
.locator('input[type="checkbox"][aria-label="Select all"]')
.uncheck();
await expect(
page.getByRole('checkbox', { name: 'Select all' })
).not.toBeChecked();
await saveAndTriggerDataContractValidation(page);
await expect(
page.getByTestId('contract-status-card-item-Quality Status')
).not.toBeVisible();
}
);
// TODO: Add a step to validate the test suite is removed from observability -> bundle test suites
await test.step('Verify YAML view', async () => { await test.step('Verify YAML view', async () => {
await table.visitEntityPage(page);
await page.getByTestId('contract').click();
await page.getByTestId('contract-view-switch-tab-yaml').click(); await page.getByTestId('contract-view-switch-tab-yaml').click();
await expect(page.getByTestId('code-mirror-container')).toBeVisible(); await expect(page.getByTestId('code-mirror-container')).toBeVisible();
@ -403,7 +445,7 @@ test.describe('Data Contracts', () => {
await test.step('Delete contract', async () => { await test.step('Delete contract', async () => {
const deleteContractResponse = page.waitForResponse( const deleteContractResponse = page.waitForResponse(
'api/v1/dataContracts/*?hardDelete=true' 'api/v1/dataContracts/*?hardDelete=true&recursive=true'
); );
await page.getByTestId('delete-contract-button').click(); await page.getByTestId('delete-contract-button').click();

View File

@ -0,0 +1,61 @@
/*
* Copyright 2025 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 { expect, Page } from '@playwright/test';
import { SidebarItem } from '../constant/sidebar';
import { toastNotification } from './common';
import { sidebarClick } from './sidebar';
export const saveAndTriggerDataContractValidation = async (page: Page) => {
const saveContractResponse = page.waitForResponse('/api/v1/dataContracts');
await page.getByTestId('save-contract-btn').click();
await saveContractResponse;
await toastNotification(page, 'Data contract saved successfully');
await expect(
page
.getByTestId('contract-card-title-container')
.filter({ hasText: 'Contract Status' })
).not.toBeVisible();
const runNowResponse = page.waitForResponse(
'/api/v1/dataContracts/*/validate'
);
await page.getByTestId('contract-run-now-button').click();
await runNowResponse;
await toastNotification(page, 'Contract validation trigger successfully.');
await page.reload();
};
export const validateDataContractInsideBundleTestSuites = async (
page: Page
) => {
await sidebarClick(page, SidebarItem.DATA_QUALITY);
const testSuiteResponse = page.waitForResponse(
'/api/v1/dataQuality/testSuites/search/list?*'
);
await page.getByTestId('test-suites').click();
await testSuiteResponse;
await page.waitForLoadState('networkidle');
await page
.locator('.ant-radio-button-wrapper')
.filter({ hasText: 'Bundle Suites' })
.click();
await expect(page.getByTestId('test-suite-table')).toBeVisible();
};

View File

@ -98,7 +98,7 @@ export const validateContractById = async (contractId: string) => {
export const deleteContractById = async (contractId: string) => { export const deleteContractById = async (contractId: string) => {
const response = await APIClient.delete<void>( const response = await APIClient.delete<void>(
`/dataContracts/${contractId}?hardDelete=true` `/dataContracts/${contractId}?hardDelete=true&recursive=true`
); );
return response.data; return response.data;