#web-driver #selenium #proc-macro #macro-derive

macro thirtyfour-querier-derive

#[derive(Querier)] macro for thirtyfour WebDriver library

2 releases

0.1.4 Jul 20, 2022
0.1.3 Jul 19, 2022
0.1.2 Jul 19, 2022
0.1.1 Jul 19, 2022

#623 in Procedural macros

MIT license

16KB
249 lines

A #[derive(Querier)] derive macro for generating the boilerplate code needed to query a collection of elements by CSS selectors using thirtyfour.

This library is highly experimental, and is initially developed for internal use.

Example

use thirtyfour::prelude::*;
use thirtyfour_querier_derive::Querier;

#[derive(Querier)]
struct PageElements {
    #[querier(css = "#first")]
    first: WebElement,

    #[querier(css = "#second")]
    second: WebElement,

    #[querier(all, css = "div")]
    all_divs: WebElement,
}

let page_elements = PageElements::query(&driver).await.unwrap();

Dependencies

The impl blocks generated by the macro uses futures and thirtyfour libraries. Since the a proc macro crate can't export items, these dependencies has to be added manually by the user. This may change in future versions.

Details

The #[derive(Querier)] macro generates an impl block containing an async constructor for the structure named query, with the following signature.

pub async fn query<T: ElementQueryable>(driver: &T)
    -> Result<Self, WebDriverError>;
  • Only structs with named fields are supported.

  • By default, the queries are performed without waiting. Use #[querier(wait = num_seconds)] to add an explicit wait.

  • Each field of the structure must have a #[querier(...)] attribute. The content of the attribute must match the type of the field in this way:

    Attribute Field Type
    #[querier(css = "...")] WebElement
    #[querier(maybe, css = "...")] Option<WebElement>
    #[querier(all, css = "...")] Vec<WebElement>
    #[querier(nested, css = "...")] (WebElement, Q)
    #[querier(maybe, nested, css = "...")] Option<(WebElement, Q)>
    #[querier(all, nested, css = "...")] Vec<(WebElement, Q)>

    The type Q in the table can be any type that also has #[derive(Querier)].

Full Example

use thirtyfour_querier_derive::Querier;
use thirtyfour::prelude::*;

const HTML: &str = r#"
data:text/html;charset=utf-8,<html>
  <body>
    <div id="single-element"></div>

    <div class="many-elements">
      <div class="nested-element"></div>
      <div class="nested-element"></div>
    </div>
    <div class="many-elements">
      <div class="nested-element"></div>
      <div class="nested-element"></div>
    </div>

    <div class="not-queried nested-element"></div>
  </body>
</html>
"#;

#[derive(Debug, Querier)]
#[allow(dead_code)]
struct Page {
    #[querier(css = "#single-element")]
    single_elem: WebElement,

    #[querier(wait = 5, css = "#single-element")]
    single_elem_with_wait: WebElement,

    #[querier(maybe, css = "#missing-element")]
    missing_elem: Option<WebElement>,

    #[querier(all, css = ".many-elements")]
    many_elems: Vec<WebElement>,

    #[querier(all, nested, css = ".many-elements")]
    many_elems_nested: Vec<(WebElement, SubElement)>,
}

#[derive(Debug, Querier)]
struct SubElement {
    #[querier(all, css = ".nested-element")]
    nested_elems: Vec<WebElement>,
}

#[tokio::main]
async fn main() {
    let driver = WebDriver::new("http://localhost:9100", DesiredCapabilities::chrome())
        .await
        .unwrap();

    // Load the html string
    driver.get(HTML).await.unwrap();

    // Query the elements specified in `struct Page`
    let page = Page::query(&driver).await.unwrap();

    // Count number of `.nested-element` within `many-element`s (should be 4)
    let mut nest_elem_count = 0;
    for (_, elem) in page.many_elems_nested {
        nest_elem_count += elem.nested_elems.len();
    }
    assert_eq!(
        nest_elem_count, 4,
        "Number of nested-element within many-element is 4"
    );
}

Dependencies

~3.5–5MB
~95K SLoC