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:
Rohit Jain 2025-10-16 14:18:59 +05:30 committed by GitHub
parent 73eca212ce
commit c0df88f02f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 44 additions and 29 deletions

View File

@ -536,8 +536,7 @@ test.describe('Mention notifications in Notification Box', () => {
const mentionsFeedResponse = adminPage.waitForResponse(
(response) =>
response.url().includes('/api/v1/feed') &&
response.url().includes('filterType=MENTIONS') &&
response.url().includes('type=Conversation')
response.url().includes('filterType=MENTIONS')
);
await mentionsTab.click();

View File

@ -17,6 +17,7 @@ import { SidebarItem } from '../../constant/sidebar';
import { EntityTypeEndpoint } from '../../support/entity/Entity.interface';
import { TableClass } from '../../support/entity/TableClass';
import { UserClass } from '../../support/user/UserClass';
import { addMentionCommentInFeed } from '../../utils/activityFeed';
import { performAdminLogin } from '../../utils/admin';
import { resetTokenFromBotPage } from '../../utils/bot';
import {
@ -25,7 +26,7 @@ import {
getApiContext,
redirectToHomePage,
} from '../../utils/common';
import { addOwner } from '../../utils/entity';
import { addOwner, waitForAllLoadersToDisappear } from '../../utils/entity';
import {
acknowledgeTask,
assignIncident,
@ -208,6 +209,27 @@ test.describe('Incident Manager', PLAYWRIGHT_INGESTION_TAG_OBJ, () => {
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(
"Re-assign incident from test case page's header",
async () => {

View File

@ -128,14 +128,6 @@ export const addMentionCommentInFeed = async (
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();
const userSuggestionsResponse = page.waitForResponse(

View File

@ -103,7 +103,7 @@ export const addOwner = async ({
await page.getByTestId(initiatorId).click();
if (type === 'Users') {
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 userListResponse;

View File

@ -52,11 +52,7 @@ import { useTourProvider } from '../../context/TourProvider/TourProvider';
import { useWebSocketConnector } from '../../context/WebSocketProvider/WebSocketProvider';
import { EntityTabs, EntityType } from '../../enums/entity.enum';
import { EntityReference } from '../../generated/entity/type';
import {
BackgroundJob,
EnumCleanupArgs,
JobType,
} from '../../generated/jobs/backgroundJob';
import { BackgroundJob, JobType } from '../../generated/jobs/backgroundJob';
import { useCurrentUserPreferences } from '../../hooks/currentUserStore/useCurrentUserStore';
import { useApplicationStore } from '../../hooks/useApplicationStore';
import useCustomLocation from '../../hooks/useCustomLocation/useCustomLocation';
@ -248,7 +244,7 @@ const NavBar = () => {
const { jobArgs, status, jobType } = backgroundJobData;
if (jobType === JobType.CustomPropertyEnumCleanup) {
const enumCleanupArgs = jobArgs as EnumCleanupArgs;
const enumCleanupArgs = jobArgs;
if (!enumCleanupArgs.entityType) {
showErrorToast(
{
@ -280,11 +276,11 @@ const NavBar = () => {
icon: Logo,
});
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
// As it does not open the concerned tab by default.
if (isChrome > -1) {
window.open(path);
globalThis.open(path);
} else {
navigate(path);
}
@ -317,7 +313,7 @@ const NavBar = () => {
const now = Date.now();
if (lastFetchTime) {
const timeSinceLastFetch = now - parseInt(lastFetchTime);
const timeSinceLastFetch = now - Number.parseInt(lastFetchTime);
if (timeSinceLastFetch < ONE_HOUR_MS) {
// Less than 1 hour since last fetch, skip API call
return;
@ -532,6 +528,7 @@ const NavBar = () => {
className="cursor-pointer"
dropdownRender={() => (
<NotificationBox
activeTab={activeTab}
hasMentionNotification={hasMentionNotification}
hasTaskNotification={hasTaskNotification}
onMarkMentionsNotificationRead={

View File

@ -38,6 +38,7 @@ import { getFilters, tabsInfo } from './NotificationBox.utils';
import NotificationFeedCard from './NotificationFeedCard.component';
const NotificationBox = ({
activeTab,
hasMentionNotification,
hasTaskNotification,
onMarkTaskNotificationRead,
@ -71,9 +72,10 @@ const NotificationBox = ({
// For mention notifications, get the actual user who made the mention from posts
let actualUser = mainFeed.from;
let actualTimestamp = mainFeed.postTs;
let feedType = feed.type || ThreadType.Conversation;
if (
feed.type === ThreadType.Conversation &&
activeTab === ThreadType.Conversation &&
feed.posts &&
feed.posts.length > 0
) {
@ -85,9 +87,10 @@ const NotificationBox = ({
)
.sort((a, b) => (b.postTs ?? 0) - (a.postTs ?? 0))[0];
if (mentionPost && mentionPost.postTs !== undefined) {
if (mentionPost?.postTs !== undefined) {
actualUser = mentionPost.from;
actualTimestamp = mentionPost.postTs;
feedType = ThreadType.Conversation;
}
}
@ -96,7 +99,7 @@ const NotificationBox = ({
createdBy={actualUser}
entityFQN={entityFQN as string}
entityType={entityType as string}
feedType={feed.type || ThreadType.Conversation}
feedType={feedType}
key={`${actualUser} ${mainFeed.id}`}
task={feed}
timestamp={actualTimestamp}
@ -106,8 +109,8 @@ const NotificationBox = ({
}, [notifications]);
const getNotificationData = (
threadType: ThreadType,
feedFilter: FeedFilter
feedFilter: FeedFilter,
threadType?: ThreadType
) => {
setIsLoading(true);
getFeedsWithFilter(currentUser?.id, feedFilter, undefined, threadType)
@ -132,7 +135,7 @@ const NotificationBox = ({
onTabChange(key);
const { threadType, feedFilter } = getFilters(key as ThreadType);
getNotificationData(threadType, feedFilter);
getNotificationData(feedFilter, threadType);
setViewAllPath(
getUserPath(
@ -156,7 +159,7 @@ const NotificationBox = ({
);
useEffect(() => {
getNotificationData(ThreadType.Task, FeedFilter.ASSIGNED_TO);
getNotificationData(FeedFilter.ASSIGNED_TO, ThreadType.Task);
}, []);
const getTabTitle = (name: string, key: string) => {

View File

@ -12,6 +12,7 @@
*/
export interface NotificationBoxProp {
activeTab: string;
hasMentionNotification: boolean;
hasTaskNotification: boolean;
onMarkMentionsNotificationRead: () => void;

View File

@ -61,6 +61,7 @@ const mockShowErrorToast = jest.fn();
const mockOnMarkTaskNotificationRead = jest.fn();
const mockProps = {
activeTab: ThreadType.Task,
hasMentionNotification: true,
hasTaskNotification: true,
onMarkMentionsNotificationRead: jest.fn(),

View File

@ -47,7 +47,7 @@ export const tabsInfo = [
];
export const getFilters = (activeTab: ThreadType) => ({
threadType: activeTab,
threadType: activeTab === ThreadType.Task ? activeTab : undefined,
feedFilter:
activeTab === ThreadType.Task
? FeedFilter.ASSIGNED_TO