use smix_input::{KeyName, SwipeDirection};
use smix_recorder_ir::IRAction;
use smix_sdk::{App, ExpectationFailure, Selector};
use std::sync::{Arc, Mutex};
use std::time::{SystemTime, UNIX_EPOCH};
#[derive(Clone, Default)]
pub struct RecordSession {
actions: Arc<Mutex<Vec<IRAction>>>,
}
impl RecordSession {
pub fn new() -> Self {
Self::default()
}
pub fn record(&self, action: IRAction) {
self.actions
.lock()
.expect("RecordSession lock poisoned")
.push(action);
}
pub fn snapshot(&self) -> Vec<IRAction> {
self.actions
.lock()
.expect("RecordSession lock poisoned")
.clone()
}
pub fn stop(&self) -> Vec<IRAction> {
let mut g = self.actions.lock().expect("RecordSession lock poisoned");
std::mem::take(&mut *g)
}
pub fn len(&self) -> usize {
self.actions
.lock()
.expect("RecordSession lock poisoned")
.len()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
fn now_ms() -> f64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|d| d.as_secs_f64() * 1000.0)
.unwrap_or(0.0)
}
pub struct RecordingApp<'a> {
app: &'a App,
session: RecordSession,
}
impl<'a> RecordingApp<'a> {
pub fn new(app: &'a App, session: RecordSession) -> Self {
Self { app, session }
}
pub fn session(&self) -> &RecordSession {
&self.session
}
pub async fn launch(&self, bundle_id: &str) -> Result<(), ExpectationFailure> {
self.app.launch(bundle_id).await
}
pub async fn terminate(&self, bundle_id: &str) -> Result<(), ExpectationFailure> {
self.app.terminate(bundle_id).await
}
pub async fn tap(&self, selector: &Selector) -> Result<(), ExpectationFailure> {
self.session.record(IRAction::Tap {
selector: selector.clone(),
timestamp_ms: now_ms(),
});
self.app.tap(selector).await
}
pub async fn fill(&self, selector: &Selector, text: &str) -> Result<(), ExpectationFailure> {
self.session.record(IRAction::Fill {
selector: selector.clone(),
text: text.to_string(),
timestamp_ms: now_ms(),
});
self.app.fill(selector, text).await
}
pub async fn clear(&self, selector: &Selector) -> Result<(), ExpectationFailure> {
self.session.record(IRAction::Clear {
selector: selector.clone(),
timestamp_ms: now_ms(),
});
self.app.clear(selector).await
}
pub async fn press_key(&self, key: KeyName) -> Result<(), ExpectationFailure> {
self.session.record(IRAction::PressKey {
key,
timestamp_ms: now_ms(),
});
self.app.press_key(key).await
}
pub async fn swipe_once(&self, direction: SwipeDirection) -> Result<(), ExpectationFailure> {
self.session.record(IRAction::Swipe {
direction,
from: None,
timestamp_ms: now_ms(),
});
self.app.swipe_once(direction).await
}
pub async fn swipe_from(
&self,
direction: SwipeDirection,
from: &Selector,
) -> Result<(), ExpectationFailure> {
self.session.record(IRAction::Swipe {
direction,
from: Some(from.clone()),
timestamp_ms: now_ms(),
});
self.app.swipe_once(direction).await
}
pub async fn go_back(&self) -> Result<(), ExpectationFailure> {
self.session.record(IRAction::GoBack {
timestamp_ms: now_ms(),
});
self.app.go_back().await
}
pub async fn wait_for(
&self,
selector: &Selector,
timeout: std::time::Duration,
) -> Result<(), ExpectationFailure> {
self.session.record(IRAction::WaitFor {
selector: selector.clone(),
timestamp_ms: now_ms(),
});
self.app.wait_for(selector, timeout).await.map(|_| ())
}
pub async fn hide_keyboard(&self) -> Result<(), ExpectationFailure> {
self.session.record(IRAction::HideKeyboard {
timestamp_ms: now_ms(),
});
self.app.hide_keyboard().await
}
}