add unit tests to app/utils/general (#8133)
This commit is contained in:
parent
6b5de21317
commit
bde7e3e006
8 changed files with 128 additions and 30 deletions
|
|
@ -11,7 +11,7 @@ import {
|
|||
} from '@mattermost/react-native-network-client';
|
||||
import {nativeApplicationVersion, nativeBuildVersion} from 'expo-application';
|
||||
import {modelName, osName, osVersion} from 'expo-device';
|
||||
import {defineMessages, createIntl} from 'react-intl';
|
||||
import {defineMessages} from 'react-intl';
|
||||
import {Alert, DeviceEventEmitter} from 'react-native';
|
||||
import urlParse from 'url-parse';
|
||||
|
||||
|
|
@ -20,9 +20,9 @@ import {Client} from '@client/rest';
|
|||
import * as ClientConstants from '@client/rest/constants';
|
||||
import ClientError from '@client/rest/error';
|
||||
import {CERTIFICATE_ERRORS} from '@constants/network';
|
||||
import {DEFAULT_LOCALE, getTranslations} from '@i18n';
|
||||
import ManagedApp from '@init/managed_app';
|
||||
import {toMilliseconds} from '@utils/datetime';
|
||||
import {getIntlShape} from '@utils/general';
|
||||
import {logDebug, logError} from '@utils/log';
|
||||
import {getCSRFFromCookie} from '@utils/security';
|
||||
|
||||
|
|
@ -50,10 +50,7 @@ const messages = defineMessages({
|
|||
class NetworkManager {
|
||||
private clients: Record<string, Client> = {};
|
||||
|
||||
private intl = createIntl({
|
||||
locale: DEFAULT_LOCALE,
|
||||
messages: getTranslations(DEFAULT_LOCALE),
|
||||
});
|
||||
private intl = getIntlShape();
|
||||
|
||||
private DEFAULT_CONFIG: APIClientConfiguration = {
|
||||
headers: {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
// See LICENSE.txt for license information.
|
||||
|
||||
import {match} from 'path-to-regexp';
|
||||
import {createIntl, type IntlShape} from 'react-intl';
|
||||
import {type IntlShape} from 'react-intl';
|
||||
import {Navigation} from 'react-native-navigation';
|
||||
import urlParse from 'url-parse';
|
||||
|
||||
|
|
@ -13,7 +13,7 @@ import DeepLinkType from '@app/constants/deep_linking';
|
|||
import {DeepLink, Launch, Screens} from '@constants';
|
||||
import {getDefaultThemeByAppearance} from '@context/theme';
|
||||
import DatabaseManager from '@database/manager';
|
||||
import {DEFAULT_LOCALE, getTranslations, t} from '@i18n';
|
||||
import {DEFAULT_LOCALE, t} from '@i18n';
|
||||
import WebsocketManager from '@managers/websocket_manager';
|
||||
import {getActiveServerUrl} from '@queries/app/servers';
|
||||
import {getCurrentUser, queryUsersByUsername} from '@queries/servers/user';
|
||||
|
|
@ -21,6 +21,7 @@ import {dismissAllModalsAndPopToRoot} from '@screens/navigation';
|
|||
import EphemeralStore from '@store/ephemeral_store';
|
||||
import NavigationStore from '@store/navigation_store';
|
||||
import {alertErrorWithFallback, errorBadChannel, errorUnkownUser} from '@utils/draft';
|
||||
import {getIntlShape} from '@utils/general';
|
||||
import {logError} from '@utils/log';
|
||||
import {escapeRegex} from '@utils/markdown';
|
||||
import {addNewServer} from '@utils/server';
|
||||
|
|
@ -68,10 +69,7 @@ export async function handleDeepLink(deepLinkUrl: string, intlShape?: IntlShape,
|
|||
const {database} = DatabaseManager.getServerDatabaseAndOperator(existingServerUrl);
|
||||
const currentUser = await getCurrentUser(database);
|
||||
const locale = currentUser?.locale || DEFAULT_LOCALE;
|
||||
const intl = intlShape || createIntl({
|
||||
locale,
|
||||
messages: getTranslations(locale),
|
||||
});
|
||||
const intl = intlShape || getIntlShape(locale);
|
||||
|
||||
switch (parsed.type) {
|
||||
case DeepLink.Channel: {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,6 @@
|
|||
// 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,
|
||||
|
|
@ -14,6 +10,7 @@ import {
|
|||
isErrorWithUrl,
|
||||
getFullErrorMessage,
|
||||
} from './errors';
|
||||
import {getIntlShape} from './general';
|
||||
|
||||
describe('Errors', () => {
|
||||
test('isServerError', () => {
|
||||
|
|
@ -53,8 +50,7 @@ describe('Errors', () => {
|
|||
});
|
||||
|
||||
test('getFullErrorMessage', () => {
|
||||
const locale = DEFAULT_LOCALE;
|
||||
const intl = createIntl({locale, messages: getTranslations(locale)});
|
||||
const intl = getIntlShape();
|
||||
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');
|
||||
|
|
|
|||
|
|
@ -5,16 +5,15 @@
|
|||
|
||||
import RNUtils from '@mattermost/rnutils';
|
||||
import {applicationName} from 'expo-application';
|
||||
import {createIntl} from 'react-intl';
|
||||
import {Alert, Platform} from 'react-native';
|
||||
import DocumentPicker, {type DocumentPickerResponse} from 'react-native-document-picker';
|
||||
import {launchCamera, launchImageLibrary, type Asset, type ImagePickerResponse} from 'react-native-image-picker';
|
||||
import Permissions from 'react-native-permissions';
|
||||
|
||||
import {getTranslations} from '@i18n';
|
||||
import {dismissBottomSheet} from '@screens/navigation';
|
||||
import TestHelper from '@test/test_helper';
|
||||
import {extractFileInfo, lookupMimeType} from '@utils/file';
|
||||
import {getIntlShape} from '@utils/general';
|
||||
import {logWarning} from '@utils/log';
|
||||
|
||||
import FilePickerUtil from '.';
|
||||
|
|
@ -46,7 +45,7 @@ jest.mock('@mattermost/rnutils', () => ({
|
|||
|
||||
describe('FilePickerUtil', () => {
|
||||
const mockUploadFiles = jest.fn();
|
||||
const intl = createIntl({locale: 'en', messages: getTranslations('en')});
|
||||
const intl = getIntlShape();
|
||||
const originalSelect = Platform.select;
|
||||
|
||||
let filePickerUtil: FilePickerUtil;
|
||||
|
|
|
|||
|
|
@ -2,11 +2,10 @@
|
|||
// See LICENSE.txt for license information.
|
||||
|
||||
import {getInfoAsync, deleteAsync} from 'expo-file-system';
|
||||
import {createIntl} from 'react-intl';
|
||||
import {Platform} from 'react-native';
|
||||
import Permissions from 'react-native-permissions';
|
||||
|
||||
import {getTranslations} from '@i18n';
|
||||
import {getIntlShape} from '@utils/general';
|
||||
import {logError} from '@utils/log';
|
||||
import {urlSafeBase64Encode} from '@utils/security';
|
||||
|
||||
|
|
@ -62,7 +61,7 @@ jest.mock('@utils/mattermost_managed', () => ({
|
|||
jest.mock('@utils/security', () => ({urlSafeBase64Encode: (url: string) => btoa(url)}));
|
||||
|
||||
describe('Image utils', () => {
|
||||
const intl = createIntl({locale: 'en', messages: getTranslations('en')});
|
||||
const intl = getIntlShape();
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
|
|
|||
108
app/utils/general/index.test.ts
Normal file
108
app/utils/general/index.test.ts
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import ReactNativeHapticFeedback, {HapticFeedbackTypes} from 'react-native-haptic-feedback';
|
||||
|
||||
import {getIntlShape, emptyFunction, generateId, hapticFeedback, sortByNewest, isBetaApp, type SortByCreatAt} from './';
|
||||
|
||||
// Mock necessary modules
|
||||
jest.mock('expo-application', () => ({
|
||||
applicationId: 'com.example.rnbeta',
|
||||
}));
|
||||
|
||||
jest.mock('expo-crypto', () => ({
|
||||
randomUUID: jest.fn(() => '12345678-1234-1234-1234-1234567890ab'),
|
||||
}));
|
||||
|
||||
jest.mock('react-intl', () => ({
|
||||
createIntl: jest.fn((config) => config),
|
||||
}));
|
||||
|
||||
jest.mock('react-native-haptic-feedback', () => ({
|
||||
trigger: jest.fn(),
|
||||
HapticFeedbackTypes: {
|
||||
impactLight: 'impactLight',
|
||||
impactMedium: 'impactMedium',
|
||||
},
|
||||
}));
|
||||
|
||||
jest.mock('@i18n', () => ({
|
||||
getTranslations: jest.fn((locale) => ({message: `translations for ${locale}`})),
|
||||
DEFAULT_LOCALE: 'en',
|
||||
}));
|
||||
|
||||
describe('getIntlShape', () => {
|
||||
it('should return intl shape with default locale', () => {
|
||||
const result = getIntlShape();
|
||||
expect(result).toEqual({
|
||||
locale: 'en',
|
||||
messages: {message: 'translations for en'},
|
||||
});
|
||||
});
|
||||
|
||||
it('should return intl shape with specified locale', () => {
|
||||
const result = getIntlShape('fr');
|
||||
expect(result).toEqual({
|
||||
locale: 'fr',
|
||||
messages: {message: 'translations for fr'},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('emptyFunction', () => {
|
||||
it('should do nothing', () => {
|
||||
expect(emptyFunction()).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('generateId', () => {
|
||||
it('should generate an ID without prefix', () => {
|
||||
const result = generateId();
|
||||
expect(result).toBe('12345678-1234-1234-1234-1234567890ab');
|
||||
});
|
||||
|
||||
it('should generate an ID with prefix', () => {
|
||||
const result = generateId('prefix');
|
||||
expect(result).toBe('prefix-12345678-1234-1234-1234-1234567890ab');
|
||||
});
|
||||
});
|
||||
|
||||
describe('hapticFeedback', () => {
|
||||
it('should trigger haptic feedback with default method', () => {
|
||||
hapticFeedback();
|
||||
expect(ReactNativeHapticFeedback.trigger).toHaveBeenCalledWith('impactLight', {
|
||||
enableVibrateFallback: false,
|
||||
ignoreAndroidSystemSettings: false,
|
||||
});
|
||||
});
|
||||
|
||||
it('should trigger haptic feedback with specified method', () => {
|
||||
hapticFeedback(HapticFeedbackTypes.impactMedium);
|
||||
expect(ReactNativeHapticFeedback.trigger).toHaveBeenCalledWith('impactMedium', {
|
||||
enableVibrateFallback: false,
|
||||
ignoreAndroidSystemSettings: false,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('sortByNewest', () => {
|
||||
it('should sort by newest create_at', () => {
|
||||
const a = {create_at: 2000} as SortByCreatAt;
|
||||
const b = {create_at: 1000} as SortByCreatAt;
|
||||
const result = sortByNewest(a, b);
|
||||
expect(result).toBe(-1);
|
||||
});
|
||||
|
||||
it('should sort by oldest create_at', () => {
|
||||
const a = {create_at: 1000} as SortByCreatAt;
|
||||
const b = {create_at: 2000} as SortByCreatAt;
|
||||
const result = sortByNewest(a, b);
|
||||
expect(result).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isBetaApp', () => {
|
||||
it('should be true if applicationId includes rnbeta', () => {
|
||||
expect(isBetaApp).toBe(true);
|
||||
});
|
||||
});
|
||||
|
|
@ -6,13 +6,13 @@ import {randomUUID} from 'expo-crypto';
|
|||
import {createIntl} from 'react-intl';
|
||||
import ReactNativeHapticFeedback, {HapticFeedbackTypes} from 'react-native-haptic-feedback';
|
||||
|
||||
import {getTranslations} from '@i18n';
|
||||
import {DEFAULT_LOCALE, getTranslations} from '@i18n';
|
||||
|
||||
type SortByCreatAt = (Session | Channel | Team | Post) & {
|
||||
export type SortByCreatAt = (Session | Channel | Team | Post) & {
|
||||
create_at: number;
|
||||
}
|
||||
|
||||
export function getIntlShape(locale = 'en') {
|
||||
export function getIntlShape(locale = DEFAULT_LOCALE) {
|
||||
return createIntl({
|
||||
locale,
|
||||
messages: getTranslations(locale),
|
||||
|
|
|
|||
|
|
@ -2,14 +2,15 @@
|
|||
// See LICENSE.txt for license information.
|
||||
|
||||
import moment from 'moment-timezone';
|
||||
import {createIntl, type IntlShape} from 'react-intl';
|
||||
import {type IntlShape} from 'react-intl';
|
||||
import {Alert, DeviceEventEmitter} from 'react-native';
|
||||
|
||||
import {Events} from '@constants';
|
||||
import {NOTIFICATION_TYPE} from '@constants/push_notification';
|
||||
import {DEFAULT_LOCALE, getTranslations} from '@i18n';
|
||||
import {DEFAULT_LOCALE} from '@i18n';
|
||||
import PushNotifications from '@init/push_notifications';
|
||||
import {popToRoot} from '@screens/navigation';
|
||||
import {getIntlShape} from '@utils/general';
|
||||
|
||||
export const convertToNotificationData = (notification: Notification, tapped = true): NotificationWithData => {
|
||||
if (!notification.payload) {
|
||||
|
|
@ -95,7 +96,7 @@ export const scheduleExpiredNotification = (serverUrl: string, session: Session,
|
|||
const expiresInHours = Math.ceil(Math.abs(moment.duration(moment().diff(moment(expiresAt))).asHours()));
|
||||
const expiresInDays = Math.floor(expiresInHours / 24); // Calculate expiresInDays
|
||||
const remainingHours = expiresInHours % 24; // Calculate remaining hours
|
||||
const intl = createIntl({locale, messages: getTranslations(locale)});
|
||||
const intl = getIntlShape(locale);
|
||||
let body = '';
|
||||
if (expiresInDays === 0) {
|
||||
body = intl.formatMessage({
|
||||
|
|
|
|||
Loading…
Reference in a new issue