kr580 2.0.0

Desktop KR580VM80 / Intel 8080 emulator.
Documentation
use super::window::{detached_printer_properties_position, detached_printer_setup_position};
use super::{
    PrinterPropertiesDialog, PrinterPropertiesFocus, PrinterPropertiesTab, PrinterPropertyDropdown,
    PrinterSetupDialog, PrinterSetupDropdown, PrinterSetupFocus, PrinterSetupTarget,
};
use crate::app::{DesktopApp, Message};
use crate::persistence::PrinterPreset;
use iced::Point;
use k580_ui::devices::printer::{PrinterInfo, PrinterSettings, PrinterStatus};
use std::time::{Duration, Instant};

#[test]
fn setup_dropdown_arrows_move_highlight_without_changing_selection() {
    let mut app = app_with_printers();

    let _ = app.update(Message::PrinterSetupDropdownToggled(
        PrinterSetupDropdown::Printer,
    ));
    let dialog = app.printer_setup_dialog.as_ref().unwrap();
    assert_eq!(dialog.open_dropdown, Some(PrinterSetupDropdown::Printer));
    assert_eq!(dialog.dropdown_highlight, Some(0));
    assert_eq!(dialog.selected_name.as_deref(), Some("First"));

    let _ = app.update(Message::ArrowKey(-1));
    let dialog = app.printer_setup_dialog.as_ref().unwrap();
    assert_eq!(dialog.dropdown_highlight, Some(1));
    assert_eq!(dialog.selected_name.as_deref(), Some("First"));
}

#[test]
fn setup_tab_cycles_forward_and_backward_over_enabled_controls() {
    let mut app = app_with_printers();
    assert!(!app.printer_setup_dialog.as_ref().unwrap().focus_visible);

    let _ = app.update(Message::FocusCycle { backward: false });
    assert_eq!(
        app.printer_setup_dialog.as_ref().unwrap().focus,
        PrinterSetupFocus::Cancel
    );
    assert!(app.printer_setup_dialog.as_ref().unwrap().focus_visible);

    let _ = app.update(Message::EnterPressed);
    assert!(!app.printer_setup_dialog.as_ref().unwrap().focus_visible);

    app.printer_setup_dialog.as_mut().unwrap().focus = PrinterSetupFocus::Printer;
    let _ = app.update(Message::FocusCycle { backward: true });
    assert_eq!(
        app.printer_setup_dialog.as_ref().unwrap().focus,
        PrinterSetupFocus::Close
    );
    assert!(app.printer_setup_dialog.as_ref().unwrap().focus_visible);
}

#[test]
fn escape_closes_dropdown_before_closing_setup() {
    let mut app = app_with_printers();
    let _ = app.update(Message::PrinterSetupDropdownToggled(
        PrinterSetupDropdown::Printer,
    ));

    let _ = app.update(Message::EscPressed);

    let dialog = app.printer_setup_dialog.as_ref().unwrap();
    assert_eq!(dialog.open_dropdown, None);
}

#[test]
fn setup_dropdown_dismissal_only_closes_its_source() {
    let mut app = app_with_printers();
    let _ = app.update(Message::PrinterSetupDropdownToggled(
        PrinterSetupDropdown::Printer,
    ));

    let _ = app.update(Message::PrinterSetupDropdownDismissed(
        PrinterSetupDropdown::Paper,
    ));
    assert_eq!(
        app.printer_setup_dialog.as_ref().unwrap().open_dropdown,
        Some(PrinterSetupDropdown::Printer)
    );
    let _ = app.update(Message::PrinterSetupDropdownDismissed(
        PrinterSetupDropdown::Printer,
    ));

    assert_eq!(
        app.printer_setup_dialog.as_ref().unwrap().open_dropdown,
        None
    );
}

#[test]
fn property_dropdown_arrows_track_profile_options() {
    let (mut app, _) = DesktopApp::with_initial_path(None);
    let presets = ["First", "Second"]
        .into_iter()
        .map(|name| PrinterPreset {
            name: name.to_owned(),
            settings: PrinterSettings::named("Printer".to_owned()),
        })
        .collect();
    let mut setup =
        PrinterSetupDialog::new(PrinterSetupTarget::Session, Some("Printer".to_owned()));
    let mut properties = PrinterPropertiesDialog::new(String::new(), presets);
    properties.loading = false;
    properties.selected_preset = Some("First".to_owned());
    setup.properties = Some(properties);
    app.printer_setup_dialog = Some(setup);

    let _ = app.update(Message::PrinterPropertyDropdownToggled(
        PrinterPropertyDropdown::Preset,
    ));
    let _ = app.update(Message::ArrowKey(-1));

    let properties = app
        .printer_setup_dialog
        .as_ref()
        .unwrap()
        .properties
        .as_ref()
        .unwrap();
    assert_eq!(properties.dropdown_highlight, Some(1));
    assert_eq!(properties.selected_preset.as_deref(), Some("First"));
}

#[test]
fn property_tabs_start_on_favorites_and_show_focus_only_after_keyboard_navigation() {
    let (mut app, _) = DesktopApp::with_initial_path(None);
    let mut setup =
        PrinterSetupDialog::new(PrinterSetupTarget::Session, Some("Printer".to_owned()));
    let mut properties = PrinterPropertiesDialog::new(String::new(), Vec::new());
    properties.loading = false;
    setup.properties = Some(properties);
    app.printer_setup_dialog = Some(setup);
    let properties = app
        .printer_setup_dialog
        .as_ref()
        .unwrap()
        .properties
        .as_ref()
        .unwrap();
    assert_eq!(
        properties.focus,
        PrinterPropertiesFocus::Tab(PrinterPropertiesTab::Favorites)
    );
    assert!(!properties.focus_visible);

    let _ = app.update(Message::PrinterPropertiesFocusResolved {
        focused: None,
        backward: false,
    });
    assert_eq!(
        app.printer_setup_dialog
            .as_ref()
            .unwrap()
            .properties
            .as_ref()
            .unwrap()
            .focus,
        PrinterPropertiesFocus::Tab(PrinterPropertiesTab::General)
    );
    assert!(
        app.printer_setup_dialog
            .as_ref()
            .unwrap()
            .properties
            .as_ref()
            .unwrap()
            .focus_visible
    );

    let _ = app.update(Message::EnterPressed);
    let properties = app
        .printer_setup_dialog
        .as_ref()
        .unwrap()
        .properties
        .as_ref()
        .unwrap();
    assert_eq!(properties.tab, PrinterPropertiesTab::General);
    assert!(!properties.focus_visible);

    let _ = app.update(Message::PrinterPropertiesTabSelected(
        PrinterPropertiesTab::Paper,
    ));
    let properties = app
        .printer_setup_dialog
        .as_ref()
        .unwrap()
        .properties
        .as_ref()
        .unwrap();
    assert_eq!(
        properties.focus,
        PrinterPropertiesFocus::Tab(PrinterPropertiesTab::Paper)
    );
    assert!(!properties.focus_visible);
}

#[test]
fn property_dropdown_dismissal_only_closes_its_source() {
    let (mut app, _) = DesktopApp::with_initial_path(None);
    let mut setup =
        PrinterSetupDialog::new(PrinterSetupTarget::Session, Some("Printer".to_owned()));
    let mut properties = PrinterPropertiesDialog::new(String::new(), Vec::new());
    properties.open_dropdown = Some(PrinterPropertyDropdown::Preset);
    setup.properties = Some(properties);
    app.printer_setup_dialog = Some(setup);

    let _ = app.update(Message::PrinterPropertyDropdownDismissed(
        PrinterPropertyDropdown::Paper,
    ));
    assert_eq!(
        app.printer_setup_dialog
            .as_ref()
            .unwrap()
            .properties
            .as_ref()
            .unwrap()
            .open_dropdown,
        Some(PrinterPropertyDropdown::Preset)
    );
    let _ = app.update(Message::PrinterPropertyDropdownDismissed(
        PrinterPropertyDropdown::Preset,
    ));

    assert_eq!(
        app.printer_setup_dialog
            .as_ref()
            .unwrap()
            .properties
            .as_ref()
            .unwrap()
            .open_dropdown,
        None
    );
}

fn app_with_printers() -> DesktopApp {
    let (mut app, _) = DesktopApp::with_initial_path(None);
    let mut dialog = PrinterSetupDialog::new(PrinterSetupTarget::Session, Some("First".to_owned()));
    dialog.loading = false;
    dialog.printers = vec![printer("First"), printer("Second")];
    app.printer_setup_dialog = Some(dialog);
    app
}

#[test]
fn detached_setup_uses_dedicated_window_without_replacing_printer() {
    let (mut app, _) = DesktopApp::with_initial_path(None);
    app.printer_open = true;
    app.printer_window.detached = true;
    let printer_window = iced::window::Id::unique();
    app.printer_window.id = Some(printer_window);

    let _ = app.open_printer_setup_dialog(PrinterSetupTarget::Session);
    assert!(!app.printer_setup_dialog.as_ref().unwrap().owner_ready);

    let _ = app.update(Message::PrinterSetupWindowPositionLoaded(Some(Point::new(
        680.0, 560.0,
    ))));
    let setup_window = app.printer_setup_window_id.unwrap();
    assert_ne!(setup_window, printer_window);
    assert_eq!(app.printer_window.id, Some(printer_window));
    let setup = app.printer_setup_dialog.as_ref().unwrap();
    assert!(!setup.owner_ready);
    assert_eq!(setup.owner_position, Some(Point::new(680.0, 560.0)));

    let _ = app.update(Message::WindowOpened(setup_window));
    assert!(app.printer_setup_dialog.as_ref().unwrap().owner_ready);
}

#[test]
fn detached_printer_dialogs_are_centered_on_printer() {
    assert_eq!(
        detached_printer_setup_position(Point::new(680.0, 560.0)),
        Point::new(700.0, 480.0)
    );
    assert_eq!(
        detached_printer_properties_position(Point::new(680.0, 560.0)),
        Point::new(540.0, 390.0)
    );
}

#[test]
fn detached_properties_use_their_own_window() {
    let (mut app, _) = DesktopApp::with_initial_path(None);
    app.printer_open = true;
    app.printer_window.detached = true;
    let printer_window = iced::window::Id::unique();
    let setup_window = iced::window::Id::unique();
    app.printer_window.id = Some(printer_window);
    app.printer_setup_window_id = Some(setup_window);

    let mut setup = PrinterSetupDialog::new(PrinterSetupTarget::Session, None);
    setup.owner_position = Some(Point::new(680.0, 560.0));
    setup.properties = Some(PrinterPropertiesDialog::new(String::new(), Vec::new()));
    setup.properties_surface_ready = false;
    app.printer_setup_dialog = Some(setup);

    let _ = app.open_detached_printer_properties_window();
    let properties_window = app.printer_properties_window_id.unwrap();
    assert_ne!(properties_window, setup_window);
    assert_ne!(properties_window, printer_window);
    assert_eq!(app.printer_setup_window_id, Some(setup_window));
    assert_eq!(app.printer_window.id, Some(printer_window));

    let _ = app.update(Message::WindowOpened(properties_window));
    assert!(
        app.printer_setup_dialog
            .as_ref()
            .unwrap()
            .properties_surface_ready
    );

    let _ = app.update(Message::WindowCloseRequested(setup_window));
    assert_eq!(app.printer_setup_window_id, Some(setup_window));
    assert_eq!(app.printer_properties_window_id, Some(properties_window));
    assert!(
        app.printer_setup_dialog
            .as_ref()
            .unwrap()
            .properties
            .is_some()
    );

    let _ = app.update(Message::ClosePrinterProperties);
    assert_eq!(app.printer_properties_window_id, None);
    assert_eq!(app.printer_setup_window_id, Some(setup_window));
    assert!(
        app.printer_setup_dialog
            .as_ref()
            .unwrap()
            .properties
            .is_none()
    );
}

#[test]
fn property_attention_pulse_rises_fades_and_expires() {
    let started_at = Instant::now();
    let mut properties = PrinterPropertiesDialog::new(String::new(), Vec::new());
    properties.restart_attention(started_at);

    assert_eq!(properties.attention_strength(started_at), 0.0);
    let rising = properties.attention_strength(started_at + Duration::from_millis(130));
    let peak = properties.attention_strength(started_at + Duration::from_millis(260));
    let falling = properties.attention_strength(started_at + Duration::from_millis(390));
    assert!((0.70..0.71).contains(&rising));
    assert!(peak > 0.99);
    assert!((0.70..0.71).contains(&falling));

    properties.expire_attention(started_at + Duration::from_millis(520));
    assert_eq!(properties.attention_strength(started_at), 0.0);
}

fn printer(name: &str) -> PrinterInfo {
    PrinterInfo {
        name: name.to_owned(),
        driver: String::new(),
        port: String::new(),
        location: String::new(),
        comment: String::new(),
        status: PrinterStatus::Unknown,
        is_default: false,
    }
}