Browser Synthetic Tests
With Watchlog Synthetic Tests, you can execute your most important web journeys—login, search, checkout, dashboards—every few minutes. Watchlog captures step-by-step screenshots, tracks duration (Avg/P95), stores run history, and sends alerts when something breaks.
This guide explains all available actions (steps) and the Import/Export JSON format. It does not require any API knowledge.
Quick start
- Create a Browser Synthetic Test.
- Add steps with the UI:
goto
→waitForNetworkIdle
→fill
→click
→waitFor
. - Pick a schedule (e.g., every 5 minutes) and configure alert channels (Email, Slack, Telegram, Custom Webhook).
- Save. Watchlog will execute on schedule and keep screenshots & history.
Minimal example for a login check:
{
"name": "Watchlog login test",
"options": {
"viewport": { "width": 1366, "height": 768 },
"timeoutMs": 5000
},
"steps": [
{ "action": "goto", "url": "https://app.watchlog.io/account/login", "waitUntil": "domcontentloaded" },
{ "action": "waitForNetworkIdle" },
{ "action": "fill", "selector": "#email", "value": "[email protected]" },
{ "action": "fill", "selector": "#password", "value": "••••••••" },
{ "action": "click", "selector": "text=login" },
{ "action": "waitFor", "selector": "text=Dashboards" }
]
}
Concepts
- Steps: A test is an ordered list of actions executed in the browser.
- Per‑step timeout: Each step respects
options.timeoutMs
(default commonly30000
). If a step exceeds the limit, the run fails and a full‑page error screenshot is captured. - Viewport: Controls the browser size. Presets are available in the UI.
- Screenshots: After every successful step, a screenshot is taken. On the first failure, a full‑page screenshot is taken.
- History & trends: Watch success rate for the last 30 days and recent runs with duration.
- Alerts: Notify Email, Slack, Telegram, or your own webhook on status changes (e.g., OK → FAIL, FAIL → OK). You can optionally override the default messages.
Actions reference
Actions are the browser operations executed during a run. Each action has its own fields. Below you’ll find the purpose, fields, and examples for each action.
Selectors
Use stable CSS selectors (e.g.,[data-test=login]
) or Playwright text selectors such astext=Login
. Avoid brittle selectors based on layout classes.
1) goto
— open a URL
Purpose: Navigate to a URL and wait until a specific page readiness event.
Fields
url
(required, string): Absolute page URL.waitUntil
(optional, enum): When to consider the navigation complete. Default:domcontentloaded
Allowed values:load
→ after thewindow.load
event (all resources loaded).domcontentloaded
→ when the DOM is ready (fast, stable default).networkidle
→ when the network has been idle for a short period (great for SPAs).
Example
{ "action": "goto", "url": "https://example.com", "waitUntil": "domcontentloaded" }
2) waitForNetworkIdle
— wait for network to settle
Purpose: After a goto
or click
, pages (especially SPAs) may issue many requests. This waits until the network is idle to reduce flakiness.
Fields: none
Example
{ "action": "waitForNetworkIdle" }
3) waitForURL
— wait until the URL matches
Purpose: Pause until the current URL matches a string or regular expression (useful for redirects or route changes).
Fields
urlOrRegex
(required, string): e.g.,"https://example.com/home"
or"/\\/Home$/"
Example
{ "action": "waitForURL", "urlOrRegex": "/\\/Home$/" }
Regex format: Provide as a string that looks like JS regex literal:
"/pattern/flags"
. For example,"/^https:\\/\\/app\\.example\\.com\\/home$/"
.
4) waitFor
— wait for an element
Purpose: Wait until the target element exists (and is visible enough for interaction).
Fields
selector
(required, string)
Example
{ "action": "waitFor", "selector": "text=Dashboards" }
5) fill
— enter text in a field
Purpose: Fill input fields such as email and password.
Fields
selector
(required, string)value
(required, string).
Examples
{ "action": "fill", "selector": "#email", "value": "[email protected]" }
{ "action": "fill", "selector": "#password", "value": "********" }
Heads‑up: If you fill a visible text input, the screenshot will include what’s on screen. Prefer password fields or pages that mask sensitive text.
6) click
— click an element
Purpose: Click a button/link/element.
Fields
selector
(required, string)
Example
{ "action": "click", "selector": "text=login" }
7) assertVisible
— element must be visible
Purpose: Ensure an element is actually visible (not only present in the DOM).
Fields
selector
(required, string)
Example
{ "action": "assertVisible", "selector": "[data-test=welcome-card]" }
8) assertHidden
— element must be hidden (JSON‑only for now)
Purpose: Ensure an element is hidden (not visible to the user).
Fields
selector
(required, string)
Example
{ "action": "assertHidden", "selector": ".spinner" }
This action is supported by the runner. If you don’t see it in the UI yet, you can still Import JSON with this step.
9) assertText
— element must contain text
Purpose: Verify the selected element contains contains
as a substring.
Fields
selector
(required, string)contains
(required, string)
Example
{
"action": "assertText",
"selector": ".toast.success",
"contains": "Welcome back"
}
Matching is substring-based.
10) assertCount
— element count must match
Purpose: Verify that exactly count
elements exist for the given selector.
Fields
selector
(required, string)count
(required, number)
Example
{ "action": "assertCount", "selector": ".product-card", "count": 12 }
Authentication / Session (optional)
Sometimes you need to run steps as an authenticated user.
- Cookies: Paste your browser cookie string (e.g.,
session=abc; other=1
). - Extra HTTP headers: Add items like
Authorization: Bearer <token>
. - Domain hint: If cookies don’t carry a domain, we infer it from your Default URL (or the first
goto
).
Spec shape (set automatically when you configure in the UI):
{
"session": {
"cookieString": "session=abc123; other=1",
"headers": { "Authorization": "Bearer <token>" },
"domainHint": "app.example.com"
}
}
Global test options
Viewport
Default desktop is1366×768
. You can override via UI or JSON:{ "options": { "viewport": { "width": 1366, "height": 768 } } }
Timeout
Max time (ms) per step before failing:{ "options": { "timeoutMs": 5000 } }
Import / Export JSON
Use Import JSON on the test editor to paste a full test definition—or export the current one with Copy JSON.
JSON structure
name
(string) — test namedefaultUrl
(optional, string) — used as cookie domain hintoptions
(object) — global settingsviewport.width
(number)viewport.height
(number)timeoutMs
(number) — per‑step timeout
session
(optional, object) — cookies & headers (see above)steps
(array) — ordered list of actions. Each step has:action
(enum) — one of:goto
,waitForNetworkIdle
,waitForURL
,waitFor
,fill
,click
,assertVisible
,assertHidden
,assertText
,assertCount
The fields required by that action (see reference above).
Example (login to dashboard)
{
"name": "Watchlog login test",
"defaultUrl": "https://app.watchlog.io",
"options": {
"viewport": { "width": 1366, "height": 768 },
"timeoutMs": 5000
},
"steps": [
{ "action": "goto", "url": "https://app.watchlog.io/account/login", "waitUntil": "domcontentloaded" },
{ "action": "waitForNetworkIdle" },
{ "action": "fill", "selector": "#email", "value": "[email protected]" },
{ "action": "fill", "selector": "#password", "value": "**********" },
{ "action": "click", "selector": "text=login" },
{ "action": "waitFor", "selector": "text=Dashboards" }
]
}
Example (redirect + assertion + count)
{
"name": "Catalog smoke test",
"options": { "viewport": { "width": 1440, "height": 900 }, "timeoutMs": 8000 },
"steps": [
{ "action": "goto", "url": "https://shop.example.com", "waitUntil": "domcontentloaded" },
{ "action": "waitForNetworkIdle" },
{ "action": "click", "selector": "text=Browse All" },
{ "action": "waitForURL", "urlOrRegex": "/\\/catalog$/" },
{ "action": "assertText", "selector": "h1", "contains": "Catalog" },
{ "action": "assertCount", "selector": ".product-card", "count": 12 }
]
}
Validations & common errors
- Unknown action → step will fail with
Unknown action: ...
. - Missing required field → step validation error (e.g.,
selector is required
). - Invalid type → e.g.,
count
must be a number. - Timeouts → increase
options.timeoutMs
or addwaitForNetworkIdle
after navigations or heavy actions. - Selectors not found → inspect your page and prefer robust selectors like
[data-test=...]
.
Best practices
- Prefer stable selectors:
[data-test=...]
,text=...
. Avoid layout classes. - Reduce flakiness: Add
waitForNetworkIdle
after navigations or clicks that trigger heavy network activity. - Smaller tests: Keep flows focused (login check, search check, checkout happy path).
- Screenshots: Use the per‑step gallery and the error full‑page capture to spot what went wrong quickly.
Need an action that isn’t here yet? Tell us and we’ll add it.