mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-08-25 17:37:57 +00:00
Migrate: Data quality and profiler test to playwright (#17705)
* Migrate: Data quality and profiler test to playwright * migrate profiler ingestion * migrated test cases to playwright * migrate filter test from data quality * migrate domain filter test
This commit is contained in:
parent
6a2eefbb46
commit
097b87e2db
2
.gitignore
vendored
2
.gitignore
vendored
@ -87,7 +87,7 @@ openmetadata-ui/src/main/resources/ui/cypress/fixtures
|
|||||||
# Playwright artifacts
|
# Playwright artifacts
|
||||||
openmetadata-ui/src/main/resources/ui/playwright/output/
|
openmetadata-ui/src/main/resources/ui/playwright/output/
|
||||||
openmetadata-ui/src/main/resources/ui/playwright/e2e/.cache/
|
openmetadata-ui/src/main/resources/ui/playwright/e2e/.cache/
|
||||||
openmetadata-ui/src/main/resources/ui/playwright/.env
|
openmetadata-ui/src/main/resources/ui/.env
|
||||||
openmetadata-ui/src/main/resources/ui/playwright/.auth
|
openmetadata-ui/src/main/resources/ui/playwright/.auth
|
||||||
|
|
||||||
#UI - Dereferenced Schemas
|
#UI - Dereferenced Schemas
|
||||||
|
@ -263,6 +263,8 @@ overrides:
|
|||||||
- off
|
- off
|
||||||
jest/no-standalone-expect:
|
jest/no-standalone-expect:
|
||||||
- off
|
- off
|
||||||
|
jest/no-conditional-expect:
|
||||||
|
- off
|
||||||
|
|
||||||
# i18next rule is not required for js, jsx, json and test file
|
# i18next rule is not required for js, jsx, json and test file
|
||||||
- files:
|
- files:
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -42,6 +42,8 @@ export const POSTGRES = {
|
|||||||
tableName: 'order_items',
|
tableName: 'order_items',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const MYSQL = 'Mysql';
|
||||||
|
|
||||||
export const HTTP_CONFIG_SOURCE = {
|
export const HTTP_CONFIG_SOURCE = {
|
||||||
DBT_CATALOG_HTTP_PATH:
|
DBT_CATALOG_HTTP_PATH:
|
||||||
'https://raw.githubusercontent.com/OnkarVO7/dbt_git_test/dbt_aut/catalog.json',
|
'https://raw.githubusercontent.com/OnkarVO7/dbt_git_test/dbt_aut/catalog.json',
|
||||||
|
@ -0,0 +1,937 @@
|
|||||||
|
/*
|
||||||
|
* 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 { expect, Page, test } from '@playwright/test';
|
||||||
|
import { getCurrentMillis } from '../../../src/utils/date-time/DateTimeUtils';
|
||||||
|
import { SidebarItem } from '../../constant/sidebar';
|
||||||
|
import { Domain } from '../../support/domain/Domain';
|
||||||
|
import { TableClass } from '../../support/entity/TableClass';
|
||||||
|
import {
|
||||||
|
assignDomain,
|
||||||
|
clickOutside,
|
||||||
|
createNewPage,
|
||||||
|
descriptionBox,
|
||||||
|
getApiContext,
|
||||||
|
redirectToHomePage,
|
||||||
|
toastNotification,
|
||||||
|
uuid,
|
||||||
|
} from '../../utils/common';
|
||||||
|
import { visitEntityPage } from '../../utils/entity';
|
||||||
|
import { sidebarClick } from '../../utils/sidebar';
|
||||||
|
import { deleteTestCase, visitDataQualityTab } from '../../utils/testCases';
|
||||||
|
|
||||||
|
// use the admin user to login
|
||||||
|
test.use({ storageState: 'playwright/.auth/admin.json' });
|
||||||
|
|
||||||
|
const table1 = new TableClass();
|
||||||
|
const table2 = new TableClass();
|
||||||
|
|
||||||
|
test.beforeAll(async ({ browser }) => {
|
||||||
|
const { apiContext, afterAction } = await createNewPage(browser);
|
||||||
|
await table1.create(apiContext);
|
||||||
|
await table2.create(apiContext);
|
||||||
|
const { testSuiteData } = await table2.createTestSuiteAndPipelines(
|
||||||
|
apiContext
|
||||||
|
);
|
||||||
|
await table2.createTestCase(apiContext, {
|
||||||
|
name: `email_column_values_to_be_in_set_${uuid()}`,
|
||||||
|
entityLink: `<#E::table::${table2.entityResponseData?.['fullyQualifiedName']}::columns::email>`,
|
||||||
|
parameterValues: [
|
||||||
|
{ name: 'allowedValues', value: '["gmail","yahoo","collate"]' },
|
||||||
|
],
|
||||||
|
testDefinition: 'columnValuesToBeInSet',
|
||||||
|
testSuite: testSuiteData?.['fullyQualifiedName'],
|
||||||
|
});
|
||||||
|
await afterAction();
|
||||||
|
});
|
||||||
|
|
||||||
|
test.afterAll(async ({ browser }) => {
|
||||||
|
const { apiContext, afterAction } = await createNewPage(browser);
|
||||||
|
await table1.delete(apiContext);
|
||||||
|
await table2.delete(apiContext);
|
||||||
|
await afterAction();
|
||||||
|
});
|
||||||
|
|
||||||
|
test.beforeEach(async ({ page }) => {
|
||||||
|
await redirectToHomePage(page);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Table test case', async ({ page }) => {
|
||||||
|
test.slow();
|
||||||
|
|
||||||
|
const NEW_TABLE_TEST_CASE = {
|
||||||
|
name: `table_column_name_to_exist_in_id_${uuid()}`,
|
||||||
|
label: 'Table Column Name To Exist',
|
||||||
|
type: 'tableColumnNameToExist',
|
||||||
|
field: 'testCase',
|
||||||
|
description: 'New table test case for TableColumnNameToExist',
|
||||||
|
};
|
||||||
|
await visitDataQualityTab(page, table1);
|
||||||
|
|
||||||
|
await page.click('[data-testid="profiler-add-table-test-btn"]');
|
||||||
|
await page.click('[data-testid="table"]');
|
||||||
|
|
||||||
|
await test.step('Create', async () => {
|
||||||
|
await page.click('#tableTestForm_testTypeId');
|
||||||
|
await page.waitForSelector(`text=${NEW_TABLE_TEST_CASE.label}`);
|
||||||
|
await page.click(`text=${NEW_TABLE_TEST_CASE.label}`);
|
||||||
|
await page.fill('#tableTestForm_testName', NEW_TABLE_TEST_CASE.name);
|
||||||
|
await page.fill(
|
||||||
|
'#tableTestForm_params_columnName',
|
||||||
|
NEW_TABLE_TEST_CASE.field
|
||||||
|
);
|
||||||
|
await page.fill(descriptionBox, NEW_TABLE_TEST_CASE.description);
|
||||||
|
await page.click('[data-testid="submit-test"]');
|
||||||
|
|
||||||
|
await page.waitForSelector('[data-testid="success-line"]');
|
||||||
|
|
||||||
|
await expect(page.locator('[data-testid="success-line"]')).toBeVisible();
|
||||||
|
|
||||||
|
await page.waitForSelector('[data-testid="add-ingestion-button"]');
|
||||||
|
await page.click('[data-testid="add-ingestion-button"]');
|
||||||
|
await page.click('[data-testid="select-all-test-cases"]');
|
||||||
|
|
||||||
|
// Schedule & Deploy
|
||||||
|
await page.click('[data-testid="cron-type"]');
|
||||||
|
await page.waitForSelector('.ant-select-item-option-content');
|
||||||
|
await page.click('.ant-select-item-option-content:has-text("Hour")');
|
||||||
|
const ingestionPipelines = page.waitForResponse(
|
||||||
|
'/api/v1/services/ingestionPipelines'
|
||||||
|
);
|
||||||
|
const deploy = page.waitForResponse(
|
||||||
|
'/api/v1/services/ingestionPipelines/deploy/*'
|
||||||
|
);
|
||||||
|
const status = page.waitForResponse(
|
||||||
|
'/api/v1/services/ingestionPipelines/status'
|
||||||
|
);
|
||||||
|
await page.click('[data-testid="deploy-button"]');
|
||||||
|
await ingestionPipelines;
|
||||||
|
await deploy;
|
||||||
|
await status;
|
||||||
|
|
||||||
|
// check success
|
||||||
|
await page.waitForSelector('[data-testid="success-line"]', {
|
||||||
|
timeout: 15000,
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(page.locator('[data-testid="success-line"]')).toBeVisible();
|
||||||
|
await expect(
|
||||||
|
page.getByText('has been created and deployed successfully')
|
||||||
|
).toBeVisible();
|
||||||
|
|
||||||
|
const testCaseResponse = page.waitForResponse(
|
||||||
|
'/api/v1/dataQuality/testCases?fields=*'
|
||||||
|
);
|
||||||
|
await page.click(`[data-testid="view-service-button"]`);
|
||||||
|
await testCaseResponse;
|
||||||
|
|
||||||
|
await expect(page.getByTestId(NEW_TABLE_TEST_CASE.name)).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
await test.step('Edit', async () => {
|
||||||
|
await page.click(`[data-testid="edit-${NEW_TABLE_TEST_CASE.name}"]`);
|
||||||
|
await page.waitForSelector('.ant-modal-title');
|
||||||
|
await page.locator('#tableTestForm_params_columnName').clear();
|
||||||
|
await page.fill('#tableTestForm_params_columnName', 'new_column_name');
|
||||||
|
const updateTestCaseResponse = page.waitForResponse(
|
||||||
|
'/api/v1/dataQuality/testCases/*'
|
||||||
|
);
|
||||||
|
await page.locator('button').filter({ hasText: 'Submit' }).click();
|
||||||
|
await updateTestCaseResponse;
|
||||||
|
await toastNotification(page, 'Test case updated successfully.');
|
||||||
|
await page.click(`[data-testid="edit-${NEW_TABLE_TEST_CASE.name}"]`);
|
||||||
|
|
||||||
|
await page.waitForSelector('#tableTestForm_params_columnName');
|
||||||
|
|
||||||
|
await expect(page.locator('#tableTestForm_params_columnName')).toHaveValue(
|
||||||
|
'new_column_name'
|
||||||
|
);
|
||||||
|
|
||||||
|
await page.getByRole('button', { name: 'Cancel' }).click();
|
||||||
|
});
|
||||||
|
|
||||||
|
await test.step('Delete', async () => {
|
||||||
|
await deleteTestCase(page, NEW_TABLE_TEST_CASE.name);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Column test case', async ({ page }) => {
|
||||||
|
test.slow();
|
||||||
|
|
||||||
|
const NEW_COLUMN_TEST_CASE = {
|
||||||
|
name: 'email_column_value_lengths_to_be_between',
|
||||||
|
column: 'email',
|
||||||
|
type: 'columnValueLengthsToBeBetween',
|
||||||
|
label: 'Column Value Lengths To Be Between',
|
||||||
|
min: '3',
|
||||||
|
max: '6',
|
||||||
|
description: 'New table test case for columnValueLengthsToBeBetween',
|
||||||
|
};
|
||||||
|
|
||||||
|
await visitDataQualityTab(page, table1);
|
||||||
|
await page.click('[data-testid="profiler-add-table-test-btn"]');
|
||||||
|
await page.click('[data-testid="column"]');
|
||||||
|
|
||||||
|
await test.step('Create', async () => {
|
||||||
|
const testDefinitionResponse = page.waitForResponse(
|
||||||
|
'/api/v1/dataQuality/testDefinitions?limit=*&entityType=COLUMN&testPlatform=OpenMetadata&supportedDataType=VARCHAR'
|
||||||
|
);
|
||||||
|
await page.click('#tableTestForm_column');
|
||||||
|
await page.click(`[title="${NEW_COLUMN_TEST_CASE.column}"]`);
|
||||||
|
await testDefinitionResponse;
|
||||||
|
await page.fill('#tableTestForm_testName', NEW_COLUMN_TEST_CASE.name);
|
||||||
|
await page.click('#tableTestForm_testTypeId');
|
||||||
|
await page.click(`[title="${NEW_COLUMN_TEST_CASE.label}"]`);
|
||||||
|
await page.fill(
|
||||||
|
'#tableTestForm_params_minLength',
|
||||||
|
NEW_COLUMN_TEST_CASE.min
|
||||||
|
);
|
||||||
|
await page.fill(
|
||||||
|
'#tableTestForm_params_maxLength',
|
||||||
|
NEW_COLUMN_TEST_CASE.max
|
||||||
|
);
|
||||||
|
await page.fill(descriptionBox, NEW_COLUMN_TEST_CASE.description);
|
||||||
|
|
||||||
|
await page.click('[data-testid="submit-test"]');
|
||||||
|
await page.waitForSelector('[data-testid="success-line"]');
|
||||||
|
|
||||||
|
await expect(page.locator('[data-testid="success-line"]')).toBeVisible();
|
||||||
|
|
||||||
|
await page.waitForSelector('[data-testid="view-service-button"]');
|
||||||
|
|
||||||
|
const testCaseResponse = page.waitForResponse(
|
||||||
|
'/api/v1/dataQuality/testCases?fields=*'
|
||||||
|
);
|
||||||
|
await page.click(`[data-testid="view-service-button"]`);
|
||||||
|
await testCaseResponse;
|
||||||
|
|
||||||
|
await page.waitForSelector(`[data-testid="${NEW_COLUMN_TEST_CASE.name}"]`);
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
page.locator(`[data-testid="${NEW_COLUMN_TEST_CASE.name}"]`)
|
||||||
|
).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
await test.step('Edit', async () => {
|
||||||
|
await page.click(`[data-testid="edit-${NEW_COLUMN_TEST_CASE.name}"]`);
|
||||||
|
await page.waitForSelector('#tableTestForm_params_minLength');
|
||||||
|
await page.locator('#tableTestForm_params_minLength').clear();
|
||||||
|
await page.fill('#tableTestForm_params_minLength', '4');
|
||||||
|
const updateTestCaseResponse = page.waitForResponse(
|
||||||
|
'/api/v1/dataQuality/testCases/*'
|
||||||
|
);
|
||||||
|
await page.locator('button').getByText('Submit').click();
|
||||||
|
await updateTestCaseResponse;
|
||||||
|
await toastNotification(page, 'Test case updated successfully.');
|
||||||
|
|
||||||
|
await page.click(`[data-testid="edit-${NEW_COLUMN_TEST_CASE.name}"]`);
|
||||||
|
await page.waitForSelector('#tableTestForm_params_minLength');
|
||||||
|
const minLengthValue = await page
|
||||||
|
.locator('#tableTestForm_params_minLength')
|
||||||
|
.inputValue();
|
||||||
|
|
||||||
|
expect(minLengthValue).toBe('4');
|
||||||
|
|
||||||
|
await page.locator('button').getByText('Cancel').click();
|
||||||
|
});
|
||||||
|
|
||||||
|
await test.step('Delete', async () => {
|
||||||
|
await deleteTestCase(page, NEW_COLUMN_TEST_CASE.name);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Profiler matrix and test case graph should visible', async ({ page }) => {
|
||||||
|
const DATA_QUALITY_TABLE = {
|
||||||
|
term: 'dim_address',
|
||||||
|
serviceName: 'sample_data',
|
||||||
|
testCaseName: 'column_value_max_to_be_between',
|
||||||
|
};
|
||||||
|
|
||||||
|
await visitEntityPage({
|
||||||
|
page,
|
||||||
|
searchTerm: DATA_QUALITY_TABLE.term,
|
||||||
|
dataTestId: `${DATA_QUALITY_TABLE.serviceName}-${DATA_QUALITY_TABLE.term}`,
|
||||||
|
});
|
||||||
|
await page.waitForSelector(`[data-testid="entity-header-display-name"]`);
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
page.locator(`[data-testid="entity-header-display-name"]`)
|
||||||
|
).toContainText(DATA_QUALITY_TABLE.term);
|
||||||
|
|
||||||
|
const profilerResponse = page.waitForResponse(
|
||||||
|
`/api/v1/tables/*/tableProfile/latest`
|
||||||
|
);
|
||||||
|
await page.click('[data-testid="profiler"]');
|
||||||
|
await profilerResponse;
|
||||||
|
await page.waitForTimeout(1000);
|
||||||
|
await page
|
||||||
|
.getByRole('menuitem', {
|
||||||
|
name: 'Column Profile',
|
||||||
|
})
|
||||||
|
.click();
|
||||||
|
const getProfilerInfo = page.waitForResponse(
|
||||||
|
'/api/v1/tables/*/columnProfile?*'
|
||||||
|
);
|
||||||
|
await page.locator('[data-row-key="shop_id"]').getByText('shop_id').click();
|
||||||
|
await getProfilerInfo;
|
||||||
|
|
||||||
|
await expect(page.locator('#count_graph')).toBeVisible();
|
||||||
|
await expect(page.locator('#proportion_graph')).toBeVisible();
|
||||||
|
await expect(page.locator('#math_graph')).toBeVisible();
|
||||||
|
await expect(page.locator('#sum_graph')).toBeVisible();
|
||||||
|
|
||||||
|
await page
|
||||||
|
.getByRole('menuitem', {
|
||||||
|
name: 'Data Quality',
|
||||||
|
})
|
||||||
|
.click();
|
||||||
|
|
||||||
|
await page.waitForSelector(
|
||||||
|
`[data-testid="${DATA_QUALITY_TABLE.testCaseName}"]`
|
||||||
|
);
|
||||||
|
const getTestCaseDetails = page.waitForResponse(
|
||||||
|
'/api/v1/dataQuality/testCases/name/*?fields=*'
|
||||||
|
);
|
||||||
|
const getTestResult = page.waitForResponse(
|
||||||
|
'/api/v1/dataQuality/testCases/*/testCaseResult?*'
|
||||||
|
);
|
||||||
|
await page
|
||||||
|
.locator(`[data-testid="${DATA_QUALITY_TABLE.testCaseName}"]`)
|
||||||
|
.getByText(DATA_QUALITY_TABLE.testCaseName)
|
||||||
|
.click();
|
||||||
|
|
||||||
|
await getTestCaseDetails;
|
||||||
|
await getTestResult;
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
page.locator(`#${DATA_QUALITY_TABLE.testCaseName}_graph`)
|
||||||
|
).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('TestCase with Array params value', async ({ page }) => {
|
||||||
|
test.slow();
|
||||||
|
|
||||||
|
const testCase = table2.testCasesResponseData[0];
|
||||||
|
const testCaseName = testCase?.['name'];
|
||||||
|
await visitDataQualityTab(page, table2);
|
||||||
|
|
||||||
|
await test.step(
|
||||||
|
'Array params value should be visible while editing the test case',
|
||||||
|
async () => {
|
||||||
|
await expect(
|
||||||
|
page.locator(`[data-testid="${testCaseName}"]`)
|
||||||
|
).toBeVisible();
|
||||||
|
await expect(
|
||||||
|
page.locator(`[data-testid="edit-${testCaseName}"]`)
|
||||||
|
).toBeVisible();
|
||||||
|
|
||||||
|
await page.click(`[data-testid="edit-${testCaseName}"]`);
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
page.locator('#tableTestForm_params_allowedValues_0_value')
|
||||||
|
).toHaveValue('gmail');
|
||||||
|
await expect(
|
||||||
|
page.locator('#tableTestForm_params_allowedValues_1_value')
|
||||||
|
).toHaveValue('yahoo');
|
||||||
|
await expect(
|
||||||
|
page.locator('#tableTestForm_params_allowedValues_2_value')
|
||||||
|
).toHaveValue('collate');
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
await test.step('Validate patch request for edit test case', async () => {
|
||||||
|
await page.fill(
|
||||||
|
'#tableTestForm_displayName',
|
||||||
|
'Table test case display name'
|
||||||
|
);
|
||||||
|
|
||||||
|
await expect(page.locator('#tableTestForm_table')).toHaveValue(
|
||||||
|
table2.entityResponseData?.['name']
|
||||||
|
);
|
||||||
|
await expect(page.locator('#tableTestForm_column')).toHaveValue('email');
|
||||||
|
await expect(page.locator('#tableTestForm_name')).toHaveValue(testCaseName);
|
||||||
|
await expect(page.locator('#tableTestForm_testDefinition')).toHaveValue(
|
||||||
|
'Column Values To Be In Set'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Edit test case display name
|
||||||
|
const updateTestCaseResponse = page.waitForResponse(
|
||||||
|
(response) =>
|
||||||
|
response.url().includes('/api/v1/dataQuality/testCases/') &&
|
||||||
|
response.request().method() === 'PATCH'
|
||||||
|
);
|
||||||
|
await page.click('.ant-modal-footer >> text=Submit');
|
||||||
|
const updateResponse1 = await updateTestCaseResponse;
|
||||||
|
const body1 = await updateResponse1.request().postData();
|
||||||
|
|
||||||
|
expect(body1).toEqual(
|
||||||
|
JSON.stringify([
|
||||||
|
{
|
||||||
|
op: 'add',
|
||||||
|
path: '/displayName',
|
||||||
|
value: 'Table test case display name',
|
||||||
|
},
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
// Edit test case description
|
||||||
|
await page.click(`[data-testid="edit-${testCaseName}"]`);
|
||||||
|
await page.fill(descriptionBox, 'Test case description');
|
||||||
|
const updateTestCaseResponse2 = page.waitForResponse(
|
||||||
|
(response) =>
|
||||||
|
response.url().includes('/api/v1/dataQuality/testCases/') &&
|
||||||
|
response.request().method() === 'PATCH'
|
||||||
|
);
|
||||||
|
await page.click('.ant-modal-footer >> text=Submit');
|
||||||
|
const updateResponse2 = await updateTestCaseResponse2;
|
||||||
|
const body2 = await updateResponse2.request().postData();
|
||||||
|
|
||||||
|
expect(body2).toEqual(
|
||||||
|
JSON.stringify([
|
||||||
|
{ op: 'add', path: '/description', value: 'Test case description' },
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
// Edit test case parameter values
|
||||||
|
await page.click(`[data-testid="edit-${testCaseName}"]`);
|
||||||
|
await page.fill('#tableTestForm_params_allowedValues_0_value', 'test');
|
||||||
|
const updateTestCaseResponse3 = page.waitForResponse(
|
||||||
|
(response) =>
|
||||||
|
response.url().includes('/api/v1/dataQuality/testCases/') &&
|
||||||
|
response.request().method() === 'PATCH'
|
||||||
|
);
|
||||||
|
await page.click('.ant-modal-footer >> text=Submit');
|
||||||
|
const updateResponse3 = await updateTestCaseResponse3;
|
||||||
|
const body3 = await updateResponse3.request().postData();
|
||||||
|
|
||||||
|
expect(body3).toEqual(
|
||||||
|
JSON.stringify([
|
||||||
|
{
|
||||||
|
op: 'replace',
|
||||||
|
path: '/parameterValues/0/value',
|
||||||
|
value: '["test","yahoo","collate"]',
|
||||||
|
},
|
||||||
|
])
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
await test.step(
|
||||||
|
'Update test case display name from Data Quality page',
|
||||||
|
async () => {
|
||||||
|
const getTestCase = page.waitForResponse(
|
||||||
|
'/api/v1/dataQuality/testCases/search/list?*'
|
||||||
|
);
|
||||||
|
await sidebarClick(page, SidebarItem.DATA_QUALITY);
|
||||||
|
await page.click('[data-testid="by-test-cases"]');
|
||||||
|
await getTestCase;
|
||||||
|
const searchTestCaseResponse = page.waitForResponse(
|
||||||
|
`/api/v1/dataQuality/testCases/search/list?*q=*${testCaseName}*`
|
||||||
|
);
|
||||||
|
await page.fill(
|
||||||
|
'[data-testid="test-case-container"] [data-testid="searchbar"]',
|
||||||
|
testCaseName
|
||||||
|
);
|
||||||
|
await searchTestCaseResponse;
|
||||||
|
await page.waitForSelector('.ant-spin', {
|
||||||
|
state: 'detached',
|
||||||
|
});
|
||||||
|
await page.click(`[data-testid="edit-${testCaseName}"]`);
|
||||||
|
await page.waitForSelector('.ant-modal-title');
|
||||||
|
|
||||||
|
await expect(page.locator('#tableTestForm_displayName')).toHaveValue(
|
||||||
|
'Table test case display name'
|
||||||
|
);
|
||||||
|
|
||||||
|
await page.locator('#tableTestForm_displayName').clear();
|
||||||
|
await page.fill('#tableTestForm_displayName', 'Updated display name');
|
||||||
|
await page.click('.ant-modal-footer >> text=Submit');
|
||||||
|
await toastNotification(page, 'Test case updated successfully.');
|
||||||
|
|
||||||
|
await expect(page.locator(`[data-testid="${testCaseName}"]`)).toHaveText(
|
||||||
|
'Updated display name'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Update profiler setting modal', async ({ page }) => {
|
||||||
|
const profilerSetting = {
|
||||||
|
profileSample: '60',
|
||||||
|
sampleDataCount: '100',
|
||||||
|
profileQuery: 'select * from table',
|
||||||
|
excludeColumns: 'user_id',
|
||||||
|
includeColumns: 'shop_id',
|
||||||
|
partitionColumnName: 'name',
|
||||||
|
partitionIntervalType: 'COLUMN-VALUE',
|
||||||
|
partitionValues: 'test',
|
||||||
|
};
|
||||||
|
|
||||||
|
await table1.visitEntityPage(page);
|
||||||
|
await page.getByTestId('profiler').click();
|
||||||
|
await page
|
||||||
|
.getByTestId('profiler-tab-left-panel')
|
||||||
|
.getByText('Table Profile')
|
||||||
|
.click();
|
||||||
|
|
||||||
|
await page.click('[data-testid="profiler-setting-btn"]');
|
||||||
|
await page.waitForSelector('.ant-modal-body');
|
||||||
|
await page.locator('[data-testid="slider-input"]').clear();
|
||||||
|
await page
|
||||||
|
.locator('[data-testid="slider-input"]')
|
||||||
|
.fill(profilerSetting.profileSample);
|
||||||
|
|
||||||
|
await page.locator('[data-testid="sample-data-count-input"]').clear();
|
||||||
|
await page
|
||||||
|
.locator('[data-testid="sample-data-count-input"]')
|
||||||
|
.fill(profilerSetting.sampleDataCount);
|
||||||
|
await page.locator('[data-testid="exclude-column-select"]').click();
|
||||||
|
await page.keyboard.type(`${profilerSetting.excludeColumns}`);
|
||||||
|
await page.keyboard.press('Enter');
|
||||||
|
await page.locator('.CodeMirror-scroll').click();
|
||||||
|
await page.keyboard.type(profilerSetting.profileQuery);
|
||||||
|
|
||||||
|
await page.locator('[data-testid="include-column-select"]').click();
|
||||||
|
await page
|
||||||
|
.locator('.ant-select-dropdown')
|
||||||
|
.locator(
|
||||||
|
`[title="${profilerSetting.includeColumns}"]:not(.ant-select-dropdown-hidden)`
|
||||||
|
)
|
||||||
|
.last()
|
||||||
|
.click();
|
||||||
|
await page.locator('[data-testid="enable-partition-switch"]').click();
|
||||||
|
await page.locator('[data-testid="interval-type"]').click();
|
||||||
|
await page
|
||||||
|
.locator('.ant-select-dropdown')
|
||||||
|
.locator(
|
||||||
|
`[title="${profilerSetting.partitionIntervalType}"]:not(.ant-select-dropdown-hidden)`
|
||||||
|
)
|
||||||
|
.click();
|
||||||
|
|
||||||
|
await page.locator('#includeColumnsProfiler_partitionColumnName').click();
|
||||||
|
await page
|
||||||
|
.locator('.ant-select-dropdown')
|
||||||
|
.locator(
|
||||||
|
`[title="${profilerSetting.partitionColumnName}"]:not(.ant-select-dropdown-hidden)`
|
||||||
|
)
|
||||||
|
.last()
|
||||||
|
.click();
|
||||||
|
await page
|
||||||
|
.locator('[data-testid="partition-value"]')
|
||||||
|
.fill(profilerSetting.partitionValues);
|
||||||
|
|
||||||
|
const updateTableProfilerConfigResponse = page.waitForResponse(
|
||||||
|
(response) =>
|
||||||
|
response.url().includes('/api/v1/tables/') &&
|
||||||
|
response.url().includes('/tableProfilerConfig') &&
|
||||||
|
response.request().method() === 'PUT'
|
||||||
|
);
|
||||||
|
await page.getByRole('button', { name: 'Save' }).click();
|
||||||
|
const updateResponse = await updateTableProfilerConfigResponse;
|
||||||
|
const requestBody = await updateResponse.request().postData();
|
||||||
|
|
||||||
|
expect(requestBody).toEqual(
|
||||||
|
JSON.stringify({
|
||||||
|
excludeColumns: ['user_id'],
|
||||||
|
profileQuery: 'select * from table',
|
||||||
|
profileSample: 60,
|
||||||
|
profileSampleType: 'PERCENTAGE',
|
||||||
|
includeColumns: [{ columnName: 'shop_id' }],
|
||||||
|
partitioning: {
|
||||||
|
partitionColumnName: 'name',
|
||||||
|
partitionIntervalType: 'COLUMN-VALUE',
|
||||||
|
partitionValues: ['test'],
|
||||||
|
enablePartitioning: true,
|
||||||
|
},
|
||||||
|
sampleDataCount: 100,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('TestCase filters', async ({ page }) => {
|
||||||
|
test.slow();
|
||||||
|
|
||||||
|
const { apiContext, afterAction } = await getApiContext(page);
|
||||||
|
const filterTable1 = new TableClass();
|
||||||
|
|
||||||
|
await filterTable1.create(apiContext);
|
||||||
|
const filterTable2 = {
|
||||||
|
...filterTable1.entity,
|
||||||
|
name: `${filterTable1.entity.name}-model`,
|
||||||
|
};
|
||||||
|
const filterTable2Response = await apiContext
|
||||||
|
.post('/api/v1/tables', {
|
||||||
|
data: filterTable2,
|
||||||
|
})
|
||||||
|
.then((response) => response.json());
|
||||||
|
const domain = new Domain();
|
||||||
|
await domain.create(apiContext);
|
||||||
|
|
||||||
|
// Add domain to table
|
||||||
|
await filterTable1.visitEntityPage(page);
|
||||||
|
await assignDomain(page, domain.responseData);
|
||||||
|
const testCases = [
|
||||||
|
`pw_first_table_column_count_to_be_between_${uuid()}`,
|
||||||
|
`pw_second_table_column_count_to_be_between_${uuid()}`,
|
||||||
|
`pw_third_table_column_count_to_be_between_${uuid()}`,
|
||||||
|
];
|
||||||
|
const smilerNameTestCase = testCases.map((test) => `${test}_version_2`);
|
||||||
|
await filterTable1.patch({
|
||||||
|
apiContext,
|
||||||
|
patchData: [
|
||||||
|
{
|
||||||
|
op: 'add',
|
||||||
|
path: '/tags/0',
|
||||||
|
value: {
|
||||||
|
tagFQN: 'PII.None',
|
||||||
|
name: 'None',
|
||||||
|
description: 'Non PII',
|
||||||
|
source: 'Classification',
|
||||||
|
labelType: 'Manual',
|
||||||
|
state: 'Confirmed',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
op: 'add',
|
||||||
|
path: '/tags/1',
|
||||||
|
value: {
|
||||||
|
tagFQN: 'Tier.Tier2',
|
||||||
|
name: 'Tier2',
|
||||||
|
source: 'Classification',
|
||||||
|
labelType: 'Manual',
|
||||||
|
state: 'Confirmed',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
await filterTable1.createTestSuiteAndPipelines(apiContext);
|
||||||
|
const { testSuiteData: testSuite2Response } =
|
||||||
|
await filterTable1.createTestSuiteAndPipelines(apiContext, {
|
||||||
|
executableEntityReference: filterTable2Response?.['fullyQualifiedName'],
|
||||||
|
});
|
||||||
|
|
||||||
|
const testCaseResult = {
|
||||||
|
result: 'Found min=10001, max=27809 vs. the expected min=90001, max=96162.',
|
||||||
|
testCaseStatus: 'Failed',
|
||||||
|
testResultValue: [
|
||||||
|
{
|
||||||
|
name: 'minValueForMaxInCol',
|
||||||
|
value: '10001',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'maxValueForMaxInCol',
|
||||||
|
value: '27809',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
timestamp: getCurrentMillis(),
|
||||||
|
};
|
||||||
|
for (let i = 0; i < testCases.length; i++) {
|
||||||
|
const testCase1 = await filterTable1.createTestCase(apiContext, {
|
||||||
|
name: testCases[i],
|
||||||
|
});
|
||||||
|
await filterTable1.addTestCaseResult(
|
||||||
|
apiContext,
|
||||||
|
testCase1?.['fullyQualifiedName'],
|
||||||
|
testCaseResult
|
||||||
|
);
|
||||||
|
const testCase2 = await filterTable1.createTestCase(apiContext, {
|
||||||
|
name: smilerNameTestCase[i],
|
||||||
|
entityLink: `<#E::table::${filterTable2Response?.['fullyQualifiedName']}>`,
|
||||||
|
testSuite: testSuite2Response?.['fullyQualifiedName'],
|
||||||
|
});
|
||||||
|
await filterTable1.addTestCaseResult(
|
||||||
|
apiContext,
|
||||||
|
testCase2?.['fullyQualifiedName'],
|
||||||
|
testCaseResult
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const verifyFilterTestCase = async (page: Page) => {
|
||||||
|
for (const testCase of testCases) {
|
||||||
|
const element = page.locator(`[data-testid="${testCase}"]`);
|
||||||
|
|
||||||
|
await expect(element).toBeVisible();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const verifyFilter2TestCase = async (page: Page, negation = false) => {
|
||||||
|
for (const testCase of smilerNameTestCase) {
|
||||||
|
const element = page.locator(`[data-testid="${testCase}"]`);
|
||||||
|
if (negation) {
|
||||||
|
await expect(element).not.toBeVisible();
|
||||||
|
} else {
|
||||||
|
await expect(element).toBeVisible();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
await sidebarClick(page, SidebarItem.DATA_QUALITY);
|
||||||
|
const getTestCaseListData = page.waitForResponse(
|
||||||
|
'/api/v1/dataQuality/testCases/search/list?*'
|
||||||
|
);
|
||||||
|
await page.click('[data-testid="by-test-cases"]');
|
||||||
|
await getTestCaseListData;
|
||||||
|
// get all the filters
|
||||||
|
await page.click('[data-testid="advanced-filter"]');
|
||||||
|
await page.click('[value="tableFqn"]');
|
||||||
|
await page.click('[data-testid="advanced-filter"]');
|
||||||
|
await page.click('[value="testPlatforms"]');
|
||||||
|
await page.click('[data-testid="advanced-filter"]');
|
||||||
|
await page.click('[value="lastRunRange"]');
|
||||||
|
await page.click('[data-testid="advanced-filter"]');
|
||||||
|
await page.click('[value="serviceName"]');
|
||||||
|
await page.click('[data-testid="advanced-filter"]');
|
||||||
|
await page.click('[value="tags"]');
|
||||||
|
await page.click('[data-testid="advanced-filter"]');
|
||||||
|
await page.click('[value="tier"]');
|
||||||
|
|
||||||
|
// Test case search filter
|
||||||
|
const searchTestCaseResponse = page.waitForResponse(
|
||||||
|
(url) =>
|
||||||
|
url.url().includes('/api/v1/dataQuality/testCases/search/list') &&
|
||||||
|
url.url().includes(testCases[0])
|
||||||
|
);
|
||||||
|
await page.fill(
|
||||||
|
'[data-testid="test-case-container"] [data-testid="searchbar"]',
|
||||||
|
testCases[0]
|
||||||
|
);
|
||||||
|
await searchTestCaseResponse;
|
||||||
|
|
||||||
|
await expect(page.locator(`[data-testid="${testCases[0]}"]`)).toBeVisible();
|
||||||
|
|
||||||
|
// clear the search filter
|
||||||
|
const getTestCaseResponse = page.waitForResponse(
|
||||||
|
'/api/v1/dataQuality/testCases/search/list?*'
|
||||||
|
);
|
||||||
|
await page.locator('.ant-input-clear-icon').click();
|
||||||
|
await getTestCaseResponse;
|
||||||
|
|
||||||
|
// Test case filter by service name
|
||||||
|
const serviceResponse = page.waitForResponse(
|
||||||
|
'/api/v1/search/query?q=*index=database_service_search_index*'
|
||||||
|
);
|
||||||
|
await page.fill('#serviceName', filterTable1.service.name);
|
||||||
|
await serviceResponse;
|
||||||
|
|
||||||
|
const testCaseByServiceName = page.waitForResponse(
|
||||||
|
`/api/v1/dataQuality/testCases/search/list?*serviceName=${filterTable1.service.name}*`
|
||||||
|
);
|
||||||
|
await page
|
||||||
|
.locator('.ant-select-dropdown')
|
||||||
|
.filter({
|
||||||
|
hasNot: page.locator('.ant-select-dropdown-hidden'),
|
||||||
|
has: page.locator(`[data-testid="${filterTable1.service.name}"]`),
|
||||||
|
})
|
||||||
|
.click();
|
||||||
|
await testCaseByServiceName;
|
||||||
|
await verifyFilterTestCase(page);
|
||||||
|
await verifyFilter2TestCase(page);
|
||||||
|
|
||||||
|
// remove service filter
|
||||||
|
await page.click('[data-testid="advanced-filter"]');
|
||||||
|
const getTestCase = page.waitForResponse(
|
||||||
|
'/api/v1/dataQuality/testCases/search/list?*'
|
||||||
|
);
|
||||||
|
await page.click('[value="serviceName"]');
|
||||||
|
await getTestCase;
|
||||||
|
|
||||||
|
// Test case filter by Tags
|
||||||
|
const tagResponse = page.waitForResponse(
|
||||||
|
'/api/v1/search/query?q=*index=tag_search_index*'
|
||||||
|
);
|
||||||
|
await page
|
||||||
|
.getByTestId('tags-select-filter')
|
||||||
|
.locator('div')
|
||||||
|
.filter({ hasText: 'Tags' })
|
||||||
|
.click();
|
||||||
|
await page.fill('#tags', 'PII.None');
|
||||||
|
await tagResponse;
|
||||||
|
|
||||||
|
const getTestCaseByTag = page.waitForResponse(
|
||||||
|
'/api/v1/dataQuality/testCases/search/list?*tags=PII.None*'
|
||||||
|
);
|
||||||
|
await page
|
||||||
|
.locator('.ant-select-dropdown')
|
||||||
|
.filter({
|
||||||
|
hasNot: page.locator('.ant-select-dropdown-hidden'),
|
||||||
|
has: page.locator(`[data-testid="PII.None"]`),
|
||||||
|
})
|
||||||
|
.click();
|
||||||
|
await getTestCaseByTag;
|
||||||
|
await verifyFilterTestCase(page);
|
||||||
|
await verifyFilter2TestCase(page, true);
|
||||||
|
|
||||||
|
// remove tags filter
|
||||||
|
await page.click('[data-testid="advanced-filter"]');
|
||||||
|
const getTestCaseWithoutTag = page.waitForResponse(
|
||||||
|
'/api/v1/dataQuality/testCases/search/list?*'
|
||||||
|
);
|
||||||
|
await page.click('[value="tags"]');
|
||||||
|
await getTestCaseWithoutTag;
|
||||||
|
|
||||||
|
// Test case filter by Tier
|
||||||
|
|
||||||
|
await page.click('#tier');
|
||||||
|
const getTestCaseByTier = page.waitForResponse(
|
||||||
|
'/api/v1/dataQuality/testCases/search/list?*tier=Tier.Tier2*'
|
||||||
|
);
|
||||||
|
await page.getByTestId('Tier.Tier2').getByText('Tier.Tier2').click();
|
||||||
|
await getTestCaseByTier;
|
||||||
|
await verifyFilterTestCase(page);
|
||||||
|
await verifyFilter2TestCase(page, true);
|
||||||
|
|
||||||
|
// remove tier filter
|
||||||
|
await page.click('[data-testid="advanced-filter"]');
|
||||||
|
const getTestCaseWithoutTier = page.waitForResponse(
|
||||||
|
'/api/v1/dataQuality/testCases/search/list?*'
|
||||||
|
);
|
||||||
|
await page.click('[value="tier"]');
|
||||||
|
await getTestCaseWithoutTier;
|
||||||
|
|
||||||
|
// Test case filter by table name
|
||||||
|
const tableSearchResponse = page.waitForResponse(
|
||||||
|
`/api/v1/search/query?q=*index=table_search_index*`
|
||||||
|
);
|
||||||
|
await page.fill('#tableFqn', filterTable1.entity.name);
|
||||||
|
await tableSearchResponse;
|
||||||
|
const getTestCaseByTable = page.waitForResponse(
|
||||||
|
`/api/v1/dataQuality/testCases/search/list?*entityLink=*${filterTable1.entity.name}*`
|
||||||
|
);
|
||||||
|
|
||||||
|
await page
|
||||||
|
.getByTestId(filterTable1.entityResponseData?.['fullyQualifiedName'])
|
||||||
|
.click();
|
||||||
|
await getTestCaseByTable;
|
||||||
|
await verifyFilterTestCase(page);
|
||||||
|
await verifyFilter2TestCase(page, true);
|
||||||
|
|
||||||
|
// Test case filter by test type column
|
||||||
|
const testCaseTypeByColumn = page.waitForResponse(
|
||||||
|
`/api/v1/dataQuality/testCases/search/list?*testCaseType=column*`
|
||||||
|
);
|
||||||
|
await page.getByTestId('test-case-type-select-filter').click();
|
||||||
|
await page.getByTitle('Column').click();
|
||||||
|
await testCaseTypeByColumn;
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
page.locator('[data-testid="search-error-placeholder"]')
|
||||||
|
).toBeVisible();
|
||||||
|
|
||||||
|
// Test case filter by test type table
|
||||||
|
const testCaseTypeByTable = page.waitForResponse(
|
||||||
|
`/api/v1/dataQuality/testCases/search/list?*testCaseType=table*`
|
||||||
|
);
|
||||||
|
await page.getByTestId('test-case-type-select-filter').click();
|
||||||
|
await page
|
||||||
|
.locator('.ant-select-dropdown')
|
||||||
|
.filter({
|
||||||
|
hasNot: page.locator('.ant-select-dropdown-hidden'),
|
||||||
|
has: page.locator(`[title="Table"]`),
|
||||||
|
hasText: 'Table',
|
||||||
|
})
|
||||||
|
.click();
|
||||||
|
await testCaseTypeByTable;
|
||||||
|
await verifyFilterTestCase(page);
|
||||||
|
|
||||||
|
// Test case filter by test type all
|
||||||
|
const testCaseTypeByAll = page.waitForResponse(
|
||||||
|
`/api/v1/dataQuality/testCases/search/list?*testCaseType=all*`
|
||||||
|
);
|
||||||
|
await page.getByTestId('test-case-type-select-filter').click();
|
||||||
|
await page.getByTitle('All').nth(1).click();
|
||||||
|
await testCaseTypeByAll;
|
||||||
|
|
||||||
|
// Test case filter by status
|
||||||
|
const testCaseStatusBySuccess = page.waitForResponse(
|
||||||
|
`/api/v1/dataQuality/testCases/search/list?*testCaseStatus=Success*`
|
||||||
|
);
|
||||||
|
await page.getByTestId('status-select-filter').click();
|
||||||
|
await page.getByTitle('Success').click();
|
||||||
|
await testCaseStatusBySuccess;
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
page.locator('[data-testid="search-error-placeholder"]')
|
||||||
|
).toBeVisible();
|
||||||
|
|
||||||
|
// Test case filter by status
|
||||||
|
const testCaseStatusByFailed = page.waitForResponse(
|
||||||
|
`/api/v1/dataQuality/testCases/search/list?*testCaseStatus=Failed*`
|
||||||
|
);
|
||||||
|
await page.getByTestId('status-select-filter').click();
|
||||||
|
await page.getByTitle('Failed').click();
|
||||||
|
await testCaseStatusByFailed;
|
||||||
|
await verifyFilterTestCase(page);
|
||||||
|
await verifyFilter2TestCase(page, true);
|
||||||
|
|
||||||
|
// Test case filter by platform
|
||||||
|
const testCasePlatformByDBT = page.waitForResponse(
|
||||||
|
`/api/v1/dataQuality/testCases/search/list?*testPlatforms=DBT*`
|
||||||
|
);
|
||||||
|
await page.getByTestId('platform-select-filter').click();
|
||||||
|
await page.getByTitle('DBT').click();
|
||||||
|
await testCasePlatformByDBT;
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
page.locator('[data-testid="search-error-placeholder"]')
|
||||||
|
).toBeVisible();
|
||||||
|
|
||||||
|
const getTestCaseWithoutPlatform = page.waitForResponse(
|
||||||
|
'/api/v1/dataQuality/testCases/search/list?*'
|
||||||
|
);
|
||||||
|
await page
|
||||||
|
.getByTestId('platform-select-filter')
|
||||||
|
.locator('.ant-select-clear')
|
||||||
|
.click();
|
||||||
|
await getTestCaseWithoutPlatform;
|
||||||
|
const testCasePlatformByOpenMetadata = page.waitForResponse(
|
||||||
|
`/api/v1/dataQuality/testCases/search/list?*testPlatforms=OpenMetadata*`
|
||||||
|
);
|
||||||
|
await page.getByTestId('platform-select-filter').click();
|
||||||
|
await page.getByTitle('OpenMetadata').click();
|
||||||
|
await testCasePlatformByOpenMetadata;
|
||||||
|
await clickOutside(page);
|
||||||
|
await verifyFilterTestCase(page);
|
||||||
|
await verifyFilter2TestCase(page, true);
|
||||||
|
const url = page.url();
|
||||||
|
await page.reload();
|
||||||
|
|
||||||
|
await expect(page.url()).toBe(url);
|
||||||
|
|
||||||
|
await page.getByTestId('advanced-filter').click();
|
||||||
|
await page.click('[value="testPlatforms"]');
|
||||||
|
await page.waitForTimeout(200);
|
||||||
|
|
||||||
|
await expect(page.getByTestId('platform-select-filter')).not.toBeVisible();
|
||||||
|
|
||||||
|
await page.reload();
|
||||||
|
|
||||||
|
await expect(page.locator('[value="tier"]')).not.toBeVisible();
|
||||||
|
|
||||||
|
// Apply domain globally
|
||||||
|
await page.locator('[data-testid="domain-dropdown"]').click();
|
||||||
|
await page
|
||||||
|
.locator(`li[data-menu-id*='${domain.responseData?.['name']}']`)
|
||||||
|
.click();
|
||||||
|
await sidebarClick(page, SidebarItem.DATA_QUALITY);
|
||||||
|
const getTestCaseList = page.waitForResponse(
|
||||||
|
'/api/v1/dataQuality/testCases/search/list?*'
|
||||||
|
);
|
||||||
|
await page.click('[data-testid="by-test-cases"]');
|
||||||
|
await getTestCaseList;
|
||||||
|
await verifyFilterTestCase(page);
|
||||||
|
await verifyFilter2TestCase(page, true);
|
||||||
|
} finally {
|
||||||
|
await filterTable1.delete(apiContext);
|
||||||
|
await domain.delete(apiContext);
|
||||||
|
await afterAction();
|
||||||
|
}
|
||||||
|
});
|
@ -12,7 +12,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import test, { expect } from '@playwright/test';
|
import test, { expect } from '@playwright/test';
|
||||||
import { POSTGRES, REDSHIFT } from '../../constant/service';
|
import { MYSQL, POSTGRES, REDSHIFT } from '../../constant/service';
|
||||||
import { GlobalSettingOptions } from '../../constant/settings';
|
import { GlobalSettingOptions } from '../../constant/settings';
|
||||||
import AirflowIngestionClass from '../../support/entity/ingestion/AirflowIngestionClass';
|
import AirflowIngestionClass from '../../support/entity/ingestion/AirflowIngestionClass';
|
||||||
import BigQueryIngestionClass from '../../support/entity/ingestion/BigQueryIngestionClass';
|
import BigQueryIngestionClass from '../../support/entity/ingestion/BigQueryIngestionClass';
|
||||||
@ -83,7 +83,9 @@ services.forEach((ServiceClass) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (
|
if (
|
||||||
[POSTGRES.serviceType, REDSHIFT.serviceType].includes(service.serviceType)
|
[POSTGRES.serviceType, REDSHIFT.serviceType, MYSQL].includes(
|
||||||
|
service.serviceType
|
||||||
|
)
|
||||||
) {
|
) {
|
||||||
test(`Service specific tests`, async ({ page }) => {
|
test(`Service specific tests`, async ({ page }) => {
|
||||||
await service.runAdditionalTests(page, test);
|
await service.runAdditionalTests(page, test);
|
||||||
|
@ -12,8 +12,12 @@
|
|||||||
*/
|
*/
|
||||||
import { expect, test } from '@playwright/test';
|
import { expect, test } from '@playwright/test';
|
||||||
import { TableClass } from '../../support/entity/TableClass';
|
import { TableClass } from '../../support/entity/TableClass';
|
||||||
import { getApiContext, redirectToHomePage } from '../../utils/common';
|
import {
|
||||||
import { deleteTestCase } from '../../utils/testCases';
|
descriptionBox,
|
||||||
|
getApiContext,
|
||||||
|
redirectToHomePage,
|
||||||
|
} from '../../utils/common';
|
||||||
|
import { deleteTestCase, visitDataQualityTab } from '../../utils/testCases';
|
||||||
|
|
||||||
// use the admin user to login
|
// use the admin user to login
|
||||||
test.use({ storageState: 'playwright/.auth/admin.json' });
|
test.use({ storageState: 'playwright/.auth/admin.json' });
|
||||||
@ -231,3 +235,104 @@ test('Custom SQL Query', async ({ page }) => {
|
|||||||
await afterAction();
|
await afterAction();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('Column Values To Be Not Null', async ({ page }) => {
|
||||||
|
test.slow();
|
||||||
|
|
||||||
|
const NEW_COLUMN_TEST_CASE_WITH_NULL_TYPE = {
|
||||||
|
name: 'id_column_values_to_be_not_null',
|
||||||
|
displayName: 'ID Column Values To Be Not Null',
|
||||||
|
column: 'user_id',
|
||||||
|
type: 'columnValuesToBeNotNull',
|
||||||
|
label: 'Column Values To Be Not Null',
|
||||||
|
description: 'New table test case for columnValuesToBeNotNull',
|
||||||
|
};
|
||||||
|
await redirectToHomePage(page);
|
||||||
|
const { afterAction, apiContext } = await getApiContext(page);
|
||||||
|
const table = new TableClass();
|
||||||
|
await table.create(apiContext);
|
||||||
|
|
||||||
|
await visitDataQualityTab(page, table);
|
||||||
|
await page.click('[data-testid="profiler-add-table-test-btn"]');
|
||||||
|
await page.click('[data-testid="column"]');
|
||||||
|
|
||||||
|
try {
|
||||||
|
await test.step('Create', async () => {
|
||||||
|
const testDefinitionResponse = page.waitForResponse(
|
||||||
|
'/api/v1/dataQuality/testDefinitions?limit=*&entityType=COLUMN&testPlatform=OpenMetadata&supportedDataType=NUMERIC'
|
||||||
|
);
|
||||||
|
await page.click('#tableTestForm_column');
|
||||||
|
await page.click(
|
||||||
|
`[title="${NEW_COLUMN_TEST_CASE_WITH_NULL_TYPE.column}"]`
|
||||||
|
);
|
||||||
|
await testDefinitionResponse;
|
||||||
|
await page.fill(
|
||||||
|
'#tableTestForm_testName',
|
||||||
|
NEW_COLUMN_TEST_CASE_WITH_NULL_TYPE.name
|
||||||
|
);
|
||||||
|
|
||||||
|
await page.fill(
|
||||||
|
'#tableTestForm_testTypeId',
|
||||||
|
NEW_COLUMN_TEST_CASE_WITH_NULL_TYPE.type
|
||||||
|
);
|
||||||
|
await page.click(
|
||||||
|
`[title="${NEW_COLUMN_TEST_CASE_WITH_NULL_TYPE.label}"]`
|
||||||
|
);
|
||||||
|
await page.fill(
|
||||||
|
descriptionBox,
|
||||||
|
NEW_COLUMN_TEST_CASE_WITH_NULL_TYPE.description
|
||||||
|
);
|
||||||
|
|
||||||
|
await page.click('[data-testid="submit-test"]');
|
||||||
|
await page.waitForSelector('[data-testid="success-line"]');
|
||||||
|
await page.waitForSelector('[data-testid="view-service-button"]');
|
||||||
|
const testCaseResponse = page.waitForResponse(
|
||||||
|
'/api/v1/dataQuality/testCases?fields=*'
|
||||||
|
);
|
||||||
|
await page.click(`[data-testid="view-service-button"]`);
|
||||||
|
await testCaseResponse;
|
||||||
|
await page.click('[data-testid="profiler-tab-left-panel"]');
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
page.locator(
|
||||||
|
`[data-testid="${NEW_COLUMN_TEST_CASE_WITH_NULL_TYPE.name}"]`
|
||||||
|
)
|
||||||
|
).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
await test.step('Edit', async () => {
|
||||||
|
await page
|
||||||
|
.getByTestId(`edit-${NEW_COLUMN_TEST_CASE_WITH_NULL_TYPE.name}`)
|
||||||
|
.click();
|
||||||
|
|
||||||
|
await expect(page.locator('.ant-modal-title')).toHaveText(
|
||||||
|
`Edit ${NEW_COLUMN_TEST_CASE_WITH_NULL_TYPE.name}`
|
||||||
|
);
|
||||||
|
await expect(page.locator('#tableTestForm_name')).toHaveValue(
|
||||||
|
NEW_COLUMN_TEST_CASE_WITH_NULL_TYPE.name
|
||||||
|
);
|
||||||
|
|
||||||
|
await page.locator('#tableTestForm_displayName').clear();
|
||||||
|
await page.fill(
|
||||||
|
'#tableTestForm_displayName',
|
||||||
|
NEW_COLUMN_TEST_CASE_WITH_NULL_TYPE.displayName
|
||||||
|
);
|
||||||
|
await page.getByText('New table test case for').first().click();
|
||||||
|
await page.keyboard.type(' update');
|
||||||
|
await page.getByRole('button', { name: 'Submit' }).click();
|
||||||
|
|
||||||
|
await expect(page.getByRole('alert')).toContainText(
|
||||||
|
'Test case updated successfully.'
|
||||||
|
);
|
||||||
|
|
||||||
|
await page.getByTestId('content-wrapper').getByLabel('close').click();
|
||||||
|
});
|
||||||
|
|
||||||
|
await test.step('Delete', async () => {
|
||||||
|
await deleteTestCase(page, NEW_COLUMN_TEST_CASE_WITH_NULL_TYPE.name);
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
await table.delete(apiContext);
|
||||||
|
await afterAction();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
@ -0,0 +1,223 @@
|
|||||||
|
/*
|
||||||
|
* 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 { expect, test } from '@playwright/test';
|
||||||
|
import { SidebarItem } from '../../constant/sidebar';
|
||||||
|
import { EntityTypeEndpoint } from '../../support/entity/Entity.interface';
|
||||||
|
import { TableClass } from '../../support/entity/TableClass';
|
||||||
|
import { UserClass } from '../../support/user/UserClass';
|
||||||
|
import {
|
||||||
|
createNewPage,
|
||||||
|
descriptionBox,
|
||||||
|
redirectToHomePage,
|
||||||
|
toastNotification,
|
||||||
|
uuid,
|
||||||
|
} from '../../utils/common';
|
||||||
|
import { addMultiOwner, removeOwnersFromList } from '../../utils/entity';
|
||||||
|
import { sidebarClick } from '../../utils/sidebar';
|
||||||
|
|
||||||
|
// use the admin user to login
|
||||||
|
test.use({ storageState: 'playwright/.auth/admin.json' });
|
||||||
|
|
||||||
|
const table = new TableClass();
|
||||||
|
const user1 = new UserClass();
|
||||||
|
const user2 = new UserClass();
|
||||||
|
|
||||||
|
test.beforeAll(async ({ browser }) => {
|
||||||
|
const { apiContext, afterAction } = await createNewPage(browser);
|
||||||
|
await table.create(apiContext);
|
||||||
|
await user1.create(apiContext);
|
||||||
|
await user2.create(apiContext);
|
||||||
|
await table.createTestCase(apiContext);
|
||||||
|
await table.createTestCase(apiContext);
|
||||||
|
await afterAction();
|
||||||
|
});
|
||||||
|
|
||||||
|
test.afterAll(async ({ browser }) => {
|
||||||
|
const { apiContext, afterAction } = await createNewPage(browser);
|
||||||
|
await table.delete(apiContext);
|
||||||
|
await user1.delete(apiContext);
|
||||||
|
await user2.delete(apiContext);
|
||||||
|
await afterAction();
|
||||||
|
});
|
||||||
|
|
||||||
|
test.beforeEach(async ({ page }) => {
|
||||||
|
await redirectToHomePage(page);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Logical TestSuite', async ({ page }) => {
|
||||||
|
const NEW_TEST_SUITE = {
|
||||||
|
name: `mysql_matrix-${uuid()}`,
|
||||||
|
description: 'mysql critical matrix',
|
||||||
|
};
|
||||||
|
const testCaseName1 = table.testCasesResponseData?.[0]?.['name'];
|
||||||
|
const testCaseName2 = table.testCasesResponseData?.[1]?.['name'];
|
||||||
|
await sidebarClick(page, SidebarItem.DATA_QUALITY);
|
||||||
|
const testSuite = page.waitForResponse(
|
||||||
|
'/api/v1/dataQuality/testSuites/search/list?*testSuiteType=logical*'
|
||||||
|
);
|
||||||
|
await page.click('[data-testid="by-test-suites"]');
|
||||||
|
await testSuite;
|
||||||
|
|
||||||
|
await test.step('Create', async () => {
|
||||||
|
await page.click('[data-testid="add-test-suite-btn"]');
|
||||||
|
await page.fill('[data-testid="test-suite-name"]', NEW_TEST_SUITE.name);
|
||||||
|
await page.fill(descriptionBox, NEW_TEST_SUITE.description);
|
||||||
|
|
||||||
|
await page.click('[data-testid="submit-button"]');
|
||||||
|
const getTestCase = page.waitForResponse(
|
||||||
|
'/api/v1/search/query?q=*&index=test_case_search_index*'
|
||||||
|
);
|
||||||
|
await page.fill('[data-testid="searchbar"]', testCaseName1);
|
||||||
|
await getTestCase;
|
||||||
|
|
||||||
|
await page.click(`[data-testid="${testCaseName1}"]`);
|
||||||
|
await page.click('[data-testid="submit"]');
|
||||||
|
|
||||||
|
await page.waitForSelector('[data-testid="success-line"]', {
|
||||||
|
state: 'visible',
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(page.locator('[data-testid="success-line"]')).toContainText(
|
||||||
|
'has been created successfully'
|
||||||
|
);
|
||||||
|
|
||||||
|
const testSuiteResponse = page.waitForResponse(
|
||||||
|
'/api/v1/dataQuality/testSuites/name/*'
|
||||||
|
);
|
||||||
|
await page.click(`[data-testid="view-service-button"]`);
|
||||||
|
await testSuiteResponse;
|
||||||
|
});
|
||||||
|
|
||||||
|
await test.step(
|
||||||
|
'User as Owner assign, update & delete for test suite',
|
||||||
|
async () => {
|
||||||
|
await addMultiOwner({
|
||||||
|
page,
|
||||||
|
ownerNames: [user1.getUserName()],
|
||||||
|
activatorBtnDataTestId: 'edit-owner',
|
||||||
|
endpoint: EntityTypeEndpoint.TestSuites,
|
||||||
|
type: 'Users',
|
||||||
|
});
|
||||||
|
await removeOwnersFromList({
|
||||||
|
page,
|
||||||
|
ownerNames: [user1.getUserName()],
|
||||||
|
endpoint: EntityTypeEndpoint.TestSuites,
|
||||||
|
});
|
||||||
|
await addMultiOwner({
|
||||||
|
page,
|
||||||
|
ownerNames: [user2.getUserName()],
|
||||||
|
activatorBtnDataTestId: 'edit-owner',
|
||||||
|
endpoint: EntityTypeEndpoint.TestSuites,
|
||||||
|
type: 'Users',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
await test.step('Add test case to logical test suite', async () => {
|
||||||
|
const testCaseResponse = page.waitForResponse(
|
||||||
|
'/api/v1/dataQuality/testCases?fields=*'
|
||||||
|
);
|
||||||
|
await page.click('[data-testid="add-test-case-btn"]');
|
||||||
|
await testCaseResponse;
|
||||||
|
|
||||||
|
const getTestCase = page.waitForResponse(
|
||||||
|
'/api/v1/search/query?q=*&index=test_case_search_index*'
|
||||||
|
);
|
||||||
|
await page.fill('[data-testid="searchbar"]', testCaseName2);
|
||||||
|
await getTestCase;
|
||||||
|
|
||||||
|
await page.click(`[data-testid="${testCaseName2}"]`);
|
||||||
|
const updateTestCase = page.waitForResponse(
|
||||||
|
'/api/v1/dataQuality/testCases/logicalTestCases'
|
||||||
|
);
|
||||||
|
await page.click('[data-testid="submit"]');
|
||||||
|
await updateTestCase;
|
||||||
|
await page.waitForSelector('.ant-modal-content', {
|
||||||
|
state: 'detached',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await test.step('Remove test case from logical test suite', async () => {
|
||||||
|
await page.click(`[data-testid="remove-${testCaseName1}"]`);
|
||||||
|
const removeTestCase1 = page.waitForResponse(
|
||||||
|
'/api/v1/dataQuality/testCases/logicalTestCases/*/*'
|
||||||
|
);
|
||||||
|
await page.click('[data-testid="save-button"]');
|
||||||
|
await removeTestCase1;
|
||||||
|
await page.click(`[data-testid="remove-${testCaseName2}"]`);
|
||||||
|
const removeTestCase2 = page.waitForResponse(
|
||||||
|
'/api/v1/dataQuality/testCases/logicalTestCases/*/*'
|
||||||
|
);
|
||||||
|
await page.click('[data-testid="save-button"]');
|
||||||
|
await removeTestCase2;
|
||||||
|
});
|
||||||
|
|
||||||
|
await test.step('Test suite filters', async () => {
|
||||||
|
const owner = user2.getUserName();
|
||||||
|
const testSuite = page.waitForResponse(
|
||||||
|
'/api/v1/dataQuality/testSuites/search/list?*testSuiteType=logical*'
|
||||||
|
);
|
||||||
|
await page.getByRole('link', { name: 'Test Suites' }).click();
|
||||||
|
await testSuite;
|
||||||
|
|
||||||
|
await page.click('[data-testid="owner-select-filter"]');
|
||||||
|
await page.waitForSelector("[data-testid='select-owner-tabs']", {
|
||||||
|
state: 'visible',
|
||||||
|
});
|
||||||
|
const getOwnerList = page.waitForResponse('/api/v1/users?*isBot=false*');
|
||||||
|
await page.click('.ant-tabs [id*=tab-users]');
|
||||||
|
await getOwnerList;
|
||||||
|
await page.waitForSelector(`[data-testid="loader"]`, {
|
||||||
|
state: 'detached',
|
||||||
|
});
|
||||||
|
|
||||||
|
const searchOwner = page.waitForResponse(
|
||||||
|
'api/v1/search/query?q=*&index=user_search_index*'
|
||||||
|
);
|
||||||
|
await page.fill('[data-testid="owner-select-users-search-bar"]', owner);
|
||||||
|
await searchOwner;
|
||||||
|
|
||||||
|
const testSuiteByOwner = page.waitForResponse(
|
||||||
|
'/api/v1/dataQuality/testSuites/search/list?*owner=*'
|
||||||
|
);
|
||||||
|
await page.click(`.ant-popover [title="${owner}"]`);
|
||||||
|
await testSuiteByOwner;
|
||||||
|
await page.waitForSelector(`[data-testid="${NEW_TEST_SUITE.name}"]`, {
|
||||||
|
state: 'visible',
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
page.locator(`[data-testid="${NEW_TEST_SUITE.name}"]`)
|
||||||
|
).toBeVisible();
|
||||||
|
|
||||||
|
await page.click(`[data-testid="${NEW_TEST_SUITE.name}"]`);
|
||||||
|
});
|
||||||
|
|
||||||
|
await test.step('Delete', async () => {
|
||||||
|
await page.click('[data-testid="manage-button"]');
|
||||||
|
await page.click('[data-testid="delete-button"]');
|
||||||
|
|
||||||
|
// Click on Permanent/Hard delete option
|
||||||
|
await page.click('[data-testid="hard-delete-option"]');
|
||||||
|
await page.fill('[data-testid="confirmation-text-input"]', 'DELETE');
|
||||||
|
const deleteResponse = page.waitForResponse(
|
||||||
|
'/api/v1/dataQuality/testSuites/*?hardDelete=true&recursive=true'
|
||||||
|
);
|
||||||
|
await page.click('[data-testid="confirm-button"]');
|
||||||
|
await deleteResponse;
|
||||||
|
await toastNotification(
|
||||||
|
page,
|
||||||
|
`"${NEW_TEST_SUITE.name}" deleted successfully!`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
@ -38,6 +38,7 @@ export enum EntityTypeEndpoint {
|
|||||||
API_COLLECTION = 'apiCollections',
|
API_COLLECTION = 'apiCollections',
|
||||||
API_ENDPOINT = 'apiEndpoints',
|
API_ENDPOINT = 'apiEndpoints',
|
||||||
DATA_PRODUCT = 'dataProducts',
|
DATA_PRODUCT = 'dataProducts',
|
||||||
|
TestSuites = 'dataQuality/testSuites',
|
||||||
}
|
}
|
||||||
|
|
||||||
export type EntityDataType = {
|
export type EntityDataType = {
|
||||||
@ -67,6 +68,15 @@ export enum ENTITY_PATH {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type TestCaseData = {
|
export type TestCaseData = {
|
||||||
parameterValues: unknown[];
|
parameterValues?: unknown[];
|
||||||
testDefinition: string;
|
name?: string;
|
||||||
|
entityLink?: string;
|
||||||
|
testDefinition?: string;
|
||||||
|
testSuite?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TestSuiteData = {
|
||||||
|
name?: string;
|
||||||
|
executableEntityReference?: string;
|
||||||
|
description?: string;
|
||||||
};
|
};
|
||||||
|
@ -11,10 +11,15 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { APIRequestContext, Page } from '@playwright/test';
|
import { APIRequestContext, Page } from '@playwright/test';
|
||||||
|
import { Operation } from 'fast-json-patch';
|
||||||
import { SERVICE_TYPE } from '../../constant/service';
|
import { SERVICE_TYPE } from '../../constant/service';
|
||||||
import { uuid } from '../../utils/common';
|
import { uuid } from '../../utils/common';
|
||||||
import { visitEntityPage } from '../../utils/entity';
|
import { visitEntityPage } from '../../utils/entity';
|
||||||
import { EntityTypeEndpoint, TestCaseData } from './Entity.interface';
|
import {
|
||||||
|
EntityTypeEndpoint,
|
||||||
|
TestCaseData,
|
||||||
|
TestSuiteData,
|
||||||
|
} from './Entity.interface';
|
||||||
import { EntityClass } from './EntityClass';
|
import { EntityClass } from './EntityClass';
|
||||||
|
|
||||||
export class TableClass extends EntityClass {
|
export class TableClass extends EntityClass {
|
||||||
@ -167,7 +172,10 @@ export class TableClass extends EntityClass {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async createTestSuiteAndPipelines(apiContext: APIRequestContext) {
|
async createTestSuiteAndPipelines(
|
||||||
|
apiContext: APIRequestContext,
|
||||||
|
testSuite?: TestSuiteData
|
||||||
|
) {
|
||||||
if (!this.entityResponseData) {
|
if (!this.entityResponseData) {
|
||||||
await this.create(apiContext);
|
await this.create(apiContext);
|
||||||
}
|
}
|
||||||
@ -179,6 +187,7 @@ export class TableClass extends EntityClass {
|
|||||||
executableEntityReference:
|
executableEntityReference:
|
||||||
this.entityResponseData?.['fullyQualifiedName'],
|
this.entityResponseData?.['fullyQualifiedName'],
|
||||||
description: 'Playwright test suite for table',
|
description: 'Playwright test suite for table',
|
||||||
|
...testSuite,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.then((res) => res.json());
|
.then((res) => res.json());
|
||||||
@ -240,13 +249,13 @@ export class TableClass extends EntityClass {
|
|||||||
data: {
|
data: {
|
||||||
name: `pw-test-case-${uuid()}`,
|
name: `pw-test-case-${uuid()}`,
|
||||||
entityLink: `<#E::table::${this.entityResponseData?.['fullyQualifiedName']}>`,
|
entityLink: `<#E::table::${this.entityResponseData?.['fullyQualifiedName']}>`,
|
||||||
testDefinition:
|
testDefinition: 'tableRowCountToBeBetween',
|
||||||
testCaseData?.testDefinition ?? 'tableRowCountToBeBetween',
|
|
||||||
testSuite: this.testSuiteResponseData?.['fullyQualifiedName'],
|
testSuite: this.testSuiteResponseData?.['fullyQualifiedName'],
|
||||||
parameterValues: testCaseData?.parameterValues ?? [
|
parameterValues: [
|
||||||
{ name: 'minValue', value: 12 },
|
{ name: 'minValue', value: 12 },
|
||||||
{ name: 'maxValue', value: 34 },
|
{ name: 'maxValue', value: 34 },
|
||||||
],
|
],
|
||||||
|
...testCaseData,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.then((res) => res.json());
|
.then((res) => res.json());
|
||||||
@ -256,6 +265,43 @@ export class TableClass extends EntityClass {
|
|||||||
return testCase;
|
return testCase;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async addTestCaseResult(
|
||||||
|
apiContext: APIRequestContext,
|
||||||
|
testCaseFqn: string,
|
||||||
|
testCaseResult: unknown
|
||||||
|
) {
|
||||||
|
const testCaseResultResponse = await apiContext.put(
|
||||||
|
`/api/v1/dataQuality/testCases/${testCaseFqn}/testCaseResult`,
|
||||||
|
{ data: testCaseResult }
|
||||||
|
);
|
||||||
|
|
||||||
|
return await testCaseResultResponse.json();
|
||||||
|
}
|
||||||
|
|
||||||
|
async patch({
|
||||||
|
apiContext,
|
||||||
|
patchData,
|
||||||
|
}: {
|
||||||
|
apiContext: APIRequestContext;
|
||||||
|
patchData: Operation[];
|
||||||
|
}) {
|
||||||
|
const response = await apiContext.patch(
|
||||||
|
`/api/v1/tables/name/${this.entityResponseData?.['fullyQualifiedName']}`,
|
||||||
|
{
|
||||||
|
data: patchData,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json-patch+json',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
this.entityResponseData = await response.json();
|
||||||
|
|
||||||
|
return {
|
||||||
|
entity: this.entityResponseData,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
async delete(apiContext: APIRequestContext) {
|
async delete(apiContext: APIRequestContext) {
|
||||||
const serviceResponse = await apiContext.delete(
|
const serviceResponse = await apiContext.delete(
|
||||||
`/api/v1/services/databaseServices/name/${encodeURIComponent(
|
`/api/v1/services/databaseServices/name/${encodeURIComponent(
|
||||||
|
@ -10,9 +10,22 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { expect, Page } from '@playwright/test';
|
import {
|
||||||
|
expect,
|
||||||
|
Page,
|
||||||
|
PlaywrightTestArgs,
|
||||||
|
PlaywrightWorkerArgs,
|
||||||
|
TestType,
|
||||||
|
} from '@playwright/test';
|
||||||
import { env } from 'process';
|
import { env } from 'process';
|
||||||
import { uuid } from '../../../utils/common';
|
import {
|
||||||
|
getApiContext,
|
||||||
|
redirectToHomePage,
|
||||||
|
toastNotification,
|
||||||
|
uuid,
|
||||||
|
} from '../../../utils/common';
|
||||||
|
import { visitEntityPage } from '../../../utils/entity';
|
||||||
|
import { visitServiceDetailsPage } from '../../../utils/service';
|
||||||
import {
|
import {
|
||||||
checkServiceFieldSectionHighlighting,
|
checkServiceFieldSectionHighlighting,
|
||||||
Services,
|
Services,
|
||||||
@ -22,6 +35,7 @@ import ServiceBaseClass from './ServiceBaseClass';
|
|||||||
class MysqlIngestionClass extends ServiceBaseClass {
|
class MysqlIngestionClass extends ServiceBaseClass {
|
||||||
name: string;
|
name: string;
|
||||||
tableFilter: string[];
|
tableFilter: string[];
|
||||||
|
profilerTable = 'alert_entity';
|
||||||
constructor() {
|
constructor() {
|
||||||
super(
|
super(
|
||||||
Services.Database,
|
Services.Database,
|
||||||
@ -62,6 +76,84 @@ class MysqlIngestionClass extends ServiceBaseClass {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async runAdditionalTests(
|
||||||
|
page: Page,
|
||||||
|
test: TestType<PlaywrightTestArgs, PlaywrightWorkerArgs>
|
||||||
|
) {
|
||||||
|
await test.step('Add Profiler ingestion', async () => {
|
||||||
|
const { apiContext } = await getApiContext(page);
|
||||||
|
await redirectToHomePage(page);
|
||||||
|
await visitServiceDetailsPage(
|
||||||
|
page,
|
||||||
|
{
|
||||||
|
type: this.category,
|
||||||
|
name: this.serviceName,
|
||||||
|
displayName: this.serviceName,
|
||||||
|
},
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
await page.click('[data-testid="ingestions"]');
|
||||||
|
await page.waitForSelector('[data-testid="ingestion-details-container"]');
|
||||||
|
await page.waitForTimeout(1000);
|
||||||
|
await page.click('[data-testid="add-new-ingestion-button"]');
|
||||||
|
await page.waitForTimeout(1000);
|
||||||
|
await page.click('[data-menu-id*="profiler"]');
|
||||||
|
|
||||||
|
await page.waitForSelector('#root\\/profileSample');
|
||||||
|
await page.fill('#root\\/profileSample', '10');
|
||||||
|
await page.click('[data-testid="submit-btn"]');
|
||||||
|
// Make sure we create ingestion with None schedule to avoid conflict between Airflow and Argo behavior
|
||||||
|
await this.scheduleIngestion(page);
|
||||||
|
|
||||||
|
await page.click('[data-testid="view-service-button"]');
|
||||||
|
|
||||||
|
// Header available once page loads
|
||||||
|
await page.waitForSelector('[data-testid="data-assets-header"]');
|
||||||
|
await page.getByTestId('loader').waitFor({ state: 'detached' });
|
||||||
|
await page.getByTestId('ingestions').click();
|
||||||
|
await page
|
||||||
|
.getByLabel('Ingestions')
|
||||||
|
.getByTestId('loader')
|
||||||
|
.waitFor({ state: 'detached' });
|
||||||
|
|
||||||
|
const response = await apiContext
|
||||||
|
.get(
|
||||||
|
`/api/v1/services/ingestionPipelines?service=${encodeURIComponent(
|
||||||
|
this.serviceName
|
||||||
|
)}&pipelineType=profiler&serviceType=databaseService&limit=1`
|
||||||
|
)
|
||||||
|
.then((res) => res.json());
|
||||||
|
|
||||||
|
await page.click(
|
||||||
|
`[data-row-key*="${response.data[0].name}"] [data-testid="more-actions"]`
|
||||||
|
);
|
||||||
|
await page.getByTestId('run-button').click();
|
||||||
|
|
||||||
|
await toastNotification(page, `Pipeline triggered successfully!`);
|
||||||
|
|
||||||
|
await this.handleIngestionRetry('profiler', page);
|
||||||
|
});
|
||||||
|
|
||||||
|
await test.step('Validate profiler ingestion', async () => {
|
||||||
|
await visitEntityPage({
|
||||||
|
page,
|
||||||
|
searchTerm: this.profilerTable,
|
||||||
|
dataTestId: `${this.serviceName}-${this.profilerTable}`,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await page.getByTestId('profiler').click();
|
||||||
|
await page
|
||||||
|
.getByTestId('profiler-tab-left-panel')
|
||||||
|
.getByText('Table Profile')
|
||||||
|
.click();
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
page.locator('[data-testid="no-profiler-placeholder"]')
|
||||||
|
).not.toBeVisible();
|
||||||
|
}
|
||||||
|
|
||||||
async validateIngestionDetails(page: Page) {
|
async validateIngestionDetails(page: Page) {
|
||||||
await page.waitForSelector('.ant-select-selection-item-content');
|
await page.waitForSelector('.ant-select-selection-item-content');
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { expect, Page } from '@playwright/test';
|
import { expect, Page } from '@playwright/test';
|
||||||
|
import { TableClass } from '../support/entity/TableClass';
|
||||||
|
|
||||||
export const deleteTestCase = async (page: Page, testCaseName: string) => {
|
export const deleteTestCase = async (page: Page, testCaseName: string) => {
|
||||||
await page.getByTestId(`delete-${testCaseName}`).click();
|
await page.getByTestId(`delete-${testCaseName}`).click();
|
||||||
@ -18,7 +19,24 @@ export const deleteTestCase = async (page: Page, testCaseName: string) => {
|
|||||||
|
|
||||||
await expect(page.getByTestId('confirm-button')).toBeEnabled();
|
await expect(page.getByTestId('confirm-button')).toBeEnabled();
|
||||||
|
|
||||||
|
const deleteResponse = page.waitForResponse(
|
||||||
|
'/api/v1/dataQuality/testCases/*?hardDelete=true&recursive=true'
|
||||||
|
);
|
||||||
await page.getByTestId('confirm-button').click();
|
await page.getByTestId('confirm-button').click();
|
||||||
|
await deleteResponse;
|
||||||
|
|
||||||
await expect(page.getByRole('alert')).toHaveText(/deleted successfully!/);
|
await expect(page.getByRole('alert')).toHaveText(/deleted successfully!/);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const visitDataQualityTab = async (page: Page, table: TableClass) => {
|
||||||
|
await table.visitEntityPage(page);
|
||||||
|
await page.getByTestId('profiler').click();
|
||||||
|
const testCaseResponse = page.waitForResponse(
|
||||||
|
'/api/v1/dataQuality/testCases?fields=*'
|
||||||
|
);
|
||||||
|
await page
|
||||||
|
.getByTestId('profiler-tab-left-panel')
|
||||||
|
.getByText('Data Quality')
|
||||||
|
.click();
|
||||||
|
await testCaseResponse;
|
||||||
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user