Add unit tests to utils config, datetime, error_handling, errors and files (#8018)
This commit is contained in:
parent
bc29223315
commit
dddd09aa9b
7 changed files with 312 additions and 8 deletions
15
app/utils/config.test.ts
Normal file
15
app/utils/config.test.ts
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {hasReliableWebsocket} from './config';
|
||||
|
||||
describe('Config utilities', () => {
|
||||
test('hasReliableWebsocket', () => {
|
||||
expect(hasReliableWebsocket('5.8.0')).toBe(false);
|
||||
expect(hasReliableWebsocket('6.4.0', 'false')).toBe(false);
|
||||
expect(hasReliableWebsocket('6.4.0', 'true')).toBe(true);
|
||||
expect(hasReliableWebsocket('9.4.0', 'false')).toBe(true);
|
||||
expect(hasReliableWebsocket('9.4.0', 'true')).toBe(true);
|
||||
expect(hasReliableWebsocket('9.4.0')).toBe(true);
|
||||
});
|
||||
});
|
||||
37
app/utils/datetime.test.ts
Normal file
37
app/utils/datetime.test.ts
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {isSameDate, isSameMonth, isSameYear, isToday, isYesterday} from './datetime';
|
||||
|
||||
describe('Datetime', () => {
|
||||
test('isSameDate (isSameMonth / isSameYear)', () => {
|
||||
expect(isSameDate(new Date('2024-03-28 02:23:27'), new Date('2025-03-28 02:23:27'))).toBe(false);
|
||||
expect(isSameDate(new Date('2024-03-28 02:23:27'), new Date('2024-02-28 02:23:27'))).toBe(false);
|
||||
expect(isSameDate(new Date('2024-03-28 02:23:27'), new Date('2024-03-18 02:23:27'))).toBe(false);
|
||||
expect(isSameDate(new Date('2024-03-28 02:23:27'), new Date('2024-03-28 00:00:00'))).toBe(true);
|
||||
expect(isSameDate(new Date('2024-03-28 02:23:27'))).toBe(false);
|
||||
expect(isSameDate(new Date())).toBe(true);
|
||||
});
|
||||
|
||||
test('isSameMonth with default', () => {
|
||||
expect(isSameMonth(new Date('2024-03-28 02:23:27'))).toBe(false);
|
||||
expect(isSameMonth(new Date())).toBe(true);
|
||||
});
|
||||
|
||||
test('isSameYear with default', () => {
|
||||
expect(isSameYear(new Date('2022-03-28 02:23:27'))).toBe(false);
|
||||
expect(isSameYear(new Date())).toBe(true);
|
||||
});
|
||||
|
||||
test('isToday', () => {
|
||||
expect(isToday(new Date('2024-03-28 02:23:27'))).toBe(false);
|
||||
expect(isToday(new Date())).toBe(true);
|
||||
});
|
||||
|
||||
test('isYesteday', () => {
|
||||
expect(isYesterday(new Date('2024-03-28 02:23:27'))).toBe(false);
|
||||
const today = new Date();
|
||||
today.setDate(today.getDate() - 1);
|
||||
expect(isYesterday(today)).toBe(true);
|
||||
});
|
||||
});
|
||||
69
app/utils/error_handling.test.ts
Normal file
69
app/utils/error_handling.test.ts
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {Alert} from 'react-native';
|
||||
import Exception from 'react-native-exception-handler';
|
||||
|
||||
import {dismissAllModals, dismissAllOverlays} from '@screens/navigation';
|
||||
import testHelper from '@test/test_helper';
|
||||
import * as Sentry from '@utils/sentry';
|
||||
|
||||
import errorHandling from './error_handling';
|
||||
import * as Log from './log';
|
||||
|
||||
jest.mock('react-native-exception-handler', () => ({
|
||||
setJSExceptionHandler: jest.fn((callback: () => void, allowInDevMode: boolean) => {
|
||||
if (!allowInDevMode) {
|
||||
callback();
|
||||
}
|
||||
}),
|
||||
}));
|
||||
|
||||
jest.mock('@utils/log', () => ({
|
||||
logWarning: jest.fn(() => ''),
|
||||
}));
|
||||
|
||||
describe('JavascriptAndNativeErrorHandler', () => {
|
||||
const warning = jest.spyOn(Log, 'logWarning');
|
||||
const error = 'some error';
|
||||
|
||||
test('Initialization', () => {
|
||||
const setJSExceptionHandler = jest.spyOn(Exception, 'setJSExceptionHandler');
|
||||
const initializeSentry = jest.spyOn(Sentry, 'initializeSentry');
|
||||
errorHandling.initializeErrorHandling();
|
||||
expect(setJSExceptionHandler).toHaveBeenCalledTimes(1);
|
||||
expect(initializeSentry).toHaveBeenCalledTimes(1);
|
||||
expect(setJSExceptionHandler).toHaveBeenCalledWith(errorHandling.errorHandler, false);
|
||||
});
|
||||
|
||||
test('nativeErrorHander', () => {
|
||||
const captureException = jest.spyOn(Sentry, 'captureException');
|
||||
errorHandling.nativeErrorHandler(error);
|
||||
expect(warning).toHaveBeenCalledTimes(1);
|
||||
expect(warning).toHaveBeenCalledWith(`Handling native error ${error}`);
|
||||
expect(captureException).toHaveBeenCalledTimes(1);
|
||||
expect(captureException).toHaveBeenCalledWith(error);
|
||||
});
|
||||
|
||||
test('errorHandler', async () => {
|
||||
const captureJSException = jest.spyOn(Sentry, 'captureJSException');
|
||||
|
||||
errorHandling.errorHandler(null, false);
|
||||
expect(warning).toHaveBeenCalledTimes(0);
|
||||
|
||||
errorHandling.errorHandler(error, true);
|
||||
expect(warning).toHaveBeenCalledTimes(1);
|
||||
expect(warning).toHaveBeenCalledWith('Handling Javascript error', error, true);
|
||||
expect(captureJSException).toHaveBeenCalledTimes(1);
|
||||
expect(captureJSException).toHaveBeenCalledWith(error, true);
|
||||
|
||||
const throwError = new Error(error);
|
||||
const alert = jest.spyOn(Alert, 'alert');
|
||||
errorHandling.errorHandler(throwError, true);
|
||||
expect(alert?.mock?.calls?.[0]?.length).toBe(4);
|
||||
alert?.mock.calls?.[0]?.[2]?.[0]?.onPress?.();
|
||||
expect(dismissAllModals).toHaveBeenCalledTimes(1);
|
||||
await testHelper.wait(20);
|
||||
expect(dismissAllOverlays).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
76
app/utils/errors.test.ts
Normal file
76
app/utils/errors.test.ts
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {createIntl} from 'react-intl';
|
||||
|
||||
import {DEFAULT_LOCALE, getTranslations} from '@i18n';
|
||||
|
||||
import {
|
||||
isServerError,
|
||||
isErrorWithMessage,
|
||||
isErrorWithDetails,
|
||||
isErrorWithIntl,
|
||||
isErrorWithStatusCode,
|
||||
isErrorWithUrl,
|
||||
getFullErrorMessage,
|
||||
} from './errors';
|
||||
|
||||
describe('Errors', () => {
|
||||
test('isServerError', () => {
|
||||
expect(isServerError('error')).toBe(false);
|
||||
expect(isServerError({message: 'some error'})).toBe(false);
|
||||
expect(isServerError({server_error_id: 'error_id'})).toBe(true);
|
||||
});
|
||||
|
||||
test('isErrorWithMessage', () => {
|
||||
expect(isErrorWithMessage('error')).toBe(false);
|
||||
expect(isErrorWithMessage({message: 'some error'})).toBe(true);
|
||||
expect(isErrorWithMessage({server_error_id: 'error_id'})).toBe(false);
|
||||
});
|
||||
|
||||
test('isErrorWithDetails', () => {
|
||||
expect(isErrorWithDetails('error')).toBe(false);
|
||||
expect(isErrorWithDetails({message: 'some error'})).toBe(false);
|
||||
expect(isErrorWithDetails({details: 'more info'})).toBe(true);
|
||||
});
|
||||
|
||||
test('isErrorWithIntl', () => {
|
||||
expect(isErrorWithIntl('error')).toBe(false);
|
||||
expect(isErrorWithIntl({message: 'some error'})).toBe(false);
|
||||
expect(isErrorWithIntl({intl: {id: 'some_error_id', defaultMessage: 'message text'}})).toBe(true);
|
||||
});
|
||||
|
||||
test('isErrorWithStatusCode', () => {
|
||||
expect(isErrorWithStatusCode('error')).toBe(false);
|
||||
expect(isErrorWithStatusCode({message: 'some error'})).toBe(false);
|
||||
expect(isErrorWithStatusCode({status_code: 95})).toBe(true);
|
||||
});
|
||||
|
||||
test('isErrorWithUrl', () => {
|
||||
expect(isErrorWithUrl('error')).toBe(false);
|
||||
expect(isErrorWithUrl({message: 'some error'})).toBe(false);
|
||||
expect(isErrorWithUrl({url: 'http://localhost:8065'})).toBe(true);
|
||||
});
|
||||
|
||||
test('getFullErrorMessage', () => {
|
||||
const locale = DEFAULT_LOCALE;
|
||||
const intl = createIntl({locale, messages: getTranslations(locale)});
|
||||
expect(getFullErrorMessage('error', intl)).toBe('error');
|
||||
expect(getFullErrorMessage({details: 'more info', message: 'error message'}, intl)).toBe('error message; more info');
|
||||
expect(getFullErrorMessage({details: 'more info', message: 'error message'}, intl, 3)).toBe('error message; error message');
|
||||
expect(getFullErrorMessage({
|
||||
details: 'more info',
|
||||
message: 'error message',
|
||||
intl: {id: 'some_error_id', defaultMessage: 'message text'},
|
||||
}, intl)).toBe('message text; more info');
|
||||
expect(getFullErrorMessage({
|
||||
details: 'more info',
|
||||
message: 'error message',
|
||||
intl: {id: 'some_error_id', defaultMessage: 'default message text'},
|
||||
})).toBe('default message text; more info');
|
||||
|
||||
expect(getFullErrorMessage({
|
||||
details: 'more info',
|
||||
})).toBe('Unknown error; more info');
|
||||
});
|
||||
});
|
||||
|
|
@ -8,9 +8,8 @@ export function isServerError(obj: unknown): obj is {server_error_id?: string} {
|
|||
typeof obj === 'object' &&
|
||||
obj !== null &&
|
||||
(
|
||||
!('server_error_id' in obj) ||
|
||||
typeof obj.server_error_id === 'string' ||
|
||||
typeof obj.server_error_id === 'undefined'
|
||||
('server_error_id' in obj) &&
|
||||
typeof obj.server_error_id === 'string'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
@ -56,11 +55,8 @@ export function isErrorWithUrl(obj: unknown): obj is {url?: string} {
|
|||
return (
|
||||
typeof obj === 'object' &&
|
||||
obj !== null &&
|
||||
(
|
||||
!('url' in obj) ||
|
||||
typeof obj.url === 'string' ||
|
||||
typeof obj.url === 'undefined'
|
||||
)
|
||||
('url' in obj) &&
|
||||
typeof obj.url === 'string'
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
110
app/utils/files.test.ts
Normal file
110
app/utils/files.test.ts
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import testHelper from '@test/test_helper';
|
||||
|
||||
import {toMilliseconds} from './datetime';
|
||||
import {getNumberFileMenuOptions, getChannelNamesWithID, getOrderedFileInfos, getFileInfosIndexes, getOrderedGalleryItems, pathWithPrefix} from './files';
|
||||
|
||||
import type ChannelModel from '@typings/database/models/servers/channel';
|
||||
|
||||
describe('Files utils', () => {
|
||||
const buildFileInfos = (): FileInfo[] => {
|
||||
return [{
|
||||
id: testHelper.generateId(),
|
||||
create_at: Date.now() + toMilliseconds({days: 3, hours: 12, minutes: 8, seconds: 23}),
|
||||
post_id: '123',
|
||||
size: 10,
|
||||
height: 0,
|
||||
width: 0,
|
||||
mime_type: 'application/pdf',
|
||||
user_id: 'me',
|
||||
extension: 'pdf',
|
||||
name: 'file 3',
|
||||
has_preview_image: false,
|
||||
}, {
|
||||
id: testHelper.generateId(),
|
||||
create_at: Date.now() + toMilliseconds({days: 1, hours: 14}),
|
||||
post_id: '123',
|
||||
size: 10,
|
||||
height: 100,
|
||||
width: 100,
|
||||
mime_type: 'image/png',
|
||||
user_id: 'me',
|
||||
extension: 'png',
|
||||
name: 'file 2',
|
||||
has_preview_image: true,
|
||||
}, {
|
||||
id: testHelper.generateId(),
|
||||
create_at: Date.now() + toMilliseconds({days: 1, hours: 12, minutes: 10}),
|
||||
post_id: '123',
|
||||
size: 10,
|
||||
height: 200,
|
||||
width: 200,
|
||||
mime_type: 'video/mp4',
|
||||
user_id: 'me',
|
||||
extension: 'mp4',
|
||||
name: 'file 1',
|
||||
has_preview_image: false,
|
||||
}];
|
||||
};
|
||||
|
||||
test('getNumberFileMenuOptions', () => {
|
||||
expect(getNumberFileMenuOptions(false, false)).toBe(1);
|
||||
expect(getNumberFileMenuOptions(true, false)).toBe(2);
|
||||
expect(getNumberFileMenuOptions(false, true)).toBe(2);
|
||||
expect(getNumberFileMenuOptions(true, true)).toBe(3);
|
||||
});
|
||||
|
||||
test('getChannelNamesWithID', () => {
|
||||
const displayNames = ['channel 1', 'channel 2', 'channel 3'];
|
||||
const channels = displayNames.map((d, i) => ({
|
||||
id: i.toString(),
|
||||
displayName: d,
|
||||
} as ChannelModel));
|
||||
const expected = channels.reduce<Record<string, string>>((obj, channel) => {
|
||||
obj[channel.id] = channel.displayName;
|
||||
return obj;
|
||||
}, {});
|
||||
|
||||
expect(getChannelNamesWithID(channels)).toEqual(expected);
|
||||
});
|
||||
|
||||
test('getOrderedFileInfos', () => {
|
||||
let fileInfos = buildFileInfos();
|
||||
const result = getOrderedFileInfos(fileInfos);
|
||||
const names = result.map((f) => f.name);
|
||||
expect(names).toEqual(['file 3', 'file 2', 'file 1']);
|
||||
|
||||
// testing the variant without setting create_at
|
||||
fileInfos = fileInfos.map((f) => ({
|
||||
...f,
|
||||
create_at: undefined,
|
||||
}));
|
||||
const result2 = getOrderedFileInfos(fileInfos);
|
||||
const names2 = result2.map((f) => f.name);
|
||||
expect(names2).toEqual(names);
|
||||
});
|
||||
|
||||
test('getFileInfosIndexes', () => {
|
||||
const fileInfos = getOrderedFileInfos(buildFileInfos());
|
||||
const result = [0, 1, 2].reduce<Record<string, number>>((obj, index) => {
|
||||
obj[fileInfos[index].id!] = index;
|
||||
return obj;
|
||||
}, {});
|
||||
|
||||
expect(getFileInfosIndexes(fileInfos)).toEqual(result);
|
||||
});
|
||||
|
||||
test('getOrderedGalleryItems', () => {
|
||||
const fileInfos = getOrderedFileInfos(buildFileInfos());
|
||||
const result = getOrderedGalleryItems(fileInfos);
|
||||
const types = result.map((f) => f.type);
|
||||
expect(types).toEqual(['file', 'image', 'video']);
|
||||
});
|
||||
|
||||
test('pathWithPrefix', () => {
|
||||
expect(pathWithPrefix('file://', 'file://something')).toEqual('file://something');
|
||||
expect(pathWithPrefix('file://', 'something')).toEqual('file://something');
|
||||
});
|
||||
});
|
||||
|
|
@ -369,6 +369,7 @@ jest.mock('@screens/navigation', () => ({
|
|||
dismissAllModalsAndPopToScreen: jest.fn(),
|
||||
dismissAllModalsAndPopToRoot: jest.fn(),
|
||||
dismissOverlay: jest.fn(() => Promise.resolve()),
|
||||
dismissAllOverlays: jest.fn(() => Promise.resolve()),
|
||||
}));
|
||||
|
||||
jest.mock('@mattermost/react-native-emm', () => ({
|
||||
|
|
|
|||
Loading…
Reference in a new issue