Element
This guide provides a detailed explanation of wdio-workflo's PageElement
and
ValuePageElement
classes. However, it does not show you how to customize these
classes. If you want to learn how to create your own, customized page element classes
by extending wdio-workflo's PageElement
or ValuePageElement
class, read the
Customizing an Element guide.
Overview and Objective
Wdio-workflo's PageElement
class is the main building block of the Page Objects
class family. It maps a single HTML element of a website and provides methods to interact
with this HTML element and read its state. To locate the mapped HTML element on a website,
PageElement
makes use of XPath selectors.
PageElement
implements an implicit waiting mechanism that automatically performs an
initial waiting condition before interacting with the mapped HTML element or reading its state.
In addition, PageElement
also provides the currently
API to check or read the current state
of the mapped HTML without waiting as well as the wait
and eventually
APIs to either wait
for the HTML element's state to meet a certain condition or to check if the HTML element's state
does eventually meet a certain condition.
An important derivation of the PageElement
class is wdio-workflo's ValuePageElement
class that provides two additional methods, getValue
and setValue
, to manage the value
of components like inputs, checkboxes or dropdowns.
PageElement
Creating a Element()
Factory Method
Instead of manually invoking the constructor of PageElement
using the new
keyword,
you should always call the Element()
factory method of the PageNodeStore
class to create an instance of the PageElement
class:
import { stores } from '?/page_objects';
// create PageElement instances by calling the `Element()` factory method of a PageNodeStore
const myElement = store.pageNode.Element('//div');
However, for the sake of completeness, let's examine the type parameters and constructor
of PageElement
in more detail!
Type Parameters
The PageElement
class requires you to define one type parameter, the type of its associated PageNodeStore
, which can be used to create other page nodes:
Constructor
The constructor of PageElement
requires two parameters:
- The XPath selector used to located the mapped HTML element on the website
- The
opts
parameter containing properties to configure thePageElement
The most important properties of the opts
parameter are:
store
=> ThePageNodeStore
instance associated with thePageElement
.timeout
=> The element's default timeout for all functions of theeventually
andwait
APIs.interval
=> The element's default interval for all functions of theeventually
andwait
APIs.waitType
=> The waiting type used for the initial waiting condition of thePageElement
.
PageNodeStore
The Associated Each PageElement
stores an instance of a PageNodeStore
in its private _store
class member variable.
If you extend the PageElement
class to create a custom page element which maps a composite website component that consists of several different HTML elements, you can use
the PageNodeStore
instance stored inside _store
to create page nodes
which map these other HTML elements.
Furthermore, PageElement
provides a public $
accessor that also refers
to the PageNodeStore
instance associated with the PageElement
. The $
accessor
additionally prepends the XPath selector of its PageElement
to the XPath selector
passed to the factory methods of the associated PageNodeStore
:
import { stores } from '?/page_objects';
// myContainer has the XPath selector '//div'
const myContainer = stores.pageNode.Element('//div');
// myButton has the combined XPath selector '//div//button'
const myButton = myContainer.$.Element('//button');
getSelector()
Method
The As already mentioned, PageElement
uses an XPath selector to located its mapped
HTML element on the website.
You can retrieve the XPath selector of a PageElement
by calling its getSelector
method:
import { stores } from '?/page_objects';
const myContainer = stores.pageNode.Element('//div');
// outputs '//div' to the console
console.log(myContainer.getSelector())
This can be useful if your PageElement
can't be located on the website and
you want to debug your testcase and output the XPath selector of the affected
PageElement
.
The Underlying WebdriverIO Element
Wdio-workflo's PageElement
class introduces an additional layer of abstraction
on top of WebdriverIO's browser.element function. The browser.element
function takes an XPath selector and fetches the first HTML
element matching this selector from the website:
browser.element('//div');
The PageElement
class does not store any information about the state of its mapped HTML
element. Instead, the state retrieval methods defined on PageElement
internally invoke WebdriverIO commands on the element returned by browser.element
to read information about the state of the mapped HTML element
directly from the GUI itself.
You can access the underlying WebdriverIO element of a PageElement
via its element
accessor. Before returning the WebdriverIO element, the element
accessor
calls the initialWait()
method of the PageElement
class to make sure that
the HTML element is fully loaded before you try to interact with it or read its state:
get element() {
this.initialWait();
return this.__element;
}
protected get __element() {
return browser.element(this._selector);
}
Most of the time you don't need to access the WebdriverIO element wrapped by the
PageElement
class from outside the class. In some rare cases, however,
you might need to interact with the WebdriverIO element directly.
One situation that requires you to interact with the WebdriverIO element directly
is if your web application has a context menu that can only be opened by performing a
right click. Wdio-workflo's PageElement
provides no method to perform a right click
but a WebdriverIO element does:
import { stores } from '?/page_objects';
const contentMenuZone = store.pageNode.Element('//div');
// `PageElement.element` triggers an implicit wait and returns the wrapped
// WebdriverIO element which provides a `rightClick` command.
contentMenuZone.element.rightClick();
If you want to access the underlying WebdriverIO element of a PageElement
without
triggering an implicit wait, you can use the element
accessor of the currently
API instead:
import { stores } from '?/page_objects';
const contentMenuZone = store.pageNode.Element('//div');
// `PageElement.currently.element` does not trigger an implicit wait.
contentMenuZone.currently.element.rightClick();
To learn more about the waiting mechanisms of PageElement
read the following
section of this guide.
State Function Types
The PageElement
class offers plenty of methods to read or modify the state of
its mapped/wrapped HTML element. These methods can be divided into three categories:
- State Retrieval Functions
- Action Functions
- State Check Functions
The following sections of this guide describe each of these function categories in full detail.
State Retrieval Functions
Overview
State retrieval functions retrieve the value of a specific attribute of the HTML
element wrapped by PageElement
. They are available either directly within the
scope of the PageElement
class or via the currently
API. If invoked directly
on the PageElement
class, state retrieval functions trigger an
implicit wait before returning an attribute value, whereas
calling a state retrieval function via the currently
API does not trigger
an implicit wait and returns the attribute value immediatly.
Types of State Retrieval Functions
State retrieval functions always start with the term get
, followed by the name
of the attribute whose value should be retrieved. However, not all methods of
PageElement
which start with the term get
are state retrieval functions -
methods that do not interact with the mapped HTML element (e.g. getSelector()
,
getTimeout()
or getTimeout()
) aren't state retrieval functions and therefore
do not trigger the implicit waiting mechanism of PageElement
.
State retrieval functions exist for the following HTML attributes:
Text
=> All text content of an HTML element (also includes text of nested HTML elements).DirectText
=> Text content that is a direct child of an HTML element (no nested HTML elements).HTML
=> All HTML content of an HTML element.Class
=> An HTML element's CSS classnames as a single string.Id
=> The ID attribute of an HTML element.Name
=> The name attribute of an HTML element.Location
=> The coordinates of an HTML element in pixels (left top corner).X
=> The X coordinate of an HTML element (left top corner).Y
=> The Y coordinate of an HTML element (left top corner).Size
=> The size of an HTML element in pixels.Width
=> The width of an HTML element in pixels.Height
=> The height of an HTML element in pixels.Value
=> The value of an element like input, checkbox... (only available inValuePageElement
class).
Usage Examples
import { stores } from '?/page_objects';
const element = stores.pageNode.Element('//div');
const input = stores.pageNode.Input('//input'); // Input implements ValuePageElement
// Retrieves the text of the element after performing an implicit wait.
const text = element.getText();
// Retrieves the text of the element immediatly, without performing an implicit wait.
const currentText = element.currently.getText();
// Retrieves the text of the input after performing an implicit wait.
const value = input.getValue();
// Retrieves the value of the element immediatly, without performing an implicit wait.
const currentValue = input.currently.getValue();
Action Functions
Overview
Action functions change the state of tested web application by interacting
with the mapped HTML element of a PageElement
- they could also be called
state modification functions. Action functions are only available directly within
the scope of PageElement
and do not exist within its currently
, wait
or
eventually
APIs. Action functions always trigger an
implicit wait before interacting with the mapped HTML element.
Types of State Check Functions
Wdio-workflo ships with three action functions:
click()
=> Clicks on a mapped HTML element.scrollTo()
=> Scrolls a mapped HTML element into view.setValue(value)
=> Sets the value of an HTML element like input, dropdown, checkbox... (only available forValuePageElement
classes).
click()
Function
Configuring the The behavior of the click()
function can be modified via its opts
parameter object.
This object includes the following properties:
postCondition
=> If set, the HTML element will be clicked and thepostCondition
function executed periodically until it returnstrue
or until a timeout is reached.customScroll
=> Allows you to overwrite the default behavior for scrolling an HTML element into view before clicking on it.timeout
=> Used to define the timeout for evaluating thepostCondition
function in periodic intervals.interval
=> Used to define the intervals for evaluating thepostCondition
function.
For a detailed description of the click()
function's behavior and the properties of
its opts
parameter, please read the
API documentation of the click()
function.
scrollTo()
Function
Configuring the The behavior of the scrollTo()
function, too, can be modified via its opts
parameter
object. This object includes the following properties:
closestContainerIncludesHidden
=> If no explicitcontainerSelector
is defined, setting this property to true will cause the auto-detection of the closest scrollable container to also recognize containers whose CSSoverflow
property is set tohidden
.containerSelector
=> The selector used to determine the parenting scroll container of the HTML element.directions
=> Defines the directions in which the scrolling container should be scrolled.offsets
=> Will be added to the final scroll position of the scrolling container.
For a detailed description of the scrollTo()
function's behavior and the properties of
its opts
parameter, please read the
API documentation of the scrollTo()
function.
Usage Examples
Below, you can find some usage examples of action functions:
import { stores } from '?/page_objects';
const element = stores.pageNode.Element('//div');
const input = stores.pageNode.Input('//input'); // Input implements ValuePageElement
const checkbox = stores.pageNode.Checkbox(
xpath('//div').classContains('ms-Checkbox')
)
// Clicks on the element after performing an implicit wait.
element.click();
// Repeatedly clicks on the element after performing an implicit wait
// until the confirm dialog is no longer visible or until 3 seconds have passed.
// If the dialog did not disappear within 3 seconds, throws an error.
element.click({
postCondition: () => dialogs.confirm.currently.not.isVisible(),
timeout: 3000
});
// Scrolls the element into view within the nearest scrollable container
// using the default scrolling behavior after performing an implicit wait.
element.scrollTo();
// Scrolls the element into view within the specified scroll container after
// performing an implicit wait. Scrolling will only occur along the `y` axis
// and not along the `x` axis => there will be no horizontal scrolling.
elem.scrollTo({
containerSelector: xpath('//div').id('scrollContainer').build(),
directions: { y: true },
});
// Performs an implicit wait and then sets the value of the input to 'wdio'.
input.setValue('wdio');
// Performs an implicit wait and then ticks the checkbox.
checkbox.setValue(true);
State Check Functions
Overview
State check functions allow you to check wether a specific attribute of the HTML element
mapped by a PageElement
has an expected value. Each state check function is available
under the same name in the currently
, wait
and eventually
APIs. In the case of the
currently
API, the check occurs only once whereas the wait
and eventually
APIs
regularly perform the check until it either returns true or until a predefined timeout
is reached.
Types of State Check Functions
Most state check functions exist in three variants (where XXX is the name of the checked attribute):
hasXXX(expectedValue)
to check if the attribute value equals an expected value.containsXXX(expectedValue)
to check if the attribute value contains an expected value.hasAnyXXX()
to check if the attribute has any value at all (is not empty).
The HTML attributes supported by these three variants of state check functions are the same attributes which are also available for state retrieval functions:
Text
=> All text content of an HTML element (also includes text of nested HTML elements).DirectText
=> Text content that is a direct child of an HTML element (no nested HTML elements).HTML
=> All HTML content of an HTML element.Class
=> An HTML element's CSS classnames as a single string.Id
=> The ID attribute of an HTML element.Name
=> The name attribute of an HTML element.Location
=> The coordinates of an HTML element in pixels (left top corner).X
=> The X coordinate of an HTML element (left top corner).Y
=> The Y coordinate of an HTML element (left top corner).Size
=> The size of an HTML element in pixels.Width
=> The width of an HTML element in pixels.Height
=> The height of an HTML element in pixels.Value
=> The value of an element like input, checkbox... (only available inValuePageElement
class).
Futhermore, there are three special state check functions to check if any arbitrary HTML attribute has an expected value:
hasAttribute({name: 'attrName', value: 'expectedValue'})
to check if the value of an an attribute with a specific name matches an expected value.containsAttribute({name: 'attrName', value: 'expectedContainedValue'})
to check if the value of an attribute with a specific name contains an expected value.hasAnyAttribute('attrName')
to check if an attribute with a specific name has any value (is not empty/undefined).
Besides, there are five state check functions that don't exist in three but only in one single variant:
exists
to check if an HTML element exists in the DOM.isVisible
to check if the HTML element is visible (not obscured by another element or hidden by CSS styles).isEnabled
to check if the HTML element is enabled (can be interacted with - not disabled).isSelected
to check if an<option>
HTML element is the selected option of a<select>
element.isChecked
to check if an HTML<input>
element has achecked
HTML attribute set.
Usage Examples
Here are some code examples of state check functions for the currently
, wait
and eventually
APIs:
import { stores } from '?/page_objects';
const element = stores.pageNode.Element('//div');
const input = stores.pageNode.Input('//input'); // Input implements ValuePageElement
// Checks if the element currently exists in the DOM.
element.currently.exists();
// Waits until the element is visible.
// Throws an error if element does not become visible within predefined timeout.
element.wait.isVisible();
// Checks if the element eventually has the text 'wdio'.
element.eventually.hasText('wdio');
// Checks if the element eventually has the value 'wdio'.
input.eventually.hasValue('wdio');
// Waits for the CSS class string of the element to contain 'active'.
// Throws an error if condition is not met within a predefined timeout.
element.wait.containsClass('active');
// Checks if the element currently has the `readonly` HTML attribute set.
element.currently.hasAnyAttribute('readonly');
// Checks if the element's `role` attribute eventually equals 'activeOption'.
element.eventually.hasAnyAttribute({name: 'role', value: 'activeOption'});
not
Modifier
Each state check function is also available in a negated version that checks
for the opposite of an expected value. To use a state check function's negated
version, you need to prepend the state check function with the .not
modifier:
import { stores } from '?/page_objects';
const element = stores.pageNode.Element('//div');
const input = stores.pageNode.Input('//input'); // Input implements ValuePageElement
// Checks if the element does currently not exist in the DOM.
element.currently.not.exists();
// Waits until the element is not/no longer visible.
element.wait.not.isVisible();
// Checks if the element eventually doesn't contain/no longer contains the text 'wdio'.
element.eventually.not.containsText('wdio');
// Checks if the element eventually doesn't contain/no longer contains the value 'wdio'.
input.eventually.not.containsValue('wdio');
It is important to always use the not
modifier instead of putting the
!
negation operator before your state check functions because these two approaches
are not the same:
The !
operator executes your state check function as is and then negates its result,
whereas the not
modifier executes the negated version of your state check function.
To better understand the difference between using the not
modifier and the !
negation
operator, take a look at the following code examples for the wait
and eventually
APIs:
If the below code examples make no sense to you, consider reading the
Explicit Waiting: currently
, wait
and eventually
section
of this guide!
import { stores } from '?/page_objects';
const element = stores.pageNode.Element(xpath('//div'));
// This will wait for the text to become 'wdio-workflo' and throw an error if
// the text stays anything other than 'wdio-workflo' until a timeout is reached.
// If the text does become 'wdio-workflo', the return value of the function
// (the element instance) will be negated which results in the value `false`.
const r1 = !element.wait.hasText('wdio-workflo')
// This will wait for the text to become anything other than 'wdio-workflo' and
// throw an error if it stays 'wdio-workflo' until a timeout is reached.
// The function returns the element itself (an instance of the PageElement class).
const r2 = element.wait.not.hasText('wdio-workflo')
// The following statements are all true because r1 !== r2.
r1 === false
r2 !== false
(r1 instanceof PageElement) !== true
(r2 instanceof PageElement) === true
// This will work, because r2 is an instance of the PageElement class.
r2.click()
// This will not work, because the function 'click' does not exist on `false`.
r1.click()
import { stores } from '?/page_objects';
const element = stores.pageNode.Element(xpath('//div'));
// Let's assume that the initial text of the element is 'wdio-workflo'!
// The `hasText` function would immediately return `true`, but the return value
// would then be negated by the `!` operator, resulting in the value `false`.
!element.eventually.hasText('wdio-workflo', {timeout: 5000})
// The `hasText` function would wait for 5 seconds for the text to become
// 'wdio-workflo' and then return the value `false` if it did not.
element.eventually.not.hasText('wdio-workflo', {timeout: 5000})
// Now let's assume that after 2 seconds, the text of the element changes to
// something other than 'wdio-workflo'.
// Since `hasText` would immediately return `true` in this example and then
// negate the return value, the result of this statement would be `false`.
const r1 = !element.eventually.hasText('wdio-workflo', {timeout: 5000})
// This statement would wait for 2 seconds until the text changes to something
// other than 'wdio-workflo'. Then it would return `true`!!!
const r2 = element.eventually.not.hasText('wdio-workflo', {timeout: 5000})
// The following statements are all true because r1 !== r2.
r1 === false
r2 === true
Waiting Mechanisms
Implicit Waiting
What is Implicit Waiting and how does it work?
When you open a website in your browser, the website and its components are usually not available immediately. A request to load the code of the website needs to be processed by a webserver, the server's response needs to be transferred to your browser and your browser needs to render the contents of the website before it can be displayed. Depending on the speed of your internet connection, the size of the website and the computational power of the webserver and your computer, it might take some time before you can read the contents of a website and interact with them.
In the age of Web 2.0, asynchronous loading of website content and single page applications further intensify this problem.
Our tests need to take these waiting times into account, otherwise we would get a lot of errors because elements could not be located on the website since the website has not been fully loaded yet.
To reduce the number of explicit waiting statements in your test code,
the PageElement
class implements an implicit waiting mechanism:
Whenever you call a method that reads the state of an HTML element mapped by PageElement
(e.g. getText()
) or interacts with it (e.g. click()
), wdio-workflo automatically
waits for an initial waiting condition to be fulfilled before executing the corresponding
command.
The four different kinds of initial waiting conditions are defined by the
Workflo.WaitType
enum and can be set via the waitType
property of the
page element's opts
parameter:
exist
waits until the element exists on the websitevisible
waits until the element is visible on the website (default value; not obscured by another element or hidden via CSS styles)text
waits until the element has any text contentvalue
waits until the element has any value (only forValuePageElement
classes)
Most factory methods of a PageNodeStore
allow you to define
the value of waitType
as property of their publicly configurable opts
parameter:
import { stores } from '?/page_objects';
const hiddenContainer = stores.pageNode.Element('//div', {
waitType: Workflo.WaitType.exist
});
What triggers the implicit waiting mechanism?
The implicit waiting mechanism is only supported by the PageElement
class itself
and any class that is derived from PageElement
. Functions that trigger implicit
waiting can be divided into two categories:
- State retrieval functions => These functions read the state of the web application
by retrieving information about the attributes of an HTML element, e.g.
getText()
. - Action functions => These functions change the state of the web application by interacting
with HTML elements, e.g.
click()
.
Please be aware that the implicit waiting mechanism only works for functions defined
directly on the PageElement
class and is not applied within the currently
,
wait
and eventually
APIs of a PageElement
. This means that PageElement.getText()
triggers implicit waiting, but PageElement.currently.getText()
does not.
Besides state retrieval and action functions, the element
accessor of the PageElement
class also performs an implicit wait before returning the WebdriverIO element wrapped
by the PageElement
class.
Other page node classes like PageElementList
, PageElementMap
and PageElementGroup
do not have an implicit waiting mechanism of their own. However, these three classes all
manage instances of the PageElement
class and whenever you invoke a state retrieval or an
action function on a managed PageElement
instance, its implicit waiting mechanism will be triggered.
currently
, wait
and eventually
Explicit Waiting: In addition to its implicit waiting mechanism, each page node class ships with
a wait
API that offers you the ability to explicitly control when your tests should wait for an HTML element to reach a certain state.
Furthermore, page nodes also have a currently
API to read or check the current state of a mapped HTML element without performing an implicit wait, and an eventually
API that lets you check if an HTML element reaches a certain state within a specific timeout.
currently
API
The Overview
The currently
API consists of state retrieval functions and state check functions.
Invoking a state retrieval function on the currently
API of a PageElement
bypasses
its implicit waiting.
State Retrieval Functions: No Implicit Waiting
Let's take a look at the following code example to better understand the difference
between invoking a state retrieval function directly on the PageElement
class and
invoking a state retrieval function on the currently
API of the PageElement
:
import { stores } from '?/page_objects';
const element = stores.pageNode.Element('//div');
// implicitly waits for the element to be visible and then returns its text
console.log( element.getText() )
// immediately returns the element's text without waiting for it to be visible
console.log( element.currently.getText() )
State Check Function Examples
To spare you the need of first reading the current state of an HTML element and then comparing it to an expected value in a second step, wdio-workflo's state check functions let you check directly if the state of an HTML element matches an expected state.
Let's examine the three variants (hasXXX
, containsXXX
, hasAnyXXX
) of currently
state check functions by the example of an HTML element's "text" attribute:
import { stores } from '?/page_objects';
const element = stores.pageNode.Element(
xpath('//div').text('wdio-workflo')
);
// returns true because the actual text equals 'wdio-workflo'
element.currently.hasText('wdio-workflo')
// returns true because the actual text contains the substring 'workflo'
element.currently.containsText('workflo')
// returns true because the element has any text (it is not blank/empty)
element.currently.hasAnyText()
not
Modifier Examples
The currently
API also offers functions to check if the state of an HTML element
does NOT match an expected state. These negated state check functions are
available via the not
modifier of the currently
API:
import { stores } from '?/page_objects';
const element = stores.pageNode.Element(
xpath('//div').text('wdio-workflo')
);
// returns true because the actual text does not equal 'icebears'
element.currently.not.hasText('icebears')
// returns true because the actual text does not contain the substring 'icebears'
element.currently.not.containsText('icebears')
// returns false because the element has the text 'wdio-workflo'
element.currently.not.hasAnyText()
Please note that action functions of a PageElement
like click()
and scrollTo()
always perform an implicit wait because interacting with elements that have not been rendered yet doesn't make much sense. Therefore, the currently
API does not contain action functions like click()
.
wait
API
The Overview
The wait
API contains a set of state check functions that wait for an HTML
element to reach a certain state within a specific timeout. If the HTML element
does not reach the expected state within the specified timeout, an error will be thrown.
Otherwise, the PageElement
instance will be returned by each function defined on the
wait
API.
Chaining Method Calls
The fact that each state check function of the wait
API returns its PageElement
instance allows you to easily chain additional PageElement
method calls:
import { stores } from '?/page_objects';
const element = stores.pageNode.Element('//div');
element.wait.hasText('wdio-workflo').click()
timeout
and interval
Defining The last parameter of a state check function defined on the wait
API of PageElement
always includes a timeout
and an interval
property. The timeout
property
lets you define after how many milliseconds the state check function will throw an error
if the expected state was not reached. The interval
property defines how
many milliseconds should pass between each evaluation of the expected state:
import { stores } from '?/page_objects';
const element = stores.pageNode.Element('//div');
// Throws an error if the element does not have the text 'wdio-workflo' within
// 3 seconds. The check of the element's text will occur every 500 milliseconds.
element.wait.hasText('wdio-workflo', {
timeout: 3000,
interval: 500
})
If you do not explicitly specify a timeout value when calling a state check function on wait
,
the default timeout of the PageElement
will be used:
import { stores } from '?/page_objects';
const element = stores.pageNode.Element('//div');
// uses this._timeout as timeout value
element.wait.hasText('wdio-workflo')
The default timeout value for a specific instance of a PageElement
can be set by
defining the timeout
property of the publicly configurable opts
parameter
of a factory method in milliseconds:
import { stores } from '?/page_objects';
const element = stores.pageNode.Element('//div', {
timeout: 10000 // 10 seconds
});
Alternatively, you can also define the global default timeout value that will be
used as a fallback value for all page nodes in workflo.conf.ts
:
export const workfloConfig: IWorkfloConfig = {
/*...*/
timeouts: {
default: 6000
}
/*...*/
};
not
Modifier Examples
The wait
API also offers functions to wait for the state of an HTML element to
NOT or no longer match an expected state. These negated state check functions are
available via the not
accessor of the wait
API:
import { stores } from '?/page_objects';
const element = stores.pageNode.Element('//div');
// waits until the text of the element not or no longer is 'wdio-workflo'
element.wait.not.hasText('wdio-workflo')
untilElement()
Generic State Checks using Finally, the wait
API also offers an untilElement
state check function that
lets you check if a generic condition is met within a specific timeout:
import { stores } from '?/page_objects';
const element = stores.pageNode.Element('//div');
element.wait.untilElement(
"has autocomplete off",
(element) => element.getAttribute("autocomplete") === 'off',
{ timeout: 3000 }
)
The untilElement
state check function takes 3 parameters:
- A string used to create an error message if the condition is not met within a specific timeout.
- A function that needs to return true if the condition is currently met
or false if it isn't. This function is passed the
PageElement
instance as parameter. - An
opts
parameter to define thetimeout
andinterval
used to wait for the condition to be met.
eventually
API
The Overview
The eventually
API is almost identical to the wait
API: It also waits for an
HTML element to each a certain state within a specific timeout. However, the
state check functions defined on the eventually
API will return true
if the
expected state is reached within the specified timeout and false
if it is not:
import { stores } from '?/page_objects';
const element = stores.pageNode.Element(
xpath('//div').text('wdio-workflo')
);
// will probably return false since the website has not finished rendering yet
element.eventually.hasText('wdio-workflo', {timeout: 1})
// will return true if the website finishes loading within 20 seconds
element.eventually.hasText('wdio-workflo', {timeout: 20000})
The eventually
API does not throw an error if the HTML element fails to reach
the expected state within the specified (or default) timeout.
timeout
and interval
Defining The last parameter of a state check function defined on the eventually
API of
PageElement
always includes a timeout
and an interval
property. The timeout
property lets you define after how many milliseconds the state check function will
throw an error if the expected state was not reached. The interval
property defines
how many milliseconds should pass between each evaluation of the expected state:
import { stores } from '?/page_objects';
const element = stores.pageNode.Element('//div');
// Returns `false` if element does not have the text 'wdio-workflo' within
// 3 seconds. The check of the element's text will occur every 500 milliseconds.
element.eventually.hasText('wdio-workflo', {
timeout: 3000,
interval: 500
})
not
Modifier Examples
Like the currently
and the wait
API, the eventually
API too offers a
not
accessor to invoke negated variants of its state check function.
These functions check if the state of an HTML does not match/stops to match an
expected state within a specific timeout:
import { stores } from '?/page_objects';
const element = stores.pageNode.Element(
xpath('//div').text('wdio-workflo')
);
element.eventually.not.hasText('icebears')
meetsCondition()
Generic State Checks using Like the untilElement
function of the wait
API, the eventually
API also
provides a state check function that lets you check if a generic condition
is met within a specific timeout. For the eventually
API, this generic
state check function is called meetsCondition
:
import { stores } from '?/page_objects';
const element = stores.pageNode.Element('//div');
element.eventually.meetsCondition(
(element) => element.getAttribute("autocomplete") === 'off',
{ timeout: 3000 }
)
The meetsCondition
state check function takes 2 parameters:
- A function that needs to return true if the condition is currently met
or false if it isn't. This function is passed the
PageElement
instance as parameter. - An
opts
parameter to define thetimeout
andinterval
used to check if the condition is eventually met.
ValuePageElement
Class
The Some components of a website like inputs, checkboxes, radio buttons, dropdowns, textareas or toggles/switches manage a value that can be modified by users.
Wdio-workflo's PageElement
class has no way of getting, setting or checking the
value of such components. In this event, you need to make use of wdio-workflo's
ValuePageElement
class. ValuePageElement
is an abstract class that inherits
all functionality of the PageElement
class and in addition provides two abstract methods, getValue
and setValue
, to manage the value of components like input or checkbox.
Futhermore, the ValuePageElement
class includes the state check functions hasValue
, containsValue
and hasAnyValue
it its currently
, wait
and eventually
APIs.
Wdio-workflo's example repository contains an Input
class that is derived from
ValuePageElement
and implements its getValue
and setValue
methods.
Below you can find code examples for how to use the getValue
and setValue
methods
as well as the hasValue
, containsValue
and hasAnyValue
state check functions
of the Input
class:
import { stores } from '?/page_objects';
const input = stores.pageNode.Input(xpath('//input'));
// returns the value after performing an implicit wait
input.getValue()
// returns the value immediately, without performing an implicit wait
input.currently.getValue()
// sets the value of the input to 'wdio-workflo' after performing an implicit wait
input.setValue('wdio-workflo')
// checks if the input currently/at this very moment has the value 'wdio-workflo'
input.currently.hasValue('wdio-workflo');
// waits for the value of the input not to contain the string 'icebears'
input.wait.not.containsValue('icebears');
// checks if the input eventually has any value within 3000 milliseconds
input.eventually.hasAnyValue({timeout: 3000})
Since the implementation of components like dropdowns or checkboxes can
vary greatly between the different available component libraries for React,
Angular and the like, wdio-workflo does not ship with a standard implementation
for getValue
and setValue
. Instead, you need to create your own, custom page
element classes that inherit from ValuePageElement
and implement its getValue
and setValue
methods.
You can read more about how to do this in the Extending ValuePageElement
section of the Customizing an Element guide.