diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/DataQualityAndProfiler.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/DataQualityAndProfiler.spec.ts index 3283c297c9d..095530cf7e2 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/DataQualityAndProfiler.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/DataQualityAndProfiler.spec.ts @@ -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), + ]); } ); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Database/Profiler/TableProfiler/ColumnProfileTable/ColumnProfileTable.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Database/Profiler/TableProfiler/ColumnProfileTable/ColumnProfileTable.test.tsx index 7adcf520723..ae669022cb8 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Database/Profiler/TableProfiler/ColumnProfileTable/ColumnProfileTable.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Database/Profiler/TableProfiler/ColumnProfileTable/ColumnProfileTable.test.tsx @@ -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 diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Database/Profiler/TableProfiler/ColumnProfileTable/ColumnProfileTable.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Database/Profiler/TableProfiler/ColumnProfileTable/ColumnProfileTable.tsx index 07b5aa28f20..c1cb848c973 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Database/Profiler/TableProfiler/ColumnProfileTable/ColumnProfileTable.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Database/Profiler/TableProfiler/ColumnProfileTable/ColumnProfileTable.tsx @@ -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) { diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/CommonUtils.test.ts b/openmetadata-ui/src/main/resources/ui/src/utils/CommonUtils.test.ts index 6649d1bad0a..27e6375e86a 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/CommonUtils.test.ts +++ b/openmetadata-ui/src/main/resources/ui/src/utils/CommonUtils.test.ts @@ -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); + }); + }); });