Playwright-like convenience methods injected by cdp.lua. These are plain Lua functions — you can override or extend them on any page object.
page:open(url, [wait_until], [timeout_ms]) async
Navigate to a URL. wait_until is a CDP event name (default "Page.loadEventFired"). Pass false to skip waiting. Returns true or nil, error.
-- Default: wait for full load (30s timeout)
local ok, err = page:open("https://example.com")
-- Wait for DOM content instead of full load
page:open("https://example.com", "Page.domContentEventFired")
-- Custom timeout
page:open("https://example.com", "Page.loadEventFired", 15000)
-- Fire-and-forget: navigate without waiting
page:open("https://example.com", false)
page:wait_for_selector(selector, [opts]) async
Poll the DOM until a CSS selector exists. Supports shadow DOM and iframe traversal. opts can be a number (timeout_ms) or a table.
| Option | Type | Default | Description |
timeout_ms | number | 10000 | Max wait time in milliseconds. |
poll_ms | number | 100 | Poll interval in milliseconds. |
visible | boolean | false | Only return true if the element is visible. |
scroll | boolean | true | Scroll to the element or scroll down if not found. |
-- Simple timeout
local found, info = page:wait_for_selector(".content", 5000)
-- Full options
local found, info = page:wait_for_selector(".product", {
timeout_ms = 15000,
poll_ms = 200,
visible = true,
scroll = true
})
if found then
print("Tag: " .. info.tagName)
print("Text: " .. info.text)
end
page:wait_for_url(pattern, timeout_ms) async
Polls location.href until it matches the string pattern. Returns the URL or nil, error.
-- Wait until the URL contains "/success"
local url = page:wait_for_url("/success", 10000)
if url then
print("Redirected to: " .. url)
end
page:wait_for_response([predicate], [timeout_ms]) async
Waits for Network.responseReceived. Optional predicate function receives the event params, return true when the desired response is found.
-- Wait for any response
local resp = page:wait_for_response(nil, 5000)
-- Wait for a specific API response
local resp = page:wait_for_response(function(params)
return params.response.url:find("/api/data") and params.response.status == 200
end, 10000)
page:wait_for_idle([timeout_ms], [quiet_ms]) async
Waits until the network has been quiet for quiet_ms (default 500). Uses performance.getEntriesByType("resource") to check for in-flight requests.
-- Wait up to 10s for the page to be idle
page:open("https://example.com", false)
page:wait_for_idle(10000, 500)
page:scroll([opts]) async
Scrolls the page with configurable behavior. Useful for infinite-scroll pages.
| Option | Type | Default | Description |
max_scrolls | number | 20 | Maximum number of scroll steps. |
step | number | 80% of viewport | Pixels to scroll per step. |
delay_ms | number | 250 | Delay between scroll steps. |
until_selector | string | none | Stop scrolling when this selector appears. |
until_bottom | boolean | true | Stop when the bottom of the page is reached. |
-- Scroll until an element appears or bottom is hit
page:scroll({
max_scrolls = 50,
delay_ms = 500,
until_selector = ".load-more"
})
page:content() async
Returns the full rendered HTML (document.documentElement.outerHTML).
local html = page:content()
print(string.sub(html, 1, 500)) -- First 500 chars
page:evaluate(js) async
Evaluates JavaScript in the page context and returns the result. Uses Runtime.evaluate with returnByValue = true.
local title = page:evaluate("document.title")
local count = page:evaluate("document.querySelectorAll('.item').length")
local data = page:evaluate("JSON.stringify(window.__INITIAL_STATE__)")
page:click(selector, [opts]) async
Clicks a CSS selector. By default uses el.click() in JavaScript. opts.real = true dispatches real mouse events via Input.dispatchMouseEvent.
-- Standard click (JS dispatch)
page:click(".submit-btn")
-- Real hardware-level mouse events (better anti-bot)
page:click(".buy-now", { real = true })
page:type(selector, text, [opts]) async
Types text into a selector. By default sets el.value and dispatches input/change events. opts.real = true uses Input.insertText for true keystroke simulation.
-- Standard input
page:type("#search", "hello world")
-- Real keystroke simulation
page:type("#username", "admin", { real = true })
page:screenshot(path, [opts]) async
Takes a screenshot and saves it to path. Returns the path on success.
| Option | Type | Default | Description |
format | string | "png" | Image format: "png" or "jpeg". |
quality | number | none | JPEG quality (1-100). Only applies to JPEG. |
full_page | boolean | false | Capture the full page (not just viewport). |
fullPage | boolean | false | Alias for full_page. |
fromSurface | boolean | true | Capture from the surface (set to false for DPI-aware capture). |
-- Basic screenshot (PNG)
page:screenshot("debug.png")
-- Full-page JPEG
page:screenshot("fullpage.jpg", {
format = "jpeg",
quality = 80,
full_page = true
})
page:set_extra_headers({
["Accept-Language"] = "en-US",
["X-Custom"] = "my-value"
})
page:set_user_agent(user_agent, [opts]) async
Overrides the User-Agent via Network.setUserAgentOverride. opts can include accept_language and platform.
page:set_user_agent("Mozilla/5.0 ...", {
accept_language = "en-US,en;q=0.9",
platform = "Linux"
})
page:cookies([urls]) async
Returns all cookies. If urls is provided, filters cookies by the given URLs. Uses Network.getAllCookies (without URLs) or Network.getCookies (with URLs).
-- All cookies
local all = page:cookies()
-- Cookies for specific URLs
local filtered = page:cookies({ "https://example.com" })
for _, c in ipairs(filtered) do
print(c.name, c.value)
end
page:set_cookies(cookies) async
Sets cookies via Network.setCookies. Each cookie should have name, value, and optionally domain, path, etc.
page:set_cookies({
{ name = "session", value = "abc123", domain = "example.com", path = "/" },
{ name = "theme", value = "dark", domain = "example.com" }
})
page:block_resources(types) async
Blocks network requests matching resource types or URL patterns. Accepts type names like "image", "font", "media", "stylesheet", "script", or custom URL patterns.
-- Block images and fonts
page:block_resources({ "image", "font" })
-- Block specific URL patterns
page:block_resources({ "*.analytics.js", "*.tracking.com/*" })