annatomic 0.2.0

The Annatomic annotation editor is intended to be used for the [RIDGES corpus](https://www.linguistik.hu-berlin.de/en/institut-en/professuren-en/korpuslinguistik/research/ridges-projekt). It is based on [graphANNIS](https://github.com/korpling/graphANNIS) and thus is internal data model is in principle suitable for a wide range of annotation concepts. "
Documentation
use std::sync::Arc;

use crate::{
    app::tests::{
        cleanup_test_project, create_app_with_corpus, create_test_harness, focus_and_wait,
        focus_wait_and_type, get_text_input, open_corpus_structure, wait_for_editor,
        wait_until_jobs_finished,
    },
    assert_screenshots,
};
use egui::{Id, Key, accesskit::Role, mutex::RwLock};
use egui_kittest::kittest::{NodeT, Queryable};
use egui_phosphor::regular::{PLUS_CIRCLE, TRASH};
use graphannis::aql;
use itertools::Itertools;

#[test]
fn show_metadata() {
    let app_state = create_app_with_corpus(
        "single_sentence",
        &include_bytes!("../../../../tests/data/single_sentence.graphml")[..],
    );
    let (mut harness, app_state) = create_test_harness(app_state);
    harness.run();

    // Select the corpus and the document
    open_corpus_structure("single_sentence", &mut harness, app_state.clone());
    harness.get_by_label("single_sentence/zossen").click();

    harness.run();
    harness.snapshot("show_metadata");
    cleanup_test_project(app_state);
}

fn query_count(query: &str, app_state: Arc<RwLock<crate::AnnatomicApp>>) -> usize {
    let app_state = app_state.read();

    let graph = app_state.project.get_selected_graph().unwrap().unwrap();

    let query = aql::parse(query, false).unwrap();
    let count = aql::execute_query_on_graph(&graph.read(), &query, true, None)
        .unwrap()
        .count();
    count
}

#[test]
fn undo_redo() {
    let app_state = create_app_with_corpus(
        "single_sentence",
        &include_bytes!("../../../../tests/data/single_sentence.graphml")[..],
    );
    let (mut harness, app_state) = create_test_harness(app_state);
    harness.run();

    // Select the corpus and the document
    open_corpus_structure("single_sentence", &mut harness, app_state.clone());
    harness.get_by_label("single_sentence/zossen").click();
    harness.run();

    // Edit the node name twice
    get_text_input(&mut harness, "zossen").focus();
    get_text_input(&mut harness, "zossen").type_text("-1");
    harness.run();
    get_text_input(&mut harness, "zossen-1").focus();
    harness.run();
    harness.key_press(Key::Enter);
    wait_until_jobs_finished(&mut harness, app_state.clone());

    assert_eq!(0, query_count("annis:doc=\"zossen\"", app_state.clone()));
    assert_eq!(1, query_count("annis:doc=\"zossen-1\"", app_state.clone()));
    let r1 = harness.try_snapshot("undo_redo_1");

    get_text_input(&mut harness, "zossen-1").focus();
    harness.run();
    harness.key_press(Key::Backspace);
    get_text_input(&mut harness, "zossen-1").type_text("2");
    harness.run();
    get_text_input(&mut harness, "zossen-2").focus();
    harness.run();
    harness.key_press(Key::Enter);

    wait_until_jobs_finished(&mut harness, app_state.clone());

    assert_eq!(1, query_count("annis:doc=\"zossen-2\"", app_state.clone()));
    assert_eq!(0, query_count("annis:doc=\"zossen-1\"", app_state.clone()));
    assert_eq!(0, query_count("annis:doc=\"zossen\"", app_state.clone()));
    let r2 = harness.try_snapshot("undo_redo_2");

    // Undo last change
    {
        let mut app_state = app_state.write();
        app_state.project.undo();
    }
    wait_for_editor(&mut harness, app_state.clone());

    assert_eq!(1, query_count("annis:doc=\"zossen-1\"", app_state.clone()));
    assert_eq!(0, query_count("annis:doc=\"zossen-2\"", app_state.clone()));
    assert_eq!(0, query_count("annis:doc=\"zossen\"", app_state.clone()));
    let r3 = harness.try_snapshot("undo_redo_3");

    // Redo, so the name should be "zossen-2" again
    {
        let mut app_state = app_state.write();
        app_state.project.redo();
    }
    wait_for_editor(&mut harness, app_state.clone());

    assert_eq!(1, query_count("annis:doc=\"zossen-2\"", app_state.clone()));
    assert_eq!(0, query_count("annis:doc=\"zossen-1\"", app_state.clone()));
    assert_eq!(0, query_count("annis:doc=\"zossen\"", app_state.clone()));
    let r4 = harness.try_snapshot("undo_redo_4");

    assert_screenshots![r1, r2, r3, r4];
    cleanup_test_project(app_state);
}

#[test]
fn add_and_delete_meta_entry() {
    let app_state = create_app_with_corpus(
        "single_sentence",
        &include_bytes!("../../../../tests/data/single_sentence.graphml")[..],
    );
    let (mut harness, app_state) = create_test_harness(app_state);
    harness.run();

    // Select the corpus and the document
    open_corpus_structure("single_sentence", &mut harness, app_state.clone());
    harness.get_by_label("single_sentence/zossen").click();
    harness.run();

    wait_for_editor(&mut harness, app_state.clone());

    let namespace_id = Id::new("new-metadata-entry-ns");
    focus_and_wait(&mut harness, namespace_id);
    harness
        .get_by(|n| n.id().0 == namespace_id.value())
        .type_text("test");
    harness.run();

    let name_id = Id::new("new-metadata-entry-name");
    focus_and_wait(&mut harness, name_id);
    harness
        .get_by(|n| n.id().0 == name_id.value())
        .type_text("example");
    harness.run();

    let value_id = Id::new("new-metadata-entry-value");
    focus_and_wait(&mut harness, value_id);
    // Fill out the the value text field
    let text_value = harness
        .get_all_by_role(Role::TextInput)
        .filter(|t| t.accesskit_node().id().0 == value_id.value())
        .next()
        .unwrap();
    text_value.type_text("example-value");
    harness.run();

    harness
        .get_all_by_label(PLUS_CIRCLE)
        .last()
        .unwrap()
        .click();

    wait_for_editor(&mut harness, app_state.clone());
    focus_and_wait(&mut harness, value_id);

    let r1 = harness.try_snapshot("after-adding-metadata");

    // Delete the entry again
    harness.get_all_by_label(TRASH).last().unwrap().click();
    wait_for_editor(&mut harness, app_state.clone());
    focus_and_wait(&mut harness, value_id);

    let r2 = harness.try_snapshot("after-deleting-metadata");

    assert_screenshots![r1, r2];
    cleanup_test_project(app_state);
}

#[test]
fn add_document() {
    let app_state = create_app_with_corpus(
        "single_sentence",
        &include_bytes!("../../../../tests/data/single_sentence.graphml")[..],
    );
    let (mut harness, app_state) = create_test_harness(app_state);
    harness.run();

    // Select the corpus and click on the "Add" button
    open_corpus_structure("single_sentence", &mut harness, app_state.clone());
    harness
        .get_all_by_label(PLUS_CIRCLE)
        .next()
        .unwrap()
        .click();
    harness.run();

    harness.get_by_label("single_sentence/new").click();
    wait_for_editor(&mut harness, app_state.clone());

    // Change the annis:node_name annotation
    get_text_input(&mut harness, "single_sentence/new").focus();
    harness.key_press(Key::Backspace);
    harness.key_press(Key::Backspace);
    harness.key_press(Key::Backspace);
    get_text_input(&mut harness, "single_sentence/new").type_text("bernau");
    harness.run();
    get_text_input(&mut harness, "single_sentence/bernau").focus();
    harness.key_press(Key::Enter);
    harness.run();

    let r1 = harness.try_snapshot("add-document-name-changed");

    // Add the "annis:doc" annotation
    focus_wait_and_type(&mut harness, Id::new("new-metadata-entry-ns"), "annis");
    focus_wait_and_type(&mut harness, Id::new("new-metadata-entry-name"), "doc");
    focus_wait_and_type(
        &mut harness,
        Id::new("new-metadata-entry-value"),
        "new-document",
    );

    harness
        .get_all_by_label(PLUS_CIRCLE)
        .last()
        .unwrap()
        .click();
    wait_until_jobs_finished(&mut harness, app_state.clone());
    focus_and_wait(&mut harness, Id::new("new-metadata-entry-value"));

    let r2 = harness.try_snapshot("add-document");

    assert_screenshots![r1, r2];
    cleanup_test_project(app_state);
}

#[test]
fn delete_document() {
    let app_state = create_app_with_corpus(
        "single_sentence",
        &include_bytes!("../../../../tests/data/single_sentence.graphml")[..],
    );
    let (mut harness, app_state) = create_test_harness(app_state);
    harness.run();

    // Select the corpus and click on the "Add" button
    open_corpus_structure("single_sentence", &mut harness, app_state.clone());
    harness.get_by_label("single_sentence/zossen").click();
    wait_for_editor(&mut harness, app_state.clone());

    let trash_buttons = harness.get_all_by_label(TRASH).collect_vec();
    trash_buttons[0].click();
    wait_until_jobs_finished(&mut harness, app_state.clone());

    assert_eq!(
        0,
        query_count("node_name=/single_sentence\\/zossen.*/", app_state.clone())
    );

    harness.snapshot("delete-document");
    cleanup_test_project(app_state);
}

#[test]
fn filter_document() {
    let app_state = create_app_with_corpus(
        "CorpusGraph",
        &include_bytes!("../../../../tests/data/CorpusGraph.graphml")[..],
    );
    let (mut harness, app_state) = create_test_harness(app_state);
    harness.run();

    open_corpus_structure("CorpusGraph", &mut harness, app_state.clone());
    wait_for_editor(&mut harness, app_state);

    // Click on both sub-corpora to expand their documents
    harness.get_by_label("CorpusGraph/sub1").click();
    harness.run();
    harness.get_by_label("CorpusGraph/sub2").click();
    harness.run();

    // Filter for a specific document
    harness.get_by_label_contains("Filter").focus();
    harness.run();
    harness.get_by_label_contains("Filter").type_text("doc2");
    harness.run();
    harness.snapshot("filter-document");
}

#[test]
fn filter_subcorpus() {
    let app_state = create_app_with_corpus(
        "CorpusGraph",
        &include_bytes!("../../../../tests/data/CorpusGraph.graphml")[..],
    );
    let (mut harness, app_state) = create_test_harness(app_state);
    harness.run();

    open_corpus_structure("CorpusGraph", &mut harness, app_state.clone());
    wait_for_editor(&mut harness, app_state.clone());

    // Click on both sub-corpora to expand their documents
    harness.get_by_label("CorpusGraph/sub1").click();
    harness.run();
    harness.get_by_label("CorpusGraph/sub2").click();
    harness.run();

    // Filter for a part of the node name
    harness.get_by_label_contains("Filter").focus();
    harness.run();
    harness.get_by_label_contains("Filter").type_text("sub2");
    harness.run();
    harness.snapshot("filter-subcorpus");
}