view tests setup

[?]
May 15, 2025, 4:55 PM
OPXFZKEBDHZZLXEJ2JRDYBOJH6YIN7UZNZYHVHMWMQVDTE2ZD53QC

Dependencies

  • [2] 6YZAVBWU Initial commit
  • [3] KLR5FRIB add fs state read/write of repos
  • [4] KT5UYXGK fix selection after adding file, add changed file diffs
  • [5] HOJZI52Y rename flowers_ui to inflorescence
  • [6] GWZGYNIB add view crate
  • [7] 23SFYK4Q big view refactor into a new crate

Change contents

  • edit in justfile at line 60
    [5.71]
    [2.1221]
    # Run tests for view changes. The `kind` can be set to "compare" or "accept".
    view kind="preview":
    TEST_KIND="{{kind}}" \
    cargo run --bin inflorescence-view-test-setup && \
    cargo test --package inflorescence-view -- view
  • edit in justfile at line 67
    [2.1222]
    [2.1222]
    # Watch tests for view changes. The `kind` can be set to "compare" or "accept".
    watch_view kind="preview":
    TEST_KIND="{{kind}}" \
    cargo watch \
    --exec "run --bin inflorescence-view-test-setup" \
    --exec "test --package inflorescence-view -- view" \
    --ignore "screenshots" \
    --ignore "preview"
  • file addition: testing.rs (----------)
    [6.85]
    use iced::{window, Element, Theme};
    use iced_test::Simulator;
    use std::{env, path::PathBuf, str::FromStr};
    /// This env var can be set to one of [`TestKind`] cases.
    pub const TEST_KIND_ENV_VAR: &str = "TEST_KIND";
    pub const SCREENSHOTS_DIR: &str = "screenshots";
    pub const PREVIEW_DIR: &str = "preview";
    #[derive(Debug, Default, strum::EnumString)]
    #[strum(ascii_case_insensitive)]
    pub enum TestKind {
    /// (default): Compare new screenshot against the stored ones. The tests with any difference will fail
    #[default]
    Compare,
    /// Preview the new screenshots in single resolution without comparing against the stored ones.
    Preview,
    /// Delete the old screenshots and save the new ones.
    Accept,
    }
    #[derive(Debug)]
    pub struct TestViewResult<'a> {
    pub name: &'a str,
    pub succeeded: bool,
    }
    #[derive(Debug, Clone, Copy)]
    pub enum Size {
    Fullscreen,
    Specific { width: f32, height: f32 },
    }
    /// The name of the test calling this function MUST start with "view".
    pub fn test_view<'a, 'b, Message, Renderer>(
    results: &mut Vec<TestViewResult<'b>>,
    uniq_name: &'b str,
    element: impl Into<Element<'a, Message, Theme, Renderer>>,
    size: Size,
    ) where
    Renderer: iced::advanced::Renderer + iced::advanced::renderer::Headless,
    {
    let test_kind = test_kind();
    let size = match size {
    Size::Fullscreen => iced::Size::new(512., 384.),
    Size::Specific { width, height } => iced::Size { width, height },
    };
    let mut sim =
    Simulator::with_size(iced::Settings::default(), size, element);
    let snapshot = sim.snapshot(&theme()).unwrap();
    let file_name = format!("{uniq_name}.png");
    match test_kind {
    TestKind::Compare => {
    let dir = PathBuf::from(PREVIEW_DIR);
    let matched = snapshot.matches_image(dir.join(&file_name)).unwrap();
    // Return result to collect all runs first
    results.push(TestViewResult {
    name: uniq_name,
    succeeded: matched,
    })
    }
    TestKind::Preview => {
    let dir = PathBuf::from(PREVIEW_DIR);
    let matched = snapshot.matches_image(dir.join(&file_name)).unwrap();
    assert!(matched, "The view {uniq_name} doesn't have a unique name");
    // Preview mode doesn't need a result
    }
    TestKind::Accept => {
    let dir = PathBuf::from(SCREENSHOTS_DIR);
    let matched = snapshot.matches_image(dir.join(&file_name)).unwrap();
    assert!(matched, "The view {uniq_name} doesn't have a unique name");
    // Accept mode doesn't need a result
    }
    }
    }
    pub fn test_kind() -> TestKind {
    if let Ok(val) = env::var(TEST_KIND_ENV_VAR) {
    let val = val.trim();
    if !val.is_empty() {
    return TestKind::from_str(val).unwrap_or_else(|err| {
    panic!("Unexpected test kind: {val}, failed parsing with {err}")
    });
    }
    }
    TestKind::default()
    }
    pub fn report_results(results: Vec<TestViewResult<'_>>) {
    if results.iter().any(|result| !result.succeeded) {
    panic!(
    "Changed views: {:#?}",
    results.into_iter().filter_map(
    |TestViewResult { name, succeeded }| if succeeded {
    None
    } else {
    Some(name)
    }
    )
    );
    }
    }
    fn theme() -> Theme {
    crate::theme(&(), window::Id::unique())
    }
  • edit in inflorescence_view/src/lib.rs at line 3
    [7.273]
    [7.273]
    pub mod testing;
  • edit in inflorescence_view/src/lib.rs at line 6
    [6.123][7.284:311]()
    use iced::{window, Theme};
  • edit in inflorescence_view/src/lib.rs at line 7
    [7.329]
    [7.329]
    use iced::{window, Theme};
  • edit in inflorescence_view/src/app.rs at line 3
    [7.12429]
    [7.12429]
    #[cfg(test)]
    mod test;
  • file addition: app (d--r------)
    [6.85]
  • file addition: test.rs (----------)
    [0.4206]
    use super::{cursor, file, view, State};
    use crate::testing::{report_results, test_view, Size};
    use libflorescence::{prelude::pijul::HashMap, repo};
    use iced::window;
    use std::{
    collections::{BTreeMap, BTreeSet},
    path::PathBuf,
    };
    #[test]
    fn view_app() {
    let window_id = window::Id::unique();
    let size = Size::Fullscreen;
    let mut results = vec![];
    // _________________________________________________________________________
    //
    let uniq_name = "app_loading_repo";
    let repo_path = PathBuf::from("test/repo/path");
    let repo = None;
    let cursor = cursor::State { selection: None };
    let record_msg = None;
    let diffs_state = HashMap::new();
    let diff = file::Diff::Loading;
    let state = State {
    repo_path: &repo_path,
    repo: repo.as_ref(),
    cursor: &cursor,
    record_msg: record_msg.as_ref(),
    diffs_state: &diffs_state,
    };
    test_view(
    &mut results,
    uniq_name,
    view(state, window_id, |_id| Some(&diff)),
    size,
    );
    // _________________________________________________________________________
    //
    let uniq_name = "app_loaded_empty_repo";
    let repo = Some(repo::State {
    dir_name: "path".to_string(),
    channel: "some_channel".to_string(),
    untracked_files: BTreeSet::new(),
    changed_files: BTreeMap::new(),
    log: vec![],
    });
    let state = State {
    repo_path: &repo_path,
    repo: repo.as_ref(),
    cursor: &cursor,
    record_msg: record_msg.as_ref(),
    diffs_state: &diffs_state,
    };
    test_view(
    &mut results,
    uniq_name,
    view(state, window_id, |_id| Some(&diff)),
    size,
    );
    // _________________________________________________________________________
    //
    report_results(results);
    }
  • file addition: bin (d--r------)
    [6.53]
  • file addition: view_test_setup.rs (----------)
    [0.6111]
    use inflorescence_view::testing::{
    test_kind, TestKind, PREVIEW_DIR, SCREENSHOTS_DIR,
    };
    use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};
    use std::io::Write;
    use std::path::Path;
    use std::{fs, path::PathBuf};
    const CRATE_DIR: &str = "inflorescence_view";
    fn main() {
    match test_kind() {
    TestKind::Compare => {
    // Copy stored screenshots into the preview dir to use for comparison
    let dir = PathBuf::from(CRATE_DIR).join(PREVIEW_DIR);
    let _res = fs::remove_dir_all(&dir);
    fs::create_dir_all(&dir).unwrap();
    for entry in fs::read_dir(&dir).unwrap() {
    let entry = entry.unwrap();
    let ty = entry.file_type().unwrap();
    if ty.is_dir() {
    panic!("Unexpectedly found dir {} inside screenshots dir {SCREENSHOTS_DIR}", entry.path().to_string_lossy());
    } else {
    fs::copy(entry.path(), dir.join(entry.file_name()))
    .unwrap();
    }
    }
    print_screenshot_target(&dir);
    }
    TestKind::Preview => {
    // Clear the preview dir to store new ones
    let dir = PathBuf::from(CRATE_DIR).join(PREVIEW_DIR);
    if let Ok(entries) = fs::read_dir(&dir) {
    for entry in entries {
    let entry = entry.unwrap();
    let ty = entry.file_type().unwrap();
    if ty.is_dir() {
    panic!("Unexpectedly found dir {} inside screenshots dir {PREVIEW_DIR}", entry.path().to_string_lossy());
    } else {
    fs::remove_file(entry.path()).unwrap();
    }
    }
    } else {
    fs::create_dir_all(&dir).unwrap();
    }
    print_screenshot_target(&dir);
    }
    TestKind::Accept => {
    // Clear the screenshots dir to store new ones
    let dir = PathBuf::from(CRATE_DIR).join(SCREENSHOTS_DIR);
    if let Ok(entries) = fs::read_dir(&dir) {
    for entry in entries {
    let entry = entry.unwrap();
    let ty = entry.file_type().unwrap();
    if ty.is_dir() {
    panic!("Unexpectedly found dir {} inside screenshots dir {PREVIEW_DIR}", entry.path().to_string_lossy());
    } else {
    fs::remove_file(entry.path()).unwrap();
    }
    }
    } else {
    fs::create_dir_all(&dir).unwrap();
    }
    print_screenshot_target(&dir);
    }
    }
    }
    fn print_screenshot_target(path: &Path) {
    let mut stdout = StandardStream::stdout(ColorChoice::Always);
    writeln!(&mut stdout, "").unwrap();
    stdout
    .set_color(ColorSpec::new().set_bg(Some(Color::Magenta)))
    .unwrap();
    write!(&mut stdout, "Screenshots will be saved in:").unwrap();
    stdout.reset().unwrap();
    writeln!(&mut stdout, " \"{}\"", path.to_string_lossy()).unwrap();
    writeln!(&mut stdout, "").unwrap();
    }
  • edit in inflorescence_view/Cargo.toml at line 8
    [6.512]
    [6.512]
    [lib]
    name = "inflorescence_view"
    path = "src/lib.rs"
    [[bin]]
    name = "inflorescence-view-test-setup"
    path = "bin/view_test_setup.rs"
  • edit in inflorescence_view/Cargo.toml at line 26
    [7.25345]
    [6.644]
    workspace = true
    [dependencies.strum]
  • edit in inflorescence_view/Cargo.toml at line 29
    [6.661]
    [dependencies.termcolor]
    workspace = true
  • edit in Cargo.toml at line 67
    [4.7938]
    [3.6043]
    [workspace.dependencies.termcolor]
    version = "1"
  • edit in Cargo.lock at line 2736
    [6.1062]
    [6.1062]
    "strum 0.27.1",
    "termcolor",
  • edit in .gitignore at line 2
    [2.107079]
    /inflorescence_view/preview