elem_expect {selenider} | R Documentation |
Test one or more conditions on HTML elements
Description
elem_expect()
waits for a set of conditions to return TRUE. If, after a
certain period of time (by default 4 seconds), this does not happen, an
informative error is thrown. Otherwise, the original element is returned.
elem_wait_until()
does the same, but returns a logical value (whether or
not the test passed), allowing you to handle the failure case explicitly.
Usage
elem_expect(x, ..., testthat = NULL, timeout = NULL)
elem_wait_until(x, ..., timeout = NULL)
Arguments
x |
A |
... |
< |
testthat |
Whether to treat the expectation as a |
timeout |
The number of seconds to wait for a condition to pass. If not
specified, the timeout used for |
Value
elem_expect()
invisibly returns the element(s) x
, or NULL
if an
element or collection of elements was not given in x
.
elem_wait_for()
returns a boolean flag: TRUE if the test passes, FALSE
otherwise.
Conditions
Conditions can be supplied as functions or calls.
Functions allow you to use unary conditions without formatting them as a
call (e.g. is_present
rather than is_present()
). It also allows you to
make use of R's anonymous function syntax to quickly
create custom conditions. x
will be supplied as the first argument of this
function.
Function calls allow you to use conditions that take multiple arguments
(e.g. has_text()
) without the use of an intermediate function. The call
will be modified so that x
is the first argument to the function call. For
example, has_text("a")
will be modified to become: has_text(x, "a")
.
The and (&&
), or (||
) and not (!
) functions can be used on both types
of conditions. If more than one condition are given in ...
, they are
combined using &&
.
Custom conditions
Any function which takes a selenider element or element collection as its first argument, and returns a logical value, can be used as a condition.
Additionally, these functions provide a few features that make creating custom conditions easy:
Errors with class
expect_error_continue
are handled, and the function is prevented from terminating early. This means that if an element is not found, the function will retry instead of immediately throwing an error.-
selenider
functions used inside conditions have their timeout, by default, set to 0, ignoring the local timeout. This is important, sinceelem_expect()
andelem_wait_until()
implement a retry mechanic manually. To override this default, manually specify a timeout.
These two features allow you to use functions like elem_text()
to access
properties of an element, without needing to worry about the errors that
they throw or the timeouts that they use. See Examples for a few examples of
a custom condition.
These custom conditions can also be used with elem_filter()
and
elem_find()
.
See Also
-
is_present()
and other conditions for predicates for HTML elements. (If you scroll down to the See also section, you will find the rest). -
elem_expect_all()
andelem_wait_until_all()
for an easy way to test a single condition on multiple elements. -
elem_filter()
andelem_find()
to use conditions to filter elements.
Examples
html <- "
<div class='class1'>
<button id='disabled-button' disabled>Disabled</button>
<p>Example text</p>
<button id='enabled-button'>Enabled</button>
</div>
<div class='class3'>
</div>
"
session <- minimal_selenider_session(html)
s(".class1") |>
elem_expect(is_present)
s("#enabled-button") |>
elem_expect(is_visible, is_enabled)
s("#disabled-button") |>
elem_expect(is_disabled)
# Error: element is visible but not enabled
s("#disabled-button") |>
elem_expect(is_visible, is_enabled, timeout = 0.5) |>
try() # Since this condition will fail
s(".class2") |>
elem_expect(!is_present, !is_in_dom, is_absent) # All 3 are equivalent
# All other conditions will error if the element does not exist
s(".class2") |>
elem_expect(is_invisible, timeout = 0.1) |>
try()
# elem_expect() returns the element, so can be used in chains
s("#enabled-button") |>
elem_expect(is_visible && is_enabled) |>
elem_click()
# Note that elem_click() will do this automatically
s("p") |>
elem_expect(is_visible, has_exact_text("Example text"))
# Or use an anonymous function
s("p") |>
elem_expect(\(elem) identical(elem_text(elem), "Example text"))
# If your conditions are not specific to an element, you can omit the `x`
# argument
elem_1 <- s(".class1")
elem_2 <- s(".class2")
elem_expect(is_present(elem_1) || is_present(elem_2))
# We can now use the conditions on their own to figure out which element
# exists
if (is_present(elem_1)) {
print("Element 1 is visible")
} else {
print("Element 2 is visible")
}
# Use elem_wait_until() to handle failures manually
elem <- s(".class2")
if (elem_wait_until(elem, is_present)) {
elem_click(elem)
} else {
reload()
}
# Creating a custom condition is easiest with an anonymous function
s("p") |>
elem_expect(
\(elem) elem |>
elem_text() |>
grepl(pattern = "Example .*")
)
# Or create a function, to reuse the condition multiple times
text_contains <- function(x, pattern) {
text <- elem_text(x)
grepl(pattern, text)
}
s("p") |>
elem_expect(text_contains("Example *"))
# If we want to continue on error, we need to use the
# "expect_error_continue" class.
# This involves making a custom error object.
error_condition <- function() {
my_condition <- list(message = "Custom error!")
class(my_condition) <- c("expect_error_continue", "error", "condition")
stop(my_condition)
}
# This is much easier with rlang::abort() / cli::cli_abort():
error_condition_2 <- function() {
rlang::abort("Custom error!", class = "expect_error_continue")
}
# This error will not be caught
try(elem_expect(stop("Uncaught error!")))
# These will eventually throw an error, but will wait 0.5 seconds to do so.
try(elem_expect(error_condition(), timeout = 0.5))
try(elem_expect(error_condition_2(), timeout = 0.5))