diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/DataQuality/AddTestCaseNewFlow.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/DataQuality/AddTestCaseNewFlow.spec.ts index 95560bc1433..88eae05c384 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/DataQuality/AddTestCaseNewFlow.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/DataQuality/AddTestCaseNewFlow.spec.ts @@ -12,25 +12,22 @@ */ import { expect, Page, Response } from '@playwright/test'; import { TableClass } from '../../../support/entity/TableClass'; -import { performAdminLogin } from '../../../utils/admin'; -import { toastNotification } from '../../../utils/common'; +import { getApiContext, redirectToHomePage } from '../../../utils/common'; import { visitDataQualityTab } from '../../../utils/testCases'; import { test } from '../../fixtures/pages'; test.describe('Add TestCase New Flow', () => { - const table1 = new TableClass(); - // Helper function to select table - const selectTable = async (page: Page, tableName: string) => { + const selectTable = async (page: Page, table: TableClass) => { await page.click('#testCaseFormV1_selectedTable'); const tableResponse = page.waitForResponse( '/api/v1/search/query?*index=table_search_index*' ); - await page.fill('#testCaseFormV1_selectedTable', tableName); + await page.fill('#testCaseFormV1_selectedTable', table.entity.name); await tableResponse; await page .locator( - `.ant-select-dropdown [title="${table1.entityResponseData.fullyQualifiedName}"]` + `.ant-select-dropdown [title="${table.entityResponseData.fullyQualifiedName}"]` ) .click(); }; @@ -103,8 +100,6 @@ test.describe('Add TestCase New Flow', () => { expect(response.status()).toBe(201); expect(ingestionPipelineCalled).toBe(false); } - - await toastNotification(page, 'Test case created successfully.'); }; // Helper function to open test case form @@ -118,22 +113,19 @@ test.describe('Add TestCase New Flow', () => { const visitDataQualityPage = async (page: Page) => { await page.goto('/data-quality/test-cases'); - await page.waitForLoadState('networkidle'); await page.waitForSelector('[data-testid="loader"]', { state: 'detached', }); }; - test.beforeAll(async ({ browser }) => { - const { apiContext, afterAction } = await performAdminLogin(browser); - - await table1.create(apiContext); - - await afterAction(); + test.beforeEach(async ({ page }) => { + await redirectToHomePage(page); }); - test('Add Table & Column Test Case', async ({ page }) => { - test.slow(true); + test('Add Table Test Case', async ({ page }) => { + const table = new TableClass(); + const { apiContext } = await getApiContext(page); + await table.create(apiContext); const testCaseDetails = { testType: 'table row count to equal', @@ -145,43 +137,7 @@ test.describe('Add TestCase New Flow', () => { await test.step('Create table-level test case', async () => { // Create table-level test case await openTestCaseForm(page); - await selectTable(page, table1.entity.name); - await createTestCase({ - page, - ...testCaseDetails, - }); - - await expect(page.getByTestId('entity-header-name')).toHaveText( - `${testCaseDetails.testTypeId}_test_case` - ); - }); - - await test.step('Create column-level test case', async () => { - const testCaseDetails = { - testType: 'Column Values To Be Unique', - testTypeId: 'columnValuesToBeUnique', - expectSchedulerCard: false, - }; - await visitDataQualityPage(page); - // Create column-level test case - await openTestCaseForm(page); - await page - .getByTestId('select-table-card') - .getByText('Column Level') - .click(); - await selectTable(page, table1.entity.name); - - await page.click('#testCaseFormV1_selectedColumn'); - await page.waitForLoadState('networkidle'); - await page.waitForSelector( - `.ant-select-dropdown [title="${table1.entity.columns[0].name}"]` - ); - await page - .locator( - `.ant-select-dropdown [title="${table1.entity.columns[0].name}"]` - ) - .click(); - + await selectTable(page, table); await createTestCase({ page, ...testCaseDetails, @@ -193,11 +149,8 @@ test.describe('Add TestCase New Flow', () => { }); await test.step('Validate test case in Entity Page', async () => { - await visitDataQualityTab(page, table1); + await visitDataQualityTab(page, table); - await expect( - page.getByTestId('columnValuesToBeUnique_test_case') - ).toBeVisible(); await expect( page.getByTestId('tableRowCountToEqual_test_case') ).toBeVisible(); @@ -212,7 +165,73 @@ test.describe('Add TestCase New Flow', () => { page .getByTestId('ingestion-list-table') .locator( - `[data-row-key*="${table1.entityResponseData.fullyQualifiedName}.testSuite"]` + `[data-row-key*="${table.entityResponseData.fullyQualifiedName}.testSuite"]` + ) + ).toHaveCount(1); + }); + }); + + test('Add Column Test Case', async ({ page }) => { + const table = new TableClass(); + const { apiContext } = await getApiContext(page); + await table.create(apiContext); + + await visitDataQualityPage(page); + + await test.step('Create column-level test case', async () => { + const testCaseDetails = { + testType: 'Column Values To Be Unique', + testTypeId: 'columnValuesToBeUnique', + }; + await visitDataQualityPage(page); + // Create column-level test case + await openTestCaseForm(page); + await page + .getByTestId('select-table-card') + .getByText('Column Level') + .click(); + await selectTable(page, table); + + await page.click('#testCaseFormV1_selectedColumn'); + // appearing dropdown takes bit time and its not based on API call so adding manual wait to prevent flakiness. + await page.waitForTimeout(2000); + await page.waitForSelector( + `.ant-select-dropdown [title="${table.entity.columns[0].name}"]` + ); + await page + .locator( + `.ant-select-dropdown [title="${table.entity.columns[0].name}"]` + ) + .click(); + + await createTestCase({ + page, + ...testCaseDetails, + }); + + await expect(page.getByTestId('entity-header-name')).toHaveText( + `${testCaseDetails.testTypeId}_test_case` + ); + }); + + await test.step('Validate test case in Entity Page', async () => { + await visitDataQualityTab(page, table); + + await expect( + page.getByTestId('columnValuesToBeUnique_test_case') + ).toBeVisible(); + + const pipelineApi = page.waitForResponse( + '/api/v1/services/ingestionPipelines?*' + ); + await page.getByTestId('pipeline').click(); + await pipelineApi; + + await expect( + page + .getByTestId('ingestion-list-table') + .locator( + `[data-row-key*="${table.entityResponseData.fullyQualifiedName}.testSuite"]` ) ).toHaveCount(1); }); @@ -221,28 +240,26 @@ test.describe('Add TestCase New Flow', () => { test('Non-owner user should not able to add test case', async ({ dataConsumerPage, dataStewardPage, + page, }) => { - await visitDataQualityPage(dataConsumerPage); - await visitDataQualityPage(dataStewardPage); + const table = new TableClass(); + const { apiContext } = await getApiContext(page); + await table.create(apiContext); - await dataConsumerPage.getByTestId('add-test-case-btn').click(); - await dataStewardPage.getByTestId('add-test-case-btn').click(); + for (const page of [dataConsumerPage, dataStewardPage]) { + await visitDataQualityPage(page); - await selectTable(dataConsumerPage, table1.entity.name); - await selectTable(dataStewardPage, table1.entity.name); + await page.getByTestId('add-test-case-btn').click(); - await dataConsumerPage.getByTestId('create-btn').click(); - await dataStewardPage.getByTestId('create-btn').click(); + await selectTable(page, table); - await expect( - dataConsumerPage.locator('#testCaseFormV1_selectedTable_help') - ).toContainText( - 'You do not have the necessary permissions to create a test case on this table.' - ); - await expect( - dataStewardPage.locator('#testCaseFormV1_selectedTable_help') - ).toContainText( - 'You do not have the necessary permissions to create a test case on this table.' - ); + await page.getByTestId('create-btn').click(); + + await expect( + page.locator('#testCaseFormV1_selectedTable_help') + ).toContainText( + 'You do not have the necessary permissions to create a test case on this table.' + ); + } }); }); diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/Tour.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/Tour.spec.ts index ff32ab93d14..b443055b017 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/Tour.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/Tour.spec.ts @@ -17,9 +17,37 @@ import { waitForAllLoadersToDisappear } from '../../utils/entity'; const user = new UserClass(); +const waitForTourBadgeWithRetry = async ( + page: Page, + maxAttempts = 3, + timeout = 10000 +) => { + for (let attempt = 1; attempt <= maxAttempts; attempt++) { + try { + await page.waitForSelector('[data-tour-elem="badge"]', { + state: 'visible', + timeout, + }); + + return; // Success + } catch (e) { + if (attempt < maxAttempts) { + await page.reload(); + await page.waitForLoadState('networkidle'); + await page.waitForSelector('[data-testid="loader"]', { + state: 'detached', + }); + await waitForAllLoadersToDisappear(page, 'entity-list-skeleton'); + } else { + throw e; + } + } + } +}; + const validateTourSteps = async (page: Page) => { await page.waitForTimeout(1000); - await page.waitForSelector(`[data-tour-elem="badge"]`); + await waitForTourBadgeWithRetry(page); await expect(page.locator(`[data-tour-elem="badge"]`)).toHaveText('1'); @@ -34,7 +62,9 @@ const validateTourSteps = async (page: Page) => { await expect(page.locator(`[data-tour-elem="badge"]`)).toHaveText('3'); await page.getByTestId('searchBox').fill('dim_a'); - await page.getByTestId('searchBox').press('Enter'); + await page.getByTestId('searchBox').press('Enter', { delay: 500 }); + + await page.waitForSelector('[data-testid="loader"]', { state: 'detached' }); await expect(page.locator(`[data-tour-elem="badge"]`)).toHaveText('4'); @@ -129,9 +159,12 @@ test.describe('Tour should work properly', () => { }); test('Tour should work from help section', async ({ page }) => { + test.slow(); + await page.locator('[data-testid="help-icon"]').click(); await page.getByRole('link', { name: 'Tour', exact: true }).click(); - await waitForAllLoadersToDisappear(page); + await page.waitForSelector('[data-testid="loader"]', { state: 'detached' }); + await waitForAllLoadersToDisappear(page, 'entity-list-skeleton'); await page.waitForURL('**/tour'); await page.waitForSelector('#feedWidgetData'); @@ -145,21 +178,34 @@ test.describe('Tour should work properly', () => { .locator('.whats-new-alert-close') .click(); await page.getByText('Take a product tour to get started!').click(); - await waitForAllLoadersToDisappear(page); + await page.waitForSelector('[data-testid="loader"]', { state: 'detached' }); + await waitForAllLoadersToDisappear(page, 'entity-list-skeleton'); await page.waitForURL('**/tour'); await page.waitForSelector('#feedWidgetData'); - - await validateTourSteps(page); + // Since the tour steps are already tested in the first test, + // here we only validate whether the tour is loading or not. + await waitForTourBadgeWithRetry(page); }); test('Tour should work from URL directly', async ({ page }) => { await page.goto('/tour'); - await waitForAllLoadersToDisappear(page); + await page.waitForSelector('[data-testid="loader"]', { state: 'detached' }); + const isWelcomeScreenVisible = await page + .getByTestId('welcome-screen') + .isVisible(); + + if (isWelcomeScreenVisible) { + await page.getByTestId('welcome-screen-close-btn').click(); + await page.waitForLoadState('networkidle'); + } + await page.waitForSelector('[data-testid="loader"]', { state: 'detached' }); + await waitForAllLoadersToDisappear(page, 'entity-list-skeleton'); await page.waitForURL('**/tour'); await page.waitForSelector('#feedWidgetData'); - - await validateTourSteps(page); + // Since the tour steps are already tested in the first test, + // here we only validate whether the tour is loading or not. + await waitForTourBadgeWithRetry(page); }); }); diff --git a/openmetadata-ui/src/main/resources/ui/playwright/utils/domain.ts b/openmetadata-ui/src/main/resources/ui/playwright/utils/domain.ts index 0e76bd36664..d4cc01c9138 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/utils/domain.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/utils/domain.ts @@ -35,7 +35,7 @@ import { redirectToHomePage, uuid, } from './common'; -import { addOwner } from './entity'; +import { addOwner, waitForAllLoadersToDisappear } from './entity'; import { sidebarClick } from './sidebar'; export const assignDomain = async (page: Page, domain: Domain['data']) => { @@ -200,6 +200,7 @@ const goToAssetsTab = async (page: Page, domain: Domain['data']) => { await selectDomain(page, domain); await checkDomainDisplayName(page, domain.displayName); await page.getByTestId('assets').click(); + await waitForAllLoadersToDisappear(page); }; const fillCommonFormItems = async ( @@ -270,11 +271,11 @@ export const verifyDomain = async ( ) => { await checkDomainDisplayName(page, domain.displayName); - const viewerContainerText = await page.textContent( - '[data-testid="viewer-container"]' - ); + await expect(page.getByText(domain.description)).toBeVisible(); - await expect(viewerContainerText).toContain(domain.description); + expect( + await page.locator(`[id="KnowledgePanel\\.Description"]`).textContent() + ).toContain(domain.description); if (!isEmpty(domain.owners) && !isUndefined(domain.owners)) { await expect( diff --git a/openmetadata-ui/src/main/resources/ui/playwright/utils/entity.ts b/openmetadata-ui/src/main/resources/ui/playwright/utils/entity.ts index b41b7965ca1..6a340ce56dc 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/utils/entity.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/utils/entity.ts @@ -38,9 +38,12 @@ import { import { searchAndClickOnOption } from './explore'; import { sidebarClick } from './sidebar'; -export const waitForAllLoadersToDisappear = async (page: Page) => { +export const waitForAllLoadersToDisappear = async ( + page: Page, + dataTestId = 'loader' +) => { for (let attempt = 0; attempt < 3; attempt++) { - const allLoaders = page.locator('[data-testid="loader"]'); + const allLoaders = page.locator(`[data-testid="${dataTestId}"]`); const count = await allLoaders.count(); let allLoadersGone = true; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/AssetsTabs.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/AssetsTabs.component.tsx index ff999c8f862..06c2bda27cd 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/AssetsTabs.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/AssetsTabs.component.tsx @@ -863,7 +863,11 @@ const AssetsTabs = forwardRef( )} {isLoading ? (