diff options
| author | Aram Drevekenin <aram@poor.dev> | 2025-10-31 20:15:32 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-10-31 20:15:32 +0100 |
| commit | 82f30e2cde5207e5b51301633686b2c7feac54f3 (patch) | |
| tree | 2bdc9a39be42b9e781619730fc6558afe0783838 /src | |
| parent | Feature: watcher clients (#4487) (diff) | |
| download | sl0ppij-82f30e2cde5207e5b51301633686b2c7feac54f3.tar.gz sl0ppij-82f30e2cde5207e5b51301633686b2c7feac54f3.tar.xz sl0ppij-82f30e2cde5207e5b51301633686b2c7feac54f3.zip | |
Improve CLI interface (add detach, switch session, blocking zellij run and more) (#4492)
* initial implementation
* fix tests
* move more ations
* screen adjustments (does not compile)
* plugin/pty adjustments (does not compile)
* more progress, tests not compiling
* fix tests
* more progress, does not compile
* more progress, compiles, tests failing
* tests passing
* rely on Drop for NotificationEnd
* atomicify some more actions
* fmt
* move unblock input to route
* don't pass hard-coded action names
* refactor: consolidate completion channel logic
* comment
* track last active client
* track cli clients explicitly
* use the last active client for cli commands when possible
* fix focus-next-pane for multiple clients
* various fixes for multi-client actions from the cli
* add detach action
* fix tests
* add switch session cli action
* fix switch session deadlock
* add and layout to switch-session cli action
* blocking zellij run: add NewBlockingPane variant
* blocking zellij run: wait forever mechanism
* blocking zellij run: thread NotificationEnd as needed until TerminalPane (tests not compiling)
* blocking zellij run: update cli
* blocking zellij run: add command
* blocking zellij run: finish implementation
* fix tests
* e2e test and close_on_exit fixes
* fmt
* fix e2e tests
* fix plugin tests
Diffstat (limited to 'src')
| -rw-r--r-- | src/main.rs | 4 | ||||
| -rw-r--r-- | src/tests/e2e/cases.rs | 98 | ||||
| -rw-r--r-- | src/tests/e2e/remote_runner.rs | 16 | ||||
| -rw-r--r-- | src/tests/e2e/snapshots/zellij__tests__e2e__cases__send_blocking_command_through_the_cli.snap | 28 | ||||
| -rw-r--r-- | src/tests/e2e/snapshots/zellij__tests__e2e__cases__send_command_through_the_cli.snap | 2 |
5 files changed, 146 insertions, 2 deletions
diff --git a/src/main.rs b/src/main.rs index 2b2e213a..f077c199 100644 --- a/src/main.rs +++ b/src/main.rs @@ -39,6 +39,7 @@ fn main() { height, pinned, stacked, + blocking, })) = opts.command { let cwd = cwd.or_else(|| std::env::current_dir().ok()); @@ -61,6 +62,7 @@ fn main() { height, pinned, stacked, + blocking, }; commands::send_action_to_session(command_cli_action, opts.session, config); std::process::exit(0); @@ -80,6 +82,7 @@ fn main() { { let cwd = None; let stacked = false; + let blocking = false; let command_cli_action = CliAction::NewPane { command: vec![], plugin: Some(url), @@ -98,6 +101,7 @@ fn main() { height, pinned, stacked, + blocking, }; commands::send_action_to_session(command_cli_action, opts.session, config); std::process::exit(0); diff --git a/src/tests/e2e/cases.rs b/src/tests/e2e/cases.rs index d2096cea..eab81039 100644 --- a/src/tests/e2e/cases.rs +++ b/src/tests/e2e/cases.rs @@ -2317,6 +2317,8 @@ pub fn send_command_through_the_cli() { fixture_folder )); std::thread::sleep(std::time::Duration::from_millis(100)); + remote_terminal.send_key(&ENTER); + std::thread::sleep(std::time::Duration::from_millis(100)); step_is_complete = true; } step_is_complete @@ -2396,6 +2398,102 @@ pub fn send_command_through_the_cli() { #[test] #[ignore] +pub fn send_blocking_command_through_the_cli() { + // here we test the following flow: + // - send a blocking command through the cli with --blocking --floating --close-on-exit + // - the command sleeps for 2 seconds (longer than the default 1s timeout) then exits with status 42 + // - verify that the CLI blocks for the full duration (we check after 2+ seconds) + // - verify that the floating pane appears while running and disappears after completion + // - verify that the exit status is properly propagated + let fake_win_size = Size { + cols: 150, + rows: 24, + }; + let mut test_attempts = 10; + let last_snapshot = loop { + RemoteRunner::kill_running_sessions(fake_win_size); + let mut runner = RemoteRunner::new(fake_win_size) + .add_step(Step { + name: "Run blocking command through the cli", + instruction: |mut remote_terminal: RemoteTerminal| -> bool { + let mut step_is_complete = false; + if remote_terminal.status_bar_appears() + && remote_terminal.cursor_position_is(3, 2) + { + // Send a command that sleeps for 2 seconds then exits with status 42 + // Using bash -c with proper escaping + // remote_terminal.send_key("aaa\n".as_bytes()); + std::thread::sleep(std::time::Duration::from_millis(100)); + remote_terminal.send_blocking_command_through_the_cli( + // "lskdjlskdjf" + "bash -c 'sleep 2 && exit 42'", + ); + std::thread::sleep(std::time::Duration::from_millis(100)); + remote_terminal.send_key(&ENTER); + std::thread::sleep(std::time::Duration::from_millis(100)); + step_is_complete = true; + } + step_is_complete + }, + }) + .add_step(Step { + name: "Wait for floating pane to appear", + instruction: |remote_terminal: RemoteTerminal| -> bool { + let mut step_is_complete = false; + // The floating pane should appear with the running command + if remote_terminal.snapshot_contains("PIN [ ]") { + std::thread::sleep(std::time::Duration::from_millis(2000)); // wait for + // command to + // end + step_is_complete = true + } + step_is_complete + }, + }) + .add_step(Step { + name: "Wait for command to complete and verify exit status", + instruction: |mut remote_terminal: RemoteTerminal| -> bool { + let mut step_is_complete = false; + // After 2+ seconds, the command should complete and the floating pane should close + // then we do echo $? to make sure we got back its exit status + if !remote_terminal.snapshot_contains("PIN [ ]") { + remote_terminal.send_key("echo $?".as_bytes()); + std::thread::sleep(std::time::Duration::from_millis(100)); + remote_terminal.send_key(&ENTER); + std::thread::sleep(std::time::Duration::from_millis(100)); + step_is_complete = true + } + step_is_complete + }, + }); + runner.run_all_steps(); + + let last_snapshot = runner.take_snapshot_after(Step { + name: "Verify CLI returned with proper exit status after command completed", + instruction: |remote_terminal: RemoteTerminal| -> bool { + let mut step_is_complete = false; + if remote_terminal.snapshot_contains("echo $?") + && remote_terminal.status_bar_appears() + { + step_is_complete = true + } + step_is_complete + }, + }); + + if runner.test_timed_out && test_attempts > 0 { + test_attempts -= 1; + continue; + } else { + break last_snapshot; + } + }; + let last_snapshot = account_for_races_in_snapshot(last_snapshot); + assert_snapshot!(last_snapshot); +} + +#[test] +#[ignore] pub fn load_plugins_in_background_on_startup() { let fake_win_size = Size { cols: 120, diff --git a/src/tests/e2e/remote_runner.rs b/src/tests/e2e/remote_runner.rs index a9b345b7..377869f9 100644 --- a/src/tests/e2e/remote_runner.rs +++ b/src/tests/e2e/remote_runner.rs @@ -273,6 +273,7 @@ fn read_from_channel( arrow_fonts, styled_underlines, explicitly_disable_kitty_keyboard_protocol, + None, ); // 0 is the pane index loop { if !should_keep_running.load(Ordering::SeqCst) { @@ -423,7 +424,20 @@ impl RemoteTerminal { channel .write_all( // note that this is run with the -s flag that suspends the command on startup - format!("{} run -s -- \"{}\"\n", ZELLIJ_EXECUTABLE_LOCATION, command).as_bytes(), + format!("{} run -s -- \"{}\"", ZELLIJ_EXECUTABLE_LOCATION, command).as_bytes(), + ) + .unwrap(); + channel.flush().unwrap(); + } + pub fn send_blocking_command_through_the_cli(&mut self, command: &str) { + let mut channel = self.channel.lock().unwrap(); + channel + .write_all( + format!( + "{} run --blocking --floating --close-on-exit -- {}", + ZELLIJ_EXECUTABLE_LOCATION, command + ) + .as_bytes(), ) .unwrap(); channel.flush().unwrap(); diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__send_blocking_command_through_the_cli.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__send_blocking_command_through_the_cli.snap new file mode 100644 index 00000000..0dce3a50 --- /dev/null +++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__send_blocking_command_through_the_cli.snap @@ -0,0 +1,28 @@ +--- +source: src/tests/e2e/cases.rs +expression: last_snapshot +--- + Zellij (e2e-test) Tab #1 +┌ Pane #1 ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│$ /usr/src/zellij/x86_64-unknown-linux-musl/release/zellij run --blocking --floating --close-on-exit -- bash -c 'sleep 2 && exit 42' │ +│$ │ +│$ echo $? │ +│42 │ +│$ │ +│$ █ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + Ctrl + <g> LOCK <p> PANE <t> TAB <n> RESIZE <h> MOVE <s> SEARCH <o> SESSION <q> QUIT Alt + <n> New Pane <f> Floating diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__send_command_through_the_cli.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__send_command_through_the_cli.snap index 1e1a014b..228407bd 100644 --- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__send_command_through_the_cli.snap +++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__send_command_through_the_cli.snap @@ -7,7 +7,7 @@ expression: last_snapshot │$ /usr/src/zellij/x86_64-unknown-linux-musl/release/zellij run -s -- "/us││foo │ │r/src/zellij/fixtures/append-echo-script.sh" ││foo │ │$ ││█ │ -│ ││ │ +│$ ││ │ │ ││ │ │ ││ │ │ ││ │ |
