These snippets automate testing OpenSEO UI interactions—from console capture and element discovery to form filling, tab navigation, filter persistence, and performance measurement—using Playwright's page object and locator API.
Capture browser console logs during Playwright automation by attaching a handler via page.on('console', ...)
console_logs = []
def handle_console_message(msg):
console_logs.append(f"[{msg.type}] {msg.text}")
print(f"Console: [{msg.type}] {msg.text}")
page.on("console", handle_console_message)
page.goto(url)
page.wait_for_load_state('networkidle')
page.click('text=Dashboard')
page.wait_for_timeout(1000)
Discover buttons, links, and input fields on a page using Playwright locators after navigating to a URL
page.goto('http://localhost:5173')
page.wait_for_load_state('networkidle')
buttons = page.locator('button').all()
for i, button in enumerate(buttons):
text = button.inner_text() if button.is_visible() else "[hidden]"
links = page.locator('a[href]').all()
for link in links[:5]:
href = link.get_attribute('href')
inputs = page.locator('input, textarea, select').all()
for input_elem in inputs:
name = input_elem.get_attribute('name') or input_elem.get_attribute('id') or "[unnamed]"
input_type = input_elem.get_attribute('type') or 'text'
Automate interaction with a local static HTML file using a file:// URL in Playwright, including form fill and screenshot
html_file_path = os.path.abspath('path/to/your/file.html')
file_url = f'file://{html_file_path}'
page.goto(file_url)
page.screenshot(path='/mnt/user-data/outputs/static_page.png', full_page=True)
page.click('text=Click Me')
page.fill('#name', 'John Doe')
page.fill('#email', 'john@example.com')
page.click('button[type="submit"]')
page.wait_for_timeout(500)
page.screenshot(path='/mnt/user-data/outputs/after_submit.png', full_page=True)
Closing an inactive search tab leaves the active tab selected and removes the closed tab from the URL
await openDomainOverview(page, "keywords");
const secondUrl = new URL(page.url());
secondUrl.searchParams.set("domain", SECONDARY_TEST_DOMAIN);
await page.goto(secondUrl.toString());
const inactiveCloseButton = page.getByRole("button", {
name: `Close ${PRIMARY_TEST_DOMAIN} tab`,
});
await inactiveCloseButton.click();
await expect.poll(() => new URL(page.url()).searchParams.get("domain")).toBe(SECONDARY_TEST_DOMAIN);
Clearing page filters via 'Clear all' does not remove keyword filters; keyword filter value persists in URL and input
await applyFilters(page, "minTraffic", "10"); // keyword filter
await switchDomainTab(page, "pages");
await applyFilters(page, "pMinTraffic", "20"); // page filter
await ensureFiltersOpen(page, "Include Page Terms");
await page.getByRole("button", { name: "Clear all" }).click();
await expect.poll(() => new URL(page.url()).searchParams.get("pMinTraffic")).toBe(null);
await expect.poll(() => new URL(page.url()).searchParams.get("minTraffic")).toBe("10");
Saved filter defaults apply when navigating without explicit tab filter params, but explicit URL params take precedence
await applyFilters(page, "pMinTraffic", "20");
const urlWithoutPageFilters = new URL(page.url());
urlWithoutPageFilters.searchParams.delete("pMinTraffic");
await page.goto(urlWithoutPageFilters.toString());
await ensureFiltersOpen(page, "Include Page Terms");
await expect(page.getByPlaceholder("Min").nth(0)).toHaveValue("20"); // saved default applied
const urlWithExplicitPageFilters = new URL(page.url());
urlWithExplicitPageFilters.searchParams.set("pMinTraffic", "30");
await page.goto(urlWithExplicitPageFilters.toString());
await ensureFiltersOpen(page, "Include Page Terms");
await expect(page.getByPlaceholder("Min").nth(0)).toHaveValue("30"); // explicit param wins
Main-thread performance is measured after applying and editing filters using CDP CPU throttling and a custom perf probe
await installDomainPerfProbe(page);
const client = await page.context().newCDPSession(page);
await client.send("Emulation.setCPUThrottlingRate", { rate: CPU_THROTTLE_RATE });
await openDomainOverview(page, "pages");
await openFilters(page);
await typeIntoDraftInput(page, page.getByPlaceholder("Min").nth(0), "10", "Pages Traffic min", {
actionTimeoutMs: PERF_BUDGETS.actionMs,
cdpSession: client,
inputLatencyBudgetMs: PERF_BUDGETS.maxInputMs,
recordPerf: true,
});
await applyFilters(page, "pMinTraffic", "10");
const finalMetrics = await getDomainPerfMetrics(page);
Playwright exposes the Chrome DevTools Protocol (CDP) via page.context().newCDPSession(), enabling low-level browser control—such as CPU throttling via Emulation.setCPUThrottlingRate—for capabilities not available through Playwright's standard API.
Clicking 'Back to Recent searches' clears the active keyword tab query param from the URL
await page.goto(`/p/${projectId}/keywords?q=keyword%20research&loc=2840&kLimit=150&mode=auto`);
const recentSearchesButton = page.locator(
'[data-testid="keyword-research-recent-searches"]:visible',
);
await recentSearchesButton.click();
await expect.poll(() => new URL(page.url()).searchParams.get("q")).toBe(null);
Closing the active middle keyword tab removes it and selects the next tab, leaving the search tabs tablist with one fewer tab
await page.getByRole("tab", { name: /^backlinks/i }).click();
const closeButton = page.getByRole("button", { name: "Close backlinks tab" });
await closeButton.click();
await expect.poll(() => new URL(page.url()).searchParams.get("q")).toBe("open seo");
await expect(page.getByRole("tab", { name: /^open seo/i })).toHaveAttribute("aria-selected", "true");
await expect(
page.getByRole("tablist", { name: "Search tabs" }).getByRole("tab"),
).toHaveCount(2);
Sources