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

[No QA] [TS migration] Migrate 'EmojiTest.js', 'SidebarFilterTest.js', 'SidebarOrderTest.js', 'LHNTestUtils.js', 'SessionTest.js' test to TypeScript #37204

Merged
merged 6 commits into from
Feb 28, 2024
Merged
Show file tree
Hide file tree
Changes from 5 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
2 changes: 1 addition & 1 deletion src/libs/EmojiUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,7 @@ function replaceAndExtractEmojis(text: string, preferredSkinTone: number = CONST
* Suggest emojis when typing emojis prefix after colon
* @param [limit] - matching emojis limit
*/
function suggestEmojis(text: string, lang: keyof SupportedLanguage, limit = CONST.AUTO_COMPLETE_SUGGESTER.MAX_AMOUNT_OF_SUGGESTIONS): Emoji[] | undefined {
function suggestEmojis(text: string, lang: SupportedLanguage, limit: number = CONST.AUTO_COMPLETE_SUGGESTER.MAX_AMOUNT_OF_SUGGESTIONS): Emoji[] | undefined {
// emojisTrie is importing the emoji JSON file on the app starting and we want to avoid it
const emojisTrie = require('./EmojiTrie').default;

Expand Down
2 changes: 1 addition & 1 deletion src/pages/home/report/ReportActionItemSingle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import type ChildrenProps from '@src/types/utils/ChildrenProps';
import ReportActionItemDate from './ReportActionItemDate';
import ReportActionItemFragment from './ReportActionItemFragment';

type ReportActionItemSingleProps = ChildrenProps & {
type ReportActionItemSingleProps = Partial<ChildrenProps> & {
/** All the data of the action */
action: ReportAction;

Expand Down
5 changes: 4 additions & 1 deletion src/types/onyx/Report.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type {ValueOf} from 'type-fest';
import type CONST from '@src/CONST';
import type ONYXKEYS from '@src/ONYXKEYS';
import type * as OnyxCommon from './OnyxCommon';
import type PersonalDetails from './PersonalDetails';
import type {PolicyReportField} from './PolicyReportField';
Expand Down Expand Up @@ -178,6 +179,8 @@ type Report = {
reportFields?: Record<string, PolicyReportField>;
};

type ReportCollectionDataSet = Record<`${typeof ONYXKEYS.COLLECTION.REPORT}${string}`, Report>;

export default Report;

export type {NotificationPreference, RoomVisibility, WriteCapability, Note};
export type {NotificationPreference, RoomVisibility, WriteCapability, Note, ReportCollectionDataSet};
3 changes: 3 additions & 0 deletions src/types/onyx/ReportAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,9 @@ type ReportActionBase = {

actorAccountID?: number;

/** The account of the last message's actor */
actor?: string;

/** Person who created the action */
person?: Person[];

Expand Down
44 changes: 22 additions & 22 deletions tests/actions/SessionTest.js → tests/actions/SessionTest.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
import {beforeEach, jest, test} from '@jest/globals';
import Onyx from 'react-native-onyx';
import CONST from '../../src/CONST';
import * as App from '../../src/libs/actions/App';
import OnyxUpdateManager from '../../src/libs/actions/OnyxUpdateManager';
import HttpUtils from '../../src/libs/HttpUtils';
import PushNotification from '../../src/libs/Notification/PushNotification';
import type {OnyxEntry} from 'react-native-onyx';
import * as App from '@libs/actions/App';
import OnyxUpdateManager from '@libs/actions/OnyxUpdateManager';
import HttpUtils from '@libs/HttpUtils';
import PushNotification from '@libs/Notification/PushNotification';
// This lib needs to be imported, but it has nothing to export since all it contains is an Onyx connection
// eslint-disable-next-line no-unused-vars
import subscribePushNotification from '../../src/libs/Notification/PushNotification/subscribePushNotification';
import ONYXKEYS from '../../src/ONYXKEYS';
import '@libs/Notification/PushNotification/subscribePushNotification';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type {Credentials, Session} from '@src/types/onyx';
import * as TestHelper from '../utils/TestHelper';
import waitForBatchedUpdates from '../utils/waitForBatchedUpdates';

// We are mocking this method so that we can later test to see if it was called and what arguments it was called with.
// We test HttpUtils.xhr() since this means that our API command turned into a network request and isn't only queued.
HttpUtils.xhr = jest.fn();
HttpUtils.xhr = jest.fn<typeof HttpUtils.xhr>();

// Mocked to ensure push notifications are subscribed/unsubscribed as the session changes
jest.mock('../../src/libs/Notification/PushNotification');
jest.mock('@libs/Notification/PushNotification');

Onyx.init({
keys: ONYXKEYS,
registerStorageEventListener: () => {},
});

OnyxUpdateManager();
Expand All @@ -35,13 +35,13 @@ describe('Session', () => {
const TEST_INITIAL_AUTH_TOKEN = 'initialAuthToken';
const TEST_REFRESHED_AUTH_TOKEN = 'refreshedAuthToken';

let credentials;
let credentials: OnyxEntry<Credentials> = null;
Onyx.connect({
key: ONYXKEYS.CREDENTIALS,
callback: (val) => (credentials = val || {}),
callback: (val) => (credentials = val),
});

let session;
let session: OnyxEntry<Session> = null;
Onyx.connect({
key: ONYXKEYS.SESSION,
callback: (val) => (session = val),
Expand All @@ -53,19 +53,19 @@ describe('Session', () => {
.then(() => {
// Then our re-authentication credentials should be generated and our session data
// have the correct information + initial authToken.
expect(credentials.login).toBe(TEST_USER_LOGIN);
expect(credentials.autoGeneratedLogin).not.toBeUndefined();
expect(credentials.autoGeneratedPassword).not.toBeUndefined();
expect(session.authToken).toBe(TEST_INITIAL_AUTH_TOKEN);
expect(session.accountID).toBe(TEST_USER_ACCOUNT_ID);
expect(session.email).toBe(TEST_USER_LOGIN);
expect(credentials?.login).toBe(TEST_USER_LOGIN);
expect(credentials?.autoGeneratedLogin).not.toBeUndefined();
expect(credentials?.autoGeneratedPassword).not.toBeUndefined();
expect(session?.authToken).toBe(TEST_INITIAL_AUTH_TOKEN);
expect(session?.accountID).toBe(TEST_USER_ACCOUNT_ID);
expect(session?.email).toBe(TEST_USER_LOGIN);

// At this point we have an authToken. To simulate it expiring we'll just make another
// request and mock the response so it returns 407. Once this happens we should attempt
// to Re-Authenticate with the stored credentials. Our next call will be to Authenticate
// so we will mock that response with a new authToken and then verify that Onyx has our
// data.
HttpUtils.xhr
(HttpUtils.xhr as jest.MockedFunction<typeof HttpUtils.xhr>)

// This will make the call to OpenApp below return with an expired session code
.mockImplementationOnce(() =>
Expand All @@ -92,7 +92,7 @@ describe('Session', () => {
.then(() => {
// Then it should fail and reauthenticate the user adding the new authToken to the session
// data in Onyx
expect(session.authToken).toBe(TEST_REFRESHED_AUTH_TOKEN);
expect(session?.authToken).toBe(TEST_REFRESHED_AUTH_TOKEN);
});
});

Expand Down
73 changes: 37 additions & 36 deletions tests/unit/EmojiTest.js → tests/unit/EmojiTest.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,30 @@
import {getUnixTime} from 'date-fns';
import lodashGet from 'lodash/get';
import Onyx from 'react-native-onyx';
import _ from 'underscore';
import Emoji from '../../assets/emojis';
import CONST from '../../src/CONST';
import * as User from '../../src/libs/actions/User';
import * as EmojiUtils from '../../src/libs/EmojiUtils';
import ONYXKEYS from '../../src/ONYXKEYS';
import Emojis from '@assets/emojis';
import type {Emoji} from '@assets/emojis/types';
import * as User from '@libs/actions/User';
import * as EmojiUtils from '@libs/EmojiUtils';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type {FrequentlyUsedEmoji} from '@src/types/onyx';
import * as TestHelper from '../utils/TestHelper';
import waitForBatchedUpdates from '../utils/waitForBatchedUpdates';

describe('EmojiTest', () => {
it('matches all the emojis in the list', () => {
// Given the set of Emojis available in the application
const emojiMatched = _.every(Emoji, (emoji) => {
if (emoji.header === true || emoji.spacer) {
const emojiMatched = Emojis.every((emoji) => {
if (('header' in emoji && emoji.header) || ('spacer' in emoji && emoji.spacer)) {
return true;
}

// When we match every Emoji Code
const isEmojiMatched = EmojiUtils.containsOnlyEmojis(emoji.code);

let skinToneMatched = true;
if (emoji.types) {
if ('types' in emoji && emoji.types) {
// and every skin tone variant of the Emoji code
skinToneMatched = _.every(emoji.types, (emojiWithSkinTone) => EmojiUtils.containsOnlyEmojis(emojiWithSkinTone));
skinToneMatched = emoji.types.every((emojiWithSkinTone) => EmojiUtils.containsOnlyEmojis(emojiWithSkinTone));
}
return skinToneMatched && isEmojiMatched;
});
Expand Down Expand Up @@ -103,42 +103,42 @@ describe('EmojiTest', () => {

it('replaces an emoji code with an emoji and a space', () => {
const text = 'Hi :smile:';
expect(lodashGet(EmojiUtils.replaceEmojis(text), 'text')).toBe('Hi 😄 ');
expect(EmojiUtils.replaceEmojis(text).text).toBe('Hi 😄 ');
});

it('will add a space after the last emoji', () => {
const text = 'Hi :smile::wave:';
expect(lodashGet(EmojiUtils.replaceEmojis(text), 'text')).toBe('Hi 😄👋 ');
expect(EmojiUtils.replaceEmojis(text).text).toBe('Hi 😄👋 ');
});

it('will add a space after the last emoji if there is text after it', () => {
const text = 'Hi :smile::wave:space after last emoji';
expect(lodashGet(EmojiUtils.replaceEmojis(text), 'text')).toBe('Hi 😄👋 space after last emoji');
expect(EmojiUtils.replaceEmojis(text).text).toBe('Hi 😄👋 space after last emoji');
});

it('will add a space after the last emoji if there is invalid emoji after it', () => {
const text = 'Hi :smile::wave:space when :invalidemoji: present';
expect(lodashGet(EmojiUtils.replaceEmojis(text), 'text')).toBe('Hi 😄👋 space when :invalidemoji: present');
expect(EmojiUtils.replaceEmojis(text).text).toBe('Hi 😄👋 space when :invalidemoji: present');
});

it('will not add a space after the last emoji if there if last emoji is immediately followed by a space', () => {
const text = 'Hi :smile::wave: space after last emoji';
expect(lodashGet(EmojiUtils.replaceEmojis(text), 'text')).toBe('Hi 😄👋 space after last emoji');
expect(EmojiUtils.replaceEmojis(text).text).toBe('Hi 😄👋 space after last emoji');
});

it('will return correct cursor position', () => {
const text = 'Hi :smile: there :wave:!';
expect(lodashGet(EmojiUtils.replaceEmojis(text), 'cursorPosition')).toBe(15);
expect(EmojiUtils.replaceEmojis(text).cursorPosition).toBe(15);
});

it('will return correct cursor position when space is not added by space follows last emoji', () => {
const text = 'Hi :smile: there!';
expect(lodashGet(EmojiUtils.replaceEmojis(text), 'cursorPosition')).toBe(6);
expect(EmojiUtils.replaceEmojis(text).cursorPosition).toBe(6);
});

it('will return undefined cursor position when no emoji is replaced', () => {
const text = 'Hi there!';
expect(lodashGet(EmojiUtils.replaceEmojis(text), 'cursorPosition')).toBe(undefined);
expect(EmojiUtils.replaceEmojis(text).cursorPosition).toBe(undefined);
});

it('suggests emojis when typing emojis prefix after colon', () => {
Expand All @@ -149,11 +149,11 @@ describe('EmojiTest', () => {
it('suggests a limited number of matching emojis', () => {
const text = 'Hi :face';
const limit = 3;
expect(EmojiUtils.suggestEmojis(text, 'en', limit).length).toBe(limit);
expect(EmojiUtils.suggestEmojis(text, 'en', limit)?.length).toBe(limit);
});

it('correct suggests emojis accounting for keywords', () => {
const thumbEmojis = [
const thumbEmojis: Emoji[] = [
{
code: '👍',
name: '+1',
Expand Down Expand Up @@ -190,10 +190,11 @@ describe('EmojiTest', () => {
});

describe('update frequently used emojis', () => {
let spy;
let spy: jest.SpyInstance;

beforeAll(() => {
Onyx.init({keys: ONYXKEYS});
// @ts-expect-error TODO: Remove this once TestHelper (https://github.com/Expensify/App/issues/25318) is migrated to TypeScript.
global.fetch = TestHelper.getGlobalFetchMock();
spy = jest.spyOn(User, 'updateFrequentlyUsedEmojis');
});
Expand All @@ -205,7 +206,7 @@ describe('EmojiTest', () => {

it('should put a less frequent and recent used emoji behind', () => {
// Given an existing frequently used emojis list with count > 1
const frequentlyEmojisList = [
const frequentlyEmojisList: FrequentlyUsedEmoji[] = [
{
code: '👋',
name: 'wave',
Expand Down Expand Up @@ -237,12 +238,12 @@ describe('EmojiTest', () => {
return waitForBatchedUpdates().then(() => {
// When add a new emoji
const currentTime = getUnixTime(new Date());
const smileEmoji = {code: '😄', name: 'smile'};
const smileEmoji: Emoji = {code: '😄', name: 'smile'};
const newEmoji = [smileEmoji];
User.updateFrequentlyUsedEmojis(EmojiUtils.getFrequentlyUsedEmojis(newEmoji));

// Then the new emoji should be at the last item of the list
const expectedSmileEmoji = {...smileEmoji, count: 1, lastUpdatedAt: currentTime};
const expectedSmileEmoji: FrequentlyUsedEmoji = {...smileEmoji, count: 1, lastUpdatedAt: currentTime};

const expectedFrequentlyEmojisList = [...frequentlyEmojisList, expectedSmileEmoji];
expect(spy).toBeCalledWith(expectedFrequentlyEmojisList);
Expand All @@ -251,8 +252,8 @@ describe('EmojiTest', () => {

it('should put more frequent and recent used emoji to the front', () => {
// Given an existing frequently used emojis list
const smileEmoji = {code: '😄', name: 'smile'};
const frequentlyEmojisList = [
const smileEmoji: Emoji = {code: '😄', name: 'smile'};
const frequentlyEmojisList: FrequentlyUsedEmoji[] = [
{
code: '😠',
name: 'angry',
Expand Down Expand Up @@ -296,10 +297,10 @@ describe('EmojiTest', () => {

it('should sorted descending by count and lastUpdatedAt for multiple emoji added', () => {
// Given an existing frequently used emojis list
const smileEmoji = {code: '😄', name: 'smile'};
const zzzEmoji = {code: '💤', name: 'zzz'};
const impEmoji = {code: '👿', name: 'imp'};
const frequentlyEmojisList = [
const smileEmoji: Emoji = {code: '😄', name: 'smile'};
const zzzEmoji: Emoji = {code: '💤', name: 'zzz'};
const impEmoji: Emoji = {code: '👿', name: 'imp'};
const frequentlyEmojisList: FrequentlyUsedEmoji[] = [
{
code: '😠',
name: 'angry',
Expand Down Expand Up @@ -345,11 +346,11 @@ describe('EmojiTest', () => {

it('make sure the most recent new emoji is added to the list even it is full with count > 1', () => {
// Given an existing full (24 items) frequently used emojis list
const smileEmoji = {code: '😄', name: 'smile'};
const zzzEmoji = {code: '💤', name: 'zzz'};
const impEmoji = {code: '👿', name: 'imp'};
const bookEmoji = {code: '📚', name: 'books'};
const frequentlyEmojisList = [
const smileEmoji: Emoji = {code: '😄', name: 'smile'};
const zzzEmoji: Emoji = {code: '💤', name: 'zzz'};
const impEmoji: Emoji = {code: '👿', name: 'imp'};
const bookEmoji: Emoji = {code: '📚', name: 'books'};
const frequentlyEmojisList: FrequentlyUsedEmoji[] = [
{
code: '😠',
name: 'angry',
Expand Down
Loading
Loading