UI Testing with YAML
Proofarc UI tests are defined in simple YAML — no page objects, no framework boilerplate. The same YAML runs on both Selenium and Playwright for cross-engine browser compatibility testing.
Example Test
steps:
- action: NAVIGATE_TO
value: "{{baseUrl}}"
- action: CLEAR
selector: "#username"
- action: SEND_KEYS
selector: "#username"
value: "{{username}}"
- action: CLEAR
selector: "#password"
- action: SEND_KEYS
selector: "#password"
value: "{{password}}"
- action: CLICK
selector: "button[type=submit]"
- action: VALIDATE_TEXT
selector: "body"
expectedText: "Dashboard"
- action: TAKE_SCREENSHOT
Variables
Both {{variable}} and ${variable} syntax are supported. Variables are resolved from:
- Credentials — injected from environment default credential (
{{username}},{{password}}) - Target URL —
{{baseUrl}}is the target's configured URL - Environment variables —
{{env.MY_VAR}}
Variable lookup is case-insensitive: {{userName}}, {{username}}, {{USERNAME}} all resolve the same.
Step Fields
| Field | Used by | Description |
|---|---|---|
action | All | Action to perform (see table below) |
selector | Most | CSS selector for the target element |
value | Input actions | Data to type, select, or navigate to |
expectedText | Validation actions | Text to assert (uses contains match) |
timeout | Wait actions | Seconds to wait (default: 30) |
attribute | VALIDATE_ATTRIBUTE | Attribute name to check |
filename | TAKE_SCREENSHOT | Custom screenshot filename |
Supported Actions
Navigation
| Action | Fields | Description |
|---|---|---|
NAVIGATE_TO | value (URL) | Navigate to URL |
NAVIGATE_BACK | — | Browser back button |
NAVIGATE_FORWARD | — | Browser forward button |
REFRESH | — | Reload the current page |
SWITCH_FRAME | selector or value | Switch context into an iframe (pass empty/default to switch back) |
SWITCH_TAB | value (index) | Switch to another browser tab |
Interaction
| Action | Fields | Description |
|---|---|---|
CLICK | selector | Click element |
DOUBLE_CLICK | selector | Double-click element |
RIGHT_CLICK | selector | Right-click (native context menu) |
SEND_KEYS | selector, value | Type text into input field |
CLEAR | selector | Clear input field |
SELECT_BY_TEXT | selector, value | Select dropdown option by visible text |
SELECT_BY_VALUE | selector, value | Select dropdown option by value attribute |
SELECT_BY_INDEX | selector, value | Select dropdown option by zero-based index |
HOVER | selector | Hover over element |
SCROLL_TO_ELEMENT | selector | Scroll element into view |
SUBMIT | selector (form) | Submit a form |
FILL_FORM | formData list of {selector, value} | Fill multiple fields in one step |
Validation
| Action | Fields | Match Type | Description |
|---|---|---|---|
VALIDATE_TEXT | selector, expectedText | Contains | Verify element contains text |
VALIDATE_TITLE | expectedText | Contains | Verify page title contains text |
VALIDATE_URL | expectedText | Contains | Verify current URL contains text |
VALIDATE_VISIBLE | selector | — | Verify element is visible |
VALIDATE_ELEMENT_VISIBLE | selector | — | Alias of VALIDATE_VISIBLE |
VALIDATE_ELEMENT_EXISTS | selector | — | Verify element exists in DOM |
VALIDATE_ATTRIBUTE | selector, attribute, expectedText | Exact | Verify attribute equals value |
Wait
| Action | Fields | Description |
|---|---|---|
WAIT_FOR_ELEMENT | selector, value (seconds) | Wait for element to appear |
WAIT_FOR_VISIBLE | selector, value (seconds) | Wait for element to be visible |
WAIT_FOR_CLICKABLE | selector, value (seconds) | Wait for element to be enabled/clickable |
WAIT_FOR_TEXT | selector, expectedText, value (seconds) | Wait for text to appear |
Read element state
The captured value lands in the step's output and is available for later assertions.
| Action | Fields | Description |
|---|---|---|
GET_TEXT | selector | Read element innerText |
GET_ATTRIBUTE | selector, attribute | Read a specific attribute |
GET_VALUE | selector | Read the value of a form control |
Utility
| Action | Fields | Description |
|---|---|---|
TAKE_SCREENSHOT | filename (optional) | Capture page screenshot |
MAXIMIZE_WINDOW | — | Maximize browser window |
EXECUTE_JS | value (or script) | Execute JavaScript — single line, or multi-line with value: | (see "Embedding multi-line JavaScript" below) |
No
SLEEPaction. Blind sleeps are anti-pattern. UseWAIT_FOR_ELEMENT/WAIT_FOR_VISIBLE/WAIT_FOR_CLICKABLE/WAIT_FOR_TEXTwith avalue(timeout in seconds) instead — they wait for a real condition rather than a fixed duration.
Cross-Engine Testing
The same YAML runs on two browser engines simultaneously:
- Selenium WebDriver (via ui-test-agent)
- Playwright (via playwright-agent) — Chromium + WebKit + Firefox
If a test passes on Selenium but fails on Playwright (WebKit), that's a browser compatibility bug — not a test bug. Proofarc detects and classifies these automatically.
To run a single suite on both engines in one call, pass drivers in the run request — see the UI Test Suites page.
Embedding multi-line JavaScript
Use YAML's literal block scalar (| after the field name) for multi-line code. Newlines and indentation are preserved verbatim, no escaping needed:
- action: EXECUTE_JS
value: |
const token = localStorage.getItem('token');
return fetch('/api/findings/' + 60 + '/status', {
method: 'PUT',
headers: {'Content-Type': 'application/json', 'Authorization': 'Bearer ' + token},
body: JSON.stringify({status: 'TRIAGED'})
}).then(function(r) { return r.json(); });
Engine differences for EXECUTE_JS
The two engines treat the EXECUTE_JS script slightly differently:
| Engine | Wraps script as | Top-level return | Async / Promises |
|---|---|---|---|
| Selenium WebDriver | function() { ... } | ✅ legal | ❌ does not await; the Promise is returned as-is and the test moves on |
| Playwright | (() => { ... })() (IIFE) | ✅ legal | ✅ page.evaluate awaits the returned Promise |
For tests that need to await asynchronous work (e.g. multiple fetch calls), use .then() chains that return a Promise; pin driver: PLAYWRIGHT if the test must wait on async results. To assert on async outcomes from a later step in either engine, write a marker to document.title from inside the .then() and use VALIDATE_TITLE afterwards.
Strict-mode selector caveat
Playwright is strict by default — locator(".MuiDrawer-root, nav") throws if the selector matches multiple elements. Selenium silently picks the first. The agents normalise this where possible (Playwright VALIDATE_VISIBLE uses .first), but if a test assertion fails on Playwright with strict mode violation, narrow the selector to a single element.
Visual Element Picker
Don't know CSS selectors? Use the visual picker:
- Navigate to a page
- Take a screenshot
- Click on elements in the screenshot
- Proofarc extracts the selector automatically