Skip to main content

Playwright Activities

Twenty-six activities drive a real browser: open a session, navigate, interact with elements, read the page, and close it. Use them when a site needs JavaScript or a login flow, where http.request would only see the unrendered HTML.

Selenium activities do the same job with a different engine. Prefer Playwright unless you specifically need Selenium: it needs no separate driver binary and its waiting is more reliable.

Sessions

playwright.browser.create returns a session_id; every other activity takes it. A session is a live browser owned by one worker process, so the whole script — from browser.create to browser.close — must stay on that worker.

This is why every activity on this page runs locally by default (execute_locally: true). A local activity always runs in the calling workflow's own worker, which pins the session.

- activity:
type: playwright.browser.create # execute_locally is already true
input_data:
browser_type: chromium
output_name: session

- activity:
type: playwright.page.goto # runs on the same worker as above
input_data:
session_id: "{{ session['session_id'] }}"
url: https://example.com

- activity:
type: playwright.browser.close
input_data:
session_id: "{{ session['session_id'] }}"
Do not set execute_locally: false

The workflow will still validate and start, but the session is no longer pinned to one worker and any step after browser.create can fail with an unknown session.

Always close the session

Sessions are per user and expire on a timer, but the per-user cap means leaked sessions eventually make browser.create fail. Close in a finally-style branch so a failure mid-script still releases the browser. A worker restart destroys every live session; there is no reconnection.

Setup

No credentials. The worker needs the playwright package and its browser binaries installed in the image.

VariableDefaultEffect
MOCO_PLAYWRIGHT_SESSION_TIMEOUT_SEC3600Idle time before a session is reaped
MOCO_PLAYWRIGHT_MAX_SESSIONS_PER_USER5Concurrent sessions one user may hold
MOCO_HTTP_PROXY, MOCO_HTTP_PROXY_BYPASSFall back into browser launch options
MOCO_IGNORE_HTTPS_ERRORSDefault for ignore_https_errors

Defaults

60 s timeout for browser.create, page.goto, page.wait_for_selector and page.wait_for_url; 300 s for page.wait_for_timeout; 30 s for everything else. All default to 3 attempts and to local execution.

Retries replay interactions

max_attempts is 3 throughout, so a element.click that times out after the click landed will be clicked again. Set max_attempts: 1 on any interaction that is not safe to repeat — submitting a form, confirming a purchase.

Common input fields

Most activities share these. They are not repeated in every table below.

FieldTypeRequiredDefaultDescription
session_idstryesThe session from browser.create
selectorstryesElement selector
selector_typeenumno"css"css, xpath, text or role
timeoutintno30000Timeout in milliseconds

Browser lifecycle

playwright.browser.create

Launches a browser and returns a session id.

Input

FieldTypeRequiredDefaultDescription
browser_typeenumno"chromium"chromium, firefox or webkit
optionsBrowserOptionsnonullLaunch options

BrowserOptions

FieldTypeRequiredDefaultDescription
headlessboolnotrueRun without a visible window
viewport_widthintno1920Viewport width in pixels
viewport_heightintno1080Viewport height in pixels
slow_mointnonullSlow every operation by this many milliseconds, for debugging
proxydict[str, str]noMOCO_HTTP_PROXY{server, bypass}, e.g. {'server': 'http://proxy:8080', 'bypass': 'localhost;*.local'}
argslist[str]nonullExtra browser command-line arguments
ignore_https_errorsboolnoMOCO_IGNORE_HTTPS_ERRORSAccept self-signed certificates

Output

FieldTypeDescription
session_idstrPass this to every other activity
browser_typestrThe browser launched
created_atstrCreation timestamp

Example

- activity:
name: open-browser
type: playwright.browser.create
input_data:
browser_type: chromium
options:
headless: true
viewport_width: 1440
viewport_height: 900
output_name: session

playwright.browser.close

Closes the session and frees the browser.

Inputsession_id.

Output{success: bool}.

- activity:
name: close-browser
type: playwright.browser.close
input_data:
session_id: "{{ session['session_id'] }}"

playwright.browser.get_info

Reports on a live session.

Inputsession_id.

Output

FieldTypeDescription
browser_typestrThe browser in use
created_atstrWhen the session was created
last_accessedstrWhen it was last used
session_countintSessions this user currently holds

playwright.page.goto

Navigates to a URL.

Input

FieldTypeRequiredDefaultDescription
session_idstryesSession
urlstryesURL to open
timeoutintno30000Timeout in milliseconds
wait_untilenumno"load"load, domcontentloaded or networkidle

Output{url: str}, the URL actually landed on after redirects.

- activity:
name: open-login
type: playwright.page.goto
input_data:
session_id: "{{ session['session_id'] }}"
url: "https://app.example.com/login"
wait_until: networkidle
output_name: landed

playwright.page.back / playwright.page.forward / playwright.page.reload

Move through history, or reload the current page.

Inputsession_id, timeout (default 30000), wait_until (default load).

Output{url: str}.


Element interaction

All four take the common fields.

playwright.element.click

Clicks the matched element. Output{status: str}.

- activity:
name: submit-login
type: playwright.element.click
input_data:
session_id: "{{ session['session_id'] }}"
selector: "button[type=submit]"
retry_policy:
max_attempts: 1 # submitting twice would be wrong

playwright.element.fill

Sets an input's value in one operation — faster and more reliable than typing.

Input — common fields plus text (str, required). Output{status: str}.

- activity:
type: playwright.element.fill
input_data:
session_id: "{{ session['session_id'] }}"
selector: "#username"
text: "{{ username }}"

playwright.element.type

Types text key by key, firing keyboard events. Use it where a field reacts to keystrokes — autocomplete, input masks.

Input — common fields, plus text (str, required) and delay (int, default 0, milliseconds between key presses). Output{status: str}.

playwright.element.clear

Empties an input. Input — common fields. Output{status: str}.

playwright.element.select

Chooses a <select> option by value, label or index — supply exactly one.

Input — common fields, plus value (str), label (str), index (int), all optional. Output{status: str}.

- activity:
type: playwright.element.select
input_data:
session_id: "{{ session['session_id'] }}"
selector: "#region"
label: "Europe"

Reading elements

playwright.element.get_text

Input — common fields. Output{text: str}.

- activity:
name: read-total
type: playwright.element.get_text
input_data:
session_id: "{{ session['session_id'] }}"
selector: ".order-total"
output_name: total

- transform:
output_data:
- order_total: "{{ float(total['text'].strip('$')) }}"

playwright.element.get_attribute

Input — common fields, plus attribute (str, required). Output{value: str \| null}.

playwright.element.is_visible

Inputsession_id, selector, selector_type. Output{visible: bool}.

playwright.element.is_enabled

Input — common fields. Output{enabled: bool}.

playwright.element.query_selector

Checks whether one element matches. Input — common fields. Output{found: bool}.

playwright.element.query_selector_all

Counts matching elements. Inputsession_id, selector, selector_type. Output{count: int}.

- activity:
name: count-rows
type: playwright.element.query_selector_all
input_data:
session_id: "{{ session['session_id'] }}"
selector: "table.results tbody tr"
output_name: rows # -> count

Page operations

playwright.page.content

Returns the rendered HTML. Inputsession_id. Output{content: str}.

This is the usual hand-off point: render with Playwright, then parse the HTML elsewhere.

playwright.page.title

Inputsession_id. Output{title: str}.

playwright.page.url

Inputsession_id. Output{url: str}.

playwright.page.screenshot

Input

FieldTypeRequiredDefaultDescription
session_idstryesSession
formatenumno"png"png or jpeg
full_pageboolnofalseCapture the whole scrollable page
qualityintnonullJPEG quality, 0–100; ignored for PNG

Output{screenshot: str, format: str}, the image base64-encoded.

- activity:
name: capture
type: playwright.page.screenshot
input_data:
session_id: "{{ session['session_id'] }}"
format: png
full_page: true
output_name: shot # -> screenshot (base64), format
Screenshots are large

The base64 image travels through workflow context and history. Capture the smallest region you need, or upload it straight out with gdrive.upload.

playwright.page.evaluate

Runs JavaScript in the page and returns its result.

Inputsession_id, script (str, required). Output{result: any}.

- activity:
name: read-app-state
type: playwright.page.evaluate
input_data:
session_id: "{{ session['session_id'] }}"
script: "() => window.__APP_STATE__.orders.length"
output_name: order_count # -> result

Waiting

playwright.page.wait_for_selector

Waits for an element to reach a state.

Input — common fields, plus state (enum, default "visible": attached, detached, visible or hidden). Output{status: str}.

- activity:
name: wait-for-results
type: playwright.page.wait_for_selector
input_data:
session_id: "{{ session['session_id'] }}"
selector: "table.results"
state: visible
timeout: 15000

playwright.page.wait_for_url

Waits until the page URL matches a pattern — how you wait out a redirect after a login.

Inputsession_id, url (str, required; string or regex), timeout (default 30000). Output{url: str}.

playwright.page.wait_for_timeout

Waits a fixed number of milliseconds.

Inputsession_id, timeout (int, required, milliseconds). Output{status: str}.

Prefer waiting for a condition

A fixed wait is either too short on a slow day or wasted time on a fast one. Use wait_for_selector or wait_for_url where you can.