Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: refactor notifications unit tests #11431

Merged
merged 41 commits into from
Oct 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
feca80a
refactor test name, and remove unnecessary mock
Sep 25, 2024
e9eafa0
updates snapshot
Sep 25, 2024
e462ad0
updates unit test naming
Sep 25, 2024
943d9e2
decouples setup test
Sep 25, 2024
ba68457
refactor toggle notifications & tests
Sep 25, 2024
36366e5
refactor useNotificationHandler tests
Sep 25, 2024
9a46353
Merge branch 'main' into fix/refactor-notifications-unit-tests
Jonathansoufer Sep 25, 2024
1afbe3a
Merge branch 'main' into fix/refactor-notifications-unit-tests
Jonathansoufer Sep 25, 2024
d08bc90
improve Reliability Rating on New Code
Sep 25, 2024
438d5c4
Update app/util/notifications/hooks/index.test.ts
Jonathansoufer Sep 25, 2024
92877e7
Update app/util/notifications/androidChannels.test.ts
Jonathansoufer Sep 25, 2024
f82813b
Update app/components/Views/Settings/NotificationsSettings/index.test…
Jonathansoufer Sep 25, 2024
84e65eb
Update app/components/Views/Settings/NotificationsSettings/useToggleN…
Jonathansoufer Sep 25, 2024
301255e
Update app/components/Views/Settings/NotificationsSettings/useToggleN…
Jonathansoufer Sep 25, 2024
d4c03b1
Update app/util/notifications/androidChannels.test.ts
Jonathansoufer Sep 25, 2024
629a56d
Update app/util/notifications/androidChannels.test.ts
Jonathansoufer Sep 25, 2024
850a6d7
Update app/util/notifications/androidChannels.test.ts
Jonathansoufer Sep 25, 2024
5225989
polishes unit test
Sep 25, 2024
9dcf4cd
Merge branch 'main' into fix/refactor-notifications-unit-tests
Jonathansoufer Sep 25, 2024
b463c4d
updates snapshot
Sep 25, 2024
9dd857e
Merge branch 'main' into fix/refactor-notifications-unit-tests
Jonathansoufer Sep 26, 2024
b76f325
Merge branch 'main' into fix/refactor-notifications-unit-tests
Jonathansoufer Sep 29, 2024
98b1b2d
Merge branch 'main' into fix/refactor-notifications-unit-tests
Jonathansoufer Oct 1, 2024
c0f959c
Merge branch 'main' into fix/refactor-notifications-unit-tests
Jonathansoufer Oct 2, 2024
0ed083c
Merge branch 'main' into fix/refactor-notifications-unit-tests
Jonathansoufer Oct 2, 2024
fe6efe5
Merge branch 'main' into fix/refactor-notifications-unit-tests
Jonathansoufer Oct 2, 2024
0ea1142
Merge branch 'main' into fix/refactor-notifications-unit-tests
Jonathansoufer Oct 2, 2024
61aff0a
fix constant import and unit test
Oct 2, 2024
1ddc49d
Merge branch 'main' into fix/refactor-notifications-unit-tests
Jonathansoufer Oct 3, 2024
e242009
add unit test back
Oct 3, 2024
58522a0
Merge branch 'fix/refactor-notifications-unit-tests' of github.com:Me…
Oct 3, 2024
efec745
Update app/util/notifications/hooks/index.test.ts
Jonathansoufer Oct 8, 2024
dadca27
Merge branch 'main' into fix/refactor-notifications-unit-tests
Jonathansoufer Oct 8, 2024
111f187
Merge branch 'main' into fix/refactor-notifications-unit-tests
Oct 9, 2024
3c32790
refactor to isolate only relevant test cases for the hook itself
Oct 9, 2024
187d716
Merge branch 'main' into fix/refactor-notifications-unit-tests
Jonathansoufer Oct 10, 2024
0ea26c8
more polishing on tests
Oct 10, 2024
e0eb917
Merge branch 'main' into fix/refactor-notifications-unit-tests
Jonathansoufer Oct 10, 2024
579928d
Merge branch 'main' into fix/refactor-notifications-unit-tests
Jonathansoufer Oct 11, 2024
3249a0b
clear mock
Oct 11, 2024
5e44234
invert logic on mock resetting
Oct 11, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`NotificationsSettings should render correctly 1`] = `
exports[`NotificationsSettings render matches snapshot 1`] = `
<RCTScrollView
style={
{
Expand Down
150 changes: 3 additions & 147 deletions app/components/Views/Settings/NotificationsSettings/index.test.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,11 @@
import React, { useCallback } from 'react';
import { renderHook, act } from '@testing-library/react-hooks';

import React from 'react';
import renderWithProvider from '../../../../util/test/renderWithProvider';
import NotificationsService from '../../../../util/notifications/services/NotificationService';
import { backgroundState } from '../../../../util/test/initial-root-state';
import NotificationsSettings from '.';

import Routes from '../../../../constants/navigation/Routes';
import { Props } from './NotificationsSettings.types';
import { MOCK_ACCOUNTS_CONTROLLER_STATE } from '../../../../util/test/accountsControllerTestUtils';
import { MetaMetricsEvents } from '../../../../core/Analytics/MetaMetrics.events';
import { NavigationProp, ParamListBase } from '@react-navigation/native';

// Mock store.getState

let mockGetState: jest.Mock;
jest.mock('../../../../store', () => {
mockGetState = jest.fn();
Expand Down Expand Up @@ -57,130 +50,10 @@ jest.mock('../../../../util/notifications/services/NotificationService', () => (
getAllPermissions: jest.fn(),
}));

jest.mock('../../../../core/Analytics/MetaMetrics.events', () => ({
MetaMetricsEvents: {
NOTIFICATIONS_SETTINGS_UPDATED: 'NOTIFICATIONS_SETTINGS_UPDATED',
},
}));

const mockDisableNotifications = jest.fn();
const mockEnableNotifications = jest.fn();
const mockSetUiNotificationStatus = jest.fn();
const mockTrackEvent = jest.fn();

const mockNavigation = {
navigate: jest.fn(),
} as unknown as NavigationProp<ParamListBase>;

const setOptions = jest.fn();

describe('toggleNotificationsEnabled', () => {
beforeEach(() => {
jest.clearAllMocks();
});

const setup = (basicFunctionalityEnabled: boolean, isMetamaskNotificationsEnabled: boolean, isProfileSyncingEnabled: boolean) => renderHook(() =>
useCallback(async () => {
if (!basicFunctionalityEnabled) {
mockNavigation.navigate(Routes.MODAL.ROOT_MODAL_FLOW, {
screen: Routes.SHEET.BASIC_FUNCTIONALITY,
params: {
caller: Routes.SETTINGS.NOTIFICATIONS,
},
});
} else if (isMetamaskNotificationsEnabled) {
mockDisableNotifications();
mockSetUiNotificationStatus(false);
} else {
const { permission } = await NotificationsService.getAllPermissions(false);
if (permission !== 'authorized') {
return;
}

mockEnableNotifications();
mockSetUiNotificationStatus(true);
}
mockTrackEvent(MetaMetricsEvents.NOTIFICATIONS_SETTINGS_UPDATED, {
settings_type: 'notifications',
old_value: isMetamaskNotificationsEnabled,
new_value: !isMetamaskNotificationsEnabled,
was_profile_syncing_on: isMetamaskNotificationsEnabled ? true : isProfileSyncingEnabled,
});
}, [])
);

it('should navigate to basic functionality screen if basicFunctionalityEnabled is false', async () => {
const { result } = setup(false, false, false);

await act(async () => {
await result.current();
});

expect(mockNavigation.navigate).toHaveBeenCalledWith(Routes.MODAL.ROOT_MODAL_FLOW, {
screen: Routes.SHEET.BASIC_FUNCTIONALITY,
params: {
caller: Routes.SETTINGS.NOTIFICATIONS,
},
});
});

it('should disable notifications if isMetamaskNotificationsEnabled is true', async () => {
const { result } = setup(true, true, false);

await act(async () => {
await result.current();
});

expect(mockDisableNotifications).toHaveBeenCalled();
expect(mockSetUiNotificationStatus).toHaveBeenCalledWith(false);
});

it('should enable notifications if isMetamaskNotificationsEnabled is false and permission is authorized', async () => {
(NotificationsService.getAllPermissions as jest.Mock).mockResolvedValue({ permission: 'authorized' });

const { result } = setup(true, false, false);

await act(async () => {
await result.current();
});

expect(mockEnableNotifications).toHaveBeenCalled();
expect(mockSetUiNotificationStatus).toHaveBeenCalledWith(true);
});

it('should not enable notifications if permission is not authorized', async () => {
(NotificationsService.getAllPermissions as jest.Mock).mockResolvedValue({ permission: 'denied' });

const { result } = setup(true, false, false);

await act(async () => {
await result.current();
});

expect(mockEnableNotifications).not.toHaveBeenCalled();
expect(mockSetUiNotificationStatus).not.toHaveBeenCalled();
});

it('should track event when notifications settings are updated', async () => {
(NotificationsService.getAllPermissions as jest.Mock).mockResolvedValue({ permission: 'authorized' });

const { result } = setup(true, false, true);

await act(async () => {
await result.current();
});

expect(mockTrackEvent).toHaveBeenCalledWith(MetaMetricsEvents.NOTIFICATIONS_SETTINGS_UPDATED, {
settings_type: 'notifications',
old_value: false,
new_value: true,
was_profile_syncing_on: true,
});
});
});

describe('NotificationsSettings', () => {
it('should render correctly', () => {
it('render matches snapshot', () => {
mockGetState.mockImplementation(() => ({
notifications: {},
}));
Expand All @@ -199,21 +72,4 @@ describe('NotificationsSettings', () => {
);
expect(toJSON()).toMatchSnapshot();
});

it('should toggle notifications and handle permission correctly', async () => {
const isMetamaskNotificationsEnabled = true;
const basicFunctionalityEnabled = true;
const isProfileSyncingEnabled = true;

const toggleNotificationsEnabledImpl = jest.fn(() => Promise.resolve({
isMetamaskNotificationsEnabled,
basicFunctionalityEnabled,
isProfileSyncingEnabled,
}));

await toggleNotificationsEnabledImpl();

expect(NotificationsService.getAllPermissions).toHaveBeenCalledTimes(1);
expect(NotificationsService.getAllPermissions).toHaveBeenCalledWith(false);
});
});
42 changes: 8 additions & 34 deletions app/components/Views/Settings/NotificationsSettings/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ import {
selectIsProfileSyncingEnabled,
} from '../../../../selectors/notifications';

import NotificationsService from '../../../../util/notifications/services/NotificationService';
import Routes from '../../../../constants/navigation/Routes';

import ButtonIcon, {
Expand All @@ -57,6 +56,7 @@ import AppConstants from '../../../../core/AppConstants';
import notificationsRows from './notificationsRows';
import { IconName } from '../../../../component-library/components/Icons/Icon';
import { MetaMetricsEvents } from '../../../../core/Analytics/MetaMetrics.events';
import { useToggleNotifications } from './useToggleNotifications';

interface MainNotificationSettingsProps extends Props {
toggleNotificationsEnabled: () => void;
Expand Down Expand Up @@ -109,6 +109,7 @@ const NotificationsSettings = ({ navigation, route }: Props) => {
const { accounts } = useAccounts();
const { trackEvent } = useMetrics();
const theme = useTheme();

const isMetamaskNotificationsEnabled = useSelector(
selectIsMetamaskNotificationsEnabled,
);
Expand Down Expand Up @@ -177,42 +178,15 @@ const NotificationsSettings = ({ navigation, route }: Props) => {
* it will request the push notifications permission and enable the notifications
* if the permission is granted.
*/
const toggleNotificationsEnabled = useCallback(async () => {
if (!basicFunctionalityEnabled) {
navigation.navigate(Routes.MODAL.ROOT_MODAL_FLOW, {
screen: Routes.SHEET.BASIC_FUNCTIONALITY,
params: {
caller: Routes.SETTINGS.NOTIFICATIONS,
},
});
} else if (isMetamaskNotificationsEnabled) {
disableNotifications();
setUiNotificationStatus(false);
} else {
const { permission } = await NotificationsService.getAllPermissions(false);
if (permission !== 'authorized') {
return;
}
enableNotifications();
setUiNotificationStatus(true);
}
trackEvent(MetaMetricsEvents.NOTIFICATIONS_SETTINGS_UPDATED, {
settings_type: 'notifications',
old_value: isMetamaskNotificationsEnabled,
new_value: !isMetamaskNotificationsEnabled,
was_profile_syncing_on: isMetamaskNotificationsEnabled
? true
: isProfileSyncingEnabled,
});
}, [
const { toggleNotificationsEnabled } = useToggleNotifications({
navigation,
basicFunctionalityEnabled,
disableNotifications,
enableNotifications,
isMetamaskNotificationsEnabled,
navigation,
trackEvent,
isProfileSyncingEnabled,
]);
disableNotifications,
enableNotifications,
setUiNotificationStatus,
});

const toggleCustomNotificationsEnabled = useCallback(async () => {
setPlatformAnnouncementsState(!platformAnnouncementsState);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import { renderHook, act } from '@testing-library/react-hooks';
import { useToggleNotifications } from './useToggleNotifications';
import NotificationsService from '../../../../util/notifications/services/NotificationService';
import Routes from '../../../../constants/navigation/Routes';
import { NavigationProp, ParamListBase } from '@react-navigation/native';

jest.mock(
'../../../../util/notifications/services/NotificationService',
() => ({
getAllPermissions: jest.fn(),
}),
);

const mockNavigation = {
navigate: jest.fn(),
} as unknown as NavigationProp<ParamListBase>;

const mockDisableNotifications = jest.fn();
const mockEnableNotifications = jest.fn();
const mockSetUiNotificationStatus = jest.fn();

describe('useToggleNotifications', () => {
beforeEach(() => {
jest.clearAllMocks();
});

it('navigates to basic functionality screen if basic functionality is disabled', async () => {
const { result } = renderHook(() =>
useToggleNotifications({
navigation: mockNavigation,
basicFunctionalityEnabled: false,
isMetamaskNotificationsEnabled: false,
isProfileSyncingEnabled: false,
disableNotifications: mockDisableNotifications,
enableNotifications: mockEnableNotifications,
setUiNotificationStatus: mockSetUiNotificationStatus,
}),
);

await act(async () => {
await result.current.toggleNotificationsEnabled();
});

expect(mockNavigation.navigate).toHaveBeenCalledWith(
Routes.MODAL.ROOT_MODAL_FLOW,
{
screen: Routes.SHEET.BASIC_FUNCTIONALITY,
params: {
caller: Routes.SETTINGS.NOTIFICATIONS,
},
},
);
});

it('switches notifications OFF if notifications previously enabled', async () => {
const { result } = renderHook(() =>
useToggleNotifications({
navigation: mockNavigation,
basicFunctionalityEnabled: true,
isMetamaskNotificationsEnabled: true,
isProfileSyncingEnabled: false,
disableNotifications: mockDisableNotifications,
enableNotifications: mockEnableNotifications,
setUiNotificationStatus: mockSetUiNotificationStatus,
}),
);

await act(async () => {
await result.current.toggleNotificationsEnabled();
});

expect(mockDisableNotifications).toHaveBeenCalled();
expect(mockSetUiNotificationStatus).toHaveBeenCalledWith(false);
});

it('switches notifications ON if notifications previously disabled and permission is authorized', async () => {
(NotificationsService.getAllPermissions as jest.Mock).mockResolvedValue({
permission: 'authorized',
});

const { result } = renderHook(() =>
useToggleNotifications({
navigation: mockNavigation,
basicFunctionalityEnabled: true,
isMetamaskNotificationsEnabled: false,
isProfileSyncingEnabled: false,
disableNotifications: mockDisableNotifications,
enableNotifications: mockEnableNotifications,
setUiNotificationStatus: mockSetUiNotificationStatus,
}),
);

await act(async () => {
await result.current.toggleNotificationsEnabled();
});

expect(mockEnableNotifications).toHaveBeenCalled();
expect(mockSetUiNotificationStatus).toHaveBeenCalledWith(true);
});

it('switches notifications OFF if device permission is not authorized', async () => {
(NotificationsService.getAllPermissions as jest.Mock).mockResolvedValue({
permission: 'denied',
});

const { result } = renderHook(() =>
useToggleNotifications({
navigation: mockNavigation,
basicFunctionalityEnabled: true,
isMetamaskNotificationsEnabled: false,
isProfileSyncingEnabled: false,
disableNotifications: mockDisableNotifications,
enableNotifications: mockEnableNotifications,
setUiNotificationStatus: mockSetUiNotificationStatus,
}),
);

await act(async () => {
await result.current.toggleNotificationsEnabled();
});

expect(mockEnableNotifications).not.toHaveBeenCalled();
expect(mockSetUiNotificationStatus).not.toHaveBeenCalled();
});
});
Loading
Loading