Comments (8)
I don't really have any experience with Actix, but I'll see if I can help.
Actix has its own async runtime, doesn't it? thirtyfour
uses reqwest
by default, which uses tokio internally, whether using sync or async, and tokio might not play nice with the runtime from Actix. I'm not sure whether the async-std client surf
would fare any better.
Were you running selenium or using the webdriver (chromedriver or geckodriver for example) directly?
Does Actix have its own http client? It seems to provide actix_web::client
. It might be possible to implement the two traits, RemoteConnectionAsyncCreate
and RemoteConnectionAsync
for this http client, and that should work.
If you look at src/http_async/reqwest_async.rs
you'll see the implementation for reqwest
. It should be fairly straightforward to adapt this for Actix_web::client
. Then it's just a matter of defining your WebDriver
type like this:
type WebDriver = GenericWebDriver<ActixHttpClient>;
You may need to specify no-default-features
in order to avoid clashes with the built-in WebDriver
type.
Let me know if you need more info. I realise this is a bit clunky at the moment.
from thirtyfour.
I have just published v0.15.0
which simplifies the traits a little.
You now only need to implement a single trait for the Actix http client. The src/http_async/reqwest_async.rs
source will still provide a pretty good example of what you want. If you adapt this for Actix_web::client
I think that will work. The WebDriver
type definition is still as above.
from thirtyfour.
Thank you for the quick update!
I got it working by implementing a quick and dirty (sync) version for ureq:
use std::fmt::Debug;
use thirtyfour::sync::http_sync::connection_sync::WebDriverHttpClientSync;
use thirtyfour::sync::prelude::*;
use thirtyfour::sync::GenericWebDriver;
use thirtyfour::{
common::command::{Command, RequestMethod},
error::{WebDriverError, WebDriverResult},
SessionId,
};
use ureq;
/// Synchronous connection to the remote WebDriver server.
#[derive(Debug)]
pub struct UreqDriver {
url: String,
client: ureq::Agent,
}
type WebDriver = GenericWebDriver<UreqDriver>;
impl WebDriverHttpClientSync for UreqDriver {
fn create(remote_server_addr: &str) -> WebDriverResult<Self> {
Ok(UreqDriver {
url: remote_server_addr.trim_end_matches('/').to_owned(),
client: ureq::agent(),
})
}
/// Execute the specified command and return the data as serde_json::Value.
fn execute(
&self,
session_id: &SessionId,
command: Command,
) -> WebDriverResult<serde_json::Value> {
let request_data = command.format_request(session_id);
let url = self.url.clone() + &request_data.url;
let mut request = match request_data.method {
RequestMethod::Get => self.client.get(&url),
RequestMethod::Post => self.client.post(&url),
RequestMethod::Delete => self.client.delete(&url),
};
let resp = if let Some(x) = request_data.body {
request.send_json(x)
} else {
request.call()
};
match resp.status() {
200..=399 => Ok(resp.into_json()?),
400..=599 => {
let status = resp.status();
let body: serde_json::Value =
resp.into_json().unwrap_or(serde_json::Value::Null);
Err(WebDriverError::parse(status, body))
}
_ => unreachable!(),
}
}
}
Though a big part of my issues above was that I used actix_web::test::init_service
instead of actix_web::test::TestServer
which I guess doesn't create a real server and therefore wasn't accessable at all 🙃
Here is what I do instead:
use crate::routes::routes;
use actix_web::{test, App};
#[actix_rt::test]
async fn test_index_get() -> WebDriverResult<()> {
let app = test::start(|| App::new().configure(routes));
let port = app.addr().port();
let req = app.get("/");
let resp = req.send().await.unwrap();
assert!(resp.status().is_success());
let caps = DesiredCapabilities::chrome();
let driver = WebDriver::new("http://localhost:4444", &caps)?;
driver.get(format!("http://localhost:{}", port))?;
Ok(())
}
Using sync is not currently an issue for me but async would of course be an improvement. Especially since I could get rid of ureq (or some other client outside of actix) as an extra dependency. Also, I don't seem to be able to import thirtyfour::sync
without the blocking
feature (which adds tokio and and reqwest which isn't used in my case), is that correct?
Finally, here are some of the errors I couldn't solve when trying to implement the actix-web client:
error[E0277]: `tests::e2e::tests::ActixDriverAsync` doesn't implement `std::fmt::Debug`
--> src/tests/e2e.rs:25:10
|
25 | impl WebDriverHttpClientAsync for ActixDriverAsync {
| ^^^^^^^^^^^^^^^^^^^^^^^^ `tests::e2e::tests::ActixDriverAsync` cannot be formatted using `{:?}`
|
::: /home/oskar/.cargo/registry/src/github.com-1ecc6299db9ec823/thirtyfour-0.15.0/src/http_async/connection_async.rs:16:37
|
16 | pub trait WebDriverHttpClientAsync: Debug + Send + Sync {
| ----- required by this bound in `thirtyfour::http_async::connection_async::WebDriverHttpClientAsync`
|
= help: the trait `std::fmt::Debug` is not implemented for `tests::e2e::tests::ActixDriverAsync`
= note: add `#[derive(Debug)]` or manually implement `std::fmt::Debug`
error[E0277]: `std::rc::Rc<awc::ClientConfig>` cannot be shared between threads safely
--> src/tests/e2e.rs:25:10
|
25 | impl WebDriverHttpClientAsync for ActixDriverAsync {
| ^^^^^^^^^^^^^^^^^^^^^^^^ `std::rc::Rc<awc::ClientConfig>` cannot be shared between threads safely
|
::: /home/oskar/.cargo/registry/src/github.com-1ecc6299db9ec823/thirtyfour-0.15.0/src/http_async/connection_async.rs:16:52
|
16 | pub trait WebDriverHttpClientAsync: Debug + Send + Sync {
| ---- required by this bound in `thirtyfour::http_async::connection_async::WebDriverHttpClientAsync`
|
= help: within `tests::e2e::tests::ActixDriverAsync`, the trait `std::marker::Sync` is not implemented for `std::rc::Rc<awc::ClientConfig>`
= note: required because it appears within the type `awc::Client`
= note: required because it appears within the type `tests::e2e::tests::ActixDriverAsync`
error[E0277]: `std::rc::Rc<awc::ClientConfig>` cannot be sent between threads safely
--> src/tests/e2e.rs:25:10
|
25 | impl WebDriverHttpClientAsync for ActixDriverAsync {
| ^^^^^^^^^^^^^^^^^^^^^^^^ `std::rc::Rc<awc::ClientConfig>` cannot be sent between threads safely
|
::: /home/oskar/.cargo/registry/src/github.com-1ecc6299db9ec823/thirtyfour-0.15.0/src/http_async/connection_async.rs:16:45
|
16 | pub trait WebDriverHttpClientAsync: Debug + Send + Sync {
| ---- required by this bound in `thirtyfour::http_async::connection_async::WebDriverHttpClientAsync`
|
= help: within `tests::e2e::tests::ActixDriverAsync`, the trait `std::marker::Send` is not implemented for `std::rc::Rc<awc::ClientConfig>`
= note: required because it appears within the type `awc::Client`
= note: required because it appears within the type `tests::e2e::tests::ActixDriverAsync`
from thirtyfour.
Your async example looks like you had the awc::ClientConfig wrapped in Rc. Is that in your code or is that in the actix http client code?
You may be able to resolve this by wrapping the client in Arc (and possibly Mutex as well?)
i.e.
client: Arc<Mutex<Client>>
That would automatically implement Send + Sync for you
from thirtyfour.
I don't have an easy way to work on this. If someone can provide a PR that would be great.
from thirtyfour.
So I have thirtyfour working async in an actix project.
https://github.com/contor-systems/contor
An example test from https://github.com/contor-systems/contor/blob/master/tests/registration_test.rs is
mod common;
use dotenv::dotenv;
use std::path::Path;
use thirtyfour::prelude::*;
// let's set up the sequence of steps we want the browser to take
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
async fn registration() -> WebDriverResult<()> {
dotenv().ok();
let driver = common::Config::new().get_driver().await?;
driver.get("http://localhost:9095").await?;
// Click the search button.
let elem_button = driver
.find_element(By::Css("button[type='submit']"))
.await?;
elem_button.click().await?;
assert!(driver
.page_source()
.await?
.contains("Invalid email or password"));
let email = common::random_email();
// Let's go and register
driver
.find_element(By::LinkText("SIGN UP"))
.await?
.click()
.await?;
driver
.find_element(By::Id("email"))
.await?
.send_keys(&email)
.await?;
driver
.find_element(By::Id("password"))
.await?
.send_keys(&email)
.await?;
driver
.find_element(By::Id("confirm_password"))
.await?
.send_keys(&email)
.await?;
driver
.find_element(By::Css("button[type='submit']"))
.await?
.click()
.await?;
let cookie = driver.get_cookie("session").await;
assert!(cookie.is_ok());
driver.get("http://localhost:9095/auth/sign_out").await?;
driver
.screenshot(Path::new("./target/registration.png"))
.await?;
let cookie = driver.get_cookie("session").await;
assert!(cookie.is_err());
Ok(())
}
from thirtyfour.
This looks good. It also tells me that there are probably no changes required on our end to support this.
Are you able to elaborate on what this part does?
let driver = common::Config::new().get_driver().await?;
I assume that sets up a new thirtyfour::WebDriver
instance and returns it?
It also looks like this test assumes the web service is already running on localhost:9095 when the test is executed. I think what the OP wanted was something that could be done from within mod test
in the server source code itself, and run as doctests. From my perspective, provided someone can instantiate the service and access it via localhost in a browser, thirtyfour
should work fine since it is entirely independent of the web service and only operates the browser similar to how an end user would.
from thirtyfour.
This looks good. It also tells me that there are probably no changes required on our end to support this.
Are you able to elaborate on what this part does?let driver = common::Config::new().get_driver().await?;I assume that sets up a new
thirtyfour::WebDriver
instance and returns it?
Yes, that's correct.
It also looks like this test assumes the web service is already running on localhost:9095 when the test is executed. I think what the OP wanted was something that could be done from within
mod test
in the server source code itself, and run as doctests. From my perspective, provided someone can instantiate the service and access it via localhost in a browser,thirtyfour
should work fine since it is entirely independent of the web service and only operates the browser similar to how an end user would.
Agreed, I was previously using https://www.cypress.io/ and that's how that works.
So for example in github actions I do something like
- name: Integraton Testing
run: |
target/x86_64-unknown-linux-musl/release/contor &
chromedriver --whitelisted-ips="" &
cargo test --release --target x86_64-unknown-linux-musl
from thirtyfour.
Related Issues (20)
- so, how to get the text value of an element HOT 1
- Error while clicking element. HOT 4
- `#shadow-root` doesn't work on Firefox with geckodriver HOT 1
- Connection refused when using containers HOT 4
- How to use selenium-manager in my code? HOT 2
- thread 'tokio-runtime-worker' panicked at 'internal error: entered unreachable code: received unknown error (no such frame) for NOT_FOUND status code' HOT 4
- How can I set the user-agent for Chromedriver? HOT 3
- Is there a way to use wait_until or something like it to way for a specific ID to be available ? HOT 2
- wait until element to disappear HOT 6
- Chrome Options example erroring HOT 2
- Does `query` work on child elements? HOT 3
- Cannot interact with WebElement HOT 1
- How to trigger onchange event without submitting the form? HOT 2
- How to set PageLoadStrategy::None in DesiredCapabilities::chrome()? HOT 1
- Running the Test without Existing Browser (HtmlUnitDriver) HOT 1
- Add cdp type from auto_generate_cdp crate HOT 2
- How to handle file picker? HOT 4
- How to capture the Chromium's requests and responses traffic using thirtyfour HOT 5
- Maintenance Status
- Device Farm incompatibility - with workaround
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from thirtyfour.