mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-08-17 13:36:56 +00:00
playwright: improve the existing playwright test (#16650)
* playwright: improve the existing playwright test * pw improvement * minor change * reduce the scope of custom property * reduce scope of custom property test * addressing comments * updated response timeout
This commit is contained in:
parent
55cd180ffa
commit
18ee2b8ff2
@ -196,7 +196,7 @@ export const testConnection = () => {
|
||||
// added extra buffer time as deleteWorkflow API can take up to 2 minute or more to send request
|
||||
cy.wait('@deleteWorkflow', {
|
||||
requestTimeout: 150000,
|
||||
responseTimeout: 50000,
|
||||
responseTimeout: 150000,
|
||||
});
|
||||
cy.get('.ant-modal-footer > .ant-btn-primary')
|
||||
.should('exist')
|
||||
|
@ -55,7 +55,6 @@ entities.forEach((EntityClass) => {
|
||||
|
||||
await EntityDataClass.preRequisitesForTests(apiContext);
|
||||
await entity.create(apiContext);
|
||||
await entity.prepareForTests(apiContext);
|
||||
await afterAction();
|
||||
});
|
||||
|
||||
@ -134,6 +133,10 @@ entities.forEach((EntityClass) => {
|
||||
// increase timeout as it using single test for multiple steps
|
||||
test.slow(true);
|
||||
|
||||
const token = await getToken(page);
|
||||
const apiContext = await getAuthContext(token);
|
||||
await entity.prepareCustomProperty(apiContext);
|
||||
|
||||
await test.step(`Set ${titleText} Custom Property`, async () => {
|
||||
for (const type of properties) {
|
||||
await entity.setCustomProperty(
|
||||
@ -153,6 +156,8 @@ entities.forEach((EntityClass) => {
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
await entity.cleanupCustomProperty(apiContext);
|
||||
});
|
||||
}
|
||||
|
||||
@ -162,7 +167,6 @@ 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();
|
||||
|
@ -24,6 +24,7 @@ import { SearchIndexServiceClass } from '../../support/entity/service/SearchInde
|
||||
import { StorageServiceClass } from '../../support/entity/service/StorageServiceClass';
|
||||
import {
|
||||
createNewPage,
|
||||
getApiContext,
|
||||
getAuthContext,
|
||||
getToken,
|
||||
redirectToHomePage,
|
||||
@ -55,7 +56,6 @@ entities.forEach((EntityClass) => {
|
||||
|
||||
await EntityDataClass.preRequisitesForTests(apiContext);
|
||||
await entity.create(apiContext);
|
||||
await entity.prepareForTests(apiContext);
|
||||
await afterAction();
|
||||
});
|
||||
|
||||
@ -124,6 +124,9 @@ entities.forEach((EntityClass) => {
|
||||
// increase timeout as it using single test for multiple steps
|
||||
test.slow(true);
|
||||
|
||||
const { apiContext, afterAction } = await getApiContext(page);
|
||||
await entity.prepareCustomProperty(apiContext);
|
||||
|
||||
await test.step(`Set ${titleText} Custom Property`, async () => {
|
||||
for (const type of properties) {
|
||||
await entity.setCustomProperty(
|
||||
@ -143,6 +146,9 @@ entities.forEach((EntityClass) => {
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
await entity.cleanupCustomProperty(apiContext);
|
||||
await afterAction();
|
||||
});
|
||||
}
|
||||
|
||||
@ -152,7 +158,6 @@ 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();
|
||||
|
@ -34,7 +34,7 @@ export class ContainerClass extends EntityClass {
|
||||
},
|
||||
};
|
||||
entity = {
|
||||
name: `pw.container%${uuid()}`,
|
||||
name: `pw-container-${uuid()}`,
|
||||
displayName: `pw-container-${uuid()}`,
|
||||
service: this.service.name,
|
||||
};
|
||||
|
@ -34,7 +34,7 @@ export class DashboardClass extends EntityClass {
|
||||
},
|
||||
};
|
||||
entity = {
|
||||
name: `pw.dashboard%${uuid()}`,
|
||||
name: `pw-dashboard-${uuid()}`,
|
||||
displayName: `pw-dashboard-${uuid()}`,
|
||||
service: this.service.name,
|
||||
};
|
||||
|
@ -34,7 +34,7 @@ export class DashboardDataModelClass extends EntityClass {
|
||||
},
|
||||
};
|
||||
entity = {
|
||||
name: `pw.dashboard-data-model%${uuid()}`,
|
||||
name: `pw-dashboard-data-model-${uuid()}`,
|
||||
displayName: `pw-dashboard-data-model-${uuid()}`,
|
||||
service: this.service.name,
|
||||
columns: [
|
||||
|
@ -13,8 +13,8 @@
|
||||
import { APIRequestContext, Page } from '@playwright/test';
|
||||
import { CustomPropertySupportedEntityList } from '../../constant/customProperty';
|
||||
import {
|
||||
createCustomPropertyForEntity,
|
||||
CustomProperty,
|
||||
createCustomPropertyForEntity,
|
||||
setValueForProperty,
|
||||
validateValueForProperty,
|
||||
} from '../../utils/customProperty';
|
||||
@ -37,15 +37,15 @@ import {
|
||||
replyAnnouncement,
|
||||
softDeleteEntity,
|
||||
unFollowEntity,
|
||||
upVote,
|
||||
updateDescription,
|
||||
updateDisplayNameForEntity,
|
||||
updateOwner,
|
||||
upVote,
|
||||
validateFollowedEntityToWidget,
|
||||
} from '../../utils/entity';
|
||||
import { Domain } from '../domain/Domain';
|
||||
import { GlossaryTerm } from '../glossary/GlossaryTerm';
|
||||
import { EntityTypeEndpoint, ENTITY_PATH } from './Entity.interface';
|
||||
import { ENTITY_PATH, EntityTypeEndpoint } from './Entity.interface';
|
||||
|
||||
export class EntityClass {
|
||||
type: string;
|
||||
@ -70,8 +70,7 @@ export class EntityClass {
|
||||
// Override for entity visit
|
||||
}
|
||||
|
||||
// Prepare for tests
|
||||
async prepareForTests(apiContext: APIRequestContext) {
|
||||
async prepareCustomProperty(apiContext: APIRequestContext) {
|
||||
// Create custom property only for supported entities
|
||||
if (CustomPropertySupportedEntityList.includes(this.endpoint)) {
|
||||
const data = await createCustomPropertyForEntity(
|
||||
@ -84,7 +83,7 @@ export class EntityClass {
|
||||
}
|
||||
}
|
||||
|
||||
async cleanup(apiContext: APIRequestContext) {
|
||||
async cleanupCustomProperty(apiContext: APIRequestContext) {
|
||||
// Delete custom property only for supported entities
|
||||
if (CustomPropertySupportedEntityList.includes(this.endpoint)) {
|
||||
await this.cleanupUser(apiContext);
|
||||
@ -122,9 +121,9 @@ export class EntityClass {
|
||||
owner2: string,
|
||||
type: 'Teams' | 'Users' = 'Users'
|
||||
) {
|
||||
await addOwner(page, owner1, type, 'data-assets-header');
|
||||
await updateOwner(page, owner2, type, 'data-assets-header');
|
||||
await removeOwner(page, 'data-assets-header');
|
||||
await addOwner(page, owner1, type, this.endpoint, 'data-assets-header');
|
||||
await updateOwner(page, owner2, type, this.endpoint, 'data-assets-header');
|
||||
await removeOwner(page, this.endpoint, 'data-assets-header');
|
||||
}
|
||||
|
||||
async tier(page: Page, tier1: string, tier2: string) {
|
||||
@ -233,6 +232,7 @@ export class EntityClass {
|
||||
propertyName: propertydetails.name,
|
||||
value,
|
||||
propertyType: propertydetails.propertyType.name,
|
||||
endpoint: this.endpoint,
|
||||
});
|
||||
await validateValueForProperty({
|
||||
page,
|
||||
@ -252,6 +252,7 @@ export class EntityClass {
|
||||
propertyName: propertydetails.name,
|
||||
value,
|
||||
propertyType: propertydetails.propertyType.name,
|
||||
endpoint: this.endpoint,
|
||||
});
|
||||
await validateValueForProperty({
|
||||
page,
|
||||
|
@ -30,7 +30,7 @@ export class MlModelClass extends EntityClass {
|
||||
},
|
||||
};
|
||||
entity = {
|
||||
name: `pw.mlmodel%${uuid()}`,
|
||||
name: `pw-mlmodel-${uuid()}`,
|
||||
displayName: `pw-mlmodel-${uuid()}`,
|
||||
service: this.service.name,
|
||||
algorithm: 'Time Series',
|
||||
|
@ -31,7 +31,7 @@ export class PipelineClass extends EntityClass {
|
||||
},
|
||||
};
|
||||
entity = {
|
||||
name: `pw.pipeline%${uuid()}`,
|
||||
name: `pw-pipeline-${uuid()}`,
|
||||
displayName: `pw-pipeline-${uuid()}`,
|
||||
service: this.service.name,
|
||||
tasks: [{ name: 'snowflake_task' }],
|
||||
|
@ -34,7 +34,7 @@ export class SearchIndexClass extends EntityClass {
|
||||
},
|
||||
};
|
||||
entity = {
|
||||
name: `pw.search-index%${uuid()}`,
|
||||
name: `pw-search-index-${uuid()}`,
|
||||
displayName: `pw-search-index-${uuid()}`,
|
||||
service: this.service.name,
|
||||
fields: [],
|
||||
|
@ -31,7 +31,7 @@ export class TopicClass extends EntityClass {
|
||||
},
|
||||
},
|
||||
};
|
||||
private topicName = `pw.topic%${uuid()}`;
|
||||
private topicName = `pw-topic-${uuid()}`;
|
||||
private fqn = `${this.service.name}.${this.topicName}`;
|
||||
entity = {
|
||||
name: this.topicName,
|
||||
|
@ -54,3 +54,16 @@ export const createNewPage = async (browser: Browser) => {
|
||||
|
||||
return { page, apiContext, afterAction };
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the API context for the given page.
|
||||
* @param page The Playwright page object.
|
||||
* @returns An object containing the API context and a cleanup function.
|
||||
*/
|
||||
export const getApiContext = async (page: Page) => {
|
||||
const token = await getToken(page);
|
||||
const apiContext = await getAuthContext(token);
|
||||
const afterAction = async () => await apiContext.dispose();
|
||||
|
||||
return { apiContext, afterAction };
|
||||
};
|
||||
|
@ -12,8 +12,8 @@
|
||||
*/
|
||||
import { APIRequestContext, expect, Page } from '@playwright/test';
|
||||
import {
|
||||
EntityTypeEndpoint,
|
||||
ENTITY_PATH,
|
||||
EntityTypeEndpoint,
|
||||
} from '../support/entity/Entity.interface';
|
||||
import { UserClass } from '../support/user/UserClass';
|
||||
import { uuid } from './common';
|
||||
@ -52,8 +52,9 @@ export const setValueForProperty = async (data: {
|
||||
propertyName: string;
|
||||
value: string;
|
||||
propertyType: string;
|
||||
endpoint: EntityTypeEndpoint;
|
||||
}) => {
|
||||
const { page, propertyName, value, propertyType } = data;
|
||||
const { page, propertyName, value, propertyType, endpoint } = data;
|
||||
await page.click('[data-testid="custom_properties"]');
|
||||
|
||||
await expect(page.getByRole('cell', { name: propertyName })).toContainText(
|
||||
@ -66,6 +67,7 @@ export const setValueForProperty = async (data: {
|
||||
await editButton.scrollIntoViewIfNeeded();
|
||||
await editButton.click({ force: true });
|
||||
|
||||
const patchRequest = page.waitForResponse(`/api/v1/${endpoint}/*`);
|
||||
switch (propertyType) {
|
||||
case 'markdown':
|
||||
await page
|
||||
@ -97,11 +99,11 @@ export const setValueForProperty = async (data: {
|
||||
break;
|
||||
|
||||
case 'enum':
|
||||
await page.locator('#enumValues').click();
|
||||
await page.locator('#enumValues').fill(value);
|
||||
await page.locator('#enumValues').press('Enter');
|
||||
await page.click('#enumValues');
|
||||
await page.fill('#enumValues', value);
|
||||
await page.press('#enumValues', 'Enter');
|
||||
await page.mouse.click(0, 0);
|
||||
await page.locator('[data-testid="inline-save-btn"]').click();
|
||||
await page.click('[data-testid="inline-save-btn"]');
|
||||
|
||||
break;
|
||||
|
||||
@ -159,6 +161,7 @@ export const setValueForProperty = async (data: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
await patchRequest;
|
||||
};
|
||||
|
||||
export const validateValueForProperty = async (data: {
|
||||
@ -219,8 +222,8 @@ export const getPropertyValues = (
|
||||
|
||||
case 'number':
|
||||
return {
|
||||
value: '123',
|
||||
newValue: '456',
|
||||
value: '1234',
|
||||
newValue: '4567',
|
||||
};
|
||||
case 'duration':
|
||||
return {
|
||||
@ -320,8 +323,8 @@ export const createCustomPropertyForEntity = async (
|
||||
`/api/v1/metadata/types/${entitySchema.id}`,
|
||||
{
|
||||
data: {
|
||||
name: `cyCustomProperty${uuid()}`,
|
||||
description: `cyCustomProperty${uuid()}`,
|
||||
name: `pwCustomProperty${uuid()}`,
|
||||
description: `pwCustomProperty${uuid()}`,
|
||||
propertyType: {
|
||||
id: item.id ?? '',
|
||||
type: 'type',
|
||||
|
@ -31,9 +31,11 @@ export const visitEntityPage = async (data: {
|
||||
dataTestId: string;
|
||||
}) => {
|
||||
const { page, searchTerm, dataTestId } = data;
|
||||
|
||||
const waitForSearchResponse = page.waitForResponse(
|
||||
'/api/v1/search/query?q=*index=dataAsset*'
|
||||
);
|
||||
await page.getByTestId('searchBox').fill(searchTerm);
|
||||
await page.waitForResponse('/api/v1/search/query?q=*index=dataAsset*');
|
||||
await waitForSearchResponse;
|
||||
await page.getByTestId(dataTestId).getByTestId('data-name').click();
|
||||
await page.getByTestId('searchBox').clear();
|
||||
};
|
||||
@ -42,6 +44,7 @@ export const addOwner = async (
|
||||
page: Page,
|
||||
owner: string,
|
||||
type: 'Teams' | 'Users' = 'Users',
|
||||
endpoint: EntityTypeEndpoint,
|
||||
dataTestId?: string
|
||||
) => {
|
||||
await page.getByTestId('edit-owner').click();
|
||||
@ -68,7 +71,9 @@ export const addOwner = async (
|
||||
await page.waitForResponse(
|
||||
`/api/v1/search/query?q=*${encodeURIComponent(owner)}*`
|
||||
);
|
||||
const patchRequest = page.waitForResponse(`/api/v1/${endpoint}/*`);
|
||||
await page.getByRole('listitem', { name: owner }).click();
|
||||
await patchRequest;
|
||||
|
||||
await expect(page.getByTestId(dataTestId ?? 'owner-link')).toContainText(
|
||||
owner
|
||||
@ -79,6 +84,7 @@ export const updateOwner = async (
|
||||
page: Page,
|
||||
owner: string,
|
||||
type: 'Teams' | 'Users' = 'Users',
|
||||
endpoint: EntityTypeEndpoint,
|
||||
dataTestId?: string
|
||||
) => {
|
||||
await page.getByTestId('edit-owner').click();
|
||||
@ -90,20 +96,29 @@ export const updateOwner = async (
|
||||
await page.waitForResponse(
|
||||
`/api/v1/search/query?q=*${encodeURIComponent(owner)}*`
|
||||
);
|
||||
|
||||
const patchRequest = page.waitForResponse(`/api/v1/${endpoint}/*`);
|
||||
await page.getByRole('listitem', { name: owner }).click();
|
||||
await patchRequest;
|
||||
|
||||
await expect(page.getByTestId(dataTestId ?? 'owner-link')).toContainText(
|
||||
owner
|
||||
);
|
||||
};
|
||||
|
||||
export const removeOwner = async (page: Page, dataTestId?: string) => {
|
||||
export const removeOwner = async (
|
||||
page: Page,
|
||||
endpoint: EntityTypeEndpoint,
|
||||
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();
|
||||
|
||||
const patchRequest = page.waitForResponse(`/api/v1/${endpoint}/*`);
|
||||
await page.getByTestId('remove-owner').locator('svg').click();
|
||||
await patchRequest;
|
||||
|
||||
await expect(page.getByTestId(dataTestId ?? 'owner-link')).toContainText(
|
||||
'No Owner'
|
||||
@ -156,6 +171,9 @@ export const assignTag = async (
|
||||
`/api/v1/search/query?q=*${encodeURIComponent(tag)}*`
|
||||
);
|
||||
await page.getByTestId(`tag-${tag}`).click();
|
||||
|
||||
await expect(page.getByTestId('saveAssociatedTag')).toBeEnabled();
|
||||
|
||||
await page.getByTestId('saveAssociatedTag').click();
|
||||
|
||||
await expect(
|
||||
@ -183,6 +201,9 @@ export const removeTag = async (page: Page, tags: string[]) => {
|
||||
const patchRequest = page.waitForRequest(
|
||||
(request) => request.method() === 'PATCH'
|
||||
);
|
||||
|
||||
await expect(page.getByTestId('saveAssociatedTag')).toBeEnabled();
|
||||
|
||||
await page.getByTestId('saveAssociatedTag').click();
|
||||
await patchRequest;
|
||||
|
||||
@ -217,6 +238,9 @@ export const assignGlossaryTerm = async (
|
||||
`/api/v1/search/query?q=*${encodeURIComponent(glossaryTerm.displayName)}*`
|
||||
);
|
||||
await page.getByTestId(`tag-${glossaryTerm.fullyQualifiedName}`).click();
|
||||
|
||||
await expect(page.getByTestId('saveAssociatedTag')).toBeEnabled();
|
||||
|
||||
await page.getByTestId('saveAssociatedTag').click();
|
||||
|
||||
await expect(
|
||||
@ -248,6 +272,9 @@ export const removeGlossaryTerm = async (
|
||||
const patchRequest = page.waitForRequest(
|
||||
(request) => request.method() === 'PATCH'
|
||||
);
|
||||
|
||||
await expect(page.getByTestId('saveAssociatedTag')).toBeEnabled();
|
||||
|
||||
await page.getByTestId('saveAssociatedTag').click();
|
||||
await patchRequest;
|
||||
|
||||
@ -763,11 +790,12 @@ export const softDeleteEntity = async (
|
||||
await expect(page.locator('.ant-modal-title')).toContainText(displayName);
|
||||
|
||||
await page.fill('[data-testid="confirmation-text-input"]', 'DELETE');
|
||||
await page.click('[data-testid="confirm-button"]');
|
||||
|
||||
await page.waitForResponse(
|
||||
const deleteResponse = page.waitForResponse(
|
||||
`/api/v1/${endPoint}/*?hardDelete=false&recursive=true`
|
||||
);
|
||||
await page.click('[data-testid="confirm-button"]');
|
||||
|
||||
await deleteResponse;
|
||||
|
||||
await expect(page.locator('.Toastify__toast-body')).toHaveText(
|
||||
/deleted successfully!/
|
||||
@ -802,7 +830,7 @@ export const softDeleteEntity = async (
|
||||
|
||||
await expect(tableCount).toContainText('1');
|
||||
|
||||
await page.click(`[data-testid=${entityName}]`);
|
||||
await page.click(`[data-testid="${entityName}"]`);
|
||||
}
|
||||
|
||||
await restoreEntity(page);
|
||||
@ -832,10 +860,11 @@ export const hardDeleteEntity = async (
|
||||
await page.click('[data-testid="hard-delete-option"]');
|
||||
await page.check('[data-testid="hard-delete"]');
|
||||
await page.fill('[data-testid="confirmation-text-input"]', 'DELETE');
|
||||
await page.click('[data-testid="confirm-button"]');
|
||||
await page.waitForResponse(
|
||||
`**/api/v1/${endPoint}/*?hardDelete=true&recursive=true`
|
||||
const deleteResponse = page.waitForResponse(
|
||||
`/api/v1/${endPoint}/*?hardDelete=true&recursive=true`
|
||||
);
|
||||
await page.click('[data-testid="confirm-button"]');
|
||||
await deleteResponse;
|
||||
|
||||
await expect(page.locator('.Toastify__toast-body')).toHaveText(
|
||||
/deleted successfully!/
|
||||
|
Loading…
x
Reference in New Issue
Block a user