From 2260b8da326a1ce9303b98601918f96d2a3f2534 Mon Sep 17 00:00:00 2001
From: Chirag Madlani <12962843+chirag-madlani@users.noreply.github.com>
Date: Wed, 19 Jul 2023 21:24:02 +0530
Subject: [PATCH] test(ui): e2e tests for recently viewed (#12506)
---
.../resources/ui/cypress/common/common.js | 2 +-
.../ui/cypress/constants/constants.js | 1 +
.../ui/cypress/e2e/Features/Following.spec.js | 125 ++++++++++++++++++
.../e2e/Features/RecentlyViewed.spec.js | 76 +++++++++++
.../RightSidebar/RightSidebar.component.tsx | 2 +-
.../PipelineDetails.component.tsx | 12 +-
.../PipelineDetails.interface.ts | 2 -
.../PipelineDetailsPage.component.tsx | 83 +++++++-----
8 files changed, 259 insertions(+), 44 deletions(-)
create mode 100644 openmetadata-ui/src/main/resources/ui/cypress/e2e/Features/Following.spec.js
create mode 100644 openmetadata-ui/src/main/resources/ui/cypress/e2e/Features/RecentlyViewed.spec.js
diff --git a/openmetadata-ui/src/main/resources/ui/cypress/common/common.js b/openmetadata-ui/src/main/resources/ui/cypress/common/common.js
index 3811c34ffd3..241b56c8a42 100644
--- a/openmetadata-ui/src/main/resources/ui/cypress/common/common.js
+++ b/openmetadata-ui/src/main/resources/ui/cypress/common/common.js
@@ -1207,7 +1207,7 @@ export const followAndOwnTheEntity = (termObj) => {
// Check followed entity on mydata page
cy.get('[data-testid="following-data-container"]')
- .find(`[data-testid="Following data-${termObj.displayName}"]`)
+ .find(`[data-testid="following-${termObj.displayName}"]`)
.should('be.visible');
// Check owned entity
diff --git a/openmetadata-ui/src/main/resources/ui/cypress/constants/constants.js b/openmetadata-ui/src/main/resources/ui/cypress/constants/constants.js
index cc5e61eb8ae..26b69c51b03 100644
--- a/openmetadata-ui/src/main/resources/ui/cypress/constants/constants.js
+++ b/openmetadata-ui/src/main/resources/ui/cypress/constants/constants.js
@@ -132,6 +132,7 @@ export const SEARCH_ENTITY_MLMODEL = {
term: 'eta_predictions',
entity: MYDATA_SUMMARY_OPTIONS.mlmodels,
serviceName: 'mlflow_svc',
+ displayName: 'ETA Predictions',
},
};
diff --git a/openmetadata-ui/src/main/resources/ui/cypress/e2e/Features/Following.spec.js b/openmetadata-ui/src/main/resources/ui/cypress/e2e/Features/Following.spec.js
new file mode 100644
index 00000000000..c5682ba0577
--- /dev/null
+++ b/openmetadata-ui/src/main/resources/ui/cypress/e2e/Features/Following.spec.js
@@ -0,0 +1,125 @@
+/*
+ * 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,
+ visitEntityDetailsPage,
+} from '../../common/common';
+import {
+ SEARCH_ENTITY_DASHBOARD,
+ SEARCH_ENTITY_MLMODEL,
+ SEARCH_ENTITY_PIPELINE,
+ SEARCH_ENTITY_TABLE,
+ SEARCH_ENTITY_TOPIC,
+} from '../../constants/constants';
+
+// eslint-disable-next-line spaced-comment
+///
+
+// Update list if we support this for other entities too
+const FOLLOWING_ENTITIES = [
+ SEARCH_ENTITY_TABLE.table_2,
+ SEARCH_ENTITY_DASHBOARD.dashboard_1,
+ SEARCH_ENTITY_TOPIC.topic_1,
+ SEARCH_ENTITY_PIPELINE.pipeline_1,
+ SEARCH_ENTITY_MLMODEL.mlmodel_2,
+];
+
+const followEntity = ({ term, serviceName, entity }, isUnfollow) => {
+ visitEntityDetailsPage(term, serviceName, entity);
+
+ interceptURL(
+ isUnfollow ? 'DELETE' : 'PUT',
+ isUnfollow ? '/api/v1/*/*/followers/*' : '/api/v1/*/*/followers',
+ 'waitAfterFollow'
+ );
+ cy.get('[data-testid="entity-follow-button"]')
+ .scrollIntoView()
+ .should('be.visible')
+ .click();
+
+ verifyResponseStatusCode('@waitAfterFollow', 200);
+};
+
+describe('Following data assets', () => {
+ beforeEach(() => {
+ cy.login();
+ });
+
+ it('following section should be present', () => {
+ cy.get('[data-testid="following-data-container"]')
+ .scrollIntoView()
+ .should('be.visible');
+
+ cy.get('[data-testid="following-data-container"]').contains(
+ 'You have not followed anything yet.'
+ );
+ cy.get(
+ `[data-testid="following-data-container"] .right-panel-list-item`
+ ).should('have.length', 0);
+ });
+
+ // Follow entity
+ FOLLOWING_ENTITIES.map((entity, index) => {
+ it(`following section should have ${entity.term} followed`, () => {
+ followEntity(entity);
+
+ interceptURL(
+ 'GET',
+ '/api/v1/feed?type=Announcement&activeAnnouncement=true',
+ 'getAnnoucemenets'
+ );
+
+ cy.clickOnLogo();
+ verifyResponseStatusCode('@getAnnoucemenets', 200);
+
+ cy.get(`[data-testid="following-${entity.displayName}"]`).should(
+ 'be.visible'
+ );
+
+ // Checking count of following
+ cy.get(`[data-testid="following-data"]`).should('contain', index + 1);
+ });
+ });
+
+ // UnFollow entity
+ FOLLOWING_ENTITIES.map((entity, index) => {
+ it(`unfollowing entity ${entity.term} should removed from following section`, () => {
+ followEntity(entity, true);
+
+ interceptURL(
+ 'GET',
+ '/api/v1/feed?type=Announcement&activeAnnouncement=true',
+ 'getAnnoucemenets'
+ );
+
+ cy.clickOnLogo();
+ verifyResponseStatusCode('@getAnnoucemenets', 200);
+
+ cy.get(`[data-testid="following-${entity.displayName}"]`).should(
+ 'not.exist'
+ );
+
+ if (index === FOLLOWING_ENTITIES.length - 1) {
+ // Checking count of following
+ cy.get(`[data-testid="following-data"]`).should('not.exist');
+ } else {
+ // Checking count of following
+ cy.get(`[data-testid="following-data"]`).should(
+ 'contain',
+ FOLLOWING_ENTITIES.length - (index + 1)
+ );
+ }
+ });
+ });
+});
diff --git a/openmetadata-ui/src/main/resources/ui/cypress/e2e/Features/RecentlyViewed.spec.js b/openmetadata-ui/src/main/resources/ui/cypress/e2e/Features/RecentlyViewed.spec.js
new file mode 100644
index 00000000000..35691838244
--- /dev/null
+++ b/openmetadata-ui/src/main/resources/ui/cypress/e2e/Features/RecentlyViewed.spec.js
@@ -0,0 +1,76 @@
+/*
+ * 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,
+ visitEntityDetailsPage,
+} from '../../common/common';
+import {
+ SEARCH_ENTITY_DASHBOARD,
+ SEARCH_ENTITY_MLMODEL,
+ SEARCH_ENTITY_PIPELINE,
+ SEARCH_ENTITY_TABLE,
+ SEARCH_ENTITY_TOPIC,
+} from '../../constants/constants';
+
+// eslint-disable-next-line spaced-comment
+///
+
+// Update list if we support this for other entities too
+const RECENTLY_VIEW_ENTITIES = [
+ SEARCH_ENTITY_TABLE.table_2,
+ SEARCH_ENTITY_DASHBOARD.dashboard_1,
+ SEARCH_ENTITY_TOPIC.topic_1,
+ SEARCH_ENTITY_PIPELINE.pipeline_1,
+ SEARCH_ENTITY_MLMODEL.mlmodel_2,
+];
+
+describe('Recently viwed data assets', () => {
+ beforeEach(() => {
+ cy.login();
+ });
+
+ it('recently view section should be present', () => {
+ cy.get('[data-testid="recently-viewed-container"]')
+ .scrollIntoView()
+ .should('be.visible');
+
+ cy.get(
+ `[data-testid="recently-viewed-container"] .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(entity.term, entity.serviceName, entity.entity);
+
+ interceptURL(
+ 'GET',
+ '/api/v1/feed?type=Announcement&activeAnnouncement=true',
+ 'getAnnoucemenets'
+ );
+
+ cy.clickOnLogo();
+ verifyResponseStatusCode('@getAnnoucemenets', 200);
+
+ cy.get(
+ `[data-testid="recently-viewed-container"] [title="${entity.displayName}"]`
+ ).should('be.visible');
+
+ // Checking count since we will only show max 5 not more than that
+ cy.get(
+ `[data-testid="recently-viewed-container"] .right-panel-list-item`
+ ).should('have.length', index > 4 ? 5 : index + 1);
+ });
+ });
+});
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/MyData/RightSidebar/RightSidebar.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/MyData/RightSidebar/RightSidebar.component.tsx
index 4552d1c694d..69a44dd7063 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/MyData/RightSidebar/RightSidebar.component.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/MyData/RightSidebar/RightSidebar.component.tsx
@@ -125,7 +125,7 @@ const RightSidebar = ({
headerTextLabel={t('label.following')}
loading={isLoadingOwnedData}
noDataPlaceholder={t('message.not-followed-anything')}
- testIDText="Following data"
+ testIDText="following"
/>
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/PipelineDetails/PipelineDetails.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/PipelineDetails/PipelineDetails.component.tsx
index 10a43b4a6f5..72f780e1c4b 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/PipelineDetails/PipelineDetails.component.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/PipelineDetails/PipelineDetails.component.tsx
@@ -77,7 +77,6 @@ import { ResourceEntity } from '../PermissionProvider/PermissionProvider.interfa
import { PipeLineDetailsProp } from './PipelineDetails.interface';
const PipelineDetails = ({
- followers,
pipelineDetails,
fetchPipeline,
descriptionUpdateHandler,
@@ -93,6 +92,7 @@ const PipelineDetails = ({
const { tab } = useParams<{ tab: EntityTabs }>();
const { t } = useTranslation();
const { postFeed, deleteFeed, updateFeed } = useActivityFeedProvider();
+ const userID = getCurrentUserId();
const {
deleted,
owner,
@@ -102,6 +102,7 @@ const PipelineDetails = ({
tier,
tags,
entityFqn,
+ followers,
} = useMemo(() => {
return {
deleted: pipelineDetails.deleted,
@@ -114,6 +115,7 @@ const PipelineDetails = ({
tags: getTagsWithoutTier(pipelineDetails.tags ?? []),
entityName: getEntityName(pipelineDetails),
entityFqn: pipelineDetails.fullyQualifiedName ?? '',
+ followers: pipelineDetails.followers ?? [],
};
}, [pipelineDetails]);
@@ -193,8 +195,8 @@ const PipelineDetails = ({
}, [pipelineDetails.id]);
const isFollowing = useMemo(
- () => followers.some(({ id }: { id: string }) => id === getCurrentUserId()),
- [followers]
+ () => followers.some(({ id }: { id: string }) => id === userID),
+ [followers, userID]
);
const onTaskUpdate = async (taskDescription: string) => {
@@ -297,13 +299,13 @@ const PipelineDetails = ({
}
};
- const followPipeline = async () => {
+ const followPipeline = useCallback(async () => {
if (isFollowing) {
await unFollowPipelineHandler(getEntityFeedCount);
} else {
await followPipelineHandler(getEntityFeedCount);
}
- };
+ }, [isFollowing, followPipelineHandler, unFollowPipelineHandler]);
const onThreadLinkSelect = (link: string, threadType?: ThreadType) => {
setThreadLink(link);
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/PipelineDetails/PipelineDetails.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/PipelineDetails/PipelineDetails.interface.ts
index 9e14946fbf8..8ada1455875 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/PipelineDetails/PipelineDetails.interface.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/components/PipelineDetails/PipelineDetails.interface.ts
@@ -13,13 +13,11 @@
import { Operation } from 'fast-json-patch';
import { Pipeline } from '../../generated/entity/data/pipeline';
-import { EntityReference } from '../../generated/type/entityReference';
import { Paging } from '../../generated/type/paging';
export interface PipeLineDetailsProp {
pipelineFQN: string;
pipelineDetails: Pipeline;
- followers: Array
;
paging: Paging;
fetchPipeline: () => void;
followPipelineHandler: (fetchCount: () => void) => Promise;
diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/PipelineDetails/PipelineDetailsPage.component.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/PipelineDetails/PipelineDetailsPage.component.tsx
index f0b4636b761..d9f8e640d3d 100644
--- a/openmetadata-ui/src/main/resources/ui/src/pages/PipelineDetails/PipelineDetailsPage.component.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/pages/PipelineDetails/PipelineDetailsPage.component.tsx
@@ -33,7 +33,6 @@ import {
import { getVersionPath } from '../../constants/constants';
import { EntityType } from '../../enums/entity.enum';
import { Pipeline } from '../../generated/entity/data/pipeline';
-import { EntityReference } from '../../generated/type/entityReference';
import { Paging } from '../../generated/type/paging';
import {
addToRecentViewed,
@@ -59,7 +58,6 @@ const PipelineDetailsPage = () => {
);
const [isLoading, setLoading] = useState(true);
- const [followers, setFollowers] = useState>([]);
const [isError, setIsError] = useState(false);
@@ -71,6 +69,8 @@ const PipelineDetailsPage = () => {
const { getEntityPermissionByFqn } = usePermissionProvider();
+ const { followers = [] } = pipelineDetails;
+
const fetchResourcePermission = async (entityFqn: string) => {
setLoading(true);
try {
@@ -143,39 +143,53 @@ const PipelineDetailsPage = () => {
}
};
- const followPipeline = async (fetchCount: () => void) => {
- try {
- const res = await addFollower(pipelineId, USERId);
- const { newValue } = res.changeDescription.fieldsAdded[0];
- setFollowers([...followers, ...newValue]);
- fetchCount();
- } catch (error) {
- showErrorToast(
- error as AxiosError,
- t('server.entity-follow-error', {
- entity: getEntityName(pipelineDetails),
- })
- );
- }
- };
+ const followPipeline = useCallback(
+ async (fetchCount: () => void) => {
+ try {
+ const res = await addFollower(pipelineId, USERId);
+ const { newValue } = res.changeDescription.fieldsAdded[0];
+ const newFollowers = [...(followers ?? []), ...newValue];
+ setPipelineDetails((prev) => {
+ return { ...prev, followers: newFollowers };
+ });
- const unFollowPipeline = async (fetchCount: () => void) => {
- try {
- const res = await removeFollower(pipelineId, USERId);
- const { oldValue } = res.changeDescription.fieldsDeleted[0];
- setFollowers(
- followers.filter((follower) => follower.id !== oldValue[0].id)
- );
- fetchCount();
- } catch (error) {
- showErrorToast(
- error as AxiosError,
- t('server.entity-unfollow-error', {
- entity: getEntityName(pipelineDetails),
- })
- );
- }
- };
+ fetchCount();
+ } catch (error) {
+ showErrorToast(
+ error as AxiosError,
+ t('server.entity-follow-error', {
+ entity: getEntityName(pipelineDetails),
+ })
+ );
+ }
+ },
+ [followers, USERId]
+ );
+
+ const unFollowPipeline = useCallback(
+ async (fetchCount: () => void) => {
+ try {
+ const res = await removeFollower(pipelineId, USERId);
+ const { oldValue } = res.changeDescription.fieldsDeleted[0];
+ setPipelineDetails((prev) => ({
+ ...prev,
+ followers: followers.filter(
+ (follower) => follower.id !== oldValue[0].id
+ ),
+ }));
+
+ fetchCount();
+ } catch (error) {
+ showErrorToast(
+ error as AxiosError,
+ t('server.entity-unfollow-error', {
+ entity: getEntityName(pipelineDetails),
+ })
+ );
+ }
+ },
+ [followers, USERId]
+ );
const descriptionUpdateHandler = async (updatedPipeline: Pipeline) => {
try {
@@ -261,7 +275,6 @@ const PipelineDetailsPage = () => {
descriptionUpdateHandler={descriptionUpdateHandler}
fetchPipeline={() => fetchPipelineDetail(pipelineFQN)}
followPipelineHandler={followPipeline}
- followers={followers}
paging={paging}
pipelineDetails={pipelineDetails}
pipelineFQN={pipelineFQN}