From 9aaf7c0d09279fb10dec9c18a8a7e2b6317276c6 Mon Sep 17 00:00:00 2001 From: Shridhar Goel <35566748+ShridharGoel@users.noreply.github.com> Date: Sun, 25 Aug 2024 20:29:48 +0000 Subject: [PATCH] Fix all lint and typecheck issues --- .../authorChecklist/authorChecklist.ts | 2 +- .../createOrUpdateStagingDeploy.ts | 9 ++ .../markPullRequestsAsDeployed.ts | 2 +- .../reviewerChecklist/reviewerChecklist.ts | 10 +- .../validateReassureOutput.ts | 5 + .github/libs/GithubUtils.ts | 12 ++- .github/scripts/createDocsRoutes.ts | 2 +- jest/setup.ts | 2 +- .../AttachmentPicker/index.native.tsx | 7 +- .../AttachmentCarousel/index.native.tsx | 12 ++- .../Attachments/AttachmentCarousel/index.tsx | 5 +- .../AttachmentViewPdf/index.android.tsx | 8 +- src/components/AvatarWithDisplayName.tsx | 3 +- .../ButtonWithDropdownMenu/index.tsx | 23 +++-- src/components/Composer/index.native.tsx | 6 +- .../CalendarPicker/generateMonthMatrix.ts | 10 +- .../DatePicker/CalendarPicker/index.tsx | 2 +- .../EmojiPickerMenu/index.native.tsx | 2 +- .../EmojiPicker/EmojiPickerMenu/index.tsx | 4 +- .../HTMLRenderers/MentionUserRenderer.tsx | 6 +- src/components/InteractiveStepSubHeader.tsx | 5 +- .../LHNOptionsList/OptionRowLHN.tsx | 3 +- src/components/MapView/utils.ts | 8 +- src/components/MentionSuggestions.tsx | 10 +- src/components/MenuItem.tsx | 3 +- .../MoneyRequestConfirmationList.tsx | 4 +- .../MultiGestureCanvas/usePanGesture.ts | 8 +- src/components/MultipleAvatars.tsx | 50 +++++----- src/components/OptionRow.tsx | 3 +- .../OptionsList/BaseOptionsList.tsx | 6 +- src/components/Picker/BasePicker.tsx | 5 +- src/components/PopoverMenu.tsx | 3 + .../MoneyRequestPreviewContent.tsx | 3 +- .../ReportActionItem/MoneyRequestView.tsx | 10 +- .../ReportActionItem/TripDetailsView.tsx | 2 +- src/components/RoomHeaderAvatars.tsx | 21 +++-- .../SelectionList/InviteMemberListItem.tsx | 5 +- .../SelectionList/Search/ReportListItem.tsx | 2 +- .../Search/TransactionListItemRow.tsx | 2 +- src/components/SelectionList/UserListItem.tsx | 3 +- src/components/TaxPicker.tsx | 2 +- src/components/WorkspaceSwitcherButton.tsx | 3 +- src/hooks/useReviewDuplicatesNavigation.tsx | 5 +- src/hooks/useSubStep/index.ts | 3 +- src/hooks/useViolations.ts | 2 +- src/libs/CollectionUtils.ts | 2 +- src/libs/EmojiUtils.ts | 9 +- src/libs/GooglePlacesUtils.ts | 2 +- src/libs/LocaleDigitUtils.ts | 4 +- src/libs/Middleware/Pagination.ts | 3 +- .../createCustomBottomTabNavigator/index.tsx | 2 +- .../createCustomStackNavigator/index.tsx | 6 +- .../linkingConfig/getAdaptedStateFromPath.ts | 4 +- .../getMatchingCentralPaneRouteForState.ts | 6 +- src/libs/Network/SequentialQueue.ts | 4 + src/libs/OptionsListUtils.ts | 23 ++++- src/libs/PaginationUtils.ts | 31 +++++-- src/libs/PersonalDetailsUtils.ts | 4 +- src/libs/PolicyUtils.ts | 7 +- src/libs/ReportActionsUtils.ts | 18 ++-- src/libs/ReportUtils.ts | 18 ++-- src/libs/SelectionScraper/index.ts | 5 +- src/libs/SidebarUtils.ts | 6 +- src/libs/actions/IOU.ts | 21 +++-- .../actions/OnyxUpdateManager/utils/index.ts | 2 +- src/libs/actions/Policy/DistanceRate.ts | 4 +- src/libs/actions/Policy/Policy.ts | 12 +-- src/libs/actions/Policy/Tag.ts | 48 +++++++--- src/libs/actions/Report.ts | 10 +- src/libs/actions/TeachersUnite.ts | 2 +- src/libs/actions/Transaction.ts | 6 +- src/libs/actions/Workflow.ts | 2 +- src/libs/fileDownload/FileUtils.ts | 4 +- src/libs/memoize/cache/ArrayCache.ts | 3 +- src/pages/EnablePayments/IdologyQuestions.tsx | 2 +- src/pages/NewChatPage.tsx | 2 +- src/pages/ReportDetailsPage.tsx | 4 +- src/pages/home/HeaderView.tsx | 3 +- .../ReportActionCompose/SuggestionEmoji.tsx | 6 +- .../ReportActionCompose/SuggestionMention.tsx | 3 + .../home/report/ReportActionItemSingle.tsx | 5 +- src/pages/home/report/ReportActionsList.tsx | 6 ++ src/pages/home/report/ReportActionsView.tsx | 12 ++- .../iou/request/step/IOURequestStepAmount.tsx | 8 +- .../step/IOURequestStepConfirmation.tsx | 13 ++- .../request/step/IOURequestStepDistance.tsx | 5 +- .../step/IOURequestStepScan/index.native.tsx | 16 +++- .../request/step/IOURequestStepScan/index.tsx | 18 +++- .../settings/Wallet/PaymentMethodList.tsx | 6 +- src/pages/workspace/WorkspacesListPage.tsx | 10 +- .../qbo/advanced/QuickbooksAdvancedPage.tsx | 2 +- ...ompanyCardExpenseAccountSelectCardPage.tsx | 2 +- .../ReportFieldsInitialListValuePicker.tsx | 2 +- .../ReportFieldTypePicker/index.tsx | 2 +- .../tags/WorkspaceTagsSettingsPage.tsx | 2 +- .../workspace/tags/WorkspaceViewTagsPage.tsx | 2 +- .../workflows/WorkspaceWorkflowsPage.tsx | 2 +- .../workflows/WorkspaceWorkflowsPayerPage.tsx | 2 +- ...orkspaceWorkflowsApprovalsApproverPage.tsx | 4 +- ...paceWorkflowsApprovalsExpensesFromPage.tsx | 2 +- src/stories/PromotedActionBar.stories.tsx | 7 +- src/styles/utils/index.ts | 6 +- src/utils/getDefaultIcon.ts | 12 +++ tests/actions/IOUTest.ts | 6 +- tests/actions/PolicyCategoryTest.ts | 4 +- tests/actions/PolicyTagTest.ts | 8 +- tests/actions/ReportTest.ts | 20 ++-- tests/e2e/compare/output/markdownTable.ts | 20 ++-- tests/e2e/measure/math.ts | 5 +- tests/e2e/testRunner.ts | 6 +- tests/ui/UnreadIndicatorsTest.tsx | 7 +- tests/unit/APITest.ts | 53 +++++------ tests/unit/CalendarPickerTest.tsx | 4 +- tests/unit/GithubUtilsTest.ts | 6 +- tests/unit/MiddlewareTest.ts | 9 +- tests/unit/NetworkTest.ts | 4 +- tests/unit/OptionsListUtilsTest.ts | 92 +++++++++---------- tests/unit/ReportUtilsTest.ts | 56 +++++------ tests/unit/RequestTest.ts | 6 +- tests/unit/WorkflowUtilsTest.ts | 23 +++-- tests/unit/generateMonthMatrixTest.ts | 4 +- tests/unit/versionUpdaterTest.ts | 4 +- tests/utils/ReportTestUtils.ts | 2 +- 123 files changed, 654 insertions(+), 422 deletions(-) create mode 100644 src/utils/getDefaultIcon.ts diff --git a/.github/actions/javascript/authorChecklist/authorChecklist.ts b/.github/actions/javascript/authorChecklist/authorChecklist.ts index 5fdda5cb7285..ea6d879f12f5 100644 --- a/.github/actions/javascript/authorChecklist/authorChecklist.ts +++ b/.github/actions/javascript/authorChecklist/authorChecklist.ts @@ -54,7 +54,7 @@ function partitionWithChecklist(body: string): string[] { async function getNumberOfItemsFromAuthorChecklist(): Promise { const response = await fetch(pathToAuthorChecklist); const fileContents = await response.text(); - const checklist = partitionWithChecklist(fileContents).at(1); + const checklist = partitionWithChecklist(fileContents).at(1) ?? ''; const numberOfChecklistItems = (checklist.match(/\[ \]/g) ?? []).length; return numberOfChecklistItems; } diff --git a/.github/actions/javascript/createOrUpdateStagingDeploy/createOrUpdateStagingDeploy.ts b/.github/actions/javascript/createOrUpdateStagingDeploy/createOrUpdateStagingDeploy.ts index ff6c94ae7930..57afa326ea01 100644 --- a/.github/actions/javascript/createOrUpdateStagingDeploy/createOrUpdateStagingDeploy.ts +++ b/.github/actions/javascript/createOrUpdateStagingDeploy/createOrUpdateStagingDeploy.ts @@ -30,6 +30,11 @@ async function run(): Promise { // Look at the state of the most recent StagingDeployCash, // if it is open then we'll update the existing one, otherwise, we'll create a new one. const mostRecentChecklist = recentDeployChecklists.at(0); + + if (!mostRecentChecklist) { + return; + } + const shouldCreateNewDeployChecklist = mostRecentChecklist.state !== 'open'; const previousChecklist = shouldCreateNewDeployChecklist ? mostRecentChecklist : recentDeployChecklists.at(1); if (shouldCreateNewDeployChecklist) { @@ -38,6 +43,10 @@ async function run(): Promise { console.log('Latest StagingDeployCash is open, updating it instead of creating a new one.', 'Current:', mostRecentChecklist, 'Previous:', previousChecklist); } + if (!previousChecklist) { + return; + } + // Parse the data from the previous and current checklists into the format used to generate the checklist const previousChecklistData = GithubUtils.getStagingDeployCashData(previousChecklist); const currentChecklistData: StagingDeployCashData | undefined = shouldCreateNewDeployChecklist ? undefined : GithubUtils.getStagingDeployCashData(mostRecentChecklist); diff --git a/.github/actions/javascript/markPullRequestsAsDeployed/markPullRequestsAsDeployed.ts b/.github/actions/javascript/markPullRequestsAsDeployed/markPullRequestsAsDeployed.ts index 79d278b001c4..1a69c29078a1 100644 --- a/.github/actions/javascript/markPullRequestsAsDeployed/markPullRequestsAsDeployed.ts +++ b/.github/actions/javascript/markPullRequestsAsDeployed/markPullRequestsAsDeployed.ts @@ -78,7 +78,7 @@ async function run() { labels: CONST.LABELS.STAGING_DEPLOY, state: 'closed', }); - const previousChecklistID = deployChecklists.at(0).number; + const previousChecklistID = deployChecklists.at(0)?.number ?? 0; // who closed the last deploy checklist? const deployer = await GithubUtils.getActorWhoClosedIssue(previousChecklistID); diff --git a/.github/actions/javascript/reviewerChecklist/reviewerChecklist.ts b/.github/actions/javascript/reviewerChecklist/reviewerChecklist.ts index 336055271330..2d2f3978fa1d 100644 --- a/.github/actions/javascript/reviewerChecklist/reviewerChecklist.ts +++ b/.github/actions/javascript/reviewerChecklist/reviewerChecklist.ts @@ -55,16 +55,16 @@ function checkIssueForCompletedChecklist(numberOfChecklistItems: number) { } const whitespace = /([\n\r])/gm; - const comment = combinedComments.at(i).replace(whitespace, ''); + const comment = combinedComments.at(i)?.replace(whitespace, ''); - console.log(`Comment ${i} starts with: ${comment.slice(0, 20)}...`); + console.log(`Comment ${i} starts with: ${comment?.slice(0, 20)}...`); // Found the reviewer checklist, so count how many completed checklist items there are - if (comment.indexOf(reviewerChecklistContains) !== -1) { + if (comment?.indexOf(reviewerChecklistContains) !== -1) { console.log('Found the reviewer checklist!'); foundReviewerChecklist = true; - numberOfFinishedChecklistItems = (comment.match(/- \[x\]/gi) ?? []).length; - numberOfUnfinishedChecklistItems = (comment.match(/- \[ \]/g) ?? []).length; + numberOfFinishedChecklistItems = (comment?.match(/- \[x\]/gi) ?? []).length; + numberOfUnfinishedChecklistItems = (comment?.match(/- \[ \]/g) ?? []).length; } } diff --git a/.github/actions/javascript/validateReassureOutput/validateReassureOutput.ts b/.github/actions/javascript/validateReassureOutput/validateReassureOutput.ts index d6b7f2c61b55..8eb089ef4bb9 100644 --- a/.github/actions/javascript/validateReassureOutput/validateReassureOutput.ts +++ b/.github/actions/javascript/validateReassureOutput/validateReassureOutput.ts @@ -16,6 +16,11 @@ const run = (): boolean => { for (let i = 0; i < regressionOutput.countChanged.length; i++) { const measurement = regressionOutput.countChanged.at(i); + + if (!measurement) { + continue; + } + const baseline: PerformanceEntry = measurement.baseline; const current: PerformanceEntry = measurement.current; diff --git a/.github/libs/GithubUtils.ts b/.github/libs/GithubUtils.ts index eae037e30e5c..ae74621b356a 100644 --- a/.github/libs/GithubUtils.ts +++ b/.github/libs/GithubUtils.ts @@ -168,7 +168,13 @@ class GithubUtils { throw new Error(`Found more than one ${CONST.LABELS.STAGING_DEPLOY} issue.`); } - return this.getStagingDeployCashData(data.at(0)); + const issue = data.at(0); + + if (!issue) { + throw new Error(`Found an undefined ${CONST.LABELS.STAGING_DEPLOY} issue.`); + } + + return this.getStagingDeployCashData(issue); }); } @@ -254,7 +260,7 @@ class GithubUtils { } internalQASection = internalQASection[1]; const internalQAPRs = [...internalQASection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST.PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2].split('-').at(0).trim(), + url: match[2].split('-').at(0)?.trim() ?? '', number: Number.parseInt(match[3], 10), isResolved: match[1] === 'x', })); @@ -459,7 +465,7 @@ class GithubUtils { repo: CONST.APP_REPO, workflow_id: workflow, }) - .then((response) => response.data.workflow_runs.at(0)?.id); + .then((response) => response.data.workflow_runs.at(0)?.id ?? -1); } /** diff --git a/.github/scripts/createDocsRoutes.ts b/.github/scripts/createDocsRoutes.ts index b61a1a44775c..5f93f772c2c6 100644 --- a/.github/scripts/createDocsRoutes.ts +++ b/.github/scripts/createDocsRoutes.ts @@ -94,7 +94,7 @@ function pushOrCreateEntry(hubs: Hub[], hub: string, function getOrderFromArticleFrontMatter(path: string): number | undefined { const frontmatter = fs.readFileSync(path, 'utf8').split('---').at(1); - const frontmatterObject = yaml.load(frontmatter) as Record; + const frontmatterObject = yaml.load(frontmatter ?? '') as Record; return frontmatterObject.order as number | undefined; } diff --git a/jest/setup.ts b/jest/setup.ts index 0308d021db84..6b6dae599905 100644 --- a/jest/setup.ts +++ b/jest/setup.ts @@ -34,7 +34,7 @@ jest.mock('react-native/Libraries/LogBox/LogBox', () => ({ // Turn off the console logs for timing events. They are not relevant for unit tests and create a lot of noise jest.spyOn(console, 'debug').mockImplementation((...params: string[]) => { - if (params.at(0).startsWith('Timing:')) { + if (params.at(0)?.startsWith('Timing:')) { return; } diff --git a/src/components/AttachmentPicker/index.native.tsx b/src/components/AttachmentPicker/index.native.tsx index e6d6108d4016..af5e19278c59 100644 --- a/src/components/AttachmentPicker/index.native.tsx +++ b/src/components/AttachmentPicker/index.native.tsx @@ -366,8 +366,11 @@ function AttachmentPicker({type = CONST.ATTACHMENT_PICKER_TYPE.FILE, children, s if (focusedIndex === -1) { return; } - selectItem(menuItemData.at(focusedIndex)); - setFocusedIndex(-1); // Reset the focusedIndex on selecting any menu + const item = menuItemData.at(focusedIndex); + if (item) { + selectItem(item); + setFocusedIndex(-1); // Reset the focusedIndex on selecting any menu + } }, { isActive: isVisible, diff --git a/src/components/Attachments/AttachmentCarousel/index.native.tsx b/src/components/Attachments/AttachmentCarousel/index.native.tsx index 37be97ce660d..cd77db4d9805 100644 --- a/src/components/Attachments/AttachmentCarousel/index.native.tsx +++ b/src/components/Attachments/AttachmentCarousel/index.native.tsx @@ -52,9 +52,11 @@ function AttachmentCarousel({report, reportActions, parentReportActions, source, setDownloadButtonVisibility(initialPage !== -1); } + const attachment = targetAttachments.at(initialPage); + // Update the parent modal's state with the source and name from the mapped attachments - if (targetAttachments.at(initialPage) !== undefined && onNavigate) { - onNavigate(targetAttachments.at(initialPage)); + if (attachment !== undefined && onNavigate) { + onNavigate(attachment); } } // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps @@ -69,9 +71,11 @@ function AttachmentCarousel({report, reportActions, parentReportActions, source, const item = attachments.at(newPageIndex); setPage(newPageIndex); - setActiveSource(item.source); + if (item) { + setActiveSource(item.source); + } - if (onNavigate) { + if (onNavigate && item) { onNavigate(item); } }, diff --git a/src/components/Attachments/AttachmentCarousel/index.tsx b/src/components/Attachments/AttachmentCarousel/index.tsx index 410527157ab1..bb200bd20390 100644 --- a/src/components/Attachments/AttachmentCarousel/index.tsx +++ b/src/components/Attachments/AttachmentCarousel/index.tsx @@ -100,9 +100,10 @@ function AttachmentCarousel({report, reportActions, parentReportActions, source, setDownloadButtonVisibility(initialPage !== -1); } + const attachment = targetAttachments.at(initialPage); // Update the parent modal's state with the source and name from the mapped attachments - if (targetAttachments.at(initialPage) !== undefined && onNavigate) { - onNavigate(targetAttachments.at(initialPage)); + if (attachment !== undefined && onNavigate) { + onNavigate(attachment); } } }, [report.privateNotes, reportActions, parentReportActions, compareImage, report.parentReportActionID, attachments, setDownloadButtonVisibility, onNavigate, accountID, type]); diff --git a/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.android.tsx b/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.android.tsx index 4114f879f638..1e3cded92bd5 100644 --- a/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.android.tsx +++ b/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.android.tsx @@ -33,8 +33,8 @@ function AttachmentViewPdf(props: AttachmentViewPdfProps) { .manualActivation(true) .onTouchesMove((evt) => { if (offsetX.value !== 0 && offsetY.value !== 0 && isScrollEnabled) { - const translateX = Math.abs(evt.allTouches.at(0).absoluteX - offsetX.value); - const translateY = Math.abs(evt.allTouches.at(0).absoluteY - offsetY.value); + const translateX = Math.abs((evt.allTouches.at(0)?.absoluteX ?? 0) - offsetX.value); + const translateY = Math.abs((evt.allTouches.at(0)?.absoluteY ?? 0) - offsetY.value); const allowEnablingScroll = !isPanGestureActive.value || isScrollEnabled.value; // if the value of X is greater than Y and the pdf is not zoomed in, @@ -49,8 +49,8 @@ function AttachmentViewPdf(props: AttachmentViewPdfProps) { } isPanGestureActive.value = true; - offsetX.value = evt.allTouches.at(0).absoluteX; - offsetY.value = evt.allTouches.at(0).absoluteY; + offsetX.value = evt.allTouches.at(0)?.absoluteX ?? 0; + offsetY.value = evt.allTouches.at(0)?.absoluteY ?? 0; }) .onTouchesUp(() => { isPanGestureActive.value = false; diff --git a/src/components/AvatarWithDisplayName.tsx b/src/components/AvatarWithDisplayName.tsx index 1ba1a6158505..78a8dd067748 100644 --- a/src/components/AvatarWithDisplayName.tsx +++ b/src/components/AvatarWithDisplayName.tsx @@ -13,6 +13,7 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {PersonalDetails, PersonalDetailsList, Policy, Report, ReportActions} from '@src/types/onyx'; +import fallbackIcon from '@src/utils/getDefaultIcon'; import CaretWrapper from './CaretWrapper'; import DisplayNames from './DisplayNames'; import MultipleAvatars from './MultipleAvatars'; @@ -121,7 +122,7 @@ function AvatarWithDisplayName({ {shouldShowSubscriptAvatar ? ( diff --git a/src/components/ButtonWithDropdownMenu/index.tsx b/src/components/ButtonWithDropdownMenu/index.tsx index bc432e66d232..763c2dd127f6 100644 --- a/src/components/ButtonWithDropdownMenu/index.tsx +++ b/src/components/ButtonWithDropdownMenu/index.tsx @@ -83,9 +83,14 @@ function ButtonWithDropdownMenu({ setIsMenuVisible(!isMenuVisible); return; } - onPress(e, selectedItem?.value); + if (selectedItem?.value) { + onPress(e, selectedItem?.value); + } } else { - onPress(e, options.at(0)?.value); + const option = options.at(0); + if (option?.value) { + onPress(e, option?.value); + } } }, { @@ -103,8 +108,9 @@ function ButtonWithDropdownMenu({ success={success} pressOnEnter={pressOnEnter} ref={dropdownButtonRef} - onPress={(event) => (!isSplitButton ? setIsMenuVisible(!isMenuVisible) : onPress(event, selectedItem.value))} - text={customText ?? selectedItem.text} + // eslint-disable-next-line no-nested-ternary + onPress={(event) => (!isSplitButton ? setIsMenuVisible(!isMenuVisible) : selectedItem?.value ? onPress(event, selectedItem?.value) : undefined)} + text={customText ?? selectedItem?.text ?? ''} isDisabled={isDisabled || !!selectedItem?.disabled} isLoading={isLoading} shouldRemoveRightBorderRadius @@ -150,11 +156,14 @@ function ButtonWithDropdownMenu({ success={success} ref={buttonRef} pressOnEnter={pressOnEnter} - isDisabled={isDisabled || !!options.at(0).disabled} + isDisabled={isDisabled || !!options.at(0)?.disabled} style={[styles.w100, style]} isLoading={isLoading} - text={selectedItem.text} - onPress={(event) => onPress(event, options.at(0).value)} + text={selectedItem?.text} + onPress={(event) => { + const option = options.at(0); + return option ? onPress(event, option.value) : undefined; + }} large={isButtonSizeLarge} medium={!isButtonSizeLarge} innerStyles={[innerStyleDropButton]} diff --git a/src/components/Composer/index.native.tsx b/src/components/Composer/index.native.tsx index f270021bc7a3..0160b51557f6 100644 --- a/src/components/Composer/index.native.tsx +++ b/src/components/Composer/index.native.tsx @@ -94,9 +94,9 @@ function Composer( if (clipboardContent?.type === 'text/plain') { return; } - const mimeType = clipboardContent.type; - const fileURI = clipboardContent.data; - const baseFileName = fileURI.split('/').pop() ?? 'file'; + const mimeType = clipboardContent?.type; + const fileURI = clipboardContent?.data; + const baseFileName = fileURI?.split('/').pop() ?? 'file'; const {fileName: stem, fileExtension: originalFileExtension} = FileUtils.splitExtensionFromFileName(baseFileName); const fileExtension = originalFileExtension || (mimeDb[mimeType].extensions?.[0] ?? 'bin'); const fileName = `${stem}.${fileExtension}`; diff --git a/src/components/DatePicker/CalendarPicker/generateMonthMatrix.ts b/src/components/DatePicker/CalendarPicker/generateMonthMatrix.ts index 5fc03cba8656..692e150c6303 100644 --- a/src/components/DatePicker/CalendarPicker/generateMonthMatrix.ts +++ b/src/components/DatePicker/CalendarPicker/generateMonthMatrix.ts @@ -50,9 +50,15 @@ export default function generateMonthMatrix(year: number, month: number) { matrix.push(currentWeek); } + const initial = matrix.at(0); + + if (!initial) { + return; + } + // Add null values for days before the first day of the month - for (let i = matrix.at(0).length; i < 7; i++) { - matrix.at(0).unshift(undefined); + for (let i = initial.length; i < 7; i++) { + initial.unshift(undefined); } return matrix; diff --git a/src/components/DatePicker/CalendarPicker/index.tsx b/src/components/DatePicker/CalendarPicker/index.tsx index 69abb8f13073..287ec3359175 100644 --- a/src/components/DatePicker/CalendarPicker/index.tsx +++ b/src/components/DatePicker/CalendarPicker/index.tsx @@ -208,7 +208,7 @@ function CalendarPicker({ ))} - {calendarDaysMatrix.map((week) => ( + {calendarDaysMatrix?.map((week) => ( ); diff --git a/src/components/EmojiPicker/EmojiPickerMenu/index.tsx b/src/components/EmojiPicker/EmojiPickerMenu/index.tsx index 5e29520b91fa..4226b5e38280 100755 --- a/src/components/EmojiPicker/EmojiPickerMenu/index.tsx +++ b/src/components/EmojiPicker/EmojiPickerMenu/index.tsx @@ -154,7 +154,7 @@ function EmojiPickerMenu({onEmojiSelected, activeEmoji}: EmojiPickerMenuProps, r } if ('types' in item || 'name' in item) { const emoji = typeof preferredSkinTone === 'number' && item?.types?.[preferredSkinTone] ? item?.types?.at(preferredSkinTone) : item.code; - onEmojiSelected(emoji, item); + onEmojiSelected(emoji ?? '', item); } // On web, avoid this Enter default input action; otherwise, it will add a new line in the subsequently focused composer. keyBoardEvent.preventDefault(); @@ -280,7 +280,7 @@ function EmojiPickerMenu({onEmojiSelected, activeEmoji}: EmojiPickerMenuProps, r } setIsUsingKeyboardMovement(false); }} - emoji={emojiCode} + emoji={emojiCode ?? ''} onFocus={() => setFocusedIndex(index)} isFocused={isEmojiFocused} isHighlighted={shouldFirstEmojiBeHighlighted || shouldEmojiBeHighlighted} diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.tsx b/src/components/HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.tsx index eefd989fa8d5..7e1f72acfd97 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.tsx +++ b/src/components/HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.tsx @@ -60,15 +60,15 @@ function MentionUserRenderer({style, tnode, TDefaultRenderer, currentUserPersona const user = personalDetails[htmlAttribAccountID]; accountID = parseInt(htmlAttribAccountID, 10); mentionDisplayText = LocalePhoneNumber.formatPhoneNumber(user?.login ?? '') || PersonalDetailsUtils.getDisplayNameOrDefault(user); - mentionDisplayText = getShortMentionIfFound(mentionDisplayText, htmlAttributeAccountID, user?.login ?? ''); + mentionDisplayText = getShortMentionIfFound(mentionDisplayText, htmlAttributeAccountID, user?.login ?? '') ?? ''; navigationRoute = ROUTES.PROFILE.getRoute(htmlAttribAccountID); } else if ('data' in tnodeClone && !isEmptyObject(tnodeClone.data)) { // We need to remove the LTR unicode and leading @ from data as it is not part of the login mentionDisplayText = tnodeClone.data.replace(CONST.UNICODE.LTR, '').slice(1); // We need to replace tnode.data here because we will pass it to TNodeChildrenRenderer below - asMutable(tnodeClone).data = tnodeClone.data.replace(mentionDisplayText, Str.removeSMSDomain(getShortMentionIfFound(mentionDisplayText, htmlAttributeAccountID))); + asMutable(tnodeClone).data = tnodeClone.data.replace(mentionDisplayText, Str.removeSMSDomain(getShortMentionIfFound(mentionDisplayText, htmlAttributeAccountID) ?? '')); - accountID = PersonalDetailsUtils.getAccountIDsByLogins([mentionDisplayText])?.at(0); + accountID = PersonalDetailsUtils.getAccountIDsByLogins([mentionDisplayText])?.at(0) ?? -1; navigationRoute = ROUTES.PROFILE.getRoute(accountID, undefined, mentionDisplayText); mentionDisplayText = Str.removeSMSDomain(mentionDisplayText); } else { diff --git a/src/components/InteractiveStepSubHeader.tsx b/src/components/InteractiveStepSubHeader.tsx index adb569a9719d..81c4659c0c72 100644 --- a/src/components/InteractiveStepSubHeader.tsx +++ b/src/components/InteractiveStepSubHeader.tsx @@ -70,7 +70,10 @@ function InteractiveStepSubHeader({stepNames, startStepIndex = 0, onStepSelected return; } setCurrentStep(index); - onStepSelected(stepNames.at(index)); + const step = stepNames.at(index); + if (step) { + onStepSelected(step); + } }; return ( diff --git a/src/components/LHNOptionsList/OptionRowLHN.tsx b/src/components/LHNOptionsList/OptionRowLHN.tsx index 15786d275ddc..d82256c76fcb 100644 --- a/src/components/LHNOptionsList/OptionRowLHN.tsx +++ b/src/components/LHNOptionsList/OptionRowLHN.tsx @@ -31,6 +31,7 @@ import * as ReportActionContextMenu from '@pages/home/report/ContextMenu/ReportA import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; +import fallbackIcon from '@src/utils/getDefaultIcon'; import type {OptionRowLHNProps} from './types'; function OptionRowLHN({reportID, isFocused = false, onSelectRow = () => {}, optionItem, viewMode = 'default', style, onLayout = () => {}, hasDraftComment}: OptionRowLHNProps) { @@ -192,7 +193,7 @@ function OptionRowLHN({reportID, isFocused = false, onSelectRow = () => {}, opti (optionItem.shouldShowSubscript ? ( diff --git a/src/components/MapView/utils.ts b/src/components/MapView/utils.ts index 165ec05a6d29..ae35bdfdeba9 100644 --- a/src/components/MapView/utils.ts +++ b/src/components/MapView/utils.ts @@ -18,10 +18,10 @@ function getBounds(waypoints: Array<[number, number]>, directionCoordinates: und function haversineDistance(coordinate1: number[], coordinate2: number[]) { // Radius of the Earth in meters const R = 6371e3; - const lat1 = (coordinate1.at(0) * Math.PI) / 180; - const lat2 = (coordinate2.at(0) * Math.PI) / 180; - const deltaLat = ((coordinate2.at(0) - coordinate1.at(0)) * Math.PI) / 180; - const deltaLon = ((coordinate2.at(1) - coordinate1.at(1)) * Math.PI) / 180; + const lat1 = ((coordinate1.at(0) ?? 0) * Math.PI) / 180; + const lat2 = ((coordinate2.at(0) ?? 0) * Math.PI) / 180; + const deltaLat = (((coordinate2.at(0) ?? 0) - (coordinate1.at(0) ?? 0)) * Math.PI) / 180; + const deltaLon = (((coordinate2.at(1) ?? 0) - (coordinate1.at(1) ?? 0)) * Math.PI) / 180; // The square of half the chord length between the points const halfChordLengthSq = Math.sin(deltaLat / 2) * Math.sin(deltaLat / 2) + Math.cos(lat1) * Math.cos(lat2) * Math.sin(deltaLon / 2) * Math.sin(deltaLon / 2); diff --git a/src/components/MentionSuggestions.tsx b/src/components/MentionSuggestions.tsx index 3b4583ed48b9..142813355118 100644 --- a/src/components/MentionSuggestions.tsx +++ b/src/components/MentionSuggestions.tsx @@ -91,13 +91,13 @@ function MentionSuggestions({ {item.icons && !!item.icons.length && ( )} diff --git a/src/components/MenuItem.tsx b/src/components/MenuItem.tsx index fa0c8ffa5b66..45ecbbc2d80d 100644 --- a/src/components/MenuItem.tsx +++ b/src/components/MenuItem.tsx @@ -21,6 +21,7 @@ import CONST from '@src/CONST'; import type {Icon as IconType} from '@src/types/onyx/OnyxCommon'; import type {TooltipAnchorAlignment} from '@src/types/utils/AnchorAlignment'; import type IconAsset from '@src/types/utils/IconAsset'; +import getDefaultIcon from '@src/utils/getDefaultIcon'; import Avatar from './Avatar'; import Badge from './Badge'; import DisplayNames from './DisplayNames'; @@ -760,7 +761,7 @@ function MenuItem( {shouldShowSubscriptRightAvatar ? ( diff --git a/src/components/MoneyRequestConfirmationList.tsx b/src/components/MoneyRequestConfirmationList.tsx index 3c2f9f49ed64..1c12a83c4cac 100755 --- a/src/components/MoneyRequestConfirmationList.tsx +++ b/src/components/MoneyRequestConfirmationList.tsx @@ -686,7 +686,7 @@ function MoneyRequestConfirmationList({ if (iouCategory || !shouldShowCategories || enabledCategories.length !== 1 || !isCategoryRequired) { return; } - IOU.setMoneyRequestCategory(transactionID, enabledCategories.at(0).name); + IOU.setMoneyRequestCategory(transactionID, enabledCategories.at(0)?.name ?? ''); // Keep 'transaction' out to ensure that we autoselect the option only once // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps }, [shouldShowCategories, policyCategories, isCategoryRequired]); @@ -700,7 +700,7 @@ function MoneyRequestConfirmationList({ if (!isTagListRequired || enabledTags.length !== 1 || TransactionUtils.getTag(transaction, index)) { return; } - updatedTagsString = IOUUtils.insertTagIntoTransactionTagsString(updatedTagsString, enabledTags.at(0) ? enabledTags.at(0).name : '', index); + updatedTagsString = IOUUtils.insertTagIntoTransactionTagsString(updatedTagsString, enabledTags.at(0)?.name ?? '', index); }); if (updatedTagsString !== TransactionUtils.getTag(transaction) && updatedTagsString) { IOU.setMoneyRequestTag(transactionID, updatedTagsString); diff --git a/src/components/MultiGestureCanvas/usePanGesture.ts b/src/components/MultiGestureCanvas/usePanGesture.ts index 28a539972d1c..b31e310055ae 100644 --- a/src/components/MultiGestureCanvas/usePanGesture.ts +++ b/src/components/MultiGestureCanvas/usePanGesture.ts @@ -177,8 +177,8 @@ const usePanGesture = ({ // TODO: this needs tuning to work properly if (!shouldDisableTransformationGestures.value && zoomScale.value === 1 && previousTouch.value !== null) { - const velocityX = Math.abs(evt.allTouches.at(0).x - previousTouch.value.x); - const velocityY = evt.allTouches.at(0).y - previousTouch.value.y; + const velocityX = Math.abs((evt.allTouches.at(0)?.x ?? 0) - previousTouch.value.x); + const velocityY = (evt.allTouches.at(0)?.y ?? 0) - previousTouch.value.y; if (Math.abs(velocityY) > velocityX && velocityY > 20) { state.activate(); @@ -192,8 +192,8 @@ const usePanGesture = ({ if (previousTouch.value === null) { previousTouch.value = { - x: evt.allTouches.at(0).x, - y: evt.allTouches.at(0).y, + x: evt.allTouches.at(0)?.x ?? 0, + y: evt.allTouches.at(0)?.y ?? 0, }; } }) diff --git a/src/components/MultipleAvatars.tsx b/src/components/MultipleAvatars.tsx index 3815d9655c5d..d947e13fa708 100644 --- a/src/components/MultipleAvatars.tsx +++ b/src/components/MultipleAvatars.tsx @@ -144,21 +144,21 @@ function MultipleAvatars({ if (icons.length === 1 && !shouldStackHorizontally) { return ( @@ -233,7 +233,7 @@ function MultipleAvatars({ // Set overlay background color with RGBA value so that the text will not inherit opacity StyleUtils.getBackgroundColorWithOpacityStyle(theme.overlay, variables.overlayOpacity), StyleUtils.getHorizontalStackedOverlayAvatarStyle(oneAvatarSize, oneAvatarBorderWidth), - icons.at(3).type === CONST.ICON_TYPE_WORKSPACE && StyleUtils.getAvatarBorderRadius(size, icons.at(3).type), + icons.at(3)?.type === CONST.ICON_TYPE_WORKSPACE && StyleUtils.getAvatarBorderRadius(size, icons.at(3)?.type), ]} > @@ -249,45 +249,45 @@ function MultipleAvatars({ )) ) : ( - + {/* View is necessary for tooltip to show for multiple avatars in LHN */} - + {icons.length === 2 ? ( diff --git a/src/components/OptionRow.tsx b/src/components/OptionRow.tsx index 10e0e68774d7..3570ea15c9f3 100644 --- a/src/components/OptionRow.tsx +++ b/src/components/OptionRow.tsx @@ -10,6 +10,7 @@ import * as OptionsListUtils from '@libs/OptionsListUtils'; import * as ReportUtils from '@libs/ReportUtils'; import type {OptionData} from '@libs/ReportUtils'; import CONST from '@src/CONST'; +import fallbackIcon from '@src/utils/getDefaultIcon'; import Button from './Button'; import DisplayNames from './DisplayNames'; import Hoverable from './Hoverable'; @@ -209,7 +210,7 @@ function OptionRow({ {!!option.icons?.length && (option.shouldShowSubscript ? ( ( // When there is only 1 element in the selector, we do the user a favor and automatically select it for them // so they don't have to spend extra time selecting the only possible value. - onInputChange(items.at(0).value, 0); + const item = items.at(0); + if (item) { + onInputChange(item.value, 0); + } // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps }, [items]); diff --git a/src/components/PopoverMenu.tsx b/src/components/PopoverMenu.tsx index aa7fc19fe721..a6714bc4758c 100644 --- a/src/components/PopoverMenu.tsx +++ b/src/components/PopoverMenu.tsx @@ -123,6 +123,9 @@ function PopoverMenu({ const selectItem = (index: number) => { const selectedItem = currentMenuItems.at(index); + if (!selectedItem) { + return; + } if (selectedItem?.subMenuItems) { setCurrentMenuItems([...selectedItem.subMenuItems]); setEnteredSubMenuIndexes([...enteredSubMenuIndexes, index]); diff --git a/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx b/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx index bea957cd3c2f..62e6533ed23e 100644 --- a/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx +++ b/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx @@ -197,7 +197,8 @@ function MoneyRequestPreviewContent({ a.type === CONST.VIOLATION_TYPES.VIOLATION ? -1 : 0, ); if (violations?.at(0)) { - const violationMessage = ViolationsUtils.getViolationTranslation(violations.at(0), translate); + const violation = violations.at(0); + const violationMessage = violation ? ViolationsUtils.getViolationTranslation(violation, translate) : ''; const violationsCount = violations.filter((v) => v.type === CONST.VIOLATION_TYPES.VIOLATION).length; const isTooLong = violationsCount > 1 || violationMessage.length > 15; const hasViolationsAndFieldErrors = violationsCount > 0 && hasFieldErrors; diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index 1def76501859..d9fa5746c6ea 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -43,7 +43,10 @@ import * as Report from '@src/libs/actions/Report'; import * as ReportActions from '@src/libs/actions/ReportActions'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +// eslint-disable-next-line import/no-duplicates import type * as OnyxTypes from '@src/types/onyx'; +// eslint-disable-next-line import/no-duplicates +import type {TransactionViolation} from '@src/types/onyx'; import type {TransactionPendingFieldsKey} from '@src/types/onyx/Transaction'; import ReportActionItemImage from './ReportActionItemImage'; @@ -329,8 +332,9 @@ function MoneyRequestView({ // Return violations if there are any if (hasViolations(field, data, policyHasDependentTags, tagValue)) { - const violations = getViolationsForField(field, data, policyHasDependentTags, tagValue); - return ViolationsUtils.getViolationTranslation(violations.at(0), translate); + const violations = getViolationsForField(field, data, policyHasDependentTags, tagValue) as TransactionViolation[]; + // eslint-disable-next-line rulesdir/prefer-at + return ViolationsUtils.getViolationTranslation(violations[0], translate); } return ''; @@ -660,7 +664,7 @@ function MoneyRequestView({ {translate('common.billable')} {!!getErrorForField('billable') && ( { - const code = `${reservation.confirmations && reservation.confirmations?.length > 0 ? `${reservation.confirmations.at(0).value} • ` : ''}`; + const code = `${reservation.confirmations && reservation.confirmations?.length > 0 ? `${reservation.confirmations.at(0)?.value} • ` : ''}`; if (reservation.type === CONST.RESERVATION_TYPE.FLIGHT) { const longName = reservation.company?.longName ? `${reservation.company?.longName} • ` : ''; const shortName = reservation?.company?.shortName ? `${reservation?.company?.shortName} ` : ''; diff --git a/src/components/RoomHeaderAvatars.tsx b/src/components/RoomHeaderAvatars.tsx index cabfa9c27c3f..1c7a78c631b8 100644 --- a/src/components/RoomHeaderAvatars.tsx +++ b/src/components/RoomHeaderAvatars.tsx @@ -36,22 +36,27 @@ function RoomHeaderAvatars({icons, reportID}: RoomHeaderAvatarsProps) { } if (icons.length === 1) { + const icon = icons.at(0); + + if (!icon) { + return; + } return ( navigateToAvatarPage(icons.at(0))} + onPress={() => navigateToAvatarPage(icon)} accessibilityRole={CONST.ACCESSIBILITY_ROLE.IMAGEBUTTON} - accessibilityLabel={icons.at(0).name ?? ''} - disabled={icons.at(0).source === Expensicons.FallbackAvatar} + accessibilityLabel={icon.name ?? ''} + disabled={icon.source === Expensicons.FallbackAvatar} > ); diff --git a/src/components/SelectionList/InviteMemberListItem.tsx b/src/components/SelectionList/InviteMemberListItem.tsx index a0aceefaaf33..871d4f70489e 100644 --- a/src/components/SelectionList/InviteMemberListItem.tsx +++ b/src/components/SelectionList/InviteMemberListItem.tsx @@ -12,6 +12,7 @@ import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import CONST from '@src/CONST'; +import fallbackIcon from '@src/utils/getDefaultIcon'; import BaseListItem from './BaseListItem'; import type {InviteMemberListItemProps, ListItem} from './types'; @@ -74,8 +75,8 @@ function InviteMemberListItem({ {!!item.icons && (item.shouldShowSubscript ? ( diff --git a/src/components/SelectionList/Search/ReportListItem.tsx b/src/components/SelectionList/Search/ReportListItem.tsx index edfeb0c56b46..660f1315da31 100644 --- a/src/components/SelectionList/Search/ReportListItem.tsx +++ b/src/components/SelectionList/Search/ReportListItem.tsx @@ -110,7 +110,7 @@ function ReportListItem({ isDisabled={isDisabled} canSelectMultiple={canSelectMultiple} onCheckboxPress={() => onCheckboxPress?.(transactionItem as unknown as TItem)} - onSelectRow={() => openReportInRHP(transactionItem)} + onSelectRow={() => (transactionItem ? openReportInRHP(transactionItem) : undefined)} onDismissError={onDismissError} onFocus={onFocus} onLongPressRow={onLongPressRow} diff --git a/src/components/SelectionList/Search/TransactionListItemRow.tsx b/src/components/SelectionList/Search/TransactionListItemRow.tsx index be42642316b0..91855c0a8a88 100644 --- a/src/components/SelectionList/Search/TransactionListItemRow.tsx +++ b/src/components/SelectionList/Search/TransactionListItemRow.tsx @@ -137,7 +137,7 @@ function MerchantCell({transactionItem, showTooltip, isLargeScreenWidth}: Transa return ( ); diff --git a/src/components/SelectionList/UserListItem.tsx b/src/components/SelectionList/UserListItem.tsx index a3c7a9b73278..d87426d90dd8 100644 --- a/src/components/SelectionList/UserListItem.tsx +++ b/src/components/SelectionList/UserListItem.tsx @@ -13,6 +13,7 @@ import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import CONST from '@src/CONST'; +import fallbackIcon from '@src/utils/getDefaultIcon'; import BaseListItem from './BaseListItem'; import type {ListItem, UserListItemProps} from './types'; @@ -100,7 +101,7 @@ function UserListItem({ {!!item.icons && (item.shouldShowSubscript ? ( 0, searchValue); + const headerMessage = OptionsListUtils.getHeaderMessageForNonUserList((sections.at(0)?.data?.length ?? 0) > 0, searchValue); const selectedOptionKey = useMemo(() => sections?.at(0)?.data?.find((taxRate) => taxRate.searchText === selectedTaxRate)?.keyForList, [sections, selectedTaxRate]); diff --git a/src/components/WorkspaceSwitcherButton.tsx b/src/components/WorkspaceSwitcherButton.tsx index 9ad0ffefafa1..3c18f24d8f27 100644 --- a/src/components/WorkspaceSwitcherButton.tsx +++ b/src/components/WorkspaceSwitcherButton.tsx @@ -9,6 +9,7 @@ import {getDefaultWorkspaceAvatar} from '@libs/ReportUtils'; import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; import type {Policy} from '@src/types/onyx'; +import fallbackIcon from '@src/utils/getDefaultIcon'; import * as Expensicons from './Icon/Expensicons'; import {PressableWithFeedback} from './Pressable'; import SubscriptAvatar from './SubscriptAvatar'; @@ -56,7 +57,7 @@ function WorkspaceSwitcherButton({policy}: WorkspaceSwitcherButtonProps) { > {({hovered}) => ( { diff --git a/src/hooks/useSubStep/index.ts b/src/hooks/useSubStep/index.ts index 846c758a8eec..73bc422b3852 100644 --- a/src/hooks/useSubStep/index.ts +++ b/src/hooks/useSubStep/index.ts @@ -56,7 +56,8 @@ export default function useSubStep({bodyContent, on }, [bodyContent]); return { - componentToRender: bodyContent.at(screenIndex), + // eslint-disable-next-line rulesdir/prefer-at + componentToRender: bodyContent[screenIndex], isEditing: isEditing.current, screenIndex, prevScreen, diff --git a/src/hooks/useViolations.ts b/src/hooks/useViolations.ts index dee0c5e0ce06..0c285664499f 100644 --- a/src/hooks/useViolations.ts +++ b/src/hooks/useViolations.ts @@ -106,7 +106,7 @@ function useViolations(violations: TransactionViolation[], shouldShowOnlyViolati { ...currentViolations.at(0), data: { - ...currentViolations.at(0).data, + ...currentViolations.at(0)?.data, tagName: data?.tagListName, }, }, diff --git a/src/libs/CollectionUtils.ts b/src/libs/CollectionUtils.ts index 2d94def3832f..01676cb5d798 100644 --- a/src/libs/CollectionUtils.ts +++ b/src/libs/CollectionUtils.ts @@ -17,7 +17,7 @@ function lastItem(object: Record = {}): T | undefined { * e.g. reportActions_1 -> 1 */ function extractCollectionItemID(key: `${OnyxCollectionKey}${string}`): string { - return key.split('_').at(1); + return key.split('_').at(1) ?? ''; } export {lastItem, extractCollectionItemID}; diff --git a/src/libs/EmojiUtils.ts b/src/libs/EmojiUtils.ts index 78f10edb9e2f..f30a8e14bf5e 100644 --- a/src/libs/EmojiUtils.ts +++ b/src/libs/EmojiUtils.ts @@ -267,7 +267,7 @@ const getEmojiCodeWithSkinColor = (item: Emoji, preferredSkinToneIndex: OnyxEntr const {code, types} = item; if (typeof preferredSkinToneIndex === 'number' && types?.[preferredSkinToneIndex]) { - return types.at(preferredSkinToneIndex); + return types.at(preferredSkinToneIndex) ?? ''; } return code; @@ -504,7 +504,12 @@ const enrichEmojiReactionWithTimestamps = (emoji: ReportActionReaction, emojiNam const usersWithTimestamps: UsersReactions = {}; Object.entries(emoji.users ?? {}).forEach(([id, user]) => { const userTimestamps = Object.values(user?.skinTones ?? {}); - const oldestUserTimestamp = userTimestamps.reduce((min, curr) => (curr < min ? curr : min), userTimestamps.at(0)); + // eslint-disable-next-line no-nested-ternary + const oldestUserTimestamp = userTimestamps.reduce((min, curr) => (min ? (curr < min ? curr : min) : curr), userTimestamps.at(0)); + + if (!oldestUserTimestamp) { + return; + } if (!oldestEmojiTimestamp || oldestUserTimestamp < oldestEmojiTimestamp) { oldestEmojiTimestamp = oldestUserTimestamp; diff --git a/src/libs/GooglePlacesUtils.ts b/src/libs/GooglePlacesUtils.ts index 39a9009525f4..1f9fd838f8d4 100644 --- a/src/libs/GooglePlacesUtils.ts +++ b/src/libs/GooglePlacesUtils.ts @@ -45,7 +45,7 @@ function getPlaceAutocompleteTerms(addressTerms: AddressTerm[]): GetPlaceAutocom const result: GetPlaceAutocompleteTermsResult = {}; fieldsToExtract.forEach((fieldToExtract, index) => { const fieldTermIndex = addressTerms.length - (index + 1); - result[fieldToExtract] = fieldTermIndex >= 0 ? addressTerms.at(fieldTermIndex).value : ''; + result[fieldToExtract] = fieldTermIndex >= 0 ? addressTerms.at(fieldTermIndex)?.value : ''; }); return result; } diff --git a/src/libs/LocaleDigitUtils.ts b/src/libs/LocaleDigitUtils.ts index 12ac79c37a53..e6e91d58b273 100644 --- a/src/libs/LocaleDigitUtils.ts +++ b/src/libs/LocaleDigitUtils.ts @@ -52,7 +52,7 @@ function toLocaleDigit(locale: Locale, digit: string): string { if (index < 0) { throw new Error(`"${digit}" must be in ${JSON.stringify(STANDARD_DIGITS)}`); } - return getLocaleDigits(locale).at(index); + return getLocaleDigits(locale).at(index) ?? ''; } /** @@ -68,7 +68,7 @@ function fromLocaleDigit(locale: Locale, localeDigit: string): string { if (index < 0) { throw new Error(`"${localeDigit}" must be in ${JSON.stringify(getLocaleDigits(locale))}`); } - return STANDARD_DIGITS.at(index); + return STANDARD_DIGITS.at(index) ?? ''; } /** diff --git a/src/libs/Middleware/Pagination.ts b/src/libs/Middleware/Pagination.ts index 9d11df1988d5..2b4d8de310af 100644 --- a/src/libs/Middleware/Pagination.ts +++ b/src/libs/Middleware/Pagination.ts @@ -111,7 +111,8 @@ const Pagination: Middleware = (requestResponse, request) => { if ((type === 'initial' && !cursorID) || (type === 'next' && newPage.length === 1 && newPage.at(0) === cursorID)) { newPage.unshift(CONST.PAGINATION_START_ID); } - if (isLastItem(sortedPageItems.at(sortedPageItems.length - 1))) { + const pageItem = sortedPageItems.at(sortedPageItems.length - 1); + if (pageItem && isLastItem(pageItem)) { newPage.push(CONST.PAGINATION_END_ID); } diff --git a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/index.tsx b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/index.tsx index e83fcc3c3c87..7401c3368124 100644 --- a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/index.tsx +++ b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/index.tsx @@ -19,7 +19,7 @@ function getStateToRender(state: StackNavigationState): StackNavi // We need to render at least one HOME screen to make sure everything load properly. This may be not necessary after changing how IS_SIDEBAR_LOADED is handled. // Currently this value will be switched only after the first HOME screen is rendered. - if (routesToRender.at(0).name !== SCREENS.HOME) { + if (routesToRender.at(0)?.name !== SCREENS.HOME) { const routeToRender = state.routes.find((route) => route.name === SCREENS.HOME); if (routeToRender) { routesToRender.unshift(routeToRender); diff --git a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/index.tsx b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/index.tsx index 3cc33da0281a..464723d15c1d 100644 --- a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/index.tsx +++ b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/index.tsx @@ -65,7 +65,8 @@ function ResponsiveStackNavigator(props: ResponsiveStackNavigatorProps) { const isSearchCentralPane = (route: RouteProp) => getTopmostCentralPaneRoute({routes: [route]} as State)?.name === SCREENS.SEARCH.CENTRAL_PANE; const lastRoute = routes.at(routes.length - 1); - const lastSearchCentralPane = isSearchCentralPane(lastRoute) ? lastRoute : undefined; + // eslint-disable-next-line no-nested-ternary + const lastSearchCentralPane = lastRoute ? (isSearchCentralPane(lastRoute) ? lastRoute : undefined) : undefined; const filteredRoutes = routes.filter((route) => !isSearchCentralPane(route)); // On narrow layout, if we are on /search route we want to hide all central pane routes and show only the bottom tab navigator. @@ -74,7 +75,8 @@ function ResponsiveStackNavigator(props: ResponsiveStackNavigatorProps) { stateToRender: { ...state, index: 0, - routes: [filteredRoutes.at(0)], + // eslint-disable-next-line rulesdir/prefer-at + routes: [filteredRoutes[0]], }, searchRoute: lastSearchCentralPane, }; diff --git a/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts b/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts index ae8229861b30..4975fd1c8c85 100644 --- a/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts +++ b/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts @@ -124,7 +124,7 @@ function getMatchingRootRouteForRHPRoute(route: NavigationPartialRoute): Navigat // If there is rhpNavigator in the state generated for backTo url, we want to get root route matching to this rhp screen. if (rhpNavigator && rhpNavigator.state) { - const isRHPinState = stateForBackTo.routes.at(0).name === NAVIGATORS.RIGHT_MODAL_NAVIGATOR; + const isRHPinState = stateForBackTo.routes.at(0)?.name === NAVIGATORS.RIGHT_MODAL_NAVIGATOR; if (isRHPinState) { return getMatchingRootRouteForRHPRoute(findFocusedRoute(stateForBackTo) as NavigationPartialRoute); @@ -340,7 +340,7 @@ function getAdaptedState(state: PartialState // - matching central pane on desktop layout // We want to make sure that the bottom tab search page is always pushed with matching central pane page. Even on the narrow layout. - if (isNarrowLayout && bottomTabNavigator.state?.routes.at(0).name !== SCREENS.SEARCH.BOTTOM_TAB) { + if (isNarrowLayout && bottomTabNavigator.state?.routes.at(0)?.name !== SCREENS.SEARCH.BOTTOM_TAB) { return { adaptedState: state, metainfo, diff --git a/src/libs/Navigation/linkingConfig/getMatchingCentralPaneRouteForState.ts b/src/libs/Navigation/linkingConfig/getMatchingCentralPaneRouteForState.ts index 3b554bf3d477..b8531ba9f292 100644 --- a/src/libs/Navigation/linkingConfig/getMatchingCentralPaneRouteForState.ts +++ b/src/libs/Navigation/linkingConfig/getMatchingCentralPaneRouteForState.ts @@ -1,5 +1,5 @@ import getTopmostBottomTabRoute from '@libs/Navigation/getTopmostBottomTabRoute'; -import type {AuthScreensParamList, CentralPaneName, NavigationPartialRoute, RootStackParamList, State} from '@libs/Navigation/types'; +import type {AuthScreensParamList, CentralPaneName, CentralPaneScreensParamList, NavigationPartialRoute, RootStackParamList, State} from '@libs/Navigation/types'; import NAVIGATORS from '@src/NAVIGATORS'; import SCREENS from '@src/SCREENS'; import TAB_TO_CENTRAL_PANE_MAPPING from './TAB_TO_CENTRAL_PANE_MAPPING'; @@ -62,10 +62,10 @@ function getMatchingCentralPaneRouteForState(state: State, r } if (topmostBottomTabRoute.name === SCREENS.HOME) { - return {name: centralPaneName, params: {reportID: getTopMostReportIDFromRHP(state)}}; + return {name: centralPaneName ?? ({} as keyof CentralPaneScreensParamList), params: {reportID: getTopMostReportIDFromRHP(state)}}; } - return {name: centralPaneName}; + return {name: centralPaneName ?? ({} as keyof CentralPaneScreensParamList)}; } export default getMatchingCentralPaneRouteForState; diff --git a/src/libs/Network/SequentialQueue.ts b/src/libs/Network/SequentialQueue.ts index 2ddde2fda973..34de7a785159 100644 --- a/src/libs/Network/SequentialQueue.ts +++ b/src/libs/Network/SequentialQueue.ts @@ -81,6 +81,10 @@ function process(): Promise { const requestToProcess = persistedRequests.at(0); + if (!requestToProcess) { + return Promise.resolve(); + } + // Set the current request to a promise awaiting its processing so that getCurrentRequest can be used to take some action after the current request has processed. currentRequest = Request.processWithMiddleware(requestToProcess, true) .then((response) => { diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index f4af1efb319b..2a6d3d253f52 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -300,6 +300,11 @@ Onyx.connect({ // Iterate over the report actions to build the sorted and lastVisible report actions objects Object.entries(allReportActions).forEach((reportActions) => { const reportID = reportActions[0].split('_').at(1); + + if (!reportID) { + return; + } + const reportActionsArray = Object.values(reportActions[1] ?? {}); let sortedReportActions = ReportActionUtils.getSortedReportActions(reportActionsArray, true); allSortedReportActions[reportID] = sortedReportActions; @@ -312,7 +317,13 @@ Onyx.connect({ sortedReportActions = ReportActionUtils.getCombinedReportActions(sortedReportActions, transactionThreadReportID, transactionThreadReportActionsArray, reportID); } - lastReportActions[reportID] = sortedReportActions.at(0); + const firstReportAction = sortedReportActions.at(0); + + if (!firstReportAction) { + return; + } + + lastReportActions[reportID] = firstReportAction; // The report is only visible if it is the last action not deleted that // does not match a closed or created state. @@ -324,7 +335,11 @@ Onyx.connect({ reportAction.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE && !ReportActionUtils.isResolvedActionTrackExpense(reportAction), ); - lastVisibleReportActions[reportID] = reportActionsForDisplay.at(0); + const reportActionForDisplay = reportActionsForDisplay.at(0); + if (!reportActionForDisplay) { + return; + } + lastVisibleReportActions[reportID] = reportActionForDisplay; }); }, }); @@ -550,7 +565,7 @@ function getAlternateText(option: ReportUtils.OptionData, {showChatPreviewLine = return showChatPreviewLine && option.lastMessageText ? option.lastMessageText - : LocalePhoneNumber.formatPhoneNumber(option.participantsList && option.participantsList.length > 0 ? option.participantsList.at(0).login ?? '' : ''); + : LocalePhoneNumber.formatPhoneNumber(option.participantsList && option.participantsList.length > 0 ? option.participantsList.at(0)?.login ?? '' : ''); } function isSearchStringMatchUserDetails(personalDetail: PersonalDetails, searchValue: string) { @@ -2368,7 +2383,7 @@ function getFirstKeyForList(data?: Option[] | null) { const firstNonEmptyDataObj = data.at(0); - return firstNonEmptyDataObj.keyForList ? firstNonEmptyDataObj.keyForList : ''; + return firstNonEmptyDataObj?.keyForList ? firstNonEmptyDataObj?.keyForList : ''; } function getPersonalDetailSearchTerms(item: Partial) { diff --git a/src/libs/PaginationUtils.ts b/src/libs/PaginationUtils.ts index f43d63c2fb0a..a1c9078898bf 100644 --- a/src/libs/PaginationUtils.ts +++ b/src/libs/PaginationUtils.ts @@ -51,7 +51,7 @@ function findLastItem(sortedItems: TResource[], page: string[], getID return {id, index: sortedItems.length - 1}; } const index = sortedItems.findIndex((item) => getID(item) === id); - if (index !== -1) { + if (index !== -1 && id) { return {id, index}; } } @@ -126,6 +126,11 @@ function mergeAndSortContinuousPages(sortedItems: TResource[], pages: const page = sortedPages.at(i); const prevPage = result.at(result.length - 1); + if (!page || !prevPage) { + // eslint-disable-next-line no-continue + continue; + } + // Current page is inside the previous page, skip if (page.lastIndex <= prevPage.lastIndex && page.lastID !== CONST.PAGINATION_END_ID) { // eslint-disable-next-line no-continue @@ -151,7 +156,7 @@ function mergeAndSortContinuousPages(sortedItems: TResource[], pages: result.push(page); } - return result.map((page) => page.ids); + return result.map((page) => page?.ids ?? []); } /** @@ -167,7 +172,13 @@ function getContinuousChain(sortedItems: TResource[], pages: Pages, g const pagesWithIndexes = getPagesWithIndexes(sortedItems, pages, getID); - let page: PageWithIndex; + let page: PageWithIndex = { + ids: [], + firstID: '', + firstIndex: 0, + lastID: '', + lastIndex: 0, + }; if (id) { const index = sortedItems.findIndex((item) => getID(item) === id); @@ -179,14 +190,20 @@ function getContinuousChain(sortedItems: TResource[], pages: Pages, g const linkedPage = pagesWithIndexes.find((pageIndex) => index >= pageIndex.firstIndex && index <= pageIndex.lastIndex); + const item = sortedItems.at(index); // If we are linked to an action in a gap return it by itself - if (!linkedPage) { - return [sortedItems.at(index)]; + if (!linkedPage && item) { + return [item]; } - page = linkedPage; + if (linkedPage) { + page = linkedPage; + } } else { - page = pagesWithIndexes.at(0); + const pageAtIndex0 = pagesWithIndexes.at(0); + if (pageAtIndex0) { + page = pageAtIndex0; + } } return page ? sortedItems.slice(page.firstIndex, page.lastIndex + 1) : sortedItems; diff --git a/src/libs/PersonalDetailsUtils.ts b/src/libs/PersonalDetailsUtils.ts index da20faaa03a7..3b4d2ec02f22 100644 --- a/src/libs/PersonalDetailsUtils.ts +++ b/src/libs/PersonalDetailsUtils.ts @@ -150,7 +150,7 @@ function getNewAccountIDsAndLogins(logins: string[], accountIDs: number[]) { const newAccountIDs: number[] = []; const newLogins: string[] = []; logins.forEach((login, index) => { - const accountID = accountIDs.at(index); + const accountID = accountIDs.at(index) ?? -1; if (isEmptyObject(allPersonalDetails?.[accountID])) { newAccountIDs.push(accountID); newLogins.push(login); @@ -169,7 +169,7 @@ function getPersonalDetailsOnyxDataForOptimisticUsers(newLogins: string[], newAc const personalDetailsCleanup: PersonalDetailsList = {}; newLogins.forEach((login, index) => { - const accountID = newAccountIDs.at(index); + const accountID = newAccountIDs.at(index) ?? -1; personalDetailsNew[accountID] = { login, accountID, diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index 8b11c4d434f1..3cc159495a77 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -292,6 +292,7 @@ function getTagList(policyTagList: OnyxEntry, tagIndex: number): name: '', required: false, tags: {}, + orderWeight: 0, } ); } @@ -488,7 +489,7 @@ function getSubmitToAccountID(policy: OnyxEntry, employeeAccountID: numb // For policy using the optional or basic workflow, the manager is the policy default approver. if (([CONST.POLICY.APPROVAL_MODE.OPTIONAL, CONST.POLICY.APPROVAL_MODE.BASIC] as Array>).includes(getApprovalWorkflow(policy))) { - return getAccountIDsByLogins([defaultApprover]).at(0); + return getAccountIDsByLogins([defaultApprover]).at(0) ?? -1; } const employee = policy?.employeeList?.[employeeLogin]; @@ -496,7 +497,7 @@ function getSubmitToAccountID(policy: OnyxEntry, employeeAccountID: numb return -1; } - return getAccountIDsByLogins([employee.submitsTo ?? defaultApprover]).at(0); + return getAccountIDsByLogins([employee.submitsTo ?? defaultApprover]).at(0) ?? -1; } function getSubmitToEmail(policy: OnyxEntry, employeeAccountID: number): string { @@ -530,7 +531,7 @@ function getForwardsToAccount(policy: OnyxEntry, employeeEmail: string, */ function getReimburserAccountID(policy: OnyxEntry): number { const reimburserEmail = policy?.achAccount?.reimburser ?? policy?.owner ?? ''; - return getAccountIDsByLogins([reimburserEmail]).at(0); + return getAccountIDsByLogins([reimburserEmail]).at(0) ?? -1; } function getPersonalPolicy() { diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts index 5fc3be1da697..636030b9d7c3 100644 --- a/src/libs/ReportActionsUtils.ts +++ b/src/libs/ReportActionsUtils.ts @@ -504,7 +504,7 @@ function findPreviousAction(reportActions: ReportAction[] | undefined, actionInd for (let i = actionIndex + 1; i < reportActions.length; i++) { // Find the next non-pending deletion report action, as the pending delete action means that it is not displayed in the UI, but still is in the report actions list. // If we are offline, all actions are pending but shown in the UI, so we take the previous action, even if it is a delete. - if (isNetworkOffline || reportActions.at(i).pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE) { + if (isNetworkOffline || reportActions.at(i)?.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE) { return reportActions.at(i); } } @@ -714,8 +714,10 @@ function replaceBaseURLInPolicyChangeLogAction(reportAction: ReportAction): Repo return updatedReportAction; } - if (Array.isArray(updatedReportAction.message) && updatedReportAction.message.at(0)) { - updatedReportAction.message.at(0).html = getReportActionHtml(reportAction)?.replace('%baseURL', environmentURL); + // eslint-disable-next-line rulesdir/prefer-at + if (Array.isArray(updatedReportAction.message) && updatedReportAction.message[0]) { + // eslint-disable-next-line rulesdir/prefer-at + updatedReportAction.message[0].html = getReportActionHtml(reportAction)?.replace('%baseURL', environmentURL); } return updatedReportAction; @@ -832,7 +834,7 @@ function getFirstVisibleReportActionID(sortedReportActions: ReportAction[] = [], return ''; } const sortedFilterReportActions = sortedReportActions.filter((action) => !isDeletedAction(action) || (action?.childVisibleActionCount ?? 0) > 0 || isOffline); - return sortedFilterReportActions.length > 1 ? sortedFilterReportActions.at(sortedFilterReportActions.length - 2).reportActionID : ''; + return sortedFilterReportActions.length > 1 ? sortedFilterReportActions.at(sortedFilterReportActions.length - 2)?.reportActionID ?? '' : ''; } /** @@ -1021,7 +1023,7 @@ function getOneTransactionThreadReportID(reportID: string, reportActions: OnyxEn } // Ensure we have a childReportID associated with the IOU report action - return singleAction.childReportID; + return singleAction?.childReportID; } /** @@ -1558,7 +1560,7 @@ function getExportIntegrationActionFragments(reportAction: OnyxEntry): boolean { const participantAccountIDs = Object.keys(report?.participants ?? {}) .map(Number) .filter((accountID) => accountID !== currentUserAccountID); - return isOptimisticPersonalDetail(participantAccountIDs.at(0)); + return isOptimisticPersonalDetail(participantAccountIDs.at(0) ?? -1); } return false; } @@ -3574,7 +3574,7 @@ function parseReportActionHtmlToText(reportAction: OnyxEntry, repo const accountIDToName: Record = {}; const accountIDs = Array.from(html.matchAll(mentionUserRegex), (mention) => Number(mention[1])); const logins = PersonalDetailsUtils.getLoginsByAccountIDs(accountIDs); - accountIDs.forEach((id, index) => (accountIDToName[id] = logins.at(index))); + accountIDs.forEach((id, index) => (accountIDToName[id] = logins.at(index) ?? '')); const textMessage = Str.removeSMSDomain(Parser.htmlToText(html, {reportIDToName, accountIDToName})); parsedReportActionMessageCache[key] = textMessage; @@ -4183,8 +4183,10 @@ function buildOptimisticTaskCommentReportAction( createdOffset = 0, ): OptimisticReportAction { const reportAction = buildOptimisticAddCommentReportAction(text, undefined, undefined, createdOffset, undefined, taskReportID); - if (Array.isArray(reportAction.reportAction.message) && reportAction.reportAction.message?.at(0)) { - reportAction.reportAction.message.at(0).taskReportID = taskReportID; + // eslint-disable-next-line rulesdir/prefer-at + if (Array.isArray(reportAction.reportAction.message) && reportAction.reportAction.message[0]) { + // eslint-disable-next-line rulesdir/prefer-at + reportAction.reportAction.message[0].taskReportID = taskReportID; } else if (!Array.isArray(reportAction.reportAction.message) && reportAction.reportAction.message) { reportAction.reportAction.message.taskReportID = taskReportID; } @@ -4281,7 +4283,7 @@ function populateOptimisticReportFormula(formula: string, report: OptimisticExpe .replaceAll('{report:created:yyyy-MM-dd}', createdDate ? format(createdDate, CONST.DATE.FNS_FORMAT_STRING) : '') .replaceAll('{report:status}', report.statusNum !== undefined ? getHumanReadableStatus(report.statusNum) : '') .replaceAll('{user:email}', currentUserEmail ?? '') - .replaceAll('{user:email|frontPart}', currentUserEmail ? currentUserEmail.split('@').at(0) : '') + .replaceAll('{user:email|frontPart}', (currentUserEmail ? currentUserEmail.split('@').at(0) : '') ?? '') .replaceAll(/\{report:(.+)}/g, ''); return result.trim().length ? result : formula; @@ -5037,7 +5039,7 @@ function buildOptimisticChatReport( // TODO: update to support workspace as an invoice receiver when workspace-to-workspace invoice room implemented optimisticChatReport.invoiceReceiver = { type: 'individual', - accountID: participantList.at(0), + accountID: participantList.at(0) ?? -1, }; } @@ -6178,7 +6180,7 @@ function getNewMarkerReportActionID(report: OnyxEntry, sortedAndFiltered const newMarkerIndex = lodashFindLastIndex(sortedAndFilteredReportActions, (reportAction) => (reportAction.created ?? '') > (report?.lastReadTime ?? '')); - return 'reportActionID' in sortedAndFilteredReportActions.at(newMarkerIndex) ? sortedAndFilteredReportActions.at(newMarkerIndex).reportActionID : ''; + return 'reportActionID' in (sortedAndFilteredReportActions?.at(newMarkerIndex) ?? {}) ? sortedAndFilteredReportActions.at(newMarkerIndex)?.reportActionID ?? '' : ''; } /** @@ -7264,7 +7266,7 @@ function getOptimisticDataForParentReportAction(reportID: string, lastVisibleAct return null; } - const ancestorReportAction = ReportActionsUtils.getReportAction(ancestorReport.reportID, ancestors.reportActionsIDs.at(index)); + const ancestorReportAction = ReportActionsUtils.getReportAction(ancestorReport.reportID, ancestors.reportActionsIDs.at(index) ?? ''); if (!ancestorReportAction || isEmptyObject(ancestorReportAction)) { return null; diff --git a/src/libs/SelectionScraper/index.ts b/src/libs/SelectionScraper/index.ts index 45540a33bacd..060cbc613acf 100644 --- a/src/libs/SelectionScraper/index.ts +++ b/src/libs/SelectionScraper/index.ts @@ -117,15 +117,16 @@ const replaceNodes = (dom: Node, isChildOfEditorElement: boolean): Node => { } } else if (dom instanceof Element) { domName = dom.name; + const child = dom.children.at(0); if (dom.attribs?.[tagAttribute]) { // If it's a markdown element, rename it according to the value of data-testid, so ExpensiMark can parse it if (markdownElements.includes(dom.attribs[tagAttribute])) { domName = dom.attribs[tagAttribute]; } - } else if (dom.name === 'div' && dom.children.length === 1 && isChildOfEditorElement) { + } else if (dom.name === 'div' && dom.children.length === 1 && isChildOfEditorElement && child) { // We are excluding divs that are children of our editor element and have only one child to prevent // additional newlines from being added in the HTML to Markdown conversion process. - return replaceNodes(dom.children.at(0), isChildOfEditorElement); + return replaceNodes(child, isChildOfEditorElement); } // We need to preserve href attribute in order to copy links. diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index 552c8720f6ae..66a30a04cd75 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -49,7 +49,11 @@ Onyx.connect({ (reportAction) => ReportActionsUtils.shouldReportActionBeVisibleAsLastAction(reportAction) && reportAction.actionName !== CONST.REPORT.ACTIONS.TYPE.CREATED, ); - visibleReportActionItems[reportID] = reportActionsForDisplay.at(reportActionsForDisplay.length - 1); + const reportAction = reportActionsForDisplay.at(reportActionsForDisplay.length - 1); + if (!reportAction) { + return; + } + visibleReportActionItems[reportID] = reportAction; }, }); diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 21049668d1ee..54eb458fdaae 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -3823,7 +3823,7 @@ function trackExpense( function getOrCreateOptimisticSplitChatReport(existingSplitChatReportID: string, participants: Participant[], participantAccountIDs: number[], currentUserAccountID: number) { // The existing chat report could be passed as reportID or exist on the sole "participant" (in this case a report option) - const existingChatReportID = existingSplitChatReportID || participants.at(0).reportID; + const existingChatReportID = existingSplitChatReportID || (participants.at(0)?.reportID ?? ''); // Check if the report is available locally if we do have one let existingSplitChatReport = existingChatReportID ? ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${existingChatReportID}`] : null; @@ -5209,8 +5209,8 @@ function editRegularMoneyRequest( '', false, ); - updatedMoneyRequestReport.lastMessageText = ReportActionsUtils.getTextFromHtml(lastMessage.at(0).html); - updatedMoneyRequestReport.lastMessageHtml = lastMessage.at(0).html; + updatedMoneyRequestReport.lastMessageText = ReportActionsUtils.getTextFromHtml(lastMessage.at(0)?.html); + updatedMoneyRequestReport.lastMessageHtml = lastMessage.at(0)?.html; // Update the last message of the chat report const hasNonReimbursableTransactions = ReportUtils.hasNonReimbursableTransactions(iouReport?.reportID); @@ -5572,9 +5572,12 @@ function prepareToCleanUpMoneyRequest(transactionID: string, reportAction: OnyxT }); if (ReportActionsUtils.getReportActionMessage(updatedReportPreviewAction)) { - if (Array.isArray(updatedReportPreviewAction?.message) && updatedReportPreviewAction.message?.at(0)) { - updatedReportPreviewAction.message.at(0).text = messageText; - updatedReportPreviewAction.message.at(0).deleted = shouldDeleteIOUReport ? DateUtils.getDBTime() : ''; + // eslint-disable-next-line rulesdir/prefer-at + if (Array.isArray(updatedReportPreviewAction?.message) && updatedReportPreviewAction.message[0]) { + // eslint-disable-next-line rulesdir/prefer-at + updatedReportPreviewAction.message[0].text = messageText; + // eslint-disable-next-line rulesdir/prefer-at + updatedReportPreviewAction.message[0].deleted = shouldDeleteIOUReport ? DateUtils.getDBTime() : ''; } else if (!Array.isArray(updatedReportPreviewAction.message) && updatedReportPreviewAction.message) { updatedReportPreviewAction.message.text = messageText; updatedReportPreviewAction.message.deleted = shouldDeleteIOUReport ? DateUtils.getDBTime() : ''; @@ -7918,10 +7921,12 @@ function mergeDuplicates(params: TransactionMergeParams) { deleted: deletedTime, }, ...(Array.isArray(reportAction.message) && - !!reportAction.message.at(0) && { + // eslint-disable-next-line rulesdir/prefer-at + !!reportAction.message[0] && { message: [ { - ...reportAction.message.at(0), + // eslint-disable-next-line rulesdir/prefer-at + ...reportAction.message[0], deleted: deletedTime, }, ...reportAction.message.slice(1), diff --git a/src/libs/actions/OnyxUpdateManager/utils/index.ts b/src/libs/actions/OnyxUpdateManager/utils/index.ts index 960ddc60596c..bd3f5d33707b 100644 --- a/src/libs/actions/OnyxUpdateManager/utils/index.ts +++ b/src/libs/actions/OnyxUpdateManager/utils/index.ts @@ -93,7 +93,7 @@ function detectGapsAndSplit(lastUpdateIDFromClient: number): DetectGapAndSplitRe if (gapExists) { // If there is a gap and we didn't detect two chained updates, "firstUpdateToBeAppliedAfterGap" will always be the the last deferred update. // We will fetch all missing updates up to the previous update and can always apply the last deferred update. - const firstUpdateToBeAppliedAfterGap = firstUpdateIDAfterGaps ?? Number(updateValues.at(updateValues.length - 1).lastUpdateID); + const firstUpdateToBeAppliedAfterGap = firstUpdateIDAfterGaps ?? Number(updateValues.at(updateValues.length - 1)?.lastUpdateID); // Add all deferred updates after the gap(s) to "updatesAfterGaps". // If "firstUpdateToBeAppliedAfterGap" is set to the last deferred update, the array will be empty. diff --git a/src/libs/actions/Policy/DistanceRate.ts b/src/libs/actions/Policy/DistanceRate.ts index 51dc2a1b035d..f446d2da6b52 100644 --- a/src/libs/actions/Policy/DistanceRate.ts +++ b/src/libs/actions/Policy/DistanceRate.ts @@ -128,7 +128,7 @@ function enablePolicyDistanceRates(policyID: string, enabled: boolean) { if (!enabled) { const policy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]; - const customUnitID = Object.keys(policy?.customUnits ?? {}).at(0); + const customUnitID = Object.keys(policy?.customUnits ?? {}).at(0) ?? ''; const customUnit = customUnitID ? policy?.customUnits?.[customUnitID] : undefined; const rateEntries = Object.entries(customUnit?.rates ?? {}); @@ -148,7 +148,7 @@ function enablePolicyDistanceRates(policyID: string, enabled: boolean) { rateID, { ...rate, - enabled: rateID === rateEntryToBeEnabled[0], + enabled: rateID === rateEntryToBeEnabled?.at(0), }, ]; }), diff --git a/src/libs/actions/Policy/Policy.ts b/src/libs/actions/Policy/Policy.ts index 31807a9d8e82..80489e8dfb09 100644 --- a/src/libs/actions/Policy/Policy.ts +++ b/src/libs/actions/Policy/Policy.ts @@ -1425,13 +1425,13 @@ function generateDefaultWorkspaceName(email = ''): string { if (!emailParts || emailParts.length !== 2) { return defaultWorkspaceName; } - const username = emailParts.at(0); - const domain = emailParts.at(1); + const username = emailParts.at(0) ?? ''; + const domain = emailParts.at(1) ?? ''; const userDetails = PersonalDetailsUtils.getPersonalDetailByEmail(sessionEmail); const displayName = userDetails?.displayName?.trim(); if (!PUBLIC_DOMAINS.some((publicDomain) => publicDomain === domain.toLowerCase())) { - defaultWorkspaceName = `${Str.UCFirst(domain.split('.').at(0))}'s Workspace`; + defaultWorkspaceName = `${Str.UCFirst(domain.split('.').at(0) ?? '')}'s Workspace`; } else if (displayName) { defaultWorkspaceName = `${Str.UCFirst(displayName)}'s Workspace`; } else if (PUBLIC_DOMAINS.some((publicDomain) => publicDomain === domain.toLowerCase())) { @@ -2350,7 +2350,7 @@ function createWorkspaceFromIOUPayment(iouReport: OnyxEntry): WorkspaceF onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${announceChatReportID}`, value: { - [Object.keys(announceChatData).at(0)]: { + [Object.keys(announceChatData).at(0) ?? '']: { pendingAction: null, }, }, @@ -2369,7 +2369,7 @@ function createWorkspaceFromIOUPayment(iouReport: OnyxEntry): WorkspaceF onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${adminsChatReportID}`, value: { - [Object.keys(adminsChatData).at(0)]: { + [Object.keys(adminsChatData).at(0) ?? '']: { pendingAction: null, }, }, @@ -2388,7 +2388,7 @@ function createWorkspaceFromIOUPayment(iouReport: OnyxEntry): WorkspaceF onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${workspaceChatReportID}`, value: { - [Object.keys(workspaceChatData).at(0)]: { + [Object.keys(workspaceChatData).at(0) ?? '']: { pendingAction: null, }, }, diff --git a/src/libs/actions/Policy/Tag.ts b/src/libs/actions/Policy/Tag.ts index 290d61a7b9d6..251be17998df 100644 --- a/src/libs/actions/Policy/Tag.ts +++ b/src/libs/actions/Policy/Tag.ts @@ -124,7 +124,7 @@ function buildOptimisticPolicyRecentlyUsedTags(policyID?: string, transactionTag } function createPolicyTag(policyID: string, tagName: string) { - const policyTag = PolicyUtils.getTagLists(allPolicyTags?.[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`] ?? {})?.at(0) ?? {}; + const policyTag = PolicyUtils.getTagLists(allPolicyTags?.[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`] ?? {})?.at(0) ?? ({} as PolicyTagList); const newTagName = PolicyUtils.escapeTagName(tagName); const onyxData: OnyxData = { @@ -189,8 +189,11 @@ function createPolicyTag(policyID: string, tagName: string) { } function setWorkspaceTagEnabled(policyID: string, tagsToUpdate: Record, tagListIndex: number) { - const policyTag = - PolicyUtils.getTagLists(allPolicyTags?.[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`] ?? {})?.at(tagListIndex) ?? ({} as OnyxValueWithOfflineFeedback); + const policyTag = PolicyUtils.getTagLists(allPolicyTags?.[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`] ?? {})?.at(tagListIndex); + + if (!policyTag) { + return; + } const optimisticPolicyTagsData = { ...Object.keys(tagsToUpdate).reduce((acc, key) => { @@ -292,7 +295,11 @@ function setWorkspaceTagEnabled(policyID: string, tagsToUpdate: Record); + const policyTag = PolicyUtils.getTagLists(allPolicyTags?.[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`] ?? {})?.at(0); + + if (!policyTag) { + return; + } const onyxData: OnyxData = { optimisticData: [ @@ -384,8 +391,11 @@ function clearPolicyTagErrors(policyID: string, tagName: string, tagListIndex: n } function clearPolicyTagListErrorField(policyID: string, tagListIndex: number, errorField: string) { - const policyTag = - PolicyUtils.getTagLists(allPolicyTags?.[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`] ?? {})?.at(tagListIndex) ?? ({} as OnyxValueWithOfflineFeedback); + const policyTag = PolicyUtils.getTagLists(allPolicyTags?.[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`] ?? {})?.at(tagListIndex); + + if (!policyTag) { + return; + } if (!policyTag.name) { return; @@ -401,8 +411,11 @@ function clearPolicyTagListErrorField(policyID: string, tagListIndex: number, er } function clearPolicyTagListErrors(policyID: string, tagListIndex: number) { - const policyTag = - PolicyUtils.getTagLists(allPolicyTags?.[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`] ?? {})?.at(tagListIndex) ?? ({} as OnyxValueWithOfflineFeedback); + const policyTag = PolicyUtils.getTagLists(allPolicyTags?.[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`] ?? {})?.at(tagListIndex); + + if (!policyTag) { + return; + } if (!policyTag.name) { return; @@ -416,7 +429,12 @@ function clearPolicyTagListErrors(policyID: string, tagListIndex: number) { } function renamePolicyTag(policyID: string, policyTag: {oldName: string; newName: string}, tagListIndex: number) { - const tagList = PolicyUtils.getTagLists(allPolicyTags?.[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`] ?? {})?.at(tagListIndex) ?? ({} as OnyxValueWithOfflineFeedback); + const tagList = PolicyUtils.getTagLists(allPolicyTags?.[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`] ?? {})?.at(tagListIndex); + + if (!tagList) { + return; + } + const tag = tagList.tags?.[policyTag.oldName]; const oldTagName = policyTag.oldName; const newTagName = PolicyUtils.escapeTagName(policyTag.newName); @@ -562,6 +580,11 @@ function enablePolicyTags(policyID: string, enabled: boolean) { }); } else if (!enabled) { const policyTag = PolicyUtils.getTagLists(policyTagList).at(0); + + if (!policyTag) { + return; + } + onyxData.optimisticData?.push( { onyxMethod: Onyx.METHOD.MERGE, @@ -724,8 +747,11 @@ function setPolicyRequiresTag(policyID: string, requiresTag: boolean) { } function setPolicyTagsRequired(policyID: string, requiresTag: boolean, tagListIndex: number) { - const policyTag = - PolicyUtils.getTagLists(allPolicyTags?.[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`] ?? {})?.at(tagListIndex) ?? ({} as OnyxValueWithOfflineFeedback); + const policyTag = PolicyUtils.getTagLists(allPolicyTags?.[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`] ?? {})?.at(tagListIndex); + + if (!policyTag) { + return; + } if (!policyTag.name) { return; diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index db3a90c3c170..772b1bba7d7c 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -852,8 +852,10 @@ function openReport( if (isCreatingNewReport) { // Change the method to set for new reports because it doesn't exist yet, is faster, // and we need the data to be available when we navigate to the chat page - optimisticData.at(0).onyxMethod = Onyx.METHOD.SET; - optimisticData.at(0).value = { + // eslint-disable-next-line rulesdir/prefer-at + optimisticData[0].onyxMethod = Onyx.METHOD.SET; + // eslint-disable-next-line rulesdir/prefer-at + optimisticData[0].value = { ...optimisticReport, reportName: CONST.REPORT.DEFAULT_REPORT_NAME, ...newReportObject, @@ -885,7 +887,7 @@ function openReport( const redundantParticipants: Record = {}; const participantAccountIDs = PersonalDetailsUtils.getAccountIDsByLogins(participantLoginList); participantLoginList.forEach((login, index) => { - const accountID = participantAccountIDs.at(index); + const accountID = participantAccountIDs.at(index) ?? -1; const isOptimisticAccount = !allPersonalDetails?.[accountID]; if (!isOptimisticAccount) { @@ -3325,7 +3327,7 @@ function completeOnboarding( // If the target report isn't opened, the permission field will not exist. So we should add the fallback permission for task report const fallbackPermission = isAccountIDOdd ? [CONST.REPORT.PERMISSIONS.READ] : [CONST.REPORT.PERMISSIONS.READ, CONST.REPORT.PERMISSIONS.WRITE]; - const actorAccountID = PersonalDetailsUtils.getAccountIDsByLogins([targetEmail]).at(0); + const actorAccountID = PersonalDetailsUtils.getAccountIDsByLogins([targetEmail]).at(0) ?? -1; const targetChatReport = ReportUtils.getChatByParticipants([actorAccountID, currentUserAccountID]); const {reportID: targetChatReportID = '', policyID: targetChatPolicyID = ''} = targetChatReport ?? {}; diff --git a/src/libs/actions/TeachersUnite.ts b/src/libs/actions/TeachersUnite.ts index 00b321e4783a..1e550f22400d 100644 --- a/src/libs/actions/TeachersUnite.ts +++ b/src/libs/actions/TeachersUnite.ts @@ -143,7 +143,7 @@ function addSchoolPrincipal(firstName: string, partnerUserID: string, lastName: onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseChatReportID}`, value: { - [Object.keys(expenseChatData).at(0)]: { + [Object.keys(expenseChatData).at(0) ?? '']: { pendingAction: null, }, }, diff --git a/src/libs/actions/Transaction.ts b/src/libs/actions/Transaction.ts index 2af45756a551..fc288c02f631 100644 --- a/src/libs/actions/Transaction.ts +++ b/src/libs/actions/Transaction.ts @@ -311,7 +311,7 @@ function dismissDuplicateTransactionViolation(transactionIDs: string[], dissmiss onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${action?.childReportID ?? '-1'}`, value: { - [optimisticDissmidedViolationReportActions.at(index).reportActionID]: optimisticDissmidedViolationReportActions.at(index) as ReportAction, + [optimisticDissmidedViolationReportActions.at(index)?.reportActionID ?? '']: optimisticDissmidedViolationReportActions.at(index) as ReportAction, }, })); const optimisticDataTransactionViolations: OnyxUpdate[] = currentTransactionViolations.map((transactionViolations) => ({ @@ -359,7 +359,7 @@ function dismissDuplicateTransactionViolation(transactionIDs: string[], dissmiss onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${action?.childReportID ?? '-1'}`, value: { - [optimisticDissmidedViolationReportActions.at(index).reportActionID]: null, + [optimisticDissmidedViolationReportActions.at(index)?.reportActionID ?? '']: null, }, })); @@ -371,7 +371,7 @@ function dismissDuplicateTransactionViolation(transactionIDs: string[], dissmiss onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${action?.childReportID ?? '-1'}`, value: { - [optimisticDissmidedViolationReportActions.at(index).reportActionID]: { + [optimisticDissmidedViolationReportActions.at(index)?.reportActionID ?? '']: { pendingAction: null, }, }, diff --git a/src/libs/actions/Workflow.ts b/src/libs/actions/Workflow.ts index 8e06b2ba51f9..c081191457db 100644 --- a/src/libs/actions/Workflow.ts +++ b/src/libs/actions/Workflow.ts @@ -119,7 +119,7 @@ function updateApprovalWorkflow(policyID: string, approvalWorkflow: ApprovalWork } const previousDefaultApprover = policy.approver ?? policy.owner; - const newDefaultApprover = approvalWorkflow.isDefault ? approvalWorkflow.approvers.at(0).email : undefined; + const newDefaultApprover = approvalWorkflow.isDefault ? approvalWorkflow.approvers.at(0)?.email : undefined; const previousEmployeeList = {...policy.employeeList}; const updatedEmployees = convertApprovalWorkflowToPolicyEmployees({approvalWorkflow, type: CONST.APPROVAL_WORKFLOW.TYPE.UPDATE, membersToRemove}); diff --git a/src/libs/fileDownload/FileUtils.ts b/src/libs/fileDownload/FileUtils.ts index bee731d08667..751d2a3dfa7e 100644 --- a/src/libs/fileDownload/FileUtils.ts +++ b/src/libs/fileDownload/FileUtils.ts @@ -221,10 +221,10 @@ const readFileAsync: ReadFileAsync = (path, fileName, onSuccess, onFailure = () */ function base64ToFile(base64: string, filename: string): File { // Decode the base64 string - const byteString = atob(base64.split(',').at(1)); + const byteString = atob(base64.split(',').at(1) ?? ''); // Get the mime type from the base64 string - const mimeString = base64.split(',').at(0).split(':').at(1).split(';').at(0); + const mimeString = base64.split(',').at(0)?.split(':').at(1)?.split(';').at(0); // Convert byte string to Uint8Array const arrayBuffer = new ArrayBuffer(byteString.length); diff --git a/src/libs/memoize/cache/ArrayCache.ts b/src/libs/memoize/cache/ArrayCache.ts index bffe6462c7cf..64ebb00344bf 100644 --- a/src/libs/memoize/cache/ArrayCache.ts +++ b/src/libs/memoize/cache/ArrayCache.ts @@ -16,7 +16,8 @@ function ArrayCache(config: CacheConfig): Cache { */ function getKeyIndex(key: K): number { for (let i = cache.length - 1; i >= 0; i--) { - if (keyComparator(cache.at(i)[0], key)) { + // eslint-disable-next-line rulesdir/prefer-at + if (keyComparator(cache[i][0], key)) { return i; } } diff --git a/src/pages/EnablePayments/IdologyQuestions.tsx b/src/pages/EnablePayments/IdologyQuestions.tsx index 1952a4d63f80..602754bef556 100644 --- a/src/pages/EnablePayments/IdologyQuestions.tsx +++ b/src/pages/EnablePayments/IdologyQuestions.tsx @@ -83,7 +83,7 @@ function IdologyQuestions({questions, idNumber}: IdologyQuestionsProps) { // Auto skip any remaining questions if (tempAnswers.length < questions.length) { for (let i = tempAnswers.length; i < questions.length; i++) { - tempAnswers[i] = {question: questions.at(i).type, answer: SKIP_QUESTION_TEXT}; + tempAnswers[i] = {question: questions.at(i)?.type ?? '', answer: SKIP_QUESTION_TEXT}; } } diff --git a/src/pages/NewChatPage.tsx b/src/pages/NewChatPage.tsx index 3f216e67aff6..d1425026cf21 100755 --- a/src/pages/NewChatPage.tsx +++ b/src/pages/NewChatPage.tsx @@ -214,7 +214,7 @@ function NewChatPage({isGroupChat}: NewChatPageProps) { if (option?.login) { login = option.login; } else if (selectedOptions.length === 1) { - login = selectedOptions.at(0).login ?? ''; + login = selectedOptions.at(0)?.login ?? ''; } if (!login) { Log.warn('Tried to create chat with empty login'); diff --git a/src/pages/ReportDetailsPage.tsx b/src/pages/ReportDetailsPage.tsx index 6f66678e6b2c..d941e357a053 100644 --- a/src/pages/ReportDetailsPage.tsx +++ b/src/pages/ReportDetailsPage.tsx @@ -496,8 +496,8 @@ function ReportDetailsPage({policies, report, session, personalDetails}: ReportD if (isGroupChat && !isThread) { return ( {shouldShowSubscript ? ( diff --git a/src/pages/home/report/ReportActionCompose/SuggestionEmoji.tsx b/src/pages/home/report/ReportActionCompose/SuggestionEmoji.tsx index 40d906d14d50..c29eb44942b2 100644 --- a/src/pages/home/report/ReportActionCompose/SuggestionEmoji.tsx +++ b/src/pages/home/report/ReportActionCompose/SuggestionEmoji.tsx @@ -84,7 +84,7 @@ function SuggestionEmoji( (highlightedEmojiIndexInner: number) => { const commentBeforeColon = value.slice(0, suggestionValues.colonIndex); const emojiObject = suggestionValues.suggestedEmojis.at(highlightedEmojiIndexInner); - const emojiCode = emojiObject.types?.[preferredSkinTone] ? emojiObject.types.at(preferredSkinTone) : emojiObject.code; + const emojiCode = emojiObject?.types?.[preferredSkinTone] ? emojiObject?.types.at(preferredSkinTone) : emojiObject?.code; const commentAfterColonWithEmojiNameRemoved = value.slice(selection.end); updateComment(`${commentBeforeColon}${emojiCode} ${SuggestionsUtils.trimLeadingSpace(commentAfterColonWithEmojiNameRemoved)}`, true); @@ -95,8 +95,8 @@ function SuggestionEmoji( resetKeyboardInput?.(); setSelection({ - start: suggestionValues.colonIndex + emojiCode.length + CONST.SPACE_LENGTH, - end: suggestionValues.colonIndex + emojiCode.length + CONST.SPACE_LENGTH, + start: suggestionValues.colonIndex + (emojiCode?.length ?? 0) + CONST.SPACE_LENGTH, + end: suggestionValues.colonIndex + (emojiCode?.length ?? 0) + CONST.SPACE_LENGTH, }); setSuggestionValues((prevState) => ({...prevState, suggestedEmojis: []})); }, diff --git a/src/pages/home/report/ReportActionCompose/SuggestionMention.tsx b/src/pages/home/report/ReportActionCompose/SuggestionMention.tsx index 55c2cead443f..54840d24a2cc 100644 --- a/src/pages/home/report/ReportActionCompose/SuggestionMention.tsx +++ b/src/pages/home/report/ReportActionCompose/SuggestionMention.tsx @@ -194,6 +194,9 @@ function SuggestionMention( (highlightedMentionIndexInner: number) => { const commentBeforeAtSign = value.slice(0, suggestionValues.atSignIndex); const mentionObject = suggestionValues.suggestedMentions.at(highlightedMentionIndexInner); + if (!mentionObject) { + return; + } const mentionCode = getMentionCode(mentionObject, suggestionValues.prefixType); const commentAfterMention = value.slice(suggestionValues.atSignIndex + suggestionValues.mentionPrefix.length + 1); diff --git a/src/pages/home/report/ReportActionItemSingle.tsx b/src/pages/home/report/ReportActionItemSingle.tsx index 7b15d6823b0b..6012f8fee61e 100644 --- a/src/pages/home/report/ReportActionItemSingle.tsx +++ b/src/pages/home/report/ReportActionItemSingle.tsx @@ -28,6 +28,7 @@ import ROUTES from '@src/ROUTES'; import type {Report, ReportAction} from '@src/types/onyx'; import type {Icon} from '@src/types/onyx/OnyxCommon'; import type ChildrenProps from '@src/types/utils/ChildrenProps'; +import getDefaultIcon from '@src/utils/getDefaultIcon'; import ReportActionItemDate from './ReportActionItemDate'; import ReportActionItemFragment from './ReportActionItemFragment'; @@ -146,7 +147,7 @@ function ReportActionItemSingle({ const avatarIconIndex = report.isOwnPolicyExpenseChat || ReportUtils.isPolicyExpenseChat(report) ? 0 : 1; const reportIcons = ReportUtils.getIcons(report, {}); - secondaryAvatar = reportIcons.at(avatarIconIndex); + secondaryAvatar = reportIcons.at(avatarIconIndex) ?? getDefaultIcon; } else { secondaryAvatar = {name: '', source: '', type: 'avatar'}; } @@ -206,7 +207,7 @@ function ReportActionItemSingle({ if (shouldShowSubscriptAvatar) { return ( diff --git a/src/pages/home/report/ReportActionsList.tsx b/src/pages/home/report/ReportActionsList.tsx index b96be98dc211..7435070f5239 100644 --- a/src/pages/home/report/ReportActionsList.tsx +++ b/src/pages/home/report/ReportActionsList.tsx @@ -241,6 +241,12 @@ function ReportActionsList({ // Scan through each visible report action until we find the appropriate action to show the unread marker for (let index = 0; index < sortedVisibleReportActions.length; index++) { const reportAction = sortedVisibleReportActions.at(index); + + if (!reportAction) { + // eslint-disable-next-line no-continue + continue; + } + if (shouldDisplayNewMarker(reportAction, index)) { return reportAction.reportActionID; } diff --git a/src/pages/home/report/ReportActionsView.tsx b/src/pages/home/report/ReportActionsView.tsx index d7544e9f5b55..6af2abf698a8 100755 --- a/src/pages/home/report/ReportActionsView.tsx +++ b/src/pages/home/report/ReportActionsView.tsx @@ -162,7 +162,7 @@ function ReportActionsView({ const actions = [...allReportActions]; const lastAction = allReportActions.at(allReportActions.length - 1); - if (!ReportActionsUtils.isCreatedAction(lastAction)) { + if (lastAction && !ReportActionsUtils.isCreatedAction(lastAction)) { const optimisticCreatedAction = ReportUtils.buildOptimisticCreatedReportAction(String(report?.ownerAccountID), DateUtils.subtractMillisecondsFromDateTime(lastAction.created, 1)); optimisticCreatedAction.pendingAction = null; actions.push(optimisticCreatedAction); @@ -193,7 +193,7 @@ function ReportActionsView({ false, false, false, - DateUtils.subtractMillisecondsFromDateTime(actions.at(actions.length - 1).created, 1), + DateUtils.subtractMillisecondsFromDateTime(actions.at(actions.length - 1)?.created ?? '', 1), ) as OnyxTypes.ReportAction; moneyRequestActions.push(optimisticIOUAction); actions.splice(actions.length - 1, 0, optimisticIOUAction); @@ -322,7 +322,9 @@ function ReportActionsView({ // This function is a placeholder as the actual pagination is handled by visibleReportActions if (!hasMoreCached && !hasNewestReportAction) { isFirstLinkedActionRender.current = false; - fetchNewerAction(newestReportAction); + if (newestReportAction) { + fetchNewerAction(newestReportAction); + } } if (isFirstLinkedActionRender.current) { isFirstLinkedActionRender.current = false; @@ -394,7 +396,7 @@ function ReportActionsView({ // If there was an error only try again once on initial mount. We should also still load // more in case we have cached messages. (!hasMoreCached && didLoadNewerChats.current && hasLoadingNewerReportActionsError) || - newestReportAction.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE) + newestReportAction?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE) ) { return; } @@ -402,7 +404,7 @@ function ReportActionsView({ didLoadNewerChats.current = true; if ((reportActionID && indexOfLinkedAction > -1) || !reportActionID) { - handleReportActionPagination({firstReportActionID: newestReportAction?.reportActionID}); + handleReportActionPagination({firstReportActionID: newestReportAction?.reportActionID ?? ''}); } }, [ diff --git a/src/pages/iou/request/step/IOURequestStepAmount.tsx b/src/pages/iou/request/step/IOURequestStepAmount.tsx index 1ef904f8f8b4..b4bb8e71bd5c 100644 --- a/src/pages/iou/request/step/IOURequestStepAmount.tsx +++ b/src/pages/iou/request/step/IOURequestStepAmount.tsx @@ -214,11 +214,11 @@ function IOURequestStepAmount({ if (iouType === CONST.IOU.TYPE.PAY || iouType === CONST.IOU.TYPE.SEND) { if (paymentMethod && paymentMethod === CONST.IOU.PAYMENT_TYPE.EXPENSIFY) { - IOU.sendMoneyWithWallet(report, backendAmount, currency, '', currentUserPersonalDetails.accountID, participants.at(0)); + IOU.sendMoneyWithWallet(report, backendAmount, currency, '', currentUserPersonalDetails.accountID, participants.at(0) ?? {}); return; } - IOU.sendMoneyElsewhere(report, backendAmount, currency, '', currentUserPersonalDetails.accountID, participants.at(0)); + IOU.sendMoneyElsewhere(report, backendAmount, currency, '', currentUserPersonalDetails.accountID, participants.at(0) ?? {}); return; } if (iouType === CONST.IOU.TYPE.SUBMIT || iouType === CONST.IOU.TYPE.REQUEST) { @@ -230,7 +230,7 @@ function IOURequestStepAmount({ '', currentUserPersonalDetails.login, currentUserPersonalDetails.accountID, - participants.at(0), + participants.at(0) ?? {}, '', {}, ); @@ -245,7 +245,7 @@ function IOURequestStepAmount({ '', currentUserPersonalDetails.login, currentUserPersonalDetails.accountID, - participants.at(0), + participants.at(0) ?? {}, '', ); return; diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx index 48a0a9ae7b61..ac1a1256284e 100644 --- a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx +++ b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx @@ -241,6 +241,11 @@ function IOURequestStepConfirmation({ return; } + const participant = selectedParticipants.at(0); + if (!participant) { + return; + } + IOU.requestMoney( report, transaction.amount, @@ -249,7 +254,7 @@ function IOURequestStepConfirmation({ transaction.merchant, currentUserPersonalDetails.login, currentUserPersonalDetails.accountID, - selectedParticipants.at(0), + participant, trimmedComment, receiptObj, transaction.category, @@ -275,6 +280,10 @@ function IOURequestStepConfirmation({ if (!report || !transaction) { return; } + const participant = selectedParticipants.at(0); + if (!participant) { + return; + } IOU.trackExpense( report, transaction.amount, @@ -283,7 +292,7 @@ function IOURequestStepConfirmation({ transaction.merchant, currentUserPersonalDetails.login, currentUserPersonalDetails.accountID, - selectedParticipants.at(0), + participant, trimmedComment, receiptObj, transaction.category, diff --git a/src/pages/iou/request/step/IOURequestStepDistance.tsx b/src/pages/iou/request/step/IOURequestStepDistance.tsx index 8b55c0234d3a..1f22f7883bdb 100644 --- a/src/pages/iou/request/step/IOURequestStepDistance.tsx +++ b/src/pages/iou/request/step/IOURequestStepDistance.tsx @@ -281,7 +281,8 @@ function IOURequestStepDistance({ } IOU.setMoneyRequestPendingFields(transactionID, {waypoints: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD}); IOU.setMoneyRequestMerchant(transactionID, translate('iou.fieldPending'), false); - if (iouType === CONST.IOU.TYPE.TRACK) { + const participant = participants.at(0); + if (iouType === CONST.IOU.TYPE.TRACK && participant) { IOU.trackExpense( report, 0, @@ -290,7 +291,7 @@ function IOURequestStepDistance({ translate('iou.fieldPending'), currentUserPersonalDetails.login, currentUserPersonalDetails.accountID, - participants.at(0), + participant, '', {}, '', diff --git a/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx b/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx index 858fecd384f0..bca2d0bb4ede 100644 --- a/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx +++ b/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx @@ -268,6 +268,10 @@ function IOURequestStepScan({ } getCurrentPosition( (successData) => { + const participant = participants.at(0); + if (!participant) { + return; + } if (iouType === CONST.IOU.TYPE.TRACK && report) { IOU.trackExpense( report, @@ -277,7 +281,7 @@ function IOURequestStepScan({ '', currentUserPersonalDetails.login, currentUserPersonalDetails.accountID, - participants.at(0), + participant, '', receipt, '', @@ -302,7 +306,7 @@ function IOURequestStepScan({ '', currentUserPersonalDetails.login, currentUserPersonalDetails.accountID, - participants.at(0), + participant, '', receipt, '', @@ -321,6 +325,10 @@ function IOURequestStepScan({ } }, (errorData) => { + const participant = participants.at(0); + if (!participant) { + return; + } Log.info('[IOURequestStepScan] getCurrentPosition failed', false, errorData); // When there is an error, the money can still be requested, it just won't include the GPS coordinates if (iouType === CONST.IOU.TYPE.TRACK && report) { @@ -332,7 +340,7 @@ function IOURequestStepScan({ '', currentUserPersonalDetails.login, currentUserPersonalDetails.accountID, - participants.at(0), + participant, '', receipt, ); @@ -345,7 +353,7 @@ function IOURequestStepScan({ '', currentUserPersonalDetails.login, currentUserPersonalDetails.accountID, - participants.at(0), + participant, '', receipt, ); diff --git a/src/pages/iou/request/step/IOURequestStepScan/index.tsx b/src/pages/iou/request/step/IOURequestStepScan/index.tsx index 2841dbb7030b..d0e276904358 100644 --- a/src/pages/iou/request/step/IOURequestStepScan/index.tsx +++ b/src/pages/iou/request/step/IOURequestStepScan/index.tsx @@ -139,7 +139,7 @@ function IOURequestStepScan({ let lastBackDeviceId = ''; for (let i = devices.length - 1; i >= 0; i--) { const device = devices.at(i); - if (device.kind === 'videoinput') { + if (device?.kind === 'videoinput') { lastBackDeviceId = device.deviceId; break; } @@ -300,6 +300,10 @@ function IOURequestStepScan({ } getCurrentPosition( (successData) => { + const participant = participants.at(0); + if (!participant) { + return; + } if (iouType === CONST.IOU.TYPE.TRACK && report) { IOU.trackExpense( report, @@ -309,7 +313,7 @@ function IOURequestStepScan({ '', currentUserPersonalDetails.login, currentUserPersonalDetails.accountID, - participants.at(0), + participant, '', receipt, '', @@ -334,7 +338,7 @@ function IOURequestStepScan({ '', currentUserPersonalDetails.login, currentUserPersonalDetails.accountID, - participants.at(0), + participant, '', receipt, '', @@ -353,6 +357,10 @@ function IOURequestStepScan({ } }, (errorData) => { + const participant = participants.at(0); + if (!participant) { + return; + } Log.info('[IOURequestStepScan] getCurrentPosition failed', false, errorData); // When there is an error, the money can still be requested, it just won't include the GPS coordinates if (iouType === CONST.IOU.TYPE.TRACK && report) { @@ -364,7 +372,7 @@ function IOURequestStepScan({ '', currentUserPersonalDetails.login, currentUserPersonalDetails.accountID, - participants.at(0), + participant, '', receipt, ); @@ -377,7 +385,7 @@ function IOURequestStepScan({ '', currentUserPersonalDetails.login, currentUserPersonalDetails.accountID, - participants.at(0), + participant, '', receipt, ); diff --git a/src/pages/settings/Wallet/PaymentMethodList.tsx b/src/pages/settings/Wallet/PaymentMethodList.tsx index e05d19413590..c30f345ec090 100644 --- a/src/pages/settings/Wallet/PaymentMethodList.tsx +++ b/src/pages/settings/Wallet/PaymentMethodList.tsx @@ -233,9 +233,11 @@ function PaymentMethodList({ // The card should be grouped to a specific domain and such domain already exists in a assignedCardsGrouped if (assignedCardsGrouped.some((item) => item.isGroupedCardDomain && item.description === card.domainName) && !isAdminIssuedVirtualCard) { const domainGroupIndex = assignedCardsGrouped.findIndex((item) => item.isGroupedCardDomain && item.description === card.domainName); - assignedCardsGrouped.at(domainGroupIndex).errors = {...assignedCardsGrouped.at(domainGroupIndex).errors, ...card.errors}; + // eslint-disable-next-line rulesdir/prefer-at + assignedCardsGrouped[domainGroupIndex].errors = {...assignedCardsGrouped.at(domainGroupIndex)?.errors, ...card.errors}; if (card.fraud === CONST.EXPENSIFY_CARD.FRAUD_TYPES.DOMAIN || card.fraud === CONST.EXPENSIFY_CARD.FRAUD_TYPES.INDIVIDUAL) { - assignedCardsGrouped.at(domainGroupIndex).brickRoadIndicator = CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR; + // eslint-disable-next-line rulesdir/prefer-at + assignedCardsGrouped[domainGroupIndex].brickRoadIndicator = CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR; } return; } diff --git a/src/pages/workspace/WorkspacesListPage.tsx b/src/pages/workspace/WorkspacesListPage.tsx index 858a57f25aab..41380cbe0a4d 100755 --- a/src/pages/workspace/WorkspacesListPage.tsx +++ b/src/pages/workspace/WorkspacesListPage.tsx @@ -318,12 +318,12 @@ function WorkspacesListPage({policies, reimbursementAccount, reports, session}: const policyInfo = Object.values(policy.policyDetailsForNonMembers).at(0); const id = Object.keys(policy.policyDetailsForNonMembers).at(0); return { - title: policyInfo.name, - icon: policyInfo.avatar ? policyInfo.avatar : ReportUtils.getDefaultWorkspaceAvatar(policy.name), + title: policyInfo?.name ?? '', + icon: policyInfo?.avatar ? policyInfo?.avatar : ReportUtils.getDefaultWorkspaceAvatar(policy.name), disabled: true, - ownerAccountID: policyInfo.ownerAccountID, - type: policyInfo.type, - iconType: policyInfo.avatar ? CONST.ICON_TYPE_AVATAR : CONST.ICON_TYPE_ICON, + ownerAccountID: policyInfo?.ownerAccountID, + type: policyInfo?.type ?? CONST.POLICY.TYPE.FREE, + iconType: policyInfo?.avatar ? CONST.ICON_TYPE_AVATAR : CONST.ICON_TYPE_ICON, iconFill: theme.textLight, fallbackIcon: Expensicons.FallbackWorkspaceAvatar, policyID: id, diff --git a/src/pages/workspace/accounting/qbo/advanced/QuickbooksAdvancedPage.tsx b/src/pages/workspace/accounting/qbo/advanced/QuickbooksAdvancedPage.tsx index e73450933f10..76670537db49 100644 --- a/src/pages/workspace/accounting/qbo/advanced/QuickbooksAdvancedPage.tsx +++ b/src/pages/workspace/accounting/qbo/advanced/QuickbooksAdvancedPage.tsx @@ -116,7 +116,7 @@ function QuickbooksAdvancedPage({policy}: WithPolicyConnectionsProps) { policyID, CONST.POLICY.CONNECTIONS.NAME.QBO, CONST.QUICK_BOOKS_CONFIG.COLLECTION_ACCOUNT_ID, - isSyncReimbursedSwitchOn ? '' : [...qboAccountOptions, ...invoiceAccountCollectionOptions].at(0).id, + isSyncReimbursedSwitchOn ? '' : [...qboAccountOptions, ...invoiceAccountCollectionOptions].at(0)?.id ?? '', ), pendingAction: pendingFields?.collectionAccountID, errors: ErrorUtils.getLatestErrorField(qboConfig ?? {}, CONST.QUICK_BOOKS_CONFIG.COLLECTION_ACCOUNT_ID), diff --git a/src/pages/workspace/accounting/qbo/export/QuickbooksCompanyCardExpenseAccountSelectCardPage.tsx b/src/pages/workspace/accounting/qbo/export/QuickbooksCompanyCardExpenseAccountSelectCardPage.tsx index 4fef8922eb28..4e080259173a 100644 --- a/src/pages/workspace/accounting/qbo/export/QuickbooksCompanyCardExpenseAccountSelectCardPage.tsx +++ b/src/pages/workspace/accounting/qbo/export/QuickbooksCompanyCardExpenseAccountSelectCardPage.tsx @@ -101,7 +101,7 @@ function QuickbooksCompanyCardExpenseAccountSelectCardPage({policy}: WithPolicyC ListItem={RadioListItem} onSelectRow={selectExportCompanyCard} shouldSingleExecuteRowSelect - initiallyFocusedOptionKey={sections.at(0).data.find((option) => option.isSelected)?.keyForList} + initiallyFocusedOptionKey={sections.at(0)?.data?.find((option) => option.isSelected)?.keyForList} footerContent={ isLocationEnabled && {translate('workspace.qbo.companyCardsLocationEnabledDescription')} } diff --git a/src/pages/workspace/reportFields/InitialListValueSelector/ReportFieldsInitialListValuePicker.tsx b/src/pages/workspace/reportFields/InitialListValueSelector/ReportFieldsInitialListValuePicker.tsx index 5d138f98d1e4..fcade7f74b8b 100644 --- a/src/pages/workspace/reportFields/InitialListValueSelector/ReportFieldsInitialListValuePicker.tsx +++ b/src/pages/workspace/reportFields/InitialListValueSelector/ReportFieldsInitialListValuePicker.tsx @@ -40,7 +40,7 @@ function ReportFieldsInitialListValuePicker({listValues, disabledOptions, value, sections={listValueSections} ListItem={RadioListItem} onSelectRow={(item) => onValueChange(item.value)} - initiallyFocusedOptionKey={listValueSections.at(0).data.find((listValue) => listValue.isSelected)?.keyForList} + initiallyFocusedOptionKey={listValueSections.at(0)?.data?.find((listValue) => listValue.isSelected)?.keyForList} /> ); } diff --git a/src/pages/workspace/reportFields/ReportFieldTypePicker/index.tsx b/src/pages/workspace/reportFields/ReportFieldTypePicker/index.tsx index 0bec71bbff4a..9c9cf0c94e29 100644 --- a/src/pages/workspace/reportFields/ReportFieldTypePicker/index.tsx +++ b/src/pages/workspace/reportFields/ReportFieldTypePicker/index.tsx @@ -48,7 +48,7 @@ function ReportFieldTypePicker({defaultValue, onOptionSelected}: ReportFieldType sections={typeSections} ListItem={RadioListItem} onSelectRow={onOptionSelected} - initiallyFocusedOptionKey={typeSections.at(0).data.find((reportField) => reportField.isSelected)?.keyForList} + initiallyFocusedOptionKey={typeSections.at(0)?.data?.find((reportField) => reportField.isSelected)?.keyForList} /> ); } diff --git a/src/pages/workspace/tags/WorkspaceTagsSettingsPage.tsx b/src/pages/workspace/tags/WorkspaceTagsSettingsPage.tsx index f37ee3905884..9f2af3216a4e 100644 --- a/src/pages/workspace/tags/WorkspaceTagsSettingsPage.tsx +++ b/src/pages/workspace/tags/WorkspaceTagsSettingsPage.tsx @@ -75,7 +75,7 @@ function WorkspaceTagsSettingsPage({route, policyTags}: WorkspaceTagsSettingsPag Navigation.navigate(ROUTES.WORKSPACE_EDIT_TAGS.getRoute(policyID, policyTagLists.at(0).orderWeight))} + onPress={() => Navigation.navigate(ROUTES.WORKSPACE_EDIT_TAGS.getRoute(policyID, policyTagLists.at(0)?.orderWeight ?? 0))} shouldShowRightIcon /> diff --git a/src/pages/workspace/tags/WorkspaceViewTagsPage.tsx b/src/pages/workspace/tags/WorkspaceViewTagsPage.tsx index 752da36394d0..754af5cb690e 100644 --- a/src/pages/workspace/tags/WorkspaceViewTagsPage.tsx +++ b/src/pages/workspace/tags/WorkspaceViewTagsPage.tsx @@ -232,7 +232,7 @@ function WorkspaceViewTagsPage({route}: WorkspaceViewTagsProps) { }; const navigateToEditTag = () => { - Navigation.navigate(ROUTES.WORKSPACE_EDIT_TAGS.getRoute(route.params.policyID, currentPolicyTag?.orderWeight)); + Navigation.navigate(ROUTES.WORKSPACE_EDIT_TAGS.getRoute(route.params.policyID, currentPolicyTag?.orderWeight ?? 0)); }; const selectionModeHeader = selectionMode?.isEnabled && isSmallScreenWidth; diff --git a/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx b/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx index 3980d9484d09..4cdaf8c561a3 100644 --- a/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx +++ b/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx @@ -179,7 +179,7 @@ function WorkspaceWorkflowsPage({policy, betas, route}: WorkspaceWorkflowsPagePr > Navigation.navigate(ROUTES.WORKSPACE_WORKFLOWS_APPROVALS_EDIT.getRoute(route.params.policyID, workflow.approvers.at(0).email))} + onPress={() => Navigation.navigate(ROUTES.WORKSPACE_WORKFLOWS_APPROVALS_EDIT.getRoute(route.params.policyID, workflow.approvers.at(0)?.email ?? ''))} /> ))} diff --git a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx index bc937e85bc9c..0931492614cc 100644 --- a/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx +++ b/src/pages/workspace/workflows/WorkspaceWorkflowsPayerPage.tsx @@ -137,7 +137,7 @@ function WorkspaceWorkflowsPayerPage({route, policy, personalDetails, isLoadingR }, [formattedPolicyAdmins, formattedAuthorizedPayer, translate, searchTerm]); const headerMessage = useMemo( - () => (searchTerm && !sections.at(0).data.length ? translate('common.noResultsFound') : ''), + () => (searchTerm && !sections.at(0)?.data.length ? translate('common.noResultsFound') : ''), // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps [translate, sections], diff --git a/src/pages/workspace/workflows/approvals/WorkspaceWorkflowsApprovalsApproverPage.tsx b/src/pages/workspace/workflows/approvals/WorkspaceWorkflowsApprovalsApproverPage.tsx index e2578997d211..a589bc5fb795 100644 --- a/src/pages/workspace/workflows/approvals/WorkspaceWorkflowsApprovalsApproverPage.tsx +++ b/src/pages/workspace/workflows/approvals/WorkspaceWorkflowsApprovalsApproverPage.tsx @@ -220,7 +220,7 @@ function WorkspaceWorkflowsApprovalsApproverPageBeta({policy, personalDetails, i setSelectedApproverEmail(approver.login); }; - const headerMessage = useMemo(() => (searchTerm && !sections.at(0).data.length ? translate('common.noResultsFound') : ''), [searchTerm, sections, translate]); + const headerMessage = useMemo(() => (searchTerm && !sections.at(0)?.data?.length ? translate('common.noResultsFound') : ''), [searchTerm, sections, translate]); return ( (searchTerm && !sections.at(0).data.length ? translate('common.noResultsFound') : ''), + () => (searchTerm && !sections.at(0)?.data?.length ? translate('common.noResultsFound') : ''), // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps [translate, sections], diff --git a/src/pages/workspace/workflows/approvals/WorkspaceWorkflowsApprovalsExpensesFromPage.tsx b/src/pages/workspace/workflows/approvals/WorkspaceWorkflowsApprovalsExpensesFromPage.tsx index 13d085b1b9c2..e18158317e0c 100644 --- a/src/pages/workspace/workflows/approvals/WorkspaceWorkflowsApprovalsExpensesFromPage.tsx +++ b/src/pages/workspace/workflows/approvals/WorkspaceWorkflowsApprovalsExpensesFromPage.tsx @@ -168,7 +168,7 @@ function WorkspaceWorkflowsApprovalsExpensesFromPage({policy, isLoadingReportDat setSelectedMembers(isAlreadySelected ? selectedMembers.filter((selectedOption) => selectedOption.login !== member.login) : [...selectedMembers, {...member, isSelected: true}]); }; - const headerMessage = useMemo(() => (searchTerm && !sections.at(0).data.length ? translate('common.noResultsFound') : ''), [searchTerm, sections, translate]); + const headerMessage = useMemo(() => (searchTerm && !sections.at(0)?.data?.length ? translate('common.noResultsFound') : ''), [searchTerm, sections, translate]); return ( ): EReceiptColo const colorHash = UserUtils.hashText(transactionID.trim(), eReceiptColors.length); - return eReceiptColors.at(colorHash); + return eReceiptColors.at(colorHash) ?? 'Yellow'; } /** diff --git a/src/utils/getDefaultIcon.ts b/src/utils/getDefaultIcon.ts new file mode 100644 index 000000000000..c6472152ca68 --- /dev/null +++ b/src/utils/getDefaultIcon.ts @@ -0,0 +1,12 @@ +import {FallbackAvatar} from '@components/Icon/Expensicons'; +import CONST from '@src/CONST'; +import type {Icon} from '@src/types/onyx/OnyxCommon'; + +const fallbackIcon: Icon = { + source: FallbackAvatar, + type: CONST.ICON_TYPE_AVATAR, + name: '', + id: -1, +}; + +export default fallbackIcon; diff --git a/tests/actions/IOUTest.ts b/tests/actions/IOUTest.ts index b4744ea9c6c9..ee627e27a00b 100644 --- a/tests/actions/IOUTest.ts +++ b/tests/actions/IOUTest.ts @@ -132,8 +132,8 @@ describe('actions/IOU', () => { ); expect(Object.values(createdActions).length).toBe(1); expect(Object.values(iouActions).length).toBe(1); - createdAction = createdActions?.at(0) ?? null; - iouAction = iouActions?.at(0) ?? null; + createdAction = createdActions?.at(0); + iouAction = iouActions?.at(0); const originalMessage = ReportActionsUtils.isMoneyRequestAction(iouAction) ? ReportActionsUtils.getOriginalMessage(iouAction) : undefined; // The CREATED action should not be created after the IOU action @@ -676,7 +676,7 @@ describe('actions/IOU', () => { const originalMessage = ReportActionsUtils.getOriginalMessage(iouAction); // The CREATED action should not be created after the IOU action - expect(Date.parse(createdAction?.created ?? '')).toBeLessThan(Date.parse(iouAction?.created ?? {})); + expect(Date.parse(createdAction?.created ?? '')).toBeLessThan(Date.parse(iouAction?.created ?? '')); // The IOUReportID should be correct expect(originalMessage?.IOUReportID).toBe(iouReportID); diff --git a/tests/actions/PolicyCategoryTest.ts b/tests/actions/PolicyCategoryTest.ts index 467712e069c6..351adaf8e982 100644 --- a/tests/actions/PolicyCategoryTest.ts +++ b/tests/actions/PolicyCategoryTest.ts @@ -117,7 +117,7 @@ describe('actions/PolicyCategory', () => { Onyx.set(`${ONYXKEYS.COLLECTION.POLICY}${fakePolicy.id}`, fakePolicy); Onyx.set(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${fakePolicy.id}`, fakeCategories); Category.renamePolicyCategory(fakePolicy.id, { - oldName: oldCategoryName, + oldName: oldCategoryName ?? '', newName: newCategoryName, }); await waitForBatchedUpdates(); @@ -128,7 +128,7 @@ describe('actions/PolicyCategory', () => { callback: (policyCategories) => { Onyx.disconnect(connection); - expect(policyCategories?.[oldCategoryName]).toBeFalsy(); + expect(policyCategories?.[oldCategoryName ?? '']).toBeFalsy(); expect(policyCategories?.[newCategoryName]?.name).toBe(newCategoryName); expect(policyCategories?.[newCategoryName]?.pendingAction).toBe(CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE); expect(policyCategories?.[newCategoryName]?.pendingFields?.name).toBe(CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE); diff --git a/tests/actions/PolicyTagTest.ts b/tests/actions/PolicyTagTest.ts index 04ab9c2bd0eb..36a6126ad208 100644 --- a/tests/actions/PolicyTagTest.ts +++ b/tests/actions/PolicyTagTest.ts @@ -177,7 +177,7 @@ describe('actions/Policy', () => { newName: newTagListName, }, fakePolicyTags, - Object.values(fakePolicyTags).at(0).orderWeight, + Object.values(fakePolicyTags).at(0)?.orderWeight ?? 0, ); return waitForBatchedUpdates(); }) @@ -245,7 +245,7 @@ describe('actions/Policy', () => { newName: newTagListName, }, fakePolicyTags, - Object.values(fakePolicyTags).at(0).orderWeight, + Object.values(fakePolicyTags).at(0)?.orderWeight ?? 0, ); return waitForBatchedUpdates(); }) @@ -519,7 +519,7 @@ describe('actions/Policy', () => { Tag.renamePolicyTag( fakePolicy.id, { - oldName: oldTagName, + oldName: oldTagName ?? '', newName: newTagName, }, 0, @@ -536,7 +536,7 @@ describe('actions/Policy', () => { Onyx.disconnect(connection); const tags = policyTags?.[tagListName]?.tags; - expect(tags?.[oldTagName]).toBeFalsy(); + expect(tags?.[oldTagName ?? '']).toBeFalsy(); expect(tags?.[newTagName]?.name).toBe(newTagName); expect(tags?.[newTagName]?.pendingAction).toBe(CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE); expect(tags?.[newTagName]?.pendingFields?.name).toBe(CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE); diff --git a/tests/actions/ReportTest.ts b/tests/actions/ReportTest.ts index 2df5e73c6d8c..cd46d2008bb3 100644 --- a/tests/actions/ReportTest.ts +++ b/tests/actions/ReportTest.ts @@ -1,4 +1,6 @@ /* eslint-disable @typescript-eslint/naming-convention */ + +/* eslint-disable rulesdir/prefer-at */ import {afterEach, beforeAll, beforeEach, describe, expect, it} from '@jest/globals'; import {utcToZonedTime} from 'date-fns-tz'; import Onyx from 'react-native-onyx'; @@ -88,7 +90,7 @@ describe('actions/Report', () => { return waitForBatchedUpdates(); }) .then(() => { - const resultAction: OnyxEntry = Object.values(reportActions ?? {}).at(0); + const resultAction: OnyxEntry = Object.values(reportActions ?? {})[0]; reportActionID = resultAction.reportActionID; expect(resultAction.message).toEqual(REPORT_ACTION.message); @@ -186,7 +188,7 @@ describe('actions/Report', () => { .then(() => { // THEN only ONE call to AddComment will happen const URL_ARGUMENT_INDEX = 0; - const addCommentCalls = (global.fetch as jest.Mock).mock.calls.filter((callArguments: string[]) => callArguments.at(URL_ARGUMENT_INDEX).includes('AddComment')); + const addCommentCalls = (global.fetch as jest.Mock).mock.calls.filter((callArguments: string[]) => callArguments[URL_ARGUMENT_INDEX].includes('AddComment')); expect(addCommentCalls.length).toBe(1); }); }); @@ -584,7 +586,7 @@ describe('actions/Report', () => { return waitForBatchedUpdates(); }) .then(() => { - reportAction = Object.values(reportActions).at(0); + reportAction = Object.values(reportActions)[0]; reportActionID = reportAction.reportActionID; // Add a reaction to the comment @@ -592,7 +594,7 @@ describe('actions/Report', () => { return waitForBatchedUpdates(); }) .then(() => { - reportAction = Object.values(reportActions).at(0); + reportAction = Object.values(reportActions)[0]; // Expect the reaction to exist in the reportActionsReactions collection expect(reportActionsReactions).toHaveProperty(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${reportActionID}`); @@ -616,20 +618,20 @@ describe('actions/Report', () => { expect(reportActionReaction?.[EMOJI.name].users[TEST_USER_ACCOUNT_ID]).toBeUndefined(); }) .then(() => { - reportAction = Object.values(reportActions).at(0); + reportAction = Object.values(reportActions)[0]; // Add the same reaction to the same report action with a different skintone Report.toggleEmojiReaction(REPORT_ID, reportAction, EMOJI, reportActionsReactions[0]); return waitForBatchedUpdates() .then(() => { - reportAction = Object.values(reportActions).at(0); + reportAction = Object.values(reportActions)[0]; const reportActionReaction = reportActionsReactions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${reportActionID}`]; Report.toggleEmojiReaction(REPORT_ID, reportAction, EMOJI, reportActionReaction, EMOJI_SKIN_TONE); return waitForBatchedUpdates(); }) .then(() => { - reportAction = Object.values(reportActions).at(0); + reportAction = Object.values(reportActions)[0]; // Expect the reaction to exist in the reportActionsReactions collection expect(reportActionsReactions).toHaveProperty(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${reportActionID}`); @@ -702,14 +704,14 @@ describe('actions/Report', () => { return waitForBatchedUpdates(); }) .then(() => { - resultAction = Object.values(reportActions).at(0); + resultAction = Object.values(reportActions)[0]; // Add a reaction to the comment Report.toggleEmojiReaction(REPORT_ID, resultAction, EMOJI, {}); return waitForBatchedUpdates(); }) .then(() => { - resultAction = Object.values(reportActions).at(0); + resultAction = Object.values(reportActions)[0]; // Now we toggle the reaction while the skin tone has changed. // As the emoji doesn't support skin tones, the emoji diff --git a/tests/e2e/compare/output/markdownTable.ts b/tests/e2e/compare/output/markdownTable.ts index 20ade25e523a..4daea0bbdd63 100644 --- a/tests/e2e/compare/output/markdownTable.ts +++ b/tests/e2e/compare/output/markdownTable.ts @@ -200,18 +200,20 @@ function markdownTable(table: Array>, options: const sizes: number[] = []; let columnIndex = -1; - if (table.at(rowIndex).length > mostCellsPerRow) { - mostCellsPerRow = table.at(rowIndex).length; + const rowData = table.at(rowIndex) ?? []; + + if (rowData.length > mostCellsPerRow) { + mostCellsPerRow = rowData.length; } - while (++columnIndex < table.at(rowIndex).length) { - const cell = serialize(table.at(rowIndex)[columnIndex]); + while (++columnIndex < rowData.length) { + const cell = serialize(rowData.at(columnIndex)); if (options.alignDelimiters !== false) { const size = stringLength(cell); sizes[columnIndex] = size; - if (longestCellByColumn.at(columnIndex) === undefined || size > longestCellByColumn.at(columnIndex)) { + if (longestCellByColumn.at(columnIndex) === undefined || size > (longestCellByColumn.at(columnIndex) ?? 0)) { longestCellByColumn[columnIndex] = size; } } @@ -258,14 +260,14 @@ function markdownTable(table: Array>, options: } // There *must* be at least one hyphen-minus in each alignment cell. - let size = options.alignDelimiters === false ? 1 : Math.max(1, longestCellByColumn.at(columnIndex) - before.length - after.length); + let size = options.alignDelimiters === false ? 1 : Math.max(1, (longestCellByColumn.at(columnIndex) ?? 0) - before.length - after.length); const cell = before + '-'.repeat(size) + after; if (options.alignDelimiters !== false) { size = before.length + size + after.length; - if (size > longestCellByColumn.at(columnIndex)) { + if (size > (longestCellByColumn.at(columnIndex) ?? 0)) { longestCellByColumn[columnIndex] = size; } @@ -289,12 +291,12 @@ function markdownTable(table: Array>, options: const line: string[] = []; while (++columnIndex < mostCellsPerRow) { - const cell = matrixRow.at(columnIndex) ?? ''; + const cell = matrixRow?.at(columnIndex) ?? ''; let before = ''; let after = ''; if (options.alignDelimiters !== false) { - const size = longestCellByColumn.at(columnIndex) - (matrixSizes.at(columnIndex) ?? 0); + const size = (longestCellByColumn.at(columnIndex) ?? 0) - (matrixSizes?.at(columnIndex) ?? 0); const code = alignments.at(columnIndex); if (code === 114 /* `r` */) { diff --git a/tests/e2e/measure/math.ts b/tests/e2e/measure/math.ts index 2784383d6a96..1d74f61e64ae 100644 --- a/tests/e2e/measure/math.ts +++ b/tests/e2e/measure/math.ts @@ -1,3 +1,4 @@ +/* eslint-disable rulesdir/prefer-at */ type Entries = number[]; type Stats = { @@ -14,8 +15,8 @@ const filterOutliersViaIQR = (data: Entries): Entries => { const values = data.slice().sort((a, b) => a - b); if ((values.length / 4) % 1 === 0) { - q1 = (1 / 2) * (values.at(values.length / 4) + values.at(values.length / 4 + 1)); - q3 = (1 / 2) * (values.at(values.length * (3 / 4)) + values.at(values.length * (3 / 4) + 1)); + q1 = (1 / 2) * (values[values.length / 4] + values[values.length / 4 + 1]); + q3 = (1 / 2) * (values[values.length * (3 / 4)] + values[values.length * (3 / 4) + 1]); } else { q1 = values[Math.floor(values.length / 4 + 1)]; q3 = values[Math.ceil(values.length * (3 / 4) + 1)]; diff --git a/tests/e2e/testRunner.ts b/tests/e2e/testRunner.ts index 1b5f257f42b8..b40c8ae2daa5 100644 --- a/tests/e2e/testRunner.ts +++ b/tests/e2e/testRunner.ts @@ -228,7 +228,7 @@ const runTests = async (): Promise => { const includes = args[args.indexOf('--includes') + 1]; // assume that "includes" is a regexp - if (!test.name.match(includes)) { + if (!test?.name?.match(includes)) { // eslint-disable-next-line no-continue continue; } @@ -243,7 +243,7 @@ const runTests = async (): Promise => { server.setTestConfig(test as TestConfig); server.setReadyToAcceptTestResults(false); - const warmupText = `Warmup for test '${test.name}' [${testIndex + 1}/${tests.length}]`; + const warmupText = `Warmup for test '${test?.name}' [${testIndex + 1}/${tests.length}]`; // by default we do 2 warmups: // - first warmup to pass a login flow @@ -286,7 +286,7 @@ const runTests = async (): Promise => { mockNetwork: true, }; - const iterationText = `Test '${test.name}' [${testIndex + 1}/${tests.length}], iteration [${testIteration + 1}/${config.RUNS}]`; + const iterationText = `Test '${test?.name}' [${testIndex + 1}/${tests.length}], iteration [${testIteration + 1}/${config.RUNS}]`; const mainIterationText = `[MAIN] ${iterationText}`; const deltaIterationText = `[DELTA] ${iterationText}`; try { diff --git a/tests/ui/UnreadIndicatorsTest.tsx b/tests/ui/UnreadIndicatorsTest.tsx index b5516e0700d8..50ab414e424b 100644 --- a/tests/ui/UnreadIndicatorsTest.tsx +++ b/tests/ui/UnreadIndicatorsTest.tsx @@ -84,8 +84,11 @@ function navigateToSidebar(): Promise { async function navigateToSidebarOption(index: number): Promise { const hintText = Localize.translateLocal('accessibilityHints.navigatesToChat'); - const optionRows = screen.queryAllByAccessibilityHint(hintText); - fireEvent(optionRows.at(index), 'press'); + const optionRow = screen.queryAllByAccessibilityHint(hintText).at(index); + if (!optionRow) { + return; + } + fireEvent(optionRow, 'press'); await waitForBatchedUpdatesWithAct(); } diff --git a/tests/unit/APITest.ts b/tests/unit/APITest.ts index 5b0eea3decdf..313446ec09ee 100644 --- a/tests/unit/APITest.ts +++ b/tests/unit/APITest.ts @@ -1,3 +1,4 @@ +/* eslint-disable rulesdir/prefer-at */ import MockedOnyx from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import type {ApiRequestCommandParameters, ReadCommand, WriteCommand} from '@libs/API/types'; @@ -148,7 +149,7 @@ describe('APITests', () => { }), ); - return promises.slice(-1).at(0); + return promises.slice(-1)[0]; }); // Given we have some requests made while we're offline @@ -168,7 +169,7 @@ describe('APITests', () => { // Then requests should remain persisted until the xhr call is resolved expect(PersistedRequests.getAll().length).toEqual(2); - xhrCalls.at(0).resolve({jsonCode: CONST.JSON_CODE.SUCCESS}); + xhrCalls[0].resolve({jsonCode: CONST.JSON_CODE.SUCCESS}); return waitForBatchedUpdates(); }) .then(waitForBatchedUpdates) @@ -177,7 +178,7 @@ describe('APITests', () => { expect(PersistedRequests.getAll()).toEqual([expect.objectContaining({command: 'mock command', data: expect.objectContaining({param2: 'value2'})})]); // When a request fails it should be retried - xhrCalls.at(1).reject(new Error(CONST.ERROR.FAILED_TO_FETCH)); + xhrCalls[1].reject(new Error(CONST.ERROR.FAILED_TO_FETCH)); return waitForBatchedUpdates(); }) .then(() => { @@ -191,7 +192,7 @@ describe('APITests', () => { }) .then(() => { // Finally, after it succeeds the queue should be empty - xhrCalls.at(2).resolve({jsonCode: CONST.JSON_CODE.SUCCESS}); + xhrCalls[2].resolve({jsonCode: CONST.JSON_CODE.SUCCESS}); return waitForBatchedUpdates(); }) .then(() => { @@ -353,13 +354,13 @@ describe('APITests', () => { // Then expect all 7 calls to have been made and for the Writes to be made in the order that we made them // The read command would have been made first (and would have failed in real-life) expect(xhr.mock.calls.length).toBe(7); - expect(xhr.mock.calls.at(0)[1].content).toBe('not-persisted'); - expect(xhr.mock.calls.at(1)[1].content).toBe('value1'); - expect(xhr.mock.calls.at(2)[1].content).toBe('value2'); - expect(xhr.mock.calls.at(3)[1].content).toBe('value3'); - expect(xhr.mock.calls.at(4)[1].content).toBe('value4'); - expect(xhr.mock.calls.at(5)[1].content).toBe('value5'); - expect(xhr.mock.calls.at(6)[1].content).toBe('value6'); + expect(xhr.mock.calls[0][1].content).toBe('not-persisted'); + expect(xhr.mock.calls[1][1].content).toBe('value1'); + expect(xhr.mock.calls[2][1].content).toBe('value2'); + expect(xhr.mock.calls[3][1].content).toBe('value3'); + expect(xhr.mock.calls[4][1].content).toBe('value4'); + expect(xhr.mock.calls[5][1].content).toBe('value5'); + expect(xhr.mock.calls[6][1].content).toBe('value6'); }); }); @@ -387,18 +388,18 @@ describe('APITests', () => { .then(() => { // Then expect only 8 calls to have been made total and for them to be made in the order that we made them despite requiring reauthentication expect(xhr.mock.calls.length).toBe(8); - expect(xhr.mock.calls.at(0)[1].content).toBe('value1'); + expect(xhr.mock.calls[0][1].content).toBe('value1'); // Our call to Authenticate will not have a "content" field - expect(xhr.mock.calls.at(1)[1].content).not.toBeDefined(); + expect(xhr.mock.calls[1][1].content).not.toBeDefined(); // Rest of the calls have the expected params and are called in sequence - expect(xhr.mock.calls.at(2)[1].content).toBe('value1'); - expect(xhr.mock.calls.at(3)[1].content).toBe('value2'); - expect(xhr.mock.calls.at(4)[1].content).toBe('value3'); - expect(xhr.mock.calls.at(5)[1].content).toBe('value4'); - expect(xhr.mock.calls.at(6)[1].content).toBe('value5'); - expect(xhr.mock.calls.at(7)[1].content).toBe('value6'); + expect(xhr.mock.calls[2][1].content).toBe('value1'); + expect(xhr.mock.calls[3][1].content).toBe('value2'); + expect(xhr.mock.calls[4][1].content).toBe('value3'); + expect(xhr.mock.calls[5][1].content).toBe('value4'); + expect(xhr.mock.calls[6][1].content).toBe('value5'); + expect(xhr.mock.calls[7][1].content).toBe('value6'); }); }); @@ -463,18 +464,18 @@ describe('APITests', () => { expect(NetworkStore.isOffline()).toBe(false); // First call to xhr is the AuthenticatePusher request that could not call Authenticate because we went offline - const [firstCommand] = xhr.mock.calls.at(0); + const [firstCommand] = xhr.mock.calls[0]; expect(firstCommand).toBe('AuthenticatePusher'); // Second call to xhr is the MockCommand that also failed with a 407 - const [secondCommand] = xhr.mock.calls.at(1); + const [secondCommand] = xhr.mock.calls[1]; expect(secondCommand).toBe('MockCommand'); // Third command should be the call to Authenticate - const [thirdCommand] = xhr.mock.calls.at(2); + const [thirdCommand] = xhr.mock.calls[2]; expect(thirdCommand).toBe('Authenticate'); - const [fourthCommand] = xhr.mock.calls.at(3); + const [fourthCommand] = xhr.mock.calls[3]; expect(fourthCommand).toBe('MockCommand'); // We are using the new authToken @@ -553,11 +554,11 @@ describe('APITests', () => { expect(PersistedRequests.getAll().length).toBe(0); // And our Write request should run before our non persistable one in a blocking way - const firstRequest = xhr.mock.calls.at(0); + const firstRequest = xhr.mock.calls[0]; const [firstRequestCommandName] = firstRequest; expect(firstRequestCommandName).toBe('MockCommandOne'); - const secondRequest = xhr.mock.calls.at(1); + const secondRequest = xhr.mock.calls[1]; const [secondRequestCommandName] = secondRequest; expect(secondRequestCommandName).toBe('MockCommandThree'); @@ -568,7 +569,7 @@ describe('APITests', () => { }) .then(() => { // THEN we should see that our third (non-persistable) request has run last - const thirdRequest = xhr.mock.calls.at(2); + const thirdRequest = xhr.mock.calls[2]; const [thirdRequestCommandName] = thirdRequest; expect(thirdRequestCommandName).toBe('MockCommandTwo'); }); diff --git a/tests/unit/CalendarPickerTest.tsx b/tests/unit/CalendarPickerTest.tsx index 079a109f348b..5cf02409ac23 100644 --- a/tests/unit/CalendarPickerTest.tsx +++ b/tests/unit/CalendarPickerTest.tsx @@ -69,7 +69,7 @@ describe('CalendarPicker', () => { fireEvent.press(screen.getByTestId('next-month-arrow')); const nextMonth = addMonths(new Date(), 1).getMonth(); - expect(screen.getByText(monthNames.at(nextMonth))).toBeTruthy(); + expect(screen.getByText(monthNames.at(nextMonth) ?? '')).toBeTruthy(); }); test('clicking previous month arrow updates the displayed month', () => { @@ -78,7 +78,7 @@ describe('CalendarPicker', () => { fireEvent.press(screen.getByTestId('prev-month-arrow')); const prevMonth = subMonths(new Date(), 1).getMonth(); - expect(screen.getByText(monthNames.at(prevMonth))).toBeTruthy(); + expect(screen.getByText(monthNames.at(prevMonth) ?? '')).toBeTruthy(); }); test('clicking a day updates the selected date', () => { diff --git a/tests/unit/GithubUtilsTest.ts b/tests/unit/GithubUtilsTest.ts index 2de95bf992f5..73bd27bf99e9 100644 --- a/tests/unit/GithubUtilsTest.ts +++ b/tests/unit/GithubUtilsTest.ts @@ -455,7 +455,7 @@ describe('GithubUtils', () => { }); test('Test some verified PRs', () => { - githubUtils.generateStagingDeployCashBodyAndAssignees(tag, basePRList, [basePRList.at(0)]).then((issue) => { + githubUtils.generateStagingDeployCashBodyAndAssignees(tag, basePRList, [basePRList.at(0) ?? '']).then((issue) => { if (typeof issue !== 'object') { return; } @@ -517,7 +517,7 @@ describe('GithubUtils', () => { }); test('Test some resolved deploy blockers', () => { - githubUtils.generateStagingDeployCashBodyAndAssignees(tag, basePRList, basePRList, baseDeployBlockerList, [baseDeployBlockerList.at(0)]).then((issue) => { + githubUtils.generateStagingDeployCashBodyAndAssignees(tag, basePRList, basePRList, baseDeployBlockerList, [baseDeployBlockerList.at(0) ?? '']).then((issue) => { if (typeof issue !== 'object') { return; } @@ -589,7 +589,7 @@ describe('GithubUtils', () => { }); test('Test some verified internalQA PRs', () => { - githubUtils.generateStagingDeployCashBodyAndAssignees(tag, [...basePRList, ...internalQAPRList], [], [], [], [internalQAPRList.at(0)]).then((issue) => { + githubUtils.generateStagingDeployCashBodyAndAssignees(tag, [...basePRList, ...internalQAPRList], [], [], [], [internalQAPRList.at(0) ?? '']).then((issue) => { if (typeof issue !== 'object') { return; } diff --git a/tests/unit/MiddlewareTest.ts b/tests/unit/MiddlewareTest.ts index f971aa25c8e8..b4e4cb517a35 100644 --- a/tests/unit/MiddlewareTest.ts +++ b/tests/unit/MiddlewareTest.ts @@ -1,3 +1,4 @@ +/* eslint-disable rulesdir/prefer-at */ import Onyx from 'react-native-onyx'; import HttpUtils from '@src/libs/HttpUtils'; import handleUnusedOptimisticID from '@src/libs/Middleware/HandleUnusedOptimisticID'; @@ -51,12 +52,12 @@ describe('Middleware', () => { expect(global.fetch).toHaveBeenCalledTimes(2); expect(global.fetch).toHaveBeenLastCalledWith('https://www.expensify.com.dev/api/AddComment?', expect.anything()); - TestHelper.assertFormDataMatchesObject(((global.fetch as jest.Mock).mock.calls.at(1) as FormDataObject[])?.at(1).body, { + TestHelper.assertFormDataMatchesObject(((global.fetch as jest.Mock).mock.calls[1] as FormDataObject[])[1].body, { reportID: '1234', reportActionID: '5678', }); expect(global.fetch).toHaveBeenNthCalledWith(1, 'https://www.expensify.com.dev/api/OpenReport?', expect.anything()); - TestHelper.assertFormDataMatchesObject(((global.fetch as jest.Mock).mock.calls.at(0) as FormDataObject[]).at(1).body, { + TestHelper.assertFormDataMatchesObject(((global.fetch as jest.Mock).mock.calls[0] as FormDataObject[])[1].body, { reportID: '1234', }); }); @@ -100,12 +101,12 @@ describe('Middleware', () => { expect(global.fetch).toHaveBeenCalledTimes(2); expect(global.fetch).toHaveBeenLastCalledWith('https://www.expensify.com.dev/api/AddComment?', expect.anything()); - TestHelper.assertFormDataMatchesObject(((global.fetch as jest.Mock).mock.calls.at(1) as FormDataObject[]).at(1).body, { + TestHelper.assertFormDataMatchesObject(((global.fetch as jest.Mock).mock.calls[1] as FormDataObject[])[1].body, { reportID: '5555', reportActionID: '5678', }); expect(global.fetch).toHaveBeenNthCalledWith(1, 'https://www.expensify.com.dev/api/OpenReport?', expect.anything()); - TestHelper.assertFormDataMatchesObject(((global.fetch as jest.Mock).mock.calls.at(0) as FormDataObject[]).at(1).body, {reportID: '1234'}); + TestHelper.assertFormDataMatchesObject(((global.fetch as jest.Mock).mock.calls[0] as FormDataObject[])[1].body, {reportID: '1234'}); }); }); }); diff --git a/tests/unit/NetworkTest.ts b/tests/unit/NetworkTest.ts index e7cb4f0c7ada..aef5204ffc4d 100644 --- a/tests/unit/NetworkTest.ts +++ b/tests/unit/NetworkTest.ts @@ -303,9 +303,9 @@ describe('NetworkTests', () => { return waitForBatchedUpdates(); }) .then(() => { - const response = onResolved.mock.calls.at(0)[0]; + const response = onResolved.mock.calls.at(0)?.at(0); expect(onResolved).toHaveBeenCalled(); - expect(response.jsonCode).toBe(CONST.JSON_CODE.UNABLE_TO_RETRY); + expect(response?.jsonCode).toBe(CONST.JSON_CODE.UNABLE_TO_RETRY); }); }); diff --git a/tests/unit/OptionsListUtilsTest.ts b/tests/unit/OptionsListUtilsTest.ts index f4f2b21912d5..13f3bac129c2 100644 --- a/tests/unit/OptionsListUtilsTest.ts +++ b/tests/unit/OptionsListUtilsTest.ts @@ -427,10 +427,10 @@ describe('OptionsListUtils', () => { expect(results.personalDetails.length).toBe(Object.values(OPTIONS.personalDetails).length - 1 - MAX_RECENT_REPORTS); // We should expect personal details sorted alphabetically - expect(results.personalDetails.at(0).text).toBe('Black Widow'); - expect(results.personalDetails.at(1).text).toBe('Invisible Woman'); - expect(results.personalDetails.at(2).text).toBe('Spider-Man'); - expect(results.personalDetails.at(3).text).toBe('The Incredible Hulk'); + expect(results.personalDetails.at(0)?.text).toBe('Black Widow'); + expect(results.personalDetails.at(1)?.text).toBe('Invisible Woman'); + expect(results.personalDetails.at(2)?.text).toBe('Spider-Man'); + expect(results.personalDetails.at(3)?.text).toBe('The Incredible Hulk'); // Then the result which has an existing report should also have the reportID attached const personalDetailWithExistingReport = results.personalDetails.find((personalDetail) => personalDetail.login === 'peterparker@expensify.com'); @@ -440,10 +440,10 @@ describe('OptionsListUtils', () => { results = OptionsListUtils.getFilteredOptions([], OPTIONS.personalDetails, [], ''); // We should expect personal details sorted alphabetically - expect(results.personalDetails.at(0).text).toBe('Black Panther'); - expect(results.personalDetails.at(1).text).toBe('Black Widow'); - expect(results.personalDetails.at(2).text).toBe('Captain America'); - expect(results.personalDetails.at(3).text).toBe('Invisible Woman'); + expect(results.personalDetails.at(0)?.text).toBe('Black Panther'); + expect(results.personalDetails.at(1)?.text).toBe('Black Widow'); + expect(results.personalDetails.at(2)?.text).toBe('Captain America'); + expect(results.personalDetails.at(3)?.text).toBe('Invisible Woman'); // When we don't include personal detail to the result results = OptionsListUtils.getFilteredOptions( @@ -519,10 +519,10 @@ describe('OptionsListUtils', () => { expect(results.personalDetails.length).toBe(Object.values(OPTIONS.personalDetails).length - 6); // We should expect personal details sorted alphabetically - expect(results.personalDetails.at(0).text).toBe('Black Widow'); - expect(results.personalDetails.at(1).text).toBe('Invisible Woman'); - expect(results.personalDetails.at(2).text).toBe('Spider-Man'); - expect(results.personalDetails.at(3).text).toBe('The Incredible Hulk'); + expect(results.personalDetails.at(0)?.text).toBe('Black Widow'); + expect(results.personalDetails.at(1)?.text).toBe('Invisible Woman'); + expect(results.personalDetails.at(2)?.text).toBe('Spider-Man'); + expect(results.personalDetails.at(3)?.text).toBe('The Incredible Hulk'); // And none of our personalDetails should include any of the users with recent reports const reportLogins = results.recentReports.map((reportOption) => reportOption.login); @@ -618,10 +618,10 @@ describe('OptionsListUtils', () => { const results = OptionsListUtils.getMemberInviteOptions(OPTIONS.personalDetails, [], ''); // We should expect personal details to be sorted alphabetically - expect(results.personalDetails.at(0).text).toBe('Black Panther'); - expect(results.personalDetails.at(1).text).toBe('Black Widow'); - expect(results.personalDetails.at(2).text).toBe('Captain America'); - expect(results.personalDetails.at(3).text).toBe('Invisible Woman'); + expect(results.personalDetails.at(0)?.text).toBe('Black Panther'); + expect(results.personalDetails.at(1)?.text).toBe('Black Widow'); + expect(results.personalDetails.at(2)?.text).toBe('Captain America'); + expect(results.personalDetails.at(3)?.text).toBe('Invisible Woman'); }); it('getFilteredOptions() for categories', () => { @@ -2591,12 +2591,12 @@ describe('OptionsListUtils', () => { const formattedMembers = Object.values(PERSONAL_DETAILS).map((personalDetail) => OptionsListUtils.formatMemberForList(personalDetail)); // We're only formatting items inside the array, so the order should be the same as the original PERSONAL_DETAILS array - expect(formattedMembers.at(0).text).toBe('Mister Fantastic'); - expect(formattedMembers.at(1).text).toBe('Iron Man'); - expect(formattedMembers.at(2).text).toBe('Spider-Man'); + expect(formattedMembers.at(0)?.text).toBe('Mister Fantastic'); + expect(formattedMembers.at(1)?.text).toBe('Iron Man'); + expect(formattedMembers.at(2)?.text).toBe('Spider-Man'); // We should expect only the first item to be selected - expect(formattedMembers.at(0).isSelected).toBe(true); + expect(formattedMembers.at(0)?.isSelected).toBe(true); // And all the others to be unselected expect(formattedMembers.slice(1).every((personalDetail) => !personalDetail.isSelected)).toBe(true); @@ -2619,11 +2619,11 @@ describe('OptionsListUtils', () => { const filteredOptions = OptionsListUtils.filterOptions(options, searchText, {sortByReportTypeInSearch: true}); expect(filteredOptions.recentReports.length).toBe(5); - expect(filteredOptions.recentReports.at(0).text).toBe('Invisible Woman'); - expect(filteredOptions.recentReports.at(1).text).toBe('Spider-Man'); - expect(filteredOptions.recentReports.at(2).text).toBe('Black Widow'); - expect(filteredOptions.recentReports.at(3).text).toBe('Mister Fantastic, Invisible Woman'); - expect(filteredOptions.recentReports.at(4).text).toBe("SHIELD's workspace (archived)"); + expect(filteredOptions.recentReports.at(0)?.text).toBe('Invisible Woman'); + expect(filteredOptions.recentReports.at(1)?.text).toBe('Spider-Man'); + expect(filteredOptions.recentReports.at(2)?.text).toBe('Black Widow'); + expect(filteredOptions.recentReports.at(3)?.text).toBe('Mister Fantastic, Invisible Woman'); + expect(filteredOptions.recentReports.at(4)?.text).toBe("SHIELD's workspace (archived)"); }); it('should filter users by email', () => { @@ -2633,7 +2633,7 @@ describe('OptionsListUtils', () => { const filteredOptions = OptionsListUtils.filterOptions(options, searchText); expect(filteredOptions.recentReports.length).toBe(1); - expect(filteredOptions.recentReports.at(0).text).toBe('Mr Sinister'); + expect(filteredOptions.recentReports.at(0)?.text).toBe('Mr Sinister'); }); it('should find archived chats', () => { @@ -2642,7 +2642,7 @@ describe('OptionsListUtils', () => { const filteredOptions = OptionsListUtils.filterOptions(options, searchText); expect(filteredOptions.recentReports.length).toBe(1); - expect(filteredOptions.recentReports.at(0).isArchivedRoom).toBe(true); + expect(filteredOptions.recentReports.at(0)?.isArchivedRoom).toBe(true); }); it('should filter options by email if dot is skipped in the email', () => { @@ -2653,7 +2653,7 @@ describe('OptionsListUtils', () => { const filteredOptions = OptionsListUtils.filterOptions(options, searchText, {sortByReportTypeInSearch: true}); expect(filteredOptions.recentReports.length).toBe(1); - expect(filteredOptions.recentReports.at(0).login).toBe('barry.allen@expensify.com'); + expect(filteredOptions.recentReports.at(0)?.login).toBe('barry.allen@expensify.com'); }); it('should include workspace rooms in the search results', () => { @@ -2663,7 +2663,7 @@ describe('OptionsListUtils', () => { const filteredOptions = OptionsListUtils.filterOptions(options, searchText); expect(filteredOptions.recentReports.length).toBe(1); - expect(filteredOptions.recentReports.at(0).subtitle).toBe('Avengers Room'); + expect(filteredOptions.recentReports.at(0)?.subtitle).toBe('Avengers Room'); }); it('should put exact match by login on the top of the list', () => { @@ -2673,7 +2673,7 @@ describe('OptionsListUtils', () => { const filteredOptions = OptionsListUtils.filterOptions(options, searchText); expect(filteredOptions.recentReports.length).toBe(2); - expect(filteredOptions.recentReports.at(0).login).toBe(searchText); + expect(filteredOptions.recentReports.at(0)?.login).toBe(searchText); }); it('should prioritize options with matching display name over chatrooms', () => { @@ -2684,7 +2684,7 @@ describe('OptionsListUtils', () => { const filterOptions = OptionsListUtils.filterOptions(options, searchText); expect(filterOptions.recentReports.length).toBe(2); - expect(filterOptions.recentReports.at(1).isChatRoom).toBe(true); + expect(filterOptions.recentReports.at(1)?.isChatRoom).toBe(true); }); it('should put the item with latest lastVisibleActionCreated on top when search value match multiple items', () => { @@ -2694,8 +2694,8 @@ describe('OptionsListUtils', () => { const filteredOptions = OptionsListUtils.filterOptions(options, searchText); expect(filteredOptions.recentReports.length).toBe(2); - expect(filteredOptions.recentReports.at(0).text).toBe('Mister Fantastic'); - expect(filteredOptions.recentReports.at(1).text).toBe('Mister Fantastic, Invisible Woman'); + expect(filteredOptions.recentReports.at(0)?.text).toBe('Mister Fantastic'); + expect(filteredOptions.recentReports.at(1)?.text).toBe('Mister Fantastic, Invisible Woman'); }); it('should return the user to invite when the search value is a valid, non-existent email', () => { @@ -2753,7 +2753,7 @@ describe('OptionsListUtils', () => { const filteredOptions = OptionsListUtils.filterOptions(options, 'peterparker@expensify.com'); expect(filteredOptions.personalDetails.length).toBe(1); - expect(filteredOptions.personalDetails.at(0).text).toBe('Spider-Man'); + expect(filteredOptions.personalDetails.at(0)?.text).toBe('Spider-Man'); }); it('should not show any recent reports if a search value does not match the group chat name (getShareDestinationsOptions)', () => { @@ -2810,7 +2810,7 @@ describe('OptionsListUtils', () => { expect(filteredOptions.recentReports.length).toBe(0); expect(filteredOptions.personalDetails.length).toBe(1); - expect(filteredOptions.personalDetails.at(0).login).toBe('brucebanner@expensify.com'); + expect(filteredOptions.personalDetails.at(0)?.login).toBe('brucebanner@expensify.com'); }); it('should return all matching reports and personal details (getFilteredOptions)', () => { @@ -2818,10 +2818,10 @@ describe('OptionsListUtils', () => { const filteredOptions = OptionsListUtils.filterOptions(options, '.com'); expect(filteredOptions.recentReports.length).toBe(5); - expect(filteredOptions.recentReports.at(0).text).toBe('Captain America'); + expect(filteredOptions.recentReports.at(0)?.text).toBe('Captain America'); expect(filteredOptions.personalDetails.length).toBe(4); - expect(filteredOptions.personalDetails.at(0).login).toBe('natasharomanoff@expensify.com'); + expect(filteredOptions.personalDetails.at(0)?.login).toBe('natasharomanoff@expensify.com'); }); it('should not return any options or user to invite if there are no search results and the string does not match a potential email or phone (getFilteredOptions)', () => { @@ -2900,7 +2900,7 @@ describe('OptionsListUtils', () => { const options = OptionsListUtils.getFilteredOptions(OPTIONS.reports, OPTIONS.personalDetails, [], ''); const filteredOptions = OptionsListUtils.filterOptions(options, 'peterparker@expensify.com', {sortByReportTypeInSearch: true}); expect(filteredOptions.recentReports.length).toBe(1); - expect(filteredOptions.recentReports.at(0).text).toBe('Spider-Man'); + expect(filteredOptions.recentReports.at(0)?.text).toBe('Spider-Man'); expect(filteredOptions.personalDetails.length).toBe(0); }); @@ -2910,10 +2910,10 @@ describe('OptionsListUtils', () => { expect(filteredOptions.personalDetails.length).toBe(4); expect(filteredOptions.recentReports.length).toBe(5); - expect(filteredOptions.personalDetails.at(0).login).toBe('natasharomanoff@expensify.com'); - expect(filteredOptions.recentReports.at(0).text).toBe('Captain America'); - expect(filteredOptions.recentReports.at(1).text).toBe('Mr Sinister'); - expect(filteredOptions.recentReports.at(2).text).toBe('Black Panther'); + expect(filteredOptions.personalDetails.at(0)?.login).toBe('natasharomanoff@expensify.com'); + expect(filteredOptions.recentReports.at(0)?.text).toBe('Captain America'); + expect(filteredOptions.recentReports.at(1)?.text).toBe('Mr Sinister'); + expect(filteredOptions.recentReports.at(2)?.text).toBe('Black Panther'); }); it('should return matching option when searching (getSearchOptions)', () => { @@ -2921,7 +2921,7 @@ describe('OptionsListUtils', () => { const filteredOptions = OptionsListUtils.filterOptions(options, 'spider'); expect(filteredOptions.recentReports.length).toBe(1); - expect(filteredOptions.recentReports.at(0).text).toBe('Spider-Man'); + expect(filteredOptions.recentReports.at(0)?.text).toBe('Spider-Man'); }); it('should return latest lastVisibleActionCreated item on top when search value matches multiple items (getSearchOptions)', () => { @@ -2929,8 +2929,8 @@ describe('OptionsListUtils', () => { const filteredOptions = OptionsListUtils.filterOptions(options, 'fantastic'); expect(filteredOptions.recentReports.length).toBe(2); - expect(filteredOptions.recentReports.at(0).text).toBe('Mister Fantastic'); - expect(filteredOptions.recentReports.at(1).text).toBe('Mister Fantastic, Invisible Woman'); + expect(filteredOptions.recentReports.at(0)?.text).toBe('Mister Fantastic'); + expect(filteredOptions.recentReports.at(1)?.text).toBe('Mister Fantastic, Invisible Woman'); return waitForBatchedUpdates() .then(() => Onyx.set(ONYXKEYS.PERSONAL_DETAILS_LIST, PERSONAL_DETAILS_WITH_PERIODS)) @@ -2940,7 +2940,7 @@ describe('OptionsListUtils', () => { const filteredResults = OptionsListUtils.filterOptions(results, 'barry.allen@expensify.com', {sortByReportTypeInSearch: true}); expect(filteredResults.recentReports.length).toBe(1); - expect(filteredResults.recentReports.at(0).text).toBe('The Flash'); + expect(filteredResults.recentReports.at(0)?.text).toBe('The Flash'); }); }); }); diff --git a/tests/unit/ReportUtilsTest.ts b/tests/unit/ReportUtilsTest.ts index 9545a7946fbd..59a0e5e86b9d 100644 --- a/tests/unit/ReportUtilsTest.ts +++ b/tests/unit/ReportUtilsTest.ts @@ -80,15 +80,15 @@ describe('ReportUtils', () => { const participants = ReportUtils.getIconsForParticipants([1, 2, 3, 4, 5], participantsPersonalDetails); expect(participants).toHaveLength(5); - expect(participants.at(0).source).toBeInstanceOf(Function); - expect(participants.at(0).name).toBe('(833) 240-3627'); - expect(participants.at(0).id).toBe(4); - expect(participants.at(0).type).toBe('avatar'); + expect(participants.at(0)?.source).toBeInstanceOf(Function); + expect(participants.at(0)?.name).toBe('(833) 240-3627'); + expect(participants.at(0)?.id).toBe(4); + expect(participants.at(0)?.type).toBe('avatar'); - expect(participants.at(1).source).toBeInstanceOf(Function); - expect(participants.at(1).name).toBe('floki@vikings.net'); - expect(participants.at(1).id).toBe(2); - expect(participants.at(1).type).toBe('avatar'); + expect(participants.at(1)?.source).toBeInstanceOf(Function); + expect(participants.at(1)?.name).toBe('floki@vikings.net'); + expect(participants.at(1)?.id).toBe(2); + expect(participants.at(1)?.type).toBe('avatar'); }); }); @@ -97,18 +97,18 @@ describe('ReportUtils', () => { const participants = ReportUtils.getDisplayNamesWithTooltips(participantsPersonalDetails, false); expect(participants).toHaveLength(5); - expect(participants.at(0).displayName).toBe('(833) 240-3627'); - expect(participants.at(0).login).toBe('+18332403627@expensify.sms'); + expect(participants.at(0)?.displayName).toBe('(833) 240-3627'); + expect(participants.at(0)?.login).toBe('+18332403627@expensify.sms'); - expect(participants.at(2).displayName).toBe('Lagertha Lothbrok'); - expect(participants.at(2).login).toBe('lagertha@vikings.net'); - expect(participants.at(2).accountID).toBe(3); - expect(participants.at(2).pronouns).toBe('She/her'); + expect(participants.at(2)?.displayName).toBe('Lagertha Lothbrok'); + expect(participants.at(2)?.login).toBe('lagertha@vikings.net'); + expect(participants.at(2)?.accountID).toBe(3); + expect(participants.at(2)?.pronouns).toBe('She/her'); - expect(participants.at(4).displayName).toBe('Ragnar Lothbrok'); - expect(participants.at(4).login).toBe('ragnar@vikings.net'); - expect(participants.at(4).accountID).toBe(1); - expect(participants.at(4).pronouns).toBeUndefined(); + expect(participants.at(4)?.displayName).toBe('Ragnar Lothbrok'); + expect(participants.at(4)?.login).toBe('ragnar@vikings.net'); + expect(participants.at(4)?.accountID).toBe(1); + expect(participants.at(4)?.pronouns).toBeUndefined(); }); }); @@ -503,7 +503,7 @@ describe('ReportUtils', () => { parentReportID: '101', policyID: paidPolicy.id, }; - const moneyRequestOptions = ReportUtils.temporary_getMoneyRequestOptions(report, paidPolicy, [currentUserAccountID, participantsAccountIDs.at(0)]); + const moneyRequestOptions = ReportUtils.temporary_getMoneyRequestOptions(report, paidPolicy, [currentUserAccountID, participantsAccountIDs.at(0) ?? -1]); expect(moneyRequestOptions.length).toBe(0); }); }); @@ -516,7 +516,7 @@ describe('ReportUtils', () => { ...LHNTestUtils.getFakeReport(), chatType, }; - const moneyRequestOptions = ReportUtils.temporary_getMoneyRequestOptions(report, undefined, [currentUserAccountID, participantsAccountIDs.at(0)]); + const moneyRequestOptions = ReportUtils.temporary_getMoneyRequestOptions(report, undefined, [currentUserAccountID, participantsAccountIDs.at(0) ?? -1]); return moneyRequestOptions.length === 1 && moneyRequestOptions.includes(CONST.IOU.TYPE.SPLIT); }); expect(onlyHaveSplitOption).toBe(true); @@ -562,7 +562,7 @@ describe('ReportUtils', () => { stateNum: CONST.REPORT.STATE_NUM.SUBMITTED, statusNum: CONST.REPORT.STATUS_NUM.SUBMITTED, }; - const moneyRequestOptions = ReportUtils.temporary_getMoneyRequestOptions(report, undefined, [currentUserAccountID, participantsAccountIDs.at(0)]); + const moneyRequestOptions = ReportUtils.temporary_getMoneyRequestOptions(report, undefined, [currentUserAccountID, participantsAccountIDs.at(0) ?? -1]); expect(moneyRequestOptions.length).toBe(1); expect(moneyRequestOptions.includes(CONST.IOU.TYPE.SUBMIT)).toBe(true); }); @@ -574,7 +574,7 @@ describe('ReportUtils', () => { stateNum: CONST.REPORT.STATE_NUM.SUBMITTED, statusNum: CONST.REPORT.STATUS_NUM.SUBMITTED, }; - const moneyRequestOptions = ReportUtils.temporary_getMoneyRequestOptions(report, undefined, [currentUserAccountID, participantsAccountIDs.at(0)]); + const moneyRequestOptions = ReportUtils.temporary_getMoneyRequestOptions(report, undefined, [currentUserAccountID, participantsAccountIDs.at(0) ?? -1]); expect(moneyRequestOptions.length).toBe(1); expect(moneyRequestOptions.includes(CONST.IOU.TYPE.SUBMIT)).toBe(true); }); @@ -621,7 +621,7 @@ describe('ReportUtils', () => { outputCurrency: '', isPolicyExpenseChatEnabled: false, } as const; - const moneyRequestOptions = ReportUtils.temporary_getMoneyRequestOptions(report, paidPolicy, [currentUserAccountID, participantsAccountIDs.at(0)]); + const moneyRequestOptions = ReportUtils.temporary_getMoneyRequestOptions(report, paidPolicy, [currentUserAccountID, participantsAccountIDs.at(0) ?? -1]); expect(moneyRequestOptions.length).toBe(2); expect(moneyRequestOptions.includes(CONST.IOU.TYPE.SUBMIT)).toBe(true); expect(moneyRequestOptions.includes(CONST.IOU.TYPE.TRACK)).toBe(true); @@ -635,7 +635,7 @@ describe('ReportUtils', () => { stateNum: CONST.REPORT.STATE_NUM.SUBMITTED, statusNum: CONST.REPORT.STATUS_NUM.SUBMITTED, }; - const moneyRequestOptions = ReportUtils.temporary_getMoneyRequestOptions(report, undefined, [currentUserAccountID, participantsAccountIDs.at(0)]); + const moneyRequestOptions = ReportUtils.temporary_getMoneyRequestOptions(report, undefined, [currentUserAccountID, participantsAccountIDs.at(0) ?? -1]); expect(moneyRequestOptions.length).toBe(1); expect(moneyRequestOptions.includes(CONST.IOU.TYPE.SUBMIT)).toBe(true); }); @@ -647,7 +647,7 @@ describe('ReportUtils', () => { stateNum: CONST.REPORT.STATE_NUM.SUBMITTED, statusNum: CONST.REPORT.STATUS_NUM.SUBMITTED, }; - const moneyRequestOptions = ReportUtils.temporary_getMoneyRequestOptions(report, undefined, [currentUserAccountID, participantsAccountIDs.at(0)]); + const moneyRequestOptions = ReportUtils.temporary_getMoneyRequestOptions(report, undefined, [currentUserAccountID, participantsAccountIDs.at(0) ?? -1]); expect(moneyRequestOptions.length).toBe(1); expect(moneyRequestOptions.includes(CONST.IOU.TYPE.SUBMIT)).toBe(true); }); @@ -680,7 +680,7 @@ describe('ReportUtils', () => { parentReportID: '101', policyID: paidPolicy.id, }; - const moneyRequestOptions = ReportUtils.temporary_getMoneyRequestOptions(report, paidPolicy, [currentUserAccountID, participantsAccountIDs.at(0)]); + const moneyRequestOptions = ReportUtils.temporary_getMoneyRequestOptions(report, paidPolicy, [currentUserAccountID, participantsAccountIDs.at(0) ?? -1]); expect(moneyRequestOptions.length).toBe(2); expect(moneyRequestOptions.includes(CONST.IOU.TYPE.SUBMIT)).toBe(true); expect(moneyRequestOptions.includes(CONST.IOU.TYPE.TRACK)).toBe(true); @@ -694,7 +694,7 @@ describe('ReportUtils', () => { ...LHNTestUtils.getFakeReport(), type: CONST.REPORT.TYPE.CHAT, }; - const moneyRequestOptions = ReportUtils.temporary_getMoneyRequestOptions(report, undefined, [currentUserAccountID, participantsAccountIDs.at(0)]); + const moneyRequestOptions = ReportUtils.temporary_getMoneyRequestOptions(report, undefined, [currentUserAccountID, participantsAccountIDs.at(0) ?? -1]); expect(moneyRequestOptions.length).toBe(3); expect(moneyRequestOptions.includes(CONST.IOU.TYPE.SPLIT)).toBe(true); expect(moneyRequestOptions.includes(CONST.IOU.TYPE.SUBMIT)).toBe(true); @@ -843,7 +843,7 @@ describe('ReportUtils', () => { const reportActionCollectionDataSet = toCollectionDataSet( ONYXKEYS.COLLECTION.REPORT_ACTIONS, reportActions.map((reportAction) => ({[reportAction.reportActionID]: reportAction})), - (actions) => Object.values(actions).at(0).reportActionID, + (actions) => Object.values(actions).at(0)?.reportActionID ?? '', ); Onyx.multiSet({ ...reportCollectionDataSet, diff --git a/tests/unit/RequestTest.ts b/tests/unit/RequestTest.ts index 0773cabb4fdb..39a408c087f8 100644 --- a/tests/unit/RequestTest.ts +++ b/tests/unit/RequestTest.ts @@ -23,7 +23,11 @@ test('Request.use() can register a middleware and it will run', () => { Request.processWithMiddleware(request, true); return waitForBatchedUpdates().then(() => { - const [promise, returnedRequest, isFromSequentialQueue] = testMiddleware.mock.calls.at(0); + const call = testMiddleware.mock.calls.at(0); + if (!call) { + return; + } + const [promise, returnedRequest, isFromSequentialQueue] = call; expect(testMiddleware).toHaveBeenCalled(); expect(returnedRequest).toEqual(request); expect(isFromSequentialQueue).toBe(true); diff --git a/tests/unit/WorkflowUtilsTest.ts b/tests/unit/WorkflowUtilsTest.ts index ec2e4ad03ac8..f7a82a0b7b30 100644 --- a/tests/unit/WorkflowUtilsTest.ts +++ b/tests/unit/WorkflowUtilsTest.ts @@ -1,4 +1,6 @@ /* eslint-disable @typescript-eslint/naming-convention */ + +/* eslint-disable rulesdir/prefer-at */ import * as WorkflowUtils from '@src/libs/WorkflowUtils'; import type {Approver, Member} from '@src/types/onyx/ApprovalWorkflow'; import type ApprovalWorkflow from '@src/types/onyx/ApprovalWorkflow'; @@ -319,15 +321,16 @@ describe('WorkflowUtils', () => { const workflows = WorkflowUtils.convertPolicyEmployeesToApprovalWorkflows({employees, defaultApprover, personalDetails}); const defaultWorkflow = buildWorkflow([2, 3, 4], [1, 3, 4], {isDefault: true}); - defaultWorkflow.approvers.at(0).forwardsTo = '3@example.com'; - defaultWorkflow.approvers.at(1).forwardsTo = '4@example.com'; - defaultWorkflow.approvers.at(1).isInMultipleWorkflows = true; - defaultWorkflow.approvers.at(2).isInMultipleWorkflows = true; + defaultWorkflow.approvers[0].forwardsTo = '3@example.com'; + defaultWorkflow.approvers[1].forwardsTo = '4@example.com'; + defaultWorkflow.approvers[1].forwardsTo = '4@example.com'; + defaultWorkflow.approvers[1].isInMultipleWorkflows = true; + defaultWorkflow.approvers[2].isInMultipleWorkflows = true; const secondWorkflow = buildWorkflow([1], [2, 3, 4]); - secondWorkflow.approvers.at(0).forwardsTo = '3@example.com'; - secondWorkflow.approvers.at(1).forwardsTo = '4@example.com'; - secondWorkflow.approvers.at(1).isInMultipleWorkflows = true; - secondWorkflow.approvers.at(2).isInMultipleWorkflows = true; + secondWorkflow.approvers[0].forwardsTo = '3@example.com'; + secondWorkflow.approvers[1].forwardsTo = '4@example.com'; + secondWorkflow.approvers[1].isInMultipleWorkflows = true; + secondWorkflow.approvers[2].isInMultipleWorkflows = true; expect(workflows).toEqual([defaultWorkflow, secondWorkflow]); }); @@ -371,8 +374,8 @@ describe('WorkflowUtils', () => { const defaultWorkflow = buildWorkflow([1, 4, 5, 6], [1], {isDefault: true}); const secondWorkflow = buildWorkflow([2, 3], [4, 5, 6]); - secondWorkflow.approvers.at(0).forwardsTo = '5@example.com'; - secondWorkflow.approvers.at(1).forwardsTo = '6@example.com'; + secondWorkflow.approvers[0].forwardsTo = '5@example.com'; + secondWorkflow.approvers[1].forwardsTo = '6@example.com'; expect(workflows).toEqual([defaultWorkflow, secondWorkflow]); }); }); diff --git a/tests/unit/generateMonthMatrixTest.ts b/tests/unit/generateMonthMatrixTest.ts index 9f6dd9bc6c09..d652efd06566 100644 --- a/tests/unit/generateMonthMatrixTest.ts +++ b/tests/unit/generateMonthMatrixTest.ts @@ -129,7 +129,7 @@ describe('generateMonthMatrix', () => { it('returns a matrix with 6 rows and 7 columns for January 2022', () => { const matrix = generateMonthMatrix(2022, 0); - expect(matrix.length).toBe(6); - expect(matrix.at(0).length).toBe(7); + expect(matrix?.length).toBe(6); + expect(matrix?.at(0)?.length).toBe(7); }); }); diff --git a/tests/unit/versionUpdaterTest.ts b/tests/unit/versionUpdaterTest.ts index d04dbcdb0a44..e59288710b85 100644 --- a/tests/unit/versionUpdaterTest.ts +++ b/tests/unit/versionUpdaterTest.ts @@ -11,7 +11,7 @@ describe('versionUpdater', () => { it('should return build as zero if not present in string', () => { const versionWithZeroBuild = [VERSION_NUMBER[0], VERSION_NUMBER[1], VERSION_NUMBER[2], 0]; - expect(versionUpdater.getVersionNumberFromString(VERSION.split('-').at(0))).toStrictEqual(versionWithZeroBuild); + expect(versionUpdater.getVersionNumberFromString(VERSION.split('-').at(0) ?? '')).toStrictEqual(versionWithZeroBuild); }); }); @@ -61,7 +61,7 @@ describe('versionUpdater', () => { }); it('should add BUILD number if there is no BUILD number', () => { - expect(versionUpdater.incrementVersion(VERSION.split('-').at(0), versionUpdater.SEMANTIC_VERSION_LEVELS.BUILD)).toStrictEqual('2.3.9-1'); + expect(versionUpdater.incrementVersion(VERSION.split('-').at(0) ?? '', versionUpdater.SEMANTIC_VERSION_LEVELS.BUILD)).toStrictEqual('2.3.9-1'); }); it('should increment patch if MINOR is above max level', () => { diff --git a/tests/utils/ReportTestUtils.ts b/tests/utils/ReportTestUtils.ts index e14ad738c29d..167e04d85015 100644 --- a/tests/utils/ReportTestUtils.ts +++ b/tests/utils/ReportTestUtils.ts @@ -55,7 +55,7 @@ const getMockedSortedReportActions = (length = 100): ReportAction[] => const getMockedReportActionsMap = (length = 100): ReportActions => { const mockReports: ReportActions[] = Array.from({length}, (element, index): ReportActions => { const reportID = index + 1; - const actionName: ReportActionName = index === 0 ? 'CREATED' : actionNames.at(index % actionNames.length); + const actionName: ReportActionName = index === 0 ? 'CREATED' : actionNames.at(index % actionNames.length) ?? 'CREATED'; const reportAction = { ...createRandomReportAction(reportID), actionName,