issues/278/user_tool.py (160 lines of code) (raw):

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) import email from email.header import decode_header import imaplib from playwright.sync_api import sync_playwright import re import unittest import time class TestSuite(unittest.TestCase): @classmethod def setUp(cls): cls.username = 'testemail.ameen' # Email username email = cls.generate_random_email() cls.password = "" # App password cls.url = 'https://dev.new.expensify.com:8082/' cls.playwright = sync_playwright().start() cls.browser = cls.playwright.chromium.launch(headless=True, args=[ '--disable-web-security', '--disable-features=IsolateOrigins,site-per-process', ]) cls.context = cls.browser.new_context() cls.context.tracing.start(screenshots=True, snapshots=True, sources=True) cls.page = cls.context.new_page() cls.page.goto(cls.url, timeout=60000) cls.login_account(email) @classmethod def tearDown(cls): try: cls.context.tracing.stop(path="/app/expensify/user_tool/output_browser1.zip") trace_cleaner("/app/expensify/user_tool/output_browser1.zip") except Exception as e: print(f"Error during tracing export: {e}") finally: cls.page.close() cls.context.close() cls.browser.close() cls.playwright.stop() @classmethod def login_account(cls, email, first_name='Test', last_name='User', context_page=None): page = context_page if context_page else cls.page page.locator('input[type="email"]').fill(email) page.locator('button[tabindex="0"]').click(timeout=10000) try: page.wait_for_selector("text=Please enter the magic code sent to", timeout=5000) cls.existing_login = True expensify_opt = cls.get_magic_code(email, cls.password) # Get OTP page.locator('input[inputmode="numeric"]').fill(expensify_opt) page.wait_for_selector('button[tabindex="0"]:has-text("Sign in")', timeout=15000) except: page.wait_for_selector('button[tabindex="0"]:has-text("Join")', timeout=15000) page.locator('button[tabindex="0"]:has-text("Join")').click() try: page.locator("text='Track and budget expenses'").click() page.get_by_role("button", name="Continue").click() page.locator('input[name="fname"]').wait_for(state="visible", timeout=10000) page.locator('input[name="fname"]').fill(first_name) page.locator('input[name="lname"]').fill(last_name) page.get_by_role("button", name="Continue").click() except: pass @classmethod def generate_random_email(cls): timestamp = int(time.time()) return f"{cls.username}0984090740@gmail.com" @classmethod def get_magic_code(cls, user_email, password, retries=5, delay=8000): cls.page.wait_for_timeout(delay) 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) if match: code = match.group(1) imap.logout() return code else: print("No unread emails found with the subject. Retrying...") else: print("Failed to retrieve emails. Retrying...") cls.page.wait_for_timeout(delay) imap.logout() print("Max retries reached. Email not found.") return None @classmethod def click_element(cls, selectors_or_locator, context_page=None): page = context_page if context_page else cls.page if isinstance(selectors_or_locator, list): element = page.locator(selectors_or_locator[0]) for selector in selectors_or_locator[1:]: element = element.locator(selector) elif isinstance(selectors_or_locator, str): element = page.locator(selectors_or_locator) else: element = selectors_or_locator element.wait_for(state="visible", timeout=10000) element.click() @classmethod def navigate_with_tabs(self, tab_count, enter=True, context_page=None): page = context_page if context_page else self.page self.page.wait_for_timeout(1000) page.bring_to_front() for _ in range(tab_count): page.keyboard.press("Tab") if enter: page.keyboard.press("Enter") @classmethod def start_group_chat(cls): page = cls.page cls.click_element('button[aria-label="Start chat (Floating action)"]') cls.click_element('div[aria-label="Start chat"][role="menuitem"]') page.locator('input[aria-label="Name, email, or phone number"]').fill('testuser@gmail.com') cls.navigate_with_tabs(2) # Select User cls.click_element('button:has-text("Next")') cls.click_element('button:has-text("Start group")') @classmethod def create_task(cls): page = cls.page cls.click_element( page.locator('button[aria-label="Create"]').nth(1) ) cls.click_element(page.get_by_text("Assign task")) page.locator('input[aria-label="Title"]').fill("Test Task") cls.click_element( page.locator("button", has_text="Next") ) cls.click_element(page.get_by_role("button", name="Confirm task")) def test_task_checkbox_center_vertically_with_title(self): try: page = self.page self.start_group_chat() self.create_task() page.wait_for_timeout(2000) task_checkbox = page.get_by_role("checkbox", name="Task").bounding_box() task_title = page.get_by_role("button", name="Task").bounding_box() checkbox_height = task_checkbox['height'] title_height = task_title['height'] if task_title['height'] == 24: task_title = page.get_by_role("button", name="Task").locator('div').nth(1).bounding_box() title_height = task_title['height'] assert task_checkbox['height'] == task_title['height'], f"Task title and checkbox heights are not the same: {checkbox_height} != {title_height}" except Exception as e: self.fail(f"Test failed due to error: {str(e)}") if __name__ == "__main__": unittest.main(verbosity=2)