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.
Arguments
- x
A
selenider_element
/selenider_elements
object, or a condition.- ...
<
dynamic-dots
> Function calls or functions that must return a logical value. If multiple conditions are given, they must all beTRUE
for the test to pass.- testthat
Whether to treat the expectation as a
testthat
test. You do not need to explicitly provide this most of the time, since by default, we can usetestthat::is_testing()
to figure out whetherelem_expect()
is being called from within atestthat
test.- timeout
The number of seconds to wait for a condition to pass. If not specified, the timeout used for
x
will be used, or the timeout of the local session if an element is not given.
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))