Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add unit tests to app/utils/general #8133

Merged
merged 1 commit into from
Aug 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
add unit tests to app/utils/general
  • Loading branch information
enahum committed Aug 7, 2024
commit d341e10b6495e0df3a6da2d4f144ea3a57e98ac9
9 changes: 3 additions & 6 deletions app/managers/network_manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -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';

Expand Down Expand Up @@ -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: {
Expand Down
10 changes: 4 additions & 6 deletions app/utils/deep_link/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -13,14 +13,15 @@ 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';
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';
Expand Down Expand Up @@ -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: {
Expand Down
8 changes: 2 additions & 6 deletions app/utils/errors.test.ts
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -14,6 +10,7 @@ import {
isErrorWithUrl,
getFullErrorMessage,
} from './errors';
import {getIntlShape} from './general';

describe('Errors', () => {
test('isServerError', () => {
Expand Down Expand Up @@ -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');
Expand Down
5 changes: 2 additions & 3 deletions app/utils/file/file_picker/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 '.';
Expand Down Expand Up @@ -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;
Expand Down
5 changes: 2 additions & 3 deletions app/utils/file/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -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();
});
Expand Down
108 changes: 108 additions & 0 deletions app/utils/general/index.test.ts
Original file line number Diff line number Diff line change
@@ -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 changes: 3 additions & 3 deletions app/utils/general/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
7 changes: 4 additions & 3 deletions app/utils/notification/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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({
Expand Down