use std::{io::Read, path::PathBuf};
use anyhow::Context;
use egui::{Id, accesskit::Role, mutex::RwLock};
use egui_kittest::{
Harness,
kittest::{NodeT, Queryable},
};
use graphannis::model::AnnotationComponentType;
use tempfile::{TempDir, env};
use super::*;
pub const MAX_WAIT_STEPS: usize = 120;
pub(crate) fn create_app_with_corpus<R: Read, S: Into<String>>(
corpus_name: S,
graphml: R,
) -> crate::AnnatomicApp {
let (mut graph, _config) = graphannis_core::graph::serialization::graphml::import::<
AnnotationComponentType,
_,
_,
>(graphml, false, |_| {})
.unwrap();
let graph_dir = TempDir::new().unwrap();
graph.persist_to(graph_dir.path()).unwrap();
let mut app_state = crate::AnnatomicApp::default();
app_state
.project
.corpus_locations
.insert(corpus_name.into(), graph_dir.keep());
app_state
}
pub(crate) fn cleanup_test_project(app: Arc<RwLock<crate::AnnatomicApp>>) {
let mut app = app.write();
let default_temp_dir = env::temp_dir();
for (_name, location) in app.project.corpus_locations.iter() {
if location.ancestors().any(|a| a == default_temp_dir) {
std::fs::remove_dir_all(location).unwrap();
}
}
app.project.corpus_locations.clear();
}
pub(crate) fn create_test_harness(
app_state: crate::AnnatomicApp,
) -> (Harness<'static>, Arc<RwLock<crate::AnnatomicApp>>) {
let app_state = Arc::new(RwLock::new(app_state));
let result_app_state = app_state.clone();
let app = move |ctx: &egui::Context| {
let frame_info = IntegrationInfo {
cpu_usage: Some(3.14),
};
let mut app_state = app_state.write();
set_fonts(ctx);
app_state.show(ctx, &frame_info);
};
let harness = Harness::builder()
.with_size(egui::Vec2::new(800.0, 600.0))
.with_max_steps(24)
.build(app);
(harness, result_app_state.clone())
}
pub(crate) fn create_test_harness_with_document_editor(
app: crate::AnnatomicApp,
corpus: &str,
document_name: &str,
) -> (Harness<'static>, Arc<RwLock<crate::AnnatomicApp>>) {
let (mut harness, app) = create_test_harness(app);
open_corpus_structure(corpus, &mut harness, app.clone());
harness.get_by_label(document_name).click();
harness.run();
harness.get_by_label(OPEN_LABEL.as_str()).click();
wait_for_editor(&mut harness, app.clone());
(harness, app)
}
pub(crate) fn open_corpus_structure(
corpus: &str,
harness: &mut Harness<'static>,
app_state: Arc<RwLock<crate::AnnatomicApp>>,
) {
harness.get_by_label(corpus).click();
wait_until_jobs_finished(harness, app_state.clone());
harness.get_by_label(OPEN_LABEL.as_str()).click();
wait_for_editor(harness, app_state);
}
pub(crate) fn wait_for_editor(
harness: &mut Harness<'static>,
app_state: Arc<RwLock<crate::AnnatomicApp>>,
) {
for i in 0..MAX_WAIT_STEPS {
harness.step();
let app_state = app_state.read();
if i > 3 && app_state.current_editor.get().is_some() {
break;
}
}
wait_until_jobs_finished(harness, app_state);
}
pub(crate) fn focus_and_wait(harness: &mut Harness<'static>, id: Id) {
harness.get_by(|n| n.id().0 == id.value()).focus();
for i in 0..MAX_WAIT_STEPS {
harness.step();
if i > 3 && harness.get_by(|n| n.id().0 == id.value()).is_focused() {
break;
}
}
harness.step();
}
pub(crate) fn focus_wait_and_type(harness: &mut Harness<'static>, id: Id, text: &str) {
focus_and_wait(harness, id);
let text_value = harness
.get_all_by_role(Role::TextInput)
.filter(|t| t.accesskit_node().id().0 == id.value())
.next()
.unwrap();
text_value.type_text(text);
harness.step();
}
pub(crate) fn wait_for_editor_vanished(
harness: &mut Harness<'static>,
app_state: Arc<RwLock<crate::AnnatomicApp>>,
) {
for i in 0..MAX_WAIT_STEPS {
harness.step();
let app_state = app_state.read();
if i > 3 && app_state.current_editor.get().is_none() {
break;
}
}
wait_until_jobs_finished(harness, app_state);
}
pub(crate) fn wait_until_jobs_finished(
harness: &mut Harness<'static>,
app_state: Arc<RwLock<crate::AnnatomicApp>>,
) {
harness.run_steps(3);
let mut steps_without_jobs = 0;
for _ in 0..MAX_WAIT_STEPS {
harness.step();
let app_state = app_state.read();
if !app_state.jobs.has_active_jobs() {
steps_without_jobs += 1;
} else {
steps_without_jobs = 0;
}
if steps_without_jobs > 10 {
break;
}
}
wait_until_notifications_finished(harness, app_state);
}
pub(crate) fn wait_until_notifications_finished(
harness: &mut Harness<'static>,
app_state: Arc<RwLock<crate::AnnatomicApp>>,
) {
let mut steps_without_notification = 0;
for _ in 0..MAX_WAIT_STEPS {
harness.step();
let app_state = app_state.read();
if !app_state.notifier.is_empty() {
steps_without_notification += 1;
} else {
steps_without_notification = 0;
}
if steps_without_notification > 10 {
break;
}
}
harness.step();
}
pub(crate) fn get_text_input<'a>(
harness: &'a Harness<'_>,
value: &'a str,
) -> egui_kittest::Node<'a> {
harness
.get_all_by_value(value)
.filter(|n| n.accesskit_node().role() == Role::TextInput)
.next()
.context(format!("Missing text input with value \"{value}\""))
.unwrap()
}
#[macro_export]
macro_rules! assert_screenshots {
($($x:expr),* ) => {
$(
match $x {
Ok(_) => {}
Err(err) => {
panic!("{}", err);
}
}
)*
};
}
#[test]
fn show_main_page() {
let mut app_state = crate::AnnatomicApp::default();
app_state
.project
.corpus_locations
.insert("single_sentence".to_string(), PathBuf::default());
app_state
.project
.corpus_locations
.insert("test".to_string(), PathBuf::default());
let (mut harness, _) = create_test_harness(app_state);
harness.run();
harness.snapshot("show_main_page");
}