diff --git a/.eslintrc.js b/.eslintrc.js index cb1219533278..661a6aa286b9 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -289,5 +289,11 @@ module.exports = { 'rulesdir/use-periods-for-error-messages': 'error', }, }, + { + files: ['*.ts', '*.tsx'], + rules: { + 'rulesdir/prefer-at': 'error', + }, + }, ], }; diff --git a/.github/actions/javascript/authorChecklist/authorChecklist.ts b/.github/actions/javascript/authorChecklist/authorChecklist.ts index f855c135cd39..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)[1]; + const checklist = partitionWithChecklist(fileContents).at(1) ?? ''; const numberOfChecklistItems = (checklist.match(/\[ \]/g) ?? []).length; return numberOfChecklistItems; } diff --git a/.github/actions/javascript/authorChecklist/index.js b/.github/actions/javascript/authorChecklist/index.js index e09b95d572ff..d662b92764f9 100644 --- a/.github/actions/javascript/authorChecklist/index.js +++ b/.github/actions/javascript/authorChecklist/index.js @@ -16771,7 +16771,7 @@ function partitionWithChecklist(body) { async function getNumberOfItemsFromAuthorChecklist() { const response = await fetch(pathToAuthorChecklist); const fileContents = await response.text(); - const checklist = partitionWithChecklist(fileContents)[1]; + const checklist = partitionWithChecklist(fileContents).at(1) ?? ''; const numberOfChecklistItems = (checklist.match(/\[ \]/g) ?? []).length; return numberOfChecklistItems; } @@ -17180,7 +17180,11 @@ class GithubUtils { if (data.length > 1) { throw new Error(`Found more than one ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); } - return this.getStagingDeployCashData(data[0]); + const issue = data.at(0); + if (!issue) { + throw new Error(`Found an undefined ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); + } + return this.getStagingDeployCashData(issue); }); } /** @@ -17258,7 +17262,7 @@ class GithubUtils { } internalQASection = internalQASection[1]; const internalQAPRs = [...internalQASection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2].split('-')[0].trim(), + url: match[2].split('-').at(0)?.trim() ?? '', number: Number.parseInt(match[3], 10), isResolved: match[1] === 'x', })); @@ -17342,7 +17346,7 @@ class GithubUtils { * Fetch all pull requests given a list of PR numbers. */ static fetchAllPullRequests(pullRequestNumbers) { - const oldestPR = pullRequestNumbers.sort((a, b) => a - b)[0]; + const oldestPR = pullRequestNumbers.sort((a, b) => a - b).at(0); return this.paginate(this.octokit.pulls.list, { owner: CONST_1.default.GITHUB_OWNER, repo: CONST_1.default.APP_REPO, @@ -17416,7 +17420,7 @@ class GithubUtils { repo: CONST_1.default.APP_REPO, workflow_id: workflow, }) - .then((response) => response.data.workflow_runs[0]?.id); + .then((response) => response.data.workflow_runs.at(0)?.id ?? -1); } /** * Generate the URL of an New Expensify pull request given the PR number. @@ -17484,7 +17488,7 @@ class GithubUtils { per_page: 1, name: artifactName, }) - .then((response) => response.data.artifacts[0]); + .then((response) => response.data.artifacts.at(0)); } /** * Given an artifact ID, returns the download URL to a zip file containing the artifact. diff --git a/.github/actions/javascript/awaitStagingDeploys/index.js b/.github/actions/javascript/awaitStagingDeploys/index.js index 561cc980a4e5..cfc7e8b4cc4a 100644 --- a/.github/actions/javascript/awaitStagingDeploys/index.js +++ b/.github/actions/javascript/awaitStagingDeploys/index.js @@ -12421,7 +12421,11 @@ class GithubUtils { if (data.length > 1) { throw new Error(`Found more than one ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); } - return this.getStagingDeployCashData(data[0]); + const issue = data.at(0); + if (!issue) { + throw new Error(`Found an undefined ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); + } + return this.getStagingDeployCashData(issue); }); } /** @@ -12499,7 +12503,7 @@ class GithubUtils { } internalQASection = internalQASection[1]; const internalQAPRs = [...internalQASection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2].split('-')[0].trim(), + url: match[2].split('-').at(0)?.trim() ?? '', number: Number.parseInt(match[3], 10), isResolved: match[1] === 'x', })); @@ -12583,7 +12587,7 @@ class GithubUtils { * Fetch all pull requests given a list of PR numbers. */ static fetchAllPullRequests(pullRequestNumbers) { - const oldestPR = pullRequestNumbers.sort((a, b) => a - b)[0]; + const oldestPR = pullRequestNumbers.sort((a, b) => a - b).at(0); return this.paginate(this.octokit.pulls.list, { owner: CONST_1.default.GITHUB_OWNER, repo: CONST_1.default.APP_REPO, @@ -12657,7 +12661,7 @@ class GithubUtils { repo: CONST_1.default.APP_REPO, workflow_id: workflow, }) - .then((response) => response.data.workflow_runs[0]?.id); + .then((response) => response.data.workflow_runs.at(0)?.id ?? -1); } /** * Generate the URL of an New Expensify pull request given the PR number. @@ -12725,7 +12729,7 @@ class GithubUtils { per_page: 1, name: artifactName, }) - .then((response) => response.data.artifacts[0]); + .then((response) => response.data.artifacts.at(0)); } /** * Given an artifact ID, returns the download URL to a zip file containing the artifact. diff --git a/.github/actions/javascript/bumpVersion/index.js b/.github/actions/javascript/bumpVersion/index.js index 43bd09558c26..c96bc1bcf884 100644 --- a/.github/actions/javascript/bumpVersion/index.js +++ b/.github/actions/javascript/bumpVersion/index.js @@ -3536,7 +3536,7 @@ exports.updateAndroidVersion = updateAndroidVersion; * Updates the CFBundleShortVersionString and the CFBundleVersion. */ function updateiOSVersion(version) { - const shortVersion = version.split('-')[0]; + const shortVersion = version.split('-').at(0); const cfVersion = version.includes('-') ? version.replace('-', '.') : `${version}.0`; console.log('Updating iOS', `CFBundleShortVersionString: ${shortVersion}`, `CFBundleVersion: ${cfVersion}`); // Update Plists diff --git a/.github/actions/javascript/checkDeployBlockers/index.js b/.github/actions/javascript/checkDeployBlockers/index.js index 74cd1509fbfa..1e9626511e5e 100644 --- a/.github/actions/javascript/checkDeployBlockers/index.js +++ b/.github/actions/javascript/checkDeployBlockers/index.js @@ -11704,7 +11704,11 @@ class GithubUtils { if (data.length > 1) { throw new Error(`Found more than one ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); } - return this.getStagingDeployCashData(data[0]); + const issue = data.at(0); + if (!issue) { + throw new Error(`Found an undefined ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); + } + return this.getStagingDeployCashData(issue); }); } /** @@ -11782,7 +11786,7 @@ class GithubUtils { } internalQASection = internalQASection[1]; const internalQAPRs = [...internalQASection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2].split('-')[0].trim(), + url: match[2].split('-').at(0)?.trim() ?? '', number: Number.parseInt(match[3], 10), isResolved: match[1] === 'x', })); @@ -11866,7 +11870,7 @@ class GithubUtils { * Fetch all pull requests given a list of PR numbers. */ static fetchAllPullRequests(pullRequestNumbers) { - const oldestPR = pullRequestNumbers.sort((a, b) => a - b)[0]; + const oldestPR = pullRequestNumbers.sort((a, b) => a - b).at(0); return this.paginate(this.octokit.pulls.list, { owner: CONST_1.default.GITHUB_OWNER, repo: CONST_1.default.APP_REPO, @@ -11940,7 +11944,7 @@ class GithubUtils { repo: CONST_1.default.APP_REPO, workflow_id: workflow, }) - .then((response) => response.data.workflow_runs[0]?.id); + .then((response) => response.data.workflow_runs.at(0)?.id ?? -1); } /** * Generate the URL of an New Expensify pull request given the PR number. @@ -12008,7 +12012,7 @@ class GithubUtils { per_page: 1, name: artifactName, }) - .then((response) => response.data.artifacts[0]); + .then((response) => response.data.artifacts.at(0)); } /** * Given an artifact ID, returns the download URL to a zip file containing the artifact. diff --git a/.github/actions/javascript/createOrUpdateStagingDeploy/createOrUpdateStagingDeploy.ts b/.github/actions/javascript/createOrUpdateStagingDeploy/createOrUpdateStagingDeploy.ts index caff455e9fa5..1964b143146d 100644 --- a/.github/actions/javascript/createOrUpdateStagingDeploy/createOrUpdateStagingDeploy.ts +++ b/.github/actions/javascript/createOrUpdateStagingDeploy/createOrUpdateStagingDeploy.ts @@ -29,15 +29,24 @@ 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[0]; + const mostRecentChecklist = recentDeployChecklists.at(0); + + if (!mostRecentChecklist) { + throw new Error('Could not find the most recent checklist'); + } + const shouldCreateNewDeployChecklist = mostRecentChecklist.state !== 'open'; - const previousChecklist = shouldCreateNewDeployChecklist ? mostRecentChecklist : recentDeployChecklists[1]; + const previousChecklist = shouldCreateNewDeployChecklist ? mostRecentChecklist : recentDeployChecklists.at(1); if (shouldCreateNewDeployChecklist) { console.log('Latest StagingDeployCash is closed, creating a new one.', mostRecentChecklist); } else { console.log('Latest StagingDeployCash is open, updating it instead of creating a new one.', 'Current:', mostRecentChecklist, 'Previous:', previousChecklist); } + if (!previousChecklist) { + throw new Error('Could not find the previous checklist'); + } + // 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/createOrUpdateStagingDeploy/index.js b/.github/actions/javascript/createOrUpdateStagingDeploy/index.js index d3e249ee6f47..1b7cccb730ff 100644 --- a/.github/actions/javascript/createOrUpdateStagingDeploy/index.js +++ b/.github/actions/javascript/createOrUpdateStagingDeploy/index.js @@ -14229,15 +14229,21 @@ async function run() { }); // 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[0]; + const mostRecentChecklist = recentDeployChecklists.at(0); + if (!mostRecentChecklist) { + throw new Error('Could not find the most recent checklist'); + } const shouldCreateNewDeployChecklist = mostRecentChecklist.state !== 'open'; - const previousChecklist = shouldCreateNewDeployChecklist ? mostRecentChecklist : recentDeployChecklists[1]; + const previousChecklist = shouldCreateNewDeployChecklist ? mostRecentChecklist : recentDeployChecklists.at(1); if (shouldCreateNewDeployChecklist) { console.log('Latest StagingDeployCash is closed, creating a new one.', mostRecentChecklist); } else { console.log('Latest StagingDeployCash is open, updating it instead of creating a new one.', 'Current:', mostRecentChecklist, 'Previous:', previousChecklist); } + if (!previousChecklist) { + throw new Error('Could not find the previous checklist'); + } // Parse the data from the previous and current checklists into the format used to generate the checklist const previousChecklistData = GithubUtils_1.default.getStagingDeployCashData(previousChecklist); const currentChecklistData = shouldCreateNewDeployChecklist ? undefined : GithubUtils_1.default.getStagingDeployCashData(mostRecentChecklist); @@ -14735,7 +14741,11 @@ class GithubUtils { if (data.length > 1) { throw new Error(`Found more than one ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); } - return this.getStagingDeployCashData(data[0]); + const issue = data.at(0); + if (!issue) { + throw new Error(`Found an undefined ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); + } + return this.getStagingDeployCashData(issue); }); } /** @@ -14813,7 +14823,7 @@ class GithubUtils { } internalQASection = internalQASection[1]; const internalQAPRs = [...internalQASection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2].split('-')[0].trim(), + url: match[2].split('-').at(0)?.trim() ?? '', number: Number.parseInt(match[3], 10), isResolved: match[1] === 'x', })); @@ -14897,7 +14907,7 @@ class GithubUtils { * Fetch all pull requests given a list of PR numbers. */ static fetchAllPullRequests(pullRequestNumbers) { - const oldestPR = pullRequestNumbers.sort((a, b) => a - b)[0]; + const oldestPR = pullRequestNumbers.sort((a, b) => a - b).at(0); return this.paginate(this.octokit.pulls.list, { owner: CONST_1.default.GITHUB_OWNER, repo: CONST_1.default.APP_REPO, @@ -14971,7 +14981,7 @@ class GithubUtils { repo: CONST_1.default.APP_REPO, workflow_id: workflow, }) - .then((response) => response.data.workflow_runs[0]?.id); + .then((response) => response.data.workflow_runs.at(0)?.id ?? -1); } /** * Generate the URL of an New Expensify pull request given the PR number. @@ -15039,7 +15049,7 @@ class GithubUtils { per_page: 1, name: artifactName, }) - .then((response) => response.data.artifacts[0]); + .then((response) => response.data.artifacts.at(0)); } /** * Given an artifact ID, returns the download URL to a zip file containing the artifact. diff --git a/.github/actions/javascript/getArtifactInfo/index.js b/.github/actions/javascript/getArtifactInfo/index.js index 82bf90ef6d2b..76cacef0221f 100644 --- a/.github/actions/javascript/getArtifactInfo/index.js +++ b/.github/actions/javascript/getArtifactInfo/index.js @@ -11665,7 +11665,11 @@ class GithubUtils { if (data.length > 1) { throw new Error(`Found more than one ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); } - return this.getStagingDeployCashData(data[0]); + const issue = data.at(0); + if (!issue) { + throw new Error(`Found an undefined ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); + } + return this.getStagingDeployCashData(issue); }); } /** @@ -11743,7 +11747,7 @@ class GithubUtils { } internalQASection = internalQASection[1]; const internalQAPRs = [...internalQASection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2].split('-')[0].trim(), + url: match[2].split('-').at(0)?.trim() ?? '', number: Number.parseInt(match[3], 10), isResolved: match[1] === 'x', })); @@ -11827,7 +11831,7 @@ class GithubUtils { * Fetch all pull requests given a list of PR numbers. */ static fetchAllPullRequests(pullRequestNumbers) { - const oldestPR = pullRequestNumbers.sort((a, b) => a - b)[0]; + const oldestPR = pullRequestNumbers.sort((a, b) => a - b).at(0); return this.paginate(this.octokit.pulls.list, { owner: CONST_1.default.GITHUB_OWNER, repo: CONST_1.default.APP_REPO, @@ -11901,7 +11905,7 @@ class GithubUtils { repo: CONST_1.default.APP_REPO, workflow_id: workflow, }) - .then((response) => response.data.workflow_runs[0]?.id); + .then((response) => response.data.workflow_runs.at(0)?.id ?? -1); } /** * Generate the URL of an New Expensify pull request given the PR number. @@ -11969,7 +11973,7 @@ class GithubUtils { per_page: 1, name: artifactName, }) - .then((response) => response.data.artifacts[0]); + .then((response) => response.data.artifacts.at(0)); } /** * Given an artifact ID, returns the download URL to a zip file containing the artifact. diff --git a/.github/actions/javascript/getDeployPullRequestList/index.js b/.github/actions/javascript/getDeployPullRequestList/index.js index 918d631778d3..cde96b76b6e6 100644 --- a/.github/actions/javascript/getDeployPullRequestList/index.js +++ b/.github/actions/javascript/getDeployPullRequestList/index.js @@ -12027,7 +12027,11 @@ class GithubUtils { if (data.length > 1) { throw new Error(`Found more than one ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); } - return this.getStagingDeployCashData(data[0]); + const issue = data.at(0); + if (!issue) { + throw new Error(`Found an undefined ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); + } + return this.getStagingDeployCashData(issue); }); } /** @@ -12105,7 +12109,7 @@ class GithubUtils { } internalQASection = internalQASection[1]; const internalQAPRs = [...internalQASection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2].split('-')[0].trim(), + url: match[2].split('-').at(0)?.trim() ?? '', number: Number.parseInt(match[3], 10), isResolved: match[1] === 'x', })); @@ -12189,7 +12193,7 @@ class GithubUtils { * Fetch all pull requests given a list of PR numbers. */ static fetchAllPullRequests(pullRequestNumbers) { - const oldestPR = pullRequestNumbers.sort((a, b) => a - b)[0]; + const oldestPR = pullRequestNumbers.sort((a, b) => a - b).at(0); return this.paginate(this.octokit.pulls.list, { owner: CONST_1.default.GITHUB_OWNER, repo: CONST_1.default.APP_REPO, @@ -12263,7 +12267,7 @@ class GithubUtils { repo: CONST_1.default.APP_REPO, workflow_id: workflow, }) - .then((response) => response.data.workflow_runs[0]?.id); + .then((response) => response.data.workflow_runs.at(0)?.id ?? -1); } /** * Generate the URL of an New Expensify pull request given the PR number. @@ -12331,7 +12335,7 @@ class GithubUtils { per_page: 1, name: artifactName, }) - .then((response) => response.data.artifacts[0]); + .then((response) => response.data.artifacts.at(0)); } /** * Given an artifact ID, returns the download URL to a zip file containing the artifact. diff --git a/.github/actions/javascript/getPullRequestDetails/index.js b/.github/actions/javascript/getPullRequestDetails/index.js index 8580842b380c..b1c096ed0be8 100644 --- a/.github/actions/javascript/getPullRequestDetails/index.js +++ b/.github/actions/javascript/getPullRequestDetails/index.js @@ -11767,7 +11767,11 @@ class GithubUtils { if (data.length > 1) { throw new Error(`Found more than one ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); } - return this.getStagingDeployCashData(data[0]); + const issue = data.at(0); + if (!issue) { + throw new Error(`Found an undefined ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); + } + return this.getStagingDeployCashData(issue); }); } /** @@ -11845,7 +11849,7 @@ class GithubUtils { } internalQASection = internalQASection[1]; const internalQAPRs = [...internalQASection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2].split('-')[0].trim(), + url: match[2].split('-').at(0)?.trim() ?? '', number: Number.parseInt(match[3], 10), isResolved: match[1] === 'x', })); @@ -11929,7 +11933,7 @@ class GithubUtils { * Fetch all pull requests given a list of PR numbers. */ static fetchAllPullRequests(pullRequestNumbers) { - const oldestPR = pullRequestNumbers.sort((a, b) => a - b)[0]; + const oldestPR = pullRequestNumbers.sort((a, b) => a - b).at(0); return this.paginate(this.octokit.pulls.list, { owner: CONST_1.default.GITHUB_OWNER, repo: CONST_1.default.APP_REPO, @@ -12003,7 +12007,7 @@ class GithubUtils { repo: CONST_1.default.APP_REPO, workflow_id: workflow, }) - .then((response) => response.data.workflow_runs[0]?.id); + .then((response) => response.data.workflow_runs.at(0)?.id ?? -1); } /** * Generate the URL of an New Expensify pull request given the PR number. @@ -12071,7 +12075,7 @@ class GithubUtils { per_page: 1, name: artifactName, }) - .then((response) => response.data.artifacts[0]); + .then((response) => response.data.artifacts.at(0)); } /** * Given an artifact ID, returns the download URL to a zip file containing the artifact. diff --git a/.github/actions/javascript/isStagingDeployLocked/index.js b/.github/actions/javascript/isStagingDeployLocked/index.js index 9e823e8da5ae..d7196cad32f7 100644 --- a/.github/actions/javascript/isStagingDeployLocked/index.js +++ b/.github/actions/javascript/isStagingDeployLocked/index.js @@ -11665,7 +11665,11 @@ class GithubUtils { if (data.length > 1) { throw new Error(`Found more than one ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); } - return this.getStagingDeployCashData(data[0]); + const issue = data.at(0); + if (!issue) { + throw new Error(`Found an undefined ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); + } + return this.getStagingDeployCashData(issue); }); } /** @@ -11743,7 +11747,7 @@ class GithubUtils { } internalQASection = internalQASection[1]; const internalQAPRs = [...internalQASection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2].split('-')[0].trim(), + url: match[2].split('-').at(0)?.trim() ?? '', number: Number.parseInt(match[3], 10), isResolved: match[1] === 'x', })); @@ -11827,7 +11831,7 @@ class GithubUtils { * Fetch all pull requests given a list of PR numbers. */ static fetchAllPullRequests(pullRequestNumbers) { - const oldestPR = pullRequestNumbers.sort((a, b) => a - b)[0]; + const oldestPR = pullRequestNumbers.sort((a, b) => a - b).at(0); return this.paginate(this.octokit.pulls.list, { owner: CONST_1.default.GITHUB_OWNER, repo: CONST_1.default.APP_REPO, @@ -11901,7 +11905,7 @@ class GithubUtils { repo: CONST_1.default.APP_REPO, workflow_id: workflow, }) - .then((response) => response.data.workflow_runs[0]?.id); + .then((response) => response.data.workflow_runs.at(0)?.id ?? -1); } /** * Generate the URL of an New Expensify pull request given the PR number. @@ -11969,7 +11973,7 @@ class GithubUtils { per_page: 1, name: artifactName, }) - .then((response) => response.data.artifacts[0]); + .then((response) => response.data.artifacts.at(0)); } /** * Given an artifact ID, returns the download URL to a zip file containing the artifact. diff --git a/.github/actions/javascript/markPullRequestsAsDeployed/index.js b/.github/actions/javascript/markPullRequestsAsDeployed/index.js index 9f97e4a72d20..6b78a204b57a 100644 --- a/.github/actions/javascript/markPullRequestsAsDeployed/index.js +++ b/.github/actions/javascript/markPullRequestsAsDeployed/index.js @@ -12733,7 +12733,10 @@ async function run() { labels: CONST_1.default.LABELS.STAGING_DEPLOY, state: 'closed', }); - const previousChecklistID = deployChecklists[0].number; + const previousChecklistID = deployChecklists.at(0)?.number; + if (!previousChecklistID) { + throw new Error('Could not find the previous checklist ID'); + } // who closed the last deploy checklist? const deployer = await GithubUtils_1.default.getActorWhoClosedIssue(previousChecklistID); // Create comment on each pull request (one at a time to avoid throttling issues) @@ -13049,7 +13052,11 @@ class GithubUtils { if (data.length > 1) { throw new Error(`Found more than one ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); } - return this.getStagingDeployCashData(data[0]); + const issue = data.at(0); + if (!issue) { + throw new Error(`Found an undefined ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); + } + return this.getStagingDeployCashData(issue); }); } /** @@ -13127,7 +13134,7 @@ class GithubUtils { } internalQASection = internalQASection[1]; const internalQAPRs = [...internalQASection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2].split('-')[0].trim(), + url: match[2].split('-').at(0)?.trim() ?? '', number: Number.parseInt(match[3], 10), isResolved: match[1] === 'x', })); @@ -13211,7 +13218,7 @@ class GithubUtils { * Fetch all pull requests given a list of PR numbers. */ static fetchAllPullRequests(pullRequestNumbers) { - const oldestPR = pullRequestNumbers.sort((a, b) => a - b)[0]; + const oldestPR = pullRequestNumbers.sort((a, b) => a - b).at(0); return this.paginate(this.octokit.pulls.list, { owner: CONST_1.default.GITHUB_OWNER, repo: CONST_1.default.APP_REPO, @@ -13285,7 +13292,7 @@ class GithubUtils { repo: CONST_1.default.APP_REPO, workflow_id: workflow, }) - .then((response) => response.data.workflow_runs[0]?.id); + .then((response) => response.data.workflow_runs.at(0)?.id ?? -1); } /** * Generate the URL of an New Expensify pull request given the PR number. @@ -13353,7 +13360,7 @@ class GithubUtils { per_page: 1, name: artifactName, }) - .then((response) => response.data.artifacts[0]); + .then((response) => response.data.artifacts.at(0)); } /** * Given an artifact ID, returns the download URL to a zip file containing the artifact. diff --git a/.github/actions/javascript/markPullRequestsAsDeployed/markPullRequestsAsDeployed.ts b/.github/actions/javascript/markPullRequestsAsDeployed/markPullRequestsAsDeployed.ts index 71a5c7d5c6ee..986c6c12afb9 100644 --- a/.github/actions/javascript/markPullRequestsAsDeployed/markPullRequestsAsDeployed.ts +++ b/.github/actions/javascript/markPullRequestsAsDeployed/markPullRequestsAsDeployed.ts @@ -78,7 +78,10 @@ async function run() { labels: CONST.LABELS.STAGING_DEPLOY, state: 'closed', }); - const previousChecklistID = deployChecklists[0].number; + const previousChecklistID = deployChecklists.at(0)?.number; + if (!previousChecklistID) { + throw new Error('Could not find the previous checklist ID'); + } // who closed the last deploy checklist? const deployer = await GithubUtils.getActorWhoClosedIssue(previousChecklistID); diff --git a/.github/actions/javascript/postTestBuildComment/index.js b/.github/actions/javascript/postTestBuildComment/index.js index 4f62879a4419..265d62c4b321 100644 --- a/.github/actions/javascript/postTestBuildComment/index.js +++ b/.github/actions/javascript/postTestBuildComment/index.js @@ -11764,7 +11764,11 @@ class GithubUtils { if (data.length > 1) { throw new Error(`Found more than one ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); } - return this.getStagingDeployCashData(data[0]); + const issue = data.at(0); + if (!issue) { + throw new Error(`Found an undefined ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); + } + return this.getStagingDeployCashData(issue); }); } /** @@ -11842,7 +11846,7 @@ class GithubUtils { } internalQASection = internalQASection[1]; const internalQAPRs = [...internalQASection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2].split('-')[0].trim(), + url: match[2].split('-').at(0)?.trim() ?? '', number: Number.parseInt(match[3], 10), isResolved: match[1] === 'x', })); @@ -11926,7 +11930,7 @@ class GithubUtils { * Fetch all pull requests given a list of PR numbers. */ static fetchAllPullRequests(pullRequestNumbers) { - const oldestPR = pullRequestNumbers.sort((a, b) => a - b)[0]; + const oldestPR = pullRequestNumbers.sort((a, b) => a - b).at(0); return this.paginate(this.octokit.pulls.list, { owner: CONST_1.default.GITHUB_OWNER, repo: CONST_1.default.APP_REPO, @@ -12000,7 +12004,7 @@ class GithubUtils { repo: CONST_1.default.APP_REPO, workflow_id: workflow, }) - .then((response) => response.data.workflow_runs[0]?.id); + .then((response) => response.data.workflow_runs.at(0)?.id ?? -1); } /** * Generate the URL of an New Expensify pull request given the PR number. @@ -12068,7 +12072,7 @@ class GithubUtils { per_page: 1, name: artifactName, }) - .then((response) => response.data.artifacts[0]); + .then((response) => response.data.artifacts.at(0)); } /** * Given an artifact ID, returns the download URL to a zip file containing the artifact. diff --git a/.github/actions/javascript/proposalPoliceComment/index.js b/.github/actions/javascript/proposalPoliceComment/index.js index 9b5b56f11a11..a3c613a7018c 100644 --- a/.github/actions/javascript/proposalPoliceComment/index.js +++ b/.github/actions/javascript/proposalPoliceComment/index.js @@ -18026,7 +18026,7 @@ async function run() { if (assistantResponse.includes(`[${CONST_1.default.NO_ACTION}]`)) { // extract the text after [NO_ACTION] from assistantResponse since this is a // bot related action keyword - const noActionContext = assistantResponse.split(`[${CONST_1.default.NO_ACTION}] `)?.[1]?.replace('"', ''); + const noActionContext = assistantResponse.split(`[${CONST_1.default.NO_ACTION}] `)?.at(1)?.replace('"', ''); console.log('[NO_ACTION] w/ context: ', noActionContext); return; } @@ -18047,10 +18047,10 @@ async function run() { else if (assistantResponse.includes('[EDIT_COMMENT]') && !payload.comment?.body.includes('Edited by **proposal-police**')) { // extract the text after [EDIT_COMMENT] from assistantResponse since this is a // bot related action keyword - let extractedNotice = assistantResponse.split('[EDIT_COMMENT] ')?.[1]?.replace('"', ''); + let extractedNotice = assistantResponse.split('[EDIT_COMMENT] ').at(1)?.replace('"', ''); // format the date like: 2024-01-24 13:15:24 UTC not 2024-01-28 18:18:28.000 UTC - const formattedDate = `${date.toISOString()?.split('.')?.[0]?.replace('T', ' ')} UTC`; - extractedNotice = extractedNotice.replace('{updated_timestamp}', formattedDate); + const formattedDate = `${date.toISOString()?.split('.').at(0)?.replace('T', ' ')} UTC`; + extractedNotice = extractedNotice?.replace('{updated_timestamp}', formattedDate); console.log('ProposalPoliceā„¢ editing issue comment...', payload.comment.id); await GithubUtils_1.default.octokit.issues.updateComment({ ...github_1.context.repo, @@ -18253,7 +18253,11 @@ class GithubUtils { if (data.length > 1) { throw new Error(`Found more than one ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); } - return this.getStagingDeployCashData(data[0]); + const issue = data.at(0); + if (!issue) { + throw new Error(`Found an undefined ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); + } + return this.getStagingDeployCashData(issue); }); } /** @@ -18331,7 +18335,7 @@ class GithubUtils { } internalQASection = internalQASection[1]; const internalQAPRs = [...internalQASection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2].split('-')[0].trim(), + url: match[2].split('-').at(0)?.trim() ?? '', number: Number.parseInt(match[3], 10), isResolved: match[1] === 'x', })); @@ -18415,7 +18419,7 @@ class GithubUtils { * Fetch all pull requests given a list of PR numbers. */ static fetchAllPullRequests(pullRequestNumbers) { - const oldestPR = pullRequestNumbers.sort((a, b) => a - b)[0]; + const oldestPR = pullRequestNumbers.sort((a, b) => a - b).at(0); return this.paginate(this.octokit.pulls.list, { owner: CONST_1.default.GITHUB_OWNER, repo: CONST_1.default.APP_REPO, @@ -18489,7 +18493,7 @@ class GithubUtils { repo: CONST_1.default.APP_REPO, workflow_id: workflow, }) - .then((response) => response.data.workflow_runs[0]?.id); + .then((response) => response.data.workflow_runs.at(0)?.id ?? -1); } /** * Generate the URL of an New Expensify pull request given the PR number. @@ -18557,7 +18561,7 @@ class GithubUtils { per_page: 1, name: artifactName, }) - .then((response) => response.data.artifacts[0]); + .then((response) => response.data.artifacts.at(0)); } /** * Given an artifact ID, returns the download URL to a zip file containing the artifact. diff --git a/.github/actions/javascript/proposalPoliceComment/proposalPoliceComment.ts b/.github/actions/javascript/proposalPoliceComment/proposalPoliceComment.ts index 19d3037a80a5..53aeb44cd311 100644 --- a/.github/actions/javascript/proposalPoliceComment/proposalPoliceComment.ts +++ b/.github/actions/javascript/proposalPoliceComment/proposalPoliceComment.ts @@ -64,7 +64,7 @@ async function run() { if (assistantResponse.includes(`[${CONST.NO_ACTION}]`)) { // extract the text after [NO_ACTION] from assistantResponse since this is a // bot related action keyword - const noActionContext = assistantResponse.split(`[${CONST.NO_ACTION}] `)?.[1]?.replace('"', ''); + const noActionContext = assistantResponse.split(`[${CONST.NO_ACTION}] `)?.at(1)?.replace('"', ''); console.log('[NO_ACTION] w/ context: ', noActionContext); return; } @@ -88,10 +88,10 @@ async function run() { } else if (assistantResponse.includes('[EDIT_COMMENT]') && !payload.comment?.body.includes('Edited by **proposal-police**')) { // extract the text after [EDIT_COMMENT] from assistantResponse since this is a // bot related action keyword - let extractedNotice = assistantResponse.split('[EDIT_COMMENT] ')?.[1]?.replace('"', ''); + let extractedNotice = assistantResponse.split('[EDIT_COMMENT] ').at(1)?.replace('"', ''); // format the date like: 2024-01-24 13:15:24 UTC not 2024-01-28 18:18:28.000 UTC - const formattedDate = `${date.toISOString()?.split('.')?.[0]?.replace('T', ' ')} UTC`; - extractedNotice = extractedNotice.replace('{updated_timestamp}', formattedDate); + const formattedDate = `${date.toISOString()?.split('.').at(0)?.replace('T', ' ')} UTC`; + extractedNotice = extractedNotice?.replace('{updated_timestamp}', formattedDate); console.log('ProposalPoliceā„¢ editing issue comment...', payload.comment.id); await GithubUtils.octokit.issues.updateComment({ ...context.repo, diff --git a/.github/actions/javascript/reopenIssueWithComment/index.js b/.github/actions/javascript/reopenIssueWithComment/index.js index 83131f363ef8..9c97e3c612a9 100644 --- a/.github/actions/javascript/reopenIssueWithComment/index.js +++ b/.github/actions/javascript/reopenIssueWithComment/index.js @@ -11675,7 +11675,11 @@ class GithubUtils { if (data.length > 1) { throw new Error(`Found more than one ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); } - return this.getStagingDeployCashData(data[0]); + const issue = data.at(0); + if (!issue) { + throw new Error(`Found an undefined ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); + } + return this.getStagingDeployCashData(issue); }); } /** @@ -11753,7 +11757,7 @@ class GithubUtils { } internalQASection = internalQASection[1]; const internalQAPRs = [...internalQASection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2].split('-')[0].trim(), + url: match[2].split('-').at(0)?.trim() ?? '', number: Number.parseInt(match[3], 10), isResolved: match[1] === 'x', })); @@ -11837,7 +11841,7 @@ class GithubUtils { * Fetch all pull requests given a list of PR numbers. */ static fetchAllPullRequests(pullRequestNumbers) { - const oldestPR = pullRequestNumbers.sort((a, b) => a - b)[0]; + const oldestPR = pullRequestNumbers.sort((a, b) => a - b).at(0); return this.paginate(this.octokit.pulls.list, { owner: CONST_1.default.GITHUB_OWNER, repo: CONST_1.default.APP_REPO, @@ -11911,7 +11915,7 @@ class GithubUtils { repo: CONST_1.default.APP_REPO, workflow_id: workflow, }) - .then((response) => response.data.workflow_runs[0]?.id); + .then((response) => response.data.workflow_runs.at(0)?.id ?? -1); } /** * Generate the URL of an New Expensify pull request given the PR number. @@ -11979,7 +11983,7 @@ class GithubUtils { per_page: 1, name: artifactName, }) - .then((response) => response.data.artifacts[0]); + .then((response) => response.data.artifacts.at(0)); } /** * Given an artifact ID, returns the download URL to a zip file containing the artifact. diff --git a/.github/actions/javascript/reviewerChecklist/index.js b/.github/actions/javascript/reviewerChecklist/index.js index 2a0977db8016..93a3ccf1a0f3 100644 --- a/.github/actions/javascript/reviewerChecklist/index.js +++ b/.github/actions/javascript/reviewerChecklist/index.js @@ -11549,14 +11549,14 @@ function checkIssueForCompletedChecklist(numberOfChecklistItems) { break; } const whitespace = /([\n\r])/gm; - const comment = combinedComments[i].replace(whitespace, ''); - console.log(`Comment ${i} starts with: ${comment.slice(0, 20)}...`); + const comment = combinedComments.at(i)?.replace(whitespace, ''); + 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; } } if (!foundReviewerChecklist) { @@ -11767,7 +11767,11 @@ class GithubUtils { if (data.length > 1) { throw new Error(`Found more than one ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); } - return this.getStagingDeployCashData(data[0]); + const issue = data.at(0); + if (!issue) { + throw new Error(`Found an undefined ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); + } + return this.getStagingDeployCashData(issue); }); } /** @@ -11845,7 +11849,7 @@ class GithubUtils { } internalQASection = internalQASection[1]; const internalQAPRs = [...internalQASection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2].split('-')[0].trim(), + url: match[2].split('-').at(0)?.trim() ?? '', number: Number.parseInt(match[3], 10), isResolved: match[1] === 'x', })); @@ -11929,7 +11933,7 @@ class GithubUtils { * Fetch all pull requests given a list of PR numbers. */ static fetchAllPullRequests(pullRequestNumbers) { - const oldestPR = pullRequestNumbers.sort((a, b) => a - b)[0]; + const oldestPR = pullRequestNumbers.sort((a, b) => a - b).at(0); return this.paginate(this.octokit.pulls.list, { owner: CONST_1.default.GITHUB_OWNER, repo: CONST_1.default.APP_REPO, @@ -12003,7 +12007,7 @@ class GithubUtils { repo: CONST_1.default.APP_REPO, workflow_id: workflow, }) - .then((response) => response.data.workflow_runs[0]?.id); + .then((response) => response.data.workflow_runs.at(0)?.id ?? -1); } /** * Generate the URL of an New Expensify pull request given the PR number. @@ -12071,7 +12075,7 @@ class GithubUtils { per_page: 1, name: artifactName, }) - .then((response) => response.data.artifacts[0]); + .then((response) => response.data.artifacts.at(0)); } /** * Given an artifact ID, returns the download URL to a zip file containing the artifact. diff --git a/.github/actions/javascript/reviewerChecklist/reviewerChecklist.ts b/.github/actions/javascript/reviewerChecklist/reviewerChecklist.ts index f57ef6c36a04..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[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/index.js b/.github/actions/javascript/validateReassureOutput/index.js index 99881e8ad9db..9eff1ee3101e 100644 --- a/.github/actions/javascript/validateReassureOutput/index.js +++ b/.github/actions/javascript/validateReassureOutput/index.js @@ -2735,7 +2735,10 @@ const run = () => { } console.log(`Processing ${regressionOutput.countChanged.length} measurements...`); for (let i = 0; i < regressionOutput.countChanged.length; i++) { - const measurement = regressionOutput.countChanged[i]; + const measurement = regressionOutput.countChanged.at(i); + if (!measurement) { + continue; + } const baseline = measurement.baseline; const current = measurement.current; console.log(`Processing measurement ${i + 1}: ${measurement.name}`); diff --git a/.github/actions/javascript/validateReassureOutput/validateReassureOutput.ts b/.github/actions/javascript/validateReassureOutput/validateReassureOutput.ts index 7e5cfd0bd9f9..24901ca55aee 100644 --- a/.github/actions/javascript/validateReassureOutput/validateReassureOutput.ts +++ b/.github/actions/javascript/validateReassureOutput/validateReassureOutput.ts @@ -15,7 +15,12 @@ const run = (): boolean => { console.log(`Processing ${regressionOutput.countChanged.length} measurements...`); for (let i = 0; i < regressionOutput.countChanged.length; i++) { - const measurement = regressionOutput.countChanged[i]; + const measurement = regressionOutput.countChanged.at(i); + + if (!measurement) { + continue; + } + const baseline: MeasureEntry = measurement.baseline; const current: MeasureEntry = measurement.current; diff --git a/.github/actions/javascript/verifySignedCommits/index.js b/.github/actions/javascript/verifySignedCommits/index.js index 49a4341b84af..8920086eea46 100644 --- a/.github/actions/javascript/verifySignedCommits/index.js +++ b/.github/actions/javascript/verifySignedCommits/index.js @@ -11707,7 +11707,11 @@ class GithubUtils { if (data.length > 1) { throw new Error(`Found more than one ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); } - return this.getStagingDeployCashData(data[0]); + const issue = data.at(0); + if (!issue) { + throw new Error(`Found an undefined ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); + } + return this.getStagingDeployCashData(issue); }); } /** @@ -11785,7 +11789,7 @@ class GithubUtils { } internalQASection = internalQASection[1]; const internalQAPRs = [...internalQASection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2].split('-')[0].trim(), + url: match[2].split('-').at(0)?.trim() ?? '', number: Number.parseInt(match[3], 10), isResolved: match[1] === 'x', })); @@ -11869,7 +11873,7 @@ class GithubUtils { * Fetch all pull requests given a list of PR numbers. */ static fetchAllPullRequests(pullRequestNumbers) { - const oldestPR = pullRequestNumbers.sort((a, b) => a - b)[0]; + const oldestPR = pullRequestNumbers.sort((a, b) => a - b).at(0); return this.paginate(this.octokit.pulls.list, { owner: CONST_1.default.GITHUB_OWNER, repo: CONST_1.default.APP_REPO, @@ -11943,7 +11947,7 @@ class GithubUtils { repo: CONST_1.default.APP_REPO, workflow_id: workflow, }) - .then((response) => response.data.workflow_runs[0]?.id); + .then((response) => response.data.workflow_runs.at(0)?.id ?? -1); } /** * Generate the URL of an New Expensify pull request given the PR number. @@ -12011,7 +12015,7 @@ class GithubUtils { per_page: 1, name: artifactName, }) - .then((response) => response.data.artifacts[0]); + .then((response) => response.data.artifacts.at(0)); } /** * Given an artifact ID, returns the download URL to a zip file containing the artifact. diff --git a/.github/libs/GithubUtils.ts b/.github/libs/GithubUtils.ts index 36363684a351..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[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('-')[0].trim(), + url: match[2].split('-').at(0)?.trim() ?? '', number: Number.parseInt(match[3], 10), isResolved: match[1] === 'x', })); @@ -367,7 +373,7 @@ class GithubUtils { * Fetch all pull requests given a list of PR numbers. */ static fetchAllPullRequests(pullRequestNumbers: number[]): Promise { - const oldestPR = pullRequestNumbers.sort((a, b) => a - b)[0]; + const oldestPR = pullRequestNumbers.sort((a, b) => a - b).at(0); return this.paginate( this.octokit.pulls.list, { @@ -459,7 +465,7 @@ class GithubUtils { repo: CONST.APP_REPO, workflow_id: workflow, }) - .then((response) => response.data.workflow_runs[0]?.id); + .then((response) => response.data.workflow_runs.at(0)?.id ?? -1); } /** @@ -533,7 +539,7 @@ class GithubUtils { per_page: 1, name: artifactName, }) - .then((response) => response.data.artifacts[0]); + .then((response) => response.data.artifacts.at(0)); } /** diff --git a/.github/libs/nativeVersionUpdater.ts b/.github/libs/nativeVersionUpdater.ts index 4ecf9d64966c..1684614b059e 100644 --- a/.github/libs/nativeVersionUpdater.ts +++ b/.github/libs/nativeVersionUpdater.ts @@ -59,7 +59,7 @@ function updateAndroidVersion(versionName: string, versionCode: string): Promise * Updates the CFBundleShortVersionString and the CFBundleVersion. */ function updateiOSVersion(version: string): string { - const shortVersion = version.split('-')[0]; + const shortVersion = version.split('-').at(0); const cfVersion = version.includes('-') ? version.replace('-', '.') : `${version}.0`; console.log('Updating iOS', `CFBundleShortVersionString: ${shortVersion}`, `CFBundleVersion: ${cfVersion}`); diff --git a/.github/scripts/createDocsRoutes.ts b/.github/scripts/createDocsRoutes.ts index a8ac3d511ff9..264422e27b99 100644 --- a/.github/scripts/createDocsRoutes.ts +++ b/.github/scripts/createDocsRoutes.ts @@ -93,7 +93,10 @@ function pushOrCreateEntry(hubs: Hub[], hub: string, } function getOrderFromArticleFrontMatter(path: string): number | undefined { - const frontmatter = fs.readFileSync(path, 'utf8').split('---')[1]; + const frontmatter = fs.readFileSync(path, 'utf8').split('---').at(1); + if (!frontmatter) { + return; + } const frontmatterObject = yaml.load(frontmatter) as Record; return frontmatterObject.order as number | undefined; } diff --git a/.storybook/webpack.config.ts b/.storybook/webpack.config.ts index f0fff8bda698..3f54b7040d06 100644 --- a/.storybook/webpack.config.ts +++ b/.storybook/webpack.config.ts @@ -67,8 +67,8 @@ const webpackConfig = ({config}: {config: Configuration}) => { // Necessary to overwrite the values in the existing DefinePlugin hardcoded to the Config staging values const definePluginIndex = config.plugins.findIndex((plugin) => plugin instanceof DefinePlugin); - if (definePluginIndex !== -1 && config.plugins[definePluginIndex] instanceof DefinePlugin) { - const definePlugin = config.plugins[definePluginIndex] as DefinePlugin; + if (definePluginIndex !== -1 && config.plugins.at(definePluginIndex) instanceof DefinePlugin) { + const definePlugin = config.plugins.at(definePluginIndex) as DefinePlugin; if (definePlugin.definitions) { definePlugin.definitions.__REACT_WEB_CONFIG__ = JSON.stringify(env); } @@ -76,7 +76,7 @@ const webpackConfig = ({config}: {config: Configuration}) => { config.resolve.extensions = custom.resolve.extensions; const babelRulesIndex = custom.module.rules.findIndex((rule) => rule.loader === 'babel-loader'); - const babelRule = custom.module.rules[babelRulesIndex]; + const babelRule = custom.module.rules.at(babelRulesIndex); if (babelRule) { config.module.rules?.push(babelRule); } diff --git a/assets/emojis/index.ts b/assets/emojis/index.ts index d230e8eec2be..4b05c8caddfa 100644 --- a/assets/emojis/index.ts +++ b/assets/emojis/index.ts @@ -33,7 +33,7 @@ const localeEmojis: LocaleEmojis = { }; const importEmojiLocale = (locale: Locale) => { - const normalizedLocale = locale.toLowerCase().split('-')[0] as Locale; + const normalizedLocale = locale.toLowerCase().split('-').at(0) as Locale; if (!localeEmojis[normalizedLocale]) { const emojiImportPromise = normalizedLocale === 'en' ? import('./en') : import('./es'); return emojiImportPromise.then((esEmojiModule) => { diff --git a/config/webpack/webpack.common.ts b/config/webpack/webpack.common.ts index 1bab57905d0e..91fc4b1bf528 100644 --- a/config/webpack/webpack.common.ts +++ b/config/webpack/webpack.common.ts @@ -47,7 +47,7 @@ const environmentToLogoSuffixMap: Record = { }; function mapEnvironmentToLogoSuffix(environmentFile: string): string { - let environment = environmentFile.split('.')[2]; + let environment = environmentFile.split('.').at(2); if (typeof environment === 'undefined') { environment = 'dev'; } diff --git a/jest/setup.ts b/jest/setup.ts index 51385ad19e45..6901ad3c66f3 100644 --- a/jest/setup.ts +++ b/jest/setup.ts @@ -35,7 +35,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[0].startsWith('Timing:')) { + if (params.at(0)?.startsWith('Timing:')) { return; } diff --git a/package-lock.json b/package-lock.json index a5ae221a1e14..f2063482c6ce 100644 --- a/package-lock.json +++ b/package-lock.json @@ -216,7 +216,7 @@ "electron-builder": "25.0.0", "eslint": "^8.57.0", "eslint-config-airbnb-typescript": "^18.0.0", - "eslint-config-expensify": "^2.0.58", + "eslint-config-expensify": "^2.0.59", "eslint-config-prettier": "^9.1.0", "eslint-plugin-deprecation": "^3.0.0", "eslint-plugin-jest": "^28.6.0", @@ -22786,9 +22786,9 @@ } }, "node_modules/eslint-config-expensify": { - "version": "2.0.58", - "resolved": "https://registry.npmjs.org/eslint-config-expensify/-/eslint-config-expensify-2.0.58.tgz", - "integrity": "sha512-iLDJeXwMYLcBRDnInVReHWjMUsNrHMnWfyoQbvuDTChcJANc+QzuDU0gdsDpBx2xjxVF0vckwEXnzmWcUW1Bpw==", + "version": "2.0.59", + "resolved": "https://registry.npmjs.org/eslint-config-expensify/-/eslint-config-expensify-2.0.59.tgz", + "integrity": "sha512-U6ZpXHHtGvTusDLFuVBWJ3r8HwngoE3Gc+BRGEGzBmDA1UkWKLxwshjafq1JQJ+HHwmvN5jwsSnZjNQRkkmbUw==", "dev": true, "dependencies": { "@lwc/eslint-plugin-lwc": "^1.7.2", diff --git a/package.json b/package.json index 3d808af65ba2..1ef7deac8d46 100644 --- a/package.json +++ b/package.json @@ -278,7 +278,7 @@ "electron-builder": "25.0.0", "eslint": "^8.57.0", "eslint-config-airbnb-typescript": "^18.0.0", - "eslint-config-expensify": "^2.0.58", + "eslint-config-expensify": "^2.0.59", "eslint-config-prettier": "^9.1.0", "eslint-plugin-deprecation": "^3.0.0", "eslint-plugin-jest": "^28.6.0", diff --git a/scripts/release-profile.ts b/scripts/release-profile.ts index a83fb55fa5ff..615f009d743d 100755 --- a/scripts/release-profile.ts +++ b/scripts/release-profile.ts @@ -36,7 +36,7 @@ if (cpuProfiles.length === 0) { process.exit(1); } else { // Construct the command - const cpuprofileName = cpuProfiles[0]; + const cpuprofileName = cpuProfiles.at(0); const command = `npx react-native-release-profiler --local "${cpuprofileName}" --sourcemap-path "${sourcemapPath}"`; console.log(`Executing: ${command}`); diff --git a/src/CONST.ts b/src/CONST.ts index bd3ea4177e3e..bbd1dbabbba9 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -4197,7 +4197,7 @@ const CONST = { PADDING: 32, DEFAULT_ZOOM: 15, SINGLE_MARKER_ZOOM: 15, - DEFAULT_COORDINATE: [-122.4021, 37.7911], + DEFAULT_COORDINATE: [-122.4021, 37.7911] as [number, number], STYLE_URL: 'mapbox://styles/expensify/cllcoiqds00cs01r80kp34tmq', ANIMATION_DURATION_ON_CENTER_ME: 1000, CENTER_BUTTON_FADE_DURATION: 300, diff --git a/src/components/AccountSwitcher.tsx b/src/components/AccountSwitcher.tsx index 71970b88eac9..538009b2565e 100644 --- a/src/components/AccountSwitcher.tsx +++ b/src/components/AccountSwitcher.tsx @@ -50,7 +50,7 @@ function AccountSwitcher() { const canSwitchAccounts = canUseNewDotCopilot && (delegators.length > 0 || isActingAsDelegate); const createBaseMenuItem = (personalDetails: PersonalDetails | undefined, errors?: Errors, additionalProps: MenuItemWithLink = {}): MenuItemWithLink => { - const error = Object.values(errors ?? {})[0] ?? ''; + const error = Object.values(errors ?? {}).at(0) ?? ''; return { title: personalDetails?.displayName ?? personalDetails?.login, description: Str.removeSMSDomain(personalDetails?.login ?? ''), diff --git a/src/components/AddPlaidBankAccount.tsx b/src/components/AddPlaidBankAccount.tsx index 4de286183ea8..11b0010ed253 100644 --- a/src/components/AddPlaidBankAccount.tsx +++ b/src/components/AddPlaidBankAccount.tsx @@ -173,7 +173,7 @@ function AddPlaidBankAccount({ const {icon, iconSize, iconStyles} = getBankIcon({styles}); const plaidErrors = plaidData?.errors; // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style - const plaidDataErrorMessage = !isEmptyObject(plaidErrors) ? (Object.values(plaidErrors)[0] as string) : ''; + const plaidDataErrorMessage = !isEmptyObject(plaidErrors) ? (Object.values(plaidErrors).at(0) as string) : ''; const bankName = plaidData?.bankName; /** diff --git a/src/components/AttachmentPicker/index.native.tsx b/src/components/AttachmentPicker/index.native.tsx index 366366423324..dd6534218359 100644 --- a/src/components/AttachmentPicker/index.native.tsx +++ b/src/components/AttachmentPicker/index.native.tsx @@ -177,7 +177,10 @@ function AttachmentPicker({type = CONST.ATTACHMENT_PICKER_TYPE.FILE, children, s const uri = manipResult.uri; const convertedAsset = { uri, - name: uri.substring(uri.lastIndexOf('/') + 1).split('?')[0], + name: uri + .substring(uri.lastIndexOf('/') + 1) + .split('?') + .at(0), type: 'image/jpeg', width: manipResult.width, height: manipResult.height, @@ -363,8 +366,11 @@ function AttachmentPicker({type = CONST.ATTACHMENT_PICKER_TYPE.FILE, children, s if (focusedIndex === -1) { return; } - selectItem(menuItemData[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 e0f7571af8c7..aae621d0197f 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[initialPage] !== undefined && onNavigate) { - onNavigate(targetAttachments[initialPage]); + if (attachment !== undefined && onNavigate) { + onNavigate(attachment); } } // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps @@ -66,13 +68,14 @@ function AttachmentCarousel({report, reportActions, parentReportActions, source, Keyboard.dismiss(); setShouldShowArrows(true); - const item = attachments[newPageIndex]; + const item = attachments.at(newPageIndex); setPage(newPageIndex); - setActiveSource(item.source); - - if (onNavigate) { - onNavigate(item); + if (item) { + setActiveSource(item.source); + if (onNavigate) { + onNavigate(item); + } } }, [setShouldShowArrows, attachments, onNavigate], diff --git a/src/components/Attachments/AttachmentCarousel/index.tsx b/src/components/Attachments/AttachmentCarousel/index.tsx index 72e0f17aa310..14fbd3925127 100644 --- a/src/components/Attachments/AttachmentCarousel/index.tsx +++ b/src/components/Attachments/AttachmentCarousel/index.tsx @@ -102,9 +102,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[initialPage] !== undefined && onNavigate) { - onNavigate(targetAttachments[initialPage]); + if (attachment !== undefined && onNavigate) { + onNavigate(attachment); } } }, [report.privateNotes, reportActions, parentReportActions, compareImage, report.parentReportActionID, attachments, setDownloadButtonVisibility, onNavigate, accountID, type]); @@ -131,7 +132,7 @@ function AttachmentCarousel({report, reportActions, parentReportActions, source, // Since we can have only one item in view at a time, we can use the first item in the array // to get the index of the current page - const entry = viewableItems[0]; + const entry = viewableItems.at(0); if (!entry) { setActiveSource(null); return; @@ -158,7 +159,7 @@ function AttachmentCarousel({report, reportActions, parentReportActions, source, } const nextIndex = page + deltaSlide; - const nextItem = attachments[nextIndex]; + const nextItem = attachments.at(nextIndex); if (!nextItem || !scrollRef.current) { return; diff --git a/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.android.tsx b/src/components/Attachments/AttachmentView/AttachmentViewPdf/index.android.tsx index 8c4af3275bd8..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[0].absoluteX - offsetX.value); - const translateY = Math.abs(evt.allTouches[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[0].absoluteX; - offsetY.value = evt.allTouches[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 1cd1bfb36d83..05d15a2ad995 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 {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,8 +122,8 @@ function AvatarWithDisplayName({ {shouldShowSubscriptAvatar ? ( ) : ( diff --git a/src/components/ButtonWithDropdownMenu/index.tsx b/src/components/ButtonWithDropdownMenu/index.tsx index c1b662c3680e..2fdbaad73c4e 100644 --- a/src/components/ButtonWithDropdownMenu/index.tsx +++ b/src/components/ButtonWithDropdownMenu/index.tsx @@ -53,7 +53,7 @@ function ButtonWithDropdownMenu({ const {windowWidth, windowHeight} = useWindowDimensions(); const dropdownAnchor = useRef(null); const dropdownButtonRef = isSplitButton ? buttonRef : mergeRefs(buttonRef, dropdownAnchor); - const selectedItem = options[selectedItemIndex] || options[0]; + const selectedItem = options.at(selectedItemIndex) ?? options.at(0); const innerStyleDropButton = StyleUtils.getDropDownButtonHeight(buttonSize); const isButtonSizeLarge = buttonSize === CONST.DROPDOWN_BUTTON_SIZE.LARGE; const nullCheckRef = (ref: MutableRefObject) => ref ?? null; @@ -86,9 +86,14 @@ function ButtonWithDropdownMenu({ setIsMenuVisible(!isMenuVisible); return; } - onPress(e, selectedItem?.value); + if (selectedItem?.value) { + onPress(e, selectedItem?.value); + } } else { - onPress(e, options[0]?.value); + const option = options.at(0); + if (option?.value) { + onPress(e, option?.value); + } } }, { @@ -107,8 +112,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 @@ -156,12 +162,15 @@ function ButtonWithDropdownMenu({ success={success} ref={buttonRef} pressOnEnter={pressOnEnter} - isDisabled={isDisabled || !!options[0].disabled} + isDisabled={isDisabled || !!options.at(0)?.disabled} style={[styles.w100, style]} disabledStyle={disabledStyle} isLoading={isLoading} - text={selectedItem.text} - onPress={(event) => onPress(event, options[0].value)} + text={selectedItem?.text} + onPress={(event) => { + const option = options.at(0); + return option ? onPress(event, option.value) : undefined; + }} large={buttonSize === CONST.DROPDOWN_BUTTON_SIZE.LARGE} medium={buttonSize === CONST.DROPDOWN_BUTTON_SIZE.MEDIUM} small={buttonSize === CONST.DROPDOWN_BUTTON_SIZE.SMALL} diff --git a/src/components/CategoryPicker.tsx b/src/components/CategoryPicker.tsx index e5c85a8f5f6d..73d07cfba229 100644 --- a/src/components/CategoryPicker.tsx +++ b/src/components/CategoryPicker.tsx @@ -62,7 +62,7 @@ function CategoryPicker({selectedCategory, policyCategories, policyRecentlyUsedC false, ); - const categoryData = categoryOptions?.[0]?.data ?? []; + const categoryData = categoryOptions?.at(0)?.data ?? []; const header = OptionsListUtils.getHeaderMessageForNonUserList(categoryData.length > 0, debouncedSearchValue); const categoriesCount = OptionsListUtils.getEnabledCategoriesCount(categories); const isCategoriesCountBelowThreshold = categoriesCount < CONST.CATEGORY_LIST_THRESHOLD; @@ -71,7 +71,7 @@ function CategoryPicker({selectedCategory, policyCategories, policyRecentlyUsedC return [categoryOptions, header, showInput]; }, [policyRecentlyUsedCategories, debouncedSearchValue, selectedOptions, policyCategories, policyCategoriesDraft]); - const selectedOptionKey = useMemo(() => (sections?.[0]?.data ?? []).filter((category) => category.searchText === selectedCategory)[0]?.keyForList, [sections, selectedCategory]); + const selectedOptionKey = useMemo(() => (sections?.at(0)?.data ?? []).filter((category) => category.searchText === selectedCategory).at(0)?.keyForList, [sections, selectedCategory]); return ( ) => { - const clipboardContent = e.nativeEvent.items[0]; - if (clipboardContent.type === 'text/plain') { + const clipboardContent = e.nativeEvent.items.at(0); + 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/ConfirmedRoute.tsx b/src/components/ConfirmedRoute.tsx index 4940a276cbf8..e1580dcae7d0 100644 --- a/src/components/ConfirmedRoute.tsx +++ b/src/components/ConfirmedRoute.tsx @@ -112,7 +112,7 @@ function ConfirmedRoute({mapboxAccessToken, transaction, isSmallerIcon, shouldHa pitchEnabled={false} initialState={{ zoom: CONST.MAPBOX.DEFAULT_ZOOM, - location: waypointMarkers?.[0]?.coordinate ?? (CONST.MAPBOX.DEFAULT_COORDINATE as [number, number]), + location: waypointMarkers?.at(0)?.coordinate ?? CONST.MAPBOX.DEFAULT_COORDINATE, }} directionCoordinates={coordinates as Array<[number, number]>} style={[styles.mapView, shouldHaveBorderRadius && styles.br4]} diff --git a/src/components/DatePicker/CalendarPicker/generateMonthMatrix.ts b/src/components/DatePicker/CalendarPicker/generateMonthMatrix.ts index 21e7ff752794..9a5cf7d7f741 100644 --- a/src/components/DatePicker/CalendarPicker/generateMonthMatrix.ts +++ b/src/components/DatePicker/CalendarPicker/generateMonthMatrix.ts @@ -51,8 +51,8 @@ export default function generateMonthMatrix(year: number, month: number) { } // Add null values for days before the first day of the month - for (let i = matrix[0].length; i < 7; i++) { - matrix[0].unshift(undefined); + for (let i = matrix.at(0)?.length ?? 0; i < 7; i++) { + matrix.at(0)?.unshift(undefined); } return matrix; diff --git a/src/components/DatePicker/CalendarPicker/index.tsx b/src/components/DatePicker/CalendarPicker/index.tsx index 533586d4bdbf..287ec3359175 100644 --- a/src/components/DatePicker/CalendarPicker/index.tsx +++ b/src/components/DatePicker/CalendarPicker/index.tsx @@ -170,7 +170,7 @@ function CalendarPicker({ testID="currentMonthText" accessibilityLabel={translate('common.currentMonth')} > - {monthNames[currentMonthView]} + {monthNames.at(currentMonthView)} ))} - {calendarDaysMatrix.map((week) => ( + {calendarDaysMatrix?.map((week) => ( { // Only shift the tooltip in case the containerLayout or Refs to the text node are available - if (!containerRef.current || !childRefs.current[index]) { + if (!containerRef.current || !childRefs.current.at(index)) { return 0; } const {width: containerWidth, left: containerLeft} = containerRef.current.getBoundingClientRect(); // We have to return the value as Number so we can't use `measureWindow` which takes a callback - const {width: textNodeWidth, left: textNodeLeft} = childRefs.current[index].getBoundingClientRect(); + const {width: textNodeWidth, left: textNodeLeft} = childRefs.current.at(index)?.getBoundingClientRect() ?? {width: 0, left: 0}; const tooltipX = textNodeWidth / 2 + textNodeLeft; const containerRight = containerWidth + containerLeft; const textNodeRight = textNodeWidth + textNodeLeft; diff --git a/src/components/DistanceRequest/DistanceRequestFooter.tsx b/src/components/DistanceRequest/DistanceRequestFooter.tsx index 43dd88f6e36c..8a4455e02bd6 100644 --- a/src/components/DistanceRequest/DistanceRequestFooter.tsx +++ b/src/components/DistanceRequest/DistanceRequestFooter.tsx @@ -107,7 +107,7 @@ function DistanceRequestFooter({waypoints, transaction, mapboxAccessToken, navig pitchEnabled={false} initialState={{ zoom: CONST.MAPBOX.DEFAULT_ZOOM, - location: waypointMarkers?.[0]?.coordinate ?? (CONST.MAPBOX.DEFAULT_COORDINATE as [number, number]), + location: waypointMarkers?.at(0)?.coordinate ?? CONST.MAPBOX.DEFAULT_COORDINATE, }} directionCoordinates={(transaction?.routes?.route0?.geometry?.coordinates as Array<[number, number]>) ?? []} style={[styles.mapView, styles.mapEditView]} diff --git a/src/components/EmojiPicker/EmojiPickerMenu/index.native.tsx b/src/components/EmojiPicker/EmojiPickerMenu/index.native.tsx index b5b4c2d7e71c..e5f36075b13f 100644 --- a/src/components/EmojiPicker/EmojiPickerMenu/index.native.tsx +++ b/src/components/EmojiPicker/EmojiPickerMenu/index.native.tsx @@ -96,7 +96,7 @@ function EmojiPickerMenu({onEmojiSelected, activeEmoji}: EmojiPickerMenuProps, r ); } - const emojiCode = typeof preferredSkinTone === 'number' && types?.[preferredSkinTone] ? types?.[preferredSkinTone] : code; + const emojiCode = typeof preferredSkinTone === 'number' && types?.[preferredSkinTone] ? types?.at(preferredSkinTone) : code; const shouldEmojiBeHighlighted = !!activeEmoji && EmojiUtils.getRemovedSkinToneEmoji(emojiCode) === EmojiUtils.getRemovedSkinToneEmoji(activeEmoji); return ( @@ -107,7 +107,7 @@ function EmojiPickerMenu({onEmojiSelected, activeEmoji}: EmojiPickerMenuProps, r } onEmojiSelected(emoji, item); })} - emoji={emojiCode} + emoji={emojiCode ?? ''} isHighlighted={shouldEmojiBeHighlighted} /> ); diff --git a/src/components/EmojiPicker/EmojiPickerMenu/index.tsx b/src/components/EmojiPicker/EmojiPickerMenu/index.tsx index 92c35d7c7685..4c7483e55a6b 100755 --- a/src/components/EmojiPicker/EmojiPickerMenu/index.tsx +++ b/src/components/EmojiPicker/EmojiPickerMenu/index.tsx @@ -178,13 +178,13 @@ function EmojiPickerMenu({onEmojiSelected, activeEmoji}: EmojiPickerMenuProps, r indexToSelect = 0; } - const item = filteredEmojis[indexToSelect]; + const item = filteredEmojis.at(indexToSelect); if (!item) { return; } if ('types' in item || 'name' in item) { - const emoji = typeof preferredSkinTone === 'number' && item?.types?.[preferredSkinTone] ? item?.types?.[preferredSkinTone] : item.code; - onEmojiSelected(emoji, item); + const emoji = typeof preferredSkinTone === 'number' && item?.types?.[preferredSkinTone] ? item?.types.at(preferredSkinTone) : item.code; + onEmojiSelected(emoji ?? '', item); } }, {shouldPreventDefault: true, shouldStopPropagation: true}, @@ -269,7 +269,7 @@ function EmojiPickerMenu({onEmojiSelected, activeEmoji}: EmojiPickerMenuProps, r ); } - const emojiCode = typeof preferredSkinTone === 'number' && types?.[preferredSkinTone] ? types[preferredSkinTone] : code; + const emojiCode = typeof preferredSkinTone === 'number' && types?.[preferredSkinTone] ? types.at(preferredSkinTone) : code; const isEmojiFocused = index === focusedIndex && isUsingKeyboardMovement; const shouldEmojiBeHighlighted = @@ -292,7 +292,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/FilePicker/index.native.tsx b/src/components/FilePicker/index.native.tsx index b74a68432cab..cc9c73d72c56 100644 --- a/src/components/FilePicker/index.native.tsx +++ b/src/components/FilePicker/index.native.tsx @@ -112,7 +112,7 @@ function FilePicker({children}: FilePickerProps) { onCanceled.current(); return Promise.resolve(); } - const fileData = files[0]; + const fileData = files.at(0); if (!fileData) { onCanceled.current(); diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/AnchorRenderer.tsx b/src/components/HTMLEngineProvider/HTMLRenderers/AnchorRenderer.tsx index 7892d8624699..d2e407ff8b55 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/AnchorRenderer.tsx +++ b/src/components/HTMLEngineProvider/HTMLRenderers/AnchorRenderer.tsx @@ -23,7 +23,7 @@ function AnchorRenderer({tnode, style, key}: AnchorRendererProps) { const {environmentURL} = useEnvironment(); // An auth token is needed to download Expensify chat attachments const isAttachment = !!htmlAttribs[CONST.ATTACHMENT_SOURCE_ATTRIBUTE]; - const tNodeChild = tnode?.domNode?.children?.[0]; + const tNodeChild = tnode?.domNode?.children?.at(0); const displayName = tNodeChild && 'data' in tNodeChild && typeof tNodeChild.data === 'string' ? tNodeChild.data : ''; const parentStyle = tnode.parent?.styles?.nativeTextRet ?? {}; const attrHref = htmlAttribs.href || htmlAttribs[CONST.ATTACHMENT_SOURCE_ATTRIBUTE] || ''; diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.tsx b/src/components/HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.tsx index 6acef20cd833..9abc63e1ba5f 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.tsx +++ b/src/components/HTMLEngineProvider/HTMLRenderers/MentionUserRenderer.tsx @@ -53,22 +53,22 @@ function MentionUserRenderer({style, tnode, TDefaultRenderer, currentUserPersona } // Otherwise, the emails must be of the same private domain, so we should remove the domain part - return displayText.split('@')[0]; + return displayText.split('@').at(0); }; if (!isEmpty(htmlAttribAccountID)) { 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])?.[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/ImportColumn.tsx b/src/components/ImportColumn.tsx index 49601787a207..94cdfd8a4df6 100644 --- a/src/components/ImportColumn.tsx +++ b/src/components/ImportColumn.tsx @@ -161,7 +161,7 @@ function ImportColumn({column, columnName, columnRoles, columnIndex}: ImportColu const columnValuesString = column.slice(containsHeader ? 1 : 0).join(', '); - const colName = findColumnName(column[0]); + const colName = findColumnName(column.at(0) ?? ''); const defaultSelectedIndex = columnRoles.findIndex((item) => item.value === colName); useEffect(() => { @@ -172,7 +172,7 @@ function ImportColumn({column, columnName, columnRoles, columnIndex}: ImportColu // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps -- we don't want this effect to run again }, []); - const columnHeader = containsHeader ? column[0] : translate('spreadsheet.column', columnName); + const columnHeader = containsHeader ? column.at(0) : translate('spreadsheet.column', columnName); return ( diff --git a/src/components/ImportSpreadsheetColumns.tsx b/src/components/ImportSpreadsheetColumns.tsx index 9ba0597bd3d6..7df36dd80c9d 100644 --- a/src/components/ImportSpreadsheetColumns.tsx +++ b/src/components/ImportSpreadsheetColumns.tsx @@ -70,9 +70,9 @@ function ImportSpreadsheetColumns({spreadsheetColumns, columnNames, columnRoles, {spreadsheetColumns.map((column, index) => { return ( diff --git a/src/components/InlineCodeBlock/WrappedText.tsx b/src/components/InlineCodeBlock/WrappedText.tsx index 3045c15c471b..00ab8ec20906 100644 --- a/src/components/InlineCodeBlock/WrappedText.tsx +++ b/src/components/InlineCodeBlock/WrappedText.tsx @@ -52,7 +52,7 @@ function WrappedText({children, wordStyles, textStyles}: WrappedTextProps) { return textMatrix.map((rowText, rowIndex) => ( {rowText.map((colText, colIndex) => ( // Outer View is important to vertically center the Text diff --git a/src/components/InteractiveStepSubHeader.tsx b/src/components/InteractiveStepSubHeader.tsx index d8899a317df5..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[index]); + const step = stepNames.at(index); + if (step) { + onStepSelected(step); + } }; return ( diff --git a/src/components/LHNOptionsList/LHNOptionsList.tsx b/src/components/LHNOptionsList/LHNOptionsList.tsx index 624e8f18e69e..08240a211804 100644 --- a/src/components/LHNOptionsList/LHNOptionsList.tsx +++ b/src/components/LHNOptionsList/LHNOptionsList.tsx @@ -140,7 +140,7 @@ function LHNOptionsList({style, contentContainerStyles, data, onSelectRow, optio const itemTransaction = transactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`]; const hasDraftComment = DraftCommentUtils.isValidDraftComment(draftComments?.[`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${reportID}`]); const sortedReportActions = ReportActionsUtils.getSortedReportActionsForDisplay(itemReportActions); - const lastReportAction = sortedReportActions[0]; + const lastReportAction = sortedReportActions.at(0); // Get the transaction for the last report action let lastReportActionTransactionID = ''; diff --git a/src/components/LHNOptionsList/OptionRowLHN.tsx b/src/components/LHNOptionsList/OptionRowLHN.tsx index 353bacdc0a25..7b7b2e643ce7 100644 --- a/src/components/LHNOptionsList/OptionRowLHN.tsx +++ b/src/components/LHNOptionsList/OptionRowLHN.tsx @@ -34,6 +34,7 @@ import * as User from '@userActions/User'; 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) { @@ -207,7 +208,6 @@ function OptionRowLHN({reportID, isFocused = false, onSelectRow = () => {}, opti if (!event) { return; } - // Prevent composer blur on left click event.preventDefault(); }} @@ -243,8 +243,8 @@ function OptionRowLHN({reportID, isFocused = false, onSelectRow = () => {}, opti (optionItem.shouldShowSubscript ? ( ) : ( diff --git a/src/components/MagicCodeInput.tsx b/src/components/MagicCodeInput.tsx index 79c2445f7ae7..89b9fc9a9e16 100644 --- a/src/components/MagicCodeInput.tsx +++ b/src/components/MagicCodeInput.tsx @@ -276,7 +276,7 @@ function MagicCodeInput( if (isDisableKeyboard && focusedIndex === undefined) { const indexBeforeLastEditIndex = editIndex === 0 ? editIndex : editIndex - 1; - const indexToFocus = numbers[editIndex] === CONST.MAGIC_CODE_EMPTY_CHAR ? indexBeforeLastEditIndex : editIndex; + const indexToFocus = numbers.at(editIndex) === CONST.MAGIC_CODE_EMPTY_CHAR ? indexBeforeLastEditIndex : editIndex; const formElement = inputRefs.current as HTMLFormElement | null; (formElement?.[indexToFocus] as HTMLInputElement)?.focus(); onChangeTextProp(value.substring(0, indexToFocus)); @@ -286,7 +286,7 @@ function MagicCodeInput( // If the currently focused index already has a value, it will delete // that value but maintain the focus on the same input. - if (focusedIndex !== undefined && numbers?.[focusedIndex] !== CONST.MAGIC_CODE_EMPTY_CHAR) { + if (focusedIndex !== undefined && numbers?.at(focusedIndex) !== CONST.MAGIC_CODE_EMPTY_CHAR) { setInput(TEXT_INPUT_EMPTY_STATE); numbers = [...numbers.slice(0, focusedIndex), CONST.MAGIC_CODE_EMPTY_CHAR, ...numbers.slice(focusedIndex + 1, maxLength)]; setEditIndex(focusedIndex); @@ -420,7 +420,7 @@ function MagicCodeInput( focusedIndex === index ? styles.borderColorFocus : {}, ]} > - {decomposeString(value, maxLength)[index] || ''} + {decomposeString(value, maxLength).at(index) ?? ''} ))} diff --git a/src/components/MapView/MapView.tsx b/src/components/MapView/MapView.tsx index 0f0a46fa0a2a..cbe604661c05 100644 --- a/src/components/MapView/MapView.tsx +++ b/src/components/MapView/MapView.tsx @@ -119,7 +119,7 @@ const MapView = forwardRef( cameraRef.current?.setCamera({ zoomLevel: CONST.MAPBOX.SINGLE_MARKER_ZOOM, animationDuration: 1500, - centerCoordinate: waypoints[0].coordinate, + centerCoordinate: waypoints.at(0)?.coordinate, }); } else { const {southWest, northEast} = utils.getBounds( diff --git a/src/components/MapView/MapView.website.tsx b/src/components/MapView/MapView.website.tsx index 58f5ff26c2dc..d30fce35a901 100644 --- a/src/components/MapView/MapView.website.tsx +++ b/src/components/MapView/MapView.website.tsx @@ -133,7 +133,7 @@ const MapView = forwardRef( if (waypoints.length === 1) { mapRef.flyTo({ - center: waypoints[0].coordinate, + center: waypoints.at(0)?.coordinate, zoom: CONST.MAPBOX.SINGLE_MARKER_ZOOM, }); return; diff --git a/src/components/MapView/utils.ts b/src/components/MapView/utils.ts index 04176d715281..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[0] * Math.PI) / 180; - const lat2 = (coordinate2[0] * Math.PI) / 180; - const deltaLat = ((coordinate2[0] - coordinate1[0]) * Math.PI) / 180; - const deltaLon = ((coordinate2[1] - coordinate1[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 bdc19316d491..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 2524658d6ffc..5f36e0bc0edd 100644 --- a/src/components/MenuItem.tsx +++ b/src/components/MenuItem.tsx @@ -20,6 +20,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'; @@ -776,8 +777,8 @@ function MenuItem( {shouldShowSubscriptRightAvatar ? ( ) : ( diff --git a/src/components/MoneyRequestConfirmationList.tsx b/src/components/MoneyRequestConfirmationList.tsx index 743a5b276c98..262a14d183b3 100755 --- a/src/components/MoneyRequestConfirmationList.tsx +++ b/src/components/MoneyRequestConfirmationList.tsx @@ -700,7 +700,7 @@ function MoneyRequestConfirmationList({ if (iouCategory || !shouldShowCategories || enabledCategories.length !== 1 || !isCategoryRequired) { return; } - IOU.setMoneyRequestCategory(transactionID, enabledCategories[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]); @@ -714,7 +714,7 @@ function MoneyRequestConfirmationList({ if (!isTagListRequired || enabledTags.length !== 1 || TransactionUtils.getTag(transaction, index)) { return; } - updatedTagsString = IOUUtils.insertTagIntoTransactionTagsString(updatedTagsString, enabledTags[0] ? enabledTags[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 fa27e48eea4c..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[0].x - previousTouch.value.x); - const velocityY = evt.allTouches[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[0].x, - y: evt.allTouches[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 601b8f7edfed..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[3].type === CONST.ICON_TYPE_WORKSPACE && StyleUtils.getAvatarBorderRadius(size, icons[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 029bd57cd876..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,8 +210,8 @@ function OptionRow({ {!!option.icons?.length && (option.shouldShowSubscript ? ( diff --git a/src/components/OptionsList/BaseOptionsList.tsx b/src/components/OptionsList/BaseOptionsList.tsx index c08f1d697dad..d7c625e7837c 100644 --- a/src/components/OptionsList/BaseOptionsList.tsx +++ b/src/components/OptionsList/BaseOptionsList.tsx @@ -138,13 +138,13 @@ function BaseOptionsList( */ // eslint-disable-next-line @typescript-eslint/naming-convention const getItemLayout = (_data: OptionsListDataWithIndexOffset[] | null, flatDataArrayIndex: number) => { - if (!flattenedData.current[flatDataArrayIndex]) { + if (!flattenedData.current.at(flatDataArrayIndex)) { flattenedData.current = buildFlatSectionArray(); } - const targetItem = flattenedData.current[flatDataArrayIndex]; + const targetItem = flattenedData.current.at(flatDataArrayIndex); return { - length: targetItem.length, - offset: targetItem.offset, + length: targetItem?.length ?? 0, + offset: targetItem?.offset ?? 0, index: flatDataArrayIndex, }; }; diff --git a/src/components/Picker/BasePicker.tsx b/src/components/Picker/BasePicker.tsx index ddd6cd544f3e..3c5fd4dc61d0 100644 --- a/src/components/Picker/BasePicker.tsx +++ b/src/components/Picker/BasePicker.tsx @@ -59,7 +59,10 @@ function BasePicker( // 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[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 3b074bf772e6..05a8305deaa1 100644 --- a/src/components/PopoverMenu.tsx +++ b/src/components/PopoverMenu.tsx @@ -141,7 +141,10 @@ function PopoverMenu({ const [focusedIndex, setFocusedIndex] = useArrowKeyFocusManager({initialFocusedIndex: currentMenuItemsFocusedIndex, maxIndex: currentMenuItems.length - 1, isActive: isVisible}); const selectItem = (index: number) => { - const selectedItem = currentMenuItems[index]; + const selectedItem = currentMenuItems.at(index); + if (!selectedItem) { + return; + } if (selectedItem?.subMenuItems) { setCurrentMenuItems([...selectedItem.subMenuItems]); setEnteredSubMenuIndexes([...enteredSubMenuIndexes, index]); diff --git a/src/components/ProfilingToolMenu/BaseProfilingToolMenu.tsx b/src/components/ProfilingToolMenu/BaseProfilingToolMenu.tsx index d9e106ec3f52..a288396ad204 100644 --- a/src/components/ProfilingToolMenu/BaseProfilingToolMenu.tsx +++ b/src/components/ProfilingToolMenu/BaseProfilingToolMenu.tsx @@ -44,7 +44,7 @@ function formatBytes(bytes: number, decimals = 2) { const i = Math.floor(Math.log(bytes) / Math.log(k)); - return `${parseFloat((bytes / k ** i).toFixed(dm))} ${sizes[i]}`; + return `${parseFloat((bytes / k ** i).toFixed(dm))} ${sizes.at(i)}`; } // WARNING: When changing this name make sure that the "scripts/symbolicate-profile.ts" script is still working! diff --git a/src/components/ReportActionItem/MoneyReportView.tsx b/src/components/ReportActionItem/MoneyReportView.tsx index 6ab2db05c49f..b9303f044005 100644 --- a/src/components/ReportActionItem/MoneyReportView.tsx +++ b/src/components/ReportActionItem/MoneyReportView.tsx @@ -73,7 +73,7 @@ function MoneyReportView({report, policy, isCombinedReport = false, shouldShowTo }, [policy, report]); const enabledReportFields = sortedPolicyReportFields.filter((reportField) => !ReportUtils.isReportFieldDisabled(report, reportField, policy)); - const isOnlyTitleFieldEnabled = enabledReportFields.length === 1 && ReportUtils.isReportFieldOfTypeTitle(enabledReportFields[0]); + const isOnlyTitleFieldEnabled = enabledReportFields.length === 1 && ReportUtils.isReportFieldOfTypeTitle(enabledReportFields.at(0)); const shouldShowReportField = !ReportUtils.isClosedExpenseReportWithNoExpenses(report) && ReportUtils.isPaidGroupPolicyExpenseReport(report) && (!isCombinedReport || !isOnlyTitleFieldEnabled); diff --git a/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx b/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx index f2460cae71ed..2108797ed3fd 100644 --- a/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx +++ b/src/components/ReportActionItem/MoneyRequestPreview/MoneyRequestPreviewContent.tsx @@ -204,9 +204,10 @@ function MoneyRequestPreviewContent({ if (shouldShowHoldMessage) { return `${message} ${CONST.DOT_SEPARATOR} ${translate('violations.hold')}`; } - if (violations?.[0]) { - const violationMessage = ViolationsUtils.getViolationTranslation(violations[0], translate); - const violationsCount = violations.filter((v) => v.type === CONST.VIOLATION_TYPES.VIOLATION).length; + const firstViolation = violations?.at(0); + if (firstViolation) { + const violationMessage = ViolationsUtils.getViolationTranslation(firstViolation, translate); + const violationsCount = violations?.filter((v) => v.type === CONST.VIOLATION_TYPES.VIOLATION).length ?? 0; 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 878fdd8a9c10..efd5b1f99a79 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -304,8 +304,12 @@ function MoneyRequestView({report, shouldShowAnimatedBackground, readonly = fals // Return violations if there are any if (hasViolations(field, data, policyHasDependentTags, tagValue)) { - const violations = getViolationsForField(field, data, policyHasDependentTags, tagValue); - return ViolationsUtils.getViolationTranslation(violations[0], translate); + const violations = getViolationsForField(field, data, policyHasDependentTags, tagValue) as OnyxTypes.TransactionViolation[]; + const firstViolation = violations.at(0); + + if (firstViolation) { + return ViolationsUtils.getViolationTranslation(firstViolation, translate); + } } return ''; @@ -646,7 +650,7 @@ function MoneyRequestView({report, shouldShowAnimatedBackground, readonly = fals {translate('common.billable')} {!!getErrorForField('billable') && ( ({...ReceiptUtils.getThumbnailAndImageURIs(transaction), transaction})); const showRTERViolationMessage = numberOfRequests === 1 && - TransactionUtils.hasPendingUI(allTransactions[0], TransactionUtils.getTransactionViolations(allTransactions[0]?.transactionID ?? '-1', transactionViolations)); + TransactionUtils.hasPendingUI(allTransactions.at(0), TransactionUtils.getTransactionViolations(allTransactions.at(0)?.transactionID ?? '-1', transactionViolations)); - let formattedMerchant = numberOfRequests === 1 ? TransactionUtils.getMerchant(allTransactions[0]) : null; - const formattedDescription = numberOfRequests === 1 ? TransactionUtils.getDescription(allTransactions[0]) : null; + let formattedMerchant = numberOfRequests === 1 ? TransactionUtils.getMerchant(allTransactions.at(0)) : null; + const formattedDescription = numberOfRequests === 1 ? TransactionUtils.getDescription(allTransactions.at(0)) : null; if (TransactionUtils.isPartialMerchant(formattedMerchant ?? '')) { formattedMerchant = null; diff --git a/src/components/ReportActionItem/TripDetailsView.tsx b/src/components/ReportActionItem/TripDetailsView.tsx index 28408731d700..d9576c79b030 100644 --- a/src/components/ReportActionItem/TripDetailsView.tsx +++ b/src/components/ReportActionItem/TripDetailsView.tsx @@ -62,7 +62,7 @@ function ReservationView({reservation}: ReservationViewProps) { const formattedDate = getFormattedDate(); const bottomDescription = useMemo(() => { - const code = `${reservation.confirmations && reservation.confirmations?.length > 0 ? `${reservation.confirmations[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 f5c4a5fad051..f50a9241d275 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[0])} + onPress={() => navigateToAvatarPage(icon)} accessibilityRole={CONST.ROLE.BUTTON} - accessibilityLabel={icons[0].name ?? ''} - disabled={icons[0].source === Expensicons.FallbackAvatar} + accessibilityLabel={icon.name ?? ''} + disabled={icon.source === Expensicons.FallbackAvatar} > ); diff --git a/src/components/SelectionList/BaseSelectionList.tsx b/src/components/SelectionList/BaseSelectionList.tsx index 9ee264bc9bf9..4dd0204551a2 100644 --- a/src/components/SelectionList/BaseSelectionList.tsx +++ b/src/components/SelectionList/BaseSelectionList.tsx @@ -242,7 +242,7 @@ function BaseSelectionList( */ const scrollToIndex = useCallback( (index: number, animated = true) => { - const item = flattenedSections.allOptions[index]; + const item = flattenedSections.allOptions.at(index) ?? 0; if (!listRef.current || !item) { return; @@ -330,7 +330,7 @@ function BaseSelectionList( }; const selectFocusedOption = () => { - const focusedOption = flattenedSections.allOptions[focusedIndex]; + const focusedOption = flattenedSections.allOptions.at(focusedIndex); if (!focusedOption || (focusedOption.isDisabled && !focusedOption.isSelected)) { return; @@ -354,7 +354,7 @@ function BaseSelectionList( * [{header}, {sectionHeader}, {item}, {item}, {sectionHeader}, {item}, {item}, {footer}] */ const getItemLayout = (data: Array>> | null, flatDataArrayIndex: number) => { - const targetItem = flattenedSections.itemLayouts[flatDataArrayIndex]; + const targetItem = flattenedSections.itemLayouts.at(flatDataArrayIndex); if (!targetItem) { return { @@ -621,7 +621,7 @@ function BaseSelectionList( /** Selects row when pressing Enter */ useKeyboardShortcut(CONST.KEYBOARD_SHORTCUTS.ENTER, selectFocusedOption, { captureOnInputs: true, - shouldBubble: !flattenedSections.allOptions[focusedIndex], + shouldBubble: !flattenedSections.allOptions.at(focusedIndex), shouldStopPropagation, isActive: !disableKeyboardShortcuts && !disableEnterShortcut && isFocused, }); @@ -630,7 +630,7 @@ function BaseSelectionList( useKeyboardShortcut( CONST.KEYBOARD_SHORTCUTS.CTRL_ENTER, (e) => { - const focusedOption = flattenedSections.allOptions[focusedIndex]; + const focusedOption = flattenedSections.allOptions.at(focusedIndex); if (onConfirm) { onConfirm(e, focusedOption); return; @@ -639,7 +639,7 @@ function BaseSelectionList( }, { captureOnInputs: true, - shouldBubble: !flattenedSections.allOptions[focusedIndex], + shouldBubble: !flattenedSections.allOptions.at(focusedIndex), isActive: !disableKeyboardShortcuts && isFocused, }, ); diff --git a/src/components/SelectionList/InviteMemberListItem.tsx b/src/components/SelectionList/InviteMemberListItem.tsx index 553adfd23c5c..dec58fb43d9d 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'; @@ -77,8 +78,8 @@ function InviteMemberListItem({ {!!item.icons && (item.shouldShowSubscript ? ( diff --git a/src/components/SelectionList/Search/ReportListItem.tsx b/src/components/SelectionList/Search/ReportListItem.tsx index a0b96547bcd8..f5128dde973d 100644 --- a/src/components/SelectionList/Search/ReportListItem.tsx +++ b/src/components/SelectionList/Search/ReportListItem.tsx @@ -100,7 +100,7 @@ function ReportListItem({ const participantToDisplayName = participantTo?.displayName ?? participantTo?.login ?? ''; if (reportItem.transactions.length === 1) { - const transactionItem = reportItem.transactions[0]; + const transactionItem = reportItem.transactions.at(0); return ( ); diff --git a/src/components/SelectionList/UserListItem.tsx b/src/components/SelectionList/UserListItem.tsx index 104990cf479c..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,8 +101,8 @@ function UserListItem({ {!!item.icons && (item.shouldShowSubscript ? ( diff --git a/src/components/SignInButtons/GetUserLanguage.ts b/src/components/SignInButtons/GetUserLanguage.ts index a1101e92f656..9fda67e1079b 100644 --- a/src/components/SignInButtons/GetUserLanguage.ts +++ b/src/components/SignInButtons/GetUserLanguage.ts @@ -10,7 +10,7 @@ type LocaleCode = ValueOf; const GetUserLanguage = (): LocaleCode => { const userLanguage = navigator.language || navigator.userLanguage; - const languageCode = userLanguage.split('-')[0] as LanguageCode; + const languageCode = userLanguage.split('-').at(0) as LanguageCode; return localeCodes[languageCode] || 'en_US'; }; diff --git a/src/components/TagPicker/index.tsx b/src/components/TagPicker/index.tsx index 8abcdb25aa60..1d72285be9a0 100644 --- a/src/components/TagPicker/index.tsx +++ b/src/components/TagPicker/index.tsx @@ -91,9 +91,9 @@ function TagPicker({selectedTag, tagListName, policyTags, tagListIndex, policyRe [searchValue, enabledTags, selectedOptions, policyRecentlyUsedTagsList], ); - const headerMessage = OptionsListUtils.getHeaderMessageForNonUserList((sections?.[0]?.data?.length ?? 0) > 0, searchValue); + const headerMessage = OptionsListUtils.getHeaderMessageForNonUserList((sections?.at(0)?.data?.length ?? 0) > 0, searchValue); - const selectedOptionKey = sections[0]?.data?.filter((policyTag) => policyTag.searchText === selectedTag)?.[0]?.keyForList; + const selectedOptionKey = sections.at(0)?.data?.filter((policyTag) => policyTag.searchText === selectedTag)?.[0]?.keyForList; return ( 0, searchValue); + const headerMessage = OptionsListUtils.getHeaderMessageForNonUserList((sections.at(0)?.data?.length ?? 0) > 0, searchValue); - const selectedOptionKey = useMemo(() => sections?.[0]?.data?.find((taxRate) => taxRate.searchText === selectedTaxRate)?.keyForList, [sections, selectedTaxRate]); + const selectedOptionKey = useMemo(() => sections?.at(0)?.data?.find((taxRate) => taxRate.searchText === selectedTaxRate)?.keyForList, [sections, selectedTaxRate]); const handleSelectRow = useCallback( (newSelectedOption: OptionsListUtils.TaxRatesOption) => { diff --git a/src/components/ValidateAccountMessage.tsx b/src/components/ValidateAccountMessage.tsx index 277b113b2351..d9810e859bfa 100644 --- a/src/components/ValidateAccountMessage.tsx +++ b/src/components/ValidateAccountMessage.tsx @@ -42,13 +42,13 @@ function ValidateAccountMessage({backTo}: ValidateAccountMessageProps) { { - const loginName = loginNames?.[0]; - const login = loginList?.[loginName] ?? {}; + const loginName = loginNames?.at(0); + const login = loginList?.[loginName ?? ''] ?? {}; if (!login?.validatedDate && !login?.validateCodeSent) { - User.requestContactMethodValidateCode(loginName); + User.requestContactMethodValidateCode(loginName ?? ''); } - Navigation.navigate(ROUTES.SETTINGS_CONTACT_METHOD_DETAILS.getRoute(login?.partnerUserID ?? loginNames?.[0], backTo)); + Navigation.navigate(ROUTES.SETTINGS_CONTACT_METHOD_DETAILS.getRoute(login?.partnerUserID ?? loginNames?.at(0) ?? '', backTo)); }} > {translate('bankAccount.validateAccountError.phrase4')} diff --git a/src/components/WorkspaceMembersSelectionList.tsx b/src/components/WorkspaceMembersSelectionList.tsx index 99f949903a2b..aff095f09839 100644 --- a/src/components/WorkspaceMembersSelectionList.tsx +++ b/src/components/WorkspaceMembersSelectionList.tsx @@ -94,7 +94,7 @@ function WorkspaceMembersSelectionList({policyID, selectedApprover, setApprover} setApprover(approver.login); }; - const headerMessage = useMemo(() => (searchTerm && !sections[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 ( {({hovered}) => ( { diff --git a/src/hooks/useSubStep/index.ts b/src/hooks/useSubStep/index.ts index c89bb5fd5735..eb4a30037ab0 100644 --- a/src/hooks/useSubStep/index.ts +++ b/src/hooks/useSubStep/index.ts @@ -1,3 +1,4 @@ +import type {ComponentType} from 'react'; import {useCallback, useRef, useState} from 'react'; import type {SubStepProps, UseSubStep} from './types'; @@ -59,7 +60,8 @@ export default function useSubStep({bodyContent, on }, [bodyContent]); return { - componentToRender: bodyContent[screenIndex], + // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style + componentToRender: bodyContent.at(screenIndex) as ComponentType, isEditing: isEditing.current, screenIndex, prevScreen, diff --git a/src/hooks/useViolations.ts b/src/hooks/useViolations.ts index f645b03a99c3..01b6ef74e6a8 100644 --- a/src/hooks/useViolations.ts +++ b/src/hooks/useViolations.ts @@ -83,7 +83,11 @@ function useViolations(violations: TransactionViolation[], shouldShowOnlyViolati // someTagLevelsRequired has special logic becase data.errorIndexes is a bit unique in how it denotes the tag list that has the violation // tagListIndex can be 0 so we compare with undefined - if (currentViolations[0]?.name === CONST.VIOLATIONS.SOME_TAG_LEVELS_REQUIRED && data?.tagListIndex !== undefined && Array.isArray(currentViolations[0]?.data?.errorIndexes)) { + if ( + currentViolations.at(0)?.name === CONST.VIOLATIONS.SOME_TAG_LEVELS_REQUIRED && + data?.tagListIndex !== undefined && + Array.isArray(currentViolations.at(0)?.data?.errorIndexes) + ) { return currentViolations .filter((violation) => violation.data?.errorIndexes?.includes(data?.tagListIndex ?? -1)) .map((violation) => ({ @@ -97,12 +101,12 @@ function useViolations(violations: TransactionViolation[], shouldShowOnlyViolati // missingTag has special logic for policies with dependent tags, because only one violation is returned for all tags // when no tags are present, so the tag name isn't set in the violation data. That's why we add it here - if (policyHasDependentTags && currentViolations[0]?.name === CONST.VIOLATIONS.MISSING_TAG && data?.tagListName) { + if (policyHasDependentTags && currentViolations.at(0)?.name === CONST.VIOLATIONS.MISSING_TAG && data?.tagListName) { return [ { - ...currentViolations[0], + ...currentViolations.at(0), data: { - ...currentViolations[0].data, + ...currentViolations.at(0)?.data, tagName: data?.tagListName, }, }, @@ -110,13 +114,13 @@ function useViolations(violations: TransactionViolation[], shouldShowOnlyViolati } // tagOutOfPolicy has special logic because we have to account for multi-level tags and use tagName to find the right tag to put the violation on - if (currentViolations[0]?.name === CONST.VIOLATIONS.TAG_OUT_OF_POLICY && data?.tagListName !== undefined && currentViolations[0]?.data?.tagName) { + if (currentViolations.at(0)?.name === CONST.VIOLATIONS.TAG_OUT_OF_POLICY && data?.tagListName !== undefined && currentViolations.at(0)?.data?.tagName) { return currentViolations.filter((violation) => violation.data?.tagName === data?.tagListName); } // allTagLevelsRequired has special logic because it is returned when one but not all the tags are set, // so we need to return the violation for the tag fields without a tag set - if (currentViolations[0]?.name === CONST.VIOLATIONS.ALL_TAG_LEVELS_REQUIRED && tagValue) { + if (currentViolations.at(0)?.name === CONST.VIOLATIONS.ALL_TAG_LEVELS_REQUIRED && tagValue) { return currentViolations.filter((violation) => violation.data?.tagName === data?.tagListName); } diff --git a/src/languages/en.ts b/src/languages/en.ts index eae87b70ecca..6e5bf7ef703c 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -3953,11 +3953,11 @@ export default { removedFromApprovalWorkflow: ({submittersNames}: {submittersNames: string[]}) => { let joinedNames = ''; if (submittersNames.length === 1) { - joinedNames = submittersNames[0]; + joinedNames = submittersNames.at(0) ?? ''; } else if (submittersNames.length === 2) { joinedNames = submittersNames.join(' and '); } else if (submittersNames.length > 2) { - joinedNames = `${submittersNames.slice(0, submittersNames.length - 1).join(', ')} and ${submittersNames[submittersNames.length - 1]}`; + joinedNames = `${submittersNames.slice(0, submittersNames.length - 1).join(', ')} and ${submittersNames.at(submittersNames.length - 1)}`; } const workflowWord = Str.pluralize('workflow', 'workflows', submittersNames.length); const chatWord = Str.pluralize('chat', 'chats', submittersNames.length); diff --git a/src/languages/es.ts b/src/languages/es.ts index d94db88f2720..b12b5a05e98c 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -4004,11 +4004,11 @@ export default { removedFromApprovalWorkflow: ({submittersNames}: {submittersNames: string[]}) => { let joinedNames = ''; if (submittersNames.length === 1) { - joinedNames = submittersNames[0]; + joinedNames = submittersNames.at(0) ?? ''; } else if (submittersNames.length === 2) { joinedNames = submittersNames.join(' y '); } else if (submittersNames.length > 2) { - joinedNames = `${submittersNames.slice(0, submittersNames.length - 1).join(', ')} y ${submittersNames[submittersNames.length - 1]}`; + joinedNames = `${submittersNames.slice(0, submittersNames.length - 1).join(', ')} y ${submittersNames.at(submittersNames.length - 1)}`; } const workflowWord = Str.pluralize('del flujo', 'de los flujos', submittersNames.length); const chatWord = Str.pluralize('del chat', 'de los chats', submittersNames.length); diff --git a/src/libs/ActiveClientManager/index.ts b/src/libs/ActiveClientManager/index.ts index e93cdb07d084..159e81de4c1d 100644 --- a/src/libs/ActiveClientManager/index.ts +++ b/src/libs/ActiveClientManager/index.ts @@ -62,7 +62,7 @@ const isClientTheLeader: IsClientTheLeader = () => { return true; } - const lastActiveClient = activeClients.length && activeClients[activeClients.length - 1]; + const lastActiveClient = activeClients.length && activeClients.at(activeClients.length - 1); return lastActiveClient === clientID; }; diff --git a/src/libs/CollectionUtils.ts b/src/libs/CollectionUtils.ts index 9d0ff5425da2..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('_')[1]; + return key.split('_').at(1) ?? ''; } export {lastItem, extractCollectionItemID}; diff --git a/src/libs/CurrencyUtils.ts b/src/libs/CurrencyUtils.ts index c3b80797d750..09328763bb35 100644 --- a/src/libs/CurrencyUtils.ts +++ b/src/libs/CurrencyUtils.ts @@ -68,7 +68,7 @@ function isCurrencySymbolLTR(currencyCode: string): boolean { }); // Currency is LTR when the first part is of currency type. - return parts[0].type === 'currency'; + return parts.at(0)?.type === 'currency'; } /** diff --git a/src/libs/DistanceRequestUtils.ts b/src/libs/DistanceRequestUtils.ts index e9a2eaa8027d..3efd711d4258 100644 --- a/src/libs/DistanceRequestUtils.ts +++ b/src/libs/DistanceRequestUtils.ts @@ -84,7 +84,7 @@ function getDefaultMileageRate(policy: OnyxInputOrEntry): MileageRate | } const mileageRates = Object.values(getMileageRates(policy)); - const distanceRate = mileageRates.find((rate) => rate.name === CONST.CUSTOM_UNITS.DEFAULT_RATE) ?? mileageRates[0] ?? {}; + const distanceRate = mileageRates.find((rate) => rate.name === CONST.CUSTOM_UNITS.DEFAULT_RATE) ?? mileageRates.at(0) ?? ({} as MileageRate); return { customUnitRateID: distanceRate.customUnitRateID, diff --git a/src/libs/E2E/tests/linkingTest.e2e.ts b/src/libs/E2E/tests/linkingTest.e2e.ts index 7e6c7ea697ba..c4d580e8c57b 100644 --- a/src/libs/E2E/tests/linkingTest.e2e.ts +++ b/src/libs/E2E/tests/linkingTest.e2e.ts @@ -45,11 +45,11 @@ const test = (config: NativeConfig) => { const subscription = DeviceEventEmitter.addListener('onViewableItemsChanged', (res: ViewableItemResponse) => { console.debug('[E2E] Viewable items retrieved, verifying correct messageā€¦', res); - if (!!res && res?.[0]?.item?.reportActionID === linkedReportActionID) { + if (!!res && res?.at(0)?.item?.reportActionID === linkedReportActionID) { appearMessageResolve(); subscription.remove(); } else { - console.debug(`[E2E] Provided message id '${res?.[0]?.item?.reportActionID}' doesn't match to an expected '${linkedReportActionID}'. Waiting for a next oneā€¦`); + console.debug(`[E2E] Provided message id '${res?.at(0)?.item?.reportActionID}' doesn't match to an expected '${linkedReportActionID}'. Waiting for a next oneā€¦`); } }); diff --git a/src/libs/EmojiTrie.ts b/src/libs/EmojiTrie.ts index e33ba77eab22..eafde585aa74 100644 --- a/src/libs/EmojiTrie.ts +++ b/src/libs/EmojiTrie.ts @@ -123,7 +123,7 @@ const emojiTrie: EmojiTrie = supportedLanguages.reduce((acc, lang) => { const buildEmojisTrie = (locale: Locale) => { Timing.start(CONST.TIMING.TRIE_INITIALIZATION); // Normalize the locale to lowercase and take the first part before any dash - const normalizedLocale = locale.toLowerCase().split('-')[0]; + const normalizedLocale = locale.toLowerCase().split('-').at(0); const localeToUse = supportedLanguages.includes(normalizedLocale as SupportedLanguage) ? (normalizedLocale as SupportedLanguage) : undefined; if (!localeToUse || emojiTrie[localeToUse]) { diff --git a/src/libs/EmojiUtils.ts b/src/libs/EmojiUtils.ts index a8958295bb96..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[preferredSkinToneIndex]; + return types.at(preferredSkinToneIndex) ?? ''; } return code; @@ -462,7 +462,7 @@ const getPreferredSkinToneIndex = (value: OnyxEntry): number => */ const getPreferredEmojiCode = (emoji: Emoji, preferredSkinTone: OnyxEntry): string => { if (emoji.types && typeof preferredSkinTone === 'number') { - const emojiCodeWithSkinTone = emoji.types[preferredSkinTone]; + const emojiCodeWithSkinTone = emoji.types.at(preferredSkinTone); // Note: it can happen that preferredSkinTone has a outdated format, // so it makes sense to check if we actually got a valid emoji code back @@ -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[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/ErrorUtils.ts b/src/libs/ErrorUtils.ts index cf852e533a20..e5b8c38c30a6 100644 --- a/src/libs/ErrorUtils.ts +++ b/src/libs/ErrorUtils.ts @@ -77,7 +77,7 @@ function getLatestErrorMessage(onyxData: O return ''; } - const key = Object.keys(errors).sort().reverse()[0]; + const key = Object.keys(errors).sort().reverse().at(0) ?? ''; return getErrorMessageWithTranslationData(errors[key] ?? ''); } @@ -88,7 +88,7 @@ function getLatestErrorMessageField(onyxDa return {}; } - const key = Object.keys(errors).sort().reverse()[0]; + const key = Object.keys(errors).sort().reverse().at(0) ?? ''; return {key: errors[key]}; } @@ -104,7 +104,7 @@ function getLatestErrorField(onyxData return {}; } - const key = Object.keys(errorsForField).sort().reverse()[0]; + const key = Object.keys(errorsForField).sort().reverse().at(0) ?? ''; return {[key]: getErrorMessageWithTranslationData(errorsForField[key])}; } @@ -115,7 +115,7 @@ function getEarliestErrorField(onyxDa return {}; } - const key = Object.keys(errorsForField).sort()[0]; + const key = Object.keys(errorsForField).sort().at(0) ?? ''; return {[key]: getErrorMessageWithTranslationData(errorsForField[key])}; } diff --git a/src/libs/GooglePlacesUtils.ts b/src/libs/GooglePlacesUtils.ts index 312a0dc61c1f..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[fieldTermIndex].value : ''; + result[fieldToExtract] = fieldTermIndex >= 0 ? addressTerms.at(fieldTermIndex)?.value : ''; }); return result; } diff --git a/src/libs/IOUUtils.ts b/src/libs/IOUUtils.ts index 55ac4ba2ac36..95c12c52889a 100644 --- a/src/libs/IOUUtils.ts +++ b/src/libs/IOUUtils.ts @@ -141,7 +141,7 @@ function insertTagIntoTransactionTagsString(transactionTags: string, tag: string const tagArray = TransactionUtils.getTagArrayFromName(transactionTags); tagArray[tagIndex] = tag; - while (tagArray.length > 0 && !tagArray[tagArray.length - 1]) { + while (tagArray.length > 0 && !tagArray.at(tagArray.length - 1)) { tagArray.pop(); } diff --git a/src/libs/LocaleDigitUtils.ts b/src/libs/LocaleDigitUtils.ts index 0362f4aa963f..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)[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[index]; + return STANDARD_DIGITS.at(index) ?? ''; } /** diff --git a/src/libs/Middleware/Pagination.ts b/src/libs/Middleware/Pagination.ts index ff5f5942674f..2b4d8de310af 100644 --- a/src/libs/Middleware/Pagination.ts +++ b/src/libs/Middleware/Pagination.ts @@ -108,10 +108,11 @@ const Pagination: Middleware = (requestResponse, request) => { // Detect if we are at the start of the list. This will always be the case for the initial request with no cursor. // For previous requests we check that no new data is returned. Ideally the server would return that info. - if ((type === 'initial' && !cursorID) || (type === 'next' && newPage.length === 1 && newPage[0] === cursorID)) { + if ((type === 'initial' && !cursorID) || (type === 'next' && newPage.length === 1 && newPage.at(0) === cursorID)) { newPage.unshift(CONST.PAGINATION_START_ID); } - if (isLastItem(sortedPageItems[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/ModifiedExpenseMessage.ts b/src/libs/ModifiedExpenseMessage.ts index 99f483cf6ef6..20c5f6d7dce9 100644 --- a/src/libs/ModifiedExpenseMessage.ts +++ b/src/libs/ModifiedExpenseMessage.ts @@ -100,11 +100,11 @@ function getForDistanceRequest(newMerchant: string, oldMerchant: string, newAmou if (CONST.REGEX.DISTANCE_MERCHANT.test(newMerchant) && CONST.REGEX.DISTANCE_MERCHANT.test(oldMerchant)) { const oldValues = oldMerchant.split('@'); - const oldDistance = oldValues[0]?.trim() || ''; - const oldRate = oldValues[1]?.trim() || ''; + const oldDistance = oldValues.at(0)?.trim() ?? ''; + const oldRate = oldValues.at(1)?.trim() ?? ''; const newValues = newMerchant.split('@'); - const newDistance = newValues[0]?.trim() || ''; - const newRate = newValues[1]?.trim() || ''; + const newDistance = newValues.at(0)?.trim() ?? ''; + const newRate = newValues.at(1)?.trim() ?? ''; if (oldDistance === newDistance && oldRate !== newRate) { changedField = 'rate'; @@ -235,8 +235,8 @@ function getForReportAction(reportID: string | undefined, reportAction: OnyxEntr sortedTagKeys.forEach((policyTagKey, index) => { const policyTagListName = policyTags[policyTagKey].name || localizedTagListName; - const newTag = splittedTag[index] ?? ''; - const oldTag = splittedOldTag[index] ?? ''; + const newTag = splittedTag.at(index) ?? ''; + const oldTag = splittedOldTag.at(index) ?? ''; if (newTag !== oldTag) { buildMessageFragmentForValue( diff --git a/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/index.tsx b/src/libs/Navigation/AppNavigator/createCustomBottomTabNavigator/index.tsx index 89b83dae816c..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[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/createCustomFullScreenNavigator/CustomFullScreenRouter.tsx b/src/libs/Navigation/AppNavigator/createCustomFullScreenNavigator/CustomFullScreenRouter.tsx index 27e976d9be0c..7dc66d06fd4a 100644 --- a/src/libs/Navigation/AppNavigator/createCustomFullScreenNavigator/CustomFullScreenRouter.tsx +++ b/src/libs/Navigation/AppNavigator/createCustomFullScreenNavigator/CustomFullScreenRouter.tsx @@ -40,7 +40,7 @@ function adaptStateIfNecessary(state: StackState) { if (state.stale === true) { state.routes.push({ name: SCREENS.WORKSPACE.PROFILE, - params: state.routes[0]?.params, + params: state.routes.at(0)?.params, }); } } diff --git a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/index.tsx b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/index.tsx index a37a102ff8a3..009a11875e83 100644 --- a/src/libs/Navigation/AppNavigator/createCustomStackNavigator/index.tsx +++ b/src/libs/Navigation/AppNavigator/createCustomStackNavigator/index.tsx @@ -64,20 +64,24 @@ function ResponsiveStackNavigator(props: ResponsiveStackNavigatorProps) { if (shouldUseNarrowLayout) { const isSearchCentralPane = (route: RouteProp) => getTopmostCentralPaneRoute({routes: [route]} as State)?.name === SCREENS.SEARCH.CENTRAL_PANE; - const lastRoute = routes[routes.length - 1]; - const lastSearchCentralPane = isSearchCentralPane(lastRoute) ? lastRoute : undefined; + const lastRoute = routes.at(routes.length - 1); + // 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. if (lastSearchCentralPane) { - return { - stateToRender: { - ...state, - index: 0, - routes: [filteredRoutes[0]], - }, - searchRoute: lastSearchCentralPane, - }; + const filteredRoute = filteredRoutes.at(0); + if (filteredRoute) { + return { + stateToRender: { + ...state, + index: 0, + routes: [filteredRoute], + }, + searchRoute: lastSearchCentralPane, + }; + } } return { diff --git a/src/libs/Navigation/OnyxTabNavigator.tsx b/src/libs/Navigation/OnyxTabNavigator.tsx index 5c5bbe201ab4..a6a77403f148 100644 --- a/src/libs/Navigation/OnyxTabNavigator.tsx +++ b/src/libs/Navigation/OnyxTabNavigator.tsx @@ -123,8 +123,8 @@ function OnyxTabNavigator({ const state = event.data.state; const index = state.index; const routeNames = state.routeNames; - Tab.setSelectedTab(id, routeNames[index] as SelectedTabRequest); - onTabSelected(routeNames[index] as IOURequestType); + Tab.setSelectedTab(id, routeNames.at(index) as SelectedTabRequest); + onTabSelected(routeNames.at(index) as IOURequestType); }, ...(screenListeners ?? {}), }} diff --git a/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts b/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts index 2c96e5796309..da77fc7be98a 100644 --- a/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts +++ b/src/libs/Navigation/linkingConfig/getAdaptedStateFromPath.ts @@ -124,7 +124,11 @@ 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) { - return getMatchingRootRouteForRHPRoute(findFocusedRoute(stateForBackTo) as NavigationPartialRoute); + const isRHPinState = stateForBackTo.routes.at(0)?.name === NAVIGATORS.RIGHT_MODAL_NAVIGATOR; + + if (isRHPinState) { + return getMatchingRootRouteForRHPRoute(findFocusedRoute(stateForBackTo) as NavigationPartialRoute); + } } // If we know that backTo targets the root route (central pane or full screen) we want to use it. @@ -336,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[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 0679bfa26285..cec00f705127 100644 --- a/src/libs/Navigation/linkingConfig/getMatchingCentralPaneRouteForState.ts +++ b/src/libs/Navigation/linkingConfig/getMatchingCentralPaneRouteForState.ts @@ -53,7 +53,10 @@ function getMatchingCentralPaneRouteForState(state: State, r return; } - const centralPaneName = TAB_TO_CENTRAL_PANE_MAPPING[topmostBottomTabRoute.name][0]; + const centralPaneName = TAB_TO_CENTRAL_PANE_MAPPING[topmostBottomTabRoute.name].at(0); + if (!centralPaneName) { + return; + } if (topmostBottomTabRoute.name === SCREENS.SETTINGS.ROOT) { // When we go back to the settings tab without switching the workspace id, we want to return to the previously opened screen diff --git a/src/libs/Network/SequentialQueue.ts b/src/libs/Network/SequentialQueue.ts index 9442ebda3cf0..2f7dc236ab7a 100644 --- a/src/libs/Network/SequentialQueue.ts +++ b/src/libs/Network/SequentialQueue.ts @@ -80,7 +80,10 @@ function process(): Promise { return Promise.resolve(); } - const requestToProcess = persistedRequests[0]; + 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) diff --git a/src/libs/NextStepUtils.ts b/src/libs/NextStepUtils.ts index 7f13e1297b6f..1eecdb1213ca 100644 --- a/src/libs/NextStepUtils.ts +++ b/src/libs/NextStepUtils.ts @@ -43,8 +43,8 @@ function parseMessage(messages: Message[] | undefined) { let tagType = part.type ?? 'span'; let content = Str.safeEscape(part.text); - const previousPart = messages[index - 1]; - const nextPart = messages[index + 1]; + const previousPart = messages?.at(index - 1); + const nextPart = messages?.at(index + 1); if (currentUserEmail === part.text || part.clickToCopyText === currentUserEmail) { tagType = 'strong'; @@ -73,7 +73,7 @@ function getNextApproverDisplayName(policy: Policy, ownerAccountID: number, subm return ReportUtils.getDisplayNameForParticipant(submitToAccountID); } - const nextApproverEmail = approvalChain.length === 1 ? approvalChain[0] : approvalChain[approvalChain.indexOf(currentUserEmail) + 1]; + const nextApproverEmail = approvalChain.length === 1 ? approvalChain.at(0) : approvalChain[approvalChain.indexOf(currentUserEmail) + 1]; if (!nextApproverEmail) { return ReportUtils.getDisplayNameForParticipant(submitToAccountID); } diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index afedd308371c..63cea4aa81e1 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -297,7 +297,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('_')[1]; + 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; @@ -310,7 +314,12 @@ Onyx.connect({ sortedReportActions = ReportActionUtils.getCombinedReportActions(sortedReportActions, transactionThreadReportID, transactionThreadReportActionsArray, reportID, false); } - lastReportActions[reportID] = sortedReportActions[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. @@ -322,7 +331,11 @@ Onyx.connect({ reportAction.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE && !ReportActionUtils.isResolvedActionTrackExpense(reportAction), ); - lastVisibleReportActions[reportID] = reportActionsForDisplay[0]; + const reportActionForDisplay = reportActionsForDisplay.at(0); + if (!reportActionForDisplay) { + return; + } + lastVisibleReportActions[reportID] = reportActionForDisplay; }); }, }); @@ -548,7 +561,7 @@ function getAlternateText(option: ReportUtils.OptionData, {showChatPreviewLine = return showChatPreviewLine && option.lastMessageText ? option.lastMessageText - : LocalePhoneNumber.formatPhoneNumber(option.participantsList && option.participantsList.length > 0 ? option.participantsList[0].login ?? '' : ''); + : LocalePhoneNumber.formatPhoneNumber(option.participantsList && option.participantsList.length > 0 ? option.participantsList.at(0)?.login ?? '' : ''); } function isSearchStringMatchUserDetails(personalDetail: PersonalDetails, searchValue: string) { @@ -722,7 +735,7 @@ function createOption( const personalDetailMap = getPersonalDetailsForAccountIDs(accountIDs, personalDetails); const personalDetailList = Object.values(personalDetailMap).filter((details): details is PersonalDetails => !!details); - const personalDetail = personalDetailList[0]; + const personalDetail = personalDetailList.at(0); let hasMultipleParticipants = personalDetailList.length > 1; let subtitle; let reportName; @@ -780,12 +793,12 @@ function createOption( result.alternateText = showPersonalDetails && personalDetail?.login ? personalDetail.login : getAlternateText(result, {showChatPreviewLine, forcePolicyNamePreview}); reportName = showPersonalDetails - ? ReportUtils.getDisplayNameForParticipant(accountIDs[0]) || LocalePhoneNumber.formatPhoneNumber(personalDetail?.login ?? '') + ? ReportUtils.getDisplayNameForParticipant(accountIDs.at(0)) || LocalePhoneNumber.formatPhoneNumber(personalDetail?.login ?? '') : ReportUtils.getReportName(report); } else { // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - reportName = ReportUtils.getDisplayNameForParticipant(accountIDs[0]) || LocalePhoneNumber.formatPhoneNumber(personalDetail?.login ?? ''); - result.keyForList = String(accountIDs[0]); + reportName = ReportUtils.getDisplayNameForParticipant(accountIDs.at(0)) || LocalePhoneNumber.formatPhoneNumber(personalDetail?.login ?? ''); + result.keyForList = String(accountIDs.at(0)); result.alternateText = LocalePhoneNumber.formatPhoneNumber(personalDetails?.[accountIDs[0]]?.login ?? ''); } @@ -2368,9 +2381,9 @@ function getFirstKeyForList(data?: Option[] | null) { return ''; } - const firstNonEmptyDataObj = data[0]; + 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 a08868f5dcc1..a1c9078898bf 100644 --- a/src/libs/PaginationUtils.ts +++ b/src/libs/PaginationUtils.ts @@ -46,12 +46,12 @@ function findFirstItem(sortedItems: TResource[], page: string[], getI */ function findLastItem(sortedItems: TResource[], page: string[], getID: (item: TResource) => string): ItemWithIndex | null { for (let i = page.length - 1; i >= 0; i--) { - const id = page[i]; + const id = page.at(i); if (id === CONST.PAGINATION_END_ID) { return {id, index: sortedItems.length - 1}; } const index = sortedItems.findIndex((item) => getID(item) === id); - if (index !== -1) { + if (index !== -1 && id) { return {id, index}; } } @@ -121,10 +121,15 @@ function mergeAndSortContinuousPages(sortedItems: TResource[], pages: return b.lastIndex - a.lastIndex; }); - const result = [sortedPages[0]]; + const result = [sortedPages.at(0)]; for (let i = 1; i < sortedPages.length; i++) { - const page = sortedPages[i]; - const prevPage = result[result.length - 1]; + 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) { @@ -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[index]]; + if (!linkedPage && item) { + return [item]; } - page = linkedPage; + if (linkedPage) { + page = linkedPage; + } } else { - page = pagesWithIndexes[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 af48fd793b43..3d9aed117ca3 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[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[index]; + const accountID = newAccountIDs.at(index) ?? -1; personalDetailsNew[accountID] = { login, accountID, @@ -233,7 +233,7 @@ function getFormattedStreet(street1 = '', street2 = '') { */ function getStreetLines(street = '') { const streets = street.split('\n'); - return [streets[0], streets[1]]; + return [streets.at(0), streets.at(1)]; } /** diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index ddcf1c0298b7..bfac0d6be3d6 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -144,9 +144,11 @@ function getRateDisplayValue(value: number, toLocaleDigit: (arg: string) => stri } if (withDecimals) { - const decimalPart = numValue.toString().split('.')[1]; - const fixedDecimalPoints = decimalPart.length > 2 && !decimalPart.endsWith('0') ? 3 : 2; - return Number(numValue).toFixed(fixedDecimalPoints).toString().replace('.', toLocaleDigit('.')); + const decimalPart = numValue.toString().split('.').at(1); + if (decimalPart) { + const fixedDecimalPoints = decimalPart.length > 2 && !decimalPart.endsWith('0') ? 3 : 2; + return Number(numValue).toFixed(fixedDecimalPoints).toString().replace('.', toLocaleDigit('.')); + } } return numValue.toString().replace('.', toLocaleDigit('.')).substring(0, value.toString().length); @@ -303,10 +305,11 @@ function getTagList(policyTagList: OnyxEntry, tagIndex: number): const tagLists = getTagLists(policyTagList); return ( - tagLists[tagIndex] ?? { + tagLists.at(tagIndex) ?? { name: '', required: false, tags: {}, + orderWeight: 0, } ); } @@ -502,12 +505,12 @@ function getDefaultApprover(policy: OnyxEntry): string { * Returns the accountID to whom the given employeeAccountID submits reports to in the given Policy. */ function getSubmitToAccountID(policy: OnyxEntry, employeeAccountID: number): number { - const employeeLogin = getLoginsByAccountIDs([employeeAccountID])[0]; + const employeeLogin = getLoginsByAccountIDs([employeeAccountID]).at(0) ?? ''; const defaultApprover = getDefaultApprover(policy); // 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])[0]; + return getAccountIDsByLogins([defaultApprover]).at(0) ?? -1; } const employee = policy?.employeeList?.[employeeLogin]; @@ -515,12 +518,12 @@ function getSubmitToAccountID(policy: OnyxEntry, employeeAccountID: numb return -1; } - return getAccountIDsByLogins([employee.submitsTo ?? defaultApprover])[0]; + return getAccountIDsByLogins([employee.submitsTo ?? defaultApprover]).at(0) ?? -1; } function getSubmitToEmail(policy: OnyxEntry, employeeAccountID: number): string { const submitToAccountID = getSubmitToAccountID(policy, employeeAccountID); - return getLoginsByAccountIDs([submitToAccountID])[0] ?? ''; + return getLoginsByAccountIDs([submitToAccountID]).at(0) ?? ''; } /** @@ -549,7 +552,7 @@ function getForwardsToAccount(policy: OnyxEntry, employeeEmail: string, */ function getReimburserAccountID(policy: OnyxEntry): number { const reimburserEmail = policy?.achAccount?.reimburser ?? policy?.owner ?? ''; - return getAccountIDsByLogins([reimburserEmail])[0]; + return getAccountIDsByLogins([reimburserEmail]).at(0) ?? -1; } function getPersonalPolicy() { diff --git a/src/libs/Queue/Queue.ts b/src/libs/Queue/Queue.ts index 8b2011e64371..26586d8014a1 100644 --- a/src/libs/Queue/Queue.ts +++ b/src/libs/Queue/Queue.ts @@ -59,7 +59,7 @@ function createQueue(processItem: (item: T) => Promise): Queue { // Function to get the item at the front of the queue without removing it function peek(): T | undefined { - return elements.length > 0 ? elements[0] : undefined; + return elements.length > 0 ? elements.at(0) : undefined; } // Function to get the number of items in the queue diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts index f584f694edd0..dbeadc28e0a1 100644 --- a/src/libs/ReportActionsUtils.ts +++ b/src/libs/ReportActionsUtils.ts @@ -130,13 +130,13 @@ function isDeletedAction(reportAction: OnyxInputOrEntry): boolean { @@ -519,8 +519,8 @@ 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[i].pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE) { - return reportActions[i]; + if (isNetworkOffline || reportActions.at(i)?.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE) { + return reportActions.at(i); } } @@ -729,8 +729,12 @@ function replaceBaseURLInPolicyChangeLogAction(reportAction: ReportAction): Repo return updatedReportAction; } - if (Array.isArray(updatedReportAction.message) && updatedReportAction.message[0]) { - updatedReportAction.message[0].html = getReportActionHtml(reportAction)?.replace('%baseURL', environmentURL); + if (Array.isArray(updatedReportAction.message)) { + const message = updatedReportAction.message.at(0); + + if (message) { + message.html = getReportActionHtml(reportAction)?.replace('%baseURL', environmentURL); + } } return updatedReportAction; @@ -750,7 +754,7 @@ function getLastVisibleAction(reportID: string, actionsToMerge: Record !isDeletedAction(action) || (action?.childVisibleActionCount ?? 0) > 0 || isOffline); - return sortedFilterReportActions.length > 1 ? sortedFilterReportActions[sortedFilterReportActions.length - 2].reportActionID : ''; + return sortedFilterReportActions.length > 1 ? sortedFilterReportActions.at(sortedFilterReportActions.length - 2)?.reportActionID ?? '-1' : ''; } /** @@ -1041,7 +1045,7 @@ function getOneTransactionThreadReportID(reportID: string, reportActions: OnyxEn return; } - const singleAction = iouRequestActions[0]; + const singleAction = iouRequestActions.at(0); const originalMessage = getOriginalMessage(singleAction); // If there's only one IOU request action associated with the report but it's been deleted, then we don't consider this a oneTransaction report @@ -1051,7 +1055,7 @@ function getOneTransactionThreadReportID(reportID: string, reportActions: OnyxEn } // Ensure we have a childReportID associated with the IOU report action - return singleAction.childReportID; + return singleAction?.childReportID; } /** @@ -1437,7 +1441,7 @@ function isCurrentActionUnread(report: OnyxEntry, reportAction: ReportAc if (currentActionIndex === -1) { return false; } - const prevReportAction = sortedReportActions[currentActionIndex - 1]; + const prevReportAction = sortedReportActions.at(currentActionIndex - 1); return isReportActionUnread(reportAction, lastReadTime) && (!prevReportAction || !isReportActionUnread(prevReportAction, lastReadTime)); } @@ -1588,7 +1592,7 @@ function getExportIntegrationActionFragments(reportAction: OnyxEntry) { function getCardIssuedMessage(reportAction: OnyxEntry, shouldRenderHTML = false) { const assigneeAccountID = (reportAction?.originalMessage as IssueNewCardOriginalMessage)?.assigneeAccountID; - const assigneeDetails = PersonalDetailsUtils.getPersonalDetailsByIDs([assigneeAccountID], currentUserAccountID ?? -1)[0]; + const assigneeDetails = PersonalDetailsUtils.getPersonalDetailsByIDs([assigneeAccountID], currentUserAccountID ?? -1).at(0); - const assignee = shouldRenderHTML ? `` : assigneeDetails?.firstName ?? assigneeDetails.login ?? ''; + const assignee = shouldRenderHTML ? `` : assigneeDetails?.firstName ?? assigneeDetails?.login ?? ''; const link = shouldRenderHTML ? `${Localize.translateLocal('cardPage.expensifyCard')}` : Localize.translateLocal('cardPage.expensifyCard'); diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index c18b6c41b420..aa6412a2bdfd 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -1238,7 +1238,7 @@ function shouldDisableDetailPage(report: OnyxEntry): boolean { const participantAccountIDs = Object.keys(report?.participants ?? {}) .map(Number) .filter((accountID) => accountID !== currentUserAccountID); - return isOptimisticPersonalDetail(participantAccountIDs[0]); + return isOptimisticPersonalDetail(participantAccountIDs.at(0) ?? -1); } return false; } @@ -1345,7 +1345,7 @@ function findLastAccessedReport(ignoreDomainRooms: boolean, openOnAdminRoom = fa // Filter out the systemChat report from the reports list, as we don't want to drop the user into that report over Concierge when they first log in reportsValues = reportsValues.filter((report) => !isSystemChat(report)) ?? []; if (reportsValues.length === 1) { - return reportsValues[0]; + return reportsValues.at(0); } return adminReport ?? reportsValues.find((report) => !isConciergeChatReport(report)); @@ -1622,7 +1622,7 @@ function isPayAtEndExpenseReport(reportID: string, transactions: Transaction[] | return false; } - return TransactionUtils.isPayAtEndExpense(transactions?.[0] ?? TransactionUtils.getAllReportTransactions(reportID)[0]); + return TransactionUtils.isPayAtEndExpense(transactions?.[0] ?? TransactionUtils.getAllReportTransactions(reportID).at(0)); } /** @@ -2182,7 +2182,7 @@ function getGroupChatName(participants?: SelectedParticipant[], shouldApplyLimit .join(', '); } - return Localize.translateLocal('groupChat.defaultReportName', {displayName: getDisplayNameForParticipant(participantAccountIDs[0], false)}); + return Localize.translateLocal('groupChat.defaultReportName', {displayName: getDisplayNameForParticipant(participantAccountIDs.at(0), false)}); } function getParticipants(reportID: string) { @@ -3565,7 +3565,7 @@ function getAdminRoomInvitedParticipants(parentReportAction: OnyxEntry 1 ? participants.join(` ${Localize.translateLocal('common.and')} `) : participants[0]; + const users = participants.length > 1 ? participants.join(` ${Localize.translateLocal('common.and')} `) : participants.at(0); if (!users) { return parentReportActionMessage; } @@ -3633,7 +3633,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[index])); + accountIDs.forEach((id, index) => (accountIDToName[id] = logins.at(index) ?? '')); const textMessage = Str.removeSMSDomain(Parser.htmlToText(html, {reportIDToName, accountIDToName})); parsedReportActionMessageCache[key] = textMessage; @@ -3872,7 +3872,7 @@ function getPayeeName(report: OnyxEntry): string | undefined { if (participantsWithoutCurrentUser.length === 0) { return undefined; } - return getDisplayNameForParticipant(participantsWithoutCurrentUser[0], true); + return getDisplayNameForParticipant(participantsWithoutCurrentUser.at(0), true); } /** @@ -3948,7 +3948,7 @@ function navigateToDetailsPage(report: OnyxEntry) { const participantAccountID = getParticipantsAccountIDsForDisplay(report); if (isSelfDMReport || isOneOnOneChatReport) { - Navigation.navigate(ROUTES.PROFILE.getRoute(participantAccountID[0])); + Navigation.navigate(ROUTES.PROFILE.getRoute(participantAccountID.at(0))); return; } @@ -3965,7 +3965,7 @@ function goBackToDetailsPage(report: OnyxEntry) { const participantAccountID = getParticipantsAccountIDsForDisplay(report); if (isOneOnOneChatReport) { - Navigation.navigate(ROUTES.PROFILE.getRoute(participantAccountID[0])); + Navigation.navigate(ROUTES.PROFILE.getRoute(participantAccountID.at(0))); return; } @@ -4001,7 +4001,7 @@ function goBackFromPrivateNotes(report: OnyxEntry, session: OnyxEntry, sortedAndFiltered const newMarkerIndex = lodashFindLastIndex(sortedAndFilteredReportActions, (reportAction) => (reportAction.created ?? '') > (report?.lastReadTime ?? '')); - return 'reportActionID' in sortedAndFilteredReportActions[newMarkerIndex] ? sortedAndFilteredReportActions[newMarkerIndex].reportActionID : ''; + return 'reportActionID' in (sortedAndFilteredReportActions?.at(newMarkerIndex) ?? {}) ? sortedAndFilteredReportActions.at(newMarkerIndex)?.reportActionID ?? '' : ''; } /** @@ -6337,8 +6340,8 @@ function parseReportRouteParams(route: string): ReportRouteParams { const pathSegments = parsingRoute.split('/'); - const reportIDSegment = pathSegments[1]; - const hasRouteReportActionID = !Number.isNaN(Number(pathSegments[2])); + const reportIDSegment = pathSegments.at(1); + const hasRouteReportActionID = !Number.isNaN(Number(reportIDSegment)); // Check for "undefined" or any other unwanted string values if (!reportIDSegment || reportIDSegment === 'undefined') { @@ -6633,7 +6636,7 @@ function canLeaveRoom(report: OnyxEntry, isPolicyEmployee: boolean): boo } function isCurrentUserTheOnlyParticipant(participantAccountIDs?: number[]): boolean { - return !!(participantAccountIDs?.length === 1 && participantAccountIDs?.[0] === currentUserAccountID); + return !!(participantAccountIDs?.length === 1 && participantAccountIDs?.at(0) === currentUserAccountID); } /** @@ -7375,13 +7378,13 @@ function getOptimisticDataForParentReportAction(reportID: string, lastVisibleAct const totalAncestor = ancestors.reportIDs.length; return Array.from(Array(totalAncestor), (_, index) => { - const ancestorReport = getReportOrDraftReport(ancestors.reportIDs[index]); + const ancestorReport = getReportOrDraftReport(ancestors.reportIDs.at(index)); if (!ancestorReport || isEmptyObject(ancestorReport)) { return null; } - const ancestorReportAction = ReportActionsUtils.getReportAction(ancestorReport.reportID, ancestors.reportActionsIDs[index]); + const ancestorReportAction = ReportActionsUtils.getReportAction(ancestorReport.reportID, ancestors.reportActionsIDs.at(index) ?? ''); if (!ancestorReportAction || isEmptyObject(ancestorReportAction)) { return null; diff --git a/src/libs/SearchUtils.ts b/src/libs/SearchUtils.ts index 9760ff80ca19..2f7c88eb7401 100644 --- a/src/libs/SearchUtils.ts +++ b/src/libs/SearchUtils.ts @@ -362,7 +362,7 @@ function getSortedTransactionData(data: TransactionListItemType[], sortBy?: Sear } function getReportNewestTransactionDate(report: ReportListItemType) { - return report.transactions?.reduce((max, curr) => (curr.modifiedCreated ?? curr.created > max.created ? curr : max), report.transactions[0])?.created; + return report.transactions?.reduce((max, curr) => (curr.modifiedCreated ?? curr.created > (max?.created ?? '') ? curr : max), report.transactions.at(0))?.created; } function getSortedReportData(data: ReportListItemType[]) { @@ -735,7 +735,7 @@ function buildFilterString(filterName: string, queryFilters: QueryFilter[], deli let filterValueString = ''; queryFilters.forEach((queryFilter, index) => { // If the previous queryFilter has the same operator (this rule applies only to eq and neq operators) then append the current value - if ((queryFilter.operator === 'eq' && queryFilters[index - 1]?.operator === 'eq') || (queryFilter.operator === 'neq' && queryFilters[index - 1]?.operator === 'neq')) { + if ((queryFilter.operator === 'eq' && queryFilters?.at(index - 1)?.operator === 'eq') || (queryFilter.operator === 'neq' && queryFilters.at(index - 1)?.operator === 'neq')) { filterValueString += `${delimiter}${sanitizeString(queryFilter.value.toString())}`; } else { filterValueString += ` ${filterName}${operatorToSignMap[queryFilter.operator]}${sanitizeString(queryFilter.value.toString())}`; @@ -766,7 +766,7 @@ function getSearchHeaderTitle( .filter(([, taxRateKeys]) => taxRateKeys.some((taxID) => taxRateIDs.includes(taxID))) .map(([taxRate]) => taxRate); displayQueryFilters = taxRateNames.map((taxRate) => ({ - operator: queryFilter[0].operator, + operator: queryFilter.at(0)?.operator ?? 'and', value: taxRate, })); } else { diff --git a/src/libs/SelectionScraper/index.ts b/src/libs/SelectionScraper/index.ts index 88726aa633b6..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[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 d056f111695e..2f8b4f7b1cbc 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[reportActionsForDisplay.length - 1]; + const reportAction = reportActionsForDisplay.at(reportActionsForDisplay.length - 1); + if (!reportAction) { + return; + } + visibleReportActionItems[reportID] = reportAction; }, }); @@ -300,7 +304,7 @@ function getOptionData({ const visibleParticipantAccountIDs = ReportUtils.getParticipantsAccountIDsForDisplay(report, true); const participantPersonalDetailList = Object.values(OptionsListUtils.getPersonalDetailsForAccountIDs(participantAccountIDs, personalDetails)); - const personalDetail = participantPersonalDetailList[0] ?? {}; + const personalDetail = participantPersonalDetailList.at(0) ?? ({} as PersonalDetails); const hasErrors = Object.keys(result.allReportErrors ?? {}).length !== 0; result.isThread = ReportUtils.isChatThread(report); @@ -476,9 +480,9 @@ function getOptionData({ } if (!hasMultipleParticipants) { - result.accountID = personalDetail?.accountID; - result.login = personalDetail?.login; - result.phoneNumber = personalDetail?.phoneNumber; + result.accountID = personalDetail?.accountID ?? -1; + result.login = personalDetail?.login ?? ''; + result.phoneNumber = personalDetail?.phoneNumber ?? ''; } const reportName = ReportUtils.getReportName(report, policy, undefined, undefined, invoiceReceiverPolicy); @@ -487,7 +491,7 @@ function getOptionData({ result.subtitle = subtitle; result.participantsList = participantPersonalDetailList; - result.icons = ReportUtils.getIcons(report, personalDetails, personalDetail?.avatar, personalDetail?.login, personalDetail?.accountID, policy, invoiceReceiverPolicy); + result.icons = ReportUtils.getIcons(report, personalDetails, personalDetail?.avatar, personalDetail?.login, personalDetail?.accountID ?? -1, policy, invoiceReceiverPolicy); result.displayNamesWithTooltips = displayNamesWithTooltips; if (status) { diff --git a/src/libs/StringUtils.ts b/src/libs/StringUtils.ts index 10730150489c..b3fcd247284e 100644 --- a/src/libs/StringUtils.ts +++ b/src/libs/StringUtils.ts @@ -94,7 +94,7 @@ function lineBreaksToSpaces(text = '') { function getFirstLine(text = '') { // Split the input string by newline characters and return the first element of the resulting array const lines = text.split('\n'); - return lines[0]; + return lines.at(0); } export default {sanitizeString, isEmptyString, removeInvisibleCharacters, normalizeAccents, normalizeCRLF, lineBreaksToSpaces, getFirstLine}; diff --git a/src/libs/TransactionUtils/index.ts b/src/libs/TransactionUtils/index.ts index 63c2f9aa9862..9feefed1d7cc 100644 --- a/src/libs/TransactionUtils/index.ts +++ b/src/libs/TransactionUtils/index.ts @@ -492,7 +492,7 @@ function getTagArrayFromName(tagName: string): string[] { function getTag(transaction: OnyxInputOrEntry, tagIndex?: number): string { if (tagIndex !== undefined) { const tagsArray = getTagArrayFromName(transaction?.tag ?? ''); - return tagsArray[tagIndex] ?? ''; + return tagsArray.at(tagIndex) ?? ''; } return transaction?.tag ?? ''; @@ -646,7 +646,7 @@ function getValidWaypoints(waypoints: WaypointCollection | undefined, reArrangeI let waypointIndex = -1; return waypointValues.reduce((acc, currentWaypoint, index) => { - const previousWaypoint = waypointValues[lastWaypointIndex]; + const previousWaypoint = waypointValues.at(lastWaypointIndex); // Check if the waypoint has a valid address if (!waypointHasValidAddress(currentWaypoint)) { @@ -987,7 +987,7 @@ function compareDuplicateTransactionFields(transactionID: string): {keep: Partia // Helper function to check if all fields are equal for a given key function areAllFieldsEqual(items: Array>, keyExtractor: (item: OnyxEntry) => string) { - const firstTransaction = transactions[0]; + const firstTransaction = transactions.at(0); return items.every((item) => keyExtractor(item) === keyExtractor(firstTransaction)); } @@ -1002,7 +1002,7 @@ function compareDuplicateTransactionFields(transactionID: string): {keep: Partia for (const fieldName in fieldsToCompare) { if (Object.prototype.hasOwnProperty.call(fieldsToCompare, fieldName)) { const keys = fieldsToCompare[fieldName]; - const firstTransaction = transactions[0]; + const firstTransaction = transactions.at(0); const isFirstTransactionCommentEmptyObject = typeof firstTransaction?.comment === 'object' && firstTransaction?.comment?.comment === ''; if (fieldName === 'description') { diff --git a/src/libs/Violations/ViolationsUtils.ts b/src/libs/Violations/ViolationsUtils.ts index adbc05460220..d5c93d382637 100644 --- a/src/libs/Violations/ViolationsUtils.ts +++ b/src/libs/Violations/ViolationsUtils.ts @@ -19,7 +19,7 @@ function getTagViolationsForSingleLevelTags( policyTagList: PolicyTagLists, ): TransactionViolation[] { const policyTagKeys = Object.keys(policyTagList); - const policyTagListName = policyTagKeys[0]; + const policyTagListName = policyTagKeys.at(0) ?? ''; const policyTags = policyTagList[policyTagListName]?.tags; const hasTagOutOfPolicyViolation = transactionViolations.some((violation) => violation.name === CONST.VIOLATIONS.TAG_OUT_OF_POLICY); const hasMissingTagViolation = transactionViolations.some((violation) => violation.name === CONST.VIOLATIONS.MISSING_TAG); @@ -93,8 +93,8 @@ function getTagViolationForIndependentTags(policyTagList: PolicyTagLists, transa const errorIndexes = []; for (let i = 0; i < policyTagKeys.length; i++) { const isTagRequired = policyTagList[policyTagKeys[i]].required ?? true; - const isTagSelected = !!selectedTags[i]; - if (isTagRequired && (!isTagSelected || (selectedTags.length === 1 && selectedTags[0] === ''))) { + const isTagSelected = !!selectedTags.at(i); + if (isTagRequired && (!isTagSelected || (selectedTags.length === 1 && selectedTags.at(0) === ''))) { errorIndexes.push(i); } } @@ -109,7 +109,7 @@ function getTagViolationForIndependentTags(policyTagList: PolicyTagLists, transa } else { let hasInvalidTag = false; for (let i = 0; i < policyTagKeys.length; i++) { - const selectedTag = selectedTags[i]; + const selectedTag = selectedTags.at(i); const tags = policyTagList[policyTagKeys[i]].tags; const isTagInPolicy = Object.values(tags).some((tag) => tag.name === selectedTag && !!tag.enabled); if (!isTagInPolicy && selectedTag) { @@ -117,7 +117,7 @@ function getTagViolationForIndependentTags(policyTagList: PolicyTagLists, transa name: CONST.VIOLATIONS.TAG_OUT_OF_POLICY, type: CONST.VIOLATION_TYPES.VIOLATION, data: { - tagName: policyTagKeys[i], + tagName: policyTagKeys.at(i), }, }); hasInvalidTag = true; diff --git a/src/libs/WorkflowUtils.ts b/src/libs/WorkflowUtils.ts index 7d936bff0b3b..67655e0f472b 100644 --- a/src/libs/WorkflowUtils.ts +++ b/src/libs/WorkflowUtils.ts @@ -157,7 +157,7 @@ function convertPolicyEmployeesToApprovalWorkflows({employees, defaultApprover, return 1; } - return (a.approvers[0]?.displayName ?? '-1').localeCompare(b.approvers[0]?.displayName ?? '-1'); + return (a.approvers.at(0)?.displayName ?? '-1').localeCompare(b.approvers.at(0)?.displayName ?? '-1'); }); // Add a default workflow if one doesn't exist (no employees submit to the default approver) diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index edee4a7fca6f..79c469841fad 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -3874,7 +3874,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[0].reportID; + const existingChatReportID = existingSplitChatReportID || (participants.at(0)?.reportID ?? '-1'); // Check if the report is available locally if we do have one let existingSplitChatReport = existingChatReportID ? ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${existingChatReportID}`] : null; @@ -5152,7 +5152,7 @@ function createDistanceRequest( chatType: splitData.chatType, }; } else { - const participant = participants[0] ?? {}; + const participant = participants.at(0) ?? {}; const { iouReport, chatReport, @@ -5265,8 +5265,8 @@ function editRegularMoneyRequest( '', false, ); - updatedMoneyRequestReport.lastMessageText = ReportActionsUtils.getTextFromHtml(lastMessage[0].html); - updatedMoneyRequestReport.lastMessageHtml = lastMessage[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); @@ -5639,9 +5639,12 @@ function prepareToCleanUpMoneyRequest(transactionID: string, reportAction: OnyxT }); if (ReportActionsUtils.getReportActionMessage(updatedReportPreviewAction)) { - if (Array.isArray(updatedReportPreviewAction?.message) && updatedReportPreviewAction.message?.[0]) { - updatedReportPreviewAction.message[0].text = messageText; - updatedReportPreviewAction.message[0].deleted = shouldDeleteIOUReport ? DateUtils.getDBTime() : ''; + if (Array.isArray(updatedReportPreviewAction?.message)) { + const message = updatedReportPreviewAction.message.at(0); + if (message) { + message.text = messageText; + message.deleted = shouldDeleteIOUReport ? DateUtils.getDBTime() : ''; + } } else if (!Array.isArray(updatedReportPreviewAction.message) && updatedReportPreviewAction.message) { updatedReportPreviewAction.message.text = messageText; updatedReportPreviewAction.message.deleted = shouldDeleteIOUReport ? DateUtils.getDBTime() : ''; @@ -6443,7 +6446,7 @@ function getReportFromHoldRequestsOnyxData( failureData: OnyxUpdate[]; } { const {holdReportActions, holdTransactions} = getHoldReportActionsAndTransactions(iouReport.reportID); - const firstHoldTransaction = holdTransactions[0]; + const firstHoldTransaction = holdTransactions.at(0); const newParentReportActionID = rand64(); const optimisticExpenseReport = ReportUtils.buildOptimisticExpenseReport( @@ -7010,7 +7013,7 @@ function isLastApprover(approvalChain: string[]): boolean { if (approvalChain.length === 0) { return true; } - return approvalChain[approvalChain.length - 1] === currentUserEmail; + return approvalChain.at(approvalChain.length - 1) === currentUserEmail; } function approveMoneyRequest(expenseReport: OnyxEntry, full?: boolean) { @@ -7517,7 +7520,7 @@ function completePaymentOnboarding(paymentSelected: ValueOf>>>((val, reportAction) => { + const firstMessage = Array.isArray(reportAction.message) ? reportAction.message.at(0) : null; // eslint-disable-next-line no-param-reassign val[reportAction.reportActionID] = { originalMessage: { deleted: deletedTime, }, - ...(Array.isArray(reportAction.message) && - !!reportAction.message[0] && { - message: [ - { - ...reportAction.message[0], - deleted: deletedTime, - }, - ...reportAction.message.slice(1), - ], - }), + ...(firstMessage && { + message: [ + { + ...firstMessage, + deleted: deletedTime, + }, + ...(Array.isArray(reportAction.message) ? reportAction.message.slice(1) : []), + ], + }), ...(!Array.isArray(reportAction.message) && { message: { deleted: deletedTime, diff --git a/src/libs/actions/ImportSpreadsheet.ts b/src/libs/actions/ImportSpreadsheet.ts index 2f3edeb3b1cb..9a597cf99bf9 100644 --- a/src/libs/actions/ImportSpreadsheet.ts +++ b/src/libs/actions/ImportSpreadsheet.ts @@ -3,15 +3,16 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; function setSpreadsheetData(data: string[][]): Promise { - if (!Array.isArray(data) || !Array.isArray(data[0])) { + if (!Array.isArray(data) || !Array.isArray(data.at(0))) { return Promise.reject(new Error('Invalid data format')); } - const transposedData = data[0].map((_, colIndex) => data.map((row) => row[colIndex])); - const columnNames: Record = data[0].reduce((acc: Record, _, colIndex) => { - acc[colIndex] = CONST.CSV_IMPORT_COLUMNS.IGNORE; - return acc; - }, {}); + const transposedData = data.at(0)?.map((_, colIndex) => data.map((row) => row.at(colIndex) ?? '')); + const columnNames: Record = + data.at(0)?.reduce((acc: Record, _, colIndex) => { + acc[colIndex] = CONST.CSV_IMPORT_COLUMNS.IGNORE; + return acc; + }, {}) ?? {}; return Onyx.merge(ONYXKEYS.IMPORTED_SPREADSHEET, {data: transposedData, columns: columnNames}); } diff --git a/src/libs/actions/OnyxUpdateManager/utils/index.ts b/src/libs/actions/OnyxUpdateManager/utils/index.ts index bb5dd599480e..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[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/Category.ts b/src/libs/actions/Policy/Category.ts index c342fe6eedb6..9fdc0b665faa 100644 --- a/src/libs/actions/Policy/Category.ts +++ b/src/libs/actions/Policy/Category.ts @@ -1212,7 +1212,10 @@ function setPolicyCategoryApprover(policyID: string, categoryName: string, appro newApprover = ''; } else { const indexToUpdate = updatedApprovalRules.indexOf(existingCategoryApproverRule); - updatedApprovalRules[indexToUpdate].approver = approver; + const approvalRule = updatedApprovalRules.at(indexToUpdate); + if (approvalRule) { + approvalRule.approver = approver; + } } const onyxData: OnyxData = { @@ -1274,7 +1277,11 @@ function setPolicyCategoryTax(policyID: string, categoryName: string, taxID: str }); } else { const indexToUpdate = updatedExpenseRules.indexOf(existingCategoryExpenseRule); - updatedExpenseRules[indexToUpdate].tax.field_id_TAX.externalID = taxID; + const expenseRule = updatedExpenseRules.at(indexToUpdate); + + if (expenseRule) { + expenseRule.tax.field_id_TAX.externalID = taxID; + } } const onyxData: OnyxData = { diff --git a/src/libs/actions/Policy/DistanceRate.ts b/src/libs/actions/Policy/DistanceRate.ts index b81b180cca52..f446d2da6b52 100644 --- a/src/libs/actions/Policy/DistanceRate.ts +++ b/src/libs/actions/Policy/DistanceRate.ts @@ -128,12 +128,12 @@ function enablePolicyDistanceRates(policyID: string, enabled: boolean) { if (!enabled) { const policy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]; - const customUnitID = Object.keys(policy?.customUnits ?? {})[0]; + const customUnitID = Object.keys(policy?.customUnits ?? {}).at(0) ?? ''; const customUnit = customUnitID ? policy?.customUnits?.[customUnitID] : undefined; const rateEntries = Object.entries(customUnit?.rates ?? {}); // find the rate to be enabled after disabling the distance rate feature - const rateEntryToBeEnabled = rateEntries[0]; + const rateEntryToBeEnabled = rateEntries.at(0); onyxData.optimisticData?.push({ onyxMethod: Onyx.METHOD.MERGE, @@ -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/Member.ts b/src/libs/actions/Policy/Member.ts index eae625388f33..32df4160b8c0 100644 --- a/src/libs/actions/Policy/Member.ts +++ b/src/libs/actions/Policy/Member.ts @@ -430,7 +430,7 @@ function removeMembers(accountIDs: number[], policyID: string) { optimisticClosedReportActions.forEach((reportAction, index) => { failureData.push({ onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${workspaceChats?.[index]?.reportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${workspaceChats?.at(index)?.reportID}`, value: {[reportAction.reportActionID]: null}, }); }); @@ -519,7 +519,7 @@ function requestWorkspaceOwnerChange(policyID: string) { const changeOwnerErrors = Object.keys(policy?.errorFields?.changeOwner ?? {}); if (changeOwnerErrors && changeOwnerErrors.length > 0) { - const currentError = changeOwnerErrors[0]; + const currentError = changeOwnerErrors.at(0); if (currentError === CONST.POLICY.OWNERSHIP_ERRORS.AMOUNT_OWED) { ownershipChecks.shouldClearOutstandingBalance = true; } diff --git a/src/libs/actions/Policy/Policy.ts b/src/libs/actions/Policy/Policy.ts index b8c9014ecdcc..411c29660632 100644 --- a/src/libs/actions/Policy/Policy.ts +++ b/src/libs/actions/Policy/Policy.ts @@ -231,7 +231,7 @@ function getPolicy(policyID: string | undefined): OnyxEntry { function getPrimaryPolicy(activePolicyID: OnyxEntry, currentUserLogin: string | undefined): Policy | undefined { const activeAdminWorkspaces = PolicyUtils.getActiveAdminWorkspaces(allPolicies, currentUserLogin); const primaryPolicy: Policy | null | undefined = activeAdminWorkspaces.find((policy) => policy.id === activePolicyID); - return primaryPolicy ?? activeAdminWorkspaces[0]; + return primaryPolicy ?? activeAdminWorkspaces.at(0); } /** Check if the policy has invoicing company details */ @@ -1201,7 +1201,7 @@ function updateGeneralSettings(policyID: string, name: string, currencyValue?: s (request) => request.data?.policyID === policyID && request.command === WRITE_COMMANDS.CREATE_WORKSPACE && request.data?.policyName !== name, ); - const createWorkspaceRequest = persistedRequests[createWorkspaceRequestChangedIndex]; + const createWorkspaceRequest = persistedRequests.at(createWorkspaceRequestChangedIndex); if (createWorkspaceRequest) { const workspaceRequest: Request = { ...createWorkspaceRequest, @@ -1471,13 +1471,13 @@ function generateDefaultWorkspaceName(email = ''): string { if (!emailParts || emailParts.length !== 2) { return defaultWorkspaceName; } - const username = emailParts[0]; - const domain = emailParts[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('.')[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())) { @@ -2455,7 +2455,7 @@ function createWorkspaceFromIOUPayment(iouReport: OnyxEntry): WorkspaceF onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${announceChatReportID}`, value: { - [Object.keys(announceChatData)[0]]: { + [Object.keys(announceChatData).at(0) ?? '']: { pendingAction: null, }, }, @@ -2474,7 +2474,7 @@ function createWorkspaceFromIOUPayment(iouReport: OnyxEntry): WorkspaceF onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${adminsChatReportID}`, value: { - [Object.keys(adminsChatData)[0]]: { + [Object.keys(adminsChatData).at(0) ?? '']: { pendingAction: null, }, }, @@ -2493,7 +2493,7 @@ function createWorkspaceFromIOUPayment(iouReport: OnyxEntry): WorkspaceF onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${workspaceChatReportID}`, value: { - [Object.keys(workspaceChatData)[0]]: { + [Object.keys(workspaceChatData).at(0) ?? '']: { pendingAction: null, }, }, diff --git a/src/libs/actions/Policy/ReportField.ts b/src/libs/actions/Policy/ReportField.ts index 9a56c719fd73..2ee1707c21d1 100644 --- a/src/libs/actions/Policy/ReportField.ts +++ b/src/libs/actions/Policy/ReportField.ts @@ -383,7 +383,7 @@ function updateReportFieldListValueEnabled(policyID: string, reportFieldID: stri valueIndexes.forEach((valueIndex) => { updatedReportField.disabledOptions[valueIndex] = !enabled; - const shouldResetDefaultValue = !enabled && reportField.defaultValue === reportField.values[valueIndex]; + const shouldResetDefaultValue = !enabled && reportField.defaultValue === reportField.values.at(valueIndex); if (shouldResetDefaultValue) { updatedReportField.defaultValue = ''; @@ -460,7 +460,7 @@ function removeReportFieldListValue(policyID: string, reportFieldID: string, val valueIndexes .sort((a, b) => b - a) .forEach((valueIndex) => { - const shouldResetDefaultValue = reportField.defaultValue === reportField.values[valueIndex]; + const shouldResetDefaultValue = reportField.defaultValue === reportField.values.at(valueIndex); if (shouldResetDefaultValue) { updatedReportField.defaultValue = ''; diff --git a/src/libs/actions/Policy/Tag.ts b/src/libs/actions/Policy/Tag.ts index d6f67e496b92..d940b0eedeff 100644 --- a/src/libs/actions/Policy/Tag.ts +++ b/src/libs/actions/Policy/Tag.ts @@ -25,6 +25,7 @@ import * as PolicyUtils from '@libs/PolicyUtils'; import {navigateWhenEnableFeature} from '@libs/PolicyUtils'; import * as ReportUtils from '@libs/ReportUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; +import type {PolicyTagList} from '@pages/workspace/tags/types'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Policy, PolicyTag, PolicyTagLists, PolicyTags, RecentlyUsedTags, Report} from '@src/types/onyx'; @@ -121,7 +122,7 @@ function buildOptimisticPolicyRecentlyUsedTags(policyID?: string, transactionTag return; } - const tagListKey = policyTagKeys[index]; + const tagListKey = policyTagKeys.at(index) ?? ''; newOptimisticPolicyRecentlyUsedTags[tagListKey] = [...new Set([tag, ...(policyRecentlyUsedTags[tagListKey] ?? [])])]; }); @@ -157,7 +158,7 @@ function updateImportSpreadsheetData(tagsLength: number): OnyxData { } function createPolicyTag(policyID: string, tagName: string) { - const policyTag = PolicyUtils.getTagLists(allPolicyTags?.[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`] ?? {})?.[0] ?? {}; + const policyTag = PolicyUtils.getTagLists(allPolicyTags?.[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`] ?? {})?.at(0) ?? ({} as PolicyTagList); const newTagName = PolicyUtils.escapeTagName(tagName); const onyxData: OnyxData = { @@ -234,7 +235,11 @@ function importPolicyTags(policyID: string, tags: PolicyTag[]) { } function setWorkspaceTagEnabled(policyID: string, tagsToUpdate: Record, tagListIndex: number) { - const policyTag = PolicyUtils.getTagLists(allPolicyTags?.[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`] ?? {})?.[tagListIndex] ?? {}; + const policyTag = PolicyUtils.getTagLists(allPolicyTags?.[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`] ?? {})?.at(tagListIndex); + + if (!policyTag) { + return; + } const optimisticPolicyTagsData = { ...Object.keys(tagsToUpdate).reduce((acc, key) => { @@ -242,8 +247,9 @@ function setWorkspaceTagEnabled(policyID: string, tagsToUpdate: Record = {}; const participantAccountIDs = PersonalDetailsUtils.getAccountIDsByLogins(participantLoginList); participantLoginList.forEach((login, index) => { - const accountID = participantAccountIDs[index]; + const accountID = participantAccountIDs.at(index) ?? -1; const isOptimisticAccount = !allPersonalDetails?.[accountID]; if (!isOptimisticAccount) { @@ -2072,7 +2075,7 @@ function updateDescription(reportID: string, previousValue: string, newValue: st pendingFields: {description: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}, lastActorAccountID: currentUserAccountID, lastVisibleActionCreated: optimisticDescriptionUpdatedReportAction.created, - lastMessageText: (optimisticDescriptionUpdatedReportAction?.message as Message[])?.[0]?.text, + lastMessageText: (optimisticDescriptionUpdatedReportAction?.message as Message[])?.at(0)?.text, }, }, { diff --git a/src/libs/actions/Task.ts b/src/libs/actions/Task.ts index 41ccfee1786b..745b4b3b01d1 100644 --- a/src/libs/actions/Task.ts +++ b/src/libs/actions/Task.ts @@ -896,7 +896,7 @@ function getShareDestination(reportID: string, reports: OnyxCollection a.localeCompare(b))[0]; + const firstTaxID = Object.keys(policyTaxRates ?? {}) + .sort((a, b) => a.localeCompare(b)) + .at(0); if (!policyTaxRates) { console.debug('Policy or tax rates not found'); diff --git a/src/libs/actions/TeachersUnite.ts b/src/libs/actions/TeachersUnite.ts index ba67e579e95a..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)[0]]: { + [Object.keys(expenseChatData).at(0) ?? '']: { pendingAction: null, }, }, diff --git a/src/libs/actions/Transaction.ts b/src/libs/actions/Transaction.ts index e19251b62ce8..3c1e02751727 100644 --- a/src/libs/actions/Transaction.ts +++ b/src/libs/actions/Transaction.ts @@ -143,7 +143,7 @@ function removeWaypoint(transaction: OnyxEntry, currentIndex: strin return Promise.resolve(); } - const isRemovedWaypointEmpty = removed.length > 0 && !TransactionUtils.waypointHasValidAddress(removed[0] ?? {}); + const isRemovedWaypointEmpty = removed.length > 0 && !TransactionUtils.waypointHasValidAddress(removed.at(0) ?? {}); // When there are only two waypoints we are adding empty waypoint back if (totalWaypoints === 2 && (index === 0 || index === totalWaypoints - 1)) { @@ -311,7 +311,7 @@ function dismissDuplicateTransactionViolation(transactionIDs: string[], dissmiss onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${action?.childReportID ?? '-1'}`, value: { - [optimisticDissmidedViolationReportActions[index].reportActionID]: optimisticDissmidedViolationReportActions[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[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[index].reportActionID]: { + [optimisticDissmidedViolationReportActions.at(index)?.reportActionID ?? '']: { pendingAction: null, }, }, diff --git a/src/libs/actions/User.ts b/src/libs/actions/User.ts index 7ef7d6b94c9e..9ea29506accc 100644 --- a/src/libs/actions/User.ts +++ b/src/libs/actions/User.ts @@ -775,7 +775,7 @@ function playSoundForMessageType(pushJSON: OnyxServerUpdate[]) { const reportActionsOnly = pushJSON.filter((update) => update.key?.includes('reportActions_')); // "reportActions_5134363522480668" -> "5134363522480668" const reportID = reportActionsOnly - .map((value) => value.key.split('_')[1]) + .map((value) => value.key.split('_').at(1)) .find((reportKey) => reportKey === Navigation.getTopmostReportId() && Visibility.isVisible() && Visibility.hasFocus()); if (!reportID) { diff --git a/src/libs/actions/Welcome/OnboardingFlow.ts b/src/libs/actions/Welcome/OnboardingFlow.ts index 4e780090299d..4c275dfdd062 100644 --- a/src/libs/actions/Welcome/OnboardingFlow.ts +++ b/src/libs/actions/Welcome/OnboardingFlow.ts @@ -46,7 +46,7 @@ function adaptOnboardingRouteState() { const rootState = navigationRef.getRootState(); const adaptedState = rootState; const lastRouteIndex = (adaptedState?.routes?.length ?? 0) - 1; - const onBoardingModalNavigatorState = adaptedState?.routes[lastRouteIndex]?.state; + const onBoardingModalNavigatorState = adaptedState?.routes.at(lastRouteIndex)?.state; if (!onBoardingModalNavigatorState || onBoardingModalNavigatorState?.routes?.length > 1) { return; } @@ -80,7 +80,11 @@ function adaptOnboardingRouteState() { } as Readonly>; } - adaptedState.routes[lastRouteIndex].state = adaptedOnboardingModalNavigatorState; + const route = adaptedState.routes.at(lastRouteIndex); + + if (route) { + route.state = adaptedOnboardingModalNavigatorState; + } navigationRef.resetRoot(adaptedState); } diff --git a/src/libs/actions/Workflow.ts b/src/libs/actions/Workflow.ts index 5e4b0408f155..9731b62838f5 100644 --- a/src/libs/actions/Workflow.ts +++ b/src/libs/actions/Workflow.ts @@ -122,7 +122,7 @@ function updateApprovalWorkflow(policyID: string, approvalWorkflow: ApprovalWork } const previousDefaultApprover = policy.approver ?? policy.owner; - const newDefaultApprover = approvalWorkflow.isDefault ? approvalWorkflow.approvers[0].email : undefined; + const newDefaultApprover = approvalWorkflow.isDefault ? approvalWorkflow.approvers.at(0)?.email : undefined; const previousEmployeeList = Object.fromEntries(Object.entries(policy.employeeList ?? {}).map(([key, value]) => [key, {...value, pendingAction: null}])); const updatedEmployees = convertApprovalWorkflowToPolicyEmployees({ previousEmployeeList, diff --git a/src/libs/fileDownload/FileUtils.ts b/src/libs/fileDownload/FileUtils.ts index 05f29390ca14..35919c29d81b 100644 --- a/src/libs/fileDownload/FileUtils.ts +++ b/src/libs/fileDownload/FileUtils.ts @@ -225,10 +225,10 @@ const readFileAsync: ReadFileAsync = (path, fileName, onSuccess, onFailure = () */ function base64ToFile(base64: string, filename: string): File { // Decode the base64 string - const byteString = atob(base64.split(',')[1]); + const byteString = atob(base64.split(',').at(1) ?? ''); // Get the mime type from the base64 string - const mimeString = base64.split(',')[0].split(':')[1].split(';')[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/fileDownload/index.desktop.ts b/src/libs/fileDownload/index.desktop.ts index 6a601a4af249..accd4905e53c 100644 --- a/src/libs/fileDownload/index.desktop.ts +++ b/src/libs/fileDownload/index.desktop.ts @@ -25,7 +25,7 @@ const fileDownload: FileDownload = (url, fileName, successMessage, shouldOpenExt }, CONST.DOWNLOADS_TIMEOUT); const handleDownloadStatus = (...args: unknown[]) => { - const arg = Array.isArray(args) ? args[0] : null; + const arg = Array.isArray(args) ? args.at(0) : null; const eventUrl = arg && typeof arg === 'object' && 'url' in arg ? arg.url : null; if (eventUrl === url) { diff --git a/src/libs/mapChildrenFlat.ts b/src/libs/mapChildrenFlat.ts index 73009a3340d4..34527f4775c1 100644 --- a/src/libs/mapChildrenFlat.ts +++ b/src/libs/mapChildrenFlat.ts @@ -22,7 +22,7 @@ const mapChildrenFlat = (element: C, fn: (child: C, index: number) => T) = const mappedChildren = React.Children.map(element, fn); if (Array.isArray(mappedChildren) && mappedChildren.length === 1) { - return mappedChildren[0]; + return mappedChildren.at(0); } return mappedChildren; diff --git a/src/libs/memoize/cache/ArrayCache.ts b/src/libs/memoize/cache/ArrayCache.ts index 058efefdb1aa..209250e47675 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[i][0], key)) { + const cacheItem = cache.at(i)?.at(0); + if (cacheItem && keyComparator(cacheItem as K, key)) { return i; } } diff --git a/src/pages/Debug/DebugDetails.tsx b/src/pages/Debug/DebugDetails.tsx index 28bcb4e0df8b..bf4e575f19af 100644 --- a/src/pages/Debug/DebugDetails.tsx +++ b/src/pages/Debug/DebugDetails.tsx @@ -74,7 +74,7 @@ function DebugDetails({data, onSave, onDelete, validate}: DebugDetailsProps) { ) // eslint-disable-next-line @typescript-eslint/no-unsafe-argument .map(([key, value]) => [key, DebugUtils.onyxDataToString(value)]) - .sort((a, b) => a[0].localeCompare(b[0])), + .sort((a, b) => (a.at(0) ?? '').localeCompare(b.at(0) ?? '')), [data], ); const dateTimeFields = useMemo(() => Object.entries(data ?? {}).filter(([key]) => DETAILS_DATETIME_FIELDS.includes(key as DetailsDatetimeFieldsKeys)) as Array<[string, string]>, [data]); diff --git a/src/pages/Debug/DebugDetailsConstantPickerPage.tsx b/src/pages/Debug/DebugDetailsConstantPickerPage.tsx index 7dff4f2fe2ff..a98ef9963542 100644 --- a/src/pages/Debug/DebugDetailsConstantPickerPage.tsx +++ b/src/pages/Debug/DebugDetailsConstantPickerPage.tsx @@ -64,7 +64,7 @@ function DebugDetailsConstantPickerPage({ Navigation.navigate(appendParam(backTo, fieldName, value)); } }; - const selectedOptionKey = useMemo(() => sections.filter((option) => option.searchText === fieldValue)[0]?.keyForList, [sections, fieldValue]); + const selectedOptionKey = useMemo(() => sections.filter((option) => option.searchText === fieldValue).at(0)?.keyForList, [sections, fieldValue]); return ( diff --git a/src/pages/EditReportFieldDropdown.tsx b/src/pages/EditReportFieldDropdown.tsx index ba6828eca782..a46a6db6f8a8 100644 --- a/src/pages/EditReportFieldDropdown.tsx +++ b/src/pages/EditReportFieldDropdown.tsx @@ -102,7 +102,7 @@ function EditReportFieldDropdownPage({onSubmit, fieldKey, fieldValue, fieldOptio return [policyReportFieldOptions, header]; }, [recentlyUsedOptions, debouncedSearchValue, fieldValue, fieldOptions]); - const selectedOptionKey = useMemo(() => (sections?.[0]?.data ?? []).filter((option) => option.searchText === fieldValue)?.[0]?.keyForList, [sections, fieldValue]); + const selectedOptionKey = useMemo(() => (sections?.[0]?.data ?? []).filter((option) => option.searchText === fieldValue)?.at(0)?.keyForList, [sections, fieldValue]); return ( !policyField.disabledOptions[index])} + fieldOptions={policyField.values.filter((_value: string, index: number) => !policyField.disabledOptions.at(index))} onSubmit={handleReportFieldChange} /> )} diff --git a/src/pages/EnablePayments/IdologyQuestions.tsx b/src/pages/EnablePayments/IdologyQuestions.tsx index b9b0ac4eca34..602754bef556 100644 --- a/src/pages/EnablePayments/IdologyQuestions.tsx +++ b/src/pages/EnablePayments/IdologyQuestions.tsx @@ -44,7 +44,7 @@ function IdologyQuestions({questions, idNumber}: IdologyQuestionsProps) { const [shouldHideSkipAnswer, setShouldHideSkipAnswer] = useState(false); const [userAnswers, setUserAnswers] = useState([]); - const currentQuestion = questions[currentQuestionIndex] || {}; + const currentQuestion = questions.at(currentQuestionIndex) ?? ({} as WalletAdditionalQuestionDetails); const possibleAnswers: Choice[] = currentQuestion.answer .map((answer) => { if (shouldHideSkipAnswer && answer === SKIP_QUESTION_TEXT) { @@ -70,7 +70,7 @@ function IdologyQuestions({questions, idNumber}: IdologyQuestionsProps) { * Show next question or send all answers for Idology verifications when we've answered enough */ const submitAnswers = () => { - if (!userAnswers[currentQuestionIndex]) { + if (!userAnswers.at(currentQuestionIndex)) { return; } // Get the number of questions that were skipped by the user. @@ -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[i].type, answer: SKIP_QUESTION_TEXT}; + tempAnswers[i] = {question: questions.at(i)?.type ?? '', answer: SKIP_QUESTION_TEXT}; } } @@ -123,7 +123,7 @@ function IdologyQuestions({questions, idNumber}: IdologyQuestionsProps) { { diff --git a/src/pages/NewChatPage.tsx b/src/pages/NewChatPage.tsx index 15263441220e..1478b5a1dab9 100755 --- a/src/pages/NewChatPage.tsx +++ b/src/pages/NewChatPage.tsx @@ -215,7 +215,7 @@ function NewChatPage({isGroupChat}: NewChatPageProps) { if (option?.login) { login = option.login; } else if (selectedOptions.length === 1) { - login = selectedOptions[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 d0049dfe5865..6dd1dcfa7116 100644 --- a/src/pages/ReportDetailsPage.tsx +++ b/src/pages/ReportDetailsPage.tsx @@ -510,8 +510,8 @@ function ReportDetailsPage({policies, report}: ReportDetailsPageProps) { if (isGroupChat && !isThread) { return ( (title ? Expensicons.Filters : popoverMenuItems[activeItemIndex]?.icon ?? Expensicons.Receipt), [activeItemIndex, popoverMenuItems, title]); - const menuTitle = useMemo(() => title ?? popoverMenuItems[activeItemIndex]?.text, [activeItemIndex, popoverMenuItems, title]); + const menuIconAndTitle = useMemo(() => { + if (title) { + return { + icon: Expensicons.Filters, + title, + }; + } + + const item = popoverMenuItems.at(activeItemIndex); + return { + icon: item?.icon ?? Expensicons.Receipt, + title: item?.text, + }; + }, [activeItemIndex, popoverMenuItems, title]); + + const menuIcon = menuIconAndTitle.icon; + const menuTitle = menuIconAndTitle.title; + const titleViewStyles = useMemo(() => (title ? {...styles.flex1, ...styles.justifyContentCenter} : {}), [title, styles]); const savedSearchItems = savedSearchesMenuItems.map((item) => ({ @@ -139,7 +155,7 @@ function SearchTypeMenuNarrow({typeMenuItems, activeItemIndex, queryJSON, title, {shouldShowSubscript ? ( ) : ( diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx index b8b551a345ca..c256e9e51232 100644 --- a/src/pages/home/ReportScreen.tsx +++ b/src/pages/home/ReportScreen.tsx @@ -687,7 +687,7 @@ function ReportScreen({route, currentReportID = '', navigation}: ReportScreenPro // After creating the task report then navigating to task detail we don't have any report actions and the last read time is empty so We need to update the initial last read time when opening the task report detail. Report.readNewestAction(report?.reportID ?? ''); }, [report]); - const mostRecentReportAction = reportActions[0]; + const mostRecentReportAction = reportActions.at(0); const shouldShowMostRecentReportAction = !!mostRecentReportAction && !ReportActionsUtils.isActionOfType(mostRecentReportAction, CONST.REPORT.ACTIONS.TYPE.CREATED) && diff --git a/src/pages/home/report/ReportActionCompose/SuggestionEmoji.tsx b/src/pages/home/report/ReportActionCompose/SuggestionEmoji.tsx index 3051bfb1689e..c29eb44942b2 100644 --- a/src/pages/home/report/ReportActionCompose/SuggestionEmoji.tsx +++ b/src/pages/home/report/ReportActionCompose/SuggestionEmoji.tsx @@ -83,8 +83,8 @@ function SuggestionEmoji( const insertSelectedEmoji = useCallback( (highlightedEmojiIndexInner: number) => { const commentBeforeColon = value.slice(0, suggestionValues.colonIndex); - const emojiObject = suggestionValues.suggestedEmojis[highlightedEmojiIndexInner]; - const emojiCode = emojiObject.types?.[preferredSkinTone] ? emojiObject.types[preferredSkinTone] : emojiObject.code; + const emojiObject = suggestionValues.suggestedEmojis.at(highlightedEmojiIndexInner); + 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 8d37a1915cbd..54840d24a2cc 100644 --- a/src/pages/home/report/ReportActionCompose/SuggestionMention.tsx +++ b/src/pages/home/report/ReportActionCompose/SuggestionMention.tsx @@ -171,7 +171,7 @@ function SuggestionMention( } // Otherwise, the emails must be of the same private domain, so we should remove the domain part - return displayText.split('@')[0]; + return displayText.split('@').at(0); }, [currentUserPersonalDetails.login], ); @@ -193,7 +193,10 @@ function SuggestionMention( const insertSelectedMention = useCallback( (highlightedMentionIndexInner: number) => { const commentBeforeAtSign = value.slice(0, suggestionValues.atSignIndex); - const mentionObject = suggestionValues.suggestedMentions[highlightedMentionIndexInner]; + 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); @@ -357,7 +360,7 @@ function SuggestionMention( const leftString = newValue.substring(afterLastBreakLineIndex, selectionEnd); const words = leftString.split(CONST.REGEX.SPACE_OR_EMOJI); const lastWord: string = words.at(-1) ?? ''; - const secondToLastWord = words[words.length - 3]; + const secondToLastWord = words.at(words.length - 3); let atSignIndex: number | undefined; let suggestionWord = ''; diff --git a/src/pages/home/report/ReportActionItemSingle.tsx b/src/pages/home/report/ReportActionItemSingle.tsx index 0e58296d39f2..c65eceed469e 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[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 6828e10e7e3b..340624247dee 100644 --- a/src/pages/home/report/ReportActionsList.tsx +++ b/src/pages/home/report/ReportActionsList.tsx @@ -221,7 +221,7 @@ function ReportActionsList({ */ const unreadMarkerReportActionID = useMemo(() => { const shouldDisplayNewMarker = (reportAction: OnyxTypes.ReportAction, index: number): boolean => { - const nextMessage = sortedVisibleReportActions[index + 1]; + const nextMessage = sortedVisibleReportActions.at(index + 1); const isCurrentMessageUnread = isMessageUnread(reportAction, unreadMarkerTime); const isNextMessageRead = !nextMessage || !isMessageUnread(nextMessage, unreadMarkerTime); let shouldDisplay = isCurrentMessageUnread && isNextMessageRead && !ReportActionsUtils.shouldHideNewMarker(reportAction); @@ -238,7 +238,13 @@ 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[index]; + const reportAction = sortedVisibleReportActions.at(index); + + if (!reportAction) { + // eslint-disable-next-line no-continue + continue; + } + if (shouldDisplayNewMarker(reportAction, index)) { return reportAction.reportActionID; } @@ -277,7 +283,7 @@ function ReportActionsList({ return; } - const mostRecentReportActionCreated = sortedVisibleReportActions[0]?.created ?? ''; + const mostRecentReportActionCreated = sortedVisibleReportActions.at(0)?.created ?? ''; if (mostRecentReportActionCreated === unreadMarkerTime) { return; } @@ -288,14 +294,14 @@ function ReportActionsList({ // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps }, [sortedVisibleReportActions]); - const lastActionIndex = sortedVisibleReportActions[0]?.reportActionID; + const lastActionIndex = sortedVisibleReportActions.at(0)?.reportActionID; const reportActionSize = useRef(sortedVisibleReportActions.length); - const hasNewestReportAction = sortedVisibleReportActions[0]?.created === report.lastVisibleActionCreated; + const hasNewestReportAction = sortedVisibleReportActions.at(0)?.created === report.lastVisibleActionCreated; const hasNewestReportActionRef = useRef(hasNewestReportAction); hasNewestReportActionRef.current = hasNewestReportAction; const previousLastIndex = useRef(lastActionIndex); - const isLastPendingActionIsDelete = sortedReportActions?.[0]?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE; + const isLastPendingActionIsDelete = sortedReportActions?.at(0)?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE; const [isFloatingMessageCounterVisible, setIsFloatingMessageCounterVisible] = useState(false); @@ -455,7 +461,7 @@ function ReportActionsList({ const firstVisibleReportActionID = useMemo(() => ReportActionsUtils.getFirstVisibleReportActionID(sortedReportActions, isOffline), [sortedReportActions, isOffline]); const shouldUseThreadDividerLine = useMemo(() => { - const topReport = sortedVisibleReportActions.length > 0 ? sortedVisibleReportActions[sortedVisibleReportActions.length - 1] : null; + const topReport = sortedVisibleReportActions.length > 0 ? sortedVisibleReportActions.at(sortedVisibleReportActions.length - 1) : null; if (topReport && topReport.actionName !== CONST.REPORT.ACTIONS.TYPE.CREATED) { return false; @@ -479,7 +485,7 @@ function ReportActionsList({ if (!isVisible || !isFocused) { if (!lastMessageTime.current) { - lastMessageTime.current = sortedVisibleReportActions[0]?.created ?? ''; + lastMessageTime.current = sortedVisibleReportActions.at(0)?.created ?? ''; } return; } diff --git a/src/pages/home/report/ReportActionsView.tsx b/src/pages/home/report/ReportActionsView.tsx index 1460942931fc..452e5bf539b9 100755 --- a/src/pages/home/report/ReportActionsView.tsx +++ b/src/pages/home/report/ReportActionsView.tsx @@ -159,9 +159,9 @@ function ReportActionsView({ } const actions = [...allReportActions]; - const lastAction = allReportActions[allReportActions.length - 1]; + 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); @@ -192,7 +192,7 @@ function ReportActionsView({ false, false, false, - DateUtils.subtractMillisecondsFromDateTime(actions[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); @@ -283,10 +283,10 @@ function ReportActionsView({ ); const hasMoreCached = reportActions.length < combinedReportActions.length; - const newestReportAction = useMemo(() => reportActions?.[0], [reportActions]); + const newestReportAction = useMemo(() => reportActions?.at(0), [reportActions]); const mostRecentIOUReportActionID = useMemo(() => ReportActionsUtils.getMostRecentIOURequestActionID(reportActions), [reportActions]); const hasCachedActionOnFirstRender = useInitialValue(() => reportActions.length > 0); - const hasNewestReportAction = reportActions[0]?.created === report.lastVisibleActionCreated || reportActions[0]?.created === transactionThreadReport?.lastVisibleActionCreated; + const hasNewestReportAction = reportActions.at(0)?.created === report.lastVisibleActionCreated || reportActions.at(0)?.created === transactionThreadReport?.lastVisibleActionCreated; const oldestReportAction = useMemo(() => reportActions?.at(-1), [reportActions]); const hasCreatedAction = oldestReportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.CREATED; @@ -321,7 +321,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; @@ -393,7 +395,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; } @@ -401,7 +403,7 @@ function ReportActionsView({ didLoadNewerChats.current = true; if ((reportActionID && indexOfLinkedAction > -1) || !reportActionID) { - handleReportActionPagination({firstReportActionID: newestReportAction?.reportActionID}); + handleReportActionPagination({firstReportActionID: newestReportAction?.reportActionID ?? '-1'}); } }, [ diff --git a/src/pages/home/report/ReportTypingIndicator.tsx b/src/pages/home/report/ReportTypingIndicator.tsx index 3ff8f2b0eb8e..7a1c90ded34a 100755 --- a/src/pages/home/report/ReportTypingIndicator.tsx +++ b/src/pages/home/report/ReportTypingIndicator.tsx @@ -26,7 +26,7 @@ function ReportTypingIndicator({userTypingStatuses}: ReportTypingIndicatorProps) const styles = useThemeStyles(); const usersTyping = useMemo(() => Object.keys(userTypingStatuses ?? {}).filter((loginOrAccountID) => userTypingStatuses?.[loginOrAccountID]), [userTypingStatuses]); - const firstUserTyping = usersTyping[0]; + const firstUserTyping = usersTyping.at(0); const isUserTypingADisplayName = Number.isNaN(Number(firstUserTyping)); diff --git a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx index 4444f519dbcd..ce69d2a67171 100644 --- a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx +++ b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx @@ -214,7 +214,7 @@ function FloatingActionButtonAndPopover( return ''; } if (quickAction?.action === CONST.QUICK_ACTIONS.SEND_MONEY && quickActionAvatars.length > 0) { - const name: string = ReportUtils.getDisplayNameForParticipant(+(quickActionAvatars[0]?.id ?? -1), true) ?? ''; + const name: string = ReportUtils.getDisplayNameForParticipant(+(quickActionAvatars.at(0)?.id ?? -1), true) ?? ''; return translate('quickAction.paySomeone', name); } const titleKey = getQuickActionTitle(quickAction?.action ?? ('' as QuickActionName)); @@ -228,7 +228,7 @@ function FloatingActionButtonAndPopover( if (quickActionAvatars.length === 0) { return false; } - const displayName = personalDetails?.[quickActionAvatars[0]?.id ?? -1]?.firstName ?? ''; + const displayName = personalDetails?.[quickActionAvatars.at(0)?.id ?? -1]?.firstName ?? ''; return quickAction?.action === CONST.QUICK_ACTIONS.SEND_MONEY && displayName.length === 0; }, [personalDetails, quickActionReport, quickAction?.action, quickActionAvatars]); diff --git a/src/pages/iou/SplitBillDetailsPage.tsx b/src/pages/iou/SplitBillDetailsPage.tsx index efefc8ccc9ea..96ae7bb69b60 100644 --- a/src/pages/iou/SplitBillDetailsPage.tsx +++ b/src/pages/iou/SplitBillDetailsPage.tsx @@ -72,7 +72,7 @@ function SplitBillDetailsPage({personalDetails, report, route, reportActions, tr let participants: Array; if (ReportUtils.isPolicyExpenseChat(report)) { participants = [ - OptionsListUtils.getParticipantsOption({accountID: participantAccountIDs[0], selected: true, reportID: ''}, personalDetails), + OptionsListUtils.getParticipantsOption({accountID: participantAccountIDs.at(0), selected: true, reportID: ''}, personalDetails), OptionsListUtils.getPolicyExpenseReportOption({...report, selected: true, reportID}), ]; } else { diff --git a/src/pages/iou/request/step/IOURequestStepAmount.tsx b/src/pages/iou/request/step/IOURequestStepAmount.tsx index a03c40e8c1f3..fb7a5144ec39 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[0]); + IOU.sendMoneyWithWallet(report, backendAmount, currency, '', currentUserPersonalDetails.accountID, participants.at(0) ?? {}); return; } - IOU.sendMoneyElsewhere(report, backendAmount, currency, '', currentUserPersonalDetails.accountID, participants[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({ CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT, currentUserPersonalDetails.login, currentUserPersonalDetails.accountID, - participants[0], + participants.at(0) ?? {}, '', {}, ); @@ -245,7 +245,7 @@ function IOURequestStepAmount({ CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT, currentUserPersonalDetails.login, currentUserPersonalDetails.accountID, - participants[0], + participants.at(0) ?? {}, '', ); return; diff --git a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx index 6c1457abef62..15dbecace7e7 100644 --- a/src/pages/iou/request/step/IOURequestStepConfirmation.tsx +++ b/src/pages/iou/request/step/IOURequestStepConfirmation.tsx @@ -245,6 +245,11 @@ function IOURequestStepConfirmation({ return; } + const participant = selectedParticipants.at(0); + if (!participant) { + return; + } + IOU.requestMoney( report, transaction.amount, @@ -253,7 +258,7 @@ function IOURequestStepConfirmation({ transaction.merchant, currentUserPersonalDetails.login, currentUserPersonalDetails.accountID, - selectedParticipants[0], + participant, trimmedComment, receiptObj, transaction.category, @@ -279,6 +284,10 @@ function IOURequestStepConfirmation({ if (!report || !transaction) { return; } + const participant = selectedParticipants.at(0); + if (!participant) { + return; + } IOU.trackExpense( report, transaction.amount, @@ -287,7 +296,7 @@ function IOURequestStepConfirmation({ transaction.merchant, currentUserPersonalDetails.login, currentUserPersonalDetails.accountID, - selectedParticipants[0], + participant, trimmedComment, receiptObj, transaction.category, @@ -552,7 +561,7 @@ function IOURequestStepConfirmation({ (paymentMethod: PaymentMethodType | undefined) => { const currency = transaction?.currency; const trimmedComment = transaction?.comment?.comment?.trim() ?? ''; - const participant = participants?.[0]; + const participant = participants?.at(0); if (!participant || !transaction?.amount || !currency) { return; diff --git a/src/pages/iou/request/step/IOURequestStepDistance.tsx b/src/pages/iou/request/step/IOURequestStepDistance.tsx index 14597df8e313..da50d39dec06 100644 --- a/src/pages/iou/request/step/IOURequestStepDistance.tsx +++ b/src/pages/iou/request/step/IOURequestStepDistance.tsx @@ -275,7 +275,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, @@ -284,7 +285,7 @@ function IOURequestStepDistance({ translate('iou.fieldPending'), currentUserPersonalDetails.login, currentUserPersonalDetails.accountID, - participants[0], + participant, '', {}, '', diff --git a/src/pages/iou/request/step/IOURequestStepDistanceRate.tsx b/src/pages/iou/request/step/IOURequestStepDistanceRate.tsx index 3cabcae9f79e..59e1591a23ff 100644 --- a/src/pages/iou/request/step/IOURequestStepDistanceRate.tsx +++ b/src/pages/iou/request/step/IOURequestStepDistanceRate.tsx @@ -83,7 +83,7 @@ function IOURequestStepDistanceRate({ }; }); - const unit = (Object.values(rates)[0]?.unit === CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES ? translate('common.mile') : translate('common.kilometer')) as Unit; + const unit = (Object.values(rates).at(0)?.unit === CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES ? translate('common.mile') : translate('common.kilometer')) as Unit; const initiallyFocusedOption = sections.find((item) => item.isSelected)?.keyForList; diff --git a/src/pages/iou/request/step/IOURequestStepParticipants.tsx b/src/pages/iou/request/step/IOURequestStepParticipants.tsx index 552ad4d54e39..0e26e08fb16a 100644 --- a/src/pages/iou/request/step/IOURequestStepParticipants.tsx +++ b/src/pages/iou/request/step/IOURequestStepParticipants.tsx @@ -49,7 +49,7 @@ function IOURequestStepParticipants({ const {canUseP2PDistanceRequests} = usePermissions(iouType); // We need to set selectedReportID if user has navigated back from confirmation page and navigates to confirmation page with already selected participant - const selectedReportID = useRef(participants?.length === 1 ? participants[0]?.reportID ?? reportID : reportID); + const selectedReportID = useRef(participants?.length === 1 ? participants.at(0)?.reportID ?? reportID : reportID); const numberOfParticipants = useRef(participants?.length ?? 0); const iouRequestType = TransactionUtils.getRequestType(transaction); const isSplitRequest = iouType === CONST.IOU.TYPE.SPLIT; @@ -94,7 +94,7 @@ function IOURequestStepParticipants({ (val: Participant[]) => { HttpUtils.cancelPendingRequests(READ_COMMANDS.SEARCH_FOR_REPORTS); - const firstParticipantReportID = val[0]?.reportID ?? ''; + const firstParticipantReportID = val.at(0)?.reportID ?? ''; const rateID = DistanceRequestUtils.getCustomUnitRateID(firstParticipantReportID, !canUseP2PDistanceRequests); const isInvoice = iouType === CONST.IOU.TYPE.INVOICE && ReportUtils.isInvoiceRoomWithID(firstParticipantReportID); numberOfParticipants.current = val.length; diff --git a/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx b/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx index 05ebdc1dfc62..728f21b2bb33 100644 --- a/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx +++ b/src/pages/iou/request/step/IOURequestStepScan/index.native.tsx @@ -269,6 +269,10 @@ function IOURequestStepScan({ } getCurrentPosition( (successData) => { + const participant = participants.at(0); + if (!participant) { + return; + } if (iouType === CONST.IOU.TYPE.TRACK && report) { IOU.trackExpense( report, @@ -278,7 +282,7 @@ function IOURequestStepScan({ '', currentUserPersonalDetails.login, currentUserPersonalDetails.accountID, - participants[0], + participant, '', receipt, '', @@ -303,7 +307,7 @@ function IOURequestStepScan({ '', currentUserPersonalDetails.login, currentUserPersonalDetails.accountID, - participants[0], + participant, '', receipt, '', @@ -322,6 +326,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) { @@ -333,7 +341,7 @@ function IOURequestStepScan({ '', currentUserPersonalDetails.login, currentUserPersonalDetails.accountID, - participants[0], + participant, '', receipt, ); @@ -346,7 +354,7 @@ function IOURequestStepScan({ '', currentUserPersonalDetails.login, currentUserPersonalDetails.accountID, - participants[0], + participant, '', receipt, ); diff --git a/src/pages/iou/request/step/IOURequestStepScan/index.tsx b/src/pages/iou/request/step/IOURequestStepScan/index.tsx index 58f20b281937..112938399722 100644 --- a/src/pages/iou/request/step/IOURequestStepScan/index.tsx +++ b/src/pages/iou/request/step/IOURequestStepScan/index.tsx @@ -139,8 +139,8 @@ function IOURequestStepScan({ navigator.mediaDevices.enumerateDevices().then((devices) => { let lastBackDeviceId = ''; for (let i = devices.length - 1; i >= 0; i--) { - const device = devices[i]; - if (device.kind === 'videoinput') { + const device = devices.at(i); + if (device?.kind === 'videoinput') { lastBackDeviceId = device.deviceId; break; } @@ -301,6 +301,10 @@ function IOURequestStepScan({ } getCurrentPosition( (successData) => { + const participant = participants.at(0); + if (!participant) { + return; + } if (iouType === CONST.IOU.TYPE.TRACK && report) { IOU.trackExpense( report, @@ -310,7 +314,7 @@ function IOURequestStepScan({ '', currentUserPersonalDetails.login, currentUserPersonalDetails.accountID, - participants[0], + participant, '', receipt, '', @@ -335,7 +339,7 @@ function IOURequestStepScan({ '', currentUserPersonalDetails.login, currentUserPersonalDetails.accountID, - participants[0], + participant, '', receipt, '', @@ -354,6 +358,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) { @@ -365,7 +373,7 @@ function IOURequestStepScan({ '', currentUserPersonalDetails.login, currentUserPersonalDetails.accountID, - participants[0], + participant, '', receipt, ); @@ -378,7 +386,7 @@ function IOURequestStepScan({ '', currentUserPersonalDetails.login, currentUserPersonalDetails.accountID, - participants[0], + participant, '', receipt, ); diff --git a/src/pages/settings/Security/AddDelegate/ValidateCodeForm/BaseValidateCodeForm.tsx b/src/pages/settings/Security/AddDelegate/ValidateCodeForm/BaseValidateCodeForm.tsx index c9816862ad35..5b01568d018e 100644 --- a/src/pages/settings/Security/AddDelegate/ValidateCodeForm/BaseValidateCodeForm.tsx +++ b/src/pages/settings/Security/AddDelegate/ValidateCodeForm/BaseValidateCodeForm.tsx @@ -161,7 +161,7 @@ function BaseValidateCodeForm({autoComplete = 'one-time-code', innerRef = () => name="validateCode" value={validateCode} onChangeText={onTextInput} - errorText={formError?.validateCode ? translate(formError?.validateCode) : Object.values(validateLoginError ?? {})[0] ?? ''} + errorText={formError?.validateCode ? translate(formError?.validateCode) : Object.values(validateLoginError ?? {}).at(0) ?? ''} hasError={!isEmptyObject(validateLoginError)} onFulfill={validateAndSubmitForm} autoFocus={false} diff --git a/src/pages/settings/Wallet/ExpensifyCardPage.tsx b/src/pages/settings/Wallet/ExpensifyCardPage.tsx index 963999def2a9..7837ba732174 100644 --- a/src/pages/settings/Wallet/ExpensifyCardPage.tsx +++ b/src/pages/settings/Wallet/ExpensifyCardPage.tsx @@ -146,8 +146,8 @@ function ExpensifyCardPage({ const hasDetectedDomainFraud = cardsToShow?.some((card) => card?.fraud === CONST.EXPENSIFY_CARD.FRAUD_TYPES.DOMAIN); const hasDetectedIndividualFraud = cardsToShow?.some((card) => card?.fraud === CONST.EXPENSIFY_CARD.FRAUD_TYPES.INDIVIDUAL); - const formattedAvailableSpendAmount = CurrencyUtils.convertToDisplayString(cardsToShow?.[0]?.availableSpend); - const {limitNameKey, limitTitleKey} = getLimitTypeTranslationKeys(cardsToShow?.[0]?.nameValuePairs?.limitType); + const formattedAvailableSpendAmount = CurrencyUtils.convertToDisplayString(cardsToShow?.at(0)?.availableSpend); + const {limitNameKey, limitTitleKey} = getLimitTypeTranslationKeys(cardsToShow?.at(0)?.nameValuePairs?.limitType); const goToGetPhysicalCardFlow = () => { let updatedDraftValues = draftValues; diff --git a/src/pages/settings/Wallet/PaymentMethodList.tsx b/src/pages/settings/Wallet/PaymentMethodList.tsx index b28b88e1ba83..3327eb4cb337 100644 --- a/src/pages/settings/Wallet/PaymentMethodList.tsx +++ b/src/pages/settings/Wallet/PaymentMethodList.tsx @@ -244,9 +244,12 @@ 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[domainGroupIndex].errors = {...assignedCardsGrouped[domainGroupIndex].errors, ...card.errors}; - if (card.fraud === CONST.EXPENSIFY_CARD.FRAUD_TYPES.DOMAIN || card.fraud === CONST.EXPENSIFY_CARD.FRAUD_TYPES.INDIVIDUAL) { - assignedCardsGrouped[domainGroupIndex].brickRoadIndicator = CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR; + const assignedCardsGroupedItem = assignedCardsGrouped.at(domainGroupIndex); + if (assignedCardsGroupedItem) { + assignedCardsGroupedItem.errors = {...assignedCardsGrouped.at(domainGroupIndex)?.errors, ...card.errors}; + if (card.fraud === CONST.EXPENSIFY_CARD.FRAUD_TYPES.DOMAIN || card.fraud === CONST.EXPENSIFY_CARD.FRAUD_TYPES.INDIVIDUAL) { + assignedCardsGroupedItem.brickRoadIndicator = CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR; + } } return; } diff --git a/src/pages/settings/Wallet/TransferBalancePage.tsx b/src/pages/settings/Wallet/TransferBalancePage.tsx index a324c91ecb85..3d1a21d9cec1 100644 --- a/src/pages/settings/Wallet/TransferBalancePage.tsx +++ b/src/pages/settings/Wallet/TransferBalancePage.tsx @@ -96,7 +96,7 @@ function TransferBalancePage({bankAccountList, fundList, userWallet, walletTrans const filteredMethods = combinedPaymentMethods.filter((paymentMethod) => paymentMethod.accountType === filterPaymentMethodType); if (filteredMethods.length === 1) { - const account = filteredMethods[0]; + const account = filteredMethods.at(0); PaymentMethods.saveWalletTransferAccountTypeAndID(filterPaymentMethodType ?? '', account?.methodID?.toString() ?? '-1'); return; } diff --git a/src/pages/workspace/WorkspacesListPage.tsx b/src/pages/workspace/WorkspacesListPage.tsx index c2a47e536d43..1acd89f19812 100755 --- a/src/pages/workspace/WorkspacesListPage.tsx +++ b/src/pages/workspace/WorkspacesListPage.tsx @@ -316,15 +316,15 @@ function WorkspacesListPage({policies, reimbursementAccount, reports, session}: .filter((policy): policy is PolicyType => PolicyUtils.shouldShowPolicy(policy, !!isOffline, session?.email)) .map((policy): WorkspaceItem => { if (policy?.isJoinRequestPending && policy?.policyDetailsForNonMembers) { - const policyInfo = Object.values(policy.policyDetailsForNonMembers)[0]; - const id = Object.keys(policy.policyDetailsForNonMembers)[0]; + 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/WorkspacesListRow.tsx b/src/pages/workspace/WorkspacesListRow.tsx index ac53252829fa..9f1984c20ebf 100644 --- a/src/pages/workspace/WorkspacesListRow.tsx +++ b/src/pages/workspace/WorkspacesListRow.tsx @@ -115,7 +115,7 @@ function WorkspacesListRow({ const threeDotsMenuContainerRef = useRef(null); const {shouldUseNarrowLayout} = useResponsiveLayout(); - const ownerDetails = ownerAccountID && PersonalDetailsUtils.getPersonalDetailsByIDs([ownerAccountID], currentUserPersonalDetails.accountID)[0]; + const ownerDetails = ownerAccountID && PersonalDetailsUtils.getPersonalDetailsByIDs([ownerAccountID], currentUserPersonalDetails.accountID).at(0); const userFriendlyWorkspaceType = useMemo(() => { switch (workspaceType) { diff --git a/src/pages/workspace/accounting/netsuite/NetSuiteSubsidiarySelector.tsx b/src/pages/workspace/accounting/netsuite/NetSuiteSubsidiarySelector.tsx index 75c9b19ffcbe..f1f0a8c9aca9 100644 --- a/src/pages/workspace/accounting/netsuite/NetSuiteSubsidiarySelector.tsx +++ b/src/pages/workspace/accounting/netsuite/NetSuiteSubsidiarySelector.tsx @@ -88,7 +88,7 @@ function NetSuiteSubsidiarySelector({policy}: WithPolicyConnectionsProps) { listItem={RadioListItem} connectionName={CONST.POLICY.CONNECTIONS.NAME.NETSUITE} onSelectRow={updateSubsidiary} - initiallyFocusedOptionKey={netsuiteConfig?.subsidiaryID ?? subsidiaryListSections?.[0]?.keyForList} + initiallyFocusedOptionKey={netsuiteConfig?.subsidiaryID ?? subsidiaryListSections?.at(0)?.keyForList} headerContent={listHeaderComponent} onBackButtonPress={() => Navigation.goBack()} title="workspace.netsuite.subsidiary" diff --git a/src/pages/workspace/accounting/qbo/advanced/QuickbooksAdvancedPage.tsx b/src/pages/workspace/accounting/qbo/advanced/QuickbooksAdvancedPage.tsx index 6b4d395832bf..524f77f584d8 100644 --- a/src/pages/workspace/accounting/qbo/advanced/QuickbooksAdvancedPage.tsx +++ b/src/pages/workspace/accounting/qbo/advanced/QuickbooksAdvancedPage.tsx @@ -150,7 +150,7 @@ function QuickbooksAdvancedPage({policy}: WithPolicyConnectionsProps) { onToggle: () => QuickbooksOnline.updateQuickbooksOnlineCollectionAccountID( policyID, - isSyncReimbursedSwitchOn ? '' : [...qboAccountOptions, ...invoiceAccountCollectionOptions][0].id, + isSyncReimbursedSwitchOn ? '' : [...qboAccountOptions, ...invoiceAccountCollectionOptions].at(0)?.id, qboConfig?.collectionAccountID, ), subscribedSetting: CONST.QUICKBOOKS_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 24a16f15ce2f..e8b74ae9ce46 100644 --- a/src/pages/workspace/accounting/qbo/export/QuickbooksCompanyCardExpenseAccountSelectCardPage.tsx +++ b/src/pages/workspace/accounting/qbo/export/QuickbooksCompanyCardExpenseAccountSelectCardPage.tsx @@ -71,7 +71,7 @@ function QuickbooksCompanyCardExpenseAccountSelectCardPage({policy}: WithPolicyC CONST.POLICY.CONNECTIONS.NAME.QBO, { [CONST.QUICKBOOKS_CONFIG.NON_REIMBURSABLE_EXPENSES_EXPORT_DESTINATION]: row.value, - [CONST.QUICKBOOKS_CONFIG.NON_REIMBURSABLE_EXPENSES_ACCOUNT]: row.accounts[0], + [CONST.QUICKBOOKS_CONFIG.NON_REIMBURSABLE_EXPENSES_ACCOUNT]: row.accounts.at(0), [CONST.QUICKBOOKS_CONFIG.NON_REIMBURSABLE_BILL_DEFAULT_VENDOR]: row.defaultVendor, }, { @@ -96,7 +96,7 @@ function QuickbooksCompanyCardExpenseAccountSelectCardPage({policy}: WithPolicyC listItem={RadioListItem} onSelectRow={(selection: SelectorType) => selectExportCompanyCard(selection as MenuItem)} shouldSingleExecuteRowSelect - initiallyFocusedOptionKey={sections[0]?.data.find((mode) => mode.isSelected)?.keyForList} + initiallyFocusedOptionKey={sections.at(0)?.data.find((mode) => mode.isSelected)?.keyForList} connectionName={CONST.POLICY.CONNECTIONS.NAME.QBO} onBackButtonPress={() => Navigation.goBack(ROUTES.POLICY_ACCOUNTING_QUICKBOOKS_ONLINE_COMPANY_CARD_EXPENSE_ACCOUNT.getRoute(policyID))} listFooterContent={ diff --git a/src/pages/workspace/accounting/qbo/export/QuickbooksNonReimbursableDefaultVendorSelectPage.tsx b/src/pages/workspace/accounting/qbo/export/QuickbooksNonReimbursableDefaultVendorSelectPage.tsx index 95fe2793af2b..d9656c41f069 100644 --- a/src/pages/workspace/accounting/qbo/export/QuickbooksNonReimbursableDefaultVendorSelectPage.tsx +++ b/src/pages/workspace/accounting/qbo/export/QuickbooksNonReimbursableDefaultVendorSelectPage.tsx @@ -74,7 +74,7 @@ function QuickbooksNonReimbursableDefaultVendorSelectPage({policy}: WithPolicyCo listItem={RadioListItem} onSelectRow={selectVendor} shouldSingleExecuteRowSelect - initiallyFocusedOptionKey={sections[0]?.data.find((mode) => mode.isSelected)?.keyForList} + initiallyFocusedOptionKey={sections.at(0)?.data.find((mode) => mode.isSelected)?.keyForList} listEmptyContent={listEmptyContent} connectionName={CONST.POLICY.CONNECTIONS.NAME.QBO} onBackButtonPress={() => Navigation.goBack(ROUTES.POLICY_ACCOUNTING_QUICKBOOKS_ONLINE_COMPANY_CARD_EXPENSE_ACCOUNT.getRoute(policyID))} diff --git a/src/pages/workspace/accounting/qbo/export/QuickbooksOutOfPocketExpenseEntitySelectPage.tsx b/src/pages/workspace/accounting/qbo/export/QuickbooksOutOfPocketExpenseEntitySelectPage.tsx index 51dee308fb75..0fd977fb5b13 100644 --- a/src/pages/workspace/accounting/qbo/export/QuickbooksOutOfPocketExpenseEntitySelectPage.tsx +++ b/src/pages/workspace/accounting/qbo/export/QuickbooksOutOfPocketExpenseEntitySelectPage.tsx @@ -90,7 +90,7 @@ function QuickbooksOutOfPocketExpenseEntitySelectPage({policy}: WithPolicyConnec CONST.POLICY.CONNECTIONS.NAME.QBO, { [CONST.QUICKBOOKS_CONFIG.REIMBURSABLE_EXPENSES_EXPORT_DESTINATION]: row.value, - [CONST.QUICKBOOKS_CONFIG.REIMBURSABLE_EXPENSES_ACCOUNT]: row.accounts[0], + [CONST.QUICKBOOKS_CONFIG.REIMBURSABLE_EXPENSES_ACCOUNT]: row.accounts.at(0), }, { [CONST.QUICKBOOKS_CONFIG.REIMBURSABLE_EXPENSES_EXPORT_DESTINATION]: qboConfig?.reimbursableExpensesExportDestination, diff --git a/src/pages/workspace/categories/WorkspaceCategoriesSettingsPage.tsx b/src/pages/workspace/categories/WorkspaceCategoriesSettingsPage.tsx index 5a048e3bdc36..10274527f682 100644 --- a/src/pages/workspace/categories/WorkspaceCategoriesSettingsPage.tsx +++ b/src/pages/workspace/categories/WorkspaceCategoriesSettingsPage.tsx @@ -102,7 +102,7 @@ function WorkspaceCategoriesSettingsPage({policy, route}: WorkspaceCategoriesSet shouldPlaceSubtitleBelowSwitch /> - {canUseWorkspaceRules && !!currentPolicy && sections[0].data.length > 0 && ( + {canUseWorkspaceRules && !!currentPolicy && (sections.at(0)?.data?.length ?? 0) > 0 && ( diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx index b1876c17efd5..75f7672199cb 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardFeedSelectorPage.tsx @@ -57,7 +57,7 @@ function WorkspaceCompanyCardFeedSelectorPage({route}: WorkspaceCompanyCardFeedS const [lastSelectedFeed] = useOnyx(`${ONYXKEYS.COLLECTION.LAST_SELECTED_FEED}${policyID}`); const cardFeeds = mockedData; - const defaultFeed = Object.keys(cardFeeds?.companyCards ?? {})[0]; + const defaultFeed = Object.keys(cardFeeds?.companyCards ?? {}).at(0); const selectedFeed = lastSelectedFeed ?? defaultFeed; const feeds: CardFeedListItem[] = Object.entries(cardFeeds?.companyCardNicknames ?? {}).map(([key, value]) => ({ diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx index fe03b2a241dd..7ad3d7fd47b1 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsPage.tsx @@ -122,7 +122,7 @@ function WorkspaceCompanyCardPage({route}: WorkspaceCompanyCardPageProps) { const workspaceAccountID = PolicyUtils.getWorkspaceAccountID(policyID); const [cardFeeds] = useOnyx(`${ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER}${workspaceAccountID}`); const [lastSelectedFeed] = useOnyx(`${ONYXKEYS.COLLECTION.LAST_SELECTED_FEED}${policyID}`); - const defaultFeed = Object.keys(cardFeeds?.companyCards ?? {})[0]; + const defaultFeed = Object.keys(cardFeeds?.companyCards ?? {}).at(0); const selectedFeed = lastSelectedFeed ?? defaultFeed; const fetchCompanyCards = useCallback(() => { Policy.openPolicyCompanyCardsPage(policyID, workspaceAccountID); @@ -131,7 +131,7 @@ function WorkspaceCompanyCardPage({route}: WorkspaceCompanyCardPageProps) { useFocusEffect(fetchCompanyCards); const companyCards = cardFeeds?.companyCards ?? {}; - const selectedCompanyCard = companyCards[selectedFeed] ?? null; + const selectedCompanyCard = companyCards[selectedFeed ?? ''] ?? null; const isNoFeed = !selectedCompanyCard; const isPending = selectedCompanyCard?.pending; const isFeedAdded = !isPending && !isNoFeed; @@ -167,7 +167,7 @@ function WorkspaceCompanyCardPage({route}: WorkspaceCompanyCardPageProps) { {(isFeedAdded || isPending) && ( )} {isNoFeed && } diff --git a/src/pages/workspace/members/WorkspaceOwnerChangeCheck.tsx b/src/pages/workspace/members/WorkspaceOwnerChangeCheck.tsx index 52d60dbd618d..344aafeedde6 100644 --- a/src/pages/workspace/members/WorkspaceOwnerChangeCheck.tsx +++ b/src/pages/workspace/members/WorkspaceOwnerChangeCheck.tsx @@ -44,7 +44,7 @@ function WorkspaceOwnerChangeCheck({personalDetails, policy, accountID, error}: const updateDisplayTexts = useCallback(() => { const changeOwnerErrors = Object.keys(policy?.errorFields?.changeOwner ?? {}); - if (error !== changeOwnerErrors[0]) { + if (error !== changeOwnerErrors.at(0)) { return; } diff --git a/src/pages/workspace/members/WorkspaceOwnerChangeWrapperPage.tsx b/src/pages/workspace/members/WorkspaceOwnerChangeWrapperPage.tsx index f6ab69b1e9c5..0213b77158ae 100644 --- a/src/pages/workspace/members/WorkspaceOwnerChangeWrapperPage.tsx +++ b/src/pages/workspace/members/WorkspaceOwnerChangeWrapperPage.tsx @@ -51,7 +51,7 @@ function WorkspaceOwnerChangeWrapperPage({route, policy}: WorkspaceOwnerChangeWr const changeOwnerErrors = Object.keys(policy?.errorFields?.changeOwner ?? {}); if (changeOwnerErrors && changeOwnerErrors.length > 0) { - Navigation.navigate(ROUTES.WORKSPACE_OWNER_CHANGE_CHECK.getRoute(policyID, accountID, changeOwnerErrors[0] as ValueOf)); + Navigation.navigate(ROUTES.WORKSPACE_OWNER_CHANGE_CHECK.getRoute(policyID, accountID, changeOwnerErrors.at(0) as ValueOf)); } }, [accountID, policy, policy?.errorFields?.changeOwner, policyID]); diff --git a/src/pages/workspace/members/WorkspaceOwnerPaymentCardForm.tsx b/src/pages/workspace/members/WorkspaceOwnerPaymentCardForm.tsx index 91e22fd9fcd5..ef9fd84dee39 100644 --- a/src/pages/workspace/members/WorkspaceOwnerPaymentCardForm.tsx +++ b/src/pages/workspace/members/WorkspaceOwnerPaymentCardForm.tsx @@ -34,7 +34,7 @@ function WorkspaceOwnerPaymentCardForm({policy}: WorkspaceOwnerPaymentCardFormPr const checkIfCanBeRendered = useCallback(() => { const changeOwnerErrors = Object.keys(policy?.errorFields?.changeOwner ?? {}); - if (changeOwnerErrors[0] !== CONST.POLICY.OWNERSHIP_ERRORS.NO_BILLING_CARD) { + if (changeOwnerErrors.at(0) !== CONST.POLICY.OWNERSHIP_ERRORS.NO_BILLING_CARD) { setShouldShowPaymentCardForm(false); } diff --git a/src/pages/workspace/reportFields/InitialListValueSelector/ReportFieldsInitialListValuePicker.tsx b/src/pages/workspace/reportFields/InitialListValueSelector/ReportFieldsInitialListValuePicker.tsx index 4e6328bbc36b..fcade7f74b8b 100644 --- a/src/pages/workspace/reportFields/InitialListValueSelector/ReportFieldsInitialListValuePicker.tsx +++ b/src/pages/workspace/reportFields/InitialListValueSelector/ReportFieldsInitialListValuePicker.tsx @@ -22,7 +22,7 @@ function ReportFieldsInitialListValuePicker({listValues, disabledOptions, value, () => [ { data: Object.values(listValues ?? {}) - .filter((listValue, index) => !disabledOptions[index]) + .filter((listValue, index) => !disabledOptions.at(index)) .sort(localeCompare) .map((listValue) => ({ keyForList: listValue, @@ -40,7 +40,7 @@ function ReportFieldsInitialListValuePicker({listValues, disabledOptions, value, sections={listValueSections} ListItem={RadioListItem} onSelectRow={(item) => onValueChange(item.value)} - initiallyFocusedOptionKey={listValueSections[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 599d7ae0da99..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[0].data.find((reportField) => reportField.isSelected)?.keyForList} + initiallyFocusedOptionKey={typeSections.at(0)?.data?.find((reportField) => reportField.isSelected)?.keyForList} /> ); } diff --git a/src/pages/workspace/reportFields/ReportFieldsListValuesPage.tsx b/src/pages/workspace/reportFields/ReportFieldsListValuesPage.tsx index 1ee20f1180ff..24abfb5df811 100644 --- a/src/pages/workspace/reportFields/ReportFieldsListValuesPage.tsx +++ b/src/pages/workspace/reportFields/ReportFieldsListValuesPage.tsx @@ -96,11 +96,11 @@ function ReportFieldsListValuesPage({ text: value, keyForList: value, isSelected: selectedValues[value] && canSelectMultiple, - enabled: !disabledListValues[index] ?? true, + enabled: !disabledListValues.at(index) ?? true, rightElement: ( ), })) @@ -190,13 +190,13 @@ function ReportFieldsListValuesPage({ } const enabledValues = selectedValuesArray.filter((valueName) => { const index = listValues?.indexOf(valueName) ?? -1; - return !disabledListValues?.[index]; + return !disabledListValues?.at(index); }); if (enabledValues.length > 0) { const valuesToDisable = selectedValuesArray.reduce((acc, valueName) => { const index = listValues?.indexOf(valueName) ?? -1; - if (!disabledListValues?.[index] && index !== -1) { + if (!disabledListValues?.at(index) && index !== -1) { acc.push(index); } @@ -222,13 +222,13 @@ function ReportFieldsListValuesPage({ const disabledValues = selectedValuesArray.filter((valueName) => { const index = listValues?.indexOf(valueName) ?? -1; - return disabledListValues?.[index]; + return disabledListValues?.at(index); }); if (disabledValues.length > 0) { const valuesToEnable = selectedValuesArray.reduce((acc, valueName) => { const index = listValues?.indexOf(valueName) ?? -1; - if (disabledListValues?.[index] && index !== -1) { + if (disabledListValues?.at(index) && index !== -1) { acc.push(index); } diff --git a/src/pages/workspace/reportFields/ReportFieldsValueSettingsPage.tsx b/src/pages/workspace/reportFields/ReportFieldsValueSettingsPage.tsx index 1db5d2ff2c9f..0886f2cd683c 100644 --- a/src/pages/workspace/reportFields/ReportFieldsValueSettingsPage.tsx +++ b/src/pages/workspace/reportFields/ReportFieldsValueSettingsPage.tsx @@ -48,8 +48,8 @@ function ReportFieldsValueSettingsPage({ if (reportFieldID) { const reportFieldKey = ReportUtils.getReportFieldKey(reportFieldID); - reportFieldValue = Object.values(policy?.fieldList?.[reportFieldKey]?.values ?? {})?.[valueIndex] ?? ''; - reportFieldDisabledValue = Object.values(policy?.fieldList?.[reportFieldKey]?.disabledOptions ?? {})?.[valueIndex] ?? false; + reportFieldValue = Object.values(policy?.fieldList?.[reportFieldKey]?.values ?? {})?.at(valueIndex) ?? ''; + reportFieldDisabledValue = Object.values(policy?.fieldList?.[reportFieldKey]?.disabledOptions ?? {})?.at(valueIndex) ?? false; } else { reportFieldValue = formDraft?.listValues?.[valueIndex] ?? ''; reportFieldDisabledValue = formDraft?.disabledListValues?.[valueIndex] ?? false; diff --git a/src/pages/workspace/reportFields/WorkspaceReportFieldsPage.tsx b/src/pages/workspace/reportFields/WorkspaceReportFieldsPage.tsx index 0ffdb362ae99..8fe57a13ef95 100644 --- a/src/pages/workspace/reportFields/WorkspaceReportFieldsPage.tsx +++ b/src/pages/workspace/reportFields/WorkspaceReportFieldsPage.tsx @@ -127,7 +127,7 @@ function WorkspaceReportFieldsPage({ ]; }, [filteredPolicyFieldList, policy, selectedReportFields, canSelectMultiple, translate]); - useAutoTurnSelectionModeOffWhenHasNoActiveOption(reportFieldsSections[0].data); + useAutoTurnSelectionModeOffWhenHasNoActiveOption(reportFieldsSections.at(0)?.data ?? ([] as ListItem[])); const updateSelectedReportFields = (item: ReportFieldForList) => { const fieldKey = ReportUtils.getReportFieldKey(item.fieldID); diff --git a/src/pages/workspace/tags/ImportedTagsPage.tsx b/src/pages/workspace/tags/ImportedTagsPage.tsx index 210a14f56998..2571fc35b513 100644 --- a/src/pages/workspace/tags/ImportedTagsPage.tsx +++ b/src/pages/workspace/tags/ImportedTagsPage.tsx @@ -92,7 +92,7 @@ function ImportedTagsPage({route}: ImportedTagsPageProps) { const tagsGLCode = tagsGLCodeColumn !== -1 ? spreadsheet?.data[tagsGLCodeColumn].map((glCode) => glCode) : []; const tags = tagsNames?.slice(containsHeader ? 1 : 0).map((name, index) => { // Right now we support only single-level tags, this check should be updated when we add multi-level support - const tagAlreadyExists = policyTagLists[0]?.tags?.[name]; + const tagAlreadyExists = policyTagLists.at(0)?.tags?.[name]; const existingGLCodeOrDefault = tagAlreadyExists?.['GL Code'] ?? ''; return { name, diff --git a/src/pages/workspace/tags/WorkspaceTagsPage.tsx b/src/pages/workspace/tags/WorkspaceTagsPage.tsx index 84722a1f0b5b..1a45210617be 100644 --- a/src/pages/workspace/tags/WorkspaceTagsPage.tsx +++ b/src/pages/workspace/tags/WorkspaceTagsPage.tsx @@ -111,7 +111,7 @@ function WorkspaceTagsPage({route}: WorkspaceTagsPageProps) { ), })); } - const sortedTags = lodashSortBy(Object.values(policyTagLists[0]?.tags ?? {}), 'name', localeCompare) as PolicyTag[]; + const sortedTags = lodashSortBy(Object.values(policyTagLists.at(0)?.tags ?? {}), 'name', localeCompare) as PolicyTag[]; return sortedTags.map((tag) => ({ value: tag.name, text: PolicyUtils.getCleanedTagName(tag.name), diff --git a/src/pages/workspace/tags/WorkspaceTagsSettingsPage.tsx b/src/pages/workspace/tags/WorkspaceTagsSettingsPage.tsx index 10902b70b25a..e6c3a1b4a058 100644 --- a/src/pages/workspace/tags/WorkspaceTagsSettingsPage.tsx +++ b/src/pages/workspace/tags/WorkspaceTagsSettingsPage.tsx @@ -58,7 +58,7 @@ function WorkspaceTagsSettingsPage({route, policyTags}: WorkspaceTagsSettingsPag const styles = useThemeStyles(); const {translate} = useLocalize(); const [policyTagLists, isMultiLevelTags] = useMemo(() => [PolicyUtils.getTagLists(policyTags), PolicyUtils.isMultiLevelTags(policyTags)], [policyTags]); - const isLoading = !PolicyUtils.getTagLists(policyTags)?.[0] || Object.keys(policyTags ?? {})[0] === 'undefined'; + const isLoading = !PolicyUtils.getTagLists(policyTags)?.at(0) || Object.keys(policyTags ?? {}).at(0) === 'undefined'; const {isOffline} = useNetwork(); const hasEnabledOptions = OptionsListUtils.hasEnabledOptions(Object.values(policyTags ?? {}).flatMap(({tags}) => Object.values(tags))); const {canUseWorkspaceRules} = usePermissions(); @@ -74,15 +74,15 @@ function WorkspaceTagsSettingsPage({route, policyTags}: WorkspaceTagsSettingsPag {!isMultiLevelTags && ( Tag.clearPolicyTagListErrors(policyID, policyTagLists[0].orderWeight)} - pendingAction={policyTags?.[policyTagLists[0]?.name]?.pendingAction} + errors={policyTags?.[policyTagLists.at(0)?.name ?? '']?.errors} + onClose={() => Tag.clearPolicyTagListErrors(policyID, policyTagLists.at(0)?.orderWeight ?? 0)} + pendingAction={policyTags?.[policyTagLists.at(0)?.name ?? '']?.pendingAction} errorRowStyles={styles.mh5} > Navigation.navigate(ROUTES.WORKSPACE_EDIT_TAGS.getRoute(policyID, policyTagLists[0].orderWeight))} + onPress={() => Navigation.navigate(ROUTES.WORKSPACE_EDIT_TAGS.getRoute(policyID, policyTagLists.at(0)?.orderWeight ?? 0))} shouldShowRightIcon /> @@ -114,6 +114,60 @@ function WorkspaceTagsSettingsPage({route, policyTags}: WorkspaceTagsSettingsPag )} + + + {translate('workspace.tags.requiresTag')} + + + + {canUseWorkspaceRules && policy?.areRulesEnabled && ( + + + {translate('workspace.tags.trackBillable')} + toggleBillableExpenses(policy)} + /> + + + )} + + + {translate('workspace.tags.requiresTag')} + + + + {canUseWorkspaceRules && policy?.areRulesEnabled && ( + + + {translate('workspace.tags.trackBillable')} + toggleBillableExpenses(policy)} + /> + + + )} ); return ( diff --git a/src/pages/workspace/tags/WorkspaceViewTagsPage.tsx b/src/pages/workspace/tags/WorkspaceViewTagsPage.tsx index fae17a847c98..b5c4d4368fd3 100644 --- a/src/pages/workspace/tags/WorkspaceViewTagsPage.tsx +++ b/src/pages/workspace/tags/WorkspaceViewTagsPage.tsx @@ -237,7 +237,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 cc80705534b2..c36ea830852b 100644 --- a/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx +++ b/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx @@ -171,7 +171,7 @@ function WorkspaceWorkflowsPage({policy, route}: WorkspaceWorkflowsPageProps) { > Navigation.navigate(ROUTES.WORKSPACE_WORKFLOWS_APPROVALS_EDIT.getRoute(route.params.policyID, workflow.approvers[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 d127a13df240..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[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 6125ac842537..5a1a0bf7dc01 100644 --- a/src/pages/workspace/workflows/approvals/WorkspaceWorkflowsApprovalsApproverPage.tsx +++ b/src/pages/workspace/workflows/approvals/WorkspaceWorkflowsApprovalsApproverPage.tsx @@ -135,7 +135,7 @@ function WorkspaceWorkflowsApprovalsApproverPage({policy, personalDetails, isLoa ]; }, [approversFromWorkflow, isDefault, approverIndex, debouncedSearchTerm, defaultApprover, personalDetails, employeeList, selectedApproverEmail, translate]); - const shouldShowListEmptyContent = !debouncedSearchTerm && approvalWorkflow && !sections[0].data.length; + const shouldShowListEmptyContent = !debouncedSearchTerm && approvalWorkflow && !sections.at(0)?.data.length; const nextStep = useCallback(() => { if (selectedApproverEmail) { @@ -196,7 +196,7 @@ function WorkspaceWorkflowsApprovalsApproverPage({policy, personalDetails, isLoa setSelectedApproverEmail(approver.login); }; - const headerMessage = useMemo(() => (searchTerm && !sections[0].data.length ? translate('common.noResultsFound') : ''), [searchTerm, sections, translate]); + const headerMessage = useMemo(() => (searchTerm && !sections.at(0)?.data?.length ? translate('common.noResultsFound') : ''), [searchTerm, sections, translate]); const listEmptyContent = useMemo( () => ( diff --git a/src/pages/workspace/workflows/approvals/WorkspaceWorkflowsApprovalsExpensesFromPage.tsx b/src/pages/workspace/workflows/approvals/WorkspaceWorkflowsApprovalsExpensesFromPage.tsx index 2c69f07ef83b..917ef5b8d2ff 100644 --- a/src/pages/workspace/workflows/approvals/WorkspaceWorkflowsApprovalsExpensesFromPage.tsx +++ b/src/pages/workspace/workflows/approvals/WorkspaceWorkflowsApprovalsExpensesFromPage.tsx @@ -176,7 +176,7 @@ function WorkspaceWorkflowsApprovalsExpensesFromPage({policy, isLoadingReportDat setSelectedMembers(isAlreadySelected ? selectedMembers.filter((selectedOption) => selectedOption.login !== member.login) : [...selectedMembers, {...member, isSelected: true}]); }; - const headerMessage = useMemo(() => (searchTerm && !sections[0].data.length ? translate('common.noResultsFound') : ''), [searchTerm, sections, translate]); + const headerMessage = useMemo(() => (searchTerm && !sections.at(0)?.data?.length ? translate('common.noResultsFound') : ''), [searchTerm, sections, translate]); const listEmptyContent = useMemo( () => ( diff --git a/src/stories/PromotedActionBar.stories.tsx b/src/stories/PromotedActionBar.stories.tsx index f3fc626fc916..30e37c2237aa 100644 --- a/src/stories/PromotedActionBar.stories.tsx +++ b/src/stories/PromotedActionBar.stories.tsx @@ -49,21 +49,28 @@ const promotedActions = [ }, ] satisfies PromotedAction[]; +const defaultPromotedAction = { + key: '', + icon: Expensicons.ExpensifyLogoNew, + text: '', + onSelected: () => {}, +}; + // Arguments can be passed to the component by binding // See: https://storybook.js.org/docs/react/writing-stories/introduction#using-args const Default: StoryType = Template.bind({}); Default.args = { - promotedActions: [promotedActions[0]], + promotedActions: [promotedActions.at(0) ?? defaultPromotedAction], }; const TwoPromotedActions: StoryType = Template.bind({}); TwoPromotedActions.args = { - promotedActions: [promotedActions[0], promotedActions[1]], + promotedActions: [promotedActions.at(0) ?? defaultPromotedAction, promotedActions.at(1) ?? defaultPromotedAction], }; const ThreePromotedActions: StoryType = Template.bind({}); ThreePromotedActions.args = { - promotedActions: [promotedActions[0], promotedActions[1], promotedActions[2]], + promotedActions: [promotedActions.at(0) ?? defaultPromotedAction, promotedActions.at(1) ?? defaultPromotedAction, promotedActions.at(2) ?? defaultPromotedAction], }; export default story; diff --git a/src/styles/utils/index.ts b/src/styles/utils/index.ts index d3d31cc2ea1b..203754b803e3 100644 --- a/src/styles/utils/index.ts +++ b/src/styles/utils/index.ts @@ -272,8 +272,7 @@ function getAvatarBorderStyle(size: AvatarSizeName, type: string): ViewStyle { */ function getDefaultWorkspaceAvatarColor(text: string): ViewStyle { const colorHash = UserUtils.hashText(text.trim(), workspaceColorOptions.length); - - return workspaceColorOptions[colorHash]; + return workspaceColorOptions.at(colorHash) ?? {backgroundColor: colors.blue200, fill: colors.blue700}; } /** @@ -292,7 +291,7 @@ function getEReceiptColorCode(transaction: OnyxEntry): EReceiptColo const colorHash = UserUtils.hashText(transactionID.trim(), eReceiptColors.length); - return eReceiptColors[colorHash]; + return eReceiptColors.at(colorHash) ?? 'Yellow'; } /** @@ -477,7 +476,7 @@ function getBackgroundColorWithOpacityStyle(backgroundColor: string, opacity: nu const result = hexadecimalToRGBArray(backgroundColor); if (result !== undefined) { return { - backgroundColor: `rgba(${result[0]}, ${result[1]}, ${result[2]}, ${opacity})`, + backgroundColor: `rgba(${result.at(0)}, ${result.at(1)}, ${result.at(2)}, ${opacity})`, }; } return {}; diff --git a/src/utils/arraysEqual.ts b/src/utils/arraysEqual.ts index 3a8111cc7bb7..1760cccc279f 100644 --- a/src/utils/arraysEqual.ts +++ b/src/utils/arraysEqual.ts @@ -15,7 +15,7 @@ function arraysEqual(a: T[], b: T[]): boolean { // you might want to clone your array first. for (let i = 0; i < a.length; ++i) { - if (a[i] !== b[i]) { + if (a.at(i) !== b.at(i)) { return false; } } 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 662eae8d7b21..dfadeb46e163 100644 --- a/tests/actions/IOUTest.ts +++ b/tests/actions/IOUTest.ts @@ -96,9 +96,9 @@ describe('actions/IOU', () => { const iouReports = Object.values(allReports ?? {}).filter((report) => report?.type === CONST.REPORT.TYPE.IOU); expect(Object.keys(chatReports).length).toBe(2); expect(Object.keys(iouReports).length).toBe(1); - const chatReport = chatReports[0]; - const transactionThreadReport = chatReports[1]; - const iouReport = iouReports[0]; + const chatReport = chatReports.at(0); + const transactionThreadReport = chatReports.at(1); + const iouReport = iouReports.at(0); iouReportID = iouReport?.reportID; transactionThread = transactionThreadReport; @@ -135,8 +135,8 @@ describe('actions/IOU', () => { ); expect(Object.values(createdActions).length).toBe(1); expect(Object.values(iouActions).length).toBe(1); - createdAction = createdActions?.[0] ?? null; - iouAction = iouActions?.[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 @@ -178,7 +178,7 @@ describe('actions/IOU', () => { (reportAction) => reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.CREATED, ); expect(Object.values(createdActions).length).toBe(1); - transactionThreadCreatedAction = createdActions[0]; + transactionThreadCreatedAction = createdActions.at(0); expect(transactionThreadCreatedAction?.pendingAction).toBe(CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD); resolve(); @@ -639,11 +639,11 @@ describe('actions/IOU', () => { const iouReports = Object.values(allReports ?? {}).filter((report) => report?.type === CONST.REPORT.TYPE.IOU); expect(Object.values(chatReports).length).toBe(2); expect(Object.values(iouReports).length).toBe(1); - const chatReport = chatReports[0]; + const chatReport = chatReports.at(0); chatReportID = chatReport?.reportID; - transactionThreadReport = chatReports[1]; + transactionThreadReport = chatReports.at(1); - const iouReport = iouReports[0]; + const iouReport = iouReports.at(0); iouReportID = iouReport?.reportID; expect(chatReport?.participants).toStrictEqual({[RORY_ACCOUNT_ID]: RORY_PARTICIPANT, [CARLOS_ACCOUNT_ID]: CARLOS_PARTICIPANT}); @@ -677,12 +677,12 @@ describe('actions/IOU', () => { ) ?? null; expect(Object.values(createdActions).length).toBe(1); expect(Object.values(iouActions).length).toBe(1); - createdAction = createdActions[0]; - iouAction = iouActions[0]; + createdAction = createdActions.at(0); + iouAction = iouActions.at(0); 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); @@ -784,7 +784,7 @@ describe('actions/IOU', () => { Onyx.disconnect(connection); expect(transaction?.pendingAction).toBe(CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD); expect(transaction?.errors).toBeTruthy(); - expect(Object.values(transaction?.errors ?? {})[0]).toEqual(Localize.translateLocal('iou.error.genericCreateFailureMessage')); + expect(Object.values(transaction?.errors ?? {}).at(0)).toEqual(Localize.translateLocal('iou.error.genericCreateFailureMessage')); resolve(); }, }); @@ -1436,7 +1436,7 @@ describe('actions/IOU', () => { expect(Object.values(allReports ?? {}).length).toBe(3); const chatReports = Object.values(allReports ?? {}).filter((report) => report?.type === CONST.REPORT.TYPE.CHAT); - chatReport = chatReports[0]; + chatReport = chatReports.at(0); expect(chatReport).toBeTruthy(); expect(chatReport).toHaveProperty('reportID'); expect(chatReport).toHaveProperty('iouReportID'); @@ -1884,7 +1884,7 @@ describe('actions/IOU', () => { Onyx.disconnect(connection); const updatedAction = Object.values(allActions ?? {}).find((reportAction) => !isEmptyObject(reportAction)); expect(updatedAction?.actionName).toEqual('MODIFIEDEXPENSE'); - expect(Object.values(updatedAction?.errors ?? {})[0]).toEqual(Localize.translateLocal('iou.error.genericEditFailureMessage')); + expect(Object.values(updatedAction?.errors ?? {}).at(0)).toEqual(Localize.translateLocal('iou.error.genericEditFailureMessage')); resolve(); }, }); @@ -2106,7 +2106,7 @@ describe('actions/IOU', () => { callback: (allActions) => { Onyx.disconnect(connection); const erroredAction = Object.values(allActions ?? {}).find((action) => !isEmptyObject(action?.errors)); - expect(Object.values(erroredAction?.errors ?? {})[0]).toEqual(Localize.translateLocal('iou.error.other')); + expect(Object.values(erroredAction?.errors ?? {}).at(0)).toEqual(Localize.translateLocal('iou.error.other')); resolve(); }, }); diff --git a/tests/actions/PolicyCategoryTest.ts b/tests/actions/PolicyCategoryTest.ts index e470771d492f..351adaf8e982 100644 --- a/tests/actions/PolicyCategoryTest.ts +++ b/tests/actions/PolicyCategoryTest.ts @@ -111,13 +111,13 @@ describe('actions/PolicyCategory', () => { it('Rename category', async () => { const fakePolicy = createRandomPolicy(0); const fakeCategories = createRandomPolicyCategories(3); - const oldCategoryName = Object.keys(fakeCategories)[0]; + const oldCategoryName = Object.keys(fakeCategories).at(0); const newCategoryName = 'Updated category'; mockFetch?.pause?.(); 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); @@ -159,7 +159,7 @@ describe('actions/PolicyCategory', () => { it('Enable category', async () => { const fakePolicy = createRandomPolicy(0); const fakeCategories = createRandomPolicyCategories(3); - const categoryNameToUpdate = Object.keys(fakeCategories)[0]; + const categoryNameToUpdate = Object.keys(fakeCategories).at(0) ?? ''; const categoriesToUpdate = { [categoryNameToUpdate]: { name: categoryNameToUpdate, @@ -209,7 +209,7 @@ describe('actions/PolicyCategory', () => { it('Delete category', async () => { const fakePolicy = createRandomPolicy(0); const fakeCategories = createRandomPolicyCategories(3); - const categoryNameToDelete = Object.keys(fakeCategories)[0]; + const categoryNameToDelete = Object.keys(fakeCategories).at(0) ?? ''; const categoriesToDelete = [categoryNameToDelete]; mockFetch?.pause?.(); Onyx.set(`${ONYXKEYS.COLLECTION.POLICY}${fakePolicy.id}`, fakePolicy); diff --git a/tests/actions/PolicyTagTest.ts b/tests/actions/PolicyTagTest.ts index 19749c7eb644..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)[0].orderWeight, + Object.values(fakePolicyTags).at(0)?.orderWeight ?? 0, ); return waitForBatchedUpdates(); }) @@ -245,7 +245,7 @@ describe('actions/Policy', () => { newName: newTagListName, }, fakePolicyTags, - Object.values(fakePolicyTags)[0].orderWeight, + Object.values(fakePolicyTags).at(0)?.orderWeight ?? 0, ); return waitForBatchedUpdates(); }) @@ -506,7 +506,7 @@ describe('actions/Policy', () => { const tagListName = 'Fake tag'; const fakePolicyTags = createRandomPolicyTags(tagListName, 2); - const oldTagName = Object.keys(fakePolicyTags?.[tagListName]?.tags)[0]; + const oldTagName = Object.keys(fakePolicyTags?.[tagListName]?.tags).at(0); const newTagName = 'New tag'; mockFetch?.pause?.(); @@ -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); @@ -574,7 +574,7 @@ describe('actions/Policy', () => { const tagListName = 'Fake tag'; const fakePolicyTags = createRandomPolicyTags(tagListName, 2); - const oldTagName = Object.keys(fakePolicyTags?.[tagListName]?.tags)[0]; + const oldTagName = Object.keys(fakePolicyTags?.[tagListName]?.tags).at(0) ?? ''; const newTagName = 'New tag'; mockFetch?.pause?.(); diff --git a/tests/actions/ReportTest.ts b/tests/actions/ReportTest.ts index a099348257f0..fe1b528245d1 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'; diff --git a/tests/e2e/compare/output/markdownTable.ts b/tests/e2e/compare/output/markdownTable.ts index 51f0beeb6979..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[rowIndex].length > mostCellsPerRow) { - mostCellsPerRow = table[rowIndex].length; + const rowData = table.at(rowIndex) ?? []; + + if (rowData.length > mostCellsPerRow) { + mostCellsPerRow = rowData.length; } - while (++columnIndex < table[rowIndex].length) { - const cell = serialize(table[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[columnIndex] === undefined || size > longestCellByColumn[columnIndex]) { + if (longestCellByColumn.at(columnIndex) === undefined || size > (longestCellByColumn.at(columnIndex) ?? 0)) { longestCellByColumn[columnIndex] = size; } } @@ -228,7 +230,7 @@ function markdownTable(table: Array>, options: if (typeof align === 'object' && 'length' in align) { while (++columnIndex < mostCellsPerRow) { - alignments[columnIndex] = toAlignment(align[columnIndex]); + alignments[columnIndex] = toAlignment(align.at(columnIndex)); } } else { const code = toAlignment(align); @@ -244,7 +246,7 @@ function markdownTable(table: Array>, options: const sizes: number[] = []; while (++columnIndex < mostCellsPerRow) { - const code = alignments[columnIndex]; + const code = alignments.at(columnIndex); let before = ''; let after = ''; @@ -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[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[columnIndex]) { + if (size > (longestCellByColumn.at(columnIndex) ?? 0)) { longestCellByColumn[columnIndex] = size; } @@ -283,19 +285,19 @@ function markdownTable(table: Array>, options: const lines: string[] = []; while (++rowIndex < cellMatrix.length) { - const matrixRow = cellMatrix[rowIndex]; - const matrixSizes = sizeMatrix[rowIndex]; + const matrixRow = cellMatrix.at(rowIndex); + const matrixSizes = sizeMatrix.at(rowIndex); columnIndex = -1; const line: string[] = []; while (++columnIndex < mostCellsPerRow) { - const cell = matrixRow[columnIndex] || ''; + const cell = matrixRow?.at(columnIndex) ?? ''; let before = ''; let after = ''; if (options.alignDelimiters !== false) { - const size = longestCellByColumn[columnIndex] - (matrixSizes[columnIndex] || 0); - const code = alignments[columnIndex]; + const size = (longestCellByColumn.at(columnIndex) ?? 0) - (matrixSizes?.at(columnIndex) ?? 0); + const code = alignments.at(columnIndex); if (code === 114 /* `r` */) { before = ' '.repeat(size); diff --git a/tests/e2e/measure/math.ts b/tests/e2e/measure/math.ts index 1cb6661007ea..4c4ef1c9ff1e 100644 --- a/tests/e2e/measure/math.ts +++ b/tests/e2e/measure/math.ts @@ -14,11 +14,11 @@ const filterOutliersViaIQR = (data: Entries): Entries => { const values = data.slice().sort((a, b) => a - b); if ((values.length / 4) % 1 === 0) { - 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]); + q1 = (1 / 2) * ((values.at(values.length / 4) ?? 0) + (values.at(values.length / 4 + 1) ?? 0)); + q3 = (1 / 2) * ((values.at(values.length * (3 / 4)) ?? 0) + (values.at(values.length * (3 / 4) + 1) ?? 0)); } else { - q1 = values[Math.floor(values.length / 4 + 1)]; - q3 = values[Math.ceil(values.length * (3 / 4) + 1)]; + q1 = values.at(Math.floor(values.length / 4 + 1)) ?? 0; + q3 = values.at(Math.ceil(values.length * (3 / 4) + 1)) ?? 0; } const iqr = q3 - q1; diff --git a/tests/e2e/testRunner.ts b/tests/e2e/testRunner.ts index 41d1d039913d..1ee7925fafa6 100644 --- a/tests/e2e/testRunner.ts +++ b/tests/e2e/testRunner.ts @@ -43,7 +43,7 @@ const getArg = (argName: string): string | undefined => { if (argIndex === -1) { return undefined; } - return args[argIndex + 1]; + return args.at(argIndex + 1); }; let config = defaultConfig; @@ -218,7 +218,7 @@ const runTests = async (): Promise => { // Run the tests const tests = Object.keys(config.TESTS_CONFIG); for (let testIndex = 0; testIndex < tests.length; testIndex++) { - const test = Object.values(config.TESTS_CONFIG)[testIndex]; + const test = Object.values(config.TESTS_CONFIG).at(testIndex); // re-instal app for each new test suite await installApp(config.MAIN_APP_PACKAGE, mainAppPath); @@ -229,7 +229,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; } @@ -244,7 +244,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 @@ -289,7 +289,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/e2e/utils/installApp.ts b/tests/e2e/utils/installApp.ts index 9c2f82e47336..3a4b34231ac9 100644 --- a/tests/e2e/utils/installApp.ts +++ b/tests/e2e/utils/installApp.ts @@ -26,7 +26,7 @@ export default function (packageName: string, path: string, platform = 'android' execAsync(`adb install ${path}`).then(() => // and grant push notifications permissions right away (the popup may block e2e tests sometimes) // eslint-disable-next-line @typescript-eslint/no-unused-vars - execAsync(`adb shell pm grant ${packageName.split('/')[0]} android.permission.POST_NOTIFICATIONS`).catch((_: ExecException) => + execAsync(`adb shell pm grant ${packageName.split('/').at(0)} android.permission.POST_NOTIFICATIONS`).catch((_: ExecException) => // in case of error - just log it and continue (if we request this permission on Android < 13 it'll fail because there is no such permission) Logger.warn( 'Failed to grant push notifications permissions. It might be due to the fact that push-notifications permission type is not supported on this OS version yet. Continue tests execution...', diff --git a/tests/perf-test/ReportActionsList.perf-test.tsx b/tests/perf-test/ReportActionsList.perf-test.tsx index 8f4c39d5f7a2..2928f73c53ce 100644 --- a/tests/perf-test/ReportActionsList.perf-test.tsx +++ b/tests/perf-test/ReportActionsList.perf-test.tsx @@ -82,7 +82,7 @@ beforeEach(() => { function ReportActionsListWrapper() { const reportActions = ReportTestUtils.getMockedSortedReportActions(500); - const lastVisibleActionCreated = reportActions[0].created; + const lastVisibleActionCreated = reportActions.at(0)?.created; const report = { ...LHNTestUtilsModule.getFakeReport(), lastVisibleActionCreated, diff --git a/tests/ui/UnreadIndicatorsTest.tsx b/tests/ui/UnreadIndicatorsTest.tsx index c9cdd81cf9ba..da9c2a524a5f 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[index], 'press'); + const optionRow = screen.queryAllByAccessibilityHint(hintText).at(index); + if (!optionRow) { + return; + } + fireEvent(optionRow, 'press'); await waitForBatchedUpdatesWithAct(); } @@ -93,7 +96,7 @@ function areYouOnChatListScreen(): boolean { const hintText = Localize.translateLocal('sidebarScreen.listOfChats'); const sidebarLinks = screen.queryAllByLabelText(hintText); - return !sidebarLinks?.[0]?.props?.accessibilityElementsHidden; + return !sidebarLinks?.at(0)?.props?.accessibilityElementsHidden; } const REPORT_ID = '1'; @@ -234,7 +237,7 @@ describe('Unread Indicators', () => { const newMessageLineIndicatorHintText = Localize.translateLocal('accessibilityHints.newMessageLineIndicator'); const unreadIndicator = screen.queryAllByLabelText(newMessageLineIndicatorHintText); expect(unreadIndicator).toHaveLength(1); - const reportActionID = unreadIndicator[0]?.props?.['data-action-id'] as string; + const reportActionID = unreadIndicator.at(0)?.props?.['data-action-id'] as string; expect(reportActionID).toBe('4'); // Scroll up and verify that the "New messages" badge appears scrollUpToRevealNewMessagesBadge(); @@ -355,11 +358,11 @@ describe('Unread Indicators', () => { const displayNameHintTexts = Localize.translateLocal('accessibilityHints.chatUserDisplayNames'); const displayNameTexts = screen.queryAllByLabelText(displayNameHintTexts); expect(displayNameTexts).toHaveLength(2); - const firstReportOption = displayNameTexts[0]; + const firstReportOption = displayNameTexts.at(0); expect((firstReportOption?.props?.style as TextStyle)?.fontWeight).toBe(FontUtils.fontWeight.bold); expect(screen.getByText('C User')).toBeOnTheScreen(); - const secondReportOption = displayNameTexts[1]; + const secondReportOption = displayNameTexts.at(1); expect((secondReportOption?.props?.style as TextStyle)?.fontWeight).toBe(FontUtils.fontWeight.bold); expect(screen.getByText('B User')).toBeOnTheScreen(); @@ -373,9 +376,9 @@ describe('Unread Indicators', () => { const hintText = Localize.translateLocal('accessibilityHints.chatUserDisplayNames'); const displayNameTexts = screen.queryAllByLabelText(hintText); expect(displayNameTexts).toHaveLength(2); - expect((displayNameTexts[0]?.props?.style as TextStyle)?.fontWeight).toBe(FontUtils.fontWeight.normal); - expect(screen.getAllByText('C User')[0]).toBeOnTheScreen(); - expect((displayNameTexts[1]?.props?.style as TextStyle)?.fontWeight).toBe(FontUtils.fontWeight.bold); + expect((displayNameTexts.at(0)?.props?.style as TextStyle)?.fontWeight).toBe(FontUtils.fontWeight.normal); + expect(screen.getAllByText('C User').at(0)).toBeOnTheScreen(); + expect((displayNameTexts.at(1)?.props?.style as TextStyle)?.fontWeight).toBe(FontUtils.fontWeight.bold); expect(screen.getByText('B User')).toBeOnTheScreen(); })); @@ -394,7 +397,7 @@ describe('Unread Indicators', () => { const newMessageLineIndicatorHintText = Localize.translateLocal('accessibilityHints.newMessageLineIndicator'); const unreadIndicator = screen.queryAllByLabelText(newMessageLineIndicatorHintText); expect(unreadIndicator).toHaveLength(1); - const reportActionID = unreadIndicator[0]?.props?.['data-action-id'] as string; + const reportActionID = unreadIndicator.at(0)?.props?.['data-action-id'] as string; expect(reportActionID).toBe('3'); // Scroll up and verify the new messages badge appears scrollUpToRevealNewMessagesBadge(); @@ -407,7 +410,7 @@ describe('Unread Indicators', () => { const hintText = Localize.translateLocal('accessibilityHints.chatUserDisplayNames'); const displayNameTexts = screen.queryAllByLabelText(hintText); expect(displayNameTexts).toHaveLength(1); - expect((displayNameTexts[0]?.props?.style as TextStyle)?.fontWeight).toBe(FontUtils.fontWeight.bold); + expect((displayNameTexts.at(0)?.props?.style as TextStyle)?.fontWeight).toBe(FontUtils.fontWeight.bold); expect(screen.getByText('B User')).toBeOnTheScreen(); // Navigate to the report again and back to the sidebar @@ -419,7 +422,7 @@ describe('Unread Indicators', () => { const hintText = Localize.translateLocal('accessibilityHints.chatUserDisplayNames'); const displayNameTexts = screen.queryAllByLabelText(hintText); expect(displayNameTexts).toHaveLength(1); - expect((displayNameTexts[0]?.props?.style as TextStyle)?.fontWeight).toBe(undefined); + expect((displayNameTexts.at(0)?.props?.style as TextStyle)?.fontWeight).toBe(undefined); expect(screen.getByText('B User')).toBeOnTheScreen(); // Navigate to the report again and verify the new line indicator is missing @@ -534,7 +537,7 @@ describe('Unread Indicators', () => { expect(alternateText).toHaveLength(1); // This message is visible on the sidebar and the report screen, so there are two occurrences. - expect(screen.getAllByText('Current User Comment 1')[0]).toBeOnTheScreen(); + expect(screen.getAllByText('Current User Comment 1').at(0)).toBeOnTheScreen(); if (lastReportAction) { Report.deleteReportComment(REPORT_ID, lastReportAction); @@ -545,7 +548,7 @@ describe('Unread Indicators', () => { const hintText = Localize.translateLocal('accessibilityHints.lastChatMessagePreview'); const alternateText = screen.queryAllByLabelText(hintText); expect(alternateText).toHaveLength(1); - expect(screen.getAllByText('Comment 9')[0]).toBeOnTheScreen(); + expect(screen.getAllByText('Comment 9').at(0)).toBeOnTheScreen(); }) ); }); diff --git a/tests/unit/APITest.ts b/tests/unit/APITest.ts index 74be6c742f51..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'; diff --git a/tests/unit/CalendarPickerTest.tsx b/tests/unit/CalendarPickerTest.tsx index 9c471be5b035..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[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[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 2bab87550357..73bd27bf99e9 100644 --- a/tests/unit/GithubUtilsTest.ts +++ b/tests/unit/GithubUtilsTest.ts @@ -425,11 +425,11 @@ describe('GithubUtils', () => { // Valid output which will be reused in the deploy blocker tests const allVerifiedExpectedOutput = `${baseExpectedOutput}` + - `${closedCheckbox}${basePRList[2]}` + - `${lineBreak}${closedCheckbox}${basePRList[0]}` + - `${lineBreak}${closedCheckbox}${basePRList[1]}` + - `${lineBreak}${closedCheckbox}${basePRList[4]}` + - `${lineBreak}${closedCheckbox}${basePRList[5]}` + + `${closedCheckbox}${basePRList.at(2)}` + + `${lineBreak}${closedCheckbox}${basePRList.at(0)}` + + `${lineBreak}${closedCheckbox}${basePRList.at(1)}` + + `${lineBreak}${closedCheckbox}${basePRList.at(4)}` + + `${lineBreak}${closedCheckbox}${basePRList.at(5)}` + `${lineBreak}`; test('Test no verified PRs', () => { @@ -439,11 +439,11 @@ describe('GithubUtils', () => { } expect(issue.issueBody).toBe( `${baseExpectedOutput}` + - `${openCheckbox}${basePRList[2]}` + - `${lineBreak}${openCheckbox}${basePRList[0]}` + - `${lineBreak}${openCheckbox}${basePRList[1]}` + - `${lineBreak}${closedCheckbox}${basePRList[4]}` + - `${lineBreak}${closedCheckbox}${basePRList[5]}` + + `${openCheckbox}${basePRList.at(2)}` + + `${lineBreak}${openCheckbox}${basePRList.at(0)}` + + `${lineBreak}${openCheckbox}${basePRList.at(1)}` + + `${lineBreak}${closedCheckbox}${basePRList.at(4)}` + + `${lineBreak}${closedCheckbox}${basePRList.at(5)}` + `${lineBreakDouble}${deployerVerificationsHeader}` + `${lineBreak}${openCheckbox}${timingDashboardVerification}` + `${lineBreak}${openCheckbox}${firebaseVerification}` + @@ -455,18 +455,18 @@ describe('GithubUtils', () => { }); test('Test some verified PRs', () => { - githubUtils.generateStagingDeployCashBodyAndAssignees(tag, basePRList, [basePRList[0]]).then((issue) => { + githubUtils.generateStagingDeployCashBodyAndAssignees(tag, basePRList, [basePRList.at(0) ?? '']).then((issue) => { if (typeof issue !== 'object') { return; } expect(issue.issueBody).toBe( `${baseExpectedOutput}` + - `${openCheckbox}${basePRList[2]}` + - `${lineBreak}${closedCheckbox}${basePRList[0]}` + - `${lineBreak}${openCheckbox}${basePRList[1]}` + - `${lineBreak}${closedCheckbox}${basePRList[4]}` + - `${lineBreak}${closedCheckbox}${basePRList[5]}` + + `${openCheckbox}${basePRList.at(2)}` + + `${lineBreak}${closedCheckbox}${basePRList.at(0)}` + + `${lineBreak}${openCheckbox}${basePRList.at(1)}` + + `${lineBreak}${closedCheckbox}${basePRList.at(4)}` + + `${lineBreak}${closedCheckbox}${basePRList.at(5)}` + `${lineBreakDouble}${deployerVerificationsHeader}` + `${lineBreak}${openCheckbox}${timingDashboardVerification}` + `${lineBreak}${openCheckbox}${firebaseVerification}` + @@ -504,8 +504,8 @@ describe('GithubUtils', () => { expect(issue.issueBody).toBe( `${allVerifiedExpectedOutput}` + `${lineBreak}${deployBlockerHeader}` + - `${lineBreak}${openCheckbox}${baseDeployBlockerList[0]}` + - `${lineBreak}${openCheckbox}${baseDeployBlockerList[1]}` + + `${lineBreak}${openCheckbox}${baseDeployBlockerList.at(0)}` + + `${lineBreak}${openCheckbox}${baseDeployBlockerList.at(1)}` + `${lineBreakDouble}${deployerVerificationsHeader}` + `${lineBreak}${openCheckbox}${timingDashboardVerification}` + `${lineBreak}${openCheckbox}${firebaseVerification}` + @@ -517,7 +517,7 @@ describe('GithubUtils', () => { }); test('Test some resolved deploy blockers', () => { - githubUtils.generateStagingDeployCashBodyAndAssignees(tag, basePRList, basePRList, baseDeployBlockerList, [baseDeployBlockerList[0]]).then((issue) => { + githubUtils.generateStagingDeployCashBodyAndAssignees(tag, basePRList, basePRList, baseDeployBlockerList, [baseDeployBlockerList.at(0) ?? '']).then((issue) => { if (typeof issue !== 'object') { return; } @@ -525,8 +525,8 @@ describe('GithubUtils', () => { expect(issue.issueBody).toBe( `${allVerifiedExpectedOutput}` + `${lineBreak}${deployBlockerHeader}` + - `${lineBreak}${closedCheckbox}${baseDeployBlockerList[0]}` + - `${lineBreak}${openCheckbox}${baseDeployBlockerList[1]}` + + `${lineBreak}${closedCheckbox}${baseDeployBlockerList.at(0)}` + + `${lineBreak}${openCheckbox}${baseDeployBlockerList.at(1)}` + `${lineBreakDouble}${deployerVerificationsHeader}` + `${lineBreak}${openCheckbox}${timingDashboardVerification}` + `${lineBreak}${openCheckbox}${firebaseVerification}` + @@ -544,14 +544,14 @@ describe('GithubUtils', () => { } expect(issue.issueBody).toBe( `${baseExpectedOutput}` + - `${closedCheckbox}${basePRList[2]}` + - `${lineBreak}${closedCheckbox}${basePRList[0]}` + - `${lineBreak}${closedCheckbox}${basePRList[1]}` + - `${lineBreak}${closedCheckbox}${basePRList[4]}` + - `${lineBreak}${closedCheckbox}${basePRList[5]}` + + `${closedCheckbox}${basePRList.at(2)}` + + `${lineBreak}${closedCheckbox}${basePRList.at(0)}` + + `${lineBreak}${closedCheckbox}${basePRList.at(1)}` + + `${lineBreak}${closedCheckbox}${basePRList.at(4)}` + + `${lineBreak}${closedCheckbox}${basePRList.at(5)}` + `${lineBreakDouble}${deployBlockerHeader}` + - `${lineBreak}${closedCheckbox}${baseDeployBlockerList[0]}` + - `${lineBreak}${closedCheckbox}${baseDeployBlockerList[1]}` + + `${lineBreak}${closedCheckbox}${baseDeployBlockerList.at(0)}` + + `${lineBreak}${closedCheckbox}${baseDeployBlockerList.at(1)}` + `${lineBreakDouble}${deployerVerificationsHeader}` + `${lineBreak}${openCheckbox}${timingDashboardVerification}` + `${lineBreak}${openCheckbox}${firebaseVerification}` + @@ -570,14 +570,14 @@ describe('GithubUtils', () => { expect(issue.issueBody).toBe( `${baseExpectedOutput}` + - `${openCheckbox}${basePRList[2]}` + - `${lineBreak}${openCheckbox}${basePRList[0]}` + - `${lineBreak}${openCheckbox}${basePRList[1]}` + - `${lineBreak}${closedCheckbox}${basePRList[4]}` + - `${lineBreak}${closedCheckbox}${basePRList[5]}` + + `${openCheckbox}${basePRList.at(2)}` + + `${lineBreak}${openCheckbox}${basePRList.at(0)}` + + `${lineBreak}${openCheckbox}${basePRList.at(1)}` + + `${lineBreak}${closedCheckbox}${basePRList.at(4)}` + + `${lineBreak}${closedCheckbox}${basePRList.at(5)}` + `${lineBreak}${internalQAHeader}` + - `${lineBreak}${openCheckbox}${internalQAPRList[0]}${assignOctocat}` + - `${lineBreak}${openCheckbox}${internalQAPRList[1]}${assignOctocat}` + + `${lineBreak}${openCheckbox}${internalQAPRList.at(0)}${assignOctocat}` + + `${lineBreak}${openCheckbox}${internalQAPRList.at(1)}${assignOctocat}` + `${lineBreakDouble}${deployerVerificationsHeader}` + `${lineBreak}${openCheckbox}${timingDashboardVerification}` + `${lineBreak}${openCheckbox}${firebaseVerification}` + @@ -589,21 +589,21 @@ describe('GithubUtils', () => { }); test('Test some verified internalQA PRs', () => { - githubUtils.generateStagingDeployCashBodyAndAssignees(tag, [...basePRList, ...internalQAPRList], [], [], [], [internalQAPRList[0]]).then((issue) => { + githubUtils.generateStagingDeployCashBodyAndAssignees(tag, [...basePRList, ...internalQAPRList], [], [], [], [internalQAPRList.at(0) ?? '']).then((issue) => { if (typeof issue !== 'object') { return; } expect(issue.issueBody).toBe( `${baseExpectedOutput}` + - `${openCheckbox}${basePRList[2]}` + - `${lineBreak}${openCheckbox}${basePRList[0]}` + - `${lineBreak}${openCheckbox}${basePRList[1]}` + - `${lineBreak}${closedCheckbox}${basePRList[4]}` + - `${lineBreak}${closedCheckbox}${basePRList[5]}` + + `${openCheckbox}${basePRList.at(2)}` + + `${lineBreak}${openCheckbox}${basePRList.at(0)}` + + `${lineBreak}${openCheckbox}${basePRList.at(1)}` + + `${lineBreak}${closedCheckbox}${basePRList.at(4)}` + + `${lineBreak}${closedCheckbox}${basePRList.at(5)}` + `${lineBreak}${internalQAHeader}` + - `${lineBreak}${closedCheckbox}${internalQAPRList[0]}${assignOctocat}` + - `${lineBreak}${openCheckbox}${internalQAPRList[1]}${assignOctocat}` + + `${lineBreak}${closedCheckbox}${internalQAPRList.at(0)}${assignOctocat}` + + `${lineBreak}${openCheckbox}${internalQAPRList.at(1)}${assignOctocat}` + `${lineBreakDouble}${deployerVerificationsHeader}` + `${lineBreak}${openCheckbox}${timingDashboardVerification}` + `${lineBreak}${openCheckbox}${firebaseVerification}` + diff --git a/tests/unit/MiddlewareTest.ts b/tests/unit/MiddlewareTest.ts index be13ded01342..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'; diff --git a/tests/unit/NetworkTest.ts b/tests/unit/NetworkTest.ts index e79a7c93c0e5..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[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 d27a53da69ce..7d917123d9aa 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[0].text).toBe('Black Widow'); - expect(results.personalDetails[1].text).toBe('Invisible Woman'); - expect(results.personalDetails[2].text).toBe('Spider-Man'); - expect(results.personalDetails[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[0].text).toBe('Black Panther'); - expect(results.personalDetails[1].text).toBe('Black Widow'); - expect(results.personalDetails[2].text).toBe('Captain America'); - expect(results.personalDetails[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[0].text).toBe('Black Widow'); - expect(results.personalDetails[1].text).toBe('Invisible Woman'); - expect(results.personalDetails[2].text).toBe('Spider-Man'); - expect(results.personalDetails[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[0].text).toBe('Black Panther'); - expect(results.personalDetails[1].text).toBe('Black Widow'); - expect(results.personalDetails[2].text).toBe('Captain America'); - expect(results.personalDetails[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[0].text).toBe('Mister Fantastic'); - expect(formattedMembers[1].text).toBe('Iron Man'); - expect(formattedMembers[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[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,10 +2619,10 @@ describe('OptionsListUtils', () => { const filteredOptions = OptionsListUtils.filterOptions(options, searchText, {sortByReportTypeInSearch: true}); expect(filteredOptions.recentReports.length).toBe(4); - expect(filteredOptions.recentReports[0].text).toBe('Invisible Woman'); - expect(filteredOptions.recentReports[1].text).toBe('Spider-Man'); - expect(filteredOptions.recentReports[2].text).toBe('Black Widow'); - expect(filteredOptions.recentReports[3].text).toBe('Mister Fantastic, Invisible Woman'); + 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'); }); it('should filter users by email', () => { @@ -2632,7 +2632,7 @@ describe('OptionsListUtils', () => { const filteredOptions = OptionsListUtils.filterOptions(options, searchText); expect(filteredOptions.recentReports.length).toBe(1); - expect(filteredOptions.recentReports[0].text).toBe('Mr Sinister'); + expect(filteredOptions.recentReports.at(0)?.text).toBe('Mr Sinister'); }); it('should find archived chats', () => { @@ -2641,7 +2641,7 @@ describe('OptionsListUtils', () => { const filteredOptions = OptionsListUtils.filterOptions(options, searchText); expect(filteredOptions.recentReports.length).toBe(1); - expect(!!filteredOptions.recentReports[0].private_isArchived).toBe(true); + expect(!!filteredOptions.recentReports.at(0)?.private_isArchived).toBe(true); }); it('should filter options by email if dot is skipped in the email', () => { @@ -2652,7 +2652,7 @@ describe('OptionsListUtils', () => { const filteredOptions = OptionsListUtils.filterOptions(options, searchText, {sortByReportTypeInSearch: true}); expect(filteredOptions.recentReports.length).toBe(1); - expect(filteredOptions.recentReports[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', () => { @@ -2662,7 +2662,7 @@ describe('OptionsListUtils', () => { const filteredOptions = OptionsListUtils.filterOptions(options, searchText); expect(filteredOptions.recentReports.length).toBe(1); - expect(filteredOptions.recentReports[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', () => { @@ -2672,7 +2672,7 @@ describe('OptionsListUtils', () => { const filteredOptions = OptionsListUtils.filterOptions(options, searchText); expect(filteredOptions.recentReports.length).toBe(1); - expect(filteredOptions.recentReports[0].login).toBe(searchText); + expect(filteredOptions.recentReports.at(0)?.login).toBe(searchText); }); it('should prioritize options with matching display name over chatrooms', () => { @@ -2683,7 +2683,7 @@ describe('OptionsListUtils', () => { const filterOptions = OptionsListUtils.filterOptions(options, searchText); expect(filterOptions.recentReports.length).toBe(2); - expect(filterOptions.recentReports[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', () => { @@ -2693,8 +2693,8 @@ describe('OptionsListUtils', () => { const filteredOptions = OptionsListUtils.filterOptions(options, searchText); expect(filteredOptions.recentReports.length).toBe(2); - expect(filteredOptions.recentReports[0].text).toBe('Mister Fantastic'); - expect(filteredOptions.recentReports[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', () => { @@ -2752,7 +2752,7 @@ describe('OptionsListUtils', () => { const filteredOptions = OptionsListUtils.filterOptions(options, 'peterparker@expensify.com'); expect(filteredOptions.personalDetails.length).toBe(1); - expect(filteredOptions.personalDetails[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)', () => { @@ -2809,7 +2809,7 @@ describe('OptionsListUtils', () => { expect(filteredOptions.recentReports.length).toBe(0); expect(filteredOptions.personalDetails.length).toBe(1); - expect(filteredOptions.personalDetails[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)', () => { @@ -2817,10 +2817,10 @@ describe('OptionsListUtils', () => { const filteredOptions = OptionsListUtils.filterOptions(options, '.com'); expect(filteredOptions.recentReports.length).toBe(5); - expect(filteredOptions.recentReports[0].text).toBe('Captain America'); + expect(filteredOptions.recentReports.at(0)?.text).toBe('Captain America'); expect(filteredOptions.personalDetails.length).toBe(4); - expect(filteredOptions.personalDetails[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)', () => { @@ -2899,7 +2899,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[0].text).toBe('Spider-Man'); + expect(filteredOptions.recentReports.at(0)?.text).toBe('Spider-Man'); expect(filteredOptions.personalDetails.length).toBe(0); }); @@ -2909,10 +2909,10 @@ describe('OptionsListUtils', () => { expect(filteredOptions.personalDetails.length).toBe(4); expect(filteredOptions.recentReports.length).toBe(5); - expect(filteredOptions.personalDetails[0].login).toBe('natasharomanoff@expensify.com'); - expect(filteredOptions.recentReports[0].text).toBe('Captain America'); - expect(filteredOptions.recentReports[1].text).toBe('Mr Sinister'); - expect(filteredOptions.recentReports[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)', () => { @@ -2920,7 +2920,7 @@ describe('OptionsListUtils', () => { const filteredOptions = OptionsListUtils.filterOptions(options, 'spider'); expect(filteredOptions.recentReports.length).toBe(1); - expect(filteredOptions.recentReports[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)', () => { @@ -2928,8 +2928,8 @@ describe('OptionsListUtils', () => { const filteredOptions = OptionsListUtils.filterOptions(options, 'fantastic'); expect(filteredOptions.recentReports.length).toBe(2); - expect(filteredOptions.recentReports[0].text).toBe('Mister Fantastic'); - expect(filteredOptions.recentReports[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)) @@ -2939,7 +2939,7 @@ describe('OptionsListUtils', () => { const filteredResults = OptionsListUtils.filterOptions(results, 'barry.allen@expensify.com', {sortByReportTypeInSearch: true}); expect(filteredResults.recentReports.length).toBe(1); - expect(filteredResults.recentReports[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 2008cf45b746..4ba6e7ce8576 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[0].source).toBeInstanceOf(Function); - expect(participants[0].name).toBe('(833) 240-3627'); - expect(participants[0].id).toBe(4); - expect(participants[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[1].source).toBeInstanceOf(Function); - expect(participants[1].name).toBe('floki@vikings.net'); - expect(participants[1].id).toBe(2); - expect(participants[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[0].displayName).toBe('(833) 240-3627'); - expect(participants[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[2].displayName).toBe('Lagertha Lothbrok'); - expect(participants[2].login).toBe('lagertha@vikings.net'); - expect(participants[2].accountID).toBe(3); - expect(participants[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[4].displayName).toBe('Ragnar Lothbrok'); - expect(participants[4].login).toBe('ragnar@vikings.net'); - expect(participants[4].accountID).toBe(1); - expect(participants[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[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[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); @@ -563,7 +563,7 @@ describe('ReportUtils', () => { statusNum: CONST.REPORT.STATUS_NUM.SUBMITTED, managerID: currentUserAccountID, }; - const moneyRequestOptions = ReportUtils.temporary_getMoneyRequestOptions(report, undefined, [currentUserAccountID, participantsAccountIDs[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); }); @@ -576,7 +576,7 @@ describe('ReportUtils', () => { statusNum: CONST.REPORT.STATUS_NUM.SUBMITTED, managerID: currentUserAccountID, }; - const moneyRequestOptions = ReportUtils.temporary_getMoneyRequestOptions(report, undefined, [currentUserAccountID, participantsAccountIDs[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); }); @@ -625,7 +625,7 @@ describe('ReportUtils', () => { outputCurrency: '', isPolicyExpenseChatEnabled: false, } as const; - const moneyRequestOptions = ReportUtils.temporary_getMoneyRequestOptions(report, paidPolicy, [currentUserAccountID, participantsAccountIDs[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); @@ -640,7 +640,7 @@ describe('ReportUtils', () => { statusNum: CONST.REPORT.STATUS_NUM.SUBMITTED, managerID: currentUserAccountID, }; - const moneyRequestOptions = ReportUtils.temporary_getMoneyRequestOptions(report, undefined, [currentUserAccountID, participantsAccountIDs[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); }); @@ -653,7 +653,7 @@ describe('ReportUtils', () => { statusNum: CONST.REPORT.STATUS_NUM.SUBMITTED, managerID: currentUserAccountID, }; - const moneyRequestOptions = ReportUtils.temporary_getMoneyRequestOptions(report, undefined, [currentUserAccountID, participantsAccountIDs[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); }); @@ -687,7 +687,7 @@ describe('ReportUtils', () => { policyID: paidPolicy.id, managerID: currentUserAccountID, }; - const moneyRequestOptions = ReportUtils.temporary_getMoneyRequestOptions(report, paidPolicy, [currentUserAccountID, participantsAccountIDs[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); @@ -701,7 +701,7 @@ describe('ReportUtils', () => { ...LHNTestUtils.getFakeReport(), type: CONST.REPORT.TYPE.CHAT, }; - const moneyRequestOptions = ReportUtils.temporary_getMoneyRequestOptions(report, undefined, [currentUserAccountID, participantsAccountIDs[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); @@ -851,7 +851,7 @@ describe('ReportUtils', () => { const reportActionCollectionDataSet = toCollectionDataSet( ONYXKEYS.COLLECTION.REPORT_ACTIONS, reportActions.map((reportAction) => ({[reportAction.reportActionID]: reportAction})), - (actions) => Object.values(actions)[0].reportActionID, + (actions) => Object.values(actions).at(0)?.reportActionID ?? '', ); Onyx.multiSet({ ...reportCollectionDataSet, @@ -864,13 +864,13 @@ describe('ReportUtils', () => { it('should return correctly all ancestors of a thread report', () => { const resultAncestors = [ - {report: reports[0], reportAction: reportActions[0], shouldDisplayNewMarker: false}, - {report: reports[1], reportAction: reportActions[1], shouldDisplayNewMarker: false}, - {report: reports[2], reportAction: reportActions[2], shouldDisplayNewMarker: false}, - {report: reports[3], reportAction: reportActions[3], shouldDisplayNewMarker: false}, + {report: reports.at(0), reportAction: reportActions.at(0), shouldDisplayNewMarker: false}, + {report: reports.at(1), reportAction: reportActions.at(1), shouldDisplayNewMarker: false}, + {report: reports.at(2), reportAction: reportActions.at(2), shouldDisplayNewMarker: false}, + {report: reports.at(3), reportAction: reportActions.at(3), shouldDisplayNewMarker: false}, ]; - expect(ReportUtils.getAllAncestorReportActions(reports[4])).toEqual(resultAncestors); + expect(ReportUtils.getAllAncestorReportActions(reports.at(4))).toEqual(resultAncestors); }); }); diff --git a/tests/unit/RequestTest.ts b/tests/unit/RequestTest.ts index 74c42cdf02c4..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[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/SidebarOrderTest.ts b/tests/unit/SidebarOrderTest.ts index 225cf6d2194d..1de2654c09bf 100644 --- a/tests/unit/SidebarOrderTest.ts +++ b/tests/unit/SidebarOrderTest.ts @@ -135,9 +135,9 @@ describe('Sidebar', () => { const displayNames = screen.queryAllByLabelText(hintText); expect(displayNames).toHaveLength(3); - expect(displayNames[0]).toHaveTextContent('Email Four'); - expect(displayNames[1]).toHaveTextContent('Email Three'); - expect(displayNames[2]).toHaveTextContent('Email Two'); + expect(displayNames.at(0)).toHaveTextContent('Email Four'); + expect(displayNames.at(1)).toHaveTextContent('Email Three'); + expect(displayNames.at(2)).toHaveTextContent('Email Two'); }) ); }); @@ -188,9 +188,9 @@ describe('Sidebar', () => { const hintText = Localize.translateLocal('accessibilityHints.chatUserDisplayNames'); const displayNames = screen.queryAllByLabelText(hintText); expect(displayNames).toHaveLength(3); - expect(displayNames[0]).toHaveTextContent('Email Two'); // this has `hasDraft` flag enabled so it will be on top - expect(displayNames[1]).toHaveTextContent('Email Four'); - expect(displayNames[2]).toHaveTextContent('Email Three'); + expect(displayNames.at(0)).toHaveTextContent('Email Two'); // this has `hasDraft` flag enabled so it will be on top + expect(displayNames.at(1)).toHaveTextContent('Email Four'); + expect(displayNames.at(2)).toHaveTextContent('Email Three'); }) ); }); @@ -239,9 +239,9 @@ describe('Sidebar', () => { const hintText = Localize.translateLocal('accessibilityHints.chatUserDisplayNames'); const displayNames = screen.queryAllByLabelText(hintText); expect(displayNames).toHaveLength(3); - expect(displayNames[0]).toHaveTextContent('Email Two'); - expect(displayNames[1]).toHaveTextContent('Email Four'); - expect(displayNames[2]).toHaveTextContent('Email Three'); + expect(displayNames.at(0)).toHaveTextContent('Email Two'); + expect(displayNames.at(1)).toHaveTextContent('Email Four'); + expect(displayNames.at(2)).toHaveTextContent('Email Three'); }) ); }); @@ -293,10 +293,10 @@ describe('Sidebar', () => { const hintText = Localize.translateLocal('accessibilityHints.chatUserDisplayNames'); const displayNames = screen.queryAllByLabelText(hintText); expect(displayNames).toHaveLength(4); - expect(displayNames[0]).toHaveTextContent(taskReportName); - expect(displayNames[1]).toHaveTextContent('Email Four'); - expect(displayNames[2]).toHaveTextContent('Email Three'); - expect(displayNames[3]).toHaveTextContent('Email Two'); + expect(displayNames.at(0)).toHaveTextContent(taskReportName); + expect(displayNames.at(1)).toHaveTextContent('Email Four'); + expect(displayNames.at(2)).toHaveTextContent('Email Three'); + expect(displayNames.at(3)).toHaveTextContent('Email Two'); }) ); }); @@ -367,10 +367,10 @@ describe('Sidebar', () => { const hintText = Localize.translateLocal('accessibilityHints.chatUserDisplayNames'); const displayNames = screen.queryAllByLabelText(hintText); expect(displayNames).toHaveLength(4); - expect(displayNames[0]).toHaveTextContent('Email Four'); - expect(displayNames[1]).toHaveTextContent('Email Four owes $100.00'); - expect(displayNames[2]).toHaveTextContent('Email Three'); - expect(displayNames[3]).toHaveTextContent('Email Two'); + expect(displayNames.at(0)).toHaveTextContent('Email Four'); + expect(displayNames.at(1)).toHaveTextContent('Email Four owes $100.00'); + expect(displayNames.at(2)).toHaveTextContent('Email Three'); + expect(displayNames.at(3)).toHaveTextContent('Email Two'); }) ); }); @@ -446,10 +446,10 @@ describe('Sidebar', () => { const hintText = Localize.translateLocal('accessibilityHints.chatUserDisplayNames'); const displayNames = screen.queryAllByLabelText(hintText); expect(displayNames).toHaveLength(4); - expect(displayNames[0]).toHaveTextContent('Email One'); - expect(displayNames[1]).toHaveTextContent('Workspace-Test-001 owes $100.00'); - expect(displayNames[2]).toHaveTextContent('Email Three'); - expect(displayNames[3]).toHaveTextContent('Email Two'); + expect(displayNames.at(0)).toHaveTextContent('Email One'); + expect(displayNames.at(1)).toHaveTextContent('Workspace-Test-001 owes $100.00'); + expect(displayNames.at(2)).toHaveTextContent('Email Three'); + expect(displayNames.at(3)).toHaveTextContent('Email Two'); }) ); }); @@ -506,9 +506,9 @@ describe('Sidebar', () => { const hintText = Localize.translateLocal('accessibilityHints.chatUserDisplayNames'); const displayNames = screen.queryAllByLabelText(hintText); expect(displayNames).toHaveLength(3); - expect(displayNames[0]).toHaveTextContent('Email Three'); - expect(displayNames[1]).toHaveTextContent('Email Four'); - expect(displayNames[2]).toHaveTextContent('Email Two'); + expect(displayNames.at(0)).toHaveTextContent('Email Three'); + expect(displayNames.at(1)).toHaveTextContent('Email Four'); + expect(displayNames.at(2)).toHaveTextContent('Email Two'); }) ); }); @@ -674,10 +674,10 @@ describe('Sidebar', () => { expect(displayNames).toHaveLength(4); expect(screen.queryAllByTestId('Pin Icon')).toHaveLength(1); expect(screen.queryAllByTestId('Pencil Icon')).toHaveLength(1); - expect(displayNames[0]).toHaveTextContent('Email Four'); - expect(displayNames[1]).toHaveTextContent('Email Two'); - expect(displayNames[2]).toHaveTextContent('Email Three'); - expect(displayNames[3]).toHaveTextContent('Email Five'); + expect(displayNames.at(0)).toHaveTextContent('Email Four'); + expect(displayNames.at(1)).toHaveTextContent('Email Two'); + expect(displayNames.at(2)).toHaveTextContent('Email Three'); + expect(displayNames.at(3)).toHaveTextContent('Email Five'); }) ); }); @@ -727,9 +727,9 @@ describe('Sidebar', () => { const hintText = Localize.translateLocal('accessibilityHints.chatUserDisplayNames'); const displayNames = screen.queryAllByLabelText(hintText); expect(displayNames).toHaveLength(3); - expect(displayNames[0]).toHaveTextContent('Email Four'); - expect(displayNames[1]).toHaveTextContent('Email Three'); - expect(displayNames[2]).toHaveTextContent('Email Two'); + expect(displayNames.at(0)).toHaveTextContent('Email Four'); + expect(displayNames.at(1)).toHaveTextContent('Email Three'); + expect(displayNames.at(2)).toHaveTextContent('Email Two'); }) // When a new report is added @@ -740,10 +740,10 @@ describe('Sidebar', () => { const hintText = Localize.translateLocal('accessibilityHints.chatUserDisplayNames'); const displayNames = screen.queryAllByLabelText(hintText); expect(displayNames).toHaveLength(4); - expect(displayNames[0]).toHaveTextContent('Email Five'); - expect(displayNames[1]).toHaveTextContent('Email Four'); - expect(displayNames[2]).toHaveTextContent('Email Three'); - expect(displayNames[3]).toHaveTextContent('Email Two'); + expect(displayNames.at(0)).toHaveTextContent('Email Five'); + expect(displayNames.at(1)).toHaveTextContent('Email Four'); + expect(displayNames.at(2)).toHaveTextContent('Email Three'); + expect(displayNames.at(3)).toHaveTextContent('Email Two'); }) ); }); @@ -796,9 +796,9 @@ describe('Sidebar', () => { const hintText = Localize.translateLocal('accessibilityHints.chatUserDisplayNames'); const displayNames = screen.queryAllByLabelText(hintText); expect(displayNames).toHaveLength(3); - expect(displayNames[0]).toHaveTextContent('Email Four'); - expect(displayNames[1]).toHaveTextContent('Email Three'); - expect(displayNames[2]).toHaveTextContent('Email Two'); + expect(displayNames.at(0)).toHaveTextContent('Email Four'); + expect(displayNames.at(1)).toHaveTextContent('Email Three'); + expect(displayNames.at(2)).toHaveTextContent('Email Two'); }) // When a new report is added @@ -816,10 +816,10 @@ describe('Sidebar', () => { const hintText = Localize.translateLocal('accessibilityHints.chatUserDisplayNames'); const displayNames = screen.queryAllByLabelText(hintText); expect(displayNames).toHaveLength(4); - expect(displayNames[0]).toHaveTextContent('Email Five'); - expect(displayNames[1]).toHaveTextContent('Email Four'); - expect(displayNames[2]).toHaveTextContent('Email Three'); - expect(displayNames[3]).toHaveTextContent('Email Two'); + expect(displayNames.at(0)).toHaveTextContent('Email Five'); + expect(displayNames.at(1)).toHaveTextContent('Email Four'); + expect(displayNames.at(2)).toHaveTextContent('Email Three'); + expect(displayNames.at(3)).toHaveTextContent('Email Two'); }) ); }); @@ -871,9 +871,9 @@ describe('Sidebar', () => { const hintText = Localize.translateLocal('accessibilityHints.chatUserDisplayNames'); const displayNames = screen.queryAllByLabelText(hintText); expect(displayNames).toHaveLength(3); - expect(displayNames[0]).toHaveTextContent('Email Four'); - expect(displayNames[1]).toHaveTextContent('Email Three'); - expect(displayNames[2]).toHaveTextContent('Report (archived)'); + expect(displayNames.at(0)).toHaveTextContent('Email Four'); + expect(displayNames.at(1)).toHaveTextContent('Email Three'); + expect(displayNames.at(2)).toHaveTextContent('Report (archived)'); }) ); }); @@ -914,9 +914,9 @@ describe('Sidebar', () => { const hintText = Localize.translateLocal('accessibilityHints.chatUserDisplayNames'); const displayNames = screen.queryAllByLabelText(hintText); expect(displayNames).toHaveLength(3); - expect(displayNames[0]).toHaveTextContent('Email Four'); - expect(displayNames[1]).toHaveTextContent('Email Three'); - expect(displayNames[2]).toHaveTextContent('Email Two'); + expect(displayNames.at(0)).toHaveTextContent('Email Four'); + expect(displayNames.at(1)).toHaveTextContent('Email Three'); + expect(displayNames.at(2)).toHaveTextContent('Email Two'); }) ); }); @@ -954,9 +954,9 @@ describe('Sidebar', () => { const hintText = Localize.translateLocal('accessibilityHints.chatUserDisplayNames'); const displayNames = screen.queryAllByLabelText(hintText); expect(displayNames).toHaveLength(3); - expect(displayNames[0]).toHaveTextContent('Email Four'); - expect(displayNames[1]).toHaveTextContent('Email Three'); - expect(displayNames[2]).toHaveTextContent('Email Two'); + expect(displayNames.at(0)).toHaveTextContent('Email Four'); + expect(displayNames.at(1)).toHaveTextContent('Email Three'); + expect(displayNames.at(2)).toHaveTextContent('Email Two'); }) // When a new report is added @@ -967,10 +967,10 @@ describe('Sidebar', () => { const hintText = Localize.translateLocal('accessibilityHints.chatUserDisplayNames'); const displayNames = screen.queryAllByLabelText(hintText); expect(displayNames).toHaveLength(4); - expect(displayNames[0]).toHaveTextContent('Email Five'); - expect(displayNames[1]).toHaveTextContent('Email Four'); - expect(displayNames[2]).toHaveTextContent('Email Three'); - expect(displayNames[3]).toHaveTextContent('Email Two'); + expect(displayNames.at(0)).toHaveTextContent('Email Five'); + expect(displayNames.at(1)).toHaveTextContent('Email Four'); + expect(displayNames.at(2)).toHaveTextContent('Email Three'); + expect(displayNames.at(3)).toHaveTextContent('Email Two'); }) ); }); @@ -1021,9 +1021,9 @@ describe('Sidebar', () => { const hintText = Localize.translateLocal('accessibilityHints.chatUserDisplayNames'); const displayNames = screen.queryAllByLabelText(hintText); expect(displayNames).toHaveLength(3); - expect(displayNames[0]).toHaveTextContent('Email Four'); - expect(displayNames[1]).toHaveTextContent('Email Three'); - expect(displayNames[2]).toHaveTextContent('Report (archived)'); + expect(displayNames.at(0)).toHaveTextContent('Email Four'); + expect(displayNames.at(1)).toHaveTextContent('Email Three'); + expect(displayNames.at(2)).toHaveTextContent('Report (archived)'); }) ); }); diff --git a/tests/unit/SidebarTest.ts b/tests/unit/SidebarTest.ts index a6e39b504b19..2c8d28c537c6 100644 --- a/tests/unit/SidebarTest.ts +++ b/tests/unit/SidebarTest.ts @@ -86,11 +86,11 @@ describe('Sidebar', () => { .then(() => { const hintText = Localize.translateLocal('accessibilityHints.chatUserDisplayNames'); const displayNames = screen.queryAllByLabelText(hintText); - expect(displayNames[0]).toHaveTextContent('Report (archived)'); + expect(displayNames.at(0)).toHaveTextContent('Report (archived)'); const hintMessagePreviewText = Localize.translateLocal('accessibilityHints.lastChatMessagePreview'); const messagePreviewTexts = screen.queryAllByLabelText(hintMessagePreviewText); - expect(messagePreviewTexts[0]).toHaveTextContent('This chat room has been archived.'); + expect(messagePreviewTexts.at(0)).toHaveTextContent('This chat room has been archived.'); }) ); }); @@ -141,11 +141,11 @@ describe('Sidebar', () => { .then(() => { const hintText = Localize.translateLocal('accessibilityHints.chatUserDisplayNames'); const displayNames = screen.queryAllByLabelText(hintText); - expect(displayNames[0]).toHaveTextContent('Report (archived)'); + expect(displayNames.at(0)).toHaveTextContent('Report (archived)'); const hintMessagePreviewText = Localize.translateLocal('accessibilityHints.lastChatMessagePreview'); const messagePreviewTexts = screen.queryAllByLabelText(hintMessagePreviewText); - expect(messagePreviewTexts[0]).toHaveTextContent('This chat is no longer active because Vikings Policy is no longer an active workspace.'); + expect(messagePreviewTexts.at(0)).toHaveTextContent('This chat is no longer active because Vikings Policy is no longer an active workspace.'); }) ); }); diff --git a/tests/unit/WorkflowUtilsTest.ts b/tests/unit/WorkflowUtilsTest.ts index 9400fc34375b..8508ab6466ba 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'; diff --git a/tests/unit/createOrUpdateStagingDeployTest.ts b/tests/unit/createOrUpdateStagingDeployTest.ts index 8cd340f317c7..59ed5b247ca3 100644 --- a/tests/unit/createOrUpdateStagingDeployTest.ts +++ b/tests/unit/createOrUpdateStagingDeployTest.ts @@ -145,13 +145,13 @@ describe('createOrUpdateStagingDeployCash', () => { // eslint-disable-next-line max-len body: `${baseExpectedOutput('1.0.1-0')}` + - `${closedCheckbox}${basePRList[0]}` + - `${lineBreak}${closedCheckbox}${basePRList[1]}` + - `${lineBreak}${closedCheckbox}${basePRList[2]}${lineBreak}` + + `${closedCheckbox}${basePRList.at(0)}` + + `${lineBreak}${closedCheckbox}${basePRList.at(1)}` + + `${lineBreak}${closedCheckbox}${basePRList.at(2)}${lineBreak}` + `${lineBreakDouble}${deployBlockerHeader}` + - `${lineBreak}${closedCheckbox}${basePRList[0]}` + - `${lineBreak}${closedCheckbox}${basePRList[3]}` + - `${lineBreak}${closedCheckbox}${basePRList[4]}` + + `${lineBreak}${closedCheckbox}${basePRList.at(0)}` + + `${lineBreak}${closedCheckbox}${basePRList.at(3)}` + + `${lineBreak}${closedCheckbox}${basePRList.at(4)}` + `${lineBreakDouble}${ccApplauseLeads}`, }; @@ -194,9 +194,9 @@ describe('createOrUpdateStagingDeployCash', () => { assignees: [CONST.APPLAUSE_BOT], body: `${baseExpectedOutput()}` + - `${openCheckbox}${basePRList[5]}` + - `${lineBreak}${openCheckbox}${basePRList[6]}` + - `${lineBreak}${openCheckbox}${basePRList[7]}${lineBreak}` + + `${openCheckbox}${basePRList.at(5)}` + + `${lineBreak}${openCheckbox}${basePRList.at(6)}` + + `${lineBreak}${openCheckbox}${basePRList.at(7)}${lineBreak}` + `${lineBreakDouble}${deployerVerificationsHeader}` + `${lineBreak}${openCheckbox}${timingDashboardVerification}` + `${lineBreak}${openCheckbox}${firebaseVerification}` + @@ -214,13 +214,13 @@ describe('createOrUpdateStagingDeployCash', () => { // eslint-disable-next-line max-len body: `${baseExpectedOutput()}` + - `${openCheckbox}${basePRList[5]}` + - `${lineBreak}${closedCheckbox}${basePRList[6]}` + - `${lineBreak}${openCheckbox}${basePRList[7]}${lineBreak}` + + `${openCheckbox}${basePRList.at(5)}` + + `${lineBreak}${closedCheckbox}${basePRList.at(6)}` + + `${lineBreak}${openCheckbox}${basePRList.at(7)}${lineBreak}` + `${lineBreakDouble}${deployBlockerHeader}` + - `${lineBreak}${openCheckbox}${basePRList[5]}` + - `${lineBreak}${openCheckbox}${basePRList[8]}` + - `${lineBreak}${closedCheckbox}${basePRList[9]}${lineBreak}` + + `${lineBreak}${openCheckbox}${basePRList.at(5)}` + + `${lineBreak}${openCheckbox}${basePRList.at(8)}` + + `${lineBreak}${closedCheckbox}${basePRList.at(9)}${lineBreak}` + `${lineBreakDouble}${deployerVerificationsHeader}` + `${lineBreak}${closedCheckbox}${timingDashboardVerification}` + `${lineBreak}${closedCheckbox}${firebaseVerification}` + @@ -309,17 +309,17 @@ describe('createOrUpdateStagingDeployCash', () => { // eslint-disable-next-line max-len body: `${baseExpectedOutput('1.0.2-2')}` + - `${openCheckbox}${basePRList[5]}` + - `${lineBreak}${closedCheckbox}${basePRList[6]}` + - `${lineBreak}${openCheckbox}${basePRList[7]}` + - `${lineBreak}${openCheckbox}${basePRList[8]}` + - `${lineBreak}${openCheckbox}${basePRList[9]}${lineBreak}` + + `${openCheckbox}${basePRList.at(5)}` + + `${lineBreak}${closedCheckbox}${basePRList.at(6)}` + + `${lineBreak}${openCheckbox}${basePRList.at(7)}` + + `${lineBreak}${openCheckbox}${basePRList.at(8)}` + + `${lineBreak}${openCheckbox}${basePRList.at(9)}${lineBreak}` + `${lineBreakDouble}${deployBlockerHeader}` + - `${lineBreak}${openCheckbox}${basePRList[5]}` + - `${lineBreak}${openCheckbox}${basePRList[8]}` + - `${lineBreak}${closedCheckbox}${basePRList[9]}` + - `${lineBreak}${openCheckbox}${baseIssueList[0]}` + - `${lineBreak}${openCheckbox}${baseIssueList[1]}${lineBreak}` + + `${lineBreak}${openCheckbox}${basePRList.at(5)}` + + `${lineBreak}${openCheckbox}${basePRList.at(8)}` + + `${lineBreak}${closedCheckbox}${basePRList.at(9)}` + + `${lineBreak}${openCheckbox}${baseIssueList.at(0)}` + + `${lineBreak}${openCheckbox}${baseIssueList.at(1)}${lineBreak}` + `${lineBreakDouble}${deployerVerificationsHeader}` + // Note: these will be unchecked with a new app version, and that's intentional `${lineBreak}${openCheckbox}${timingDashboardVerification}` + @@ -385,15 +385,15 @@ describe('createOrUpdateStagingDeployCash', () => { // eslint-disable-next-line max-len body: `${baseExpectedOutput('1.0.2-1')}` + - `${openCheckbox}${basePRList[5]}` + - `${lineBreak}${closedCheckbox}${basePRList[6]}` + - `${lineBreak}${openCheckbox}${basePRList[7]}${lineBreak}` + + `${openCheckbox}${basePRList.at(5)}` + + `${lineBreak}${closedCheckbox}${basePRList.at(6)}` + + `${lineBreak}${openCheckbox}${basePRList.at(7)}${lineBreak}` + `${lineBreakDouble}${deployBlockerHeader}` + - `${lineBreak}${closedCheckbox}${basePRList[5]}` + - `${lineBreak}${openCheckbox}${basePRList[8]}` + - `${lineBreak}${closedCheckbox}${basePRList[9]}` + - `${lineBreak}${openCheckbox}${baseIssueList[0]}` + - `${lineBreak}${openCheckbox}${baseIssueList[1]}${lineBreak}` + + `${lineBreak}${closedCheckbox}${basePRList.at(5)}` + + `${lineBreak}${openCheckbox}${basePRList.at(8)}` + + `${lineBreak}${closedCheckbox}${basePRList.at(9)}` + + `${lineBreak}${openCheckbox}${baseIssueList.at(0)}` + + `${lineBreak}${openCheckbox}${baseIssueList.at(1)}${lineBreak}` + `${lineBreakDouble}${deployerVerificationsHeader}` + `${lineBreak}${closedCheckbox}${timingDashboardVerification}` + `${lineBreak}${closedCheckbox}${firebaseVerification}` + diff --git a/tests/unit/generateMonthMatrixTest.ts b/tests/unit/generateMonthMatrixTest.ts index a0605204c1c1..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[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 384395c4acef..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('-')[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('-')[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/LHNTestUtils.tsx b/tests/utils/LHNTestUtils.tsx index 7197529cd43c..c16d18455046 100644 --- a/tests/utils/LHNTestUtils.tsx +++ b/tests/utils/LHNTestUtils.tsx @@ -197,7 +197,7 @@ function getFakeReportWithPolicy(participantAccountIDs = [1, 2], millisecondsInT policyID: '08CE60F05A5D86E1', oldPolicyName: '', isOwnPolicyExpenseChat: false, - ownerAccountID: participantAccountIDs[0], + ownerAccountID: participantAccountIDs.at(0), }; } diff --git a/tests/utils/ReportTestUtils.ts b/tests/utils/ReportTestUtils.ts index deab909cd272..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[index % actionNames.length]; + const actionName: ReportActionName = index === 0 ? 'CREATED' : actionNames.at(index % actionNames.length) ?? 'CREATED'; const reportAction = { ...createRandomReportAction(reportID), actionName,