mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-11-11 16:31:57 +00:00
Fixes 23223: Mentions from incidents/entities not showing in Notifications (#23874)
* Fix: Notifications not showing the mentions from incident/entity * Fix: Fixed the unit test * Playwright: Added test for verifing incident manager mention notification * Fix: Addressed PR comments * Fix: fixed code smells * Playwright: Fixed the playwright tests --------- Co-authored-by: Ashish Gupta <ashish@getcollate.io>
This commit is contained in:
parent
73eca212ce
commit
c0df88f02f
@ -536,8 +536,7 @@ test.describe('Mention notifications in Notification Box', () => {
|
|||||||
const mentionsFeedResponse = adminPage.waitForResponse(
|
const mentionsFeedResponse = adminPage.waitForResponse(
|
||||||
(response) =>
|
(response) =>
|
||||||
response.url().includes('/api/v1/feed') &&
|
response.url().includes('/api/v1/feed') &&
|
||||||
response.url().includes('filterType=MENTIONS') &&
|
response.url().includes('filterType=MENTIONS')
|
||||||
response.url().includes('type=Conversation')
|
|
||||||
);
|
);
|
||||||
|
|
||||||
await mentionsTab.click();
|
await mentionsTab.click();
|
||||||
|
|||||||
@ -17,6 +17,7 @@ import { SidebarItem } from '../../constant/sidebar';
|
|||||||
import { EntityTypeEndpoint } from '../../support/entity/Entity.interface';
|
import { EntityTypeEndpoint } from '../../support/entity/Entity.interface';
|
||||||
import { TableClass } from '../../support/entity/TableClass';
|
import { TableClass } from '../../support/entity/TableClass';
|
||||||
import { UserClass } from '../../support/user/UserClass';
|
import { UserClass } from '../../support/user/UserClass';
|
||||||
|
import { addMentionCommentInFeed } from '../../utils/activityFeed';
|
||||||
import { performAdminLogin } from '../../utils/admin';
|
import { performAdminLogin } from '../../utils/admin';
|
||||||
import { resetTokenFromBotPage } from '../../utils/bot';
|
import { resetTokenFromBotPage } from '../../utils/bot';
|
||||||
import {
|
import {
|
||||||
@ -25,7 +26,7 @@ import {
|
|||||||
getApiContext,
|
getApiContext,
|
||||||
redirectToHomePage,
|
redirectToHomePage,
|
||||||
} from '../../utils/common';
|
} from '../../utils/common';
|
||||||
import { addOwner } from '../../utils/entity';
|
import { addOwner, waitForAllLoadersToDisappear } from '../../utils/entity';
|
||||||
import {
|
import {
|
||||||
acknowledgeTask,
|
acknowledgeTask,
|
||||||
assignIncident,
|
assignIncident,
|
||||||
@ -208,6 +209,27 @@ test.describe('Incident Manager', PLAYWRIGHT_INGESTION_TAG_OBJ, () => {
|
|||||||
await updateAssignee;
|
await updateAssignee;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await test.step(
|
||||||
|
'Verify that notifications correctly display mentions for the incident manager',
|
||||||
|
async () => {
|
||||||
|
const testcaseName = await page
|
||||||
|
.getByTestId('entity-header-name')
|
||||||
|
.innerText();
|
||||||
|
await addMentionCommentInFeed(page, 'admin', true);
|
||||||
|
|
||||||
|
await adminPage.waitForLoadState('networkidle');
|
||||||
|
await waitForAllLoadersToDisappear(adminPage);
|
||||||
|
await adminPage.getByRole('button', { name: 'Notifications' }).click();
|
||||||
|
await adminPage.getByText('Mentions').click();
|
||||||
|
await adminPage.waitForLoadState('networkidle');
|
||||||
|
await waitForAllLoadersToDisappear(adminPage);
|
||||||
|
|
||||||
|
await expect(adminPage.getByLabel('Mentions')).toContainText(
|
||||||
|
`mentioned you on the testCase ${testcaseName}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
await test.step(
|
await test.step(
|
||||||
"Re-assign incident from test case page's header",
|
"Re-assign incident from test case page's header",
|
||||||
async () => {
|
async () => {
|
||||||
|
|||||||
@ -128,14 +128,6 @@ export const addMentionCommentInFeed = async (
|
|||||||
|
|
||||||
await page.waitForSelector('[data-testid="loader"]', { state: 'detached' });
|
await page.waitForSelector('[data-testid="loader"]', { state: 'detached' });
|
||||||
|
|
||||||
const firstMessage = getNthFeedMessage(page, 0);
|
|
||||||
|
|
||||||
await expect(firstMessage).toBeVisible();
|
|
||||||
|
|
||||||
await firstMessage.locator('[data-testid="reply-count"]').click();
|
|
||||||
await page.waitForSelector('.ant-drawer-content', {
|
|
||||||
state: 'visible',
|
|
||||||
});
|
|
||||||
await page.getByTestId('comments-input-field').click();
|
await page.getByTestId('comments-input-field').click();
|
||||||
|
|
||||||
const userSuggestionsResponse = page.waitForResponse(
|
const userSuggestionsResponse = page.waitForResponse(
|
||||||
|
|||||||
@ -103,7 +103,7 @@ export const addOwner = async ({
|
|||||||
await page.getByTestId(initiatorId).click();
|
await page.getByTestId(initiatorId).click();
|
||||||
if (type === 'Users') {
|
if (type === 'Users') {
|
||||||
const userListResponse = page.waitForResponse(
|
const userListResponse = page.waitForResponse(
|
||||||
'/api/v1/search/query?q=&index=user_search_index&*'
|
'/api/v1/search/query?q=*&index=user_search_index&*'
|
||||||
);
|
);
|
||||||
await page.getByRole('tab', { name: type }).click();
|
await page.getByRole('tab', { name: type }).click();
|
||||||
await userListResponse;
|
await userListResponse;
|
||||||
|
|||||||
@ -52,11 +52,7 @@ import { useTourProvider } from '../../context/TourProvider/TourProvider';
|
|||||||
import { useWebSocketConnector } from '../../context/WebSocketProvider/WebSocketProvider';
|
import { useWebSocketConnector } from '../../context/WebSocketProvider/WebSocketProvider';
|
||||||
import { EntityTabs, EntityType } from '../../enums/entity.enum';
|
import { EntityTabs, EntityType } from '../../enums/entity.enum';
|
||||||
import { EntityReference } from '../../generated/entity/type';
|
import { EntityReference } from '../../generated/entity/type';
|
||||||
import {
|
import { BackgroundJob, JobType } from '../../generated/jobs/backgroundJob';
|
||||||
BackgroundJob,
|
|
||||||
EnumCleanupArgs,
|
|
||||||
JobType,
|
|
||||||
} from '../../generated/jobs/backgroundJob';
|
|
||||||
import { useCurrentUserPreferences } from '../../hooks/currentUserStore/useCurrentUserStore';
|
import { useCurrentUserPreferences } from '../../hooks/currentUserStore/useCurrentUserStore';
|
||||||
import { useApplicationStore } from '../../hooks/useApplicationStore';
|
import { useApplicationStore } from '../../hooks/useApplicationStore';
|
||||||
import useCustomLocation from '../../hooks/useCustomLocation/useCustomLocation';
|
import useCustomLocation from '../../hooks/useCustomLocation/useCustomLocation';
|
||||||
@ -248,7 +244,7 @@ const NavBar = () => {
|
|||||||
const { jobArgs, status, jobType } = backgroundJobData;
|
const { jobArgs, status, jobType } = backgroundJobData;
|
||||||
|
|
||||||
if (jobType === JobType.CustomPropertyEnumCleanup) {
|
if (jobType === JobType.CustomPropertyEnumCleanup) {
|
||||||
const enumCleanupArgs = jobArgs as EnumCleanupArgs;
|
const enumCleanupArgs = jobArgs;
|
||||||
if (!enumCleanupArgs.entityType) {
|
if (!enumCleanupArgs.entityType) {
|
||||||
showErrorToast(
|
showErrorToast(
|
||||||
{
|
{
|
||||||
@ -280,11 +276,11 @@ const NavBar = () => {
|
|||||||
icon: Logo,
|
icon: Logo,
|
||||||
});
|
});
|
||||||
notification.onclick = () => {
|
notification.onclick = () => {
|
||||||
const isChrome = window.navigator.userAgent.indexOf('Chrome');
|
const isChrome = globalThis.navigator.userAgent.indexOf('Chrome');
|
||||||
// Applying logic to open a new window onclick of browser notification from chrome
|
// Applying logic to open a new window onclick of browser notification from chrome
|
||||||
// As it does not open the concerned tab by default.
|
// As it does not open the concerned tab by default.
|
||||||
if (isChrome > -1) {
|
if (isChrome > -1) {
|
||||||
window.open(path);
|
globalThis.open(path);
|
||||||
} else {
|
} else {
|
||||||
navigate(path);
|
navigate(path);
|
||||||
}
|
}
|
||||||
@ -317,7 +313,7 @@ const NavBar = () => {
|
|||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
|
|
||||||
if (lastFetchTime) {
|
if (lastFetchTime) {
|
||||||
const timeSinceLastFetch = now - parseInt(lastFetchTime);
|
const timeSinceLastFetch = now - Number.parseInt(lastFetchTime);
|
||||||
if (timeSinceLastFetch < ONE_HOUR_MS) {
|
if (timeSinceLastFetch < ONE_HOUR_MS) {
|
||||||
// Less than 1 hour since last fetch, skip API call
|
// Less than 1 hour since last fetch, skip API call
|
||||||
return;
|
return;
|
||||||
@ -532,6 +528,7 @@ const NavBar = () => {
|
|||||||
className="cursor-pointer"
|
className="cursor-pointer"
|
||||||
dropdownRender={() => (
|
dropdownRender={() => (
|
||||||
<NotificationBox
|
<NotificationBox
|
||||||
|
activeTab={activeTab}
|
||||||
hasMentionNotification={hasMentionNotification}
|
hasMentionNotification={hasMentionNotification}
|
||||||
hasTaskNotification={hasTaskNotification}
|
hasTaskNotification={hasTaskNotification}
|
||||||
onMarkMentionsNotificationRead={
|
onMarkMentionsNotificationRead={
|
||||||
|
|||||||
@ -38,6 +38,7 @@ import { getFilters, tabsInfo } from './NotificationBox.utils';
|
|||||||
import NotificationFeedCard from './NotificationFeedCard.component';
|
import NotificationFeedCard from './NotificationFeedCard.component';
|
||||||
|
|
||||||
const NotificationBox = ({
|
const NotificationBox = ({
|
||||||
|
activeTab,
|
||||||
hasMentionNotification,
|
hasMentionNotification,
|
||||||
hasTaskNotification,
|
hasTaskNotification,
|
||||||
onMarkTaskNotificationRead,
|
onMarkTaskNotificationRead,
|
||||||
@ -71,9 +72,10 @@ const NotificationBox = ({
|
|||||||
// For mention notifications, get the actual user who made the mention from posts
|
// For mention notifications, get the actual user who made the mention from posts
|
||||||
let actualUser = mainFeed.from;
|
let actualUser = mainFeed.from;
|
||||||
let actualTimestamp = mainFeed.postTs;
|
let actualTimestamp = mainFeed.postTs;
|
||||||
|
let feedType = feed.type || ThreadType.Conversation;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
feed.type === ThreadType.Conversation &&
|
activeTab === ThreadType.Conversation &&
|
||||||
feed.posts &&
|
feed.posts &&
|
||||||
feed.posts.length > 0
|
feed.posts.length > 0
|
||||||
) {
|
) {
|
||||||
@ -85,9 +87,10 @@ const NotificationBox = ({
|
|||||||
)
|
)
|
||||||
.sort((a, b) => (b.postTs ?? 0) - (a.postTs ?? 0))[0];
|
.sort((a, b) => (b.postTs ?? 0) - (a.postTs ?? 0))[0];
|
||||||
|
|
||||||
if (mentionPost && mentionPost.postTs !== undefined) {
|
if (mentionPost?.postTs !== undefined) {
|
||||||
actualUser = mentionPost.from;
|
actualUser = mentionPost.from;
|
||||||
actualTimestamp = mentionPost.postTs;
|
actualTimestamp = mentionPost.postTs;
|
||||||
|
feedType = ThreadType.Conversation;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,7 +99,7 @@ const NotificationBox = ({
|
|||||||
createdBy={actualUser}
|
createdBy={actualUser}
|
||||||
entityFQN={entityFQN as string}
|
entityFQN={entityFQN as string}
|
||||||
entityType={entityType as string}
|
entityType={entityType as string}
|
||||||
feedType={feed.type || ThreadType.Conversation}
|
feedType={feedType}
|
||||||
key={`${actualUser} ${mainFeed.id}`}
|
key={`${actualUser} ${mainFeed.id}`}
|
||||||
task={feed}
|
task={feed}
|
||||||
timestamp={actualTimestamp}
|
timestamp={actualTimestamp}
|
||||||
@ -106,8 +109,8 @@ const NotificationBox = ({
|
|||||||
}, [notifications]);
|
}, [notifications]);
|
||||||
|
|
||||||
const getNotificationData = (
|
const getNotificationData = (
|
||||||
threadType: ThreadType,
|
feedFilter: FeedFilter,
|
||||||
feedFilter: FeedFilter
|
threadType?: ThreadType
|
||||||
) => {
|
) => {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
getFeedsWithFilter(currentUser?.id, feedFilter, undefined, threadType)
|
getFeedsWithFilter(currentUser?.id, feedFilter, undefined, threadType)
|
||||||
@ -132,7 +135,7 @@ const NotificationBox = ({
|
|||||||
onTabChange(key);
|
onTabChange(key);
|
||||||
const { threadType, feedFilter } = getFilters(key as ThreadType);
|
const { threadType, feedFilter } = getFilters(key as ThreadType);
|
||||||
|
|
||||||
getNotificationData(threadType, feedFilter);
|
getNotificationData(feedFilter, threadType);
|
||||||
|
|
||||||
setViewAllPath(
|
setViewAllPath(
|
||||||
getUserPath(
|
getUserPath(
|
||||||
@ -156,7 +159,7 @@ const NotificationBox = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getNotificationData(ThreadType.Task, FeedFilter.ASSIGNED_TO);
|
getNotificationData(FeedFilter.ASSIGNED_TO, ThreadType.Task);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const getTabTitle = (name: string, key: string) => {
|
const getTabTitle = (name: string, key: string) => {
|
||||||
|
|||||||
@ -12,6 +12,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export interface NotificationBoxProp {
|
export interface NotificationBoxProp {
|
||||||
|
activeTab: string;
|
||||||
hasMentionNotification: boolean;
|
hasMentionNotification: boolean;
|
||||||
hasTaskNotification: boolean;
|
hasTaskNotification: boolean;
|
||||||
onMarkMentionsNotificationRead: () => void;
|
onMarkMentionsNotificationRead: () => void;
|
||||||
|
|||||||
@ -61,6 +61,7 @@ const mockShowErrorToast = jest.fn();
|
|||||||
const mockOnMarkTaskNotificationRead = jest.fn();
|
const mockOnMarkTaskNotificationRead = jest.fn();
|
||||||
|
|
||||||
const mockProps = {
|
const mockProps = {
|
||||||
|
activeTab: ThreadType.Task,
|
||||||
hasMentionNotification: true,
|
hasMentionNotification: true,
|
||||||
hasTaskNotification: true,
|
hasTaskNotification: true,
|
||||||
onMarkMentionsNotificationRead: jest.fn(),
|
onMarkMentionsNotificationRead: jest.fn(),
|
||||||
|
|||||||
@ -47,7 +47,7 @@ export const tabsInfo = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
export const getFilters = (activeTab: ThreadType) => ({
|
export const getFilters = (activeTab: ThreadType) => ({
|
||||||
threadType: activeTab,
|
threadType: activeTab === ThreadType.Task ? activeTab : undefined,
|
||||||
feedFilter:
|
feedFilter:
|
||||||
activeTab === ThreadType.Task
|
activeTab === ThreadType.Task
|
||||||
? FeedFilter.ASSIGNED_TO
|
? FeedFilter.ASSIGNED_TO
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user