-
Notifications
You must be signed in to change notification settings - Fork 135
[draft] Create wait structure #95
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -146,6 +146,9 @@ pub use hyper::Method; | |
| /// Error types. | ||
| pub mod error; | ||
|
|
||
| /// Wait logic | ||
| pub mod wait; | ||
|
|
||
| /// The long-running session future we spawn for multiplexing onto a running WebDriver instance. | ||
| mod session; | ||
| use crate::session::{Cmd, Session}; | ||
|
|
@@ -711,7 +714,7 @@ impl Client { | |
| /// While this currently just spins and yields, it may be more efficient than this in the | ||
| /// future. In particular, in time, it may only run `is_ready` again when an event occurs on | ||
| /// the page. | ||
| pub async fn wait_for<F, FF>(&mut self, mut is_ready: F) -> Result<(), error::CmdError> | ||
| pub async fn wait_for<'a, F, FF>(&mut self, mut is_ready: F) -> Result<(), error::CmdError> | ||
| where | ||
| F: FnMut(&mut Client) -> FF, | ||
| FF: Future<Output = Result<bool, error::CmdError>>, | ||
|
|
@@ -727,20 +730,31 @@ impl Client { | |
| /// future. In particular, in time, it may only run `is_ready` again when an event occurs on | ||
| /// the page. | ||
| pub async fn wait_for_find(&mut self, search: Locator<'_>) -> Result<Element, error::CmdError> { | ||
| let s: webdriver::command::LocatorParameters = search.into(); | ||
| loop { | ||
| match self | ||
| .by(webdriver::command::LocatorParameters { | ||
| using: s.using, | ||
| value: s.value.clone(), | ||
| }) | ||
| .await | ||
| { | ||
| Ok(v) => break Ok(v), | ||
| Err(error::CmdError::NoSuchElement(_)) => {} | ||
| Err(e) => break Err(e), | ||
| fn closure( | ||
| s: Locator<'_>, | ||
| mut client: Client, // TODO: handle lifetime issues with &mut Client type | ||
| ) -> impl Future<Output = Option<Result<Element, error::CmdError>>> { | ||
| let s: webdriver::command::LocatorParameters = s.into(); | ||
| async move { | ||
| match client | ||
| .by(webdriver::command::LocatorParameters { | ||
| using: s.using, | ||
| value: s.value.clone(), | ||
| }) | ||
| .await | ||
| { | ||
| Ok(v) => Some(Ok(v)), | ||
| Err(error::CmdError::NoSuchElement(_)) => None, | ||
| Err(e) => Some(Err(e)), | ||
| } | ||
| } | ||
| } | ||
|
|
||
| const DEFAULT_TIMEOUT: std::time::Duration = std::time::Duration::from_millis(500); | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think, by default, this method should never time out. The user asked us to wait indefinitely. If the user wants a timeout, they can do so by wrapping the returned future in a
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I hope that you're speaking about changes in Probably there could be introduced a new type WaitForever which would do just
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My argument was more that all you need is the ability to wait forever. If the user wants to stop waiting before the action succeeds, they can just wrap the future in |
||
| let mut wait = wait::Wait::new(self, DEFAULT_TIMEOUT); | ||
| wait.until(move |client| closure(search, client.clone())) | ||
| .await | ||
| .map_err(|timeout| error::CmdError::Timeout(timeout))? | ||
| } | ||
|
|
||
| /// Wait for the page to navigate to a new URL before proceeding. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| /// The module contains a wait primitive | ||
| use crate::Client; | ||
| use std::future::Future; | ||
| use std::time::Duration; | ||
|
|
||
| const DEFAULT_POOLING_INTERVAL: Duration = Duration::from_millis(500); | ||
|
|
||
| /// | ||
| #[derive(Debug)] | ||
| pub struct Wait<'a> { | ||
| waiter: DefaultWait<&'a mut Client>, | ||
| } | ||
|
|
||
| impl<'a> Wait<'a> { | ||
| /// | ||
| pub fn new(client: &'a mut Client, timeout: Duration) -> Self { | ||
| Self { | ||
| waiter: DefaultWait::new(client, timeout, DEFAULT_POOLING_INTERVAL), | ||
| } | ||
| } | ||
|
|
||
| pub(crate) async fn until<F, FF, R>(&mut self, condition: F) -> Result<R, Duration> | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the whole
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It was meant to be a It may worth it. |
||
| where | ||
| F: FnMut(&mut &mut Client) -> FF, | ||
| FF: Future<Output = Option<R>>, | ||
| { | ||
| self.waiter.until(condition).await | ||
| } | ||
| } | ||
|
|
||
| /// | ||
| #[derive(Debug)] | ||
| pub(crate) struct DefaultWait<T> { | ||
| input: T, | ||
| timeout: Duration, | ||
| pooling_interval: Duration, | ||
| } | ||
|
|
||
| impl<T> DefaultWait<T> { | ||
| /// | ||
| pub(crate) fn new(input: T, timeout: Duration, pooling_interval: Duration) -> Self { | ||
| Self { | ||
| input, | ||
| timeout, | ||
| pooling_interval, | ||
| } | ||
| } | ||
|
|
||
| /// | ||
| pub(crate) async fn until<F, FF, R>(&mut self, mut condition: F) -> Result<R, Duration> | ||
| where | ||
| F: FnMut(&mut T) -> FF, | ||
| FF: Future<Output = Option<R>>, | ||
| { | ||
| let now = std::time::Instant::now(); | ||
|
|
||
| loop { | ||
| match condition(&mut self.input).await { | ||
| Some(result) => return Ok(result), | ||
| None => (), | ||
| } | ||
|
|
||
| if now.elapsed() > self.timeout { | ||
| return Err(now.elapsed() - self.timeout)?; | ||
| } | ||
|
|
||
| tokio::time::delay_for(self.pooling_interval).await; | ||
| } | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think your problem likely stems from trying to define a
fn closurehere. If you just put this code directly into themove |client| {}below, I think you should be fine, and not need the clone?