test: migrate recently viewed spec to playwright (#17741)

(cherry picked from commit 8568eaf65318a954cc34d471c3035d7f8bc6e7a6)
This commit is contained in:
Sachin Chaurasiya 2024-09-06 21:20:36 +05:30
parent 83079606e4
commit cee82c86eb
10 changed files with 116 additions and 153 deletions

View File

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

View File

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

View File

@ -19,6 +19,7 @@ import { EntityTypeEndpoint } from './Entity.interface';
import { EntityClass } from './EntityClass';
export class ContainerClass extends EntityClass {
private containerName = `pw-container-${uuid()}`;
service = {
name: `pw-storage-service-${uuid()}`,
serviceType: 'S3',
@ -36,8 +37,8 @@ export class ContainerClass extends EntityClass {
},
};
entity = {
name: `pw-container-${uuid()}`,
displayName: `pw-container-${uuid()}`,
name: this.containerName,
displayName: this.containerName,
service: this.service.name,
};

View File

@ -19,6 +19,7 @@ import { EntityTypeEndpoint } from './Entity.interface';
import { EntityClass } from './EntityClass';
export class DashboardClass extends EntityClass {
private dashboardName = `pw-dashboard-${uuid()}`;
service = {
name: `pw-dashboard-service-${uuid()}`,
serviceType: 'Superset',
@ -41,8 +42,8 @@ export class DashboardClass extends EntityClass {
service: this.service.name,
};
entity = {
name: `pw-dashboard-${uuid()}`,
displayName: `pw-dashboard-${uuid()}`,
name: this.dashboardName,
displayName: this.dashboardName,
service: this.service.name,
};

View File

@ -19,6 +19,7 @@ import { EntityTypeEndpoint } from './Entity.interface';
import { EntityClass } from './EntityClass';
export class DashboardDataModelClass extends EntityClass {
private dashboardDataModelName = `pw-dashboard-data-model-${uuid()}`;
service = {
name: `pw-dashboard-service-${uuid()}`,
serviceType: 'Superset',
@ -47,8 +48,8 @@ export class DashboardDataModelClass extends EntityClass {
];
entity = {
name: `pw-dashboard-data-model-${uuid()}`,
displayName: `pw-dashboard-data-model-${uuid()}`,
name: this.dashboardDataModelName,
displayName: this.dashboardDataModelName,
service: this.service.name,
columns: this.children,
dataModelType: 'SupersetDataModel',

View File

@ -27,6 +27,7 @@ type ResponseDataType = {
};
export class MlModelClass extends EntityClass {
private mlModelName = `pw-mlmodel-${uuid()}`;
service = {
name: `pw-ml-model-service-${uuid()}`,
serviceType: 'Mlflow',
@ -49,8 +50,8 @@ export class MlModelClass extends EntityClass {
];
entity = {
name: `pw-mlmodel-${uuid()}`,
displayName: `pw-mlmodel-${uuid()}`,
name: this.mlModelName,
displayName: this.mlModelName,
service: this.service.name,
algorithm: 'Time Series',
mlFeatures: this.children,

View File

@ -19,6 +19,7 @@ import { EntityTypeEndpoint } from './Entity.interface';
import { EntityClass } from './EntityClass';
export class PipelineClass extends EntityClass {
private pipelineName = `pw-pipeline-${uuid()}`;
service = {
name: `pw-pipeline-service-${uuid()}`,
serviceType: 'Dagster',
@ -36,8 +37,8 @@ export class PipelineClass extends EntityClass {
children = [{ name: 'snowflake_task' }];
entity = {
name: `pw-pipeline-${uuid()}`,
displayName: `pw-pipeline-${uuid()}`,
name: this.pipelineName,
displayName: this.pipelineName,
service: this.service.name,
tasks: this.children,
};

View File

@ -85,7 +85,7 @@ export class SearchIndexClass extends EntityClass {
entity = {
name: this.searchIndexName,
displayName: `pw-search-index-${uuid()}`,
displayName: this.searchIndexName,
service: this.service.name,
fields: this.children,
};

View File

@ -37,7 +37,7 @@ import {
} from '../../context/PermissionProvider/PermissionProvider.interface';
import { ClientErrors } from '../../enums/Axios.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 { Tag } from '../../generated/entity/classification/tag';
import { DashboardDataModel } from '../../generated/entity/data/dashboardDataModel';
@ -53,10 +53,12 @@ import {
} from '../../rest/dataModelsAPI';
import { postThread } from '../../rest/feedsAPI';
import {
addToRecentViewed,
getEntityMissingError,
sortTagsCaseInsensitive,
} from '../../utils/CommonUtils';
import { getSortedDataModelColumnTags } from '../../utils/DataModelsUtils';
import { getEntityName } from '../../utils/EntityUtils';
import { DEFAULT_ENTITY_PERMISSION } from '../../utils/PermissionsUtils';
import { getTierTags } from '../../utils/TableUtils';
import { updateTierTag } from '../../utils/TagsUtils';
@ -134,6 +136,15 @@ const DataModelsPage = () => {
include: Include.All,
});
setDataModelData(response);
addToRecentViewed({
displayName: getEntityName(response),
entityType: EntityType.DASHBOARD_DATA_MODEL,
fqn: response.fullyQualifiedName ?? '',
serviceType: response.serviceType,
timestamp: 0,
id: response.id,
});
} catch (error) {
showErrorToast(error as AxiosError);
setHasError(true);

View File

@ -125,6 +125,7 @@ jest.mock('../../rest/feedsAPI', () => ({
}));
jest.mock('../../utils/CommonUtils', () => ({
addToRecentViewed: jest.fn(),
getEntityMissingError: jest.fn(() => ENTITY_MISSING_ERROR),
sortTagsCaseInsensitive: jest.fn((tags) => tags),
}));