feat(tui) Permissions update history item (#11550)
## Summary We should document in the tui when you switch permissions! ## Testing - [x] Added unit tests - [x] Tested locally
This commit is contained in:
parent
3164670101
commit
ebceb71db6
5 changed files with 237 additions and 7 deletions
|
|
@ -5423,15 +5423,23 @@ impl ChatWidget {
|
|||
});
|
||||
})]
|
||||
} else {
|
||||
Self::approval_preset_actions(preset.approval, preset.sandbox.clone())
|
||||
Self::approval_preset_actions(
|
||||
preset.approval,
|
||||
preset.sandbox.clone(),
|
||||
name.clone(),
|
||||
)
|
||||
}
|
||||
}
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
{
|
||||
Self::approval_preset_actions(preset.approval, preset.sandbox.clone())
|
||||
Self::approval_preset_actions(
|
||||
preset.approval,
|
||||
preset.sandbox.clone(),
|
||||
name.clone(),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
Self::approval_preset_actions(preset.approval, preset.sandbox.clone())
|
||||
Self::approval_preset_actions(preset.approval, preset.sandbox.clone(), name.clone())
|
||||
};
|
||||
items.push(SelectionItem {
|
||||
name,
|
||||
|
|
@ -5485,6 +5493,7 @@ impl ChatWidget {
|
|||
fn approval_preset_actions(
|
||||
approval: AskForApproval,
|
||||
sandbox: SandboxPolicy,
|
||||
label: String,
|
||||
) -> Vec<SelectionAction> {
|
||||
vec![Box::new(move |tx| {
|
||||
let sandbox_clone = sandbox.clone();
|
||||
|
|
@ -5501,6 +5510,9 @@ impl ChatWidget {
|
|||
}));
|
||||
tx.send(AppEvent::UpdateAskForApprovalPolicy(approval));
|
||||
tx.send(AppEvent::UpdateSandboxPolicy(sandbox_clone));
|
||||
tx.send(AppEvent::InsertHistoryCell(Box::new(
|
||||
history_cell::new_info_event(format!("Permissions updated to {label}"), None),
|
||||
)));
|
||||
})]
|
||||
}
|
||||
|
||||
|
|
@ -5547,6 +5559,7 @@ impl ChatWidget {
|
|||
preset: ApprovalPreset,
|
||||
return_to_permissions: bool,
|
||||
) {
|
||||
let selected_name = preset.label.to_string();
|
||||
let approval = preset.approval;
|
||||
let sandbox = preset.sandbox;
|
||||
let mut header_children: Vec<Box<dyn Renderable>> = Vec::new();
|
||||
|
|
@ -5563,12 +5576,14 @@ impl ChatWidget {
|
|||
));
|
||||
let header = ColumnRenderable::with(header_children);
|
||||
|
||||
let mut accept_actions = Self::approval_preset_actions(approval, sandbox.clone());
|
||||
let mut accept_actions =
|
||||
Self::approval_preset_actions(approval, sandbox.clone(), selected_name.clone());
|
||||
accept_actions.push(Box::new(|tx| {
|
||||
tx.send(AppEvent::UpdateFullAccessWarningAcknowledged(true));
|
||||
}));
|
||||
|
||||
let mut accept_and_remember_actions = Self::approval_preset_actions(approval, sandbox);
|
||||
let mut accept_and_remember_actions =
|
||||
Self::approval_preset_actions(approval, sandbox, selected_name);
|
||||
accept_and_remember_actions.push(Box::new(|tx| {
|
||||
tx.send(AppEvent::UpdateFullAccessWarningAcknowledged(true));
|
||||
tx.send(AppEvent::PersistFullAccessWarningAcknowledged);
|
||||
|
|
@ -5678,7 +5693,11 @@ impl ChatWidget {
|
|||
}));
|
||||
}
|
||||
if let (Some(approval), Some(sandbox)) = (approval, sandbox.clone()) {
|
||||
accept_actions.extend(Self::approval_preset_actions(approval, sandbox));
|
||||
accept_actions.extend(Self::approval_preset_actions(
|
||||
approval,
|
||||
sandbox,
|
||||
mode_label.to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
let mut accept_and_remember_actions: Vec<SelectionAction> = Vec::new();
|
||||
|
|
@ -5687,7 +5706,11 @@ impl ChatWidget {
|
|||
tx.send(AppEvent::PersistWorldWritableWarningAcknowledged);
|
||||
}));
|
||||
if let (Some(approval), Some(sandbox)) = (approval, sandbox) {
|
||||
accept_and_remember_actions.extend(Self::approval_preset_actions(approval, sandbox));
|
||||
accept_and_remember_actions.extend(Self::approval_preset_actions(
|
||||
approval,
|
||||
sandbox,
|
||||
mode_label.to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
let items = vec![
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
source: tui/src/chatwidget/tests.rs
|
||||
expression: "lines_to_single_string(&cells[0])"
|
||||
---
|
||||
• Permissions updated to Full Access
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
source: tui/src/chatwidget/tests.rs
|
||||
expression: "lines_to_single_string(&cells[0])"
|
||||
---
|
||||
• Permissions updated to Default
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
source: tui/src/chatwidget/tests.rs
|
||||
expression: "lines_to_single_string(&cells[0])"
|
||||
---
|
||||
• Permissions updated to Default (non-admin sandbox)
|
||||
|
|
@ -20,6 +20,8 @@ use codex_core::config::Config;
|
|||
use codex_core::config::ConfigBuilder;
|
||||
use codex_core::config::Constrained;
|
||||
use codex_core::config::ConstraintError;
|
||||
#[cfg(target_os = "windows")]
|
||||
use codex_core::config::types::WindowsSandboxModeToml;
|
||||
use codex_core::config_loader::RequirementSource;
|
||||
use codex_core::features::Feature;
|
||||
use codex_core::models_manager::manager::ModelsManager;
|
||||
|
|
@ -5430,6 +5432,196 @@ async fn approvals_popup_navigation_skips_disabled() {
|
|||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn permissions_selection_emits_history_cell_when_selection_changes() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None).await;
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
chat.config.notices.hide_world_writable_warning = Some(true);
|
||||
chat.set_windows_sandbox_mode(Some(WindowsSandboxModeToml::Unelevated));
|
||||
}
|
||||
chat.config.notices.hide_full_access_warning = Some(true);
|
||||
|
||||
chat.open_permissions_popup();
|
||||
chat.handle_key_event(KeyEvent::from(KeyCode::Down));
|
||||
chat.handle_key_event(KeyEvent::from(KeyCode::Enter));
|
||||
|
||||
let cells = drain_insert_history(&mut rx);
|
||||
assert_eq!(
|
||||
cells.len(),
|
||||
1,
|
||||
"expected one permissions selection history cell"
|
||||
);
|
||||
let rendered = lines_to_single_string(&cells[0]);
|
||||
assert!(
|
||||
rendered.contains("Permissions updated to"),
|
||||
"expected permissions selection history message, got: {rendered}"
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn permissions_selection_history_snapshot_after_mode_switch() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None).await;
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
chat.config.notices.hide_world_writable_warning = Some(true);
|
||||
chat.set_windows_sandbox_mode(Some(WindowsSandboxModeToml::Unelevated));
|
||||
}
|
||||
chat.config.notices.hide_full_access_warning = Some(true);
|
||||
|
||||
chat.open_permissions_popup();
|
||||
chat.handle_key_event(KeyEvent::from(KeyCode::Down));
|
||||
#[cfg(target_os = "windows")]
|
||||
chat.handle_key_event(KeyEvent::from(KeyCode::Down));
|
||||
chat.handle_key_event(KeyEvent::from(KeyCode::Enter));
|
||||
|
||||
let cells = drain_insert_history(&mut rx);
|
||||
assert_eq!(cells.len(), 1, "expected one mode-switch history cell");
|
||||
assert_snapshot!(
|
||||
"permissions_selection_history_after_mode_switch",
|
||||
lines_to_single_string(&cells[0])
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn permissions_selection_history_snapshot_full_access_to_default() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None).await;
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
chat.config.notices.hide_world_writable_warning = Some(true);
|
||||
chat.set_windows_sandbox_mode(Some(WindowsSandboxModeToml::Unelevated));
|
||||
}
|
||||
chat.config.notices.hide_full_access_warning = Some(true);
|
||||
chat.config
|
||||
.permissions
|
||||
.approval_policy
|
||||
.set(AskForApproval::Never)
|
||||
.expect("set approval policy");
|
||||
chat.config
|
||||
.permissions
|
||||
.sandbox_policy
|
||||
.set(SandboxPolicy::DangerFullAccess)
|
||||
.expect("set sandbox policy");
|
||||
|
||||
chat.open_permissions_popup();
|
||||
chat.handle_key_event(KeyEvent::from(KeyCode::Up));
|
||||
chat.handle_key_event(KeyEvent::from(KeyCode::Enter));
|
||||
|
||||
let cells = drain_insert_history(&mut rx);
|
||||
assert_eq!(cells.len(), 1, "expected one mode-switch history cell");
|
||||
let rendered = lines_to_single_string(&cells[0]);
|
||||
#[cfg(target_os = "windows")]
|
||||
insta::with_settings!({ snapshot_suffix => "windows" }, {
|
||||
assert_snapshot!("permissions_selection_history_full_access_to_default", rendered);
|
||||
});
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
assert_snapshot!(
|
||||
"permissions_selection_history_full_access_to_default",
|
||||
rendered
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn permissions_selection_emits_history_cell_when_current_is_selected() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None).await;
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
chat.config.notices.hide_world_writable_warning = Some(true);
|
||||
chat.set_windows_sandbox_mode(Some(WindowsSandboxModeToml::Unelevated));
|
||||
}
|
||||
chat.config
|
||||
.permissions
|
||||
.approval_policy
|
||||
.set(AskForApproval::OnRequest)
|
||||
.expect("set approval policy");
|
||||
chat.config
|
||||
.permissions
|
||||
.sandbox_policy
|
||||
.set(SandboxPolicy::new_workspace_write_policy())
|
||||
.expect("set sandbox policy");
|
||||
|
||||
chat.open_permissions_popup();
|
||||
chat.handle_key_event(KeyEvent::from(KeyCode::Enter));
|
||||
|
||||
let cells = drain_insert_history(&mut rx);
|
||||
assert_eq!(
|
||||
cells.len(),
|
||||
1,
|
||||
"expected history cell even when selecting current permissions"
|
||||
);
|
||||
let rendered = lines_to_single_string(&cells[0]);
|
||||
assert!(
|
||||
rendered.contains("Permissions updated to"),
|
||||
"expected permissions update history message, got: {rendered}"
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn permissions_full_access_history_cell_emitted_only_after_confirmation() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None).await;
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
chat.config.notices.hide_world_writable_warning = Some(true);
|
||||
chat.set_windows_sandbox_mode(Some(WindowsSandboxModeToml::Unelevated));
|
||||
}
|
||||
chat.config.notices.hide_full_access_warning = None;
|
||||
|
||||
chat.open_permissions_popup();
|
||||
chat.handle_key_event(KeyEvent::from(KeyCode::Down));
|
||||
#[cfg(target_os = "windows")]
|
||||
chat.handle_key_event(KeyEvent::from(KeyCode::Down));
|
||||
chat.handle_key_event(KeyEvent::from(KeyCode::Enter));
|
||||
|
||||
let mut open_confirmation_event = None;
|
||||
let mut cells_before_confirmation = Vec::new();
|
||||
while let Ok(event) = rx.try_recv() {
|
||||
match event {
|
||||
AppEvent::InsertHistoryCell(cell) => {
|
||||
cells_before_confirmation.push(cell.display_lines(80));
|
||||
}
|
||||
AppEvent::OpenFullAccessConfirmation {
|
||||
preset,
|
||||
return_to_permissions,
|
||||
} => {
|
||||
open_confirmation_event = Some((preset, return_to_permissions));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
if cfg!(not(target_os = "windows")) {
|
||||
assert!(
|
||||
cells_before_confirmation.is_empty(),
|
||||
"did not expect history cell before confirming full access"
|
||||
);
|
||||
}
|
||||
let (preset, return_to_permissions) =
|
||||
open_confirmation_event.expect("expected full access confirmation event");
|
||||
chat.open_full_access_confirmation(preset, return_to_permissions);
|
||||
|
||||
let popup = render_bottom_popup(&chat, 80);
|
||||
assert!(
|
||||
popup.contains("Enable full access?"),
|
||||
"expected full access confirmation popup, got: {popup}"
|
||||
);
|
||||
|
||||
chat.handle_key_event(KeyEvent::from(KeyCode::Enter));
|
||||
let cells_after_confirmation = drain_insert_history(&mut rx);
|
||||
let total_history_cells = cells_before_confirmation.len() + cells_after_confirmation.len();
|
||||
assert_eq!(
|
||||
total_history_cells, 1,
|
||||
"expected one full access history cell total"
|
||||
);
|
||||
let rendered = if !cells_before_confirmation.is_empty() {
|
||||
lines_to_single_string(&cells_before_confirmation[0])
|
||||
} else {
|
||||
lines_to_single_string(&cells_after_confirmation[0])
|
||||
};
|
||||
assert!(
|
||||
rendered.contains("Permissions updated to Full Access"),
|
||||
"expected full access update history message, got: {rendered}"
|
||||
);
|
||||
}
|
||||
|
||||
//
|
||||
// Snapshot test: command approval modal
|
||||
//
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue