diff options
| author | Can Celik <ogulcancelik@gmail.com> | 2026-09-18 21:50:26 +0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-09-18 21:50:26 +0300 |
| commit | 028780cebccc135d67a9ff97fbb0b11a150608de (patch) | |
| tree | b4282db58167539473fa1f0a7f4acd569db9ec53 /src/client/shell/tests/endpoints.rs | |
| parent | 241063f7ffb9583ba9b3438b32a82fcec3cf32a3 (diff) | |
fix: reveal selected agent when cycling the sidebar (#4355)
* fix: reveal selected agent when cycling the sidebar
* fix: reveal agent after endpoint activation
Diffstat (limited to 'src/client/shell/tests/endpoints.rs')
| -rw-r--r-- | src/client/shell/tests/endpoints.rs | 148 |
1 files changed, 148 insertions, 0 deletions
diff --git a/src/client/shell/tests/endpoints.rs b/src/client/shell/tests/endpoints.rs index 846806f1..e4914971 100644 --- a/src/client/shell/tests/endpoints.rs +++ b/src/client/shell/tests/endpoints.rs @@ -110,6 +110,154 @@ fn state_with_scrollable_agents() -> (ClientShellState, ClientEndpointId) { } #[test] +fn agent_navigation_reveals_offscreen_targets() { + use crate::input::KeybindAction; + + for action in [ + KeybindAction::NextAgent, + KeybindAction::PreviousAgent, + KeybindAction::FocusAgent(0), + ] { + let (mut state, remote) = state_with_scrollable_agents(); + let (endpoint_id, pane_id) = match action { + KeybindAction::NextAgent => (ClientEndpointId::Local, "pane_2"), + KeybindAction::PreviousAgent => (remote, "pane_8"), + _ => (ClientEndpointId::Local, "pane_1"), + }; + state.agent_scroll = if action == KeybindAction::PreviousAgent { + 0 + } else { + state.hits.agent_max_scroll + }; + state.compose(100, 28).unwrap(); + assert!(!state + .hits + .endpoint_agents + .iter() + .any(|(_, endpoint, pane)| { endpoint == &endpoint_id && pane == pane_id })); + + let mut outcome = ClientShellInput::default(); + assert!(state.handle_endpoint_navigation(action, &mut outcome)); + assert!(outcome.repaint, "agent navigation must request a frame"); + if endpoint_id != state.active_endpoint_id { + assert!(state.activate_endpoint_projection(&endpoint_id)); + } + state.compose(100, 28).unwrap(); + assert!( + state + .hits + .endpoint_agents + .iter() + .any(|(_, endpoint, pane)| { endpoint == &endpoint_id && pane == pane_id }), + "{action:?} must reveal the selected agent" + ); + } +} + +#[test] +fn agent_navigation_reveals_target_using_destination_sort() { + use crate::api::schema::{ + AgentViewBuiltinSortField, AgentViewSort, AgentViewSortField, AgentViewSortOrder, + }; + + let (mut state, remote) = state_with_scrollable_agents(); + for (endpoint_id, base) in [(ClientEndpointId::Local, 0), (remote.clone(), 8)] { + let mut projection = state + .endpoints + .iter() + .find(|endpoint| endpoint.endpoint_id == endpoint_id) + .unwrap() + .snapshot + .clone() + .unwrap(); + for (index, agent) in projection.agents.iter_mut().enumerate() { + agent.state_change_seq = base + index as u64; + } + if endpoint_id == remote { + projection.agent_view_label = Some("recent".into()); + } + state.set_endpoint_snapshot(&endpoint_id, projection); + } + state.set_test_endpoint_agent_view(&ClientEndpointId::Local, None); + let mut view = current_workspace_view(); + view.label = Some("recent".into()); + view.filter = None; + view.sort = vec![AgentViewSort { + field: AgentViewSortField::Builtin(AgentViewBuiltinSortField::StateChangeSeq), + order: AgentViewSortOrder::Desc, + }]; + state.set_test_endpoint_agent_view(&remote, Some(view)); + state.compose(100, 28).unwrap(); + + let mut outcome = ClientShellInput::default(); + assert!(state + .handle_endpoint_navigation(crate::input::KeybindAction::FocusAgent(15), &mut outcome,)); + assert!(matches!( + outcome.actions.as_slice(), + [ClientShellAction::ActivateEndpoint { + endpoint_id, + target: Some(ClientEndpointFocusTarget::Pane(pane_id)), + }] if endpoint_id == &remote && pane_id == "pane_8" + )); + // A superseded handoff restores its source before activating the new target. + assert!(state.activate_endpoint_projection(&ClientEndpointId::Local)); + state.compose(100, 28).unwrap(); + assert!(state.activate_endpoint_projection(&remote)); + state.compose(100, 28).unwrap(); + assert!(state + .hits + .endpoint_agents + .iter() + .any(|(_, endpoint, pane)| { endpoint == &remote && pane == "pane_8" })); +} + +#[test] +fn agent_navigation_reveal_is_cancelled_by_another_selection() { + for select_pane in [false, true] { + let (mut state, remote) = state_with_scrollable_agents(); + let scroll = state.agent_scroll; + let mut outcome = ClientShellInput::default(); + assert!(state + .handle_endpoint_navigation(crate::input::KeybindAction::PreviousAgent, &mut outcome,)); + assert_eq!(state.agent_scroll, scroll); + if select_pane { + assert!(state.focus_or_activate( + remote.clone(), + ClientEndpointFocusTarget::Pane("pane_1".into()), + &mut outcome, + )); + } else { + assert!(state.activate_endpoint(remote.clone(), &mut outcome)); + } + assert!(state.activate_endpoint_projection(&remote)); + state.compose(100, 28).unwrap(); + assert_eq!(state.agent_scroll, scroll); + } +} + +#[test] +fn agent_navigation_keeps_scroll_when_target_is_visible() { + let (mut state, _) = state_with_scrollable_agents(); + let (_, endpoint_id, pane_id) = state.hits.endpoint_agents[1].clone(); + let targets = super::super::aggregate_navigation::online_agent_targets( + &state.endpoints, + &state.active_endpoint_id, + state.config.agent_panel_sort, + ); + let index = targets + .iter() + .position(|target| target.endpoint_id == endpoint_id && target.pane_id == pane_id) + .unwrap(); + let scroll = state.agent_scroll; + assert!(state.handle_endpoint_navigation( + crate::input::KeybindAction::FocusAgent(index), + &mut ClientShellInput::default(), + )); + state.compose(100, 28).unwrap(); + assert_eq!(state.agent_scroll, scroll); +} + +#[test] fn switching_machines_preserves_aggregate_agent_scroll_and_visible_rows() { let (mut state, remote) = state_with_scrollable_agents(); for endpoint_id in [remote.clone(), ClientEndpointId::Local, remote] { |
