issues/26258_492/user_tool.py (208 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 time
from playwright.sync_api import sync_playwright
import random
import imaplib
import email as email_lib
from playwright.sync_api import sync_playwright, expect
import os
from utils.trace_cleaner import trace_cleaner
NEWDOT_URL = "https://dev.new.expensify.com:8082/"
class IMAPOTPExtractor:
def __init__(self, email_address, password):
if not self._validate_email(email_address):
raise ValueError("Invalid email address format.")
self.original_email = email_address
self.password = password
random_suffix = random.randint(1000, 9999)
random_suffix_2 = random_suffix + 1
current_timestamp = int(time.time())
random_suffix = f"565439872"
random_suffix_2 = f"6552447256"
self.new_email_1 = self._generate_new_email(email_address, random_suffix)
self.new_email_2 = self._generate_new_email(email_address, random_suffix_2)
def _validate_email(self, email_address):
return bool(re.match(r"[^@]+@[^@]+\.[^@]+", email_address))
def _generate_new_email(self, email_address, suffix):
username, domain = email_address.split('@')
return f"{username}+{suffix}@{domain}"
def get_new_email_1(self):
return self.new_email_1
def get_new_email_2(self):
return self.new_email_2
def check_for_otp(self, email):
return "123456"
def create_simple_pdf(filename="sample.pdf"):
"""
Creates a simple PDF file in the current working directory without using any external libraries.
Args:
- filename (str): The name of the PDF file to be created.
Returns:
- str: The absolute path of the created PDF file.
"""
pdf_content = """%PDF-1.4
1 0 obj
<< /Type /Catalog /Pages 2 0 R >>
endobj
2 0 obj
<< /Type /Pages /Kids [3 0 R] /Count 1 >>
endobj
3 0 obj
<< /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792] /Contents 4 0 R /Resources << >> >>
endobj
4 0 obj
<< /Length 44 >>
stream
BT
/F1 24 Tf
100 700 Td
(Hello, this is a simple PDF file) Tj
ET
endstream
endobj
5 0 obj
<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica >>
endobj
xref
0 6
0000000000 65535 f
0000000010 00000 n
0000000079 00000 n
0000000178 00000 n
0000000328 00000 n
0000000407 00000 n
trailer
<< /Size 6 /Root 1 0 R >>
startxref
488
%%EOF
"""
with open(filename, "wb") as file:
file.write(pdf_content.encode('latin1')) # PDF requires Latin-1 encoding
return filename
def delete_pdf(filename="sample.pdf"):
"""
Deletes a PDF file in the current working directory.
Args:
- filename (str): The name of the PDF file to be deleted.
Returns:
- bool: True if the file was successfully deleted, False if the file does not exist.
"""
file_path = os.path.join(os.getcwd(), filename)
if os.path.exists(file_path):
os.remove(file_path)
return True
else:
print(f"File not found, could not delete: {file_path}")
return False
def click_until_visible(page, button_selector: str, desired_selector: str, max_retries: int = 10, timeout: int = 30):
"""
Clicks a button until the desired element becomes visible or max retries are reached.
:param page: Playwright Page instance.
:param button_selector: Selector for the button to click.
:param desired_selector: Selector for the element to wait for.
:param max_retries: Maximum number of clicks to attempt.
:param timeout: Maximum time to wait for the desired element to become visible.
:return: Locator for the desired element.
"""
timeout = timeout * 1000 # Convert seconds to milliseconds
retries = 0
while retries < max_retries:
page.locator(button_selector).click(timeout=timeout)
try:
locator = page.locator(desired_selector).wait_for(state="visible", timeout=timeout)
return locator
except Exception:
retries += 1
raise Exception(f"Desired element '{desired_selector}' not found after {max_retries} clicks.")
def launch_app(pw, headless=True, device=None, geolocation=None):
browser = pw.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 = {"viewport": {"width": 1024, "height": 640}}
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_with_otp(page, email, email_extractor):
page.get_by_role("textbox", name="Phone or email").fill(email)
page.wait_for_timeout(1000)
click_until_visible(page, 'button:has-text("Continue")', 'input[data-testid="validateCode"]', timeout=5)
otp = None
print("Waiting for OTP email...")
page.wait_for_timeout(5000) # Wait for email to arrive
for i in range(30):
print("Trying to fetch OTP from email, attempt:", i+1)
otp = email_extractor.check_for_otp(email)
if otp:
print("OTP found in email:", otp)
break
print("No OTP found yet. Waiting for 3 seconds...")
page.wait_for_timeout(3000)
if not otp:
print("No OTP found in unread emails.")
return
otp_input = page.locator('input[data-testid="validateCode"]')
expect(otp_input).to_be_visible(timeout=10000)
otp_input.fill(otp)
def login_and_initial_setup(page, email, is_logout = False, is_first = False):
if is_first:
page.goto(NEWDOT_URL)
page.get_by_role("textbox", name="Phone or email").fill(email)
page.wait_for_timeout(1000)
continue_button = page.locator('button[tabindex="0"]')
continue_button.wait_for(state="visible", timeout=10000)
continue_button.click()
page.wait_for_timeout(2000)
try:
page.locator('button[tabindex="0"]').click()
except Exception:
page.wait_for_timeout(1000)
page.locator('button[tabindex="0"]').click()
page.wait_for_timeout(2000)
try:
expect(page.get_by_text("want to do today")).to_be_visible(timeout=15000)
except AssertionError:
page.wait_for_timeout(1000)
page.locator('button[tabindex="0"]').click()
page.wait_for_timeout(2000)
page.locator("text='Track and budget expenses'").wait_for(state="visible", timeout=10000)
page.locator("text='Track and budget expenses'").click()
page.get_by_role("button", name="Continue").click()
page.locator('input[name="fname"]').fill("Test")
page.locator('input[name="lname"]').fill("User")
page.get_by_role("button", name="Continue").last.click()
page.wait_for_timeout(1000)
if is_logout:
page.get_by_label("My settings").click()
page.wait_for_timeout(1000)
page.get_by_label("Sign out").click()
page.wait_for_timeout(1000)
def start_chat_and_send_file(page, emal_to_message):
try:
page.get_by_label("Start chat (Floating action)").click()
page.get_by_label("Start chat", exact=True).click()
page.wait_for_timeout(1000)
page.get_by_test_id("selection-list-text-input").fill(emal_to_message)
page.wait_for_timeout(1000)
try:
page.get_by_label("Test").click()
except Exception:
page.wait_for_timeout(1000)
page.get_by_label(emal_to_message).click()
page.wait_for_timeout(1000)
page.get_by_label("Create").last.click()
page.wait_for_timeout(1000)
add_attachment = page.get_by_label("Add attachment")
with page.expect_file_chooser() as file_chooser_info:
add_attachment.click()
page.wait_for_timeout(2000)
file_chooser = file_chooser_info.value
current_directory = os.getcwd()
file_path = os.path.join(current_directory, "sample.pdf")
file_chooser.set_files(file_path)
page.wait_for_timeout(2000)
page.get_by_role("dialog").get_by_role("button", name="Send").click()
page.wait_for_timeout(2000)
page.get_by_label("My settings").click()
page.wait_for_timeout(1000)
page.get_by_label("Sign out").click()
page.wait_for_timeout(1000)
except Exception as e:
print(f"Error during workspace and chat setup: {e}")
def check_dowload_button(page):
page.get_by_label("Navigates to a chat").last.click()
page.wait_for_timeout(2000)
page.context.set_offline(True)
page.get_by_label("View attachment").first.click()
download_button = 0
try:
download_button = page.get_by_label("Download").count()
except:
pass
assert download_button == 0, "Download button is not hidden when offline, issue exists!"
def test_download_button_visibility():
with sync_playwright() as pw:
browser, context, page = None, None, None
try:
email_address = "rgarciatammy4@gmail.com"
password = ""
email_extractor = IMAPOTPExtractor(email_address, password)
email_1 = email_extractor.get_new_email_1()
email_2 = email_extractor.get_new_email_2()
browser, context, page = launch_app(pw)
context.tracing.start(screenshots=True, snapshots=True)
create_simple_pdf("sample.pdf")
login_and_initial_setup(page, email_2, is_logout=True, is_first=True)
context.clear_cookies()
login_and_initial_setup(page, email_1)
start_chat_and_send_file(page, email_2)
login_user_with_otp(page, email_2, email_extractor)
check_dowload_button(page)
delete_pdf("sample.pdf")
except Exception as e:
print(f"Test encountered an error: {e}")
raise
finally:
if context:
context.tracing.stop(path="/app/expensify/user_tool/output_browser1.zip")
trace_cleaner("/app/expensify/user_tool/output_browser1.zip")
context.close()
if browser:
browser.close()