diff --git a/issues/18909_734/commit_id.txt b/issues/18909_734/commit_id.txt new file mode 100644 index 0000000..b00c18d --- /dev/null +++ b/issues/18909_734/commit_id.txt @@ -0,0 +1 @@ +2b791c9f3053c1682ddcb50ab036deb3e55a7542 diff --git a/issues/18909_734/flow.mitm b/issues/18909_734/flow.mitm new file mode 100644 index 0000000..b6b9c0e Binary files /dev/null and b/issues/18909_734/flow.mitm differ diff --git a/issues/18909_734/git_tag.txt b/issues/18909_734/git_tag.txt new file mode 100644 index 0000000..e69de29 diff --git a/issues/18909_734/issue_data.json b/issues/18909_734/issue_data.json new file mode 100644 index 0000000..fcdc174 --- /dev/null +++ b/issues/18909_734/issue_data.json @@ -0,0 +1 @@ +{"price": 1000.0, "title": "[HOLD for payment 2023-06-12] [$1000] Padding above \u201cno result found\u201d error in message input \u201cEmoji- Search bar\u201d is not consistent.", "issue_repo_steps": "Action performed: 1. Open \u201cChats\u201d\r\n2. Click \u201cEmoji\u201d.\r\n3. Search an invalid Emoji, so that \u201cno result found\u201d error appear.\r\n4. Observe that the bottom padding of the input field is 8px 16px.\r\n5. Please note that all below search bar reflects padding of 0px 20px 12px on search of an invalid item.\r\nSearch bar next to chats.\r\nSend money- select a currency.\r\nProfile- Pronouns\r\nProfile- Timezone\r\nDOB-Year\n\nExpected result: None\n\nActual result: None"} \ No newline at end of file diff --git a/issues/18909_734/issue_introduction.patch b/issues/18909_734/issue_introduction.patch new file mode 100644 index 0000000..4315303 --- /dev/null +++ b/issues/18909_734/issue_introduction.patch @@ -0,0 +1,39 @@ +diff --git a/src/components/EmojiPicker/CategoryShortcutBar.tsx b/src/components/EmojiPicker/CategoryShortcutBar.tsx +index 25c1ff85f6a..e440e8c7a97 100644 +--- a/src/components/EmojiPicker/CategoryShortcutBar.tsx ++++ b/src/components/EmojiPicker/CategoryShortcutBar.tsx +@@ -15,7 +15,7 @@ type CategoryShortcutBarProps = { + function CategoryShortcutBar({onPress, headerEmojis}: CategoryShortcutBarProps) { + const styles = useThemeStyles(); + return ( +- ++ + {headerEmojis.map((headerEmoji) => ( + +- ++ + setIsDownloadFailureModalVisible(false)} + /> +- ++ + tuple[Browser, Page, str]: + + browser = p.chromium.launch(channel="chrome", headless=True, args=[ + "--disable-web-security", + "--disable-features=IsolateOrigins,site-per-process" + ]) + page = browser.new_page() + user_email = generate_random_email() + + + page.goto('https://dev.new.expensify.com:8082/') + + + page.locator('input[type="email"]').fill(user_email) + page.get_by_role("button", name="Continue").click() + + + page.get_by_role("button", name="Join").click() + + page.wait_for_selector("text=What do you want to do today?") + expect(page.locator("text=What do you want to do today?")).to_be_visible() + + + page.locator("text='Track and budget expenses'").click() + page.get_by_role("button", name="Continue").click() + page.wait_for_timeout(500) + + + page.locator('input[name="fname"]').fill(first_name) + page.locator('input[name="lname"]').fill(last_name) + page.get_by_role("button", name="Continue").last.click() + page.wait_for_timeout(500) + + return browser, page, user_email + + +def test_issue_18909(): + with sync_playwright() as p: + + browser, page, user_email = login_user(p) + + own_chat = page.get_by_text("Milan T (you)") + expect(own_chat).to_be_visible() + own_chat.click() + page.wait_for_timeout(500) + + emoji = page.locator('div[aria-label="Emoji"]').nth(1) + expect(emoji).to_be_visible() + emoji.click() + page.wait_for_timeout(500) + + page.locator('input[aria-label="Search"]').nth(0).fill("avsfb") + test = page.locator("div").filter(has_text=re.compile(r"^Search$")).first + page.wait_for_timeout(500) + expect(test).to_have_css("padding", "8px 16px 12px") + + browser.close() \ No newline at end of file diff --git a/issues/18909_734/user_tool.py b/issues/18909_734/user_tool.py new file mode 100644 index 0000000..330f6be --- /dev/null +++ b/issues/18909_734/user_tool.py @@ -0,0 +1,90 @@ +import sys, os + +base_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) +sys.path.insert(0, base_path) + +import time +import random +import re +from playwright.sync_api import sync_playwright, expect +from playwright.sync_api._context_manager import PlaywrightContextManager +from playwright._impl._browser import Browser +from playwright._impl._page import Page +from utils.trace_cleaner import trace_cleaner + + +def generate_random_email(): + return "redx224+6856@gmail.com" + + +def login_user(p: PlaywrightContextManager, first_name="Milan", last_name="T") -> tuple[Browser, Page, str]: + + browser = p.chromium.launch(channel="chrome", headless=True, args=[ + "--disable-web-security", + "--disable-features=IsolateOrigins,site-per-process" + ]) + context = browser.new_context() + + + context.tracing.start(screenshots=True, snapshots=True, sources=True) + + page = context.new_page() + user_email = generate_random_email() + + + page.goto('https://dev.new.expensify.com:8082/') + + + page.locator('input[type="email"]').fill(user_email) + page.get_by_role("button", name="Continue").click() + + + page.get_by_role("button", name="Join").click() + + page.wait_for_selector("text=What do you want to do today?") + expect(page.locator("text=What do you want to do today?")).to_be_visible() + + + page.locator("text='Track and budget expenses'").click() + page.get_by_role("button", name="Continue").click() + page.wait_for_timeout(500) + + + page.locator('input[name="fname"]').fill(first_name) + page.locator('input[name="lname"]').fill(last_name) + page.get_by_role("button", name="Continue").last.click() + page.wait_for_timeout(500) + + + return browser, page, user_email + + +def test_issue_18909(): + with sync_playwright() as p: + browser, page, user_email = login_user(p) + + context = page.context + try: + own_chat = page.get_by_text("Milan T (you)") + expect(own_chat).to_be_visible() + own_chat.click() + page.wait_for_timeout(500) + + emoji = page.locator('div[aria-label="Emoji"]').nth(1) + expect(emoji).to_be_visible() + emoji.click() + page.wait_for_timeout(500) + + page.locator('input[aria-label="Search"]').nth(0).fill("avsfb") + test = page.locator("div").filter(has_text=re.compile(r"^Search$")).first + page.wait_for_timeout(500) + expect(test).to_have_css("padding", "8px 16px 12px") + except Exception as e: + + raise e + finally: + + context.tracing.stop(path="/app/expensify/user_tool/output_browser1.zip") + trace_cleaner("/app/expensify/user_tool/output_browser1.zip") + + browser.close() diff --git a/issues/19023_868/commit_id.txt b/issues/19023_868/commit_id.txt new file mode 100644 index 0000000..b00c18d --- /dev/null +++ b/issues/19023_868/commit_id.txt @@ -0,0 +1 @@ +2b791c9f3053c1682ddcb50ab036deb3e55a7542 diff --git a/issues/19023_868/flow.mitm b/issues/19023_868/flow.mitm new file mode 100644 index 0000000..2d5d679 Binary files /dev/null and b/issues/19023_868/flow.mitm differ diff --git a/issues/19023_868/git_tag.txt b/issues/19023_868/git_tag.txt new file mode 100644 index 0000000..e69de29 diff --git a/issues/19023_868/issue_data.json b/issues/19023_868/issue_data.json new file mode 100644 index 0000000..3c082ee --- /dev/null +++ b/issues/19023_868/issue_data.json @@ -0,0 +1 @@ +{"price": 1000.0, "title": "[HOLD for payment 2023-06-02] [$1000] On task title editing page when title field is empty than error message is `This field is required` instead of `please enter a title`", "issue_repo_steps": "Action performed: 1. Launch the app -> Login in account\r\n2. Click on FAB button -> Click on Assign task option\r\n3. Enter title -> Assign task -> Click on confirm task -> will navigate to task detail page\r\n4. On task detail page click on Task (name) -> clear title field and press save -> Notice error message is `This field is required` instead of `please enter a title`\n\nExpected result: None\n\nActual result: None"} \ No newline at end of file diff --git a/issues/19023_868/issue_introduction.patch b/issues/19023_868/issue_introduction.patch new file mode 100644 index 0000000..02d9e31 --- /dev/null +++ b/issues/19023_868/issue_introduction.patch @@ -0,0 +1,13 @@ +diff --git a/src/pages/tasks/TaskTitlePage.tsx b/src/pages/tasks/TaskTitlePage.tsx +index 900809256c4..b56baa4ee30 100644 +--- a/src/pages/tasks/TaskTitlePage.tsx ++++ b/src/pages/tasks/TaskTitlePage.tsx +@@ -39,7 +39,7 @@ function TaskTitlePage({report, currentUserPersonalDetails}: TaskTitlePageProps) + const errors: FormInputErrors = {}; + + if (!title) { +- ErrorUtils.addErrorMessage(errors, INPUT_IDS.TITLE, translate('newTaskPage.pleaseEnterTaskName')); ++ ErrorUtils.addErrorMessage(errors, INPUT_IDS.TITLE, translate('common.error.fieldRequired')); + } else if (title.length > CONST.TITLE_CHARACTER_LIMIT) { + ErrorUtils.addErrorMessage(errors, INPUT_IDS.TITLE, translate('common.error.characterLimitExceedCounter', {length: title.length, limit: CONST.TITLE_CHARACTER_LIMIT})); + } diff --git a/issues/19023_868/revert_command.txt b/issues/19023_868/revert_command.txt new file mode 100644 index 0000000..e69de29 diff --git a/issues/19023_868/test.py b/issues/19023_868/test.py new file mode 100644 index 0000000..f024c58 --- /dev/null +++ b/issues/19023_868/test.py @@ -0,0 +1,161 @@ +import sys, os + +base_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) +sys.path.insert(0, base_path) + +import re +import time +from playwright.sync_api import sync_playwright, expect +from playwright.sync_api._context_manager import PlaywrightContextManager +from playwright._impl._browser import Browser +from playwright._impl._page import Page +import time +import imaplib +import email +from email.header import decode_header +import re + +USER_EMAIL = "playwright2user+1@gmail.com" +PASSWORD = "zehr mglm gizg gjcc" + +def fetch_verification_code_from_email(user_email, password, retries=10, delay=10): + """ + Fetch the OTP code from the latest email. + """ + imap = imaplib.IMAP4_SSL("imap.gmail.com") + imap.login(user_email, password) + + for attempt in range(retries): + imap.select("inbox") + status, messages = imap.search( + None, '(UNSEEN SUBJECT "Expensify magic sign-in code")' + ) + if status == "OK": + email_ids = messages[0].split() + + if email_ids: + + latest_email_id = email_ids[-1] + status, msg_data = imap.fetch(latest_email_id, "(RFC822)") + + for response_part in msg_data: + if isinstance(response_part, tuple): + msg = email.message_from_bytes(response_part[1]) + + + if msg.is_multipart(): + for part in msg.walk(): + content_type = part.get_content_type() + if content_type == "text/plain": + body = part.get_payload(decode=True).decode() + match = re.search(r'\b\d{6}\b', body) + if match: + otp_code = match.group(0) + imap.logout() + return otp_code + else: + + body = msg.get_payload(decode=True).decode() + match = re.search(r'\b\d{6}\b', body) + if match: + otp_code = match.group(0) + imap.logout() + return otp_code + else: + print("No new emails found. Retrying...") + otp_code="123456" + return otp_code + else: + print("Failed to retrieve emails. Retrying...") + + + time.sleep(delay) + + imap.logout() + raise Exception("Max retries reached. No magic code email found.") + + +def generate_random_email(): + timestamp = int(time.time()) + return f"testuser{timestamp}@gmail.com" + + +def login_user(p: PlaywrightContextManager, first_name="Ayush", last_name="G") -> tuple[Browser, Page, str]: + + browser = p.chromium.launch( + headless=True, # Set to False to see the browser + args=[ + "--disable-web-security", # Disable CORS (for testing only) + "--disable-features=IsolateOrigins,site-per-process" + ]) + context = browser.new_context(ignore_https_errors=True) + page = context.new_page() + user_email = USER_EMAIL + + + page.goto('https://dev.new.expensify.com:8082/') + + + page.locator('input[type="email"]').fill(user_email) + page.locator('button[tabindex="0"]').click() + time.sleep(1) + + verification_code = fetch_verification_code_from_email(user_email, PASSWORD) + + + otp_input = page.locator("[autocomplete='one-time-code']") + expect(otp_input).to_be_visible() + otp_input.fill(verification_code) + + + try: + page.locator('button[tabindex="0"]').click() + time.sleep(1) + except Exception: + pass + + + return browser, page, user_email + + +def test_save_description_in_submitted_expense(): + with sync_playwright() as p: + + browser, page, user_email = login_user(p) + page.wait_for_timeout(5000) + + page.locator('span:text("Test (you)")').first.click() + + plus_create_icon = page.locator('button[aria-label="Create"]').last + expect(plus_create_icon).to_be_visible() + plus_create_icon.click() + + page.get_by_text("Assign task").click() + page.get_by_role("textbox", name="Title").fill("sample title") + page.get_by_role("textbox", name="Title").press("Tab") + page.get_by_role("textbox", name="Description (optional)").fill("sample description") + page.get_by_role("button", name="Next").click() + + page.locator('button div:text("Confirm task")').nth(0).click() + page.wait_for_timeout(5000) + + task_title_div = page.locator('div[data-testid="comment"] div:has-text("sample title")').last + task_title_div.click() + + expanded_task_title_div = page.locator('div[aria-label="sample title"]').first + expanded_task_title_div.click() + + page.get_by_role("textbox", name="Title").fill("") + page.get_by_role("button", name="Save").click() + page.wait_for_timeout(10000) + + + title_missing_warning = page.locator('div:text("Please enter a title")') + expect(title_missing_warning).to_be_visible() + + page.wait_for_timeout(10000) + + field_required_warning = page.locator('div:text("This field is required.")') + expect(field_required_warning).to_be_hidden() + browser.close() + diff --git a/issues/19023_868/user_tool.py b/issues/19023_868/user_tool.py new file mode 100644 index 0000000..cc1e785 --- /dev/null +++ b/issues/19023_868/user_tool.py @@ -0,0 +1,179 @@ +import sys, os + +base_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) +sys.path.insert(0, base_path) + +import re +import time +from playwright.sync_api import sync_playwright, expect +from playwright.sync_api._context_manager import PlaywrightContextManager +from playwright._impl._browser import Browser +from playwright._impl._page import Page +import time +import imaplib +import email +from email.header import decode_header +import re + + +from utils.trace_cleaner import trace_cleaner + +USER_EMAIL = "playwright2user+1@gmail.com" +PASSWORD = "zehr mglm gizg gjcc" + +def fetch_verification_code_from_email(user_email, password, retries=10, delay=10): + """ + Fetch the OTP code from the latest email. + """ + imap = imaplib.IMAP4_SSL("imap.gmail.com") + imap.login(user_email, password) + + for attempt in range(retries): + imap.select("inbox") + status, messages = imap.search( + None, '(UNSEEN SUBJECT "Expensify magic sign-in code")' + ) + if status == "OK": + email_ids = messages[0].split() + + if email_ids: + + latest_email_id = email_ids[-1] + status, msg_data = imap.fetch(latest_email_id, "(RFC822)") + + for response_part in msg_data: + if isinstance(response_part, tuple): + msg = email.message_from_bytes(response_part[1]) + + + if msg.is_multipart(): + for part in msg.walk(): + content_type = part.get_content_type() + if content_type == "text/plain": + body = part.get_payload(decode=True).decode() + match = re.search(r'\b\d{6}\b', body) + if match: + otp_code = match.group(0) + imap.logout() + return otp_code + else: + + body = msg.get_payload(decode=True).decode() + match = re.search(r'\b\d{6}\b', body) + if match: + otp_code = match.group(0) + imap.logout() + return otp_code + else: + print("No new emails found. Retrying...") + otp_code="123456" + return otp_code + else: + print("Failed to retrieve emails. Retrying...") + + + time.sleep(delay) + + imap.logout() + raise Exception("Max retries reached. No magic code email found.") + + +def generate_random_email(): + timestamp = int(time.time()) + return f"testuser{timestamp}@gmail.com" + + +def login_user(p: PlaywrightContextManager, first_name="Ayush", last_name="G") -> tuple[Browser, Page, str]: + + browser = p.chromium.launch( + headless=True, # Set to False to see the browser + args=[ + "--disable-web-security", # Disable CORS (for testing only) + "--disable-features=IsolateOrigins,site-per-process" + ]) + context = browser.new_context(ignore_https_errors=True) + page = context.new_page() + user_email = USER_EMAIL + + + page.goto('https://dev.new.expensify.com:8082/') + + + page.locator('input[type="email"]').fill(user_email) + page.locator('button[tabindex="0"]').click() + time.sleep(1) + + verification_code = fetch_verification_code_from_email(user_email, PASSWORD) + + + otp_input = page.locator("[autocomplete='one-time-code']") + expect(otp_input).to_be_visible() + otp_input.fill(verification_code) + + + try: + page.locator('button[tabindex="0"]').click() + time.sleep(1) + except Exception: + pass + + return browser, page, user_email + + +def test_save_description_in_submitted_expense(): + with sync_playwright() as p: + + browser, page, user_email = login_user(p) + context = page.context + + + context.tracing.start(screenshots=True, snapshots=True) + try: + page.wait_for_timeout(5000) + + page.locator('span:text("Test (you)")').first.click() + + plus_create_icon = page.locator('button[aria-label="Create"]').last + expect(plus_create_icon).to_be_visible() + plus_create_icon.click() + + page.get_by_text("Assign task").click() + page.get_by_role("textbox", name="Title").fill("sample title") + page.get_by_role("textbox", name="Title").press("Tab") + page.get_by_role("textbox", name="Description (optional)").fill("sample description") + page.get_by_role("button", name="Next").click() + + page.locator('button div:text("Confirm task")').nth(0).click() + page.wait_for_timeout(5000) + + task_title_div = page.locator('div[data-testid="comment"] div:has-text("sample title")').last + task_title_div.click() + + expanded_task_title_div = page.locator('div[aria-label="sample title"]').first + expanded_task_title_div.click() + + page.get_by_role("textbox", name="Title").fill("") + page.get_by_role("button", name="Save").click() + page.wait_for_timeout(10000) + + + title_missing_warning = page.locator('div:text("Please enter a title")') + expect(title_missing_warning).to_be_visible() + + page.wait_for_timeout(10000) + + field_required_warning = page.locator('div:text("This field is required.")') + expect(field_required_warning).to_be_hidden() + except Exception as e: + + print("Test encountered an exception:", e) + raise + finally: + + context.tracing.stop(path="/app/expensify/user_tool/output_browser1.zip") + + trace_cleaner("/app/expensify/user_tool/output_browser1.zip") + + + browser.close() +} \ No newline at end of file diff --git a/issues/24508_791/commit_id.txt b/issues/24508_791/commit_id.txt new file mode 100644 index 0000000..b00c18d --- /dev/null +++ b/issues/24508_791/commit_id.txt @@ -0,0 +1 @@ +2b791c9f3053c1682ddcb50ab036deb3e55a7542 diff --git a/issues/24508_791/flow.mitm b/issues/24508_791/flow.mitm new file mode 100644 index 0000000..620904f Binary files /dev/null and b/issues/24508_791/flow.mitm differ diff --git a/issues/24508_791/git_tag.txt b/issues/24508_791/git_tag.txt new file mode 100644 index 0000000..e69de29 diff --git a/issues/24508_791/issue_data.json b/issues/24508_791/issue_data.json new file mode 100644 index 0000000..4549430 --- /dev/null +++ b/issues/24508_791/issue_data.json @@ -0,0 +1 @@ +{"price": 1000.0, "title": "[HOLD for payment 2023-09-04] [$1000] App allows '\u00f7\u00d7' symbols in legal name even though error states that 'Name can only include letters'", "issue_repo_steps": "Action performed: 1. Open the app\r\n2. Open settings\r\n3. Open profile\r\n4. Open personal details\r\n5. Open legal name\r\n6. Add '\u00f7\u00d7' in first or last name ( in web, copy & paste from other source and in mobile switch the keyboard to number keypad) and click save, observe it allows to save\r\n7. Add any other symbol or number and click save, observe that it will throw 'Name can only include letters' error\n\nExpected result: App should not allow any symbols in legal name\n\nActual result: None"} \ No newline at end of file diff --git a/issues/24508_791/issue_introduction.patch b/issues/24508_791/issue_introduction.patch new file mode 100644 index 0000000..92781ad --- /dev/null +++ b/issues/24508_791/issue_introduction.patch @@ -0,0 +1,15 @@ +diff --git a/src/CONST.ts b/src/CONST.ts +index 4ca9b45f13d..4c7875e5219 100755 +--- a/src/CONST.ts ++++ b/src/CONST.ts +@@ -2616,8 +2616,8 @@ const CONST = { + REGEX: { + SPECIAL_CHARS_WITHOUT_NEWLINE: /((?!\n)[()-\s\t])/g, + DIGITS_AND_PLUS: /^\+?[0-9]*$/, +- ALPHABETIC_AND_LATIN_CHARS: /^[\p{Script=Latin} ]*$/u, +- NON_ALPHABETIC_AND_NON_LATIN_CHARS: /[^\p{Script=Latin}]/gu, ++ ALPHABETIC_AND_LATIN_CHARS: /^[a-zA-ZÀ-ÿ ]*$/, ++ NON_ALPHABETIC_AND_NON_LATIN_CHARS: /[^a-zA-ZÀ-ÿ]/g, + POSITIVE_INTEGER: /^\d+$/, + PO_BOX: /\b[P|p]?(OST|ost)?\.?\s*[O|o|0]?(ffice|FFICE)?\.?\s*[B|b][O|o|0]?[X|x]?\.?\s+[#]?(\d+)\b/, + ANY_VALUE: /^.+$/, \ No newline at end of file diff --git a/issues/24508_791/revert_command.txt b/issues/24508_791/revert_command.txt new file mode 100644 index 0000000..e69de29 diff --git a/issues/24508_791/test.py b/issues/24508_791/test.py new file mode 100644 index 0000000..b6b204d --- /dev/null +++ b/issues/24508_791/test.py @@ -0,0 +1,67 @@ +import sys, os + +base_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) +sys.path.insert(0, base_path) + +from playwright.sync_api import sync_playwright, expect +from playwright.sync_api._context_manager import PlaywrightContextManager +from playwright._impl._page import Page + +FIRST_NAME = "Milan" +LAST_NAME = "T" + + +def get_user_email(): + return f"nitish.expensify+{92}@gmail.com" + + +def login_user(p: PlaywrightContextManager): + browser = p.chromium.launch(headless=True, args=[ + "--ignore-certificate-errors", "--disable-web-security", "--disable-features=IsolateOrigins,site-per-process"], + slow_mo=2000) + + page = browser.new_page() + user_email = get_user_email() + + page.goto('https://dev.new.expensify.com:8082/') + + page.locator('input[type="email"]').fill(user_email) + page.locator('button[tabindex="0"]').click() + + try: + expect(page.get_by_role("button", name="Sign in")).to_be_visible() + page.get_by_test_id("validateCode").fill("123456") + except Exception: + page.get_by_role("button", name="Join").click() + + return browser, page + + +def navigate_to_profile(page: Page): + page.locator('button[aria-label="My settings"]').click() + page.locator('div[aria-label="Profile"]').first.click() + + +def edit_legal_name(page: Page): + page.locator('div[role="menuitem"]', has_text="Legal name").click() + + fname_input_field = page.locator('div[aria-label="Legal first name"] input') + lname_input_field = page.locator('div[aria-label="Legal last name"] input') + save_button = page.get_by_role('button', name="Save") + + fname_input_field.fill(FIRST_NAME + '÷×') + lname_input_field.fill(LAST_NAME + '÷×') + save_button.click() + error_msg = page.get_by_text("Name can only include Latin characters.").all() + assert len(error_msg) == 2 + + +def test_fake_assignee_name_removed(): + + with sync_playwright() as p: + browser, page = login_user(p) + + navigate_to_profile(page) + edit_legal_name(page) + + browser.close() diff --git a/issues/24508_791/user_tool.py b/issues/24508_791/user_tool.py new file mode 100644 index 0000000..72a834c --- /dev/null +++ b/issues/24508_791/user_tool.py @@ -0,0 +1,74 @@ +import sys, os +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) + +from playwright.sync_api import sync_playwright, expect +from playwright.sync_api._context_manager import PlaywrightContextManager +from playwright._impl._page import Page + +FIRST_NAME = "Milan" +LAST_NAME = "T" + + +def get_user_email(): + return f"nitish.expensify+{92}@gmail.com" + + +def login_user(p: PlaywrightContextManager): + browser = p.chromium.launch( + headless=True, + args=["--ignore-certificate-errors", "--disable-web-security", "--disable-features=IsolateOrigins,site-per-process"], + slow_mo=2000 + ) + context = browser.new_context() + page = context.new_page() + user_email = get_user_email() + + page.goto('https://dev.new.expensify.com:8082/') + + page.locator('input[type="email"]').fill(user_email) + page.locator('button[tabindex="0"]').click() + + try: + expect(page.get_by_role("button", name="Sign in")).to_be_visible() + page.get_by_test_id("validateCode").fill("123456") + except Exception: + page.get_by_role("button", name="Join").click() + + return browser, context, page + + +def navigate_to_profile(page: Page): + page.locator('button[aria-label="My settings"]').click() + page.locator('div[aria-label="Profile"]').first.click() + + +def edit_legal_name(page: Page): + page.locator('div[role="menuitem"]', has_text="Legal name").click() + + fname_input_field = page.locator('div[aria-label="Legal first name"] input') + lname_input_field = page.locator('div[aria-label="Legal last name"] input') + save_button = page.get_by_role('button', name="Save") + + fname_input_field.fill(FIRST_NAME + '÷×') + lname_input_field.fill(LAST_NAME + '÷×') + save_button.click() + error_msg = page.get_by_text("Name can only include Latin characters.").all() + assert len(error_msg) == 2 + + +def test_fake_assignee_name_removed(): + with sync_playwright() as p: + browser, context, page = login_user(p) + context.tracing.start(screenshots=True, snapshots=True, sources=True) + try: + navigate_to_profile(page) + edit_legal_name(page) + except Exception as e: + raise e + finally: + context.tracing.stop(path="/app/expensify/user_tool/output_browser1.zip") + trace_cleaner("/app/expensify/user_tool/output_browser1.zip") + browser.close() \ No newline at end of file diff --git a/issues/24642_386/commit_id.txt b/issues/24642_386/commit_id.txt new file mode 100644 index 0000000..4c52b32 --- /dev/null +++ b/issues/24642_386/commit_id.txt @@ -0,0 +1 @@ +544661b6df46a0bb60575c4e89478e8e62079674 \ No newline at end of file diff --git a/issues/24642_386/flow.mitm b/issues/24642_386/flow.mitm new file mode 100644 index 0000000..f434e68 Binary files /dev/null and b/issues/24642_386/flow.mitm differ diff --git a/issues/24642_386/git_tag.txt b/issues/24642_386/git_tag.txt new file mode 100644 index 0000000..e69de29 diff --git a/issues/24642_386/issue_data.json b/issues/24642_386/issue_data.json new file mode 100644 index 0000000..4971555 --- /dev/null +++ b/issues/24642_386/issue_data.json @@ -0,0 +1 @@ +{"price": 1000.0, "title": "[HOLD for payment 2023-09-20] [$1000] Web - Profile - No back button on personal details offline mode", "issue_repo_steps": "Action performed: 1. Open the app\r\n2. If logged in then logout\r\n3. Login and switch off internet connection\r\n4. Visit settings->profile->personal details and observe that page does not have back button while loading\n\nExpected result: App should display back button on personal details page while loading on offline mode\n\nActual result: None"} \ No newline at end of file diff --git a/issues/24642_386/issue_introduction.patch b/issues/24642_386/issue_introduction.patch new file mode 100644 index 0000000..4f55dda --- /dev/null +++ b/issues/24642_386/issue_introduction.patch @@ -0,0 +1,445 @@ +diff --git a/src/pages/settings/Profile/PersonalDetails/AddressPage.js b/src/pages/settings/Profile/PersonalDetails/AddressPage.js +index 886e207bf68..b26c833aee1 100644 +--- a/src/pages/settings/Profile/PersonalDetails/AddressPage.js ++++ b/src/pages/settings/Profile/PersonalDetails/AddressPage.js +@@ -68,7 +68,6 @@ function AddressPage({privatePersonalDetails}) { + const zipFormat = translate('common.zipCodeExampleFormat', {zipSampleFormat}); + + const address = lodashGet(privatePersonalDetails, 'address') || {}; +- const isLoadingPersonalDetails = lodashGet(privatePersonalDetails, 'isLoading', true); + const [street1, street2] = (address.street || '').split('\n'); + const [state, setState] = useState(address.state); + /** +@@ -128,6 +127,10 @@ function AddressPage({privatePersonalDetails}) { + setState(value); + }; + ++ if (lodashGet(privatePersonalDetails, 'isLoading', true)) { ++ return ; ++ } ++ + return ( + + Navigation.goBack(ROUTES.SETTINGS_PERSONAL_DETAILS)} + /> +- {isLoadingPersonalDetails ? ( +- +- ) : ( +-
+- +- +- +- +- ++ ++ +- ++ ++ ++ ++ ++ ++ ++ ++ ++ {isUSAForm ? ( + +- +- +- +- {isUSAForm ? ( +- +- +- +- ) : ( +- +- )} +- ++ ++ ) : ( + +- +- +- +- )} ++ )} ++ ++ ++ ++ ++ +
+ ); + } +diff --git a/src/pages/settings/Profile/PersonalDetails/DateOfBirthPage.js b/src/pages/settings/Profile/PersonalDetails/DateOfBirthPage.js +index 22346a48658..90c469c4e25 100644 +--- a/src/pages/settings/Profile/PersonalDetails/DateOfBirthPage.js ++++ b/src/pages/settings/Profile/PersonalDetails/DateOfBirthPage.js +@@ -38,7 +38,6 @@ const defaultProps = { + + function DateOfBirthPage({translate, privatePersonalDetails}) { + usePrivatePersonalDetails(); +- const isLoadingPersonalDetails = lodashGet(privatePersonalDetails, 'isLoading', true); + + /** + * @param {Object} values +@@ -60,32 +59,32 @@ function DateOfBirthPage({translate, privatePersonalDetails}) { + return errors; + }, []); + ++ if (lodashGet(privatePersonalDetails, 'isLoading', true)) { ++ return ; ++ } ++ + return ( + + Navigation.goBack(ROUTES.SETTINGS_PERSONAL_DETAILS)} + /> +- {isLoadingPersonalDetails ? ( +- +- ) : ( +-
+- +- +- )} ++
++ ++ +
+ ); + } +diff --git a/src/pages/settings/Profile/PersonalDetails/LegalNamePage.js b/src/pages/settings/Profile/PersonalDetails/LegalNamePage.js +index 0caf20a3e12..03181624731 100644 +--- a/src/pages/settings/Profile/PersonalDetails/LegalNamePage.js ++++ b/src/pages/settings/Profile/PersonalDetails/LegalNamePage.js +@@ -47,7 +47,6 @@ function LegalNamePage(props) { + usePrivatePersonalDetails(); + const legalFirstName = lodashGet(props.privatePersonalDetails, 'legalFirstName', ''); + const legalLastName = lodashGet(props.privatePersonalDetails, 'legalLastName', ''); +- const isLoadingPersonalDetails = lodashGet(props.privatePersonalDetails, 'isLoading', true); + + const validate = useCallback((values) => { + const errors = {}; +@@ -67,6 +66,10 @@ function LegalNamePage(props) { + return errors; + }, []); + ++ if (lodashGet(props.privatePersonalDetails, 'isLoading', true)) { ++ return ; ++ } ++ + return ( + Navigation.goBack(ROUTES.SETTINGS_PERSONAL_DETAILS)} + /> +- {isLoadingPersonalDetails ? ( +- +- ) : ( +-
+- +- +- +- +- +- +-
+- )} ++
++ ++ ++ ++ ++ ++ ++
+
+ ); + } +diff --git a/src/pages/settings/Profile/PersonalDetails/PersonalDetailsInitialPage.js b/src/pages/settings/Profile/PersonalDetails/PersonalDetailsInitialPage.js +index e1c4f14047a..e22aeca6a3d 100644 +--- a/src/pages/settings/Profile/PersonalDetails/PersonalDetailsInitialPage.js ++++ b/src/pages/settings/Profile/PersonalDetails/PersonalDetailsInitialPage.js +@@ -60,7 +60,6 @@ function PersonalDetailsInitialPage(props) { + const privateDetails = props.privatePersonalDetails || {}; + const address = privateDetails.address || {}; + const legalName = `${privateDetails.legalFirstName || ''} ${privateDetails.legalLastName || ''}`.trim(); +- const isLoadingPersonalDetails = lodashGet(props.privatePersonalDetails, 'isLoading', true); + + /** + * Applies common formatting to each piece of an address +@@ -84,42 +83,42 @@ function PersonalDetailsInitialPage(props) { + return formattedAddress.trim().replace(/,$/, ''); + }; + ++ if (lodashGet(props.privatePersonalDetails, 'isLoading', true)) { ++ return ; ++ } ++ + return ( + + Navigation.goBack(ROUTES.SETTINGS_PROFILE)} + /> +- {isLoadingPersonalDetails ? ( +- +- ) : ( +- +- +- +- {props.translate('privatePersonalDetails.privateDataMessage')} +- +- Navigation.navigate(ROUTES.SETTINGS_PERSONAL_DETAILS_LEGAL_NAME)} +- /> +- Navigation.navigate(ROUTES.SETTINGS_PERSONAL_DETAILS_DATE_OF_BIRTH)} +- titleStyle={[styles.flex1]} +- /> +- Navigation.navigate(ROUTES.SETTINGS_PERSONAL_DETAILS_ADDRESS)} +- /> ++ ++ ++ ++ {props.translate('privatePersonalDetails.privateDataMessage')} + +- +- )} ++ Navigation.navigate(ROUTES.SETTINGS_PERSONAL_DETAILS_LEGAL_NAME)} ++ /> ++ Navigation.navigate(ROUTES.SETTINGS_PERSONAL_DETAILS_DATE_OF_BIRTH)} ++ titleStyle={[styles.flex1]} ++ /> ++ Navigation.navigate(ROUTES.SETTINGS_PERSONAL_DETAILS_ADDRESS)} ++ /> ++ ++ + + ); + } diff --git a/issues/24642_386/revert_command.txt b/issues/24642_386/revert_command.txt new file mode 100644 index 0000000..e69de29 diff --git a/issues/24642_386/test.py b/issues/24642_386/test.py new file mode 100644 index 0000000..0e1206d --- /dev/null +++ b/issues/24642_386/test.py @@ -0,0 +1,128 @@ +import sys, os + +base_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) +sys.path.insert(0, base_path) + +from playwright.sync_api import sync_playwright, expect +import re, time +from datetime import datetime, timezone +from email.header import decode_header + +import imaplib, pytest +import email +import re +import random + + +NEWDOT_URL = "http://localhost:8082/" +EMAIL = "f2139887@gmail.com" +PASSWORD = "sdib pdop ovyl uawy" + + +def generate_random_email(): + return f"f2139887+46784@gmail.com" + + +def get_magic_code(user_email, password, since=None, retries=5, delay=10): + if since is None: + since = datetime.now(timezone.utc).replace(second=0, microsecond=0) + + imap = imaplib.IMAP4_SSL("imap.gmail.com") + imap.login(user_email, password) + + for _ in range(retries): + imap.select("inbox") + status, messages = imap.search(None, '(UNSEEN SUBJECT "Expensify magic sign-in code:")') + + if status == "OK": + email_ids = messages[0].split() + + if email_ids: + latest_email_id = email_ids[-1] + status, msg_data = imap.fetch(latest_email_id, "(RFC822)") + + for response_part in msg_data: + if isinstance(response_part, tuple): + msg = email.message_from_bytes(response_part[1]) + subject, encoding = decode_header(msg["Subject"])[0] + if isinstance(subject, bytes): + subject = subject.decode(encoding or "utf-8") + + match = re.search(r"Expensify magic sign-in code: (\d+)", subject) + email_date = email.utils.parsedate_to_datetime(msg["Date"]) + if match and email_date >= since: + code = match.group(1) + imap.logout() + return code + print("Email doesn't match conditions. Retrying...") + else: + print("No unread emails found with the subject. Retrying...") + else: + print("Failed to retrieve emails. Retrying...") + time.sleep(delay) + + imap.logout() + print("Max retries reached. Email not found.") + return None + + +def login_user_ND_with_magiccode(user_email, password, page): + + + page.goto(NEWDOT_URL) + + + page.locator('input[type="email"]').fill(user_email) + page.locator("div:nth-child(3) > div:nth-child(2) > div > div > div > .css-view-175oi2r").click() + + page.wait_for_timeout(5000) + + + + magic_code = "123456" + + page.locator('input[autocomplete="one-time-code"]').fill(magic_code) + page.wait_for_timeout(500) + + + + +def test_issue_24642(): + with sync_playwright() as p: + browser = p.chromium.launch( + channel="chrome", + headless=True, + args=[ + "--ignore-certificate-errors", + "--disable-web-security", + "--disable-features=IsolateOrigins,site-per-process" + ], + proxy={"server": "http://localhost:8080"}, + slow_mo=500 + ) + + context_args = {} + context_args["geolocation"] = {"latitude": 37.7749, "longitude": -122.4194} + context_args["permissions"] = ["geolocation"] + context_args["ignore_https_errors"] = True + + context = browser.new_context(**context_args) + page = context.new_page() + first_user_email = generate_random_email() + + login_user_ND_with_magiccode(first_user_email, PASSWORD, page) + page.wait_for_timeout(2000) + context.set_offline(True) + page.get_by_label("My settings").click() + page.get_by_text("Profile").click() + page.get_by_text("Personal details").click() + try: + + page.get_by_role("button", name="Back").click(timeout=10000) + + except Exception as e: + pytest.fail("Test failed due to an exception") + + + context.close() + browser.close() diff --git a/issues/24642_386/user_tool.py b/issues/24642_386/user_tool.py new file mode 100644 index 0000000..45edc30 --- /dev/null +++ b/issues/24642_386/user_tool.py @@ -0,0 +1,136 @@ +import sys, os + +base_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) +sys.path.insert(0, base_path) + +from playwright.sync_api import sync_playwright, expect +import re, time +from datetime import datetime, timezone +from email.header import decode_header + +import imaplib, pytest +import email +import re +import random + +from utils.trace_cleaner import trace_cleaner + + +NEWDOT_URL = "http://localhost:8082/" +EMAIL = "f2139887@gmail.com" +PASSWORD = "sdib pdop ovyl uawy" + + +def generate_random_email(): + return f"f2139887+46784@gmail.com" + + +def get_magic_code(user_email, password, since=None, retries=5, delay=10): + if since is None: + since = datetime.now(timezone.utc).replace(second=0, microsecond=0) + + imap = imaplib.IMAP4_SSL("imap.gmail.com") + imap.login(user_email, password) + + for _ in range(retries): + imap.select("inbox") + status, messages = imap.search(None, '(UNSEEN SUBJECT "Expensify magic sign-in code:")') + + if status == "OK": + email_ids = messages[0].split() + + if email_ids: + latest_email_id = email_ids[-1] + status, msg_data = imap.fetch(latest_email_id, "(RFC822)") + + for response_part in msg_data: + if isinstance(response_part, tuple): + msg = email.message_from_bytes(response_part[1]) + subject, encoding = decode_header(msg["Subject"])[0] + if isinstance(subject, bytes): + subject = subject.decode(encoding or "utf-8") + + match = re.search(r"Expensify magic sign-in code: (\d+)", subject) + email_date = email.utils.parsedate_to_datetime(msg["Date"]) + if match and email_date >= since: + code = match.group(1) + imap.logout() + return code + print("Email doesn't match conditions. Retrying...") + else: + print("No unread emails found with the subject. Retrying...") + else: + print("Failed to retrieve emails. Retrying...") + time.sleep(delay) + + imap.logout() + print("Max retries reached. Email not found.") + return None + + +def login_user_ND_with_magiccode(user_email, password, page): + + + page.goto(NEWDOT_URL) + + + page.locator('input[type="email"]').fill(user_email) + page.locator("div:nth-child(3) > div:nth-child(2) > div > div > div > .css-view-175oi2r").click() + + page.wait_for_timeout(5000) + + + + magic_code = "123456" + + page.locator('input[autocomplete="one-time-code"]').fill(magic_code) + page.wait_for_timeout(500) + + +def test_issue_24642(): + with sync_playwright() as p: + browser = p.chromium.launch( + channel="chrome", + headless=True, + args=[ + "--ignore-certificate-errors", + "--disable-web-security", + "--disable-features=IsolateOrigins,site-per-process" + ], + proxy={"server": "http://localhost:8080"}, + slow_mo=500 + ) + + context_args = {} + context_args["geolocation"] = {"latitude": 37.7749, "longitude": -122.4194} + context_args["permissions"] = ["geolocation"] + context_args["ignore_https_errors"] = True + + context = browser.new_context(**context_args) + context.tracing.start(screenshots=True, snapshots=True, sources=True) + + try: + page = context.new_page() + first_user_email = generate_random_email() + + login_user_ND_with_magiccode(first_user_email, PASSWORD, page) + page.wait_for_timeout(2000) + context.set_offline(True) + page.get_by_label("My settings").click() + page.get_by_text("Profile").click() + page.get_by_text("Personal details").click() + try: + page.get_by_role("button", name="Back").click(timeout=10000) + + except Exception as e: + pytest.fail("Test failed due to an exception") + + except Exception as main_ex: + raise main_ex + finally: + context.tracing.stop() + trace_path = "/app/expensify/user_tool/output_browser1.zip" + context.tracing.export(trace_path) + trace_cleaner(trace_path) + context.close() + browser.close() diff --git a/issues/25570_780/commit_id.txt b/issues/25570_780/commit_id.txt new file mode 100644 index 0000000..b00c18d --- /dev/null +++ b/issues/25570_780/commit_id.txt @@ -0,0 +1 @@ +2b791c9f3053c1682ddcb50ab036deb3e55a7542 diff --git a/issues/25570_780/flow.mitm b/issues/25570_780/flow.mitm new file mode 100644 index 0000000..251c783 Binary files /dev/null and b/issues/25570_780/flow.mitm differ diff --git a/issues/25570_780/git_tag.txt b/issues/25570_780/git_tag.txt new file mode 100644 index 0000000..e69de29 diff --git a/issues/25570_780/issue_data.json b/issues/25570_780/issue_data.json new file mode 100644 index 0000000..f0acf4f --- /dev/null +++ b/issues/25570_780/issue_data.json @@ -0,0 +1 @@ +{"price": 1000.0, "title": "[HOLD for payment 2023-09-08] [$1000] mWeb - Chat - Hide the keyboard on click of confirm button", "issue_repo_steps": "Action performed: 1. go to any chat and send password protected pdf\r\n2. click on PDF and type wrong password\r\n3. click on confirm button\r\n\r\n\r\n\r\n[example protected PDF.pdf](https://github.com/Expensify/App/files/12428724/example.protected.PDF.pdf)\r\nPassword: `25570`\n\nExpected result: Should not hide the keyboard same as native app\n\nActual result: None"} \ No newline at end of file diff --git a/issues/25570_780/issue_introduction.patch b/issues/25570_780/issue_introduction.patch new file mode 100644 index 0000000..70240e5 --- /dev/null +++ b/issues/25570_780/issue_introduction.patch @@ -0,0 +1,13 @@ +diff --git a/src/components/PDFView/PDFPasswordForm.tsx b/src/components/PDFView/PDFPasswordForm.tsx +index 4b6491addbe..ff0f7321c17 100644 +--- a/src/components/PDFView/PDFPasswordForm.tsx ++++ b/src/components/PDFView/PDFPasswordForm.tsx +@@ -134,8 +134,6 @@ function PDFPasswordForm({isFocused, isPasswordInvalid = false, shouldShowLoadin + secureTextEntry + /> +