diff --git a/app/init/global_event_handler.js b/app/init/global_event_handler.js index 2396422646..27fb747c8c 100644 --- a/app/init/global_event_handler.js +++ b/app/init/global_event_handler.js @@ -5,7 +5,7 @@ import {Alert, AppState, Dimensions, Linking, NativeModules, Platform} from 'rea import CookieManager from 'react-native-cookies'; import DeviceInfo from 'react-native-device-info'; import RNFetchBlob from 'rn-fetch-blob'; -import semver from 'semver'; +import semver from 'semver/preload'; import {setAppState, setServerVersion} from 'mattermost-redux/actions/general'; import {loadMe, logout} from 'mattermost-redux/actions/users'; @@ -226,7 +226,8 @@ class GlobalEventHandler { onServerVersionChanged = async (serverVersion) => { const {dispatch, getState} = this.store; const state = getState(); - const version = serverVersion.match(/^[0-9]*.[0-9]*.[0-9]*(-[a-zA-Z0-9.-]*)?/g)[0]; + const match = serverVersion && serverVersion.match(/^[0-9]*.[0-9]*.[0-9]*(-[a-zA-Z0-9.-]*)?/g); + const version = match && match[0]; const locale = getCurrentLocale(state); const translations = getTranslations(locale); diff --git a/app/init/global_event_handler.test.js b/app/init/global_event_handler.test.js index 55faad4337..beb29e1a1c 100644 --- a/app/init/global_event_handler.test.js +++ b/app/init/global_event_handler.test.js @@ -1,14 +1,18 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. +import {Alert} from 'react-native'; import configureMockStore from 'redux-mock-store'; import thunk from 'redux-thunk'; +import semver from 'semver/preload'; import intitialState from 'app/initial_state'; import PushNotification from 'app/push_notifications'; import mattermostBucket from 'app/mattermost_bucket'; import * as I18n from 'app/i18n'; +import {MinServerVersion} from 'assets/config'; + import GlobalEventHandler from './global_event_handler'; jest.mock('app/init/credentials', () => ({ @@ -35,6 +39,16 @@ jest.mock('react-native-status-bar-size', () => ({ addEventListener: jest.fn(), })); +jest.mock('mattermost-redux/actions/general', () => ({ + setAppState: jest.fn(), + setServerVersion: jest.fn().mockReturnValue('setServerVersion'), +})); + +jest.mock('app/actions/views/root', () => ({ + startDataCleanup: jest.fn(), + loadConfigAndLicense: jest.fn().mockReturnValue('loadConfigAndLicense'), +})); + const mockStore = configureMockStore([thunk]); const store = mockStore(intitialState); GlobalEventHandler.store = store; @@ -105,4 +119,83 @@ describe('GlobalEventHandler', () => { expect(onAppStateChange).toHaveBeenCalledWith('active'); expect(setUserTimezone).toHaveBeenCalledTimes(1); }); + + describe('onServerVersionChanged', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + const minVersion = semver.parse(MinServerVersion); + const currentUserId = 'current-user-id'; + GlobalEventHandler.store.getState = jest.fn().mockReturnValue({ + entities: { + users: { + currentUserId, + profiles: { + [currentUserId]: {}, + }, + }, + }, + }); + GlobalEventHandler.store.dispatch = jest.fn().mockReturnValue({}); + + const dispatch = jest.spyOn(GlobalEventHandler.store, 'dispatch'); + const configureAnalytics = jest.spyOn(GlobalEventHandler, 'configureAnalytics'); + const alert = jest.spyOn(Alert, 'alert'); + + it('should dispatch on invalid version with currentUserId', async () => { + const invalidVersion = 'a.b.c'; + await GlobalEventHandler.onServerVersionChanged(invalidVersion); + + expect(alert).not.toHaveBeenCalled(); + expect(dispatch).toHaveBeenCalledTimes(2); + expect(dispatch).toHaveBeenCalledWith('setServerVersion'); + expect(dispatch).toHaveBeenCalledWith('loadConfigAndLicense'); + expect(configureAnalytics).toHaveBeenCalledTimes(1); + }); + + it('should dispatch on gte min server version with currentUserId', async () => { + let version = minVersion.version; + await GlobalEventHandler.onServerVersionChanged(version); + expect(alert).not.toHaveBeenCalled(); + expect(dispatch).toHaveBeenCalledTimes(2); + expect(dispatch).toHaveBeenCalledWith('setServerVersion'); + expect(dispatch).toHaveBeenCalledWith('loadConfigAndLicense'); + expect(configureAnalytics).toHaveBeenCalledTimes(1); + + version = semver.coerce(minVersion.major + 1).version; + await GlobalEventHandler.onServerVersionChanged(version); + expect(alert).not.toHaveBeenCalled(); + expect(dispatch).toHaveBeenCalledTimes(4); + expect(configureAnalytics).toHaveBeenCalledTimes(2); + }); + + it('should alert on lt min server version', async () => { + const version = semver.coerce(minVersion.major - 1).version; + await GlobalEventHandler.onServerVersionChanged(version); + expect(alert).toHaveBeenCalled(); + expect(dispatch).not.toHaveBeenCalled(); + expect(configureAnalytics).not.toHaveBeenCalled(); + }); + + it('should not alert nor dispatch on empty, null, undefined server version', async () => { + let version; + await GlobalEventHandler.onServerVersionChanged(version); + expect(alert).not.toHaveBeenCalled(); + expect(dispatch).not.toHaveBeenCalled(); + expect(configureAnalytics).not.toHaveBeenCalled(); + + version = ''; + await GlobalEventHandler.onServerVersionChanged(version); + expect(alert).not.toHaveBeenCalled(); + expect(dispatch).not.toHaveBeenCalled(); + expect(configureAnalytics).not.toHaveBeenCalled(); + + version = null; + await GlobalEventHandler.onServerVersionChanged(version); + expect(alert).not.toHaveBeenCalled(); + expect(dispatch).not.toHaveBeenCalled(); + expect(configureAnalytics).not.toHaveBeenCalled(); + }); + }); });