From 1a63d65a52d36c8bdf781711b4d9fc6095f6a820 Mon Sep 17 00:00:00 2001 From: ruben-rebelo Date: Wed, 28 Feb 2024 08:20:43 +0000 Subject: [PATCH 01/15] [TS migration] Migrate compare, currencyUtilsTest, IOUUtilsTest, SideBarTest and TestHelper --- tests/e2e/compare/{compare.js => compare.ts} | 60 +++-- tests/e2e/measure/math.ts | 1 + ...rencyUtilsTest.js => CurrencyUtilsTest.ts} | 17 +- .../unit/{IOUUtilsTest.js => IOUUtilsTest.ts} | 43 +-- tests/unit/{SidebarTest.js => SidebarTest.ts} | 86 +++--- tests/utils/TestHelper.js | 244 ------------------ tests/utils/TestHelper.ts | 242 +++++++++++++++++ 7 files changed, 356 insertions(+), 337 deletions(-) rename tests/e2e/compare/{compare.js => compare.ts} (68%) rename tests/unit/{CurrencyUtilsTest.js => CurrencyUtilsTest.ts} (92%) rename tests/unit/{IOUUtilsTest.js => IOUUtilsTest.ts} (81%) rename tests/unit/{SidebarTest.js => SidebarTest.ts} (62%) delete mode 100644 tests/utils/TestHelper.js create mode 100644 tests/utils/TestHelper.ts diff --git a/tests/e2e/compare/compare.js b/tests/e2e/compare/compare.ts similarity index 68% rename from tests/e2e/compare/compare.js rename to tests/e2e/compare/compare.ts index 7feaa8b266d1..cf74b1240bf8 100644 --- a/tests/e2e/compare/compare.js +++ b/tests/e2e/compare/compare.ts @@ -1,9 +1,25 @@ -import _ from 'underscore'; +import type {Stats} from '../measure/math'; import getStats from '../measure/math'; import * as math from './math'; import printToConsole from './output/console'; import writeToMarkdown from './output/markdown'; +type Entry = { + name: string; + baseline?: Stats; + current?: Stats; + diff?: number; + relativeDurationDiff?: number; + isDurationDiffOfSignificance?: boolean; + mean?: number; +}; + +type Result = { + name: string; + current?: Entry; + baseline?: Entry; +}; + /* * base implementation from: https://github.com/callstack/reassure/blob/main/packages/reassure-compare/src/compare.ts * This module reads from the baseline and compare files and compares the results. @@ -25,14 +41,7 @@ const PROBABILITY_CONSIDERED_SIGNIFICANCE = 0.02; */ const DURATION_DIFF_THRESHOLD_SIGNIFICANCE = 100; -/** - * - * @param {string} name - * @param {Object} compare - * @param {Object} baseline - * @returns {Object} - */ -function buildCompareEntry(name, compare, baseline) { +function buildCompareEntry(name: string, compare: Stats, baseline: Stats): Entry { const diff = compare.mean - baseline.mean; const relativeDurationDiff = diff / baseline.mean; @@ -53,20 +62,16 @@ function buildCompareEntry(name, compare, baseline) { /** * Compare results between baseline and current entries and categorize. - * - * @param {Object} compareEntries - * @param {Object} baselineEntries - * @returns {Object} */ -function compareResults(compareEntries, baselineEntries) { +function compareResults(compareEntries: Record, baselineEntries: Record) { // Unique test scenario names - const names = [...new Set([..._.keys(compareEntries), ..._.keys(baselineEntries || {})])]; + const names: string[] = [...new Set([...Object(compareEntries).keys(), ...Object(baselineEntries ?? {}).keys()])]; - const compared = []; - const added = []; - const removed = []; + const compared: Entry[] = []; + const added: Result[] = []; + const removed: Result[] = []; - names.forEach((name) => { + names.forEach((name: string) => { const current = compareEntries[name]; const baseline = baselineEntries[name]; @@ -88,15 +93,12 @@ function compareResults(compareEntries, baselineEntries) { } }); - const significance = _.chain(compared) - .filter((item) => item.isDurationDiffOfSignificance) - .value(); - const meaningless = _.chain(compared) - .filter((item) => !item.isDurationDiffOfSignificance) - .value(); + const significance = compared.filter((item) => item.isDurationDiffOfSignificance); - added.sort((a, b) => b.current.mean - a.current.mean); - removed.sort((a, b) => b.baseline.mean - a.baseline.mean); + const meaningless = compared.filter((item) => !item.isDurationDiffOfSignificance); + + added.sort((a, b) => (b?.current?.mean ?? 0) - (a.current?.mean ?? 0)); + removed.sort((a, b) => (b.baseline?.mean ?? 0) - (a.baseline?.mean ?? 0)); return { significance, @@ -106,7 +108,7 @@ function compareResults(compareEntries, baselineEntries) { }; } -export default (main, delta, outputFile, outputFormat = 'all') => { +export default (main: Record, delta: Record, outputFile: string, outputFormat = 'all') => { // IMPORTANT NOTE: make sure you are passing the delta/compare results first, then the main/baseline results: const outputData = compareResults(delta, main); @@ -118,3 +120,5 @@ export default (main, delta, outputFile, outputFormat = 'all') => { return writeToMarkdown(outputFile, outputData); } }; + +export type {Entry}; diff --git a/tests/e2e/measure/math.ts b/tests/e2e/measure/math.ts index e1c0cb981a0c..c9c0219ef1fd 100644 --- a/tests/e2e/measure/math.ts +++ b/tests/e2e/measure/math.ts @@ -49,3 +49,4 @@ const getStats = (entries: Entries): Stats => { }; export default getStats; +export type {Stats} diff --git a/tests/unit/CurrencyUtilsTest.js b/tests/unit/CurrencyUtilsTest.ts similarity index 92% rename from tests/unit/CurrencyUtilsTest.js rename to tests/unit/CurrencyUtilsTest.ts index 89e1e2ffb3be..246af64b1d87 100644 --- a/tests/unit/CurrencyUtilsTest.js +++ b/tests/unit/CurrencyUtilsTest.ts @@ -1,9 +1,9 @@ import Onyx from 'react-native-onyx'; -import _ from 'underscore'; -import CONST from '../../src/CONST'; -import * as CurrencyUtils from '../../src/libs/CurrencyUtils'; -import LocaleListener from '../../src/libs/Localize/LocaleListener'; -import ONYXKEYS from '../../src/ONYXKEYS'; +import CONST from '@src/CONST'; +import * as CurrencyUtils from '@src/libs/CurrencyUtils'; +import LocaleListener from '@src/libs/Localize/LocaleListener'; +import ONYXKEYS from '@src/ONYXKEYS'; +import {isEmptyObject} from '@src/types/utils/EmptyObject'; import waitForBatchedUpdates from '../utils/waitForBatchedUpdates'; // This file can get outdated. In that case, you can follow these steps to update it: // - open your browser console and navigate to the Network tab @@ -13,7 +13,7 @@ import waitForBatchedUpdates from '../utils/waitForBatchedUpdates'; // - update currencyList.json import currencyList from './currencyList.json'; -const currencyCodeList = _.keys(currencyList); +const currencyCodeList = Object.keys(currencyList); const AVAILABLE_LOCALES = [CONST.LOCALES.EN, CONST.LOCALES.ES]; describe('CurrencyUtils', () => { @@ -37,7 +37,10 @@ describe('CurrencyUtils', () => { describe('getLocalizedCurrencySymbol', () => { test.each(AVAILABLE_LOCALES)('Returns non empty string for all currencyCode with preferredLocale %s', (prefrredLocale) => Onyx.set(ONYXKEYS.NVP_PREFERRED_LOCALE, prefrredLocale).then(() => { - _.forEach(currencyCodeList, (currencyCode) => { + if (isEmptyObject(currencyCodeList)) { + return; + } + currencyCodeList.forEach((currencyCode: string) => { const localizedSymbol = CurrencyUtils.getLocalizedCurrencySymbol(currencyCode); expect(localizedSymbol).toBeTruthy(); diff --git a/tests/unit/IOUUtilsTest.js b/tests/unit/IOUUtilsTest.ts similarity index 81% rename from tests/unit/IOUUtilsTest.js rename to tests/unit/IOUUtilsTest.ts index ac04b74a0ca5..b390d0cd70a3 100644 --- a/tests/unit/IOUUtilsTest.js +++ b/tests/unit/IOUUtilsTest.ts @@ -1,8 +1,10 @@ +import type {NullishDeep} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; -import * as IOUUtils from '../../src/libs/IOUUtils'; -import * as ReportUtils from '../../src/libs/ReportUtils'; -import * as TransactionUtils from '../../src/libs/TransactionUtils'; -import ONYXKEYS from '../../src/ONYXKEYS'; +import * as IOUUtils from '@src/libs/IOUUtils'; +import * as ReportUtils from '@src/libs/ReportUtils'; +import * as TransactionUtils from '@src/libs/TransactionUtils'; +import ONYXKEYS from '@src/ONYXKEYS'; +import type Transaction from '@src/types/onyx/Transaction'; import waitForBatchedUpdates from '../utils/waitForBatchedUpdates'; import currencyList from './currencyList.json'; @@ -25,34 +27,35 @@ describe('IOUUtils', () => { }); test('Requesting money offline in a different currency will show the pending conversion message', () => { - const iouReport = ReportUtils.buildOptimisticIOUReport(1, 2, 100, 1, 'USD'); + const iouReport = ReportUtils.buildOptimisticIOUReport(1, 2, 100, '1', 'USD'); const usdPendingTransaction = TransactionUtils.buildOptimisticTransaction(100, 'USD', iouReport.reportID); const aedPendingTransaction = TransactionUtils.buildOptimisticTransaction(100, 'AED', iouReport.reportID); + const MergeQueries: Record<`${typeof ONYXKEYS.COLLECTION.TRANSACTION}${string}`, NullishDeep> = {}; + MergeQueries[`${ONYXKEYS.COLLECTION.TRANSACTION}${usdPendingTransaction.transactionID}`] = usdPendingTransaction; + MergeQueries[`${ONYXKEYS.COLLECTION.TRANSACTION}${aedPendingTransaction.transactionID}`] = aedPendingTransaction; - return Onyx.mergeCollection(ONYXKEYS.COLLECTION.TRANSACTION, { - [`${ONYXKEYS.COLLECTION.TRANSACTION}${usdPendingTransaction.transactionID}`]: usdPendingTransaction, - [`${ONYXKEYS.COLLECTION.TRANSACTION}${aedPendingTransaction.transactionID}`]: aedPendingTransaction, - }).then(() => { + return Onyx.mergeCollection(ONYXKEYS.COLLECTION.TRANSACTION, MergeQueries).then(() => { // We requested money offline in a different currency, we don't know the total of the iouReport until we're back online expect(IOUUtils.isIOUReportPendingCurrencyConversion(iouReport)).toBe(true); }); }); test('Requesting money online in a different currency will not show the pending conversion message', () => { - const iouReport = ReportUtils.buildOptimisticIOUReport(2, 3, 100, 1, 'USD'); + const iouReport = ReportUtils.buildOptimisticIOUReport(2, 3, 100, '1', 'USD'); const usdPendingTransaction = TransactionUtils.buildOptimisticTransaction(100, 'USD', iouReport.reportID); const aedPendingTransaction = TransactionUtils.buildOptimisticTransaction(100, 'AED', iouReport.reportID); - return Onyx.mergeCollection(ONYXKEYS.COLLECTION.TRANSACTION, { - [`${ONYXKEYS.COLLECTION.TRANSACTION}${usdPendingTransaction.transactionID}`]: { - ...usdPendingTransaction, - pendingAction: null, - }, - [`${ONYXKEYS.COLLECTION.TRANSACTION}${aedPendingTransaction.transactionID}`]: { - ...aedPendingTransaction, - pendingAction: null, - }, - }).then(() => { + const MergeQueries: Record<`${typeof ONYXKEYS.COLLECTION.TRANSACTION}${string}`, NullishDeep> = {}; + MergeQueries[`${ONYXKEYS.COLLECTION.TRANSACTION}${usdPendingTransaction.transactionID}`] = { + ...usdPendingTransaction, + pendingAction: null, + }; + MergeQueries[`${ONYXKEYS.COLLECTION.TRANSACTION}${aedPendingTransaction.transactionID}`] = { + ...aedPendingTransaction, + pendingAction: null, + }; + + return Onyx.mergeCollection(ONYXKEYS.COLLECTION.TRANSACTION, MergeQueries).then(() => { // We requested money online in a different currency, we know the iouReport total and there's no need to show the pending conversion message expect(IOUUtils.isIOUReportPendingCurrencyConversion(iouReport)).toBe(false); }); diff --git a/tests/unit/SidebarTest.js b/tests/unit/SidebarTest.ts similarity index 62% rename from tests/unit/SidebarTest.js rename to tests/unit/SidebarTest.ts index 6a813ef1fa8c..4a3be66ebe37 100644 --- a/tests/unit/SidebarTest.js +++ b/tests/unit/SidebarTest.ts @@ -1,35 +1,23 @@ import {cleanup, screen} from '@testing-library/react-native'; -import lodashGet from 'lodash/get'; +import type {NullishDeep} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; -import CONST from '../../src/CONST'; -import * as Localize from '../../src/libs/Localize'; +import CONST from '@src/CONST'; +import * as Localize from '@src/libs/Localize'; +import ONYXKEYS from '@src/ONYXKEYS'; +import type * as OnyxTypes from '@src/types/onyx'; import * as LHNTestUtils from '../utils/LHNTestUtils'; import waitForBatchedUpdates from '../utils/waitForBatchedUpdates'; import wrapOnyxWithWaitForBatchedUpdates from '../utils/wrapOnyxWithWaitForBatchedUpdates'; // Be sure to include the mocked Permissions and Expensicons libraries as well as the usePermissions hook or else the beta tests won't work -jest.mock('../../src/libs/Permissions'); -jest.mock('../../src/hooks/usePermissions.ts'); -jest.mock('../../src/components/Icon/Expensicons'); - -const ONYXKEYS = { - PERSONAL_DETAILS_LIST: 'personalDetailsList', - IS_LOADING_APP: 'isLoadingApp', - NVP_PRIORITY_MODE: 'nvp_priorityMode', - SESSION: 'session', - BETAS: 'betas', - COLLECTION: { - REPORT: 'report_', - REPORT_ACTIONS: 'reportActions_', - }, - NETWORK: 'network', -}; +jest.mock('@src/libs/Permissions'); +jest.mock('@src/hooks/usePermissions.ts'); +jest.mock('@src/components/Icon/Expensicons'); describe('Sidebar', () => { beforeAll(() => Onyx.init({ keys: ONYXKEYS, - registerStorageEventListener: () => {}, safeEvictionKeys: [ONYXKEYS.COLLECTION.REPORT_ACTIONS], }), ); @@ -50,6 +38,7 @@ describe('Sidebar', () => { describe('archived chats', () => { it('renders the archive reason as the preview message of the chat', () => { const report = { + // @ts-expect-error TODO: Remove this once LHNTestUtils (https://github.com/Expensify/App/issues/25320) is migrated to TypeScript. ...LHNTestUtils.getFakeReport(['email1@test.com', 'email2@test.com'], 3, true), chatType: CONST.REPORT.CHAT_TYPE.POLICY_ROOM, statusNum: CONST.REPORT.STATUS_NUM.CLOSED, @@ -57,6 +46,7 @@ describe('Sidebar', () => { }; const action = { + // @ts-expect-error TODO: Remove this once LHNTestUtils (https://github.com/Expensify/App/issues/25320) is migrated to TypeScript. ...LHNTestUtils.getFakeReportAction('email1@test.com', 3, true), actionName: 'CLOSED', originalMessage: { @@ -70,29 +60,40 @@ describe('Sidebar', () => { return ( waitForBatchedUpdates() // When Onyx is updated with the data and the sidebar re-renders - .then(() => - Onyx.multiSet({ + .then(() => { + const reportCollection = { + // @ts-expect-error TODO: Remove this once LHNTestUtils (https://github.com/Expensify/App/issues/25320) is migrated to TypeScript. + [`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`]: report, + } as Record<`${typeof ONYXKEYS.COLLECTION.REPORT}${string}`, NullishDeep>; + + const reportAction = { + // @ts-expect-error TODO: Remove this once LHNTestUtils (https://github.com/Expensify/App/issues/25320) is migrated to TypeScript. + [`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.reportID}`]: {[action.reportActionId]: action}, + } as Record<`${typeof ONYXKEYS.COLLECTION.REPORT_ACTIONS}${string}`, NullishDeep>; + + return Onyx.multiSet({ [ONYXKEYS.BETAS]: betas, [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.GSD, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, - [`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`]: report, - [`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.reportID}`]: {[action.reportActionId]: action}, - }), - ) + ...reportCollection, + ...reportAction, + }); + }) .then(() => { const hintText = Localize.translateLocal('accessibilityHints.chatUserDisplayNames'); const displayNames = screen.queryAllByLabelText(hintText); - expect(lodashGet(displayNames, [0, 'props', 'children', 0])).toBe('Report (archived)'); + expect(displayNames[0].props.children[0]).toBe('Report (archived)'); const hintMessagePreviewText = Localize.translateLocal('accessibilityHints.lastChatMessagePreview'); const messagePreviewTexts = screen.queryAllByLabelText(hintMessagePreviewText); - expect(lodashGet(messagePreviewTexts, [0, 'props', 'children'])).toBe('This chat room has been archived.'); + expect(messagePreviewTexts[0].props.children).toBe('This chat room has been archived.'); }) ); }); it('renders the policy deleted archive reason as the preview message of the chat', () => { const report = { + // @ts-expect-error TODO: Remove this once LHNTestUtils (https://github.com/Expensify/App/issues/25320) is migrated to TypeScript. ...LHNTestUtils.getFakeReport(['email1@test.com', 'email2@test.com'], 3, true), policyName: 'Vikings Policy', chatType: CONST.REPORT.CHAT_TYPE.POLICY_ROOM, @@ -100,6 +101,7 @@ describe('Sidebar', () => { stateNum: CONST.REPORT.STATE_NUM.APPROVED, }; const action = { + // @ts-expect-error TODO: Remove this once LHNTestUtils (https://github.com/Expensify/App/issues/25320) is migrated to TypeScript. ...LHNTestUtils.getFakeReportAction('email1@test.com', 3, true), actionName: 'CLOSED', originalMessage: { @@ -114,26 +116,34 @@ describe('Sidebar', () => { return ( waitForBatchedUpdates() // When Onyx is updated with the data and the sidebar re-renders - .then(() => - Onyx.multiSet({ + .then(() => { + const reportCollection = { + // @ts-expect-error TODO: Remove this once LHNTestUtils (https://github.com/Expensify/App/issues/25320) is migrated to TypeScript. + [`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`]: report, + } as Record<`${typeof ONYXKEYS.COLLECTION.REPORT}${string}`, NullishDeep>; + + const reportAction = { + // @ts-expect-error TODO: Remove this once LHNTestUtils (https://github.com/Expensify/App/issues/25320) is migrated to TypeScript. + [`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.reportID}`]: {[action.reportActionId]: action}, + } as Record<`${typeof ONYXKEYS.COLLECTION.REPORT_ACTIONS}${string}`, NullishDeep>; + + return Onyx.multiSet({ [ONYXKEYS.BETAS]: betas, [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.GSD, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, - [`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`]: report, - [`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.reportID}`]: {[action.reportActionId]: action}, - }), - ) + ...reportCollection, + ...reportAction, + }); + }) .then(() => { const hintText = Localize.translateLocal('accessibilityHints.chatUserDisplayNames'); const displayNames = screen.queryAllByLabelText(hintText); - expect(lodashGet(displayNames, [0, 'props', 'children', 0])).toBe('Report (archived)'); + expect(displayNames[0].props.children[0]).toBe('Report (archived)'); const hintMessagePreviewText = Localize.translateLocal('accessibilityHints.lastChatMessagePreview'); const messagePreviewTexts = screen.queryAllByLabelText(hintMessagePreviewText); - expect(lodashGet(messagePreviewTexts, [0, 'props', 'children'])).toBe( - 'This workspace chat is no longer active because Vikings Policy is no longer an active workspace.', - ); + expect(messagePreviewTexts[0].props.children).toBe('This workspace chat is no longer active because Vikings Policy is no longer an active workspace.'); }) ); }); diff --git a/tests/utils/TestHelper.js b/tests/utils/TestHelper.js deleted file mode 100644 index 9059041afd19..000000000000 --- a/tests/utils/TestHelper.js +++ /dev/null @@ -1,244 +0,0 @@ -import Str from 'expensify-common/lib/str'; -import Onyx from 'react-native-onyx'; -import _ from 'underscore'; -import CONST from '../../src/CONST'; -import * as Session from '../../src/libs/actions/Session'; -import HttpUtils from '../../src/libs/HttpUtils'; -import * as NumberUtils from '../../src/libs/NumberUtils'; -import ONYXKEYS from '../../src/ONYXKEYS'; -import waitForBatchedUpdates from './waitForBatchedUpdates'; - -/** - * @param {String} login - * @param {Number} accountID - * @param {String} [firstName] - * @returns {Object} - */ -function buildPersonalDetails(login, accountID, firstName = 'Test') { - return { - accountID, - login, - avatar: 'https://d2k5nsl2zxldvw.cloudfront.net/images/avatars/avatar_7.png', - avatarThumbnail: 'https://d2k5nsl2zxldvw.cloudfront.net/images/avatars/avatar_7.png', - displayName: `${firstName} User`, - firstName, - lastName: 'User', - pronouns: '', - timezone: CONST.DEFAULT_TIME_ZONE, - phoneNumber: '', - }; -} - -/** - * Simulate signing in and make sure all API calls in this flow succeed. Every time we add - * a mockImplementationOnce() we are altering what Network.post() will return. - * - * @param {Number} [accountID] - * @param {String} [login] - * @param {String} [password] - * @param {String} [authToken] - * @param {String} [firstName] - * @return {Promise} - */ -function signInWithTestUser(accountID = 1, login = 'test@user.com', password = 'Password1', authToken = 'asdfqwerty', firstName = 'Test') { - const originalXhr = HttpUtils.xhr; - HttpUtils.xhr = jest.fn(); - HttpUtils.xhr.mockResolvedValue({ - onyxData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.CREDENTIALS, - value: { - login, - }, - }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.ACCOUNT, - value: { - validated: true, - }, - }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.PERSONAL_DETAILS_LIST, - value: { - [accountID]: buildPersonalDetails(login, accountID, firstName), - }, - }, - ], - jsonCode: 200, - }); - - // Simulate user entering their login and populating the credentials.login - Session.beginSignIn(login); - return waitForBatchedUpdates() - .then(() => { - // Response is the same for calls to Authenticate and BeginSignIn - HttpUtils.xhr.mockResolvedValue({ - onyxData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.SESSION, - value: { - authToken, - accountID, - email: login, - encryptedAuthToken: authToken, - }, - }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.CREDENTIALS, - value: { - autoGeneratedLogin: Str.guid('expensify.cash-'), - autoGeneratedPassword: Str.guid(), - }, - }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.USER, - value: { - isUsingExpensifyCard: false, - }, - }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.BETAS, - value: ['all'], - }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.NVP_PRIVATE_PUSH_NOTIFICATION_ID, - value: 'randomID', - }, - ], - jsonCode: 200, - }); - Session.signIn(password); - return waitForBatchedUpdates(); - }) - .then(() => { - HttpUtils.xhr = originalXhr; - }); -} - -function signOutTestUser() { - const originalXhr = HttpUtils.xhr; - HttpUtils.xhr = jest.fn(); - HttpUtils.xhr.mockResolvedValue({jsonCode: 200}); - Session.signOutAndRedirectToSignIn(); - return waitForBatchedUpdates().then(() => (HttpUtils.xhr = originalXhr)); -} - -/** - * Use for situations where fetch() is required. This mock is stateful and has some additional methods to control its behavior: - * - * - pause() – stop resolving promises until you call resume() - * - resume() - flush the queue of promises, and start resolving new promises immediately - * - fail() - start returning a failure response - * - success() - go back to returning a success response - * - * @example - * - * beforeAll(() => { - * global.fetch = TestHelper.getGlobalFetchMock(); - * }); - * - * @returns {Function} - */ -function getGlobalFetchMock() { - const queue = []; - let isPaused = false; - let shouldFail = false; - - const getResponse = () => - shouldFail - ? { - ok: true, - json: () => Promise.resolve({jsonCode: 400}), - } - : { - ok: true, - json: () => Promise.resolve({jsonCode: 200}), - }; - - const mockFetch = jest.fn().mockImplementation(() => { - if (!isPaused) { - return Promise.resolve(getResponse()); - } - return new Promise((resolve) => queue.push(resolve)); - }); - - mockFetch.pause = () => (isPaused = true); - mockFetch.resume = () => { - isPaused = false; - _.each(queue, (resolve) => resolve(getResponse())); - return waitForBatchedUpdates(); - }; - mockFetch.fail = () => (shouldFail = true); - mockFetch.succeed = () => (shouldFail = false); - - return mockFetch; -} - -/** - * @param {String} login - * @param {Number} accountID - * @returns {Promise} - */ -function setPersonalDetails(login, accountID) { - Onyx.merge(ONYXKEYS.PERSONAL_DETAILS_LIST, { - [accountID]: buildPersonalDetails(login, accountID), - }); - return waitForBatchedUpdates(); -} - -/** - * @param {String} created - * @param {Number} actorAccountID - * @param {String} actionID - * @returns {Object} - */ -function buildTestReportComment(created, actorAccountID, actionID = null) { - const reportActionID = actionID || NumberUtils.rand64(); - return { - actionName: CONST.REPORT.ACTIONS.TYPE.ADDCOMMENT, - person: [{type: 'TEXT', style: 'strong', text: 'User B'}], - created, - message: [{type: 'COMMENT', html: `Comment ${actionID}`, text: `Comment ${actionID}`}], - reportActionID, - actorAccountID, - }; -} - -function assertFormDataMatchesObject(formData, obj) { - expect(_.reduce(Array.from(formData.entries()), (memo, x) => ({...memo, [x[0]]: x[1]}), {})).toEqual(expect.objectContaining(obj)); -} - -/** - * This is a helper function to create a mock for the addListener function of the react-navigation library. - * The reason we need this is because we need to trigger the transitionEnd event in our tests to simulate - * the transitionEnd event that is triggered when the screen transition animation is completed. - * - * @returns {Object} An object with two functions: triggerTransitionEnd and addListener - */ -const createAddListenerMock = () => { - const transitionEndListeners = []; - const triggerTransitionEnd = () => { - transitionEndListeners.forEach((transitionEndListener) => transitionEndListener()); - }; - - const addListener = jest.fn().mockImplementation((listener, callback) => { - if (listener === 'transitionEnd') { - transitionEndListeners.push(callback); - } - return () => { - _.filter(transitionEndListeners, (cb) => cb !== callback); - }; - }); - - return {triggerTransitionEnd, addListener}; -}; - -export {getGlobalFetchMock, signInWithTestUser, signOutTestUser, setPersonalDetails, buildPersonalDetails, buildTestReportComment, assertFormDataMatchesObject, createAddListenerMock}; diff --git a/tests/utils/TestHelper.ts b/tests/utils/TestHelper.ts new file mode 100644 index 000000000000..edef0bd55e5f --- /dev/null +++ b/tests/utils/TestHelper.ts @@ -0,0 +1,242 @@ +import Str from 'expensify-common/lib/str'; +import Onyx from 'react-native-onyx'; +import CONST from '@src/CONST'; +import * as Session from '@src/libs/actions/Session'; +import HttpUtils from '@src/libs/HttpUtils'; +import * as NumberUtils from '@src/libs/NumberUtils'; +import ONYXKEYS from '@src/ONYXKEYS'; +import type {PersonalDetails, Report} from '@src/types/onyx'; +import waitForBatchedUpdates from './waitForBatchedUpdates'; + +type MockFetch = ReturnType & {pause?: () => void; fail?: () => void; succeed?: () => void; resume?: () => Promise}; + +type Response = {ok: boolean; json: () => Promise<{jsonCode: number}>}; +type QueueItem = (value: Response | PromiseLike) => void; + +type FormData = { + entries: () => Array<[string, string | Blob]>; +}; + +type Listener = () => void; + +function buildPersonalDetails(login: string, accountID: number, firstName = 'Test'): PersonalDetails { + return { + accountID, + login, + avatar: 'https://d2k5nsl2zxldvw.cloudfront.net/images/avatars/avatar_7.png', + avatarThumbnail: 'https://d2k5nsl2zxldvw.cloudfront.net/images/avatars/avatar_7.png', + displayName: `${firstName} User`, + firstName, + lastName: 'User', + pronouns: '', + timezone: CONST.DEFAULT_TIME_ZONE, + phoneNumber: '', + }; +} + +/** + * Simulate signing in and make sure all API calls in this flow succeed. Every time we add + * a mockImplementationOnce() we are altering what Network.post() will return. + */ +function signInWithTestUser(accountID = 1, login = 'test@user.com', password = 'Password1', authToken = 'asdfqwerty', firstName = 'Test') { + const originalXhr = HttpUtils.xhr; + + HttpUtils.xhr = jest.fn().mockImplementation(() => { + // Your mocked response object + const mockedResponse = { + onyxData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: ONYXKEYS.CREDENTIALS, + value: { + login, + }, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: ONYXKEYS.ACCOUNT, + value: { + validated: true, + }, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: ONYXKEYS.PERSONAL_DETAILS_LIST, + value: { + [accountID]: buildPersonalDetails(login, accountID, firstName), + }, + }, + ], + jsonCode: 200, + }; + + // Return a Promise that resolves with the mocked response + return Promise.resolve(mockedResponse); + }); + + // Simulate user entering their login and populating the credentials.login + Session.beginSignIn(login); + return waitForBatchedUpdates() + .then(() => { + // Response is the same for calls to Authenticate and BeginSignIn + HttpUtils.xhr = jest.fn().mockImplementation(() => { + // Your mocked response object + const mockedResponse = { + onyxData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: ONYXKEYS.SESSION, + value: { + authToken, + accountID, + email: login, + encryptedAuthToken: authToken, + }, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: ONYXKEYS.CREDENTIALS, + value: { + autoGeneratedLogin: Str.guid('expensify.cash-'), + autoGeneratedPassword: Str.guid(), + }, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: ONYXKEYS.USER, + value: { + isUsingExpensifyCard: false, + }, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: ONYXKEYS.BETAS, + value: ['all'], + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: ONYXKEYS.NVP_PRIVATE_PUSH_NOTIFICATION_ID, + value: 'randomID', + }, + ], + jsonCode: 200, + }; + + // Return a Promise that resolves with the mocked response + return Promise.resolve(mockedResponse); + }); + Session.signIn(password); + return waitForBatchedUpdates(); + }) + .then(() => { + HttpUtils.xhr = originalXhr; + }); +} + +function signOutTestUser() { + const originalXhr = HttpUtils.xhr; + HttpUtils.xhr = jest.fn().mockImplementation(() => { + // Your mocked response object + const mockedResponse = { + jsonCode: 200, + }; + + // Return a Promise that resolves with the mocked response + return Promise.resolve(mockedResponse); + }); + Session.signOutAndRedirectToSignIn(); + return waitForBatchedUpdates().then(() => (HttpUtils.xhr = originalXhr)); +} + +/** + * Use for situations where fetch() is required. This mock is stateful and has some additional methods to control its behavior: + * + * - pause() – stop resolving promises until you call resume() + * - resume() - flush the queue of promises, and start resolving new promises immediately + * - fail() - start returning a failure response + * - success() - go back to returning a success response + */ +function getGlobalFetchMock() { + const queue: QueueItem[] = []; + let isPaused = false; + let shouldFail = false; + + const getResponse = () => + shouldFail + ? { + ok: true, + json: () => Promise.resolve({jsonCode: 400}), + } + : { + ok: true, + json: () => Promise.resolve({jsonCode: 200}), + }; + + const mockFetch: MockFetch = jest.fn().mockImplementation(() => { + if (!isPaused) { + return Promise.resolve(getResponse()); + } + return new Promise((resolve) => queue.push(resolve)); + }); + + mockFetch.pause = () => (isPaused = true); + mockFetch.resume = () => { + isPaused = false; + queue.forEach((resolve) => resolve(getResponse())); + return waitForBatchedUpdates(); + }; + mockFetch.fail = () => (shouldFail = true); + mockFetch.succeed = () => (shouldFail = false); + + return mockFetch; +} + +function setPersonalDetails(login: string, accountID: number) { + Onyx.merge(ONYXKEYS.PERSONAL_DETAILS_LIST, { + [accountID]: buildPersonalDetails(login, accountID), + }); + return waitForBatchedUpdates(); +} + +function buildTestReportComment(created: string, actorAccountID: number, actionID: string | null = null) { + const reportActionID = actionID ?? NumberUtils.rand64(); + return { + actionName: CONST.REPORT.ACTIONS.TYPE.ADDCOMMENT, + person: [{type: 'TEXT', style: 'strong', text: 'User B'}], + created, + message: [{type: 'COMMENT', html: `Comment ${actionID}`, text: `Comment ${actionID}`}], + reportActionID, + actorAccountID, + }; +} + +function assertFormDataMatchesObject(formData: FormData, obj: Report) { + expect(Array.from(formData.entries()).reduce((memo, x) => ({...memo, [x[0]]: x[1]}), {})).toEqual(expect.objectContaining(obj)); +} + +/** + * This is a helper function to create a mock for the addListener function of the react-navigation library. + * The reason we need this is because we need to trigger the transitionEnd event in our tests to simulate + * the transitionEnd event that is triggered when the screen transition animation is completed. + * + * @returns An object with two functions: triggerTransitionEnd and addListener + */ +const createAddListenerMock = () => { + const transitionEndListeners: Listener[] = []; + const triggerTransitionEnd = () => { + transitionEndListeners.forEach((transitionEndListener) => transitionEndListener()); + }; + + const addListener = jest.fn().mockImplementation((listener, callback) => { + if (listener === 'transitionEnd') { + transitionEndListeners.push(callback); + } + return () => { + transitionEndListeners.filter((cb) => cb !== callback); + }; + }); + + return {triggerTransitionEnd, addListener}; +}; + +export {getGlobalFetchMock, signInWithTestUser, signOutTestUser, setPersonalDetails, buildPersonalDetails, buildTestReportComment, assertFormDataMatchesObject, createAddListenerMock}; From 703bd25dd4354abcddac108d5f7b6384691d68c1 Mon Sep 17 00:00:00 2001 From: ruben-rebelo Date: Wed, 28 Feb 2024 18:31:21 +0000 Subject: [PATCH 02/15] [TS migration] Fixed compare --- tests/e2e/compare/compare.ts | 31 ++++++------------------------- 1 file changed, 6 insertions(+), 25 deletions(-) diff --git a/tests/e2e/compare/compare.ts b/tests/e2e/compare/compare.ts index cf74b1240bf8..bc3d7fae23ea 100644 --- a/tests/e2e/compare/compare.ts +++ b/tests/e2e/compare/compare.ts @@ -14,11 +14,7 @@ type Entry = { mean?: number; }; -type Result = { - name: string; - current?: Entry; - baseline?: Entry; -}; +type Metric = Record; /* * base implementation from: https://github.com/callstack/reassure/blob/main/packages/reassure-compare/src/compare.ts @@ -63,13 +59,13 @@ function buildCompareEntry(name: string, compare: Stats, baseline: Stats): Entry /** * Compare results between baseline and current entries and categorize. */ -function compareResults(compareEntries: Record, baselineEntries: Record) { +function compareResults(compareEntries: Metric, baselineEntries: Metric) { // Unique test scenario names - const names: string[] = [...new Set([...Object(compareEntries).keys(), ...Object(baselineEntries ?? {}).keys()])]; + const compareKeys = Object.keys(compareEntries); + const baselineKeys = baselineEntries ? Object.keys(baselineEntries) : []; + const names = Array.from(new Set([...compareKeys, ...baselineKeys])); const compared: Entry[] = []; - const added: Result[] = []; - const removed: Result[] = []; names.forEach((name: string) => { const current = compareEntries[name]; @@ -80,16 +76,6 @@ function compareResults(compareEntries: Record, baselineEntries: if (baseline && current) { compared.push(buildCompareEntry(name, deltaStats, currentStats)); - } else if (current) { - added.push({ - name, - current, - }); - } else if (baseline) { - removed.push({ - name, - baseline, - }); } }); @@ -97,18 +83,13 @@ function compareResults(compareEntries: Record, baselineEntries: const meaningless = compared.filter((item) => !item.isDurationDiffOfSignificance); - added.sort((a, b) => (b?.current?.mean ?? 0) - (a.current?.mean ?? 0)); - removed.sort((a, b) => (b.baseline?.mean ?? 0) - (a.baseline?.mean ?? 0)); - return { significance, meaningless, - added, - removed, }; } -export default (main: Record, delta: Record, outputFile: string, outputFormat = 'all') => { +export default (main: Metric, delta: Metric, outputFile: string, outputFormat = 'all') => { // IMPORTANT NOTE: make sure you are passing the delta/compare results first, then the main/baseline results: const outputData = compareResults(delta, main); From 6b655b588b8c36e80909c6e12dd020ef46cfef39 Mon Sep 17 00:00:00 2001 From: ruben-rebelo Date: Thu, 29 Feb 2024 09:41:04 +0000 Subject: [PATCH 03/15] [TS migratio] Lint --- tests/e2e/measure/math.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e/measure/math.ts b/tests/e2e/measure/math.ts index c9c0219ef1fd..1cb6661007ea 100644 --- a/tests/e2e/measure/math.ts +++ b/tests/e2e/measure/math.ts @@ -49,4 +49,4 @@ const getStats = (entries: Entries): Stats => { }; export default getStats; -export type {Stats} +export type {Stats}; From af3fd5581859a9e408a15daba5a27d1c069e9186 Mon Sep 17 00:00:00 2001 From: ruben-rebelo Date: Fri, 1 Mar 2024 12:04:48 +0000 Subject: [PATCH 04/15] [TS migration][G3] Feedback --- src/types/onyx/ReportAction.ts | 6 +++++- src/types/onyx/Transaction.ts | 18 +++++++++++++++++- tests/e2e/compare/compare.ts | 5 ++--- tests/unit/CurrencyUtilsTest.ts | 4 ---- tests/unit/IOUUtilsTest.ts | 7 +++---- tests/unit/SidebarTest.ts | 30 ++++++++++++------------------ tests/utils/TestHelper.ts | 10 +++++++++- 7 files changed, 48 insertions(+), 32 deletions(-) diff --git a/src/types/onyx/ReportAction.ts b/src/types/onyx/ReportAction.ts index bb5bf50ec6cf..0971fb6b77e1 100644 --- a/src/types/onyx/ReportAction.ts +++ b/src/types/onyx/ReportAction.ts @@ -2,6 +2,8 @@ import type {ValueOf} from 'type-fest'; import type {FileObject} from '@components/AttachmentModal'; import type {AvatarSource} from '@libs/UserUtils'; import type CONST from '@src/CONST'; +import type ONYXKEYS from '@src/ONYXKEYS'; +import type CollectionDataSet from '@src/types/utils/CollectionDataSet'; import type {EmptyObject} from '@src/types/utils/EmptyObject'; import type * as OnyxCommon from './OnyxCommon'; import type {Decision, Reaction} from './OriginalMessage'; @@ -224,5 +226,7 @@ type ReportAction = ReportActionBase & OriginalMessage; type ReportActions = Record; +type ReportActionCollectionDataSet = CollectionDataSet; + export default ReportAction; -export type {ReportActions, ReportActionBase, Message, LinkMetadata, OriginalMessage}; +export type {ReportActions, ReportActionBase, Message, LinkMetadata, OriginalMessage, ReportActionCollectionDataSet}; diff --git a/src/types/onyx/Transaction.ts b/src/types/onyx/Transaction.ts index 1a7541955720..709597c16140 100644 --- a/src/types/onyx/Transaction.ts +++ b/src/types/onyx/Transaction.ts @@ -1,5 +1,7 @@ import type {KeysOfUnion, 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 {Participant, Split} from './IOU'; import type * as OnyxCommon from './OnyxCommon'; import type RecentWaypoint from './RecentWaypoint'; @@ -224,5 +226,19 @@ type AdditionalTransactionChanges = { type TransactionChanges = Partial & AdditionalTransactionChanges; +type TransactionCollectionDataSet = CollectionDataSet; + export default Transaction; -export type {WaypointCollection, Comment, Receipt, Waypoint, ReceiptError, ReceiptErrors, TransactionPendingFieldsKey, TransactionChanges, TaxRate, ReceiptSource}; +export type { + WaypointCollection, + Comment, + Receipt, + Waypoint, + ReceiptError, + ReceiptErrors, + TransactionPendingFieldsKey, + TransactionChanges, + TaxRate, + ReceiptSource, + TransactionCollectionDataSet, +}; diff --git a/tests/e2e/compare/compare.ts b/tests/e2e/compare/compare.ts index bc3d7fae23ea..2de54b416828 100644 --- a/tests/e2e/compare/compare.ts +++ b/tests/e2e/compare/compare.ts @@ -61,9 +61,8 @@ function buildCompareEntry(name: string, compare: Stats, baseline: Stats): Entry */ function compareResults(compareEntries: Metric, baselineEntries: Metric) { // Unique test scenario names - const compareKeys = Object.keys(compareEntries); - const baselineKeys = baselineEntries ? Object.keys(baselineEntries) : []; - const names = Array.from(new Set([...compareKeys, ...baselineKeys])); + const baselineKeys = Object.keys(baselineEntries ?? {}); + const names = Array.from(new Set([...baselineKeys])); const compared: Entry[] = []; diff --git a/tests/unit/CurrencyUtilsTest.ts b/tests/unit/CurrencyUtilsTest.ts index 246af64b1d87..a1e4b03fa715 100644 --- a/tests/unit/CurrencyUtilsTest.ts +++ b/tests/unit/CurrencyUtilsTest.ts @@ -3,7 +3,6 @@ import CONST from '@src/CONST'; import * as CurrencyUtils from '@src/libs/CurrencyUtils'; import LocaleListener from '@src/libs/Localize/LocaleListener'; import ONYXKEYS from '@src/ONYXKEYS'; -import {isEmptyObject} from '@src/types/utils/EmptyObject'; import waitForBatchedUpdates from '../utils/waitForBatchedUpdates'; // This file can get outdated. In that case, you can follow these steps to update it: // - open your browser console and navigate to the Network tab @@ -37,9 +36,6 @@ describe('CurrencyUtils', () => { describe('getLocalizedCurrencySymbol', () => { test.each(AVAILABLE_LOCALES)('Returns non empty string for all currencyCode with preferredLocale %s', (prefrredLocale) => Onyx.set(ONYXKEYS.NVP_PREFERRED_LOCALE, prefrredLocale).then(() => { - if (isEmptyObject(currencyCodeList)) { - return; - } currencyCodeList.forEach((currencyCode: string) => { const localizedSymbol = CurrencyUtils.getLocalizedCurrencySymbol(currencyCode); diff --git a/tests/unit/IOUUtilsTest.ts b/tests/unit/IOUUtilsTest.ts index b390d0cd70a3..33dd1ff05e13 100644 --- a/tests/unit/IOUUtilsTest.ts +++ b/tests/unit/IOUUtilsTest.ts @@ -1,10 +1,9 @@ -import type {NullishDeep} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import * as IOUUtils from '@src/libs/IOUUtils'; import * as ReportUtils from '@src/libs/ReportUtils'; import * as TransactionUtils from '@src/libs/TransactionUtils'; import ONYXKEYS from '@src/ONYXKEYS'; -import type Transaction from '@src/types/onyx/Transaction'; +import type {TransactionCollectionDataSet} from '@src/types/onyx/Transaction'; import waitForBatchedUpdates from '../utils/waitForBatchedUpdates'; import currencyList from './currencyList.json'; @@ -30,7 +29,7 @@ describe('IOUUtils', () => { const iouReport = ReportUtils.buildOptimisticIOUReport(1, 2, 100, '1', 'USD'); const usdPendingTransaction = TransactionUtils.buildOptimisticTransaction(100, 'USD', iouReport.reportID); const aedPendingTransaction = TransactionUtils.buildOptimisticTransaction(100, 'AED', iouReport.reportID); - const MergeQueries: Record<`${typeof ONYXKEYS.COLLECTION.TRANSACTION}${string}`, NullishDeep> = {}; + const MergeQueries: TransactionCollectionDataSet = {}; MergeQueries[`${ONYXKEYS.COLLECTION.TRANSACTION}${usdPendingTransaction.transactionID}`] = usdPendingTransaction; MergeQueries[`${ONYXKEYS.COLLECTION.TRANSACTION}${aedPendingTransaction.transactionID}`] = aedPendingTransaction; @@ -45,7 +44,7 @@ describe('IOUUtils', () => { const usdPendingTransaction = TransactionUtils.buildOptimisticTransaction(100, 'USD', iouReport.reportID); const aedPendingTransaction = TransactionUtils.buildOptimisticTransaction(100, 'AED', iouReport.reportID); - const MergeQueries: Record<`${typeof ONYXKEYS.COLLECTION.TRANSACTION}${string}`, NullishDeep> = {}; + const MergeQueries: TransactionCollectionDataSet = {}; MergeQueries[`${ONYXKEYS.COLLECTION.TRANSACTION}${usdPendingTransaction.transactionID}`] = { ...usdPendingTransaction, pendingAction: null, diff --git a/tests/unit/SidebarTest.ts b/tests/unit/SidebarTest.ts index 4a3be66ebe37..89e7a86e095a 100644 --- a/tests/unit/SidebarTest.ts +++ b/tests/unit/SidebarTest.ts @@ -5,6 +5,8 @@ import CONST from '@src/CONST'; import * as Localize from '@src/libs/Localize'; import ONYXKEYS from '@src/ONYXKEYS'; import type * as OnyxTypes from '@src/types/onyx'; +import type {ReportCollectionDataSet} from '@src/types/onyx/Report'; +import type {ReportActionCollectionDataSet} from '@src/types/onyx/ReportAction'; import * as LHNTestUtils from '../utils/LHNTestUtils'; import waitForBatchedUpdates from '../utils/waitForBatchedUpdates'; import wrapOnyxWithWaitForBatchedUpdates from '../utils/wrapOnyxWithWaitForBatchedUpdates'; @@ -38,16 +40,14 @@ describe('Sidebar', () => { describe('archived chats', () => { it('renders the archive reason as the preview message of the chat', () => { const report = { - // @ts-expect-error TODO: Remove this once LHNTestUtils (https://github.com/Expensify/App/issues/25320) is migrated to TypeScript. - ...LHNTestUtils.getFakeReport(['email1@test.com', 'email2@test.com'], 3, true), + ...LHNTestUtils.getFakeReport([1, 2], 3, true), chatType: CONST.REPORT.CHAT_TYPE.POLICY_ROOM, statusNum: CONST.REPORT.STATUS_NUM.CLOSED, stateNum: CONST.REPORT.STATE_NUM.APPROVED, }; const action = { - // @ts-expect-error TODO: Remove this once LHNTestUtils (https://github.com/Expensify/App/issues/25320) is migrated to TypeScript. - ...LHNTestUtils.getFakeReportAction('email1@test.com', 3, true), + ...LHNTestUtils.getFakeReportAction('email1@test.com', 3), actionName: 'CLOSED', originalMessage: { reason: CONST.REPORT.ARCHIVE_REASON.DEFAULT, @@ -62,14 +62,12 @@ describe('Sidebar', () => { // When Onyx is updated with the data and the sidebar re-renders .then(() => { const reportCollection = { - // @ts-expect-error TODO: Remove this once LHNTestUtils (https://github.com/Expensify/App/issues/25320) is migrated to TypeScript. [`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`]: report, - } as Record<`${typeof ONYXKEYS.COLLECTION.REPORT}${string}`, NullishDeep>; + } as ReportCollectionDataSet; const reportAction = { - // @ts-expect-error TODO: Remove this once LHNTestUtils (https://github.com/Expensify/App/issues/25320) is migrated to TypeScript. - [`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.reportID}`]: {[action.reportActionId]: action}, - } as Record<`${typeof ONYXKEYS.COLLECTION.REPORT_ACTIONS}${string}`, NullishDeep>; + [`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.reportID}`]: {[action.reportActionID]: action}, + } as ReportActionCollectionDataSet; return Onyx.multiSet({ [ONYXKEYS.BETAS]: betas, @@ -93,16 +91,14 @@ describe('Sidebar', () => { }); it('renders the policy deleted archive reason as the preview message of the chat', () => { const report = { - // @ts-expect-error TODO: Remove this once LHNTestUtils (https://github.com/Expensify/App/issues/25320) is migrated to TypeScript. - ...LHNTestUtils.getFakeReport(['email1@test.com', 'email2@test.com'], 3, true), + ...LHNTestUtils.getFakeReport([1, 2], 3, true), policyName: 'Vikings Policy', chatType: CONST.REPORT.CHAT_TYPE.POLICY_ROOM, statusNum: CONST.REPORT.STATUS_NUM.CLOSED, stateNum: CONST.REPORT.STATE_NUM.APPROVED, }; const action = { - // @ts-expect-error TODO: Remove this once LHNTestUtils (https://github.com/Expensify/App/issues/25320) is migrated to TypeScript. - ...LHNTestUtils.getFakeReportAction('email1@test.com', 3, true), + ...LHNTestUtils.getFakeReportAction('email1@test.com', 3), actionName: 'CLOSED', originalMessage: { policyName: 'Vikings Policy', @@ -118,14 +114,12 @@ describe('Sidebar', () => { // When Onyx is updated with the data and the sidebar re-renders .then(() => { const reportCollection = { - // @ts-expect-error TODO: Remove this once LHNTestUtils (https://github.com/Expensify/App/issues/25320) is migrated to TypeScript. [`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`]: report, - } as Record<`${typeof ONYXKEYS.COLLECTION.REPORT}${string}`, NullishDeep>; + } as ReportCollectionDataSet; const reportAction = { - // @ts-expect-error TODO: Remove this once LHNTestUtils (https://github.com/Expensify/App/issues/25320) is migrated to TypeScript. - [`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.reportID}`]: {[action.reportActionId]: action}, - } as Record<`${typeof ONYXKEYS.COLLECTION.REPORT_ACTIONS}${string}`, NullishDeep>; + [`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.reportID}`]: {[action.reportActionID]: action}, + } as ReportActionCollectionDataSet; return Onyx.multiSet({ [ONYXKEYS.BETAS]: betas, diff --git a/tests/utils/TestHelper.ts b/tests/utils/TestHelper.ts index edef0bd55e5f..7f78d5080967 100644 --- a/tests/utils/TestHelper.ts +++ b/tests/utils/TestHelper.ts @@ -8,7 +8,15 @@ import ONYXKEYS from '@src/ONYXKEYS'; import type {PersonalDetails, Report} from '@src/types/onyx'; import waitForBatchedUpdates from './waitForBatchedUpdates'; -type MockFetch = ReturnType & {pause?: () => void; fail?: () => void; succeed?: () => void; resume?: () => Promise}; +type MockFetch = ReturnType & { + pause?: () => void; + + fail?: () => void; + + succeed?: () => void; + + resume?: () => Promise; +}; type Response = {ok: boolean; json: () => Promise<{jsonCode: number}>}; type QueueItem = (value: Response | PromiseLike) => void; From 8022a87f172b44351504cda0fc726745d3575e69 Mon Sep 17 00:00:00 2001 From: ruben-rebelo Date: Fri, 1 Mar 2024 12:12:10 +0000 Subject: [PATCH 05/15] [TS migration][G3] Lint fix --- tests/unit/SidebarTest.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/unit/SidebarTest.ts b/tests/unit/SidebarTest.ts index 89e7a86e095a..c913f5a2900d 100644 --- a/tests/unit/SidebarTest.ts +++ b/tests/unit/SidebarTest.ts @@ -1,10 +1,8 @@ import {cleanup, screen} from '@testing-library/react-native'; -import type {NullishDeep} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import CONST from '@src/CONST'; import * as Localize from '@src/libs/Localize'; import ONYXKEYS from '@src/ONYXKEYS'; -import type * as OnyxTypes from '@src/types/onyx'; import type {ReportCollectionDataSet} from '@src/types/onyx/Report'; import type {ReportActionCollectionDataSet} from '@src/types/onyx/ReportAction'; import * as LHNTestUtils from '../utils/LHNTestUtils'; From 1825d3a3c0357e6a2ef29d867758f843c9df66ec Mon Sep 17 00:00:00 2001 From: ruben-rebelo Date: Mon, 4 Mar 2024 09:51:58 +0000 Subject: [PATCH 06/15] [TS migration][G3] Fixed lint issue --- tests/utils/TestHelper.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/utils/TestHelper.ts b/tests/utils/TestHelper.ts index 7f78d5080967..165ffc670902 100644 --- a/tests/utils/TestHelper.ts +++ b/tests/utils/TestHelper.ts @@ -184,7 +184,10 @@ function getGlobalFetchMock() { if (!isPaused) { return Promise.resolve(getResponse()); } - return new Promise((resolve) => queue.push(resolve)); + return new Promise((resolve) => { + queue.push(resolve); + }); + }); mockFetch.pause = () => (isPaused = true); From de503429dfab4d0bd6d8d33460a4b52304e401a7 Mon Sep 17 00:00:00 2001 From: ruben-rebelo Date: Mon, 4 Mar 2024 10:04:26 +0000 Subject: [PATCH 07/15] [TS migration][G3] TS fix --- tests/e2e/compare/compare.ts | 13 +------------ tests/utils/TestHelper.ts | 1 - 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/tests/e2e/compare/compare.ts b/tests/e2e/compare/compare.ts index 2de54b416828..443265802a98 100644 --- a/tests/e2e/compare/compare.ts +++ b/tests/e2e/compare/compare.ts @@ -1,19 +1,10 @@ import type {Stats} from '../measure/math'; import getStats from '../measure/math'; import * as math from './math'; +import type {Entry} from './output/console'; import printToConsole from './output/console'; import writeToMarkdown from './output/markdown'; -type Entry = { - name: string; - baseline?: Stats; - current?: Stats; - diff?: number; - relativeDurationDiff?: number; - isDurationDiffOfSignificance?: boolean; - mean?: number; -}; - type Metric = Record; /* @@ -100,5 +91,3 @@ export default (main: Metric, delta: Metric, outputFile: string, outputFormat = return writeToMarkdown(outputFile, outputData); } }; - -export type {Entry}; diff --git a/tests/utils/TestHelper.ts b/tests/utils/TestHelper.ts index 165ffc670902..a9f72a40cf3f 100644 --- a/tests/utils/TestHelper.ts +++ b/tests/utils/TestHelper.ts @@ -187,7 +187,6 @@ function getGlobalFetchMock() { return new Promise((resolve) => { queue.push(resolve); }); - }); mockFetch.pause = () => (isPaused = true); From e6cdbd96e714be5486721540fdf60e7ae7b40991 Mon Sep 17 00:00:00 2001 From: ruben-rebelo Date: Fri, 8 Mar 2024 14:35:54 +0000 Subject: [PATCH 08/15] [TS migration][G3] Feedback --- tests/unit/SidebarTest.ts | 8 ++++---- tests/utils/TestHelper.ts | 11 +++++++++-- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/tests/unit/SidebarTest.ts b/tests/unit/SidebarTest.ts index c913f5a2900d..846c714ce19d 100644 --- a/tests/unit/SidebarTest.ts +++ b/tests/unit/SidebarTest.ts @@ -59,11 +59,11 @@ describe('Sidebar', () => { waitForBatchedUpdates() // When Onyx is updated with the data and the sidebar re-renders .then(() => { - const reportCollection = { + const reportCollection: ReportActionCollectionDataSet = { [`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`]: report, } as ReportCollectionDataSet; - const reportAction = { + const reportAction: ReportActionCollectionDataSet = { [`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.reportID}`]: {[action.reportActionID]: action}, } as ReportActionCollectionDataSet; @@ -111,11 +111,11 @@ describe('Sidebar', () => { waitForBatchedUpdates() // When Onyx is updated with the data and the sidebar re-renders .then(() => { - const reportCollection = { + const reportCollection: ReportCollectionDataSet = { [`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`]: report, } as ReportCollectionDataSet; - const reportAction = { + const reportAction: ReportActionCollectionDataSet = { [`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.reportID}`]: {[action.reportActionID]: action}, } as ReportActionCollectionDataSet; diff --git a/tests/utils/TestHelper.ts b/tests/utils/TestHelper.ts index a9f72a40cf3f..097433e8813d 100644 --- a/tests/utils/TestHelper.ts +++ b/tests/utils/TestHelper.ts @@ -1,4 +1,5 @@ import Str from 'expensify-common/lib/str'; +import type {OnyxUpdate} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import CONST from '@src/CONST'; import * as Session from '@src/libs/actions/Session'; @@ -18,7 +19,13 @@ type MockFetch = ReturnType & { resume?: () => Promise; }; -type Response = {ok: boolean; json: () => Promise<{jsonCode: number}>}; +type Response = { + ok?: boolean; + json?: () => Promise<{jsonCode: number}>; + jsonCode?: number; + onyxData?: OnyxUpdate[]; +}; + type QueueItem = (value: Response | PromiseLike) => void; type FormData = { @@ -51,7 +58,7 @@ function signInWithTestUser(accountID = 1, login = 'test@user.com', password = ' HttpUtils.xhr = jest.fn().mockImplementation(() => { // Your mocked response object - const mockedResponse = { + const mockedResponse: Response = { onyxData: [ { onyxMethod: Onyx.METHOD.MERGE, From 000491f03e57981cc5a1ba6e7e1643f50458f428 Mon Sep 17 00:00:00 2001 From: ruben-rebelo Date: Tue, 12 Mar 2024 09:49:51 +0000 Subject: [PATCH 09/15] [TS migration][G3] Feedback --- tests/utils/TestHelper.ts | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/tests/utils/TestHelper.ts b/tests/utils/TestHelper.ts index 097433e8813d..10313ad46d47 100644 --- a/tests/utils/TestHelper.ts +++ b/tests/utils/TestHelper.ts @@ -1,12 +1,11 @@ import Str from 'expensify-common/lib/str'; -import type {OnyxUpdate} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import CONST from '@src/CONST'; import * as Session from '@src/libs/actions/Session'; import HttpUtils from '@src/libs/HttpUtils'; import * as NumberUtils from '@src/libs/NumberUtils'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {PersonalDetails, Report} from '@src/types/onyx'; +import type {Response as OnyxResponse, PersonalDetails, Report} from '@src/types/onyx'; import waitForBatchedUpdates from './waitForBatchedUpdates'; type MockFetch = ReturnType & { @@ -19,14 +18,7 @@ type MockFetch = ReturnType & { resume?: () => Promise; }; -type Response = { - ok?: boolean; - json?: () => Promise<{jsonCode: number}>; - jsonCode?: number; - onyxData?: OnyxUpdate[]; -}; - -type QueueItem = (value: Response | PromiseLike) => void; +type QueueItem = (value: Partial | PromiseLike>) => void; type FormData = { entries: () => Array<[string, string | Blob]>; @@ -58,7 +50,7 @@ function signInWithTestUser(accountID = 1, login = 'test@user.com', password = ' HttpUtils.xhr = jest.fn().mockImplementation(() => { // Your mocked response object - const mockedResponse: Response = { + const mockedResponse: OnyxResponse = { onyxData: [ { onyxMethod: Onyx.METHOD.MERGE, @@ -176,7 +168,7 @@ function getGlobalFetchMock() { let isPaused = false; let shouldFail = false; - const getResponse = () => + const getResponse = (): Partial => shouldFail ? { ok: true, @@ -256,4 +248,4 @@ const createAddListenerMock = () => { return {triggerTransitionEnd, addListener}; }; -export {getGlobalFetchMock, signInWithTestUser, signOutTestUser, setPersonalDetails, buildPersonalDetails, buildTestReportComment, assertFormDataMatchesObject, createAddListenerMock}; +export {assertFormDataMatchesObject, buildPersonalDetails, buildTestReportComment, createAddListenerMock, getGlobalFetchMock, setPersonalDetails, signInWithTestUser, signOutTestUser}; From fd7802b60ce2ca3348ae8d2a4bc77145777780d9 Mon Sep 17 00:00:00 2001 From: ruben-rebelo Date: Tue, 12 Mar 2024 10:21:44 +0000 Subject: [PATCH 10/15] [TS migration][G3] Feedback --- tests/unit/SidebarTest.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/unit/SidebarTest.ts b/tests/unit/SidebarTest.ts index fd068336d545..499af3031553 100644 --- a/tests/unit/SidebarTest.ts +++ b/tests/unit/SidebarTest.ts @@ -59,13 +59,13 @@ describe('Sidebar', () => { waitForBatchedUpdates() // When Onyx is updated with the data and the sidebar re-renders .then(() => { - const reportCollection: ReportActionCollectionDataSet = { + const reportCollection: ReportCollectionDataSet = { [`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`]: report, - } as ReportCollectionDataSet; + } const reportAction: ReportActionCollectionDataSet = { [`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.reportID}`]: {[action.reportActionID]: action}, - } as ReportActionCollectionDataSet; + } as ReportActionCollectionDataSet return Onyx.multiSet({ [ONYXKEYS.BETAS]: betas, @@ -113,11 +113,11 @@ describe('Sidebar', () => { .then(() => { const reportCollection: ReportCollectionDataSet = { [`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`]: report, - } as ReportCollectionDataSet; + } const reportAction: ReportActionCollectionDataSet = { [`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.reportID}`]: {[action.reportActionID]: action}, - } as ReportActionCollectionDataSet; + } as ReportActionCollectionDataSet return Onyx.multiSet({ [ONYXKEYS.BETAS]: betas, From 873493f83b6060b3ddca3877518d7e5f76cc1c55 Mon Sep 17 00:00:00 2001 From: ruben-rebelo Date: Tue, 12 Mar 2024 10:34:28 +0000 Subject: [PATCH 11/15] [TS migration][G3] Updated response type --- tests/utils/TestHelper.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/utils/TestHelper.ts b/tests/utils/TestHelper.ts index 323d48e08044..bf83845a81be 100644 --- a/tests/utils/TestHelper.ts +++ b/tests/utils/TestHelper.ts @@ -82,7 +82,7 @@ function signInWithTestUser(accountID = 1, login = 'test@user.com', password = ' return waitForBatchedUpdates() .then(() => { HttpUtils.xhr = jest.fn().mockImplementation(() => { - const mockedResponse = { + const mockedResponse: OnyxResponse = { onyxData: [ { onyxMethod: Onyx.METHOD.MERGE, @@ -137,7 +137,7 @@ function signInWithTestUser(accountID = 1, login = 'test@user.com', password = ' function signOutTestUser() { const originalXhr = HttpUtils.xhr; HttpUtils.xhr = jest.fn().mockImplementation(() => { - const mockedResponse = { + const mockedResponse: OnyxResponse = { jsonCode: 200, }; From e8d3ec69485aa78de14c7251808a0cdc2fdb7bb4 Mon Sep 17 00:00:00 2001 From: ruben-rebelo Date: Fri, 15 Mar 2024 17:33:44 +0000 Subject: [PATCH 12/15] [TS migration][G3] Lint --- tests/unit/SidebarTest.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/unit/SidebarTest.ts b/tests/unit/SidebarTest.ts index 499af3031553..004667b45fba 100644 --- a/tests/unit/SidebarTest.ts +++ b/tests/unit/SidebarTest.ts @@ -61,11 +61,11 @@ describe('Sidebar', () => { .then(() => { const reportCollection: ReportCollectionDataSet = { [`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`]: report, - } + }; const reportAction: ReportActionCollectionDataSet = { [`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.reportID}`]: {[action.reportActionID]: action}, - } as ReportActionCollectionDataSet + } as ReportActionCollectionDataSet; return Onyx.multiSet({ [ONYXKEYS.BETAS]: betas, @@ -113,11 +113,11 @@ describe('Sidebar', () => { .then(() => { const reportCollection: ReportCollectionDataSet = { [`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`]: report, - } + }; const reportAction: ReportActionCollectionDataSet = { [`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.reportID}`]: {[action.reportActionID]: action}, - } as ReportActionCollectionDataSet + } as ReportActionCollectionDataSet; return Onyx.multiSet({ [ONYXKEYS.BETAS]: betas, From c8848903b7e8effe4404c77ec986b985007a6483 Mon Sep 17 00:00:00 2001 From: ruben-rebelo Date: Thu, 21 Mar 2024 12:13:28 +0000 Subject: [PATCH 13/15] [TS migration][G3] TS issue fixes --- tests/e2e/compare/compare.ts | 27 ++++++++++++------------ tests/perf-test/SearchPage.perf-test.tsx | 4 ---- tests/ui/UnreadIndicatorsTest.tsx | 18 ++++++++-------- 3 files changed, 23 insertions(+), 26 deletions(-) diff --git a/tests/e2e/compare/compare.ts b/tests/e2e/compare/compare.ts index 443265802a98..bc1a2c30ccdb 100644 --- a/tests/e2e/compare/compare.ts +++ b/tests/e2e/compare/compare.ts @@ -50,25 +50,26 @@ function buildCompareEntry(name: string, compare: Stats, baseline: Stats): Entry /** * Compare results between baseline and current entries and categorize. */ -function compareResults(compareEntries: Metric, baselineEntries: Metric) { +function compareResults(compareEntries: Metric | string, baselineEntries: Metric | string) { // Unique test scenario names const baselineKeys = Object.keys(baselineEntries ?? {}); const names = Array.from(new Set([...baselineKeys])); const compared: Entry[] = []; - names.forEach((name: string) => { - const current = compareEntries[name]; - const baseline = baselineEntries[name]; + if (typeof compareEntries !== 'string' && typeof baselineEntries !== 'string') { + names.forEach((name: string) => { + const current = compareEntries[name]; + const baseline = baselineEntries[name]; - const currentStats = getStats(baseline); - const deltaStats = getStats(current); - - if (baseline && current) { - compared.push(buildCompareEntry(name, deltaStats, currentStats)); - } - }); + const currentStats = getStats(baseline); + const deltaStats = getStats(current); + if (baseline && current) { + compared.push(buildCompareEntry(name, deltaStats, currentStats)); + } + }); + } const significance = compared.filter((item) => item.isDurationDiffOfSignificance); const meaningless = compared.filter((item) => !item.isDurationDiffOfSignificance); @@ -79,9 +80,9 @@ function compareResults(compareEntries: Metric, baselineEntries: Metric) { }; } -export default (main: Metric, delta: Metric, outputFile: string, outputFormat = 'all') => { +export default (main: Metric | string, delta: Metric | string, outputFile: string, outputFormat = 'all') => { // IMPORTANT NOTE: make sure you are passing the delta/compare results first, then the main/baseline results: - const outputData = compareResults(delta, main); + const outputData = compareResults(main, main); if (outputFormat === 'console' || outputFormat === 'all') { printToConsole(outputData); diff --git a/tests/perf-test/SearchPage.perf-test.tsx b/tests/perf-test/SearchPage.perf-test.tsx index 3f3395092b26..5f2dd3800e0f 100644 --- a/tests/perf-test/SearchPage.perf-test.tsx +++ b/tests/perf-test/SearchPage.perf-test.tsx @@ -135,7 +135,6 @@ function SearchPageWrapper(args: SearchPageProps) { } test('[Search Page] should interact when text input changes', async () => { - // @ts-expect-error TODO: Remove this once TestHelper (https://github.com/Expensify/App/issues/25318) is migrated to TypeScript. const {addListener} = TestHelper.createAddListenerMock(); const scenario = async () => { @@ -165,7 +164,6 @@ test('[Search Page] should interact when text input changes', async () => { }); test('[Search Page] should render selection list', async () => { - // @ts-expect-error TODO: Remove this once TestHelper (https://github.com/Expensify/App/issues/25318) is migrated to TypeScript. const {triggerTransitionEnd, addListener} = TestHelper.createAddListenerMock(); const smallMockedPersonalDetails = getMockedPersonalDetails(5); @@ -195,7 +193,6 @@ test('[Search Page] should render selection list', async () => { }); test('[Search Page] should search in selection list', async () => { - // @ts-expect-error TODO: Remove this once TestHelper (https://github.com/Expensify/App/issues/25318) is migrated to TypeScript. const {triggerTransitionEnd, addListener} = TestHelper.createAddListenerMock(); const scenario = async () => { @@ -227,7 +224,6 @@ test('[Search Page] should search in selection list', async () => { }); test('[Search Page] should click on list item', async () => { - // @ts-expect-error TODO: Remove this once TestHelper (https://github.com/Expensify/App/issues/25318) is migrated to TypeScript. const {triggerTransitionEnd, addListener} = TestHelper.createAddListenerMock(); const scenario = async () => { diff --git a/tests/ui/UnreadIndicatorsTest.tsx b/tests/ui/UnreadIndicatorsTest.tsx index 0726dbc9c88d..c040e634c1ea 100644 --- a/tests/ui/UnreadIndicatorsTest.tsx +++ b/tests/ui/UnreadIndicatorsTest.tsx @@ -253,15 +253,15 @@ function signInAndGetAppWithUnreadChat(): Promise { }, ], }, - 1: TestHelper.buildTestReportComment(format(addSeconds(TEN_MINUTES_AGO, 10), CONST.DATE.FNS_DB_FORMAT_STRING), USER_B_ACCOUNT_ID, '1', createdReportActionID), - 2: TestHelper.buildTestReportComment(format(addSeconds(TEN_MINUTES_AGO, 20), CONST.DATE.FNS_DB_FORMAT_STRING), USER_B_ACCOUNT_ID, '2', '1'), - 3: TestHelper.buildTestReportComment(reportAction3CreatedDate, USER_B_ACCOUNT_ID, '3', '2'), - 4: TestHelper.buildTestReportComment(format(addSeconds(TEN_MINUTES_AGO, 40), CONST.DATE.FNS_DB_FORMAT_STRING), USER_B_ACCOUNT_ID, '4', '3'), - 5: TestHelper.buildTestReportComment(format(addSeconds(TEN_MINUTES_AGO, 50), CONST.DATE.FNS_DB_FORMAT_STRING), USER_B_ACCOUNT_ID, '5', '4'), - 6: TestHelper.buildTestReportComment(format(addSeconds(TEN_MINUTES_AGO, 60), CONST.DATE.FNS_DB_FORMAT_STRING), USER_B_ACCOUNT_ID, '6', '5'), - 7: TestHelper.buildTestReportComment(format(addSeconds(TEN_MINUTES_AGO, 70), CONST.DATE.FNS_DB_FORMAT_STRING), USER_B_ACCOUNT_ID, '7', '6'), - 8: TestHelper.buildTestReportComment(format(addSeconds(TEN_MINUTES_AGO, 80), CONST.DATE.FNS_DB_FORMAT_STRING), USER_B_ACCOUNT_ID, '8', '7'), - 9: TestHelper.buildTestReportComment(reportAction9CreatedDate, USER_B_ACCOUNT_ID, '9', '8'), + 1: TestHelper.buildTestReportComment(format(addSeconds(TEN_MINUTES_AGO, 10), CONST.DATE.FNS_DB_FORMAT_STRING), USER_B_ACCOUNT_ID, '1'), + 2: TestHelper.buildTestReportComment(format(addSeconds(TEN_MINUTES_AGO, 20), CONST.DATE.FNS_DB_FORMAT_STRING), USER_B_ACCOUNT_ID, '2'), + 3: TestHelper.buildTestReportComment(reportAction3CreatedDate, USER_B_ACCOUNT_ID, '3'), + 4: TestHelper.buildTestReportComment(format(addSeconds(TEN_MINUTES_AGO, 40), CONST.DATE.FNS_DB_FORMAT_STRING), USER_B_ACCOUNT_ID, '4'), + 5: TestHelper.buildTestReportComment(format(addSeconds(TEN_MINUTES_AGO, 50), CONST.DATE.FNS_DB_FORMAT_STRING), USER_B_ACCOUNT_ID, '5'), + 6: TestHelper.buildTestReportComment(format(addSeconds(TEN_MINUTES_AGO, 60), CONST.DATE.FNS_DB_FORMAT_STRING), USER_B_ACCOUNT_ID, '6'), + 7: TestHelper.buildTestReportComment(format(addSeconds(TEN_MINUTES_AGO, 70), CONST.DATE.FNS_DB_FORMAT_STRING), USER_B_ACCOUNT_ID, '7'), + 8: TestHelper.buildTestReportComment(format(addSeconds(TEN_MINUTES_AGO, 80), CONST.DATE.FNS_DB_FORMAT_STRING), USER_B_ACCOUNT_ID, '8'), + 9: TestHelper.buildTestReportComment(reportAction9CreatedDate, USER_B_ACCOUNT_ID, '9'), }); await Onyx.merge(ONYXKEYS.PERSONAL_DETAILS_LIST, { [USER_B_ACCOUNT_ID]: TestHelper.buildPersonalDetails(USER_B_EMAIL, USER_B_ACCOUNT_ID, 'B'), From 3dc33fc926959d3334538564ce6cb671c45be8d6 Mon Sep 17 00:00:00 2001 From: ruben-rebelo Date: Tue, 26 Mar 2024 09:06:13 +0000 Subject: [PATCH 14/15] [TS migration][G3] TS fixes --- tests/perf-test/ReportScreen.perf-test.tsx | 2 -- tests/unit/MiddlewareTest.ts | 8 ++++---- tests/unit/SidebarTest.ts | 10 +++++----- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/tests/perf-test/ReportScreen.perf-test.tsx b/tests/perf-test/ReportScreen.perf-test.tsx index b79a6916b7cf..ff3d1473c662 100644 --- a/tests/perf-test/ReportScreen.perf-test.tsx +++ b/tests/perf-test/ReportScreen.perf-test.tsx @@ -163,7 +163,6 @@ const reportActions = ReportTestUtils.getMockedReportActionsMap(500); const mockRoute = {params: {reportID: '1'}}; test('[ReportScreen] should render ReportScreen with composer interactions', () => { - // @ts-expect-error TODO: Remove this once TestHelper (https://github.com/Expensify/App/issues/25318) is migrated to TypeScript. const {triggerTransitionEnd, addListener} = TestHelper.createAddListenerMock(); const scenario = async () => { /** @@ -232,7 +231,6 @@ test('[ReportScreen] should render ReportScreen with composer interactions', () }); test('[ReportScreen] should press of the report item', () => { - // @ts-expect-error TODO: Remove this once TestHelper (https://github.com/Expensify/App/issues/25318) is migrated to TypeScript. const {triggerTransitionEnd, addListener} = TestHelper.createAddListenerMock(); const scenario = async () => { /** diff --git a/tests/unit/MiddlewareTest.ts b/tests/unit/MiddlewareTest.ts index b3ba6d32a7bb..78a54461ae20 100644 --- a/tests/unit/MiddlewareTest.ts +++ b/tests/unit/MiddlewareTest.ts @@ -39,7 +39,7 @@ describe('Middleware', () => { }, { command: 'AddComment', - data: {authToken: 'testToken', reportID: '1234', reportActionID: '5678', reportComment: 'foo'}, + data: {authToken: 'testToken', reportID: '1234', reportActionID: '5678'}, }, ]; for (const request of requests) { @@ -50,7 +50,7 @@ describe('Middleware', () => { expect(global.fetch).toHaveBeenCalledTimes(2); expect(global.fetch).toHaveBeenLastCalledWith('https://www.expensify.com.dev/api/AddComment?', expect.anything()); - TestHelper.assertFormDataMatchesObject((global.fetch as jest.Mock).mock.calls[1][1].body, {reportID: '1234', reportActionID: '5678', reportComment: 'foo'}); + TestHelper.assertFormDataMatchesObject((global.fetch as jest.Mock).mock.calls[1][1].body, {reportID: '1234', reportActionID: '5678'}); expect(global.fetch).toHaveBeenNthCalledWith(1, 'https://www.expensify.com.dev/api/OpenReport?', expect.anything()); TestHelper.assertFormDataMatchesObject((global.fetch as jest.Mock).mock.calls[0][1].body, {reportID: '1234'}); }); @@ -64,7 +64,7 @@ describe('Middleware', () => { }, { command: 'AddComment', - data: {authToken: 'testToken', reportID: '1234', reportActionID: '5678', reportComment: 'foo'}, + data: {authToken: 'testToken', reportID: '1234', reportActionID: '5678'}, }, ]; for (const request of requests) { @@ -94,7 +94,7 @@ describe('Middleware', () => { expect(global.fetch).toHaveBeenCalledTimes(2); expect(global.fetch).toHaveBeenLastCalledWith('https://www.expensify.com.dev/api/AddComment?', expect.anything()); - TestHelper.assertFormDataMatchesObject((global.fetch as jest.Mock).mock.calls[1][1].body, {reportID: '5555', reportActionID: '5678', reportComment: 'foo'}); + TestHelper.assertFormDataMatchesObject((global.fetch as jest.Mock).mock.calls[1][1].body, {reportID: '5555', reportActionID: '5678'}); expect(global.fetch).toHaveBeenNthCalledWith(1, 'https://www.expensify.com.dev/api/OpenReport?', expect.anything()); TestHelper.assertFormDataMatchesObject((global.fetch as jest.Mock).mock.calls[0][1].body, {reportID: '1234'}); }); diff --git a/tests/unit/SidebarTest.ts b/tests/unit/SidebarTest.ts index 004667b45fba..23ea0d377634 100644 --- a/tests/unit/SidebarTest.ts +++ b/tests/unit/SidebarTest.ts @@ -4,7 +4,7 @@ import CONST from '@src/CONST'; import * as Localize from '@src/libs/Localize'; import ONYXKEYS from '@src/ONYXKEYS'; import type {ReportCollectionDataSet} from '@src/types/onyx/Report'; -import type {ReportActionCollectionDataSet} from '@src/types/onyx/ReportAction'; +import type {ReportActionsCollectionDataSet} from '@src/types/onyx/ReportAction'; import * as LHNTestUtils from '../utils/LHNTestUtils'; import waitForBatchedUpdates from '../utils/waitForBatchedUpdates'; import wrapOnyxWithWaitForBatchedUpdates from '../utils/wrapOnyxWithWaitForBatchedUpdates'; @@ -63,9 +63,9 @@ describe('Sidebar', () => { [`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`]: report, }; - const reportAction: ReportActionCollectionDataSet = { + const reportAction: ReportActionsCollectionDataSet = { [`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.reportID}`]: {[action.reportActionID]: action}, - } as ReportActionCollectionDataSet; + } as ReportActionsCollectionDataSet; return Onyx.multiSet({ [ONYXKEYS.BETAS]: betas, @@ -115,9 +115,9 @@ describe('Sidebar', () => { [`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`]: report, }; - const reportAction: ReportActionCollectionDataSet = { + const reportAction: ReportActionsCollectionDataSet = { [`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.reportID}`]: {[action.reportActionID]: action}, - } as ReportActionCollectionDataSet; + } as ReportActionsCollectionDataSet; return Onyx.multiSet({ [ONYXKEYS.BETAS]: betas, From 3b081fd45d30dda87a39f0ecfe29a40742dc216a Mon Sep 17 00:00:00 2001 From: ruben-rebelo Date: Tue, 26 Mar 2024 09:46:56 +0000 Subject: [PATCH 15/15] [TS migration][G3] Fixed unit test --- tests/ui/UnreadIndicatorsTest.tsx | 20 ++++++++++---------- tests/utils/TestHelper.ts | 5 +++-- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/tests/ui/UnreadIndicatorsTest.tsx b/tests/ui/UnreadIndicatorsTest.tsx index c040e634c1ea..66da1a0eff80 100644 --- a/tests/ui/UnreadIndicatorsTest.tsx +++ b/tests/ui/UnreadIndicatorsTest.tsx @@ -233,7 +233,7 @@ function signInAndGetAppWithUnreadChat(): Promise { lastActorAccountID: USER_B_ACCOUNT_ID, type: CONST.REPORT.TYPE.CHAT, }); - const createdReportActionID = NumberUtils.rand64(); + const createdReportActionID = NumberUtils.rand64().toString(); await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${REPORT_ID}`, { [createdReportActionID]: { actionName: CONST.REPORT.ACTIONS.TYPE.CREATED, @@ -253,15 +253,15 @@ function signInAndGetAppWithUnreadChat(): Promise { }, ], }, - 1: TestHelper.buildTestReportComment(format(addSeconds(TEN_MINUTES_AGO, 10), CONST.DATE.FNS_DB_FORMAT_STRING), USER_B_ACCOUNT_ID, '1'), - 2: TestHelper.buildTestReportComment(format(addSeconds(TEN_MINUTES_AGO, 20), CONST.DATE.FNS_DB_FORMAT_STRING), USER_B_ACCOUNT_ID, '2'), - 3: TestHelper.buildTestReportComment(reportAction3CreatedDate, USER_B_ACCOUNT_ID, '3'), - 4: TestHelper.buildTestReportComment(format(addSeconds(TEN_MINUTES_AGO, 40), CONST.DATE.FNS_DB_FORMAT_STRING), USER_B_ACCOUNT_ID, '4'), - 5: TestHelper.buildTestReportComment(format(addSeconds(TEN_MINUTES_AGO, 50), CONST.DATE.FNS_DB_FORMAT_STRING), USER_B_ACCOUNT_ID, '5'), - 6: TestHelper.buildTestReportComment(format(addSeconds(TEN_MINUTES_AGO, 60), CONST.DATE.FNS_DB_FORMAT_STRING), USER_B_ACCOUNT_ID, '6'), - 7: TestHelper.buildTestReportComment(format(addSeconds(TEN_MINUTES_AGO, 70), CONST.DATE.FNS_DB_FORMAT_STRING), USER_B_ACCOUNT_ID, '7'), - 8: TestHelper.buildTestReportComment(format(addSeconds(TEN_MINUTES_AGO, 80), CONST.DATE.FNS_DB_FORMAT_STRING), USER_B_ACCOUNT_ID, '8'), - 9: TestHelper.buildTestReportComment(reportAction9CreatedDate, USER_B_ACCOUNT_ID, '9'), + 1: TestHelper.buildTestReportComment(format(addSeconds(TEN_MINUTES_AGO, 10), CONST.DATE.FNS_DB_FORMAT_STRING), USER_B_ACCOUNT_ID, '1', createdReportActionID), + 2: TestHelper.buildTestReportComment(format(addSeconds(TEN_MINUTES_AGO, 20), CONST.DATE.FNS_DB_FORMAT_STRING), USER_B_ACCOUNT_ID, '2', '1'), + 3: TestHelper.buildTestReportComment(reportAction3CreatedDate, USER_B_ACCOUNT_ID, '3', '2'), + 4: TestHelper.buildTestReportComment(format(addSeconds(TEN_MINUTES_AGO, 40), CONST.DATE.FNS_DB_FORMAT_STRING), USER_B_ACCOUNT_ID, '4', '3'), + 5: TestHelper.buildTestReportComment(format(addSeconds(TEN_MINUTES_AGO, 50), CONST.DATE.FNS_DB_FORMAT_STRING), USER_B_ACCOUNT_ID, '5', '4'), + 6: TestHelper.buildTestReportComment(format(addSeconds(TEN_MINUTES_AGO, 60), CONST.DATE.FNS_DB_FORMAT_STRING), USER_B_ACCOUNT_ID, '6', '5'), + 7: TestHelper.buildTestReportComment(format(addSeconds(TEN_MINUTES_AGO, 70), CONST.DATE.FNS_DB_FORMAT_STRING), USER_B_ACCOUNT_ID, '7', '6'), + 8: TestHelper.buildTestReportComment(format(addSeconds(TEN_MINUTES_AGO, 80), CONST.DATE.FNS_DB_FORMAT_STRING), USER_B_ACCOUNT_ID, '8', '7'), + 9: TestHelper.buildTestReportComment(reportAction9CreatedDate, USER_B_ACCOUNT_ID, '9', '8'), }); await Onyx.merge(ONYXKEYS.PERSONAL_DETAILS_LIST, { [USER_B_ACCOUNT_ID]: TestHelper.buildPersonalDetails(USER_B_EMAIL, USER_B_ACCOUNT_ID, 'B'), diff --git a/tests/utils/TestHelper.ts b/tests/utils/TestHelper.ts index bf83845a81be..9f3b28973923 100644 --- a/tests/utils/TestHelper.ts +++ b/tests/utils/TestHelper.ts @@ -200,8 +200,8 @@ function setPersonalDetails(login: string, accountID: number) { return waitForBatchedUpdates(); } -function buildTestReportComment(created: string, actorAccountID: number, actionID: string | null = null) { - const reportActionID = actionID ?? NumberUtils.rand64(); +function buildTestReportComment(created: string, actorAccountID: number, actionID: string | null = null, previousReportActionID: string | null = null) { + const reportActionID = actionID ?? NumberUtils.rand64().toString(); return { actionName: CONST.REPORT.ACTIONS.TYPE.ADDCOMMENT, person: [{type: 'TEXT', style: 'strong', text: 'User B'}], @@ -209,6 +209,7 @@ function buildTestReportComment(created: string, actorAccountID: number, actionI message: [{type: 'COMMENT', html: `Comment ${actionID}`, text: `Comment ${actionID}`}], reportActionID, actorAccountID, + previousReportActionID, }; }