issues/190/user_tool.py (151 lines of code) (raw):

from io import BytesIO import time from playwright.sync_api import Playwright, BrowserContext, Page, sync_playwright, expect import pytest from PIL import Image from utils.email_handler import EmailHandler from utils.trace_cleaner import trace_cleaner NEWDOT_URL = "https://dev.new.expensify.com:8082/" EMAIL_ADDRESS = "t56826807@gmail.com" PASSWORD = "" FIRST_NAME = "D" LAST_NAME = "C" # Global variable to hold the browser instance for closing in the test final block BROWSER = None def generate_random_email(base_email=EMAIL_ADDRESS, seed=None): if seed is None: seed = int(time.time()) email_user, domain = base_email.split('@') return f"{email_user}+{seed}@{domain}" @pytest.fixture(scope='session') def playwright_instance(): with sync_playwright() as playwright: yield playwright @pytest.fixture(scope='session') def context(playwright_instance: Playwright): global BROWSER BROWSER = playwright_instance.chromium.launch( channel="chrome", headless=False, args=[ "--ignore-certificate-errors", "--disable-web-security", "--disable-features=IsolateOrigins,site-per-process" ], proxy={"server": "http://localhost:8080"}, slow_mo=500, ) context = BROWSER.new_context( # viewport={"width": 1280, "height": 680}, ) yield context # Removed browser.close() here; it will be closed in the test's final block # BROWSER.close() @pytest.fixture(scope='function') def page(context: BrowserContext): page = context.new_page() yield page # Removed context.close() here to avoid double-closing def login_user(page: Page, email: str, password: str, first_name=FIRST_NAME, last_name=LAST_NAME, mock_otp: bool=False): try: # If the user is already logged in, the inbox should be visible expect(page.get_by_label("Inbox")).to_be_visible(timeout=3000) return except: pass with EmailHandler(email, password) as email_handler: # Clean inbox if not mock_otp: email_handler.mark_all_unread_as_read() # Enter email and click continue page.get_by_test_id("username").fill(email) page.get_by_role("button", name="Continue").click() # Check if we have can join directly or we need a magic code join_button = page.get_by_role("button", name="Join") validate_code_input = page.get_by_test_id("validateCode") expect(join_button.or_(validate_code_input)).to_be_visible() if (join_button.is_visible()): join_button.click() onboarding_user(page, first_name, last_name) else: # Await OTP otp = "123456" if mock_otp else email_handler.read_otp_code() validate_code_input.fill(otp) # Wait sign in to complete page.get_by_text("Please enter the magic code").wait_for(state="hidden") def onboarding_user(page: Page, first_name=FIRST_NAME, last_name=LAST_NAME): expect(page.get_by_text("What do you want to do today?")).to_be_visible(timeout=30000) # Select 'Track and budget expenses' in onboarding page and click Continue activity_text = "Track and budget expenses" page.get_by_label(activity_text).click() # Enter first name, last name and click continue page.get_by_role("textbox", name="First name").fill(first_name) page.get_by_role("textbox", name="Last name").fill(last_name) page.get_by_role("button", name="Continue").last.click() def create_new_workspace( page: Page, name: str = None, default_currency: str = None, members: list[str] = None, should_go_back: bool = True, ): # Click on + icon and click on "New workspace" page.get_by_label("Start chat (Floating action)").click() page.get_by_label("New workspace").click() if name: page.get_by_test_id("WorkspacePageWithSections").get_by_text("Name").click() page.get_by_role("textbox", name="Name").fill(name) page.get_by_role("button", name="Save").click() if default_currency: page.get_by_text("Default currency").click() page.get_by_test_id("selection-list-text-input").fill(default_currency) page.get_by_label(default_currency).click() if members: page.get_by_label("Members").click() for member_email in members: invite_member(page, member_email) if should_go_back: page.get_by_label("Back").click() return page def invite_member(ws_setting_members_page: Page, member_email: str): ws_setting_members_page.get_by_role("button", name="Invite member").click() ws_setting_members_page.get_by_test_id("selection-list-text-input").fill(member_email) ws_setting_members_page.get_by_label(member_email).last.click() ws_setting_members_page.get_by_role("button", name="Next").click() ws_setting_members_page.get_by_test_id("WorkspaceInviteMessagePage").get_by_role("button", name="Invite").click() def submit_scan_expense(page: Page): # Click plus page.get_by_label("Create").last.click() # Select Submit expense page.get_by_label("Submit expense").click() # In the Right Hand Modal(RHM) select Scan page.get_by_label("Scan").click() def create_sample_jpg(): # Generates an image in memory and returns the bytes image = Image.new("RGB", (100, 100), color="blue") img_byte_arr = BytesIO() image.save(img_byte_arr, format="JPEG") img_byte_arr.seek(0) return img_byte_arr.getvalue() # Choose a file with page.expect_file_chooser() as fc_info: page.get_by_label("Choose file").click() file_chooser = fc_info.value file_chooser.set_files( { "name": "input.jpg", "mimeType": "image/jpeg", "buffer": create_sample_jpg(), } ) # Click Submit expense page.get_by_role("button", name="Submit expense").click() def test(page: Page): # Start tracing on the current browser context context = page.context context.tracing.start(screenshots=True, snapshots=True, sources=True) try: # Open expensify url page.goto(NEWDOT_URL) # Login user email_address = generate_random_email(seed=1739593958) login_user(page, email_address, PASSWORD, FIRST_NAME, LAST_NAME) # Enable all beta features create_new_workspace(page) # Go to the WS chat page.get_by_label("Inbox").click() display_name = f"{FIRST_NAME} {LAST_NAME}" workspace_name = f"{display_name}'s Workspace" page.locator( 'button[aria-label="Navigates to a chat"]', has=page.locator('div[data-testid="DisplayNamesWithTooltip"]', has_text=workspace_name) ).click() # Submit the first scan expense submit_scan_expense(page) try: # Dismiss the pop up ask for loocation permision page.get_by_role("button", name="Not Now").click() except: pass # Submit the second scan expense submit_scan_expense(page) # Verify the user is able to submit back to back scan expenses expect(page.get_by_text("Please enter a correct merchant.")).not_to_be_visible(timeout=5000) finally: trace_path = "/app/expensify/user_tool/output_browser1.zip" context.tracing.stop(path=trace_path) trace_cleaner(trace_path) global BROWSER if BROWSER is not None: BROWSER.close()