cydonia 0.1.13

Desktop workspace for the ACP agents you run, keeping what they produce as files on your disk
Documentation
use super::*;

fn startup() -> ChatItem {
    ChatItem::Process {
        command: "agent".into(),
        output: "startup warning".into(),
    }
}

#[test]
fn startup_logs_do_not_create_a_turn() {
    assert!(turns(&[]).is_empty());
    assert!(turns(&[startup()]).is_empty());
    let turns = turns(&[
        startup(),
        ChatItem::User("hello".into()),
        ChatItem::Agent("hi".into()),
    ]);
    assert_eq!(turns.len(), 1);
    assert_eq!(turns[0].range, 1..3);
}

#[test]
fn a_failure_before_the_first_prompt_is_visible() {
    let turns = turns(&[ChatItem::Notice {
        text: "connection failed".into(),
        failed: true,
    }]);
    assert_eq!(turns.len(), 1);
    assert_eq!(turns[0].range, 0..1);
}

#[test]
fn startup_tool_failures_are_included_in_the_work() {
    let turns = turns(&[ChatItem::Tool {
        id: "startup".into(),
        kind: ToolKind::Other,
        label: "MCP startup".into(),
        status: ToolStatus::Failure,
        output: "connection failed".into(),
    }]);
    assert_eq!(turns.len(), 1);
    assert_eq!(turns[0].range, 0..1);
}

#[test]
fn prose_before_a_trailing_tool_call_is_not_work() {
    let items = [
        ChatItem::User("why".into()),
        ChatItem::Agent("the long answer".into()),
        ChatItem::Tool {
            id: "status".into(),
            kind: ToolKind::Other,
            label: "board_set_card_status".into(),
            status: ToolStatus::Success,
            output: String::new(),
        },
        ChatItem::Agent("tagged".into()),
    ];
    assert!(!interim(&items[1]));
    assert!(interim(&items[2]));
    assert!(!interim(&items[3]));
}

#[gpui::test]
fn selecting_message_text_takes_focus_from_the_composer(cx: &mut gpui::TestAppContext) {
    use crate::view::component::composer::Composer;
    use bezel::gpui::{self, Focusable as _, Render};
    struct Messages {
        composer: gpui::Entity<Composer>,
        user: gpui::FocusHandle,
        agent: gpui::FocusHandle,
    }
    impl Render for Messages {
        fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
            div()
                .size_full()
                .flex()
                .flex_col()
                .child(
                    selectable::surface(&self.user, &markdown::parse("User message"), None, cx)
                        .id("user-text")
                        .debug_selector(|| "user-text".into())
                        .h(px(40.))
                        .child("User message"),
                )
                .child(
                    selectable::surface(&self.agent, &markdown::parse("Agent response"), None, cx)
                        .id("agent-text")
                        .debug_selector(|| "agent-text".into())
                        .h(px(40.))
                        .child("Agent response"),
                )
                .child(self.composer.clone())
        }
    }
    cx.update(|cx| Theme::install(bezel::theme::Appearance::Light, cx));
    let composer = cx.new(Composer::new);
    let window = cx.add_window(|window, cx| {
        window.focus(&composer.focus_handle(cx), cx);
        Messages {
            composer: composer.clone(),
            user: cx.focus_handle(),
            agent: cx.focus_handle(),
        }
    });
    let mut visual = gpui::VisualTestContext::from_window(window.into(), cx);
    visual.run_until_parked();
    for selector in ["user-text", "agent-text"] {
        let point = visual.debug_bounds(selector).unwrap().center();
        visual.simulate_click(point, gpui::Modifiers::default());
        visual.run_until_parked();
        window
            .update(&mut visual, |messages, window, cx| {
                let focus = if selector == "user-text" {
                    &messages.user
                } else {
                    &messages.agent
                };
                assert!(focus.is_focused(window));
                assert!(!messages.composer.focus_handle(cx).is_focused(window));
            })
            .unwrap();
    }
}

#[test]
fn only_the_item_the_press_landed_in_is_told_it_is_dragging() {
    use markdown::{Cursor, Part};
    let mut state = State::default();
    assert!(!state.dragging_in(0));
    state.point(1, Pointer::Down(Cursor::new(0, Part::Body, 0)));
    assert!(state.dragging_in(1));
    assert!(
        !state.dragging_in(0),
        "an item holding no selection is idle"
    );
    state.point(1, Pointer::Up);
    assert!(!state.dragging_in(1));
}