ashbythorpe / selenider

Concise, Lazy and Reliable Wrapper for 'chromote' and 'selenium'
https://ashbythorpe.github.io/selenider/
Other
31 stars 2 forks source link

Interact with DT datatable fields #4

Closed sybrohee closed 10 months ago

sybrohee commented 10 months ago

Thank you for this really nice and useful library with which I wish I will be able to use R instead of selenium and python for my shiny applications production tests. Indeed, as shiny is in R, I would find it more logical to make use of tests that are written in pure R. I have not been convinced by RSelenium which I found way to heavy or by cypress which I found too complicated.

I wanted to use shinytest2 but unfortunately it does not seem to be compatible with applications rendered via shinyproxy .

With selenider, I have been able without (too much) struggling to connect and to display my shiny application.

However, when I wanted to change the number of displayed lines by selecting the field on top of the table (classical DT field), I have not been able to make it change.

image

Having a look at the code, it seems that this selector should be available through something like

find_element(mySeleniderSession, xpath = '//*[@id="myTable"]') |> find_element(xpath = '//*[@class="dataTables_length"]') |> find_element(css = "label:nth-child(1)") |> find_element(css = "select:nth-child(1)") |> find_element(css = "option:nth-child(5)") |> elem_click()

This is what I was doing with Selenium but in this case, the "click" simply does not take place.

Would you have any insight of what I am missing.

Thank you again for this really good library.

Regards

ashbythorpe commented 10 months ago

This issue is universal with all select elements, and is most likely an issue with chromote, which I've reported here: https://github.com/rstudio/chromote/issues/131

For now, you can use elem_set_value() on the select input, e.g:

library(selenider)

session <- minimal_selenider_session("
<select name='select'>
  <option value='1'>One</option>
  <option value='2'>Two</option>
</select>
", view = TRUE)

s("select") |>
  elem_set_value("Two")

This works quite nicely, although for some reason it highlights the select element.

I guess in your case it would be something like:

find_element(mySeleniderSession, "#myTable") |>
  find_element(".dataTables_length") |>
  find_element("select") |>
  elem_set_value("100")

In the future, I might create an elem_select() function to interact with select elements more easily.

sybrohee commented 10 months ago

Hi @ashbythorpe,

Thanks a lof for your fast answer. It is working now and in combination with rvest (as described in your documentation), I could extract a data.table from my DT:datatable in my shiny app.

However, I would really like that chromote could allow any type of click. For example, I am not sure that the issue I raised a while ago about airDatePicker would be working (I should test it, though).

Again, many thanks for your help and good luck with the new library

ashbythorpe commented 10 months ago

Thanks @sybrohee,

Since the issue is specific to <select> elements, and airDatePicker elements use custom <div> elements instead of <select> elements, chromote should work fine with airDatePicker elements.

I tested this out with the following code:

library(selenider)
library(callr)

start_app <- function() {
  library(shiny)
  library(shinyWidgets)

  ui <- fluidPage(
    airDatepickerInput("date")
  )

  server <- function(input, output, session) {}

  runApp(shinyApp(ui, server), host = "127.0.0.1", port = 4566L)
}

process <- callr::r_bg(start_app)

Sys.sleep(5) # Wait for the server to start

session <- selenider_session(view = TRUE)

open_url("http://127.0.0.1:4566")

# Focus the input element, opening the Calander.
s("#date") |>
  elem_click()

s(".air-datepicker-nav--title") |>
  elem_click() |> # This moves from days to months
  elem_click() # This moves from months to years

# Select the year 2021
s(".air-datepicker-body.-years-") |>
  find_element("div[data-year='2021']") |>
  elem_click()

# Select the month April
s(".air-datepicker-body.-months-") |>
  find_elements(".air-datepicker-cell") |>
  elem_find(has_text("Apr")) |>
  elem_click()

# Select the 26th day
s(".air-datepicker-body.-days-") |>
  find_element("div[data-date='26']:not(.-other-month-)") |> # Make sure the day is not from another month
  elem_click()

# Stop focusing the input
elem_send_keys(NULL, keys$esc)

# Make sure the value is as expected
execute_js_fn("x => x.value", s("#date"))
#> [1] "2021-04-26"

process$finalize()
process$kill()

This selects a date using only clicks.

sybrohee commented 10 months ago

Hello @ashbythorpe ,

With this little piece of code ... you made my day!

Long live Selenider :-) ... chromote was really difficult to understand (to me) as soon as the actions we wanted to achieve were not straightforward and with Selenider you really made evertything easier.