mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-11-02 11:39:12 +00:00
playwright: migrate entity, database and service spec (#16584)
* playwright: migrating entity spec * added test for customProperty * added custom property test * add boolean for slow test * add comment
This commit is contained in:
parent
0b205faefc
commit
8d81c0068d
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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 { EntityTypeEndpoint } from '../support/entity/Entity.interface';
|
||||
|
||||
export const CustomPropertySupportedEntityList = [
|
||||
EntityTypeEndpoint.Database,
|
||||
EntityTypeEndpoint.DatabaseSchema,
|
||||
EntityTypeEndpoint.Table,
|
||||
EntityTypeEndpoint.StoreProcedure,
|
||||
EntityTypeEndpoint.Topic,
|
||||
EntityTypeEndpoint.Dashboard,
|
||||
EntityTypeEndpoint.Pipeline,
|
||||
EntityTypeEndpoint.Container,
|
||||
EntityTypeEndpoint.MlModel,
|
||||
EntityTypeEndpoint.GlossaryTerm,
|
||||
EntityTypeEndpoint.SearchIndex,
|
||||
];
|
||||
|
||||
export const ENTITY_REFERENCE_PROPERTIES = [
|
||||
'Entity Reference',
|
||||
'Entity Reference List',
|
||||
];
|
||||
@ -11,7 +11,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { test } from '@playwright/test';
|
||||
import { ENTITIES_WITHOUT_FOLLOWING_BUTTON } from '../../constant/delete';
|
||||
import { CustomPropertySupportedEntityList } from '../../constant/customProperty';
|
||||
import { ContainerClass } from '../../support/entity/ContainerClass';
|
||||
import { DashboardClass } from '../../support/entity/DashboardClass';
|
||||
import { DashboardDataModelClass } from '../../support/entity/DashboardDataModelClass';
|
||||
@ -19,13 +19,7 @@ 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 { DashboardServiceClass } from '../../support/entity/service/DashboardServiceClass';
|
||||
import { DatabaseServiceClass } from '../../support/entity/service/DatabaseServiceClass';
|
||||
import { MessagingServiceClass } from '../../support/entity/service/MessagingServiceClass';
|
||||
import { MlmodelServiceClass } from '../../support/entity/service/MlmodelServiceClass';
|
||||
import { PipelineServiceClass } from '../../support/entity/service/PipelineServiceClass';
|
||||
import { SearchIndexServiceClass } from '../../support/entity/service/SearchIndexServiceClass';
|
||||
import { StorageServiceClass } from '../../support/entity/service/StorageServiceClass';
|
||||
import { StoredProcedureClass } from '../../support/entity/StoredProcedureClass';
|
||||
import { TableClass } from '../../support/entity/TableClass';
|
||||
import { TopicClass } from '../../support/entity/TopicClass';
|
||||
import {
|
||||
@ -34,16 +28,11 @@ import {
|
||||
getToken,
|
||||
redirectToHomePage,
|
||||
} from '../../utils/common';
|
||||
import { CustomPropertyTypeByName } from '../../utils/customProperty';
|
||||
|
||||
const entities = [
|
||||
DatabaseServiceClass,
|
||||
DashboardServiceClass,
|
||||
MessagingServiceClass,
|
||||
MlmodelServiceClass,
|
||||
PipelineServiceClass,
|
||||
SearchIndexServiceClass,
|
||||
StorageServiceClass,
|
||||
TableClass,
|
||||
StoredProcedureClass,
|
||||
DashboardClass,
|
||||
PipelineClass,
|
||||
TopicClass,
|
||||
@ -59,9 +48,6 @@ test.use({ storageState: 'playwright/.auth/admin.json' });
|
||||
entities.forEach((EntityClass) => {
|
||||
const entity = new EntityClass();
|
||||
const deleteEntity = new EntityClass();
|
||||
const allowFollowUnfollowTest = !ENTITIES_WITHOUT_FOLLOWING_BUTTON.includes(
|
||||
entity.endpoint
|
||||
);
|
||||
|
||||
test.describe(entity.getType(), () => {
|
||||
test.beforeAll('Setup pre-requests', async ({ browser }) => {
|
||||
@ -69,6 +55,7 @@ entities.forEach((EntityClass) => {
|
||||
|
||||
await EntityDataClass.preRequisitesForTests(apiContext);
|
||||
await entity.create(apiContext);
|
||||
await entity.prepareForTests(apiContext);
|
||||
await afterAction();
|
||||
});
|
||||
|
||||
@ -128,15 +115,44 @@ entities.forEach((EntityClass) => {
|
||||
await entity.inactiveAnnouncement(page);
|
||||
});
|
||||
|
||||
if (allowFollowUnfollowTest) {
|
||||
test(`UpVote & DownVote entity`, async ({ page }) => {
|
||||
await entity.upVote(page);
|
||||
await entity.downVote(page);
|
||||
});
|
||||
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(`Follow & Un-follow entity`, async ({ page }) => {
|
||||
const entityName = entity.entityResponseData?.['displayName'];
|
||||
await entity.followUnfollowEntity(page, entityName);
|
||||
});
|
||||
|
||||
// Create custom property only for supported entities
|
||||
if (CustomPropertySupportedEntityList.includes(entity.endpoint)) {
|
||||
const properties = Object.values(CustomPropertyTypeByName);
|
||||
const titleText = properties.join(', ');
|
||||
|
||||
test(`Set & Update ${titleText} Custom Property `, async ({ page }) => {
|
||||
// increase timeout as it using single test for multiple steps
|
||||
test.slow(true);
|
||||
|
||||
await test.step(`Set ${titleText} Custom Property`, async () => {
|
||||
for (const type of properties) {
|
||||
await entity.setCustomProperty(
|
||||
page,
|
||||
entity.customPropertyValue[type].property,
|
||||
entity.customPropertyValue[type].value
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
await test.step(`Update ${titleText} Custom Property`, async () => {
|
||||
for (const type of properties) {
|
||||
await entity.updateCustomProperty(
|
||||
page,
|
||||
entity.customPropertyValue[type].property,
|
||||
entity.customPropertyValue[type].newValue
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@ -146,6 +162,7 @@ entities.forEach((EntityClass) => {
|
||||
|
||||
test.afterAll('Cleanup', async ({ browser }) => {
|
||||
const { apiContext, afterAction } = await createNewPage(browser);
|
||||
await entity.cleanup(apiContext);
|
||||
await entity.delete(apiContext);
|
||||
await EntityDataClass.postRequisitesForTests(apiContext);
|
||||
await afterAction();
|
||||
@ -153,6 +170,9 @@ entities.forEach((EntityClass) => {
|
||||
});
|
||||
|
||||
test(`Delete ${deleteEntity.getType()}`, async ({ page }) => {
|
||||
// increase timeout as it using single test for multiple steps
|
||||
test.slow(true);
|
||||
|
||||
await redirectToHomePage(page);
|
||||
// get the token from localStorage
|
||||
const token = await getToken(page);
|
||||
|
||||
@ -0,0 +1,157 @@
|
||||
/*
|
||||
* 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 { test } from '@playwright/test';
|
||||
import { DatabaseClass } from '../../support/entity/DatabaseClass';
|
||||
import { DatabaseSchemaClass } from '../../support/entity/DatabaseSchemaClass';
|
||||
import { EntityDataClass } from '../../support/entity/EntityDataClass';
|
||||
import { DashboardServiceClass } from '../../support/entity/service/DashboardServiceClass';
|
||||
import { DatabaseServiceClass } from '../../support/entity/service/DatabaseServiceClass';
|
||||
import { MessagingServiceClass } from '../../support/entity/service/MessagingServiceClass';
|
||||
import { MlmodelServiceClass } from '../../support/entity/service/MlmodelServiceClass';
|
||||
import { PipelineServiceClass } from '../../support/entity/service/PipelineServiceClass';
|
||||
import { SearchIndexServiceClass } from '../../support/entity/service/SearchIndexServiceClass';
|
||||
import { StorageServiceClass } from '../../support/entity/service/StorageServiceClass';
|
||||
import {
|
||||
createNewPage,
|
||||
getAuthContext,
|
||||
getToken,
|
||||
redirectToHomePage,
|
||||
} from '../../utils/common';
|
||||
|
||||
const entities = [
|
||||
DatabaseServiceClass,
|
||||
DashboardServiceClass,
|
||||
MessagingServiceClass,
|
||||
MlmodelServiceClass,
|
||||
PipelineServiceClass,
|
||||
SearchIndexServiceClass,
|
||||
StorageServiceClass,
|
||||
DatabaseClass,
|
||||
DatabaseSchemaClass,
|
||||
] as const;
|
||||
|
||||
// use the admin user to login
|
||||
test.use({ storageState: 'playwright/.auth/admin.json' });
|
||||
|
||||
entities.forEach((EntityClass) => {
|
||||
const entity = new EntityClass();
|
||||
const deleteEntity = new EntityClass();
|
||||
|
||||
test.describe(entity.getType(), () => {
|
||||
test.beforeAll('Setup pre-requests', async ({ browser }) => {
|
||||
const { apiContext, afterAction } = await createNewPage(browser);
|
||||
|
||||
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('Domain Add, Update and Remove', async ({ page }) => {
|
||||
await entity.domain(
|
||||
page,
|
||||
EntityDataClass.domain1.responseData,
|
||||
EntityDataClass.domain2.responseData
|
||||
);
|
||||
});
|
||||
|
||||
test('User as Owner Add, Update and Remove', async ({ page }) => {
|
||||
const OWNER1 = EntityDataClass.user1.getUserName();
|
||||
const OWNER2 = EntityDataClass.user2.getUserName();
|
||||
await entity.owner(page, OWNER1, 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', 'Tier5');
|
||||
});
|
||||
|
||||
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
|
||||
);
|
||||
});
|
||||
|
||||
test(`Announcement create & delete`, async ({ page }) => {
|
||||
await entity.announcement(
|
||||
page,
|
||||
entity.entityResponseData?.['fullyQualifiedName']
|
||||
);
|
||||
});
|
||||
|
||||
test(`Inactive Announcement create & delete`, async ({ page }) => {
|
||||
await entity.inactiveAnnouncement(page);
|
||||
});
|
||||
|
||||
test(`Update displayName`, async ({ page }) => {
|
||||
await entity.renameEntity(page, entity.entity.name);
|
||||
});
|
||||
|
||||
test.afterAll('Cleanup', async ({ browser }) => {
|
||||
const { apiContext, afterAction } = await createNewPage(browser);
|
||||
await entity.delete(apiContext);
|
||||
await EntityDataClass.postRequisitesForTests(apiContext);
|
||||
await afterAction();
|
||||
});
|
||||
});
|
||||
|
||||
test(`Delete ${deleteEntity.getType()}`, async ({ page }) => {
|
||||
// increase timeout as it using single test for multiple steps
|
||||
test.slow(true);
|
||||
|
||||
await redirectToHomePage(page);
|
||||
// get the token from localStorage
|
||||
const token = await getToken(page);
|
||||
|
||||
// create a new context with the token
|
||||
const apiContext = await getAuthContext(token);
|
||||
await deleteEntity.create(apiContext);
|
||||
await redirectToHomePage(page);
|
||||
await deleteEntity.visitEntityPage(page);
|
||||
|
||||
await test.step('Soft delete', async () => {
|
||||
await deleteEntity.softDeleteEntity(
|
||||
page,
|
||||
deleteEntity.entity.name,
|
||||
deleteEntity.entityResponseData?.['displayName']
|
||||
);
|
||||
});
|
||||
|
||||
await test.step('Hard delete', async () => {
|
||||
await deleteEntity.hardDeleteEntity(
|
||||
page,
|
||||
deleteEntity.entity.name,
|
||||
deleteEntity.entityResponseData?.['displayName']
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,113 @@
|
||||
/*
|
||||
* 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 { APIRequestContext, Page } from '@playwright/test';
|
||||
import { SERVICE_TYPE } from '../../constant/service';
|
||||
import { uuid } from '../../utils/common';
|
||||
import { visitServiceDetailsPage } from '../../utils/service';
|
||||
import { EntityTypeEndpoint } from './Entity.interface';
|
||||
import { EntityClass } from './EntityClass';
|
||||
|
||||
export class DatabaseClass extends EntityClass {
|
||||
service = {
|
||||
name: `pw-database-service-${uuid()}`,
|
||||
serviceType: 'Mysql',
|
||||
connection: {
|
||||
config: {
|
||||
type: 'Mysql',
|
||||
scheme: 'mysql+pymysql',
|
||||
username: 'username',
|
||||
authType: {
|
||||
password: 'password',
|
||||
},
|
||||
hostPort: 'mysql:3306',
|
||||
supportsMetadataExtraction: true,
|
||||
supportsDBTExtraction: true,
|
||||
supportsProfiler: true,
|
||||
supportsQueryComment: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
entity = {
|
||||
name: `pw-database-${uuid()}`,
|
||||
service: this.service.name,
|
||||
};
|
||||
|
||||
serviceResponseData: unknown;
|
||||
entityResponseData: unknown;
|
||||
|
||||
constructor(name?: string) {
|
||||
super(EntityTypeEndpoint.Database);
|
||||
this.service.name = name ?? this.service.name;
|
||||
this.type = 'Database';
|
||||
}
|
||||
|
||||
async create(apiContext: APIRequestContext) {
|
||||
const serviceResponse = await apiContext.post(
|
||||
'/api/v1/services/databaseServices',
|
||||
{
|
||||
data: this.service,
|
||||
}
|
||||
);
|
||||
const entityResponse = await apiContext.post('/api/v1/databases', {
|
||||
data: this.entity,
|
||||
});
|
||||
|
||||
const service = await serviceResponse.json();
|
||||
const entity = await entityResponse.json();
|
||||
|
||||
this.serviceResponseData = service;
|
||||
this.entityResponseData = entity;
|
||||
|
||||
return {
|
||||
service,
|
||||
entity,
|
||||
};
|
||||
}
|
||||
|
||||
get() {
|
||||
return {
|
||||
service: this.serviceResponseData,
|
||||
entity: this.entityResponseData,
|
||||
};
|
||||
}
|
||||
|
||||
async visitEntityPage(page: Page) {
|
||||
await visitServiceDetailsPage(
|
||||
page,
|
||||
{
|
||||
name: this.service.name,
|
||||
type: SERVICE_TYPE.Database,
|
||||
},
|
||||
false
|
||||
);
|
||||
|
||||
const databaseResponse = page.waitForResponse(
|
||||
`/api/v1/databases/name/*${this.entity.name}?**`
|
||||
);
|
||||
await page.getByTestId(this.entity.name).click();
|
||||
await databaseResponse;
|
||||
}
|
||||
|
||||
async delete(apiContext: APIRequestContext) {
|
||||
const serviceResponse = await apiContext.delete(
|
||||
`/api/v1/services/databaseServices/name/${encodeURIComponent(
|
||||
this.serviceResponseData?.['fullyQualifiedName']
|
||||
)}?recursive=true&hardDelete=true`
|
||||
);
|
||||
|
||||
return {
|
||||
service: serviceResponse.body,
|
||||
entity: this.entityResponseData,
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,130 @@
|
||||
/*
|
||||
* 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 { APIRequestContext, Page } from '@playwright/test';
|
||||
import { SERVICE_TYPE } from '../../constant/service';
|
||||
import { uuid } from '../../utils/common';
|
||||
import { visitServiceDetailsPage } from '../../utils/service';
|
||||
import { EntityTypeEndpoint } from './Entity.interface';
|
||||
import { EntityClass } from './EntityClass';
|
||||
|
||||
export class DatabaseSchemaClass extends EntityClass {
|
||||
service = {
|
||||
name: `pw-database-service-${uuid()}`,
|
||||
serviceType: 'Mysql',
|
||||
connection: {
|
||||
config: {
|
||||
type: 'Mysql',
|
||||
scheme: 'mysql+pymysql',
|
||||
username: 'username',
|
||||
authType: {
|
||||
password: 'password',
|
||||
},
|
||||
hostPort: 'mysql:3306',
|
||||
supportsMetadataExtraction: true,
|
||||
supportsDBTExtraction: true,
|
||||
supportsProfiler: true,
|
||||
supportsQueryComment: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
database = {
|
||||
name: `pw-database-${uuid()}`,
|
||||
service: this.service.name,
|
||||
};
|
||||
entity = {
|
||||
name: `pw-database-schema-${uuid()}`,
|
||||
database: `${this.service.name}.${this.database.name}`,
|
||||
};
|
||||
|
||||
serviceResponseData: unknown;
|
||||
databaseResponseData: unknown;
|
||||
entityResponseData: unknown;
|
||||
|
||||
constructor(name?: string) {
|
||||
super(EntityTypeEndpoint.DatabaseSchema);
|
||||
this.service.name = name ?? this.service.name;
|
||||
this.type = 'Database Schema';
|
||||
}
|
||||
|
||||
async create(apiContext: APIRequestContext) {
|
||||
const serviceResponse = await apiContext.post(
|
||||
'/api/v1/services/databaseServices',
|
||||
{
|
||||
data: this.service,
|
||||
}
|
||||
);
|
||||
const databaseResponse = await apiContext.post('/api/v1/databases', {
|
||||
data: this.database,
|
||||
});
|
||||
const entityResponse = await apiContext.post('/api/v1/databaseSchemas', {
|
||||
data: this.entity,
|
||||
});
|
||||
|
||||
const service = await serviceResponse.json();
|
||||
const database = await databaseResponse.json();
|
||||
const entity = await entityResponse.json();
|
||||
|
||||
this.serviceResponseData = service;
|
||||
this.databaseResponseData = database;
|
||||
this.entityResponseData = entity;
|
||||
|
||||
return {
|
||||
service,
|
||||
database,
|
||||
entity,
|
||||
};
|
||||
}
|
||||
|
||||
get() {
|
||||
return {
|
||||
service: this.serviceResponseData,
|
||||
database: this.databaseResponseData,
|
||||
entity: this.entityResponseData,
|
||||
};
|
||||
}
|
||||
|
||||
async visitEntityPage(page: Page) {
|
||||
await visitServiceDetailsPage(
|
||||
page,
|
||||
{
|
||||
name: this.service.name,
|
||||
type: SERVICE_TYPE.Database,
|
||||
},
|
||||
false
|
||||
);
|
||||
|
||||
const databaseResponse = page.waitForResponse(
|
||||
`/api/v1/databases/name/*${this.database.name}?**`
|
||||
);
|
||||
await page.getByTestId(this.database.name).click();
|
||||
await databaseResponse;
|
||||
const databaseSchemaResponse = page.waitForResponse(
|
||||
`/api/v1/databaseSchemas/name/*${this.entity}?*`
|
||||
);
|
||||
await page.getByTestId(this.entity.name).click();
|
||||
await databaseSchemaResponse;
|
||||
}
|
||||
|
||||
async delete(apiContext: APIRequestContext) {
|
||||
const serviceResponse = await apiContext.delete(
|
||||
`/api/v1/services/databaseServices/name/${encodeURIComponent(
|
||||
this.serviceResponseData?.['fullyQualifiedName']
|
||||
)}?recursive=true&hardDelete=true`
|
||||
);
|
||||
|
||||
return {
|
||||
service: serviceResponse.body,
|
||||
entity: this.entityResponseData,
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -41,3 +41,19 @@ export type EntityDataType = {
|
||||
entityDetails: unknown;
|
||||
endPoint: EntityTypeEndpoint;
|
||||
};
|
||||
|
||||
export enum ENTITY_PATH {
|
||||
tables = 'table',
|
||||
topics = 'topic',
|
||||
dashboards = 'dashboard',
|
||||
pipelines = 'pipeline',
|
||||
mlmodels = 'mlmodel',
|
||||
containers = 'container',
|
||||
tags = 'tag',
|
||||
glossaries = 'glossary',
|
||||
searchIndexes = 'searchIndex',
|
||||
storedProcedures = 'storedProcedure',
|
||||
glossaryTerm = 'glossaryTerm',
|
||||
databases = 'database',
|
||||
databaseSchemas = 'databaseSchema',
|
||||
}
|
||||
|
||||
@ -10,7 +10,14 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { Page } from '@playwright/test';
|
||||
import { APIRequestContext, Page } from '@playwright/test';
|
||||
import { CustomPropertySupportedEntityList } from '../../constant/customProperty';
|
||||
import {
|
||||
createCustomPropertyForEntity,
|
||||
CustomProperty,
|
||||
setValueForProperty,
|
||||
validateValueForProperty,
|
||||
} from '../../utils/customProperty';
|
||||
import { assignDomain, removeDomain, updateDomain } from '../../utils/domain';
|
||||
import {
|
||||
addOwner,
|
||||
@ -38,12 +45,17 @@ import {
|
||||
} from '../../utils/entity';
|
||||
import { Domain } from '../domain/Domain';
|
||||
import { GlossaryTerm } from '../glossary/GlossaryTerm';
|
||||
import { EntityTypeEndpoint } from './Entity.interface';
|
||||
import { EntityTypeEndpoint, ENTITY_PATH } from './Entity.interface';
|
||||
|
||||
export class EntityClass {
|
||||
type: string;
|
||||
endpoint: EntityTypeEndpoint;
|
||||
|
||||
customPropertyValue: Record<
|
||||
string,
|
||||
{ value: string; newValue: string; property: CustomProperty }
|
||||
>;
|
||||
|
||||
constructor(endpoint: EntityTypeEndpoint) {
|
||||
this.endpoint = endpoint;
|
||||
}
|
||||
@ -57,6 +69,40 @@ export class EntityClass {
|
||||
// Override for entity visit
|
||||
}
|
||||
|
||||
// Prepare for tests
|
||||
async prepareForTests(apiContext: APIRequestContext) {
|
||||
// Create custom property only for supported entities
|
||||
if (CustomPropertySupportedEntityList.includes(this.endpoint)) {
|
||||
const data = await createCustomPropertyForEntity(
|
||||
apiContext,
|
||||
this.endpoint
|
||||
);
|
||||
|
||||
this.customPropertyValue = data;
|
||||
}
|
||||
}
|
||||
|
||||
async cleanup(apiContext: APIRequestContext) {
|
||||
// Delete custom property only for supported entities
|
||||
if (CustomPropertySupportedEntityList.includes(this.endpoint)) {
|
||||
const entitySchemaResponse = await apiContext.get(
|
||||
`/api/v1/metadata/types/name/${ENTITY_PATH[this.endpoint]}`
|
||||
);
|
||||
const entitySchema = await entitySchemaResponse.json();
|
||||
await apiContext.patch(`/api/v1/metadata/types/${entitySchema.id}`, {
|
||||
data: [
|
||||
{
|
||||
op: 'remove',
|
||||
path: '/customProperties',
|
||||
},
|
||||
],
|
||||
headers: {
|
||||
'Content-Type': 'application/json-patch+json',
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async domain(
|
||||
page: Page,
|
||||
domain1: Domain['responseData'],
|
||||
@ -173,4 +219,42 @@ export class EntityClass {
|
||||
async hardDeleteEntity(page: Page, entityName: string, displayName?: string) {
|
||||
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,
|
||||
});
|
||||
await validateValueForProperty({
|
||||
page,
|
||||
propertyName: propertydetails.name,
|
||||
value,
|
||||
propertyType: propertydetails.propertyType.name,
|
||||
});
|
||||
}
|
||||
|
||||
async updateCustomProperty(
|
||||
page: Page,
|
||||
propertydetails: CustomProperty,
|
||||
value: string
|
||||
) {
|
||||
await setValueForProperty({
|
||||
page,
|
||||
propertyName: propertydetails.name,
|
||||
value,
|
||||
propertyType: propertydetails.propertyType.name,
|
||||
});
|
||||
await validateValueForProperty({
|
||||
page,
|
||||
propertyName: propertydetails.name,
|
||||
value,
|
||||
propertyType: propertydetails.propertyType.name,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,130 @@
|
||||
/*
|
||||
* 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 { APIRequestContext, Page } from '@playwright/test';
|
||||
import { uuid } from '../../utils/common';
|
||||
import { visitEntityPage } from '../../utils/entity';
|
||||
import { EntityTypeEndpoint } from './Entity.interface';
|
||||
import { EntityClass } from './EntityClass';
|
||||
|
||||
export class StoredProcedureClass extends EntityClass {
|
||||
service = {
|
||||
name: `pw-database-service-${uuid()}`,
|
||||
serviceType: 'Mysql',
|
||||
connection: {
|
||||
config: {
|
||||
type: 'Mysql',
|
||||
scheme: 'mysql+pymysql',
|
||||
username: 'username',
|
||||
authType: {
|
||||
password: 'password',
|
||||
},
|
||||
hostPort: 'mysql:3306',
|
||||
supportsMetadataExtraction: true,
|
||||
supportsDBTExtraction: true,
|
||||
supportsProfiler: true,
|
||||
supportsQueryComment: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
database = {
|
||||
name: `pw-database-${uuid()}`,
|
||||
service: this.service.name,
|
||||
};
|
||||
schema = {
|
||||
name: `pw-database-schema-${uuid()}`,
|
||||
database: `${this.service.name}.${this.database.name}`,
|
||||
};
|
||||
entity = {
|
||||
name: `pw-stored-procedure-${uuid()}`,
|
||||
databaseSchema: `${this.service.name}.${this.database.name}.${this.schema.name}`,
|
||||
storedProcedureCode: {
|
||||
code: 'CREATE OR REPLACE PROCEDURE output_message(message VARCHAR)\nRETURNS VARCHAR NOT NULL\nLANGUAGE SQL\nAS\n$$\nBEGIN\n RETURN message;\nEND;\n$$\n;',
|
||||
},
|
||||
};
|
||||
|
||||
serviceResponseData: unknown;
|
||||
databaseResponseData: unknown;
|
||||
schemaResponseData: unknown;
|
||||
entityResponseData: unknown;
|
||||
|
||||
constructor(name?: string) {
|
||||
super(EntityTypeEndpoint.StoreProcedure);
|
||||
this.service.name = name ?? this.service.name;
|
||||
this.type = 'Store Procedure';
|
||||
}
|
||||
|
||||
async create(apiContext: APIRequestContext) {
|
||||
const serviceResponse = await apiContext.post(
|
||||
'/api/v1/services/databaseServices',
|
||||
{
|
||||
data: this.service,
|
||||
}
|
||||
);
|
||||
const databaseResponse = await apiContext.post('/api/v1/databases', {
|
||||
data: this.database,
|
||||
});
|
||||
const schemaResponse = await apiContext.post('/api/v1/databaseSchemas', {
|
||||
data: this.schema,
|
||||
});
|
||||
const entityResponse = await apiContext.post('/api/v1/storedProcedures', {
|
||||
data: this.entity,
|
||||
});
|
||||
|
||||
const service = await serviceResponse.json();
|
||||
const database = await databaseResponse.json();
|
||||
const schema = await schemaResponse.json();
|
||||
const entity = await entityResponse.json();
|
||||
|
||||
this.serviceResponseData = service;
|
||||
this.databaseResponseData = database;
|
||||
this.schemaResponseData = schema;
|
||||
this.entityResponseData = entity;
|
||||
|
||||
return {
|
||||
service,
|
||||
database,
|
||||
schema,
|
||||
entity,
|
||||
};
|
||||
}
|
||||
|
||||
get() {
|
||||
return {
|
||||
service: this.serviceResponseData,
|
||||
database: this.databaseResponseData,
|
||||
schema: this.schemaResponseData,
|
||||
entity: this.entityResponseData,
|
||||
};
|
||||
}
|
||||
|
||||
async visitEntityPage(page: Page) {
|
||||
await visitEntityPage({
|
||||
page,
|
||||
searchTerm: this.entityResponseData?.['fullyQualifiedName'],
|
||||
dataTestId: `${this.service.name}-${this.entity.name}`,
|
||||
});
|
||||
}
|
||||
|
||||
async delete(apiContext: APIRequestContext) {
|
||||
const serviceResponse = await apiContext.delete(
|
||||
`/api/v1/services/databaseServices/name/${encodeURIComponent(
|
||||
this.serviceResponseData?.['fullyQualifiedName']
|
||||
)}?recursive=true&hardDelete=true`
|
||||
);
|
||||
|
||||
return {
|
||||
service: serviceResponse.body,
|
||||
entity: this.entityResponseData,
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -12,6 +12,7 @@
|
||||
*/
|
||||
import { APIRequestContext } from '@playwright/test';
|
||||
import { uuid } from '../../utils/common';
|
||||
import { getRandomFirstName } from '../../utils/user';
|
||||
|
||||
type ResponseDataType = {
|
||||
name: string;
|
||||
@ -25,9 +26,10 @@ type ResponseDataType = {
|
||||
};
|
||||
|
||||
export class Glossary {
|
||||
randomName = getRandomFirstName();
|
||||
data = {
|
||||
name: `PW%General.${uuid()}`,
|
||||
displayName: `PW % General ${uuid()}`,
|
||||
name: `PW%${uuid()}.${this.randomName}`,
|
||||
displayName: `PW % ${uuid()} ${this.randomName}`,
|
||||
description:
|
||||
'Glossary terms that describe general conceptual terms. **Note that these conceptual terms are used for automatically labeling the data.**',
|
||||
reviewers: [],
|
||||
@ -48,10 +50,10 @@ export class Glossary {
|
||||
|
||||
this.responseData = await response.json();
|
||||
|
||||
return response.body;
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
async get() {
|
||||
get() {
|
||||
return this.responseData;
|
||||
}
|
||||
|
||||
@ -62,6 +64,6 @@ export class Glossary {
|
||||
)}?recursive=true&hardDelete=true`
|
||||
);
|
||||
|
||||
return response.body;
|
||||
return await response.json();
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,6 +12,7 @@
|
||||
*/
|
||||
import { APIRequestContext } from '@playwright/test';
|
||||
import { uuid } from '../../utils/common';
|
||||
import { getRandomLastName } from '../../utils/user';
|
||||
|
||||
type ResponseDataType = {
|
||||
name: string;
|
||||
@ -28,9 +29,10 @@ type ResponseDataType = {
|
||||
};
|
||||
|
||||
export class GlossaryTerm {
|
||||
randomName = getRandomLastName();
|
||||
data = {
|
||||
name: `PW.Bank%Number-${uuid()}`,
|
||||
displayName: `PW BankNumber ${uuid()}`,
|
||||
name: `PW.${uuid()}%${this.randomName}`,
|
||||
displayName: `PW ${uuid()}%${this.randomName}`,
|
||||
description: 'A bank account number.',
|
||||
mutuallyExclusive: false,
|
||||
glossary: '',
|
||||
@ -50,10 +52,10 @@ export class GlossaryTerm {
|
||||
|
||||
this.responseData = await response.json();
|
||||
|
||||
return response.body;
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
async get() {
|
||||
get() {
|
||||
return this.responseData;
|
||||
}
|
||||
|
||||
@ -64,6 +66,6 @@ export class GlossaryTerm {
|
||||
)}?recursive=true&hardDelete=true`
|
||||
);
|
||||
|
||||
return response.body;
|
||||
return await response.json();
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,368 @@
|
||||
/*
|
||||
* 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 { APIRequestContext, expect, Page } from '@playwright/test';
|
||||
import {
|
||||
EntityTypeEndpoint,
|
||||
ENTITY_PATH,
|
||||
} from '../support/entity/Entity.interface';
|
||||
import { uuid } from './common';
|
||||
|
||||
export enum CustomPropertyType {
|
||||
STRING = 'String',
|
||||
INTEGER = 'Integer',
|
||||
MARKDOWN = 'Markdown',
|
||||
}
|
||||
export enum CustomPropertyTypeByName {
|
||||
STRING = 'string',
|
||||
INTEGER = 'integer',
|
||||
MARKDOWN = 'markdown',
|
||||
NUMBER = 'number',
|
||||
DURATION = 'duration',
|
||||
EMAIL = 'email',
|
||||
ENUM = 'enum',
|
||||
SQL_QUERY = 'sqlQuery',
|
||||
TIMESTAMP = 'timestamp',
|
||||
ENTITY_REFERENCE = 'entityReference',
|
||||
ENTITY_REFERENCE_LIST = 'entityReferenceList',
|
||||
}
|
||||
|
||||
export interface CustomProperty {
|
||||
name: string;
|
||||
type: CustomPropertyType;
|
||||
description: string;
|
||||
propertyType: {
|
||||
name: string;
|
||||
type: string;
|
||||
};
|
||||
}
|
||||
|
||||
export const setValueForProperty = async (data: {
|
||||
page: Page;
|
||||
propertyName: string;
|
||||
value: string;
|
||||
propertyType: string;
|
||||
}) => {
|
||||
const { page, propertyName, value, propertyType } = data;
|
||||
await page.click('[data-testid="custom_properties"]');
|
||||
|
||||
await expect(page.getByRole('cell', { name: propertyName })).toContainText(
|
||||
propertyName
|
||||
);
|
||||
|
||||
const editButton = page.locator(
|
||||
`[data-row-key="${propertyName}"] [data-testid="edit-icon"]`
|
||||
);
|
||||
await editButton.scrollIntoViewIfNeeded();
|
||||
await editButton.click({ force: true });
|
||||
|
||||
switch (propertyType) {
|
||||
case 'markdown':
|
||||
await page
|
||||
.locator(
|
||||
'.toastui-editor-md-container > .toastui-editor > .ProseMirror'
|
||||
)
|
||||
.isVisible();
|
||||
await page
|
||||
.locator(
|
||||
'.toastui-editor-md-container > .toastui-editor > .ProseMirror'
|
||||
)
|
||||
.fill(value);
|
||||
await page.locator('[data-testid="save"]').click();
|
||||
|
||||
break;
|
||||
|
||||
case 'email':
|
||||
await page.locator('[data-testid="email-input"]').isVisible();
|
||||
await page.locator('[data-testid="email-input"]').fill(value);
|
||||
await page.locator('[data-testid="inline-save-btn"]').click();
|
||||
|
||||
break;
|
||||
|
||||
case 'duration':
|
||||
await page.locator('[data-testid="duration-input"]').isVisible();
|
||||
await page.locator('[data-testid="duration-input"]').fill(value);
|
||||
await page.locator('[data-testid="inline-save-btn"]').click();
|
||||
|
||||
break;
|
||||
|
||||
case 'enum':
|
||||
await page.locator('#enumValues').click();
|
||||
await page.locator('#enumValues').fill(value);
|
||||
await page.locator('#enumValues').press('Enter');
|
||||
await page.mouse.click(0, 0);
|
||||
await page.locator('[data-testid="inline-save-btn"]').click();
|
||||
|
||||
break;
|
||||
|
||||
case 'sqlQuery':
|
||||
await page.locator("pre[role='presentation']").last().click();
|
||||
await page.keyboard.type(value);
|
||||
await page.locator('[data-testid="inline-save-btn"]').click();
|
||||
|
||||
break;
|
||||
|
||||
case 'timestamp':
|
||||
await page.locator('[data-testid="timestamp-input"]').isVisible();
|
||||
await page.locator('[data-testid="timestamp-input"]').fill(value);
|
||||
await page.locator('[data-testid="inline-save-btn"]').click();
|
||||
|
||||
break;
|
||||
|
||||
case 'timeInterval': {
|
||||
const [startValue, endValue] = value.split(',');
|
||||
await page.locator('[data-testid="start-input"]').isVisible();
|
||||
await page.locator('[data-testid="start-input"]').fill(startValue);
|
||||
await page.locator('[data-testid="end-input"]').isVisible();
|
||||
await page.locator('[data-testid="end-input"]').fill(endValue);
|
||||
await page.locator('[data-testid="inline-save-btn"]').click();
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'string':
|
||||
case 'integer':
|
||||
case 'number':
|
||||
await page.locator('[data-testid="value-input"]').isVisible();
|
||||
await page.locator('[data-testid="value-input"]').fill(value);
|
||||
await page.locator('[data-testid="inline-save-btn"]').click();
|
||||
|
||||
break;
|
||||
|
||||
case 'entityReference':
|
||||
case 'entityReferenceList': {
|
||||
const refValues = value.split(',');
|
||||
|
||||
for (const val of refValues) {
|
||||
const searchApi = `**/api/v1/search/query?q=*${encodeURIComponent(
|
||||
val
|
||||
)}*`;
|
||||
await page.route(searchApi, (route) => route.continue());
|
||||
await page.locator('#entityReference').clear();
|
||||
await page.locator('#entityReference').fill(val);
|
||||
await page.waitForResponse(searchApi);
|
||||
await page.locator(`[data-testid="${val}"]`).click();
|
||||
}
|
||||
|
||||
await page.locator('[data-testid="inline-save-btn"]').click();
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
await page.waitForResponse('/api/v1/*/*');
|
||||
if (propertyType === 'enum') {
|
||||
await expect(
|
||||
page.getByLabel('Custom Properties').getByTestId('enum-value')
|
||||
).toContainText(value);
|
||||
} else if (propertyType === 'timeInterval') {
|
||||
const [startValue, endValue] = value.split(',');
|
||||
|
||||
await expect(
|
||||
page.getByLabel('Custom Properties').getByTestId('time-interval-value')
|
||||
).toContainText(startValue);
|
||||
await expect(
|
||||
page.getByLabel('Custom Properties').getByTestId('time-interval-value')
|
||||
).toContainText(endValue);
|
||||
} else if (propertyType === 'sqlQuery') {
|
||||
await expect(
|
||||
page.getByLabel('Custom Properties').locator('.CodeMirror-scroll')
|
||||
).toContainText(value);
|
||||
} else if (
|
||||
!['entityReference', 'entityReferenceList'].includes(propertyType)
|
||||
) {
|
||||
await expect(page.getByRole('row', { name: propertyName })).toContainText(
|
||||
value.replace(/\*|_/gi, '')
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export const validateValueForProperty = async (data: {
|
||||
page: Page;
|
||||
propertyName: string;
|
||||
value: string;
|
||||
propertyType: string;
|
||||
}) => {
|
||||
const { page, propertyName, value, propertyType } = data;
|
||||
await page.click('[data-testid="custom_properties"]');
|
||||
|
||||
if (propertyType === 'enum') {
|
||||
await expect(
|
||||
page.getByLabel('Custom Properties').getByTestId('enum-value')
|
||||
).toContainText(value);
|
||||
} else if (propertyType === 'timeInterval') {
|
||||
const [startValue, endValue] = value.split(',');
|
||||
|
||||
await expect(
|
||||
page.getByLabel('Custom Properties').getByTestId('time-interval-value')
|
||||
).toContainText(startValue);
|
||||
await expect(
|
||||
page.getByLabel('Custom Properties').getByTestId('time-interval-value')
|
||||
).toContainText(endValue);
|
||||
} else if (propertyType === 'sqlQuery') {
|
||||
await expect(
|
||||
page.getByLabel('Custom Properties').locator('.CodeMirror-scroll')
|
||||
).toContainText(value);
|
||||
} else if (
|
||||
!['entityReference', 'entityReferenceList'].includes(propertyType)
|
||||
) {
|
||||
await expect(page.getByRole('row', { name: propertyName })).toContainText(
|
||||
value.replace(/\*|_/gi, '')
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export const getPropertyValues = (type: string) => {
|
||||
switch (type) {
|
||||
case 'integer':
|
||||
return {
|
||||
value: '123',
|
||||
newValue: '456',
|
||||
};
|
||||
case 'string':
|
||||
return {
|
||||
value: 'string value',
|
||||
newValue: 'new string value',
|
||||
};
|
||||
case 'markdown':
|
||||
return {
|
||||
value: '**Bold statement**',
|
||||
newValue: '__Italic statement__',
|
||||
};
|
||||
|
||||
case 'number':
|
||||
return {
|
||||
value: '123',
|
||||
newValue: '456',
|
||||
};
|
||||
case 'duration':
|
||||
return {
|
||||
value: 'PT1H',
|
||||
newValue: 'PT2H',
|
||||
};
|
||||
case 'email':
|
||||
return {
|
||||
value: 'john@gamil.com',
|
||||
newValue: 'user@getcollate.io',
|
||||
};
|
||||
case 'enum':
|
||||
return {
|
||||
value: 'small',
|
||||
newValue: 'medium',
|
||||
};
|
||||
case 'sqlQuery':
|
||||
return {
|
||||
value: 'Select * from table',
|
||||
newValue: 'Select * from table where id = 1',
|
||||
};
|
||||
|
||||
case 'timestamp':
|
||||
return {
|
||||
value: '1710831125922',
|
||||
newValue: '1710831125923',
|
||||
};
|
||||
case 'entityReference':
|
||||
return {
|
||||
value: 'Adam Matthews',
|
||||
newValue: 'Aaron Singh',
|
||||
};
|
||||
|
||||
case 'entityReferenceList':
|
||||
return {
|
||||
value: 'Aaron Johnson,Organization',
|
||||
newValue: 'Aaron Warren',
|
||||
};
|
||||
|
||||
default:
|
||||
return {
|
||||
value: '',
|
||||
newValue: '',
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
export const createCustomPropertyForEntity = async (
|
||||
apiContext: APIRequestContext,
|
||||
endpoint: EntityTypeEndpoint
|
||||
) => {
|
||||
const propertiesResponse = await apiContext.get(
|
||||
'/api/v1/metadata/types?category=field&limit=20'
|
||||
);
|
||||
const properties = await propertiesResponse.json();
|
||||
const propertyList = properties.data.filter((item) =>
|
||||
Object.values(CustomPropertyTypeByName).includes(item.name)
|
||||
);
|
||||
|
||||
const entitySchemaResponse = await apiContext.get(
|
||||
`/api/v1/metadata/types/name/${ENTITY_PATH[endpoint]}`
|
||||
);
|
||||
const entitySchema = await entitySchemaResponse.json();
|
||||
|
||||
let customProperties = {} as Record<
|
||||
string,
|
||||
{
|
||||
value: string;
|
||||
newValue: string;
|
||||
property: CustomProperty;
|
||||
}
|
||||
>;
|
||||
|
||||
for (const item of propertyList) {
|
||||
const customPropertyResponse = await apiContext.put(
|
||||
`/api/v1/metadata/types/${entitySchema.id}`,
|
||||
{
|
||||
data: {
|
||||
name: `cyCustomProperty${uuid()}`,
|
||||
description: `cyCustomProperty${uuid()}`,
|
||||
propertyType: {
|
||||
id: item.id ?? '',
|
||||
type: 'type',
|
||||
},
|
||||
...(item.name === 'enum'
|
||||
? {
|
||||
customPropertyConfig: {
|
||||
config: {
|
||||
multiSelect: true,
|
||||
values: ['small', 'medium', 'large'],
|
||||
},
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
...(['entityReference', 'entityReferenceList'].includes(item.name)
|
||||
? {
|
||||
customPropertyConfig: {
|
||||
config: ['user', 'team'],
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const customProperty = await customPropertyResponse.json();
|
||||
|
||||
// Process the custom properties
|
||||
customProperties = customProperty.customProperties.reduce((prev, curr) => {
|
||||
const propertyTypeName = curr.propertyType.name;
|
||||
|
||||
return {
|
||||
...prev,
|
||||
[propertyTypeName]: {
|
||||
...getPropertyValues(propertyTypeName),
|
||||
property: curr,
|
||||
},
|
||||
};
|
||||
}, {});
|
||||
}
|
||||
|
||||
return customProperties;
|
||||
};
|
||||
@ -15,6 +15,7 @@ import { Domain } from '../support/domain/Domain';
|
||||
|
||||
export const assignDomain = 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')
|
||||
@ -31,6 +32,7 @@ export const assignDomain = async (page: Page, domain: Domain['data']) => {
|
||||
|
||||
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();
|
||||
await page
|
||||
.getByTestId('selectable-list')
|
||||
@ -48,6 +50,7 @@ export const updateDomain = async (page: Page, domain: Domain['data']) => {
|
||||
|
||||
export const removeDomain = async (page: Page) => {
|
||||
await page.getByTestId('add-domain').click();
|
||||
await page.waitForSelector('[data-testid="loader"]', { state: 'detached' });
|
||||
|
||||
await expect(page.getByTestId('remove-owner').locator('path')).toBeVisible();
|
||||
|
||||
|
||||
@ -33,6 +33,7 @@ export const visitEntityPage = async (data: {
|
||||
const { page, searchTerm, dataTestId } = data;
|
||||
|
||||
await page.getByTestId('searchBox').fill(searchTerm);
|
||||
await page.waitForResponse('/api/v1/search/query?q=*index=dataAsset*');
|
||||
await page.getByTestId(dataTestId).getByTestId('data-name').click();
|
||||
await page.getByTestId('searchBox').clear();
|
||||
};
|
||||
@ -98,6 +99,7 @@ export const updateOwner = async (
|
||||
|
||||
export const removeOwner = async (page: Page, dataTestId?: string) => {
|
||||
await page.getByTestId('edit-owner').click();
|
||||
await page.waitForSelector('[data-testid="loader"]', { state: 'detached' });
|
||||
|
||||
await expect(page.getByTestId('remove-owner').locator('svg')).toBeVisible();
|
||||
|
||||
@ -110,6 +112,7 @@ export const removeOwner = async (page: Page, dataTestId?: string) => {
|
||||
|
||||
export const assignTier = async (page: Page, tier: string) => {
|
||||
await page.getByTestId('edit-tier').click();
|
||||
await page.waitForSelector('[data-testid="loader"]', { state: 'detached' });
|
||||
await page.getByTestId(`radio-btn-${tier}`).click();
|
||||
await page.getByTestId('Tier').click();
|
||||
|
||||
@ -118,6 +121,7 @@ export const assignTier = async (page: Page, tier: string) => {
|
||||
|
||||
export const removeTier = async (page: Page) => {
|
||||
await page.getByTestId('edit-tier').click();
|
||||
await page.waitForSelector('[data-testid="loader"]', { state: 'detached' });
|
||||
await page.getByTestId('clear-tier').click();
|
||||
await page.getByTestId('Tier').click();
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user