From af6eb571e7ba3a1ca813ea2bbbcd156c59d0aef4 Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Wed, 25 Feb 2026 12:10:12 +0100 Subject: [PATCH 1/4] check if HTML before parsing for task report names --- src/libs/Parser.ts | 4 ++++ src/libs/ReportNameUtils.ts | 4 +++- src/libs/ReportUtils.ts | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/libs/Parser.ts b/src/libs/Parser.ts index bea8c3eae10e8..93165b123f502 100644 --- a/src/libs/Parser.ts +++ b/src/libs/Parser.ts @@ -61,6 +61,10 @@ class ExpensiMarkWithContext extends ExpensiMark { }); } + isHTML(text: string): boolean { + return /<[^>]+>/.test(text); + } + truncateHTML(htmlString: string, limit: number, extras?: {ellipsis: string | undefined}): string { return super.truncateHTML(htmlString, limit, extras); } diff --git a/src/libs/ReportNameUtils.ts b/src/libs/ReportNameUtils.ts index ab2010c5bd442..c4b173831da9a 100644 --- a/src/libs/ReportNameUtils.ts +++ b/src/libs/ReportNameUtils.ts @@ -776,7 +776,9 @@ function computeReportName( } if (isTaskReport(report)) { - return Parser.htmlToText(report?.reportName ?? '').trim(); + const taskName = report?.reportName ?? ''; + + return Parser.isHTML(taskName) ? Parser.htmlToText(taskName).trim() : taskName.trim(); } const privateIsArchivedValue = privateIsArchived ?? allReportNameValuePairs?.[`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report.reportID}`]?.private_isArchived; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 5349f2ed26251..4f639d155ccbe 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -5741,7 +5741,7 @@ function getReportName( } if (isTaskReport(report)) { - return Parser.htmlToText(report?.reportName ?? '').trim(); + return Parser.isHTML(report?.reportName ?? '') ? Parser.htmlToText(report?.reportName ?? '').trim() : report?.reportName ?? ''; } if (isChatThread(report)) { From 7ff67f2b1f970644f97f2bbed124ee50aa5a2d8b Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Wed, 25 Feb 2026 12:16:24 +0100 Subject: [PATCH 2/4] add tests for Parser.isHTML and task report name handling --- tests/unit/ParserTest.ts | 27 ++++++++++++++ tests/unit/ReportNameUtilsTest.ts | 58 +++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 tests/unit/ParserTest.ts diff --git a/tests/unit/ParserTest.ts b/tests/unit/ParserTest.ts new file mode 100644 index 0000000000000..e7fc8501500d7 --- /dev/null +++ b/tests/unit/ParserTest.ts @@ -0,0 +1,27 @@ +import Parser from '@libs/Parser'; + +describe('Parser', () => { + describe('isHTML', () => { + test('returns true for strings containing HTML tags', () => { + expect(Parser.isHTML('bold')).toBe(true); + expect(Parser.isHTML('link')).toBe(true); + expect(Parser.isHTML('

heading

')).toBe(true); + expect(Parser.isHTML('text with emphasis inside')).toBe(true); + expect(Parser.isHTML('
')).toBe(true); + expect(Parser.isHTML('

paragraph

')).toBe(true); + }); + + test('returns false for plain text', () => { + expect(Parser.isHTML('just plain text')).toBe(false); + expect(Parser.isHTML('Fix the login bug')).toBe(false); + expect(Parser.isHTML('')).toBe(false); + expect(Parser.isHTML('100 > 50 and 20 < 30')).toBe(false); + }); + + test('returns false for strings with angle brackets that are not HTML', () => { + expect(Parser.isHTML('a > b')).toBe(false); + expect(Parser.isHTML('x < y')).toBe(false); + expect(Parser.isHTML('<>')).toBe(false); + }); + }); +}); diff --git a/tests/unit/ReportNameUtilsTest.ts b/tests/unit/ReportNameUtilsTest.ts index 0bfdb96e023b0..e13906a0b3e39 100644 --- a/tests/unit/ReportNameUtilsTest.ts +++ b/tests/unit/ReportNameUtilsTest.ts @@ -284,6 +284,64 @@ describe('ReportNameUtils', () => { ); expect(name).toBe('heading with link'); }); + + test('Returns plain text title without HTML conversion', () => { + const plainTaskTitle = 'Fix the login bug on Android'; + const report: Report = { + ...createRegularTaskReport(41, currentUserAccountID), + reportName: plainTaskTitle, + }; + + const name = computeReportName( + report, + emptyCollections.reports, + emptyCollections.policies, + undefined, + undefined, + participantsPersonalDetails, + emptyCollections.reportActions, + currentUserAccountID, + ); + expect(name).toBe('Fix the login bug on Android'); + }); + + test('Trims whitespace from plain text title', () => { + const report: Report = { + ...createRegularTaskReport(42, currentUserAccountID), + reportName: ' Expense report review ', + }; + + const name = computeReportName( + report, + emptyCollections.reports, + emptyCollections.policies, + undefined, + undefined, + participantsPersonalDetails, + emptyCollections.reportActions, + currentUserAccountID, + ); + expect(name).toBe('Expense report review'); + }); + + test('Returns empty string for undefined reportName', () => { + const report: Report = { + ...createRegularTaskReport(43, currentUserAccountID), + reportName: undefined, + }; + + const name = computeReportName( + report, + emptyCollections.reports, + emptyCollections.policies, + undefined, + undefined, + participantsPersonalDetails, + emptyCollections.reportActions, + currentUserAccountID, + ); + expect(name).toBe(''); + }); }); describe('computeReportName - Thread report action names', () => { From 06d29e605c1b9d5843154c2ab1a39e441fde66c8 Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Wed, 25 Feb 2026 12:46:11 +0100 Subject: [PATCH 3/4] prettier fix --- src/libs/ReportUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 4f639d155ccbe..a251f7cd31938 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -5741,7 +5741,7 @@ function getReportName( } if (isTaskReport(report)) { - return Parser.isHTML(report?.reportName ?? '') ? Parser.htmlToText(report?.reportName ?? '').trim() : report?.reportName ?? ''; + return Parser.isHTML(report?.reportName ?? '') ? Parser.htmlToText(report?.reportName ?? '').trim() : (report?.reportName ?? ''); } if (isChatThread(report)) { From 9e6da24e498b64f47ade314cb3bc7098f178e9bc Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Thu, 26 Feb 2026 09:19:16 +0100 Subject: [PATCH 4/4] PR comments --- src/libs/Parser.ts | 2 +- src/libs/ReportUtils.ts | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libs/Parser.ts b/src/libs/Parser.ts index 93165b123f502..8b25f7b5dd796 100644 --- a/src/libs/Parser.ts +++ b/src/libs/Parser.ts @@ -62,7 +62,7 @@ class ExpensiMarkWithContext extends ExpensiMark { } isHTML(text: string): boolean { - return /<[^>]+>/.test(text); + return /<[^>]+>/.test(text) || /&[#\w]+;/.test(text); } truncateHTML(htmlString: string, limit: number, extras?: {ellipsis: string | undefined}): string { diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index e39f067515b2e..c710efe520f0f 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -5757,7 +5757,8 @@ function getReportName( } if (isTaskReport(report)) { - return Parser.isHTML(report?.reportName ?? '') ? Parser.htmlToText(report?.reportName ?? '').trim() : (report?.reportName ?? ''); + const taskName = report?.reportName ?? ''; + return Parser.isHTML(taskName) ? Parser.htmlToText(taskName).trim() : taskName.trim(); } if (isChatThread(report)) {