Working with async playwright

While using pytest-asyncio, pytest-playwright may not works fine. The package pytest-playwright-asyncio will include the async version that can also be used instead.

In that case, feature has to be tagged @asyncio in order to generate coroutine instead of function.

Create a scenario

@asyncio
Feature: Basic Test

  Scenario: Hello world
    Given anonymous user on /
    Then the user sees the text "Hello, World!"

Step definitions

from playwright.async_api import Page, expect

from tursu import given, then, when


@given("anonymous user on {path}")
@when("I visit {path}")
async def i_visit(page: Page, http_server: str, path: str):
    await page.goto(f"{http_server}{path}")


@then('the user sees the text "{text}"')
async def assert_text(page: Page, text: str):
    loc = page.get_by_text(text)
    await expect(loc).to_be_visible()

Example of application as a pytest fixture

import asyncio
import socket

import pytest
import uvicorn
from playwright.async_api import async_playwright
from starlette.applications import Starlette
from starlette.requests import Request
from starlette.responses import HTMLResponse
from starlette.routing import Route

from tursu import tursu_collect_file

tursu_collect_file()


async def homepage(request: Request):
    return HTMLResponse("<body>Hello, World!</body>")


app = Starlette(routes=[Route("/", homepage)])


async def wait_for_socket(
    host: str, port: int, timeout: int = 5, poll_time: float = 0.1
):
    """Wait until the socket is open, or raise an error if the timeout is exceeded."""
    for _ in range(timeout * int(1 / poll_time)):
        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
            if sock.connect_ex((host, port)) == 0:
                break
        await asyncio.sleep(poll_time)
    else:
        raise RuntimeError(f"Server on {host}:{port} did not start in time.")


@pytest.fixture(autouse=True)
async def http_server():
    """Start the service in an asyncio task."""
    host, port = "127.0.0.1", 8888
    config = uvicorn.Config(
        app, host=host, port=port, log_level="error", loop="asyncio"
    )
    server = uvicorn.Server(config)

    task = asyncio.create_task(server.serve())
    await wait_for_socket(host, port)

    yield f"http://{host}:{port}"

    server.should_exit = True
    await task


@pytest.fixture()
async def page():
    async with async_playwright() as p:
        browser = await p.chromium.launch(headless=True)
        context = await browser.new_context()
        page = await context.new_page()
        yield page
        await browser.close()

Note

In this test, a page fixture has been created instead of using pytest-playwright-asyncio,

this should not be necessary, if the package pytest-playwright-asyncio is installed.

A more complete example is available at:

https://github.com/mardiros/fastlife/tree/main/tests/functionals