From a2dca382c74569c87a35ea8e7d779c95f89e7c56 Mon Sep 17 00:00:00 2001 From: Elias Nahum Date: Thu, 29 Aug 2024 00:04:12 +0800 Subject: [PATCH] add unit tests to app/utils/notification (#8140) --- app/utils/notification/index.test.ts | 146 +++++++++++++++++++++++++++ test/setup.ts | 1 + 2 files changed, 147 insertions(+) create mode 100644 app/utils/notification/index.test.ts diff --git a/app/utils/notification/index.test.ts b/app/utils/notification/index.test.ts new file mode 100644 index 00000000000..c7c36887cf8 --- /dev/null +++ b/app/utils/notification/index.test.ts @@ -0,0 +1,146 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import moment from 'moment-timezone'; +import {createIntl} from 'react-intl'; +import {Alert, DeviceEventEmitter} from 'react-native'; +import {Notifications} from 'react-native-notifications'; + +import {Events} from '@constants'; +import {DEFAULT_LOCALE, getTranslations} from '@i18n'; +import {popToRoot} from '@screens/navigation'; + +import { + convertToNotificationData, + notificationError, + emitNotificationError, + scheduleExpiredNotification, +} from '.'; + +describe('Notification Utils', () => { + const intl = createIntl({locale: DEFAULT_LOCALE, messages: getTranslations(DEFAULT_LOCALE)}); + const notification = { + identifier: 'id', + payload: { + ack_id: 'ack_id', + channel_id: 'channel_id', + channel_name: 'channel_name', + from_webhook: true, + message: 'Test message', + override_icon_url: 'icon_url', + override_username: 'username', + post_id: 'post_id', + root_id: 'root_id', + sender_id: 'sender_id', + sender_name: 'sender_name', + server_id: 'server_id', + server_url: 'server_url', + team_id: 'team_id', + type: 'message', + sub_type: 'sub_type', + use_user_icon: true, + version: '1.0', + is_crt_enabled: 'true', + data: {}, + }, + body: 'body', + }; + + const session = { + expires_at: moment().add(10, 'hours').valueOf(), + }; + + afterEach(() => { + jest.clearAllMocks(); + }); + + describe('convertToNotificationData', () => { + it('should convert notification with payload to NotificationWithData', () => { + const result = convertToNotificationData(notification as any, true); + const not = {...notification}; + Reflect.deleteProperty(not.payload, 'is_crt_enabled'); + expect(result).toEqual({ + ...not, + payload: { + ...not.payload, + identifier: 'id', + isCRTEnabled: true, + message: 'Test message', + }, + userInteraction: true, + foreground: false, + }); + }); + + it('should return the original notification if no payload is present', () => { + const result = convertToNotificationData({identifier: 'id'} as any, false); + expect(result).toEqual({identifier: 'id'}); + }); + }); + + describe('notificationError', () => { + it('should display alert and popToRoot for Channel type', () => { + notificationError(intl, 'Channel'); + expect(Alert.alert).toHaveBeenCalledWith( + 'Message not found', + 'This message belongs to a channel where you are not a member.', + ); + expect(popToRoot).toHaveBeenCalled(); + }); + + it('should display alert and popToRoot for Team type', () => { + notificationError(intl, 'Team'); + expect(Alert.alert).toHaveBeenCalledWith( + 'Message not found', + 'This message belongs to a team where you are not a member.', + ); + expect(popToRoot).toHaveBeenCalled(); + }); + + it('should display alert and popToRoot for Post type', () => { + notificationError(intl, 'Post'); + expect(Alert.alert).toHaveBeenCalledWith( + 'Message not found', + 'The message has not been found.', + ); + expect(popToRoot).toHaveBeenCalled(); + }); + + it('should display alert and popToRoot for Connection type', () => { + notificationError(intl, 'Connection'); + expect(Alert.alert).toHaveBeenCalledWith( + 'Message not found', + 'The server is unreachable and it was not possible to retrieve the specific message information for the notification.', + ); + expect(popToRoot).toHaveBeenCalled(); + }); + }); + + describe('emitNotificationError', () => { + it('should emit notification error after 500ms', (done) => { + const spyEmit = jest.spyOn(DeviceEventEmitter, 'emit'); + emitNotificationError('Channel'); + setTimeout(() => { + expect(spyEmit).toHaveBeenCalledWith(Events.NOTIFICATION_ERROR, 'Channel'); + done(); + }, 600); // wait a little longer than 500ms to ensure the timeout has executed + }); + }); + + describe('scheduleExpiredNotification', () => { + it('should schedule a notification for session expiration with hours', () => { + const result = scheduleExpiredNotification('server_url', session as any, 'ServerName', 'en'); + expect(Notifications.postLocalNotification).toHaveBeenCalledWith(expect.objectContaining({ + fireDate: new Date(session.expires_at).toISOString(), + body: 'Please log in to continue receiving notifications. Sessions for ServerName are configured to expire every 10 hours.', + title: 'Session Expired', + })); + expect(result).toBeDefined(); + }); + + it('should return 0 if expiresAt is not defined', () => { + const result = scheduleExpiredNotification('server_url', {} as any, 'ServerName', 'en'); + expect(result).toBe(0); + }); + }); +}); diff --git a/test/setup.ts b/test/setup.ts index 409753cc81e..2243502e752 100644 --- a/test/setup.ts +++ b/test/setup.ts @@ -323,6 +323,7 @@ jest.mock('react-native-notifications', () => { }), setBadgeCount: jest.fn(), }, + postLocalNotification: jest.fn((notification) => notification), }, }; });