issues/225/user_tool.py (132 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)
from utils.trace_cleaner import trace_cleaner
import time
import imaplib
import logging
import sys
import re
import email
import string, random
import pytest
from playwright.sync_api import sync_playwright, expect, Page
EXPENSIFY_URL = "https://dev.new.expensify.com:8082/"
EMAIL_USERNAME = "naturesv057@gmail.com"
EMAIL_PASSWORD = "hyjk ilxi pnom oret"
EMAIL_USERNAME_POSTFIX = "49298_4"
logging.basicConfig(level=logging.INFO, stream=sys.stdout, format="%(message)s")
LOGGER = logging.getLogger(__name__)
def generate_user_email(user_id=None):
"""
Generate an email address for a user login.
"""
temp = EMAIL_USERNAME.rsplit("@", 1)
username = temp[0].strip()
domain = temp[1].strip()
return f"{username}+{EMAIL_USERNAME_POSTFIX}@{domain}"
def clear_inbox(username, password):
"""
Delete all existing messages from the Inbox.
"""
LOGGER.info("Deleting all existing messages from the email inbox")
with imaplib.IMAP4_SSL(host="imap.gmail.com") as imap:
imap.login(username, password)
imap.select("inbox")
imap.store("1:*", "+FLAGS", "\Deleted")
imap.expunge()
imap.close()
def get_otp_from_email(username, password, retries=2, delay=2):
"""
Read the OTP email and return the OTP code.
"""
LOGGER.info("Reading the OTP email")
with imaplib.IMAP4_SSL(host="imap.gmail.com") as imap:
imap.login(username, password)
for i in range(1, retries + 1):
imap.select("inbox")
status, messages = imap.search(None, "ALL")
if status == "OK":
for message_id in reversed(messages[0].split()):
status, data = imap.fetch(message_id, "(RFC822)")
if status == "OK":
email_message = email.message_from_bytes(data[0][1])
subject, encoding = email.header.decode_header(email_message["Subject"])[0]
if isinstance(subject, bytes):
subject = subject.decode(encoding)
if subject.startswith("Expensify magic sign-in code:"):
otp_code = subject.split(":")[-1].strip()
LOGGER.info("Got the OTP %s", otp_code)
return otp_code
time.sleep(delay)
imap.close()
return "123456"
def launch_browser(pw, headless=True, device=None, geolocation=None):
"""
Launch the browser.
"""
browser = pw.chromium.launch(
headless=True,
proxy={
'server': 'http://127.0.0.1:8080',
},
args=[
'--disable-web-security',
'--disable-features=IsolateOrigins,site-per-process',
],
)
context_args = {}
if device:
context_args.update(pw.devices[device])
if geolocation:
context_args["geolocation"] = geolocation
context_args["permissions"] = ["geolocation"]
context = browser.new_context(**context_args)
page = context.new_page()
return browser, context, page
def login_user(page, email, first_name="John", last_name="Doe"):
"""
Login to the Expensify app and complete the onboarding.
"""
clear_inbox(EMAIL_USERNAME, EMAIL_PASSWORD)
page.goto(EXPENSIFY_URL)
page.get_by_test_id("username").fill(email)
page.get_by_role("button", name="Continue").click()
otp_code = get_otp_from_email(EMAIL_USERNAME, EMAIL_PASSWORD)
page.get_by_test_id("validateCode").fill(otp_code)
def test():
with sync_playwright() as p:
email = generate_user_email()
LOGGER.info("User email %s", email)
browser, context, page = launch_browser(p)
# Start tracing the context
context.tracing.start(screenshots=True, snapshots=True, sources=True)
try:
login_user(page, email)
page.wait_for_timeout(5000)
LOGGER.info("User logged in successfully")
page.locator(
'button[aria-label="Start chat (Floating action)"][tabindex="0"]'
).click()
page.locator('div[aria-label="Start chat"][role="menuitem"]').click()
page.locator('input[aria-label="Name, email, or phone number"]').fill(
"t56826807+13@gmail.com"
)
try:
page.locator('button[aria-label="Employee"]').last.click(timeout=3000)
except:
page.get_by_label("t56826807+13@gmail.com").click()
page.locator('button[aria-label="Employee"]').last.click()
page.locator('div[aria-label="Private notes"]').last.click()
page.locator('div[aria-label="Private notes"][role="textbox"]').fill(
"new line\n" * 100
)
page.locator('button[data-listener="Enter"]', has_text="Save").last.click()
page.locator('div[aria-label="Private notes"]').last.click()
text = page.locator('div[data-testid="PrivateNotesListPage"]').inner_text()
print(text)
page.wait_for_selector('div[data-testid="PrivateNotesListPage"]')
parent_div = page.locator('div[data-testid="PrivateNotesListPage"]')
child_with_scroll_y = parent_div.evaluate_handle(
"""parent => {
const children = parent.querySelectorAll("div");
return Array.from(children).find(child => {
const style = getComputedStyle(child);
return style.overflowY === "scroll" || style.overflowY === "auto";
});
}"""
)
if child_with_scroll_y:
text = child_with_scroll_y.evaluate("element => element.innerText")
print(text)
else:
raise Exception("No child div with overflow-y set to scroll or auto found.")
assert (
"Keep notes about this chat here. You're the only person who can add, edit, or view these notes."
in text
), "The intro text on private notes list stays fixed when it should scroll with the page"
finally:
trace_path = "/app/expensify/user_tool/output_browser1.zip"
context.tracing.stop()
context.tracing.export_trace(trace_path)
trace_cleaner(trace_path)
browser.close()
if __name__ == '__main__':
test()