diff --git a/openmetadata-ui/src/main/resources/ui/cypress/common/TaskUtils.js b/openmetadata-ui/src/main/resources/ui/cypress/common/TaskUtils.js
new file mode 100644
index 00000000000..1a2b747681f
--- /dev/null
+++ b/openmetadata-ui/src/main/resources/ui/cypress/common/TaskUtils.js
@@ -0,0 +1,108 @@
+/*
+ * 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 {
+ descriptionBox,
+ interceptURL,
+ toastNotification,
+ verifyResponseStatusCode,
+} from './common';
+
+const assignee = 'admin';
+const secondAssignee = 'aaron_johnson0';
+
+export const verifyTaskDetails = (regexPattern) => {
+ cy.get('#task-panel').should('be.visible');
+ cy.get('[data-testid="task-title"]')
+ .invoke('text')
+ .then((textContent) => {
+ const matches = textContent.match(regexPattern);
+
+ expect(matches).to.not.be.null;
+ });
+
+ cy.get('[data-testid="owner-link"]').contains(assignee);
+
+ cy.get(`[data-testid="assignee-${assignee}"]`).should('be.visible');
+};
+
+export const editAssignee = () => {
+ interceptURL('PATCH', 'api/v1/feed/*', 'editAssignee');
+
+ cy.get('[data-testid="edit-assignees"]').click();
+
+ cy.get('[data-testid="select-assignee"] > .ant-select-selector').type(
+ secondAssignee
+ );
+ // select value from dropdown
+ verifyResponseStatusCode('@suggestApi', 200);
+
+ cy.get(`[data-testid="assignee-option-${secondAssignee}"]`)
+ .should('be.visible')
+ .trigger('mouseover')
+ .trigger('click');
+
+ cy.clickOutside();
+
+ cy.get('[data-testid="inline-save-btn"]').click();
+
+ verifyResponseStatusCode('@editAssignee', 200);
+
+ cy.get(`[data-testid="assignee-${assignee}"]`).should('be.visible');
+};
+
+export const createDescriptionTask = (value) => {
+ interceptURL('POST', 'api/v1/feed', 'createTask');
+
+ cy.get('#title').should(
+ 'have.value',
+ `Update description for table ${value.term}`
+ );
+
+ cy.get('[data-testid="select-assignee"] > .ant-select-selector').type(
+ assignee
+ );
+ // select value from dropdown
+ verifyResponseStatusCode('@suggestApi', 200);
+
+ cy.get(`[data-testid="assignee-option-${assignee}"]`)
+ .should('be.visible')
+ .trigger('mouseover')
+ .trigger('click');
+
+ cy.clickOutside();
+
+ cy.get(descriptionBox).scrollIntoView().clear().type('Updated description');
+
+ cy.get('button[type="submit"]').click();
+ verifyResponseStatusCode('@createTask', 201);
+ toastNotification('Task created successfully.');
+};
+
+export const createAndUpdateDescriptionTask = (value) => {
+ createDescriptionTask(value);
+
+ // verify the task details
+ verifyTaskDetails(/#(\d+) UpdateDescriptionfordescription/);
+
+ // edit task assignees
+ editAssignee();
+
+ // Accept the description suggestion which is created
+ cy.get('.ant-btn-compact-first-item').contains('Accept Suggestion').click();
+
+ verifyResponseStatusCode('@taskResolve', 200);
+
+ toastNotification('Task resolved successfully');
+
+ verifyResponseStatusCode('@entityFeed', 200);
+};
diff --git a/openmetadata-ui/src/main/resources/ui/cypress/e2e/Features/ActivityFeed.spec.js b/openmetadata-ui/src/main/resources/ui/cypress/e2e/Features/ActivityFeed.spec.js
new file mode 100644
index 00000000000..72cf03eb80a
--- /dev/null
+++ b/openmetadata-ui/src/main/resources/ui/cypress/e2e/Features/ActivityFeed.spec.js
@@ -0,0 +1,254 @@
+/*
+ * 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 { createDescriptionTask } from '../../common/TaskUtils';
+import { SEARCH_ENTITY_TABLE } from '../../constants/constants';
+
+// eslint-disable-next-line spaced-comment
+///
+
+const reactOnFeed = (feedSelector, reaction) => {
+ cy.get(feedSelector).within(() => {
+ cy.get('[data-testid="feed-actions"]').invoke('show');
+ cy.get('[data-testid="feed-actions"]').within(() => {
+ cy.get('[data-testid="add-reactions"]').click();
+ });
+ });
+
+ cy.get(
+ `#reaction-popover [data-testid="reaction-button"][title="${reaction}"]`
+ ).click();
+};
+
+describe('Recently viwed data assets', () => {
+ beforeEach(() => {
+ cy.login();
+ });
+
+ it('Feed widget should be visible', () => {
+ cy.get('[data-testid="activity-feed-widget"]').as('feedWidget');
+ cy.get('@feedWidget').should('be.visible');
+ cy.get('@feedWidget').should('contain', 'All');
+ cy.get('@feedWidget').should('contain', '@Mentions');
+ cy.get('@feedWidget').should('contain', 'Tasks');
+ cy.get('@feedWidget').should('contain', '0');
+ });
+
+ it('Feed widget should have some feeds', () => {
+ cy.get(
+ '[data-testid="activity-feed-widget"] [data-testid="message-container"]'
+ ).should('have.length.gte', 1);
+ });
+
+ it('Emoji reaction on feed should be working fine', () => {
+ // Assign reaction for latest feed
+ [
+ 'thumbsUp',
+ 'thumbsDown',
+ 'laugh',
+ 'hooray',
+ 'confused',
+ 'heart',
+ 'eyes',
+ 'rocket',
+ ].map((reaction) =>
+ reactOnFeed(
+ '[data-testid="activity-feed-widget"] [data-testid="message-container"]:first-child',
+ reaction
+ )
+ );
+
+ // Verify if reaction is working or not
+ cy.get(
+ '[data-testid="activity-feed-widget"] [data-testid="message-container"]:first-child'
+ ).within(() => {
+ ['🚀', '😕', '👀', '❤️', '🎉', '😄', '👎', '👍'].map((reaction) =>
+ cy
+ .get('[data-testid="feed-reaction-container"]')
+ .should('contain', reaction)
+ );
+ });
+ });
+
+ it('User should be able to reply to feed', () => {
+ interceptURL('GET', '/api/v1/feed/*', 'fetchFeed');
+ cy.get(
+ '[data-testid="activity-feed-widget"] [data-testid="message-container"]:first-child'
+ ).within(() => {
+ cy.get('[data-testid="feed-actions"]').invoke('show');
+ cy.get('[data-testid="feed-actions"]').within(() => {
+ cy.get('[data-testid="add-reply"]').click();
+ });
+ });
+ verifyResponseStatusCode('@fetchFeed', 200);
+
+ interceptURL('POST', '/api/v1/feed/*/posts', 'postReply');
+ interceptURL(
+ 'GET',
+ '/api/v1/search/suggest?q=aa&index=user_search_index%2Cteam_search_index',
+ 'suggestUser'
+ );
+ interceptURL(
+ 'GET',
+ // eslint-disable-next-line max-len
+ '/api/v1/search/suggest?q=dim_add&index=dashboard_search_index%2Ctable_search_index%2Ctopic_search_index%2Cpipeline_search_index%2Cmlmodel_search_index%2Ccontainer_search_index%2Cglossary_search_index%2Ctag_search_index',
+ 'suggestAsset'
+ );
+
+ cy.get('[data-testid="editor-wrapper"]').should('be.visible');
+ cy.get(
+ '[data-testid="editor-wrapper"] [contenteditable="true"].ql-editor'
+ ).as('editor');
+ cy.get('@editor').click();
+ cy.get('@editor').type('Cypress has replied here. Thanks! @aa');
+
+ verifyResponseStatusCode('@suggestUser', 200);
+ cy.get('[data-value="@aaron_johnson0"]').click();
+ cy.get('@editor').type(' #dim_add');
+ verifyResponseStatusCode('@suggestAsset', 200);
+ cy.get('[data-value="#table/dim_address"]').click();
+
+ cy.get('[data-testid="send-button"]')
+ .should('be.visible')
+ .and('not.be.disabled');
+ cy.get('[data-testid="send-button"]').click();
+
+ verifyResponseStatusCode('@postReply', 201);
+
+ cy.get('[data-testid="replies"]').should('contain', '1 reply');
+ cy.get('[data-testid="replies"] .activity-feed-card.activity-feed-card-v1')
+ .children('.ant-row')
+ .eq(1)
+ .invoke('text')
+ .should(
+ 'eq',
+ 'Cypress has replied here. Thanks! @aaron_johnson0 #table/dim_address\n'
+ );
+
+ cy.get('[data-testid="closeDrawer"]').click();
+
+ cy.get(
+ '[data-testid="activity-feed-widget"] [data-testid="message-container"]:first-child'
+ ).within(() => {
+ cy.get('[data-testid="thread-count"]').should('contain', 1);
+ });
+ });
+
+ it('Mention should work for the feed reply', () => {
+ interceptURL('GET', '/api/v1/feed/*', 'fetchFeed');
+ cy.get(
+ '[data-testid="activity-feed-widget"] [data-testid="message-container"]:first-child'
+ ).within(() => {
+ cy.get('[data-testid="feed-actions"]').invoke('show');
+ cy.get('[data-testid="feed-actions"]').within(() => {
+ cy.get('[data-testid="add-reply"]').click();
+ });
+ });
+ verifyResponseStatusCode('@fetchFeed', 200);
+
+ interceptURL('POST', '/api/v1/feed/*/posts', 'postReply');
+ interceptURL(
+ 'GET',
+ '/api/v1/search/suggest?q=aa&index=user_search_index%2Cteam_search_index',
+ 'suggestUser'
+ );
+
+ cy.get('[data-testid="editor-wrapper"]').should('be.visible');
+ cy.get(
+ '[data-testid="editor-wrapper"] [contenteditable="true"].ql-editor'
+ ).as('editor');
+ cy.get('@editor').click();
+ cy.get('@editor').type('Can you resolve this thread for me? @admin');
+ // verifyResponseStatusCode('@suggestUser', 200);
+ cy.get('[data-value="@admin"]').click();
+
+ cy.get('[data-testid="send-button"]')
+ .should('be.visible')
+ .and('not.be.disabled');
+ cy.get('[data-testid="send-button"]').click();
+
+ verifyResponseStatusCode('@postReply', 201);
+
+ cy.get('[data-testid="closeDrawer"]').click();
+
+ let feedText1 = '';
+ cy.get(
+ '[data-testid="activity-feed-widget"] [data-testid="message-container"]:first-child [data-testid="viewer-container"]'
+ )
+ .invoke('text')
+ .then((text) => (feedText1 = text));
+
+ cy.get('[data-testid="activity-feed-widget"]')
+ .contains('@Mentions')
+ .click();
+
+ // Verify mentioned thread should be there int he mentioned tab
+ cy.get(
+ '[data-testid="message-container"] > .activity-feed-card [data-testid="viewer-container"]'
+ )
+ .invoke('text')
+ .then((text) => expect(text).to.contain(feedText1));
+ });
+
+ it('Assigned task should appear to task tab', () => {
+ cy.get('[data-testid="activity-feed-widget"]')
+ .contains('Tasks')
+ .should('contain', 0);
+
+ cy.get('[data-testid="activity-feed-widget"]').contains('Tasks').click();
+
+ cy.get(
+ '[data-testid="activity-feed-widget"] [data-testid="no-data-placeholder"]'
+ ).should('be.visible');
+
+ const value = SEARCH_ENTITY_TABLE.table_1;
+ interceptURL('GET', `/api/v1/${value.entity}/name/*`, 'getEntityDetails');
+
+ visitEntityDetailsPage(value.term, value.serviceName, value.entity);
+
+ cy.get('[data-testid="request-description"]').click();
+
+ verifyResponseStatusCode('@getEntityDetails', 200);
+
+ interceptURL('GET', '/api/v1/search/suggest?q=*', 'suggestApi');
+
+ // create description task
+ createDescriptionTask(value);
+
+ cy.clickOnLogo();
+
+ cy.get('[data-testid="activity-feed-widget"]')
+ .contains('Tasks')
+ .should('contain', 1)
+ .click();
+
+ cy.get(
+ '[data-testid="activity-feed-widget"] [data-testid="no-data-placeholder"]'
+ ).should('not.exist');
+
+ cy.get('[data-testid="message-container"]')
+ .invoke('text')
+ .then((textContent) => {
+ const matches = textContent.match(/#(\d+) UpdateDescriptionfortable/);
+
+ expect(matches).to.not.be.null;
+ });
+
+ cy.get(`[data-testid="assignee-admin"]`).should('be.visible');
+ });
+});
diff --git a/openmetadata-ui/src/main/resources/ui/cypress/e2e/Flow/Task.spec.js b/openmetadata-ui/src/main/resources/ui/cypress/e2e/Flow/Task.spec.js
index dc6be6391ed..68882b0ab3b 100644
--- a/openmetadata-ui/src/main/resources/ui/cypress/e2e/Flow/Task.spec.js
+++ b/openmetadata-ui/src/main/resources/ui/cypress/e2e/Flow/Task.spec.js
@@ -14,12 +14,16 @@
///
import {
- descriptionBox,
interceptURL,
toastNotification,
verifyResponseStatusCode,
visitEntityDetailsPage,
} from '../../common/common';
+import {
+ createAndUpdateDescriptionTask,
+ editAssignee,
+ verifyTaskDetails,
+} from '../../common/TaskUtils';
import { SEARCH_ENTITY_TABLE } from '../../constants/constants';
describe('Task flow should work', () => {
@@ -37,92 +41,8 @@ describe('Task flow should work', () => {
});
const assignee = 'admin';
- const secondAssignee = 'aaron_johnson0';
const tag = 'Personal';
- const editAssignee = () => {
- interceptURL('PATCH', 'api/v1/feed/*', 'editAssignee');
-
- cy.get('[data-testid="edit-assignees"]').click();
-
- cy.get('[data-testid="select-assignee"] > .ant-select-selector').type(
- secondAssignee
- );
- // select value from dropdown
- verifyResponseStatusCode('@suggestApi', 200);
-
- cy.get(`[data-testid="assignee-option-${secondAssignee}"]`)
- .should('be.visible')
- .trigger('mouseover')
- .trigger('click');
-
- cy.clickOutside();
-
- cy.get('[data-testid="inline-save-btn"]').click();
-
- verifyResponseStatusCode('@editAssignee', 200);
-
- cy.get(`[data-testid="assignee-${assignee}"]`).should('be.visible');
- };
-
- const verifyTaskDetails = (regexPattern) => {
- cy.get('#task-panel').should('be.visible');
- cy.get('[data-testid="task-title"]')
- .invoke('text')
- .then((textContent) => {
- const matches = textContent.match(regexPattern);
-
- expect(matches).to.not.be.null;
- });
-
- cy.get('[data-testid="owner-link"]').contains(assignee);
-
- cy.get(`[data-testid="assignee-${assignee}"]`).should('be.visible');
- };
-
- const createDescriptionTask = (value) => {
- interceptURL('POST', 'api/v1/feed', 'createTask');
-
- cy.get('#title').should(
- 'have.value',
- `Update description for table ${value.term}`
- );
-
- cy.get('[data-testid="select-assignee"] > .ant-select-selector').type(
- assignee
- );
- // select value from dropdown
- verifyResponseStatusCode('@suggestApi', 200);
-
- cy.get(`[data-testid="assignee-option-${assignee}"]`)
- .should('be.visible')
- .trigger('mouseover')
- .trigger('click');
-
- cy.clickOutside();
-
- cy.get(descriptionBox).scrollIntoView().clear().type('Updated description');
-
- cy.get('button[type="submit"]').click();
- verifyResponseStatusCode('@createTask', 201);
- toastNotification('Task created successfully.');
-
- // verify the task details
- verifyTaskDetails(/#(\d+) UpdateDescriptionfordescription/);
-
- // edit task assignees
- editAssignee();
-
- // Accept the description suggestion which is created
- cy.get('.ant-btn-compact-first-item').contains('Accept Suggestion').click();
-
- verifyResponseStatusCode('@taskResolve', 200);
-
- toastNotification('Task resolved successfully');
-
- verifyResponseStatusCode('@entityFeed', 200);
- };
-
const createTagTask = (value) => {
interceptURL('POST', 'api/v1/feed', 'createTask');
@@ -186,7 +106,7 @@ describe('Task flow should work', () => {
verifyResponseStatusCode('@getEntityDetails', 200);
// create description task
- createDescriptionTask(value);
+ createAndUpdateDescriptionTask(value);
});
it('Task flow for table tags', () => {
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedCard/ActivityFeedCardV1.tsx b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedCard/ActivityFeedCardV1.tsx
index ab84582776a..89294fcebaf 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedCard/ActivityFeedCardV1.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedCard/ActivityFeedCardV1.tsx
@@ -129,6 +129,7 @@ const ActivityFeedCardV1 = ({
{' '}
{postLength}
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/Shared/ActivityFeedActions.tsx b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/Shared/ActivityFeedActions.tsx
index f70b4bba355..90348372a8c 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/Shared/ActivityFeedActions.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/Shared/ActivityFeedActions.tsx
@@ -139,7 +139,7 @@ const ActivityFeedActions = ({
return (
<>
-
+
{feed.type !== ThreadType.Task && !isPost && (
= ({
setIsClicked(false);
}, [reaction]);
+ const element = React.createElement(
+ 'g-emoji',
+ {
+ alias: reactionObject?.alias,
+ className: 'd-flex',
+ 'data-testid': 'emoji',
+ 'fallback-src': image,
+ },
+ reactionObject?.emoji
+ );
+
return (
= ({
size="small"
onClick={handleEmojiOnClick}
onMouseOver={() => setVisible(true)}>
-
- ${reactionObject?.emoji}
- `,
- }}
- />
+ {element}
{reactionList.length}
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Reactions/Reaction.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Reactions/Reaction.tsx
index 1983ec8eb39..1f054837e5b 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/Reactions/Reaction.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Reactions/Reaction.tsx
@@ -51,6 +51,17 @@ const Reaction: FC = ({
onHide();
};
+ const element = React.createElement(
+ 'g-emoji',
+ {
+ alias: reaction?.alias,
+ className: 'd-flex',
+ 'data-testid': 'emoji',
+ 'fallback-src': image,
+ },
+ reaction?.emoji
+ );
+
return (