"""
@pytest_asyncio.fixture(scope="function")
async def browser():
"""
Launch a headless browser for each test function.
"""
async with async_playwright() as pw:
browser = await pw.chromium.launch(headless=True)
yield browser
await browser.close()
@pytest_asyncio.fixture
async def context(browser: Browser):
"""
Create a fresh BrowserContext for each test.
"""
ctx = await browser.new_context()
yield ctx
await ctx.close()
@pytest_asyncio.fixture
async def page(context: BrowserContext, controller):
"""
Provide a new Page with FAKE_HTML loaded and the controller.
Returns a tuple of (page, controller).
"""
p = await context.new_page()
await p.set_content(FAKE_HTML)
yield (p, controller) # Return both page and controller as a tuple
await p.close()
@pytest.fixture
def controller(tmp_path):
"""
Return an instance of the PlaywrightController.
"""
downloads_folder = str(tmp_path / "downloads")
os.makedirs(downloads_folder, exist_ok=True)
# Instantiate your real controller
# e.g. from your_module import PlaywrightController
ctrl = PlaywrightController(
downloads_folder=downloads_folder,
animate_actions=False,
viewport_width=800,
viewport_height=600,
to_resize_viewport=True,
timeout_load=2,
sleep_after_action=1,
single_tab_mode=True,
)
return ctrl
@pytest.mark.asyncio
class TestPlaywrightController:
async def test_get_interactive_rects(self, page):
page_obj, pc = page
rects = await pc.get_interactive_rects(page_obj)
# We expect the 4 elements with __elementId to be listed: 10, 11, 12, 13, 14, 15, 16
assert isinstance(rects, dict)
assert "10" in rects # "Click Me" button
assert "13" in rects # input box
# "12" is hidden, but it's assigned __elementId. Depending on your script's logic,
# a hidden element might not show up as an interactive rect if it's not visible.
# So check conditionally:
if "12" in rects:
# That means the script is capturing it even though it's display=none
# This depends on your page_script logic. Adjust as needed.
pass
async def test_get_focused_rect_id(self, page):
page_obj, pc = page
# Focus the input box (elementId="13")
await page_obj.click("#input-box")
focused_id = await pc.get_focused_rect_id(page_obj)
assert focused_id == "13"
async def test_get_page_metadata(self, page):
page_obj, pc = page
metadata = await pc.get_page_metadata(page_obj)
# Might be empty if the script didn't find JSON-LD or meta tags
# We'll check it's a dict anyway
assert isinstance(metadata, dict)
async def test_go_back_and_go_forward(self, page):
"""
This is a contrived example:
We'll do 2 navigations (fake), then check go_back/go_forward.
In practice, you'd do real URLs or data URLs.
"""
page_obj, pc = page
# Start at FAKE_HTML
# Now navigate somewhere else:
await pc.visit_page(
page_obj,
"data:text/html;base64," + base64.b64encode(FAKE_HTML.encode()).decode(),
)
# We go back
back_ok = await pc.go_back(page_obj)
# In some cases, if there's no real history, back_ok might be False
# Then forward
forward_ok = await pc.go_forward(page_obj)
# Just ensure calls don't crash
assert isinstance(back_ok, bool)
assert isinstance(forward_ok, bool)
async def test_visit_page(self, page):
page_obj, pc = page
reset_prior, reset_last = await pc.visit_page(
page_obj,
"data:text/html;base64," + base64.b64encode(FAKE_HTML.encode()).decode(),
)
assert (
reset_prior is True
) # The page loaded, so presumably we reset the metadata hash
# If no download is triggered, reset_last should be False
assert reset_last is False
async def test_refresh_page(self, page):
page_obj, pc = page
# Refresh
original_url = page_obj.url
await pc.refresh_page(page_obj)
assert page_obj.url == original_url
async def test_page_down_and_up(self, page):
page_obj, pc = page
# page_down and page_up won't raise exceptions, let's just ensure the calls work
await pc.page_down(page_obj)
await pc.page_up(page_obj)
async def test_add_remove_cursor_box(self, page):
page_obj, pc = page
# Animations are off by default in the fixture, so we just call:
await pc.add_cursor_box(page_obj, "10")
# There's no direct "result" but we can check that the script didn't crash
# Then remove
await pc.remove_cursor_box(page_obj, "10")
async def test_click_id(self, context, page):
page_obj, pc = page
# Get initial click count
initial_clicks = await page_obj.evaluate("() => window.clickCount")
assert initial_clicks == 0
# Test clicking the "Click Me" button (elementId="10")
new_page = await pc.click_id(context, page_obj, "10")
# Because single_tab_mode=False, if that button had a 'target=_blank' link, it might open a new page.
# But here it's just a button. So new_page is expected to be None
assert new_page is None
# Verify the button was actually clicked
final_clicks = await page_obj.evaluate("() => window.clickCount")
assert final_clicks == 1, "Button click was not registered"
# Test right click (should not increment clickCount, but should not error)
await pc.click_id(context, page_obj, "10", button="right")
right_clicks = await page_obj.evaluate("() => window.clickCount")
assert (
right_clicks == final_clicks
), "Right click should not increment clickCount"
# Test holding left click for 0.2 seconds (should increment clickCount by 1)
await pc.click_id(context, page_obj, "10", hold=0.2)
held_clicks = await page_obj.evaluate("() => window.clickCount")
assert (
held_clicks == right_clicks + 1
), "Held left click should increment clickCount by 1"
# If we attempt to click the disabled button (elementId="11"), your script might throw an exception
# or ignore it. We'll see:
try:
await pc.click_id(context, page_obj, "11")
except Exception:
# It's okay if it fails because it's disabled
pass
async def test_hover_id(self, page):
page_obj, pc = page
# Hover over "click-me" button
await pc.hover_id(page_obj, "10")
# If the script is animating, it might move the mouse. We just expect no error.
async def test_fill_id(self, page):
page_obj, pc = page
# Fill text into the input box
await pc.fill_id(page_obj, "13", "Hello world", press_enter=False)
# Retrieve the value to verify
value = await page_obj.evaluate(
"() => document.querySelector('#input-box').value"
)
assert value == "Hello world"
async def test_scroll_id(self, page):
page_obj, pc = page
# We'll attempt to scroll the SELECT element with __elementId="14"
# It's not scrollable in this simple HTML, but let's just ensure no crash:
await pc.scroll_id(page_obj, "14", "down")
await pc.scroll_id(page_obj, "14", "up")
async def test_select_option(self, context, page):
page_obj, pc = page
# We'll select the second option (elementId="16")
await pc.select_option(context, page_obj, "16")
# Check if the