Skip to content

Commit

Permalink
test: notifications integration tests (#28022)
Browse files Browse the repository at this point in the history
<!--
Please submit this PR as a draft initially.
Do not mark it as "Ready for review" until the template has been
completely filled out, and PR status checks have passed at least once.
-->

## **Description**
First set of integration tests for notifications feature
Adds Tests for:
* Turning on notifications for first time 
* Marking notifications as read
* Disabling notifications
* Toggling product announcements
* Metametrics for all the above

[![Open in GitHub
Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/28022?quickstart=1)

## **Related issues**

https://consensyssoftware.atlassian.net/browse/NOTIFY-1259

## **Manual testing steps**

1. Run integration tests using `yarn test:integration`
2. All tests should pass

## **Screenshots/Recordings**
<img width="441" alt="Screenshot 2024-10-22 at 21 00 13"
src="https://github.com/user-attachments/assets/14f65cc3-0653-4cb6-88da-dbc0d20b267f">


### **Before**

<!-- [screenshots/recordings] -->

### **After**

<!-- [screenshots/recordings] -->

## **Pre-merge author checklist**

- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask
Extension Coding
Standards](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/CODING_GUIDELINES.md).
- [x] I've completed the PR template to the best of my ability
- [x] I’ve included tests if applicable
- [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [ ] I’ve applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.

## **Pre-merge reviewer checklist**

- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.

---------

Co-authored-by: Priya Narayanaswamy <priya.narayanaswamy@consensys.net>
  • Loading branch information
cmd-ob and pnarayanaswamy authored Oct 24, 2024
1 parent f0aad1c commit da377be
Show file tree
Hide file tree
Showing 9 changed files with 726 additions and 1 deletion.
3 changes: 2 additions & 1 deletion test/integration/data/integration-init-state.json
Original file line number Diff line number Diff line change
Expand Up @@ -783,7 +783,8 @@
"showTestNetworks": true,
"smartTransactionsOptInStatus": false,
"petnamesEnabled": false,
"showConfirmationAdvancedDetails": false
"showConfirmationAdvancedDetails": false,
"showMultiRpcModal": false
},
"preventPollingOnNetworkRestart": true,
"previousAppVersion": "11.14.4",
Expand Down
54 changes: 54 additions & 0 deletions test/integration/notifications&auth/data/notification-state.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import {
INotification,
TRIGGER_TYPES,
processNotification,
} from '@metamask/notification-services-controller/notification-services';
import {
createMockNotificationEthSent,
createMockFeatureAnnouncementRaw,
} from '@metamask/notification-services-controller/notification-services/mocks';
import mockMetaMaskState from '../../data/integration-init-state.json';

const notificationsAccountAddress =
mockMetaMaskState.internalAccounts.accounts[
mockMetaMaskState.internalAccounts
.selectedAccount as keyof typeof mockMetaMaskState.internalAccounts.accounts
].address;

export const ethSentNotification = processNotification(
createMockNotificationEthSent(),
) as Extract<INotification, { type: TRIGGER_TYPES.ETH_SENT }>;

if (ethSentNotification.type === TRIGGER_TYPES.ETH_SENT) {
ethSentNotification.address = notificationsAccountAddress;
ethSentNotification.data.from = notificationsAccountAddress;
ethSentNotification.isRead = true;
}

export const featureNotification = processNotification(
createMockFeatureAnnouncementRaw(),
) as Extract<INotification, { type: TRIGGER_TYPES.FEATURES_ANNOUNCEMENT }>;

if (featureNotification.type === TRIGGER_TYPES.FEATURES_ANNOUNCEMENT) {
featureNotification.isRead = true;
}

export const getMockedNotificationsState = () => {
return {
...mockMetaMaskState,
isProfileSyncingEnabled: true,
isProfileSyncingUpdateLoading: false,
isMetamaskNotificationsFeatureSeen: true,
isNotificationServicesEnabled: true,
isFeatureAnnouncementsEnabled: true,
notifications: {},
metamaskNotificationsReadList: [featureNotification.id],
metamaskNotificationsList: [featureNotification, ethSentNotification],
isUpdatingMetamaskNotifications: false,
isFetchingMetamaskNotifications: false,
isUpdatingMetamaskNotificationsAccount: [],
useExternalServices: true,
pendingApprovalCount: 0,
pendingApprovals: {},
};
};
196 changes: 196 additions & 0 deletions test/integration/notifications&auth/notifications-activation.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
import {
act,
fireEvent,
waitFor,
screen,
within,
} from '@testing-library/react';
import { integrationTestRender } from '../../lib/render-helpers';
import * as backgroundConnection from '../../../ui/store/background-connection';
import { createMockImplementation } from '../helpers';
import {
MetaMetricsEventCategory,
MetaMetricsEventName,
} from '../../../shared/constants/metametrics';
import { getMockedNotificationsState } from './data/notification-state';

jest.mock('../../../ui/store/background-connection', () => ({
...jest.requireActual('../../../ui/store/background-connection'),
submitRequestToBackground: jest.fn(),
callBackgroundMethod: jest.fn(),
}));

const backgroundConnectionMocked = {
onNotification: jest.fn(),
};

const mockedBackgroundConnection = jest.mocked(backgroundConnection);

const setupSubmitRequestToBackgroundMocks = (
mockRequests?: Record<string, unknown>,
) => {
mockedBackgroundConnection.submitRequestToBackground.mockImplementation(
createMockImplementation({
...(mockRequests ?? {}),
}),
);
};

const trackNotificationsActivatedMetaMetricsEvent = async (
actionType: string,
profileSyncEnabled: boolean,
) => {
const expectedCall = [
'trackMetaMetricsEvent',
[
expect.objectContaining({
event: MetaMetricsEventName.NotificationsActivated,
category: MetaMetricsEventCategory.NotificationsActivationFlow,
properties: {
action_type: actionType,
is_profile_syncing_enabled: profileSyncEnabled,
},
}),
],
];

expect(
mockedBackgroundConnection.submitRequestToBackground.mock.calls,
).toStrictEqual(expect.arrayContaining([expectedCall]));
};
describe('Notifications Activation', () => {
beforeEach(() => {
jest.resetAllMocks();
setupSubmitRequestToBackgroundMocks();
});

afterEach(() => {
window.history.pushState({}, '', '/'); // return to homescreen
});

const clickElement = async (testId: string) => {
await act(async () => {
fireEvent.click(screen.getByTestId(testId));
});
};

const waitForElement = async (testId: string) => {
await waitFor(() => {
expect(screen.getByTestId(testId)).toBeInTheDocument();
});
};

it('should successfully activate notification for the first time', async () => {
const mockedState = getMockedNotificationsState();
await act(async () => {
await integrationTestRender({
preloadedState: {
...mockedState,
isProfileSyncingEnabled: false,
isNotificationServicesEnabled: false,
isFeatureAnnouncementsEnabled: false,
isMetamaskNotificationsFeatureSeen: false,
},
backgroundConnection: backgroundConnectionMocked,
});

await clickElement('account-options-menu-button');
await waitForElement('notifications-menu-item');
await clickElement('notifications-menu-item');

await waitFor(() => {
expect(
within(screen.getByRole('dialog')).getByText('Turn on'),
).toBeInTheDocument();
});

await act(async () => {
fireEvent.click(screen.getByText('Turn on'));
});

await waitFor(() => {
const createOnChainTriggersCall =
mockedBackgroundConnection.submitRequestToBackground.mock.calls?.find(
(call) => call[0] === 'createOnChainTriggers',
);

expect(createOnChainTriggersCall?.[0]).toBe('createOnChainTriggers');
});

await trackNotificationsActivatedMetaMetricsEvent('started', false);
await trackNotificationsActivatedMetaMetricsEvent('activated', true);
});
});

it('should successfully send correct metrics when notifications modal is dismissed', async () => {
const mockedState = getMockedNotificationsState();
await act(async () => {
await integrationTestRender({
preloadedState: {
...mockedState,
isProfileSyncingEnabled: false,
isNotificationServicesEnabled: false,
isFeatureAnnouncementsEnabled: false,
isMetamaskNotificationsFeatureSeen: false,
},
backgroundConnection: backgroundConnectionMocked,
});

await clickElement('account-options-menu-button');
await waitForElement('notifications-menu-item');
await clickElement('notifications-menu-item');

await waitFor(() => {
expect(
within(screen.getByRole('dialog')).getByText('Turn on'),
).toBeInTheDocument();
});

await act(async () => {
fireEvent.click(
within(screen.getByRole('dialog')).getByRole('button', {
name: 'Close',
}),
);
});

await trackNotificationsActivatedMetaMetricsEvent('dismissed', false);
});
});

it('should successfully send correct metrics when notifications modal is dismissed', async () => {
const mockedState = getMockedNotificationsState();
await act(async () => {
await integrationTestRender({
preloadedState: {
...mockedState,
isProfileSyncingEnabled: false,
isNotificationServicesEnabled: false,
isFeatureAnnouncementsEnabled: false,
isMetamaskNotificationsFeatureSeen: false,
},
backgroundConnection: backgroundConnectionMocked,
});

await clickElement('account-options-menu-button');
await waitForElement('notifications-menu-item');
await clickElement('notifications-menu-item');

await waitFor(() => {
expect(
within(screen.getByRole('dialog')).getByText('Turn on'),
).toBeInTheDocument();
});

await act(async () => {
fireEvent.click(
within(screen.getByRole('dialog')).getByRole('button', {
name: 'Close',
}),
);
});

await trackNotificationsActivatedMetaMetricsEvent('dismissed', false);
});
});
});
Loading

0 comments on commit da377be

Please sign in to comment.