Skip to content

Commit

Permalink
Merge pull request #37204 from VickyStash/ts-migration/tests
Browse files Browse the repository at this point in the history
[No QA] [TS migration] Migrate 'EmojiTest.js', 'SidebarFilterTest.js', 'SidebarOrderTest.js', 'LHNTestUtils.js', 'SessionTest.js' test to TypeScript
  • Loading branch information
cristipaval authored Feb 28, 2024
2 parents 1649978 + 34fc666 commit dcf2e3e
Show file tree
Hide file tree
Showing 11 changed files with 549 additions and 439 deletions.
2 changes: 1 addition & 1 deletion src/ONYXKEYS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -582,4 +582,4 @@ type MissingOnyxKeysError = `Error: Types don't match, OnyxKey type is missing:
type AssertOnyxKeys = AssertTypesEqual<AllOnyxKeys, OnyxKey, MissingOnyxKeysError>;

export default ONYXKEYS;
export type {OnyxValues, OnyxKey, OnyxCollectionKey, OnyxValue, OnyxValueKey, OnyxFormKey, OnyxFormValuesMapping, OnyxFormDraftKey};
export type {OnyxValues, OnyxKey, OnyxCollectionKey, OnyxValue, OnyxValueKey, OnyxFormKey, OnyxFormValuesMapping, OnyxFormDraftKey, OnyxCollectionValuesMapping};
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
6 changes: 5 additions & 1 deletion src/types/onyx/Report.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import type {ValueOf} from 'type-fest';
import type CONST from '@src/CONST';
import type ONYXKEYS from '@src/ONYXKEYS';
import type CollectionDataSet from '@src/types/utils/CollectionDataSet';
import type * as OnyxCommon from './OnyxCommon';
import type PersonalDetails from './PersonalDetails';
import type {PolicyReportField} from './PolicyReportField';
Expand Down Expand Up @@ -174,6 +176,8 @@ type Report = OnyxCommon.OnyxValueWithOfflineFeedback<
PolicyReportField['fieldID']
>;

type ReportCollectionDataSet = CollectionDataSet<typeof ONYXKEYS.COLLECTION.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 = OnyxCommon.OnyxValueWithOfflineFeedback<{

actorAccountID?: number;

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

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

Expand Down
6 changes: 6 additions & 0 deletions src/types/utils/CollectionDataSet.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import type {OnyxCollectionKey, OnyxCollectionValuesMapping} from '@src/ONYXKEYS';

/** Helps with typing a collection item update inside Onyx.multiSet call */
type CollectionDataSet<TCollectionKey extends OnyxCollectionKey> = Record<`${TCollectionKey}${string}`, OnyxCollectionValuesMapping[TCollectionKey]>;

export default CollectionDataSet;
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

0 comments on commit dcf2e3e

Please sign in to comment.