mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-11-14 01:40:08 +00:00
chore(ui): refactor data contract spec test (#23741)
* chore(ui): fix data contract flaky test around contract tab not found * modify the test to have their own table to run to avoid flakiness * fix contract failing * revert unwanted commit * change the contract test admin page way * fix the blank page issue after redirect
This commit is contained in:
parent
8ccd879655
commit
9286933d5c
@ -36,6 +36,7 @@ import { selectOption } from '../../utils/advancedSearch';
|
|||||||
import { resetTokenFromBotPage } from '../../utils/bot';
|
import { resetTokenFromBotPage } from '../../utils/bot';
|
||||||
import {
|
import {
|
||||||
clickOutside,
|
clickOutside,
|
||||||
|
getApiContext,
|
||||||
redirectToHomePage,
|
redirectToHomePage,
|
||||||
toastNotification,
|
toastNotification,
|
||||||
} from '../../utils/common';
|
} from '../../utils/common';
|
||||||
@ -54,10 +55,10 @@ import {
|
|||||||
assignTier,
|
assignTier,
|
||||||
} from '../../utils/entity';
|
} from '../../utils/entity';
|
||||||
import { settingClick } from '../../utils/sidebar';
|
import { settingClick } from '../../utils/sidebar';
|
||||||
|
import { test } from '../fixtures/pages';
|
||||||
|
|
||||||
const adminUser = new UserClass();
|
const adminUser = new UserClass();
|
||||||
|
const testPersona = base.extend<{ page: Page }>({
|
||||||
const test = base.extend<{ page: Page }>({
|
|
||||||
page: async ({ browser }, use) => {
|
page: async ({ browser }, use) => {
|
||||||
const adminPage = await browser.newPage();
|
const adminPage = await browser.newPage();
|
||||||
await adminUser.login(adminPage);
|
await adminUser.login(adminPage);
|
||||||
@ -67,57 +68,8 @@ const test = base.extend<{ page: Page }>({
|
|||||||
});
|
});
|
||||||
|
|
||||||
test.describe('Data Contracts', () => {
|
test.describe('Data Contracts', () => {
|
||||||
const table = new TableClass();
|
|
||||||
const table2 = new TableClass();
|
|
||||||
const testClassification = new ClassificationClass();
|
|
||||||
const testTag = new TagClass({
|
|
||||||
classification: testClassification.data.name,
|
|
||||||
});
|
|
||||||
const testGlossary = new Glossary();
|
|
||||||
const testGlossaryTerm = new GlossaryTerm(testGlossary);
|
|
||||||
const testPersona = new PersonaClass();
|
|
||||||
|
|
||||||
test.beforeAll('Setup pre-requests', async ({ browser }) => {
|
test.beforeAll('Setup pre-requests', async ({ browser }) => {
|
||||||
test.slow(true);
|
const { afterAction, page } = await performAdminLogin(browser);
|
||||||
|
|
||||||
const { apiContext, afterAction, page } = await performAdminLogin(browser);
|
|
||||||
await table.create(apiContext);
|
|
||||||
await table2.create(apiContext);
|
|
||||||
await testClassification.create(apiContext);
|
|
||||||
await testTag.create(apiContext);
|
|
||||||
await testGlossary.create(apiContext);
|
|
||||||
await testGlossaryTerm.create(apiContext);
|
|
||||||
await testPersona.create(apiContext);
|
|
||||||
await adminUser.create(apiContext);
|
|
||||||
await adminUser.setAdminRole(apiContext);
|
|
||||||
await adminUser.patch({
|
|
||||||
apiContext,
|
|
||||||
patchData: [
|
|
||||||
{
|
|
||||||
op: 'add',
|
|
||||||
path: '/personas/0',
|
|
||||||
value: {
|
|
||||||
id: testPersona.responseData.id,
|
|
||||||
name: testPersona.responseData.name,
|
|
||||||
displayName: testPersona.responseData.displayName,
|
|
||||||
fullyQualifiedName: testPersona.responseData.fullyQualifiedName,
|
|
||||||
type: 'persona',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
op: 'add',
|
|
||||||
path: '/defaultPersona',
|
|
||||||
value: {
|
|
||||||
id: testPersona.responseData.id,
|
|
||||||
name: testPersona.responseData.name,
|
|
||||||
displayName: testPersona.responseData.displayName,
|
|
||||||
fullyQualifiedName: testPersona.responseData.fullyQualifiedName,
|
|
||||||
type: 'persona',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!process.env.PLAYWRIGHT_IS_OSS) {
|
if (!process.env.PLAYWRIGHT_IS_OSS) {
|
||||||
// Todo: Remove this patch once the issue is fixed #19140
|
// Todo: Remove this patch once the issue is fixed #19140
|
||||||
await resetTokenFromBotPage(page, 'testsuite-bot');
|
await resetTokenFromBotPage(page, 'testsuite-bot');
|
||||||
@ -126,9 +78,28 @@ test.describe('Data Contracts', () => {
|
|||||||
await afterAction();
|
await afterAction();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test.beforeEach('Redirect to Home Page', async ({ page }) => {
|
||||||
|
await redirectToHomePage(page);
|
||||||
|
});
|
||||||
|
|
||||||
test('Create Data Contract and validate', async ({ page }) => {
|
test('Create Data Contract and validate', async ({ page }) => {
|
||||||
test.setTimeout(360000);
|
test.setTimeout(360000);
|
||||||
|
|
||||||
|
const table = new TableClass();
|
||||||
|
const testClassification = new ClassificationClass();
|
||||||
|
const testTag = new TagClass({
|
||||||
|
classification: testClassification.data.name,
|
||||||
|
});
|
||||||
|
const testGlossary = new Glossary();
|
||||||
|
const testGlossaryTerm = new GlossaryTerm(testGlossary);
|
||||||
|
|
||||||
|
const { apiContext } = await getApiContext(page);
|
||||||
|
await table.create(apiContext);
|
||||||
|
await testClassification.create(apiContext);
|
||||||
|
await testTag.create(apiContext);
|
||||||
|
await testGlossary.create(apiContext);
|
||||||
|
await testGlossaryTerm.create(apiContext);
|
||||||
|
|
||||||
await test.step('Redirect to Home Page and visit entity', async () => {
|
await test.step('Redirect to Home Page and visit entity', async () => {
|
||||||
await redirectToHomePage(page);
|
await redirectToHomePage(page);
|
||||||
await table.visitEntityPage(page);
|
await table.visitEntityPage(page);
|
||||||
@ -421,11 +392,18 @@ test.describe('Data Contracts', () => {
|
|||||||
'Pipeline will only be triggered manually.'
|
'Pipeline will only be triggered manually.'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const pipelineResponse = page.waitForResponse(
|
||||||
|
'/api/v1/services/ingestionPipelines'
|
||||||
|
);
|
||||||
|
|
||||||
const testCaseResponse = page.waitForResponse(
|
const testCaseResponse = page.waitForResponse(
|
||||||
'/api/v1/dataQuality/testCases'
|
'/api/v1/dataQuality/testCases'
|
||||||
);
|
);
|
||||||
await page.click('[data-testid="create-btn"]');
|
await page.click('[data-testid="create-btn"]');
|
||||||
await testCaseResponse;
|
await testCaseResponse;
|
||||||
|
await pipelineResponse;
|
||||||
|
|
||||||
|
await expect(page.getByRole('dialog')).not.toBeVisible();
|
||||||
|
|
||||||
await page.waitForLoadState('networkidle');
|
await page.waitForLoadState('networkidle');
|
||||||
await page.waitForSelector('[data-testid="loader"]', {
|
await page.waitForSelector('[data-testid="loader"]', {
|
||||||
@ -681,246 +659,6 @@ test.describe('Data Contracts', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Contract Status badge should be visible on condition if Contract Tab is present/hidden by Persona', async ({
|
|
||||||
page,
|
|
||||||
}) => {
|
|
||||||
test.slow(true);
|
|
||||||
|
|
||||||
await test.step(
|
|
||||||
'Create Data Contract in Table and validate it fails',
|
|
||||||
async () => {
|
|
||||||
await table2.visitEntityPage(page);
|
|
||||||
|
|
||||||
// Open contract section and start adding contract
|
|
||||||
await page.click('[data-testid="contract"]');
|
|
||||||
await page.waitForSelector('[data-testid="loader"]', {
|
|
||||||
state: 'detached',
|
|
||||||
});
|
|
||||||
|
|
||||||
await expect(page.getByTestId('no-data-placeholder')).toBeVisible();
|
|
||||||
await expect(page.getByTestId('add-contract-button')).toBeVisible();
|
|
||||||
|
|
||||||
await page.getByTestId('add-contract-button').click();
|
|
||||||
|
|
||||||
await expect(page.getByTestId('add-contract-card')).toBeVisible();
|
|
||||||
|
|
||||||
// Fill Contract Details form
|
|
||||||
await page
|
|
||||||
.getByTestId('contract-name')
|
|
||||||
.fill(DATA_CONTRACT_DETAILS.name);
|
|
||||||
await page.fill(
|
|
||||||
'.om-block-editor[contenteditable="true"]',
|
|
||||||
DATA_CONTRACT_DETAILS.description
|
|
||||||
);
|
|
||||||
|
|
||||||
await page.getByTestId('select-owners').click();
|
|
||||||
await page.locator('.rc-virtual-list-holder-inner li').first().click();
|
|
||||||
|
|
||||||
await expect(page.getByTestId('user-tag')).toBeVisible();
|
|
||||||
|
|
||||||
// Fill Contract Schema form
|
|
||||||
await page
|
|
||||||
.getByTestId('add-contract-card')
|
|
||||||
.getByRole('tab', { name: 'Schema' })
|
|
||||||
.click();
|
|
||||||
|
|
||||||
await page
|
|
||||||
.locator('input[type="checkbox"][aria-label="Select all"]')
|
|
||||||
.check();
|
|
||||||
|
|
||||||
await expect(
|
|
||||||
page.getByRole('checkbox', { name: 'Select all' })
|
|
||||||
).toBeChecked();
|
|
||||||
|
|
||||||
// Fill Contract Semantics form
|
|
||||||
await page.getByRole('tab', { name: 'Semantics' }).click();
|
|
||||||
|
|
||||||
await expect(page.getByTestId('add-semantic-button')).toBeDisabled();
|
|
||||||
|
|
||||||
await page.fill('#semantics_0_name', DATA_CONTRACT_SEMANTICS1.name);
|
|
||||||
await page.fill(
|
|
||||||
'#semantics_0_description',
|
|
||||||
DATA_CONTRACT_SEMANTICS1.description
|
|
||||||
);
|
|
||||||
|
|
||||||
const ruleLocator = page.locator('.group').nth(0);
|
|
||||||
await selectOption(
|
|
||||||
page,
|
|
||||||
ruleLocator.locator('.group--field .ant-select'),
|
|
||||||
DATA_CONTRACT_SEMANTICS1.rules[0].field,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
await selectOption(
|
|
||||||
page,
|
|
||||||
ruleLocator.locator('.rule--operator .ant-select'),
|
|
||||||
DATA_CONTRACT_SEMANTICS1.rules[0].operator
|
|
||||||
);
|
|
||||||
await selectOption(
|
|
||||||
page,
|
|
||||||
ruleLocator.locator('.rule--value .ant-select'),
|
|
||||||
'admin',
|
|
||||||
true
|
|
||||||
);
|
|
||||||
await page.getByTestId('save-semantic-button').click();
|
|
||||||
|
|
||||||
await expect(
|
|
||||||
page
|
|
||||||
.getByTestId('contract-semantics-card-0')
|
|
||||||
.locator('.semantic-form-item-title')
|
|
||||||
).toContainText(DATA_CONTRACT_SEMANTICS1.name);
|
|
||||||
|
|
||||||
// Save contract and validate for semantics - should fail initially
|
|
||||||
await saveAndTriggerDataContractValidation(page, true);
|
|
||||||
|
|
||||||
await expect(
|
|
||||||
page.getByTestId('contract-status-card-item-semantics-status')
|
|
||||||
).toContainText('Failed');
|
|
||||||
await expect(
|
|
||||||
page.getByTestId('data-contract-latest-result-btn')
|
|
||||||
).toContainText('Contract Failed');
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
await test.step('Create Persona and assign user to it', async () => {
|
|
||||||
await redirectToHomePage(page);
|
|
||||||
|
|
||||||
const personaGetResponse = page.waitForResponse('/api/v1/personas**');
|
|
||||||
await settingClick(page, GlobalSettingOptions.PERSONA);
|
|
||||||
await personaGetResponse;
|
|
||||||
|
|
||||||
await page.waitForSelector('.ant-skeleton-content', {
|
|
||||||
state: 'detached',
|
|
||||||
});
|
|
||||||
|
|
||||||
// Navigate to persona details
|
|
||||||
await page
|
|
||||||
.getByTestId(`persona-details-card-${testPersona.data.name}`)
|
|
||||||
.click();
|
|
||||||
await page.getByRole('tab', { name: 'Users' }).click();
|
|
||||||
|
|
||||||
// Add user to persona
|
|
||||||
await page.getByTestId('add-persona-button').click();
|
|
||||||
await page.waitForSelector('[data-testid="loader"]', {
|
|
||||||
state: 'detached',
|
|
||||||
});
|
|
||||||
|
|
||||||
const searchUser = page.waitForResponse(
|
|
||||||
`/api/v1/search/query?q=*${encodeURIComponent(
|
|
||||||
adminUser.responseData.displayName
|
|
||||||
)}*`
|
|
||||||
);
|
|
||||||
await page
|
|
||||||
.getByTestId('searchbar')
|
|
||||||
.fill(adminUser.responseData.displayName);
|
|
||||||
await searchUser;
|
|
||||||
|
|
||||||
await page
|
|
||||||
.getByRole('listitem', { name: adminUser.responseData.displayName })
|
|
||||||
.click();
|
|
||||||
|
|
||||||
const personaResponse = page.waitForResponse('/api/v1/personas/*');
|
|
||||||
|
|
||||||
await page.getByTestId('selectable-list-update-btn').click();
|
|
||||||
await personaResponse;
|
|
||||||
});
|
|
||||||
|
|
||||||
await test.step(
|
|
||||||
'Verify Contract tab and status badge are visible if persona is set',
|
|
||||||
async () => {
|
|
||||||
await redirectToHomePage(page);
|
|
||||||
await table2.visitEntityPage(page);
|
|
||||||
await page.waitForLoadState('networkidle');
|
|
||||||
await page.waitForSelector('[data-testid="loader"]', {
|
|
||||||
state: 'detached',
|
|
||||||
});
|
|
||||||
|
|
||||||
// Verify Contract tab is not visible (should be hidden by persona customization)
|
|
||||||
await expect(page.getByTestId('contract')).toBeVisible();
|
|
||||||
|
|
||||||
// Verify Contract status badge is not visible in header
|
|
||||||
await expect(
|
|
||||||
page.getByTestId('data-contract-latest-result-btn')
|
|
||||||
).toBeVisible();
|
|
||||||
|
|
||||||
// Additional verification: Check that other tabs are still visible
|
|
||||||
await expect(page.getByTestId('schema')).toBeVisible();
|
|
||||||
await expect(page.getByTestId('activity_feed')).toBeVisible();
|
|
||||||
await expect(page.getByTestId('sample_data')).toBeVisible();
|
|
||||||
await expect(page.getByTestId('table_queries')).toBeVisible();
|
|
||||||
await expect(page.getByTestId('profiler')).toBeVisible();
|
|
||||||
await expect(page.getByTestId('lineage')).toBeVisible();
|
|
||||||
await expect(page.getByTestId('custom_properties')).toBeVisible();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
await test.step('Customize Table page to hide Contract tab', async () => {
|
|
||||||
await settingClick(page, GlobalSettingOptions.PERSONA);
|
|
||||||
await page.waitForLoadState('networkidle');
|
|
||||||
await page.waitForSelector('[data-testid="loader"]', {
|
|
||||||
state: 'detached',
|
|
||||||
});
|
|
||||||
|
|
||||||
// Navigate to persona details and customize UI
|
|
||||||
await page
|
|
||||||
.getByTestId(`persona-details-card-${testPersona.data.name}`)
|
|
||||||
.click();
|
|
||||||
await page.getByRole('tab', { name: 'Customize UI' }).click();
|
|
||||||
await page.waitForLoadState('networkidle');
|
|
||||||
|
|
||||||
// Navigate to Table customization
|
|
||||||
await page.getByTestId('data-assets').getByText('Data Assets').click();
|
|
||||||
await page.getByText('Table', { exact: true }).click();
|
|
||||||
|
|
||||||
await page.waitForSelector('[data-testid="loader"]', {
|
|
||||||
state: 'detached',
|
|
||||||
});
|
|
||||||
|
|
||||||
// Hide the Contract tab
|
|
||||||
await page.getByTestId('tab-contract').click();
|
|
||||||
await page.getByText('Hide', { exact: true }).click();
|
|
||||||
|
|
||||||
// Save the customization
|
|
||||||
await page.getByTestId('save-button').click();
|
|
||||||
await toastNotification(
|
|
||||||
page,
|
|
||||||
/^Page layout (created|updated) successfully\.$/
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
await test.step(
|
|
||||||
'Verify Contract tab and status badge are hidden after persona customization',
|
|
||||||
async () => {
|
|
||||||
// After applying persona customization to hide the contract tab,
|
|
||||||
// we need to verify that the contract tab and status badge are not visible
|
|
||||||
// when viewing the table page with the customized persona.
|
|
||||||
|
|
||||||
await redirectToHomePage(page);
|
|
||||||
await table2.visitEntityPage(page);
|
|
||||||
await page.waitForLoadState('networkidle');
|
|
||||||
await page.waitForSelector('[data-testid="loader"]', {
|
|
||||||
state: 'detached',
|
|
||||||
});
|
|
||||||
|
|
||||||
// Verify Contract tab is not visible (should be hidden by persona customization)
|
|
||||||
await expect(page.getByTestId('contract')).not.toBeVisible();
|
|
||||||
|
|
||||||
// Verify Contract status badge is not visible in header
|
|
||||||
await expect(
|
|
||||||
page.getByTestId('data-contract-latest-result-btn')
|
|
||||||
).not.toBeVisible();
|
|
||||||
|
|
||||||
// Additional verification: Check that other tabs are still visible
|
|
||||||
await expect(page.getByTestId('schema')).toBeVisible();
|
|
||||||
await expect(page.getByTestId('activity_feed')).toBeVisible();
|
|
||||||
await expect(page.getByTestId('sample_data')).toBeVisible();
|
|
||||||
await expect(page.getByTestId('table_queries')).toBeVisible();
|
|
||||||
await expect(page.getByTestId('profiler')).toBeVisible();
|
|
||||||
await expect(page.getByTestId('lineage')).toBeVisible();
|
|
||||||
await expect(page.getByTestId('custom_properties')).toBeVisible();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Pagination in Schema Tab with Selection Persistent', async ({
|
test('Pagination in Schema Tab with Selection Persistent', async ({
|
||||||
page,
|
page,
|
||||||
}) => {
|
}) => {
|
||||||
@ -1232,6 +970,21 @@ test.describe('Data Contracts', () => {
|
|||||||
}) => {
|
}) => {
|
||||||
test.slow(true);
|
test.slow(true);
|
||||||
|
|
||||||
|
const table = new TableClass();
|
||||||
|
const testClassification = new ClassificationClass();
|
||||||
|
const testTag = new TagClass({
|
||||||
|
classification: testClassification.data.name,
|
||||||
|
});
|
||||||
|
const testGlossary = new Glossary();
|
||||||
|
const testGlossaryTerm = new GlossaryTerm(testGlossary);
|
||||||
|
|
||||||
|
const { apiContext } = await getApiContext(page);
|
||||||
|
await table.create(apiContext);
|
||||||
|
await testClassification.create(apiContext);
|
||||||
|
await testTag.create(apiContext);
|
||||||
|
await testGlossary.create(apiContext);
|
||||||
|
await testGlossaryTerm.create(apiContext);
|
||||||
|
|
||||||
await redirectToHomePage(page);
|
await redirectToHomePage(page);
|
||||||
await table.visitEntityPage(page);
|
await table.visitEntityPage(page);
|
||||||
await page.click('[data-testid="contract"]');
|
await page.click('[data-testid="contract"]');
|
||||||
@ -1402,6 +1155,21 @@ test.describe('Data Contracts', () => {
|
|||||||
test('Semantic with Not_Contains Operator should work for Tier, Tag and Glossary', async ({
|
test('Semantic with Not_Contains Operator should work for Tier, Tag and Glossary', async ({
|
||||||
page,
|
page,
|
||||||
}) => {
|
}) => {
|
||||||
|
const table = new TableClass();
|
||||||
|
const testClassification = new ClassificationClass();
|
||||||
|
const testTag = new TagClass({
|
||||||
|
classification: testClassification.data.name,
|
||||||
|
});
|
||||||
|
const testGlossary = new Glossary();
|
||||||
|
const testGlossaryTerm = new GlossaryTerm(testGlossary);
|
||||||
|
|
||||||
|
const { apiContext } = await getApiContext(page);
|
||||||
|
await table.create(apiContext);
|
||||||
|
await testClassification.create(apiContext);
|
||||||
|
await testTag.create(apiContext);
|
||||||
|
await testGlossary.create(apiContext);
|
||||||
|
await testGlossaryTerm.create(apiContext);
|
||||||
|
|
||||||
await redirectToHomePage(page);
|
await redirectToHomePage(page);
|
||||||
await table.visitEntityPage(page);
|
await table.visitEntityPage(page);
|
||||||
await page.click('[data-testid="contract"]');
|
await page.click('[data-testid="contract"]');
|
||||||
@ -1574,6 +1342,10 @@ test.describe('Data Contracts', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('Nested Column should not be selectable', async ({ page }) => {
|
test('Nested Column should not be selectable', async ({ page }) => {
|
||||||
|
const { apiContext } = await getApiContext(page);
|
||||||
|
const table = new TableClass();
|
||||||
|
await table.create(apiContext);
|
||||||
|
|
||||||
const entityFQN = table.entityResponseData.fullyQualifiedName;
|
const entityFQN = table.entityResponseData.fullyQualifiedName;
|
||||||
await redirectToHomePage(page);
|
await redirectToHomePage(page);
|
||||||
await table.visitEntityPage(page);
|
await table.visitEntityPage(page);
|
||||||
@ -1642,6 +1414,10 @@ test.describe('Data Contracts', () => {
|
|||||||
test('should allow adding a semantic with multiple rules', async ({
|
test('should allow adding a semantic with multiple rules', async ({
|
||||||
page,
|
page,
|
||||||
}) => {
|
}) => {
|
||||||
|
const { apiContext } = await getApiContext(page);
|
||||||
|
const table = new TableClass();
|
||||||
|
await table.create(apiContext);
|
||||||
|
|
||||||
await redirectToHomePage(page);
|
await redirectToHomePage(page);
|
||||||
await table.visitEntityPage(page);
|
await table.visitEntityPage(page);
|
||||||
await page.click('[data-testid="contract"]');
|
await page.click('[data-testid="contract"]');
|
||||||
@ -1717,6 +1493,10 @@ test.describe('Data Contracts', () => {
|
|||||||
test('should allow adding a second semantic and verify its rule', async ({
|
test('should allow adding a second semantic and verify its rule', async ({
|
||||||
page,
|
page,
|
||||||
}) => {
|
}) => {
|
||||||
|
const { apiContext } = await getApiContext(page);
|
||||||
|
const table = new TableClass();
|
||||||
|
await table.create(apiContext);
|
||||||
|
|
||||||
await redirectToHomePage(page);
|
await redirectToHomePage(page);
|
||||||
await table.visitEntityPage(page);
|
await table.visitEntityPage(page);
|
||||||
await page.click('[data-testid="contract"]');
|
await page.click('[data-testid="contract"]');
|
||||||
@ -1806,6 +1586,10 @@ test.describe('Data Contracts', () => {
|
|||||||
test('should allow editing a semantic and reflect changes', async ({
|
test('should allow editing a semantic and reflect changes', async ({
|
||||||
page,
|
page,
|
||||||
}) => {
|
}) => {
|
||||||
|
const { apiContext } = await getApiContext(page);
|
||||||
|
const table = new TableClass();
|
||||||
|
await table.create(apiContext);
|
||||||
|
|
||||||
await redirectToHomePage(page);
|
await redirectToHomePage(page);
|
||||||
await table.visitEntityPage(page);
|
await table.visitEntityPage(page);
|
||||||
await page.click('[data-testid="contract"]');
|
await page.click('[data-testid="contract"]');
|
||||||
@ -1860,6 +1644,10 @@ test.describe('Data Contracts', () => {
|
|||||||
test('should allow deleting a semantic and remove it from the list', async ({
|
test('should allow deleting a semantic and remove it from the list', async ({
|
||||||
page,
|
page,
|
||||||
}) => {
|
}) => {
|
||||||
|
const { apiContext } = await getApiContext(page);
|
||||||
|
const table = new TableClass();
|
||||||
|
await table.create(apiContext);
|
||||||
|
|
||||||
await redirectToHomePage(page);
|
await redirectToHomePage(page);
|
||||||
await table.visitEntityPage(page);
|
await table.visitEntityPage(page);
|
||||||
await page.click('[data-testid="contract"]');
|
await page.click('[data-testid="contract"]');
|
||||||
@ -1925,10 +1713,14 @@ test.describe('Data Contracts', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('Add and update Security and SLA tabs', async ({ page }) => {
|
test('Add and update Security and SLA tabs', async ({ page }) => {
|
||||||
|
const { apiContext } = await getApiContext(page);
|
||||||
|
const table = new TableClass();
|
||||||
|
await table.create(apiContext);
|
||||||
|
|
||||||
|
await test.step('Add Security and SLA Details', async () => {
|
||||||
await redirectToHomePage(page);
|
await redirectToHomePage(page);
|
||||||
await table.visitEntityPage(page);
|
await table.visitEntityPage(page);
|
||||||
|
|
||||||
await test.step('Add Security and SLA Details', async () => {
|
|
||||||
await page.click('[data-testid="contract"]');
|
await page.click('[data-testid="contract"]');
|
||||||
await page.waitForSelector('[data-testid="loader"]', {
|
await page.waitForSelector('[data-testid="loader"]', {
|
||||||
state: 'detached',
|
state: 'detached',
|
||||||
@ -2148,3 +1940,297 @@ test.describe('Data Contracts', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testPersona.describe('Data Contracts With Persona', () => {
|
||||||
|
test.beforeAll('Setup pre-requests', async ({ browser }) => {
|
||||||
|
const { apiContext, afterAction } = await performAdminLogin(browser);
|
||||||
|
await adminUser.create(apiContext);
|
||||||
|
await adminUser.setAdminRole(apiContext);
|
||||||
|
await afterAction();
|
||||||
|
});
|
||||||
|
|
||||||
|
testPersona(
|
||||||
|
'Contract Status badge should be visible on condition if Contract Tab is present/hidden by Persona',
|
||||||
|
async ({ page }) => {
|
||||||
|
testPersona.slow(true);
|
||||||
|
const { apiContext } = await getApiContext(page);
|
||||||
|
const table = new TableClass();
|
||||||
|
const persona = new PersonaClass();
|
||||||
|
await table.create(apiContext);
|
||||||
|
await persona.create(apiContext);
|
||||||
|
await adminUser.patch({
|
||||||
|
apiContext,
|
||||||
|
patchData: [
|
||||||
|
{
|
||||||
|
op: 'add',
|
||||||
|
path: '/personas/0',
|
||||||
|
value: {
|
||||||
|
id: persona.responseData.id,
|
||||||
|
name: persona.responseData.name,
|
||||||
|
displayName: persona.responseData.displayName,
|
||||||
|
fullyQualifiedName: persona.responseData.fullyQualifiedName,
|
||||||
|
type: 'persona',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
op: 'add',
|
||||||
|
path: '/defaultPersona',
|
||||||
|
value: {
|
||||||
|
id: persona.responseData.id,
|
||||||
|
name: persona.responseData.name,
|
||||||
|
displayName: persona.responseData.displayName,
|
||||||
|
fullyQualifiedName: persona.responseData.fullyQualifiedName,
|
||||||
|
type: 'persona',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
await testPersona.step(
|
||||||
|
'Create Data Contract in Table and validate it fails',
|
||||||
|
async () => {
|
||||||
|
await table.visitEntityPage(page);
|
||||||
|
|
||||||
|
// Open contract section and start adding contract
|
||||||
|
await page.click('[data-testid="contract"]');
|
||||||
|
await page.waitForSelector('[data-testid="loader"]', {
|
||||||
|
state: 'detached',
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(page.getByTestId('no-data-placeholder')).toBeVisible();
|
||||||
|
await expect(page.getByTestId('add-contract-button')).toBeVisible();
|
||||||
|
|
||||||
|
await page.getByTestId('add-contract-button').click();
|
||||||
|
|
||||||
|
await expect(page.getByTestId('add-contract-card')).toBeVisible();
|
||||||
|
|
||||||
|
// Fill Contract Details form
|
||||||
|
await page
|
||||||
|
.getByTestId('contract-name')
|
||||||
|
.fill(DATA_CONTRACT_DETAILS.name);
|
||||||
|
await page.fill(
|
||||||
|
'.om-block-editor[contenteditable="true"]',
|
||||||
|
DATA_CONTRACT_DETAILS.description
|
||||||
|
);
|
||||||
|
|
||||||
|
await page.getByTestId('select-owners').click();
|
||||||
|
await page
|
||||||
|
.locator('.rc-virtual-list-holder-inner li')
|
||||||
|
.first()
|
||||||
|
.click();
|
||||||
|
|
||||||
|
await expect(page.getByTestId('user-tag')).toBeVisible();
|
||||||
|
|
||||||
|
// Fill Contract Schema form
|
||||||
|
await page
|
||||||
|
.getByTestId('add-contract-card')
|
||||||
|
.getByRole('tab', { name: 'Schema' })
|
||||||
|
.click();
|
||||||
|
|
||||||
|
await page
|
||||||
|
.locator('input[type="checkbox"][aria-label="Select all"]')
|
||||||
|
.check();
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
page.getByRole('checkbox', { name: 'Select all' })
|
||||||
|
).toBeChecked();
|
||||||
|
|
||||||
|
// Fill Contract Semantics form
|
||||||
|
await page.getByRole('tab', { name: 'Semantics' }).click();
|
||||||
|
|
||||||
|
await expect(page.getByTestId('add-semantic-button')).toBeDisabled();
|
||||||
|
|
||||||
|
await page.fill('#semantics_0_name', DATA_CONTRACT_SEMANTICS1.name);
|
||||||
|
await page.fill(
|
||||||
|
'#semantics_0_description',
|
||||||
|
DATA_CONTRACT_SEMANTICS1.description
|
||||||
|
);
|
||||||
|
|
||||||
|
const ruleLocator = page.locator('.group').nth(0);
|
||||||
|
await selectOption(
|
||||||
|
page,
|
||||||
|
ruleLocator.locator('.group--field .ant-select'),
|
||||||
|
DATA_CONTRACT_SEMANTICS1.rules[0].field,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
await selectOption(
|
||||||
|
page,
|
||||||
|
ruleLocator.locator('.rule--operator .ant-select'),
|
||||||
|
DATA_CONTRACT_SEMANTICS1.rules[0].operator
|
||||||
|
);
|
||||||
|
await selectOption(
|
||||||
|
page,
|
||||||
|
ruleLocator.locator('.rule--value .ant-select'),
|
||||||
|
'admin',
|
||||||
|
true
|
||||||
|
);
|
||||||
|
await page.getByTestId('save-semantic-button').click();
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
page
|
||||||
|
.getByTestId('contract-semantics-card-0')
|
||||||
|
.locator('.semantic-form-item-title')
|
||||||
|
).toContainText(DATA_CONTRACT_SEMANTICS1.name);
|
||||||
|
|
||||||
|
// Save contract and validate for semantics - should fail initially
|
||||||
|
await saveAndTriggerDataContractValidation(page, true);
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
page.getByTestId('contract-status-card-item-semantics-status')
|
||||||
|
).toContainText('Failed');
|
||||||
|
await expect(
|
||||||
|
page.getByTestId('data-contract-latest-result-btn')
|
||||||
|
).toContainText('Contract Failed');
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
await testPersona.step(
|
||||||
|
'Create Persona and assign user to it',
|
||||||
|
async () => {
|
||||||
|
await redirectToHomePage(page);
|
||||||
|
|
||||||
|
const personaGetResponse = page.waitForResponse('/api/v1/personas**');
|
||||||
|
await settingClick(page, GlobalSettingOptions.PERSONA);
|
||||||
|
await personaGetResponse;
|
||||||
|
|
||||||
|
await page.waitForSelector('.ant-skeleton-content', {
|
||||||
|
state: 'detached',
|
||||||
|
});
|
||||||
|
|
||||||
|
// Navigate to persona details
|
||||||
|
await page
|
||||||
|
.getByTestId(`persona-details-card-${persona.data.name}`)
|
||||||
|
.click();
|
||||||
|
await page.getByRole('tab', { name: 'Users' }).click();
|
||||||
|
|
||||||
|
// Add user to persona
|
||||||
|
await page.getByTestId('add-persona-button').click();
|
||||||
|
await page.waitForSelector('[data-testid="loader"]', {
|
||||||
|
state: 'detached',
|
||||||
|
});
|
||||||
|
|
||||||
|
const searchUser = page.waitForResponse(
|
||||||
|
`/api/v1/search/query?q=*${encodeURIComponent(
|
||||||
|
adminUser.responseData.displayName
|
||||||
|
)}*`
|
||||||
|
);
|
||||||
|
await page
|
||||||
|
.getByTestId('searchbar')
|
||||||
|
.fill(adminUser.responseData.displayName);
|
||||||
|
await searchUser;
|
||||||
|
|
||||||
|
await page
|
||||||
|
.getByRole('listitem', { name: adminUser.responseData.displayName })
|
||||||
|
.click();
|
||||||
|
|
||||||
|
const personaResponse = page.waitForResponse('/api/v1/personas/*');
|
||||||
|
|
||||||
|
await page.getByTestId('selectable-list-update-btn').click();
|
||||||
|
await personaResponse;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
await testPersona.step(
|
||||||
|
'Verify Contract tab and status badge are visible if persona is set',
|
||||||
|
async () => {
|
||||||
|
await redirectToHomePage(page);
|
||||||
|
await table.visitEntityPage(page);
|
||||||
|
await page.waitForLoadState('networkidle');
|
||||||
|
await page.waitForSelector('[data-testid="loader"]', {
|
||||||
|
state: 'detached',
|
||||||
|
});
|
||||||
|
|
||||||
|
// Verify Contract tab is not visible (should be hidden by persona customization)
|
||||||
|
await expect(page.getByTestId('contract')).toBeVisible();
|
||||||
|
|
||||||
|
// Verify Contract status badge is not visible in header
|
||||||
|
await expect(
|
||||||
|
page.getByTestId('data-contract-latest-result-btn')
|
||||||
|
).toBeVisible();
|
||||||
|
|
||||||
|
// Additional verification: Check that other tabs are still visible
|
||||||
|
await expect(page.getByTestId('schema')).toBeVisible();
|
||||||
|
await expect(page.getByTestId('activity_feed')).toBeVisible();
|
||||||
|
await expect(page.getByTestId('sample_data')).toBeVisible();
|
||||||
|
await expect(page.getByTestId('table_queries')).toBeVisible();
|
||||||
|
await expect(page.getByTestId('profiler')).toBeVisible();
|
||||||
|
await expect(page.getByTestId('lineage')).toBeVisible();
|
||||||
|
await expect(page.getByTestId('custom_properties')).toBeVisible();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
await testPersona.step(
|
||||||
|
'Customize Table page to hide Contract tab',
|
||||||
|
async () => {
|
||||||
|
await settingClick(page, GlobalSettingOptions.PERSONA);
|
||||||
|
await page.waitForLoadState('networkidle');
|
||||||
|
await page.waitForSelector('[data-testid="loader"]', {
|
||||||
|
state: 'detached',
|
||||||
|
});
|
||||||
|
|
||||||
|
// Navigate to persona details and customize UI
|
||||||
|
await page
|
||||||
|
.getByTestId(`persona-details-card-${persona.data.name}`)
|
||||||
|
.click();
|
||||||
|
await page.getByRole('tab', { name: 'Customize UI' }).click();
|
||||||
|
await page.waitForLoadState('networkidle');
|
||||||
|
|
||||||
|
// Navigate to Table customization
|
||||||
|
await page
|
||||||
|
.getByTestId('data-assets')
|
||||||
|
.getByText('Data Assets')
|
||||||
|
.click();
|
||||||
|
await page.getByText('Table', { exact: true }).click();
|
||||||
|
|
||||||
|
await page.waitForSelector('[data-testid="loader"]', {
|
||||||
|
state: 'detached',
|
||||||
|
});
|
||||||
|
|
||||||
|
// Hide the Contract tab
|
||||||
|
await page.getByTestId('tab-contract').click();
|
||||||
|
await page.getByText('Hide', { exact: true }).click();
|
||||||
|
|
||||||
|
// Save the customization
|
||||||
|
await page.getByTestId('save-button').click();
|
||||||
|
await toastNotification(
|
||||||
|
page,
|
||||||
|
/^Page layout (created|updated) successfully\.$/
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
await testPersona.step(
|
||||||
|
'Verify Contract tab and status badge are hidden after persona customization',
|
||||||
|
async () => {
|
||||||
|
// After applying persona customization to hide the contract tab,
|
||||||
|
// we need to verify that the contract tab and status badge are not visible
|
||||||
|
// when viewing the table page with the customized persona.
|
||||||
|
|
||||||
|
await redirectToHomePage(page);
|
||||||
|
await table.visitEntityPage(page);
|
||||||
|
await page.waitForLoadState('networkidle');
|
||||||
|
await page.waitForSelector('[data-testid="loader"]', {
|
||||||
|
state: 'detached',
|
||||||
|
});
|
||||||
|
|
||||||
|
// Verify Contract tab is not visible (should be hidden by persona customization)
|
||||||
|
await expect(page.getByTestId('contract')).not.toBeVisible();
|
||||||
|
|
||||||
|
// Verify Contract status badge is not visible in header
|
||||||
|
await expect(
|
||||||
|
page.getByTestId('data-contract-latest-result-btn')
|
||||||
|
).not.toBeVisible();
|
||||||
|
|
||||||
|
// Additional verification: Check that other tabs are still visible
|
||||||
|
await expect(page.getByTestId('schema')).toBeVisible();
|
||||||
|
await expect(page.getByTestId('activity_feed')).toBeVisible();
|
||||||
|
await expect(page.getByTestId('sample_data')).toBeVisible();
|
||||||
|
await expect(page.getByTestId('table_queries')).toBeVisible();
|
||||||
|
await expect(page.getByTestId('profiler')).toBeVisible();
|
||||||
|
await expect(page.getByTestId('lineage')).toBeVisible();
|
||||||
|
await expect(page.getByTestId('custom_properties')).toBeVisible();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|||||||
@ -187,10 +187,18 @@ export const selectOption = async (
|
|||||||
.locator('.ant-select-selector')
|
.locator('.ant-select-selector')
|
||||||
.click({ force: true });
|
.click({ force: true });
|
||||||
|
|
||||||
|
await page.waitForSelector('.ant-select-item-empty', {
|
||||||
|
state: 'detached',
|
||||||
|
});
|
||||||
|
|
||||||
// Clear any existing input and type the new value
|
// Clear any existing input and type the new value
|
||||||
const combobox = dropdownLocator.getByRole('combobox');
|
const combobox = dropdownLocator.getByRole('combobox');
|
||||||
await combobox.clear();
|
await combobox.clear();
|
||||||
await combobox.fill(optionTitle);
|
await combobox.fill(optionTitle);
|
||||||
|
|
||||||
|
await page.waitForSelector('.ant-select-item-empty', {
|
||||||
|
state: 'detached',
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
await dropdownLocator.click();
|
await dropdownLocator.click();
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user