Skip to content

Commit

Permalink
test: add more tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Prithpal-Sooriya committed Feb 6, 2025
1 parent b3461c1 commit 78da3be
Show file tree
Hide file tree
Showing 2 changed files with 150 additions and 19 deletions.
114 changes: 106 additions & 8 deletions app/components/Views/Notifications/index.test.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,25 @@
import React from 'react';
import NotificationsView from './';
import { renderHook, act } from '@testing-library/react-native';
import { processNotification } from '@metamask/notification-services-controller/notification-services';
import {
createMockNotificationEthSent,
createMockFeatureAnnouncementRaw,
} from '@metamask/notification-services-controller/notification-services/mocks';

import NotificationsView, {
useMarkAsReadCallback,
useNotificationFilters,
} from './';
import renderWithProvider, {
DeepPartial,
} from '../../../util/test/renderWithProvider';
import { NavigationProp, ParamListBase } from '@react-navigation/native';
import { RootState } from '../../../reducers';
import { backgroundState } from '../../../util/test/initial-root-state';
import { useMetrics } from '../../../components/hooks/useMetrics';
// eslint-disable-next-line import/no-namespace
import * as UseNotificationsModule from '../../../util/notifications/hooks/useNotifications';
import NotificationsService from '../../../util/notifications/services/NotificationService';

const navigationMock = {
navigate: jest.fn(),
Expand All @@ -16,29 +30,113 @@ jest.mock('@react-navigation/native', () => ({
useNavigation: jest.fn(() => ({})),
}));

jest.mock('../../../components/hooks/useMetrics', () => ({
useMetrics: jest.fn(),
}));

const mockMetrics = {
trackEvent: jest.fn(),
createEventBuilder: jest.fn().mockReturnValue({ build: jest.fn() }),
} as unknown as ReturnType<typeof useMetrics>;
jest.mocked(useMetrics).mockReturnValue(mockMetrics);

const mockInitialState: DeepPartial<RootState> = {
engine: {
backgroundState: {
...backgroundState,
NotificationServicesController: {
isNotificationServicesEnabled: true,
metamaskNotificationsList: [],
},
},
},
};

jest.mock('react-redux', () => ({
...jest.requireActual('react-redux'),
// TODO: Replace "any" with type
// eslint-disable-next-line @typescript-eslint/no-explicit-any
useSelector: (fn: any) => fn(mockInitialState),
}));

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

it('should render correctly', () => {
const { toJSON } = renderWithProvider(
<NotificationsView navigation={navigationMock} />,
{ state: mockInitialState },
);
expect(toJSON()).toMatchSnapshot();
});
});

describe('useMarkAsReadCallback', () => {
const arrangeMocks = () => {
const mockMarkNotificationsAsRead = jest.fn();
jest
.spyOn(UseNotificationsModule, 'useMarkNotificationAsRead')
.mockReturnValue({
loading: false,
markNotificationAsRead: mockMarkNotificationsAsRead,
});

const mockSetBadgeCount = jest.spyOn(NotificationsService, 'setBadgeCount');

return {
mockMetrics,
mockMarkNotificationsAsRead,
mockSetBadgeCount,
};
};

beforeEach(() => {
jest.clearAllMocks();
});

it('marks content as read and fires event', async () => {
const mocks = arrangeMocks();
const notifications = [
processNotification(createMockNotificationEthSent()),
];
const hook = renderHook(() => useMarkAsReadCallback({ notifications }));

await act(() => hook.result.current.handleMarkAllAsRead());

// Assert - mark as read action called
expect(mocks.mockMarkNotificationsAsRead).toHaveBeenCalled();

// Assert - badge count updated
expect(mocks.mockSetBadgeCount).toHaveBeenCalled();

// Assert - event fired
expect(mocks.mockMetrics.trackEvent).toHaveBeenCalled();
});
});

describe('useNotificationFilters', () => {
const createEthNotif = () =>
processNotification(createMockNotificationEthSent());
const createAnnonucementNotif = () =>
processNotification(createMockFeatureAnnouncementRaw());

const arrangeNotifications = (ids: string[]) => {
const notifications = ids.map((id) => {
const notif = createEthNotif();
notif.id = id;
notif.createdAt = Date.now().toString();
return notif;
});

return notifications;
};

it('deduplicates any notifications', () => {
const notifications = arrangeNotifications(['1', '1', '1']);
const hook = renderHook(() => useNotificationFilters({ notifications }));
expect(hook.result.current.allNotifications).toHaveLength(1);
});

it('filters and returns wallet notifications and announcement notifications', () => {
const notifications = [createEthNotif(), createAnnonucementNotif()];
const hook = renderHook(() => useNotificationFilters({ notifications }));
expect(hook.result.current.allNotifications).toHaveLength(2);
expect(hook.result.current.walletNotifications).toHaveLength(1);
expect(hook.result.current.announcementNotifications).toHaveLength(1);
});
});
55 changes: 44 additions & 11 deletions app/components/Views/Notifications/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import React, { useCallback, useMemo } from 'react';
import { View } from 'react-native';
import { useSelector } from 'react-redux';
import { TRIGGER_TYPES } from '@metamask/notification-services-controller/notification-services';
import {
TRIGGER_TYPES,
INotification,
} from '@metamask/notification-services-controller/notification-services';

import { useMetrics } from '../../../components/hooks/useMetrics';
import { NotificationsViewSelectorsIDs } from '../../../../e2e/selectors/wallet/NotificationsView.selectors';
Expand Down Expand Up @@ -36,18 +39,12 @@ import ButtonIcon, {
} from '../../../component-library/components/Buttons/ButtonIcon';
import { MetaMetricsEvents } from '../../../core/Analytics';

const NotificationsView = ({
navigation,
}: {
navigation: NavigationProp<ParamListBase>;
}) => {
export function useMarkAsReadCallback(props: {
notifications: INotification[];
}) {
const { notifications } = props;
const { trackEvent, createEventBuilder } = useMetrics();
const { isLoading } = useListNotifications();
const isNotificationEnabled = useSelector(
selectIsMetamaskNotificationsEnabled,
);
const { markNotificationAsRead, loading } = useMarkNotificationAsRead();
const notifications = useSelector(getNotificationsList);

const handleMarkAllAsRead = useCallback(() => {
markNotificationAsRead(notifications);
Expand All @@ -59,6 +56,17 @@ const NotificationsView = ({
);
}, [markNotificationAsRead, notifications, trackEvent, createEventBuilder]);

return {
handleMarkAllAsRead,
loading,
};
}

export function useNotificationFilters(props: {
notifications: INotification[];
}) {
const { notifications } = props;

const allNotifications = useMemo(() => {
// All unique notifications
const uniqueIDs = new Set<string>();
Expand Down Expand Up @@ -92,6 +100,31 @@ const NotificationsView = ({
[allNotifications],
);

return {
allNotifications,
walletNotifications,
announcementNotifications,
};
}

const NotificationsView = ({
navigation,
}: {
navigation: NavigationProp<ParamListBase>;
}) => {
const { isLoading } = useListNotifications();
const isNotificationEnabled = useSelector(
selectIsMetamaskNotificationsEnabled,
);
const notifications = useSelector(getNotificationsList);

const { handleMarkAllAsRead, loading } = useMarkAsReadCallback({
notifications,
});

const { allNotifications, walletNotifications, announcementNotifications } =
useNotificationFilters({ notifications });

const unreadCount = useMemo(
() => allNotifications.filter((n) => !n.isRead).length,
[allNotifications],
Expand Down

0 comments on commit 78da3be

Please sign in to comment.