diff --git a/android/app/build.gradle b/android/app/build.gradle index 4db8a0836477..89e4bd2dfb56 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -91,8 +91,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1001040601 - versionName "1.4.6-1" + versionCode 1001040602 + versionName "1.4.6-2" } flavorDimensions "default" @@ -151,7 +151,7 @@ android { signingConfig null // buildTypes take precedence over productFlavors when it comes to the signing configuration, // thus we need to manually set the signing config, so that the e2e uses the debug config again. - // In other words, the signingConfig setting above will be ignored when we build the flavor in release mode. + // In other words, the signingConfig setting above will be ignored when we build the flavor in release mode. productFlavors.all { flavor -> // All release builds should be signed with the release config ... flavor.signingConfig signingConfigs.release diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index 5ce1b7f51147..1125f62243d3 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -1,128 +1,128 @@ - - CADisableMinimumFrameDurationOnPhone - - CFBundleDevelopmentRegion - en - CFBundleDisplayName - $(PRODUCT_NAME) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - APPL - CFBundleShortVersionString - 1.4.6 - CFBundleSignature - ???? - CFBundleURLTypes - - - CFBundleURLSchemes - - new-expensify - - - - CFBundleTypeRole - Editor - CFBundleURLSchemes - - com.googleusercontent.apps.921154746561-s3uqn2oe4m85tufi6mqflbfbuajrm2i3 - - - - CFBundleVersion - 1.4.6.1 - ITSAppUsesNonExemptEncryption - - LSApplicationQueriesSchemes - - venmo - - LSRequiresIPhoneOS - - LSSupportsOpeningDocumentsInPlace - - NSAppTransportSecurity - NSAllowsArbitraryLoads - - NSExceptionDomains - - localhost + CADisableMinimumFrameDurationOnPhone + + CFBundleDevelopmentRegion + en + CFBundleDisplayName + $(PRODUCT_NAME) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.4.6 + CFBundleSignature + ???? + CFBundleURLTypes + + + CFBundleURLSchemes + + new-expensify + + - NSExceptionAllowsInsecureHTTPLoads - - NSIncludesSubdomains - + CFBundleTypeRole + Editor + CFBundleURLSchemes + + com.googleusercontent.apps.921154746561-s3uqn2oe4m85tufi6mqflbfbuajrm2i3 + - www.expensify.com.dev + + CFBundleVersion + 1.4.6.2 + ITSAppUsesNonExemptEncryption + + LSApplicationQueriesSchemes + + venmo + + LSRequiresIPhoneOS + + LSSupportsOpeningDocumentsInPlace + + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + NSExceptionDomains - NSExceptionAllowsInsecureHTTPLoads - - NSIncludesSubdomains - + localhost + + NSExceptionAllowsInsecureHTTPLoads + + NSIncludesSubdomains + + + www.expensify.com.dev + + NSExceptionAllowsInsecureHTTPLoads + + NSIncludesSubdomains + + + NSCameraUsageDescription + Your camera is used to create chat attachments, documents, and facial capture. + NSLocationAlwaysAndWhenInUseUsageDescription + Your location is used to determine your default currency and timezone. + NSLocationWhenInUseUsageDescription + Your location is used to determine your default currency and timezone. + NSMicrophoneUsageDescription + Required for video capture + NSPhotoLibraryAddUsageDescription + Your camera roll is used to store chat attachments. + NSPhotoLibraryUsageDescription + Your photos are used to create chat attachments. + UIAppFonts + + ExpensifyNewKansas-Medium.otf + ExpensifyNewKansas-MediumItalic.otf + ExpensifyMono-Bold.otf + ExpensifyMono-Regular.otf + ExpensifyNeue-Bold.otf + ExpensifyNeue-BoldItalic.otf + ExpensifyNeue-Italic.otf + ExpensifyNeue-Regular.otf + + UIBackgroundModes + + remote-notification + + UIFileSharingEnabled + + UILaunchStoryboardName + BootSplash + UIRequiredDeviceCapabilities + + armv7 + + UIRequiresFullScreen + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + + UIUserInterfaceStyle + Dark + UIViewControllerBasedStatusBarAppearance + - NSCameraUsageDescription - Your camera is used to create chat attachments, documents, and facial capture. - NSLocationAlwaysAndWhenInUseUsageDescription - Your location is used to determine your default currency and timezone. - NSLocationWhenInUseUsageDescription - Your location is used to determine your default currency and timezone. - NSMicrophoneUsageDescription - Required for video capture - NSPhotoLibraryAddUsageDescription - Your camera roll is used to store chat attachments. - NSPhotoLibraryUsageDescription - Your photos are used to create chat attachments. - UIAppFonts - - ExpensifyNewKansas-Medium.otf - ExpensifyNewKansas-MediumItalic.otf - ExpensifyMono-Bold.otf - ExpensifyMono-Regular.otf - ExpensifyNeue-Bold.otf - ExpensifyNeue-BoldItalic.otf - ExpensifyNeue-Italic.otf - ExpensifyNeue-Regular.otf - - UIBackgroundModes - - remote-notification - - UIFileSharingEnabled - - UILaunchStoryboardName - BootSplash - UIRequiredDeviceCapabilities - - armv7 - - UIRequiresFullScreen - - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - - UIUserInterfaceStyle - Dark - UIViewControllerBasedStatusBarAppearance - - diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist index f9675bc7cc27..f67483e2be59 100644 --- a/ios/NewExpensifyTests/Info.plist +++ b/ios/NewExpensifyTests/Info.plist @@ -1,24 +1,24 @@ - - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - BNDL - CFBundleShortVersionString - 1.4.6 - CFBundleSignature - ???? - CFBundleVersion - 1.4.6.1 - + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.4.6 + CFBundleSignature + ???? + CFBundleVersion + 1.4.6.2 + diff --git a/package-lock.json b/package-lock.json index 289c13c27e27..92f3de4e6271 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "1.4.6-1", + "version": "1.4.6-2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "1.4.6-1", + "version": "1.4.6-2", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index de4be2ace0f4..24fbe3b0995f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "1.4.6-1", + "version": "1.4.6-2", "author": "Expensify, Inc.", "homepage": "https://new.expensify.com", "description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.", diff --git a/src/components/FeatureList.js b/src/components/FeatureList.js index 85195864cdc3..7b0c70372579 100644 --- a/src/components/FeatureList.js +++ b/src/components/FeatureList.js @@ -42,6 +42,7 @@ function FeatureList({menuItems, headline, description}) { iconHeight={60} iconStyles={[styles.mr3, styles.ml3]} interactive={false} + wrapperStyle={[styles.cursorAuto]} /> ))} diff --git a/src/pages/KeyboardShortcutsPage.js b/src/pages/KeyboardShortcutsPage.js index fde4bf5ff3df..c44c6213d3a5 100644 --- a/src/pages/KeyboardShortcutsPage.js +++ b/src/pages/KeyboardShortcutsPage.js @@ -36,7 +36,7 @@ function KeyboardShortcutsPage() { key={shortcut.displayName} title={shortcut.displayName} description={translate(`keyboardShortcutsPage.shortcuts.${shortcut.descriptionKey}`)} - wrapperStyle={styles.ph0} + wrapperStyle={[styles.ph0, styles.cursorAuto]} interactive={false} /> ); diff --git a/src/styles/utilities/overscrollBehaviorContain/index.js b/src/styles/utilities/overscrollBehaviorContain/index.js deleted file mode 100644 index 1377e4b47d28..000000000000 --- a/src/styles/utilities/overscrollBehaviorContain/index.js +++ /dev/null @@ -1,3 +0,0 @@ -export default { - overscrollBehavior: 'contain', -}; diff --git a/src/styles/utilities/overscrollBehaviorContain/index.native.js b/src/styles/utilities/overscrollBehaviorContain/index.native.js deleted file mode 100644 index ff8b4c56321a..000000000000 --- a/src/styles/utilities/overscrollBehaviorContain/index.native.js +++ /dev/null @@ -1 +0,0 @@ -export default {}; diff --git a/src/styles/utilities/overscrollBehaviorContain/index.native.ts b/src/styles/utilities/overscrollBehaviorContain/index.native.ts new file mode 100644 index 000000000000..fd6b2f496912 --- /dev/null +++ b/src/styles/utilities/overscrollBehaviorContain/index.native.ts @@ -0,0 +1,5 @@ +import OverscrollBehaviorStyles from './types'; + +const overscrollBehaviorContain: OverscrollBehaviorStyles = {}; + +export default overscrollBehaviorContain; diff --git a/src/styles/utilities/overscrollBehaviorContain/index.ts b/src/styles/utilities/overscrollBehaviorContain/index.ts new file mode 100644 index 000000000000..18cbe810d336 --- /dev/null +++ b/src/styles/utilities/overscrollBehaviorContain/index.ts @@ -0,0 +1,7 @@ +import OverscrollBehaviorStyles from './types'; + +const overscrollBehaviorContain: OverscrollBehaviorStyles = { + overscrollBehavior: 'contain', +}; + +export default overscrollBehaviorContain; diff --git a/src/styles/utilities/overscrollBehaviorContain/types.ts b/src/styles/utilities/overscrollBehaviorContain/types.ts new file mode 100644 index 000000000000..1e806eb60642 --- /dev/null +++ b/src/styles/utilities/overscrollBehaviorContain/types.ts @@ -0,0 +1,5 @@ +import {ViewStyle} from 'react-native'; + +type OverscrollBehaviorStyles = Pick; + +export default OverscrollBehaviorStyles; diff --git a/tests/perf-test/ReportActionsUtils.perf-test.js b/tests/perf-test/ReportActionsUtils.perf-test.js deleted file mode 100644 index 8e9f5570d5d8..000000000000 --- a/tests/perf-test/ReportActionsUtils.perf-test.js +++ /dev/null @@ -1,91 +0,0 @@ -import Onyx from 'react-native-onyx'; -import {measureFunction} from 'reassure'; -import _ from 'underscore'; -import CONST from '../../src/CONST'; -import * as ReportActionsUtils from '../../src/libs/ReportActionsUtils'; -import ONYXKEYS from '../../src/ONYXKEYS'; -import * as LHNTestUtils from '../utils/LHNTestUtils'; -import waitForBatchedUpdates from '../utils/waitForBatchedUpdates'; - -beforeAll(() => - Onyx.init({ - keys: ONYXKEYS, - safeEvictionKeys: [ONYXKEYS.COLLECTION.REPORT_ACTIONS], - registerStorageEventListener: () => {}, - }), -); - -// Clear out Onyx after each test so that each test starts with a clean slate -afterEach(() => { - Onyx.clear(); -}); - -const getMockedReportActionsMap = (reportsLength = 10, actionsPerReportLength = 100) => { - const mockReportActions = Array.from({length: actionsPerReportLength}, (_reportAction, i) => { - const reportActionKey = i + 1; - const email = `actor+${reportActionKey}@mail.com`; - const reportAction = LHNTestUtils.getFakeReportAction(email); - - return {[reportActionKey]: reportAction}; - }); - - const reportKeysMap = Array.from({length: reportsLength}, (_report, i) => { - const key = i + 1; - - return {[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${key}`]: _.assign({}, ...mockReportActions)}; - }); - - return _.assign({}, ...reportKeysMap); -}; - -const mockedReportActionsMap = getMockedReportActionsMap(2, 10000); - -/** - * This function will be executed 20 times and the average time will be used on the comparison. - * It will fail based on the CI configuration around Reassure: - * @see /.github/workflows/reassurePerformanceTests.yml - * - * Max deviation on the duration is set to 20% at the time of writing. - * - * More on the measureFunction API: - * @see https://callstack.github.io/reassure/docs/api#measurefunction-function - */ -test('getLastVisibleAction on 10k reportActions', async () => { - const reportId = '1'; - - await Onyx.multiSet({ - ...mockedReportActionsMap, - }); - await waitForBatchedUpdates(); - await measureFunction(() => ReportActionsUtils.getLastVisibleAction(reportId), {runs: 20}); -}); - -test('getLastVisibleAction on 10k reportActions with actionsToMerge', async () => { - const reportId = '1'; - const parentReportActionId = '1'; - const fakeParentAction = mockedReportActionsMap[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportId}`][parentReportActionId]; - const actionsToMerge = { - [parentReportActionId]: { - pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, - previousMessage: fakeParentAction.message, - message: [ - { - translationKey: '', - type: 'COMMENT', - html: '', - text: '', - isEdited: true, - isDeletedParentAction: true, - }, - ], - errors: null, - linkMetaData: [], - }, - }; - - await Onyx.multiSet({ - ...mockedReportActionsMap, - }); - await waitForBatchedUpdates(); - await measureFunction(() => ReportActionsUtils.getLastVisibleAction(reportId, actionsToMerge), {runs: 20}); -}); diff --git a/tests/perf-test/ReportActionsUtils.perf-test.ts b/tests/perf-test/ReportActionsUtils.perf-test.ts new file mode 100644 index 000000000000..a52e75c8b75f --- /dev/null +++ b/tests/perf-test/ReportActionsUtils.perf-test.ts @@ -0,0 +1,167 @@ +import Onyx from 'react-native-onyx'; +import {measureFunction} from 'reassure'; +import * as ReportActionsUtils from '@libs/ReportActionsUtils'; +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; +import ReportAction, {ReportActions} from '@src/types/onyx/ReportAction'; +import createCollection from '../utils/collections/createCollection'; +import createRandomReportAction from '../utils/collections/reportActions'; +import waitForBatchedUpdates from '../utils/waitForBatchedUpdates'; + +beforeAll(() => + Onyx.init({ + keys: ONYXKEYS, + safeEvictionKeys: [ONYXKEYS.COLLECTION.REPORT_ACTIONS], + }), +); + +// Clear out Onyx after each test so that each test starts with a clean slate +afterEach(() => { + Onyx.clear(); +}); + +const getMockedReportActionsMap = (reportsLength = 10, actionsPerReportLength = 100) => { + const mockReportActions = Array.from({length: actionsPerReportLength}, (v, i) => { + const reportActionKey = i + 1; + const reportAction = createRandomReportAction(reportActionKey); + + return {[reportActionKey]: reportAction}; + }); + + const reportKeysMap = Array.from({length: reportsLength}, (v, i) => { + const key = i + 1; + + return {[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${key}`]: Object.assign({}, ...mockReportActions)}; + }); + + return Object.assign({}, ...reportKeysMap) as Partial; +}; + +const mockedReportActionsMap = getMockedReportActionsMap(2, 10000); + +const reportActions = createCollection( + (item) => `${item.reportActionID}`, + (index) => createRandomReportAction(index), +); + +const reportId = '1'; + +const runs = CONST.PERFORMANCE_TESTS.RUNS; + +/** + * This function will be executed 20 times and the average time will be used on the comparison. + * It will fail based on the CI configuration around Reassure: + * @see /.github/workflows/reassurePerformanceTests.yml + * + * Max deviation on the duration is set to 20% at the time of writing. + * + * More on the measureFunction API: + * @see https://callstack.github.io/reassure/docs/api#measurefunction-function + */ +test('getLastVisibleAction on 10k reportActions', async () => { + await Onyx.multiSet({ + ...mockedReportActionsMap, + }); + + await waitForBatchedUpdates(); + await measureFunction(() => ReportActionsUtils.getLastVisibleAction(reportId), {runs}); +}); + +test('getLastVisibleAction on 10k reportActions with actionsToMerge', async () => { + const parentReportActionId = '1'; + const fakeParentAction = reportActions[parentReportActionId]; + const actionsToMerge = { + [parentReportActionId]: { + pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, + previousMessage: fakeParentAction.message, + message: [ + { + translationKey: '', + type: 'COMMENT', + html: '', + text: '', + isEdited: true, + isDeletedParentAction: true, + }, + ], + errors: null, + linkMetaData: [], + }, + } as unknown as ReportActions; + + await Onyx.multiSet({ + ...mockedReportActionsMap, + }); + await waitForBatchedUpdates(); + await measureFunction(() => ReportActionsUtils.getLastVisibleAction(reportId, actionsToMerge), {runs}); +}); + +test('getMostRecentIOURequestActionID on 10k ReportActions', async () => { + const reportActionsArray = ReportActionsUtils.getSortedReportActionsForDisplay(reportActions); + await Onyx.multiSet({ + ...mockedReportActionsMap, + }); + await waitForBatchedUpdates(); + await measureFunction(() => ReportActionsUtils.getMostRecentIOURequestActionID(reportActionsArray), {runs}); +}); + +test('getLastVisibleMessage on 10k ReportActions', async () => { + await Onyx.multiSet({ + ...mockedReportActionsMap, + }); + await waitForBatchedUpdates(); + await measureFunction(() => ReportActionsUtils.getLastVisibleMessage(reportId), {runs}); +}); + +test('getLastVisibleMessage on 10k ReportActions with actionsToMerge', async () => { + const parentReportActionId = '1'; + const fakeParentAction = reportActions[parentReportActionId]; + const actionsToMerge = { + [parentReportActionId]: { + pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, + previousMessage: fakeParentAction.message, + message: [ + { + translationKey: '', + type: 'COMMENT', + html: '', + text: '', + isEdited: true, + isDeletedParentAction: true, + }, + ], + errors: null, + linkMetaData: [], + }, + } as unknown as ReportActions; + + await Onyx.multiSet({ + ...mockedReportActionsMap, + }); + await waitForBatchedUpdates(); + await measureFunction(() => ReportActionsUtils.getLastVisibleMessage(reportId, actionsToMerge), {runs}); +}); + +test('getSortedReportActionsForDisplay on 10k ReportActions', async () => { + await Onyx.multiSet({ + ...mockedReportActionsMap, + }); + await waitForBatchedUpdates(); + await measureFunction(() => ReportActionsUtils.getSortedReportActionsForDisplay(reportActions), {runs}); +}); + +test('getLastClosedReportAction on 10k ReportActions', async () => { + await Onyx.multiSet({ + ...mockedReportActionsMap, + }); + await waitForBatchedUpdates(); + await measureFunction(() => ReportActionsUtils.getLastClosedReportAction(reportActions), {runs}); +}); + +test('getMostRecentReportActionLastModified', async () => { + await Onyx.multiSet({ + ...mockedReportActionsMap, + }); + await waitForBatchedUpdates(); + await measureFunction(() => ReportActionsUtils.getMostRecentReportActionLastModified(), {runs}); +}); diff --git a/tests/perf-test/ReportScreen.perf-test.js b/tests/perf-test/ReportScreen.perf-test.js index 20af5603d177..b901888bba0f 100644 --- a/tests/perf-test/ReportScreen.perf-test.js +++ b/tests/perf-test/ReportScreen.perf-test.js @@ -155,7 +155,7 @@ test('should render ReportScreen with composer interactions', () => { }; const report = LHNTestUtils.getFakeReport(); - const reportActions = ReportTestUtils.getMockedReportsMap(1000); + const reportActions = ReportTestUtils.getMockedReportActionsMap(1000); const mockRoute = {params: {reportID: '1'}}; return waitForBatchedUpdates() @@ -198,7 +198,7 @@ test('should press of the report item', () => { }; const report = LHNTestUtils.getFakeReport(); - const reportActions = ReportTestUtils.getMockedReportsMap(1000); + const reportActions = ReportTestUtils.getMockedReportActionsMap(1000); const mockRoute = {params: {reportID: '2'}}; return waitForBatchedUpdates() diff --git a/tests/utils/ReportTestUtils.js b/tests/utils/ReportTestUtils.js index 48e5ebfaa56d..910f2200876b 100644 --- a/tests/utils/ReportTestUtils.js +++ b/tests/utils/ReportTestUtils.js @@ -1,6 +1,6 @@ import _ from 'underscore'; -const actionNames = ['ADDCOMMENT', 'IOU', 'REPORTPREVIEW']; +const actionNames = ['ADDCOMMENT', 'IOU', 'REPORTPREVIEW', 'CLOSED']; const getFakeReportAction = (index, actionName) => ({ actionName, @@ -47,7 +47,7 @@ const getFakeReportAction = (index, actionName) => ({ const getMockedSortedReportActions = (length = 100) => Array.from({length}, (__, i) => getFakeReportAction(i)); -const getMockedReportsMap = (length = 100) => { +const getMockedReportActionsMap = (length = 100) => { const mockReports = Array.from({length}, (__, i) => { const reportID = i + 1; const actionName = i === 0 ? 'CREATED' : actionNames[i % actionNames.length]; @@ -58,4 +58,4 @@ const getMockedReportsMap = (length = 100) => { return _.assign({}, ...mockReports); }; -export {getFakeReportAction, getMockedSortedReportActions, getMockedReportsMap}; +export {getFakeReportAction, getMockedSortedReportActions, getMockedReportActionsMap}; diff --git a/tests/utils/collections/reportActions.ts b/tests/utils/collections/reportActions.ts new file mode 100644 index 000000000000..abc83eb82abe --- /dev/null +++ b/tests/utils/collections/reportActions.ts @@ -0,0 +1,72 @@ +import {rand, randAggregation, randBoolean, randPastDate, randWord} from '@ngneat/falso'; +import CONST from '@src/CONST'; +import {ReportAction} from '@src/types/onyx'; + +type ActionType = keyof typeof CONST.REPORT.ACTIONS.TYPE; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const flattenActionNamesValues = (actionNames: any) => { + let result = [] as ActionType[]; + Object.keys(actionNames).forEach((key) => { + if (typeof actionNames[key] === 'object') { + result = result.concat(flattenActionNamesValues(actionNames[key])); + } else { + result.push(actionNames[key]); + } + }); + return result; +}; + +export default function createRandomReportAction(index: number): ReportAction { + return { + // we need to add any here because of the way we are generating random values + // eslint-disable-next-line @typescript-eslint/no-explicit-any + actionName: rand(flattenActionNamesValues(CONST.REPORT.ACTIONS.TYPE)) as any, + reportActionID: index.toString(), + previousReportActionID: index.toString(), + actorAccountID: index, + person: [ + { + type: randWord(), + style: randWord(), + text: randWord(), + }, + ], + created: randPastDate().toISOString(), + message: [ + { + type: randWord(), + html: randWord(), + style: randWord(), + text: randWord(), + isEdited: randBoolean(), + isDeletedParentAction: randBoolean(), + whisperedTo: randAggregation(), + reactions: [ + { + emoji: randWord(), + users: [ + { + accountID: index, + skinTone: index, + }, + ], + }, + ], + }, + ], + originalMessage: { + html: randWord(), + type: rand(Object.values(CONST.IOU.REPORT_ACTION_TYPE)), + }, + whisperedToAccountIDs: randAggregation(), + avatar: randWord(), + automatic: randBoolean(), + shouldShow: randBoolean(), + lastModified: randPastDate().toISOString(), + pendingAction: rand(Object.values(CONST.RED_BRICK_ROAD_PENDING_ACTION)), + delegateAccountID: index.toString(), + errors: {}, + isAttachment: randBoolean(), + }; +}