329 lines
12 KiB
TypeScript
329 lines
12 KiB
TypeScript
// 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]);
|
|
});
|
|
});
|
|
});
|