mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-11-12 17:02:23 +00:00
test: migrate recently viewed spec to playwright (#17741)
(cherry picked from commit 8568eaf65318a954cc34d471c3035d7f8bc6e7a6)
This commit is contained in:
parent
83079606e4
commit
cee82c86eb
@ -1,141 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2023 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 { interceptURL, verifyResponseStatusCode } from '../../common/common';
|
|
||||||
import {
|
|
||||||
createEntityTable,
|
|
||||||
createSingleLevelEntity,
|
|
||||||
hardDeleteService,
|
|
||||||
} from '../../common/EntityUtils';
|
|
||||||
import { visitEntityDetailsPage } from '../../common/Utils/Entity';
|
|
||||||
import { getToken } from '../../common/Utils/LocalStorage';
|
|
||||||
import {
|
|
||||||
DATABASE_SERVICE,
|
|
||||||
SINGLE_LEVEL_SERVICE,
|
|
||||||
STORED_PROCEDURE_DETAILS,
|
|
||||||
VISIT_ENTITIES_DATA,
|
|
||||||
} from '../../constants/EntityConstant';
|
|
||||||
import { SERVICE_CATEGORIES } from '../../constants/service.constants';
|
|
||||||
|
|
||||||
// Update list if we support this for other entities too
|
|
||||||
const RECENTLY_VIEW_ENTITIES = [
|
|
||||||
VISIT_ENTITIES_DATA.table,
|
|
||||||
VISIT_ENTITIES_DATA.dashboard,
|
|
||||||
VISIT_ENTITIES_DATA.topic,
|
|
||||||
VISIT_ENTITIES_DATA.pipeline,
|
|
||||||
VISIT_ENTITIES_DATA.mlmodel,
|
|
||||||
// ES issue
|
|
||||||
// VISIT_ENTITIES_DATA.storedProcedure,
|
|
||||||
];
|
|
||||||
|
|
||||||
describe('Recently viwed data assets', { tags: 'DataAssets' }, () => {
|
|
||||||
before(() => {
|
|
||||||
cy.login();
|
|
||||||
cy.getAllLocalStorage().then((data) => {
|
|
||||||
const token = getToken(data);
|
|
||||||
|
|
||||||
createEntityTable({
|
|
||||||
token,
|
|
||||||
...DATABASE_SERVICE,
|
|
||||||
tables: [DATABASE_SERVICE.entity],
|
|
||||||
});
|
|
||||||
SINGLE_LEVEL_SERVICE.forEach((data) => {
|
|
||||||
createSingleLevelEntity({
|
|
||||||
token,
|
|
||||||
...data,
|
|
||||||
entity: [data.entity],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// creating stored procedure
|
|
||||||
cy.request({
|
|
||||||
method: 'POST',
|
|
||||||
url: `/api/v1/storedProcedures`,
|
|
||||||
headers: { Authorization: `Bearer ${token}` },
|
|
||||||
body: STORED_PROCEDURE_DETAILS,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
after(() => {
|
|
||||||
cy.login();
|
|
||||||
cy.getAllLocalStorage().then((data) => {
|
|
||||||
const token = getToken(data);
|
|
||||||
|
|
||||||
hardDeleteService({
|
|
||||||
token,
|
|
||||||
serviceFqn: DATABASE_SERVICE.service.name,
|
|
||||||
serviceType: SERVICE_CATEGORIES.DATABASE_SERVICES,
|
|
||||||
});
|
|
||||||
SINGLE_LEVEL_SERVICE.forEach((data) => {
|
|
||||||
hardDeleteService({
|
|
||||||
token,
|
|
||||||
serviceFqn: data.service.name,
|
|
||||||
serviceType: data.serviceType,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
cy.login();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('recently view section should be present', () => {
|
|
||||||
cy.get('[data-testid="recently-viewed-widget"]')
|
|
||||||
.scrollIntoView()
|
|
||||||
.should('be.visible');
|
|
||||||
|
|
||||||
cy.get(
|
|
||||||
`[data-testid="recently-viewed-widget"] .right-panel-list-item`
|
|
||||||
).should('have.length', 0);
|
|
||||||
});
|
|
||||||
|
|
||||||
it(`recently view section should have at max list of 5 entity`, () => {
|
|
||||||
RECENTLY_VIEW_ENTITIES.map((entity, index) => {
|
|
||||||
visitEntityDetailsPage({
|
|
||||||
term: entity.term,
|
|
||||||
serviceName: entity.serviceName,
|
|
||||||
entity: entity.entity,
|
|
||||||
});
|
|
||||||
cy.get('[data-testid="entity-header-display-name"]').should(
|
|
||||||
'contain',
|
|
||||||
entity.term
|
|
||||||
);
|
|
||||||
interceptURL(
|
|
||||||
'GET',
|
|
||||||
'/api/v1/feed?type=Announcement&activeAnnouncement=true',
|
|
||||||
'getAnnouncements'
|
|
||||||
);
|
|
||||||
|
|
||||||
cy.clickOnLogo();
|
|
||||||
verifyResponseStatusCode('@getAnnouncements', 200);
|
|
||||||
|
|
||||||
// need to add manual wait as we are dependant on local storage for recently view data
|
|
||||||
cy.wait(500);
|
|
||||||
cy.get('[data-testid="recently-viewed-widget"]')
|
|
||||||
.scrollIntoView()
|
|
||||||
.should('be.visible');
|
|
||||||
cy.get(
|
|
||||||
`[data-testid="recently-viewed-widget"] [title="${entity.displayName}"]`,
|
|
||||||
{ timeout: 10000 }
|
|
||||||
)
|
|
||||||
.scrollIntoView()
|
|
||||||
.should('be.visible');
|
|
||||||
|
|
||||||
// Checking count since we will only show max 5 not more than that
|
|
||||||
cy.get(
|
|
||||||
`[data-testid="recently-viewed-widget"] .right-panel-list-item`
|
|
||||||
).should('have.length', index + 1);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
* 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, test } from '@playwright/test';
|
||||||
|
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 { 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 { createNewPage, redirectToHomePage } from '../../utils/common';
|
||||||
|
|
||||||
|
const entities = [
|
||||||
|
new ApiEndpointClass(),
|
||||||
|
new TableClass(),
|
||||||
|
new StoredProcedureClass(),
|
||||||
|
new DashboardClass(),
|
||||||
|
new PipelineClass(),
|
||||||
|
new TopicClass(),
|
||||||
|
new MlModelClass(),
|
||||||
|
new ContainerClass(),
|
||||||
|
new SearchIndexClass(),
|
||||||
|
new DashboardDataModelClass(),
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
// use the admin user to login
|
||||||
|
test.use({ storageState: 'playwright/.auth/admin.json' });
|
||||||
|
|
||||||
|
test.describe('Recently viewed data assets', () => {
|
||||||
|
test.beforeEach(async ({ page }) => {
|
||||||
|
await redirectToHomePage(page);
|
||||||
|
});
|
||||||
|
|
||||||
|
test.beforeAll(async ({ browser }) => {
|
||||||
|
const { afterAction, apiContext } = await createNewPage(browser);
|
||||||
|
for await (const entity of entities) {
|
||||||
|
await entity.create(apiContext);
|
||||||
|
}
|
||||||
|
await afterAction();
|
||||||
|
});
|
||||||
|
|
||||||
|
test.afterAll(async ({ browser }) => {
|
||||||
|
const { afterAction, apiContext } = await createNewPage(browser);
|
||||||
|
for await (const entity of entities) {
|
||||||
|
await entity.delete(apiContext);
|
||||||
|
}
|
||||||
|
await afterAction();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Recently viewed widget should be visible on the home page', async ({
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
|
test.slow(true);
|
||||||
|
|
||||||
|
for await (const entity of entities) {
|
||||||
|
await test.step(
|
||||||
|
`Check ${entity.getType()} in recently viewed widget `,
|
||||||
|
async () => {
|
||||||
|
await entity.visitEntityPage(page);
|
||||||
|
|
||||||
|
await page.waitForSelector(`[data-testid="breadcrumb"]`);
|
||||||
|
|
||||||
|
await redirectToHomePage(page);
|
||||||
|
|
||||||
|
await page.waitForSelector(`[data-testid="recently-viewed-widget"]`);
|
||||||
|
|
||||||
|
const selector = `[data-testid="recently-viewed-widget"] [title="${entity.entity.name}"]`;
|
||||||
|
|
||||||
|
await expect(page.locator(selector)).toBeVisible();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -19,6 +19,7 @@ import { EntityTypeEndpoint } from './Entity.interface';
|
|||||||
import { EntityClass } from './EntityClass';
|
import { EntityClass } from './EntityClass';
|
||||||
|
|
||||||
export class ContainerClass extends EntityClass {
|
export class ContainerClass extends EntityClass {
|
||||||
|
private containerName = `pw-container-${uuid()}`;
|
||||||
service = {
|
service = {
|
||||||
name: `pw-storage-service-${uuid()}`,
|
name: `pw-storage-service-${uuid()}`,
|
||||||
serviceType: 'S3',
|
serviceType: 'S3',
|
||||||
@ -36,8 +37,8 @@ export class ContainerClass extends EntityClass {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
entity = {
|
entity = {
|
||||||
name: `pw-container-${uuid()}`,
|
name: this.containerName,
|
||||||
displayName: `pw-container-${uuid()}`,
|
displayName: this.containerName,
|
||||||
service: this.service.name,
|
service: this.service.name,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -19,6 +19,7 @@ import { EntityTypeEndpoint } from './Entity.interface';
|
|||||||
import { EntityClass } from './EntityClass';
|
import { EntityClass } from './EntityClass';
|
||||||
|
|
||||||
export class DashboardClass extends EntityClass {
|
export class DashboardClass extends EntityClass {
|
||||||
|
private dashboardName = `pw-dashboard-${uuid()}`;
|
||||||
service = {
|
service = {
|
||||||
name: `pw-dashboard-service-${uuid()}`,
|
name: `pw-dashboard-service-${uuid()}`,
|
||||||
serviceType: 'Superset',
|
serviceType: 'Superset',
|
||||||
@ -41,8 +42,8 @@ export class DashboardClass extends EntityClass {
|
|||||||
service: this.service.name,
|
service: this.service.name,
|
||||||
};
|
};
|
||||||
entity = {
|
entity = {
|
||||||
name: `pw-dashboard-${uuid()}`,
|
name: this.dashboardName,
|
||||||
displayName: `pw-dashboard-${uuid()}`,
|
displayName: this.dashboardName,
|
||||||
service: this.service.name,
|
service: this.service.name,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -19,6 +19,7 @@ import { EntityTypeEndpoint } from './Entity.interface';
|
|||||||
import { EntityClass } from './EntityClass';
|
import { EntityClass } from './EntityClass';
|
||||||
|
|
||||||
export class DashboardDataModelClass extends EntityClass {
|
export class DashboardDataModelClass extends EntityClass {
|
||||||
|
private dashboardDataModelName = `pw-dashboard-data-model-${uuid()}`;
|
||||||
service = {
|
service = {
|
||||||
name: `pw-dashboard-service-${uuid()}`,
|
name: `pw-dashboard-service-${uuid()}`,
|
||||||
serviceType: 'Superset',
|
serviceType: 'Superset',
|
||||||
@ -47,8 +48,8 @@ export class DashboardDataModelClass extends EntityClass {
|
|||||||
];
|
];
|
||||||
|
|
||||||
entity = {
|
entity = {
|
||||||
name: `pw-dashboard-data-model-${uuid()}`,
|
name: this.dashboardDataModelName,
|
||||||
displayName: `pw-dashboard-data-model-${uuid()}`,
|
displayName: this.dashboardDataModelName,
|
||||||
service: this.service.name,
|
service: this.service.name,
|
||||||
columns: this.children,
|
columns: this.children,
|
||||||
dataModelType: 'SupersetDataModel',
|
dataModelType: 'SupersetDataModel',
|
||||||
|
|||||||
@ -27,6 +27,7 @@ type ResponseDataType = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export class MlModelClass extends EntityClass {
|
export class MlModelClass extends EntityClass {
|
||||||
|
private mlModelName = `pw-mlmodel-${uuid()}`;
|
||||||
service = {
|
service = {
|
||||||
name: `pw-ml-model-service-${uuid()}`,
|
name: `pw-ml-model-service-${uuid()}`,
|
||||||
serviceType: 'Mlflow',
|
serviceType: 'Mlflow',
|
||||||
@ -49,8 +50,8 @@ export class MlModelClass extends EntityClass {
|
|||||||
];
|
];
|
||||||
|
|
||||||
entity = {
|
entity = {
|
||||||
name: `pw-mlmodel-${uuid()}`,
|
name: this.mlModelName,
|
||||||
displayName: `pw-mlmodel-${uuid()}`,
|
displayName: this.mlModelName,
|
||||||
service: this.service.name,
|
service: this.service.name,
|
||||||
algorithm: 'Time Series',
|
algorithm: 'Time Series',
|
||||||
mlFeatures: this.children,
|
mlFeatures: this.children,
|
||||||
|
|||||||
@ -19,6 +19,7 @@ import { EntityTypeEndpoint } from './Entity.interface';
|
|||||||
import { EntityClass } from './EntityClass';
|
import { EntityClass } from './EntityClass';
|
||||||
|
|
||||||
export class PipelineClass extends EntityClass {
|
export class PipelineClass extends EntityClass {
|
||||||
|
private pipelineName = `pw-pipeline-${uuid()}`;
|
||||||
service = {
|
service = {
|
||||||
name: `pw-pipeline-service-${uuid()}`,
|
name: `pw-pipeline-service-${uuid()}`,
|
||||||
serviceType: 'Dagster',
|
serviceType: 'Dagster',
|
||||||
@ -36,8 +37,8 @@ export class PipelineClass extends EntityClass {
|
|||||||
children = [{ name: 'snowflake_task' }];
|
children = [{ name: 'snowflake_task' }];
|
||||||
|
|
||||||
entity = {
|
entity = {
|
||||||
name: `pw-pipeline-${uuid()}`,
|
name: this.pipelineName,
|
||||||
displayName: `pw-pipeline-${uuid()}`,
|
displayName: this.pipelineName,
|
||||||
service: this.service.name,
|
service: this.service.name,
|
||||||
tasks: this.children,
|
tasks: this.children,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -85,7 +85,7 @@ export class SearchIndexClass extends EntityClass {
|
|||||||
|
|
||||||
entity = {
|
entity = {
|
||||||
name: this.searchIndexName,
|
name: this.searchIndexName,
|
||||||
displayName: `pw-search-index-${uuid()}`,
|
displayName: this.searchIndexName,
|
||||||
service: this.service.name,
|
service: this.service.name,
|
||||||
fields: this.children,
|
fields: this.children,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -37,7 +37,7 @@ import {
|
|||||||
} from '../../context/PermissionProvider/PermissionProvider.interface';
|
} from '../../context/PermissionProvider/PermissionProvider.interface';
|
||||||
import { ClientErrors } from '../../enums/Axios.enum';
|
import { ClientErrors } from '../../enums/Axios.enum';
|
||||||
import { ERROR_PLACEHOLDER_TYPE } from '../../enums/common.enum';
|
import { ERROR_PLACEHOLDER_TYPE } from '../../enums/common.enum';
|
||||||
import { TabSpecificField } from '../../enums/entity.enum';
|
import { EntityType, TabSpecificField } from '../../enums/entity.enum';
|
||||||
import { CreateThread } from '../../generated/api/feed/createThread';
|
import { CreateThread } from '../../generated/api/feed/createThread';
|
||||||
import { Tag } from '../../generated/entity/classification/tag';
|
import { Tag } from '../../generated/entity/classification/tag';
|
||||||
import { DashboardDataModel } from '../../generated/entity/data/dashboardDataModel';
|
import { DashboardDataModel } from '../../generated/entity/data/dashboardDataModel';
|
||||||
@ -53,10 +53,12 @@ import {
|
|||||||
} from '../../rest/dataModelsAPI';
|
} from '../../rest/dataModelsAPI';
|
||||||
import { postThread } from '../../rest/feedsAPI';
|
import { postThread } from '../../rest/feedsAPI';
|
||||||
import {
|
import {
|
||||||
|
addToRecentViewed,
|
||||||
getEntityMissingError,
|
getEntityMissingError,
|
||||||
sortTagsCaseInsensitive,
|
sortTagsCaseInsensitive,
|
||||||
} from '../../utils/CommonUtils';
|
} from '../../utils/CommonUtils';
|
||||||
import { getSortedDataModelColumnTags } from '../../utils/DataModelsUtils';
|
import { getSortedDataModelColumnTags } from '../../utils/DataModelsUtils';
|
||||||
|
import { getEntityName } from '../../utils/EntityUtils';
|
||||||
import { DEFAULT_ENTITY_PERMISSION } from '../../utils/PermissionsUtils';
|
import { DEFAULT_ENTITY_PERMISSION } from '../../utils/PermissionsUtils';
|
||||||
import { getTierTags } from '../../utils/TableUtils';
|
import { getTierTags } from '../../utils/TableUtils';
|
||||||
import { updateTierTag } from '../../utils/TagsUtils';
|
import { updateTierTag } from '../../utils/TagsUtils';
|
||||||
@ -134,6 +136,15 @@ const DataModelsPage = () => {
|
|||||||
include: Include.All,
|
include: Include.All,
|
||||||
});
|
});
|
||||||
setDataModelData(response);
|
setDataModelData(response);
|
||||||
|
|
||||||
|
addToRecentViewed({
|
||||||
|
displayName: getEntityName(response),
|
||||||
|
entityType: EntityType.DASHBOARD_DATA_MODEL,
|
||||||
|
fqn: response.fullyQualifiedName ?? '',
|
||||||
|
serviceType: response.serviceType,
|
||||||
|
timestamp: 0,
|
||||||
|
id: response.id,
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
showErrorToast(error as AxiosError);
|
showErrorToast(error as AxiosError);
|
||||||
setHasError(true);
|
setHasError(true);
|
||||||
|
|||||||
@ -125,6 +125,7 @@ jest.mock('../../rest/feedsAPI', () => ({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
jest.mock('../../utils/CommonUtils', () => ({
|
jest.mock('../../utils/CommonUtils', () => ({
|
||||||
|
addToRecentViewed: jest.fn(),
|
||||||
getEntityMissingError: jest.fn(() => ENTITY_MISSING_ERROR),
|
getEntityMissingError: jest.fn(() => ENTITY_MISSING_ERROR),
|
||||||
sortTagsCaseInsensitive: jest.fn((tags) => tags),
|
sortTagsCaseInsensitive: jest.fn((tags) => tags),
|
||||||
}));
|
}));
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user