Refactor: Update DataQualityAndProfiler tests and ColumnProfileTable logic (#22364)

* Refactor: Update DataQualityAndProfiler tests and ColumnProfileTable logic

- Replaced `createNewPage` with `performAdminLogin` for improved login handling in DataQualityAndProfiler tests.
- Enhanced test case for profiler matrix visibility to include checks for admin, data consumer, and data steward roles.
- Updated ColumnProfileTable to use `fqn` consistently instead of `tableProfiler?.fullyQualifiedName` for fetching table columns.

This refactor aims to streamline the test setup and improve code clarity.

* Enhance ColumnProfileTable to utilize getTableFQNFromColumnFQN for improved FQN handling

- Updated ColumnProfileTable to derive table FQN from column FQN using the new utility function.
- Refactored related logic to ensure consistent usage of table FQN across navigation and data fetching.
- Added unit tests for getTableFQNFromColumnFQN to validate its functionality.

This change aims to streamline the handling of fully qualified names in the profiler component.
This commit is contained in:
Shailesh Parmar 2025-07-15 19:32:28 +05:30 committed by GitHub
parent 05fa0b3185
commit 71ad8766d9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 125 additions and 73 deletions

View File

@ -10,7 +10,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { expect, Page, test } from '@playwright/test';
import { expect, Page } from '@playwright/test';
import { PLAYWRIGHT_INGESTION_TAG_OBJ } from '../../constant/config';
import { SidebarItem } from '../../constant/sidebar';
import { Domain } from '../../support/domain/Domain';
@ -19,10 +19,10 @@ import { Glossary } from '../../support/glossary/Glossary';
import { GlossaryTerm } from '../../support/glossary/GlossaryTerm';
import { ClassificationClass } from '../../support/tag/ClassificationClass';
import { TagClass } from '../../support/tag/TagClass';
import { performAdminLogin } from '../../utils/admin';
import {
assignDomain,
clickOutside,
createNewPage,
descriptionBox,
getApiContext,
redirectToHomePage,
@ -33,9 +33,7 @@ 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' });
import { test } from '../fixtures/pages';
const table1 = new TableClass();
const table2 = new TableClass();
@ -53,7 +51,7 @@ const testGlossaryTerm1 = new GlossaryTerm(testGlossary);
const testGlossaryTerm2 = new GlossaryTerm(testGlossary);
test.beforeAll(async ({ browser }) => {
const { apiContext, afterAction } = await createNewPage(browser);
const { apiContext, afterAction } = await performAdminLogin(browser);
await table1.create(apiContext);
await table2.create(apiContext);
await table2.createTestCase(apiContext, {
@ -77,7 +75,7 @@ test.beforeAll(async ({ browser }) => {
});
test.afterAll(async ({ browser }) => {
const { apiContext, afterAction } = await createNewPage(browser);
const { apiContext, afterAction } = await performAdminLogin(browser);
await table1.delete(apiContext);
await table2.delete(apiContext);
@ -408,74 +406,106 @@ test('Column test case', PLAYWRIGHT_INGESTION_TAG_OBJ, async ({ page }) => {
});
test(
'Profiler matrix and test case graph should visible',
'Profiler matrix and test case graph should visible for admin, data consumer and data steward',
PLAYWRIGHT_INGESTION_TAG_OBJ,
async ({ page }) => {
async ({ page: adminPage, dataConsumerPage, dataStewardPage }) => {
test.slow();
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"]`);
const runProfilerTest = async (page: Page) => {
await redirectToHomePage(page);
await visitEntityPage({
page,
searchTerm: DATA_QUALITY_TABLE.term,
dataTestId: `${DATA_QUALITY_TABLE.serviceName}-${DATA_QUALITY_TABLE.term}`,
});
await expect(
page.locator(`[data-testid="entity-header-name"]`)
).toContainText(DATA_QUALITY_TABLE.term);
await page.waitForSelector(`[data-testid="entity-header-name"]`);
const profilerResponse = page.waitForResponse(
`/api/v1/tables/*/tableProfile/latest?includeColumnProfile=false`
);
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(`[data-testid="entity-header-name"]`)
).toContainText(DATA_QUALITY_TABLE.term);
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();
const profilerApiCall = page.waitForResponse(
`/api/v1/tables/*/tableProfile/latest?includeColumnProfile=false`
);
await page.click('[data-testid="profiler"]');
const profilerResponse = await profilerApiCall;
await page
.getByRole('menuitem', {
name: 'Data Quality',
})
.click();
expect(profilerResponse.status()).toBe(200);
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();
const listColumnApiCall = page.waitForResponse(
'/api/v1/tables/name/*/columns?*'
);
await page
.getByRole('menuitem', {
name: 'Column Profile',
})
.click();
await listColumnApiCall;
const listColumnResponse = await listColumnApiCall;
await getTestCaseDetails;
await getTestResult;
expect(listColumnResponse.status()).toBe(200);
await expect(
page.locator(`#${DATA_QUALITY_TABLE.testCaseName}_graph`)
).toBeVisible();
const getProfilerInfo = page.waitForResponse(
'/api/v1/tables/*/columnProfile?*'
);
await page
.locator('[data-row-key="shop_id"]')
.getByText('shop_id')
.click();
await getProfilerInfo;
const getProfilerInfoResponse = await getProfilerInfo;
expect(getProfilerInfoResponse.status()).toBe(200);
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();
const getTestCaseDetailsResponse = await getTestCaseDetails;
const getTestResultResponse = await getTestResult;
expect(getTestCaseDetailsResponse.status()).toBe(200);
expect(getTestResultResponse.status()).toBe(200);
await expect(
page.locator(`#${DATA_QUALITY_TABLE.testCaseName}_graph`)
).toBeVisible();
};
// Run all three user roles in parallel
await Promise.all([
runProfilerTest(adminPage),
runProfilerTest(dataConsumerPage),
runProfilerTest(dataStewardPage),
]);
}
);

View File

@ -49,6 +49,7 @@ jest.mock('../../../../common/SummaryCard/SummaryCard.component', () => ({
jest.mock('../../../../../utils/CommonUtils', () => ({
formatNumberWithComma: jest.fn(),
getTableFQNFromColumnFQN: jest.fn().mockImplementation((fqn) => fqn),
}));
jest.mock('../../../../common/SearchBarComponent/SearchBar.component', () => {
return jest

View File

@ -56,7 +56,10 @@ import {
searchTableColumnsByFQN,
} from '../../../../../rest/tableAPI';
import { getListTestCaseBySearch } from '../../../../../rest/testAPI';
import { formatNumberWithComma } from '../../../../../utils/CommonUtils';
import {
formatNumberWithComma,
getTableFQNFromColumnFQN,
} from '../../../../../utils/CommonUtils';
import { getEntityName } from '../../../../../utils/EntityUtils';
import { getEntityColumnFQN } from '../../../../../utils/FeedUtils';
import { getPrioritizedEditPermission } from '../../../../../utils/PermissionsUtils';
@ -92,6 +95,7 @@ const ColumnProfileTable = () => {
const { t } = useTranslation();
const navigate = useNavigate();
const { fqn } = useFqn();
const tableFqn = useMemo(() => getTableFQNFromColumnFQN(fqn), [fqn]);
const {
isTestsLoading,
isProfilerDataLoading,
@ -316,7 +320,7 @@ const ColumnProfileTable = () => {
navigate({
pathname: getAddDataQualityTableTestPath(
ProfilerDashboardType.COLUMN,
fqn
tableFqn
),
search: activeColumnFqn ? Qs.stringify({ activeColumnFqn }) : '',
});
@ -327,7 +331,10 @@ const ColumnProfileTable = () => {
key: 'custom-metric',
onClick: () => {
navigate({
pathname: getAddCustomMetricPath(ProfilerDashboardType.COLUMN, fqn),
pathname: getAddCustomMetricPath(
ProfilerDashboardType.COLUMN,
tableFqn
),
search: activeColumnFqn ? Qs.stringify({ activeColumnFqn }) : '',
});
},
@ -405,9 +412,7 @@ const ColumnProfileTable = () => {
const fetchTableColumnWithProfiler = useCallback(
async (page: number, searchText: string) => {
const tableFQN = tableProfiler?.fullyQualifiedName;
if (!tableFQN) {
if (!tableFqn) {
return;
}
@ -416,13 +421,13 @@ const ColumnProfileTable = () => {
const offset = (page - 1) * pageSize;
// Use search API if there's a search query, otherwise use regular pagination
const response = searchText
? await searchTableColumnsByFQN(tableFQN, {
? await searchTableColumnsByFQN(tableFqn, {
q: searchText,
limit: pageSize,
offset: offset,
fields: TabSpecificField.PROFILE,
})
: await getTableColumnsByFQN(tableFQN, {
: await getTableColumnsByFQN(tableFqn, {
limit: pageSize,
offset: offset,
fields: TabSpecificField.PROFILE,
@ -440,7 +445,7 @@ const ColumnProfileTable = () => {
setIsColumnsLoading(false);
}
},
[tableProfiler?.fullyQualifiedName, pageSize, searchText]
[tableFqn, pageSize, searchText]
);
const handleColumnProfilePageChange = useCallback(
@ -451,10 +456,10 @@ const ColumnProfileTable = () => {
);
useEffect(() => {
if (tableProfiler?.fullyQualifiedName) {
if (tableFqn) {
fetchTableColumnWithProfiler(currentPage, searchText);
}
}, [tableProfiler?.fullyQualifiedName, currentPage, searchText, pageSize]);
}, [tableFqn, currentPage, searchText, pageSize]);
useEffect(() => {
if (activeColumnFqn) {

View File

@ -11,7 +11,7 @@
* limitations under the License.
*/
import { filterSelectOptions } from './CommonUtils';
import { filterSelectOptions, getTableFQNFromColumnFQN } from './CommonUtils';
describe('Tests for CommonUtils', () => {
describe('filterSelectOptions', () => {
@ -64,4 +64,20 @@ describe('Tests for CommonUtils', () => {
expect(filterSelectOptions(input, option)).toBe(true);
});
});
describe('getTableFQNFromColumnFQN', () => {
it('should return the table FQN from a column FQN', () => {
const columnFQN = 'service.database.schema.table.column';
const tableFQN = getTableFQNFromColumnFQN(columnFQN);
expect(tableFQN).toBe('service.database.schema.table');
});
it('should return the table FQN as it is if table FQN is provided', () => {
const tableFQN = 'service.database.schema.table';
const result = getTableFQNFromColumnFQN(tableFQN);
expect(result).toBe(tableFQN);
});
});
});