Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

1040 lines
35 KiB
TypeScript
Raw Normal View History

/*
* 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 { PLAYWRIGHT_INGESTION_TAG_OBJ } from '../../constant/config';
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 { getCurrentMillis } from '../../utils/dateTime';
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::${table2.entity?.columns[3].name}>`,
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', PLAYWRIGHT_INGESTION_TAG_OBJ, 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
);
feat(#15380): replace description editor with block editor (#19003) * feat(#15380): replace the description editor with a new block editor. * chore: Add placeholder support to BlockEditor * feat: Add autofocus support to BlockEditor * chore: implement block editor in richtext editor * chore: Remove unused CSS import in RichTextEditor * fix: ensure safe access to getEditorContent in Markdown editor components * minor change * fix: add support for ttf file extension in pom.xml * fix: adjust block editor styles for better usability and overflow handling * fix: update RichTextEditorPreviewer to RichTextEditorPreviewerV1 and adjust block editor styles * fix: update description box selector to use om-block-editor for consistency * fix: disable autoFocus on BlockEditor in RichTextEditorPreviewerV1 * refactor: update RichTextEditorPreviewer references to RichTextEditorPreviewerV1 in tests * test: update timer handling in ApplicationCard and SuggestionsAlert tests * fix(diff): update diff view rendering and attributes for consistency * fix(RichTextEditor): add placeholder for empty markdown descriptions * Add data-diff in <span> * Fix test * fix: update description box selector to use locator method for better reliability * feat: integrate RichTextEditorPreviewerV1 for description rendering in Notification and Observability Alerts pages * feat: add markdown conversion for mentions and hashtags in BlockEditorUtils * fix: update initial value handling in TagsForm and formUtils * MINOR - Update description handling in Playwright tests and utilities * Refactor Playwright tests to use descriptionBox for tag and team descriptions * Refactor custom property handling and update description check logic * Enhance entity version page tests by adding description box read-only check and updating data-testid attributes * Refactor description handling and improve diff rendering logic in task pages * Fix user description clearing logic in UserDetails.spec.ts * Enhance Rich Text Editor by adding custom styles and converting markdown to HTML for backward compatibility * Remove test for rendering alert description when not present in AlertDetailsPage * Clean up RichTextEditorPreviewerV1 by removing unnecessary comments and improving readability * Update SearchIndexApplication.spec.ts to select 'Table' instead of 'Topic' in the tree widget * Refactor BlockEditor and FeedUtils to improve code organization and readability * Fix regex in getTextFromHtmlString to correctly remove HTML tags * Add tests for getTextFromHtmlString and improve HTML tag removal regex --------- Co-authored-by: mohitdeuex <mohit.y@deuexsolutions.com>
2024-12-27 20:57:37 +05:30
await page.locator(descriptionBox).fill(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/*'
);
await page.click('[data-testid="deploy-button"]');
await ingestionPipelines;
await deploy;
// 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/search/list?*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', PLAYWRIGHT_INGESTION_TAG_OBJ, async ({ page }) => {
test.slow();
const NEW_COLUMN_TEST_CASE = {
name: 'email_column_value_lengths_to_be_between',
column: table1.entity?.columns[3].name,
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(`[data-testid="${NEW_COLUMN_TEST_CASE.type}"]`);
await page.fill(
'#tableTestForm_params_minLength',
NEW_COLUMN_TEST_CASE.min
);
await page.fill(
'#tableTestForm_params_maxLength',
NEW_COLUMN_TEST_CASE.max
);
feat(#15380): replace description editor with block editor (#19003) * feat(#15380): replace the description editor with a new block editor. * chore: Add placeholder support to BlockEditor * feat: Add autofocus support to BlockEditor * chore: implement block editor in richtext editor * chore: Remove unused CSS import in RichTextEditor * fix: ensure safe access to getEditorContent in Markdown editor components * minor change * fix: add support for ttf file extension in pom.xml * fix: adjust block editor styles for better usability and overflow handling * fix: update RichTextEditorPreviewer to RichTextEditorPreviewerV1 and adjust block editor styles * fix: update description box selector to use om-block-editor for consistency * fix: disable autoFocus on BlockEditor in RichTextEditorPreviewerV1 * refactor: update RichTextEditorPreviewer references to RichTextEditorPreviewerV1 in tests * test: update timer handling in ApplicationCard and SuggestionsAlert tests * fix(diff): update diff view rendering and attributes for consistency * fix(RichTextEditor): add placeholder for empty markdown descriptions * Add data-diff in <span> * Fix test * fix: update description box selector to use locator method for better reliability * feat: integrate RichTextEditorPreviewerV1 for description rendering in Notification and Observability Alerts pages * feat: add markdown conversion for mentions and hashtags in BlockEditorUtils * fix: update initial value handling in TagsForm and formUtils * MINOR - Update description handling in Playwright tests and utilities * Refactor Playwright tests to use descriptionBox for tag and team descriptions * Refactor custom property handling and update description check logic * Enhance entity version page tests by adding description box read-only check and updating data-testid attributes * Refactor description handling and improve diff rendering logic in task pages * Fix user description clearing logic in UserDetails.spec.ts * Enhance Rich Text Editor by adding custom styles and converting markdown to HTML for backward compatibility * Remove test for rendering alert description when not present in AlertDetailsPage * Clean up RichTextEditorPreviewerV1 by removing unnecessary comments and improving readability * Update SearchIndexApplication.spec.ts to select 'Table' instead of 'Topic' in the tree widget * Refactor BlockEditor and FeedUtils to improve code organization and readability * Fix regex in getTextFromHtmlString to correctly remove HTML tags * Add tests for getTextFromHtmlString and improve HTML tag removal regex --------- Co-authored-by: mohitdeuex <mohit.y@deuexsolutions.com>
2024-12-27 20:57:37 +05:30
await page.locator(descriptionBox).fill(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/search/list?*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',
PLAYWRIGHT_INGESTION_TAG_OBJ,
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-name"]`);
await expect(
page.locator(`[data-testid="entity-header-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/testCaseResults/*?*'
);
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',
PLAYWRIGHT_INGESTION_TAG_OBJ,
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(
table2.entity?.columns[3].name
);
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}"]`);
feat(#15380): replace description editor with block editor (#19003) * feat(#15380): replace the description editor with a new block editor. * chore: Add placeholder support to BlockEditor * feat: Add autofocus support to BlockEditor * chore: implement block editor in richtext editor * chore: Remove unused CSS import in RichTextEditor * fix: ensure safe access to getEditorContent in Markdown editor components * minor change * fix: add support for ttf file extension in pom.xml * fix: adjust block editor styles for better usability and overflow handling * fix: update RichTextEditorPreviewer to RichTextEditorPreviewerV1 and adjust block editor styles * fix: update description box selector to use om-block-editor for consistency * fix: disable autoFocus on BlockEditor in RichTextEditorPreviewerV1 * refactor: update RichTextEditorPreviewer references to RichTextEditorPreviewerV1 in tests * test: update timer handling in ApplicationCard and SuggestionsAlert tests * fix(diff): update diff view rendering and attributes for consistency * fix(RichTextEditor): add placeholder for empty markdown descriptions * Add data-diff in <span> * Fix test * fix: update description box selector to use locator method for better reliability * feat: integrate RichTextEditorPreviewerV1 for description rendering in Notification and Observability Alerts pages * feat: add markdown conversion for mentions and hashtags in BlockEditorUtils * fix: update initial value handling in TagsForm and formUtils * MINOR - Update description handling in Playwright tests and utilities * Refactor Playwright tests to use descriptionBox for tag and team descriptions * Refactor custom property handling and update description check logic * Enhance entity version page tests by adding description box read-only check and updating data-testid attributes * Refactor description handling and improve diff rendering logic in task pages * Fix user description clearing logic in UserDetails.spec.ts * Enhance Rich Text Editor by adding custom styles and converting markdown to HTML for backward compatibility * Remove test for rendering alert description when not present in AlertDetailsPage * Clean up RichTextEditorPreviewerV1 by removing unnecessary comments and improving readability * Update SearchIndexApplication.spec.ts to select 'Table' instead of 'Topic' in the tree widget * Refactor BlockEditor and FeedUtils to improve code organization and readability * Fix regex in getTextFromHtmlString to correctly remove HTML tags * Add tests for getTextFromHtmlString and improve HTML tag removal regex --------- Co-authored-by: mohitdeuex <mohit.y@deuexsolutions.com>
2024-12-27 20:57:37 +05:30
await page.locator(descriptionBox).fill('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([
feat(#15380): replace description editor with block editor (#19003) * feat(#15380): replace the description editor with a new block editor. * chore: Add placeholder support to BlockEditor * feat: Add autofocus support to BlockEditor * chore: implement block editor in richtext editor * chore: Remove unused CSS import in RichTextEditor * fix: ensure safe access to getEditorContent in Markdown editor components * minor change * fix: add support for ttf file extension in pom.xml * fix: adjust block editor styles for better usability and overflow handling * fix: update RichTextEditorPreviewer to RichTextEditorPreviewerV1 and adjust block editor styles * fix: update description box selector to use om-block-editor for consistency * fix: disable autoFocus on BlockEditor in RichTextEditorPreviewerV1 * refactor: update RichTextEditorPreviewer references to RichTextEditorPreviewerV1 in tests * test: update timer handling in ApplicationCard and SuggestionsAlert tests * fix(diff): update diff view rendering and attributes for consistency * fix(RichTextEditor): add placeholder for empty markdown descriptions * Add data-diff in <span> * Fix test * fix: update description box selector to use locator method for better reliability * feat: integrate RichTextEditorPreviewerV1 for description rendering in Notification and Observability Alerts pages * feat: add markdown conversion for mentions and hashtags in BlockEditorUtils * fix: update initial value handling in TagsForm and formUtils * MINOR - Update description handling in Playwright tests and utilities * Refactor Playwright tests to use descriptionBox for tag and team descriptions * Refactor custom property handling and update description check logic * Enhance entity version page tests by adding description box read-only check and updating data-testid attributes * Refactor description handling and improve diff rendering logic in task pages * Fix user description clearing logic in UserDetails.spec.ts * Enhance Rich Text Editor by adding custom styles and converting markdown to HTML for backward compatibility * Remove test for rendering alert description when not present in AlertDetailsPage * Clean up RichTextEditorPreviewerV1 by removing unnecessary comments and improving readability * Update SearchIndexApplication.spec.ts to select 'Table' instead of 'Topic' in the tree widget * Refactor BlockEditor and FeedUtils to improve code organization and readability * Fix regex in getTextFromHtmlString to correctly remove HTML tags * Add tests for getTextFromHtmlString and improve HTML tag removal regex --------- Co-authored-by: mohitdeuex <mohit.y@deuexsolutions.com>
2024-12-27 20:57:37 +05:30
{
op: 'add',
path: '/description',
value: '<p>Test case description</p>',
},
])
);
// 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',
PLAYWRIGHT_INGESTION_TAG_OBJ,
async ({ page }) => {
const profilerSetting = {
profileSample: '60',
sampleDataCount: '100',
profileQuery: 'select * from table',
excludeColumns: table1.entity?.columns[0].name,
includeColumns: table1.entity?.columns[1].name,
partitionColumnName: table1.entity?.columns[2].name,
partitionIntervalType: 'COLUMN-VALUE',
partitionValues: 'test',
};
await table1.visitEntityPage(page);
await page.getByTestId('profiler').click();
await page
.getByTestId('profiler-tab-left-panel')
.getByText('Data Quality')
.click();
await page.reload();
await page.waitForLoadState('networkidle');
await test.step('Update profiler setting', async () => {
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: [table1.entity?.columns[0].name],
profileQuery: 'select * from table',
profileSample: 60,
profileSampleType: 'PERCENTAGE',
includeColumns: [{ columnName: table1.entity?.columns[1].name }],
partitioning: {
partitionColumnName: table1.entity?.columns[2].name,
partitionIntervalType: 'COLUMN-VALUE',
partitionValues: ['test'],
enablePartitioning: true,
},
sampleDataCount: 100,
})
);
});
await test.step('Reset profile sample type', async () => {
await page.click('[data-testid="profiler-setting-btn"]');
await page.waitForSelector('.ant-modal-body');
await expect(
page.locator('[data-testid="profile-sample"]')
).toBeVisible();
await page.getByTestId('clear-slider-input').click();
await expect(page.locator('[data-testid="slider-input"]')).toBeEmpty();
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: [table1.entity?.columns[0].name],
profileQuery: 'select * from table',
profileSample: null,
profileSampleType: 'PERCENTAGE',
includeColumns: [{ columnName: table1.entity?.columns[1].name }],
partitioning: {
partitionColumnName: table1.entity?.columns[2].name,
partitionIntervalType: 'COLUMN-VALUE',
partitionValues: ['test'],
enablePartitioning: true,
},
sampleDataCount: 100,
})
);
await page.waitForSelector('.ant-modal-body', {
state: 'detached',
});
// Validate the profiler setting is updated
await page.click('[data-testid="profiler-setting-btn"]');
await page.waitForSelector('.ant-modal-body');
await expect(
page.locator('[data-testid="profile-sample"]')
).toBeVisible();
await expect(page.locator('[data-testid="slider-input"]')).toBeEmpty();
await expect(
page.getByTestId('profile-sample').locator('div')
).toBeVisible();
});
}
);
test('TestCase filters', PLAYWRIGHT_INGESTION_TAG_OBJ, async ({ page }) => {
test.setTimeout(360000);
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, {
basicEntityReference: 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');
await page.fill('#tier', 'Tier2');
await page.waitForLoadState('domcontentloaded');
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.getByTestId('domain-dropdown').click();
await page
.getByTestId(`tag-${domain.responseData.fullyQualifiedName}`)
.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);
await visitDataQualityTab(page, filterTable1);
const searchTestCase = page.waitForResponse(
(url) =>
url.url().includes('/api/v1/dataQuality/testCases/search/list') &&
url.url().includes(testCases[0])
);
await page
.getByTestId('table-profiler-container')
.getByTestId('searchbar')
.fill(testCases[0]);
await searchTestCase;
await expect(page.locator(`[data-testid="${testCases[0]}"]`)).toBeVisible();
await expect(
page.locator(`[data-testid="${testCases[1]}"]`)
).not.toBeVisible();
await expect(
page.locator(`[data-testid="${testCases[2]}"]`)
).not.toBeVisible();
} finally {
await filterTable1.delete(apiContext);
await domain.delete(apiContext);
await afterAction();
}
});