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:
Aniket Katkar 2024-08-13 18:43:03 +05:30 committed by GitHub
parent ac2264e9c7
commit d77aac425f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
26 changed files with 1109 additions and 326 deletions

View File

@ -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'],

View File

@ -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

View File

@ -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();
});
});
});

View File

@ -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();
});
});
});

View File

@ -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,

View File

@ -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();

View File

@ -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

View File

@ -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 });

View File

@ -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',
});
}
}
}

View File

@ -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) {

View File

@ -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) {

View File

@ -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',
});
}
}
}

View File

@ -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,

View File

@ -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) {

View File

@ -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) {

View File

@ -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) {

View File

@ -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) {

View File

@ -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) {

View File

@ -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(

View File

@ -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();
}

View File

@ -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',
});
}
};

View File

@ -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');
};

View File

@ -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 (

View File

@ -317,7 +317,7 @@ const DataModelDetails = ({
label: (
<TabsLabel
data-testid={EntityTabs.MODEL}
id={EntityTabs.DETAILS}
id={EntityTabs.MODEL}
name={t('label.model')}
/>
),

View File

@ -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>

View File

@ -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"
/>