mirror of
https://github.com/openai/codex.git
synced 2026-02-01 22:47:52 +00:00
tests
This commit is contained in:
@@ -2385,6 +2385,84 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn footer_flash_overrides_footer_hint_override() {
|
||||
let (tx, _rx) = unbounded_channel::<AppEvent>();
|
||||
let sender = AppEventSender::new(tx);
|
||||
let mut composer = ChatComposer::new(
|
||||
true,
|
||||
sender,
|
||||
false,
|
||||
"Ask Codex to do anything".to_string(),
|
||||
false,
|
||||
);
|
||||
composer.set_footer_hint_override(Some(vec![("K".to_string(), "label".to_string())]));
|
||||
composer.show_footer_flash(Line::from("FLASH"), Duration::from_secs(10));
|
||||
|
||||
let area = Rect::new(0, 0, 60, 6);
|
||||
let mut buf = Buffer::empty(area);
|
||||
composer.render(area, &mut buf);
|
||||
|
||||
let mut bottom_row = String::new();
|
||||
for x in 0..area.width {
|
||||
bottom_row.push(
|
||||
buf[(x, area.height - 1)]
|
||||
.symbol()
|
||||
.chars()
|
||||
.next()
|
||||
.unwrap_or(' '),
|
||||
);
|
||||
}
|
||||
assert!(
|
||||
bottom_row.contains("FLASH"),
|
||||
"expected flash content to render in footer row, saw: {bottom_row:?}",
|
||||
);
|
||||
assert!(
|
||||
!bottom_row.contains("K label"),
|
||||
"expected flash to override hint override, saw: {bottom_row:?}",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn footer_flash_expires_and_falls_back_to_hint_override() {
|
||||
let (tx, _rx) = unbounded_channel::<AppEvent>();
|
||||
let sender = AppEventSender::new(tx);
|
||||
let mut composer = ChatComposer::new(
|
||||
true,
|
||||
sender,
|
||||
false,
|
||||
"Ask Codex to do anything".to_string(),
|
||||
false,
|
||||
);
|
||||
composer.set_footer_hint_override(Some(vec![("K".to_string(), "label".to_string())]));
|
||||
composer.show_footer_flash(Line::from("FLASH"), Duration::from_secs(10));
|
||||
composer.footer_flash.as_mut().unwrap().expires_at =
|
||||
Instant::now() - Duration::from_secs(1);
|
||||
|
||||
let area = Rect::new(0, 0, 60, 6);
|
||||
let mut buf = Buffer::empty(area);
|
||||
composer.render(area, &mut buf);
|
||||
|
||||
let mut bottom_row = String::new();
|
||||
for x in 0..area.width {
|
||||
bottom_row.push(
|
||||
buf[(x, area.height - 1)]
|
||||
.symbol()
|
||||
.chars()
|
||||
.next()
|
||||
.unwrap_or(' '),
|
||||
);
|
||||
}
|
||||
assert!(
|
||||
bottom_row.contains("K label"),
|
||||
"expected hint override to render after flash expired, saw: {bottom_row:?}",
|
||||
);
|
||||
assert!(
|
||||
!bottom_row.contains("FLASH"),
|
||||
"expected expired flash to be hidden, saw: {bottom_row:?}",
|
||||
);
|
||||
}
|
||||
|
||||
fn snapshot_composer_state<F>(name: &str, enhanced_keys_supported: bool, setup: F)
|
||||
where
|
||||
F: FnOnce(&mut ChatComposer),
|
||||
|
||||
@@ -421,4 +421,40 @@ mod tests {
|
||||
"expected fuzzy search for '/ac' to include compact and feedback, got {cmds:?}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn collab_command_hidden_when_collaboration_modes_disabled() {
|
||||
let mut popup = CommandPopup::new(Vec::new(), CommandPopupFlags::default());
|
||||
popup.on_composer_text_change("/coll".to_string());
|
||||
|
||||
let cmds: Vec<&str> = popup
|
||||
.filtered_items()
|
||||
.into_iter()
|
||||
.filter_map(|item| match item {
|
||||
CommandItem::Builtin(cmd) => Some(cmd.command()),
|
||||
CommandItem::UserPrompt(_) => None,
|
||||
})
|
||||
.collect();
|
||||
assert!(
|
||||
!cmds.contains(&"collab"),
|
||||
"expected '/collab' to be hidden when collaboration modes are disabled, got {cmds:?}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn collab_command_visible_when_collaboration_modes_enabled() {
|
||||
let mut popup = CommandPopup::new(
|
||||
Vec::new(),
|
||||
CommandPopupFlags {
|
||||
skills_enabled: false,
|
||||
collaboration_modes_enabled: true,
|
||||
},
|
||||
);
|
||||
popup.on_composer_text_change("/collab".to_string());
|
||||
|
||||
match popup.selected_item() {
|
||||
Some(CommandItem::Builtin(cmd)) => assert_eq!(cmd.command(), "collab"),
|
||||
other => panic!("expected collab to be selected for exact match, got {other:?}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -567,6 +567,21 @@ mod tests {
|
||||
},
|
||||
);
|
||||
|
||||
snapshot_footer(
|
||||
"footer_shortcuts_collaboration_modes_enabled",
|
||||
FooterProps {
|
||||
mode: FooterMode::ShortcutOverlay,
|
||||
esc_backtrack_hint: false,
|
||||
use_shift_enter_hint: false,
|
||||
is_task_running: false,
|
||||
steer_enabled: false,
|
||||
collaboration_modes_enabled: true,
|
||||
quit_shortcut_key: key_hint::ctrl(KeyCode::Char('c')),
|
||||
context_window_percent: None,
|
||||
context_window_used_tokens: None,
|
||||
},
|
||||
);
|
||||
|
||||
snapshot_footer(
|
||||
"footer_ctrl_c_quit_idle",
|
||||
FooterProps {
|
||||
@@ -686,20 +701,5 @@ mod tests {
|
||||
context_window_used_tokens: None,
|
||||
},
|
||||
);
|
||||
|
||||
snapshot_footer(
|
||||
"footer_context_only_collab_hint",
|
||||
FooterProps {
|
||||
mode: FooterMode::ContextOnly,
|
||||
esc_backtrack_hint: false,
|
||||
use_shift_enter_hint: false,
|
||||
is_task_running: false,
|
||||
steer_enabled: false,
|
||||
collaboration_modes_enabled: true,
|
||||
quit_shortcut_key: key_hint::ctrl(KeyCode::Char('c')),
|
||||
context_window_percent: None,
|
||||
context_window_used_tokens: None,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
source: tui/src/bottom_pane/chat_composer.rs
|
||||
assertion_line: 2492
|
||||
expression: terminal.backend()
|
||||
---
|
||||
" "
|
||||
@@ -15,4 +16,4 @@ expression: terminal.backend()
|
||||
" @ for file paths ctrl + v to paste images "
|
||||
" ctrl + g to edit in external editor esc again to edit previous message "
|
||||
" ctrl + c to exit "
|
||||
" ctrl + t to view transcript "
|
||||
" ctrl + t to view transcript "
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
source: tui/src/bottom_pane/footer.rs
|
||||
expression: terminal.backend()
|
||||
---
|
||||
" 100% context left · shift + tab to change mode "
|
||||
@@ -0,0 +1,11 @@
|
||||
---
|
||||
source: tui/src/bottom_pane/footer.rs
|
||||
assertion_line: 535
|
||||
expression: terminal.backend()
|
||||
---
|
||||
" / for commands ! for shell commands "
|
||||
" ctrl + j for newline tab to queue message "
|
||||
" @ for file paths ctrl + v to paste images "
|
||||
" ctrl + g to edit in external editor esc esc to edit previous message "
|
||||
" ctrl + c to exit shift + tab to change mode "
|
||||
" ctrl + t to view transcript "
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
source: tui/src/bottom_pane/footer.rs
|
||||
assertion_line: 535
|
||||
expression: terminal.backend()
|
||||
---
|
||||
" / for commands ! for shell commands "
|
||||
@@ -7,4 +8,4 @@ expression: terminal.backend()
|
||||
" @ for file paths ctrl + v to paste images "
|
||||
" ctrl + g to edit in external editor esc again to edit previous message "
|
||||
" ctrl + c to exit "
|
||||
" ctrl + t to view transcript "
|
||||
" ctrl + t to view transcript "
|
||||
|
||||
@@ -1508,6 +1508,114 @@ async fn slash_init_skips_when_project_doc_exists() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_collaboration_mode_selection_accepts_common_aliases() {
|
||||
assert_eq!(
|
||||
parse_collaboration_mode_selection("plan"),
|
||||
Some(CollaborationModeSelection::Plan)
|
||||
);
|
||||
assert_eq!(
|
||||
parse_collaboration_mode_selection("PAIR"),
|
||||
Some(CollaborationModeSelection::PairProgramming)
|
||||
);
|
||||
assert_eq!(
|
||||
parse_collaboration_mode_selection("pair_programming"),
|
||||
Some(CollaborationModeSelection::PairProgramming)
|
||||
);
|
||||
assert_eq!(
|
||||
parse_collaboration_mode_selection("pp"),
|
||||
Some(CollaborationModeSelection::PairProgramming)
|
||||
);
|
||||
assert_eq!(
|
||||
parse_collaboration_mode_selection(" exec "),
|
||||
Some(CollaborationModeSelection::Execute)
|
||||
);
|
||||
assert_eq!(
|
||||
parse_collaboration_mode_selection("execute"),
|
||||
Some(CollaborationModeSelection::Execute)
|
||||
);
|
||||
assert_eq!(parse_collaboration_mode_selection("unknown"), None);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn collab_mode_shift_tab_cycles_only_when_enabled_and_idle() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None).await;
|
||||
chat.set_feature_enabled(Feature::CollaborationModes, false);
|
||||
|
||||
let initial = chat.collaboration_mode;
|
||||
chat.handle_key_event(KeyEvent::from(KeyCode::BackTab));
|
||||
assert_eq!(chat.collaboration_mode, initial);
|
||||
assert_eq!(chat.pending_collaboration_mode, None);
|
||||
|
||||
chat.set_feature_enabled(Feature::CollaborationModes, true);
|
||||
|
||||
chat.handle_key_event(KeyEvent::from(KeyCode::BackTab));
|
||||
assert_eq!(chat.collaboration_mode, CollaborationModeSelection::Execute);
|
||||
assert_eq!(
|
||||
chat.pending_collaboration_mode,
|
||||
Some(CollaborationModeSelection::Execute)
|
||||
);
|
||||
|
||||
chat.handle_key_event(KeyEvent::from(KeyCode::BackTab));
|
||||
assert_eq!(chat.collaboration_mode, CollaborationModeSelection::Plan);
|
||||
assert_eq!(
|
||||
chat.pending_collaboration_mode,
|
||||
Some(CollaborationModeSelection::Plan)
|
||||
);
|
||||
|
||||
chat.on_task_started();
|
||||
chat.handle_key_event(KeyEvent::from(KeyCode::BackTab));
|
||||
assert_eq!(chat.collaboration_mode, CollaborationModeSelection::Plan);
|
||||
assert_eq!(
|
||||
chat.pending_collaboration_mode,
|
||||
Some(CollaborationModeSelection::Plan)
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn collab_slash_command_sets_mode_and_next_submit_sends_user_turn() {
|
||||
let (mut chat, _rx, mut op_rx) = make_chatwidget_manual(None).await;
|
||||
chat.thread_id = Some(ThreadId::new());
|
||||
chat.set_feature_enabled(Feature::CollaborationModes, true);
|
||||
|
||||
chat.dispatch_command_with_args(SlashCommand::Collab, "plan".to_string());
|
||||
assert_eq!(chat.collaboration_mode, CollaborationModeSelection::Plan);
|
||||
assert_eq!(
|
||||
chat.pending_collaboration_mode,
|
||||
Some(CollaborationModeSelection::Plan)
|
||||
);
|
||||
|
||||
fn next_submit_op(op_rx: &mut tokio::sync::mpsc::UnboundedReceiver<Op>) -> Op {
|
||||
loop {
|
||||
match op_rx.try_recv() {
|
||||
Ok(op @ Op::UserTurn { .. }) => return op,
|
||||
Ok(op @ Op::UserInput { .. }) => return op,
|
||||
Ok(_) => continue,
|
||||
Err(TryRecvError::Empty) => panic!("expected a submit op but queue was empty"),
|
||||
Err(TryRecvError::Disconnected) => panic!("expected submit op but channel closed"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
chat.bottom_pane.set_composer_text("hello".to_string());
|
||||
chat.handle_key_event(KeyEvent::from(KeyCode::Enter));
|
||||
match next_submit_op(&mut op_rx) {
|
||||
Op::UserTurn {
|
||||
collaboration_mode: Some(CollaborationMode::Plan(_)),
|
||||
..
|
||||
} => {}
|
||||
other => panic!("expected Op::UserTurn with plan collab mode, got {other:?}"),
|
||||
}
|
||||
assert_eq!(chat.pending_collaboration_mode, None);
|
||||
|
||||
chat.bottom_pane.set_composer_text("follow up".to_string());
|
||||
chat.handle_key_event(KeyEvent::from(KeyCode::Enter));
|
||||
match next_submit_op(&mut op_rx) {
|
||||
Op::UserInput { .. } => {}
|
||||
other => panic!("expected Op::UserInput after pending mode cleared, got {other:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn slash_quit_requests_exit() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None).await;
|
||||
|
||||
Reference in New Issue
Block a user