diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 4f78ee6b69bf..e9e4da575b4b 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -1,2 +1,6 @@
# Every PR gets a review from an internal Expensify engineer
* @Expensify/pullerbear
+
+# Assign the Design team to review changes to our styles & assets
+src/styles/ @Expensify/design @Expensify/pullerbear
+assets/ @Expensify/design @Expensify/pullerbear
diff --git a/.github/workflows/reassurePerformanceTests.yml b/.github/workflows/reassurePerformanceTests.yml
index 9887943c77e0..98f876fd60e2 100644
--- a/.github/workflows/reassurePerformanceTests.yml
+++ b/.github/workflows/reassurePerformanceTests.yml
@@ -1,8 +1,11 @@
name: Reassure Performance Tests
on:
+ push:
+ branches: [main]
+ paths-ignore: [docs/**, contributingGuides/**, jest/**, workflow_tests/**]
pull_request:
- types: [opened, synchronize, closed]
+ types: [opened, synchronize]
branches-ignore: [staging, production]
paths-ignore: [docs/**, .github/**, contributingGuides/**, tests/**, workflow_tests/**, '**.md', '**.sh']
diff --git a/android/app/build.gradle b/android/app/build.gradle
index cb7056283750..971d3c215aca 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -98,8 +98,8 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
multiDexEnabled rootProject.ext.multiDexEnabled
- versionCode 1001045808
- versionName "1.4.58-8"
+ versionCode 1001045900
+ versionName "1.4.59-0"
}
flavorDimensions "default"
diff --git a/android/app/src/main/java/com/expensify/chat/MainActivity.kt b/android/app/src/main/java/com/expensify/chat/MainActivity.kt
index 935ba8c8825f..2daebb9b1c00 100644
--- a/android/app/src/main/java/com/expensify/chat/MainActivity.kt
+++ b/android/app/src/main/java/com/expensify/chat/MainActivity.kt
@@ -14,6 +14,8 @@ import com.facebook.react.ReactActivityDelegate
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled
import com.facebook.react.defaults.DefaultReactActivityDelegate
+import com.oblador.performance.RNPerformance
+
class MainActivity : ReactActivity() {
/**
* Returns the name of the main component registered from JavaScript. This is used to schedule
@@ -82,4 +84,9 @@ class MainActivity : ReactActivity() {
KeyCommandModule.getInstance().onKeyDownEvent(keyCode, event)
return super.onKeyUp(keyCode, event)
}
+
+ override fun onStart() {
+ super.onStart()
+ RNPerformance.getInstance().mark("appCreationEnd", false);
+ }
}
diff --git a/android/app/src/main/java/com/expensify/chat/MainApplication.kt b/android/app/src/main/java/com/expensify/chat/MainApplication.kt
index 2362af009979..e660a871359d 100644
--- a/android/app/src/main/java/com/expensify/chat/MainApplication.kt
+++ b/android/app/src/main/java/com/expensify/chat/MainApplication.kt
@@ -15,6 +15,7 @@ import com.facebook.react.defaults.DefaultReactNativeHost
import com.facebook.react.modules.i18nmanager.I18nUtil
import com.facebook.soloader.SoLoader
import com.google.firebase.crashlytics.FirebaseCrashlytics
+import com.oblador.performance.RNPerformance
import expo.modules.ApplicationLifecycleDispatcher
import expo.modules.ReactNativeHostWrapper
@@ -42,6 +43,8 @@ class MainApplication : MultiDexApplication(), ReactApplication {
override fun onCreate() {
super.onCreate()
+ RNPerformance.getInstance().mark("appCreationStart", false);
+
if (isOnfidoProcess()) {
return
}
diff --git a/docs/redirects.csv b/docs/redirects.csv
index f239d95187c8..9c12c4b0048a 100644
--- a/docs/redirects.csv
+++ b/docs/redirects.csv
@@ -67,6 +67,12 @@ https://help.expensify.com/articles/expensify-classic/settings/Merge-Accounts,ht
https://help.expensify.com/articles/expensify-classic/settings/Preferences,https://help.expensify.com/expensify-classic/hubs/settings/account-settings
https://help.expensify.com/articles/expensify-classic/getting-started/support/Your-Expensify-Account-Manager,https://use.expensify.com/support
https://help.expensify.com/articles/expensify-classic/settings/Copilot,https://help.expensify.com/expensify-classic/hubs/copilots-and-delegates/
+https://help.expensify.com/articles/expensify-classic/workspace-and-domain-settings/reports/Currency,https://help.expensify.com/articles/expensify-classic/reports/Currency
+https://help.expensify.com/articles/expensify-classic/workspace-and-domain-settings/reports/Report-Fields-And-Titles,https://help.expensify.com/articles/expensify-classic/workspaces/reports/Report-Fields-And-Titles
+https://help.expensify.com/articles/expensify-classic/workspace-and-domain-settings/reports/Scheduled-Submit,https://help.expensify.com/articles/expensify-classic/workspaces/reports/Scheduled-Submit
+https://help.expensify.com/articles/expensify-classic/workspace-and-domain-settings/Categories,https://help.expensify.com/articles/expensify-classic/workspaces/Categori
+https://help.expensify.com/articles/expensify-classic/workspace-and-domain-settings/Expenses,https://help.expensify.com/expensify-classic/hubs/expenses/
+https://help.expensify.com/articles/expensify-classic/workspace-and-domain-settings/Per-Diem,https://help.expensify.com/articles/expensify-classic/expenses/Per-Diem-Expenses
https://help.expensify.com/articles/expensify-classic/workspace-and-domain-settings/Budgets,https://help.expensify.com/articles/expensify-classic/workspaces/Budgets
https://help.expensify.com/articles/expensify-classic/workspace-and-domain-settings/Reimbursement,https://help.expensify.com/articles/expensify-classic/send-payments/Reimbursing-Reports
https://help.expensify.com/articles/expensify-classic/workspace-and-domain-settings/Tags,https://help.expensify.com/articles/expensify-classic/workspaces/Tags
diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist
index 7711fe679b3f..4964185c64e1 100644
--- a/ios/NewExpensify/Info.plist
+++ b/ios/NewExpensify/Info.plist
@@ -19,7 +19,7 @@
CFBundlePackageType
APPL
CFBundleShortVersionString
- 1.4.58
+ 1.4.59
CFBundleSignature
????
CFBundleURLTypes
@@ -40,7 +40,7 @@
CFBundleVersion
- 1.4.58.8
+ 1.4.59.0
ITSAppUsesNonExemptEncryption
LSApplicationQueriesSchemes
diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist
index 3e503e2c1a08..254c69a48fb5 100644
--- a/ios/NewExpensifyTests/Info.plist
+++ b/ios/NewExpensifyTests/Info.plist
@@ -15,10 +15,10 @@
CFBundlePackageType
BNDL
CFBundleShortVersionString
- 1.4.58
+ 1.4.59
CFBundleSignature
????
CFBundleVersion
- 1.4.58.8
+ 1.4.59.0
diff --git a/ios/NotificationServiceExtension/Info.plist b/ios/NotificationServiceExtension/Info.plist
index 6f5a7fa2c754..e9b6cf9365f2 100644
--- a/ios/NotificationServiceExtension/Info.plist
+++ b/ios/NotificationServiceExtension/Info.plist
@@ -11,9 +11,9 @@
CFBundleName
$(PRODUCT_NAME)
CFBundleShortVersionString
- 1.4.58
+ 1.4.59
CFBundleVersion
- 1.4.58.8
+ 1.4.59.0
NSExtension
NSExtensionPointIdentifier
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index b2995c35c9bc..24ef0704be25 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -1415,7 +1415,7 @@ PODS:
- SDWebImage/Core (~> 5.17)
- SocketRocket (0.6.1)
- Turf (2.7.0)
- - VisionCamera (2.16.8):
+ - VisionCamera (4.0.0-beta.11):
- React
- React-callinvoker
- React-Core
@@ -1920,7 +1920,7 @@ SPEC CHECKSUMS:
SDWebImageWebPCoder: af09429398d99d524cae2fe00f6f0f6e491ed102
SocketRocket: f32cd54efbe0f095c4d7594881e52619cfe80b17
Turf: 13d1a92d969ca0311bbc26e8356cca178ce95da2
- VisionCamera: 0a6794d1974aed5d653d0d0cb900493e2583e35a
+ VisionCamera: b6b6f46949eae83b71429c971162af337ef34fa3
Yoga: e64aa65de36c0832d04e8c7bd614396c77a80047
PODFILE CHECKSUM: a431c146e1501391834a2f299a74093bac53b530
diff --git a/package-lock.json b/package-lock.json
index c55667a21424..96316e4c617c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "new.expensify",
- "version": "1.4.58-8",
+ "version": "1.4.59-0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "new.expensify",
- "version": "1.4.58-8",
+ "version": "1.4.59-0",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
@@ -114,7 +114,7 @@
"react-native-tab-view": "^3.5.2",
"react-native-url-polyfill": "^2.0.0",
"react-native-view-shot": "3.8.0",
- "react-native-vision-camera": "2.16.8",
+ "react-native-vision-camera": "^4.0.0-beta.11",
"react-native-web": "^0.19.9",
"react-native-web-linear-gradient": "^1.1.2",
"react-native-web-sound": "^0.1.3",
@@ -39769,11 +39769,18 @@
}
},
"node_modules/react-native-vision-camera": {
- "version": "2.16.8",
- "license": "MIT",
+ "version": "4.0.0-beta.11",
+ "resolved": "https://registry.npmjs.org/react-native-vision-camera/-/react-native-vision-camera-4.0.0-beta.11.tgz",
+ "integrity": "sha512-cKg/nwT0q0H1ivEVG+PQt/QxhFgf/dd7SiMm7bCzlSCmt0T2tXyIdLsuY312iKBB2qasQZOYtzRsoQjipHQkDw==",
"peerDependencies": {
"react": "*",
- "react-native": "*"
+ "react-native": "*",
+ "react-native-worklets-core": "*"
+ },
+ "peerDependenciesMeta": {
+ "react-native-worklets-core": {
+ "optional": true
+ }
}
},
"node_modules/react-native-web": {
diff --git a/package.json b/package.json
index cfb98a2f317d..8f766d0ba1b0 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "new.expensify",
- "version": "1.4.58-8",
+ "version": "1.4.59-0",
"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.",
@@ -165,7 +165,7 @@
"react-native-tab-view": "^3.5.2",
"react-native-url-polyfill": "^2.0.0",
"react-native-view-shot": "3.8.0",
- "react-native-vision-camera": "2.16.8",
+ "react-native-vision-camera": "^4.0.0-beta.11",
"react-native-web": "^0.19.9",
"react-native-web-linear-gradient": "^1.1.2",
"react-native-web-sound": "^0.1.3",
diff --git a/patches/react-native-vision-camera+2.16.8.patch b/patches/react-native-vision-camera+2.16.8.patch
deleted file mode 100644
index 3afc4573985d..000000000000
--- a/patches/react-native-vision-camera+2.16.8.patch
+++ /dev/null
@@ -1,22 +0,0 @@
-diff --git a/node_modules/react-native-vision-camera/android/build.gradle b/node_modules/react-native-vision-camera/android/build.gradle
-index ddfa243..bafffc3 100644
---- a/node_modules/react-native-vision-camera/android/build.gradle
-+++ b/node_modules/react-native-vision-camera/android/build.gradle
-@@ -334,7 +334,7 @@ if (ENABLE_FRAME_PROCESSORS) {
- def thirdPartyVersions = new Properties()
- thirdPartyVersions.load(new FileInputStream(thirdPartyVersionsFile))
-
-- def BOOST_VERSION = thirdPartyVersions["BOOST_VERSION"]
-+ def BOOST_VERSION = thirdPartyVersions["BOOST_VERSION"] ?: "1.83.0"
- def boost_file = new File(downloadsDir, "boost_${BOOST_VERSION}.tar.gz")
- def DOUBLE_CONVERSION_VERSION = thirdPartyVersions["DOUBLE_CONVERSION_VERSION"]
- def double_conversion_file = new File(downloadsDir, "double-conversion-${DOUBLE_CONVERSION_VERSION}.tar.gz")
-@@ -352,7 +352,7 @@ if (ENABLE_FRAME_PROCESSORS) {
-
- task downloadBoost(dependsOn: createNativeDepsDirectories, type: Download) {
- def transformedVersion = BOOST_VERSION.replace("_", ".")
-- def srcUrl = "https://boostorg.jfrog.io/artifactory/main/release/${transformedVersion}/source/boost_${BOOST_VERSION}.tar.gz"
-+ def srcUrl = "https://archives.boost.io/release/${transformedVersion}/source/boost_${BOOST_VERSION}.tar.gz"
- if (REACT_NATIVE_VERSION < 69) {
- srcUrl = "https://github.com/react-native-community/boost-for-react-native/releases/download/v${transformedVersion}-0/boost_${BOOST_VERSION}.tar.gz"
- }
diff --git a/src/Expensify.tsx b/src/Expensify.tsx
index 026025593aef..6a57d6fdcc10 100644
--- a/src/Expensify.tsx
+++ b/src/Expensify.tsx
@@ -26,6 +26,7 @@ import NavigationRoot from './libs/Navigation/NavigationRoot';
import NetworkConnection from './libs/NetworkConnection';
import PushNotification from './libs/Notification/PushNotification';
import './libs/Notification/PushNotification/subscribePushNotification';
+import Performance from './libs/Performance';
import StartupTimer from './libs/StartupTimer';
// This lib needs to be imported, but it has nothing to export since all it contains is an Onyx connection
import './libs/UnreadIndicatorUpdater';
@@ -130,6 +131,7 @@ function Expensify({
const onSplashHide = useCallback(() => {
setIsSplashHidden(true);
+ Performance.markEnd(CONST.TIMING.SIDEBAR_LOADED);
}, []);
useLayoutEffect(() => {
diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts
index c134d2a65db2..46b2c5f8055c 100755
--- a/src/ONYXKEYS.ts
+++ b/src/ONYXKEYS.ts
@@ -632,7 +632,7 @@ type OnyxValuesMapping = {
[ONYXKEYS.RECENTLY_USED_REPORT_FIELDS]: OnyxTypes.RecentlyUsedReportFields;
[ONYXKEYS.UPDATE_REQUIRED]: boolean;
[ONYXKEYS.PLAID_CURRENT_EVENT]: string;
- [ONYXKEYS.LOGS]: Record;
+ [ONYXKEYS.LOGS]: OnyxTypes.CapturedLogs;
[ONYXKEYS.SHOULD_STORE_LOGS]: boolean;
[ONYXKEYS.CACHED_PDF_PATHS]: Record;
[ONYXKEYS.POLICY_OWNERSHIP_CHANGE_CHECKS]: Record;
diff --git a/src/components/ClientSideLoggingToolMenu/BaseClientSideLoggingToolMenu.tsx b/src/components/ClientSideLoggingToolMenu/BaseClientSideLoggingToolMenu.tsx
new file mode 100644
index 000000000000..fcad770908a6
--- /dev/null
+++ b/src/components/ClientSideLoggingToolMenu/BaseClientSideLoggingToolMenu.tsx
@@ -0,0 +1,96 @@
+import React from 'react';
+import {Alert} from 'react-native';
+import {withOnyx} from 'react-native-onyx';
+import type {OnyxEntry} from 'react-native-onyx';
+import Button from '@components/Button';
+import Switch from '@components/Switch';
+import TestToolRow from '@components/TestToolRow';
+import Text from '@components/Text';
+import useLocalize from '@hooks/useLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
+import * as Console from '@libs/actions/Console';
+import {parseStringifiedMessages} from '@libs/Console';
+import ONYXKEYS from '@src/ONYXKEYS';
+import type {CapturedLogs, Log} from '@src/types/onyx';
+
+type BaseClientSideLoggingToolMenuOnyxProps = {
+ /** Logs captured on the current device */
+ capturedLogs: OnyxEntry;
+
+ /** Whether or not logs should be stored */
+ shouldStoreLogs: OnyxEntry;
+};
+
+type BaseClientSideLoggingToolProps = {
+ /** Locally created file */
+ file?: {path: string; newFileName: string; size: number};
+ /** Action to run when pressing Share button */
+ onShareLogs?: () => void;
+ /** Action to run when disabling the switch */
+ onDisableLogging: (logs: Log[]) => void;
+ /** Action to run when enabling logging */
+ onEnableLogging?: () => void;
+} & BaseClientSideLoggingToolMenuOnyxProps;
+
+function BaseClientSideLoggingToolMenu({shouldStoreLogs, capturedLogs, file, onShareLogs, onDisableLogging, onEnableLogging}: BaseClientSideLoggingToolProps) {
+ const {translate} = useLocalize();
+
+ const onToggle = () => {
+ if (!shouldStoreLogs) {
+ Console.setShouldStoreLogs(true);
+
+ if (onEnableLogging) {
+ onEnableLogging();
+ }
+
+ return;
+ }
+
+ if (!capturedLogs) {
+ Alert.alert(translate('initialSettingsPage.troubleshoot.noLogsToShare'));
+ Console.disableLoggingAndFlushLogs();
+ return;
+ }
+
+ const logs = Object.values(capturedLogs);
+ const logsWithParsedMessages = parseStringifiedMessages(logs);
+
+ onDisableLogging(logsWithParsedMessages);
+ Console.disableLoggingAndFlushLogs();
+ };
+ const styles = useThemeStyles();
+ return (
+ <>
+
+
+
+ {!!file && (
+ <>
+ {`path: ${file.path}`}
+
+
+
+ >
+ )}
+ >
+ );
+}
+
+BaseClientSideLoggingToolMenu.displayName = 'BaseClientSideLoggingToolMenu';
+
+export default withOnyx({
+ capturedLogs: {
+ key: ONYXKEYS.LOGS,
+ },
+ shouldStoreLogs: {
+ key: ONYXKEYS.SHOULD_STORE_LOGS,
+ },
+})(BaseClientSideLoggingToolMenu);
diff --git a/src/components/ClientSideLoggingToolMenu/index.android.tsx b/src/components/ClientSideLoggingToolMenu/index.android.tsx
new file mode 100644
index 000000000000..0be6e96fcafe
--- /dev/null
+++ b/src/components/ClientSideLoggingToolMenu/index.android.tsx
@@ -0,0 +1,47 @@
+import React, {useState} from 'react';
+import RNFetchBlob from 'react-native-blob-util';
+import Share from 'react-native-share';
+import type {Log} from '@libs/Console';
+import localFileCreate from '@libs/localFileCreate';
+import BaseClientSideLoggingToolMenu from './BaseClientSideLoggingToolMenu';
+
+function ClientSideLoggingToolMenu() {
+ const [file, setFile] = useState<{path: string; newFileName: string; size: number}>();
+
+ const createAndSaveFile = (logs: Log[]) => {
+ localFileCreate('logs', JSON.stringify(logs, null, 2)).then((localFile) => {
+ RNFetchBlob.MediaCollection.copyToMediaStore(
+ {
+ name: localFile.newFileName,
+ parentFolder: '',
+ mimeType: 'text/plain',
+ },
+ 'Download',
+ localFile.path,
+ );
+ setFile(localFile);
+ });
+ };
+
+ const shareLogs = () => {
+ if (!file) {
+ return;
+ }
+ Share.open({
+ url: `file://${file.path}`,
+ });
+ };
+
+ return (
+ setFile(undefined)}
+ onDisableLogging={createAndSaveFile}
+ onShareLogs={shareLogs}
+ />
+ );
+}
+
+ClientSideLoggingToolMenu.displayName = 'ClientSideLoggingToolMenu';
+
+export default ClientSideLoggingToolMenu;
diff --git a/src/components/ClientSideLoggingToolMenu/index.ios.tsx b/src/components/ClientSideLoggingToolMenu/index.ios.tsx
new file mode 100644
index 000000000000..cc596e54a973
--- /dev/null
+++ b/src/components/ClientSideLoggingToolMenu/index.ios.tsx
@@ -0,0 +1,37 @@
+import React, {useState} from 'react';
+import Share from 'react-native-share';
+import type {Log} from '@libs/Console';
+import localFileCreate from '@libs/localFileCreate';
+import BaseClientSideLoggingToolMenu from './BaseClientSideLoggingToolMenu';
+
+function ClientSideLoggingToolMenu() {
+ const [file, setFile] = useState<{path: string; newFileName: string; size: number}>();
+
+ const createFile = (logs: Log[]) => {
+ localFileCreate('logs', JSON.stringify(logs, null, 2)).then((localFile) => {
+ setFile(localFile);
+ });
+ };
+
+ const shareLogs = () => {
+ if (!file) {
+ return;
+ }
+ Share.open({
+ url: `file://${file.path}`,
+ });
+ };
+
+ return (
+ setFile(undefined)}
+ onDisableLogging={createFile}
+ onShareLogs={shareLogs}
+ />
+ );
+}
+
+ClientSideLoggingToolMenu.displayName = 'ClientSideLoggingToolMenu';
+
+export default ClientSideLoggingToolMenu;
diff --git a/src/components/ClientSideLoggingToolMenu/index.tsx b/src/components/ClientSideLoggingToolMenu/index.tsx
new file mode 100644
index 000000000000..182c3bf99b74
--- /dev/null
+++ b/src/components/ClientSideLoggingToolMenu/index.tsx
@@ -0,0 +1,16 @@
+import React from 'react';
+import type {Log} from '@libs/Console';
+import localFileDownload from '@libs/localFileDownload';
+import BaseClientSideLoggingToolMenu from './BaseClientSideLoggingToolMenu';
+
+function ClientSideLoggingToolMenu() {
+ const downloadFile = (logs: Log[]) => {
+ localFileDownload('logs', JSON.stringify(logs, null, 2));
+ };
+
+ return ;
+}
+
+ClientSideLoggingToolMenu.displayName = 'ClientSideLoggingToolMenu';
+
+export default ClientSideLoggingToolMenu;
diff --git a/src/components/ProfilingToolMenu/index.native.tsx b/src/components/ProfilingToolMenu/index.native.tsx
index e6a89a317ac7..a202a39032ba 100644
--- a/src/components/ProfilingToolMenu/index.native.tsx
+++ b/src/components/ProfilingToolMenu/index.native.tsx
@@ -9,6 +9,7 @@ import Button from '@components/Button';
import Switch from '@components/Switch';
import TestToolRow from '@components/TestToolRow';
import Text from '@components/Text';
+import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import toggleProfileTool from '@libs/actions/ProfilingTool';
import getPlatform from '@libs/getPlatform';
@@ -44,6 +45,7 @@ function ProfilingToolMenu({isProfilingInProgress = false}: ProfilingToolMenuPro
const [sharePath, setSharePath] = useState('');
const [totalMemory, setTotalMemory] = useState(0);
const [usedMemory, setUsedMemory] = useState(0);
+ const {translate} = useLocalize();
// eslint-disable-next-line @lwc/lwc/no-async-await
const stop = useCallback(async () => {
@@ -142,29 +144,24 @@ function ProfilingToolMenu({isProfilingInProgress = false}: ProfilingToolMenuPro
return (
<>
-
- Release options
-
-
-
+
- {!!pathIOS && `path: ${pathIOS}`}
{!!pathIOS && (
-
-
-
+ <>
+ {`path: ${pathIOS}`}
+
+
+
+ >
)}
>
);
diff --git a/src/components/SelectionList/BaseSelectionList.tsx b/src/components/SelectionList/BaseSelectionList.tsx
index f3efee4045cf..e40a62377989 100644
--- a/src/components/SelectionList/BaseSelectionList.tsx
+++ b/src/components/SelectionList/BaseSelectionList.tsx
@@ -70,6 +70,7 @@ function BaseSelectionList(
listHeaderWrapperStyle,
isRowMultilineSupported = false,
textInputRef,
+ headerMessageStyle,
}: BaseSelectionListProps,
ref: ForwardedRef,
) {
@@ -516,7 +517,7 @@ function BaseSelectionList(
)}
{!!headerMessage && (
-
+
{headerMessage}
)}
diff --git a/src/components/SelectionList/types.ts b/src/components/SelectionList/types.ts
index 88a864af2728..dfad7dc8860b 100644
--- a/src/components/SelectionList/types.ts
+++ b/src/components/SelectionList/types.ts
@@ -231,6 +231,9 @@ type BaseSelectionListProps = Partial & {
/** Message to display at the top of the list */
headerMessage?: string;
+ /** Styles to apply to the header message */
+ headerMessageStyle?: StyleProp;
+
/** Text to display on the confirm button */
confirmButtonText?: string;
diff --git a/src/components/TestToolsModal.tsx b/src/components/TestToolsModal.tsx
index 824162e63c51..026eafc7a13d 100644
--- a/src/components/TestToolsModal.tsx
+++ b/src/components/TestToolsModal.tsx
@@ -3,14 +3,18 @@ import {View} from 'react-native';
import type {OnyxEntry} from 'react-native-onyx';
import {withOnyx} from 'react-native-onyx';
import useEnvironment from '@hooks/useEnvironment';
+import useLocalize from '@hooks/useLocalize';
import useStyleUtils from '@hooks/useStyleUtils';
+import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
import toggleTestToolsModal from '@userActions/TestTool';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
+import ClientSideLoggingToolMenu from './ClientSideLoggingToolMenu';
import Modal from './Modal';
import ProfilingToolMenu from './ProfilingToolMenu';
import TestToolMenu from './TestToolMenu';
+import Text from './Text';
type TestToolsModalOnyxProps = {
/** Whether the test tools modal is open */
@@ -23,6 +27,8 @@ function TestToolsModal({isTestToolsModalOpen = false}: TestToolsModalProps) {
const {isDevelopment} = useEnvironment();
const {windowWidth} = useWindowDimensions();
const StyleUtils = useStyleUtils();
+ const styles = useThemeStyles();
+ const {translate} = useLocalize();
return (
{isDevelopment && }
+
+ {translate('initialSettingsPage.troubleshoot.releaseOptions')}
+
+
);
diff --git a/src/languages/en.ts b/src/languages/en.ts
index fd2daa50942f..752d17e37d03 100755
--- a/src/languages/en.ts
+++ b/src/languages/en.ts
@@ -891,6 +891,11 @@ export default {
submitBug: 'submit a bug',
confirmResetDescription: 'All unsent draft messages will be lost, but the rest of your data is safe.',
resetAndRefresh: 'Reset and refresh',
+ clientSideLogging: 'Client side logging',
+ noLogsToShare: 'No logs to share',
+ useProfiling: 'Use profiling',
+ profileTrace: 'Profile trace',
+ releaseOptions: 'Release options',
},
debugConsole: {
saveLog: 'Save log',
@@ -899,6 +904,7 @@ export default {
execute: 'Execute',
noLogsAvailable: 'No logs available',
logSizeTooLarge: ({size}: LogSizeParams) => `Log size exceeds the limit of ${size} MB. Please use "Save log" to download the log file instead.`,
+ logs: 'Logs',
},
security: 'Security',
signOut: 'Sign out',
diff --git a/src/languages/es.ts b/src/languages/es.ts
index 1482ad5e0c5c..53ee6d3fba79 100644
--- a/src/languages/es.ts
+++ b/src/languages/es.ts
@@ -889,6 +889,11 @@ export default {
submitBug: 'envíe un error',
confirmResetDescription: 'Todos los borradores no enviados se perderán, pero el resto de tus datos estarán a salvo.',
resetAndRefresh: 'Restablecer y actualizar',
+ clientSideLogging: 'Logs del cliente',
+ noLogsToShare: 'No hay logs que compartir',
+ useProfiling: 'Usar el trazado',
+ profileTrace: 'Traza de ejecución',
+ releaseOptions: 'Opciones de publicación',
},
debugConsole: {
saveLog: 'Guardar registro',
@@ -897,6 +902,7 @@ export default {
execute: 'Ejecutar',
noLogsAvailable: 'No hay registros disponibles',
logSizeTooLarge: ({size}: LogSizeParams) => `El tamaño del registro excede el límite de ${size} MB. Utilice "Guardar registro" para descargar el archivo de registro.`,
+ logs: 'Logs',
},
security: 'Seguridad',
restoreStashed: 'Restablecer login guardado',
diff --git a/src/libs/Console/index.ts b/src/libs/Console/index.ts
index bf7aa246aaf4..423e1037a605 100644
--- a/src/libs/Console/index.ts
+++ b/src/libs/Console/index.ts
@@ -1,4 +1,5 @@
/* eslint-disable @typescript-eslint/naming-convention */
+import isEmpty from 'lodash/isEmpty';
import Onyx from 'react-native-onyx';
import {addLog} from '@libs/actions/Console';
import CONFIG from '@src/CONFIG';
@@ -118,5 +119,29 @@ function createLog(text: string) {
}
}
-export {sanitizeConsoleInput, createLog, shouldAttachLog};
+/**
+ * Loops through all the logs and parses the message if it's a stringified JSON
+ * @param logs Logs captured on the current device
+ * @returns CapturedLogs with parsed messages
+ */
+function parseStringifiedMessages(logs: Log[]): Log[] {
+ if (isEmpty(logs)) {
+ return logs;
+ }
+
+ return logs.map((log) => {
+ try {
+ const parsedMessage = JSON.parse(log.message);
+ return {
+ ...log,
+ message: parsedMessage,
+ };
+ } catch {
+ // If the message can't be parsed, just return the original log
+ return log;
+ }
+ });
+}
+
+export {sanitizeConsoleInput, createLog, shouldAttachLog, parseStringifiedMessages};
export type {Log};
diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts
index dac3a98f4d7c..b88c44b9aa70 100644
--- a/src/libs/Navigation/types.ts
+++ b/src/libs/Navigation/types.ts
@@ -357,47 +357,48 @@ type MoneyRequestNavigatorParamList = {
iouType: string;
reportID: string;
currency: string;
- backTo: string;
+ backTo: Routes;
};
[SCREENS.MONEY_REQUEST.STEP_DATE]: {
action: ValueOf;
iouType: ValueOf;
transactionID: string;
reportID: string;
- backTo: string;
+ backTo: Routes;
};
[SCREENS.MONEY_REQUEST.STEP_DESCRIPTION]: {
action: ValueOf;
iouType: ValueOf;
transactionID: string;
reportID: string;
- backTo: string;
+ backTo: Routes;
+ reportActionID: string;
};
[SCREENS.MONEY_REQUEST.STEP_CATEGORY]: {
action: ValueOf;
iouType: ValueOf;
transactionID: string;
reportID: string;
- backTo: string;
+ backTo: Routes;
};
[SCREENS.MONEY_REQUEST.STEP_TAX_AMOUNT]: {
iouType: string;
transactionID: string;
reportID: string;
- backTo: string;
+ backTo: Routes;
};
[SCREENS.MONEY_REQUEST.STEP_TAG]: {
action: ValueOf;
iouType: ValueOf;
transactionID: string;
reportID: string;
- backTo: string;
+ backTo: Routes;
};
[SCREENS.MONEY_REQUEST.STEP_TAX_RATE]: {
iouType: string;
transactionID: string;
reportID: string;
- backTo: string;
+ backTo: Routes;
};
[SCREENS.MONEY_REQUEST.STEP_WAYPOINT]: {
iouType: ValueOf;
@@ -412,7 +413,7 @@ type MoneyRequestNavigatorParamList = {
iouType: ValueOf;
transactionID: string;
reportID: string;
- backTo: string;
+ backTo: Routes;
};
[SCREENS.IOU_SEND.ENABLE_PAYMENTS]: undefined;
[SCREENS.IOU_SEND.ADD_BANK_ACCOUNT]: undefined;
@@ -428,7 +429,7 @@ type MoneyRequestNavigatorParamList = {
iouType: ValueOf;
transactionID: string;
reportID: string;
- backTo: string;
+ backTo: Routes;
};
[SCREENS.MONEY_REQUEST.RECEIPT]: {
iouType: string;
diff --git a/src/libs/Performance.tsx b/src/libs/Performance.tsx
index 8d812014dbf8..9302b538a621 100644
--- a/src/libs/Performance.tsx
+++ b/src/libs/Performance.tsx
@@ -134,6 +134,13 @@ if (Metrics.canCapturePerformanceMetrics()) {
if (entry.name === 'runJsBundleEnd') {
Performance.measureFailSafe('runJsBundle', 'runJsBundleStart', 'runJsBundleEnd');
}
+ if (entry.name === 'appCreationEnd') {
+ Performance.measureFailSafe('appCreation', 'appCreationStart', 'appCreationEnd');
+ Performance.measureFailSafe('nativeLaunchEnd_To_appCreationStart', 'nativeLaunchEnd', 'appCreationStart');
+ }
+ if (entry.name === 'contentAppeared') {
+ Performance.measureFailSafe('appCreationEnd_To_contentAppeared', 'appCreationEnd', 'contentAppeared');
+ }
// We don't need to keep the observer past this point
if (entry.name === 'runJsBundleEnd' || entry.name === 'downloadEnd') {
@@ -154,6 +161,7 @@ if (Metrics.canCapturePerformanceMetrics()) {
// Capture any custom measures or metrics below
if (mark.name === `${CONST.TIMING.SIDEBAR_LOADED}_end`) {
+ Performance.measureFailSafe('contentAppeared_To_screenTTI', 'contentAppeared', mark.name);
Performance.measureTTI(mark.name);
}
});
@@ -163,6 +171,10 @@ if (Metrics.canCapturePerformanceMetrics()) {
Performance.getPerformanceMetrics = (): PerformanceEntry[] =>
[
...rnPerformance.getEntriesByName('nativeLaunch'),
+ ...rnPerformance.getEntriesByName('nativeLaunchEnd_To_appCreationStart'),
+ ...rnPerformance.getEntriesByName('appCreation'),
+ ...rnPerformance.getEntriesByName('appCreationEnd_To_contentAppeared'),
+ ...rnPerformance.getEntriesByName('contentAppeared_To_screenTTI'),
...rnPerformance.getEntriesByName('runJsBundle'),
...rnPerformance.getEntriesByName('jsBundleDownload'),
...rnPerformance.getEntriesByName('TTI'),
diff --git a/src/libs/actions/App.ts b/src/libs/actions/App.ts
index 3a9ab4c52b3c..11bbc5c12f53 100644
--- a/src/libs/actions/App.ts
+++ b/src/libs/actions/App.ts
@@ -131,7 +131,6 @@ function setSidebarLoaded() {
}
Onyx.set(ONYXKEYS.IS_SIDEBAR_LOADED, true);
- Performance.markEnd(CONST.TIMING.SIDEBAR_LOADED);
Performance.markStart(CONST.TIMING.REPORT_INITIAL_RENDER);
}
diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts
index f99942a3d7a8..92e05996fe3d 100644
--- a/src/libs/actions/IOU.ts
+++ b/src/libs/actions/IOU.ts
@@ -4574,8 +4574,9 @@ function canApproveIOU(iouReport: OnyxEntry | EmptyObject, cha
const isOpenExpenseReport = isPolicyExpenseChat && ReportUtils.isOpenExpenseReport(iouReport);
const isApproved = ReportUtils.isReportApproved(iouReport);
const iouSettled = ReportUtils.isSettled(iouReport?.reportID);
+ const isArchivedReport = ReportUtils.isArchivedRoom(iouReport);
- return isCurrentUserManager && !isOpenExpenseReport && !isApproved && !iouSettled;
+ return isCurrentUserManager && !isOpenExpenseReport && !isApproved && !iouSettled && !isArchivedReport;
}
function canIOUBePaid(iouReport: OnyxEntry | EmptyObject, chatReport: OnyxEntry | EmptyObject, policy: OnyxEntry | EmptyObject) {
@@ -4733,35 +4734,48 @@ function submitReport(expenseReport: OnyxTypes.Report) {
const policy = ReportUtils.getPolicy(expenseReport.policyID);
const isCurrentUserManager = currentUserPersonalDetails.accountID === expenseReport.managerID;
const optimisticNextStep = NextStepUtils.buildNextStep(expenseReport, CONST.REPORT.STATUS_NUM.SUBMITTED);
+ const isSubmitAndClosePolicy = PolicyUtils.isSubmitAndClose(policy);
- const optimisticData: OnyxUpdate[] = [
- {
- onyxMethod: Onyx.METHOD.MERGE,
- key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseReport.reportID}`,
- value: {
- [optimisticSubmittedReportAction.reportActionID]: {
- ...(optimisticSubmittedReportAction as OnyxTypes.ReportAction),
- pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD,
- },
- },
- },
- {
- onyxMethod: Onyx.METHOD.MERGE,
- key: `${ONYXKEYS.COLLECTION.REPORT}${expenseReport.reportID}`,
- value: {
- ...expenseReport,
- lastMessageText: optimisticSubmittedReportAction.message?.[0].text ?? '',
- lastMessageHtml: optimisticSubmittedReportAction.message?.[0].html ?? '',
- stateNum: CONST.REPORT.STATE_NUM.SUBMITTED,
- statusNum: CONST.REPORT.STATUS_NUM.SUBMITTED,
- },
- },
- {
- onyxMethod: Onyx.METHOD.MERGE,
- key: `${ONYXKEYS.COLLECTION.NEXT_STEP}${expenseReport.reportID}`,
- value: optimisticNextStep,
- },
- ];
+ const optimisticData: OnyxUpdate[] = !isSubmitAndClosePolicy
+ ? [
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseReport.reportID}`,
+ value: {
+ [optimisticSubmittedReportAction.reportActionID]: {
+ ...(optimisticSubmittedReportAction as OnyxTypes.ReportAction),
+ pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD,
+ },
+ },
+ },
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.REPORT}${expenseReport.reportID}`,
+ value: {
+ ...expenseReport,
+ lastMessageText: optimisticSubmittedReportAction.message?.[0].text ?? '',
+ lastMessageHtml: optimisticSubmittedReportAction.message?.[0].html ?? '',
+ stateNum: CONST.REPORT.STATE_NUM.SUBMITTED,
+ statusNum: CONST.REPORT.STATUS_NUM.SUBMITTED,
+ },
+ },
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.NEXT_STEP}${expenseReport.reportID}`,
+ value: optimisticNextStep,
+ },
+ ]
+ : [
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.REPORT}${expenseReport.reportID}`,
+ value: {
+ ...expenseReport,
+ stateNum: CONST.REPORT.STATE_NUM.APPROVED,
+ statusNum: CONST.REPORT.STATUS_NUM.CLOSED,
+ },
+ },
+ ];
if (parentReport?.reportID) {
optimisticData.push({
@@ -4776,8 +4790,9 @@ function submitReport(expenseReport: OnyxTypes.Report) {
});
}
- const successData: OnyxUpdate[] = [
- {
+ const successData: OnyxUpdate[] = [];
+ if (!isSubmitAndClosePolicy) {
+ successData.push({
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseReport.reportID}`,
value: {
@@ -4785,19 +4800,10 @@ function submitReport(expenseReport: OnyxTypes.Report) {
pendingAction: null,
},
},
- },
- ];
+ });
+ }
const failureData: OnyxUpdate[] = [
- {
- onyxMethod: Onyx.METHOD.MERGE,
- key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseReport.reportID}`,
- value: {
- [optimisticSubmittedReportAction.reportActionID]: {
- errors: ErrorUtils.getMicroSecondOnyxError('iou.error.other'),
- },
- },
- },
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.REPORT}${expenseReport.reportID}`,
@@ -4806,12 +4812,25 @@ function submitReport(expenseReport: OnyxTypes.Report) {
stateNum: CONST.REPORT.STATE_NUM.OPEN,
},
},
- {
- onyxMethod: Onyx.METHOD.MERGE,
- key: `${ONYXKEYS.COLLECTION.NEXT_STEP}${expenseReport.reportID}`,
- value: currentNextStep,
- },
];
+ if (!isSubmitAndClosePolicy) {
+ failureData.push(
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseReport.reportID}`,
+ value: {
+ [optimisticSubmittedReportAction.reportActionID]: {
+ errors: ErrorUtils.getMicroSecondOnyxError('iou.error.other'),
+ },
+ },
+ },
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.NEXT_STEP}${expenseReport.reportID}`,
+ value: currentNextStep,
+ },
+ );
+ }
if (parentReport?.reportID) {
failureData.push({
diff --git a/src/pages/SearchPage/index.tsx b/src/pages/SearchPage/index.tsx
index 4f87c14af07a..d1d7146c6aba 100644
--- a/src/pages/SearchPage/index.tsx
+++ b/src/pages/SearchPage/index.tsx
@@ -161,6 +161,7 @@ function SearchPage({betas, reports, isSearchingForReports, navigation}: SearchP
textInputHint={offlineMessage}
onChangeText={setSearchValue}
headerMessage={headerMessage}
+ headerMessageStyle={headerMessage === translate('common.noResultsFound') ? [themeStyles.ph4, themeStyles.pb5] : undefined}
onLayout={setPerformanceTimersEnd}
autoFocus
onSelectRow={selectReport}
diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx
index 152b02366227..eb24109d5229 100644
--- a/src/pages/home/ReportScreen.tsx
+++ b/src/pages/home/ReportScreen.tsx
@@ -283,6 +283,10 @@ function ReportScreen({
const screenWrapperStyle: ViewStyle[] = [styles.appContent, styles.flex1, {marginTop: viewportOffsetTop}];
const isEmptyChat = useMemo((): boolean => reportActions.length === 0, [reportActions]);
const isOptimisticDelete = report.statusNum === CONST.REPORT.STATUS_NUM.CLOSED;
+ const isLinkedMessageAvailable = useMemo(
+ (): boolean => sortedAllReportActions.findIndex((obj) => String(obj.reportActionID) === String(reportActionIDFromRoute)) > -1,
+ [sortedAllReportActions, reportActionIDFromRoute],
+ );
// If there's a non-404 error for the report we should show it instead of blocking the screen
const hasHelpfulErrors = Object.keys(report?.errorFields ?? {}).some((key) => key !== 'notFound');
@@ -351,11 +355,12 @@ function ReportScreen({
const isLoading = !ReportUtils.isValidReportIDFromPath(reportIDFromRoute) || !isSidebarLoaded || PersonalDetailsUtils.isPersonalDetailsEmpty();
const shouldShowSkeleton =
- isLinkingToMessage ||
- !isCurrentReportLoadedFromOnyx ||
- (reportActions.length === 0 && !!reportMetadata?.isLoadingInitialReportActions) ||
- isLoading ||
- (!!reportActionIDFromRoute && reportMetadata?.isLoadingInitialReportActions);
+ !isLinkedMessageAvailable &&
+ (isLinkingToMessage ||
+ !isCurrentReportLoadedFromOnyx ||
+ (reportActions.length === 0 && !!reportMetadata?.isLoadingInitialReportActions) ||
+ isLoading ||
+ (!!reportActionIDFromRoute && reportMetadata?.isLoadingInitialReportActions));
const shouldShowReportActionList = isCurrentReportLoadedFromOnyx && !isLoading;
// eslint-disable-next-line rulesdir/no-negated-variables
const shouldShowNotFoundPage = useMemo(
diff --git a/src/pages/home/report/ReportActionsView.tsx b/src/pages/home/report/ReportActionsView.tsx
index 1b6a9614a466..d5142313329a 100755
--- a/src/pages/home/report/ReportActionsView.tsx
+++ b/src/pages/home/report/ReportActionsView.tsx
@@ -142,7 +142,7 @@ function ReportActionsView({
listOldID = newID;
return newID;
// eslint-disable-next-line react-hooks/exhaustive-deps
- }, [route, isLoadingInitialReportActions]);
+ }, [route, isLoadingInitialReportActions, reportActionID]);
// Get a sorted array of reportActions for both the current report and the transaction thread report associated with this report (if there is one)
// so that we display transaction-level and report-level report actions in order in the one-transaction view
@@ -162,18 +162,16 @@ function ReportActionsView({
}, [allReportActions, transactionThreadReportActions]);
const indexOfLinkedAction = useMemo(() => {
- if (!reportActionID || isLoading) {
+ if (!reportActionID) {
return -1;
}
-
return combinedReportActions.findIndex((obj) => String(obj.reportActionID) === String(isFirstLinkedActionRender.current ? reportActionID : currentReportActionID));
- }, [combinedReportActions, currentReportActionID, reportActionID, isLoading]);
+ }, [combinedReportActions, currentReportActionID, reportActionID]);
const reportActions = useMemo(() => {
if (!reportActionID) {
return combinedReportActions;
}
-
if (isLoading || indexOfLinkedAction === -1) {
return [];
}
@@ -264,7 +262,7 @@ function ReportActionsView({
}, []);
useEffect(() => {
- if (!reportActionID) {
+ if (!reportActionID || indexOfLinkedAction > -1) {
return;
}
@@ -273,7 +271,7 @@ function ReportActionsView({
// There should be only one openReport execution per page start or navigating
Report.openReport(reportID, reportActionID);
// eslint-disable-next-line react-hooks/exhaustive-deps
- }, [route]);
+ }, [route, indexOfLinkedAction]);
useEffect(() => {
const prevNetwork = prevNetworkRef.current;
diff --git a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.js b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.js
index be8a43b1a483..faf9a7c53cf8 100644
--- a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.js
+++ b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.js
@@ -280,6 +280,23 @@ function FloatingActionButtonAndPopover(props) {
text: translate('sidebarScreen.fabNewChat'),
onSelected: () => interceptAnonymousUser(Report.startNewChat),
},
+ ...(canUseTrackExpense
+ ? [
+ {
+ icon: Expensicons.DocumentPlus,
+ text: translate('iou.trackExpense'),
+ onSelected: () =>
+ interceptAnonymousUser(() =>
+ IOU.startMoneyRequest(
+ CONST.IOU.TYPE.TRACK_EXPENSE,
+ // When starting to create a track expense from the global FAB, we need to retrieve selfDM reportID.
+ // If it doesn't exist, we generate a random optimistic reportID and use it for all of the routes in the creation flow.
+ ReportUtils.findSelfDMReportID() || ReportUtils.generateReportID(),
+ ),
+ ),
+ },
+ ]
+ : []),
{
icon: Expensicons.MoneyCircle,
text: translate('iou.requestMoney'),
@@ -306,23 +323,6 @@ function FloatingActionButtonAndPopover(props) {
),
),
},
- ...(canUseTrackExpense
- ? [
- {
- icon: Expensicons.DocumentPlus,
- text: translate('iou.trackExpense'),
- onSelected: () =>
- interceptAnonymousUser(() =>
- IOU.startMoneyRequest(
- CONST.IOU.TYPE.TRACK_EXPENSE,
- // When starting to create a track expense from the global FAB, we need to retrieve selfDM reportID.
- // If it doesn't exist, we generate a random optimistic reportID and use it for all of the routes in the creation flow.
- ReportUtils.findSelfDMReportID() || ReportUtils.generateReportID(),
- ),
- ),
- },
- ]
- : []),
{
icon: Expensicons.Task,
text: translate('newTaskPage.assignTask'),
diff --git a/src/pages/iou/request/step/IOURequestStepDate.js b/src/pages/iou/request/step/IOURequestStepDate.tsx
similarity index 57%
rename from src/pages/iou/request/step/IOURequestStepDate.js
rename to src/pages/iou/request/step/IOURequestStepDate.tsx
index f7b8b1ca3869..682204a4510f 100644
--- a/src/pages/iou/request/step/IOURequestStepDate.js
+++ b/src/pages/iou/request/step/IOURequestStepDate.tsx
@@ -1,58 +1,46 @@
-import lodashGet from 'lodash/get';
import lodashIsEmpty from 'lodash/isEmpty';
-import PropTypes from 'prop-types';
import React from 'react';
+import type {OnyxEntry} from 'react-native-onyx';
import {withOnyx} from 'react-native-onyx';
-import categoryPropTypes from '@components/categoryPropTypes';
import DatePicker from '@components/DatePicker';
import FormProvider from '@components/Form/FormProvider';
import InputWrapper from '@components/Form/InputWrapper';
-import tagPropTypes from '@components/tagPropTypes';
-import transactionPropTypes from '@components/transactionPropTypes';
+import type {FormOnyxValues} from '@components/Form/types';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
-import compose from '@libs/compose';
import * as IOUUtils from '@libs/IOUUtils';
import Navigation from '@libs/Navigation/Navigation';
import * as TransactionUtils from '@libs/TransactionUtils';
import * as IOU from '@userActions/IOU';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
-import {policyPropTypes} from '@src/pages/workspace/withPolicy';
+import type SCREENS from '@src/SCREENS';
import INPUT_IDS from '@src/types/form/MoneyRequestDateForm';
-import IOURequestStepRoutePropTypes from './IOURequestStepRoutePropTypes';
+import type * as OnyxTypes from '@src/types/onyx';
import StepScreenWrapper from './StepScreenWrapper';
import withFullTransactionOrNotFound from './withFullTransactionOrNotFound';
+import type {WithWritableReportOrNotFoundProps} from './withWritableReportOrNotFound';
import withWritableReportOrNotFound from './withWritableReportOrNotFound';
-const propTypes = {
- /** Navigation route context info provided by react navigation */
- route: IOURequestStepRoutePropTypes.isRequired,
-
- /** Onyx Props */
- /** Holds data related to Money Request view state, rather than the underlying Money Request data. */
- transaction: transactionPropTypes,
-
+type IOURequestStepDateOnyxProps = {
/** The draft transaction that holds data to be persisted on the current transaction */
- splitDraftTransaction: transactionPropTypes,
+ splitDraftTransaction: OnyxEntry;
/** The policy of the report */
- policy: policyPropTypes.policy,
+ policy: OnyxEntry;
/** Collection of categories attached to a policy */
- policyCategories: PropTypes.objectOf(categoryPropTypes),
+ policyCategories: OnyxEntry;
/** Collection of tags attached to a policy */
- policyTags: tagPropTypes,
+ policyTags: OnyxEntry;
};
-const defaultProps = {
- transaction: {},
- splitDraftTransaction: {},
- policy: null,
- policyTags: null,
- policyCategories: null,
-};
+type IOURequestStepDateProps = IOURequestStepDateOnyxProps &
+ WithWritableReportOrNotFoundProps & {
+ /** Holds data related to Money Request view state, rather than the underlying Money Request data. */
+ transaction: OnyxEntry;
+ };
function IOURequestStepDate({
route: {
@@ -63,7 +51,7 @@ function IOURequestStepDate({
policy,
policyTags,
policyCategories,
-}) {
+}: IOURequestStepDateProps) {
const styles = useThemeStyles();
const {translate} = useLocalize();
const isEditing = action === CONST.IOU.ACTION.EDIT;
@@ -75,11 +63,7 @@ function IOURequestStepDate({
Navigation.goBack(backTo);
};
- /**
- * @param {Object} value
- * @param {String} value.moneyRequestCreated
- */
- const updateDate = (value) => {
+ const updateDate = (value: FormOnyxValues) => {
const newCreated = value.moneyRequestCreated;
// Only update created if it has changed
@@ -90,15 +74,15 @@ function IOURequestStepDate({
// In the split flow, when editing we use SPLIT_TRANSACTION_DRAFT to save draft value
if (isEditingSplitBill) {
- IOU.setDraftSplitTransaction(transaction.transactionID, {created: newCreated});
+ IOU.setDraftSplitTransaction(transaction?.transactionID ?? '0', {created: newCreated});
navigateBack();
return;
}
- IOU.setMoneyRequestCreated(transaction.transactionID, newCreated, action === CONST.IOU.ACTION.CREATE);
+ IOU.setMoneyRequestCreated(transaction?.transactionID ?? '0', newCreated, action === CONST.IOU.ACTION.CREATE);
if (isEditing) {
- IOU.updateMoneyRequestDate(transaction.transactionID, reportID, newCreated, policy, policyTags, policyCategories);
+ IOU.updateMoneyRequestDate(transaction?.transactionID ?? '0', reportID, newCreated, policy, policyTags, policyCategories);
}
navigateBack();
@@ -108,7 +92,7 @@ function IOURequestStepDate({
@@ -132,28 +116,29 @@ function IOURequestStepDate({
);
}
-IOURequestStepDate.propTypes = propTypes;
-IOURequestStepDate.defaultProps = defaultProps;
IOURequestStepDate.displayName = 'IOURequestStepDate';
-export default compose(
- withWritableReportOrNotFound,
- withFullTransactionOrNotFound,
- withOnyx({
- splitDraftTransaction: {
- key: ({route}) => {
- const transactionID = lodashGet(route, 'params.transactionID', 0);
- return `${ONYXKEYS.COLLECTION.SPLIT_TRANSACTION_DRAFT}${transactionID}`;
- },
- },
- policy: {
- key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${report ? report.policyID : '0'}`,
- },
- policyCategories: {
- key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${report ? report.policyID : '0'}`,
- },
- policyTags: {
- key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY_TAGS}${report ? report.policyID : '0'}`,
+const IOURequestStepDateWithOnyx = withOnyx({
+ splitDraftTransaction: {
+ key: ({route}) => {
+ const transactionID = route?.params.transactionID ?? 0;
+ return `${ONYXKEYS.COLLECTION.SPLIT_TRANSACTION_DRAFT}${transactionID}`;
},
- }),
-)(IOURequestStepDate);
+ },
+ policy: {
+ key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${report ? report.policyID : '0'}`,
+ },
+ policyCategories: {
+ key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${report ? report.policyID : '0'}`,
+ },
+ policyTags: {
+ key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY_TAGS}${report ? report.policyID : '0'}`,
+ },
+})(IOURequestStepDate);
+
+// eslint-disable-next-line rulesdir/no-negated-variables
+const IOURequestStepDateWithWritableReportOrNotFound = withWritableReportOrNotFound(IOURequestStepDateWithOnyx);
+// eslint-disable-next-line rulesdir/no-negated-variables
+const IOURequestStepDateWithFullTransactionOrNotFound = withFullTransactionOrNotFound(IOURequestStepDateWithWritableReportOrNotFound);
+
+export default IOURequestStepDateWithFullTransactionOrNotFound;
diff --git a/src/pages/iou/request/step/IOURequestStepDescription.js b/src/pages/iou/request/step/IOURequestStepDescription.tsx
similarity index 57%
rename from src/pages/iou/request/step/IOURequestStepDescription.js
rename to src/pages/iou/request/step/IOURequestStepDescription.tsx
index 8c3327a5ee5c..d075ed81c956 100644
--- a/src/pages/iou/request/step/IOURequestStepDescription.js
+++ b/src/pages/iou/request/step/IOURequestStepDescription.tsx
@@ -1,81 +1,57 @@
import {useFocusEffect} from '@react-navigation/native';
-import lodashGet from 'lodash/get';
import lodashIsEmpty from 'lodash/isEmpty';
-import PropTypes from 'prop-types';
import React, {useCallback, useRef} from 'react';
import {View} from 'react-native';
+import type {OnyxEntry} from 'react-native-onyx';
import {withOnyx} from 'react-native-onyx';
-import categoryPropTypes from '@components/categoryPropTypes';
import FormProvider from '@components/Form/FormProvider';
import InputWrapperWithRef from '@components/Form/InputWrapper';
-import tagPropTypes from '@components/tagPropTypes';
+import type {FormInputErrors, FormOnyxValues} from '@components/Form/types';
+import type {AnimatedTextInputRef} from '@components/RNTextInput';
import TextInput from '@components/TextInput';
-import transactionPropTypes from '@components/transactionPropTypes';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
-import compose from '@libs/compose';
import * as ErrorUtils from '@libs/ErrorUtils';
import Navigation from '@libs/Navigation/Navigation';
import * as ReportUtils from '@libs/ReportUtils';
import * as TransactionUtils from '@libs/TransactionUtils';
import updateMultilineInputRange from '@libs/updateMultilineInputRange';
-import reportActionPropTypes from '@pages/home/report/reportActionPropTypes';
-import reportPropTypes from '@pages/reportPropTypes';
import * as IOU from '@userActions/IOU';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
-import {policyPropTypes} from '@src/pages/workspace/withPolicy';
+import type SCREENS from '@src/SCREENS';
import INPUT_IDS from '@src/types/form/MoneyRequestDescriptionForm';
-import IOURequestStepRoutePropTypes from './IOURequestStepRoutePropTypes';
+import type * as OnyxTypes from '@src/types/onyx';
import StepScreenWrapper from './StepScreenWrapper';
import withFullTransactionOrNotFound from './withFullTransactionOrNotFound';
+import type {WithWritableReportOrNotFoundProps} from './withWritableReportOrNotFound';
import withWritableReportOrNotFound from './withWritableReportOrNotFound';
-const propTypes = {
- /** Navigation route context info provided by react navigation */
- route: IOURequestStepRoutePropTypes.isRequired,
-
- /** Onyx Props */
- /** Holds data related to Money Request view state, rather than the underlying Money Request data. */
- transaction: transactionPropTypes,
-
+type IOURequestStepDescriptionOnyxProps = {
/** The draft transaction that holds data to be persisted on the current transaction */
- splitDraftTransaction: transactionPropTypes,
+ splitDraftTransaction: OnyxEntry;
/** The policy of the report */
- policy: policyPropTypes.policy,
+ policy: OnyxEntry;
/** Collection of categories attached to a policy */
- policyCategories: PropTypes.objectOf(categoryPropTypes),
+ policyCategories: OnyxEntry;
/** Collection of tags attached to a policy */
- policyTags: tagPropTypes,
+ policyTags: OnyxEntry;
/** The actions from the parent report */
- reportActions: PropTypes.shape(reportActionPropTypes),
+ reportActions: OnyxEntry;
/** Session info for the currently logged in user. */
- session: PropTypes.shape({
- /** Currently logged in user accountID */
- accountID: PropTypes.number,
-
- /** Currently logged in user email */
- email: PropTypes.string,
- }).isRequired,
-
- /** The report attached to the transaction */
- report: reportPropTypes,
+ session: OnyxEntry;
};
-const defaultProps = {
- transaction: {},
- splitDraftTransaction: {},
- policy: null,
- policyTags: null,
- policyCategories: null,
- reportActions: {},
- report: {},
-};
+type IOURequestStepDescriptionProps = IOURequestStepDescriptionOnyxProps &
+ WithWritableReportOrNotFoundProps & {
+ /** Holds data related to Money Request view state, rather than the underlying Money Request data. */
+ transaction: OnyxEntry;
+ };
function IOURequestStepDescription({
route: {
@@ -89,15 +65,14 @@ function IOURequestStepDescription({
reportActions,
session,
report,
-}) {
+}: IOURequestStepDescriptionProps) {
const styles = useThemeStyles();
const {translate} = useLocalize();
- const inputRef = useRef(null);
- const focusTimeoutRef = useRef(null);
+ const inputRef = useRef(null);
+ const focusTimeoutRef = useRef(null);
// In the split flow, when editing we use SPLIT_TRANSACTION_DRAFT to save draft value
const isEditingSplitBill = iouType === CONST.IOU.TYPE.SPLIT && action === CONST.IOU.ACTION.EDIT;
- const currentDescription =
- isEditingSplitBill && !lodashIsEmpty(splitDraftTransaction) ? lodashGet(splitDraftTransaction, 'comment.comment', '') : lodashGet(transaction, 'comment.comment', '');
+ const currentDescription = isEditingSplitBill && !lodashIsEmpty(splitDraftTransaction) ? splitDraftTransaction?.comment.comment ?? '' : transaction?.comment.comment ?? '';
useFocusEffect(
useCallback(() => {
focusTimeoutRef.current = setTimeout(() => {
@@ -115,11 +90,9 @@ function IOURequestStepDescription({
);
/**
- * @param {Object} values
- * @param {String} values.title
- * @returns {Object} - An object containing the errors for each inputID
+ * @returns - An object containing the errors for each inputID
*/
- const validate = useCallback((values) => {
+ const validate = useCallback((values: FormOnyxValues): FormInputErrors => {
const errors = {};
if (values.moneyRequestComment.length > CONST.DESCRIPTION_LIMIT) {
@@ -136,11 +109,7 @@ function IOURequestStepDescription({
Navigation.goBack(backTo);
};
- /**
- * @param {Object} value
- * @param {String} value.moneyRequestComment
- */
- const updateComment = (value) => {
+ const updateComment = (value: FormOnyxValues) => {
const newComment = value.moneyRequestComment.trim();
// Only update comment if it has changed
@@ -151,24 +120,25 @@ function IOURequestStepDescription({
// In the split flow, when editing we use SPLIT_TRANSACTION_DRAFT to save draft value
if (isEditingSplitBill) {
- IOU.setDraftSplitTransaction(transaction.transactionID, {comment: newComment});
+ IOU.setDraftSplitTransaction(transaction?.transactionID ?? '0', {comment: newComment});
navigateBack();
return;
}
- IOU.setMoneyRequestDescription(transaction.transactionID, newComment, action === CONST.IOU.ACTION.CREATE);
+ IOU.setMoneyRequestDescription(transaction?.transactionID ?? '0', newComment, action === CONST.IOU.ACTION.CREATE);
if (action === CONST.IOU.ACTION.EDIT) {
- IOU.updateMoneyRequestDescription(transaction.transactionID, reportID, newComment, policy, policyTags, policyCategories);
+ IOU.updateMoneyRequestDescription(transaction?.transactionID ?? '0', reportID, newComment, policy, policyTags, policyCategories);
}
navigateBack();
};
- const reportAction = reportActions[report.parentReportActionID || reportActionID];
+ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- nullish coalescing doesn't achieve the same result in this case
+ const reportAction = reportActions?.[report?.parentReportActionID || reportActionID] ?? null;
const isEditing = action === CONST.IOU.ACTION.EDIT;
const isSplitBill = iouType === CONST.IOU.TYPE.SPLIT;
- const canEditSplitBill = isSplitBill && reportAction && session.accountID === reportAction.actorAccountID && TransactionUtils.areRequiredFieldsEmpty(transaction);
+ const canEditSplitBill = isSplitBill && reportAction && session?.accountID === reportAction.actorAccountID && TransactionUtils.areRequiredFieldsEmpty(transaction);
// eslint-disable-next-line rulesdir/no-negated-variables
const shouldShowNotFoundPage = isEditing && (isSplitBill ? !canEditSplitBill : !ReportUtils.canEditMoneyRequest(reportAction));
return (
@@ -213,46 +183,47 @@ function IOURequestStepDescription({
);
}
-IOURequestStepDescription.propTypes = propTypes;
-IOURequestStepDescription.defaultProps = defaultProps;
IOURequestStepDescription.displayName = 'IOURequestStepDescription';
-export default compose(
- withWritableReportOrNotFound,
- withFullTransactionOrNotFound,
- withOnyx({
- splitDraftTransaction: {
- key: ({route}) => {
- const transactionID = lodashGet(route, 'params.transactionID', 0);
- return `${ONYXKEYS.COLLECTION.SPLIT_TRANSACTION_DRAFT}${transactionID}`;
- },
- },
- policy: {
- key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${report ? report.policyID : '0'}`,
- },
- policyCategories: {
- key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${report ? report.policyID : '0'}`,
+const IOURequestStepDescriptionWithOnyx = withOnyx({
+ splitDraftTransaction: {
+ key: ({route}) => {
+ const transactionID = route?.params.transactionID ?? 0;
+ return `${ONYXKEYS.COLLECTION.SPLIT_TRANSACTION_DRAFT}${transactionID}`;
},
- policyTags: {
- key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY_TAGS}${report ? report.policyID : '0'}`,
- },
- reportActions: {
- key: ({
- report,
- route: {
- params: {action, iouType},
- },
- }) => {
- let reportID = '0';
- if (action === CONST.IOU.ACTION.EDIT) {
- reportID = iouType === CONST.IOU.TYPE.SPLIT ? report.reportID : report.parentReportID;
- }
- return `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`;
+ },
+ policy: {
+ key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${report ? report.policyID : '0'}`,
+ },
+ policyCategories: {
+ key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${report ? report.policyID : '0'}`,
+ },
+ policyTags: {
+ key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY_TAGS}${report ? report.policyID : '0'}`,
+ },
+ reportActions: {
+ key: ({
+ report,
+ route: {
+ params: {action, iouType},
},
- canEvict: false,
+ }) => {
+ let reportID = '0';
+ if (action === CONST.IOU.ACTION.EDIT) {
+ reportID = iouType === CONST.IOU.TYPE.SPLIT ? report?.reportID ?? '0' : report?.parentReportID ?? '0';
+ }
+ return `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`;
},
- session: {
- key: ONYXKEYS.SESSION,
- },
- }),
-)(IOURequestStepDescription);
+ canEvict: false,
+ },
+ session: {
+ key: ONYXKEYS.SESSION,
+ },
+})(IOURequestStepDescription);
+
+// eslint-disable-next-line rulesdir/no-negated-variables
+const IOURequestStepDescriptionWithWritableReportOrNotFound = withWritableReportOrNotFound(IOURequestStepDescriptionWithOnyx);
+// eslint-disable-next-line rulesdir/no-negated-variables
+const IOURequestStepDescriptionWithFullTransactionOrNotFound = withFullTransactionOrNotFound(IOURequestStepDescriptionWithWritableReportOrNotFound);
+
+export default IOURequestStepDescriptionWithFullTransactionOrNotFound;
diff --git a/src/pages/iou/request/step/IOURequestStepScan/NavigationAwareCamera/index.native.js b/src/pages/iou/request/step/IOURequestStepScan/NavigationAwareCamera/index.native.js
index 65c17d3cb7ab..64fa291b2003 100644
--- a/src/pages/iou/request/step/IOURequestStepScan/NavigationAwareCamera/index.native.js
+++ b/src/pages/iou/request/step/IOURequestStepScan/NavigationAwareCamera/index.native.js
@@ -15,6 +15,7 @@ const NavigationAwareCamera = React.forwardRef(({cameraTabIndex, ...props}, ref)
return (
{
// Store the receipt on the transaction object in Onyx
@@ -257,7 +258,7 @@ function IOURequestStepScan({
showCameraAlert();
Log.warn('Error taking photo', error);
});
- }, [flash, action, translate, transactionID, updateScanAndNavigate, navigateToConfirmationStep, cameraPermissionStatus]);
+ }, [flash, hasFlash, action, translate, transactionID, updateScanAndNavigate, navigateToConfirmationStep, cameraPermissionStatus]);
// Wait for camera permission status to render
if (cameraPermissionStatus == null) {
@@ -356,20 +357,22 @@ function IOURequestStepScan({
height={CONST.RECEIPT.SHUTTER_SIZE}
/>
- setFlash((prevFlash) => !prevFlash)}
- >
-
-
+ {hasFlash && (
+ setFlash((prevFlash) => !prevFlash)}
+ >
+
+
+ )}
);
diff --git a/src/pages/iou/request/step/IOURequestStepWaypoint.tsx b/src/pages/iou/request/step/IOURequestStepWaypoint.tsx
index 4e61ac944aac..a186288e8a27 100644
--- a/src/pages/iou/request/step/IOURequestStepWaypoint.tsx
+++ b/src/pages/iou/request/step/IOURequestStepWaypoint.tsx
@@ -27,6 +27,7 @@ import CONST from '@src/CONST';
import type {TranslationPaths} from '@src/languages/types';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
+import type SCREENS from '@src/SCREENS';
import type * as OnyxTypes from '@src/types/onyx';
import type {Waypoint} from '@src/types/onyx/Transaction';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
@@ -41,10 +42,10 @@ type IOURequestStepWaypointOnyxProps = {
userLocation: OnyxEntry;
};
-type IOURequestStepWaypointProps = {
- transaction: OnyxEntry;
-} & IOURequestStepWaypointOnyxProps &
- WithWritableReportOrNotFoundProps;
+type IOURequestStepWaypointProps = IOURequestStepWaypointOnyxProps &
+ WithWritableReportOrNotFoundProps & {
+ transaction: OnyxEntry;
+ };
function IOURequestStepWaypoint({
route: {
diff --git a/src/pages/iou/request/step/withWritableReportOrNotFound.tsx b/src/pages/iou/request/step/withWritableReportOrNotFound.tsx
index d5d27d8268b1..de49a451da08 100644
--- a/src/pages/iou/request/step/withWritableReportOrNotFound.tsx
+++ b/src/pages/iou/request/step/withWritableReportOrNotFound.tsx
@@ -17,11 +17,13 @@ type WithWritableReportOrNotFoundOnyxProps = {
report: OnyxEntry;
};
-type Route = RouteProp;
+type MoneyRequestRouteName = typeof SCREENS.MONEY_REQUEST.STEP_WAYPOINT | typeof SCREENS.MONEY_REQUEST.STEP_DESCRIPTION;
-type WithWritableReportOrNotFoundProps = WithWritableReportOrNotFoundOnyxProps & {route: Route};
+type Route = RouteProp;
-export default function (
+type WithWritableReportOrNotFoundProps = WithWritableReportOrNotFoundOnyxProps & {route: Route};
+
+export default function , TRef>(
WrappedComponent: ComponentType>,
): React.ComponentType, keyof WithWritableReportOrNotFoundOnyxProps>> {
// eslint-disable-next-line rulesdir/no-negated-variables
diff --git a/src/pages/settings/AboutPage/ConsolePage.tsx b/src/pages/settings/AboutPage/ConsolePage.tsx
index 8b4f7a6ebc36..c9532fa041a0 100644
--- a/src/pages/settings/AboutPage/ConsolePage.tsx
+++ b/src/pages/settings/AboutPage/ConsolePage.tsx
@@ -1,5 +1,4 @@
import {format} from 'date-fns';
-import isEmpty from 'lodash/isEmpty';
import React, {useCallback, useEffect, useMemo, useState} from 'react';
import {View} from 'react-native';
import type {ListRenderItem, ListRenderItemInfo} from 'react-native';
@@ -17,7 +16,7 @@ import useKeyboardShortcut from '@hooks/useKeyboardShortcut';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import {addLog} from '@libs/actions/Console';
-import {createLog, sanitizeConsoleInput} from '@libs/Console';
+import {createLog, parseStringifiedMessages, sanitizeConsoleInput} from '@libs/Console';
import type {Log} from '@libs/Console';
import localFileCreate from '@libs/localFileCreate';
import localFileDownload from '@libs/localFileDownload';
@@ -25,8 +24,7 @@ import Navigation from '@libs/Navigation/Navigation';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
-
-type CapturedLogs = Record;
+import type {CapturedLogs} from '@src/types/onyx';
type ConsolePageOnyxProps = {
/** Logs captured on the current device */
@@ -38,30 +36,6 @@ type ConsolePageOnyxProps = {
type ConsolePageProps = ConsolePageOnyxProps;
-/**
- * Loops through all the logs and parses the message if it's a stringified JSON
- * @param logs Logs captured on the current device
- * @returns CapturedLogs with parsed messages
- */
-const parseStringifyMessages = (logs: Log[]) => {
- if (isEmpty(logs)) {
- return;
- }
-
- return logs.map((log) => {
- try {
- const parsedMessage = JSON.parse(log.message);
- return {
- ...log,
- message: parsedMessage,
- };
- } catch {
- // If the message can't be parsed, just return the original log
- return log;
- }
- });
-};
-
function ConsolePage({capturedLogs, shouldStoreLogs}: ConsolePageProps) {
const [input, setInput] = useState('');
const [logs, setLogs] = useState(capturedLogs);
@@ -97,14 +71,14 @@ function ConsolePage({capturedLogs, shouldStoreLogs}: ConsolePageProps) {
useKeyboardShortcut(CONST.KEYBOARD_SHORTCUTS.ENTER, executeArbitraryCode);
const saveLogs = () => {
- const logsWithParsedMessages = parseStringifyMessages(logsList);
+ const logsWithParsedMessages = parseStringifiedMessages(logsList);
localFileDownload('logs', JSON.stringify(logsWithParsedMessages, null, 2));
};
const shareLogs = () => {
setIsGeneratingLogsFile(true);
- const logsWithParsedMessages = parseStringifyMessages(logsList);
+ const logsWithParsedMessages = parseStringifiedMessages(logsList);
// Generate a file with the logs and pass its path to the list of reports to share it with
localFileCreate('logs', JSON.stringify(logsWithParsedMessages, null, 2)).then(({path, size}) => {
diff --git a/src/pages/settings/AboutPage/TroubleshootPage.tsx b/src/pages/settings/AboutPage/TroubleshootPage.tsx
index e04a834c6341..0e192540ebd2 100644
--- a/src/pages/settings/AboutPage/TroubleshootPage.tsx
+++ b/src/pages/settings/AboutPage/TroubleshootPage.tsx
@@ -3,14 +3,13 @@ import {View} from 'react-native';
import Onyx, {withOnyx} from 'react-native-onyx';
import type {OnyxEntry} from 'react-native-onyx';
import type {SvgProps} from 'react-native-svg';
+import ClientSideLoggingToolMenu from '@components/ClientSideLoggingToolMenu';
import ConfirmModal from '@components/ConfirmModal';
import * as Expensicons from '@components/Icon/Expensicons';
import IllustratedHeaderPageLayout from '@components/IllustratedHeaderPageLayout';
import LottieAnimations from '@components/LottieAnimations';
import MenuItemList from '@components/MenuItemList';
-import Switch from '@components/Switch';
import TestToolMenu from '@components/TestToolMenu';
-import TestToolRow from '@components/TestToolRow';
import Text from '@components/Text';
import TextLink from '@components/TextLink';
import useEnvironment from '@hooks/useEnvironment';
@@ -18,7 +17,6 @@ import useLocalize from '@hooks/useLocalize';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import useWaitForNavigation from '@hooks/useWaitForNavigation';
-import * as Console from '@libs/actions/Console';
import Navigation from '@libs/Navigation/Navigation';
import * as App from '@userActions/App';
import * as Report from '@userActions/Report';
@@ -113,13 +111,7 @@ function TroubleshootPage({shouldStoreLogs}: TroubleshootPageProps) {
-
- (shouldStoreLogs ? Console.disableLoggingAndFlushLogs() : Console.setShouldStoreLogs(true))}
- />
-
+
;
+
+export type {Log, CapturedLogs};
diff --git a/src/types/onyx/index.ts b/src/types/onyx/index.ts
index 915fba9308d2..e56066440d80 100644
--- a/src/types/onyx/index.ts
+++ b/src/types/onyx/index.ts
@@ -6,7 +6,7 @@ import type Beta from './Beta';
import type BlockedFromConcierge from './BlockedFromConcierge';
import type Card from './Card';
import type {CardList} from './Card';
-import type Log from './Console';
+import type {CapturedLogs, Log} from './Console';
import type Credentials from './Credentials';
import type Currency from './Currency';
import type {CurrencyList} from './Currency';
@@ -175,4 +175,5 @@ export type {
NewGroupChatDraft,
Log,
PolicyJoinMember,
+ CapturedLogs,
};
diff --git a/workflow_tests/assertions/testBuildAssertions.ts b/workflow_tests/assertions/testBuildAssertions.ts
index 02e64fb83889..5fb0a8fd9342 100644
--- a/workflow_tests/assertions/testBuildAssertions.ts
+++ b/workflow_tests/assertions/testBuildAssertions.ts
@@ -397,7 +397,7 @@ function assertPostGithubCommentJobExecuted(
});
}
-export {
+export default {
assertValidateActorJobExecuted,
assertGetBranchRefJobExecuted,
assertAndroidJobExecuted,
diff --git a/workflow_tests/assertions/validateGithubActionsAssertions.ts b/workflow_tests/assertions/validateGithubActionsAssertions.ts
index 34d694111b1e..89eb444299ed 100644
--- a/workflow_tests/assertions/validateGithubActionsAssertions.ts
+++ b/workflow_tests/assertions/validateGithubActionsAssertions.ts
@@ -18,5 +18,4 @@ function assertVerifyJobExecuted(workflowResult: Step[], didExecute = true) {
});
}
-// eslint-disable-next-line import/prefer-default-export
-export {assertVerifyJobExecuted};
+export default {assertVerifyJobExecuted};
diff --git a/workflow_tests/assertions/verifyPodfileAssertions.ts b/workflow_tests/assertions/verifyPodfileAssertions.ts
index c2e4a1d8f30e..d837507ede1b 100644
--- a/workflow_tests/assertions/verifyPodfileAssertions.ts
+++ b/workflow_tests/assertions/verifyPodfileAssertions.ts
@@ -17,5 +17,4 @@ function assertVerifyJobExecuted(workflowResult: Step[], didExecute = true) {
});
}
-// eslint-disable-next-line import/prefer-default-export
-export {assertVerifyJobExecuted};
+export default {assertVerifyJobExecuted};
diff --git a/workflow_tests/assertions/verifySignedCommitsAssertions.ts b/workflow_tests/assertions/verifySignedCommitsAssertions.ts
index 99b2e28eb582..8aaabafa9d9d 100644
--- a/workflow_tests/assertions/verifySignedCommitsAssertions.ts
+++ b/workflow_tests/assertions/verifySignedCommitsAssertions.ts
@@ -14,4 +14,4 @@ function assertVerifySignedCommitsJobExecuted(workflowResult: Step[], didExecute
}
// eslint-disable-next-line import/prefer-default-export
-export {assertVerifySignedCommitsJobExecuted};
+export default {assertVerifySignedCommitsJobExecuted};
diff --git a/workflow_tests/jest.config.js b/workflow_tests/jest.config.js
deleted file mode 100644
index cecdf8589d7f..000000000000
--- a/workflow_tests/jest.config.js
+++ /dev/null
@@ -1,9 +0,0 @@
-module.exports = {
- verbose: true,
- transform: {
- '^.+\\.jsx?$': 'babel-jest',
- '^.+\\.tsx?$': 'ts-jest',
- },
- clearMocks: true,
- resetMocks: true,
-};
diff --git a/workflow_tests/jest.config.ts b/workflow_tests/jest.config.ts
new file mode 100644
index 000000000000..8156e449039f
--- /dev/null
+++ b/workflow_tests/jest.config.ts
@@ -0,0 +1,13 @@
+/* eslint-disable @typescript-eslint/naming-convention */
+import type {Config} from 'jest';
+
+const config: Config = {
+ verbose: true,
+ transform: {
+ '^.+\\.(js|jsx|ts|tsx)$': 'ts-jest',
+ },
+ clearMocks: true,
+ resetMocks: true,
+};
+
+export default config;
diff --git a/workflow_tests/mocks/testBuildMocks.ts b/workflow_tests/mocks/testBuildMocks.ts
index f502bfb248ba..cfd194d07185 100644
--- a/workflow_tests/mocks/testBuildMocks.ts
+++ b/workflow_tests/mocks/testBuildMocks.ts
@@ -247,7 +247,7 @@ const TESTBUILD__POSTGITHUBCOMMENT__STEP_MOCKS = [
TESTBUILD__POSTGITHUBCOMMENT__PUBLISH_LINKS_TO_APPS_FOR_DOWNLOAD__STEP_MOCK,
];
-export {
+export default {
TESTBUILD__VALIDATEACTOR__TEAM_MEMBER_HAS_FLAG__STEP_MOCKS,
TESTBUILD__VALIDATEACTOR__TEAM_MEMBER_NO_FLAG__STEP_MOCKS,
TESTBUILD__VALIDATEACTOR__NO_TEAM_MEMBER_HAS_FLAG__STEP_MOCKS,
diff --git a/workflow_tests/scripts/runWorkflowTests.sh b/workflow_tests/scripts/runWorkflowTests.sh
index 71ddcdceffb5..c8ee88e33e99 100755
--- a/workflow_tests/scripts/runWorkflowTests.sh
+++ b/workflow_tests/scripts/runWorkflowTests.sh
@@ -60,4 +60,4 @@ info 'ACT_BINARY environment variable set to an Act executable'
success 'Environment setup properly - running tests'
# Run tests
-npm test -- --config=workflow_tests/jest.config.js --runInBand "$@"
+npm test -- --config=workflow_tests/jest.config.ts --runInBand "$@"
diff --git a/workflow_tests/testBuild.test.js b/workflow_tests/testBuild.test.ts
similarity index 97%
rename from workflow_tests/testBuild.test.js
rename to workflow_tests/testBuild.test.ts
index 7e93b0a9ac9f..cd793fa9aaca 100644
--- a/workflow_tests/testBuild.test.js
+++ b/workflow_tests/testBuild.test.ts
@@ -1,12 +1,13 @@
-const path = require('path');
-const kieMockGithub = require('@kie/mock-github');
-const utils = require('./utils/utils');
-const assertions = require('./assertions/testBuildAssertions');
-const mocks = require('./mocks/testBuildMocks');
-const ExtendedAct = require('./utils/ExtendedAct').default;
+/* eslint-disable @typescript-eslint/naming-convention */
+import {MockGithub} from '@kie/mock-github';
+import path from 'path';
+import assertions from './assertions/testBuildAssertions';
+import mocks from './mocks/testBuildMocks';
+import ExtendedAct from './utils/ExtendedAct';
+import * as utils from './utils/utils';
jest.setTimeout(90 * 1000);
-let mockGithub;
+let mockGithub: MockGithub;
const FILES_TO_COPY_INTO_TEST_REPO = [
...utils.deepCopy(utils.FILES_TO_COPY_INTO_TEST_REPO),
{
@@ -32,7 +33,7 @@ describe('test workflow testBuild', () => {
MYAPP_UPLOAD_KEY_PASSWORD: 'dummy_myapp_upload_key_password',
};
- beforeAll(async () => {
+ beforeAll(() => {
// in case of the tests being interrupted without cleanup the mock repo directory may be left behind
// which breaks the next test run, this removes any possible leftovers
utils.removeMockRepoDir();
@@ -40,7 +41,7 @@ describe('test workflow testBuild', () => {
beforeEach(async () => {
// create a local repository and copy required files
- mockGithub = new kieMockGithub.MockGithub({
+ mockGithub = new MockGithub({
repo: {
testTestBuildWorkflowRepo: {
files: FILES_TO_COPY_INTO_TEST_REPO,
@@ -60,7 +61,7 @@ describe('test workflow testBuild', () => {
PULL_REQUEST_NUMBER: '1234',
};
it('executes workflow', async () => {
- const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') || '';
+ const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') ?? '';
const workflowPath = path.join(repoPath, '.github', 'workflows', 'testBuild.yml');
let act = new ExtendedAct(repoPath, workflowPath);
act = utils.setUpActParams(act, event, {}, secrets, githubToken, {}, inputs);
@@ -91,7 +92,7 @@ describe('test workflow testBuild', () => {
});
describe('actor is not a team member', () => {
it('stops the workflow after validation', async () => {
- const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') || '';
+ const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') ?? '';
const workflowPath = path.join(repoPath, '.github', 'workflows', 'testBuild.yml');
let act = new ExtendedAct(repoPath, workflowPath);
act = utils.setUpActParams(act, event, {}, secrets, githubToken, {}, inputs);
@@ -123,7 +124,7 @@ describe('test workflow testBuild', () => {
});
describe('PR does not have READY_TO_BUILD label', () => {
it('stops the workflow after validation', async () => {
- const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') || '';
+ const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') ?? '';
const workflowPath = path.join(repoPath, '.github', 'workflows', 'testBuild.yml');
let act = new ExtendedAct(repoPath, workflowPath);
act = utils.setUpActParams(act, event, {}, secrets, githubToken, {}, inputs);
@@ -155,7 +156,7 @@ describe('test workflow testBuild', () => {
});
describe('actor is not a team member and PR does not have READY_TO_BUILD label', () => {
it('stops the workflow after validation', async () => {
- const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') || '';
+ const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') ?? '';
const workflowPath = path.join(repoPath, '.github', 'workflows', 'testBuild.yml');
let act = new ExtendedAct(repoPath, workflowPath);
act = utils.setUpActParams(act, event, {}, secrets, githubToken, {}, inputs);
@@ -187,7 +188,7 @@ describe('test workflow testBuild', () => {
});
describe('android fails', () => {
it('executes workflow, failure reflected', async () => {
- const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') || '';
+ const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') ?? '';
const workflowPath = path.join(repoPath, '.github', 'workflows', 'testBuild.yml');
let act = new ExtendedAct(repoPath, workflowPath);
act = utils.setUpActParams(act, event, {}, secrets, githubToken, {}, inputs);
@@ -220,7 +221,7 @@ describe('test workflow testBuild', () => {
});
describe('iOS fails', () => {
it('executes workflow, failure reflected', async () => {
- const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') || '';
+ const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') ?? '';
const workflowPath = path.join(repoPath, '.github', 'workflows', 'testBuild.yml');
let act = new ExtendedAct(repoPath, workflowPath);
act = utils.setUpActParams(act, event, {}, secrets, githubToken, {}, inputs);
@@ -253,7 +254,7 @@ describe('test workflow testBuild', () => {
});
describe('desktop fails', () => {
it('executes workflow, failure reflected', async () => {
- const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') || '';
+ const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') ?? '';
const workflowPath = path.join(repoPath, '.github', 'workflows', 'testBuild.yml');
let act = new ExtendedAct(repoPath, workflowPath);
act = utils.setUpActParams(act, event, {}, secrets, githubToken, {}, inputs);
@@ -295,7 +296,7 @@ describe('test workflow testBuild', () => {
});
describe('web fails', () => {
it('executes workflow, failure reflected', async () => {
- const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') || '';
+ const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') ?? '';
const workflowPath = path.join(repoPath, '.github', 'workflows', 'testBuild.yml');
let act = new ExtendedAct(repoPath, workflowPath);
act = utils.setUpActParams(act, event, {}, secrets, githubToken, {}, inputs);
@@ -348,7 +349,7 @@ describe('test workflow testBuild', () => {
},
};
it('executes workflow, without getBranchRef', async () => {
- const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') || '';
+ const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') ?? '';
const workflowPath = path.join(repoPath, '.github', 'workflows', 'testBuild.yml');
let act = new ExtendedAct(repoPath, workflowPath);
act = utils.setUpActParams(act, event, eventOptions, secrets, githubToken, {});
@@ -379,7 +380,7 @@ describe('test workflow testBuild', () => {
});
describe('actor is not a team member', () => {
it('stops the workflow after validation', async () => {
- const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') || '';
+ const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') ?? '';
const workflowPath = path.join(repoPath, '.github', 'workflows', 'testBuild.yml');
let act = new ExtendedAct(repoPath, workflowPath);
act = utils.setUpActParams(act, event, eventOptions, secrets, githubToken, {});
@@ -411,7 +412,7 @@ describe('test workflow testBuild', () => {
});
describe('PR does not have READY_TO_BUILD label', () => {
it('stops the workflow after validation', async () => {
- const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') || '';
+ const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') ?? '';
const workflowPath = path.join(repoPath, '.github', 'workflows', 'testBuild.yml');
let act = new ExtendedAct(repoPath, workflowPath);
act = utils.setUpActParams(act, event, eventOptions, secrets, githubToken, {});
@@ -443,7 +444,7 @@ describe('test workflow testBuild', () => {
});
describe('actor is not a team member and PR does not have READY_TO_BUILD label', () => {
it('stops the workflow after validation', async () => {
- const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') || '';
+ const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') ?? '';
const workflowPath = path.join(repoPath, '.github', 'workflows', 'testBuild.yml');
let act = new ExtendedAct(repoPath, workflowPath);
act = utils.setUpActParams(act, event, eventOptions, secrets, githubToken, {});
@@ -486,7 +487,7 @@ describe('test workflow testBuild', () => {
},
};
it('executes workflow, without getBranchRef', async () => {
- const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') || '';
+ const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') ?? '';
const workflowPath = path.join(repoPath, '.github', 'workflows', 'testBuild.yml');
let act = new ExtendedAct(repoPath, workflowPath);
act = utils.setUpActParams(act, event, eventOptions, secrets, githubToken, {});
@@ -517,7 +518,7 @@ describe('test workflow testBuild', () => {
});
describe('actor is not a team member', () => {
it('stops the workflow after validation', async () => {
- const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') || '';
+ const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') ?? '';
const workflowPath = path.join(repoPath, '.github', 'workflows', 'testBuild.yml');
let act = new ExtendedAct(repoPath, workflowPath);
act = utils.setUpActParams(act, event, eventOptions, secrets, githubToken, {});
@@ -549,7 +550,7 @@ describe('test workflow testBuild', () => {
});
describe('PR does not have READY_TO_BUILD label', () => {
it('stops the workflow after validation', async () => {
- const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') || '';
+ const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') ?? '';
const workflowPath = path.join(repoPath, '.github', 'workflows', 'testBuild.yml');
let act = new ExtendedAct(repoPath, workflowPath);
act = utils.setUpActParams(act, event, eventOptions, secrets, githubToken, {});
@@ -581,7 +582,7 @@ describe('test workflow testBuild', () => {
});
describe('actor is not a team member and PR does not have READY_TO_BUILD label', () => {
it('stops the workflow after validation', async () => {
- const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') || '';
+ const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') ?? '';
const workflowPath = path.join(repoPath, '.github', 'workflows', 'testBuild.yml');
let act = new ExtendedAct(repoPath, workflowPath);
act = utils.setUpActParams(act, event, eventOptions, secrets, githubToken, {});
@@ -624,7 +625,7 @@ describe('test workflow testBuild', () => {
},
};
it('executes workflow, withuout getBranchRef', async () => {
- const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') || '';
+ const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') ?? '';
const workflowPath = path.join(repoPath, '.github', 'workflows', 'testBuild.yml');
let act = new ExtendedAct(repoPath, workflowPath);
act = utils.setUpActParams(act, event, eventOptions, secrets, githubToken, {});
@@ -655,7 +656,7 @@ describe('test workflow testBuild', () => {
});
describe('actor is not a team member', () => {
it('stops the workflow after validation', async () => {
- const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') || '';
+ const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') ?? '';
const workflowPath = path.join(repoPath, '.github', 'workflows', 'testBuild.yml');
let act = new ExtendedAct(repoPath, workflowPath);
act = utils.setUpActParams(act, event, eventOptions, secrets, githubToken, {});
@@ -687,7 +688,7 @@ describe('test workflow testBuild', () => {
});
describe('PR does not have READY_TO_BUILD label', () => {
it('stops the workflow after validation', async () => {
- const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') || '';
+ const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') ?? '';
const workflowPath = path.join(repoPath, '.github', 'workflows', 'testBuild.yml');
let act = new ExtendedAct(repoPath, workflowPath);
act = utils.setUpActParams(act, event, eventOptions, secrets, githubToken, {});
@@ -719,7 +720,7 @@ describe('test workflow testBuild', () => {
});
describe('actor is not a team member and PR does not have READY_TO_BUILD label', () => {
it('stops the workflow after validation', async () => {
- const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') || '';
+ const repoPath = mockGithub.repo.getPath('testTestBuildWorkflowRepo') ?? '';
const workflowPath = path.join(repoPath, '.github', 'workflows', 'testBuild.yml');
let act = new ExtendedAct(repoPath, workflowPath);
act = utils.setUpActParams(act, event, eventOptions, secrets, githubToken, {});
diff --git a/workflow_tests/validateGithubActions.test.js b/workflow_tests/validateGithubActions.test.ts
similarity index 86%
rename from workflow_tests/validateGithubActions.test.js
rename to workflow_tests/validateGithubActions.test.ts
index 404de4c66101..f4972a9ae653 100644
--- a/workflow_tests/validateGithubActions.test.js
+++ b/workflow_tests/validateGithubActions.test.ts
@@ -1,12 +1,12 @@
-const path = require('path');
-const kieMockGithub = require('@kie/mock-github');
-const utils = require('./utils/utils');
-const assertions = require('./assertions/validateGithubActionsAssertions');
-const mocks = require('./mocks/validateGithubActionsMocks').default;
-const ExtendedAct = require('./utils/ExtendedAct').default;
+import {MockGithub} from '@kie/mock-github';
+import path from 'path';
+import assertions from './assertions/validateGithubActionsAssertions';
+import mocks from './mocks/validateGithubActionsMocks';
+import ExtendedAct from './utils/ExtendedAct';
+import * as utils from './utils/utils';
jest.setTimeout(90 * 1000);
-let mockGithub;
+let mockGithub: MockGithub;
const FILES_TO_COPY_INTO_TEST_REPO = [
...utils.deepCopy(utils.FILES_TO_COPY_INTO_TEST_REPO),
{
@@ -19,7 +19,7 @@ describe('test workflow validateGithubActions', () => {
const githubToken = 'dummy_github_token';
const actor = 'Dummy Actor';
- beforeAll(async () => {
+ beforeAll(() => {
// in case of the tests being interrupted without cleanup the mock repo directory may be left behind
// which breaks the next test run, this removes any possible leftovers
utils.removeMockRepoDir();
@@ -27,7 +27,7 @@ describe('test workflow validateGithubActions', () => {
beforeEach(async () => {
// create a local repository and copy required files
- mockGithub = new kieMockGithub.MockGithub({
+ mockGithub = new MockGithub({
repo: {
testValidateGithubActionsWorkflowRepo: {
files: FILES_TO_COPY_INTO_TEST_REPO,
@@ -47,7 +47,7 @@ describe('test workflow validateGithubActions', () => {
action: 'opened',
};
it('executes verification', async () => {
- const repoPath = mockGithub.repo.getPath('testValidateGithubActionsWorkflowRepo') || '';
+ const repoPath = mockGithub.repo.getPath('testValidateGithubActionsWorkflowRepo') ?? '';
const workflowPath = path.join(repoPath, '.github', 'workflows', 'validateGithubActions.yml');
let act = new ExtendedAct(repoPath, workflowPath);
act = utils.setUpActParams(act, event, eventOptions, {}, githubToken);
@@ -70,7 +70,7 @@ describe('test workflow validateGithubActions', () => {
action: 'synchronize',
};
it('executes verification', async () => {
- const repoPath = mockGithub.repo.getPath('testValidateGithubActionsWorkflowRepo') || '';
+ const repoPath = mockGithub.repo.getPath('testValidateGithubActionsWorkflowRepo') ?? '';
const workflowPath = path.join(repoPath, '.github', 'workflows', 'validateGithubActions.yml');
let act = new ExtendedAct(repoPath, workflowPath);
act = utils.setUpActParams(act, event, eventOptions, {}, githubToken);
diff --git a/workflow_tests/verifyPodfile.test.js b/workflow_tests/verifyPodfile.test.ts
similarity index 91%
rename from workflow_tests/verifyPodfile.test.js
rename to workflow_tests/verifyPodfile.test.ts
index a58e10f974e4..6124cdc856c1 100644
--- a/workflow_tests/verifyPodfile.test.js
+++ b/workflow_tests/verifyPodfile.test.ts
@@ -1,12 +1,13 @@
-const path = require('path');
-const kieMockGithub = require('@kie/mock-github');
-const utils = require('./utils/utils');
-const assertions = require('./assertions/verifyPodfileAssertions');
-const mocks = require('./mocks/verifyPodfileMocks').default;
-const ExtendedAct = require('./utils/ExtendedAct').default;
+import {MockGithub} from '@kie/mock-github';
+import path from 'path';
+import assertions from './assertions/verifyPodfileAssertions';
+import mocks from './mocks/verifyPodfileMocks';
+import ExtendedAct from './utils/ExtendedAct';
+import * as utils from './utils/utils';
jest.setTimeout(90 * 1000);
-let mockGithub;
+let mockGithub: MockGithub;
+
const FILES_TO_COPY_INTO_TEST_REPO = [
...utils.deepCopy(utils.FILES_TO_COPY_INTO_TEST_REPO),
{
@@ -20,7 +21,7 @@ describe('test workflow verifyPodfile', () => {
const actor = 'Dummy Actor';
const osbotifyActor = 'OSBotify';
- beforeAll(async () => {
+ beforeAll(() => {
// in case of the tests being interrupted without cleanup the mock repo directory may be left behind
// which breaks the next test run, this removes any possible leftovers
utils.removeMockRepoDir();
@@ -28,7 +29,7 @@ describe('test workflow verifyPodfile', () => {
beforeEach(async () => {
// create a local repository and copy required files
- mockGithub = new kieMockGithub.MockGithub({
+ mockGithub = new MockGithub({
repo: {
testVerifyPodfileWorkflowRepo: {
files: FILES_TO_COPY_INTO_TEST_REPO,
@@ -48,7 +49,7 @@ describe('test workflow verifyPodfile', () => {
action: 'opened',
};
it('executes workflow', async () => {
- const repoPath = mockGithub.repo.getPath('testVerifyPodfileWorkflowRepo') || '';
+ const repoPath = mockGithub.repo.getPath('testVerifyPodfileWorkflowRepo') ?? '';
const workflowPath = path.join(repoPath, '.github', 'workflows', 'verifyPodfile.yml');
let act = new ExtendedAct(repoPath, workflowPath);
act = utils.setUpActParams(act, event, eventOptions, {}, githubToken);
@@ -67,7 +68,7 @@ describe('test workflow verifyPodfile', () => {
});
describe('actor is OSBotify', () => {
it('does not execute workflow', async () => {
- const repoPath = mockGithub.repo.getPath('testVerifyPodfileWorkflowRepo') || '';
+ const repoPath = mockGithub.repo.getPath('testVerifyPodfileWorkflowRepo') ?? '';
const workflowPath = path.join(repoPath, '.github', 'workflows', 'verifyPodfile.yml');
let act = new ExtendedAct(repoPath, workflowPath);
act = utils.setUpActParams(act, event, eventOptions, {}, githubToken);
@@ -92,7 +93,7 @@ describe('test workflow verifyPodfile', () => {
action: 'synchronize',
};
it('executes workflow', async () => {
- const repoPath = mockGithub.repo.getPath('testVerifyPodfileWorkflowRepo') || '';
+ const repoPath = mockGithub.repo.getPath('testVerifyPodfileWorkflowRepo') ?? '';
const workflowPath = path.join(repoPath, '.github', 'workflows', 'verifyPodfile.yml');
let act = new ExtendedAct(repoPath, workflowPath);
act = utils.setUpActParams(act, event, eventOptions, {}, githubToken);
@@ -111,7 +112,7 @@ describe('test workflow verifyPodfile', () => {
});
describe('actor is OSBotify', () => {
it('does not execute workflow', async () => {
- const repoPath = mockGithub.repo.getPath('testVerifyPodfileWorkflowRepo') || '';
+ const repoPath = mockGithub.repo.getPath('testVerifyPodfileWorkflowRepo') ?? '';
const workflowPath = path.join(repoPath, '.github', 'workflows', 'verifyPodfile.yml');
let act = new ExtendedAct(repoPath, workflowPath);
act = utils.setUpActParams(act, event, eventOptions, {}, githubToken);
diff --git a/workflow_tests/verifySignedCommits.test.js b/workflow_tests/verifySignedCommits.test.ts
similarity index 86%
rename from workflow_tests/verifySignedCommits.test.js
rename to workflow_tests/verifySignedCommits.test.ts
index 4c21cd24e360..3bf6c15ec1bb 100644
--- a/workflow_tests/verifySignedCommits.test.js
+++ b/workflow_tests/verifySignedCommits.test.ts
@@ -1,12 +1,13 @@
-const path = require('path');
-const kieMockGithub = require('@kie/mock-github');
-const utils = require('./utils/utils');
-const assertions = require('./assertions/verifySignedCommitsAssertions');
-const mocks = require('./mocks/verifySignedCommitsMocks').default;
-const ExtendedAct = require('./utils/ExtendedAct').default;
+import {MockGithub} from '@kie/mock-github';
+import path from 'path';
+import assertions from './assertions/verifySignedCommitsAssertions';
+import mocks from './mocks/verifySignedCommitsMocks';
+import ExtendedAct from './utils/ExtendedAct';
+import * as utils from './utils/utils';
jest.setTimeout(90 * 1000);
-let mockGithub;
+let mockGithub: MockGithub;
+
const FILES_TO_COPY_INTO_TEST_REPO = [
...utils.deepCopy(utils.FILES_TO_COPY_INTO_TEST_REPO),
{
@@ -19,7 +20,7 @@ describe('test workflow verifySignedCommits', () => {
const githubToken = 'dummy_github_token';
const actor = 'Dummy Actor';
- beforeAll(async () => {
+ beforeAll(() => {
// in case of the tests being interrupted without cleanup the mock repo directory may be left behind
// which breaks the next test run, this removes any possible leftovers
utils.removeMockRepoDir();
@@ -27,7 +28,7 @@ describe('test workflow verifySignedCommits', () => {
beforeEach(async () => {
// create a local repository and copy required files
- mockGithub = new kieMockGithub.MockGithub({
+ mockGithub = new MockGithub({
repo: {
testVerifySignedCommitsWorkflowRepo: {
files: FILES_TO_COPY_INTO_TEST_REPO,
@@ -47,7 +48,7 @@ describe('test workflow verifySignedCommits', () => {
action: 'opened',
};
it('test stub', async () => {
- const repoPath = mockGithub.repo.getPath('testVerifySignedCommitsWorkflowRepo') || '';
+ const repoPath = mockGithub.repo.getPath('testVerifySignedCommitsWorkflowRepo') ?? '';
const workflowPath = path.join(repoPath, '.github', 'workflows', 'verifySignedCommits.yml');
let act = new ExtendedAct(repoPath, workflowPath);
act = utils.setUpActParams(act, event, eventOptions, {}, githubToken);
@@ -70,7 +71,7 @@ describe('test workflow verifySignedCommits', () => {
action: 'synchronize',
};
it('test stub', async () => {
- const repoPath = mockGithub.repo.getPath('testVerifySignedCommitsWorkflowRepo') || '';
+ const repoPath = mockGithub.repo.getPath('testVerifySignedCommitsWorkflowRepo') ?? '';
const workflowPath = path.join(repoPath, '.github', 'workflows', 'verifySignedCommits.yml');
let act = new ExtendedAct(repoPath, workflowPath);
act = utils.setUpActParams(act, event, eventOptions, {}, githubToken);