aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAram Drevekenin <aram@poor.dev>2025-10-31 20:15:32 +0100
committerGitHub <noreply@github.com>2025-10-31 20:15:32 +0100
commit82f30e2cde5207e5b51301633686b2c7feac54f3 (patch)
tree2bdc9a39be42b9e781619730fc6558afe0783838 /src
parentFeature: watcher clients (#4487) (diff)
downloadsl0ppij-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.rs4
-rw-r--r--src/tests/e2e/cases.rs98
-rw-r--r--src/tests/e2e/remote_runner.rs16
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__send_blocking_command_through_the_cli.snap28
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__send_command_through_the_cli.snap2
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 │
│$ ││█ │
-│ ││ │
+│$ ││ │
│ ││ │
│ ││ │
│ ││ │