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:
Shailesh Parmar 2024-09-07 21:48:10 +05:30
parent 6a2eefbb46
commit 097b87e2db
13 changed files with 1453 additions and 1331 deletions

2
.gitignore vendored
View File

@ -87,7 +87,7 @@ openmetadata-ui/src/main/resources/ui/cypress/fixtures
# Playwright artifacts
openmetadata-ui/src/main/resources/ui/playwright/output/
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
#UI - Dereferenced Schemas

View File

@ -263,6 +263,8 @@ overrides:
- off
jest/no-standalone-expect:
- off
jest/no-conditional-expect:
- off
# i18next rule is not required for js, jsx, json and test file
- files:

View File

@ -42,6 +42,8 @@ export const POSTGRES = {
tableName: 'order_items',
};
export const MYSQL = 'Mysql';
export const HTTP_CONFIG_SOURCE = {
DBT_CATALOG_HTTP_PATH:
'https://raw.githubusercontent.com/OnkarVO7/dbt_git_test/dbt_aut/catalog.json',

View File

@ -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();
}
});

View File

@ -12,7 +12,7 @@
*/
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 AirflowIngestionClass from '../../support/entity/ingestion/AirflowIngestionClass';
import BigQueryIngestionClass from '../../support/entity/ingestion/BigQueryIngestionClass';
@ -83,7 +83,9 @@ services.forEach((ServiceClass) => {
});
if (
[POSTGRES.serviceType, REDSHIFT.serviceType].includes(service.serviceType)
[POSTGRES.serviceType, REDSHIFT.serviceType, MYSQL].includes(
service.serviceType
)
) {
test(`Service specific tests`, async ({ page }) => {
await service.runAdditionalTests(page, test);

View File

@ -12,8 +12,12 @@
*/
import { expect, test } from '@playwright/test';
import { TableClass } from '../../support/entity/TableClass';
import { getApiContext, redirectToHomePage } from '../../utils/common';
import { deleteTestCase } from '../../utils/testCases';
import {
descriptionBox,
getApiContext,
redirectToHomePage,
} from '../../utils/common';
import { deleteTestCase, visitDataQualityTab } from '../../utils/testCases';
// use the admin user to login
test.use({ storageState: 'playwright/.auth/admin.json' });
@ -231,3 +235,104 @@ test('Custom SQL Query', async ({ page }) => {
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();
}
});

View File

@ -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!`
);
});
});

View File

@ -38,6 +38,7 @@ export enum EntityTypeEndpoint {
API_COLLECTION = 'apiCollections',
API_ENDPOINT = 'apiEndpoints',
DATA_PRODUCT = 'dataProducts',
TestSuites = 'dataQuality/testSuites',
}
export type EntityDataType = {
@ -67,6 +68,15 @@ export enum ENTITY_PATH {
}
export type TestCaseData = {
parameterValues: unknown[];
testDefinition: string;
parameterValues?: unknown[];
name?: string;
entityLink?: string;
testDefinition?: string;
testSuite?: string;
};
export type TestSuiteData = {
name?: string;
executableEntityReference?: string;
description?: string;
};

View File

@ -11,10 +11,15 @@
* limitations under the License.
*/
import { APIRequestContext, Page } from '@playwright/test';
import { Operation } from 'fast-json-patch';
import { SERVICE_TYPE } from '../../constant/service';
import { uuid } from '../../utils/common';
import { visitEntityPage } from '../../utils/entity';
import { EntityTypeEndpoint, TestCaseData } from './Entity.interface';
import {
EntityTypeEndpoint,
TestCaseData,
TestSuiteData,
} from './Entity.interface';
import { EntityClass } from './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) {
await this.create(apiContext);
}
@ -179,6 +187,7 @@ export class TableClass extends EntityClass {
executableEntityReference:
this.entityResponseData?.['fullyQualifiedName'],
description: 'Playwright test suite for table',
...testSuite,
},
})
.then((res) => res.json());
@ -240,13 +249,13 @@ export class TableClass extends EntityClass {
data: {
name: `pw-test-case-${uuid()}`,
entityLink: `<#E::table::${this.entityResponseData?.['fullyQualifiedName']}>`,
testDefinition:
testCaseData?.testDefinition ?? 'tableRowCountToBeBetween',
testDefinition: 'tableRowCountToBeBetween',
testSuite: this.testSuiteResponseData?.['fullyQualifiedName'],
parameterValues: testCaseData?.parameterValues ?? [
parameterValues: [
{ name: 'minValue', value: 12 },
{ name: 'maxValue', value: 34 },
],
...testCaseData,
},
})
.then((res) => res.json());
@ -256,6 +265,43 @@ export class TableClass extends EntityClass {
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) {
const serviceResponse = await apiContext.delete(
`/api/v1/services/databaseServices/name/${encodeURIComponent(

View File

@ -10,9 +10,22 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { expect, Page } from '@playwright/test';
import {
expect,
Page,
PlaywrightTestArgs,
PlaywrightWorkerArgs,
TestType,
} from '@playwright/test';
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 {
checkServiceFieldSectionHighlighting,
Services,
@ -22,6 +35,7 @@ import ServiceBaseClass from './ServiceBaseClass';
class MysqlIngestionClass extends ServiceBaseClass {
name: string;
tableFilter: string[];
profilerTable = 'alert_entity';
constructor() {
super(
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) {
await page.waitForSelector('.ant-select-selection-item-content');

View File

@ -176,10 +176,10 @@ class ServiceBaseClass {
.getByLabel('Ingestions')
.getByTestId('loader')
.waitFor({ state: 'detached' });
// need manual wait to settle down the deployed pipeline, before triggering the pipeline
await page.waitForTimeout(2000);
await page.getByTestId('more-actions').first().click();
await page.getByTestId('run-button').click();

View File

@ -11,6 +11,7 @@
* limitations under the License.
*/
import { expect, Page } from '@playwright/test';
import { TableClass } from '../support/entity/TableClass';
export const deleteTestCase = async (page: Page, testCaseName: string) => {
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();
const deleteResponse = page.waitForResponse(
'/api/v1/dataQuality/testCases/*?hardDelete=true&recursive=true'
);
await page.getByTestId('confirm-button').click();
await deleteResponse;
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;
};