agent-desktop/tests/integration/snapshot_test.rs
Lahfir a346f242c2 feat: Phase 1 foundation — workspace scaffold, core engine, macOS adapter, 31 commands
Implements the complete agent-desktop Phase 1 specification:

- Workspace: 5-crate layout (core, macos, windows/linux stubs, binary)
- Core: AccessibilityNode, Action, ErrorCode, PlatformAdapter trait, RefMap
  with atomic writes, SnapshotEngine with depth-first ref allocation
- macOS adapter: AXUIElement tree traversal, action execution, input
  synthesis via CGEvent, screenshot via CGWindowListCreateImage, clipboard
  via pbpaste/pbcopy, window listing and app management via osascript
- 31 CLI subcommands via clap derive: snapshot, find, screenshot, get, is,
  click, double-click, right-click, type, set-value, focus, select, toggle,
  expand, collapse, scroll, press, launch, close-app, list-windows,
  list-apps, focus-window, clipboard-get/set, wait, status, permissions,
  version, batch
- Windows/Linux: not-supported stubs ready for Phase 2 implementation
- JSON output contract: {version,ok,command,data} envelope with structured
  error payloads including SCREAMING_SNAKE_CASE error codes
- Ref system: @e{N} sequential refs for interactive elements, stored at
  ~/.agent-desktop/last_refmap.json with 0o600/0o700 permissions
- CI: GitHub Actions macOS runner with dependency isolation check, clippy,
  unit tests, release build, and 15MB binary size gate
2026-02-19 10:44:38 -08:00

93 lines
3 KiB
Rust

/// Integration tests for the snapshot command.
///
/// These tests require macOS with Accessibility permissions granted to the
/// terminal running the tests. They are skipped automatically on other
/// platforms or when the binary is not built.
#[cfg(test)]
mod tests {
use std::process::Command;
fn agent_desktop_bin() -> std::path::PathBuf {
let mut p = std::env::current_exe().unwrap();
p.pop();
p.pop();
p.push("agent-desktop");
p
}
#[test]
#[cfg(target_os = "macos")]
#[ignore = "requires Accessibility permissions and running macOS apps"]
fn snapshot_finder_returns_non_empty_tree() {
let bin = agent_desktop_bin();
let output = Command::new(&bin)
.args(["snapshot", "--app", "Finder"])
.output()
.expect("failed to run agent-desktop");
let stdout = String::from_utf8_lossy(&output.stdout);
let json: serde_json::Value =
serde_json::from_str(&stdout).expect("output is not valid JSON");
assert_eq!(json["ok"], true);
assert!(json["data"]["ref_count"].as_u64().unwrap_or(0) > 0);
}
#[test]
#[cfg(target_os = "macos")]
#[ignore = "requires Accessibility permissions and running macOS apps"]
fn snapshot_textedit_returns_refs() {
let bin = agent_desktop_bin();
let output = Command::new(&bin)
.args(["snapshot", "--app", "TextEdit"])
.output()
.expect("failed to run agent-desktop");
let stdout = String::from_utf8_lossy(&output.stdout);
let json: serde_json::Value =
serde_json::from_str(&stdout).expect("output is not valid JSON");
assert_eq!(json["ok"], true);
}
#[test]
fn version_command_outputs_json() {
let bin = agent_desktop_bin();
if !bin.exists() {
return; // binary not built yet
}
let output = Command::new(&bin)
.args(["version", "--json"])
.output()
.expect("failed to run agent-desktop");
let stdout = String::from_utf8_lossy(&output.stdout);
let json: serde_json::Value =
serde_json::from_str(&stdout).expect("output is not valid JSON");
assert_eq!(json["ok"], true);
assert!(json["data"]["version"].is_string());
}
#[test]
fn list_apps_on_non_macos_errors_gracefully() {
#[cfg(not(target_os = "macos"))]
{
let bin = agent_desktop_bin();
if !bin.exists() {
return;
}
let output = Command::new(&bin)
.args(["list-apps"])
.output()
.expect("failed to run agent-desktop");
let stdout = String::from_utf8_lossy(&output.stdout);
let json: serde_json::Value =
serde_json::from_str(&stdout).expect("output is not valid JSON");
assert_eq!(json["ok"], false);
assert_eq!(json["error"]["code"], "PLATFORM_NOT_SUPPORTED");
}
}
}