WaitBuilder API
The WaitBuilder
class implements the builder pattern for chaining wait conditions. It's returned by the wait()
method of E2EWrapper
and allows you to compose multiple wait conditions before execution.
Interface
interface IWaitBuilder {
forVisible(options?: WaitOptions): IWaitBuilder
forEnabled(options?: WaitOptions): IWaitBuilder
forExists(options?: WaitOptions): IWaitBuilder
execute(): Promise<boolean>
}
Methods
forVisible()
forVisible(options?: WaitOptions): IWaitBuilder
Adds a visibility condition to the wait chain.
Parameters:
options
: Optional wait configuration
Returns: IWaitBuilder
for method chaining
Example:
await wrapper
.wait()
.forVisible({ timeout: 10000 })
.execute()
forEnabled()
forEnabled(options?: WaitOptions): IWaitBuilder
Adds an enabled/interactable condition to the wait chain.
Parameters:
options
: Optional wait configuration
Returns: IWaitBuilder
for method chaining
Example:
await wrapper
.wait()
.forEnabled({ timeout: 5000 })
.execute()
forExists()
forExists(options?: WaitOptions): IWaitBuilder
Adds an existence condition to the wait chain.
Parameters:
options
: Optional wait configuration
Returns: IWaitBuilder
for method chaining
Example:
await wrapper
.wait()
.forExists({ timeout: 3000 })
.execute()
execute()
execute(): Promise<boolean>
Executes all chained wait conditions in the order they were added.
Returns: Promise resolving to:
true
if all conditions are satisfied within their timeoutsfalse
if any condition times out
Throws: May throw framework-specific errors
Example:
const success = await wrapper
.wait()
.forVisible()
.forEnabled()
.execute()
if (!success) {
console.log('Wait conditions were not met')
}
Usage Patterns
Single Condition
// Wait for element to be visible
const isVisible = await wrapper
.wait()
.forVisible()
.execute()
Multiple Conditions (Ordered)
// Wait for element to exist, then be visible, then be enabled
const isReady = await wrapper
.wait()
.forExists()
.forVisible()
.forEnabled()
.execute()
Multiple Conditions (Any Order)
The beauty of the builder pattern is that conditions can be chained in any order:
// All of these are equivalent
await wrapper.wait().forVisible().forEnabled().execute()
await wrapper.wait().forEnabled().forVisible().execute()
await wrapper.wait().forExists().forVisible().forEnabled().execute()
await wrapper.wait().forEnabled().forExists().forVisible().execute()
Custom Timeouts
Each condition can have its own timeout:
await wrapper
.wait()
.forExists({ timeout: 2000 }) // 2 seconds to exist
.forVisible({ timeout: 5000 }) // 5 seconds to be visible
.forEnabled({ timeout: 1000 }) // 1 second to be enabled
.execute()
Custom Intervals
Adjust how frequently conditions are checked:
await wrapper
.wait()
.forVisible({
timeout: 10000, // 10 seconds total
interval: 500 // Check every 500ms
})
.execute()
Execution Behavior
Sequential Processing
Conditions are checked sequentially in the order they were added:
- First condition is checked repeatedly until satisfied or timeout
- Second condition is checked repeatedly until satisfied or timeout
- Continue for all conditions
- Return
true
if all conditions pass,false
if any timeout
Early Termination
If any condition times out, execution stops immediately and returns false
:
const result = await wrapper
.wait()
.forExists({ timeout: 1000 }) // If this times out...
.forVisible({ timeout: 5000 }) // ...this won't be checked
.execute()
// result will be false if forExists() times out
Error Handling
The builder handles two types of issues:
Timeouts (Normal)
const success = await wrapper.wait().forVisible().execute()
if (!success) {
// Handle timeout - this is expected behavior
console.log('Element did not become visible in time')
}
Framework Errors (Exceptions)
try {
await wrapper.wait().forVisible().execute()
} catch (error) {
// Handle framework-specific errors
console.error('Framework error:', error.message)
}
Advanced Usage
Conditional Chaining
Build conditions dynamically:
let builder = wrapper.wait().forExists()
if (needsVisibility) {
builder = builder.forVisible()
}
if (needsInteraction) {
builder = builder.forEnabled()
}
const success = await builder.execute()
Reusable Builders
Create reusable wait patterns:
function createReadyBuilder(wrapper: E2EWrapper<any>) {
return wrapper
.wait()
.forExists({ timeout: 2000 })
.forVisible({ timeout: 5000 })
.forEnabled({ timeout: 1000 })
}
// Use across multiple elements
const button1Ready = await createReadyBuilder(button1).execute()
const button2Ready = await createReadyBuilder(button2).execute()
Framework-Specific Optimizations
import { TestFramework } from 'e2e-wrapper'
const wrapper = createDetoxWrapper({ testId: 'button' })
if (wrapper.getFramework() === TestFramework.DETOX) {
// Detox is generally fast, use shorter timeouts
await wrapper
.wait()
.forVisible({ timeout: 3000 })
.forEnabled({ timeout: 1000 })
.execute()
} else {
// Other frameworks might need longer timeouts
await wrapper
.wait()
.forVisible({ timeout: 10000 })
.forEnabled({ timeout: 5000 })
.execute()
}
Implementation Details
Default Values
When no options are provided, the following defaults are used:
const defaultOptions: WaitOptions = {
timeout: 5000, // 5 seconds
interval: 100 // 100 milliseconds
}
Memory Efficiency
The builder pattern is implemented efficiently:
- Conditions are stored as lightweight objects
- No unnecessary element queries until
execute()
is called - Builder instances can be reused (though not recommended)
Thread Safety
Each builder instance maintains its own state and is safe to use in concurrent scenarios, though individual framework drivers may have their own threading considerations.