test: Add tests for launch.ts initialization logic (#8547)
This commit is contained in:
parent
53b8b6e59f
commit
fe2cc0673d
2 changed files with 332 additions and 3 deletions
329
app/init/launch.test.ts
Normal file
329
app/init/launch.test.ts
Normal file
|
|
@ -0,0 +1,329 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {AppState, Linking, Platform} from 'react-native';
|
||||
import {Notifications} from 'react-native-notifications';
|
||||
|
||||
import {removePost} from '@actions/local/post';
|
||||
import {switchToChannelById} from '@actions/remote/channel';
|
||||
import {appEntry, pushNotificationEntry} from '@actions/remote/entry';
|
||||
import {fetchAndSwitchToThread} from '@actions/remote/thread';
|
||||
import LocalConfig from '@assets/config.json';
|
||||
import {DeepLink, Launch, PushNotification} from '@constants';
|
||||
import DatabaseManager from '@database/manager';
|
||||
import {getActiveServerUrl, getServerCredentials} from '@init/credentials';
|
||||
import {getLastViewedChannelIdAndServer, getOnboardingViewed, getLastViewedThreadIdAndServer} from '@queries/app/global';
|
||||
import {getAllServers} from '@queries/app/servers';
|
||||
import {queryPostsByType} from '@queries/servers/post';
|
||||
import {getCurrentUserId} from '@queries/servers/system';
|
||||
import {queryMyTeams} from '@queries/servers/team';
|
||||
import {resetToHome, resetToSelectServer, resetToOnboarding} from '@screens/navigation';
|
||||
import {getLaunchPropsFromDeepLink} from '@utils/deep_link';
|
||||
|
||||
import {initialLaunch, launchToHome, cleanupEphemeralPosts, getLaunchPropsFromNotification} from './launch';
|
||||
|
||||
import type ServersModel from '@typings/database/models/app/servers';
|
||||
import type {LaunchProps} from '@typings/launch';
|
||||
|
||||
jest.mock('react-native-notifications', () => ({
|
||||
Notifications: {
|
||||
getInitialNotification: jest.fn().mockResolvedValue(null),
|
||||
ios: {
|
||||
getDeliveredNotifications: jest.fn().mockResolvedValue([]),
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
jest.mock('@actions/local/post');
|
||||
jest.mock('@actions/remote/channel');
|
||||
jest.mock('@actions/remote/entry', () => ({
|
||||
appEntry: jest.fn(),
|
||||
pushNotificationEntry: jest.fn(),
|
||||
upgradeEntry: jest.fn().mockResolvedValue({}),
|
||||
}));
|
||||
jest.mock('@actions/remote/thread');
|
||||
jest.mock('@database/manager', () => ({
|
||||
getActiveServerUrl: jest.fn(),
|
||||
getActiveServerDisplayName: jest.fn(),
|
||||
destroyServerDatabase: jest.fn(),
|
||||
deleteServerDatabase: jest.fn(),
|
||||
searchUrl: jest.fn(),
|
||||
getServerUrlFromIdentifier: jest.fn(),
|
||||
getServerDatabaseAndOperator: jest.fn(),
|
||||
serverDatabases: {},
|
||||
}));
|
||||
jest.mock('@init/credentials');
|
||||
jest.mock('@queries/app/global');
|
||||
jest.mock('@queries/app/servers');
|
||||
jest.mock('@queries/servers/post');
|
||||
jest.mock('@queries/servers/preference');
|
||||
jest.mock('@queries/servers/system');
|
||||
jest.mock('@queries/servers/team');
|
||||
jest.mock('@screens/navigation', () => ({
|
||||
resetToHome: jest.fn().mockResolvedValue(''),
|
||||
resetToSelectServer: jest.fn().mockResolvedValue(''),
|
||||
resetToTeams: jest.fn().mockResolvedValue(''),
|
||||
resetToOnboarding: jest.fn().mockResolvedValue(''),
|
||||
}));
|
||||
jest.mock('@utils/deep_link');
|
||||
jest.mock('@store/ephemeral_store');
|
||||
|
||||
describe('Launch', () => {
|
||||
const mockServerUrl = 'http://server-1.com';
|
||||
jest.mocked(DatabaseManager.getActiveServerUrl).mockResolvedValue(mockServerUrl);
|
||||
|
||||
const mockDatabase = {};
|
||||
const mockServerDatabases = {
|
||||
[mockServerUrl]: {
|
||||
database: mockDatabase,
|
||||
},
|
||||
} as any;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
DatabaseManager.serverDatabases = mockServerDatabases;
|
||||
Platform.OS = 'ios';
|
||||
AppState.currentState = 'active';
|
||||
});
|
||||
|
||||
describe('initialLaunch', () => {
|
||||
it('should handle deep link launch', async () => {
|
||||
const deepLinkUrl = 'mattermost://server-1.com';
|
||||
const launchProps = {
|
||||
launchType: Launch.DeepLink,
|
||||
serverUrl: 'server-1.com',
|
||||
extra: {data: {serverUrl: 'server-1.com'}, type: DeepLink.Server},
|
||||
} as LaunchProps;
|
||||
|
||||
jest.spyOn(Linking, 'getInitialURL').mockResolvedValue(deepLinkUrl);
|
||||
jest.mocked(getLaunchPropsFromDeepLink).mockReturnValue(launchProps);
|
||||
jest.mocked(resetToSelectServer).mockResolvedValue('');
|
||||
|
||||
await initialLaunch();
|
||||
|
||||
expect(Linking.getInitialURL).toHaveBeenCalled();
|
||||
expect(getLaunchPropsFromDeepLink).toHaveBeenCalledWith(deepLinkUrl, true);
|
||||
expect(resetToSelectServer).toHaveBeenCalledWith(launchProps);
|
||||
});
|
||||
|
||||
it('should handle notification launch', async () => {
|
||||
const payload = {type: PushNotification.NOTIFICATION_TYPE.SESSION, ack_id: 'ack1'};
|
||||
|
||||
jest.spyOn(Linking, 'getInitialURL').mockResolvedValue(null);
|
||||
jest.mocked(resetToSelectServer).mockResolvedValue('');
|
||||
|
||||
jest.mocked(Notifications.getInitialNotification).mockResolvedValueOnce({payload} as any);
|
||||
|
||||
await initialLaunch();
|
||||
|
||||
expect(Linking.getInitialURL).toHaveBeenCalled();
|
||||
expect(resetToSelectServer).toHaveBeenCalledWith(expect.objectContaining({launchType: Launch.Notification}));
|
||||
});
|
||||
|
||||
it('should handle normal launch with active server', async () => {
|
||||
const serverUrl = 'http://server-1.com';
|
||||
const credentials = {token: 'token1'} as ServerCredential;
|
||||
|
||||
jest.spyOn(Linking, 'getInitialURL').mockResolvedValue(null);
|
||||
jest.mocked(getActiveServerUrl).mockResolvedValue(serverUrl);
|
||||
jest.mocked(getServerCredentials).mockResolvedValue(credentials);
|
||||
jest.mocked(getCurrentUserId).mockResolvedValue('user1');
|
||||
jest.mocked(queryMyTeams).mockReturnValue({fetchCount: () => 1} as any);
|
||||
|
||||
await initialLaunch();
|
||||
|
||||
expect(getActiveServerUrl).toHaveBeenCalled();
|
||||
expect(getServerCredentials).toHaveBeenCalledWith(serverUrl);
|
||||
expect(getCurrentUserId).toHaveBeenCalled();
|
||||
expect(appEntry).toHaveBeenCalledWith(serverUrl);
|
||||
expect(resetToHome).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should handle upgrade launch with no current user', async () => {
|
||||
jest.mocked(getCurrentUserId).mockResolvedValueOnce('');
|
||||
|
||||
await initialLaunch();
|
||||
|
||||
expect(resetToHome).toHaveBeenCalledWith(expect.objectContaining({launchType: Launch.Upgrade}));
|
||||
});
|
||||
|
||||
it('should show onboarding when enabled and not viewed', async () => {
|
||||
LocalConfig.ShowOnboarding = true;
|
||||
jest.spyOn(Linking, 'getInitialURL').mockResolvedValue(null);
|
||||
jest.mocked(getActiveServerUrl).mockResolvedValue(undefined);
|
||||
jest.mocked(getOnboardingViewed).mockResolvedValue(false);
|
||||
|
||||
await initialLaunch();
|
||||
|
||||
expect(resetToOnboarding).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('launchToHome', () => {
|
||||
const serverUrl = 'http://server-1.com';
|
||||
|
||||
beforeEach(() => {
|
||||
DatabaseManager.serverDatabases = {
|
||||
[serverUrl]: {database: {}} as any,
|
||||
};
|
||||
});
|
||||
|
||||
it('should handle deep link launch', async () => {
|
||||
const props = {
|
||||
launchType: Launch.DeepLink,
|
||||
serverUrl,
|
||||
};
|
||||
|
||||
await launchToHome(props);
|
||||
|
||||
expect(appEntry).toHaveBeenCalledWith(serverUrl);
|
||||
expect(resetToHome).toHaveBeenCalledWith(props);
|
||||
});
|
||||
|
||||
it('should handle notification launch', async () => {
|
||||
const notification = {
|
||||
payload: {
|
||||
channel_id: 'channel1',
|
||||
},
|
||||
userInteraction: true,
|
||||
};
|
||||
const props = {
|
||||
launchType: Launch.Notification,
|
||||
serverUrl,
|
||||
extra: notification,
|
||||
} as LaunchProps;
|
||||
|
||||
await launchToHome(props);
|
||||
|
||||
expect(pushNotificationEntry).toHaveBeenCalledWith(serverUrl, notification.payload, 'Notification');
|
||||
expect(resetToHome).toHaveBeenCalledWith(props);
|
||||
});
|
||||
|
||||
it('should handle cold start with last viewed channel', async () => {
|
||||
const lastChannel = {
|
||||
server_url: serverUrl,
|
||||
channel_id: 'channel1',
|
||||
};
|
||||
jest.mocked(getLastViewedChannelIdAndServer).mockResolvedValue(lastChannel);
|
||||
jest.mocked(getLastViewedThreadIdAndServer).mockResolvedValue(null);
|
||||
|
||||
const props = {
|
||||
launchType: Launch.Normal,
|
||||
serverUrl,
|
||||
coldStart: true,
|
||||
};
|
||||
|
||||
await launchToHome(props);
|
||||
|
||||
expect(switchToChannelById).toHaveBeenCalledWith(serverUrl, 'channel1');
|
||||
expect(appEntry).toHaveBeenCalledWith(serverUrl);
|
||||
});
|
||||
|
||||
it('should handle cold start with last viewed thread', async () => {
|
||||
const lastThread = {
|
||||
server_url: serverUrl,
|
||||
thread_id: 'thread1',
|
||||
};
|
||||
jest.mocked(getLastViewedChannelIdAndServer).mockResolvedValue(null);
|
||||
jest.mocked(getLastViewedThreadIdAndServer).mockResolvedValue(lastThread);
|
||||
|
||||
const props = {
|
||||
launchType: Launch.Normal,
|
||||
serverUrl,
|
||||
coldStart: true,
|
||||
};
|
||||
|
||||
await launchToHome(props);
|
||||
|
||||
expect(fetchAndSwitchToThread).toHaveBeenCalledWith(serverUrl, 'thread1');
|
||||
expect(appEntry).toHaveBeenCalledWith(serverUrl);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getLaunchPropsFromNotification', () => {
|
||||
DatabaseManager.getServerDatabaseAndOperator = jest.fn().mockReturnValue({
|
||||
database: {},
|
||||
operator: {},
|
||||
});
|
||||
|
||||
it('should return launch props with server url from payload', async () => {
|
||||
const notification = {
|
||||
payload: {
|
||||
server_url: 'http://server-1.com',
|
||||
channel_id: 'channel1',
|
||||
},
|
||||
userInteraction: true,
|
||||
} as NotificationWithData;
|
||||
|
||||
jest.mocked(DatabaseManager.searchUrl).mockReturnValue('http://server-1.com');
|
||||
|
||||
const result = await getLaunchPropsFromNotification(notification);
|
||||
|
||||
expect(result).toEqual({
|
||||
launchType: Launch.Notification,
|
||||
extra: notification,
|
||||
serverUrl: 'http://server-1.com',
|
||||
coldStart: false,
|
||||
});
|
||||
});
|
||||
|
||||
it('should return launch props with server url from server id', async () => {
|
||||
const notification = {
|
||||
payload: {
|
||||
server_id: 'server1',
|
||||
channel_id: 'channel1',
|
||||
},
|
||||
userInteraction: true,
|
||||
} as NotificationWithData;
|
||||
|
||||
jest.mocked(DatabaseManager.getServerUrlFromIdentifier).mockResolvedValue('http://server-1.com');
|
||||
|
||||
const result = await getLaunchPropsFromNotification(notification);
|
||||
|
||||
expect(result).toEqual({
|
||||
launchType: Launch.Notification,
|
||||
extra: notification,
|
||||
serverUrl: 'http://server-1.com',
|
||||
coldStart: false,
|
||||
});
|
||||
});
|
||||
|
||||
it('should return launch props with error when server not found', async () => {
|
||||
const notification = {
|
||||
payload: {
|
||||
channel_id: 'channel1',
|
||||
},
|
||||
userInteraction: true,
|
||||
} as NotificationWithData;
|
||||
|
||||
const result = await getLaunchPropsFromNotification(notification);
|
||||
|
||||
expect(result).toEqual({
|
||||
launchType: Launch.Notification,
|
||||
extra: notification,
|
||||
launchError: true,
|
||||
coldStart: false,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('cleanupEphemeralPosts', () => {
|
||||
it('should remove ephemeral posts', async () => {
|
||||
const serverUrl = 'http://server-1.com';
|
||||
const ephemeralPosts = [{id: 'post1'}, {id: 'post2'}];
|
||||
|
||||
DatabaseManager.serverDatabases = {
|
||||
[serverUrl]: {database: {}} as any,
|
||||
};
|
||||
|
||||
jest.mocked(getAllServers).mockResolvedValue([{url: serverUrl} as ServersModel]);
|
||||
jest.mocked(queryPostsByType).mockReturnValue({fetch: () => ephemeralPosts} as any);
|
||||
|
||||
await cleanupEphemeralPosts();
|
||||
|
||||
expect(removePost).toHaveBeenCalledTimes(2);
|
||||
expect(removePost).toHaveBeenCalledWith(serverUrl, ephemeralPosts[0]);
|
||||
expect(removePost).toHaveBeenCalledWith(serverUrl, ephemeralPosts[1]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -76,7 +76,7 @@ const launchAppFromNotification = async (notification: NotificationWithData, col
|
|||
|
||||
* @returns a redirection to a screen, either onboarding, add_server, login or home depending on the scenario
|
||||
*/
|
||||
const launchApp = async (props: LaunchProps) => {
|
||||
export const launchApp = async (props: LaunchProps) => {
|
||||
let serverUrl: string | undefined;
|
||||
switch (props?.launchType) {
|
||||
case Launch.DeepLink:
|
||||
|
|
@ -169,7 +169,7 @@ const launchApp = async (props: LaunchProps) => {
|
|||
return resetToSelectServer(props);
|
||||
};
|
||||
|
||||
const launchToHome = async (props: LaunchProps) => {
|
||||
export const launchToHome = async (props: LaunchProps) => {
|
||||
let openPushNotification = false;
|
||||
|
||||
switch (props.launchType) {
|
||||
|
|
@ -259,7 +259,7 @@ export const getLaunchPropsFromNotification = async (notification: NotificationW
|
|||
return launchProps;
|
||||
};
|
||||
|
||||
async function cleanupEphemeralPosts() {
|
||||
export async function cleanupEphemeralPosts() {
|
||||
const servers = await getAllServers();
|
||||
|
||||
for (const server of servers) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue