Implementation:Webdriverio Webdriverio Shim
| Knowledge Sources | |
|---|---|
| Domains | Core_Architecture, Command_Wrapping |
| Last Updated | 2026-02-12 00:00 GMT |
Overview
Provides command wrapping with before/after hook execution, chainable element query proxying, and async test execution with retry and timeout support.
Description
The Shim module is a central piece of WebdriverIO's command execution pipeline. executeHooksWithArgs runs an array of hook functions concurrently, catching errors and handling Mocha/Jasmine skip signals gracefully. wrapCommand wraps each protocol or element command with beforeCommand and afterCommand hook invocation, and for element query commands ($, $$, etc.) returns a Proxy-based chainable promise that intercepts property access to enable fluent chaining like $('foo').$('bar').getText(). The proxy handles numeric indexing for element arrays with automatic waitUntil for out-of-bounds access, iterator protocol support, and delegation to pIteration methods for array operations. executeAsync runs spec or hook functions with a timeout race and retry logic, respecting framework-specific timeouts from Mocha and Jasmine. A module-level inCommandHook flag prevents recursive hook execution.
Usage
Use wrapCommand when registering protocol commands on the WebdriverIO client to ensure hooks are executed and element chaining works. Use executeHooksWithArgs to run lifecycle hooks (beforeTest, afterTest, etc.) from framework adapters. Use executeAsync within test framework wrappers to execute spec and hook functions with timeout and retry support.
Code Reference
Source Location
- Repository: Webdriverio_Webdriverio
- File: packages/wdio-utils/src/shim.ts
- Lines: 1-419
Signature
export async function executeHooksWithArgs<T>(
this: unknown,
hookName: string,
hooks?: Function | Function[],
args?: unknown[]
): Promise<(T | Error)[]>
export function wrapCommand<T>(
commandName: string,
fn: Function
): (...args: unknown[]) => Promise<T>
export async function executeAsync(
this: WebdriverIOInstance,
fn: Function,
retries: Frameworks.TestRetries,
args?: unknown[],
timeout?: number
): Promise<unknown>
Import
import { executeHooksWithArgs, wrapCommand, executeAsync } from '@wdio/utils'
I/O Contract
Inputs
| Name | Type | Required | Description |
|---|---|---|---|
| hookName | string | Yes (executeHooksWithArgs) | Name of the hook being executed (e.g., "beforeCommand", "afterTest"), used for logging. |
| hooks | Function or Function[] | No | Single hook function or array of hook functions to execute concurrently. |
| args | unknown[] | No | Arguments to pass to each hook function via apply. |
| commandName | string | Yes (wrapCommand) | Name of the command being wrapped (e.g., "getTitle", "$"). |
| fn | Function | Yes | The original command or spec function to wrap. |
| retries | Frameworks.TestRetries | Yes (executeAsync) | Object with limit (max retries) and attempts (current attempt count). |
| timeout | number | No (executeAsync) | Maximum time in milliseconds to wait for function completion. Defaults to 20000ms minus a 3ms buffer. |
Outputs
| Name | Type | Description |
|---|---|---|
| executeHooksWithArgs result | Promise<(T or Error)[]> | Array of hook return values or Error objects for hooks that threw (errors are caught, not re-thrown). |
| wrapCommand result | (...args) => Promise<T> | A wrapped function that executes beforeCommand hooks, runs the command, executes afterCommand hooks, and supports element chaining via Proxy. |
| executeAsync result | Promise<unknown> | The resolved value of the spec/hook function, or throws after all retries are exhausted or timeout occurs. |
Usage Examples
import { executeHooksWithArgs, wrapCommand, executeAsync } from '@wdio/utils'
// Execute before hooks
const hookResults = await executeHooksWithArgs.call(
browser,
'beforeCommand',
[
(commandName, args) => console.log(`Running ${commandName}`),
(commandName, args) => analytics.track(commandName)
],
['click', [{ x: 100, y: 200 }]]
)
// Wrap a protocol command
const wrappedGetTitle = wrapCommand('getTitle', async function () {
const response = await this.execute('getTitle')
return response.value
})
// Execute a test function with retries
const result = await executeAsync.call(
testContext,
async () => {
const title = await browser.getTitle()
expect(title).toBe('My Page')
},
{ limit: 2, attempts: 0 },
[],
30000
)