@qavajs/steps-wdio provides 100+ pre-built Cucumber step definitions for qavajs, powered by WebdriverIO.
Write browser and mobile automation in plain Gherkin — no glue code required.
- Installation
- Configuration
- Page Object
- Steps Reference
- Screenshots
- Snapshots
- Session Reuse
- TypeScript
npm install @qavajs/steps-wdio
Register the step definitions in your qavajs config and configure the browser section:
import App from './page_object';
export default {
require: [
'node_modules/@qavajs/steps-wdio/index.js'
],
pageObject: new App(),
browser: {
capabilities: {
browserName: 'chrome'
},
timeout: {
present: 10000, // ms to wait for element to be present in DOM
visible: 20000, // ms to wait for element to be visible
clickable: 15000, // ms to wait for element to be clickable
page: 10000, // page load timeout
implicit: 0, // WebdriverIO implicit wait
element: 2000, // ms for element to be accessible
value: 5000, // timeout for value-based assertions
valueInterval: 500, // polling interval for value assertions
actionInterval: 1000, // polling interval for action retry loops
pageRefreshInterval: 2000 // interval between page refreshes in wait steps
}
}
}Define your page object using the locator helper exported from @qavajs/steps-wdio/po:
import { locator } from '@qavajs/steps-wdio/po';
class BodyComponent {
TextElement = locator('#textValue');
SubmitButton = locator('button[type=submit]');
}
export default class App {
// Simple CSS/XPath selector
SearchInput = locator('#search');
// Template selector — alias text in parentheses is passed as argument
ListItemByIndex = locator.template(idx => `li:nth-child(${idx})`);
// Native WebdriverIO selector — full access to browser/driver
NativeButton = locator.native(({ driver }) => driver.$('#button'));
// Component composition — resolves nested selectors relative to parent
Body = locator('body').as(BodyComponent);
// Default resolver — handles any alias not explicitly defined
defaultResolver({ alias }: { alias: string }) {
return ({ parent }: { parent: any }) => parent.$(`//*[text()="${alias}"]`);
}
}Reference elements in steps using the class property name in Title Case (spaces between words):
When I click 'Search Input'
Then I expect text of 'Body > Text Element' to be equal 'hello'
Then I expect text of 'List Item By Index (3)' to be equal 'item 3'Use > to traverse component chains and parentheses to pass template arguments.
| Step | Description |
|---|---|
I open {url} url |
Navigate to URL |
I click {element} |
Click an element |
I double click {element} |
Double-click an element |
I right click {element} |
Right-click an element |
I force click {element} |
Click via JS (bypasses visibility checks) |
I click {value} text in {element} collection |
Click element in collection matching text |
I click {value} coordinates in {element} |
Click at relative x,y coordinates |
I type {value} into {element} |
Type text into an element |
I type {value} chars into {element} |
Type text character by character |
I clear {element} |
Clear input value |
I press {value} key(s) |
Press key or key chord (e.g. Control+A) |
I press {value} key(s) {int} time(s) |
Press key N times |
I select {value} option from {element} dropdown |
Select option by text |
I select {int}(st|nd|rd|th) option from {element} dropdown |
Select option by index |
I scroll to {element} |
Scroll element into view |
I scroll by {value} |
Scroll page by x, y pixels |
I scroll by {value} in {element} |
Scroll inside element |
I scroll until {element} to be visible |
Scroll page until element is visible |
I scroll in {element} until {element} to be visible |
Scroll inside element until target visible |
I hover over {element} |
Move mouse over element |
I drag and drop {element} to {element} |
Drag source onto target |
I upload {value} file to {element} |
Set file path on a file input |
I switch to {int} frame |
Switch to frame by index |
I switch to {element} frame |
Switch to frame by element |
I switch to parent frame |
Switch back to parent frame |
I switch to {int} window |
Switch to browser tab by index |
I switch to {value} window |
Switch to browser tab by title |
I open new tab |
Open a new browser tab |
I close current tab |
Close the current browser tab |
I refresh page |
Reload the current page |
I click {button} button |
Click browser Back or Forward button |
I set window size {value} |
Resize window to width x height |
Conditions: present, not present, visible, not visible, clickable, not clickable, enabled, disabled, in viewport, not in viewport, fully in viewport, not fully in viewport
| Step | Description |
|---|---|
I expect {element} {condition} |
Assert element state |
I expect text of {element} {validation} {value} |
Assert element text |
I expect value of {element} {validation} {value} |
Assert input value |
I expect {property} property of {element} {validation} {value} |
Assert JS property |
I expect {attribute} attribute of {element} {validation} {value} |
Assert HTML attribute |
I expect {property} css property of {element} {validation} {value} |
Assert CSS property |
I expect {customProperty} custom property of {element} {validation} {value} |
Assert custom script result |
I expect number of elements in {element} collection {validation} {value} |
Assert collection size |
I expect text of every element in {element} collection {validation} {value} |
Assert all texts match |
I expect every element in {element} collection {condition} |
Assert all elements match condition |
I expect {attribute} attribute of every element in {element} collection {validation} {value} |
Assert attribute on each element |
I expect {property} property of every element in {element} collection {validation} {value} |
Assert property on each element |
I expect {property} css property of every element in {element} collection {validation} {value} |
Assert CSS on each element |
I expect {customProperty} custom property of every element in {element} collection {validation} {value} |
Assert custom property on each element |
I expect current url {validation} {value} |
Assert current page URL |
I expect page title {validation} {value} |
Assert page title |
| Step | Description |
|---|---|
I wait {int} ms |
Pause execution for N milliseconds |
I refresh page until {element} {condition}( ){timeout} |
Refresh until element meets condition |
I refresh page until text of {element} {validation} {value}( ){timeout} |
Refresh until text matches |
I click {element} until text of {element} {validation} {value}( ){timeout} |
Click until text matches |
I click {element} until value of {element} {validation} {value}( ){timeout} |
Click until value matches |
Timeout is optional and expressed as {int}ms or {int}s (e.g. 3000ms, 10s).
Save element state to memory and reference it later with $key syntax.
| Step | Description |
|---|---|
I save text of {element} as {key} |
Save element text |
I save value of {element} as {key} |
Save input value |
I save {property} property of {element} as {key} |
Save JS property |
I save {attribute} attribute of {element} as {key} |
Save HTML attribute |
I save {cssProperty} css property of {element} as {key} |
Save CSS property value |
I save {customProperty} custom property of {element} as {key} |
Save custom script result |
I save number of elements in {element} collection as {key} |
Save collection count |
I save text of every element of {element} collection as {key} |
Save array of texts |
I save {attribute} attribute of every element of {element} collection as {key} |
Save array of attributes |
I save {property} property of every element of {element} collection as {key} |
Save array of properties |
I save {customProperty} custom property of every element of {element} collection as {key} |
Save array of custom properties |
I save current url as {key} |
Save current URL |
I save page title as {key} |
Save page title |
I save screenshot as {key} |
Save full-page screenshot as base64 |
I save screenshot of {element} as {key} |
Save element screenshot as base64 |
I save bounding rect of {element} as {key} |
Save element bounding rect object |
When I save text of 'Header' as 'heading'
Then I expect text of 'Title' to be equal '$heading'| Step | Description |
|---|---|
I hover over {element} |
Move pointer over element |
I press {button} mouse button |
Hold left / right / middle button |
I release {button} mouse button |
Release held mouse button |
| Step | Description |
|---|---|
I hold down {string} key |
Hold a key (e.g. "Shift") |
I release {string} key |
Release a held key |
I press {value} key(s) |
Press key or chord (e.g. Control+A) |
I press {value} key(s) {int} time(s) |
Press key N times |
| Step | Description |
|---|---|
I will wait for alert |
Register expectation for an upcoming alert |
I accept alert |
Accept (OK) the dialog |
I dismiss alert |
Dismiss (Cancel) the dialog |
I expect alert text {validation} {value} |
Assert dialog message text |
| Step | Description |
|---|---|
I set {name} cookie as {value} |
Set a cookie by name |
I save value of {name} cookie as {key} |
Save cookie value to memory |
| Step | Description |
|---|---|
I set {key} {storageType} storage value as {value} |
Set local or session storage item |
I save value of {key} {storageType} storage as {key} |
Save storage item to memory |
| Step | Description |
|---|---|
I execute {script} function |
Execute a JS function from memory/config |
I execute {script} script |
Execute an inline JS expression |
I execute {script} function and save result as {key} |
Execute function and save return value |
I execute {script} script and save result as {key} |
Execute script and save return value |
Network mocking requires WebdriverIO's mock capability (Chrome DevTools / BiDi).
| Step | Description |
|---|---|
I create mock for {urlTemplate} as {key} |
Create network mock and save reference |
I mock {mockKey} to respond with {statusCode} and {body} |
Respond with custom status and body |
I mock {mockKey} to abort with {reason} |
Abort matched requests with error reason |
I expect mock {mockKey} to {callCount} calls |
Assert number of intercepted calls |
| Step | Description |
|---|---|
I create interception for {urlPattern} as {key} |
Register request interception |
I wait for {interceptionKey} response |
Wait until intercepted response arrives |
I save request of {interceptionKey} as {key} |
Save intercepted request to memory |
I save response of {interceptionKey} as {key} |
Save intercepted response to memory |
Additional steps for native mobile automation (Appium):
| Step | Description |
|---|---|
I tap {element} |
Tap an element |
I swipe {direction} |
Swipe up / down / left / right on screen |
I swipe {direction} in {element} |
Swipe within a specific element |
I shake device |
Shake the device |
I lock device |
Lock the device screen |
I unlock device |
Unlock the device screen |
I set {orientation} orientation |
Set portrait or landscape orientation |
I set device time {value} |
Set device time (format: HH:mm or custom) |
Built-in screenshot capture on specific events. Add screenshot to the browser config:
export default {
browser: {
capabilities: { browserName: 'chrome' },
screenshot: {
event: ['onFail'] // 'onFail' | 'beforeStep' | 'afterStep'
}
}
}Built-in page snapshot capture. Add snapshot to the browser config:
export default {
browser: {
capabilities: { browserName: 'chrome' },
snapshot: {
event: ['onFail'] // 'onFail' | 'beforeStep' | 'afterStep'
}
}
}By default a new browser session is started and closed for each test run. Set reuseSession: true to share the session across tests in the same process — session lifecycle then becomes your responsibility:
export default {
browser: {
reuseSession: true
}
}The package ships with TypeScript declarations. Extend the config interface and world type in your project:
import { IQavajsWdioConfig, QavajsWdioWorld } from '@qavajs/steps-wdio';
// Augment config type
const config: IQavajsWdioConfig = { ... };
// Access wdio browser in custom steps
import { When } from '@cucumber/cucumber';
When('I do something custom', async function (this: QavajsWdioWorld) {
const browser = this.wdio.browser;
await browser.execute(() => console.log('hello'));
});