auv-cli-invoke 0.0.22

Application Use Via... script-able OS automation library
use std::collections::BTreeMap;

use image::RgbaImage;

use super::*;
use crate::InvokeCancellation;

fn inputs(values: [(&str, &str); 4]) -> BTreeMap<String, String> {
  values.into_iter().map(|(name, value)| (name.to_string(), value.to_string())).collect()
}

#[test]
fn capture_region_validates_the_same_region_before_dry_and_live_branches() {
  let valid_dry_run = InvokeCommandInput {
    command_id: "screen.captureRegion".to_string(),
    target: None,
    inputs: inputs([("x", "1"), ("y", "2"), ("width", "3"), ("height", "4")]),
    typed_args: None,
    dry_run: true,
    cancellation: InvokeCancellation::new(),
  };
  assert!(futures_executor::block_on(capture_region_invoke_command().invoke(valid_dry_run)).is_ok());

  let invalid_live = InvokeCommandInput {
    command_id: "screen.captureRegion".to_string(),
    target: None,
    inputs: inputs([("x", "1"), ("y", "2"), ("width", "0"), ("height", "4")]),
    typed_args: None,
    dry_run: false,
    cancellation: InvokeCancellation::new(),
  };
  let error = futures_executor::block_on(capture_region_invoke_command().invoke(invalid_live))
    .expect_err("invalid live region must fail before capture");
  assert!(error.message.contains("greater than zero"));
}

#[cfg(target_os = "macos")]
#[test]
fn screen_text_output_returns_typed_ocr_matches() {
  let matches = auv_driver::OcrMatches {
    matches: vec![auv_driver::OcrMatch {
      text: "Pause".to_string(),
      confidence: 0.95,
      bounds: auv_driver::Rect::new(10.0, 20.0, 80.0, 24.0),
    }],
  };

  let output = screen_text_matches_output(&matches).expect("OCR result should serialize");

  assert_eq!(output.result(), Some(&serde_json::to_value(&matches).expect("fixture should serialize")));
}

#[test]
fn region_capture_result_keeps_pixels_out_of_json() {
  let capture = auv_driver::RegionCapture {
    display: auv_driver::Display {
      id: "display_1".to_string(),
      name: None,
      frame: auv_driver::Rect::new(0.0, 0.0, 1920.0, 1080.0),
      coordinate_space: auv_driver::CoordinateSpace::Screen,
      scale_factor: 1.0,
      is_primary: false,
      is_builtin: Some(false),
    },
    capture: auv_driver::Capture {
      origin: None,
      image: RgbaImage::new(320, 180),
      bounds: auv_driver::Rect::new(100.0, 120.0, 320.0, 180.0),
      scale_factor: 1.0,
      backend: "fixture-region".to_string(),
      fallback_reason: Some("fixture fallback".to_string()),
    },
  };

  let output = region_capture_output(&capture, None).expect("region result should serialize");
  let result = output.result().expect("capture should have a result");

  assert_eq!(result["display"]["id"], "display_1");
  assert_eq!(result["capture"]["bounds"]["origin"]["x"], 100.0);
  assert_eq!(result["capture"]["pixel_dimensions"]["width"], 320);
  assert_eq!(result["capture"]["backend"], "fixture-region");
  assert_eq!(result["capture"]["fallback_reason"], "fixture fallback");
  assert!(result.get("image").is_none());
}

#[test]
fn screen_text_click_result_keeps_resolution_and_delivery_together() {
  let click = ScreenTextClick {
    matches: auv_driver::OcrMatches {
      matches: vec![auv_driver::OcrMatch {
        text: "Pause".to_string(),
        confidence: 0.97,
        bounds: auv_driver::Rect::new(40.0, 50.0, 70.0, 20.0),
      }],
    },
    point: auv_driver::Point::new(75.0, 60.0),
    action: auv_driver::InputActionResult::single_success(auv_driver::InputDeliveryPath::ForegroundSystemEvents),
  };

  let output = screen_text_click_output(&click).expect("screen click result should serialize");
  let result = output.result().expect("click should have a result");

  assert_eq!(result["matches"]["matches"][0]["text"], "Pause");
  assert_eq!(result["point"]["x"], 75.0);
  assert_eq!(result["action"]["selected_path"], "foreground_system_events");
  let report = output.report.as_ref().expect("screen click report");
  assert_eq!(report_field(report, "Delivery"), "delivered");
  assert_eq!(report_field(report, "Verification"), "delivery_only");
  assert_eq!(report_field(report, "Path"), "foreground_system_events");
}

fn report_field<'a>(report: &'a InvokeReport, label: &str) -> &'a str {
  report.fields.iter().find(|field| field.label == label).map(|field| field.value.as_str()).expect("report field")
}