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}