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(),
+ };
+}