В Selenium для Python и Java можно дождаться изменения URL-адреса на определенное значение. Есть ли в крейте thirtyfour
по ржавчине подобные функции?
Пример Java:
new WebDriverWait(driver, 20).Until(ExpectedConditions.UrlToBe("my-url"));
Прямо сейчас я написал вместо этого эту функцию ожидания:
use tokio::time;
use tokio::time::{Duration, Instant};
use anyhow::{bail, Context, Result};
use thirtyfour::{prelude::*, Key};
async fn wait_for_url(driver: &WebDriver, url: &str, timeout: &Duration) -> Result<()> {
let start = Instant::now();
loop {
time::sleep(Duration::from_millis(500)).await;
let current_url = driver.current_url().await?;
if current_url.as_str() == url {
return Ok(());
}
if &(time::Instant::now() - start) > timeout {
bail!("Timeout waiting for {url}. Current url is {current_url}");
}
}
}
@JeffC У тебя есть причина верить, что оно там? Я не смог его найти.
@JeffC Я просмотрел документацию, но я еще не очень привык к Rust и структуре документации по созданию.
Я не смог найти в thirtyfour
функцию, которая делает то, что вы хотите, и то, что вы сделали, по сути, является тем же, что делает версия Python «за капотом» (даже с тем же временем опроса), поэтому я думаю, что вам следует сделать именно это.
Вы можете сделать что-то подобное, чтобы воспроизвести поведение, которое вы получаете в Python/Java.
pub trait ExpectedDriverCondition<T> {
async fn check(&self, driver: &WebDriver) -> WebDriverResult<T>;
}
pub struct WebDriverWait<'a> {
handle: &'a WebDriver,
timeout: Duration,
sleep: Duration,
}
impl<'a> WebDriverWait<'a> {
pub fn new(
handle: &'a WebDriver,
timeout: Option<Duration>,
sleep: Option<Duration>,
) -> WebDriverWait {
WebDriverWait {
handle,
timeout: match timeout {
Some(timeout) => timeout,
None => BASE_TIMEOUT,
},
sleep: match sleep {
Some(sleep) => sleep,
None => BASE_SLEEP,
},
}
}
pub async fn until<T, U>(&self, ec: &T) -> WebDriverResult<U>
where
T: ExpectedDriverCondition<U>,
{
let start = Instant::now();
loop {
time::sleep(self.sleep).await;
let last_error = match ec.check(self.handle).await {
Ok(result) => break Ok(result),
Err(error) => error,
};
if &(time::Instant::now() - start) > &self.timeout {
return Err(WebDriverError::Timeout(format!(
"Expected condition timed out.\n{last_error}"
)));
}
}
}
}
Для базовых таймаутов и сна вы можете использовать:
const BASE_TIMEOUT: Duration = Duration::from_secs(5);
const BASE_SLEEP: Duration = Duration::from_millis(500);
И тогда вы можете определить свои EC следующим образом:
pub mod expected_conditions {
use super::{ExpectedCondition, ExpectedDriverCondition, Query};
use thirtyfour::{
error::{WebDriverError, WebDriverResult},
prelude::{By, WebDriver, WebElement},
};
pub struct UrlToBe<'a>(pub &'a String);
impl<'a> ExpectedDriverCondition<()> for UrlToBe<'a> {
async fn check(&self, driver: &WebDriver) -> WebDriverResult<()> {
let current_url = driver.current_url().await?;
if current_url.as_str() == self.0 {
return Ok(());
} else {
return Err(WebDriverError::CustomError("URLs don't match".into()));
}
}
}
}
Чтобы создать ожидание, вы можете сделать:
WebDriverWait::new(driver, Some(time::Duration::from_secs(60 * 60)), None)
.until(&expected_conditions::UrlToBe(&self.url))
.await?;
Таким образом, вы можете создавать больше EC по мере необходимости.
Кроме того, в качестве краткого примечания: поскольку WebDriver и WebElement имеют общие черты, вам, вероятно, придется определить WebElementWait и WebDriverWait с разными EC для каждого из них.
Я новичок в Rust, поэтому не знаю, является ли это идиоматическим способом, но я использовал его для проекта, и до сих пор он работал отлично.
Надеюсь, поможет!
Вы проверяли документы?