use crate::element::Element;
use crate::error::Result;
use crate::input::keyboard::{self, Key, KeyModifiers};
impl Element {
pub async fn type_text(&self, text: impl AsRef<str>) -> Result<()> {
let text = text.as_ref().to_string();
self.with_refresh(|| {
let text = text.clone();
async move {
self.focus().await?;
let input = self.inner.tab.input().clone();
keyboard::type_text_realistic(&input, &self.inner.tab, &text).await
}
})
.await
}
pub async fn type_text_fast(&self, text: impl AsRef<str>) -> Result<()> {
let text = text.as_ref().to_string();
self.with_refresh(|| {
let text = text.clone();
async move {
self.focus().await?;
let input = self.inner.tab.input().clone();
keyboard::type_text_fast(&input, &self.inner.tab, &text).await
}
})
.await
}
pub async fn press(&self, key: Key) -> Result<()> {
self.with_refresh(|| async move {
self.focus().await?;
let input = self.inner.tab.input().clone();
let mods = input.state.lock().await.modifiers_held.cdp_bits();
dispatch_key(&self.inner.tab, key, mods).await
})
.await
}
pub async fn press_with(&self, key: Key, mods: KeyModifiers) -> Result<()> {
self.with_refresh(|| async move {
self.focus().await?;
dispatch_key(&self.inner.tab, key, mods.cdp_bits()).await
})
.await
}
}
async fn dispatch_key(tab: &crate::tab::Tab, key: Key, modifier_bits: i32) -> Result<()> {
match key {
Key::Char(c) => keyboard::dispatch_char(tab, c, modifier_bits).await,
Key::Special(k) => keyboard::dispatch_special(tab, k, modifier_bits).await,
}
}
#[cfg(test)]
#[allow(clippy::panic, clippy::unwrap_used)]
mod tests {
use super::*;
use crate::tab::Tab;
use serde_json::{Value, json};
use zendriver_transport::SessionHandle;
use zendriver_transport::testing::MockConnection;
#[tokio::test]
async fn type_text_fast_emits_two_dispatchkeyevent_per_char() {
let (mut mock, conn) = MockConnection::pair();
let sess = SessionHandle::new(conn.clone(), "S1");
let tab = Tab::new_for_test(sess);
let el = Element::from_jsret(tab.clone(), 17, "R17".to_string());
let fut = tokio::spawn({
let e = el.clone();
async move { e.type_text_fast("hi").await }
});
for _ in 0..2 {
let id = mock.expect_cmd("Runtime.callFunctionOn").await;
mock.reply(
id,
json!({ "result": { "value": true, "type": "boolean" } }),
)
.await;
}
let id = mock.expect_cmd("Runtime.callFunctionOn").await;
let sent = mock.last_sent();
assert!(
sent["params"]["functionDeclaration"]
.as_str()
.unwrap()
.contains("this.focus()")
);
mock.reply(id, json!({ "result": { "type": "undefined" } }))
.await;
let expected = [
("h", "keyDown"),
("h", "keyUp"),
("i", "keyDown"),
("i", "keyUp"),
];
for (ch, kind) in expected {
let id = mock.expect_cmd("Input.dispatchKeyEvent").await;
let last = mock.last_sent();
assert_eq!(last["params"]["text"].as_str().unwrap(), ch);
assert_eq!(last["params"]["type"].as_str().unwrap(), kind);
mock.reply(id, Value::Null).await;
}
fut.await.unwrap().unwrap();
conn.shutdown();
}
}