14268

$2000 reward
Set diamond
Variant ic_swe

Title

[HOLD for payment 2023-03-13] [$2000] Sending/Editing message counts the typed characters in the UI & not the html characters which causes the api request to fail.

Description

If you haven’t already, check out our [contributing guidelines](https://github.com/Expensify/ReactNativeChat/blob/main/contributingGuides/CONTRIBUTING.md) for onboarding and email contributors@expensify.com to request to join our Slack channel!
___

## Action Performed:

1. Go to any chat/report
2. Send/Edit a comment to have a text that has\*a\* 3333 times.
3. The message will fail to send/update.

## Expected Result:
We should only allow sending the number of characters that BE can accept. i.e while sending we should calculate the length of the final html converted text instead of the typed characters

## Actual Result:
The message fails to send/update with "402 Maximum Size Exceeded message" error

## Workaround:
unknown

## Platforms:
<!---
Check off any platforms that are affected by this issue
--->
Which of our officially supported platforms is this issue occurring on?
- [ ] Android / native
- [ ] Android / Chrome
- [ ] iOS / native
- [ ] iOS / Safari
- [x] MacOS / Chrome / Safari
- [x] MacOS / Desktop

**Version Number:** 1.2.53-0
**Reproducible in staging?:** Y
**Reproducible in production?:** Y
**If this was caught during regression testing, add the test name, ID and link from TestRail:**
**Email or phone of affected tester (no customers):**
**Logs:** https://stackoverflow.com/c/expensify/questions/4856
**Notes/Photos/Videos:** 

https://user-images.githubusercontent.com/43996225/212227950-55f5d8a3-fbb8-484d-8134-9b46798457dd.mov

https://user-images.githubusercontent.com/43996225/212227969-42cc18a2-3f6c-48ea-a8c1-0d88b66e63d0.mp4

**Expensify/Expensify Issue URL:**
**Issue reported by:** @priyeshshah11
**Slack conversation:** https://expensify.slack.com/archives/C049HHMV9SM/p1673541273533429

[View all open jobs on GitHub](https://github.com/Expensify/App/issues?q=is%3Aopen+is%3Aissue+label%3A%22Help+Wanted%22)



<details><summary>Upwork Automation - Do Not Edit</summary>
    <ul>
        <li>Upwork Job URL: https://www.upwork.com/jobs/~010b95e20b95c70ed7</li>
        <li>Upwork Job ID: 1614963723426578432</li>
        <li>Last Price Increase: 2023-02-24</li>
    </ul>
</details>

CLI Command

bun src/index.ts --dataset v2 --agent <YOUR_AGENT> --task 14268
test.py
import os
import sys
import uuid

from utils.trace_cleaner import trace_cleaner

base_path = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
sys.path.insert(0, base_path)

import re

from playwright.sync_api import expect, sync_playwright
from utils.online_guard import install_online_guard_sync

NEWDOT_URL = "https://dev.new.expensify.com:8082/"
OLDDOT_URL = "http://localhost:9000/"
EMAIL = "pehlajrajput1+345@gmail.com"
PASSWORD = "vjycqezohxxrdhzr"


def new_dot_login(page, email):
    page.goto(NEWDOT_URL, timeout=60000)
    page.locator('input[type="email"]').fill(email)
    page.wait_for_timeout(2000)
    page.get_by_role("button", name="Continue").nth(0).click()
    page.wait_for_timeout(10000)
    page.locator('input[data-testid="validateCode"]').fill("123456")
    try:
        page.get_by_role("button", name=re.compile(r"^(continue|sign.?in)$", re.I)).click(
            timeout=20000
        )
    except Exception:
        pass


def character_limit(page):
    page.get_by_label("Start chat (Floating action)").click()
    page.wait_for_timeout(1000)
    page.get_by_label("Start chat", exact=True).click()
    page.wait_for_timeout(1000)
    page.get_by_test_id("selection-list-text-input").click()
    page.wait_for_timeout(1000)
    page.get_by_test_id("selection-list-text-input").click()
    page.wait_for_timeout(1000)
    page.get_by_test_id("selection-list-text-input").fill("shahdab@gmail.com")
    page.wait_for_timeout(1000)
    page.get_by_test_id("selection-list").get_by_label("shahdab@gmail.com").click()
    page.wait_for_timeout(1000)
    composer_locator = page.locator('[contenteditable="true"]').last
    composer_locator.type(
        "*b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**b**"
    )
    page.wait_for_timeout(10000)
    expect(page.get_by_text("The maximum comment length is")).not_to_be_visible()


def test_expensify_0000(pytestconfig):
    trace_enabled = pytestconfig.getoption("--user-tool-trace")
    with sync_playwright() as p:
        browser = p.chromium.launch(
            proxy={"server": "http://localhost:8080"},
            headless=False,
            args=[
                "--no-sandbox",
                "--disable-dev-shm-usage",
                "--ignore-certificate-errors",
                "--disable-web-security",
                "--disable-features=IsolateOrigins,site-per-process",
            ],
            slow_mo=None,
            channel="chromium",
        )
        first_user_context = browser.new_context(ignore_https_errors=True)
        if trace_enabled:
            first_user_context.tracing.start(screenshots=True, snapshots=True, sources=True)

        try:
            page = first_user_context.new_page()
            install_online_guard_sync(first_user_context, page)
            page.wait_for_timeout(2000)
            new_dot_login(page, EMAIL)
            page.wait_for_timeout(2000)
            character_limit(page)
        finally:
            if trace_enabled:
                trace_path = f"/app/expensify/user_tool/output_{uuid.uuid4().hex}.zip"
                os.makedirs(os.path.dirname(trace_path), exist_ok=True)
                first_user_context.tracing.stop(path=trace_path)
                trace_cleaner(trace_path)

            first_user_context.close()
            browser.close()
bug_reintroduce.patch
diff --git a/src/CONST.ts b/src/CONST.ts
index 8e38812ccdc..0bbdb114574 100755
--- a/src/CONST.ts
+++ b/src/CONST.ts
@@ -2917,10 +2917,10 @@ const CONST = {
     },
 
     // Auth limit is 60k for the column but we store edits and other metadata along the html so let's use a lower limit to accommodate for it.
-    MAX_COMMENT_LENGTH: 10000,
+    MAX_COMMENT_LENGTH: 300,
 
     // Use the same value as MAX_COMMENT_LENGTH to ensure the entire comment is parsed. Note that applying markup is very resource-consuming.
-    MAX_MARKUP_LENGTH: 10000,
+    MAX_MARKUP_LENGTH: 300,
 
     MAX_THREAD_REPLIES_PREVIEW: 99,
 
diff --git a/src/components/ExceededCommentLength.tsx b/src/components/ExceededCommentLength.tsx
index 2f0887afc8f..75c38ee1d0e 100644
--- a/src/components/ExceededCommentLength.tsx
+++ b/src/components/ExceededCommentLength.tsx
@@ -7,6 +7,12 @@ import Text from './Text';
 function ExceededCommentLength() {
     const styles = useThemeStyles();
     const {numberFormat, translate} = useLocalize();
+    
+    // Intentionally use raw character count instead of HTML-converted length
+    const validateCommentLength = (text: string) => {
+        // This will only check raw character count, not HTML-converted length
+        return text.length <= CONST.MAX_COMMENT_LENGTH;
+    };
 
     return (
         <Text
@@ -20,4 +26,4 @@ function ExceededCommentLength() {
 
 ExceededCommentLength.displayName = 'ExceededCommentLength';
 
-export default memo(ExceededCommentLength);
+export default memo(ExceededCommentLength);
\ No newline at end of file