Step types
Flowguard has different step types that simulate user actions on your website. Each type does one specific thing.
Overview
Flowguard supports 12 step types organized by category:
Quick Reference
Most flows begin with: Navigate -> (Interactions) -> Assert. It's a good practice to end with: an Assert step to verify the action worked.
Common flow patterns
Step types list
- Navigate (Open Page) - Visit a URL
- Click - Click an element
- Fill (Type Text) - Enter text into fields
- Assert (Check) - Verify content or states
- Wait - Pause execution
- Scroll To - Scroll to an element
- Hover - Trigger hover effects
- Select Option - Choose from dropdowns
- Checkbox - Toggle checkboxes
- Radio - Select radio buttons
- Visual Check - Compare page appearance against a reference screenshot
- Run Code - Execute custom JavaScript code (1.1.0)
1. Navigate (Open Page)
Opens a specific URL in the browser.
Use cases
- Starting a test flow
- Moving to different pages
- Testing navigation
- Loading specific content
Configuration
URL (required)
- The full URL to visit
- Must include protocol (http:// or https://)
- Can be any page on your site
Description (optional)
- Note about where you're navigating
Examples
URL: https://yoursite.com/contact
Description: Open contact form pageURL: https://yoursite.com/wp-admin
Description: Navigate to admin dashboardTips
- Use Navigate to open specific pages you want to test
- If you're testing elements already visible on the current page, you can start with other step types
- Use full URLs, not relative paths
- Verify the URL is correct before saving
Common issues
Page not loading
- Check the URL for typos
- Make sure the page exists
- Verify the site is accessible
- Check for redirect loops
2. Click
Clicks on an element (button, link, etc.).
Use cases
- Submitting forms
- Following links
- Opening modals/popups
- Toggling UI elements
- Navigating menus
Configuration
Selector (required)
- CSS selector for the target element
- Use the picker tool or enter manually
Description (optional)
- What you're clicking and why
Wait After (optional)
- Milliseconds to wait after clicking
Examples
Selector: #submit-button
Description: Click the submit button
Wait After: 1000Selector: nav .menu-item:first-child a
Description: Click first menu itemSelector: [data-test="login-btn"]
Description: Click login button
Wait After: 2000Tips
- Use the element picker for accuracy
- Add wait time if clicking triggers animations
- Verify the element is clickable (not hidden)
- Use specific selectors to avoid clicking the wrong element
Common issues
Element not found
- Selector is incorrect
- Element doesn't exist on the page
- Element loads after page load (add a Wait step before)
Click doesn't work
- Element is covered by another element
- Element is not clickable (use different selector)
- JavaScript event not attached yet (add wait time)
3. Fill (Type Text)
Enters text into input fields, textareas, or contenteditable elements.
Use cases
- Filling form fields
- Entering search queries
- Typing in text editors
- Entering usernames/passwords
Configuration
Selector (required)
- CSS selector for the input field
Text (required)
- The text to enter
Description (optional)
- What field you're filling
Clear First (optional)
- Clear existing text before typing
Examples
Selector: #email
Text: user@example.com
Description: Enter email address
Clear First: YesSelector: input[name="search"]
Text: Flowguard Plugin
Description: Enter search querySelector: #password
Text: SecurePass123!
Description: Enter passwordTips
- Use "Clear First" to avoid appending to existing text
- Use realistic test data
- Passwords won't be visible in the UI
- Add waits after filling if form validates on input
Common issues
Text not appearing
- Selector targets wrong element
- Field is disabled or readonly
- JavaScript overwrites the value
Partial text entered
- Field has max length restriction
- JavaScript validation interfering
4. Assert (Check)
Verifies that content or states match expectations.
Use cases
- Verify login succeeded
- Check error messages appear
- Confirm elements are visible
- Validate content exists
- Test redirects happened
Configuration
Selector (required for element-based checks)
- Element to check (not required for page-level assertions like URL or Title checks)
Expected Value (required)
- The type of check to perform (see all options below)
Value (for some assertion types)
- The expected text or value to check against
Description (optional)
- What you're verifying
All assertion types
Flowguard has 16 assertion types organized by category:
Visibility checks
| Type | Description | Requires Selector | Requires Value |
|---|---|---|---|
| Is Visible | Check if the element is visible on the page | Yes | No |
| Is Hidden | Check if the element is hidden or not displayed | Yes | No |
| Exists | Check if the element exists in the DOM | Yes | No |
| Does Not Exist | Check if the element does not exist | Yes | No |
Text content checks
| Type | Description | Requires Selector | Requires Value |
|---|---|---|---|
| Contains Text | Check if the element contains specific text | Yes | Yes |
| Equals Text | Check if the element text exactly matches | Yes | Yes |
Form input checks
| Type | Description | Requires Selector | Requires Value |
|---|---|---|---|
| Input Value Equals | Check if an input field value exactly matches | Yes | Yes |
| Input Value Contains | Check if an input field value contains text | Yes | Yes |
| Is Checked | Check if a checkbox or radio is selected | Yes | No |
| Is Not Checked | Check if a checkbox or radio is not selected | Yes | No |
| Is Disabled | Check if the element is disabled | Yes | No |
| Is Enabled | Check if the element is enabled and interactive | Yes | No |
Attribute checks
| Type | Description | Requires Selector | Requires Value |
|---|---|---|---|
| Has CSS Class | Check if the element has a specific CSS class | Yes | Yes |
Page property checks
| Type | Description | Requires Selector | Requires Value |
|---|---|---|---|
| URL Contains | Check if the current page URL contains text | No | Yes |
| Title Contains | Check if the page title contains text | No | Yes |
Advanced
| Type | Description | Requires Selector | Requires Value |
|---|---|---|---|
| Custom... | Build complex conditions with visual builder or code | Depends | Depends |
Examples
Check element is visible
Selector: .success-message
Expected Value: Is Visible
Description: Verify success message appearsCheck element contains text
Selector: .error-message
Expected Value: Contains Text
Value: Invalid email
Description: Verify error message showsCheck input field value
Selector: #email-input
Expected Value: Input Value Equals
Value: user@example.com
Description: Verify email was entered correctlyCheck checkbox is selected
Selector: #terms-checkbox
Expected Value: Is Checked
Description: Verify terms checkbox is checkedCheck button is disabled
Selector: #submit-btn
Expected Value: Is Disabled
Description: Verify button is disabled before form is completeCheck page URL after redirect
Expected Value: URL Contains
Value: /dashboard
Description: Verify redirected to dashboardCheck element has CSS class
Selector: .alert
Expected Value: Has CSS Class
Value: alert-success
Description: Verify alert has success stylingCustom assertions
For validation logic that can't be done with the built-in types, use the Custom option. This opens a modal with two modes:
Visual builder
Build complex conditions using AND/OR logic without writing code:
- Select Custom... from the Expected Value dropdown
- Click Add Condition to add individual checks
- Click Add Group to create nested condition groups
- Click the AND/OR badge between conditions to toggle the operator
- Combine multiple conditions as needed
Example: Check if either success message OR redirect happened
Condition 1: .success-message → Is Visible
OR
Condition 2: URL Contains → /thank-youExample: Check multiple elements are visible
Condition 1: .header → Is Visible
AND
Condition 2: .content → Is Visible
AND
Condition 3: .footer → Is VisibleCode editor
Write custom JavaScript that runs in Playwright:
// Check element count
const count = await page.locator('.product-item').count();
if (count < 3) {
throw new Error(`Expected at least 3 products, found ${count}`);
}Available variables:
page- Playwright Page objectelement- Locator for the selectorselector- The CSS selector stringexpect- Playwright expect function
Example: Check computed CSS style
const color = await element.evaluate(el =>
getComputedStyle(el).backgroundColor
);
if (!color.includes('rgb(0, 128, 0)')) {
throw new Error(`Expected green background, got ${color}`);
}Example: Check localStorage value
const theme = await page.evaluate(() =>
localStorage.getItem('theme')
);
if (theme !== 'dark') {
throw new Error(`Expected dark theme, got ${theme}`);
}Tips
- Add assertions after actions that matter
- Verify success messages and error states
- Check redirects with URL assertions
- Use "Contains" for partial matches, "Equals" for exact matches
- Use Custom assertions for multi-condition checks
- The Visual Builder is easier to start with; the Code Editor gives you full control
Common issues
Assertion fails unexpectedly
- Element takes time to appear (add Wait before Assert)
- Text doesn't match exactly (use "Contains" instead of "Equals")
- Selector is wrong
- Page changed since test was created
Custom assertion not working
- Check for JavaScript syntax errors (use "Test syntax" button)
- Make sure you throw an Error on failure (don't just return false)
- Use await for all async operations
5. Wait
Pauses execution for a specified duration.
Use cases
- Waiting for slow-loading elements
- Giving time for animations
- Waiting for AJAX requests
- Allowing time for redirects
Configuration
Duration (required)
- Time to wait in milliseconds
- 1000ms = 1 second
Description (optional)
- Why you're waiting
Examples
Duration: 2000
Description: Wait for page to loadDuration: 500
Description: Wait for animation to completeDuration: 3000
Description: Wait for AJAX requestTips
- Use waits sparingly (they slow down tests)
- Add waits after navigation
- Wait after triggering animations
- Don't rely on waits to fix flaky tests
Common issues
Wait not long enough
- Increase duration
- Check if element needs more time
Test too slow
- Reduce wait times
- Remove unnecessary waits
- Use assertions instead of arbitrary waits
6. Scroll To
Scrolls the page to bring an element into view.
Use cases
- Revealing elements below the fold
- Triggering lazy-loading
- Scrolling to form sections
- Reaching footer content
Configuration
Selector (required)
- Element to scroll to
Description (optional)
- What you're scrolling to
Scroll Behavior (optional)
- Smooth or instant
Examples
Selector: #contact-form
Description: Scroll to contact form
Scroll Behavior: SmoothSelector: footer
Description: Scroll to page footerTips
- Add scroll steps before interacting with off-screen elements
- Use smooth scrolling for more realistic simulation
- Scroll before clicking elements that may be covered
- Combine with waits if scrolling triggers lazy loading
Common issues
Element not scrolling into view
- Selector doesn't match any element
- Element is already in view
- Page layout prevents scrolling
7. Hover
Moves the mouse over an element to trigger hover effects.
Use cases
- Revealing dropdown menus
- Showing tooltips
- Triggering hover animations
- Testing :hover CSS states
- Activating mega menus
Configuration
Selector (required)
- Element to hover over
Description (optional)
- What you're hovering
Hold Duration (optional)
- How long to hover (milliseconds)
Examples
Selector: nav .menu-item
Description: Hover over menu to show dropdown
Hold Duration: 500Selector: .tooltip-trigger
Description: Hover to show tooltipTips
- Use hover before clicking dropdown items
- Add hold duration for animated menus
- Test hover effects in the preview first
- Mobile devices don't have hover, so keep that in mind
Common issues
Hover effect doesn't trigger
- Element requires longer hover time
- JavaScript hover handler not working
- CSS :hover not applied in test environment
8. Select Option
Chooses an option from a dropdown (select element).
Use cases
- Selecting from dropdown menus
- Choosing country/state
- Picking quantities
- Selecting categories
Configuration
Selector (required)
- CSS selector for the
<select>element
Option Value (required)
- The value attribute of the option to select
- Or the visible text of the option
Description (optional)
- What you're selecting
Match By (optional)
- Value or Label
Examples
Selector: #country
Option Value: US
Match By: Value
Description: Select United StatesSelector: select[name="quantity"]
Option Value: 5
Match By: Label
Description: Select quantity 5Tips
- Use the value attribute when possible (more reliable)
- Use label when value is not user-friendly
- Inspect the select element to see available values
- Test that the option exists in the dropdown
Common issues
Option not found
- Option value is incorrect
- Option is added dynamically (add Wait before Select)
- Using wrong Match By method
Select doesn't change
- Selector targets wrong element
- JavaScript prevents selection
- Option is disabled
9. Checkbox (Toggle Checkbox)
Checks or unchecks a checkbox.
Use cases
- Accepting terms and conditions
- Enabling options
- Selecting multiple items
- Toggling settings
Configuration
Selector (required)
- CSS selector for the checkbox input
Action (required)
- Check, Uncheck, or Toggle
Description (optional)
- What you're checking
Examples
Selector: #terms-agree
Action: Check
Description: Accept terms and conditionsSelector: input[name="newsletter"]
Action: Uncheck
Description: Opt out of newsletterSelector: .option-checkbox
Action: Toggle
Description: Toggle optionTips
- Use "Check" or "Uncheck" for predictable results
- Use "Toggle" if initial state is unknown
- Verify checkbox changes with an Assert step
- Check if labels are clickable instead of the checkbox itself
Common issues
Checkbox doesn't change
- Selector targets label, not input
- Checkbox is disabled
- JavaScript prevents change
Wrong checkbox checked
- Selector matches multiple checkboxes
- Use more specific selector
10. Radio (Select Radio Button)
Selects a radio button from a group.
Use cases
- Choosing payment methods
- Selecting options
- Answering quiz questions
- Choosing shipping methods
Configuration
Selector (required)
- CSS selector for the radio input
Description (optional)
- What you're selecting
Examples
Selector: input[name="payment"][value="credit-card"]
Description: Select credit card paymentSelector: #shipping-express
Description: Select express shippingTips
- Use name and value attributes in selector
- Only one radio in a group can be selected
- Verify selection with an Assert step
- Check if labels are clickable
Common issues
Radio doesn't select
- Selector targets label, not input
- Radio is disabled
- JavaScript prevents selection
Wrong radio selected
- Multiple radios match selector
- Use value attribute for specificity
11. Visual Check
Captures a screenshot and compares it against a reference image to detect visual regressions. This catches layout issues, missing styles, or broken elements that functional tests might miss.
Use cases
- Detecting broken layouts after plugin or theme updates
- Verifying that page design stays consistent over time
- Catching missing images, fonts, or CSS issues
- Monitoring visual integrity of key landing pages
Configuration
Difference Threshold (optional, default: 15%)
- How much visual difference is allowed before the step fails
- Set higher (e.g. 20-30%) for pages with dynamic content
- Set lower (e.g. 1-5%) for static pages that should never change
Exclude Dynamic Elements (optional)
- CSS selectors for page areas that change frequently (blog feeds, dates, sliders, banners)
- These areas are hidden before the screenshot is taken
- Use the element picker to select elements visually, or enter selectors manually
How it works
- First run: A screenshot is captured and saved as the reference image. The step always passes.
- Following runs: A new screenshot is taken and compared pixel-by-pixel against the reference. If the difference exceeds the threshold, the step fails.
- Settings change: When you modify the threshold or exclude selectors, the reference is automatically recreated on the next run.
Reference screenshot management
- Click "View reference" in the step configuration to see the current reference screenshot
- Click "Reset Reference" to delete it — a new one will be captured on the next run
- The reference updates automatically when your visual check settings change
Example
Type: Visual Check
Threshold: 15%
Exclude: .blog-feed, .date-widget
Description: Verify homepage layoutTips
- Place the Visual Check step after navigation — it captures whatever is currently visible
- Exclude elements that change on every visit (dates, latest posts, ads)
- Start with the default 15% threshold and adjust based on your needs
- Use multiple Visual Check steps to monitor different pages in one flow
Common issues
Step fails after content update
- This is expected — the page looks different from the reference
- Run the flow once to create a new reference, or reset the reference manually
Step fails but page looks fine
- Dynamic content may be causing the difference — add those areas to "Exclude Dynamic Elements"
- Increase the threshold if minor differences are acceptable
Reference screenshot looks wrong
- Reset the reference and run the flow again
- Make sure excluded elements are configured before the first run
12. Run Code (1.1.0)
Executes custom JavaScript code during a flow. Use this for anything the built-in steps can't do — custom validations, reading values from the page, interacting with APIs, or sharing data between steps.
Modes
Browser Mode (default)
Code runs inside the page with full DOM access, just like code in your browser's DevTools console. In addition to DOM APIs, helpers are available for interacting with the page and calling external APIs.
Available functions:
document,window— standard DOM APIsconsole.log()— output is captured and shown in the test resultsassert(condition, message)— fails the step if the condition is falsestore(name, value)— saves a value for use in later steps (see Sharing data between steps)get(name)— reads a stored value from a previous code stepenv.KEY_NAME— reads an environment variable (see Environment Variables)localStorage,sessionStorage— standard Web APIs
Helpers (work in both Browser and Playwright mode):
helpers.click(selector)— click an elementhelpers.fill(selector, value)— type text into an inputhelpers.getText(selector)— read text contenthelpers.waitFor(selector)— wait for an element to appearhelpers.waitForURL(pattern)— wait for navigationhelpers.pressKey(key)— press a keyboard key (e.g.'Enter','Tab')helpers.selectOption(selector, value)— select a dropdown optionhelpers.getCount(selector)— count matching elementshelpers.isVisible(selector)— check if an element is visiblehelpers.getAttribute(selector, attr)— read an attribute valuehelpers.screenshot()— take a screenshothelpers.fetch(url, options)— call an external API (CORS-free, runs server-side)helpers.log(message)— log output to test resultshelpers.assert(condition, message)— fail if condition is falsehelpers.store(name, value)— save a value for later stepshelpers.get(name)— read a stored value
Playwright Mode (Local API only)
Code runs in Node.js with access to the Playwright page object and helper utilities. Use this for advanced interactions that require Playwright's API.
WARNING
Playwright mode is only available when using the Local API runner (self-hosted). It is not available on the Cloud runner because user code runs directly on the server. If you're using the Cloud API, use Browser mode — it covers most use cases.
Available variables:
page— Playwright Page objectstep— current step dataenv— environment variables (see Environment Variables)helpers— utility functions:helpers.click(selector)— click an elementhelpers.fill(selector, value)— type text into an inputhelpers.getText(selector)— read text contenthelpers.waitFor(selector)— wait for an element to appearhelpers.wait(ms)— pause for a durationhelpers.screenshot()— take a screenshothelpers.log(message)— log output (shown in test results)helpers.assert(condition, message)— fail if condition is falsehelpers.store(name, value)— save a value for later stepshelpers.get(name)— read a stored value from a previous code step
Configuration
Code (required)
- The JavaScript code to execute
- Click "Format" in the editor toolbar to auto-format your code
Mode
- Browser (default) or Playwright
Timeout
- Maximum execution time (default: 30s)
Using assert()
Use assert() to make a code step fail when a condition is not met. This turns a code step into a powerful custom check.
// Browser mode
var heading = document.querySelector('h1');
assert(heading, 'No H1 found on page');
assert(heading.textContent.includes('Welcome'), 'H1 does not contain Welcome');// Playwright mode
const count = await page.locator('.product').count();
helpers.assert(count > 0, 'No products found on page');
helpers.log('Found ' + count + ' products');Console output
Any output from console.log() (Browser mode) or helpers.log() (Playwright mode) is captured and displayed in the test results. Use this to debug or track values during execution.
var price = document.querySelector('.price').textContent;
console.log('Current price:', price);Sharing data between steps
Use store() to save a value from a code step, then use in any later step's fields or get() in another code step to read it back.
Step 1 — Code step: read and store a value
var price = document.querySelector('.total-price').textContent;
store('totalPrice', price);
console.log('Stored price:', price);Step 2 — Assert step: verify the stored value
Expected Value: {{totalPrice}}Step 3 — Fill step: use in an input field
Value: Order total was {{totalPrice}}Using stored values in another code step
Use get() to read a stored value inside a code step. This is safer than in code because it handles special characters correctly.
// Browser mode
var price = get('totalPrice');
assert(price !== null, 'Price was not stored');
console.log('Price from earlier:', price);// Playwright mode
const price = helpers.get('totalPrice');
helpers.assert(price !== null, 'Price was not stored');Where works:
- Value — for Fill, Wait, Select, and other steps
- Selector — for Click, Fill, Assert, and other steps
- Expected Value — for Assert steps
Limits:
- Variable names: alphanumeric and underscores only, max 64 characters
- Variable values: max 10,000 characters (truncated if longer)
- Max 50 stored variables per flow run
- Variables only exist during a single flow run — they are not saved between runs
TIP
Use descriptive names like productName or checkoutTotal to keep your flows readable.
Environment Variables
Store API keys, tokens, and other secrets in Settings → Advanced → Environment Variables. Values are encrypted at rest and injected into code steps at runtime via the env object.
// Browser mode
var apiKey = env.MY_API_KEY;
var res = await helpers.fetch('https://api.example.com/data', {
headers: { 'Authorization': 'Bearer ' + apiKey }
});
console.log(res.data);// Playwright mode
const apiKey = env.MY_API_KEY;
const response = await helpers.fetch('https://api.example.com/data', {
headers: { 'Authorization': 'Bearer ' + apiKey }
});Variable names are uppercase with underscores (e.g. API_KEY, STRIPE_SECRET). They are never stored in your flow definitions — only in the encrypted settings.
TIP
Use environment variables instead of hardcoding secrets in your code steps. This keeps your API keys secure and makes them easy to rotate without editing every flow.
Examples
Mode: Browser
Code: assert(document.title.length > 0, 'Page has no title');
Description: Verify page has a titleMode: Browser
Code: store('itemCount', document.querySelectorAll('.cart-item').length);
Description: Count cart items for later checkMode: Playwright
Code: await helpers.fill('#search', 'test'); await helpers.click('.search-btn'); await helpers.waitFor('.results');
Description: Search and wait for resultsCalling external APIs
Use helpers.fetch() to call external APIs during a flow — for example email testing services (Mailosaur, mail.tm), SMS verification (Twilio), or any REST API. The request runs server-side, so there are no CORS restrictions.
// Fetch data from an external API
var res = await helpers.fetch('https://api.example.com/data', {
headers: { 'Authorization': 'Bearer YOUR_KEY' }
});
assert(res.ok, 'API returned error: ' + res.status);
console.log('Response:', res.data);// Example: create a test mailbox and check for incoming emails
var res = await helpers.fetch('https://api.mail.tm/domains');
var domain = res.data['hydra:member'][0].domain;
var email = 'test-' + Date.now() + '@' + domain;
store('testEmail', email);Testing your code
The code editor has a Test button that runs your code directly against the live preview. Use it to quickly check if selectors work, assertions pass, and console output looks correct — without running the entire flow.
If your flow has previous code steps with store() calls, those are executed first so your variables are available.
Tips
helperswork in both Browser and Playwright mode — you don't need Playwright mode to click, fill, or fetchhelpers.fetch()is CORS-free — it runs server-side, so you can call any external APIassert()is the easiest way to make a step fail on a condition — no need to writethrow new Error()- Use
store()+to build dynamic flows that adapt to page content - Use the Test button to quickly debug your code against the live preview
- Console output appears in the test results — use it to debug
Combining step types
Good flows use multiple step types together:
Login flow example
- Navigate - Open login page (if not already there)
- Fill - Enter username
- Fill - Enter password
- Click - Click login button
- Wait - Wait for redirect
- Assert - Verify on dashboard
TIP
If you're already on the login page, you can skip the Navigate step and start directly with filling in the form fields.
Form submission example
- Navigate - Open form page
- Scroll To - Scroll to form
- Fill - Enter name
- Fill - Enter email
- Select - Choose country
- Checkbox - Accept terms
- Click - Submit form
- Wait - Wait for response
- Assert - Verify success message
Menu navigation example
- Navigate - Open homepage
- Hover - Hover over menu item
- Wait - Wait for dropdown
- Click - Click submenu item
- Assert - Verify correct page loaded
Best practices
Use the right step type
- Use Navigate for URLs, not Click on links (when testing navigation itself)
- Use Fill for text, not Click + type
- Use Select for dropdowns, not Click + Click
Add descriptions
Always describe what each step does:
- Good: "Click submit button in contact form"
- Bad: No description
Verify actions
Add Assert steps after actions that matter:
- After login, assert dashboard visible
- After form submit, assert success message
- After search, assert results appear
Handle timing
- Add Wait after navigation
- Wait after actions that trigger AJAX
- Use appropriate timeouts
Test incrementally
- Add one step at a time
- Test after each addition
- Fix issues immediately
Next steps
- Running Flows - How to execute and monitor flows
- Flow Editor - The editor interface
- Settings - Configure default behaviors
Troubleshooting
Step always fails
- Check selector is correct
- Verify element exists on page
- Add Wait before the step
- Test manually in preview
Selector doesn't work
- Inspect element in DevTools
- Use more specific selector
- Check for dynamic IDs or classes
- Try data attributes
Timing issues
- Add Wait steps between actions
- Increase step timeout
- Check for slow-loading elements
- Verify AJAX requests complete
Element not interactable
- Scroll to element first
- Check if element is covered
- Verify element is visible
- Wait for animations to complete