issues/289/user_tool.py (125 lines of code) (raw):
import sys, os
base_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
sys.path.insert(0, base_path)
import re
import random
import string
from imaplib import IMAP4_SSL
import email
from email.header import decode_header
from playwright.sync_api import sync_playwright, expect, Browser, Page
from playwright.sync_api._context_manager import PlaywrightContextManager
URL = "https://dev.new.expensify.com:8082/"
IMAP_SERVER = "imap.gmail.com"
MAX_TRIES = 2
USERNAME = "doorsqueaky@gmail.com"
PASSWORD = ""
def login_to_email(email: str=USERNAME, password: str=PASSWORD):
imap = IMAP4_SSL(IMAP_SERVER)
imap.login(email, password)
return imap
def logout_email(imap: IMAP4_SSL):
imap.logout()
def clear_inbox(imap: IMAP4_SSL):
imap.select("inbox")
status, messages = imap.search(None, 'ALL')
email_ids = messages[0].split()
for email_id in email_ids:
imap.store(email_id, "+FLAGS", "\\Deleted")
imap.expunge()
def generate_random_string(length: int) -> str:
random_string = ''.join(random.choice(string.ascii_letters) for i in range(length))
return random_string
def get_otp(imap: IMAP4_SSL):
imap.select("inbox") # Use "inbox" or other folder name
status, messages = imap.search(None, 'ALL')
email_ids = messages[0].split()
for email_id in email_ids:
status, msg_data = imap.fetch(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 if encoding else "utf-8")
r = re.search(r"Expensify magic sign-in code:.*", subject, re.IGNORECASE)
if not r:
return
temp = subject.split(":")
otp_code = temp[-1].strip()
return otp_code
return None
def launch_app(pw: PlaywrightContextManager, headless: bool=False) -> tuple[Browser, Page]:
"""
Launch the Expensify app.
"""
browser = pw.chromium.launch(headless=headless, args=["--disable-web-security", "--disable-features=IsolateOrigins,site-per-process"])
page = browser.new_page()
return browser, page
def login(page: Page, imap: IMAP4_SSL, email: str=USERNAME):
"""
Login to the Expensify app and complete the onboarding.
"""
page.goto(URL)
page.get_by_role("textbox", name="Phone or email").fill(email)
page.locator("div:nth-child(3) > div:nth-child(2) > div > div").first.click()
otp = None
tries = 0
while not otp and tries < MAX_TRIES:
otp = get_otp(imap)
tries += 1
page.wait_for_timeout(3000)
if not otp:
otp = "123456"
page.locator('input[name="validateCode"]').fill(otp)
try:
page.locator("div").filter(has_text=re.compile(r"^Sign in$")).nth(1).click(timeout=1000)
except:
pass
def cleanup(browser: Browser, page: Page) -> None:
page.wait_for_timeout(1000)
browser.close()
def create_workspace(page: Page) -> str:
page.locator('button[aria-label="My settings"]').click()
page.locator('div[aria-label="Workspaces"][role="menuitem"]').click()
page.locator('button[aria-label="New workspace"]').first.click()
page.locator(r'div[role="menuitem"]', has_text="Name").last.click()
input_field = page.locator('input[aria-label="Name"]')
input_field.clear()
workspace_name = generate_random_string(3) + " Workspace"
input_field.fill(workspace_name)
page.get_by_role('button', name="Save").click()
page.locator('button[aria-label="Back"]').click()
page.locator('button[aria-label="Inbox"]').click()
return workspace_name
def switch_workspace(page: Page, workspace_name: str):
page.locator('button[aria-label="Workspaces"]').click()
page.locator(f'button[aria-label="{workspace_name}"]').last.click()
title_inbox = page.get_by_text("Inbox").first
inbox_parent = title_inbox.locator('..')
title_workspace = inbox_parent.filter(has_text=f"{workspace_name}")
expect(title_workspace).to_be_visible()
def check_title_post_reload(page: Page, workspace_name: str):
page.reload()
title_inbox = page.get_by_text("Inbox").first
inbox_parent = title_inbox.locator('..')
title_workspace = inbox_parent.filter(has_text=f"{workspace_name}")
expect(title_workspace).to_be_visible()
def test_workspace_switch_on_reload():
"""
Demonstrate the Expensify app email login verification, now with tracing.
"""
from utils.trace_cleaner import trace_cleaner
trace_path = "/app/expensify/user_tool/output_browser1.zip"
with sync_playwright() as pw:
browser = pw.chromium.launch(headless=False, 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()
imap = None
try:
imap = login_to_email()
clear_inbox(imap)
login(page, imap, USERNAME)
workspace_name = create_workspace(page)
switch_workspace(page, workspace_name)
check_title_post_reload(page, workspace_name)
except Exception as e:
raise e
finally:
context.tracing.stop(path=trace_path)
trace_cleaner(trace_path)
if imap:
logout_email(imap)
cleanup(browser, page)