jonhoo / fantoccini

A high-level API for programmatically interacting with web pages through WebDriver.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Add Trait for supporting other WebDriver-compatible commands (CDP, Selenium, Appium, etc)

stevepryde opened this issue · comments

Most (perhaps all) WebDriver commands simply consist of an endpoint, method, optional body, and JSON response, so this could be abstracted behind a trait. This would then allow other compatible commands to be supported in future, either as part of fantoccini (potentially CDP) or as part of downstream crates (thirtyfour, and potentially an appium crate later if anyone is interested).

The trait would look something like this:

pub trait WebDriverCompatibleCommand {
    fn endpoint(&self, session_id: Option<String>) -> Result<url::Url, url::ParseError>;
    fn method_and_body(&self) -> (http::Method, Option<serde_json::Value>);
}

That should be enough I think.

Since the WebDriverCommand enum would then become a subset, I'd recommend implementing the trait for it, and then changing the Cmd::WebDriver(..) variant to WebDriver(Box<dyn WebDriverCompatibleCommand>) instead of WebDriver(Wcmd).

The other part that is required is to make Client::issue() completely public, or alternatively a
pub fn issue_cmd(cmd: Box<dyn WebDriverCompatibleCmd>) -> Result<serde_json::Value, error::CmdError>
to restrict it to just this particular subset of commands.

I think this is the last remaining change required to fully support the changeover for #145 :)
It also opens the door to add support for CDP, either natively in fantoccini or in a separate crate.

Note that actually adding support for any additional commands is not a requirement for resolving this issue.

I'm in favor of landing something like this 👍

@jonhoo Do you have a preference between making Client::issue() public vs adding a new pub fn issue_cmd(cmd: Box<dyn WebDriverCompatibleCommand>) for just the trait?

I think I prefer the second one just because I don't think we need/want to expose all of the other session::Cmd variants externally

I'd prefer the latter too, yeah. Although I wonder if it should be generic rather than taking Box<dyn>. What do you think?

I'm happy to go either way but I lean slightly towards dynamic dispatch here.

My reasoning is as follows:

  • any performance implication is unlikely to matter much in an application so heavily bound to network requests (even if the webdriver server is running on the local machine).
  • generics would likely increase compile time

These are probably really minor factors though.

There is a slight ergonomics cost in having to do Box::new(cmd) but most "end users" would only interact with this via fantoccini or another crate that calls Client::issue_cmd(), and even in that crate it would likely be wrapped. So I think this aspect is ok.

If there are other factors I've missed let me know