Skip to content

Commit

Permalink
Wait in click.
Browse files Browse the repository at this point in the history
Closes tidyverse#405.

I tried to make this automatic, and got it working some of the time, but I couldn't find a way to detect that something might change, and thus wait in that situation. I tried to make it as clear as possible for users to be able to fix this. Once a strategy is agreed on, the same strategy should probably be applied to other methods.
  • Loading branch information
jonthegeek committed Jul 11, 2024
1 parent e223d9d commit 9c0b78f
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 5 deletions.
42 changes: 37 additions & 5 deletions R/live.R
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ LiveHTML <- R6::R6Class(
self$session$Page$navigate(url, wait_ = FALSE)
self$session$wait_for(p)

private$root_id <- self$session$DOM$getDocument(0)$root$nodeId
private$refresh_root()
},

#' @description Called when `print()`ed
Expand Down Expand Up @@ -129,10 +129,21 @@ LiveHTML <- R6::R6Class(
#' @description Simulate a click on an HTML element.
#' @param css CSS selector or xpath expression.
#' @param n_clicks Number of clicks
click = function(css, n_clicks = 1) {
#' @param new_page Whether to wait for a new page to load, such as after
#' clicking a link.
click = function(css, n_clicks = 1, new_page = FALSE) {
private$check_active()
check_number_whole(n_clicks, min = 1)

# Wait for new page, #405.
if (new_page) {
p <- self$session$Page$loadEventFired(wait_ = FALSE)
on.exit({
self$session$wait_for(p)
private$refresh_root()
}, add = TRUE)
}

# Implementation based on puppeteer as described in
# https://medium.com/@aslushnikov/automating-clicks-in-chromium-a50e7f01d3fb
# With code from https://github.com/puppeteer/puppeteer/blob/b53de4e0942e93c/packages/puppeteer-core/src/cdp/Input.ts#L431-L459
Expand Down Expand Up @@ -170,6 +181,7 @@ LiveHTML <- R6::R6Class(
button = "left"
)
}

invisible(self)
},

Expand Down Expand Up @@ -224,6 +236,7 @@ LiveHTML <- R6::R6Class(
deltaX = left,
deltaY = top
)

invisible(self)
},

Expand Down Expand Up @@ -268,14 +281,14 @@ LiveHTML <- R6::R6Class(
if (new_chromote && !self$session$is_active()) {
suppressMessages({
self$session <- self$session$respawn()
private$root_id <- self$session$DOM$getDocument(0)$root$nodeId
private$refresh_root()
})
}
},

wait_for_selector = function(css, timeout = 5) {
done <- now() + timeout
while(now() < done) {
while (now() < done) {
nodes <- private$find_nodes(css)
if (length(nodes) > 0) {
return(nodes)
Expand All @@ -289,7 +302,22 @@ LiveHTML <- R6::R6Class(
find_nodes = function(css, xpath) {
check_exclusive(css, xpath)
if (!missing(css)) {
unlist(self$session$DOM$querySelectorAll(private$root_id, css)$nodeIds)
node_ids <- try_fetch(
self$session$DOM$querySelectorAll(private$root_id, css)$nodeIds,
error = function(cnd) {
if (grepl("-32000", cnd_message(cnd))) {
cli::cli_abort(
c(
"Can't find root node.",
i = "Did you issue a {.code click()} without waiting for a {.arg new_page}?"
),
class = "rvest_error-missing_node",
parent = cnd
)
}
}
)
unlist(node_ids)
} else {
search <- glue::glue("
(function() {{
Expand Down Expand Up @@ -324,6 +352,10 @@ LiveHTML <- R6::R6Class(
object_id = function(node_id) {
# https://chromedevtools.github.io/devtools-protocol/tot/DOM/#method-resolveNode
self$session$DOM$resolveNode(node_id)$object$objectId
},

refresh_root = function() {
private$root_id <- self$session$DOM$getDocument(0)$root$nodeId
}
)
)
Expand Down
8 changes: 8 additions & 0 deletions tests/testthat/html/navigate1.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<!DOCTYPE html>
<html>
<head>
<title>Navigate 1</title>
</head>
<body>
<a href="navigate2.html">Navigate to Page 2</a>
</body>
8 changes: 8 additions & 0 deletions tests/testthat/html/navigate2.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<!DOCTYPE html>
<html>
<head>
<title>Navigate 2</title>
</head>
<body>
<p>Success!</p>
</body>
18 changes: 18 additions & 0 deletions tests/testthat/test-live.R
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,14 @@ test_that("can click a button", {
expect_equal(html_text(html_element(sess, "p")), "double clicked")
})

test_that("can find elements after click that navigates", {
skip_if_no_chromote()

sess <- read_html_live(html_test_path("navigate1"))
sess$click("a", new_page = TRUE)
expect_equal(html_text2(html_element(sess, "p")), "Success!")
})

test_that("can scroll in various ways", {
skip_if_no_chromote()

Expand Down Expand Up @@ -88,6 +96,16 @@ test_that("can press special keys",{
expect_equal(html_text(html_element(sess, "#keyInfo")), "]/BracketRight")
})

test_that("gracefully errors on missing root node", {
skip_if_no_chromote()

sess <- read_html_live(html_test_path("navigate1"))
sess$click("a")
expect_error(
html_element(sess, "p"),
class = "rvest_error-missing_node"
)
})

# as_key_desc -------------------------------------------------------------

Expand Down

0 comments on commit 9c0b78f

Please sign in to comment.