diff --git a/src/CONST.ts b/src/CONST.ts index a9160e606bd..b52a37889e0 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -976,6 +976,7 @@ const CONST = { SWITCH_REPORT_THREAD: 'switch_report_thread', SIDEBAR_LOADED: 'sidebar_loaded', LOAD_SEARCH_OPTIONS: 'load_search_options', + MESSAGE_SENT: 'message_sent', COLD: 'cold', WARM: 'warm', REPORT_ACTION_ITEM_LAYOUT_DEBOUNCE_TIME: 1500, diff --git a/src/libs/E2E/tests/reportTypingTest.e2e.ts b/src/libs/E2E/tests/reportTypingTest.e2e.ts index 9624d7ab992..efe1c380dfd 100644 --- a/src/libs/E2E/tests/reportTypingTest.e2e.ts +++ b/src/libs/E2E/tests/reportTypingTest.e2e.ts @@ -1,13 +1,16 @@ import type {NativeConfig} from 'react-native-config'; import Config from 'react-native-config'; +import {runOnUI} from 'react-native-reanimated'; import E2ELogin from '@libs/E2E/actions/e2eLogin'; import waitForAppLoaded from '@libs/E2E/actions/waitForAppLoaded'; import waitForKeyboard from '@libs/E2E/actions/waitForKeyboard'; import E2EClient from '@libs/E2E/client'; import getConfigValueOrThrow from '@libs/E2E/utils/getConfigValueOrThrow'; +import getPromiseWithResolve from '@libs/E2E/utils/getPromiseWithResolve'; import Navigation from '@libs/Navigation/Navigation'; import Performance from '@libs/Performance'; import {getRerenderCount, resetRerenderCount} from '@pages/home/report/ReportActionCompose/ComposerWithSuggestions/index.e2e'; +import {onSubmitAction} from '@pages/home/report/ReportActionCompose/ReportActionCompose'; import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; import * as NativeCommands from '../../../../tests/e2e/nativeCommands/NativeCommandsAction'; @@ -17,6 +20,7 @@ const test = (config: NativeConfig) => { console.debug('[E2E] Logging in for typing'); const reportID = getConfigValueOrThrow('reportID', config); + const message = getConfigValueOrThrow('message', config); E2ELogin().then((neededLogin) => { if (neededLogin) { @@ -28,7 +32,26 @@ const test = (config: NativeConfig) => { console.debug('[E2E] Logged in, getting typing metrics and submitting themโ€ฆ'); + const [renderTimesPromise, renderTimesResolve] = getPromiseWithResolve(); + const [messageSentPromise, messageSentResolve] = getPromiseWithResolve(); + + Promise.all([renderTimesPromise, messageSentPromise]).then(() => { + console.debug(`[E2E] Submitting!`); + + E2EClient.submitTestDone(); + }); + Performance.subscribeToMeasurements((entry) => { + if (entry.name === CONST.TIMING.MESSAGE_SENT) { + E2EClient.submitTestResults({ + branch: Config.E2E_BRANCH, + name: 'Message sent', + metric: entry.duration, + unit: 'ms', + }).then(messageSentResolve); + return; + } + if (entry.name !== CONST.TIMING.SIDEBAR_LOADED) { return; } @@ -46,18 +69,26 @@ const test = (config: NativeConfig) => { return Promise.resolve(); }) .then(() => E2EClient.sendNativeCommand(NativeCommands.makeTypeTextCommand('A'))) - .then(() => { - setTimeout(() => { - const rerenderCount = getRerenderCount(); + .then( + () => + new Promise((resolve) => { + setTimeout(() => { + const rerenderCount = getRerenderCount(); - E2EClient.submitTestResults({ - branch: Config.E2E_BRANCH, - name: 'Composer typing rerender count', - metric: rerenderCount, - unit: 'renders', - }).then(E2EClient.submitTestDone); - }, 3000); - }) + E2EClient.submitTestResults({ + branch: Config.E2E_BRANCH, + name: 'Composer typing rerender count', + metric: rerenderCount, + unit: 'renders', + }) + .then(renderTimesResolve) + .then(resolve); + }, 3000); + }), + ) + .then(() => E2EClient.sendNativeCommand(NativeCommands.makeBackspaceCommand())) + .then(() => E2EClient.sendNativeCommand(NativeCommands.makeTypeTextCommand(message))) + .then(() => runOnUI(onSubmitAction)()) .catch((error) => { console.error('[E2E] Error while test', error); E2EClient.submitTestDone(); diff --git a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx index 005824fa949..da7b4f72bfb 100644 --- a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx +++ b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx @@ -1,4 +1,5 @@ import {useNavigation} from '@react-navigation/native'; +import noop from 'lodash/noop'; import React, {memo, useCallback, useEffect, useMemo, useRef, useState} from 'react'; import type {MeasureInWindowOnSuccessCallback, NativeSyntheticEvent, TextInputFocusEventData, TextInputSelectionChangeEventData} from 'react-native'; import {View} from 'react-native'; @@ -31,6 +32,7 @@ import canFocusInputOnScreenFocus from '@libs/canFocusInputOnScreenFocus'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import {getDraftComment} from '@libs/DraftCommentUtils'; import getModalState from '@libs/getModalState'; +import Performance from '@libs/Performance'; import * as ReportUtils from '@libs/ReportUtils'; import playSound, {SOUNDS} from '@libs/Sound'; import willBlurTextInputOnTapOutsideFunc from '@libs/willBlurTextInputOnTapOutside'; @@ -116,6 +118,9 @@ const shouldFocusInputOnScreenFocus = canFocusInputOnScreenFocus(); const willBlurTextInputOnTapOutside = willBlurTextInputOnTapOutsideFunc(); +// eslint-disable-next-line import/no-mutable-exports +let onSubmitAction = noop; + function ReportActionCompose({ blockedFromConcierge, currentUserPersonalDetails, @@ -309,6 +314,7 @@ function ReportActionCompose({ Report.addAttachment(reportID, attachmentFileRef.current, newCommentTrimmed); attachmentFileRef.current = null; } else { + Performance.markStart(CONST.TIMING.MESSAGE_SENT, {message: newCommentTrimmed}); onSubmit(newCommentTrimmed); } }, @@ -390,6 +396,9 @@ function ReportActionCompose({ clearComposer(); }, [isSendDisabled, isReportReadyForDisplay, composerRefShared]); + // eslint-disable-next-line react-compiler/react-compiler + onSubmitAction = handleSendMessage; + const emojiShiftVertical = useMemo(() => { const chatItemComposeSecondaryRowHeight = styles.chatItemComposeSecondaryRow.height + styles.chatItemComposeSecondaryRow.marginTop + styles.chatItemComposeSecondaryRow.marginBottom; const reportActionComposeHeight = styles.chatItemComposeBox.minHeight + chatItemComposeSecondaryRowHeight; @@ -594,5 +603,5 @@ export default withCurrentUserPersonalDetails( }, })(memo(ReportActionCompose)), ); - +export {onSubmitAction}; export type {SuggestionsRef, ComposerRef}; diff --git a/src/pages/home/report/comment/TextCommentFragment.tsx b/src/pages/home/report/comment/TextCommentFragment.tsx index 68827de9617..9d1a4d9ad4b 100644 --- a/src/pages/home/report/comment/TextCommentFragment.tsx +++ b/src/pages/home/report/comment/TextCommentFragment.tsx @@ -1,6 +1,6 @@ import {Str} from 'expensify-common'; import {isEmpty} from 'lodash'; -import React, {memo} from 'react'; +import React, {memo, useEffect} from 'react'; import type {StyleProp, TextStyle} from 'react-native'; import Text from '@components/Text'; import ZeroWidthView from '@components/ZeroWidthView'; @@ -11,6 +11,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import convertToLTR from '@libs/convertToLTR'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import * as EmojiUtils from '@libs/EmojiUtils'; +import Performance from '@libs/Performance'; import variables from '@styles/variables'; import CONST from '@src/CONST'; import type {OriginalMessageSource} from '@src/types/onyx/OriginalMessage'; @@ -48,6 +49,10 @@ function TextCommentFragment({fragment, styleAsDeleted, styleAsMuted = false, so const {translate} = useLocalize(); const {shouldUseNarrowLayout} = useResponsiveLayout(); + useEffect(() => { + Performance.markEnd(CONST.TIMING.MESSAGE_SENT, {message: text}); + }, [text]); + // If the only difference between fragment.text and fragment.html is
tags and emoji tag // on native, we render it as text, not as html // on other device, only render it as text if the only difference is
tag diff --git a/tests/e2e/config.ts b/tests/e2e/config.ts index 8963e07c31c..bdb24bee0bc 100644 --- a/tests/e2e/config.ts +++ b/tests/e2e/config.ts @@ -83,6 +83,7 @@ export default { }, // Crowded Policy (Do Not Delete) Report, has a input bar available: reportID: '8268282951170052', + message: `Measure_performance#${Math.floor(Math.random() * 1000000)}`, }, [TEST_NAMES.ChatOpening]: { name: TEST_NAMES.ChatOpening, diff --git a/tests/e2e/nativeCommands/adbBackspace.ts b/tests/e2e/nativeCommands/adbBackspace.ts index 2891d1daf0e..112a80bdb37 100644 --- a/tests/e2e/nativeCommands/adbBackspace.ts +++ b/tests/e2e/nativeCommands/adbBackspace.ts @@ -1,10 +1,9 @@ import execAsync from '../utils/execAsync'; import * as Logger from '../utils/logger'; -const adbBackspace = () => { +const adbBackspace = (): Promise => { Logger.log(`๐Ÿ”™ Pressing backspace`); - execAsync(`adb shell input keyevent KEYCODE_DEL`); - return true; + return execAsync(`adb shell input keyevent KEYCODE_DEL`).then(() => true); }; export default adbBackspace; diff --git a/tests/e2e/nativeCommands/adbTypeText.ts b/tests/e2e/nativeCommands/adbTypeText.ts index 72fefbd25d2..deea16b198c 100644 --- a/tests/e2e/nativeCommands/adbTypeText.ts +++ b/tests/e2e/nativeCommands/adbTypeText.ts @@ -3,8 +3,7 @@ import * as Logger from '../utils/logger'; const adbTypeText = (text: string) => { Logger.log(`๐Ÿ“ Typing text: ${text}`); - execAsync(`adb shell input text "${text}"`); - return true; + return execAsync(`adb shell input text "${text}"`).then(() => true); }; export default adbTypeText; diff --git a/tests/e2e/nativeCommands/index.ts b/tests/e2e/nativeCommands/index.ts index 31af618c8ec..310aa2ab3c2 100644 --- a/tests/e2e/nativeCommands/index.ts +++ b/tests/e2e/nativeCommands/index.ts @@ -4,7 +4,7 @@ import adbTypeText from './adbTypeText'; // eslint-disable-next-line rulesdir/prefer-import-module-contents import {NativeCommandsAction} from './NativeCommandsAction'; -const executeFromPayload = (actionName?: string, payload?: NativeCommandPayload): boolean => { +const executeFromPayload = (actionName?: string, payload?: NativeCommandPayload): Promise => { switch (actionName) { case NativeCommandsAction.scroll: throw new Error('Not implemented yet'); diff --git a/tests/e2e/server/index.ts b/tests/e2e/server/index.ts index 494b8b4644e..51249290804 100644 --- a/tests/e2e/server/index.ts +++ b/tests/e2e/server/index.ts @@ -169,8 +169,8 @@ const createServerInstance = (): ServerInstance => { case Routes.testNativeCommand: { getPostJSONRequestData(req, res) - ?.then((data) => { - const status = nativeCommands.executeFromPayload(data?.actionName, data?.payload); + ?.then((data) => nativeCommands.executeFromPayload(data?.actionName, data?.payload)) + .then((status) => { if (status) { res.end('ok'); return;