mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-10-11 08:43:31 +00:00
Minor: Add playwright test for entity pages with users with different roles (#17316)
* Add playwright test for a user with DataConsumer role * Add entity tests for Data Steward role * Fix flaky playwright tests * Fix the playwright for owner actions * playwright config change and minor fix * minor testing * minor setting change * Worked improvement comments. * Fix glossary spec flakiness with Assets count verification step `Rename the same entity again` * Improved util function argument types --------- Co-authored-by: Shailesh Parmar <shailesh.parmar.webdev@gmail.com>
This commit is contained in:
parent
ac2264e9c7
commit
d77aac425f
@ -30,9 +30,10 @@ export default defineConfig({
|
||||
/* Fail the build on CI if you accidentally left test.only in the source code. */
|
||||
forbidOnly: !!process.env.CI,
|
||||
/* Retry on CI only */
|
||||
retries: process.env.CI ? 2 : 0,
|
||||
retries: process.env.CI ? 1 : 0,
|
||||
/* Opt out of parallel tests on CI. */
|
||||
workers: process.env.CI ? 2 : undefined,
|
||||
workers: process.env.CI ? 3 : undefined,
|
||||
maxFailures: 30,
|
||||
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
||||
reporter: [
|
||||
['list'],
|
||||
|
@ -11,6 +11,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { test } from '@playwright/test';
|
||||
import { isUndefined } from 'lodash';
|
||||
import { CustomPropertySupportedEntityList } from '../../constant/customProperty';
|
||||
import { ApiEndpointClass } from '../../support/entity/ApiEndpointClass';
|
||||
import { ContainerClass } from '../../support/entity/ContainerClass';
|
||||
@ -113,6 +114,42 @@ entities.forEach((EntityClass) => {
|
||||
);
|
||||
});
|
||||
|
||||
// Run only if entity has children
|
||||
if (!isUndefined(entity.childrenTabId)) {
|
||||
test('Tag Add, Update and Remove for child entities', async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.getByTestId(entity.childrenTabId ?? '').click();
|
||||
|
||||
await entity.tagChildren({
|
||||
page: page,
|
||||
tag1: 'PersonalData.Personal',
|
||||
tag2: 'PII.None',
|
||||
rowId: entity.childrenSelectorId ?? '',
|
||||
rowSelector:
|
||||
entity.type === 'MlModel' ? 'data-testid' : 'data-row-key',
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Run only if entity has children
|
||||
if (!isUndefined(entity.childrenTabId)) {
|
||||
test('Glossary Term Add, Update and Remove for child entities', async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.getByTestId(entity.childrenTabId ?? '').click();
|
||||
|
||||
await entity.glossaryTermChildren({
|
||||
page: page,
|
||||
glossaryTerm1: EntityDataClass.glossaryTerm1.responseData,
|
||||
glossaryTerm2: EntityDataClass.glossaryTerm2.responseData,
|
||||
rowId: entity.childrenSelectorId ?? '',
|
||||
rowSelector:
|
||||
entity.type === 'MlModel' ? 'data-testid' : 'data-row-key',
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
test(`Announcement create & delete`, async ({ page }) => {
|
||||
await entity.announcement(
|
||||
page,
|
||||
@ -148,7 +185,7 @@ entities.forEach((EntityClass) => {
|
||||
|
||||
await test.step(`Set ${titleText} Custom Property`, async () => {
|
||||
for (const type of properties) {
|
||||
await entity.setCustomProperty(
|
||||
await entity.updateCustomProperty(
|
||||
page,
|
||||
entity.customPropertyValue[type].property,
|
||||
entity.customPropertyValue[type].value
|
||||
|
@ -0,0 +1,171 @@
|
||||
/*
|
||||
* 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 as base } from '@playwright/test';
|
||||
import { isUndefined } from 'lodash';
|
||||
import { ApiEndpointClass } from '../../support/entity/ApiEndpointClass';
|
||||
import { ContainerClass } from '../../support/entity/ContainerClass';
|
||||
import { DashboardClass } from '../../support/entity/DashboardClass';
|
||||
import { DashboardDataModelClass } from '../../support/entity/DashboardDataModelClass';
|
||||
import { EntityDataClass } from '../../support/entity/EntityDataClass';
|
||||
import { MlModelClass } from '../../support/entity/MlModelClass';
|
||||
import { PipelineClass } from '../../support/entity/PipelineClass';
|
||||
import { SearchIndexClass } from '../../support/entity/SearchIndexClass';
|
||||
import { StoredProcedureClass } from '../../support/entity/StoredProcedureClass';
|
||||
import { TableClass } from '../../support/entity/TableClass';
|
||||
import { TopicClass } from '../../support/entity/TopicClass';
|
||||
import { UserClass } from '../../support/user/UserClass';
|
||||
import { performAdminLogin } from '../../utils/admin';
|
||||
import { redirectToHomePage } from '../../utils/common';
|
||||
|
||||
const user = new UserClass();
|
||||
|
||||
const entities = [
|
||||
ApiEndpointClass,
|
||||
TableClass,
|
||||
StoredProcedureClass,
|
||||
DashboardClass,
|
||||
PipelineClass,
|
||||
TopicClass,
|
||||
MlModelClass,
|
||||
ContainerClass,
|
||||
SearchIndexClass,
|
||||
DashboardDataModelClass,
|
||||
] as const;
|
||||
|
||||
// Create 2 page and authenticate 1 with admin and another with normal user
|
||||
const test = base.extend<{
|
||||
page: Page;
|
||||
}>({
|
||||
page: async ({ browser }, use) => {
|
||||
const page = await browser.newPage();
|
||||
await user.login(page);
|
||||
await use(page);
|
||||
await page.close();
|
||||
},
|
||||
});
|
||||
|
||||
entities.forEach((EntityClass) => {
|
||||
const entity = new EntityClass();
|
||||
|
||||
test.describe(entity.getType(), () => {
|
||||
test.beforeAll('Setup pre-requests', async ({ browser }) => {
|
||||
const { apiContext, afterAction } = await performAdminLogin(browser);
|
||||
|
||||
await user.create(apiContext);
|
||||
await EntityDataClass.preRequisitesForTests(apiContext);
|
||||
await entity.create(apiContext);
|
||||
await afterAction();
|
||||
});
|
||||
|
||||
test.beforeEach('Visit entity details page', async ({ page }) => {
|
||||
await redirectToHomePage(page);
|
||||
await entity.visitEntityPage(page);
|
||||
});
|
||||
|
||||
test('User as Owner Add, Update and Remove', async ({ page }) => {
|
||||
test.slow(true);
|
||||
|
||||
const OWNER1 = EntityDataClass.user1.getUserName();
|
||||
const OWNER2 = EntityDataClass.user2.getUserName();
|
||||
const OWNER3 = EntityDataClass.user3.getUserName();
|
||||
await entity.owner(page, [OWNER1, OWNER3], [OWNER2], undefined, false);
|
||||
});
|
||||
|
||||
test('No edit owner permission', async ({ page }) => {
|
||||
await page.reload();
|
||||
await page.waitForSelector('[data-testid="loader"]', {
|
||||
state: 'detached',
|
||||
});
|
||||
|
||||
await expect(page.getByTestId('edit-owner')).not.toBeAttached();
|
||||
});
|
||||
|
||||
test('Tier Add, Update and Remove', async ({ page }) => {
|
||||
await entity.tier(
|
||||
page,
|
||||
'Tier1',
|
||||
EntityDataClass.tierTag1.data.displayName
|
||||
);
|
||||
});
|
||||
|
||||
test('Update description', async ({ page }) => {
|
||||
await entity.descriptionUpdate(page);
|
||||
});
|
||||
|
||||
test('Tag Add, Update and Remove', async ({ page }) => {
|
||||
await entity.tag(page, 'PersonalData.Personal', 'PII.None');
|
||||
});
|
||||
|
||||
test('Glossary Term Add, Update and Remove', async ({ page }) => {
|
||||
await entity.glossaryTerm(
|
||||
page,
|
||||
EntityDataClass.glossaryTerm1.responseData,
|
||||
EntityDataClass.glossaryTerm2.responseData
|
||||
);
|
||||
});
|
||||
|
||||
// Run only if entity has children
|
||||
if (!isUndefined(entity.childrenTabId)) {
|
||||
test('Tag Add, Update and Remove for child entities', async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.getByTestId(entity.childrenTabId ?? '').click();
|
||||
|
||||
await entity.tagChildren({
|
||||
page,
|
||||
tag1: 'PersonalData.Personal',
|
||||
tag2: 'PII.None',
|
||||
rowId: entity.childrenSelectorId ?? '',
|
||||
rowSelector:
|
||||
entity.type === 'MlModel' ? 'data-testid' : 'data-row-key',
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Run only if entity has children
|
||||
if (!isUndefined(entity.childrenTabId)) {
|
||||
test('Glossary Term Add, Update and Remove for child entities', async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.getByTestId(entity.childrenTabId ?? '').click();
|
||||
|
||||
await entity.glossaryTermChildren({
|
||||
page,
|
||||
glossaryTerm1: EntityDataClass.glossaryTerm1.responseData,
|
||||
glossaryTerm2: EntityDataClass.glossaryTerm2.responseData,
|
||||
rowId: entity.childrenSelectorId ?? '',
|
||||
rowSelector:
|
||||
entity.type === 'MlModel' ? 'data-testid' : 'data-row-key',
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
test(`UpVote & DownVote entity`, async ({ page }) => {
|
||||
await entity.upVote(page);
|
||||
await entity.downVote(page);
|
||||
});
|
||||
|
||||
test(`Follow & Un-follow entity`, async ({ page }) => {
|
||||
const entityName = entity.entityResponseData?.['displayName'];
|
||||
await entity.followUnfollowEntity(page, entityName);
|
||||
});
|
||||
|
||||
test.afterAll('Cleanup', async ({ browser }) => {
|
||||
const { apiContext, afterAction } = await performAdminLogin(browser);
|
||||
await user.delete(apiContext);
|
||||
await entity.delete(apiContext);
|
||||
await EntityDataClass.postRequisitesForTests(apiContext);
|
||||
await afterAction();
|
||||
});
|
||||
});
|
||||
});
|
@ -0,0 +1,190 @@
|
||||
/*
|
||||
* 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 { Page, test as base } from '@playwright/test';
|
||||
import { isUndefined } from 'lodash';
|
||||
import { ApiEndpointClass } from '../../support/entity/ApiEndpointClass';
|
||||
import { ContainerClass } from '../../support/entity/ContainerClass';
|
||||
import { DashboardClass } from '../../support/entity/DashboardClass';
|
||||
import { DashboardDataModelClass } from '../../support/entity/DashboardDataModelClass';
|
||||
import { EntityDataClass } from '../../support/entity/EntityDataClass';
|
||||
import { MlModelClass } from '../../support/entity/MlModelClass';
|
||||
import { PipelineClass } from '../../support/entity/PipelineClass';
|
||||
import { SearchIndexClass } from '../../support/entity/SearchIndexClass';
|
||||
import { StoredProcedureClass } from '../../support/entity/StoredProcedureClass';
|
||||
import { TableClass } from '../../support/entity/TableClass';
|
||||
import { TopicClass } from '../../support/entity/TopicClass';
|
||||
import { UserClass } from '../../support/user/UserClass';
|
||||
import { performAdminLogin } from '../../utils/admin';
|
||||
import { redirectToHomePage } from '../../utils/common';
|
||||
|
||||
const user = new UserClass();
|
||||
|
||||
const entities = [
|
||||
ApiEndpointClass,
|
||||
TableClass,
|
||||
StoredProcedureClass,
|
||||
DashboardClass,
|
||||
PipelineClass,
|
||||
TopicClass,
|
||||
MlModelClass,
|
||||
ContainerClass,
|
||||
SearchIndexClass,
|
||||
DashboardDataModelClass,
|
||||
] as const;
|
||||
|
||||
// Create 2 page and authenticate 1 with admin and another with normal user
|
||||
const test = base.extend<{
|
||||
page: Page;
|
||||
}>({
|
||||
page: async ({ browser }, use) => {
|
||||
const page = await browser.newPage();
|
||||
await user.login(page);
|
||||
await use(page);
|
||||
await page.close();
|
||||
},
|
||||
});
|
||||
|
||||
entities.forEach((EntityClass) => {
|
||||
const entity = new EntityClass();
|
||||
|
||||
test.describe(entity.getType(), () => {
|
||||
test.beforeAll('Setup pre-requests', async ({ browser }) => {
|
||||
const { apiContext, afterAction } = await performAdminLogin(browser);
|
||||
|
||||
await user.create(apiContext);
|
||||
|
||||
const dataStewardRoleResponse = await apiContext.get(
|
||||
'/api/v1/roles/name/DataSteward'
|
||||
);
|
||||
|
||||
const dataStewardRole = await dataStewardRoleResponse.json();
|
||||
|
||||
await user.patch({
|
||||
apiContext,
|
||||
patchData: [
|
||||
{
|
||||
op: 'add',
|
||||
path: '/roles/0',
|
||||
value: {
|
||||
id: dataStewardRole.id,
|
||||
type: 'role',
|
||||
name: dataStewardRole.name,
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
await EntityDataClass.preRequisitesForTests(apiContext);
|
||||
await entity.create(apiContext);
|
||||
await afterAction();
|
||||
});
|
||||
|
||||
test.beforeEach('Visit entity details page', async ({ page }) => {
|
||||
await redirectToHomePage(page);
|
||||
await entity.visitEntityPage(page);
|
||||
});
|
||||
|
||||
test('User as Owner Add, Update and Remove', async ({ page }) => {
|
||||
test.slow(true);
|
||||
|
||||
const OWNER1 = EntityDataClass.user1.getUserName();
|
||||
const OWNER2 = EntityDataClass.user2.getUserName();
|
||||
const OWNER3 = EntityDataClass.user3.getUserName();
|
||||
await entity.owner(page, [OWNER1, OWNER3], [OWNER2]);
|
||||
});
|
||||
|
||||
test('Team as Owner Add, Update and Remove', async ({ page }) => {
|
||||
const OWNER1 = EntityDataClass.team1.data.displayName;
|
||||
const OWNER2 = EntityDataClass.team2.data.displayName;
|
||||
await entity.owner(page, [OWNER1], [OWNER2], 'Teams');
|
||||
});
|
||||
|
||||
test('Tier Add, Update and Remove', async ({ page }) => {
|
||||
await entity.tier(
|
||||
page,
|
||||
'Tier1',
|
||||
EntityDataClass.tierTag1.data.displayName
|
||||
);
|
||||
});
|
||||
|
||||
test('Update description', async ({ page }) => {
|
||||
await entity.descriptionUpdate(page);
|
||||
});
|
||||
|
||||
test('Tag Add, Update and Remove', async ({ page }) => {
|
||||
await entity.tag(page, 'PersonalData.Personal', 'PII.None');
|
||||
});
|
||||
|
||||
test('Glossary Term Add, Update and Remove', async ({ page }) => {
|
||||
await entity.glossaryTerm(
|
||||
page,
|
||||
EntityDataClass.glossaryTerm1.responseData,
|
||||
EntityDataClass.glossaryTerm2.responseData
|
||||
);
|
||||
});
|
||||
|
||||
// Run only if entity has children
|
||||
if (!isUndefined(entity.childrenTabId)) {
|
||||
test('Tag Add, Update and Remove for child entities', async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.getByTestId(entity.childrenTabId ?? '').click();
|
||||
|
||||
await entity.tagChildren({
|
||||
page,
|
||||
tag1: 'PersonalData.Personal',
|
||||
tag2: 'PII.None',
|
||||
rowId: entity.childrenSelectorId ?? '',
|
||||
rowSelector:
|
||||
entity.type === 'MlModel' ? 'data-testid' : 'data-row-key',
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Run only if entity has children
|
||||
if (!isUndefined(entity.childrenTabId)) {
|
||||
test('Glossary Term Add, Update and Remove for child entities', async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.getByTestId(entity.childrenTabId ?? '').click();
|
||||
|
||||
await entity.glossaryTermChildren({
|
||||
page,
|
||||
glossaryTerm1: EntityDataClass.glossaryTerm1.responseData,
|
||||
glossaryTerm2: EntityDataClass.glossaryTerm2.responseData,
|
||||
rowId: entity.childrenSelectorId ?? '',
|
||||
rowSelector:
|
||||
entity.type === 'MlModel' ? 'data-testid' : 'data-row-key',
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
test(`UpVote & DownVote entity`, async ({ page }) => {
|
||||
await entity.upVote(page);
|
||||
await entity.downVote(page);
|
||||
});
|
||||
|
||||
test(`Follow & Un-follow entity`, async ({ page }) => {
|
||||
const entityName = entity.entityResponseData?.['displayName'];
|
||||
await entity.followUnfollowEntity(page, entityName);
|
||||
});
|
||||
|
||||
test.afterAll('Cleanup', async ({ browser }) => {
|
||||
const { apiContext, afterAction } = await performAdminLogin(browser);
|
||||
await user.delete(apiContext);
|
||||
await entity.delete(apiContext);
|
||||
await EntityDataClass.postRequisitesForTests(apiContext);
|
||||
await afterAction();
|
||||
});
|
||||
});
|
||||
});
|
@ -360,7 +360,7 @@ test.describe('Glossary tests', () => {
|
||||
await sidebarClick(page, SidebarItem.GLOSSARY);
|
||||
|
||||
await selectActiveGlossary(page, glossary2.data.displayName);
|
||||
await goToAssetsTab(page, glossaryTerm3.data.displayName, '2');
|
||||
await goToAssetsTab(page, glossaryTerm3.data.displayName, 2);
|
||||
|
||||
// Check if the selected asset are present
|
||||
const assetContainer = await page.locator(
|
||||
@ -426,7 +426,11 @@ test.describe('Glossary tests', () => {
|
||||
await redirectToHomePage(page);
|
||||
await sidebarClick(page, SidebarItem.GLOSSARY);
|
||||
await selectActiveGlossary(page, glossary1.data.displayName);
|
||||
await goToAssetsTab(page, glossaryTerm1.data.displayName);
|
||||
await goToAssetsTab(
|
||||
page,
|
||||
glossaryTerm1.data.displayName,
|
||||
assets.length
|
||||
);
|
||||
await renameGlossaryTerm(page, glossaryTerm1, newName);
|
||||
await verifyGlossaryTermAssets(
|
||||
page,
|
||||
|
@ -82,6 +82,7 @@ test('Glossary', async ({ page }) => {
|
||||
resultTestId: 'glossary-right-panel-owner-link',
|
||||
endpoint: EntityTypeEndpoint.Glossary,
|
||||
isSelectableInsideForm: true,
|
||||
type: 'Users',
|
||||
});
|
||||
|
||||
await page.reload();
|
||||
@ -109,6 +110,7 @@ test('Glossary', async ({ page }) => {
|
||||
activatorBtnDataTestId: 'Add',
|
||||
resultTestId: 'glossary-reviewer-name',
|
||||
endpoint: EntityTypeEndpoint.Glossary,
|
||||
type: 'Users',
|
||||
});
|
||||
|
||||
await page.reload();
|
||||
@ -177,6 +179,7 @@ test('GlossaryTerm', async ({ page }) => {
|
||||
resultTestId: 'glossary-right-panel-owner-link',
|
||||
endpoint: EntityTypeEndpoint.Glossary,
|
||||
isSelectableInsideForm: true,
|
||||
type: 'Users',
|
||||
});
|
||||
|
||||
await page.reload();
|
||||
@ -204,6 +207,7 @@ test('GlossaryTerm', async ({ page }) => {
|
||||
activatorBtnDataTestId: 'Add',
|
||||
resultTestId: 'glossary-reviewer-name',
|
||||
endpoint: EntityTypeEndpoint.GlossaryTerm,
|
||||
type: 'Users',
|
||||
});
|
||||
|
||||
await page.reload();
|
||||
|
@ -136,7 +136,7 @@ entities.forEach((EntityClass) => {
|
||||
|
||||
await test.step(`Set ${titleText} Custom Property`, async () => {
|
||||
for (const type of properties) {
|
||||
await entity.setCustomProperty(
|
||||
await entity.updateCustomProperty(
|
||||
page,
|
||||
entity.customPropertyValue[type].property,
|
||||
entity.customPropertyValue[type].value
|
||||
|
@ -13,7 +13,7 @@
|
||||
import { test as setup } from '@playwright/test';
|
||||
import { JWT_EXPIRY_TIME_MAP } from '../constant/login';
|
||||
import { AdminClass } from '../support/user/AdminClass';
|
||||
import { getApiContext } from '../utils/common';
|
||||
import { getApiContext, getToken } from '../utils/common';
|
||||
import { updateJWTTokenExpiryTime } from '../utils/login';
|
||||
const adminFile = 'playwright/.auth/admin.json';
|
||||
|
||||
@ -26,6 +26,14 @@ setup('authenticate as admin', async ({ page }) => {
|
||||
const { apiContext, afterAction } = await getApiContext(page);
|
||||
await updateJWTTokenExpiryTime(apiContext, JWT_EXPIRY_TIME_MAP['4 hours']);
|
||||
await afterAction();
|
||||
await admin.logout(page);
|
||||
await page.waitForURL('**/signin');
|
||||
await admin.login(page);
|
||||
await page.waitForURL('**/my-data');
|
||||
|
||||
const token = await getToken(page);
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(token);
|
||||
|
||||
// End of authentication steps.
|
||||
await page.context().storageState({ path: adminFile });
|
||||
|
@ -238,29 +238,29 @@ export class ApiCollectionClass extends EntityClass {
|
||||
type: 'Teams' | 'Users' = 'Users'
|
||||
): Promise<void> {
|
||||
if (type === 'Teams') {
|
||||
await addOwner(
|
||||
await addOwner({
|
||||
page,
|
||||
owner1[0],
|
||||
owner: owner1[0],
|
||||
type,
|
||||
this.endpoint,
|
||||
'data-assets-header'
|
||||
);
|
||||
await updateOwner(
|
||||
endpoint: this.endpoint,
|
||||
dataTestId: 'data-assets-header',
|
||||
});
|
||||
await updateOwner({
|
||||
page,
|
||||
owner2[0],
|
||||
owner: owner2[0],
|
||||
type,
|
||||
this.endpoint,
|
||||
'data-assets-header'
|
||||
);
|
||||
endpoint: this.endpoint,
|
||||
dataTestId: 'data-assets-header',
|
||||
});
|
||||
await this.verifyOwnerPropagation(page, owner2[0]);
|
||||
|
||||
await removeOwner(
|
||||
await removeOwner({
|
||||
page,
|
||||
this.endpoint,
|
||||
owner2[0],
|
||||
endpoint: this.endpoint,
|
||||
ownerName: owner2[0],
|
||||
type,
|
||||
'data-assets-header'
|
||||
);
|
||||
dataTestId: 'data-assets-header',
|
||||
});
|
||||
} else {
|
||||
await addMultiOwner({
|
||||
page,
|
||||
@ -279,13 +279,13 @@ export class ApiCollectionClass extends EntityClass {
|
||||
type,
|
||||
});
|
||||
await this.verifyOwnerPropagation(page, owner2[0]);
|
||||
await removeOwner(
|
||||
await removeOwner({
|
||||
page,
|
||||
this.endpoint,
|
||||
owner2[0],
|
||||
endpoint: this.endpoint,
|
||||
ownerName: owner2[0],
|
||||
type,
|
||||
'data-assets-header'
|
||||
);
|
||||
dataTestId: 'data-assets-header',
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -38,13 +38,7 @@ export class ApiEndpointClass extends EntityClass {
|
||||
private apiEndpointName = `pw-api-endpoint-${uuid()}`;
|
||||
private fqn = `${this.service.name}.${this.apiCollection.name}.${this.apiEndpointName}`;
|
||||
|
||||
entity = {
|
||||
name: this.apiEndpointName,
|
||||
apiCollection: `${this.service.name}.${this.apiCollection.name}`,
|
||||
endpointURL: 'https://sandbox-beta.open-metadata.org/swagger.json',
|
||||
requestSchema: {
|
||||
schemaType: 'JSON',
|
||||
schemaFields: [
|
||||
children = [
|
||||
{
|
||||
name: 'default',
|
||||
dataType: 'RECORD',
|
||||
@ -86,7 +80,15 @@ export class ApiEndpointClass extends EntityClass {
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
];
|
||||
|
||||
entity = {
|
||||
name: this.apiEndpointName,
|
||||
apiCollection: `${this.service.name}.${this.apiCollection.name}`,
|
||||
endpointURL: 'https://sandbox-beta.open-metadata.org/swagger.json',
|
||||
requestSchema: {
|
||||
schemaType: 'JSON',
|
||||
schemaFields: this.children,
|
||||
},
|
||||
responseSchema: {
|
||||
schemaType: 'JSON',
|
||||
@ -143,6 +145,8 @@ export class ApiEndpointClass extends EntityClass {
|
||||
super(EntityTypeEndpoint.API_ENDPOINT);
|
||||
this.service.name = name ?? this.service.name;
|
||||
this.type = 'ApiEndpoint';
|
||||
this.childrenTabId = 'schema';
|
||||
this.childrenSelectorId = this.children[0].name;
|
||||
}
|
||||
|
||||
async create(apiContext: APIRequestContext) {
|
||||
|
@ -33,11 +33,8 @@ export class DashboardDataModelClass extends EntityClass {
|
||||
},
|
||||
},
|
||||
};
|
||||
entity = {
|
||||
name: `pw-dashboard-data-model-${uuid()}`,
|
||||
displayName: `pw-dashboard-data-model-${uuid()}`,
|
||||
service: this.service.name,
|
||||
columns: [
|
||||
|
||||
children = [
|
||||
{
|
||||
name: 'country_name',
|
||||
dataType: 'VARCHAR',
|
||||
@ -45,7 +42,13 @@ export class DashboardDataModelClass extends EntityClass {
|
||||
dataTypeDisplay: 'varchar',
|
||||
description: 'Name of the country.',
|
||||
},
|
||||
],
|
||||
];
|
||||
|
||||
entity = {
|
||||
name: `pw-dashboard-data-model-${uuid()}`,
|
||||
displayName: `pw-dashboard-data-model-${uuid()}`,
|
||||
service: this.service.name,
|
||||
columns: this.children,
|
||||
dataModelType: 'SupersetDataModel',
|
||||
};
|
||||
|
||||
@ -56,6 +59,8 @@ export class DashboardDataModelClass extends EntityClass {
|
||||
super(EntityTypeEndpoint.DataModel);
|
||||
this.service.name = name ?? this.service.name;
|
||||
this.type = 'Dashboard Data Model';
|
||||
this.childrenTabId = 'model';
|
||||
this.childrenSelectorId = this.children[0].name;
|
||||
}
|
||||
|
||||
async create(apiContext: APIRequestContext) {
|
||||
|
@ -214,29 +214,29 @@ export class DatabaseClass extends EntityClass {
|
||||
type: 'Teams' | 'Users' = 'Users'
|
||||
): Promise<void> {
|
||||
if (type === 'Teams') {
|
||||
await addOwner(
|
||||
await addOwner({
|
||||
page,
|
||||
owner1[0],
|
||||
owner: owner1[0],
|
||||
type,
|
||||
this.endpoint,
|
||||
'data-assets-header'
|
||||
);
|
||||
await updateOwner(
|
||||
endpoint: this.endpoint,
|
||||
dataTestId: 'data-assets-header',
|
||||
});
|
||||
await updateOwner({
|
||||
page,
|
||||
owner2[0],
|
||||
owner: owner2[0],
|
||||
type,
|
||||
this.endpoint,
|
||||
'data-assets-header'
|
||||
);
|
||||
endpoint: this.endpoint,
|
||||
dataTestId: 'data-assets-header',
|
||||
});
|
||||
await this.verifyOwnerPropagation(page, owner2[0]);
|
||||
|
||||
await removeOwner(
|
||||
await removeOwner({
|
||||
page,
|
||||
this.endpoint,
|
||||
owner2[0],
|
||||
endpoint: this.endpoint,
|
||||
ownerName: owner2[0],
|
||||
type,
|
||||
'data-assets-header'
|
||||
);
|
||||
dataTestId: 'data-assets-header',
|
||||
});
|
||||
} else {
|
||||
await addMultiOwner({
|
||||
page,
|
||||
@ -255,13 +255,13 @@ export class DatabaseClass extends EntityClass {
|
||||
type,
|
||||
});
|
||||
await this.verifyOwnerPropagation(page, owner2[0]);
|
||||
await removeOwner(
|
||||
await removeOwner({
|
||||
page,
|
||||
this.endpoint,
|
||||
owner2[0],
|
||||
endpoint: this.endpoint,
|
||||
ownerName: owner2[0],
|
||||
type,
|
||||
'data-assets-header'
|
||||
);
|
||||
dataTestId: 'data-assets-header',
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,9 @@ import {
|
||||
addMultiOwner,
|
||||
addOwner,
|
||||
assignGlossaryTerm,
|
||||
assignGlossaryTermToChildren,
|
||||
assignTag,
|
||||
assignTagToChildren,
|
||||
assignTier,
|
||||
createAnnouncement,
|
||||
createInactiveAnnouncement,
|
||||
@ -32,8 +34,10 @@ import {
|
||||
followEntity,
|
||||
hardDeleteEntity,
|
||||
removeGlossaryTerm,
|
||||
removeGlossaryTermFromChildren,
|
||||
removeOwner,
|
||||
removeTag,
|
||||
removeTagsFromChildren,
|
||||
removeTier,
|
||||
replyAnnouncement,
|
||||
softDeleteEntity,
|
||||
@ -50,6 +54,8 @@ import { EntityTypeEndpoint, ENTITY_PATH } from './Entity.interface';
|
||||
|
||||
export class EntityClass {
|
||||
type: string;
|
||||
childrenTabId?: string;
|
||||
childrenSelectorId?: string;
|
||||
endpoint: EntityTypeEndpoint;
|
||||
cleanupUser: (apiContext: APIRequestContext) => Promise<void>;
|
||||
|
||||
@ -120,30 +126,33 @@ export class EntityClass {
|
||||
page: Page,
|
||||
owner1: string[],
|
||||
owner2: string[],
|
||||
type: 'Teams' | 'Users' = 'Users'
|
||||
type: 'Teams' | 'Users' = 'Users',
|
||||
isEditPermission = true
|
||||
) {
|
||||
if (type === 'Teams') {
|
||||
await addOwner(
|
||||
await addOwner({
|
||||
page,
|
||||
owner1[0],
|
||||
owner: owner1[0],
|
||||
type,
|
||||
this.endpoint,
|
||||
'data-assets-header'
|
||||
);
|
||||
await updateOwner(
|
||||
endpoint: this.endpoint,
|
||||
dataTestId: 'data-assets-header',
|
||||
});
|
||||
if (isEditPermission) {
|
||||
await updateOwner({
|
||||
page,
|
||||
owner2[0],
|
||||
owner: owner2[0],
|
||||
type,
|
||||
this.endpoint,
|
||||
'data-assets-header'
|
||||
);
|
||||
await removeOwner(
|
||||
endpoint: this.endpoint,
|
||||
dataTestId: 'data-assets-header',
|
||||
});
|
||||
await removeOwner({
|
||||
page,
|
||||
this.endpoint,
|
||||
owner2[0],
|
||||
endpoint: this.endpoint,
|
||||
ownerName: owner2[0],
|
||||
type,
|
||||
'data-assets-header'
|
||||
);
|
||||
dataTestId: 'data-assets-header',
|
||||
});
|
||||
}
|
||||
} else {
|
||||
await addMultiOwner({
|
||||
page,
|
||||
@ -153,6 +162,7 @@ export class EntityClass {
|
||||
endpoint: this.endpoint,
|
||||
type,
|
||||
});
|
||||
if (isEditPermission) {
|
||||
await addMultiOwner({
|
||||
page,
|
||||
ownerNames: owner2,
|
||||
@ -161,13 +171,14 @@ export class EntityClass {
|
||||
endpoint: this.endpoint,
|
||||
type,
|
||||
});
|
||||
await removeOwner(
|
||||
await removeOwner({
|
||||
page,
|
||||
this.endpoint,
|
||||
owner2[0],
|
||||
endpoint: this.endpoint,
|
||||
ownerName: owner2[0],
|
||||
type,
|
||||
'data-assets-header'
|
||||
);
|
||||
dataTestId: 'data-assets-header',
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -197,6 +208,41 @@ export class EntityClass {
|
||||
.isVisible();
|
||||
}
|
||||
|
||||
async tagChildren({
|
||||
page,
|
||||
tag1,
|
||||
tag2,
|
||||
rowId,
|
||||
rowSelector = 'data-row-key',
|
||||
}: {
|
||||
page: Page;
|
||||
tag1: string;
|
||||
tag2: string;
|
||||
rowId: string;
|
||||
rowSelector?: string;
|
||||
}) {
|
||||
await assignTagToChildren({ page, tag: tag1, rowId, rowSelector });
|
||||
await assignTagToChildren({
|
||||
page,
|
||||
tag: tag2,
|
||||
rowId,
|
||||
rowSelector,
|
||||
action: 'Edit',
|
||||
});
|
||||
await removeTagsFromChildren({
|
||||
page,
|
||||
tags: [tag1, tag2],
|
||||
rowId,
|
||||
rowSelector,
|
||||
});
|
||||
|
||||
await page
|
||||
.locator(`[${rowSelector}="${rowId}"]`)
|
||||
.getByTestId('tags-container')
|
||||
.getByTestId('Add')
|
||||
.isVisible();
|
||||
}
|
||||
|
||||
async glossaryTerm(
|
||||
page: Page,
|
||||
glossaryTerm1: GlossaryTerm['responseData'],
|
||||
@ -213,6 +259,46 @@ export class EntityClass {
|
||||
.isVisible();
|
||||
}
|
||||
|
||||
async glossaryTermChildren({
|
||||
page,
|
||||
glossaryTerm1,
|
||||
glossaryTerm2,
|
||||
rowId,
|
||||
rowSelector = 'data-row-key',
|
||||
}: {
|
||||
page: Page;
|
||||
glossaryTerm1: GlossaryTerm['responseData'];
|
||||
glossaryTerm2: GlossaryTerm['responseData'];
|
||||
rowId: string;
|
||||
rowSelector?: string;
|
||||
}) {
|
||||
await assignGlossaryTermToChildren({
|
||||
page,
|
||||
glossaryTerm: glossaryTerm1,
|
||||
rowId,
|
||||
rowSelector,
|
||||
});
|
||||
await assignGlossaryTermToChildren({
|
||||
page,
|
||||
glossaryTerm: glossaryTerm2,
|
||||
rowId,
|
||||
rowSelector,
|
||||
action: 'Edit',
|
||||
});
|
||||
await removeGlossaryTermFromChildren({
|
||||
page,
|
||||
glossaryTerms: [glossaryTerm1, glossaryTerm2],
|
||||
rowId,
|
||||
rowSelector,
|
||||
});
|
||||
|
||||
await page
|
||||
.locator(`[${rowSelector}="${rowId}"]`)
|
||||
.getByTestId('glossary-container')
|
||||
.getByTestId('Add')
|
||||
.isVisible();
|
||||
}
|
||||
|
||||
async upVote(page: Page) {
|
||||
await upVote(page, this.endpoint);
|
||||
}
|
||||
@ -267,26 +353,6 @@ export class EntityClass {
|
||||
await hardDeleteEntity(page, displayName ?? entityName, this.endpoint);
|
||||
}
|
||||
|
||||
async setCustomProperty(
|
||||
page: Page,
|
||||
propertydetails: CustomProperty,
|
||||
value: string
|
||||
) {
|
||||
await setValueForProperty({
|
||||
page,
|
||||
propertyName: propertydetails.name,
|
||||
value,
|
||||
propertyType: propertydetails.propertyType.name,
|
||||
endpoint: this.endpoint,
|
||||
});
|
||||
await validateValueForProperty({
|
||||
page,
|
||||
propertyName: propertydetails.name,
|
||||
value,
|
||||
propertyType: propertydetails.propertyType.name,
|
||||
});
|
||||
}
|
||||
|
||||
async updateCustomProperty(
|
||||
page: Page,
|
||||
propertydetails: CustomProperty,
|
||||
|
@ -38,18 +38,21 @@ export class MlModelClass extends EntityClass {
|
||||
},
|
||||
},
|
||||
};
|
||||
entity = {
|
||||
name: `pw-mlmodel-${uuid()}`,
|
||||
displayName: `pw-mlmodel-${uuid()}`,
|
||||
service: this.service.name,
|
||||
algorithm: 'Time Series',
|
||||
mlFeatures: [
|
||||
|
||||
children = [
|
||||
{
|
||||
name: 'sales',
|
||||
dataType: 'numerical',
|
||||
description: 'Sales amount',
|
||||
},
|
||||
],
|
||||
];
|
||||
|
||||
entity = {
|
||||
name: `pw-mlmodel-${uuid()}`,
|
||||
displayName: `pw-mlmodel-${uuid()}`,
|
||||
service: this.service.name,
|
||||
algorithm: 'Time Series',
|
||||
mlFeatures: this.children,
|
||||
};
|
||||
|
||||
serviceResponseData: ResponseDataType;
|
||||
@ -59,6 +62,8 @@ export class MlModelClass extends EntityClass {
|
||||
super(EntityTypeEndpoint.MlModel);
|
||||
this.service.name = name ?? this.service.name;
|
||||
this.type = 'MlModel';
|
||||
this.childrenTabId = 'features';
|
||||
this.childrenSelectorId = `feature-card-${this.children[0].name}`;
|
||||
}
|
||||
|
||||
async create(apiContext: APIRequestContext) {
|
||||
|
@ -30,11 +30,14 @@ export class PipelineClass extends EntityClass {
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
children = [{ name: 'snowflake_task' }];
|
||||
|
||||
entity = {
|
||||
name: `pw-pipeline-${uuid()}`,
|
||||
displayName: `pw-pipeline-${uuid()}`,
|
||||
service: this.service.name,
|
||||
tasks: [{ name: 'snowflake_task' }],
|
||||
tasks: this.children,
|
||||
};
|
||||
|
||||
serviceResponseData: unknown;
|
||||
@ -44,6 +47,8 @@ export class PipelineClass extends EntityClass {
|
||||
super(EntityTypeEndpoint.Pipeline);
|
||||
this.service.name = name ?? this.service.name;
|
||||
this.type = 'Pipeline';
|
||||
this.childrenTabId = 'tasks';
|
||||
this.childrenSelectorId = this.children[0].name;
|
||||
}
|
||||
|
||||
async create(apiContext: APIRequestContext) {
|
||||
|
@ -33,11 +33,59 @@ export class SearchIndexClass extends EntityClass {
|
||||
},
|
||||
},
|
||||
};
|
||||
private searchIndexName = `pw-search-index-${uuid()}`;
|
||||
private fqn = `${this.service.name}.${this.searchIndexName}`;
|
||||
|
||||
children = [
|
||||
{
|
||||
name: 'name',
|
||||
dataType: 'TEXT',
|
||||
dataTypeDisplay: 'text',
|
||||
description: 'Table Entity Name.',
|
||||
fullyQualifiedName: `${this.fqn}.name`,
|
||||
tags: [],
|
||||
},
|
||||
{
|
||||
name: 'description',
|
||||
dataType: 'TEXT',
|
||||
dataTypeDisplay: 'text',
|
||||
description: 'Table Entity Description.',
|
||||
fullyQualifiedName: `${this.fqn}.description`,
|
||||
tags: [],
|
||||
},
|
||||
{
|
||||
name: 'columns',
|
||||
dataType: 'NESTED',
|
||||
dataTypeDisplay: 'nested',
|
||||
description: 'Table Columns.',
|
||||
fullyQualifiedName: `${this.fqn}.columns`,
|
||||
tags: [],
|
||||
children: [
|
||||
{
|
||||
name: 'name',
|
||||
dataType: 'TEXT',
|
||||
dataTypeDisplay: 'text',
|
||||
description: 'Column Name.',
|
||||
fullyQualifiedName: `${this.fqn}.columns.name`,
|
||||
tags: [],
|
||||
},
|
||||
{
|
||||
name: 'description',
|
||||
dataType: 'TEXT',
|
||||
dataTypeDisplay: 'text',
|
||||
description: 'Column Description.',
|
||||
fullyQualifiedName: `${this.fqn}.columns.description`,
|
||||
tags: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
entity = {
|
||||
name: `pw-search-index-${uuid()}`,
|
||||
name: this.searchIndexName,
|
||||
displayName: `pw-search-index-${uuid()}`,
|
||||
service: this.service.name,
|
||||
fields: [],
|
||||
fields: this.children,
|
||||
};
|
||||
|
||||
serviceResponseData: unknown;
|
||||
@ -47,6 +95,8 @@ export class SearchIndexClass extends EntityClass {
|
||||
super(EntityTypeEndpoint.SearchIndex);
|
||||
this.service.name = name ?? this.service.name;
|
||||
this.type = 'SearchIndex';
|
||||
this.childrenTabId = 'fields';
|
||||
this.childrenSelectorId = this.children[0].fullyQualifiedName;
|
||||
}
|
||||
|
||||
async create(apiContext: APIRequestContext) {
|
||||
|
@ -44,10 +44,7 @@ export class TableClass extends EntityClass {
|
||||
name: `pw-database-schema-${uuid()}`,
|
||||
database: `${this.service.name}.${this.database.name}`,
|
||||
};
|
||||
entity = {
|
||||
name: `pw-table-${uuid()}`,
|
||||
description: 'description',
|
||||
columns: [
|
||||
children = [
|
||||
{
|
||||
name: 'user_id',
|
||||
dataType: 'NUMERIC',
|
||||
@ -91,7 +88,12 @@ export class TableClass extends EntityClass {
|
||||
dataTypeDisplay: 'varchar',
|
||||
description: 'Email address of the staff member.',
|
||||
},
|
||||
],
|
||||
];
|
||||
|
||||
entity = {
|
||||
name: `pw-table-${uuid()}`,
|
||||
description: 'description',
|
||||
columns: this.children,
|
||||
databaseSchema: `${this.service.name}.${this.database.name}.${this.schema.name}`,
|
||||
};
|
||||
|
||||
@ -107,6 +109,8 @@ export class TableClass extends EntityClass {
|
||||
super(EntityTypeEndpoint.Table);
|
||||
this.service.name = name ?? this.service.name;
|
||||
this.type = 'Table';
|
||||
this.childrenTabId = 'schema';
|
||||
this.childrenSelectorId = `${this.entity.databaseSchema}.${this.entity.name}.${this.children[0].name}`;
|
||||
}
|
||||
|
||||
async create(apiContext: APIRequestContext) {
|
||||
|
@ -33,14 +33,8 @@ export class TopicClass extends EntityClass {
|
||||
};
|
||||
private topicName = `pw-topic-${uuid()}`;
|
||||
private fqn = `${this.service.name}.${this.topicName}`;
|
||||
entity = {
|
||||
name: this.topicName,
|
||||
service: this.service.name,
|
||||
messageSchema: {
|
||||
schemaText: `{"type":"object","required":["name","age","club_name"],"properties":{"name":{"type":"object","required":["first_name","last_name"],
|
||||
"properties":{"first_name":{"type":"string"},"last_name":{"type":"string"}}},"age":{"type":"integer"},"club_name":{"type":"string"}}}`,
|
||||
schemaType: 'JSON',
|
||||
schemaFields: [
|
||||
|
||||
children = [
|
||||
{
|
||||
name: 'default',
|
||||
dataType: 'RECORD',
|
||||
@ -82,7 +76,16 @@ export class TopicClass extends EntityClass {
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
];
|
||||
|
||||
entity = {
|
||||
name: this.topicName,
|
||||
service: this.service.name,
|
||||
messageSchema: {
|
||||
schemaText: `{"type":"object","required":["name","age","club_name"],"properties":{"name":{"type":"object","required":["first_name","last_name"],
|
||||
"properties":{"first_name":{"type":"string"},"last_name":{"type":"string"}}},"age":{"type":"integer"},"club_name":{"type":"string"}}}`,
|
||||
schemaType: 'JSON',
|
||||
schemaFields: this.children,
|
||||
},
|
||||
partitions: 128,
|
||||
};
|
||||
@ -94,6 +97,8 @@ export class TopicClass extends EntityClass {
|
||||
super(EntityTypeEndpoint.Topic);
|
||||
this.service.name = name ?? this.service.name;
|
||||
this.type = 'Topic';
|
||||
this.childrenTabId = 'schema';
|
||||
this.childrenSelectorId = this.children[0].name;
|
||||
}
|
||||
|
||||
async create(apiContext: APIRequestContext) {
|
||||
|
@ -141,13 +141,14 @@ export const assignDomain = async (
|
||||
) => {
|
||||
await page.getByTestId('add-domain').click();
|
||||
await page.waitForSelector('[data-testid="loader"]', { state: 'detached' });
|
||||
const searchDomain = page.waitForResponse(
|
||||
`/api/v1/search/query?q=*${encodeURIComponent(domain.name)}*`
|
||||
);
|
||||
await page
|
||||
.getByTestId('selectable-list')
|
||||
.getByTestId('searchbar')
|
||||
.fill(domain.name);
|
||||
await page.waitForResponse(
|
||||
`/api/v1/search/query?q=*${encodeURIComponent(domain.name)}*`
|
||||
);
|
||||
await searchDomain;
|
||||
await page.getByRole('listitem', { name: domain.displayName }).click();
|
||||
|
||||
await expect(page.getByTestId('domain-link')).toContainText(
|
||||
@ -162,13 +163,14 @@ export const updateDomain = async (
|
||||
await page.getByTestId('add-domain').click();
|
||||
await page.waitForSelector('[data-testid="loader"]', { state: 'detached' });
|
||||
await page.getByTestId('selectable-list').getByTestId('searchbar').clear();
|
||||
const searchDomain = page.waitForResponse(
|
||||
`/api/v1/search/query?q=*${encodeURIComponent(domain.name)}*`
|
||||
);
|
||||
await page
|
||||
.getByTestId('selectable-list')
|
||||
.getByTestId('searchbar')
|
||||
.fill(domain.name);
|
||||
await page.waitForResponse(
|
||||
`/api/v1/search/query?q=*${encodeURIComponent(domain.name)}*`
|
||||
);
|
||||
await searchDomain;
|
||||
await page.getByRole('listitem', { name: domain.displayName }).click();
|
||||
|
||||
await expect(page.getByTestId('domain-link')).toContainText(
|
||||
|
@ -185,8 +185,9 @@ export const setValueForProperty = async (data: {
|
||||
)}*`;
|
||||
await page.route(searchApi, (route) => route.continue());
|
||||
await page.locator('#entityReference').clear();
|
||||
const searchEntity = page.waitForResponse(searchApi);
|
||||
await page.locator('#entityReference').fill(val);
|
||||
await page.waitForResponse(searchApi);
|
||||
await searchEntity;
|
||||
await page.locator(`[data-testid="${val}"]`).click();
|
||||
}
|
||||
|
||||
|
@ -32,13 +32,14 @@ import { addOwner } from './entity';
|
||||
export const assignDomain = async (page: Page, domain: Domain['data']) => {
|
||||
await page.getByTestId('add-domain').click();
|
||||
await page.waitForSelector('[data-testid="loader"]', { state: 'detached' });
|
||||
const searchDomain = page.waitForResponse(
|
||||
`/api/v1/search/query?q=*${encodeURIComponent(domain.name)}*`
|
||||
);
|
||||
await page
|
||||
.getByTestId('selectable-list')
|
||||
.getByTestId('searchbar')
|
||||
.fill(domain.name);
|
||||
await page.waitForResponse(
|
||||
`/api/v1/search/query?q=*${encodeURIComponent(domain.name)}*`
|
||||
);
|
||||
await searchDomain;
|
||||
await page.getByRole('listitem', { name: domain.displayName }).click();
|
||||
|
||||
await expect(page.getByTestId('domain-link')).toContainText(
|
||||
@ -50,13 +51,14 @@ export const updateDomain = async (page: Page, domain: Domain['data']) => {
|
||||
await page.getByTestId('add-domain').click();
|
||||
await page.waitForSelector('[data-testid="loader"]', { state: 'detached' });
|
||||
await page.getByTestId('selectable-list').getByTestId('searchbar').clear();
|
||||
const searchDomain = page.waitForResponse(
|
||||
`/api/v1/search/query?q=*${encodeURIComponent(domain.name)}*`
|
||||
);
|
||||
await page
|
||||
.getByTestId('selectable-list')
|
||||
.getByTestId('searchbar')
|
||||
.fill(domain.name);
|
||||
await page.waitForResponse(
|
||||
`/api/v1/search/query?q=*${encodeURIComponent(domain.name)}*`
|
||||
);
|
||||
await searchDomain;
|
||||
await page.getByRole('listitem', { name: domain.displayName }).click();
|
||||
|
||||
await expect(page.getByTestId('domain-link')).toContainText(
|
||||
@ -150,14 +152,14 @@ const fillCommonFormItems = async (
|
||||
await page.locator('[data-testid="display-name"]').fill(entity.displayName);
|
||||
await page.fill(descriptionBox, entity.description);
|
||||
if (!isEmpty(entity.owners) && !isUndefined(entity.owners)) {
|
||||
await addOwner(
|
||||
await addOwner({
|
||||
page,
|
||||
entity.owners[0].name,
|
||||
entity.owners[0].type as 'Users' | 'Teams',
|
||||
EntityTypeEndpoint.Domain,
|
||||
'owner-container',
|
||||
'add-owner'
|
||||
);
|
||||
owner: entity.owners[0].name,
|
||||
type: entity.owners[0].type as 'Users' | 'Teams',
|
||||
endpoint: EntityTypeEndpoint.Domain,
|
||||
dataTestId: 'owner-container',
|
||||
initiatorId: 'add-owner',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -41,14 +41,21 @@ export const visitEntityPage = async (data: {
|
||||
await page.getByTestId('searchBox').clear();
|
||||
};
|
||||
|
||||
export const addOwner = async (
|
||||
page: Page,
|
||||
owner: string,
|
||||
type: 'Teams' | 'Users' = 'Users',
|
||||
endpoint: EntityTypeEndpoint,
|
||||
dataTestId?: string,
|
||||
initiatorId = 'edit-owner'
|
||||
) => {
|
||||
export const addOwner = async ({
|
||||
page,
|
||||
owner,
|
||||
endpoint,
|
||||
type = 'Users',
|
||||
dataTestId,
|
||||
initiatorId = 'edit-owner',
|
||||
}: {
|
||||
page: Page;
|
||||
owner: string;
|
||||
endpoint: EntityTypeEndpoint;
|
||||
type?: 'Teams' | 'Users';
|
||||
dataTestId?: string;
|
||||
initiatorId?: string;
|
||||
}) => {
|
||||
await page.getByTestId(initiatorId).click();
|
||||
if (type === 'Users') {
|
||||
const userListResponse = page.waitForResponse(
|
||||
@ -67,12 +74,13 @@ export const addOwner = async (
|
||||
await page.getByRole('tab', { name: type }).click();
|
||||
}
|
||||
|
||||
const searchUser = page.waitForResponse(
|
||||
`/api/v1/search/query?q=*${encodeURIComponent(owner)}*`
|
||||
);
|
||||
await page
|
||||
.getByTestId(`owner-select-${lowerCase(type)}-search-bar`)
|
||||
.fill(owner);
|
||||
await page.waitForResponse(
|
||||
`/api/v1/search/query?q=*${encodeURIComponent(owner)}*`
|
||||
);
|
||||
await searchUser;
|
||||
|
||||
if (type === 'Teams') {
|
||||
const patchRequest = page.waitForResponse(`/api/v1/${endpoint}/*`);
|
||||
@ -91,22 +99,30 @@ export const addOwner = async (
|
||||
);
|
||||
};
|
||||
|
||||
export const updateOwner = async (
|
||||
page: Page,
|
||||
owner: string,
|
||||
type: 'Teams' | 'Users' = 'Users',
|
||||
endpoint: EntityTypeEndpoint,
|
||||
dataTestId?: string
|
||||
) => {
|
||||
export const updateOwner = async ({
|
||||
page,
|
||||
owner,
|
||||
endpoint,
|
||||
type = 'Users',
|
||||
dataTestId,
|
||||
}: {
|
||||
page: Page;
|
||||
owner: string;
|
||||
endpoint: EntityTypeEndpoint;
|
||||
type?: 'Teams' | 'Users';
|
||||
dataTestId?: string;
|
||||
}) => {
|
||||
await page.getByTestId('edit-owner').click();
|
||||
await page.getByRole('tab', { name: type }).click();
|
||||
await page.waitForSelector('[data-testid="loader"]', { state: 'detached' });
|
||||
|
||||
const searchUser = page.waitForResponse(
|
||||
`/api/v1/search/query?q=*${encodeURIComponent(owner)}*`
|
||||
);
|
||||
await page
|
||||
.getByTestId(`owner-select-${lowerCase(type)}-search-bar`)
|
||||
.fill(owner);
|
||||
await page.waitForResponse(
|
||||
`/api/v1/search/query?q=*${encodeURIComponent(owner)}*`
|
||||
);
|
||||
await searchUser;
|
||||
|
||||
if (type === 'Teams') {
|
||||
const patchRequest = page.waitForResponse(`/api/v1/${endpoint}/*`);
|
||||
@ -125,13 +141,19 @@ export const updateOwner = async (
|
||||
);
|
||||
};
|
||||
|
||||
export const removeOwner = async (
|
||||
page: Page,
|
||||
endpoint: EntityTypeEndpoint,
|
||||
ownerName: string,
|
||||
type: 'Teams' | 'Users' = 'Users',
|
||||
dataTestId?: string
|
||||
) => {
|
||||
export const removeOwner = async ({
|
||||
page,
|
||||
endpoint,
|
||||
ownerName,
|
||||
type = 'Users',
|
||||
dataTestId,
|
||||
}: {
|
||||
page: Page;
|
||||
endpoint: EntityTypeEndpoint;
|
||||
ownerName: string;
|
||||
type?: 'Teams' | 'Users';
|
||||
dataTestId?: string;
|
||||
}) => {
|
||||
await page.getByTestId('edit-owner').click();
|
||||
await page.waitForSelector('[data-testid="loader"]', { state: 'detached' });
|
||||
|
||||
@ -280,10 +302,11 @@ export const assignTag = async (
|
||||
.getByTestId(action === 'Add' ? 'add-tag' : 'edit-button')
|
||||
.click();
|
||||
|
||||
await page.locator('#tagsForm_tags').fill(tag);
|
||||
await page.waitForResponse(
|
||||
const searchTags = page.waitForResponse(
|
||||
`/api/v1/search/query?q=*${encodeURIComponent(tag)}*`
|
||||
);
|
||||
await page.locator('#tagsForm_tags').fill(tag);
|
||||
await searchTags;
|
||||
await page.getByTestId(`tag-${tag}`).click();
|
||||
|
||||
await expect(page.getByTestId('saveAssociatedTag')).toBeEnabled();
|
||||
@ -298,6 +321,53 @@ export const assignTag = async (
|
||||
).toBeVisible();
|
||||
};
|
||||
|
||||
export const assignTagToChildren = async ({
|
||||
page,
|
||||
tag,
|
||||
rowId,
|
||||
action = 'Add',
|
||||
rowSelector = 'data-row-key',
|
||||
}: {
|
||||
page: Page;
|
||||
tag: string;
|
||||
rowId: string;
|
||||
action?: 'Add' | 'Edit';
|
||||
rowSelector?: string;
|
||||
}) => {
|
||||
await page
|
||||
.locator(`[${rowSelector}="${rowId}"]`)
|
||||
.getByTestId('tags-container')
|
||||
.getByTestId(action === 'Add' ? 'add-tag' : 'edit-button')
|
||||
.click();
|
||||
|
||||
const searchTags = page.waitForResponse(
|
||||
`/api/v1/search/query?q=*${encodeURIComponent(tag)}*`
|
||||
);
|
||||
|
||||
await page.locator('#tagsForm_tags').fill(tag);
|
||||
|
||||
await searchTags;
|
||||
|
||||
await page.getByTestId(`tag-${tag}`).click();
|
||||
|
||||
const patchRequest = page.waitForResponse(
|
||||
(response) => response.request().method() === 'PATCH'
|
||||
);
|
||||
|
||||
await expect(page.getByTestId('saveAssociatedTag')).toBeEnabled();
|
||||
|
||||
await page.getByTestId('saveAssociatedTag').click();
|
||||
|
||||
await patchRequest;
|
||||
|
||||
await expect(
|
||||
page
|
||||
.locator(`[${rowSelector}="${rowId}"]`)
|
||||
.getByTestId('tags-container')
|
||||
.getByTestId(`tag-${tag}`)
|
||||
).toBeVisible();
|
||||
};
|
||||
|
||||
export const removeTag = async (page: Page, tags: string[]) => {
|
||||
for (const tag of tags) {
|
||||
await page
|
||||
@ -312,8 +382,8 @@ export const removeTag = async (page: Page, tags: string[]) => {
|
||||
.locator('svg')
|
||||
.click();
|
||||
|
||||
const patchRequest = page.waitForRequest(
|
||||
(request) => request.method() === 'PATCH'
|
||||
const patchRequest = page.waitForResponse(
|
||||
(response) => response.request().method() === 'PATCH'
|
||||
);
|
||||
|
||||
await expect(page.getByTestId('saveAssociatedTag')).toBeEnabled();
|
||||
@ -330,6 +400,49 @@ export const removeTag = async (page: Page, tags: string[]) => {
|
||||
}
|
||||
};
|
||||
|
||||
export const removeTagsFromChildren = async ({
|
||||
page,
|
||||
rowId,
|
||||
tags,
|
||||
rowSelector = 'data-row-key',
|
||||
}: {
|
||||
page: Page;
|
||||
tags: string[];
|
||||
rowId: string;
|
||||
rowSelector?: string;
|
||||
}) => {
|
||||
for (const tag of tags) {
|
||||
await page
|
||||
.locator(`[${rowSelector}="${rowId}"]`)
|
||||
.getByTestId('tags-container')
|
||||
.getByTestId('edit-button')
|
||||
.click();
|
||||
|
||||
await page
|
||||
.getByTestId('tag-selector')
|
||||
.getByTestId(`selected-tag-${tag}`)
|
||||
.getByTestId('remove-tags')
|
||||
.click();
|
||||
|
||||
const patchTagRequest = page.waitForResponse(
|
||||
(response) => response.request().method() === 'PATCH'
|
||||
);
|
||||
|
||||
await expect(page.getByTestId('saveAssociatedTag')).toBeEnabled();
|
||||
|
||||
await page.getByTestId('saveAssociatedTag').click();
|
||||
|
||||
await patchTagRequest;
|
||||
|
||||
await expect(
|
||||
page
|
||||
.locator(`[${rowSelector}="${rowId}"]`)
|
||||
.getByTestId('tags-container')
|
||||
.getByTestId(`tag-${tag}`)
|
||||
).not.toBeVisible();
|
||||
}
|
||||
};
|
||||
|
||||
type GlossaryTermOption = {
|
||||
displayName: string;
|
||||
name: string;
|
||||
@ -347,10 +460,12 @@ export const assignGlossaryTerm = async (
|
||||
.getByTestId(action === 'Add' ? 'add-tag' : 'edit-button')
|
||||
.click();
|
||||
|
||||
await page.locator('#tagsForm_tags').fill(glossaryTerm.displayName);
|
||||
await page.waitForResponse(
|
||||
const searchGlossaryTerm = page.waitForResponse(
|
||||
`/api/v1/search/query?q=*${encodeURIComponent(glossaryTerm.displayName)}*`
|
||||
);
|
||||
|
||||
await page.locator('#tagsForm_tags').fill(glossaryTerm.displayName);
|
||||
await searchGlossaryTerm;
|
||||
await page.getByTestId(`tag-${glossaryTerm.fullyQualifiedName}`).click();
|
||||
|
||||
await expect(page.getByTestId('saveAssociatedTag')).toBeEnabled();
|
||||
@ -365,6 +480,50 @@ export const assignGlossaryTerm = async (
|
||||
).toBeVisible();
|
||||
};
|
||||
|
||||
export const assignGlossaryTermToChildren = async ({
|
||||
page,
|
||||
glossaryTerm,
|
||||
action = 'Add',
|
||||
rowId,
|
||||
rowSelector = 'data-row-key',
|
||||
}: {
|
||||
page: Page;
|
||||
glossaryTerm: GlossaryTermOption;
|
||||
rowId: string;
|
||||
action?: 'Add' | 'Edit';
|
||||
rowSelector?: string;
|
||||
}) => {
|
||||
await page
|
||||
.locator(`[${rowSelector}="${rowId}"]`)
|
||||
.getByTestId('glossary-container')
|
||||
.getByTestId(action === 'Add' ? 'add-tag' : 'edit-button')
|
||||
.click();
|
||||
|
||||
const searchGlossaryTerm = page.waitForResponse(
|
||||
`/api/v1/search/query?q=*${encodeURIComponent(glossaryTerm.displayName)}*`
|
||||
);
|
||||
await page.locator('#tagsForm_tags').fill(glossaryTerm.displayName);
|
||||
await searchGlossaryTerm;
|
||||
await page.getByTestId(`tag-${glossaryTerm.fullyQualifiedName}`).click();
|
||||
|
||||
const patchRequest = page.waitForResponse(
|
||||
(response) => response.request().method() === 'PATCH'
|
||||
);
|
||||
|
||||
await expect(page.getByTestId('saveAssociatedTag')).toBeEnabled();
|
||||
|
||||
await page.getByTestId('saveAssociatedTag').click();
|
||||
|
||||
await patchRequest;
|
||||
|
||||
await expect(
|
||||
page
|
||||
.locator(`[${rowSelector}="${rowId}"]`)
|
||||
.getByTestId('glossary-container')
|
||||
.getByTestId(`tag-${glossaryTerm.fullyQualifiedName}`)
|
||||
).toBeVisible();
|
||||
};
|
||||
|
||||
export const removeGlossaryTerm = async (
|
||||
page: Page,
|
||||
glossaryTerms: GlossaryTermOption[]
|
||||
@ -383,8 +542,8 @@ export const removeGlossaryTerm = async (
|
||||
.locator('svg')
|
||||
.click();
|
||||
|
||||
const patchRequest = page.waitForRequest(
|
||||
(request) => request.method() === 'PATCH'
|
||||
const patchRequest = page.waitForResponse(
|
||||
(response) => response.request().method() === 'PATCH'
|
||||
);
|
||||
|
||||
await expect(page.getByTestId('saveAssociatedTag')).toBeEnabled();
|
||||
@ -401,16 +560,63 @@ export const removeGlossaryTerm = async (
|
||||
}
|
||||
};
|
||||
|
||||
export const removeGlossaryTermFromChildren = async ({
|
||||
page,
|
||||
glossaryTerms,
|
||||
rowId,
|
||||
rowSelector = 'data-row-key',
|
||||
}: {
|
||||
page: Page;
|
||||
glossaryTerms: GlossaryTermOption[];
|
||||
rowId: string;
|
||||
rowSelector?: string;
|
||||
}) => {
|
||||
for (const tag of glossaryTerms) {
|
||||
await page
|
||||
.locator(`[${rowSelector}="${rowId}"]`)
|
||||
.getByTestId('glossary-container')
|
||||
.getByTestId('edit-button')
|
||||
.click();
|
||||
|
||||
await page
|
||||
.getByTestId('glossary-container')
|
||||
.getByTestId(new RegExp(tag.name))
|
||||
.getByTestId('remove-tags')
|
||||
.locator('svg')
|
||||
.click();
|
||||
|
||||
const patchRequest = page.waitForResponse(
|
||||
(response) => response.request().method() === 'PATCH'
|
||||
);
|
||||
|
||||
await expect(page.getByTestId('saveAssociatedTag')).toBeEnabled();
|
||||
|
||||
await page.getByTestId('saveAssociatedTag').click();
|
||||
|
||||
await patchRequest;
|
||||
|
||||
expect(
|
||||
page
|
||||
.locator(`[${rowSelector}="${rowId}"]`)
|
||||
.getByTestId('glossary-container')
|
||||
.getByTestId(`tag-${tag.fullyQualifiedName}`)
|
||||
).not.toBeVisible();
|
||||
}
|
||||
};
|
||||
|
||||
export const upVote = async (page: Page, endPoint: string) => {
|
||||
const patchRequest = page.waitForResponse(`/api/v1/${endPoint}/*/vote`);
|
||||
|
||||
await page.getByTestId('up-vote-btn').click();
|
||||
await page.waitForResponse(`/api/v1/${endPoint}/*/vote`);
|
||||
await patchRequest;
|
||||
|
||||
await expect(page.getByTestId('up-vote-count')).toContainText('1');
|
||||
};
|
||||
|
||||
export const downVote = async (page: Page, endPoint: string) => {
|
||||
const patchRequest = page.waitForResponse(`/api/v1/${endPoint}/*/vote`);
|
||||
await page.getByTestId('down-vote-btn').click();
|
||||
await page.waitForResponse(`/api/v1/${endPoint}/*/vote`);
|
||||
await patchRequest;
|
||||
|
||||
await expect(page.getByTestId('down-vote-count')).toContainText('1');
|
||||
};
|
||||
@ -484,8 +690,11 @@ const announcementForm = async (
|
||||
);
|
||||
|
||||
await page.locator('#announcement-submit').scrollIntoViewIfNeeded();
|
||||
const announcementSubmit = page.waitForResponse(
|
||||
'/api/v1/feed?entityLink=*type=Announcement*'
|
||||
);
|
||||
await page.click('#announcement-submit');
|
||||
await page.waitForResponse('/api/v1/feed?entityLink=*type=Announcement*');
|
||||
await announcementSubmit;
|
||||
await page.click('.Toastify__close-button');
|
||||
};
|
||||
|
||||
|
@ -73,7 +73,7 @@ export const selectActiveGlossaryTerm = async (
|
||||
export const goToAssetsTab = async (
|
||||
page: Page,
|
||||
displayName: string,
|
||||
count = '0'
|
||||
count = 0
|
||||
) => {
|
||||
await selectActiveGlossaryTerm(page, displayName);
|
||||
await page.getByTestId('assets').click();
|
||||
@ -81,7 +81,7 @@ export const goToAssetsTab = async (
|
||||
|
||||
await expect(
|
||||
page.getByTestId('assets').getByTestId('filter-count')
|
||||
).toContainText(count);
|
||||
).toContainText(`${count}`);
|
||||
};
|
||||
|
||||
export const removeReviewer = async (
|
||||
@ -190,10 +190,16 @@ export const addTeamAsReviewer = async (
|
||||
await page.fill('[data-testid="owner-select-teams-search-bar"]', teamName);
|
||||
await teamsSearchResponse;
|
||||
|
||||
await page.click(`.ant-popover [title="${teamName}"]`);
|
||||
const ownerItem = page.locator(`.ant-popover [title="${teamName}"]`);
|
||||
|
||||
if (!isSelectableInsideForm) {
|
||||
await page.waitForRequest((request) => request.method() === 'PATCH');
|
||||
if (isSelectableInsideForm) {
|
||||
await ownerItem.click();
|
||||
} else {
|
||||
const patchRequest = page.waitForRequest(
|
||||
(request) => request.method() === 'PATCH'
|
||||
);
|
||||
await ownerItem.click();
|
||||
await patchRequest;
|
||||
}
|
||||
|
||||
await expect(
|
||||
@ -261,6 +267,7 @@ export const createGlossary = async (
|
||||
resultTestId: 'reviewers-container',
|
||||
endpoint: EntityTypeEndpoint.Glossary,
|
||||
isSelectableInsideForm: true,
|
||||
type: 'Users',
|
||||
});
|
||||
} else {
|
||||
await addTeamAsReviewer(
|
||||
@ -349,16 +356,18 @@ export const deleteGlossary = async (page: Page, glossary: GlossaryData) => {
|
||||
|
||||
await page.fill('[data-testid="confirmation-text-input"]', 'DELETE');
|
||||
|
||||
await page.click('[data-testid="confirm-button"]');
|
||||
|
||||
// Wait for the API response and verify the status code
|
||||
await page.waitForResponse(
|
||||
const deleteGlossary = page.waitForResponse(
|
||||
(response) =>
|
||||
response.url().includes('/api/v1/glossaries/') &&
|
||||
response.request().method() === 'DELETE' &&
|
||||
response.status() === 200
|
||||
);
|
||||
|
||||
await page.click('[data-testid="confirm-button"]');
|
||||
|
||||
// Wait for the API response and verify the status code
|
||||
await deleteGlossary;
|
||||
|
||||
// Display toast notification
|
||||
await expect(page.locator('.toast-notification')).toHaveText(
|
||||
'"Glossary" deleted successfully!'
|
||||
@ -450,6 +459,7 @@ export const fillGlossaryTermDetails = async (
|
||||
resultTestId: 'owner-container',
|
||||
endpoint: EntityTypeEndpoint.GlossaryTerm,
|
||||
isSelectableInsideForm: true,
|
||||
type: 'Users',
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -619,11 +629,7 @@ export const verifyGlossaryTermAssets = async (
|
||||
await redirectToHomePage(page);
|
||||
await sidebarClick(page, SidebarItem.GLOSSARY);
|
||||
await selectActiveGlossary(page, glossary.displayName);
|
||||
await goToAssetsTab(
|
||||
page,
|
||||
glossaryTermData.displayName,
|
||||
assetsLength.toString()
|
||||
);
|
||||
await goToAssetsTab(page, glossaryTermData.displayName, assetsLength);
|
||||
};
|
||||
|
||||
export const renameGlossaryTerm = async (
|
||||
|
@ -317,7 +317,7 @@ const DataModelDetails = ({
|
||||
label: (
|
||||
<TabsLabel
|
||||
data-testid={EntityTabs.MODEL}
|
||||
id={EntityTabs.DETAILS}
|
||||
id={EntityTabs.MODEL}
|
||||
name={t('label.model')}
|
||||
/>
|
||||
),
|
||||
|
@ -315,6 +315,9 @@ function AlertDetailsPage({
|
||||
<Button
|
||||
className="flex flex-center"
|
||||
data-testid="edit-button"
|
||||
disabled={
|
||||
alertDetails?.provider === ProviderType.System
|
||||
}
|
||||
icon={<EditIcon height={16} width={16} />}
|
||||
/>
|
||||
</Tooltip>
|
||||
|
@ -191,6 +191,7 @@ const NotificationListPage = () => {
|
||||
<Button
|
||||
className="flex flex-center"
|
||||
data-testid={`alert-edit-${record.name}`}
|
||||
disabled={record.provider === ProviderType.System}
|
||||
icon={<EditIcon height={16} />}
|
||||
type="text"
|
||||
/>
|
||||
|
Loading…
x
Reference in New Issue
Block a user