├── .Rbuildignore ├── .github ├── .gitignore └── workflows │ ├── R-CMD-check-selenium.yaml │ ├── R-CMD-check.yaml │ ├── pkgdown.yaml │ ├── pr-commands.yaml │ └── test-coverage.yaml ├── .gitignore ├── DESCRIPTION ├── LICENSE ├── LICENSE.md ├── NAMESPACE ├── NEWS.md ├── R ├── aaa_env.R ├── actions.R ├── api.R ├── cache.R ├── children.R ├── collections.R ├── conditions.R ├── docs.R ├── elem_filter.R ├── elem_flatten.R ├── empty_selenider_elements.R ├── equal.R ├── eval_conditions.R ├── expect.R ├── expect_all.R ├── find_actual_element.R ├── find_browser.R ├── find_element.R ├── find_elements.R ├── format.R ├── get_actual_element.R ├── global_actions.R ├── import-standalone-obj-type.R ├── import-standalone-types-check.R ├── js.R ├── keys.R ├── lazy_list.R ├── minimal_selenider_session.R ├── pretty_dt.R ├── print.R ├── print_lazy.R ├── properties.R ├── retry_with_timeout.R ├── rvest.R ├── selector.R ├── selenider-package.R ├── selenium.R ├── session-options.R ├── session.R ├── sysdata.rda ├── testthat.R ├── utils-checks.R ├── utils-chromote.R ├── utils-errors.R └── utils.R ├── README.Rmd ├── README.md ├── _pkgdown.yml ├── codecov.yml ├── codemeta.json ├── cran-comments.md ├── data-raw ├── internal.R └── keys.R ├── data └── keys.rda ├── man ├── as.list.selenider_elements.Rd ├── as_pretty_dt.Rd ├── back.Rd ├── chromote_options.Rd ├── close_session.Rd ├── create_chromote_session.Rd ├── current_url.Rd ├── elem_ancestors.Rd ├── elem_attr.Rd ├── elem_cache.Rd ├── elem_click.Rd ├── elem_css_property.Rd ├── elem_equal.Rd ├── elem_expect.Rd ├── elem_expect_all.Rd ├── elem_filter.Rd ├── elem_flatmap.Rd ├── elem_flatten.Rd ├── elem_hover.Rd ├── elem_name.Rd ├── elem_scroll_to.Rd ├── elem_select.Rd ├── elem_set_value.Rd ├── elem_size.Rd ├── elem_submit.Rd ├── elem_text.Rd ├── execute_js_fn.Rd ├── figures │ ├── lifecycle-archived.svg │ ├── lifecycle-defunct.svg │ ├── lifecycle-deprecated.svg │ ├── lifecycle-experimental.svg │ ├── lifecycle-maturing.svg │ ├── lifecycle-questioning.svg │ ├── lifecycle-soft-deprecated.svg │ ├── lifecycle-stable.svg │ └── lifecycle-superseded.svg ├── find_each_element.Rd ├── find_element.Rd ├── find_elements.Rd ├── get_actual_element.Rd ├── get_page_source.Rd ├── get_session.Rd ├── has_attr.Rd ├── has_css_property.Rd ├── has_length.Rd ├── has_name.Rd ├── has_text.Rd ├── is_enabled.Rd ├── is_present.Rd ├── is_visible.Rd ├── keys.Rd ├── minimal_selenider_session.Rd ├── open_url.Rd ├── print.selenider_element.Rd ├── print_lazy.Rd ├── read_html.selenider_session.Rd ├── reload.Rd ├── s.Rd ├── scroll_to.Rd ├── selenider-config.Rd ├── selenider-package.Rd ├── selenider_available.Rd ├── selenider_session.Rd ├── take_screenshot.Rd └── wdman_server_options.Rd ├── selenider.Rproj ├── tests ├── manual │ └── test-session.R ├── testthat.R └── testthat │ ├── _snaps │ ├── actions.md │ ├── empty_selenider_elements.md │ ├── expect.md │ ├── expect_all.md │ ├── keys.md │ ├── print.md │ └── session.md │ ├── helper-devtools.R │ ├── helper-session.R │ ├── setup-disable-crashpad.R │ ├── test-aaa_env.R │ ├── test-actions.R │ ├── test-api.R │ ├── test-cache.R │ ├── test-children.R │ ├── test-collections.R │ ├── test-conditions.R │ ├── test-conditions_multiple.R │ ├── test-elem_filter.R │ ├── test-elem_flatten.R │ ├── test-empty_selenider_elements.R │ ├── test-eval_conditions.R │ ├── test-expect.R │ ├── test-expect_all.R │ ├── test-find_actual_element.R │ ├── test-find_browser.R │ ├── test-find_element.R │ ├── test-find_elements.R │ ├── test-get_actual_element.R │ ├── test-global_actions.R │ ├── test-js.R │ ├── test-keys.R │ ├── test-lazy_list.R │ ├── test-print.R │ ├── test-properties-multiple.R │ ├── test-properties.R │ ├── test-rvest.R │ ├── test-session.R │ ├── test-site.R │ ├── test-utils-checks.R │ ├── test-utils-errors.R │ └── test-utils.R └── vignettes ├── .gitignore ├── advanced-usage.Rmd ├── articles ├── .gitignore ├── jquery-3.7.1.min.js ├── test-site.Rmd ├── test_site.html └── test_site.js ├── selenider.Rmd ├── unit-testing.Rmd └── with-rvest.Rmd /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^selenider\.Rproj$ 2 | ^\.Rproj\.user$ 3 | ^README\.Rmd$ 4 | ^LICENSE\.md$ 5 | ^man/macros/examples[.]Rd$ 6 | ^data-raw$ 7 | ^\.github$ 8 | ^_pkgdown\.yml$ 9 | ^docs$ 10 | ^pkgdown$ 11 | ^vignettes/articles$ 12 | ^codecov\.yml$ 13 | ^doc$ 14 | ^Meta$ 15 | ^codemeta\.json$ 16 | ^cran-comments\.md$ 17 | ^CRAN-SUBMISSION$ 18 | -------------------------------------------------------------------------------- /.github/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | -------------------------------------------------------------------------------- /.github/workflows/R-CMD-check-selenium.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: [main, master] 6 | pull_request: 7 | branches: [main, master] 8 | 9 | name: R-CMD-check-selenium 10 | 11 | jobs: 12 | R-CMD-check: 13 | runs-on: ubuntu-latest 14 | 15 | name: ${{ matrix.browser }} - ${{ matrix.config.os }} (${{ matrix.config.r }}) 16 | 17 | strategy: 18 | fail-fast: false 19 | matrix: 20 | config: 21 | - { os: ubuntu-latest, r: "devel", http-user-agent: "release" } 22 | - { os: ubuntu-latest, r: "release" } 23 | - { os: ubuntu-latest, r: "oldrel-1" } 24 | browser: [chrome, firefox] 25 | 26 | services: 27 | selenium: 28 | image: selenium/standalone-${{ matrix.browser }}:4.15.0-20231122 29 | ports: 30 | - 4444:4444 31 | options: >- 32 | --shm-size="2g" 33 | 34 | env: 35 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 36 | R_KEEP_PKG_SOURCE: yes 37 | SELENIDER_SESSION: selenium 38 | SELENIDER_BROWSER: ${{ matrix.browser }} 39 | SELENIDER_PORT: 4444 40 | SELENIDER_DOCKER: TRUE 41 | 42 | steps: 43 | - uses: actions/checkout@v4 44 | 45 | - uses: r-lib/actions/setup-pandoc@v2 46 | 47 | - uses: r-lib/actions/setup-r@v2 48 | with: 49 | r-version: "release" 50 | use-public-rspm: true 51 | 52 | - uses: r-lib/actions/setup-r-dependencies@v2 53 | with: 54 | extra-packages: any::rcmdcheck 55 | needs: check 56 | 57 | - uses: r-lib/actions/check-r-package@v2 58 | with: 59 | upload-snapshots: true 60 | build_args: 'c("--no-manual","--compact-vignettes=gs+qpdf")' 61 | -------------------------------------------------------------------------------- /.github/workflows/R-CMD-check.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: [main, master] 6 | pull_request: 7 | branches: [main, master] 8 | 9 | name: R-CMD-check 10 | 11 | jobs: 12 | R-CMD-check: 13 | runs-on: ${{ matrix.config.os }} 14 | 15 | name: ${{ matrix.config.os }} (${{ matrix.config.r }}) 16 | 17 | strategy: 18 | fail-fast: false 19 | matrix: 20 | config: 21 | - {os: macos-latest, r: 'release'} 22 | - {os: windows-latest, r: 'release'} 23 | - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'} 24 | - {os: ubuntu-latest, r: 'release'} 25 | - {os: ubuntu-latest, r: 'oldrel-1'} 26 | 27 | env: 28 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 29 | R_KEEP_PKG_SOURCE: yes 30 | 31 | steps: 32 | - uses: actions/checkout@v4 33 | 34 | - uses: r-lib/actions/setup-pandoc@v2 35 | 36 | - uses: r-lib/actions/setup-r@v2 37 | with: 38 | r-version: ${{ matrix.config.r }} 39 | http-user-agent: ${{ matrix.config.http-user-agent }} 40 | use-public-rspm: true 41 | 42 | - uses: r-lib/actions/setup-r-dependencies@v2 43 | with: 44 | extra-packages: any::rcmdcheck 45 | needs: check 46 | 47 | - uses: r-lib/actions/check-r-package@v2 48 | with: 49 | upload-snapshots: true 50 | build_args: 'c("--no-manual","--compact-vignettes=gs+qpdf")' 51 | -------------------------------------------------------------------------------- /.github/workflows/pkgdown.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: [main, master] 6 | pull_request: 7 | branches: [main, master] 8 | release: 9 | types: [published] 10 | workflow_dispatch: 11 | 12 | name: pkgdown 13 | 14 | jobs: 15 | pkgdown: 16 | runs-on: ubuntu-latest 17 | # Only restrict concurrency for non-PR jobs 18 | concurrency: 19 | group: pkgdown-${{ github.event_name != 'pull_request' || github.run_id }} 20 | env: 21 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 22 | permissions: 23 | contents: write 24 | steps: 25 | - uses: actions/checkout@v4 26 | 27 | - uses: r-lib/actions/setup-pandoc@v2 28 | 29 | - uses: r-lib/actions/setup-r@v2 30 | with: 31 | use-public-rspm: true 32 | 33 | - uses: r-lib/actions/setup-r-dependencies@v2 34 | with: 35 | extra-packages: any::pkgdown, local::. 36 | needs: website 37 | 38 | - name: Build site 39 | run: pkgdown::build_site_github_pages(new_process = FALSE, install = FALSE, examples = FALSE) 40 | shell: Rscript {0} 41 | 42 | - name: Deploy to GitHub pages 🚀 43 | if: github.event_name != 'pull_request' 44 | uses: JamesIves/github-pages-deploy-action@v4.5.0 45 | with: 46 | clean: false 47 | branch: gh-pages 48 | folder: docs 49 | -------------------------------------------------------------------------------- /.github/workflows/pr-commands.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | issue_comment: 5 | types: [created] 6 | 7 | name: Commands 8 | 9 | jobs: 10 | document: 11 | if: ${{ github.event.issue.pull_request && (github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'OWNER') && startsWith(github.event.comment.body, '/document') }} 12 | name: document 13 | runs-on: ubuntu-latest 14 | env: 15 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 16 | steps: 17 | - uses: actions/checkout@v4 18 | 19 | - uses: r-lib/actions/pr-fetch@v2 20 | with: 21 | repo-token: ${{ secrets.GITHUB_TOKEN }} 22 | 23 | - uses: r-lib/actions/setup-r@v2 24 | with: 25 | use-public-rspm: true 26 | 27 | - uses: r-lib/actions/setup-r-dependencies@v2 28 | with: 29 | extra-packages: any::roxygen2 30 | needs: pr-document 31 | 32 | - name: Document 33 | run: roxygen2::roxygenise() 34 | shell: Rscript {0} 35 | 36 | - name: commit 37 | run: | 38 | git config --local user.name "$GITHUB_ACTOR" 39 | git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com" 40 | git add man/\* NAMESPACE 41 | git commit -m 'Document' 42 | 43 | - uses: r-lib/actions/pr-push@v2 44 | with: 45 | repo-token: ${{ secrets.GITHUB_TOKEN }} 46 | 47 | style: 48 | if: ${{ github.event.issue.pull_request && (github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'OWNER') && startsWith(github.event.comment.body, '/style') }} 49 | name: style 50 | runs-on: ubuntu-latest 51 | env: 52 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 53 | steps: 54 | - uses: actions/checkout@v4 55 | 56 | - uses: r-lib/actions/pr-fetch@v2 57 | with: 58 | repo-token: ${{ secrets.GITHUB_TOKEN }} 59 | 60 | - uses: r-lib/actions/setup-r@v2 61 | 62 | - name: Install dependencies 63 | run: install.packages("styler") 64 | shell: Rscript {0} 65 | 66 | - name: Style 67 | run: styler::style_pkg() 68 | shell: Rscript {0} 69 | 70 | - name: commit 71 | run: | 72 | git config --local user.name "$GITHUB_ACTOR" 73 | git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com" 74 | git add \*.R 75 | git commit -m 'Style' 76 | 77 | - uses: r-lib/actions/pr-push@v2 78 | with: 79 | repo-token: ${{ secrets.GITHUB_TOKEN }} 80 | -------------------------------------------------------------------------------- /.github/workflows/test-coverage.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: [main, master] 6 | pull_request: 7 | branches: [main, master] 8 | 9 | name: test-coverage 10 | 11 | jobs: 12 | test-coverage: 13 | runs-on: ubuntu-latest 14 | env: 15 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 16 | 17 | steps: 18 | - uses: actions/checkout@v4 19 | 20 | - uses: r-lib/actions/setup-r@v2 21 | with: 22 | use-public-rspm: true 23 | 24 | - uses: r-lib/actions/setup-r-dependencies@v2 25 | with: 26 | extra-packages: any::covr 27 | needs: coverage 28 | 29 | - name: Test coverage 30 | run: | 31 | covr::codecov( 32 | quiet = FALSE, 33 | clean = FALSE, 34 | install_path = file.path(normalizePath(Sys.getenv("RUNNER_TEMP"), winslash = "/"), "package") 35 | ) 36 | shell: Rscript {0} 37 | 38 | - name: Show testthat output 39 | if: always() 40 | run: | 41 | ## -------------------------------------------------------------------- 42 | find '${{ runner.temp }}/package' -name 'testthat.Rout*' -exec cat '{}' \; || true 43 | shell: bash 44 | 45 | - name: Upload test results 46 | if: failure() 47 | uses: actions/upload-artifact@v4 48 | with: 49 | name: coverage-test-failures 50 | path: ${{ runner.temp }}/package 51 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | .Rhistory 3 | .Rdata 4 | .httr-oauth 5 | .DS_Store 6 | /man/macros/examples.Rd 7 | docs 8 | inst/doc 9 | /doc/ 10 | /Meta/ 11 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: selenider 2 | Title: Concise, Lazy and Reliable Wrapper for 'chromote' and 'selenium' 3 | Version: 0.4.1.9000 4 | Authors@R: 5 | person("Ashby", "Thorpe", , "ashbythorpe@gmail.com", role = c("aut", "cre", "cph"), 6 | comment = c(ORCID = "0000-0003-3106-099X")) 7 | Description: A user-friendly wrapper for web automation, using either 8 | 'chromote' or 'selenium'. Provides a simple and consistent API to make 9 | web scraping and testing scripts easy to write and understand. 10 | Elements are lazy, and automatically wait for the website to be valid, 11 | resulting in reliable and reproducible code, with no visible impact on 12 | the experience of the programmer. 13 | License: MIT + file LICENSE 14 | RoxygenNote: 7.3.2 15 | URL: https://github.com/ashbythorpe/selenider, 16 | https://ashbythorpe.github.io/selenider/ 17 | BugReports: https://github.com/ashbythorpe/selenider/issues 18 | Depends: 19 | R (>= 2.10) 20 | Imports: 21 | cli, 22 | coro, 23 | curl, 24 | lifecycle, 25 | prettyunits, 26 | rlang (>= 1.1.0), 27 | utils, 28 | vctrs, 29 | withr 30 | Suggests: 31 | chromote, 32 | jsonlite, 33 | knitr, 34 | purrr, 35 | rmarkdown, 36 | RSelenium, 37 | rvest, 38 | selenium (>= 0.1.3), 39 | shiny, 40 | shinytest2, 41 | showimage, 42 | testthat (>= 3.0.0), 43 | wdman, 44 | xml2 45 | VignetteBuilder: 46 | knitr 47 | Config/Needs/website: rmarkdown 48 | Config/testthat/edition: 3 49 | Encoding: UTF-8 50 | Language: en-GB 51 | LazyData: true 52 | Roxygen: list(markdown = TRUE) 53 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | YEAR: 2023 2 | COPYRIGHT HOLDER: selenider authors 3 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | Copyright (c) 2023 selenider authors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /R/api.R: -------------------------------------------------------------------------------- 1 | #' Select HTML elements 2 | #' 3 | #' @description 4 | #' Both `s()` and `ss()` allow you to select elements without specifying a 5 | #' session object. 6 | #' 7 | #' `s()` selects a single element, being a shorthand for [find_element()] 8 | #' on the current session. 9 | #' 10 | #' `ss()` selects multiple elements, being a shorthand for [find_elements()]. 11 | #' 12 | #' @param css A css selector. 13 | #' @param xpath An XPath. 14 | #' @param id The id of the element you want to select. 15 | #' @param class_name The class name of the element you want to select. 16 | #' @param name The name attribute of the element you want to select. 17 | #' 18 | #' @details 19 | #' Both functions allow the starting point for chains of selectors to be made 20 | #' more concise. Both use [get_session()] to get the global session object. 21 | #' If you want to pass in a session, use [find_element()]/[find_elements()] 22 | #' instead. 23 | #' 24 | #' @returns 25 | #' `s()` returns a `selenider_element` object. 26 | #' `ss()` returns a `selenider_elements` object. Note that this is not a list, 27 | #' and you should be careful with the functions that you use with it. See the 28 | #' advanced usage vignette for more details: 29 | #' `vignette("advanced-usage", package = "selenider")`. 30 | #' 31 | #' @seealso 32 | #' * [find_element()] and [find_elements()] 33 | #' * [selenider_session()] to begin a session. 34 | #' 35 | #' @examplesIf selenider::selenider_available(online = FALSE) 36 | #' html <- " 37 | #'
38 | #'

39 | #'
40 | #'

41 | #'
42 | #'
43 | #' " 44 | #' 45 | #' session <- minimal_selenider_session(html) 46 | #' 47 | #' s("#id1") 48 | #' 49 | #' # This is the equivalent of: 50 | #' find_element(session, "#id1") 51 | #' 52 | #' ss(".inner") 53 | #' 54 | #' # This is the equivalent of: 55 | #' find_element(session, ".inner") 56 | #' 57 | #' # This provides a more concise way to begin a chain of selectors 58 | #' s("div") |> 59 | #' find_element(".child") |> 60 | #' find_element(".inner") 61 | #' 62 | #' @export 63 | s <- function(css = NULL, 64 | xpath = NULL, 65 | id = NULL, 66 | class_name = NULL, 67 | name = NULL) { 68 | session <- get_session(.env = caller_env()) 69 | 70 | find_element(session, css, xpath, id, class_name, name) 71 | } 72 | 73 | #' @rdname s 74 | #' 75 | #' @export 76 | ss <- function(css = NULL, 77 | xpath = NULL, 78 | id = NULL, 79 | class_name = NULL, 80 | name = NULL) { 81 | session <- get_session(.env = caller_env()) 82 | 83 | find_elements(session, css, xpath, id, class_name, name) 84 | } 85 | -------------------------------------------------------------------------------- /R/docs.R: -------------------------------------------------------------------------------- 1 | #' Selenider options 2 | #' 3 | #' @description 4 | #' `selenider` has a few options, allowing you to specify the session and 5 | #' browser to use without having to tell [selenider_session()] this information 6 | #' every time. 7 | #' 8 | #' * `selenider.session` - The package to use as a backend: either "chromote", 9 | #' "selenium" or "rselenium". 10 | #' * `selenider.browser` - The name of the browser to run the session in; one 11 | #' of "chrome", "firefox", "edge", "safari", or another valid browser name. 12 | #' 13 | #' @name selenider-config 14 | NULL 15 | -------------------------------------------------------------------------------- /R/empty_selenider_elements.R: -------------------------------------------------------------------------------- 1 | empty_selenider_elements <- function(driver, driver_id) { 2 | res <- list( 3 | driver = driver, 4 | driver_id = driver_id 5 | ) 6 | 7 | class(res) <- c("empty_selenider_elements", "selenider_elements", "list") 8 | 9 | res 10 | } 11 | 12 | #' @export 13 | format.empty_selenider_elements <- function(x, ...) { 14 | cli::cli_format_method( 15 | cli::cli_text("A collection of selenider elements containing no elements.") 16 | ) 17 | } 18 | 19 | #' @export 20 | print.empty_selenider_elements <- function(x, ...) { 21 | cat(format(x, ...), sep = "\n") 22 | } 23 | 24 | elements_is_empty <- function(x) { 25 | inherits(x, "empty_selenider_elements") 26 | } 27 | -------------------------------------------------------------------------------- /R/equal.R: -------------------------------------------------------------------------------- 1 | #' Are two elements equivalent? 2 | #' 3 | #' Checks if two `selenider_element` objects point to the 4 | #' same element on the page. `elem_equal()` is equivalent to 5 | #' using `==`, but allows you to specify a timeout value if 6 | #' needed. 7 | #' 8 | #' @param x,y,e1,e2 `selenider_element` objects to compare. 9 | #' @param timeout How long to wait for the elements to be present. 10 | #' 11 | #' @returns 12 | #' `TRUE` or `FALSE`. 13 | #' 14 | #' @seealso 15 | #' * [elem_filter()] and [elem_find()] for filtering collection of elements. 16 | #' 17 | #' @examplesIf selenider::selenider_available(online = FALSE) 18 | #' html <- " 19 | #'
20 | #'
21 | #'

22 | #'
23 | #' " 24 | #' 25 | #' session <- minimal_selenider_session(html) 26 | #' 27 | #' s("div") == ss("div")[[1]] 28 | #' 29 | #' has_p_child <- function(x) { 30 | #' x |> 31 | #' elem_children() |> # Direct children 32 | #' elem_filter(has_name("p")) |> 33 | #' has_at_least(1) 34 | #' } 35 | #' 36 | #' ss("div") |> 37 | #' elem_find(has_p_child) |> 38 | #' elem_equal(s(".second")) # TRUE 39 | #' 40 | #' @export 41 | elem_equal <- function(x, y, timeout = NULL) { 42 | check_class(x, "selenider_element") 43 | check_class(y, "selenider_element") 44 | 45 | check_active(x) 46 | 47 | if (x$driver_id != y$driver_id) { 48 | return(FALSE) 49 | } 50 | 51 | timeout <- get_timeout(timeout, x$timeout) 52 | if (!elem_wait_until(is_present(x), is_present(y), timeout = timeout)) { 53 | missing_arg <- if (is_present(x)) "y" else "x" 54 | stop_not_actionable( 55 | c( 56 | "To compare {.arg x} and {.arg y}, both must be present in the DOM.", 57 | paste0( 58 | format_timeout_for_error(timeout), 59 | "{.arg {missing_arg}} was not found." 60 | ) 61 | ) 62 | ) 63 | } 64 | 65 | element_x <- get_element(x) 66 | element_y <- get_element(y) 67 | 68 | if (x$session == "chromote") { 69 | element_x == element_y 70 | } else if (x$session == "selenium") { 71 | selenium_equal(element_x, element_y, driver = x$driver) 72 | } else { 73 | rselenium_equal(element_x, element_y, driver = x$driver) 74 | } 75 | } 76 | 77 | #' @rdname elem_equal 78 | #' 79 | #' @export 80 | `==.selenider_element` <- function(e1, e2) { 81 | elem_equal(e1, e2) 82 | } 83 | 84 | selenium_equal <- function(x, y, driver) { 85 | driver$execute_script("return arguments[0].isSameNode(arguments[1])", x, y) 86 | } 87 | 88 | rselenium_equal <- function(x, y, driver) { 89 | driver$executeScript( 90 | "return arguments[0].isSameNode(arguments[1])", 91 | list(x, y) 92 | )[[1]] 93 | } 94 | -------------------------------------------------------------------------------- /R/find_browser.R: -------------------------------------------------------------------------------- 1 | # Tries to find a browser that we can use 2 | find_browser <- function() { # nolint: cyclocomp_linter 3 | if (is_mac()) { 4 | path <- "/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome" 5 | 6 | if (file.exists(path)) { 7 | return("chrome") 8 | } else { 9 | find_browser_linux() 10 | } 11 | } else if (is_windows()) { 12 | find_browser_windows() 13 | } else if (is_linux()) { 14 | find_browser_linux() 15 | } 16 | } 17 | 18 | find_browser_linux <- function() { 19 | possible_names <- c( 20 | "google-chrome", 21 | "google-chrome-stable", 22 | "chromium-browser", 23 | "chromium", 24 | "google-chrome-beta", 25 | "google-chrome-unstable", 26 | "chrome" 27 | ) 28 | 29 | for (path in possible_names) { 30 | path <- Sys.which(path) 31 | if (nzchar(path)) { 32 | return("chrome") 33 | } 34 | } 35 | 36 | path <- Sys.which("firefox") 37 | 38 | if (nzchar(path)) { 39 | "firefox" 40 | } else { 41 | NULL 42 | } 43 | } 44 | 45 | find_browser_windows <- function() { 46 | path <- tryCatch( 47 | { 48 | path <- utils::readRegistry( 49 | "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\chrome.exe\\" 50 | ) 51 | path[["(Default)"]] 52 | }, 53 | error = function(e) { 54 | NULL 55 | } 56 | ) 57 | 58 | if (!is.null(path)) { 59 | return("chrome") 60 | } 61 | 62 | path <- tryCatch( 63 | { 64 | path <- utils::readRegistry( 65 | "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\firefox.exe\\" 66 | ) 67 | path[["(Default)"]] 68 | }, 69 | error = function(e) { 70 | NULL 71 | } 72 | ) 73 | 74 | if (!is.null(path)) { 75 | return("firefox") 76 | } 77 | 78 | batch_file <- 79 | "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\iexplore.exe\\" 80 | 81 | path <- tryCatch( 82 | { 83 | path <- utils::readRegistry( 84 | batch_file 85 | ) 86 | path[["(Default)"]] 87 | }, 88 | error = function(e) { 89 | NULL 90 | } 91 | ) 92 | 93 | if (!is.null(path)) { 94 | return("internet explorer") 95 | } else { 96 | NULL 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /R/minimal_selenider_session.R: -------------------------------------------------------------------------------- 1 | #' Create a session with custom HTML 2 | #' 3 | #' Create a `selenider_session` using custom HTML/JavaScript. 4 | #' 5 | #' @param html A string to use as HTML. Can also be an `xml2` object. 6 | #' @param js A string (or `NULL`) to use as JavaScript. 7 | #' @param ... Passed into [selenider_session()]. 8 | #' @param .env The environment in which the session will be used. 9 | #' 10 | #' @details 11 | #' The function works by combining `html` and `js` into a single string, then 12 | #' writing this to a temporary file (and opening it in the session's browser). 13 | #' 14 | #' @returns 15 | #' A `selenider_session` object. 16 | #' 17 | #' @seealso [selenider_session()] 18 | #' 19 | #' @examplesIf selenider::selenider_available(online = FALSE) 20 | #' session <- minimal_selenider_session("

Example

") 21 | #' 22 | #' @export 23 | minimal_selenider_session <- function(html, 24 | js = NULL, 25 | ..., 26 | .env = rlang::caller_env()) { 27 | # nocov start 28 | check_string(js, allow_null = TRUE) 29 | if (!is.character(html) || length(html) != 1) { 30 | if (inherits_any(html, c("xml_missing", "xml_node", "xml_nodeset"))) { 31 | html <- paste0(as.character(html), sep = "\n") 32 | } 33 | } 34 | if (!grepl("doctype", tolower(html), fixed = TRUE)) { 35 | html <- paste0( 36 | "\n", 37 | html 38 | ) 39 | } 40 | 41 | if (!is.null(js)) { 42 | html <- paste0( 43 | html, 44 | "" 47 | ) 48 | } 49 | 50 | session <- selenider_session(..., .env = .env) 51 | open_url(paste0("data:text/html,", utils::URLencode(html)), session = session) 52 | session 53 | } # nocov end 54 | -------------------------------------------------------------------------------- /R/pretty_dt.R: -------------------------------------------------------------------------------- 1 | #' Format a difftime 2 | #' 3 | #' Internal method used to print a [selenider_session()] object. Designed to 4 | #' be used with [prettyunits::pretty_dt()], [prettyunits::pretty_ms()] and 5 | #' [prettyunits::pretty_sec()]. 6 | #' 7 | #' @param x A string representing a difftime. 8 | #' @param style,... Not used. 9 | #' 10 | #' @returns 11 | #' An object of class `pretty_dt`. 12 | #' 13 | #' @examples 14 | #' x <- as_pretty_dt(prettyunits::pretty_sec(10)) 15 | #' 16 | #' cli::cli_text("{.val x}") 17 | #' 18 | #' @keywords internal 19 | #' 20 | #' @export 21 | as_pretty_dt <- function(x) { 22 | class(x) <- "pretty_dt" 23 | x 24 | } 25 | 26 | #' @rdname as_pretty_dt 27 | #' 28 | #' @importFrom cli cli_format 29 | #' @export 30 | cli_format.pretty_dt <- function(x, style = NULL, ...) { 31 | x 32 | } 33 | -------------------------------------------------------------------------------- /R/print_lazy.R: -------------------------------------------------------------------------------- 1 | #' Print an element without fetching it 2 | #' 3 | #' @description 4 | #' `r lifecycle::badge("deprecated")` 5 | #' 6 | #' Display a summary of the steps needed to reach an element. This function 7 | #' is deprecated, as it is not useful for most users. 8 | #' 9 | #' @param x A `selenider_element` or `selenider_elements` object. 10 | #' @param ... Not used. 11 | #' 12 | #' @returns `x`, invisibly. 13 | #' 14 | #' @export 15 | print_lazy <- function(x, ...) { 16 | lifecycle::deprecate_warn("0.4.0", "print_lazy()") 17 | 18 | UseMethod("print_lazy") 19 | 20 | invisible(x) 21 | } 22 | 23 | #' @rdname print_lazy 24 | #' 25 | #' @export 26 | print_lazy.selenider_element <- function(x, ...) { 27 | cat(format_lazy_selenider_element(x, ...), sep = "\n") 28 | 29 | invisible(x) 30 | } 31 | 32 | #' @rdname print_lazy 33 | #' 34 | #' @export 35 | print_lazy.selenider_elements <- function(x, ...) { 36 | cat(format_lazy_selenider_elements(x, ...), sep = "\n") 37 | } 38 | 39 | format_lazy_selenider_element <- function(x, ...) { 40 | cli::cli_format_method({ 41 | bullets <- format_lazy_element(x) 42 | cli::cli_text("A selenider element selecting:") 43 | 44 | if (length(bullets[names(bullets) != " "]) == 1) { 45 | cli::cli_text(bullets) 46 | } else { 47 | cli::cli_bullets(bullets) 48 | } 49 | }) 50 | } 51 | 52 | format_lazy_element <- function(x, ...) { 53 | selectors <- x$selectors 54 | 55 | if (length(selectors) == 1) { 56 | res <- format(selectors[[1]], first = TRUE, ...) 57 | replace_names_bullets(res) 58 | } else { 59 | first <- format(selectors[[1]], first = TRUE, ...) 60 | 61 | # Unlist since format can return a character vector of length >1 62 | formatted <- unlist(lapply(selectors[-1], format, ...)) 63 | 64 | c(replace_names_bullets(first), replace_names_bullets(formatted)) 65 | } 66 | } 67 | 68 | format_lazy_selenider_elements <- function(x, ...) { 69 | cli::cli_format_method({ 70 | bullets <- format_lazy_elements(x) 71 | cli::cli_text("A collection of selenider elements selecting:") 72 | 73 | if (length(bullets[names(bullets) != " "]) == 1) { 74 | cli::cli_text(bullets) 75 | } else { 76 | cli::cli_bullets(bullets) 77 | } 78 | }) 79 | } 80 | 81 | format_lazy_elements <- function(x, ...) { 82 | selectors <- x$selectors 83 | 84 | if (length(selectors) == 1) { 85 | res <- format(selectors[[1]], first = TRUE, multiple = TRUE, ...) 86 | replace_names_bullets(res) 87 | } else if (length(selectors) == 2) { 88 | first <- format(selectors[[1]], first = TRUE, ...) 89 | 90 | last <- format(selectors[[2]], multiple = TRUE, ...) 91 | 92 | c(replace_names_bullets(first), replace_names_bullets(last)) 93 | } else { 94 | first <- format(selectors[[1]], first = TRUE, ...) 95 | 96 | last <- format(selectors[[length(selectors)]], multiple = TRUE, ...) 97 | 98 | formatted <- unlist(lapply( 99 | selectors[c(-1, -length(selectors))], 100 | format, ... 101 | )) 102 | 103 | c( 104 | replace_names_bullets(first), 105 | replace_names_bullets(formatted), 106 | replace_names_bullets(last) 107 | ) 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /R/rvest.R: -------------------------------------------------------------------------------- 1 | #' Read a live HTML document 2 | #' 3 | #' @description 4 | #' [xml2::read_html()] can be used on a selenider session to read the HTML of 5 | #' the entire page, or on a selenider element to get the HTML of that element. 6 | #' 7 | #' @param x A `selenider_session`/`selenider_element` object. 8 | #' @param timeout How long to wait for `x` to exist in the DOM before throwing 9 | #' an error. 10 | #' @param outer Whether to read the inner (all children of the current element) 11 | #' or outer (including the element itself) HTML of `x`. 12 | #' @param encoding,...,options Passed into [xml2::read_html()]. 13 | #' 14 | #' @returns 15 | #' `read_html()` returns an XML document. Note that HTML will always be wrapped 16 | #' in a `` and `` tag, if it isn't already. 17 | #' 18 | #' @examplesIf selenider::selenider_available(online = FALSE) 19 | #' library(rvest) 20 | #' 21 | #' html <- " 22 | #'
23 | #'

Example text

24 | #'
25 | #' " 26 | #' 27 | #' session <- minimal_selenider_session(html) 28 | #' 29 | #' read_html(session) 30 | #' read_html(s("div")) 31 | #' 32 | #' @exportS3Method xml2::read_html selenider_session 33 | read_html.selenider_session <- function(x, 34 | encoding = "", 35 | ..., 36 | options = c( 37 | "RECOVER", 38 | "NOERROR", 39 | "NOBLANKS" 40 | )) { 41 | check_session_active(x) 42 | 43 | driver <- x$driver 44 | if (x$session == "chromote") { 45 | document <- driver$DOM$getDocument() 46 | root <- document$root$nodeId 47 | x <- driver$DOM$getOuterHTML(root)$outerHTML 48 | } else if (x$session == "selenium") { 49 | x <- driver$get_page_source() 50 | } else { 51 | x <- unpack_list(driver$getPageSource()) 52 | } 53 | 54 | NextMethod() 55 | } 56 | 57 | #' @rdname read_html.selenider_session 58 | #' 59 | #' @exportS3Method xml2::read_html selenider_element 60 | read_html.selenider_element <- function(x, 61 | encoding = "", 62 | timeout = NULL, 63 | outer = TRUE, 64 | ..., 65 | options = c( 66 | "RECOVER", 67 | "NOERROR", 68 | "NOBLANKS" 69 | )) { 70 | check_active(x) 71 | 72 | check_number_decimal(timeout, allow_null = TRUE) 73 | check_bool(outer) 74 | 75 | timout <- get_timeout(timeout, x$timeout) 76 | 77 | driver <- x$driver 78 | 79 | element <- get_element_for_property( 80 | x, 81 | action = paste0("Read the HTML of {.arg x}"), 82 | timeout = timeout 83 | ) 84 | 85 | if (outer) { 86 | if (x$session == "chromote") { 87 | x <- driver$DOM$getOuterHTML(backendNodeId = element)$outerHTML 88 | } else { 89 | x <- execute_js_fn_on( 90 | "x => x.outerHTML", 91 | element, 92 | session = x$session, 93 | driver = driver 94 | ) 95 | } 96 | } else if (x$session != "rselenium") { 97 | x <- execute_js_fn_on( 98 | "x => x.innerHTML", 99 | element, 100 | session = x$session, 101 | driver = driver 102 | ) 103 | } else { 104 | x <- unpack_list(execute_js_fn_on( 105 | "x => x.innerHTML", 106 | element, 107 | session = x$session, 108 | driver = driver 109 | )) 110 | } 111 | 112 | NextMethod() 113 | } 114 | -------------------------------------------------------------------------------- /R/selenider-package.R: -------------------------------------------------------------------------------- 1 | #' @keywords internal 2 | "_PACKAGE" 3 | 4 | ## usethis namespace: start 5 | #' @import rlang 6 | #' @importFrom lifecycle deprecated 7 | ## usethis namespace: end 8 | NULL 9 | -------------------------------------------------------------------------------- /R/selenium.R: -------------------------------------------------------------------------------- 1 | has_default_selenium_object <- function() { 2 | !is.null(default_selenium_object()) && 3 | default_selenium_object()$is_alive() 4 | } 5 | 6 | default_selenium_object <- function() { 7 | get_from_env("selenium") 8 | } 9 | 10 | set_default_selenium_object <- function(x) { 11 | if (has_default_selenium_object()) { 12 | default_selenium_object()$kill() 13 | } 14 | 15 | set_in_env(selenium = x) 16 | } 17 | 18 | default_selenium_options <- function() { 19 | get_from_env("selenium_options") 20 | } 21 | 22 | set_default_selenium_options <- function(x) { 23 | set_in_env(selenium_options = x) 24 | } 25 | -------------------------------------------------------------------------------- /R/sysdata.rda: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ashbythorpe/selenider/72ee4f916a886d3d1ab901917c1d1e566f22a67b/R/sysdata.rda -------------------------------------------------------------------------------- /R/testthat.R: -------------------------------------------------------------------------------- 1 | #' Create a elem_expect test failure 2 | #' 3 | #' Uses testthat to create a test failure instead of an error if 4 | #' [elem_expect()] fails. 5 | #' 6 | #' @param condition A character vector of error bullets. 7 | #' @param parent The parent error to inherit from, if any. 8 | #' @param call The environment of [elem_expect()], to throw the error in. 9 | #' @param x The element on which the conditions failed. 10 | #' @param x_name The name describing `x`. 11 | #' @param env The environment in which to evaluate the `condition` bullets 12 | #' using glue. 13 | #' 14 | #' @noRd 15 | elem_expect_fail <- function(condition, 16 | parent, 17 | call, 18 | x, 19 | x_name, 20 | env = rlang::caller_env()) { 21 | condition_text <- cli::format_error(condition, .envir = env) 22 | 23 | if (!is.null(parent)) { 24 | # Capture the "Caused by error in fn()" message. 25 | error_container <- try_fetch(abort("", parent = parent), error = identity) 26 | 27 | # Contains a newline at the start 28 | parent_text <- cnd_message(error_container) 29 | 30 | condition_text <- paste0(condition_text, parent_text) 31 | } 32 | 33 | if (!is.null(x)) { 34 | formatted <- if (inherits(x, "selenider_element")) { 35 | format_lazy_selenider_element(x) 36 | } else { 37 | format_lazy_selenider_elements(x) 38 | } 39 | object_text <- paste0( 40 | "Where `", x_name, "` is:\n", 41 | paste(formatted, collapse = "\n"), 42 | "\n" 43 | ) 44 | 45 | condition_text <- paste0(condition_text, "\n\n", object_text, "\n") 46 | } 47 | 48 | testthat::fail(condition_text, trace_env = call) 49 | } 50 | -------------------------------------------------------------------------------- /R/utils-checks.R: -------------------------------------------------------------------------------- 1 | check_class <- function(x, 2 | cls, 3 | ..., 4 | allow_null = FALSE, 5 | arg = rlang::caller_arg(x), 6 | call = rlang::caller_env()) { 7 | if (allow_null) { 8 | if (!is.null(x) && !inherits_any(x, cls)) { 9 | what <- cli::format_inline("a {.cls {cls}} object or `NULL`") 10 | stop_input_type(x, what, ..., arg = arg, call = call) 11 | } 12 | } else { 13 | if (!inherits_any(x, cls)) { 14 | what <- cli::format_inline("a {.cls {cls}} object") 15 | stop_input_type(x, what, ..., arg = arg, call = call) 16 | } 17 | } 18 | } 19 | 20 | is_selenium_server <- function(x) { 21 | inherits(x, "process") || 22 | (is.list(x) && all(c("process", "log", "stop") %in% names(x))) 23 | } 24 | 25 | check_selenium_server <- function(x, call = rlang::caller_env()) { 26 | if (!is_selenium_server(x)) { 27 | cli::cli_abort(c( 28 | "{.code driver$server} must be a valid Selenium server object", 29 | "i" = paste0( 30 | "This can be the result of {.fun selenider::create_selenium_server} ", 31 | "or {.fun wdman::selenium}." 32 | ) 33 | ), class = "selenider_error_invalid_server", call = call) 34 | } 35 | x 36 | } 37 | 38 | is_selenium_client <- function(x) { 39 | inherits_any(x, c("SeleniumSession", "remoteDriver")) 40 | } 41 | 42 | check_selenium_client <- function(x, call = rlang::caller_env()) { 43 | if (!is_selenium_client(x)) { 44 | cli::cli_abort(c( 45 | "{.code driver$client} must be a {.cls SeleniumSession} object", 46 | "i" = paste0( 47 | "This can be the result of {.fun selenider::create_selenium_client} ", 48 | "or {.fun selenium::SeleniumSession$new}." 49 | ) 50 | ), class = "selenider_error_invalid_client", call = call) 51 | } 52 | x 53 | } 54 | 55 | check_vector <- function(x, 56 | check_fun, 57 | ..., 58 | allow_null = FALSE, 59 | arg = rlang::caller_arg(x), 60 | call = rlang::caller_env()) { 61 | if (is.vector(x)) { 62 | good <- TRUE 63 | for (a in x) { 64 | good <- tryCatch( 65 | { 66 | check_fun( 67 | a, 68 | allow_null = allow_null, 69 | ... 70 | ) 71 | TRUE 72 | }, 73 | error = function(e) { 74 | FALSE 75 | } 76 | ) 77 | } 78 | 79 | if (good) { 80 | return(invisible(NULL)) 81 | } 82 | } else if (allow_null && is.null(x)) { 83 | return(invisible(NULL)) 84 | } 85 | 86 | stop_input_type( 87 | x, 88 | "a whole number larger than or equal to 1", 89 | ..., 90 | allow_null = allow_null, 91 | arg = arg, 92 | call = call 93 | ) 94 | } 95 | 96 | check_list <- function(x, 97 | ..., 98 | allow_null = FALSE, 99 | arg = rlang::caller_arg(x), 100 | call = rlang::caller_env()) { 101 | if (is.list(x)) { 102 | return(invisible(NULL)) 103 | } else if (allow_null && is.null(x)) { 104 | return(invisible(NULL)) 105 | } 106 | 107 | stop_input_type( 108 | x, 109 | "a list", 110 | ..., 111 | allow_null = allow_null, 112 | arg = arg, 113 | call = call 114 | ) 115 | } 116 | -------------------------------------------------------------------------------- /_pkgdown.yml: -------------------------------------------------------------------------------- 1 | url: https://ashbythorpe.github.io/selenider/ 2 | template: 3 | bootstrap: 5 4 | 5 | development: 6 | mode: auto 7 | 8 | reference: 9 | - title: Start a session 10 | - contents: 11 | - selenider_session 12 | - chromote_options 13 | - close_session 14 | - get_session 15 | - title: Global Actions 16 | desc: Actions on the entire page 17 | - contents: 18 | - has_concept("global actions") 19 | - title: Selecting elements 20 | - desc: Specifying the paths to HTML elements 21 | - contents: 22 | - s 23 | - find_element 24 | - find_elements 25 | - elem_ancestors 26 | - elem_find 27 | - title: Properties 28 | - contents: 29 | - has_concept("properties") 30 | - elem_equal 31 | - title: Actions 32 | - contents: 33 | - has_concept("actions") 34 | - keys 35 | - title: Expectations 36 | - contents: 37 | - elem_expect 38 | - elem_expect_all 39 | - title: Conditions 40 | - contents: 41 | - has_concept("conditions") 42 | - title: Working with element collections 43 | desc: Combine, modify and iterate over groups of HTML elements 44 | - contents: 45 | - elem_flatten 46 | - as.list.selenider_elements 47 | - find_each_element 48 | - title: Other functions 49 | - contents: 50 | - elem_cache 51 | - read_html.selenider_session 52 | - get_actual_element 53 | - minimal_selenider_session 54 | - selenider_available 55 | - print.selenider_element 56 | - title: Configuration 57 | - contents: 58 | - selenider-config 59 | - title: Superceded and deprecated functions 60 | - contents: 61 | - wdman_server_options 62 | - create_chromote_session 63 | - element_list 64 | - print_lazy 65 | 66 | articles: 67 | - title: Articles 68 | navbar: ~ 69 | contents: 70 | - unit-testing 71 | - with-rvest 72 | - advanced-usage 73 | - title: Testing selenider 74 | desc: An article used to test selenider 75 | contents: 76 | - articles/test-site 77 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | comment: false 2 | 3 | coverage: 4 | status: 5 | project: 6 | default: 7 | target: auto 8 | threshold: 1% 9 | informational: true 10 | patch: 11 | default: 12 | target: auto 13 | threshold: 1% 14 | informational: true 15 | -------------------------------------------------------------------------------- /cran-comments.md: -------------------------------------------------------------------------------- 1 | ## R CMD CHECK results 2 | 3 | 0 errors | 0 warnings | 0 notes 4 | 5 | ## revdepcheck results 6 | 7 | There are currently no downstream dependencies for this package. 8 | -------------------------------------------------------------------------------- /data-raw/keys.R: -------------------------------------------------------------------------------- 1 | ## code to prepare `keys` dataset goes here 2 | key <- function(x) { 3 | class(x) <- "selenider_key" 4 | x 5 | } 6 | 7 | keys <- list( 8 | backspace = key("BACKSPACE"), 9 | tab = key("TAB"), 10 | return = key("RETURN"), 11 | enter = key("ENTER"), 12 | shift = key("SHIFT"), 13 | control = key("CTRL"), 14 | alt = key("ALT"), 15 | escape = key("ESC"), 16 | space = key("SPACE"), 17 | up = key("UP"), 18 | down = key("DOWN"), 19 | left = key("LEFT"), 20 | right = key("RIGHT"), 21 | insert = key("INSERT"), 22 | f1 = key("F1"), 23 | f2 = key("F2"), 24 | f3 = key("F3"), 25 | f4 = key("F4"), 26 | f5 = key("F5"), 27 | f6 = key("F6"), 28 | f7 = key("F7"), 29 | f8 = key("F8"), 30 | f9 = key("F9"), 31 | f10 = key("F10"), 32 | f11 = key("F11"), 33 | f12 = key("F12"), 34 | command = key("COMMAND"), 35 | meta = key("META") 36 | ) 37 | 38 | usethis::use_data(keys, overwrite = TRUE) 39 | -------------------------------------------------------------------------------- /data/keys.rda: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ashbythorpe/selenider/72ee4f916a886d3d1ab901917c1d1e566f22a67b/data/keys.rda -------------------------------------------------------------------------------- /man/as.list.selenider_elements.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/collections.R 3 | \name{as.list.selenider_elements} 4 | \alias{as.list.selenider_elements} 5 | \title{Iterate over an element collection} 6 | \usage{ 7 | \method{as.list}{selenider_elements}(x, timeout = NULL, ...) 8 | } 9 | \arguments{ 10 | \item{x}{A \code{selenider_elements} object.} 11 | 12 | \item{timeout}{How long to wait for \code{x} to exist while computing its length.} 13 | 14 | \item{...}{Not used.} 15 | } 16 | \value{ 17 | A list of \code{selenider_element} objects. 18 | } 19 | \description{ 20 | This function essentially turns \code{x} into: 21 | \code{list(x[[1]], x[[2]], ...)} 22 | However, to do this, the length of \code{x} must be computed. This means that 23 | while each element inside the list is still lazy, the list itself cannot be 24 | considered lazy, since the number of elements in the DOM may change. To 25 | avoid problems, it is recommended to use an element list just after it is 26 | created, to make sure the list is an accurate representation of the DOM 27 | when it is being used. 28 | } 29 | \details{ 30 | Transform a \code{selenider_elements} object into a list of 31 | \code{selenider_element} objects. The result can then be used in for loops and 32 | higher order functions like \code{\link[=lapply]{lapply()}}/\code{\link[purrr:map]{purrr::map()}} (whereas a 33 | \code{selenider_element} object cannot). 34 | } 35 | \examples{ 36 | \dontshow{if (selenider::selenider_available(online = FALSE)) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 37 | html <- " 38 |
39 |

Text 1

40 |
41 |
42 |

Text 2

43 |
44 |
45 |

Text 3

46 |
47 |
48 |

Text 4

49 |
50 | " 51 | 52 | session <- minimal_selenider_session(html) 53 | 54 | p_tags <- ss("p") 55 | 56 | for (elem in as.list(p_tags)) { 57 | print(elem_text(elem)) 58 | } 59 | 60 | p_tags |> 61 | as.list() |> 62 | lapply(elem_text) 63 | \dontshow{\}) # examplesIf} 64 | } 65 | \seealso{ 66 | \itemize{ 67 | \item \code{\link[=elem_flatten]{elem_flatten()}} to combine multiple 68 | \code{selenider_element}/\code{selenider_elements} objects into a single object. 69 | \item \code{\link[=find_each_element]{find_each_element()}} and \code{\link[=find_all_elements]{find_all_elements()}} to select elements 70 | using an element collection while preserving laziness. 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /man/as_pretty_dt.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/pretty_dt.R 3 | \name{as_pretty_dt} 4 | \alias{as_pretty_dt} 5 | \alias{cli_format.pretty_dt} 6 | \title{Format a difftime} 7 | \usage{ 8 | as_pretty_dt(x) 9 | 10 | \method{cli_format}{pretty_dt}(x, style = NULL, ...) 11 | } 12 | \arguments{ 13 | \item{x}{A string representing a difftime.} 14 | 15 | \item{style, ...}{Not used.} 16 | } 17 | \value{ 18 | An object of class \code{pretty_dt}. 19 | } 20 | \description{ 21 | Internal method used to print a \code{\link[=selenider_session]{selenider_session()}} object. Designed to 22 | be used with \code{\link[prettyunits:pretty_dt]{prettyunits::pretty_dt()}}, \code{\link[prettyunits:pretty_ms]{prettyunits::pretty_ms()}} and 23 | \code{\link[prettyunits:pretty_sec]{prettyunits::pretty_sec()}}. 24 | } 25 | \examples{ 26 | x <- as_pretty_dt(prettyunits::pretty_sec(10)) 27 | 28 | cli::cli_text("{.val x}") 29 | 30 | } 31 | \keyword{internal} 32 | -------------------------------------------------------------------------------- /man/back.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/global_actions.R 3 | \name{back} 4 | \alias{back} 5 | \alias{forward} 6 | \title{Move back or forward in browsing history} 7 | \usage{ 8 | back(timeout = NULL, session = NULL) 9 | 10 | forward(timeout = NULL, session = NULL) 11 | } 12 | \arguments{ 13 | \item{timeout}{The maximum time to wait for the page to load, in seconds. 14 | This defaults to 60, unless in a Github Action, in which case it defaults 15 | to 5 minutes.} 16 | 17 | \item{session}{A \code{selenider_session} object. If not specified, the global 18 | session object (the result of \code{\link[=get_session]{get_session()}}) is used.} 19 | } 20 | \value{ 21 | The session object, invisibly. 22 | } 23 | \description{ 24 | \code{back()} navigates to the previously opened URL, or the previously opened 25 | page in your browsing history. 26 | 27 | \code{forward()} reverses the action of \code{back()}, going to the next page in your 28 | browsing history. 29 | } 30 | \examples{ 31 | \dontshow{if (selenider::selenider_available()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 32 | session <- selenider_session() 33 | 34 | open_url("https://r-project.org") 35 | 36 | open_url("https://www.tidyverse.org/") 37 | 38 | back() 39 | 40 | forward() 41 | \dontshow{\}) # examplesIf} 42 | } 43 | \seealso{ 44 | Other global actions: 45 | \code{\link{current_url}()}, 46 | \code{\link{execute_js_fn}()}, 47 | \code{\link{get_page_source}()}, 48 | \code{\link{open_url}()}, 49 | \code{\link{reload}()}, 50 | \code{\link{scroll_to}()}, 51 | \code{\link{take_screenshot}()} 52 | } 53 | \concept{global actions} 54 | -------------------------------------------------------------------------------- /man/chromote_options.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/session-options.R 3 | \name{chromote_options} 4 | \alias{chromote_options} 5 | \alias{selenium_options} 6 | \alias{selenium_server_options} 7 | \alias{selenium_client_options} 8 | \title{Driver options} 9 | \usage{ 10 | chromote_options( 11 | headless = TRUE, 12 | parent = NULL, 13 | width = 992, 14 | height = 1323, 15 | targetId = NULL, 16 | wait_ = TRUE, 17 | auto_events = NULL 18 | ) 19 | 20 | selenium_options( 21 | client_options = selenium_client_options(), 22 | server_options = selenium_server_options() 23 | ) 24 | 25 | selenium_server_options( 26 | version = "latest", 27 | port = 4444L, 28 | selenium_manager = NULL, 29 | verbose = FALSE, 30 | temp = TRUE, 31 | path = NULL, 32 | interactive = FALSE, 33 | echo_cmd = FALSE, 34 | extra_args = c() 35 | ) 36 | 37 | selenium_client_options( 38 | port = 4444L, 39 | host = "localhost", 40 | verbose = FALSE, 41 | capabilities = NULL, 42 | request_body = NULL, 43 | timeout = 60 44 | ) 45 | } 46 | \arguments{ 47 | \item{headless}{Whether to run the browser in headless mode, meaning 48 | that you won't actually be able to see the browser as you control it. 49 | For debugging purposes and interactive use, it is often useful to set 50 | this to \code{FALSE}.} 51 | 52 | \item{parent}{The parent chromote session.} 53 | 54 | \item{width, height, targetId, wait_, auto_events}{Passed into 55 | \link[chromote:ChromoteSession]{chromote::ChromoteSession$new()}.} 56 | 57 | \item{client_options}{A \code{\link[=selenium_client_options]{selenium_client_options()}} object.} 58 | 59 | \item{server_options}{A \code{\link[=selenium_server_options]{selenium_server_options()}} object, or \code{NULL} if you 60 | don't want one to be created.} 61 | 62 | \item{version}{The version of Selenium server to use.} 63 | 64 | \item{port}{The port number to use.} 65 | 66 | \item{selenium_manager, verbose, temp, path, interactive, echo_cmd, extra_args}{Passed into \code{\link[selenium:selenium_server]{selenium::selenium_server()}}.} 67 | 68 | \item{host, capabilities, request_body, timeout}{Passed into \link[selenium:SeleniumSession]{selenium::SeleniumSession$new()}.} 69 | } 70 | \description{ 71 | \code{chromote_options()} and \code{selenium_options()} return a list of options that 72 | can be passed to the \code{options} argument of \code{selenider_session()}. 73 | 74 | \code{chromote_options()} allows you to control the creation of a chromote driver 75 | created using \link[chromote:ChromoteSession]{chromote::ChromoteSession$new()}. 76 | 77 | \code{selenium_options()} allows you to control the creation of a selenium driver. 78 | 79 | \code{selenium_server_options()} should be passed to the \code{server_options} 80 | argument of \code{selenium_options()}, allowing you to control the creation of 81 | the server using \code{\link[selenium:selenium_server]{selenium::selenium_server()}}. 82 | 83 | \code{selenium_client_options()} should be passed to the \code{client_options} argument 84 | of \code{selenium_options()}, allowing you to control the creation of a Selenium 85 | client created using 86 | \link[selenium:SeleniumSession]{selenium::SeleniumSession$new()}. 87 | } 88 | -------------------------------------------------------------------------------- /man/close_session.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/session.R 3 | \name{close_session} 4 | \alias{close_session} 5 | \title{Close a session object} 6 | \usage{ 7 | close_session(x = NULL) 8 | } 9 | \arguments{ 10 | \item{x}{A \code{selenider_session} object. If omitted, the local session object 11 | will be closed.} 12 | } 13 | \value{ 14 | Nothing. 15 | } 16 | \description{ 17 | Shut down a session object, closing the browser and stopping the server. 18 | This will be done automatically if the session is set as the local session 19 | (which happens by default). 20 | } 21 | \examples{ 22 | \dontshow{if (selenider::selenider_available()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 23 | session <- selenider_session(local = FALSE) 24 | 25 | close_session(session) 26 | \dontshow{\}) # examplesIf} 27 | } 28 | \seealso{ 29 | \code{\link[=selenider_session]{selenider_session()}} 30 | } 31 | -------------------------------------------------------------------------------- /man/create_chromote_session.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/session.R 3 | \name{create_chromote_session} 4 | \alias{create_chromote_session} 5 | \alias{create_selenium_server} 6 | \alias{create_selenium_client} 7 | \alias{create_rselenium_client} 8 | \title{Deprecated functions} 9 | \usage{ 10 | create_chromote_session(parent = NULL, ...) 11 | 12 | create_selenium_server( 13 | browser, 14 | version = "latest", 15 | driver_version = "latest", 16 | port = 4444L, 17 | quiet = TRUE, 18 | selenium_manager = TRUE, 19 | ... 20 | ) 21 | 22 | create_selenium_client(browser, port = 4444L, host = "localhost", ...) 23 | 24 | create_rselenium_client(browser, port = 4444L, ...) 25 | } 26 | \arguments{ 27 | \item{parent, ..., version, driver_version, port, quiet, host}{See the 28 | documentation for \code{\link[=chromote_options]{chromote_options()}}, \code{\link[=selenium_options]{selenium_options()}}, 29 | \code{\link[=selenium_client_options]{selenium_client_options()}}, \code{\link[=wdman_server_options]{wdman_server_options()}}, 30 | \code{\link[=selenium_client_options]{selenium_client_options()}} and \code{\link[=rselenium_client_options]{rselenium_client_options()}} for details 31 | about what these arguments mean.} 32 | 33 | \item{browser}{The browser to use.} 34 | 35 | \item{selenium_manager}{If this is \code{FALSE}, \code{\link[wdman:selenium]{wdman::selenium()}} will be used 36 | instead of \code{\link[selenium:selenium_server]{selenium::selenium_server()}}. The equivalent of using 37 | \code{\link[=wdman_server_options]{wdman_server_options()}} over \code{\link[=selenium_server_options]{selenium_server_options()}} in 38 | \code{\link[=selenium_options]{selenium_options()}}.} 39 | } 40 | \value{ 41 | \code{create_chromote_session()} returns a \link[chromote:ChromoteSession]{chromote::ChromoteSession} object. 42 | 43 | \code{create_selenium_server()} returns a \link[processx:process]{processx::process} or wdman 44 | equivalent. 45 | 46 | \code{create_selenium_client()} returns a \link[selenium:SeleniumSession]{selenium::SeleniumSession} object. 47 | 48 | \code{create_rselenium_client()} returns an \link[RSelenium:remoteDriver-class]{RSelenium::remoteDriver} object. 49 | } 50 | \description{ 51 | \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} 52 | 53 | These functions are deprecated and will be removed in a future release. 54 | Use the \code{options} argument to \code{\link[=selenider_session]{selenider_session()}} instead. If you want 55 | to manually create a chromote or selenium session, use 56 | \link[chromote:ChromoteSession]{chromote::ChromoteSession}, \link[selenium:SeleniumSession]{selenium::SeleniumSession} and 57 | \code{\link[selenium:selenium_server]{selenium::selenium_server()}} manually, since these functions 58 | are only a thin wrapper around them. 59 | } 60 | -------------------------------------------------------------------------------- /man/current_url.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/global_actions.R 3 | \name{current_url} 4 | \alias{current_url} 5 | \title{Get the URL of the current page} 6 | \usage{ 7 | current_url(session = NULL) 8 | } 9 | \arguments{ 10 | \item{session}{Optionally, a \code{selenider_session} object.} 11 | } 12 | \value{ 13 | A string: the current URL. 14 | } 15 | \description{ 16 | Get the full URL of the current page. 17 | } 18 | \examples{ 19 | \dontshow{if (selenider::selenider_available()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 20 | session <- selenider_session() 21 | 22 | open_url("https://r-project.org") 23 | 24 | current_url() 25 | \dontshow{\}) # examplesIf} 26 | } 27 | \seealso{ 28 | Other global actions: 29 | \code{\link{back}()}, 30 | \code{\link{execute_js_fn}()}, 31 | \code{\link{get_page_source}()}, 32 | \code{\link{open_url}()}, 33 | \code{\link{reload}()}, 34 | \code{\link{scroll_to}()}, 35 | \code{\link{take_screenshot}()} 36 | } 37 | \concept{global actions} 38 | -------------------------------------------------------------------------------- /man/elem_ancestors.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/children.R 3 | \name{elem_ancestors} 4 | \alias{elem_ancestors} 5 | \alias{elem_parent} 6 | \alias{elem_siblings} 7 | \alias{elem_children} 8 | \alias{elem_descendants} 9 | \title{Get the DOM family of an element} 10 | \usage{ 11 | elem_ancestors(x) 12 | 13 | elem_parent(x) 14 | 15 | elem_siblings(x) 16 | 17 | elem_children(x) 18 | 19 | elem_descendants(x) 20 | } 21 | \arguments{ 22 | \item{x}{A \code{selenider_element} object.} 23 | } 24 | \value{ 25 | All functions return a \code{selenider_elements} object, except 26 | \code{elem_parent()}, which returns a \code{selenider_element} object (since an 27 | element can only have one parent). 28 | } 29 | \description{ 30 | Find all elements with a certain relative position to an HTML element. 31 | 32 | \code{elem_ancestors()} selects every element which contains the current element 33 | (children, grand-children, etc.). 34 | 35 | \code{elem_parent()} selects the element that contains the current element. 36 | 37 | \code{elem_siblings()} selects every element which has the same parent as the 38 | current element. 39 | 40 | \code{elem_children()} selects every element which is connected to and directly 41 | below the current element. 42 | 43 | \code{elem_descendants()} selects every element that is contained by the current 44 | element. The current element does not have to be a direct parent, but must 45 | be some type of ancestor. 46 | } 47 | \details{ 48 | All functions except \code{elem_children()} and \code{elem_descendants()} use XPath 49 | selectors, so may be slow, especially when using \code{chromote} as a backend. 50 | } 51 | \examples{ 52 | \dontshow{if (selenider::selenider_available(online = FALSE)) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 53 | html <- " 54 | 55 | 56 |
57 |
58 |

59 |
60 |

61 |
62 |
63 |
64 |
65 |

66 |
67 | 68 | 69 | " 70 | 71 | session <- minimal_selenider_session(html) 72 | 73 | current <- s("#current") 74 | 75 | # Get all the names of an element collection 76 | elem_names <- function(x) { 77 | x |> 78 | as.list() |> 79 | vapply(elem_name, FUN.VALUE = character(1)) 80 | } 81 | 82 | current |> 83 | elem_ancestors() |> 84 | elem_expect(has_length(3)) |> 85 | elem_names() # html, div, body 86 | 87 | current |> 88 | elem_parent() |> 89 | elem_name() # div 90 | 91 | current |> 92 | elem_siblings() |> 93 | elem_expect(has_length(2)) |> 94 | elem_names() # div, p 95 | 96 | current |> 97 | elem_children() |> 98 | elem_expect(has_length(2)) |> 99 | elem_names() # p, div 100 | 101 | current |> 102 | elem_descendants() |> 103 | elem_expect(has_length(4)) |> 104 | elem_names() # p, div, p, br 105 | \dontshow{\}) # examplesIf} 106 | } 107 | \seealso{ 108 | \itemize{ 109 | \item \url{http://web.simmons.edu/~grovesd/comm244/notes/week4/document-tree} for a 110 | simple and visual explanation of the document tree. 111 | \item \code{\link[=find_element]{find_element()}} and \code{\link[=find_elements]{find_elements()}} for other ways of selecting 112 | elements. These functions allow you to select ancestors using one or more 113 | conditions (e.g. CSS selectors). 114 | \item \code{\link[=elem_filter]{elem_filter()}} and \code{\link[=elem_find]{elem_find()}} for filtering element collections. 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /man/elem_attr.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/properties.R 3 | \name{elem_attr} 4 | \alias{elem_attr} 5 | \alias{elem_attrs} 6 | \alias{elem_value} 7 | \title{Get attributes of an element} 8 | \usage{ 9 | elem_attr(x, name, default = NULL, timeout = NULL) 10 | 11 | elem_attrs(x, timeout = NULL) 12 | 13 | elem_value(x, ptype = character(), timeout = NULL) 14 | } 15 | \arguments{ 16 | \item{x}{A \code{selenider_element} object.} 17 | 18 | \item{name}{The name of the attribute to get; a string.} 19 | 20 | \item{default}{The default value to use if the attribute does not exist in 21 | the element.} 22 | 23 | \item{timeout}{The time to wait for \code{x} to exist.} 24 | 25 | \item{ptype}{The type to cast the value to. Useful when the value is an 26 | integer or decimal number. By default, the value is returned as a string.} 27 | } 28 | \value{ 29 | \code{elem_attr()} returns a character vector of length 1. \code{elem_attrs()} 30 | returns a named list of strings. The return value of \code{elem_value()} has the 31 | same type as \code{ptype} and length 1. 32 | } 33 | \description{ 34 | Get an attribute of a \code{selenider_element} object. 35 | 36 | \code{elem_attr()} returns a \emph{single} attribute value as a string. 37 | 38 | \code{elem_attrs()} returns a named list containing \emph{every} attribute. 39 | 40 | \code{elem_value()} returns the 'value' attribute. 41 | } 42 | \examples{ 43 | \dontshow{if (selenider::selenider_available(online = FALSE)) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 44 | html <- " 45 | R 46 | 47 | " 48 | 49 | session <- minimal_selenider_session(html) 50 | 51 | s("a") |> 52 | elem_attr("href") 53 | 54 | s("a") |> 55 | elem_attrs() 56 | 57 | s("input[type='number']") |> 58 | elem_value(ptype = integer()) 59 | \dontshow{\}) # examplesIf} 60 | } 61 | \seealso{ 62 | Other properties: 63 | \code{\link{elem_css_property}()}, 64 | \code{\link{elem_name}()}, 65 | \code{\link{elem_size}()}, 66 | \code{\link{elem_text}()} 67 | } 68 | \concept{properties} 69 | -------------------------------------------------------------------------------- /man/elem_cache.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/cache.R 3 | \name{elem_cache} 4 | \alias{elem_cache} 5 | \title{Force an element to be collected and stored} 6 | \usage{ 7 | elem_cache(x, timeout = NULL) 8 | } 9 | \arguments{ 10 | \item{x}{A \code{selenider_element}/\code{selenider_elements} object.} 11 | 12 | \item{timeout}{How long to wait for the element(s) to exist while collecting 13 | them.} 14 | } 15 | \value{ 16 | A modified version of \code{x}. The result of \code{elem_cache()} can be used 17 | as a normal \code{selenider_element}/\code{selenider_elements} object. 18 | } 19 | \description{ 20 | \code{selenider_element}/\code{selenider_elements} objects are generally 21 | \emph{lazy}, meaning they only collect the actual element in the DOM 22 | when absolutely necessary, and forget it immediately after. This 23 | is to avoid situations where the DOM changes after an element 24 | has been collected, resulting in errors and unreliable behaviour. 25 | 26 | \code{elem_cache()} forces an element or collection of elements to be collected 27 | and stored, making it eager rather than lazy. This is useful when you are 28 | operating on the same element multiple times, since only collecting the 29 | element once will improve performance. However, you must be sure that the 30 | element will not change on the page while you are using it. 31 | } 32 | \details{ 33 | These functions do not make selenider elements \emph{permanently} eager. Further 34 | sub-elements will not be cached unless specified. 35 | 36 | For example, consider the following code: 37 | 38 | \if{html}{\out{
}}\preformatted{s(".class1") |> 39 | elem_parent() |> 40 | elem_cache() |> 41 | find_element(".class2") 42 | }\if{html}{\out{
}} 43 | 44 | In this example, the parent of the element with class ".class1" will be 45 | cached, but the child element with class ".class2" will not. 46 | } 47 | \examples{ 48 | \dontshow{if (selenider::selenider_available(online = FALSE)) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 49 | html <- " 50 |
51 |

52 | 53 |
54 | " 55 | 56 | session <- minimal_selenider_session(html) 57 | 58 | # Selecting this button may be slow, since we are using relative XPath 59 | # selectors. 60 | button <- s("#specifictext") |> 61 | elem_siblings() |> 62 | elem_find(has_name("button")) 63 | 64 | # But we need to click the button 10 times! 65 | # Normally, this would involve fetching the button from the DOM 10 times 66 | click_button_10_times <- function(x) { 67 | lapply(1:10, \(unnused) elem_click(x)) 68 | invisible(NULL) 69 | } 70 | 71 | # But with elem_cache(), the button will only be fetched once 72 | cached_button <- elem_cache(button) 73 | 74 | click_button_10_times(cached_button) 75 | 76 | # But the cached button is less reliable if the DOM is changing 77 | execute_js_fn("x => { x.outerHTML = ''; }", button) 78 | 79 | try(elem_click(cached_button, timeout = 0.1)) 80 | 81 | # But the non-cached version works 82 | elem_click(button) 83 | \dontshow{\}) # examplesIf} 84 | } 85 | \seealso{ 86 | \itemize{ 87 | \item \code{\link[=find_element]{find_element()}} and \code{\link[=find_elements]{find_elements()}} to select elements. 88 | \item \code{\link[=as.list.selenider_elements]{as.list.selenider_elements()}}, \code{\link[=find_each_element]{find_each_element()}} and 89 | \code{\link[=find_all_elements]{find_all_elements()}} if you want to iterate over an element collection. 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /man/elem_click.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/actions.R 3 | \name{elem_click} 4 | \alias{elem_click} 5 | \alias{elem_double_click} 6 | \alias{elem_right_click} 7 | \title{Click an element} 8 | \usage{ 9 | elem_click(x, js = FALSE, timeout = NULL) 10 | 11 | elem_double_click(x, js = FALSE, timeout = NULL) 12 | 13 | elem_right_click(x, js = FALSE, timeout = NULL) 14 | } 15 | \arguments{ 16 | \item{x}{A \code{selenider_element} object.} 17 | 18 | \item{js}{Whether to click the element using JavaScript.} 19 | 20 | \item{timeout}{How long to wait for the element to exist.} 21 | } 22 | \value{ 23 | \code{x}, invisibly. 24 | } 25 | \description{ 26 | Clicks on an HTML element, either by simulating a mouse click or by 27 | triggering the element's "click" event. 28 | 29 | \code{elem_click()} left clicks on the element, \code{elem_double_click()} left clicks 30 | on the element two times in a short period of time, while 31 | \code{elem_right_click()} right clicks on an element, opening its context menu. 32 | } 33 | \examples{ 34 | \dontshow{if (selenider::selenider_available(online = FALSE)) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 35 | html <- " 36 | 37 |

Hello!

38 | " 39 | 40 | js <- " 41 | function hidetext() { 42 | document.getElementById('texttohide').style.display = 'none' 43 | } 44 | 45 | function showtext() { 46 | document.getElementById('texttohide').style.display = 'block' 47 | } 48 | " 49 | 50 | session <- minimal_selenider_session(html, js = js) 51 | 52 | elem_expect(s("p"), is_visible) 53 | 54 | s("button") |> 55 | elem_click() 56 | 57 | elem_expect(s("p"), is_invisible) 58 | 59 | s("button") |> 60 | elem_right_click() 61 | 62 | elem_expect(s("p"), is_visible) 63 | \dontshow{\}) # examplesIf} 64 | } 65 | \seealso{ 66 | Other actions: 67 | \code{\link{elem_hover}()}, 68 | \code{\link{elem_scroll_to}()}, 69 | \code{\link{elem_select}()}, 70 | \code{\link{elem_set_value}()}, 71 | \code{\link{elem_submit}()} 72 | } 73 | \concept{actions} 74 | -------------------------------------------------------------------------------- /man/elem_css_property.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/properties.R 3 | \name{elem_css_property} 4 | \alias{elem_css_property} 5 | \title{Get a CSS property of an element} 6 | \usage{ 7 | elem_css_property(x, name, timeout = NULL) 8 | } 9 | \arguments{ 10 | \item{x}{A \code{selenider_element} object.} 11 | 12 | \item{name}{The name of the CSS property to get.} 13 | 14 | \item{timeout}{The time to wait for \code{x} to exist.} 15 | } 16 | \value{ 17 | A string, or \code{NULL} if the property does not exist. 18 | } 19 | \description{ 20 | Get a CSS property of an element (e.g. \code{"background-color"}). 21 | Specifically, the \emph{computed} style is returned, meaning that, 22 | for example, widths and heights will be returned in pixels, and 23 | colours will be returned as an RGB value. 24 | } 25 | \examples{ 26 | \dontshow{if (selenider::selenider_available(online = FALSE)) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 27 | html <- " 28 |

Text

29 | " 30 | 31 | session <- minimal_selenider_session(html) 32 | 33 | s("p") |> 34 | elem_css_property("visibility") 35 | 36 | s("p") |> 37 | elem_css_property("color") 38 | \dontshow{\}) # examplesIf} 39 | } 40 | \seealso{ 41 | Other properties: 42 | \code{\link{elem_attr}()}, 43 | \code{\link{elem_name}()}, 44 | \code{\link{elem_size}()}, 45 | \code{\link{elem_text}()} 46 | } 47 | \concept{properties} 48 | -------------------------------------------------------------------------------- /man/elem_equal.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/equal.R 3 | \name{elem_equal} 4 | \alias{elem_equal} 5 | \alias{==.selenider_element} 6 | \title{Are two elements equivalent?} 7 | \usage{ 8 | elem_equal(x, y, timeout = NULL) 9 | 10 | \method{==}{selenider_element}(e1, e2) 11 | } 12 | \arguments{ 13 | \item{x, y, e1, e2}{\code{selenider_element} objects to compare.} 14 | 15 | \item{timeout}{How long to wait for the elements to be present.} 16 | } 17 | \value{ 18 | \code{TRUE} or \code{FALSE}. 19 | } 20 | \description{ 21 | Checks if two \code{selenider_element} objects point to the 22 | same element on the page. \code{elem_equal()} is equivalent to 23 | using \code{==}, but allows you to specify a timeout value if 24 | needed. 25 | } 26 | \examples{ 27 | \dontshow{if (selenider::selenider_available(online = FALSE)) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 28 | html <- " 29 |
30 |
31 |

32 |
33 | " 34 | 35 | session <- minimal_selenider_session(html) 36 | 37 | s("div") == ss("div")[[1]] 38 | 39 | has_p_child <- function(x) { 40 | x |> 41 | elem_children() |> # Direct children 42 | elem_filter(has_name("p")) |> 43 | has_at_least(1) 44 | } 45 | 46 | ss("div") |> 47 | elem_find(has_p_child) |> 48 | elem_equal(s(".second")) # TRUE 49 | \dontshow{\}) # examplesIf} 50 | } 51 | \seealso{ 52 | \itemize{ 53 | \item \code{\link[=elem_filter]{elem_filter()}} and \code{\link[=elem_find]{elem_find()}} for filtering collection of elements. 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /man/elem_expect_all.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/expect_all.R 3 | \name{elem_expect_all} 4 | \alias{elem_expect_all} 5 | \alias{elem_wait_until_all} 6 | \title{Test conditions on multiple elements} 7 | \usage{ 8 | elem_expect_all(x, ..., testthat = NULL, timeout = NULL) 9 | 10 | elem_wait_until_all(x, ..., timeout = NULL) 11 | } 12 | \arguments{ 13 | \item{x}{A \code{selenider_elements()} object.} 14 | 15 | \item{...}{<\code{\link[rlang:dyn-dots]{dynamic-dots}}> Function calls or functions 16 | that must return a logical value. If multiple conditions are given, they 17 | must all be \code{TRUE} for the test to pass. See \code{\link[=elem_expect]{elem_expect()}} for more 18 | details.} 19 | 20 | \item{testthat}{Whether to treat the expectation as a \code{testthat} test. You 21 | \emph{do not} need to explicitly provide this most of the time, since by 22 | default, we can use \code{\link[testthat:is_testing]{testthat::is_testing()}} to figure out whether 23 | \code{elem_expect()} is being called from within a \code{testthat} test.} 24 | 25 | \item{timeout}{The number of seconds to wait for a condition to pass. If not 26 | specified, the timeout used for \code{x} will be used, or the timeout of the 27 | local session if an element is not given.} 28 | } 29 | \value{ 30 | \code{elem_expect_all()} returns \code{x}, invisibly. 31 | 32 | \code{elem_wait_until_all()} returns a boolean flag: TRUE if the test passes, 33 | FALSE otherwise. 34 | } 35 | \description{ 36 | \code{elem_expect_all()} and \code{elem_wait_until_all()} are complements to 37 | \code{\link[=elem_expect]{elem_expect()}} and \code{\link[=elem_wait_until]{elem_wait_until()}} that test conditions on 38 | multiple elements in an element collection. 39 | } 40 | \details{ 41 | If \code{x} does not contain any elements, \code{elem_expect_all()} and 42 | \code{elem_wait_until_all()} will succeed. You may want to first verify that 43 | at least one element exists with \code{\link[=has_at_least]{has_at_least()}}. 44 | 45 | \code{elem_expect_all()} and \code{elem_wait_until_all()} can be thought of as 46 | alternatives to the use of \code{all(vapply(FUN.VALUE = logical(1)))} (or 47 | \code{\link[purrr:every]{purrr::every()}}) within \code{\link[=elem_expect]{elem_expect()}} and \code{\link[=elem_wait_until]{elem_wait_until()}}. 48 | 49 | For example, the following two expressions are equivalent (where \code{x} is an 50 | element collection). 51 | 52 | \if{html}{\out{
}}\preformatted{elem_expect( 53 | x, 54 | \\(element) all(vapply(as.list(element), is_present, logical(1))) 55 | ) 56 | elem_expect_all(x, is_present) 57 | }\if{html}{\out{
}} 58 | 59 | However, the second example will give a more detailed error message on 60 | failure. 61 | } 62 | \examples{ 63 | \dontshow{if (selenider::selenider_available(online = FALSE)) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 64 | html <- " 65 |
Content 1
66 |
Content 2
67 | 68 |
Content 4
69 | " 70 | 71 | session <- minimal_selenider_session(html) 72 | 73 | ss("div") |> 74 | elem_expect_all(is_visible, timeout = 0.1) |> 75 | try() 76 | 77 | ss("div")[-3] |> 78 | elem_expect_all(is_visible) 79 | \dontshow{\}) # examplesIf} 80 | } 81 | \seealso{ 82 | \itemize{ 83 | \item \code{\link[=elem_expect]{elem_expect()}} and \code{\link[=elem_wait_until]{elem_wait_until()}}. 84 | \item \code{\link[=is_present]{is_present()}} and other conditions for predicates for HTML elements. 85 | (If you scroll down to the \emph{See also} section, you will find the rest). 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /man/elem_filter.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/elem_filter.R 3 | \name{elem_filter} 4 | \alias{elem_filter} 5 | \alias{elem_find} 6 | \alias{[.selenider_elements} 7 | \alias{[[.selenider_elements} 8 | \title{Extract a subset of HTML elements} 9 | \usage{ 10 | elem_filter(x, ...) 11 | 12 | elem_find(x, ...) 13 | 14 | \method{[}{selenider_elements}(x, i) 15 | 16 | \method{[[}{selenider_elements}(x, i) 17 | } 18 | \arguments{ 19 | \item{x}{A \code{selenider_elements} object.} 20 | 21 | \item{...}{<\code{\link[rlang:dyn-dots]{dynamic-dots}}> Conditions (functions or 22 | function calls) that are used to filter the elements of \code{x}.} 23 | 24 | \item{i}{A number (or for \code{[}, a vector of one or more numbers) used to 25 | select elements by position.} 26 | } 27 | \value{ 28 | \code{elem_filter()} and \code{[} return a \code{selenider_elements} object, since they can 29 | result in multiple elements. 30 | \code{elem_find()} and \code{[[} return a single \code{selenider_element} object. 31 | } 32 | \description{ 33 | Operators to extract a subset of elements, or a single element, from 34 | a selenider element collection. 35 | 36 | \code{elem_filter()} and \code{elem_find()} allow you to use conditions to filter HTML 37 | elements (see \code{\link[=is_present]{is_present()}} and other conditions). \code{elem_find()} returns 38 | the \emph{first} element that satisfies one or more conditions, while 39 | \code{elem_filter()} returns every element that satisfies these conditions. 40 | 41 | \code{[} and \code{[[} with a numeric subscript can be used on an element collection 42 | to filter the elements by position. \code{[} returns a single element at a 43 | specified location, while \code{[[} returns a collection of the elements at more 44 | than one position. 45 | } 46 | \details{ 47 | As with the \code{\link[=find_element]{find_element()}} and \code{\link[=find_elements]{find_elements()}} functions, these 48 | functions are lazy, meaning that the elements are not fetched and filtered 49 | until they are needed. 50 | 51 | Conditions can be functions or function calls (see \code{\link[=elem_expect]{elem_expect()}} for more 52 | details). 53 | } 54 | \examples{ 55 | \dontshow{if (selenider::selenider_available(online = FALSE)) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 56 | html <- " 57 | 58 | 59 |

Text

60 |
61 | " 62 | session <- minimal_selenider_session(html) 63 | 64 | elements <- ss("*") 65 | 66 | # Gives the same result as s() 67 | elements[[1]] 68 | 69 | elements[1:3] 70 | 71 | elements[-2] 72 | 73 | elements |> 74 | elem_filter(is_visible) 75 | 76 | elements |> 77 | elem_find(is_visible) 78 | 79 | # The above is equivalent to: 80 | visible_elems <- elements |> 81 | elem_filter(is_visible) 82 | visible_elems[[1]] 83 | 84 | # In R >= 4.3.0, we can instead do: 85 | # ss(".class1") |> 86 | # elem_filter(is_visible) |> 87 | # _[[1]] 88 | 89 | ss("button") |> 90 | elem_filter(is_enabled) 91 | \dontshow{\}) # examplesIf} 92 | } 93 | \seealso{ 94 | \itemize{ 95 | \item \code{\link[=find_elements]{find_elements()}} and \code{\link[=ss]{ss()}} to get elements to filter. 96 | \item \code{\link[=is_present]{is_present()}} and other conditions for predicates on HTML elements. 97 | (If you scroll down to the \emph{See also} section, you will find the rest). 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /man/elem_flatmap.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/collections.R 3 | \name{elem_flatmap} 4 | \alias{elem_flatmap} 5 | \alias{element_list} 6 | \title{Iterate over an element collection} 7 | \usage{ 8 | elem_flatmap(x, .f, ...) 9 | 10 | element_list(x, timeout = NULL) 11 | } 12 | \arguments{ 13 | \item{x}{A \code{selenider_elements} object.} 14 | 15 | \item{.f}{A function that takes a \code{selenider_element} and returns a 16 | \code{selenider_element} or \code{selenider_elements} object.} 17 | 18 | \item{...}{Passed into \code{.f}.} 19 | 20 | \item{timeout}{How long to wait for \code{x} to exist while computing its length.} 21 | } 22 | \value{ 23 | \code{elem_flatmap()} returns a \code{selenider_elements} object. 24 | 25 | \code{element_list()} returns a list of \code{selenider_element} objects. 26 | } 27 | \description{ 28 | \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} 29 | 30 | \code{elem_flatmap()} previously allowed you to apply a function to each 31 | element in a collection in a lazy manner. This function is now deprecated, 32 | as it did not work in all cases. Use \code{\link[=find_each_element]{find_each_element()}} and 33 | \code{\link[=find_all_elements]{find_all_elements()}} instead for the simple case where you want to 34 | select the children of a collection. 35 | 36 | \code{element_list()} is a deprecated alias for \code{\link[=as.list.selenider_elements]{as.list.selenider_elements()}}. 37 | } 38 | -------------------------------------------------------------------------------- /man/elem_flatten.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/elem_flatten.R 3 | \name{elem_flatten} 4 | \alias{elem_flatten} 5 | \alias{c.selenider_element} 6 | \alias{c.selenider_elements} 7 | \title{Combine multiple HTML elements} 8 | \usage{ 9 | elem_flatten(...) 10 | 11 | \method{c}{selenider_element}(...) 12 | 13 | \method{c}{selenider_elements}(...) 14 | } 15 | \arguments{ 16 | \item{...}{<\code{\link[rlang:dyn-dots]{dynamic-dots}}> \code{selenider_element} or 17 | \code{selenider_elements} objects to be combined, or lists of such objects.} 18 | } 19 | \value{ 20 | A \code{selenider_elements} object. 21 | } 22 | \description{ 23 | Combine a set of \code{selenider_element}/\code{selenider_elements} objects 24 | into a single \code{selenider_elements} object, allowing you to 25 | perform actions on them at once. \code{c()} and \code{elem_flatten()} do the same 26 | thing, but \code{elem_flatten()} works when given a list of 27 | \code{selenider_element}/\code{selenider_elements} objects. 28 | } 29 | \examples{ 30 | \dontshow{if (selenider::selenider_available(online = FALSE)) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 31 | html <- " 32 |
33 |
34 | 35 |
36 | 37 |
38 | " 39 | 40 | session <- minimal_selenider_session(html) 41 | 42 | button_1 <- s("#button1") 43 | button_2 <- s("#button2") 44 | 45 | buttons <- elem_flatten(button_1, button_2) 46 | 47 | buttons |> 48 | elem_expect_all(is_enabled) 49 | 50 | buttons |> 51 | as.list() |> 52 | lapply(elem_click) 53 | 54 | # Doesn't just have to be single elements 55 | first_2_divs <- ss("div")[1:2] 56 | 57 | elem_flatten(first_2_divs, button_2) |> 58 | length() 59 | 60 | # We would like to use multiple css selectors and combine the results 61 | selectors <- c( 62 | "#id1", # Will select 1 element 63 | "button", # Will select 2 elements 64 | "p" # Will select 0 elements 65 | ) 66 | 67 | lapply(selectors, ss) |> 68 | elem_flatten() |> 69 | length() # 3 70 | \dontshow{\}) # examplesIf} 71 | } 72 | \seealso{ 73 | \itemize{ 74 | \item \code{\link[=as.list.selenider_elements]{as.list.selenider_elements()}} to iterate over element collections. 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /man/elem_hover.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/actions.R 3 | \name{elem_hover} 4 | \alias{elem_hover} 5 | \alias{elem_focus} 6 | \title{Hover over an element} 7 | \usage{ 8 | elem_hover(x, js = FALSE, timeout = NULL) 9 | 10 | elem_focus(x, timeout = NULL) 11 | } 12 | \arguments{ 13 | \item{x}{A \code{selenider_element} object.} 14 | 15 | \item{js}{Whether to hover over the element using JavaScript.} 16 | 17 | \item{timeout}{How long to wait for the element to exist.} 18 | } 19 | \value{ 20 | \code{x}, invisibly. 21 | } 22 | \description{ 23 | \code{elem_hover()} moves the mouse over to an HTML element and hovers over it, 24 | without actually clicking or interacting with it. 25 | 26 | \code{elem_focus()} focuses an HTML element. 27 | } 28 | \examples{ 29 | \dontshow{if (selenider::selenider_available(online = FALSE)) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 30 | html <- " 31 | 32 |

33 | " 34 | 35 | js <- " 36 | function settext() { 37 | const element = document.getElementsByClassName('text').item(0); 38 | 39 | element.innerHTML = 'Button hovered!'; 40 | } 41 | " 42 | 43 | session <- minimal_selenider_session(html, js = js) 44 | 45 | elem_expect(s(".text"), has_exact_text("")) 46 | 47 | s("button") |> 48 | elem_hover() 49 | 50 | elem_expect(s(".text"), has_text("Button hovered!")) 51 | 52 | s("button") |> 53 | elem_focus() 54 | \dontshow{\}) # examplesIf} 55 | } 56 | \seealso{ 57 | Other actions: 58 | \code{\link{elem_click}()}, 59 | \code{\link{elem_scroll_to}()}, 60 | \code{\link{elem_select}()}, 61 | \code{\link{elem_set_value}()}, 62 | \code{\link{elem_submit}()} 63 | } 64 | \concept{actions} 65 | -------------------------------------------------------------------------------- /man/elem_name.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/properties.R 3 | \name{elem_name} 4 | \alias{elem_name} 5 | \title{Get the tag name of an element} 6 | \usage{ 7 | elem_name(x, timeout = NULL) 8 | } 9 | \arguments{ 10 | \item{x}{A \code{selenider_element} object.} 11 | 12 | \item{timeout}{The time to wait for \code{x} to exist.} 13 | } 14 | \value{ 15 | A string. 16 | } 17 | \description{ 18 | Get the tag name (e.g. \code{"p"} for a \verb{

} tag) of a \code{selenider_element} 19 | object. 20 | } 21 | \examples{ 22 | \dontshow{if (selenider::selenider_available(online = FALSE)) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 23 | html <- " 24 |

25 | " 26 | session <- minimal_selenider_session(html) 27 | 28 | s(".mydiv") |> 29 | elem_name() 30 | \dontshow{\}) # examplesIf} 31 | } 32 | \seealso{ 33 | Other properties: 34 | \code{\link{elem_attr}()}, 35 | \code{\link{elem_css_property}()}, 36 | \code{\link{elem_size}()}, 37 | \code{\link{elem_text}()} 38 | } 39 | \concept{properties} 40 | -------------------------------------------------------------------------------- /man/elem_scroll_to.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/actions.R 3 | \name{elem_scroll_to} 4 | \alias{elem_scroll_to} 5 | \title{Scroll to an element} 6 | \usage{ 7 | elem_scroll_to(x, js = FALSE, timeout = NULL) 8 | } 9 | \arguments{ 10 | \item{x}{A \code{selenider_element} object.} 11 | 12 | \item{js}{Whether to scroll to the element using JavaScript.} 13 | 14 | \item{timeout}{How long to wait for the element to exist.} 15 | } 16 | \value{ 17 | \code{x}, invisibly. 18 | } 19 | \description{ 20 | Scrolls to an HTML element. 21 | } 22 | \examples{ 23 | \dontshow{if (selenider::selenider_available(online = FALSE)) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 24 | html <- " 25 |
26 | 27 |

Scroll down to find me!

28 | " 29 | 30 | js <- " 31 | function checkScrolled() { 32 | let element = document.getElementsByTagName('p').item(0); 33 | let rect = element.getBoundingClientRect(); 34 | // If paragraph is in view 35 | const height = window.innerHeight || document.documentElement.clientHeight; 36 | if (rect.bottom <= height) { 37 | element.innerText = 'You found me!'; 38 | } 39 | } 40 | " 41 | 42 | session <- minimal_selenider_session(html, js = js) 43 | 44 | s("p") |> 45 | elem_scroll_to() 46 | 47 | s("button") |> 48 | elem_click() 49 | 50 | elem_expect(s("p"), has_text("You found me!")) 51 | \dontshow{\}) # examplesIf} 52 | } 53 | \seealso{ 54 | Other actions: 55 | \code{\link{elem_click}()}, 56 | \code{\link{elem_hover}()}, 57 | \code{\link{elem_select}()}, 58 | \code{\link{elem_set_value}()}, 59 | \code{\link{elem_submit}()} 60 | } 61 | \concept{actions} 62 | -------------------------------------------------------------------------------- /man/elem_select.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/actions.R 3 | \name{elem_select} 4 | \alias{elem_select} 5 | \title{Select an HTML element} 6 | \usage{ 7 | elem_select( 8 | x, 9 | value = NULL, 10 | text = NULL, 11 | index = NULL, 12 | timeout = NULL, 13 | reset_other = TRUE 14 | ) 15 | } 16 | \arguments{ 17 | \item{x}{A \code{selenider_element} object representing a \code{select} or \code{option} 18 | element.} 19 | 20 | \item{value}{If \code{x} is a \code{select} element, the value of the option to 21 | select. Can be a character vector, in which case multiple options will be 22 | selected.} 23 | 24 | \item{text}{The text content of the option to select. This does not have to 25 | be a complete match, and multiple options can be selected.} 26 | 27 | \item{index}{A vector of indexes. The nth option elements will be selected.} 28 | 29 | \item{timeout}{How long to wait for the element to exist.} 30 | 31 | \item{reset_other}{If \code{TRUE} (the default), the other options will be 32 | deselected.} 33 | } 34 | \value{ 35 | \code{x}, invisibly. 36 | } 37 | \description{ 38 | Select or deselect \code{select} and \code{option} elements. 39 | } 40 | \details{ 41 | If no arguments apart from \code{x} are supplied, and \code{x} is a \code{select} element, 42 | all options will be deselected. 43 | } 44 | \examples{ 45 | \dontshow{if (selenider::selenider_available(online = FALSE)) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 46 | html <- " 47 | 52 | " 53 | session <- minimal_selenider_session(html) 54 | 55 | s("select") |> 56 | elem_select("a") 57 | 58 | s("select") |> 59 | elem_select(text = c("Option A.", "Option C.")) 60 | 61 | s("select") |> 62 | elem_select(index = 2, reset_other = FALSE) 63 | 64 | # Reset selection 65 | s("select") |> 66 | elem_select() 67 | 68 | s("select") |> 69 | elem_select("b") 70 | \dontshow{\}) # examplesIf} 71 | } 72 | \seealso{ 73 | Other actions: 74 | \code{\link{elem_click}()}, 75 | \code{\link{elem_hover}()}, 76 | \code{\link{elem_scroll_to}()}, 77 | \code{\link{elem_set_value}()}, 78 | \code{\link{elem_submit}()} 79 | } 80 | \concept{actions} 81 | -------------------------------------------------------------------------------- /man/elem_set_value.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/actions.R 3 | \name{elem_set_value} 4 | \alias{elem_set_value} 5 | \alias{elem_send_keys} 6 | \alias{elem_clear_value} 7 | \title{Set the value of an input} 8 | \usage{ 9 | elem_set_value(x, text, timeout = NULL) 10 | 11 | elem_send_keys(x, ..., modifiers = NULL, timeout = NULL) 12 | 13 | elem_clear_value(x, timeout = NULL) 14 | } 15 | \arguments{ 16 | \item{x}{A \code{selenider_element} object. For \code{\link[=elem_send_keys]{elem_send_keys()}}, this can be 17 | \code{NULL}, meaning that the keys will be sent to the current page (or the 18 | currently focused element) instead of a specific element.} 19 | 20 | \item{text}{A string to set the value of the input element to.} 21 | 22 | \item{timeout}{How long to wait for the element to exist.} 23 | 24 | \item{...}{A set of inputs to send to \code{x}.} 25 | 26 | \item{modifiers}{A character vector; one or more of "shift", 27 | "ctrl"/"control", "alt", and "command"/meta". Note that when using 28 | chromote as a backend, these do not work on Mac OS.} 29 | } 30 | \value{ 31 | \code{x}, invisibly. 32 | } 33 | \description{ 34 | \code{elem_set_value()} sets the value of an HTML input element to a string. 35 | } 36 | \details{ 37 | \code{elem_send_keys()} sends a set of inputs to an element. 38 | 39 | \code{elem_clear_value()} sets the value of an HTML element to \code{""}, removing any 40 | existing content. 41 | } 42 | \examples{ 43 | \dontshow{if (selenider::selenider_available(online = FALSE)) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 44 | html <- " 45 | 50 |

51 | " 52 | 53 | js <- " 54 | function recordChange(e) { 55 | document.getElementsByTagName('p').item(0).innerText = e.target.value; 56 | } 57 | 58 | function checkEnter(e) { 59 | // If the key pressed was Enter 60 | if (e.keyCode == 13) { 61 | document.getElementsByTagName('p').item(0).innerText = 'Enter pressed!'; 62 | return false; 63 | } 64 | return true; 65 | } 66 | " 67 | 68 | session <- minimal_selenider_session(html, js = js) 69 | 70 | elem_expect(s("p"), has_exact_text("")) 71 | 72 | input <- s("input") 73 | 74 | elem_set_value(input, "my text") 75 | 76 | elem_expect(s("p"), has_text("my text")) 77 | 78 | elem_clear_value(input) 79 | 80 | elem_expect(s("p"), has_exact_text("")) 81 | 82 | elem_send_keys(input, keys$enter) 83 | 84 | elem_expect(s("p"), has_text("Enter pressed!")) 85 | \dontshow{\}) # examplesIf} 86 | } 87 | \seealso{ 88 | Other actions: 89 | \code{\link{elem_click}()}, 90 | \code{\link{elem_hover}()}, 91 | \code{\link{elem_scroll_to}()}, 92 | \code{\link{elem_select}()}, 93 | \code{\link{elem_submit}()} 94 | } 95 | \concept{actions} 96 | -------------------------------------------------------------------------------- /man/elem_size.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/properties.R 3 | \name{elem_size} 4 | \alias{elem_size} 5 | \alias{length.selenider_elements} 6 | \title{Get the number of elements in a collection} 7 | \usage{ 8 | elem_size(x, timeout = NULL) 9 | 10 | \method{length}{selenider_elements}(x) 11 | } 12 | \arguments{ 13 | \item{x}{A \code{selenider_elements} object.} 14 | 15 | \item{timeout}{The time to wait for the parent of \code{x} (if any) to exist.} 16 | } 17 | \value{ 18 | An integer representing the number of elements in the collection. 19 | } 20 | \description{ 21 | Get the number of elements in a HTML element collection, waiting for the 22 | parent elements (if any) to exist before returning a value. 23 | 24 | \code{length()} and \code{elem_size()} can be used interchangeably, the only 25 | difference being that \code{elem_size()} allows you to specify a timeout. 26 | } 27 | \examples{ 28 | \dontshow{if (selenider::selenider_available(online = FALSE)) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 29 | html <- " 30 |
31 |
32 |
33 |
34 | " 35 | session <- minimal_selenider_session(html) 36 | 37 | ss("div") |> 38 | length() 39 | \dontshow{\}) # examplesIf} 40 | } 41 | \seealso{ 42 | Other properties: 43 | \code{\link{elem_attr}()}, 44 | \code{\link{elem_css_property}()}, 45 | \code{\link{elem_name}()}, 46 | \code{\link{elem_text}()} 47 | } 48 | \concept{properties} 49 | -------------------------------------------------------------------------------- /man/elem_submit.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/actions.R 3 | \name{elem_submit} 4 | \alias{elem_submit} 5 | \title{Submit an element} 6 | \usage{ 7 | elem_submit(x, js = FALSE, timeout = NULL) 8 | } 9 | \arguments{ 10 | \item{x}{A \code{selenider_element} object.} 11 | 12 | \item{js}{Whether to submit the form using JavaScript.} 13 | 14 | \item{timeout}{How long to wait for the element to exist.} 15 | } 16 | \value{ 17 | \code{x}, invisibly. 18 | } 19 | \description{ 20 | If an element is an ancestor of a form, submits the form. 21 | Works by walking up the DOM, checking each ancestor element until 22 | the element is a \verb{
} element, which it then submits. If such 23 | an element does not exist, an error is thrown. 24 | } 25 | \examples{ 26 | \dontshow{if (selenider::selenider_available(online = FALSE)) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 27 | html <- " 28 | 29 | 30 |

Random text

31 |
32 | Random link 33 | " 34 | 35 | session <- minimal_selenider_session(html) 36 | 37 | elem_submit(s("input")) 38 | elem_submit(s("p")) 39 | 40 | # Won't work since the element doesn't have a form ancestor 41 | try(elem_submit(s("a"), timeout = 0.5)) 42 | \dontshow{\}) # examplesIf} 43 | } 44 | \seealso{ 45 | Other actions: 46 | \code{\link{elem_click}()}, 47 | \code{\link{elem_hover}()}, 48 | \code{\link{elem_scroll_to}()}, 49 | \code{\link{elem_select}()}, 50 | \code{\link{elem_set_value}()} 51 | } 52 | \concept{actions} 53 | -------------------------------------------------------------------------------- /man/elem_text.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/properties.R 3 | \name{elem_text} 4 | \alias{elem_text} 5 | \title{Get the text inside an element} 6 | \usage{ 7 | elem_text(x, timeout = NULL) 8 | } 9 | \arguments{ 10 | \item{x}{A \code{selenider_element} object.} 11 | 12 | \item{timeout}{The time to wait for \code{x} to exist.} 13 | } 14 | \value{ 15 | A string. 16 | } 17 | \description{ 18 | Get the inner text of a \code{selenider_element} object. 19 | } 20 | \examples{ 21 | \dontshow{if (selenider::selenider_available(online = FALSE)) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 22 | html <- " 23 |

Example text

24 | " 25 | 26 | session <- minimal_selenider_session(html) 27 | 28 | s("p") |> 29 | elem_text() 30 | \dontshow{\}) # examplesIf} 31 | } 32 | \seealso{ 33 | Other properties: 34 | \code{\link{elem_attr}()}, 35 | \code{\link{elem_css_property}()}, 36 | \code{\link{elem_name}()}, 37 | \code{\link{elem_size}()} 38 | } 39 | \concept{properties} 40 | -------------------------------------------------------------------------------- /man/execute_js_fn.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/js.R 3 | \name{execute_js_fn} 4 | \alias{execute_js_fn} 5 | \alias{execute_js_expr} 6 | \title{Execute a JavaScript function} 7 | \usage{ 8 | execute_js_fn(fn, ..., .timeout = NULL, .session = NULL, .debug = FALSE) 9 | 10 | execute_js_expr(expr, ..., .timeout = NULL, .session = NULL, .debug = FALSE) 11 | } 12 | \arguments{ 13 | \item{fn}{A string defining the function.} 14 | 15 | \item{...}{Arguments to the function/expression. These must be unnamed, since 16 | JavaScript does not support named arguments.} 17 | 18 | \item{.timeout}{How long to wait for any elements to exist in the DOM.} 19 | 20 | \item{.session}{The session to use, if \code{...} does not contain any 21 | selenider elements.} 22 | 23 | \item{.debug}{Whether to print the final expression that is executed. Mostly 24 | used for debugging the functions themselves, but can also be used to 25 | identify problems in your own JavaScript code.} 26 | 27 | \item{expr}{An expression to execute.} 28 | } 29 | \value{ 30 | The return value of the JavaScript function, turned back into an R object. 31 | } 32 | \description{ 33 | \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#experimental}{\figure{lifecycle-experimental.svg}{options: alt='[Experimental]'}}}{\strong{[Experimental]}} 34 | 35 | Execute a JavaScript function on zero or more arguments. 36 | 37 | \code{execute_js_expr()} is a simpler version of \code{execute_js_fn()} that can 38 | evaluate simple expressions (e.g. "alert()"). To return a value, you must 39 | do so explicitly using "return". 40 | 41 | These functions are experimental because their names and parameters are 42 | liable to change. Additionally, their behaviour can be inconsistent between 43 | different session types (chromote and selenium) and different browsers. 44 | } 45 | \details{ 46 | \code{...} can contain \code{selenider_element}/\code{selenider_elements} objects, 47 | which will be collected and then passed into the function. However, 48 | more complex objects (e.g. lists of selenider elements) will not be 49 | moved into the JavaScript world correctly. 50 | 51 | Similarly, nodes and lists of nodes returned from a JavaScript function will 52 | be converted into their corresponding 53 | \code{selenider_element}/\code{selenider_elements} objects, while more complex objects 54 | will not. These elements are not lazy (see \code{\link[=elem_cache]{elem_cache()}}), so make sure you 55 | only use them while you are sure they are still on the page. 56 | } 57 | \examples{ 58 | \dontshow{if (selenider::selenider_available(online = FALSE)) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 59 | html <- " 60 | 61 | " 62 | session <- minimal_selenider_session(html) 63 | 64 | execute_js_fn("(x, y) => x + y", 1, 1) 65 | 66 | execute_js_expr("arguments[0] + arguments[1]", 1, 1) 67 | 68 | execute_js_fn("x => x.click()", s(".mybutton")) 69 | 70 | execute_js_expr("arguments[0].click()", s(".mybutton")) 71 | \dontshow{\}) # examplesIf} 72 | } 73 | \seealso{ 74 | Other global actions: 75 | \code{\link{back}()}, 76 | \code{\link{current_url}()}, 77 | \code{\link{get_page_source}()}, 78 | \code{\link{open_url}()}, 79 | \code{\link{reload}()}, 80 | \code{\link{scroll_to}()}, 81 | \code{\link{take_screenshot}()} 82 | } 83 | \concept{global actions} 84 | -------------------------------------------------------------------------------- /man/figures/lifecycle-archived.svg: -------------------------------------------------------------------------------- 1 | 2 | lifecycle: archived 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | lifecycle 18 | 19 | archived 20 | 21 | 22 | -------------------------------------------------------------------------------- /man/figures/lifecycle-defunct.svg: -------------------------------------------------------------------------------- 1 | 2 | lifecycle: defunct 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | lifecycle 18 | 19 | defunct 20 | 21 | 22 | -------------------------------------------------------------------------------- /man/figures/lifecycle-deprecated.svg: -------------------------------------------------------------------------------- 1 | 2 | lifecycle: deprecated 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | lifecycle 18 | 19 | deprecated 20 | 21 | 22 | -------------------------------------------------------------------------------- /man/figures/lifecycle-experimental.svg: -------------------------------------------------------------------------------- 1 | 2 | lifecycle: experimental 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | lifecycle 18 | 19 | experimental 20 | 21 | 22 | -------------------------------------------------------------------------------- /man/figures/lifecycle-maturing.svg: -------------------------------------------------------------------------------- 1 | 2 | lifecycle: maturing 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | lifecycle 18 | 19 | maturing 20 | 21 | 22 | -------------------------------------------------------------------------------- /man/figures/lifecycle-questioning.svg: -------------------------------------------------------------------------------- 1 | 2 | lifecycle: questioning 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | lifecycle 18 | 19 | questioning 20 | 21 | 22 | -------------------------------------------------------------------------------- /man/figures/lifecycle-soft-deprecated.svg: -------------------------------------------------------------------------------- 1 | 2 | lifecycle: soft-deprecated 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | lifecycle 18 | 19 | soft-deprecated 20 | 21 | 22 | -------------------------------------------------------------------------------- /man/figures/lifecycle-stable.svg: -------------------------------------------------------------------------------- 1 | 2 | lifecycle: stable 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 19 | 20 | lifecycle 21 | 22 | 25 | 26 | stable 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /man/figures/lifecycle-superseded.svg: -------------------------------------------------------------------------------- 1 | 2 | lifecycle: superseded 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | lifecycle 18 | 19 | superseded 20 | 21 | 22 | -------------------------------------------------------------------------------- /man/find_each_element.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/collections.R 3 | \name{find_each_element} 4 | \alias{find_each_element} 5 | \alias{find_all_elements} 6 | \title{Find HTML children from a collection} 7 | \usage{ 8 | find_each_element( 9 | x, 10 | css = NULL, 11 | xpath = NULL, 12 | id = NULL, 13 | class_name = NULL, 14 | name = NULL 15 | ) 16 | 17 | find_all_elements( 18 | x, 19 | css = NULL, 20 | xpath = NULL, 21 | id = NULL, 22 | class_name = NULL, 23 | name = NULL 24 | ) 25 | } 26 | \arguments{ 27 | \item{x}{A \code{selenider_elements} object.} 28 | 29 | \item{css}{A CSS selector.} 30 | 31 | \item{xpath}{An XPath.} 32 | 33 | \item{id}{The id of the elements you want to select.} 34 | 35 | \item{class_name}{The class name of the elements you want to select.} 36 | 37 | \item{name}{The name attribute of the elements you want to select.} 38 | } 39 | \value{ 40 | A \code{selenider_elements} object. 41 | } 42 | \description{ 43 | Find HTML child elements from elements in a collection. Provides 44 | a convenient way to operate on a collection of elements. 45 | 46 | \code{find_each_element()} finds the first child element of each element in 47 | the collection. 48 | 49 | \code{find_all_elements()} finds every child element of every element in the 50 | collection. 51 | } 52 | \details{ 53 | \code{find_each_element()} will usually preserve the length of the input, since 54 | for each element in the collection, one new element will be found. However, 55 | if an element in the collection cannot be found, it will not be included in 56 | the resulting collection. 57 | 58 | \code{find_each_element(x, ...)} is roughly equivalent to: 59 | 60 | \if{html}{\out{
}}\preformatted{x |> 61 | as.list() |> 62 | lapply(\\(x) find_element(x, ...)) |> 63 | elem_flatten() 64 | }\if{html}{\out{
}} 65 | 66 | Similarly, \code{find_all_elements(x, ...)} is roughly equivalent to: 67 | 68 | \if{html}{\out{
}}\preformatted{x |> 69 | as.list() |> 70 | lapply(\\(x) find_elements(x, ...)) |> 71 | elem_flatten() 72 | }\if{html}{\out{
}} 73 | } 74 | \examples{ 75 | \dontshow{if (selenider::selenider_available(online = FALSE)) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 76 | html <- " 77 |
78 |

Text 1

79 | 80 |
81 |
82 |

Text 2

83 |
84 |
85 |

Text 3

86 |
87 |
88 |

Text 4

89 |
90 | " 91 | 92 | session <- minimal_selenider_session(html) 93 | 94 | divs <- ss("div") 95 | 96 | # Get the

tag inside each div. 97 | divs |> 98 | find_each_element("p") 99 | 100 | 101 | # Get the 32 | 33 | " 34 | 35 | session <- minimal_selenider_session(html) 36 | 37 | is_enabled(s("button")) # TRUE 38 | 39 | is_disabled(ss("button")[[2]]) # TRUE 40 | \dontshow{\}) # examplesIf} 41 | } 42 | \seealso{ 43 | Other conditions: 44 | \code{\link{has_attr}()}, 45 | \code{\link{has_css_property}()}, 46 | \code{\link{has_length}()}, 47 | \code{\link{has_name}()}, 48 | \code{\link{has_text}()}, 49 | \code{\link{is_present}()}, 50 | \code{\link{is_visible}()} 51 | } 52 | \concept{conditions} 53 | -------------------------------------------------------------------------------- /man/is_present.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/conditions.R 3 | \name{is_present} 4 | \alias{is_present} 5 | \alias{is_in_dom} 6 | \alias{is_absent} 7 | \title{Does an element exist?} 8 | \usage{ 9 | is_present(x) 10 | 11 | is_in_dom(x) 12 | 13 | is_absent(x) 14 | } 15 | \arguments{ 16 | \item{x}{A \code{selenider_element} object.} 17 | } 18 | \value{ 19 | A boolean value: TRUE or FALSE. 20 | } 21 | \description{ 22 | \code{is_present()} and \code{is_in_dom()} checks if an element is present on the page, 23 | while \code{is_missing()} and \code{is_absent()} checks the opposite. 24 | } 25 | \details{ 26 | These functions do not implement a retry mechanism, and only test a condition 27 | once. Use \code{\link[=elem_expect]{elem_expect()}} or \code{\link[=elem_wait_until]{elem_wait_until()}} to use these conditions in 28 | tests. 29 | } 30 | \examples{ 31 | \dontshow{if (selenider::selenider_available(online = FALSE)) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 32 | html <- " 33 |

34 | " 35 | 36 | session <- minimal_selenider_session(html) 37 | 38 | is_present(s(".class1")) # TRUE 39 | 40 | is_in_dom(s(".class2")) # FALSE 41 | 42 | is_absent(s(".class2")) # TRUE 43 | \dontshow{\}) # examplesIf} 44 | } 45 | \seealso{ 46 | Other conditions: 47 | \code{\link{has_attr}()}, 48 | \code{\link{has_css_property}()}, 49 | \code{\link{has_length}()}, 50 | \code{\link{has_name}()}, 51 | \code{\link{has_text}()}, 52 | \code{\link{is_enabled}()}, 53 | \code{\link{is_visible}()} 54 | } 55 | \concept{conditions} 56 | -------------------------------------------------------------------------------- /man/is_visible.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/conditions.R 3 | \name{is_visible} 4 | \alias{is_visible} 5 | \alias{is_displayed} 6 | \alias{is_hidden} 7 | \alias{is_invisible} 8 | \title{Is an element visible?} 9 | \usage{ 10 | is_visible(x) 11 | 12 | is_displayed(x) 13 | 14 | is_hidden(x) 15 | 16 | is_invisible(x) 17 | } 18 | \arguments{ 19 | \item{x}{A \code{selenider_element} object.} 20 | } 21 | \value{ 22 | A boolean value: TRUE or FALSE. 23 | } 24 | \description{ 25 | \code{is_visible()} and \code{is_displayed()} checks that an element can be seen on the 26 | page, while \code{is_invisible()} and \code{is_hidden()} checks the opposite. All 27 | functions throw an error if the element is not in the DOM. 28 | } 29 | \details{ 30 | These functions do not implement a retry mechanism, and only test a condition 31 | once. Use \code{\link[=elem_expect]{elem_expect()}} or \code{\link[=elem_wait_until]{elem_wait_until()}} to use these conditions in 32 | tests. 33 | } 34 | \examples{ 35 | \dontshow{if (selenider::selenider_available(online = FALSE)) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 36 | html <- " 37 |
Content 1
38 |
Content 2
39 |
Content 3
40 | " 41 | 42 | session <- minimal_selenider_session(html) 43 | 44 | is_visible(s("div")) # FALSE 45 | 46 | is_invisible(ss("div")[[2]]) # TRUE 47 | 48 | is_visible(ss("div")[[3]]) # TRUE 49 | \dontshow{\}) # examplesIf} 50 | } 51 | \seealso{ 52 | Other conditions: 53 | \code{\link{has_attr}()}, 54 | \code{\link{has_css_property}()}, 55 | \code{\link{has_length}()}, 56 | \code{\link{has_name}()}, 57 | \code{\link{has_text}()}, 58 | \code{\link{is_enabled}()}, 59 | \code{\link{is_present}()} 60 | } 61 | \concept{conditions} 62 | -------------------------------------------------------------------------------- /man/keys.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/keys.R 3 | \docType{data} 4 | \name{keys} 5 | \alias{keys} 6 | \title{Special keys} 7 | \format{ 8 | A list containing \code{selenider_key} objects. 9 | } 10 | \usage{ 11 | keys 12 | } 13 | \description{ 14 | List of special keys, for use with \code{\link[=elem_send_keys]{elem_send_keys()}}. 15 | } 16 | \examples{ 17 | keys$backspace 18 | } 19 | \keyword{datasets} 20 | -------------------------------------------------------------------------------- /man/minimal_selenider_session.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/minimal_selenider_session.R 3 | \name{minimal_selenider_session} 4 | \alias{minimal_selenider_session} 5 | \title{Create a session with custom HTML} 6 | \usage{ 7 | minimal_selenider_session(html, js = NULL, ..., .env = rlang::caller_env()) 8 | } 9 | \arguments{ 10 | \item{html}{A string to use as HTML. Can also be an \code{xml2} object.} 11 | 12 | \item{js}{A string (or \code{NULL}) to use as JavaScript.} 13 | 14 | \item{...}{Passed into \code{\link[=selenider_session]{selenider_session()}}.} 15 | 16 | \item{.env}{The environment in which the session will be used.} 17 | } 18 | \value{ 19 | A \code{selenider_session} object. 20 | } 21 | \description{ 22 | Create a \code{selenider_session} using custom HTML/JavaScript. 23 | } 24 | \details{ 25 | The function works by combining \code{html} and \code{js} into a single string, then 26 | writing this to a temporary file (and opening it in the session's browser). 27 | } 28 | \examples{ 29 | \dontshow{if (selenider::selenider_available(online = FALSE)) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 30 | session <- minimal_selenider_session("

Example

") 31 | \dontshow{\}) # examplesIf} 32 | } 33 | \seealso{ 34 | \code{\link[=selenider_session]{selenider_session()}} 35 | } 36 | -------------------------------------------------------------------------------- /man/open_url.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/global_actions.R 3 | \name{open_url} 4 | \alias{open_url} 5 | \title{Open a URL} 6 | \usage{ 7 | open_url(url, timeout = NULL, session = NULL) 8 | } 9 | \arguments{ 10 | \item{url}{The URL to navigate to: a string.} 11 | 12 | \item{timeout}{The maximum time to wait for the page to load, in seconds. 13 | This defaults to 60, unless in a Github Action, in which case it defaults 14 | to 5 minutes.} 15 | 16 | \item{session}{A \code{selenider_session} object. If not specified, the global 17 | session object (the result of \code{\link[=get_session]{get_session()}}) is used.} 18 | } 19 | \value{ 20 | The session object, invisibly. 21 | } 22 | \description{ 23 | Navigate the browser to specified URL, waiting until the page is considered 24 | open before finishing. 25 | } 26 | \examples{ 27 | \dontshow{if (selenider::selenider_available()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 28 | session <- selenider_session() 29 | 30 | open_url("https://r-project.org") 31 | 32 | # Or: 33 | open_url(session = session, "https://r-project.org") 34 | \dontshow{\}) # examplesIf} 35 | } 36 | \seealso{ 37 | Other global actions: 38 | \code{\link{back}()}, 39 | \code{\link{current_url}()}, 40 | \code{\link{execute_js_fn}()}, 41 | \code{\link{get_page_source}()}, 42 | \code{\link{reload}()}, 43 | \code{\link{scroll_to}()}, 44 | \code{\link{take_screenshot}()} 45 | } 46 | \concept{global actions} 47 | -------------------------------------------------------------------------------- /man/print.selenider_element.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/print.R 3 | \name{print.selenider_element} 4 | \alias{print.selenider_element} 5 | \alias{print.selenider_elements} 6 | \title{Print a live HTML element} 7 | \usage{ 8 | \method{print}{selenider_element}(x, width = getOption("width"), ..., timeout = NULL) 9 | 10 | \method{print}{selenider_elements}(x, width = getOption("width"), ..., n = 20, timeout = NULL) 11 | } 12 | \arguments{ 13 | \item{x}{A \code{selenider_element} or \code{selenider_elements} object.} 14 | 15 | \item{width}{The maximum width of the output.} 16 | 17 | \item{...}{Not used.} 18 | 19 | \item{timeout}{How long to wait for \code{x} to exist in order to print its HTML.} 20 | 21 | \item{n}{The maximum number of elements to print.} 22 | } 23 | \value{ 24 | \code{x}, invisibly. 25 | } 26 | \description{ 27 | Display an element or collection of elements by fetching the elements and 28 | displaying their HTML contents. 29 | } 30 | \examples{ 31 | \dontshow{if (selenider::selenider_available(online = FALSE)) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 32 | html <- " 33 |
34 |

Text 1

35 |

Text 2

36 |

Text 3

37 |

Text 4

38 |
39 | " 40 | 41 | session <- minimal_selenider_session(html) 42 | 43 | print(s("div")) 44 | 45 | print(ss("p")) 46 | 47 | print(ss("p"), n = 3) 48 | \dontshow{\}) # examplesIf} 49 | } 50 | -------------------------------------------------------------------------------- /man/print_lazy.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/print_lazy.R 3 | \name{print_lazy} 4 | \alias{print_lazy} 5 | \alias{print_lazy.selenider_element} 6 | \alias{print_lazy.selenider_elements} 7 | \title{Print an element without fetching it} 8 | \usage{ 9 | print_lazy(x, ...) 10 | 11 | \method{print_lazy}{selenider_element}(x, ...) 12 | 13 | \method{print_lazy}{selenider_elements}(x, ...) 14 | } 15 | \arguments{ 16 | \item{x}{A \code{selenider_element} or \code{selenider_elements} object.} 17 | 18 | \item{...}{Not used.} 19 | } 20 | \value{ 21 | \code{x}, invisibly. 22 | } 23 | \description{ 24 | \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} 25 | 26 | Display a summary of the steps needed to reach an element. This function 27 | is deprecated, as it is not useful for most users. 28 | } 29 | -------------------------------------------------------------------------------- /man/read_html.selenider_session.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/rvest.R 3 | \name{read_html.selenider_session} 4 | \alias{read_html.selenider_session} 5 | \alias{read_html.selenider_element} 6 | \title{Read a live HTML document} 7 | \usage{ 8 | read_html.selenider_session( 9 | x, 10 | encoding = "", 11 | ..., 12 | options = c("RECOVER", "NOERROR", "NOBLANKS") 13 | ) 14 | 15 | read_html.selenider_element( 16 | x, 17 | encoding = "", 18 | timeout = NULL, 19 | outer = TRUE, 20 | ..., 21 | options = c("RECOVER", "NOERROR", "NOBLANKS") 22 | ) 23 | } 24 | \arguments{ 25 | \item{x}{A \code{selenider_session}/\code{selenider_element} object.} 26 | 27 | \item{encoding, ..., options}{Passed into \code{\link[xml2:read_xml]{xml2::read_html()}}.} 28 | 29 | \item{timeout}{How long to wait for \code{x} to exist in the DOM before throwing 30 | an error.} 31 | 32 | \item{outer}{Whether to read the inner (all children of the current element) 33 | or outer (including the element itself) HTML of \code{x}.} 34 | } 35 | \value{ 36 | \code{read_html()} returns an XML document. Note that HTML will always be wrapped 37 | in a \verb{} and \verb{} tag, if it isn't already. 38 | } 39 | \description{ 40 | \code{\link[xml2:read_xml]{xml2::read_html()}} can be used on a selenider session to read the HTML of 41 | the entire page, or on a selenider element to get the HTML of that element. 42 | } 43 | \examples{ 44 | \dontshow{if (selenider::selenider_available(online = FALSE)) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 45 | library(rvest) 46 | 47 | html <- " 48 |
49 |

Example text

50 |
51 | " 52 | 53 | session <- minimal_selenider_session(html) 54 | 55 | read_html(session) 56 | read_html(s("div")) 57 | \dontshow{\}) # examplesIf} 58 | } 59 | -------------------------------------------------------------------------------- /man/reload.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/global_actions.R 3 | \name{reload} 4 | \alias{reload} 5 | \alias{refresh} 6 | \title{Reload the current page} 7 | \usage{ 8 | reload(timeout = NULL, session = NULL) 9 | 10 | refresh(timeout = NULL, session = NULL) 11 | } 12 | \arguments{ 13 | \item{timeout}{The maximum time to wait for the page to load, in seconds. 14 | This defaults to 60, unless in a Github Action, in which case it defaults 15 | to 5 minutes.} 16 | 17 | \item{session}{A \code{selenider_session} object. If not specified, the global 18 | session object (the result of \code{\link[=get_session]{get_session()}}) is used.} 19 | } 20 | \value{ 21 | The session object, invisibly. 22 | } 23 | \description{ 24 | \code{reload()} and \code{refresh()} both reload the current page. 25 | } 26 | \examples{ 27 | \dontshow{if (selenider::selenider_available()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 28 | session <- selenider_session() 29 | 30 | open_url("https://r-project.org") 31 | 32 | reload() 33 | \dontshow{\}) # examplesIf} 34 | } 35 | \seealso{ 36 | Other global actions: 37 | \code{\link{back}()}, 38 | \code{\link{current_url}()}, 39 | \code{\link{execute_js_fn}()}, 40 | \code{\link{get_page_source}()}, 41 | \code{\link{open_url}()}, 42 | \code{\link{scroll_to}()}, 43 | \code{\link{take_screenshot}()} 44 | } 45 | \concept{global actions} 46 | -------------------------------------------------------------------------------- /man/s.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/api.R 3 | \name{s} 4 | \alias{s} 5 | \alias{ss} 6 | \title{Select HTML elements} 7 | \usage{ 8 | s(css = NULL, xpath = NULL, id = NULL, class_name = NULL, name = NULL) 9 | 10 | ss(css = NULL, xpath = NULL, id = NULL, class_name = NULL, name = NULL) 11 | } 12 | \arguments{ 13 | \item{css}{A css selector.} 14 | 15 | \item{xpath}{An XPath.} 16 | 17 | \item{id}{The id of the element you want to select.} 18 | 19 | \item{class_name}{The class name of the element you want to select.} 20 | 21 | \item{name}{The name attribute of the element you want to select.} 22 | } 23 | \value{ 24 | \code{s()} returns a \code{selenider_element} object. 25 | \code{ss()} returns a \code{selenider_elements} object. Note that this is not a list, 26 | and you should be careful with the functions that you use with it. See the 27 | advanced usage vignette for more details: 28 | \code{vignette("advanced-usage", package = "selenider")}. 29 | } 30 | \description{ 31 | Both \code{s()} and \code{ss()} allow you to select elements without specifying a 32 | session object. 33 | 34 | \code{s()} selects a single element, being a shorthand for \code{\link[=find_element]{find_element()}} 35 | on the current session. 36 | 37 | \code{ss()} selects multiple elements, being a shorthand for \code{\link[=find_elements]{find_elements()}}. 38 | } 39 | \details{ 40 | Both functions allow the starting point for chains of selectors to be made 41 | more concise. Both use \code{\link[=get_session]{get_session()}} to get the global session object. 42 | If you want to pass in a session, use \code{\link[=find_element]{find_element()}}/\code{\link[=find_elements]{find_elements()}} 43 | instead. 44 | } 45 | \examples{ 46 | \dontshow{if (selenider::selenider_available(online = FALSE)) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 47 | html <- " 48 |
49 |

50 |
51 |

52 |
53 |
54 | " 55 | 56 | session <- minimal_selenider_session(html) 57 | 58 | s("#id1") 59 | 60 | # This is the equivalent of: 61 | find_element(session, "#id1") 62 | 63 | ss(".inner") 64 | 65 | # This is the equivalent of: 66 | find_element(session, ".inner") 67 | 68 | # This provides a more concise way to begin a chain of selectors 69 | s("div") |> 70 | find_element(".child") |> 71 | find_element(".inner") 72 | \dontshow{\}) # examplesIf} 73 | } 74 | \seealso{ 75 | \itemize{ 76 | \item \code{\link[=find_element]{find_element()}} and \code{\link[=find_elements]{find_elements()}} 77 | \item \code{\link[=selenider_session]{selenider_session()}} to begin a session. 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /man/scroll_to.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/global_actions.R 3 | \name{scroll_to} 4 | \alias{scroll_to} 5 | \alias{scroll_by} 6 | \title{Scroll along the page} 7 | \usage{ 8 | scroll_to(top = 0, left = 0, session = NULL) 9 | 10 | scroll_by(top = 0, left = 0, session = NULL) 11 | } 12 | \arguments{ 13 | \item{top}{The vertical scroll position/offset, in pixels.} 14 | 15 | \item{left}{The horizontal scroll position/offset, in pixels.} 16 | 17 | \item{session}{A \code{selenider_session} object. If not specified, the global 18 | session object (the result of \code{\link[=get_session]{get_session()}}) is used.} 19 | } 20 | \description{ 21 | \code{scroll_to()} scrolls the page to the specified coordinates. 22 | 23 | \code{scroll_by()} scrolls the page by the specified amount. 24 | } 25 | \examples{ 26 | \dontshow{if (selenider::selenider_available()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 27 | session <- selenider_session() 28 | 29 | open_url("https://r-project.org") 30 | 31 | scroll_to(100, 100) 32 | 33 | # Scrolls an additional 100 pixels down 34 | scroll_by(100) 35 | \dontshow{\}) # examplesIf} 36 | } 37 | \seealso{ 38 | Other global actions: 39 | \code{\link{back}()}, 40 | \code{\link{current_url}()}, 41 | \code{\link{execute_js_fn}()}, 42 | \code{\link{get_page_source}()}, 43 | \code{\link{open_url}()}, 44 | \code{\link{reload}()}, 45 | \code{\link{take_screenshot}()} 46 | } 47 | \concept{global actions} 48 | -------------------------------------------------------------------------------- /man/selenider-config.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/docs.R 3 | \name{selenider-config} 4 | \alias{selenider-config} 5 | \title{Selenider options} 6 | \description{ 7 | \code{selenider} has a few options, allowing you to specify the session and 8 | browser to use without having to tell \code{\link[=selenider_session]{selenider_session()}} this information 9 | every time. 10 | \itemize{ 11 | \item \code{selenider.session} - The package to use as a backend: either "chromote", 12 | "selenium" or "rselenium". 13 | \item \code{selenider.browser} - The name of the browser to run the session in; one 14 | of "chrome", "firefox", "edge", "safari", or another valid browser name. 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /man/selenider-package.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/selenider-package.R 3 | \docType{package} 4 | \name{selenider-package} 5 | \alias{selenider} 6 | \alias{selenider-package} 7 | \title{selenider: Concise, Lazy and Reliable Wrapper for 'chromote' and 'selenium'} 8 | \description{ 9 | A user-friendly wrapper for web automation, using either 'chromote' or 'selenium'. Provides a simple and consistent API to make web scraping and testing scripts easy to write and understand. Elements are lazy, and automatically wait for the website to be valid, resulting in reliable and reproducible code, with no visible impact on the experience of the programmer. 10 | } 11 | \seealso{ 12 | Useful links: 13 | \itemize{ 14 | \item \url{https://github.com/ashbythorpe/selenider} 15 | \item \url{https://ashbythorpe.github.io/selenider/} 16 | \item Report bugs at \url{https://github.com/ashbythorpe/selenider/issues} 17 | } 18 | 19 | } 20 | \author{ 21 | \strong{Maintainer}: Ashby Thorpe \email{ashbythorpe@gmail.com} (\href{https://orcid.org/0000-0003-3106-099X}{ORCID}) [copyright holder] 22 | 23 | } 24 | \keyword{internal} 25 | -------------------------------------------------------------------------------- /man/selenider_available.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/utils.R 3 | \name{selenider_available} 4 | \alias{selenider_available} 5 | \alias{skip_if_selenider_unavailable} 6 | \title{Check if selenider can be used} 7 | \usage{ 8 | selenider_available(session = c("chromote", "selenium"), online = TRUE) 9 | 10 | skip_if_selenider_unavailable(session = c("chromote", "selenium")) 11 | } 12 | \arguments{ 13 | \item{session}{Which session we should check. \code{"chromote"} is used by 14 | default.} 15 | 16 | \item{online}{Whether we need to check for an internet connection.} 17 | } 18 | \value{ 19 | A boolean flag: \code{TRUE} or \code{FALSE}. 20 | } 21 | \description{ 22 | Checks if selenider's dependencies are available, and that we are in an 23 | environment where it makes sense to open a selenider session. 24 | 25 | \code{skip_if_selenider_unavailable()} skips a testthat test if 26 | \code{selenider_available()} returns \code{FALSE}. 27 | } 28 | \details{ 29 | Specifically, the following is checked: 30 | \itemize{ 31 | \item The \code{SELENIDER_AVAILABLE} environment variable. Set this to \code{"TRUE" }or 32 | \code{"FALSE"} to override the return value of this function. 33 | \item Whether we are on CRAN (using the \code{NOT_CRAN} environment variable). If we 34 | are, the function returns \code{FALSE}. 35 | \item Whether an internet connection is available (using \code{\link[curl:nslookup]{curl::nslookup()}}). 36 | } 37 | 38 | If \code{session} is \code{"chromote"}, we also check: 39 | \itemize{ 40 | \item Whether \code{chromote} is installed. 41 | \item Whether \code{\link[chromote:find_chrome]{chromote::find_chrome()}} does not error. 42 | } 43 | 44 | If \code{session} is \code{"selenium"}, we check: 45 | \itemize{ 46 | \item Whether \code{selenium} is installed. 47 | \item Whether we can find a valid browser that is supported by \code{selenium}. 48 | } 49 | } 50 | \examples{ 51 | selenider_available() 52 | 53 | } 54 | -------------------------------------------------------------------------------- /man/take_screenshot.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/global_actions.R 3 | \name{take_screenshot} 4 | \alias{take_screenshot} 5 | \title{Take a screenshot of the current page} 6 | \usage{ 7 | take_screenshot(file = NULL, view = FALSE, session = NULL) 8 | } 9 | \arguments{ 10 | \item{file}{The file path to save the screenshot to.} 11 | 12 | \item{view}{Whether to open the interactively view the screenshot. If this is 13 | \code{TRUE} and \code{file} is \code{NULL}, the screenshot will be deleted after viewing.} 14 | 15 | \item{session}{A \code{selenider_session} object. If not specified, the global 16 | session object (the result of \code{\link[=get_session]{get_session()}}) is used.} 17 | } 18 | \value{ 19 | \code{file}, if it is not \code{NULL}. Otherwise, the session object is returned, invisibly. 20 | } 21 | \description{ 22 | Take a screenshot of the current session state, saving this image to a file. 23 | } 24 | \examples{ 25 | \dontshow{if (selenider::selenider_available()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 26 | session <- selenider_session() 27 | 28 | open_url("https://www.google.com") 29 | 30 | file_path <- withr::local_tempfile(fileext = ".png") 31 | 32 | take_screenshot(file_path) 33 | \dontshow{\}) # examplesIf} 34 | } 35 | \seealso{ 36 | Other global actions: 37 | \code{\link{back}()}, 38 | \code{\link{current_url}()}, 39 | \code{\link{execute_js_fn}()}, 40 | \code{\link{get_page_source}()}, 41 | \code{\link{open_url}()}, 42 | \code{\link{reload}()}, 43 | \code{\link{scroll_to}()} 44 | } 45 | \concept{global actions} 46 | -------------------------------------------------------------------------------- /man/wdman_server_options.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/session-options.R 3 | \name{wdman_server_options} 4 | \alias{wdman_server_options} 5 | \alias{rselenium_client_options} 6 | \title{RSelenium options} 7 | \usage{ 8 | wdman_server_options( 9 | version = "latest", 10 | driver_version = "latest", 11 | port = 4444L, 12 | check = TRUE, 13 | verbose = FALSE, 14 | retcommand = FALSE, 15 | ... 16 | ) 17 | 18 | rselenium_client_options( 19 | port = 4444L, 20 | host = "localhost", 21 | path = "/wd/hub", 22 | version = "", 23 | platform = "ANY", 24 | javascript = TRUE, 25 | native_events = TRUE, 26 | extra_capabilities = list() 27 | ) 28 | } 29 | \arguments{ 30 | \item{version}{The version of Selenium server to use.} 31 | 32 | \item{driver_version}{The version of the browser-specific driver to use.} 33 | 34 | \item{port}{The port to run selenium client/server on.} 35 | 36 | \item{check, verbose, retcommand, ...}{Passed into \code{\link[wdman:selenium]{wdman::selenium()}}.} 37 | 38 | \item{host}{The host to connect to the selenium server over.} 39 | 40 | \item{path, platform, javascript, native_events, extra_capabilities}{Passed into \code{\link[RSelenium:remoteDriver-class]{RSelenium::remoteDriver()}}.} 41 | } 42 | \value{ 43 | An options object that can be passed into \code{\link[=selenium_options]{selenium_options()}}. 44 | } 45 | \description{ 46 | \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#superseded}{\figure{lifecycle-superseded.svg}{options: alt='[Superseded]'}}}{\strong{[Superseded]}} 47 | Instruct selenider to use RSelenium instead of selenium. Passed into 48 | \code{\link[=selenium_options]{selenium_options()}}. This is not recommended, since RSelenium does not 49 | support the latest version of Selenium, and wdman (the server manager that 50 | RSelenium) uses, is not compatible with the latest version of Chrome. 51 | } 52 | \details{ 53 | In \code{\link[=selenium_options]{selenium_options()}}, you can supply options to configure a server and 54 | client run by RSelenium instead of selenium. 55 | Instead of \code{selenium_server_options()}, you can use \code{wdman_server_options()} 56 | to allow \code{wdman} to run the Selenium server using \code{\link[wdman:selenium]{wdman::selenium()}}. 57 | 58 | Instead of using \code{selenium_client_options()}, you can use 59 | \code{rselenium_client_options()} to control the creation of an 60 | \code{\link[RSelenium:remoteDriver-class]{RSelenium::remoteDriver()}} object instead. 61 | 62 | Note that the \code{driver} option of \code{selenider_session()} also accepts these 63 | objects in place of their selenium equivalents. 64 | } 65 | -------------------------------------------------------------------------------- /selenider.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: No 4 | SaveWorkspace: No 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 2 10 | Encoding: UTF-8 11 | 12 | RnwWeave: Sweave 13 | LaTeX: pdfLaTeX 14 | 15 | AutoAppendNewline: Yes 16 | LineEndingConversion: Posix 17 | 18 | BuildType: Package 19 | PackageUseDevtools: Yes 20 | PackageInstallArgs: --no-multiarch --with-keep.source 21 | PackageRoxygenize: rd,collate,namespace 22 | -------------------------------------------------------------------------------- /tests/manual/test-session.R: -------------------------------------------------------------------------------- 1 | test_that("Creating sessions using shinytest2 AppDrivers works", { 2 | testthat::skip_if_not_installed("shinytest2") 3 | 4 | shiny_app <- shiny::shinyApp(ui = shiny::fluidPage(), server = function(input, output) {}) 5 | 6 | app <- shinytest2::AppDriver$new(shiny_app) 7 | 8 | session <- selenider_session(driver = app) 9 | 10 | expect_equal(session$session, "chromote") 11 | expect_s3_class(session$driver, "ChromoteSession") 12 | }) 13 | 14 | test_that("Creating sessions using RSelenium works", { 15 | skip_if_selenider_unavailable("selenium") 16 | 17 | session <- selenider_session("selenium", browser = "firefox") 18 | 19 | expect_true(is_selenium_server(session$server)) 20 | expect_true(is_selenium_client(session$driver)) 21 | expect_equal(session$session, "selenium") 22 | 23 | withr::deferred_run() 24 | 25 | server <- create_selenium_server_internal("firefox", options = selenium_server_options(port = 4444L)) 26 | 27 | session <- selenider_session(browser = "firefox", driver = server) 28 | 29 | expect_equal(find_port_from_server(server), 4444L) 30 | 31 | expect_true(is_selenium_server(session$server)) 32 | expect_true(is_selenium_client(session$driver)) 33 | expect_equal(session$session, "selenium") 34 | 35 | withr::deferred_run() 36 | 37 | server <- create_selenium_server_internal("firefox", options = selenium_server_options(port = 4445L)) 38 | 39 | session <- selenider_session(browser = "firefox", driver = list(server = server)) 40 | 41 | expect_true(is_selenium_server(session$server)) 42 | expect_true(is_selenium_client(session$driver)) 43 | expect_equal(session$session, "selenium") 44 | 45 | withr::deferred_run() 46 | 47 | server <- create_selenium_server_internal("firefox", options = selenium_server_options(port = 4446L)) 48 | 49 | session <- selenider_session(browser = "firefox", driver = list(server)) 50 | 51 | expect_true(is_selenium_server(session$server)) 52 | expect_true(is_selenium_client(session$driver)) 53 | expect_equal(session$session, "selenium") 54 | 55 | withr::deferred_run() 56 | 57 | server <- create_selenium_server_internal("firefox", options = selenium_server_options(port = 4447L)) 58 | client <- create_selenium_client_internal("firefox", options = selenium_client_options(port = 4447L)) 59 | 60 | session <- selenider_session(driver = list(client, server)) 61 | 62 | expect_true(is_selenium_server(session$server)) 63 | expect_true(is_selenium_client(session$driver)) 64 | expect_equal(session$session, "selenium") 65 | 66 | withr::deferred_run() 67 | 68 | server <- create_selenium_server_internal("firefox", options = selenium_server_options(port = 4448L)) 69 | client <- create_selenium_client_internal("firefox", options = selenium_client_options(port = 4448L)) 70 | 71 | session <- selenider_session(driver = list(client = client, server = server)) 72 | 73 | expect_true(is_selenium_server(session$server)) 74 | expect_true(is_selenium_client(session$driver)) 75 | expect_equal(session$session, "selenium") 76 | 77 | withr::deferred_run() 78 | 79 | server <- create_selenium_server_internal("firefox", options = selenium_server_options(port = 4449L)) 80 | client <- create_selenium_client_internal("firefox", options = selenium_client_options(port = 4449L)) 81 | 82 | session <- selenider_session(driver = list(client)) 83 | 84 | expect_null(session$server) 85 | expect_true(is_selenium_client(session$driver)) 86 | expect_equal(session$session, "selenium") 87 | 88 | withr::deferred_run() 89 | server$kill() 90 | }) 91 | -------------------------------------------------------------------------------- /tests/testthat.R: -------------------------------------------------------------------------------- 1 | # This file is part of the standard setup for testthat. 2 | # It is recommended that you do not modify it. 3 | # 4 | # Where should you do additional test configuration? 5 | # Learn more about the roles of various files in: 6 | # * https://r-pkgs.org/tests.html 7 | # * https://testthat.r-lib.org/reference/test_package.html#special-files 8 | 9 | library(testthat) 10 | library(selenider) 11 | 12 | test_check("selenider") 13 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/actions.md: -------------------------------------------------------------------------------- 1 | # get_element_for_selection() works 2 | 3 | Code 4 | elem_select(selection, reset_other = FALSE, timeout = 0.1) 5 | Condition 6 | Error in `get_select_element()`: 7 | ! To select `x`, it must be an `