issues/76/user_tool.py [14:215]:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SLOW_MO: int | None = 500
PROXY_URL = os.getenv("PROXY_URL")


async def get_magic_code(
    page: Page, user_email: str = USER_EMAIL, password: str = PASSWORD
):
    return "123456"


async def create_user(page: Page, firstname: str = "User", lastname: str = EMAIL_ALIAS):
    await page.get_by_role("button", name="Join").click()

    # Update profile
    await page.get_by_text("Track and budget expenses").click()
    await page.get_by_role("textbox", name="First name").fill(firstname)
    await page.get_by_role("textbox", name="Last name").fill(lastname)
    await page.get_by_role("button", name="Continue").click()


async def login(page: Page):
    # get current timestamp
    magic_code = await get_magic_code(page)

    if magic_code is None:
        raise ValueError("Failed to retrieve magic code")

    await page.get_by_role("textbox").fill(magic_code)


async def login_or_create_user(
    page: Page,
    firstname: str = "User",
    lastname: str = EMAIL_ALIAS,
    user_email: str = USER_EMAIL,
):
    await page.goto("https://dev.new.expensify.com:8082/")
    await page.get_by_test_id("username").fill(user_email)
    await page.get_by_role("button", name="Continue").click()

    try:
        await page.get_by_role("button", name="Join").wait_for(
            state="visible", timeout=2000
        )
        await create_user(page, firstname=firstname, lastname=lastname)
    except TimeoutError:
        await login(page)

    await page.get_by_label("Inbox").wait_for(state="visible", timeout=10000)


async def create_workspace(page: Page, workspace_name: str):
    """
    Create a workspace if it does not already exist
    """
    try:
        await (
            page.locator('button[aria-label="row"]')
            .filter(has_text=workspace_name)
            .last.click(timeout=3000)
        )
    except TimeoutError:
        await page.get_by_label("New workspace").last.click()

        await (
            page.get_by_test_id("WorkspacePageWithSections")
            .get_by_text("Workspace name", exact=True)
            .click()
        )
        name_input = page.get_by_role("textbox", name="Name")
        await name_input.clear()
        await name_input.type(workspace_name, delay=200)
        await page.get_by_role("button", name="Save").click()


def generate_random_string(length: int = 8) -> str:
    return "".join(random.choices(string.ascii_letters + string.digits, k=length))


def generate_random_number(minimum=1, maximum=1000):
    return random.randint(minimum, maximum)


async def submit_expense(page: Page, workspace_name: str):
    workspace_chat = (
        page.get_by_text("Submit expenses using your workspace chat below:")
        .locator("xpath=following-sibling::div[1]")
        .get_by_label(workspace_name)
    )
    await workspace_chat.click()

    await page.get_by_label("Create").last.click()
    await page.get_by_text("Submit expense", exact=True).click()
    await page.get_by_label("Manual").click()
    await page.get_by_placeholder("0").fill(str(generate_random_number()))
    await (
        page.locator("#numPadContainerView").get_by_role("button", name="Next").click()
    )
    await page.get_by_text("Merchant").click()
    await page.get_by_role("textbox", name="Merchant").last.fill(
        generate_random_string()
    )
    await page.get_by_role("button", name="Save").click()
    await page.get_by_role("button", name="Submit").click()


async def close_button_if_present(page: Page):
    """
    Occasionally, there is a close button that prevents any clicks on the page as
    it covers most of the screen. This button cannot be seen visually.
    """
    close_button = page.locator('button[aria-label="Close"]')
    if await close_button.is_visible():
        await close_button.click()


async def delete_workspaces(page: Page):
    await page.get_by_label("My settings").click()
    await page.get_by_role("menuitem", name="Workspaces").click()
    # Loop until no more rows are found
    while True:
        row = (
            page.get_by_test_id("WorkspacesListPage")
            .locator('button[aria-label="row"]')
            .filter(has=page.locator('button[aria-label="More"]'))
        )

        if await row.count() == 0:
            break  # Exit loop when no more rows with "More" button are found

        await row.last.get_by_label("More").click()

        await page.get_by_label("Delete workspace").click()
        await page.get_by_role("button", name="Delete").click()


async def pay_elsewhere(page: Page, workspace_name: str):
    expense_preview = page.get_by_role("button", name="View details").filter(
        has_text=f"{workspace_name} owes:"
    )
    await expense_preview.click()

    try:
        await page.get_by_role("button", name="Got it").click(timeout=2000)
    except TimeoutError:
        pass

    await (
        page.get_by_role("button", name="Pay")
        .locator("xpath=../following-sibling::*[1]")
        .click()
    )
    await page.get_by_role("menuitem", name="elsewhere").click()
    await page.get_by_role("button", name=re.compile(r".* elsewhere")).click()

    try:
        await page.get_by_role("button", name="Pay").last.click(timeout=2000)
    except TimeoutError:
        pass


async def cancel_payment(page: Page, workspace_name: str):
    payment_details = page.locator(
        f'div[data-testid="DisplayNamesWithTooltip"]:has-text("{workspace_name} paid")'
    ).last
    await payment_details.click()

    await page.get_by_role("menuitem", name="Cancel payment").click()
    await page.get_by_role("button", name="Cancel payment").click()


async def assert_scroll_to_comments(page: Page):
    messages = page.get_by_label("List of chat messages").last
    scroll_top = await messages.evaluate("el => el.scrollTop")
    assert scroll_top == 0, "Last message is not visible; didn't scroll"


async def send_messages(page: Page):
    for message in [f"Message {i}" for i in range(7)]:
        await page.get_by_placeholder("Write something...").last.fill(message)
        await page.get_by_role("button", name="Send").click()

    messages = page.get_by_label("List of chat messages").last
    await messages.evaluate(
        "el => el.scrollTop = 1000;"  # scroll to the top of the container
    )


@pytest.mark.asyncio(scope='session')
async def test_scroll_to_comments():
    async with async_playwright() as p:
        browser = await p.chromium.launch(
            proxy={"server": "http://localhost:8080"},
            headless=False,
            args=["--no-sandbox", "--disable-dev-shm-usage", "--ignore-certificate-errors", "--disable-web-security", "--disable-features=IsolateOrigins,site-per-process"],
            slow_mo=SLOW_MO,
            channel="chromium"
        )

        context = await browser.new_context()
        await context.tracing.start(screenshots=True, snapshots=True, sources=True)
        page = await context.new_page()
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -



issues/77/user_tool.py [14:214]:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SLOW_MO: int | None = 500
PROXY_URL = os.getenv("PROXY_URL")


async def get_magic_code(
    page: Page, user_email: str = USER_EMAIL, password: str = PASSWORD
):
    return "123456"


async def create_user(page: Page, firstname: str = "User", lastname: str = EMAIL_ALIAS):
    await page.get_by_role("button", name="Join").click()

    # Update profile
    await page.get_by_text("Track and budget expenses").click()
    await page.get_by_role("textbox", name="First name").fill(firstname)
    await page.get_by_role("textbox", name="Last name").fill(lastname)
    await page.get_by_role("button", name="Continue").click()


async def login(page: Page):
    # get current timestamp
    magic_code = await get_magic_code(page)

    if magic_code is None:
        raise ValueError("Failed to retrieve magic code")

    await page.get_by_role("textbox").fill(magic_code)


async def login_or_create_user(
    page: Page,
    firstname: str = "User",
    lastname: str = EMAIL_ALIAS,
    user_email: str = USER_EMAIL,
):
    await page.goto("https://dev.new.expensify.com:8082/")
    await page.get_by_test_id("username").fill(user_email)
    await page.get_by_role("button", name="Continue").click()

    try:
        await page.get_by_role("button", name="Join").wait_for(
            state="visible", timeout=2000
        )
        await create_user(page, firstname=firstname, lastname=lastname)
    except TimeoutError:
        await login(page)

    await page.get_by_label("Inbox").wait_for(state="visible", timeout=10000)


async def create_workspace(page: Page, workspace_name: str):
    """
    Create a workspace if it does not already exist
    """
    try:
        await (
            page.locator('button[aria-label="row"]')
            .filter(has_text=workspace_name)
            .last.click(timeout=3000)
        )
    except TimeoutError:
        await page.get_by_label("New workspace").last.click()

        await (
            page.get_by_test_id("WorkspacePageWithSections")
            .get_by_text("Workspace name", exact=True)
            .click()
        )
        name_input = page.get_by_role("textbox", name="Name")
        await name_input.clear()
        await name_input.type(workspace_name, delay=200)
        await page.get_by_role("button", name="Save").click()


def generate_random_string(length: int = 8) -> str:
    return "".join(random.choices(string.ascii_letters + string.digits, k=length))


def generate_random_number(minimum=1, maximum=1000):
    return random.randint(minimum, maximum)


async def submit_expense(page: Page, workspace_name: str):
    workspace_chat = (
        page.get_by_text("Submit expenses using your workspace chat below:")
        .locator("xpath=following-sibling::div[1]")
        .get_by_label(workspace_name)
    )
    await workspace_chat.click()

    await page.get_by_label("Create").last.click()
    await page.get_by_text("Submit expense", exact=True).click()
    await page.get_by_label("Manual").click()
    await page.get_by_placeholder("0").fill(str(generate_random_number()))
    await (
        page.locator("#numPadContainerView").get_by_role("button", name="Next").click()
    )
    await page.get_by_text("Merchant").click()
    await page.get_by_role("textbox", name="Merchant").last.fill(
        generate_random_string()
    )
    await page.get_by_role("button", name="Save").click()
    await page.get_by_role("button", name="Submit").click()


async def close_button_if_present(page: Page):
    """
    Occasionally, there is a close button that prevents any clicks on the page as
    it covers most of the screen. This button cannot be seen visually.
    """
    close_button = page.locator('button[aria-label="Close"]')
    if await close_button.is_visible():
        await close_button.click()


async def delete_workspaces(page: Page):
    await page.get_by_label("My settings").click()
    await page.get_by_role("menuitem", name="Workspaces").click()
    # Loop until no more rows are found
    while True:
        row = (
            page.get_by_test_id("WorkspacesListPage")
            .locator('button[aria-label="row"]')
            .filter(has=page.locator('button[aria-label="More"]'))
        )

        if await row.count() == 0:
            break  # Exit loop when no more rows with "More" button are found

        await row.last.get_by_label("More").click()

        await page.get_by_label("Delete workspace").click()
        await page.get_by_role("button", name="Delete").click()


async def pay_elsewhere(page: Page, workspace_name: str):
    expense_preview = page.get_by_role("button", name="View details").filter(
        has_text=f"{workspace_name} owes:"
    )
    await expense_preview.click()

    try:
        await page.get_by_role("button", name="Got it").click(timeout=2000)
    except TimeoutError:
        pass

    await (
        page.get_by_role("button", name="Pay")
        .locator("xpath=../following-sibling::*[1]")
        .click()
    )
    await page.get_by_role("menuitem", name="elsewhere").click()
    await page.get_by_role("button", name=re.compile(r".* elsewhere")).click()

    try:
        await page.get_by_role("button", name="Pay").last.click(timeout=2000)
    except TimeoutError:
        pass


async def cancel_payment(page: Page, workspace_name: str):
    payment_details = page.locator(
        f'div[data-testid="DisplayNamesWithTooltip"]:has-text("{workspace_name} paid")'
    ).last
    await payment_details.click()

    await page.get_by_role("menuitem", name="Cancel payment").click()
    await page.get_by_role("button", name="Cancel payment").click()


async def assert_scroll_to_comments(page: Page):
    messages = page.get_by_label("List of chat messages").last
    scroll_top = await messages.evaluate("el => el.scrollTop")
    assert scroll_top == 0, "Last message is not visible; didn't scroll"


async def send_messages(page: Page):
    for message in [f"Message {i}" for i in range(7)]:
        await page.get_by_placeholder("Write something...").last.fill(message)
        await page.get_by_role("button", name="Send").click()

    messages = page.get_by_label("List of chat messages").last
    await messages.evaluate(
        "el => el.scrollTop = 1000;"  # scroll to the top of the container
    )


@pytest.mark.asyncio(scope='session')
async def test_scroll_to_comments():
    async with async_playwright() as p:
        browser = await p.chromium.launch(
            proxy={"server": "http://localhost:8080"},
            headless=False,
            args=["--no-sandbox", "--disable-dev-shm-usage", "--ignore-certificate-errors", "--disable-web-security", "--disable-features=IsolateOrigins,site-per-process"],
            slow_mo=SLOW_MO,
            channel="chromium"
        )
        context = await browser.new_context()
        await context.tracing.start(screenshots=True, snapshots=True, sources=True)
        page = await context.new_page()
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -



