aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAram Drevekenin <aram@poor.dev>2026-06-25 17:09:26 +0200
committerGitHub <noreply@github.com>2026-06-25 17:09:26 +0200
commit27291bea2893589b3274d5da7c81d80b3f487d26 (patch)
treee71e46925f236b55ef73fd4f0ff7c6c3092eed32
parenthotfix: fix flaky ci tests (diff)
downloadsl0ppij-27291bea2893589b3274d5da7c81d80b3f487d26.tar.gz
sl0ppij-27291bea2893589b3274d5da7c81d80b3f487d26.tar.xz
sl0ppij-27291bea2893589b3274d5da7c81d80b3f487d26.zip
tests: add more integration tests (#5308)
* test all the things * consolidate shared harness * rustfmt
-rw-r--r--zellij-integration-tests/src/client_screen.rs28
-rw-r--r--zellij-integration-tests/src/harness.rs47
-rw-r--r--zellij-integration-tests/src/keys.rs62
-rw-r--r--zellij-integration-tests/src/lib.rs7
-rw-r--r--zellij-integration-tests/src/runner.rs18
-rw-r--r--zellij-integration-tests/tests/app.rs51
-rw-r--r--zellij-integration-tests/tests/basic.rs60
-rw-r--r--zellij-integration-tests/tests/cli.rs35
-rw-r--r--zellij-integration-tests/tests/clients.rs79
-rw-r--r--zellij-integration-tests/tests/misc.rs136
-rw-r--r--zellij-integration-tests/tests/modes.rs192
-rw-r--r--zellij-integration-tests/tests/mouse.rs51
-rw-r--r--zellij-integration-tests/tests/panes.rs463
-rw-r--r--zellij-integration-tests/tests/plugins.rs7
-rw-r--r--zellij-integration-tests/tests/resize.rs277
-rw-r--r--zellij-integration-tests/tests/resurrection.rs12
-rw-r--r--zellij-integration-tests/tests/scroll.rs134
-rw-r--r--zellij-integration-tests/tests/search.rs161
-rw-r--r--zellij-integration-tests/tests/session.rs31
-rw-r--r--zellij-integration-tests/tests/snapshots/app__use_custom_layout_with_relative_path.snap1
-rw-r--r--zellij-integration-tests/tests/snapshots/misc__focus_next_pane_via_tmux_mode.snap28
-rw-r--r--zellij-integration-tests/tests/snapshots/misc__next_swap_layout.snap28
-rw-r--r--zellij-integration-tests/tests/snapshots/misc__previous_swap_layout.snap28
-rw-r--r--zellij-integration-tests/tests/snapshots/misc__toggle_group_marking.snap28
-rw-r--r--zellij-integration-tests/tests/snapshots/misc__toggle_pane_in_group.snap28
-rw-r--r--zellij-integration-tests/tests/snapshots/misc__write_byte_to_focused_pane_via_tmux_mode.snap28
-rw-r--r--zellij-integration-tests/tests/snapshots/modes__enter_search_mode.snap28
-rw-r--r--zellij-integration-tests/tests/snapshots/modes__return_to_normal_mode.snap28
-rw-r--r--zellij-integration-tests/tests/snapshots/modes__switch_to_move_mode.snap28
-rw-r--r--zellij-integration-tests/tests/snapshots/modes__switch_to_pane_mode.snap28
-rw-r--r--zellij-integration-tests/tests/snapshots/modes__switch_to_resize_mode.snap28
-rw-r--r--zellij-integration-tests/tests/snapshots/modes__switch_to_scroll_mode.snap28
-rw-r--r--zellij-integration-tests/tests/snapshots/modes__switch_to_search_mode.snap28
-rw-r--r--zellij-integration-tests/tests/snapshots/modes__switch_to_session_mode.snap28
-rw-r--r--zellij-integration-tests/tests/snapshots/modes__switch_to_tab_mode.snap28
-rw-r--r--zellij-integration-tests/tests/snapshots/panes__move_focus_down_in_pane_mode.snap28
-rw-r--r--zellij-integration-tests/tests/snapshots/panes__move_focus_left_in_pane_mode.snap28
-rw-r--r--zellij-integration-tests/tests/snapshots/panes__move_focus_or_tab_moves_focus_between_panes.snap28
-rw-r--r--zellij-integration-tests/tests/snapshots/panes__move_focus_or_tab_switches_tab_when_single_pane.snap28
-rw-r--r--zellij-integration-tests/tests/snapshots/panes__move_focus_right_in_pane_mode.snap28
-rw-r--r--zellij-integration-tests/tests/snapshots/panes__move_focus_up_in_pane_mode.snap28
-rw-r--r--zellij-integration-tests/tests/snapshots/panes__move_pane_backwards_swaps_with_neighbor.snap28
-rw-r--r--zellij-integration-tests/tests/snapshots/panes__move_pane_down.snap28
-rw-r--r--zellij-integration-tests/tests/snapshots/panes__move_pane_left.snap28
-rw-r--r--zellij-integration-tests/tests/snapshots/panes__move_pane_swaps_with_neighbor.snap28
-rw-r--r--zellij-integration-tests/tests/snapshots/panes__new_pane_in_normal_mode.snap28
-rw-r--r--zellij-integration-tests/tests/snapshots/panes__new_pane_in_pane_mode.snap28
-rw-r--r--zellij-integration-tests/tests/snapshots/panes__new_stacked_pane.snap28
-rw-r--r--zellij-integration-tests/tests/snapshots/panes__split_pane_downward.snap28
-rw-r--r--zellij-integration-tests/tests/snapshots/panes__switch_focus_in_pane_mode.snap28
-rw-r--r--zellij-integration-tests/tests/snapshots/panes__toggle_pane_embed_or_floating.snap28
-rw-r--r--zellij-integration-tests/tests/snapshots/panes__toggle_pane_frames.snap28
-rw-r--r--zellij-integration-tests/tests/snapshots/panes__toggle_pane_pinned.snap28
-rw-r--r--zellij-integration-tests/tests/snapshots/resize__resize_decrease_down.snap28
-rw-r--r--zellij-integration-tests/tests/snapshots/resize__resize_decrease_in_normal_mode.snap28
-rw-r--r--zellij-integration-tests/tests/snapshots/resize__resize_decrease_in_resize_mode.snap28
-rw-r--r--zellij-integration-tests/tests/snapshots/resize__resize_decrease_left.snap28
-rw-r--r--zellij-integration-tests/tests/snapshots/resize__resize_decrease_right.snap28
-rw-r--r--zellij-integration-tests/tests/snapshots/resize__resize_decrease_up.snap28
-rw-r--r--zellij-integration-tests/tests/snapshots/resize__resize_increase_down.snap28
-rw-r--r--zellij-integration-tests/tests/snapshots/resize__resize_increase_in_normal_mode.snap28
-rw-r--r--zellij-integration-tests/tests/snapshots/resize__resize_increase_in_resize_mode.snap28
-rw-r--r--zellij-integration-tests/tests/snapshots/resize__resize_increase_right.snap28
-rw-r--r--zellij-integration-tests/tests/snapshots/resize__resize_increase_up.snap28
-rw-r--r--zellij-integration-tests/tests/snapshots/scroll__half_page_scroll_down.snap28
-rw-r--r--zellij-integration-tests/tests/snapshots/scroll__half_page_scroll_up.snap28
-rw-r--r--zellij-integration-tests/tests/snapshots/scroll__page_scroll_down.snap28
-rw-r--r--zellij-integration-tests/tests/snapshots/scroll__page_scroll_up.snap28
-rw-r--r--zellij-integration-tests/tests/snapshots/scroll__scroll_down_one_line.snap28
-rw-r--r--zellij-integration-tests/tests/snapshots/scroll__scroll_to_bottom.snap28
-rw-r--r--zellij-integration-tests/tests/snapshots/search__search_down_to_next_match.snap28
-rw-r--r--zellij-integration-tests/tests/snapshots/search__search_input_jumps_to_a_match.snap28
-rw-r--r--zellij-integration-tests/tests/snapshots/search__search_toggle_case_sensitivity.snap28
-rw-r--r--zellij-integration-tests/tests/snapshots/search__search_toggle_whole_word.snap28
-rw-r--r--zellij-integration-tests/tests/snapshots/search__search_toggle_wrap.snap28
-rw-r--r--zellij-integration-tests/tests/snapshots/search__search_up_to_previous_match.snap28
-rw-r--r--zellij-integration-tests/tests/snapshots/tabs__break_pane_into_new_tab.snap28
-rw-r--r--zellij-integration-tests/tests/snapshots/tabs__break_pane_to_the_left.snap28
-rw-r--r--zellij-integration-tests/tests/snapshots/tabs__break_pane_to_the_right.snap28
-rw-r--r--zellij-integration-tests/tests/snapshots/tabs__go_to_next_tab.snap28
-rw-r--r--zellij-integration-tests/tests/snapshots/tabs__go_to_previous_tab.snap28
-rw-r--r--zellij-integration-tests/tests/snapshots/tabs__go_to_tab_by_number.snap28
-rw-r--r--zellij-integration-tests/tests/snapshots/tabs__toggle_active_sync_tab.snap28
-rw-r--r--zellij-integration-tests/tests/snapshots/tabs__toggle_tab_returns_to_last_used.snap28
-rw-r--r--zellij-integration-tests/tests/tabs.rs295
85 files changed, 3501 insertions, 438 deletions
diff --git a/zellij-integration-tests/src/client_screen.rs b/zellij-integration-tests/src/client_screen.rs
index 3bddfa07..479c9b56 100644
--- a/zellij-integration-tests/src/client_screen.rs
+++ b/zellij-integration-tests/src/client_screen.rs
@@ -109,6 +109,26 @@ pub struct CursorPosition {
pub y: usize,
}
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub struct Coord {
+ pub col: usize,
+ pub row: usize,
+}
+
+pub fn col(col: usize) -> CoordBuilder {
+ CoordBuilder { col }
+}
+
+pub struct CoordBuilder {
+ col: usize,
+}
+
+impl CoordBuilder {
+ pub fn row(self, row: usize) -> Coord {
+ Coord { col: self.col, row }
+ }
+}
+
#[derive(Clone, Debug)]
pub struct GridSnapshot {
pub text: String,
@@ -119,8 +139,12 @@ impl GridSnapshot {
pub fn contains(&self, needle: &str) -> bool {
self.text.contains(needle)
}
- pub fn cursor_is_at(&self, x: usize, y: usize) -> bool {
- self.cursor == Some(CursorPosition { x, y })
+ pub fn cursor_is_at(&self, coord: Coord) -> bool {
+ self.cursor
+ == Some(CursorPosition {
+ x: coord.col,
+ y: coord.row,
+ })
}
pub fn status_bar_appears(&self) -> bool {
self.text.contains("Ctrl +") && self.text.contains("LOCK")
diff --git a/zellij-integration-tests/src/harness.rs b/zellij-integration-tests/src/harness.rs
new file mode 100644
index 00000000..eb860f8f
--- /dev/null
+++ b/zellij-integration-tests/src/harness.rs
@@ -0,0 +1,47 @@
+use crate::{col, keys, FakePtyHandle, Size, TestRunner, TestSession};
+
+pub const TERMINAL_SIZE: Size = Size {
+ cols: 120,
+ rows: 24,
+};
+pub const PROMPT: &[u8] = b"$ ";
+
+pub fn start_zellij() -> TestSession {
+ TestRunner::new(TERMINAL_SIZE).start()
+}
+
+pub fn claim_first_terminal_and_wait_for_prompt(zellij: &TestSession) -> FakePtyHandle {
+ let terminal = zellij.expect_pty_spawn();
+ terminal.output(PROMPT);
+ zellij.wait_until(
+ "first terminal prompt rendered in loaded app",
+ |grid_snapshot| {
+ grid_snapshot.tab_bar_appears()
+ && grid_snapshot.status_bar_appears()
+ && grid_snapshot.cursor_is_at(col(3).row(2))
+ },
+ );
+ terminal
+}
+
+pub fn split_right_and_wait_for_prompt(zellij: &TestSession) -> FakePtyHandle {
+ zellij.send_stdin(&keys::ctrl('p'));
+ zellij.send_stdin(&keys::key('r'));
+ let terminal = zellij.expect_pty_spawn();
+ terminal.output(PROMPT);
+ zellij.wait_until("right terminal prompt rendered", |grid_snapshot| {
+ grid_snapshot.status_bar_appears() && grid_snapshot.cursor_is_at(col(63).row(2))
+ });
+ terminal
+}
+
+pub fn split_down_and_wait_for_prompt(zellij: &TestSession) -> FakePtyHandle {
+ zellij.send_stdin(&keys::ctrl('p'));
+ zellij.send_stdin(&keys::key('d'));
+ let terminal = zellij.expect_pty_spawn();
+ terminal.output(PROMPT);
+ zellij.wait_until("lower terminal prompt rendered", |grid_snapshot| {
+ grid_snapshot.status_bar_appears() && grid_snapshot.cursor_is_at(col(3).row(13))
+ });
+ terminal
+}
diff --git a/zellij-integration-tests/src/keys.rs b/zellij-integration-tests/src/keys.rs
index fe54b42d..e2cef994 100644
--- a/zellij-integration-tests/src/keys.rs
+++ b/zellij-integration-tests/src/keys.rs
@@ -1,63 +1,21 @@
const ESC_BYTE: u8 = 0x1b;
-const fn ctrl(character: u8) -> [u8; 1] {
- [character & 0x1f]
+pub const fn ctrl(character: char) -> [u8; 1] {
+ [(character as u8) & 0x1f]
}
-const fn alt(character: u8) -> [u8; 2] {
- [ESC_BYTE, character]
+pub const fn alt(character: char) -> [u8; 2] {
+ [ESC_BYTE, character as u8]
+}
+
+pub const fn key(character: char) -> [u8; 1] {
+ [character as u8]
}
-pub const QUIT: [u8; 1] = ctrl(b'q');
-pub const ESC: [u8; 1] = [ESC_BYTE];
pub const ENTER: [u8; 1] = [b'\r'];
+pub const ESC: [u8; 1] = [ESC_BYTE];
pub const SPACE: [u8; 1] = [b' '];
-pub const LOCK_MODE: [u8; 1] = ctrl(b'g');
-
-pub const MOVE_FOCUS_LEFT_IN_NORMAL_MODE: [u8; 2] = alt(b'h');
-pub const MOVE_FOCUS_RIGHT_IN_NORMAL_MODE: [u8; 2] = alt(b'l');
-
-pub const PANE_MODE: [u8; 1] = ctrl(b'p');
-pub const TMUX_MODE: [u8; 1] = ctrl(b'b');
-pub const SPAWN_TERMINAL_IN_PANE_MODE: [u8; 1] = [b'n'];
-pub const MOVE_FOCUS_IN_PANE_MODE: [u8; 1] = [b'p'];
-pub const SPLIT_DOWN_IN_PANE_MODE: [u8; 1] = [b'd'];
-pub const SPLIT_RIGHT_IN_PANE_MODE: [u8; 1] = [b'r'];
-pub const SPLIT_RIGHT_IN_TMUX_MODE: [u8; 1] = [b'%'];
-pub const TOGGLE_ACTIVE_TERMINAL_FULLSCREEN_IN_PANE_MODE: [u8; 1] = [b'f'];
-pub const TOGGLE_FLOATING_PANES: [u8; 1] = [b'w'];
-pub const CLOSE_PANE_IN_PANE_MODE: [u8; 1] = [b'x'];
-pub const MOVE_FOCUS_DOWN_IN_PANE_MODE: [u8; 1] = [b'j'];
-pub const MOVE_FOCUS_UP_IN_PANE_MODE: [u8; 1] = [b'k'];
-pub const MOVE_FOCUS_LEFT_IN_PANE_MODE: [u8; 1] = [b'h'];
-pub const MOVE_FOCUS_RIGHT_IN_PANE_MODE: [u8; 1] = [b'l'];
-pub const RENAME_PANE_MODE: [u8; 1] = [b'c'];
-
-pub const SCROLL_MODE: [u8; 1] = ctrl(b's');
-pub const SCROLL_UP_IN_SCROLL_MODE: [u8; 1] = [b'k'];
-pub const SCROLL_DOWN_IN_SCROLL_MODE: [u8; 1] = [b'j'];
-pub const SCROLL_PAGE_UP_IN_SCROLL_MODE: [u8; 1] = ctrl(b'b');
-pub const SCROLL_PAGE_DOWN_IN_SCROLL_MODE: [u8; 1] = ctrl(b'f');
-pub const EDIT_SCROLLBACK: [u8; 1] = [b'e'];
-
-pub const RESIZE_MODE: [u8; 1] = ctrl(b'n');
-pub const RESIZE_DOWN_IN_RESIZE_MODE: [u8; 1] = [b'j'];
-pub const RESIZE_UP_IN_RESIZE_MODE: [u8; 1] = [b'k'];
-pub const RESIZE_LEFT_IN_RESIZE_MODE: [u8; 1] = [b'h'];
-pub const RESIZE_RIGHT_IN_RESIZE_MODE: [u8; 1] = [b'l'];
-
-pub const TAB_MODE: [u8; 1] = ctrl(b't');
-pub const NEW_TAB_IN_TAB_MODE: [u8; 1] = [b'n'];
-pub const SWITCH_NEXT_TAB_IN_TAB_MODE: [u8; 1] = [b'l'];
-pub const SWITCH_PREV_TAB_IN_TAB_MODE: [u8; 1] = [b'h'];
-pub const CLOSE_TAB_IN_TAB_MODE: [u8; 1] = [b'x'];
-pub const RENAME_TAB_MODE: [u8; 1] = [b'r'];
-
-pub const MOVE_TAB_LEFT: [u8; 2] = alt(b'i');
-pub const MOVE_TAB_RIGHT: [u8; 2] = alt(b'o');
-
-pub const SESSION_MODE: [u8; 1] = ctrl(b'o');
-pub const DETACH_IN_SESSION_MODE: [u8; 1] = [b'd'];
+pub const TAB: [u8; 1] = [b'\t'];
pub const BRACKETED_PASTE_START: [u8; 6] = *b"\x1b[200~";
pub const BRACKETED_PASTE_END: [u8; 6] = *b"\x1b[201~";
diff --git a/zellij-integration-tests/src/lib.rs b/zellij-integration-tests/src/lib.rs
index 15afe865..18f652fb 100644
--- a/zellij-integration-tests/src/lib.rs
+++ b/zellij-integration-tests/src/lib.rs
@@ -4,12 +4,17 @@ pub mod client_screen;
pub mod fake_client_os_api;
pub mod fake_pty;
pub mod fake_server_os_api;
+pub mod harness;
pub mod keys;
pub mod runner;
pub mod test_env;
-pub use client_screen::{ClientScreen, CursorPosition, GridSnapshot};
+pub use client_screen::{col, ClientScreen, Coord, CoordBuilder, CursorPosition, GridSnapshot};
pub use fake_pty::FakePtyHandle;
+pub use harness::{
+ claim_first_terminal_and_wait_for_prompt, split_down_and_wait_for_prompt,
+ split_right_and_wait_for_prompt, start_zellij, PROMPT, TERMINAL_SIZE,
+};
pub use runner::{normalized, TestClient, TestRunner, TestSession};
pub use zellij_utils::data::LayoutInfo;
pub use zellij_utils::pane_size::Size;
diff --git a/zellij-integration-tests/src/runner.rs b/zellij-integration-tests/src/runner.rs
index 26b59014..c430f94b 100644
--- a/zellij-integration-tests/src/runner.rs
+++ b/zellij-integration-tests/src/runner.rs
@@ -220,7 +220,7 @@ impl TestClient {
}
pub fn quit(mut self) {
- self.send_stdin(&keys::QUIT);
+ self.send_stdin(&keys::ctrl('q'));
self.join();
}
@@ -477,8 +477,8 @@ impl TestSession {
pub fn detach_main_client(&mut self) {
let connected_clients_before_detach = self.fake_server_os_api.connected_client_count();
- self.send_stdin(&keys::SESSION_MODE);
- self.send_stdin(&keys::DETACH_IN_SESSION_MODE);
+ self.send_stdin(&keys::ctrl('o'));
+ self.send_stdin(&keys::key('d'));
self.main_client.join();
self.wait_for_server_to_release_a_client(connected_clients_before_detach);
}
@@ -498,7 +498,7 @@ impl TestSession {
pub fn quit(&mut self) {
if self.main_client.thread.is_some() {
- self.send_stdin(&keys::QUIT);
+ self.send_stdin(&keys::ctrl('q'));
self.main_client.join();
}
if let Some(server_thread) = self.server_thread.lock().unwrap().take() {
@@ -511,7 +511,7 @@ impl TestSession {
.main_client
.fake_client_handle
.stdin_tx
- .send(keys::QUIT.to_vec());
+ .send(keys::ctrl('q').to_vec());
}
}
@@ -526,9 +526,17 @@ impl Drop for TestSession {
pub fn normalized(grid_snapshot: &GridSnapshot) -> String {
let text = replace_unique_session_name(&grid_snapshot.text);
let text = strip_swap_layout_indication(&text);
+ let text = strip_tip_indication(&text);
strip_trailing_whitespace(&text)
}
+fn strip_tip_indication(text: &str) -> String {
+ regex::Regex::new(r" Tip: [^\n]*")
+ .unwrap()
+ .replace_all(text, "")
+ .to_string()
+}
+
fn replace_unique_session_name(text: &str) -> String {
regex::Regex::new(r"Zellij \(test-\d+\)")
.unwrap()
diff --git a/zellij-integration-tests/tests/app.rs b/zellij-integration-tests/tests/app.rs
index 70fd3a64..74ed11e4 100644
--- a/zellij-integration-tests/tests/app.rs
+++ b/zellij-integration-tests/tests/app.rs
@@ -1,16 +1,10 @@
#![cfg(unix)]
use insta::assert_snapshot;
-use zellij_integration_tests::{keys, normalized, FakePtyHandle, Size, TestRunner, TestSession};
-
-const TERMINAL_SIZE: Size = Size {
- cols: 120,
- rows: 24,
+use zellij_integration_tests::{
+ claim_first_terminal_and_wait_for_prompt, col, keys, normalized,
+ split_right_and_wait_for_prompt, Size, TestRunner, PROMPT, TERMINAL_SIZE,
};
-const PROMPT: &[u8] = b"$ ";
-const PROMPT_ROW: usize = 2;
-const FIRST_PANE_PROMPT_X: usize = 3;
-const RIGHT_PANE_PROMPT_X: usize = 63;
const CHANGED_KEYS_CONFIG: &str = r#"
keybinds clear-defaults=true {
@@ -27,38 +21,12 @@ keybinds clear-defaults=true {
}
"#;
-fn claim_first_terminal_and_wait_for_prompt(zellij: &TestSession) -> FakePtyHandle {
- let terminal = zellij.expect_pty_spawn();
- terminal.output(PROMPT);
- zellij.wait_until(
- "first terminal prompt rendered in loaded app",
- |grid_snapshot| {
- grid_snapshot.tab_bar_appears()
- && grid_snapshot.status_bar_appears()
- && grid_snapshot.cursor_is_at(FIRST_PANE_PROMPT_X, PROMPT_ROW)
- },
- );
- terminal
-}
-
-fn split_right_and_wait_for_prompt(zellij: &TestSession) -> FakePtyHandle {
- zellij.send_stdin(&keys::PANE_MODE);
- zellij.send_stdin(&keys::SPLIT_RIGHT_IN_PANE_MODE);
- let terminal = zellij.expect_pty_spawn();
- terminal.output(PROMPT);
- zellij.wait_until("right terminal prompt rendered", |grid_snapshot| {
- grid_snapshot.status_bar_appears()
- && grid_snapshot.cursor_is_at(RIGHT_PANE_PROMPT_X, PROMPT_ROW)
- });
- terminal
-}
-
#[test]
fn exit_zellij() {
let zellij = TestRunner::new(TERMINAL_SIZE).start();
claim_first_terminal_and_wait_for_prompt(&zellij);
- zellij.send_stdin(&keys::QUIT);
+ zellij.send_stdin(&keys::ctrl('q'));
zellij.wait_until("zellij said goodbye", |grid_snapshot| {
!grid_snapshot.status_bar_appears() && grid_snapshot.contains("Bye from Zellij!")
@@ -86,7 +54,7 @@ fn resize_terminal_window() {
zellij.wait_until("app re-rendered at the new window size", |grid_snapshot| {
grid_snapshot.contains("Ctrl +")
&& grid_snapshot.tab_bar_appears()
- && grid_snapshot.cursor_is_at(53, PROMPT_ROW)
+ && grid_snapshot.cursor_is_at(col(53).row(2))
});
assert_snapshot!(normalized(&grid_snapshot));
zellij.quit();
@@ -102,10 +70,7 @@ fn status_bar_loads_custom_keybindings() {
let grid_snapshot = zellij.wait_until(
"status bar reflects the custom keybindings",
- |grid_snapshot| {
- grid_snapshot.cursor_is_at(FIRST_PANE_PROMPT_X, PROMPT_ROW)
- && grid_snapshot.contains("LOCK")
- },
+ |grid_snapshot| grid_snapshot.cursor_is_at(col(3).row(2)) && grid_snapshot.contains("LOCK"),
);
assert_snapshot!(normalized(&grid_snapshot));
}
@@ -142,8 +107,8 @@ fn edit_scrollback() {
grid_snapshot.contains("SCROLLBACK_MARKER_TWO")
});
- zellij.send_stdin(&keys::SCROLL_MODE);
- zellij.send_stdin(&keys::EDIT_SCROLLBACK);
+ zellij.send_stdin(&keys::ctrl('s'));
+ zellij.send_stdin(&keys::key('e'));
zellij.expect_pty_spawn();
let grid_snapshot = zellij.wait_until("scrollback editor pane open", |grid_snapshot| {
diff --git a/zellij-integration-tests/tests/basic.rs b/zellij-integration-tests/tests/basic.rs
index 1b12316b..e72cd1c6 100644
--- a/zellij-integration-tests/tests/basic.rs
+++ b/zellij-integration-tests/tests/basic.rs
@@ -1,54 +1,14 @@
#![cfg(unix)]
use insta::assert_snapshot;
-use zellij_integration_tests::{keys, normalized, FakePtyHandle, Size, TestRunner, TestSession};
-
-const TERMINAL_SIZE: Size = Size {
- cols: 120,
- rows: 24,
+use zellij_integration_tests::{
+ claim_first_terminal_and_wait_for_prompt, col, keys, normalized,
+ split_right_and_wait_for_prompt, start_zellij, FakePtyHandle, TestSession,
};
-const PROMPT: &[u8] = b"$ ";
-const PROMPT_ROW: usize = 2;
-const FIRST_PANE_PROMPT_X: usize = 3;
-const RIGHT_PANE_PROMPT_X: usize = 63;
-
-fn start_zellij() -> TestSession {
- TestRunner::new(TERMINAL_SIZE).start()
-}
-
-fn claim_first_terminal_and_wait_for_prompt(zellij: &TestSession) -> FakePtyHandle {
- let terminal = zellij.expect_pty_spawn();
- terminal.output(PROMPT);
- zellij.wait_until(
- "first terminal prompt rendered in loaded app",
- |grid_snapshot| {
- grid_snapshot.tab_bar_appears()
- && grid_snapshot.status_bar_appears()
- && grid_snapshot.cursor_is_at(FIRST_PANE_PROMPT_X, PROMPT_ROW)
- },
- );
- terminal
-}
-
-fn split_pane_right(zellij: &TestSession) {
- zellij.send_stdin(&keys::PANE_MODE);
- zellij.send_stdin(&keys::SPLIT_RIGHT_IN_PANE_MODE);
-}
-
-fn split_right_and_wait_for_prompt(zellij: &TestSession) -> FakePtyHandle {
- split_pane_right(zellij);
- let terminal = zellij.expect_pty_spawn();
- terminal.output(PROMPT);
- zellij.wait_until("right terminal prompt rendered", |grid_snapshot| {
- grid_snapshot.status_bar_appears()
- && grid_snapshot.cursor_is_at(RIGHT_PANE_PROMPT_X, PROMPT_ROW)
- });
- terminal
-}
fn resize_focused_pane_left(zellij: &TestSession) {
- zellij.send_stdin(&keys::RESIZE_MODE);
- zellij.send_stdin(&keys::RESIZE_LEFT_IN_RESIZE_MODE);
+ zellij.send_stdin(&keys::ctrl('n'));
+ zellij.send_stdin(&keys::key('h'));
zellij.send_stdin(&keys::ENTER);
}
@@ -63,7 +23,7 @@ fn starts_with_one_terminal() {
let grid_snapshot = zellij.wait_until("steady loaded state", |grid_snapshot| {
grid_snapshot.tab_bar_appears()
&& grid_snapshot.status_bar_appears()
- && grid_snapshot.cursor_is_at(FIRST_PANE_PROMPT_X, PROMPT_ROW)
+ && grid_snapshot.cursor_is_at(col(3).row(2))
});
assert_snapshot!(normalized(&grid_snapshot));
zellij.quit();
@@ -75,8 +35,7 @@ fn split_terminals_vertically() {
claim_first_terminal_and_wait_for_prompt(&zellij);
split_right_and_wait_for_prompt(&zellij);
let grid_snapshot = zellij.wait_until("split rendered in normal mode", |grid_snapshot| {
- grid_snapshot.status_bar_appears()
- && grid_snapshot.cursor_is_at(RIGHT_PANE_PROMPT_X, PROMPT_ROW)
+ grid_snapshot.status_bar_appears() && grid_snapshot.cursor_is_at(col(63).row(2))
});
assert_snapshot!(normalized(&grid_snapshot));
zellij.quit();
@@ -99,7 +58,7 @@ fn resize_pane() {
let cursor_shift_left = (resized_cols.saturating_sub(initial_cols)) as usize;
let grid_snapshot = zellij.wait_until("resized layout in normal mode", |grid_snapshot| {
grid_snapshot.status_bar_appears()
- && grid_snapshot.cursor_is_at(RIGHT_PANE_PROMPT_X - cursor_shift_left, PROMPT_ROW)
+ && grid_snapshot.cursor_is_at(col(63 - cursor_shift_left).row(2))
});
assert_snapshot!(normalized(&grid_snapshot));
zellij.quit();
@@ -120,8 +79,7 @@ fn command_pane_closes_on_exit() {
zellij.wait_until(
"right terminal closed, focus back on first",
|grid_snapshot| {
- !grid_snapshot.contains("about to exit")
- && grid_snapshot.cursor_is_at(FIRST_PANE_PROMPT_X, PROMPT_ROW)
+ !grid_snapshot.contains("about to exit") && grid_snapshot.cursor_is_at(col(3).row(2))
},
);
zellij.quit();
diff --git a/zellij-integration-tests/tests/cli.rs b/zellij-integration-tests/tests/cli.rs
index 50440f2f..75c3ce4a 100644
--- a/zellij-integration-tests/tests/cli.rs
+++ b/zellij-integration-tests/tests/cli.rs
@@ -1,30 +1,10 @@
#![cfg(unix)]
use insta::assert_snapshot;
-use zellij_integration_tests::{keys, normalized, FakePtyHandle, Size, TestRunner, TestSession};
-
-const TERMINAL_SIZE: Size = Size {
- cols: 120,
- rows: 24,
+use zellij_integration_tests::{
+ claim_first_terminal_and_wait_for_prompt, col, keys, normalized, start_zellij, PROMPT,
+ TERMINAL_SIZE,
};
-const PROMPT: &[u8] = b"$ ";
-const PROMPT_ROW: usize = 2;
-const FIRST_PANE_PROMPT_X: usize = 3;
-
-fn start_zellij() -> TestSession {
- TestRunner::new(TERMINAL_SIZE).start()
-}
-
-fn claim_first_terminal_and_wait_for_prompt(zellij: &TestSession) -> FakePtyHandle {
- let terminal = zellij.expect_pty_spawn();
- terminal.output(PROMPT);
- zellij.wait_until("first terminal prompt rendered", |grid_snapshot| {
- grid_snapshot.tab_bar_appears()
- && grid_snapshot.status_bar_appears()
- && grid_snapshot.cursor_is_at(FIRST_PANE_PROMPT_X, PROMPT_ROW)
- });
- terminal
-}
#[test]
fn override_layout_from_default_to_compact() {
@@ -95,8 +75,7 @@ fn send_blocking_command_through_the_cli() {
assert_eq!(blocking_command.wait_for_exit(), 42);
let grid_snapshot = zellij.wait_until("floating pane closed on exit", |grid_snapshot| {
- !grid_snapshot.contains("PIN")
- && grid_snapshot.cursor_is_at(FIRST_PANE_PROMPT_X, PROMPT_ROW)
+ !grid_snapshot.contains("PIN") && grid_snapshot.cursor_is_at(col(3).row(2))
});
assert_snapshot!(normalized(&grid_snapshot));
zellij.quit();
@@ -116,15 +95,15 @@ fn watcher_client_functionality() {
grid_snapshot.status_bar_appears() && grid_snapshot.contains("WATCHER_OUTPUT_1")
});
- zellij.send_stdin(&keys::PANE_MODE);
- zellij.send_stdin(&keys::SPLIT_RIGHT_IN_PANE_MODE);
+ zellij.send_stdin(&keys::ctrl('p'));
+ zellij.send_stdin(&keys::key('r'));
let right_terminal = zellij.expect_pty_spawn();
right_terminal.output(PROMPT);
watcher.wait_until("watcher sees the split", |grid_snapshot| {
grid_snapshot.contains("┐┌")
});
- let ignored_new_tab_from_watcher = [keys::TAB_MODE, keys::NEW_TAB_IN_TAB_MODE].concat();
+ let ignored_new_tab_from_watcher = [keys::ctrl('t'), keys::key('n')].concat();
watcher.send_stdin(&ignored_new_tab_from_watcher);
zellij.detach_main_client();
diff --git a/zellij-integration-tests/tests/clients.rs b/zellij-integration-tests/tests/clients.rs
index 09db9941..669e5aa3 100644
--- a/zellij-integration-tests/tests/clients.rs
+++ b/zellij-integration-tests/tests/clients.rs
@@ -2,34 +2,12 @@
use insta::assert_snapshot;
use zellij_integration_tests::{
- keys, normalized, FakePtyHandle, GridSnapshot, Size, TestRunner, TestSession,
+ claim_first_terminal_and_wait_for_prompt, col, keys, normalized, start_zellij, GridSnapshot,
+ TestRunner, PROMPT, TERMINAL_SIZE,
};
-const TERMINAL_SIZE: Size = Size {
- cols: 120,
- rows: 24,
-};
-const PROMPT: &[u8] = b"$ ";
-const PROMPT_ROW: usize = 2;
-const FIRST_PANE_PROMPT_X: usize = 3;
-const RIGHT_PANE_PROMPT_X: usize = 63;
-
-fn start_zellij() -> TestSession {
- TestRunner::new(TERMINAL_SIZE).start()
-}
-
-fn claim_first_terminal_and_wait_for_prompt(zellij: &TestSession) -> FakePtyHandle {
- let terminal = zellij.expect_pty_spawn();
- terminal.output(PROMPT);
- zellij.wait_until(
- "first terminal prompt rendered in loaded app",
- |grid_snapshot| {
- grid_snapshot.tab_bar_appears()
- && grid_snapshot.status_bar_appears()
- && grid_snapshot.cursor_is_at(FIRST_PANE_PROMPT_X, PROMPT_ROW)
- },
- );
- terminal
+fn session_name_rendered(grid_snapshot: &GridSnapshot) -> bool {
+ grid_snapshot.contains("(test-")
}
#[test]
@@ -43,19 +21,20 @@ fn mirrored_sessions() {
second_client.wait_until("second client loaded on the first tab", |grid_snapshot| {
grid_snapshot.tab_bar_appears()
&& grid_snapshot.status_bar_appears()
- && grid_snapshot.cursor_is_at(FIRST_PANE_PROMPT_X, PROMPT_ROW)
+ && grid_snapshot.cursor_is_at(col(3).row(2))
});
- second_client.send_stdin(&keys::PANE_MODE);
- second_client.send_stdin(&keys::SPLIT_RIGHT_IN_PANE_MODE);
+ second_client.send_stdin(&keys::ctrl('p'));
+ second_client.send_stdin(&keys::key('r'));
let right_terminal = zellij.expect_pty_spawn();
right_terminal.output(PROMPT);
let mirror_focused_right_pane = |grid_snapshot: &GridSnapshot| {
grid_snapshot.tab_bar_appears()
+ && session_name_rendered(grid_snapshot)
&& grid_snapshot.status_bar_appears()
&& grid_snapshot.contains("Pane #2")
- && grid_snapshot.cursor_is_at(RIGHT_PANE_PROMPT_X, PROMPT_ROW)
+ && grid_snapshot.cursor_is_at(col(63).row(2))
};
let main_grid = zellij.wait_until(
"main client follows the second client's focus into the split it never asked for",
@@ -84,17 +63,19 @@ fn multiple_users_in_same_pane_and_tab() {
let second_grid =
second_client.wait_until("second client shares the focused pane", |grid_snapshot| {
grid_snapshot.tab_bar_appears()
+ && session_name_rendered(grid_snapshot)
&& grid_snapshot.status_bar_appears()
&& grid_snapshot.contains("MY FOCUS")
- && grid_snapshot.cursor_is_at(FIRST_PANE_PROMPT_X, PROMPT_ROW)
+ && grid_snapshot.cursor_is_at(col(3).row(2))
});
let main_grid = zellij.wait_until(
"main client shows the shared-focus indicator",
|grid_snapshot| {
grid_snapshot.tab_bar_appears()
+ && session_name_rendered(grid_snapshot)
&& grid_snapshot.status_bar_appears()
&& grid_snapshot.contains("MY FOCUS")
- && grid_snapshot.cursor_is_at(FIRST_PANE_PROMPT_X, PROMPT_ROW)
+ && grid_snapshot.cursor_is_at(col(3).row(2))
},
);
assert_snapshot!(normalized(&main_grid));
@@ -110,12 +91,11 @@ fn multiple_users_in_different_panes_and_same_tab() {
let second_client = zellij.attach_client(TERMINAL_SIZE);
second_client.wait_until("second client loaded on the first tab", |grid_snapshot| {
- grid_snapshot.status_bar_appears()
- && grid_snapshot.cursor_is_at(FIRST_PANE_PROMPT_X, PROMPT_ROW)
+ grid_snapshot.status_bar_appears() && grid_snapshot.cursor_is_at(col(3).row(2))
});
- second_client.send_stdin(&keys::PANE_MODE);
- second_client.send_stdin(&keys::SPLIT_RIGHT_IN_PANE_MODE);
+ second_client.send_stdin(&keys::ctrl('p'));
+ second_client.send_stdin(&keys::key('r'));
let right_terminal = zellij.expect_pty_spawn();
right_terminal.output(PROMPT);
@@ -123,17 +103,19 @@ fn multiple_users_in_different_panes_and_same_tab() {
"second client focused the new right pane",
|grid_snapshot| {
grid_snapshot.tab_bar_appears()
+ && session_name_rendered(grid_snapshot)
&& grid_snapshot.status_bar_appears()
- && grid_snapshot.cursor_is_at(RIGHT_PANE_PROMPT_X, PROMPT_ROW)
+ && grid_snapshot.cursor_is_at(col(63).row(2))
},
);
let main_grid = zellij.wait_until(
"main client sees the second client's split while staying on the left pane",
|grid_snapshot| {
grid_snapshot.tab_bar_appears()
+ && session_name_rendered(grid_snapshot)
&& grid_snapshot.contains("Pane #2")
&& grid_snapshot.status_bar_appears()
- && grid_snapshot.cursor_is_at(FIRST_PANE_PROMPT_X, PROMPT_ROW)
+ && grid_snapshot.cursor_is_at(col(3).row(2))
},
);
assert_snapshot!(normalized(&main_grid));
@@ -149,29 +131,30 @@ fn multiple_users_in_different_tabs() {
let second_client = zellij.attach_client(TERMINAL_SIZE);
second_client.wait_until("second client loaded on the first tab", |grid_snapshot| {
- grid_snapshot.status_bar_appears()
- && grid_snapshot.cursor_is_at(FIRST_PANE_PROMPT_X, PROMPT_ROW)
+ grid_snapshot.status_bar_appears() && grid_snapshot.cursor_is_at(col(3).row(2))
});
- second_client.send_stdin(&keys::TAB_MODE);
- second_client.send_stdin(&keys::NEW_TAB_IN_TAB_MODE);
+ second_client.send_stdin(&keys::ctrl('t'));
+ second_client.send_stdin(&keys::key('n'));
let second_tab_terminal = zellij.expect_pty_spawn();
second_tab_terminal.output(PROMPT);
let second_grid =
second_client.wait_until("second client moved to the new tab", |grid_snapshot| {
grid_snapshot.tab_bar_appears()
+ && session_name_rendered(grid_snapshot)
&& grid_snapshot.status_bar_appears()
&& grid_snapshot.contains("Tab #2")
- && grid_snapshot.cursor_is_at(FIRST_PANE_PROMPT_X, PROMPT_ROW)
+ && grid_snapshot.cursor_is_at(col(3).row(2))
});
let main_grid = zellij.wait_until(
"main client sees the new tab while staying on the first tab",
|grid_snapshot| {
grid_snapshot.tab_bar_appears()
+ && session_name_rendered(grid_snapshot)
&& grid_snapshot.status_bar_appears()
&& grid_snapshot.contains("Tab #2")
- && grid_snapshot.cursor_is_at(FIRST_PANE_PROMPT_X, PROMPT_ROW)
+ && grid_snapshot.cursor_is_at(col(3).row(2))
},
);
assert_snapshot!(normalized(&main_grid));
@@ -185,13 +168,12 @@ fn detach_and_attach_session() {
let mut zellij = start_zellij();
claim_first_terminal_and_wait_for_prompt(&zellij);
- zellij.send_stdin(&keys::PANE_MODE);
- zellij.send_stdin(&keys::SPLIT_RIGHT_IN_PANE_MODE);
+ zellij.send_stdin(&keys::ctrl('p'));
+ zellij.send_stdin(&keys::key('r'));
let right_terminal = zellij.expect_pty_spawn();
right_terminal.output(PROMPT);
zellij.wait_until("right terminal prompt rendered", |grid_snapshot| {
- grid_snapshot.status_bar_appears()
- && grid_snapshot.cursor_is_at(RIGHT_PANE_PROMPT_X, PROMPT_ROW)
+ grid_snapshot.status_bar_appears() && grid_snapshot.cursor_is_at(col(63).row(2))
});
right_terminal.output(b"I am some text");
@@ -206,6 +188,7 @@ fn detach_and_attach_session() {
"reattached client sees the restored split and text",
|grid_snapshot| {
grid_snapshot.tab_bar_appears()
+ && session_name_rendered(grid_snapshot)
&& grid_snapshot.status_bar_appears()
&& grid_snapshot.contains("Pane #2")
&& grid_snapshot.contains("I am some text")
diff --git a/zellij-integration-tests/tests/misc.rs b/zellij-integration-tests/tests/misc.rs
new file mode 100644
index 00000000..83a02878
--- /dev/null
+++ b/zellij-integration-tests/tests/misc.rs
@@ -0,0 +1,136 @@
+#![cfg(unix)]
+
+use insta::assert_snapshot;
+use zellij_integration_tests::{
+ claim_first_terminal_and_wait_for_prompt, col, keys, normalized,
+ split_right_and_wait_for_prompt, start_zellij,
+};
+
+#[test]
+fn quit_with_keybinding() {
+ let zellij = start_zellij();
+ claim_first_terminal_and_wait_for_prompt(&zellij);
+
+ zellij.send_stdin(&keys::ctrl('q'));
+
+ zellij.wait_until("zellij exited after the quit keybinding", |grid_snapshot| {
+ grid_snapshot.contains("Bye from Zellij!")
+ });
+}
+
+#[test]
+fn write_byte_to_focused_pane_via_tmux_mode() {
+ let mut zellij = start_zellij();
+ let terminal = claim_first_terminal_and_wait_for_prompt(&zellij);
+
+ zellij.send_stdin(&keys::ctrl('b'));
+ zellij.send_stdin(&keys::ctrl('b'));
+
+ let stdin_bytes = terminal.wait_for_stdin("ctrl-b byte reached the pane", |stdin_bytes| {
+ stdin_bytes.contains(&0x02)
+ });
+ assert!(stdin_bytes.contains(&0x02));
+ let grid_snapshot = zellij.wait_until("mode settled back to normal", |grid_snapshot| {
+ grid_snapshot.status_bar_appears()
+ });
+ assert_snapshot!(normalized(&grid_snapshot));
+ zellij.quit();
+}
+
+#[test]
+fn focus_next_pane_via_tmux_mode() {
+ let mut zellij = start_zellij();
+ claim_first_terminal_and_wait_for_prompt(&zellij);
+ split_right_and_wait_for_prompt(&zellij);
+
+ zellij.send_stdin(&keys::ctrl('b'));
+ zellij.send_stdin(&keys::key('o'));
+ zellij.send_stdin(&keys::ENTER);
+
+ let grid_snapshot = zellij.wait_until("focus cycled to the other pane", |grid_snapshot| {
+ grid_snapshot.status_bar_appears() && grid_snapshot.cursor_is_at(col(3).row(2))
+ });
+ assert_snapshot!(normalized(&grid_snapshot));
+ zellij.quit();
+}
+
+#[test]
+fn toggle_group_marking() {
+ let mut zellij = start_zellij();
+ claim_first_terminal_and_wait_for_prompt(&zellij);
+ split_right_and_wait_for_prompt(&zellij);
+
+ zellij.send_stdin(&keys::alt('P'));
+
+ let grid_snapshot = zellij.wait_until("group marking engaged", |grid_snapshot| {
+ grid_snapshot.contains("GROUP ACTIONS")
+ && grid_snapshot.contains("SELECTED PANE")
+ && grid_snapshot.contains("Follow Focus")
+ });
+ assert_snapshot!(normalized(&grid_snapshot));
+ zellij.quit();
+}
+
+#[test]
+fn toggle_pane_in_group() {
+ let mut zellij = start_zellij();
+ claim_first_terminal_and_wait_for_prompt(&zellij);
+ split_right_and_wait_for_prompt(&zellij);
+
+ zellij.send_stdin(&keys::alt('p'));
+
+ let grid_snapshot = zellij.wait_until("pane added to group", |grid_snapshot| {
+ grid_snapshot.contains("GROUP ACTIONS")
+ && grid_snapshot.contains("SELECTED PANE")
+ && grid_snapshot.contains("Follow Focus")
+ });
+ assert_snapshot!(normalized(&grid_snapshot));
+ zellij.quit();
+}
+
+#[test]
+fn next_swap_layout() {
+ let mut zellij = start_zellij();
+ claim_first_terminal_and_wait_for_prompt(&zellij);
+ split_right_and_wait_for_prompt(&zellij);
+ zellij.wait_until("swap layout at base", |grid_snapshot| {
+ grid_snapshot.contains("BASE")
+ });
+
+ zellij.send_stdin(&keys::alt(']'));
+
+ zellij.wait_until("swap layout advanced to vertical", |grid_snapshot| {
+ grid_snapshot.status_bar_appears()
+ && grid_snapshot.contains("VERTICAL")
+ && !grid_snapshot.contains("BASE")
+ });
+ zellij.send_stdin(&keys::alt(']'));
+ let grid_snapshot = zellij.wait_until("swap layout advanced to horizontal", |grid_snapshot| {
+ grid_snapshot.status_bar_appears()
+ && !grid_snapshot.contains("VERTICAL")
+ && grid_snapshot.contains("HORIZONTAL")
+ });
+ assert_snapshot!(normalized(&grid_snapshot));
+ zellij.quit();
+}
+
+#[test]
+fn previous_swap_layout() {
+ let mut zellij = start_zellij();
+ claim_first_terminal_and_wait_for_prompt(&zellij);
+ split_right_and_wait_for_prompt(&zellij);
+ zellij.wait_until("swap layout at base", |grid_snapshot| {
+ grid_snapshot.contains("BASE")
+ });
+
+ zellij.send_stdin(&keys::alt('['));
+
+ let grid_snapshot =
+ zellij.wait_until("swap layout moved back to horizontal", |grid_snapshot| {
+ grid_snapshot.status_bar_appears()
+ && grid_snapshot.contains("HORIZONTAL")
+ && !grid_snapshot.contains("BASE")
+ });
+ assert_snapshot!(normalized(&grid_snapshot));
+ zellij.quit();
+}
diff --git a/zellij-integration-tests/tests/modes.rs b/zellij-integration-tests/tests/modes.rs
index c564be70..0fc093be 100644
--- a/zellij-integration-tests/tests/modes.rs
+++ b/zellij-integration-tests/tests/modes.rs
@@ -1,33 +1,147 @@
#![cfg(unix)]
use insta::assert_snapshot;
-use zellij_integration_tests::{keys, normalized, FakePtyHandle, Size, TestRunner, TestSession};
-
-const TERMINAL_SIZE: Size = Size {
- cols: 120,
- rows: 24,
+use zellij_integration_tests::{
+ claim_first_terminal_and_wait_for_prompt, col, keys, normalized, start_zellij, PROMPT,
};
-const PROMPT: &[u8] = b"$ ";
-const PROMPT_ROW: usize = 2;
-const FIRST_PANE_PROMPT_X: usize = 3;
-const RIGHT_PANE_PROMPT_X: usize = 63;
-fn start_zellij() -> TestSession {
- TestRunner::new(TERMINAL_SIZE).start()
+#[test]
+fn switch_to_pane_mode() {
+ let mut zellij = start_zellij();
+ claim_first_terminal_and_wait_for_prompt(&zellij);
+
+ zellij.send_stdin(&keys::ctrl('p'));
+
+ let grid_snapshot = zellij.wait_until("pane mode active", |grid_snapshot| {
+ grid_snapshot.contains("PANE") && !grid_snapshot.contains("LOCK")
+ });
+ assert_snapshot!(normalized(&grid_snapshot));
+ zellij.quit();
+}
+
+#[test]
+fn switch_to_tab_mode() {
+ let mut zellij = start_zellij();
+ claim_first_terminal_and_wait_for_prompt(&zellij);
+
+ zellij.send_stdin(&keys::ctrl('t'));
+
+ let grid_snapshot = zellij.wait_until("tab mode active", |grid_snapshot| {
+ grid_snapshot.contains("TAB") && !grid_snapshot.contains("LOCK")
+ });
+ assert_snapshot!(normalized(&grid_snapshot));
+ zellij.quit();
+}
+
+#[test]
+fn switch_to_resize_mode() {
+ let mut zellij = start_zellij();
+ claim_first_terminal_and_wait_for_prompt(&zellij);
+
+ zellij.send_stdin(&keys::ctrl('n'));
+
+ let grid_snapshot = zellij.wait_until("resize mode active", |grid_snapshot| {
+ grid_snapshot.contains("RESIZE") && !grid_snapshot.contains("LOCK")
+ });
+ assert_snapshot!(normalized(&grid_snapshot));
+ zellij.quit();
+}
+
+#[test]
+fn switch_to_move_mode() {
+ let mut zellij = start_zellij();
+ claim_first_terminal_and_wait_for_prompt(&zellij);
+
+ zellij.send_stdin(&keys::ctrl('h'));
+
+ let grid_snapshot = zellij.wait_until("move mode active", |grid_snapshot| {
+ grid_snapshot.contains("MOVE") && !grid_snapshot.contains("LOCK")
+ });
+ assert_snapshot!(normalized(&grid_snapshot));
+ zellij.quit();
+}
+
+#[test]
+fn switch_to_scroll_mode() {
+ let mut zellij = start_zellij();
+ claim_first_terminal_and_wait_for_prompt(&zellij);
+
+ zellij.send_stdin(&keys::ctrl('s'));
+
+ let grid_snapshot = zellij.wait_until("scroll mode active", |grid_snapshot| {
+ grid_snapshot.contains("Edit") && !grid_snapshot.contains("LOCK")
+ });
+ assert_snapshot!(normalized(&grid_snapshot));
+ zellij.quit();
}
-fn claim_first_terminal_and_wait_for_prompt(zellij: &TestSession) -> FakePtyHandle {
- let terminal = zellij.expect_pty_spawn();
- terminal.output(PROMPT);
- zellij.wait_until(
- "first terminal prompt rendered in loaded app",
- |grid_snapshot| {
- grid_snapshot.tab_bar_appears()
- && grid_snapshot.status_bar_appears()
- && grid_snapshot.cursor_is_at(FIRST_PANE_PROMPT_X, PROMPT_ROW)
- },
- );
- terminal
+#[test]
+fn switch_to_session_mode() {
+ let mut zellij = start_zellij();
+ claim_first_terminal_and_wait_for_prompt(&zellij);
+
+ zellij.send_stdin(&keys::ctrl('o'));
+
+ let grid_snapshot = zellij.wait_until("session mode active", |grid_snapshot| {
+ grid_snapshot.contains("Detach") && !grid_snapshot.contains("LOCK")
+ });
+ assert_snapshot!(normalized(&grid_snapshot));
+ zellij.quit();
+}
+
+#[test]
+fn enter_search_mode() {
+ let mut zellij = start_zellij();
+ claim_first_terminal_and_wait_for_prompt(&zellij);
+
+ zellij.send_stdin(&keys::ctrl('s'));
+ zellij.send_stdin(&keys::key('s'));
+
+ let grid_snapshot = zellij.wait_until("search input mode active", |grid_snapshot| {
+ grid_snapshot.contains("ENTERING SEARCH TERM") && !grid_snapshot.contains("LOCK")
+ });
+ assert_snapshot!(normalized(&grid_snapshot));
+ zellij.quit();
+}
+
+#[test]
+fn switch_to_search_mode() {
+ let mut zellij = start_zellij();
+ claim_first_terminal_and_wait_for_prompt(&zellij);
+
+ zellij.send_stdin(&keys::ctrl('s'));
+ zellij.send_stdin(&keys::key('s'));
+ zellij.wait_until("search input mode active", |grid_snapshot| {
+ grid_snapshot.contains("ENTERING SEARCH TERM")
+ });
+ zellij.send_stdin(&keys::ENTER);
+
+ let grid_snapshot = zellij.wait_until("search navigation mode active", |grid_snapshot| {
+ grid_snapshot.contains("SEARCHING")
+ && grid_snapshot.contains("Case")
+ && !grid_snapshot.contains("LOCK")
+ });
+ assert_snapshot!(normalized(&grid_snapshot));
+ zellij.quit();
+}
+
+#[test]
+fn return_to_normal_mode() {
+ let mut zellij = start_zellij();
+ claim_first_terminal_and_wait_for_prompt(&zellij);
+
+ zellij.send_stdin(&keys::ctrl('p'));
+ zellij.wait_until("pane mode active", |grid_snapshot| {
+ grid_snapshot.contains("PANE") && !grid_snapshot.contains("LOCK")
+ });
+
+ zellij.send_stdin(&keys::ENTER);
+
+ let grid_snapshot = zellij.wait_until("returned to normal mode", |grid_snapshot| {
+ grid_snapshot.status_bar_appears() && grid_snapshot.cursor_is_at(col(3).row(2))
+ });
+ assert_snapshot!(normalized(&grid_snapshot));
+ zellij.quit();
}
#[test]
@@ -35,13 +149,13 @@ fn lock_mode() {
let mut zellij = start_zellij();
let terminal = claim_first_terminal_and_wait_for_prompt(&zellij);
- zellij.send_stdin(&keys::LOCK_MODE);
+ zellij.send_stdin(&keys::ctrl('g'));
zellij.wait_until("interface locked", |grid_snapshot| {
grid_snapshot.contains("LOCK") && !grid_snapshot.contains("PANE")
});
- zellij.send_stdin(&keys::TAB_MODE);
- zellij.send_stdin(&keys::NEW_TAB_IN_TAB_MODE);
+ zellij.send_stdin(&keys::ctrl('t'));
+ zellij.send_stdin(&keys::key('n'));
zellij.send_stdin(b"abc");
terminal.wait_for_stdin("forwarded keys reached the pane", |stdin_bytes| {
@@ -55,7 +169,7 @@ fn lock_mode() {
});
assert_snapshot!(normalized(&grid_snapshot));
- zellij.send_stdin(&keys::LOCK_MODE);
+ zellij.send_stdin(&keys::ctrl('g'));
zellij.wait_until("interface unlocked", |grid_snapshot| {
grid_snapshot.contains("PANE")
});
@@ -67,14 +181,13 @@ fn tmux_mode() {
let mut zellij = start_zellij();
claim_first_terminal_and_wait_for_prompt(&zellij);
- zellij.send_stdin(&keys::TMUX_MODE);
- zellij.send_stdin(&keys::SPLIT_RIGHT_IN_TMUX_MODE);
+ zellij.send_stdin(&keys::ctrl('b'));
+ zellij.send_stdin(&keys::key('%'));
let right_terminal = zellij.expect_pty_spawn();
right_terminal.output(PROMPT);
let grid_snapshot = zellij.wait_until("right terminal opened via tmux mode", |grid_snapshot| {
- grid_snapshot.status_bar_appears()
- && grid_snapshot.cursor_is_at(RIGHT_PANE_PROMPT_X, PROMPT_ROW)
+ grid_snapshot.status_bar_appears() && grid_snapshot.cursor_is_at(col(63).row(2))
});
assert_snapshot!(normalized(&grid_snapshot));
zellij.quit();
@@ -86,8 +199,8 @@ fn bracketed_paste() {
let terminal = claim_first_terminal_and_wait_for_prompt(&zellij);
zellij.send_stdin(&keys::BRACKETED_PASTE_START);
- zellij.send_stdin(&keys::TAB_MODE);
- zellij.send_stdin(&keys::NEW_TAB_IN_TAB_MODE);
+ zellij.send_stdin(&keys::ctrl('t'));
+ zellij.send_stdin(&keys::key('n'));
zellij.send_stdin(b"abc");
zellij.send_stdin(&keys::BRACKETED_PASTE_END);
@@ -108,13 +221,12 @@ fn scrolling_inside_a_pane() {
let mut zellij = start_zellij();
claim_first_terminal_and_wait_for_prompt(&zellij);
- zellij.send_stdin(&keys::PANE_MODE);
- zellij.send_stdin(&keys::SPLIT_RIGHT_IN_PANE_MODE);
+ zellij.send_stdin(&keys::ctrl('p'));
+ zellij.send_stdin(&keys::key('r'));
let right_terminal = zellij.expect_pty_spawn();
right_terminal.output(PROMPT);
zellij.wait_until("right terminal prompt rendered", |grid_snapshot| {
- grid_snapshot.status_bar_appears()
- && grid_snapshot.cursor_is_at(RIGHT_PANE_PROMPT_X, PROMPT_ROW)
+ grid_snapshot.status_bar_appears() && grid_snapshot.cursor_is_at(col(63).row(2))
});
for line in 1..=21 {
@@ -124,9 +236,9 @@ fn scrolling_inside_a_pane() {
grid_snapshot.contains("line21")
});
- zellij.send_stdin(&keys::SCROLL_MODE);
- zellij.send_stdin(&keys::SCROLL_UP_IN_SCROLL_MODE);
- zellij.send_stdin(&keys::SCROLL_UP_IN_SCROLL_MODE);
+ zellij.send_stdin(&keys::ctrl('s'));
+ zellij.send_stdin(&keys::key('k'));
+ zellij.send_stdin(&keys::key('k'));
let grid_snapshot =
zellij.wait_until("scrolled up one line inside the pane", |grid_snapshot| {
diff --git a/zellij-integration-tests/tests/mouse.rs b/zellij-integration-tests/tests/mouse.rs
index 40562a12..e4aa038c 100644
--- a/zellij-integration-tests/tests/mouse.rs
+++ b/zellij-integration-tests/tests/mouse.rs
@@ -1,16 +1,10 @@
#![cfg(unix)]
use insta::assert_snapshot;
-use zellij_integration_tests::{keys, normalized, FakePtyHandle, Size, TestRunner, TestSession};
-
-const TERMINAL_SIZE: Size = Size {
- cols: 120,
- rows: 24,
+use zellij_integration_tests::{
+ claim_first_terminal_and_wait_for_prompt, col, keys, normalized,
+ split_right_and_wait_for_prompt, TestRunner, TestSession, PROMPT, TERMINAL_SIZE,
};
-const PROMPT: &[u8] = b"$ ";
-const PROMPT_ROW: usize = 2;
-const FIRST_PANE_PROMPT_X: usize = 3;
-const RIGHT_PANE_PROMPT_X: usize = 63;
fn start_zellij() -> TestSession {
TestRunner::new(TERMINAL_SIZE)
@@ -22,32 +16,6 @@ fn sgr_mouse_report(column: usize, line: usize, button: u8) -> Vec<u8> {
format!("\u{1b}[<{};{};{}M", button, column, line).into_bytes()
}
-fn claim_first_terminal_and_wait_for_prompt(zellij: &TestSession) -> FakePtyHandle {
- let terminal = zellij.expect_pty_spawn();
- terminal.output(PROMPT);
- zellij.wait_until(
- "first terminal prompt rendered in loaded app",
- |grid_snapshot| {
- grid_snapshot.tab_bar_appears()
- && grid_snapshot.status_bar_appears()
- && grid_snapshot.cursor_is_at(FIRST_PANE_PROMPT_X, PROMPT_ROW)
- },
- );
- terminal
-}
-
-fn split_right_and_wait_for_prompt(zellij: &TestSession) -> FakePtyHandle {
- zellij.send_stdin(&keys::PANE_MODE);
- zellij.send_stdin(&keys::SPLIT_RIGHT_IN_PANE_MODE);
- let terminal = zellij.expect_pty_spawn();
- terminal.output(PROMPT);
- zellij.wait_until("right terminal prompt rendered", |grid_snapshot| {
- grid_snapshot.status_bar_appears()
- && grid_snapshot.cursor_is_at(RIGHT_PANE_PROMPT_X, PROMPT_ROW)
- });
- terminal
-}
-
#[test]
fn focus_pane_with_mouse() {
let mut zellij = start_zellij();
@@ -56,8 +24,7 @@ fn focus_pane_with_mouse() {
zellij.send_stdin(&sgr_mouse_report(2, 5, 0));
let grid_snapshot = zellij.wait_until("focus moved back to the left pane", |grid_snapshot| {
- grid_snapshot.status_bar_appears()
- && grid_snapshot.cursor_is_at(FIRST_PANE_PROMPT_X, PROMPT_ROW)
+ grid_snapshot.status_bar_appears() && grid_snapshot.cursor_is_at(col(3).row(2))
});
assert_snapshot!(normalized(&grid_snapshot));
zellij.quit();
@@ -91,8 +58,8 @@ fn pin_floating_panes() {
let mut zellij = start_zellij();
let tiled_terminal = claim_first_terminal_and_wait_for_prompt(&zellij);
- zellij.send_stdin(&keys::PANE_MODE);
- zellij.send_stdin(&keys::TOGGLE_FLOATING_PANES);
+ zellij.send_stdin(&keys::ctrl('p'));
+ zellij.send_stdin(&keys::key('w'));
let floating_terminal = zellij.expect_pty_spawn();
floating_terminal.output(PROMPT);
zellij.wait_until("floating pane shows the unpinned button", |grid_snapshot| {
@@ -104,14 +71,14 @@ fn pin_floating_panes() {
grid_snapshot.contains("PIN [+]")
});
- zellij.send_stdin(&keys::PANE_MODE);
- zellij.send_stdin(&keys::TOGGLE_FLOATING_PANES);
+ zellij.send_stdin(&keys::ctrl('p'));
+ zellij.send_stdin(&keys::key('w'));
zellij.wait_until(
"focus settled on the tiled pane while the pinned pane stays up",
|grid_snapshot| {
grid_snapshot.status_bar_appears()
&& grid_snapshot.contains("PIN [+]")
- && grid_snapshot.cursor_is_at(FIRST_PANE_PROMPT_X, PROMPT_ROW)
+ && grid_snapshot.cursor_is_at(col(3).row(2))
},
);
diff --git a/zellij-integration-tests/tests/panes.rs b/zellij-integration-tests/tests/panes.rs
index 5e14b103..9defe5f2 100644
--- a/zellij-integration-tests/tests/panes.rs
+++ b/zellij-integration-tests/tests/panes.rs
@@ -1,46 +1,11 @@
#![cfg(unix)]
use insta::assert_snapshot;
-use zellij_integration_tests::{keys, normalized, FakePtyHandle, Size, TestRunner, TestSession};
-
-const TERMINAL_SIZE: Size = Size {
- cols: 120,
- rows: 24,
+use zellij_integration_tests::{
+ claim_first_terminal_and_wait_for_prompt, col, keys, normalized,
+ split_down_and_wait_for_prompt, split_right_and_wait_for_prompt, start_zellij, Coord, Size,
+ TestRunner, TestSession, PROMPT, TERMINAL_SIZE,
};
-const PROMPT: &[u8] = b"$ ";
-const PROMPT_ROW: usize = 2;
-const FIRST_PANE_PROMPT_X: usize = 3;
-const RIGHT_PANE_PROMPT_X: usize = 63;
-
-fn start_zellij() -> TestSession {
- TestRunner::new(TERMINAL_SIZE).start()
-}
-
-fn claim_first_terminal_and_wait_for_prompt(zellij: &TestSession) -> FakePtyHandle {
- let terminal = zellij.expect_pty_spawn();
- terminal.output(PROMPT);
- zellij.wait_until(
- "first terminal prompt rendered in loaded app",
- |grid_snapshot| {
- grid_snapshot.tab_bar_appears()
- && grid_snapshot.status_bar_appears()
- && grid_snapshot.cursor_is_at(FIRST_PANE_PROMPT_X, PROMPT_ROW)
- },
- );
- terminal
-}
-
-fn split_right_and_wait_for_prompt(zellij: &TestSession) -> FakePtyHandle {
- zellij.send_stdin(&keys::PANE_MODE);
- zellij.send_stdin(&keys::SPLIT_RIGHT_IN_PANE_MODE);
- let terminal = zellij.expect_pty_spawn();
- terminal.output(PROMPT);
- zellij.wait_until("right terminal prompt rendered", |grid_snapshot| {
- grid_snapshot.status_bar_appears()
- && grid_snapshot.cursor_is_at(RIGHT_PANE_PROMPT_X, PROMPT_ROW)
- });
- terminal
-}
#[test]
fn cannot_split_terminals_vertically_when_active_terminal_is_too_small() {
@@ -48,12 +13,12 @@ fn cannot_split_terminals_vertically_when_active_terminal_is_too_small() {
let terminal = zellij.expect_pty_spawn();
terminal.output(PROMPT);
zellij.wait_until("first terminal prompt rendered", |grid_snapshot| {
- grid_snapshot.cursor_is_at(FIRST_PANE_PROMPT_X, PROMPT_ROW)
+ grid_snapshot.cursor_is_at(col(3).row(2))
});
let (width_before_split, _) = terminal.wait_for_size("first terminal sized", |_, _| true);
- zellij.send_stdin(&keys::PANE_MODE);
- zellij.send_stdin(&keys::SPLIT_RIGHT_IN_PANE_MODE);
+ zellij.send_stdin(&keys::ctrl('p'));
+ zellij.send_stdin(&keys::key('r'));
terminal.output(b"done");
let tab_bar_glyph = '\u{e0b0}';
@@ -79,18 +44,16 @@ fn key_after_mode_switch_is_interpreted_in_new_mode_not_leaked_to_pane() {
let mut zellij = start_zellij();
let first_terminal = claim_first_terminal_and_wait_for_prompt(&zellij);
- zellij.send_stdin(&keys::PANE_MODE);
- zellij.send_stdin(&keys::SPLIT_RIGHT_IN_PANE_MODE);
+ zellij.send_stdin(&keys::ctrl('p'));
+ zellij.send_stdin(&keys::key('r'));
let second_terminal = zellij.expect_pty_spawn();
second_terminal.output(PROMPT);
zellij.wait_until("split-right interpreted in pane mode", |grid_snapshot| {
- grid_snapshot.cursor_is_at(RIGHT_PANE_PROMPT_X, PROMPT_ROW)
+ grid_snapshot.cursor_is_at(col(63).row(2))
});
- assert!(!first_terminal
- .stdin_bytes()
- .contains(&keys::SPLIT_RIGHT_IN_PANE_MODE[0]));
+ assert!(!first_terminal.stdin_bytes().contains(&keys::key('r')[0]));
zellij.quit();
}
@@ -100,11 +63,11 @@ fn toggle_pane_fullscreen() {
claim_first_terminal_and_wait_for_prompt(&zellij);
split_right_and_wait_for_prompt(&zellij);
- zellij.send_stdin(&keys::PANE_MODE);
- zellij.send_stdin(&keys::TOGGLE_ACTIVE_TERMINAL_FULLSCREEN_IN_PANE_MODE);
+ zellij.send_stdin(&keys::ctrl('p'));
+ zellij.send_stdin(&keys::key('f'));
let grid_snapshot = zellij.wait_until("focused pane is fullscreen", |grid_snapshot| {
- grid_snapshot.cursor_is_at(FIRST_PANE_PROMPT_X, PROMPT_ROW)
+ grid_snapshot.cursor_is_at(col(3).row(2))
&& grid_snapshot.contains("LOCK")
&& grid_snapshot.contains("(FULLSCREEN)")
});
@@ -118,13 +81,12 @@ fn close_pane() {
claim_first_terminal_and_wait_for_prompt(&zellij);
split_right_and_wait_for_prompt(&zellij);
- zellij.send_stdin(&keys::PANE_MODE);
- zellij.send_stdin(&keys::CLOSE_PANE_IN_PANE_MODE);
+ zellij.send_stdin(&keys::ctrl('p'));
+ zellij.send_stdin(&keys::key('x'));
let grid_snapshot =
zellij.wait_until("right pane closed, focus back on first", |grid_snapshot| {
- grid_snapshot.status_bar_appears()
- && grid_snapshot.cursor_is_at(FIRST_PANE_PROMPT_X, PROMPT_ROW)
+ grid_snapshot.status_bar_appears() && grid_snapshot.cursor_is_at(col(3).row(2))
});
assert_snapshot!(normalized(&grid_snapshot));
zellij.quit();
@@ -135,8 +97,8 @@ fn closing_last_pane_exits_zellij() {
let zellij = start_zellij();
claim_first_terminal_and_wait_for_prompt(&zellij);
- zellij.send_stdin(&keys::PANE_MODE);
- zellij.send_stdin(&keys::CLOSE_PANE_IN_PANE_MODE);
+ zellij.send_stdin(&keys::ctrl('p'));
+ zellij.send_stdin(&keys::key('x'));
zellij.wait_until(
"zellij exited after closing the last pane",
@@ -154,8 +116,7 @@ fn pane_closes_when_its_process_exits() {
let grid_snapshot =
zellij.wait_until("right pane closed, focus back on first", |grid_snapshot| {
- grid_snapshot.status_bar_appears()
- && grid_snapshot.cursor_is_at(FIRST_PANE_PROMPT_X, PROMPT_ROW)
+ grid_snapshot.status_bar_appears() && grid_snapshot.cursor_is_at(col(3).row(2))
});
assert_snapshot!(normalized(&grid_snapshot));
zellij.quit();
@@ -166,13 +127,13 @@ fn toggle_floating_panes() {
let mut zellij = start_zellij();
claim_first_terminal_and_wait_for_prompt(&zellij);
- zellij.send_stdin(&keys::PANE_MODE);
- zellij.send_stdin(&keys::TOGGLE_FLOATING_PANES);
+ zellij.send_stdin(&keys::ctrl('p'));
+ zellij.send_stdin(&keys::key('w'));
let floating_terminal = zellij.expect_pty_spawn();
floating_terminal.output(PROMPT);
let grid_snapshot = zellij.wait_until("floating pane appeared", |grid_snapshot| {
- grid_snapshot.cursor_is_at(33, 8)
+ grid_snapshot.cursor_is_at(col(33).row(8))
&& grid_snapshot.contains("STAGGERED")
&& grid_snapshot.contains("LOCK")
});
@@ -185,8 +146,8 @@ fn undo_rename_pane() {
let mut zellij = start_zellij();
claim_first_terminal_and_wait_for_prompt(&zellij);
- zellij.send_stdin(&keys::PANE_MODE);
- zellij.send_stdin(&keys::RENAME_PANE_MODE);
+ zellij.send_stdin(&keys::ctrl('p'));
+ zellij.send_stdin(&keys::key('c'));
zellij.send_stdin(b"aa");
zellij.send_stdin(&keys::ESC);
zellij.send_stdin(&keys::ESC);
@@ -198,6 +159,370 @@ fn undo_rename_pane() {
zellij.quit();
}
+fn spawn_second_pane_and_wait_for_grid(
+ zellij: &TestSession,
+ prompt: Coord,
+) -> zellij_integration_tests::GridSnapshot {
+ let terminal = zellij.expect_pty_spawn();
+ terminal.output(PROMPT);
+ zellij.wait_until(
+ "second pane spawned and prompt rendered",
+ move |grid_snapshot| {
+ grid_snapshot.status_bar_appears()
+ && grid_snapshot.contains("Pane #2")
+ && grid_snapshot.cursor_is_at(prompt)
+ },
+ )
+}
+
+fn appears_in_order(
+ grid_snapshot: &zellij_integration_tests::GridSnapshot,
+ labels: &[&str],
+) -> bool {
+ let mut search_from = 0;
+ for label in labels {
+ match grid_snapshot.text[search_from..].find(label) {
+ Some(offset) => search_from += offset + label.len(),
+ None => return false,
+ }
+ }
+ true
+}
+
+#[test]
+fn move_focus_up_in_pane_mode() {
+ let mut zellij = start_zellij();
+ claim_first_terminal_and_wait_for_prompt(&zellij);
+ split_down_and_wait_for_prompt(&zellij);
+
+ zellij.send_stdin(&keys::ctrl('p'));
+ zellij.send_stdin(&keys::key('k'));
+ zellij.send_stdin(&keys::ENTER);
+
+ let grid_snapshot = zellij.wait_until("focus moved to the upper pane", |grid_snapshot| {
+ grid_snapshot.status_bar_appears() && grid_snapshot.cursor_is_at(col(3).row(2))
+ });
+ assert_snapshot!(normalized(&grid_snapshot));
+ zellij.quit();
+}
+
+#[test]
+fn move_focus_down_in_pane_mode() {
+ let mut zellij = start_zellij();
+ claim_first_terminal_and_wait_for_prompt(&zellij);
+ split_down_and_wait_for_prompt(&zellij);
+
+ zellij.send_stdin(&keys::ctrl('p'));
+ zellij.send_stdin(&keys::key('k'));
+ zellij.wait_until("focus moved to the upper pane", |grid_snapshot| {
+ grid_snapshot.cursor_is_at(col(3).row(2))
+ });
+
+ zellij.send_stdin(&keys::key('j'));
+ zellij.send_stdin(&keys::ENTER);
+
+ let grid_snapshot = zellij.wait_until("focus moved back to the lower pane", |grid_snapshot| {
+ grid_snapshot.status_bar_appears() && grid_snapshot.cursor_is_at(col(3).row(13))
+ });
+ assert_snapshot!(normalized(&grid_snapshot));
+ zellij.quit();
+}
+
+#[test]
+fn move_pane_swaps_with_neighbor() {
+ let mut zellij = start_zellij();
+ let left_terminal = claim_first_terminal_and_wait_for_prompt(&zellij);
+ let right_terminal = split_right_and_wait_for_prompt(&zellij);
+ left_terminal.output(b"AAA");
+ right_terminal.output(b"BBB");
+ zellij.wait_until("both panes labelled left to right", |grid_snapshot| {
+ appears_in_order(grid_snapshot, &["AAA", "BBB"])
+ });
+
+ zellij.send_stdin(&keys::ctrl('h'));
+ zellij.send_stdin(&keys::key('n'));
+ zellij.send_stdin(&keys::ENTER);
+
+ let grid_snapshot = zellij.wait_until("focused pane swapped to the left", |grid_snapshot| {
+ appears_in_order(grid_snapshot, &["BBB", "AAA"]) && grid_snapshot.status_bar_appears()
+ });
+ assert_snapshot!(normalized(&grid_snapshot));
+ zellij.quit();
+}
+
+#[test]
+fn move_pane_backwards_swaps_with_neighbor() {
+ let mut zellij = start_zellij();
+ let left_terminal = claim_first_terminal_and_wait_for_prompt(&zellij);
+ let right_terminal = split_right_and_wait_for_prompt(&zellij);
+ left_terminal.output(b"AAA");
+ right_terminal.output(b"BBB");
+ zellij.wait_until("both panes labelled left to right", |grid_snapshot| {
+ appears_in_order(grid_snapshot, &["AAA", "BBB"])
+ });
+
+ zellij.send_stdin(&keys::ctrl('h'));
+ zellij.send_stdin(&keys::key('p'));
+ zellij.send_stdin(&keys::ENTER);
+
+ let grid_snapshot = zellij.wait_until("focused pane swapped backwards", |grid_snapshot| {
+ appears_in_order(grid_snapshot, &["BBB", "AAA"]) && grid_snapshot.status_bar_appears()
+ });
+ assert_snapshot!(normalized(&grid_snapshot));
+ zellij.quit();
+}
+
+#[test]
+fn move_pane_left() {
+ let mut zellij = start_zellij();
+ let left_terminal = claim_first_terminal_and_wait_for_prompt(&zellij);
+ let right_terminal = split_right_and_wait_for_prompt(&zellij);
+ left_terminal.output(b"AAA");
+ right_terminal.output(b"BBB");
+ zellij.wait_until("both panes labelled left to right", |grid_snapshot| {
+ appears_in_order(grid_snapshot, &["AAA", "BBB"])
+ });
+
+ zellij.send_stdin(&keys::ctrl('h'));
+ zellij.send_stdin(&keys::key('h'));
+ zellij.send_stdin(&keys::ENTER);
+
+ let grid_snapshot = zellij.wait_until("focused pane moved to the left", |grid_snapshot| {
+ appears_in_order(grid_snapshot, &["BBB", "AAA"]) && grid_snapshot.status_bar_appears()
+ });
+ assert_snapshot!(normalized(&grid_snapshot));
+ zellij.quit();
+}
+
+#[test]
+fn move_pane_down() {
+ let mut zellij = start_zellij();
+ let upper_terminal = claim_first_terminal_and_wait_for_prompt(&zellij);
+ let lower_terminal = split_down_and_wait_for_prompt(&zellij);
+ upper_terminal.output(b"AAA");
+ lower_terminal.output(b"BBB");
+ zellij.wait_until("both panes labelled top to bottom", |grid_snapshot| {
+ appears_in_order(grid_snapshot, &["AAA", "BBB"])
+ });
+
+ zellij.send_stdin(&keys::ctrl('h'));
+ zellij.send_stdin(&keys::key('k'));
+ zellij.send_stdin(&keys::ENTER);
+
+ let grid_snapshot = zellij.wait_until("focused pane moved upward", |grid_snapshot| {
+ appears_in_order(grid_snapshot, &["BBB", "AAA"]) && grid_snapshot.status_bar_appears()
+ });
+ assert_snapshot!(normalized(&grid_snapshot));
+ zellij.quit();
+}
+
+#[test]
+fn toggle_pane_embed_or_floating() {
+ let mut zellij = start_zellij();
+ claim_first_terminal_and_wait_for_prompt(&zellij);
+ split_right_and_wait_for_prompt(&zellij);
+
+ zellij.send_stdin(&keys::ctrl('p'));
+ zellij.send_stdin(&keys::key('e'));
+
+ let grid_snapshot = zellij.wait_until("focused pane became floating", |grid_snapshot| {
+ grid_snapshot.contains("STAGGERED")
+ && grid_snapshot.contains("LOCK")
+ && grid_snapshot.cursor_is_at(col(33).row(8))
+ });
+ assert_snapshot!(normalized(&grid_snapshot));
+ zellij.quit();
+}
+
+#[test]
+fn toggle_pane_pinned() {
+ let mut zellij = start_zellij();
+ claim_first_terminal_and_wait_for_prompt(&zellij);
+
+ zellij.send_stdin(&keys::ctrl('p'));
+ zellij.send_stdin(&keys::key('w'));
+ let floating_terminal = zellij.expect_pty_spawn();
+ floating_terminal.output(PROMPT);
+ zellij.wait_until("floating pane appeared", |grid_snapshot| {
+ grid_snapshot.contains("STAGGERED")
+ });
+
+ zellij.send_stdin(&keys::ctrl('p'));
+ zellij.send_stdin(&keys::key('i'));
+
+ let grid_snapshot = zellij.wait_until("floating pane pinned", |grid_snapshot| {
+ grid_snapshot.contains("PIN [+]")
+ && grid_snapshot.contains("LOCK")
+ && grid_snapshot.cursor_is_at(col(33).row(8))
+ });
+ assert_snapshot!(normalized(&grid_snapshot));
+ zellij.quit();
+}
+
+#[test]
+fn move_focus_or_tab_moves_focus_between_panes() {
+ let mut zellij = start_zellij();
+ claim_first_terminal_and_wait_for_prompt(&zellij);
+ split_right_and_wait_for_prompt(&zellij);
+
+ zellij.send_stdin(&keys::alt('h'));
+
+ let grid_snapshot = zellij.wait_until("focus moved to the left pane", |grid_snapshot| {
+ grid_snapshot.status_bar_appears() && grid_snapshot.cursor_is_at(col(3).row(2))
+ });
+ assert_snapshot!(normalized(&grid_snapshot));
+ zellij.quit();
+}
+
+#[test]
+fn move_focus_or_tab_switches_tab_when_single_pane() {
+ let mut zellij = start_zellij();
+ let first_terminal = claim_first_terminal_and_wait_for_prompt(&zellij);
+ first_terminal.output(b"oneone");
+ zellij.wait_until("first tab pane labelled", |grid_snapshot| {
+ grid_snapshot.contains("oneone")
+ });
+
+ zellij.send_stdin(&keys::ctrl('t'));
+ zellij.send_stdin(&keys::key('n'));
+ let second_terminal = zellij.expect_pty_spawn();
+ second_terminal.output(PROMPT);
+ zellij.wait_until("second tab focused, first tab hidden", |grid_snapshot| {
+ grid_snapshot.contains("Tab #2") && !grid_snapshot.contains("oneone")
+ });
+
+ zellij.send_stdin(&keys::alt('h'));
+
+ let grid_snapshot = zellij
+ .wait_until("focus switched back to the first tab", |grid_snapshot| {
+ grid_snapshot.status_bar_appears() && grid_snapshot.contains("oneone")
+ });
+ assert_snapshot!(normalized(&grid_snapshot));
+ zellij.quit();
+}
+
+#[test]
+fn new_pane_in_pane_mode() {
+ let mut zellij = start_zellij();
+ claim_first_terminal_and_wait_for_prompt(&zellij);
+
+ zellij.send_stdin(&keys::ctrl('p'));
+ zellij.send_stdin(&keys::key('n'));
+ let grid_snapshot = spawn_second_pane_and_wait_for_grid(&zellij, col(63).row(2));
+ assert_snapshot!(normalized(&grid_snapshot));
+ zellij.quit();
+}
+
+#[test]
+fn new_pane_in_normal_mode() {
+ let mut zellij = start_zellij();
+ claim_first_terminal_and_wait_for_prompt(&zellij);
+
+ zellij.send_stdin(&keys::alt('n'));
+ let grid_snapshot = spawn_second_pane_and_wait_for_grid(&zellij, col(63).row(2));
+ assert_snapshot!(normalized(&grid_snapshot));
+ zellij.quit();
+}
+
+#[test]
+fn split_pane_downward() {
+ let mut zellij = start_zellij();
+ claim_first_terminal_and_wait_for_prompt(&zellij);
+
+ zellij.send_stdin(&keys::ctrl('p'));
+ zellij.send_stdin(&keys::key('d'));
+ let grid_snapshot = spawn_second_pane_and_wait_for_grid(&zellij, col(3).row(13));
+ assert_snapshot!(normalized(&grid_snapshot));
+ zellij.quit();
+}
+
+#[test]
+fn new_stacked_pane() {
+ let mut zellij = start_zellij();
+ claim_first_terminal_and_wait_for_prompt(&zellij);
+
+ zellij.send_stdin(&keys::ctrl('p'));
+ zellij.send_stdin(&keys::key('s'));
+ let grid_snapshot = spawn_second_pane_and_wait_for_grid(&zellij, col(3).row(3));
+ assert_snapshot!(normalized(&grid_snapshot));
+ zellij.quit();
+}
+
+#[test]
+fn move_focus_left_in_pane_mode() {
+ let mut zellij = start_zellij();
+ claim_first_terminal_and_wait_for_prompt(&zellij);
+ split_right_and_wait_for_prompt(&zellij);
+
+ zellij.send_stdin(&keys::ctrl('p'));
+ zellij.send_stdin(&keys::key('h'));
+ zellij.send_stdin(&keys::ENTER);
+
+ let grid_snapshot = zellij.wait_until("focus moved to the left pane", |grid_snapshot| {
+ grid_snapshot.status_bar_appears() && grid_snapshot.cursor_is_at(col(3).row(2))
+ });
+ assert_snapshot!(normalized(&grid_snapshot));
+ zellij.quit();
+}
+
+#[test]
+fn move_focus_right_in_pane_mode() {
+ let mut zellij = start_zellij();
+ claim_first_terminal_and_wait_for_prompt(&zellij);
+ split_right_and_wait_for_prompt(&zellij);
+
+ zellij.send_stdin(&keys::ctrl('p'));
+ zellij.send_stdin(&keys::key('h'));
+ zellij.wait_until("focus moved to the left pane", |grid_snapshot| {
+ grid_snapshot.cursor_is_at(col(3).row(2))
+ });
+
+ zellij.send_stdin(&keys::key('l'));
+ zellij.send_stdin(&keys::ENTER);
+
+ let grid_snapshot = zellij.wait_until("focus moved back to the right pane", |grid_snapshot| {
+ grid_snapshot.status_bar_appears() && grid_snapshot.cursor_is_at(col(63).row(2))
+ });
+ assert_snapshot!(normalized(&grid_snapshot));
+ zellij.quit();
+}
+
+#[test]
+fn switch_focus_in_pane_mode() {
+ let mut zellij = start_zellij();
+ claim_first_terminal_and_wait_for_prompt(&zellij);
+ split_right_and_wait_for_prompt(&zellij);
+
+ zellij.send_stdin(&keys::ctrl('p'));
+ zellij.send_stdin(&keys::key('p'));
+ zellij.send_stdin(&keys::ENTER);
+
+ let grid_snapshot = zellij.wait_until("focus switched to the other pane", |grid_snapshot| {
+ grid_snapshot.status_bar_appears() && grid_snapshot.cursor_is_at(col(3).row(2))
+ });
+ assert_snapshot!(normalized(&grid_snapshot));
+ zellij.quit();
+}
+
+#[test]
+fn toggle_pane_frames() {
+ let mut zellij = start_zellij();
+ claim_first_terminal_and_wait_for_prompt(&zellij);
+ split_right_and_wait_for_prompt(&zellij);
+
+ zellij.send_stdin(&keys::ctrl('p'));
+ zellij.send_stdin(&keys::key('z'));
+
+ let grid_snapshot = zellij.wait_until("pane frames removed from both panes", |grid_snapshot| {
+ grid_snapshot.status_bar_appears()
+ && !grid_snapshot.contains("Pane #1")
+ && !grid_snapshot.contains("Pane #2")
+ && grid_snapshot.cursor_is_at(col(62).row(1))
+ });
+ assert_snapshot!(normalized(&grid_snapshot));
+ zellij.quit();
+}
+
#[test]
fn start_without_pane_frames() {
let mut zellij = TestRunner::new(TERMINAL_SIZE)
@@ -210,18 +535,20 @@ fn start_without_pane_frames() {
|grid_snapshot| {
grid_snapshot.tab_bar_appears()
&& grid_snapshot.status_bar_appears()
- && grid_snapshot.cursor_is_at(2, 1)
+ && grid_snapshot.cursor_is_at(col(2).row(1))
},
);
- zellij.send_stdin(&keys::PANE_MODE);
- zellij.send_stdin(&keys::SPLIT_RIGHT_IN_PANE_MODE);
+ zellij.send_stdin(&keys::ctrl('p'));
+ zellij.send_stdin(&keys::key('r'));
let right_terminal = zellij.expect_pty_spawn();
right_terminal.output(PROMPT);
let grid_snapshot = zellij.wait_until(
"right frameless terminal prompt rendered",
- |grid_snapshot| grid_snapshot.status_bar_appears() && grid_snapshot.cursor_is_at(62, 1),
+ |grid_snapshot| {
+ grid_snapshot.status_bar_appears() && grid_snapshot.cursor_is_at(col(62).row(1))
+ },
);
assert_snapshot!(normalized(&grid_snapshot));
zellij.quit();
diff --git a/zellij-integration-tests/tests/plugins.rs b/zellij-integration-tests/tests/plugins.rs
index e39c1af3..5b14c633 100644
--- a/zellij-integration-tests/tests/plugins.rs
+++ b/zellij-integration-tests/tests/plugins.rs
@@ -1,12 +1,7 @@
#![cfg(unix)]
use insta::assert_snapshot;
-use zellij_integration_tests::{normalized, Size, TestRunner};
-
-const TERMINAL_SIZE: Size = Size {
- cols: 120,
- rows: 24,
-};
+use zellij_integration_tests::{normalized, TestRunner, TERMINAL_SIZE};
fn load_background_plugin_config() -> String {
let plugin_path = format!(
diff --git a/zellij-integration-tests/tests/resize.rs b/zellij-integration-tests/tests/resize.rs
new file mode 100644
index 00000000..bf1263c3
--- /dev/null
+++ b/zellij-integration-tests/tests/resize.rs
@@ -0,0 +1,277 @@
+#![cfg(unix)]
+
+use insta::assert_snapshot;
+use zellij_integration_tests::{
+ claim_first_terminal_and_wait_for_prompt, col, keys, normalized,
+ split_down_and_wait_for_prompt, split_right_and_wait_for_prompt, start_zellij, FakePtyHandle,
+ GridSnapshot, TestSession,
+};
+
+fn focus_left_pane(zellij: &TestSession) {
+ zellij.send_stdin(&keys::ctrl('p'));
+ zellij.send_stdin(&keys::key('h'));
+ zellij.wait_until("focus moved to the left pane", |grid_snapshot| {
+ grid_snapshot.cursor_is_at(col(3).row(2))
+ });
+}
+
+fn focus_upper_pane(zellij: &TestSession) {
+ zellij.send_stdin(&keys::ctrl('p'));
+ zellij.send_stdin(&keys::key('k'));
+ zellij.wait_until("focus moved to the upper pane", |grid_snapshot| {
+ grid_snapshot.cursor_is_at(col(3).row(2))
+ });
+}
+
+fn resize_in_resize_mode(zellij: &TestSession, key: &[u8]) {
+ zellij.send_stdin(&keys::ctrl('n'));
+ zellij.send_stdin(key);
+ zellij.send_stdin(&keys::ENTER);
+}
+
+fn width(terminal: &FakePtyHandle, what: &str) -> u16 {
+ terminal.wait_for_size(what, |_, _| true).0
+}
+
+fn height(terminal: &FakePtyHandle, what: &str) -> u16 {
+ terminal.wait_for_size(what, |_, _| true).1
+}
+
+fn pane_two_header_column(grid_snapshot: &GridSnapshot) -> Option<usize> {
+ grid_snapshot.lines().into_iter().find_map(|line| {
+ line.find("Pane #2")
+ .map(|byte_index| line[..byte_index].chars().count())
+ })
+}
+
+fn pane_two_header_row(grid_snapshot: &GridSnapshot) -> Option<usize> {
+ grid_snapshot
+ .lines()
+ .into_iter()
+ .position(|line| line.contains("Pane #2"))
+}
+
+fn vertical_boundary_column(zellij: &TestSession) -> usize {
+ pane_two_header_column(&zellij.snapshot()).expect("vertical pane boundary rendered")
+}
+
+fn horizontal_boundary_row(zellij: &TestSession) -> usize {
+ pane_two_header_row(&zellij.snapshot()).expect("horizontal pane boundary rendered")
+}
+
+#[test]
+fn resize_increase_right() {
+ let mut zellij = start_zellij();
+ let left_terminal = claim_first_terminal_and_wait_for_prompt(&zellij);
+ split_right_and_wait_for_prompt(&zellij);
+ focus_left_pane(&zellij);
+ let initial_cols = width(&left_terminal, "left terminal initial width");
+ let initial_boundary = vertical_boundary_column(&zellij);
+
+ resize_in_resize_mode(&zellij, &keys::key('l'));
+
+ left_terminal.wait_for_size("left terminal grew rightward", |cols, _| {
+ cols > initial_cols
+ });
+ let grid_snapshot = zellij.wait_until("pane boundary moved right", |grid_snapshot| {
+ grid_snapshot.status_bar_appears()
+ && pane_two_header_column(grid_snapshot).is_some_and(|column| column > initial_boundary)
+ });
+ assert_snapshot!(normalized(&grid_snapshot));
+ zellij.quit();
+}
+
+#[test]
+fn resize_decrease_left() {
+ let mut zellij = start_zellij();
+ claim_first_terminal_and_wait_for_prompt(&zellij);
+ let right_terminal = split_right_and_wait_for_prompt(&zellij);
+ let initial_cols = width(&right_terminal, "right terminal initial width");
+ let initial_boundary = vertical_boundary_column(&zellij);
+
+ resize_in_resize_mode(&zellij, &keys::key('H'));
+
+ right_terminal.wait_for_size("right terminal shrank", |cols, _| cols < initial_cols);
+ let grid_snapshot = zellij.wait_until("pane boundary moved right", |grid_snapshot| {
+ grid_snapshot.status_bar_appears()
+ && pane_two_header_column(grid_snapshot).is_some_and(|column| column > initial_boundary)
+ });
+ assert_snapshot!(normalized(&grid_snapshot));
+ zellij.quit();
+}
+
+#[test]
+fn resize_decrease_right() {
+ let mut zellij = start_zellij();
+ let left_terminal = claim_first_terminal_and_wait_for_prompt(&zellij);
+ split_right_and_wait_for_prompt(&zellij);
+ focus_left_pane(&zellij);
+ let initial_cols = width(&left_terminal, "left terminal initial width");
+ let initial_boundary = vertical_boundary_column(&zellij);
+
+ resize_in_resize_mode(&zellij, &keys::key('L'));
+
+ left_terminal.wait_for_size("left terminal shrank", |cols, _| cols < initial_cols);
+ let grid_snapshot = zellij.wait_until("pane boundary moved left", |grid_snapshot| {
+ grid_snapshot.status_bar_appears()
+ && pane_two_header_column(grid_snapshot).is_some_and(|column| column < initial_boundary)
+ });
+ assert_snapshot!(normalized(&grid_snapshot));
+ zellij.quit();
+}
+
+#[test]
+fn resize_increase_down() {
+ let mut zellij = start_zellij();
+ let upper_terminal = claim_first_terminal_and_wait_for_prompt(&zellij);
+ split_down_and_wait_for_prompt(&zellij);
+ focus_upper_pane(&zellij);
+ let initial_rows = height(&upper_terminal, "upper terminal initial height");
+ let initial_boundary = horizontal_boundary_row(&zellij);
+
+ resize_in_resize_mode(&zellij, &keys::key('j'));
+
+ upper_terminal.wait_for_size("upper terminal grew downward", |_, rows| {
+ rows > initial_rows
+ });
+ let grid_snapshot = zellij.wait_until("pane boundary moved down", |grid_snapshot| {
+ grid_snapshot.status_bar_appears()
+ && pane_two_header_row(grid_snapshot).is_some_and(|row| row > initial_boundary)
+ });
+ assert_snapshot!(normalized(&grid_snapshot));
+ zellij.quit();
+}
+
+#[test]
+fn resize_increase_up() {
+ let mut zellij = start_zellij();
+ claim_first_terminal_and_wait_for_prompt(&zellij);
+ let lower_terminal = split_down_and_wait_for_prompt(&zellij);
+ let initial_rows = height(&lower_terminal, "lower terminal initial height");
+ let initial_boundary = horizontal_boundary_row(&zellij);
+
+ resize_in_resize_mode(&zellij, &keys::key('k'));
+
+ lower_terminal.wait_for_size("lower terminal grew upward", |_, rows| rows > initial_rows);
+ let grid_snapshot = zellij.wait_until("pane boundary moved up", |grid_snapshot| {
+ grid_snapshot.status_bar_appears()
+ && pane_two_header_row(grid_snapshot).is_some_and(|row| row < initial_boundary)
+ });
+ assert_snapshot!(normalized(&grid_snapshot));
+ zellij.quit();
+}
+
+#[test]
+fn resize_decrease_down() {
+ let mut zellij = start_zellij();
+ let upper_terminal = claim_first_terminal_and_wait_for_prompt(&zellij);
+ split_down_and_wait_for_prompt(&zellij);
+ focus_upper_pane(&zellij);
+ let initial_rows = height(&upper_terminal, "upper terminal initial height");
+ let initial_boundary = horizontal_boundary_row(&zellij);
+
+ resize_in_resize_mode(&zellij, &keys::key('J'));
+
+ upper_terminal.wait_for_size("upper terminal shrank", |_, rows| rows < initial_rows);
+ let grid_snapshot = zellij.wait_until("pane boundary moved up", |grid_snapshot| {
+ grid_snapshot.status_bar_appears()
+ && pane_two_header_row(grid_snapshot).is_some_and(|row| row < initial_boundary)
+ });
+ assert_snapshot!(normalized(&grid_snapshot));
+ zellij.quit();
+}
+
+#[test]
+fn resize_decrease_up() {
+ let mut zellij = start_zellij();
+ claim_first_terminal_and_wait_for_prompt(&zellij);
+ let lower_terminal = split_down_and_wait_for_prompt(&zellij);
+ let initial_rows = height(&lower_terminal, "lower terminal initial height");
+ let initial_boundary = horizontal_boundary_row(&zellij);
+
+ resize_in_resize_mode(&zellij, &keys::key('K'));
+
+ lower_terminal.wait_for_size("lower terminal shrank", |_, rows| rows < initial_rows);
+ let grid_snapshot = zellij.wait_until("pane boundary moved down", |grid_snapshot| {
+ grid_snapshot.status_bar_appears()
+ && pane_two_header_row(grid_snapshot).is_some_and(|row| row > initial_boundary)
+ });
+ assert_snapshot!(normalized(&grid_snapshot));
+ zellij.quit();
+}
+
+#[test]
+fn resize_increase_in_resize_mode() {
+ let mut zellij = start_zellij();
+ claim_first_terminal_and_wait_for_prompt(&zellij);
+ let right_terminal = split_right_and_wait_for_prompt(&zellij);
+ let initial_cols = width(&right_terminal, "right terminal initial width");
+ let initial_boundary = vertical_boundary_column(&zellij);
+
+ resize_in_resize_mode(&zellij, &keys::key('='));
+
+ right_terminal.wait_for_size("right terminal grew", |cols, _| cols > initial_cols);
+ let grid_snapshot = zellij.wait_until("pane boundary moved left", |grid_snapshot| {
+ grid_snapshot.status_bar_appears()
+ && pane_two_header_column(grid_snapshot).is_some_and(|column| column < initial_boundary)
+ });
+ assert_snapshot!(normalized(&grid_snapshot));
+ zellij.quit();
+}
+
+#[test]
+fn resize_decrease_in_resize_mode() {
+ let mut zellij = start_zellij();
+ claim_first_terminal_and_wait_for_prompt(&zellij);
+ let right_terminal = split_right_and_wait_for_prompt(&zellij);
+ let initial_cols = width(&right_terminal, "right terminal initial width");
+ let initial_boundary = vertical_boundary_column(&zellij);
+
+ resize_in_resize_mode(&zellij, &keys::key('-'));
+
+ right_terminal.wait_for_size("right terminal shrank", |cols, _| cols < initial_cols);
+ let grid_snapshot = zellij.wait_until("pane boundary moved right", |grid_snapshot| {
+ grid_snapshot.status_bar_appears()
+ && pane_two_header_column(grid_snapshot).is_some_and(|column| column > initial_boundary)
+ });
+ assert_snapshot!(normalized(&grid_snapshot));
+ zellij.quit();
+}
+
+#[test]
+fn resize_increase_in_normal_mode() {
+ let mut zellij = start_zellij();
+ claim_first_terminal_and_wait_for_prompt(&zellij);
+ let right_terminal = split_right_and_wait_for_prompt(&zellij);
+ let initial_cols = width(&right_terminal, "right terminal initial width");
+ let initial_boundary = vertical_boundary_column(&zellij);
+
+ zellij.send_stdin(&keys::alt('='));
+
+ right_terminal.wait_for_size("right terminal grew", |cols, _| cols > initial_cols);
+ let grid_snapshot = zellij.wait_until("pane boundary moved left", |grid_snapshot| {
+ grid_snapshot.status_bar_appears()
+ && pane_two_header_column(grid_snapshot).is_some_and(|column| column < initial_boundary)
+ });
+ assert_snapshot!(normalized(&grid_snapshot));
+ zellij.quit();
+}
+
+#[test]
+fn resize_decrease_in_normal_mode() {
+ let mut zellij = start_zellij();
+ claim_first_terminal_and_wait_for_prompt(&zellij);
+ let right_terminal = split_right_and_wait_for_prompt(&zellij);
+ let initial_cols = width(&right_terminal, "right terminal initial width");
+ let initial_boundary = vertical_boundary_column(&zellij);
+
+ zellij.send_stdin(&keys::alt('-'));
+
+ right_terminal.wait_for_size("right terminal shrank", |cols, _| cols < initial_cols);
+ let grid_snapshot = zellij.wait_until("pane boundary moved right", |grid_snapshot| {
+ grid_snapshot.status_bar_appears()
+ && pane_two_header_column(grid_snapshot).is_some_and(|column| column > initial_boundary)
+ });
+ assert_snapshot!(normalized(&grid_snapshot));
+ zellij.quit();
+}
diff --git a/zellij-integration-tests/tests/resurrection.rs b/zellij-integration-tests/tests/resurrection.rs
index 55df5b32..7f6a10b0 100644
--- a/zellij-integration-tests/tests/resurrection.rs
+++ b/zellij-integration-tests/tests/resurrection.rs
@@ -1,13 +1,9 @@
#![cfg(unix)]
use insta::assert_snapshot;
-use zellij_integration_tests::{keys, normalized, LayoutInfo, Size, TestRunner, TestSession};
-
-const TERMINAL_SIZE: Size = Size {
- cols: 120,
- rows: 24,
+use zellij_integration_tests::{
+ keys, normalized, LayoutInfo, TestRunner, TestSession, PROMPT, TERMINAL_SIZE,
};
-const PROMPT: &[u8] = b"$ ";
const RESURRECT_LAYOUT: &str = r#"
layout {
@@ -49,8 +45,8 @@ fn quit_and_resurrect_session() {
let mut zellij = start_serializing_session("");
wait_for_layout_loaded(&zellij);
- zellij.send_stdin(&keys::PANE_MODE);
- zellij.send_stdin(&keys::SPLIT_RIGHT_IN_PANE_MODE);
+ zellij.send_stdin(&keys::ctrl('p'));
+ zellij.send_stdin(&keys::key('r'));
let new_pane = zellij.expect_pty_spawn();
new_pane.output(PROMPT);
zellij.wait_until("new pane opened before serialization", |grid_snapshot| {
diff --git a/zellij-integration-tests/tests/scroll.rs b/zellij-integration-tests/tests/scroll.rs
new file mode 100644
index 00000000..35316957
--- /dev/null
+++ b/zellij-integration-tests/tests/scroll.rs
@@ -0,0 +1,134 @@
+#![cfg(unix)]
+
+use insta::assert_snapshot;
+use zellij_integration_tests::{
+ claim_first_terminal_and_wait_for_prompt, keys, normalized, start_zellij, FakePtyHandle,
+ TestSession,
+};
+
+const LAST_LINE: &str = "line40";
+
+fn fill_pane_past_viewport(zellij: &TestSession) -> FakePtyHandle {
+ let terminal = claim_first_terminal_and_wait_for_prompt(zellij);
+ for line in 1..=40 {
+ terminal.output(format!("line{:02}\r\n", line).as_bytes());
+ }
+ zellij.wait_until("pane filled past its viewport", |grid_snapshot| {
+ grid_snapshot.contains(LAST_LINE)
+ });
+ terminal
+}
+
+#[test]
+fn page_scroll_up() {
+ let mut zellij = start_zellij();
+ fill_pane_past_viewport(&zellij);
+
+ zellij.send_stdin(&keys::ctrl('s'));
+ zellij.send_stdin(&keys::ctrl('b'));
+
+ let grid_snapshot = zellij.wait_until("scrolled up by a page", |grid_snapshot| {
+ grid_snapshot.contains("PgDn|PgUp") && !grid_snapshot.contains(LAST_LINE)
+ });
+ assert_snapshot!(normalized(&grid_snapshot));
+ zellij.quit();
+}
+
+#[test]
+fn page_scroll_down() {
+ let mut zellij = start_zellij();
+ fill_pane_past_viewport(&zellij);
+
+ zellij.send_stdin(&keys::ctrl('s'));
+ zellij.send_stdin(&keys::ctrl('b'));
+ zellij.wait_until("scrolled up by a page", |grid_snapshot| {
+ grid_snapshot.contains("PgDn|PgUp") && !grid_snapshot.contains(LAST_LINE)
+ });
+
+ zellij.send_stdin(&keys::ctrl('f'));
+
+ let grid_snapshot = zellij
+ .wait_until("scrolled back down toward the bottom", |grid_snapshot| {
+ grid_snapshot.contains("line39") && grid_snapshot.contains("PgDn|PgUp")
+ });
+ assert_snapshot!(normalized(&grid_snapshot));
+ zellij.quit();
+}
+
+#[test]
+fn half_page_scroll_up() {
+ let mut zellij = start_zellij();
+ fill_pane_past_viewport(&zellij);
+
+ zellij.send_stdin(&keys::ctrl('s'));
+ zellij.send_stdin(&keys::key('u'));
+
+ let grid_snapshot = zellij.wait_until("scrolled up by half a page", |grid_snapshot| {
+ grid_snapshot.contains("PgDn|PgUp") && !grid_snapshot.contains(LAST_LINE)
+ });
+ assert_snapshot!(normalized(&grid_snapshot));
+ zellij.quit();
+}
+
+#[test]
+fn half_page_scroll_down() {
+ let mut zellij = start_zellij();
+ fill_pane_past_viewport(&zellij);
+
+ zellij.send_stdin(&keys::ctrl('s'));
+ zellij.send_stdin(&keys::key('u'));
+ zellij.wait_until("scrolled up by half a page", |grid_snapshot| {
+ grid_snapshot.contains("PgDn|PgUp") && !grid_snapshot.contains(LAST_LINE)
+ });
+
+ zellij.send_stdin(&keys::key('d'));
+
+ let grid_snapshot = zellij
+ .wait_until("scrolled back down toward the bottom", |grid_snapshot| {
+ grid_snapshot.contains("line39") && grid_snapshot.contains("PgDn|PgUp")
+ });
+ assert_snapshot!(normalized(&grid_snapshot));
+ zellij.quit();
+}
+
+#[test]
+fn scroll_down_one_line() {
+ let mut zellij = start_zellij();
+ fill_pane_past_viewport(&zellij);
+
+ zellij.send_stdin(&keys::ctrl('s'));
+ zellij.send_stdin(&keys::key('k'));
+ zellij.send_stdin(&keys::key('k'));
+ zellij.wait_until("scrolled up two lines", |grid_snapshot| {
+ grid_snapshot.contains("SCROLL: 2/")
+ });
+
+ zellij.send_stdin(&keys::key('j'));
+
+ let grid_snapshot = zellij.wait_until("scrolled back down one line", |grid_snapshot| {
+ grid_snapshot.contains("SCROLL: 1/") && grid_snapshot.contains("PgDn|PgUp")
+ });
+ assert_snapshot!(normalized(&grid_snapshot));
+ zellij.quit();
+}
+
+#[test]
+fn scroll_to_bottom() {
+ let mut zellij = start_zellij();
+ fill_pane_past_viewport(&zellij);
+
+ zellij.send_stdin(&keys::ctrl('s'));
+ zellij.send_stdin(&keys::ctrl('b'));
+ zellij.wait_until("scrolled up by a page", |grid_snapshot| {
+ grid_snapshot.contains("PgDn|PgUp") && !grid_snapshot.contains(LAST_LINE)
+ });
+
+ zellij.send_stdin(&keys::ctrl('c'));
+
+ let grid_snapshot = zellij
+ .wait_until("returned to the bottom in normal mode", |grid_snapshot| {
+ grid_snapshot.status_bar_appears() && grid_snapshot.contains(LAST_LINE)
+ });
+ assert_snapshot!(normalized(&grid_snapshot));
+ zellij.quit();
+}
diff --git a/zellij-integration-tests/tests/search.rs b/zellij-integration-tests/tests/search.rs
new file mode 100644
index 00000000..c15fe76e
--- /dev/null
+++ b/zellij-integration-tests/tests/search.rs
@@ -0,0 +1,161 @@
+#![cfg(unix)]
+
+use insta::assert_snapshot;
+use zellij_integration_tests::{
+ claim_first_terminal_and_wait_for_prompt, keys, normalized, start_zellij, FakePtyHandle,
+ TestSession,
+};
+
+fn fill_pane_with_two_matches(zellij: &TestSession) -> FakePtyHandle {
+ let terminal = claim_first_terminal_and_wait_for_prompt(zellij);
+ for line in 1..=40 {
+ let content = match line {
+ 5 => "line05 NEEDLE upper\r\n".to_string(),
+ 35 => "line35 NEEDLE lower\r\n".to_string(),
+ other => format!("line{:02}\r\n", other),
+ };
+ terminal.output(content.as_bytes());
+ }
+ zellij.wait_until(
+ "pane filled with the lower match visible",
+ |grid_snapshot| {
+ grid_snapshot.contains("NEEDLE lower") && !grid_snapshot.contains("NEEDLE upper")
+ },
+ );
+ terminal
+}
+
+fn enter_search_for_needle(zellij: &TestSession) {
+ zellij.send_stdin(&keys::ctrl('s'));
+ zellij.send_stdin(&keys::key('s'));
+ zellij.wait_until("search input mode active", |grid_snapshot| {
+ grid_snapshot.contains("ENTERING SEARCH TERM")
+ });
+ zellij.send_stdin(b"NEEDLE");
+ zellij.send_stdin(&keys::ENTER);
+ zellij.wait_until("search navigation mode active", |grid_snapshot| {
+ grid_snapshot.contains("PgDn|PgUp")
+ });
+}
+
+fn enter_search_on_visible_match(zellij: &TestSession) {
+ let terminal = claim_first_terminal_and_wait_for_prompt(zellij);
+ terminal.output(b"NEEDLE on screen\r\n");
+ zellij.wait_until("match visible on screen", |grid_snapshot| {
+ grid_snapshot.contains("NEEDLE on screen")
+ });
+ enter_search_for_needle(zellij);
+}
+
+#[test]
+fn search_input_jumps_to_a_match() {
+ let mut zellij = start_zellij();
+ let terminal = claim_first_terminal_and_wait_for_prompt(&zellij);
+ for line in 1..=40 {
+ let content = if line == 5 {
+ "line05 NEEDLE\r\n".to_string()
+ } else {
+ format!("line{:02}\r\n", line)
+ };
+ terminal.output(content.as_bytes());
+ }
+ zellij.wait_until("upper match initially off screen", |grid_snapshot| {
+ grid_snapshot.contains("line40") && !grid_snapshot.contains("line05")
+ });
+
+ zellij.send_stdin(&keys::ctrl('s'));
+ zellij.send_stdin(&keys::key('s'));
+ zellij.wait_until("search input mode active", |grid_snapshot| {
+ grid_snapshot.contains("ENTERING SEARCH TERM")
+ });
+ zellij.send_stdin(b"NEEDLE");
+
+ let grid_snapshot = zellij.wait_until("scrolled to the typed match", |grid_snapshot| {
+ grid_snapshot.contains("line05") && grid_snapshot.contains("ENTERING SEARCH TERM")
+ });
+ assert_snapshot!(normalized(&grid_snapshot));
+ zellij.quit();
+}
+
+#[test]
+fn search_up_to_previous_match() {
+ let mut zellij = start_zellij();
+ fill_pane_with_two_matches(&zellij);
+ enter_search_for_needle(&zellij);
+
+ zellij.send_stdin(&keys::key('p'));
+ zellij.send_stdin(&keys::key('p'));
+
+ let grid_snapshot =
+ zellij.wait_until("upper match revealed by searching up", |grid_snapshot| {
+ grid_snapshot.contains("NEEDLE upper") && grid_snapshot.contains("PgDn|PgUp")
+ });
+ assert_snapshot!(normalized(&grid_snapshot));
+ zellij.quit();
+}
+
+#[test]
+fn search_down_to_next_match() {
+ let mut zellij = start_zellij();
+ fill_pane_with_two_matches(&zellij);
+ enter_search_for_needle(&zellij);
+
+ zellij.send_stdin(&keys::key('p'));
+ zellij.send_stdin(&keys::key('p'));
+ zellij.wait_until("upper match revealed by searching up", |grid_snapshot| {
+ grid_snapshot.contains("NEEDLE upper")
+ });
+
+ zellij.send_stdin(&keys::key('n'));
+
+ let grid_snapshot =
+ zellij.wait_until("lower match revealed by searching down", |grid_snapshot| {
+ grid_snapshot.contains("NEEDLE lower")
+ && !grid_snapshot.contains("NEEDLE upper")
+ && grid_snapshot.contains("PgDn|PgUp")
+ });
+ assert_snapshot!(normalized(&grid_snapshot));
+ zellij.quit();
+}
+
+#[test]
+fn search_toggle_case_sensitivity() {
+ let mut zellij = start_zellij();
+ enter_search_on_visible_match(&zellij);
+
+ zellij.send_stdin(&keys::key('c'));
+
+ let grid_snapshot = zellij.wait_until("case sensitivity toggled on", |grid_snapshot| {
+ grid_snapshot.contains("[c]")
+ });
+ assert_snapshot!(normalized(&grid_snapshot));
+ zellij.quit();
+}
+
+#[test]
+fn search_toggle_wrap() {
+ let mut zellij = start_zellij();
+ enter_search_on_visible_match(&zellij);
+
+ zellij.send_stdin(&keys::key('w'));
+
+ let grid_snapshot = zellij.wait_until("wrap toggled on", |grid_snapshot| {
+ grid_snapshot.contains("[w]")
+ });
+ assert_snapshot!(normalized(&grid_snapshot));
+ zellij.quit();
+}
+
+#[test]
+fn search_toggle_whole_word() {
+ let mut zellij = start_zellij();
+ enter_search_on_visible_match(&zellij);
+
+ zellij.send_stdin(&keys::key('o'));
+
+ let grid_snapshot = zellij.wait_until("whole word toggled on", |grid_snapshot| {
+ grid_snapshot.contains("[o]")
+ });
+ assert_snapshot!(normalized(&grid_snapshot));
+ zellij.quit();
+}
diff --git a/zellij-integration-tests/tests/session.rs b/zellij-integration-tests/tests/session.rs
new file mode 100644
index 00000000..97ca0d01
--- /dev/null
+++ b/zellij-integration-tests/tests/session.rs
@@ -0,0 +1,31 @@
+#![cfg(unix)]
+
+use zellij_integration_tests::{claim_first_terminal_and_wait_for_prompt, keys, start_zellij};
+
+#[test]
+fn launch_session_manager_plugin() {
+ let mut zellij = start_zellij();
+ claim_first_terminal_and_wait_for_prompt(&zellij);
+
+ zellij.send_stdin(&keys::ctrl('o'));
+ zellij.send_stdin(&keys::key('w'));
+
+ zellij.wait_until("session manager plugin opened", |grid_snapshot| {
+ grid_snapshot.contains("Session Manager") && grid_snapshot.status_bar_appears()
+ });
+ zellij.quit();
+}
+
+#[test]
+fn launch_about_plugin() {
+ let mut zellij = start_zellij();
+ claim_first_terminal_and_wait_for_prompt(&zellij);
+
+ zellij.send_stdin(&keys::ctrl('o'));
+ zellij.send_stdin(&keys::key('a'));
+
+ zellij.wait_until("about plugin opened", |grid_snapshot| {
+ grid_snapshot.contains("About Zellij") && grid_snapshot.status_bar_appears()
+ });
+ zellij.quit();
+}
diff --git a/zellij-integration-tests/tests/snapshots/app__use_custom_layout_with_relative_path.snap b/zellij-integration-tests/tests/snapshots/app__use_custom_layout_with_relative_path.snap
index a60604ea..03d134ea 100644
--- a/zellij-integration-tests/tests/snapshots/app__use_custom_layout_with_relative_path.snap
+++ b/zellij-integration-tests/tests/snapshots/app__use_custom_layout_with_relative_path.snap
@@ -3,7 +3,6 @@ source: zellij-integration-tests/tests/app.rs
expression: normalized(&grid_snapshot)
---
- Tip: UNBOUND => open new pane. UNBOUND => navigate between panes. UNBOUND => increase/decrease pane size.
┌ Pane #1 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│$ █ │
│ │
diff --git a/zellij-integration-tests/tests/snapshots/misc__focus_next_pane_via_tmux_mode.snap b/zellij-integration-tests/tests/snapshots/misc__focus_next_pane_via_tmux_mode.snap
new file mode 100644
index 00000000..a114ffc5
--- /dev/null
+++ b/zellij-integration-tests/tests/snapshots/misc__focus_next_pane_via_tmux_mode.snap
@@ -0,0 +1,28 @@
+---
+source: zellij-integration-tests/tests/misc.rs
+expression: normalized(&grid_snapshot)
+---
+ Zellij (test)  Tab #1 
+┌ Pane #1 ─────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────────────────────┐
+│$ █ ││$ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+└──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT  ...
diff --git a/zellij-integration-tests/tests/snapshots/misc__next_swap_layout.snap b/zellij-integration-tests/tests/snapshots/misc__next_swap_layout.snap
new file mode 100644
index 00000000..8e954ab9
--- /dev/null
+++ b/zellij-integration-tests/tests/snapshots/misc__next_swap_layout.snap
@@ -0,0 +1,28 @@
+---
+source: zellij-integration-tests/tests/misc.rs
+expression: normalized(&grid_snapshot)
+---
+ Zellij (test)  Tab #1 
+┌ Pane #1 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
+│$ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
+┌ Pane #2 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
+│$ █ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT  ...
diff --git a/zellij-integration-tests/tests/snapshots/misc__previous_swap_layout.snap b/zellij-integration-tests/tests/snapshots/misc__previous_swap_layout.snap
new file mode 100644
index 00000000..8e954ab9
--- /dev/null
+++ b/zellij-integration-tests/tests/snapshots/misc__previous_swap_layout.snap
@@ -0,0 +1,28 @@
+---
+source: zellij-integration-tests/tests/misc.rs
+expression: normalized(&grid_snapshot)
+---
+ Zellij (test)  Tab #1 
+┌ Pane #1 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
+│$ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
+┌ Pane #2 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
+│$ █ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT  ...
diff --git a/zellij-integration-tests/tests/snapshots/misc__toggle_group_marking.snap b/zellij-integration-tests/tests/snapshots/misc__toggle_group_marking.snap
new file mode 100644
index 00000000..9932251c
--- /dev/null
+++ b/zellij-integration-tests/tests/snapshots/misc__toggle_group_marking.snap
@@ -0,0 +1,28 @@
+---
+source: zellij-integration-tests/tests/misc.rs
+expression: normalized(&grid_snapshot)
+---
+ Zellij (test)  Tab #1 
+┌ Pane #1 ─────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────────────────────┐
+│$ ││$ █ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ┌ Multiple Pane Select ────────────────────────┐ ││ │
+│ │<ESC> - cancel, <TAB> - move │ ││ │
+│ │ │ ││ │
+│ │GROUP ACTIONS (1 SELECTED PANE) │ ││ │
+│ │<b> - break out, <s> - stack, <c> - close │ ││ │
+│ │<l> - break left, <r> - break right │ ││ │
+│ │<e> - embed, <f> - float │ ││ │
+│ │ │ ││ │
+│ │Alt +  <p> Toggle  <Shift p> Follow Focus │ ││ │
+│ └──────────────────────────────────────────────┘ ││ │
+└──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT  ...
diff --git a/zellij-integration-tests/tests/snapshots/misc__toggle_pane_in_group.snap b/zellij-integration-tests/tests/snapshots/misc__toggle_pane_in_group.snap
new file mode 100644
index 00000000..9932251c
--- /dev/null
+++ b/zellij-integration-tests/tests/snapshots/misc__toggle_pane_in_group.snap
@@ -0,0 +1,28 @@
+---
+source: zellij-integration-tests/tests/misc.rs
+expression: normalized(&grid_snapshot)
+---
+ Zellij (test)  Tab #1 
+┌ Pane #1 ─────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────────────────────┐
+│$ ││$ █ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ┌ Multiple Pane Select ────────────────────────┐ ││ │
+│ │<ESC> - cancel, <TAB> - move │ ││ │
+│ │ │ ││ │
+│ │GROUP ACTIONS (1 SELECTED PANE) │ ││ │
+│ │<b> - break out, <s> - stack, <c> - close │ ││ │
+│ │<l> - break left, <r> - break right │ ││ │
+│ │<e> - embed, <f> - float │ ││ │
+│ │ │ ││ │
+│ │Alt +  <p> Toggle  <Shift p> Follow Focus │ ││ │
+│ └──────────────────────────────────────────────┘ ││ │
+└──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT  ...
diff --git a/zellij-integration-tests/tests/snapshots/misc__write_byte_to_focused_pane_via_tmux_mode.snap b/zellij-integration-tests/tests/snapshots/misc__write_byte_to_focused_pane_via_tmux_mode.snap
new file mode 100644
index 00000000..29857afc
--- /dev/null
+++ b/zellij-integration-tests/tests/snapshots/misc__write_byte_to_focused_pane_via_tmux_mode.snap
@@ -0,0 +1,28 @@
+---
+source: zellij-integration-tests/tests/misc.rs
+expression: normalized(&grid_snapshot)
+---
+ Zellij (test)  Tab #1 
+┌ Pane #1 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
+│$ █ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT  ...
diff --git a/zellij-integration-tests/tests/snapshots/modes__enter_search_mode.snap b/zellij-integration-tests/tests/snapshots/modes__enter_search_mode.snap
new file mode 100644
index 00000000..e2170783
--- /dev/null
+++ b/zellij-integration-tests/tests/snapshots/modes__enter_search_mode.snap
@@ -0,0 +1,28 @@
+---
+source: zellij-integration-tests/tests/modes.rs
+expression: normalized(&grid_snapshot)
+---
+ Zellij (test)  Tab #1 
+┌ Enter search... ─────────────────────────────────────────────────────────────────────────────────────────────────────┐
+│$ █ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
+ ENTERING SEARCH TERM   ENTER  When done 
diff --git a/zellij-integration-tests/tests/snapshots/modes__return_to_normal_mode.snap b/zellij-integration-tests/tests/snapshots/modes__return_to_normal_mode.snap
new file mode 100644
index 00000000..b8e8dc2c
--- /dev/null
+++ b/zellij-integration-tests/tests/snapshots/modes__return_to_normal_mode.snap
@@ -0,0 +1,28 @@
+---
+source: zellij-integration-tests/tests/modes.rs
+expression: normalized(&grid_snapshot)
+---
+ Zellij (test)  Tab #1 
+┌ Pane #1 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
+│$ █ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT  ...
diff --git a/zellij-integration-tests/tests/snapshots/modes__switch_to_move_mode.snap b/zellij-integration-tests/tests/snapshots/modes__switch_to_move_mode.snap
new file mode 100644
index 00000000..2c38ca5b
--- /dev/null
+++ b/zellij-integration-tests/tests/snapshots/modes__switch_to_move_mode.snap
@@ -0,0 +1,28 @@
+---
+source: zellij-integration-tests/tests/modes.rs
+expression: normalized(&grid_snapshot)
+---
+ Zellij (test)  Tab #1 
+┌ Pane #1 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
+│$ █ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
+ Ctrl + <h> MOVE   ←↓↑→  Switch Location  ENTER  When done 
diff --git a/zellij-integration-tests/tests/snapshots/modes__switch_to_pane_mode.snap b/zellij-integration-tests/tests/snapshots/modes__switch_to_pane_mode.snap
new file mode 100644
index 00000000..77a3a87a
--- /dev/null
+++ b/zellij-integration-tests/tests/snapshots/modes__switch_to_pane_mode.snap
@@ -0,0 +1,28 @@
+---
+source: zellij-integration-tests/tests/modes.rs
+expression: normalized(&grid_snapshot)
+---
+ Zellij (test)  Tab #1 
+┌ Pane #1 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
+│$ █ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
+ Ctrl + <p> PANE   n  New  ←↓↑→  Move  x  Close  c  Rename  f  Fullscreen  Alt f  Floating  ...
diff --git a/zellij-integration-tests/tests/snapshots/modes__switch_to_resize_mode.snap b/zellij-integration-tests/tests/snapshots/modes__switch_to_resize_mode.snap
new file mode 100644
index 00000000..934b93fa
--- /dev/null
+++ b/zellij-integration-tests/tests/snapshots/modes__switch_to_resize_mode.snap
@@ -0,0 +1,28 @@
+---
+source: zellij-integration-tests/tests/modes.rs
+expression: normalized(&grid_snapshot)
+---
+ Zellij (test)  Tab #1 
+┌ Pane #1 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
+│$ █ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
+ Ctrl + <n> RESIZE   +|-  Increase/Decrease  ←↓↑→  Increase  HJKL  Decrease  ENTER  Select 
diff --git a/zellij-integration-tests/tests/snapshots/modes__switch_to_scroll_mode.snap b/zellij-integration-tests/tests/snapshots/modes__switch_to_scroll_mode.snap
new file mode 100644
index 00000000..bb92c9f8
--- /dev/null
+++ b/zellij-integration-tests/tests/snapshots/modes__switch_to_scroll_mode.snap
@@ -0,0 +1,28 @@
+---
+source: zellij-integration-tests/tests/modes.rs
+expression: normalized(&grid_snapshot)
+---
+ Zellij (test)  Tab #1 
+┌ Pane #1 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
+│$ █ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
+ Ctrl + <s> SEARCH   s  Search  ↓↑  Scroll  PgDn|PgUp  Scroll  d|u  Scroll  e  Edit  ENTER  Select 
diff --git a/zellij-integration-tests/tests/snapshots/modes__switch_to_search_mode.snap b/zellij-integration-tests/tests/snapshots/modes__switch_to_search_mode.snap
new file mode 100644
index 00000000..9e647b1a
--- /dev/null
+++ b/zellij-integration-tests/tests/snapshots/modes__switch_to_search_mode.snap
@@ -0,0 +1,28 @@
+---
+source: zellij-integration-tests/tests/modes.rs
+expression: normalized(&grid_snapshot)
+---
+ Zellij (test)  Tab #1 
+┌ Pane #1 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
+│$ █ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
+ SEARCHING   ↓↑  Scroll  PgDn|PgUp  Scroll  d|u  Scroll  n  Down  p  Up  c  Case  w  Wrap  o  Whole 
diff --git a/zellij-integration-tests/tests/snapshots/modes__switch_to_session_mode.snap b/zellij-integration-tests/tests/snapshots/modes__switch_to_session_mode.snap
new file mode 100644
index 00000000..df752793
--- /dev/null
+++ b/zellij-integration-tests/tests/snapshots/modes__switch_to_session_mode.snap
@@ -0,0 +1,28 @@
+---
+source: zellij-integration-tests/tests/modes.rs
+expression: normalized(&grid_snapshot)
+---
+ Zellij (test)  Tab #1 
+┌ Pane #1 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
+│$ █ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
+ Ctrl + <o> SESSION   d  Detach  w  Manager  s  Share  c  Config  l  Layouts  p  Plugins  ...
diff --git a/zellij-integration-tests/tests/snapshots/modes__switch_to_tab_mode.snap b/zellij-integration-tests/tests/snapshots/modes__switch_to_tab_mode.snap
new file mode 100644
index 00000000..0bd07696
--- /dev/null
+++ b/zellij-integration-tests/tests/snapshots/modes__switch_to_tab_mode.snap
@@ -0,0 +1,28 @@
+---
+source: zellij-integration-tests/tests/modes.rs
+expression: normalized(&grid_snapshot)
+---
+ Zellij (test)  Tab #1 
+┌ Pane #1 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
+│$ █ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
+ Ctrl + <t> TAB   n  New  ←→  Move  x  Close  r  Rename  s  Sync  b  Break out  []  Break  ...
diff --git a/zellij-integration-tests/tests/snapshots/panes__move_focus_down_in_pane_mode.snap b/zellij-integration-tests/tests/snapshots/panes__move_focus_down_in_pane_mode.snap
new file mode 100644
index 00000000..bf220375
--- /dev/null
+++ b/zellij-integration-tests/tests/snapshots/panes__move_focus_down_in_pane_mode.snap
@@ -0,0 +1,28 @@
+---
+source: zellij-integration-tests/tests/panes.rs
+expression: normalized(&grid_snapshot)
+---
+ Zellij (test)  Tab #1 
+┌ Pane #1 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
+│$ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
+┌ Pane #2 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
+│$ █ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT  ...
diff --git a/zellij-integration-tests/tests/snapshots/panes__move_focus_left_in_pane_mode.snap b/zellij-integration-tests/tests/snapshots/panes__move_focus_left_in_pane_mode.snap
new file mode 100644
index 00000000..fbbe4940
--- /dev/null
+++ b/zellij-integration-tests/tests/snapshots/panes__move_focus_left_in_pane_mode.snap
@@ -0,0 +1,28 @@
+---
+source: zellij-integration-tests/tests/panes.rs
+expression: normalized(&grid_snapshot)
+---
+ Zellij (test)  Tab #1 
+┌ Pane #1 ─────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────────────────────┐
+│$ █ ││$ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+└──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT  ...
diff --git a/zellij-integration-tests/tests/snapshots/panes__move_focus_or_tab_moves_focus_between_panes.snap b/zellij-integration-tests/tests/snapshots/panes__move_focus_or_tab_moves_focus_between_panes.snap
new file mode 100644
index 00000000..fbbe4940
--- /dev/null
+++ b/zellij-integration-tests/tests/snapshots/panes__move_focus_or_tab_moves_focus_between_panes.snap
@@ -0,0 +1,28 @@
+---
+source: zellij-integration-tests/tests/panes.rs
+expression: normalized(&grid_snapshot)
+---
+ Zellij (test)  Tab #1 
+┌ Pane #1 ─────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────────────────────┐
+│$ █ ││$ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+└──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT  ...
diff --git a/zellij-integration-tests/tests/snapshots/panes__move_focus_or_tab_switches_tab_when_single_pane.snap b/zellij-integration-tests/tests/snapshots/panes__move_focus_or_tab_switches_tab_when_single_pane.snap
new file mode 100644
index 00000000..f0254ce4
--- /dev/null
+++ b/zellij-integration-tests/tests/snapshots/panes__move_focus_or_tab_switches_tab_when_single_pane.snap
@@ -0,0 +1,28 @@
+---
+source: zellij-integration-tests/tests/panes.rs
+expression: normalized(&grid_snapshot)
+---
+ Zellij (test)  Tab #1  Tab #2 
+┌ Pane #1 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
+│$ oneone█ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT  ...
diff --git a/zellij-integration-tests/tests/snapshots/panes__move_focus_right_in_pane_mode.snap b/zellij-integration-tests/tests/snapshots/panes__move_focus_right_in_pane_mode.snap
new file mode 100644
index 00000000..f68a25e2
--- /dev/null
+++ b/zellij-integration-tests/tests/snapshots/panes__move_focus_right_in_pane_mode.snap
@@ -0,0 +1,28 @@
+---
+source: zellij-integration-tests/tests/panes.rs
+expression: normalized(&grid_snapshot)
+---
+ Zellij (test)  Tab #1 
+┌ Pane #1 ─────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────────────────────┐
+│$ ││$ █ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+└──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT  ...
diff --git a/zellij-integration-tests/tests/snapshots/panes__move_focus_up_in_pane_mode.snap b/zellij-integration-tests/tests/snapshots/panes__move_focus_up_in_pane_mode.snap
new file mode 100644
index 00000000..1bf6e70d
--- /dev/null
+++ b/zellij-integration-tests/tests/snapshots/panes__move_focus_up_in_pane_mode.snap
@@ -0,0 +1,28 @@
+---
+source: zellij-integration-tests/tests/panes.rs
+expression: normalized(&grid_snapshot)
+---
+ Zellij (test)  Tab #1 
+┌ Pane #1 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
+│$ █ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
+┌ Pane #2 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
+│$ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT  ...
diff --git a/zellij-integration-tests/tests/snapshots/panes__move_pane_backwards_swaps_with_neighbor.snap b/zellij-integration-tests/tests/snapshots/panes__move_pane_backwards_swaps_with_neighbor.snap
new file mode 100644
index 00000000..a5ec291c
--- /dev/null
+++ b/zellij-integration-tests/tests/snapshots/panes__move_pane_backwards_swaps_with_neighbor.snap
@@ -0,0 +1,28 @@
+---
+source: zellij-integration-tests/tests/panes.rs
+expression: normalized(&grid_snapshot)
+---
+ Zellij (test)  Tab #1 
+┌ Pane #2 ─────────────────────────────────────────────────┐┌ Pane #1 ─────────────────────────────────────────────────┐
+│$ BBB█ ││$ AAA │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+└──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT  ...
diff --git a/zellij-integration-tests/tests/snapshots/panes__move_pane_down.snap b/zellij-integration-tests/tests/snapshots/panes__move_pane_down.snap
new file mode 100644
index 00000000..3efaa27f
--- /dev/null
+++ b/zellij-integration-tests/tests/snapshots/panes__move_pane_down.snap
@@ -0,0 +1,28 @@
+---
+source: zellij-integration-tests/tests/panes.rs
+expression: normalized(&grid_snapshot)
+---
+ Zellij (test)  Tab #1 
+┌ Pane #2 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
+│$ BBB█ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
+┌ Pane #1 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
+│$ AAA │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT  ...
diff --git a/zellij-integration-tests/tests/snapshots/panes__move_pane_left.snap b/zellij-integration-tests/tests/snapshots/panes__move_pane_left.snap
new file mode 100644
index 00000000..a5ec291c
--- /dev/null
+++ b/zellij-integration-tests/tests/snapshots/panes__move_pane_left.snap
@@ -0,0 +1,28 @@
+---
+source: zellij-integration-tests/tests/panes.rs
+expression: normalized(&grid_snapshot)
+---
+ Zellij (test)  Tab #1 
+┌ Pane #2 ─────────────────────────────────────────────────┐┌ Pane #1 ─────────────────────────────────────────────────┐
+│$ BBB█ ││$ AAA │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+└──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT  ...
diff --git a/zellij-integration-tests/tests/snapshots/panes__move_pane_swaps_with_neighbor.snap b/zellij-integration-tests/tests/snapshots/panes__move_pane_swaps_with_neighbor.snap
new file mode 100644
index 00000000..a5ec291c
--- /dev/null
+++ b/zellij-integration-tests/tests/snapshots/panes__move_pane_swaps_with_neighbor.snap
@@ -0,0 +1,28 @@
+---
+source: zellij-integration-tests/tests/panes.rs
+expression: normalized(&grid_snapshot)
+---
+ Zellij (test)  Tab #1 
+┌ Pane #2 ─────────────────────────────────────────────────┐┌ Pane #1 ─────────────────────────────────────────────────┐
+│$ BBB█ ││$ AAA │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+└──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT  ...
diff --git a/zellij-integration-tests/tests/snapshots/panes__new_pane_in_normal_mode.snap b/zellij-integration-tests/tests/snapshots/panes__new_pane_in_normal_mode.snap
new file mode 100644
index 00000000..f68a25e2
--- /dev/null
+++ b/zellij-integration-tests/tests/snapshots/panes__new_pane_in_normal_mode.snap
@@ -0,0 +1,28 @@
+---
+source: zellij-integration-tests/tests/panes.rs
+expression: normalized(&grid_snapshot)
+---
+ Zellij (test)  Tab #1 
+┌ Pane #1 ─────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────────────────────┐
+│$ ││$ █ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+└──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT  ...
diff --git a/zellij-integration-tests/tests/snapshots/panes__new_pane_in_pane_mode.snap b/zellij-integration-tests/tests/snapshots/panes__new_pane_in_pane_mode.snap
new file mode 100644
index 00000000..f68a25e2
--- /dev/null
+++ b/zellij-integration-tests/tests/snapshots/panes__new_pane_in_pane_mode.snap
@@ -0,0 +1,28 @@
+---
+source: zellij-integration-tests/tests/panes.rs
+expression: normalized(&grid_snapshot)
+---
+ Zellij (test)  Tab #1 
+┌ Pane #1 ─────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────────────────────┐
+│$ ││$ █ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+└──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT  ...
diff --git a/zellij-integration-tests/tests/snapshots/panes__new_stacked_pane.snap b/zellij-integration-tests/tests/snapshots/panes__new_stacked_pane.snap
new file mode 100644
index 00000000..64abb7f3
--- /dev/null
+++ b/zellij-integration-tests/tests/snapshots/panes__new_stacked_pane.snap
@@ -0,0 +1,28 @@
+---
+source: zellij-integration-tests/tests/panes.rs
+expression: normalized(&grid_snapshot)
+---
+ Zellij (test)  Tab #1 
+┌ Pane #1 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
+┌ Pane #2 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
+│$ █ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT  ...
diff --git a/zellij-integration-tests/tests/snapshots/panes__split_pane_downward.snap b/zellij-integration-tests/tests/snapshots/panes__split_pane_downward.snap
new file mode 100644
index 00000000..bf220375
--- /dev/null
+++ b/zellij-integration-tests/tests/snapshots/panes__split_pane_downward.snap
@@ -0,0 +1,28 @@
+---
+source: zellij-integration-tests/tests/panes.rs
+expression: normalized(&grid_snapshot)
+---
+ Zellij (test)  Tab #1 
+┌ Pane #1 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
+│$ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
+┌ Pane #2 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
+│$ █ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT  ...
diff --git a/zellij-integration-tests/tests/snapshots/panes__switch_focus_in_pane_mode.snap b/zellij-integration-tests/tests/snapshots/panes__switch_focus_in_pane_mode.snap
new file mode 100644
index 00000000..fbbe4940
--- /dev/null
+++ b/zellij-integration-tests/tests/snapshots/panes__switch_focus_in_pane_mode.snap
@@ -0,0 +1,28 @@
+---
+source: zellij-integration-tests/tests/panes.rs
+expression: normalized(&grid_snapshot)
+---
+ Zellij (test)  Tab #1 
+┌ Pane #1 ─────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────────────────────┐
+│$ █ ││$ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+└──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT  ...
diff --git a/zellij-integration-tests/tests/snapshots/panes__toggle_pane_embed_or_floating.snap b/zellij-integration-tests/tests/snapshots/panes__toggle_pane_embed_or_floating.snap
new file mode 100644
index 00000000..3a4e1240
--- /dev/null
+++ b/zellij-integration-tests/tests/snapshots/panes__toggle_pane_embed_or_floating.snap
@@ -0,0 +1,28 @@
+---
+source: zellij-integration-tests/tests/panes.rs
+expression: normalized(&grid_snapshot)
+---
+ Zellij (test)  Tab #1 
+┌ Pane #1 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
+│$ │
+│ │
+│ │
+│ │
+│ │
+│ ┌ Pane #2 ──────────────────────────────────────── PIN [ ] ┐ │
+│ │$ █ │ │
+│ │ │ │
+│ │ │ │
+│ │ │ │
+│ │ │ │
+│ │ │ │
+│ │ │ │
+│ │ │ │
+│ │ │ │
+│ └──────────────────────────────────────────────────────────┘ │
+│ │
+│ │
+│ │
+│ │
+└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT  ...
diff --git a/zellij-integration-tests/tests/snapshots/panes__toggle_pane_frames.snap b/zellij-integration-tests/tests/snapshots/panes__toggle_pane_frames.snap
new file mode 100644
index 00000000..e58b8c3e
--- /dev/null
+++ b/zellij-integration-tests/tests/snapshots/panes__toggle_pane_frames.snap
@@ -0,0 +1,28 @@
+---
+source: zellij-integration-tests/tests/panes.rs
+expression: normalized(&grid_snapshot)
+---
+ Zellij (test)  Tab #1 
+$ │$ █
+ │
+ │
+ │
+ │
+ │
+ │
+ │
+ │
+ │
+ │
+ │
+ │
+ │
+ │
+ │
+ │
+ │
+ │
+ │
+ │
+ │
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT  ...
diff --git a/zellij-integration-tests/tests/snapshots/panes__toggle_pane_pinned.snap b/zellij-integration-tests/tests/snapshots/panes__toggle_pane_pinned.snap
new file mode 100644
index 00000000..858b2446
--- /dev/null
+++ b/zellij-integration-tests/tests/snapshots/panes__toggle_pane_pinned.snap
@@ -0,0 +1,28 @@
+---
+source: zellij-integration-tests/tests/panes.rs
+expression: normalized(&grid_snapshot)
+---
+ Zellij (test)  Tab #1 
+┌ Pane #1 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
+│$ │
+│ │
+│ │
+│ │
+│ │
+│ ┌ Pane #2 ──────────────────────────────────────── PIN [+] ┐ │
+│ │$ █ │ │
+│ │ │ │
+│ │ │ │
+│ │ │ │
+│ │ │ │
+│ │ │ │
+│ │ │ │
+│ │ │ │
+│ │ │ │
+│ └──────────────────────────────────────────────────────────┘ │
+│ │
+│ │
+│ │
+│ │
+└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT  ...
diff --git a/zellij-integration-tests/tests/snapshots/resize__resize_decrease_down.snap b/zellij-integration-tests/tests/snapshots/resize__resize_decrease_down.snap
new file mode 100644
index 00000000..6bb097ff
--- /dev/null
+++ b/zellij-integration-tests/tests/snapshots/resize__resize_decrease_down.snap
@@ -0,0 +1,28 @@
+---
+source: zellij-integration-tests/tests/resize.rs
+expression: normalized(&grid_snapshot)
+---
+ Zellij (test)  Tab #1 
+┌ Pane #1 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
+│$ █ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
+┌ Pane #2 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
+│$ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT  ...
diff --git a/zellij-integration-tests/tests/snapshots/resize__resize_decrease_in_normal_mode.snap b/zellij-integration-tests/tests/snapshots/resize__resize_decrease_in_normal_mode.snap
new file mode 100644
index 00000000..6d94b5be
--- /dev/null
+++ b/zellij-integration-tests/tests/snapshots/resize__resize_decrease_in_normal_mode.snap
@@ -0,0 +1,28 @@
+---
+source: zellij-integration-tests/tests/resize.rs
+expression: normalized(&grid_snapshot)
+---
+ Zellij (test)  Tab #1 
+┌ Pane #1 ─────────────────────────────────────────────────────────────────────────────────────┐┌ Pane #2 ─────────────┐
+│$ ││$ █ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+└──────────────────────────────────────────────────────────────────────────────────────────────┘└──────────────────────┘
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT  ...
diff --git a/zellij-integration-tests/tests/snapshots/resize__resize_decrease_in_resize_mode.snap b/zellij-integration-tests/tests/snapshots/resize__resize_decrease_in_resize_mode.snap
new file mode 100644
index 00000000..6d94b5be
--- /dev/null
+++ b/zellij-integration-tests/tests/snapshots/resize__resize_decrease_in_resize_mode.snap
@@ -0,0 +1,28 @@
+---
+source: zellij-integration-tests/tests/resize.rs
+expression: normalized(&grid_snapshot)
+---
+ Zellij (test)  Tab #1 
+┌ Pane #1 ─────────────────────────────────────────────────────────────────────────────────────┐┌ Pane #2 ─────────────┐
+│$ ││$ █ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+└──────────────────────────────────────────────────────────────────────────────────────────────┘└──────────────────────┘
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT  ...
diff --git a/zellij-integration-tests/tests/snapshots/resize__resize_decrease_left.snap b/zellij-integration-tests/tests/snapshots/resize__resize_decrease_left.snap
new file mode 100644
index 00000000..31a95ba2
--- /dev/null
+++ b/zellij-integration-tests/tests/snapshots/resize__resize_decrease_left.snap
@@ -0,0 +1,28 @@
+---
+source: zellij-integration-tests/tests/resize.rs
+expression: normalized(&grid_snapshot)
+---
+ Zellij (test)  Tab #1 
+┌ Pane #1 ───────────────────────────────────────────────────────┐┌ Pane #2 ───────────────────────────────────────────┐
+│$ ││$ █ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+└────────────────────────────────────────────────────────────────┘└────────────────────────────────────────────────────┘
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT  ...
diff --git a/zellij-integration-tests/tests/snapshots/resize__resize_decrease_right.snap b/zellij-integration-tests/tests/snapshots/resize__resize_decrease_right.snap
new file mode 100644
index 00000000..8071443e
--- /dev/null
+++ b/zellij-integration-tests/tests/snapshots/resize__resize_decrease_right.snap
@@ -0,0 +1,28 @@
+---
+source: zellij-integration-tests/tests/resize.rs
+expression: normalized(&grid_snapshot)
+---
+ Zellij (test)  Tab #1 
+┌ Pane #1 ───────────────────────────────────────────┐┌ Pane #2 ───────────────────────────────────────────────────────┐
+│$ █ ││$ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+└────────────────────────────────────────────────────┘└────────────────────────────────────────────────────────────────┘
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT  ...
diff --git a/zellij-integration-tests/tests/snapshots/resize__resize_decrease_up.snap b/zellij-integration-tests/tests/snapshots/resize__resize_decrease_up.snap
new file mode 100644
index 00000000..f60a7abc
--- /dev/null
+++ b/zellij-integration-tests/tests/snapshots/resize__resize_decrease_up.snap
@@ -0,0 +1,28 @@
+---
+source: zellij-integration-tests/tests/resize.rs
+expression: normalized(&grid_snapshot)
+---
+ Zellij (test)  Tab #1 
+┌ Pane #1 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
+│$ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
+┌ Pane #2 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
+│$ █ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT  ...
diff --git a/zellij-integration-tests/tests/snapshots/resize__resize_increase_down.snap b/zellij-integration-tests/tests/snapshots/resize__resize_increase_down.snap
new file mode 100644
index 00000000..b04b511a
--- /dev/null
+++ b/zellij-integration-tests/tests/snapshots/resize__resize_increase_down.snap
@@ -0,0 +1,28 @@
+---
+source: zellij-integration-tests/tests/resize.rs
+expression: normalized(&grid_snapshot)
+---
+ Zellij (test)  Tab #1 
+┌ Pane #1 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
+│$ █ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
+┌ Pane #2 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
+│$ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT  ...
diff --git a/zellij-integration-tests/tests/snapshots/resize__resize_increase_in_normal_mode.snap b/zellij-integration-tests/tests/snapshots/resize__resize_increase_in_normal_mode.snap
new file mode 100644
index 00000000..1da118af
--- /dev/null
+++ b/zellij-integration-tests/tests/snapshots/resize__resize_increase_in_normal_mode.snap
@@ -0,0 +1,28 @@
+---
+source: zellij-integration-tests/tests/resize.rs
+expression: normalized(&grid_snapshot)
+---
+ Zellij (test)  Tab #1 
+┌ Pane #1 ─────────────┐┌ Pane #2 ─────────────────────────────────────────────────────────────────────────────────────┐
+│$ ││$ █ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+└──────────────────────┘└──────────────────────────────────────────────────────────────────────────────────────────────┘
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT  ...
diff --git a/zellij-integration-tests/tests/snapshots/resize__resize_increase_in_resize_mode.snap b/zellij-integration-tests/tests/snapshots/resize__resize_increase_in_resize_mode.snap
new file mode 100644
index 00000000..1da118af
--- /dev/null
+++ b/zellij-integration-tests/tests/snapshots/resize__resize_increase_in_resize_mode.snap
@@ -0,0 +1,28 @@
+---
+source: zellij-integration-tests/tests/resize.rs
+expression: normalized(&grid_snapshot)
+---
+ Zellij (test)  Tab #1 
+┌ Pane #1 ─────────────┐┌ Pane #2 ─────────────────────────────────────────────────────────────────────────────────────┐
+│$ ││$ █ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+└──────────────────────┘└──────────────────────────────────────────────────────────────────────────────────────────────┘
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT  ...
diff --git a/zellij-integration-tests/tests/snapshots/resize__resize_increase_right.snap b/zellij-integration-tests/tests/snapshots/resize__resize_increase_right.snap
new file mode 100644
index 00000000..fca16a54
--- /dev/null
+++ b/zellij-integration-tests/tests/snapshots/resize__resize_increase_right.snap
@@ -0,0 +1,28 @@
+---
+source: zellij-integration-tests/tests/resize.rs
+expression: normalized(&grid_snapshot)
+---
+ Zellij (test)  Tab #1 
+┌ Pane #1 ───────────────────────────────────────────────────────┐┌ Pane #2 ───────────────────────────────────────────┐
+│$ █ ││$ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+└────────────────────────────────────────────────────────────────┘└────────────────────────────────────────────────────┘
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT  ...
diff --git a/zellij-integration-tests/tests/snapshots/resize__resize_increase_up.snap b/zellij-integration-tests/tests/snapshots/resize__resize_increase_up.snap
new file mode 100644
index 00000000..5ff0c7aa
--- /dev/null
+++ b/zellij-integration-tests/tests/snapshots/resize__resize_increase_up.snap
@@ -0,0 +1,28 @@
+---
+source: zellij-integration-tests/tests/resize.rs
+expression: normalized(&grid_snapshot)
+---
+ Zellij (test)  Tab #1 
+┌ Pane #1 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
+│$ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
+┌ Pane #2 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
+│$ █ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT  ...
diff --git a/zellij-integration-tests/tests/snapshots/scroll__half_page_scroll_down.snap b/zellij-integration-tests/tests/snapshots/scroll__half_page_scroll_down.snap
new file mode 100644
index 00000000..2ce2743f
--- /dev/null
+++ b/zellij-integration-tests/tests/snapshots/scroll__half_page_scroll_down.snap
@@ -0,0 +1,28 @@
+---
+source: zellij-integration-tests/tests/scroll.rs
+expression: normalized(&grid_snapshot)
+---
+ Zellij (test)  Tab #1 
+┌ Pane #1 ────────────────────────────────────────────────────────────────────────────────────────────── SCROLL: 0/21 ┐
+│line22 │
+│line23 │
+│line24 │
+│line25 │
+│line26 │
+│line27 │
+│line28 │
+│line29 │
+│line30 │
+│line31 │
+│line32 │
+│line33 │
+│line34 │
+│line35 │
+│line36 │
+│line37 │
+│line38 │
+│line39 │
+│line40 │
+│█ │
+└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
+ Ctrl + <s> SEARCH   s  Search  ↓↑  Scroll  PgDn|PgUp  Scroll  d|u  Scroll  e  Edit  ENTER  Select 
diff --git a/zellij-integration-tests/tests/snapshots/scroll__half_page_scroll_up.snap b/zellij-integration-tests/tests/snapshots/scroll__half_page_scroll_up.snap
new file mode 100644
index 00000000..4adb7c17
--- /dev/null
+++ b/zellij-integration-tests/tests/snapshots/scroll__half_page_scroll_up.snap
@@ -0,0 +1,28 @@
+---
+source: zellij-integration-tests/tests/scroll.rs
+expression: normalized(&grid_snapshot)
+---
+ Zellij (test)  Tab #1 
+┌ Pane #1 ───────────────────────────────────────────────────────────────────────────────────────────── SCROLL: 10/21 ┐
+│line12 │
+│line13 │
+│line14 │
+│line15 │
+│line16 │
+│line17 │
+│line18 │
+│line19 │
+│line20 │
+│line21 │
+│line22 │
+│line23 │
+│line24 │
+│line25 │
+│line26 │
+│line27 │
+│line28 │
+│line29 │
+│line30 │
+│█ine31 │
+└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
+ Ctrl + <s> SEARCH   s  Search  ↓↑  Scroll  PgDn|PgUp  Scroll  d|u  Scroll  e  Edit  ENTER  Select 
diff --git a/zellij-integration-tests/tests/snapshots/scroll__page_scroll_down.snap b/zellij-integration-tests/tests/snapshots/scroll__page_scroll_down.snap
new file mode 100644
index 00000000..6b3cec51
--- /dev/null
+++ b/zellij-integration-tests/tests/snapshots/scroll__page_scroll_down.snap
@@ -0,0 +1,28 @@
+---
+source: zellij-integration-tests/tests/scroll.rs
+expression: normalized(&grid_snapshot)
+---
+ Zellij (test)  Tab #1 
+┌ Pane #1 ────────────────────────────────────────────────────────────────────────────────────────────── SCROLL: 1/21 ┐
+│line21 │
+│line22 │
+│line23 │
+│line24 │
+│line25 │
+│line26 │
+│line27 │
+│line28 │
+│line29 │
+│line30 │
+│line31 │
+│line32 │
+│line33 │
+│line34 │
+│line35 │
+│line36 │
+│line37 │
+│line38 │
+│line39 │
+│█ine40 │
+└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
+ Ctrl + <s> SEARCH   s  Search  ↓↑  Scroll  PgDn|PgUp  Scroll  d|u  Scroll  e  Edit  ENTER  Select 
diff --git a/zellij-integration-tests/tests/snapshots/scroll__page_scroll_up.snap b/zellij-integration-tests/tests/snapshots/scroll__page_scroll_up.snap
new file mode 100644
index 00000000..d742f0d3
--- /dev/null
+++ b/zellij-integration-tests/tests/snapshots/scroll__page_scroll_up.snap
@@ -0,0 +1,28 @@
+---
+source: zellij-integration-tests/tests/scroll.rs
+expression: normalized(&grid_snapshot)
+---
+ Zellij (test)  Tab #1 
+┌ Pane #1 ───────────────────────────────────────────────────────────────────────────────────────────── SCROLL: 21/21 ┐
+│$ line01 │
+│line02 │
+│line03 │
+│line04 │
+│line05 │
+│line06 │
+│line07 │
+│line08 │
+│line09 │
+│line10 │
+│line11 │
+│line12 │
+│line13 │
+│line14 │
+│line15 │
+│line16 │
+│line17 │
+│line18 │
+│line19 │
+│█ine20 │
+└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
+ Ctrl + <s> SEARCH   s  Search  ↓↑  Scroll  PgDn|PgUp  Scroll  d|u  Scroll  e  Edit  ENTER  Select 
diff --git a/zellij-integration-tests/tests/snapshots/scroll__scroll_down_one_line.snap b/zellij-integration-tests/tests/snapshots/scroll__scroll_down_one_line.snap
new file mode 100644
index 00000000..6b3cec51
--- /dev/null
+++ b/zellij-integration-tests/tests/snapshots/scroll__scroll_down_one_line.snap
@@ -0,0 +1,28 @@
+---
+source: zellij-integration-tests/tests/scroll.rs
+expression: normalized(&grid_snapshot)
+---
+ Zellij (test)  Tab #1 
+┌ Pane #1 ────────────────────────────────────────────────────────────────────────────────────────────── SCROLL: 1/21 ┐
+│line21 │
+│line22 │
+│line23 │
+│line24 │
+│line25 │
+│line26 │
+│line27 │
+│line28 │
+│line29 │
+│line30 │
+│line31 │
+│line32 │
+│line33 │
+│line34 │
+│line35 │
+│line36 │
+│line37 │
+│line38 │
+│line39 │
+│█ine40 │
+└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
+ Ctrl + <s> SEARCH   s  Search  ↓↑  Scroll  PgDn|PgUp  Scroll  d|u  Scroll  e  Edit  ENTER  Select 
diff --git a/zellij-integration-tests/tests/snapshots/scroll__scroll_to_bottom.snap b/zellij-integration-tests/tests/snapshots/scroll__scroll_to_bottom.snap
new file mode 100644
index 00000000..4e486cc2
--- /dev/null
+++ b/zellij-integration-tests/tests/snapshots/scroll__scroll_to_bottom.snap
@@ -0,0 +1,28 @@
+---
+source: zellij-integration-tests/tests/scroll.rs
+expression: normalized(&grid_snapshot)
+---
+ Zellij (test)  Tab #1 
+┌ Pane #1 ────────────────────────────────────────────────────────────────────────────────────────────── SCROLL: 0/21 ┐
+│line22 │
+│line23 │
+│line24 │
+│line25 │
+│line26 │
+│line27 │
+│line28 │
+│line29 │
+│line30 │
+│line31 │
+│line32 │
+│line33 │
+│line34 │
+│line35 │
+│line36 │
+│line37 │
+│line38 │
+│line39 │
+│line40 │
+│█ │
+└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT  ...
diff --git a/zellij-integration-tests/tests/snapshots/search__search_down_to_next_match.snap b/zellij-integration-tests/tests/snapshots/search__search_down_to_next_match.snap
new file mode 100644
index 00000000..7f89ceaa
--- /dev/null
+++ b/zellij-integration-tests/tests/snapshots/search__search_down_to_next_match.snap
@@ -0,0 +1,28 @@
+---
+source: zellij-integration-tests/tests/search.rs
+expression: normalized(&grid_snapshot)
+---
+ Zellij (test)  Tab #1 
+┌ SEARCHING: NEEDLE ──────────────────────────────────────────────────────────────────────────────────── SCROLL: 6/21 ┐
+│line16 │
+│line17 │
+│line18 │
+│line19 │
+│line20 │
+│line21 │
+│line22 │
+│line23 │
+│line24 │
+│line25 │
+│line26 │
+│line27 │
+│line28 │
+│line29 │
+│line30 │
+│line31 │
+│line32 │
+│line33 │
+│line34 │
+│█ine35 NEEDLE lower │
+└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
+ SEARCHING   ↓↑  Scroll  PgDn|PgUp  Scroll  d|u  Scroll  n  Down  p  Up  c  Case  w  Wrap  o  Whole 
diff --git a/zellij-integration-tests/tests/snapshots/search__search_input_jumps_to_a_match.snap b/zellij-integration-tests/tests/snapshots/search__search_input_jumps_to_a_match.snap
new file mode 100644
index 00000000..d3c4a72d
--- /dev/null
+++ b/zellij-integration-tests/tests/snapshots/search__search_input_jumps_to_a_match.snap
@@ -0,0 +1,28 @@
+---
+source: zellij-integration-tests/tests/search.rs
+expression: normalized(&grid_snapshot)
+---
+ Zellij (test)  Tab #1 
+┌ SEARCHING: NEEDLE ─────────────────────────────────────────────────────────────────────────────────── SCROLL: 17/21 ┐
+│line05 NEEDLE │
+│line06 │
+│line07 │
+│line08 │
+│line09 │
+│line10 │
+│line11 │
+│line12 │
+│line13 │
+│line14 │
+│line15 │
+│line16 │
+│line17 │
+│line18 │
+│line19 │
+│line20 │
+│line21 │
+│line22 │
+│line23 │
+│█ine24 │
+└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
+ ENTERING SEARCH TERM   ENTER  When done 
diff --git a/zellij-integration-tests/tests/snapshots/search__search_toggle_case_sensitivity.snap b/zellij-integration-tests/tests/snapshots/search__search_toggle_case_sensitivity.snap
new file mode 100644
index 00000000..8fab3747
--- /dev/null
+++ b/zellij-integration-tests/tests/snapshots/search__search_toggle_case_sensitivity.snap
@@ -0,0 +1,28 @@
+---
+source: zellij-integration-tests/tests/search.rs
+expression: normalized(&grid_snapshot)
+---
+ Zellij (test)  Tab #1 
+┌ SEARCHING: NEEDLE [c] ───────────────────────────────────────────────────────────────────────────────────────────────┐
+│$ NEEDLE on screen │
+│█ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
+ SEARCHING   ↓↑  Scroll  PgDn|PgUp  Scroll  d|u  Scroll  n  Down  p  Up  c  Case  w  Wrap  o  Whole 
diff --git a/zellij-integration-tests/tests/snapshots/search__search_toggle_whole_word.snap b/zellij-integration-tests/tests/snapshots/search__search_toggle_whole_word.snap
new file mode 100644
index 00000000..a611c034
--- /dev/null
+++ b/zellij-integration-tests/tests/snapshots/search__search_toggle_whole_word.snap
@@ -0,0 +1,28 @@
+---
+source: zellij-integration-tests/tests/search.rs
+expression: normalized(&grid_snapshot)
+---
+ Zellij (test)  Tab #1 
+┌ SEARCHING: NEEDLE [o] ───────────────────────────────────────────────────────────────────────────────────────────────┐
+│$ NEEDLE on screen │
+│█ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
+ SEARCHING   ↓↑  Scroll  PgDn|PgUp  Scroll  d|u  Scroll  n  Down  p  Up  c  Case  w  Wrap  o  Whole 
diff --git a/zellij-integration-tests/tests/snapshots/search__search_toggle_wrap.snap b/zellij-integration-tests/tests/snapshots/search__search_toggle_wrap.snap
new file mode 100644
index 00000000..42903ace
--- /dev/null
+++ b/zellij-integration-tests/tests/snapshots/search__search_toggle_wrap.snap
@@ -0,0 +1,28 @@
+---
+source: zellij-integration-tests/tests/search.rs
+expression: normalized(&grid_snapshot)
+---
+ Zellij (test)  Tab #1 
+┌ SEARCHING: NEEDLE [w] ───────────────────────────────────────────────────────────────────────────────────────────────┐
+│$ NEEDLE on screen │
+│█ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
+ SEARCHING   ↓↑  Scroll  PgDn|PgUp  Scroll  d|u  Scroll  n  Down  p  Up  c  Case  w  Wrap  o  Whole 
diff --git a/zellij-integration-tests/tests/snapshots/search__search_up_to_previous_match.snap b/zellij-integration-tests/tests/snapshots/search__search_up_to_previous_match.snap
new file mode 100644
index 00000000..c113cdad
--- /dev/null
+++ b/zellij-integration-tests/tests/snapshots/search__search_up_to_previous_match.snap
@@ -0,0 +1,28 @@
+---
+source: zellij-integration-tests/tests/search.rs
+expression: normalized(&grid_snapshot)
+---
+ Zellij (test)  Tab #1 
+┌ SEARCHING: NEEDLE ─────────────────────────────────────────────────────────────────────────────────── SCROLL: 17/21 ┐
+│line05 NEEDLE upper │
+│line06 │
+│line07 │
+│line08 │
+│line09 │
+│line10 │
+│line11 │
+│line12 │
+│line13 │
+│line14 │
+│line15 │
+│line16 │
+│line17 │
+│line18 │
+│line19 │
+│line20 │
+│line21 │
+│line22 │
+│line23 │
+│█ine24 │
+└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
+ SEARCHING   ↓↑  Scroll  PgDn|PgUp  Scroll  d|u  Scroll  n  Down  p  Up  c  Case  w  Wrap  o  Whole 
diff --git a/zellij-integration-tests/tests/snapshots/tabs__break_pane_into_new_tab.snap b/zellij-integration-tests/tests/snapshots/tabs__break_pane_into_new_tab.snap
new file mode 100644
index 00000000..83ddbab4
--- /dev/null
+++ b/zellij-integration-tests/tests/snapshots/tabs__break_pane_into_new_tab.snap
@@ -0,0 +1,28 @@
+---
+source: zellij-integration-tests/tests/tabs.rs
+expression: normalized(&grid_snapshot)
+---
+ Zellij (test)  Tab #1  Tab #2 
+┌ Pane #2 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
+│$ █ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT  ...
diff --git a/zellij-integration-tests/tests/snapshots/tabs__break_pane_to_the_left.snap b/zellij-integration-tests/tests/snapshots/tabs__break_pane_to_the_left.snap
new file mode 100644
index 00000000..f64e72ee
--- /dev/null
+++ b/zellij-integration-tests/tests/snapshots/tabs__break_pane_to_the_left.snap
@@ -0,0 +1,28 @@
+---
+source: zellij-integration-tests/tests/tabs.rs
+expression: normalized(&grid_snapshot)
+---
+ Zellij (test)  Tab #1 
+┌ Pane #1 ─────────────────────────────────────────────────┐┌ Pane #1 ─────────────────────────────────────────────────┐
+│$ alpha ││$ beta█ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+└──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT  ...
diff --git a/zellij-integration-tests/tests/snapshots/tabs__break_pane_to_the_right.snap b/zellij-integration-tests/tests/snapshots/tabs__break_pane_to_the_right.snap
new file mode 100644
index 00000000..db5fb026
--- /dev/null
+++ b/zellij-integration-tests/tests/snapshots/tabs__break_pane_to_the_right.snap
@@ -0,0 +1,28 @@
+---
+source: zellij-integration-tests/tests/tabs.rs
+expression: normalized(&grid_snapshot)
+---
+ Zellij (test)  Tab #1  Tab #2 
+┌ Pane #1 ─────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────────────────────┐
+│$ gamma ││$ beta█ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+└──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT  ...
diff --git a/zellij-integration-tests/tests/snapshots/tabs__go_to_next_tab.snap b/zellij-integration-tests/tests/snapshots/tabs__go_to_next_tab.snap
new file mode 100644
index 00000000..9441a9b1
--- /dev/null
+++ b/zellij-integration-tests/tests/snapshots/tabs__go_to_next_tab.snap
@@ -0,0 +1,28 @@
+---
+source: zellij-integration-tests/tests/tabs.rs
+expression: normalized(&grid_snapshot)
+---
+ Zellij (test)  Tab #1  Tab #2 
+┌ Pane #1 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
+│$ twotwo█ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT  ...
diff --git a/zellij-integration-tests/tests/snapshots/tabs__go_to_previous_tab.snap b/zellij-integration-tests/tests/snapshots/tabs__go_to_previous_tab.snap
new file mode 100644
index 00000000..e60935a0
--- /dev/null
+++ b/zellij-integration-tests/tests/snapshots/tabs__go_to_previous_tab.snap
@@ -0,0 +1,28 @@
+---
+source: zellij-integration-tests/tests/tabs.rs
+expression: normalized(&grid_snapshot)
+---
+ Zellij (test)  Tab #1  Tab #2 
+┌ Pane #1 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
+│$ oneone█ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT  ...
diff --git a/zellij-integration-tests/tests/snapshots/tabs__go_to_tab_by_number.snap b/zellij-integration-tests/tests/snapshots/tabs__go_to_tab_by_number.snap
new file mode 100644
index 00000000..e60935a0
--- /dev/null
+++ b/zellij-integration-tests/tests/snapshots/tabs__go_to_tab_by_number.snap
@@ -0,0 +1,28 @@
+---
+source: zellij-integration-tests/tests/tabs.rs
+expression: normalized(&grid_snapshot)
+---
+ Zellij (test)  Tab #1  Tab #2 
+┌ Pane #1 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
+│$ oneone█ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT  ...
diff --git a/zellij-integration-tests/tests/snapshots/tabs__toggle_active_sync_tab.snap b/zellij-integration-tests/tests/snapshots/tabs__toggle_active_sync_tab.snap
new file mode 100644
index 00000000..f34fad73
--- /dev/null
+++ b/zellij-integration-tests/tests/snapshots/tabs__toggle_active_sync_tab.snap
@@ -0,0 +1,28 @@
+---
+source: zellij-integration-tests/tests/tabs.rs
+expression: normalized(&grid_snapshot)
+---
+ Zellij (test)  Tab #1 (SYNC) 
+┌ Pane #1 ─────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────────────────────┐
+│$ ││$ █ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+│ ││ │
+└──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT  ...
diff --git a/zellij-integration-tests/tests/snapshots/tabs__toggle_tab_returns_to_last_used.snap b/zellij-integration-tests/tests/snapshots/tabs__toggle_tab_returns_to_last_used.snap
new file mode 100644
index 00000000..e60935a0
--- /dev/null
+++ b/zellij-integration-tests/tests/snapshots/tabs__toggle_tab_returns_to_last_used.snap
@@ -0,0 +1,28 @@
+---
+source: zellij-integration-tests/tests/tabs.rs
+expression: normalized(&grid_snapshot)
+---
+ Zellij (test)  Tab #1  Tab #2 
+┌ Pane #1 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
+│$ oneone█ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+│ │
+└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <n> RESIZE  <h> MOVE  <s> SEARCH  <o> SESSION  <q> QUIT  ...
diff --git a/zellij-integration-tests/tests/tabs.rs b/zellij-integration-tests/tests/tabs.rs
index fedaf9f1..7ba3194a 100644
--- a/zellij-integration-tests/tests/tabs.rs
+++ b/zellij-integration-tests/tests/tabs.rs
@@ -2,35 +2,10 @@
use insta::assert_snapshot;
use zellij_integration_tests::{
- keys, normalized, FakePtyHandle, GridSnapshot, Size, TestRunner, TestSession,
+ claim_first_terminal_and_wait_for_prompt, col, keys, normalized, start_zellij, FakePtyHandle,
+ GridSnapshot, TestSession, PROMPT,
};
-const TERMINAL_SIZE: Size = Size {
- cols: 120,
- rows: 24,
-};
-const PROMPT: &[u8] = b"$ ";
-const PROMPT_ROW: usize = 2;
-const FIRST_PANE_PROMPT_X: usize = 3;
-
-fn start_zellij() -> TestSession {
- TestRunner::new(TERMINAL_SIZE).start()
-}
-
-fn claim_first_terminal_and_wait_for_prompt(zellij: &TestSession) -> FakePtyHandle {
- let terminal = zellij.expect_pty_spawn();
- terminal.output(PROMPT);
- zellij.wait_until(
- "first terminal prompt rendered in loaded app",
- |grid_snapshot| {
- grid_snapshot.tab_bar_appears()
- && grid_snapshot.status_bar_appears()
- && grid_snapshot.cursor_is_at(FIRST_PANE_PROMPT_X, PROMPT_ROW)
- },
- );
- terminal
-}
-
fn tabs_in_order(grid_snapshot: &GridSnapshot, labels: &[&str]) -> bool {
let mut search_from = 0;
for label in labels {
@@ -43,19 +18,253 @@ fn tabs_in_order(grid_snapshot: &GridSnapshot, labels: &[&str]) -> bool {
}
fn open_new_tab_and_wait_for_prompt(zellij: &TestSession, expected_tab: &str) -> FakePtyHandle {
- zellij.send_stdin(&keys::TAB_MODE);
- zellij.send_stdin(&keys::NEW_TAB_IN_TAB_MODE);
+ zellij.send_stdin(&keys::ctrl('t'));
+ zellij.send_stdin(&keys::key('n'));
let terminal = zellij.expect_pty_spawn();
terminal.output(PROMPT);
let expected_tab = expected_tab.to_owned();
zellij.wait_until("new tab opened with prompt", move |grid_snapshot| {
grid_snapshot.status_bar_appears()
&& grid_snapshot.contains(&expected_tab)
- && grid_snapshot.cursor_is_at(FIRST_PANE_PROMPT_X, PROMPT_ROW)
+ && grid_snapshot.cursor_is_at(col(3).row(2))
});
terminal
}
+fn label_first_tab_pane(zellij: &TestSession, terminal: &FakePtyHandle, marker: &str) {
+ terminal.output(marker.as_bytes());
+ let marker = marker.to_owned();
+ zellij.wait_until("first tab pane labelled", move |grid_snapshot| {
+ grid_snapshot.contains(&marker)
+ });
+}
+
+fn open_marked_tab(zellij: &TestSession, expected_tab: &str, marker: &str) -> FakePtyHandle {
+ zellij.send_stdin(&keys::ctrl('t'));
+ zellij.send_stdin(&keys::key('n'));
+ let terminal = zellij.expect_pty_spawn();
+ terminal.output(PROMPT);
+ terminal.output(marker.as_bytes());
+ let expected_tab = expected_tab.to_owned();
+ let marker = marker.to_owned();
+ zellij.wait_until("new marked tab opened", move |grid_snapshot| {
+ grid_snapshot.status_bar_appears()
+ && grid_snapshot.contains(&expected_tab)
+ && grid_snapshot.contains(&marker)
+ });
+ terminal
+}
+
+#[test]
+fn go_to_previous_tab() {
+ let mut zellij = start_zellij();
+ let first_terminal = claim_first_terminal_and_wait_for_prompt(&zellij);
+ label_first_tab_pane(&zellij, &first_terminal, "oneone");
+ open_marked_tab(&zellij, "Tab #2", "twotwo");
+
+ zellij.send_stdin(&keys::ctrl('t'));
+ zellij.send_stdin(&keys::key('h'));
+ zellij.send_stdin(&keys::ENTER);
+
+ let grid_snapshot = zellij.wait_until("previous tab focused", |grid_snapshot| {
+ grid_snapshot.status_bar_appears()
+ && grid_snapshot.contains("Tab #2")
+ && grid_snapshot.contains("oneone")
+ && !grid_snapshot.contains("twotwo")
+ });
+ assert_snapshot!(normalized(&grid_snapshot));
+ zellij.quit();
+}
+
+#[test]
+fn go_to_next_tab() {
+ let mut zellij = start_zellij();
+ let first_terminal = claim_first_terminal_and_wait_for_prompt(&zellij);
+ label_first_tab_pane(&zellij, &first_terminal, "oneone");
+ open_marked_tab(&zellij, "Tab #2", "twotwo");
+
+ zellij.send_stdin(&keys::ctrl('t'));
+ zellij.send_stdin(&keys::key('h'));
+ zellij.wait_until("previous tab focused", |grid_snapshot| {
+ grid_snapshot.contains("oneone") && !grid_snapshot.contains("twotwo")
+ });
+
+ zellij.send_stdin(&keys::key('l'));
+ zellij.send_stdin(&keys::ENTER);
+
+ let grid_snapshot = zellij.wait_until("next tab focused again", |grid_snapshot| {
+ grid_snapshot.status_bar_appears()
+ && grid_snapshot.contains("Tab #2")
+ && grid_snapshot.contains("twotwo")
+ && !grid_snapshot.contains("oneone")
+ });
+ assert_snapshot!(normalized(&grid_snapshot));
+ zellij.quit();
+}
+
+#[test]
+fn go_to_tab_by_number() {
+ let mut zellij = start_zellij();
+ let first_terminal = claim_first_terminal_and_wait_for_prompt(&zellij);
+ label_first_tab_pane(&zellij, &first_terminal, "oneone");
+ open_marked_tab(&zellij, "Tab #2", "twotwo");
+
+ zellij.send_stdin(&keys::ctrl('t'));
+ zellij.send_stdin(&keys::key('1'));
+
+ let grid_snapshot = zellij.wait_until("first tab focused by number", |grid_snapshot| {
+ grid_snapshot.status_bar_appears()
+ && grid_snapshot.contains("oneone")
+ && !grid_snapshot.contains("twotwo")
+ });
+ assert_snapshot!(normalized(&grid_snapshot));
+ zellij.quit();
+}
+
+#[test]
+fn toggle_tab_returns_to_last_used() {
+ let mut zellij = start_zellij();
+ let first_terminal = claim_first_terminal_and_wait_for_prompt(&zellij);
+ label_first_tab_pane(&zellij, &first_terminal, "oneone");
+ open_marked_tab(&zellij, "Tab #2", "twotwo");
+
+ zellij.send_stdin(&keys::ctrl('t'));
+ zellij.send_stdin(&keys::TAB);
+ zellij.send_stdin(&keys::ENTER);
+
+ let grid_snapshot = zellij.wait_until("toggled back to the first tab", |grid_snapshot| {
+ grid_snapshot.status_bar_appears()
+ && grid_snapshot.contains("Tab #2")
+ && grid_snapshot.contains("oneone")
+ && !grid_snapshot.contains("twotwo")
+ });
+ assert_snapshot!(normalized(&grid_snapshot));
+ zellij.quit();
+}
+
+#[test]
+fn toggle_active_sync_tab() {
+ let mut zellij = start_zellij();
+ let first_terminal = claim_first_terminal_and_wait_for_prompt(&zellij);
+
+ zellij.send_stdin(&keys::ctrl('p'));
+ zellij.send_stdin(&keys::key('n'));
+ let second_terminal = zellij.expect_pty_spawn();
+ second_terminal.output(PROMPT);
+ zellij.wait_until("second pane spawned in first tab", |grid_snapshot| {
+ grid_snapshot.status_bar_appears() && grid_snapshot.contains("Pane #2")
+ });
+
+ zellij.send_stdin(&keys::ctrl('t'));
+ zellij.send_stdin(&keys::key('s'));
+
+ let grid_snapshot = zellij.wait_until("tab marked as syncing", |grid_snapshot| {
+ grid_snapshot.status_bar_appears() && grid_snapshot.contains("SYNC")
+ });
+ assert_snapshot!(normalized(&grid_snapshot));
+
+ zellij.send_stdin(&keys::ENTER);
+ zellij.send_stdin(b"synced-input");
+
+ let first_stdin = first_terminal.wait_for_stdin("synced input reached first pane", |stdin| {
+ stdin.windows(12).any(|window| window == b"synced-input")
+ });
+ let second_stdin = second_terminal
+ .wait_for_stdin("synced input reached second pane", |stdin| {
+ stdin.windows(12).any(|window| window == b"synced-input")
+ });
+ assert!(first_stdin
+ .windows(12)
+ .any(|window| window == b"synced-input"));
+ assert!(second_stdin
+ .windows(12)
+ .any(|window| window == b"synced-input"));
+ zellij.quit();
+}
+
+#[test]
+fn break_pane_into_new_tab() {
+ let mut zellij = start_zellij();
+ claim_first_terminal_and_wait_for_prompt(&zellij);
+
+ zellij.send_stdin(&keys::ctrl('p'));
+ zellij.send_stdin(&keys::key('n'));
+ let second_terminal = zellij.expect_pty_spawn();
+ second_terminal.output(PROMPT);
+ zellij.wait_until("second pane spawned in first tab", |grid_snapshot| {
+ grid_snapshot.status_bar_appears() && grid_snapshot.contains("Pane #2")
+ });
+
+ zellij.send_stdin(&keys::ctrl('t'));
+ zellij.send_stdin(&keys::key('b'));
+
+ let grid_snapshot = zellij.wait_until("focused pane broken into a new tab", |grid_snapshot| {
+ grid_snapshot.status_bar_appears()
+ && grid_snapshot.contains("Tab #2")
+ && grid_snapshot.contains("Pane #2")
+ && !grid_snapshot.contains("Pane #1")
+ && grid_snapshot.cursor_is_at(col(3).row(2))
+ });
+ assert_snapshot!(normalized(&grid_snapshot));
+ zellij.quit();
+}
+
+#[test]
+fn break_pane_to_the_right() {
+ let mut zellij = start_zellij();
+ let first_terminal = claim_first_terminal_and_wait_for_prompt(&zellij);
+ label_first_tab_pane(&zellij, &first_terminal, "alpha");
+
+ zellij.send_stdin(&keys::ctrl('p'));
+ zellij.send_stdin(&keys::key('n'));
+ let beta_terminal = zellij.expect_pty_spawn();
+ beta_terminal.output(PROMPT);
+ beta_terminal.output(b"beta");
+ zellij.wait_until("second pane spawned in first tab", |grid_snapshot| {
+ grid_snapshot.contains("beta") && grid_snapshot.contains("Pane #2")
+ });
+
+ open_marked_tab(&zellij, "Tab #2", "gamma");
+
+ zellij.send_stdin(&keys::ctrl('t'));
+ zellij.send_stdin(&keys::key('h'));
+ zellij.wait_until("back on the first tab with its panes", |grid_snapshot| {
+ grid_snapshot.contains("beta") && !grid_snapshot.contains("gamma")
+ });
+
+ zellij.send_stdin(&keys::key(']'));
+
+ let grid_snapshot = zellij.wait_until("beta pane moved into the right tab", |grid_snapshot| {
+ grid_snapshot.status_bar_appears()
+ && grid_snapshot.contains("beta")
+ && grid_snapshot.contains("gamma")
+ && !grid_snapshot.contains("alpha")
+ });
+ assert_snapshot!(normalized(&grid_snapshot));
+ zellij.quit();
+}
+
+#[test]
+fn break_pane_to_the_left() {
+ let mut zellij = start_zellij();
+ let first_terminal = claim_first_terminal_and_wait_for_prompt(&zellij);
+ label_first_tab_pane(&zellij, &first_terminal, "alpha");
+
+ open_marked_tab(&zellij, "Tab #2", "beta");
+
+ zellij.send_stdin(&keys::ctrl('t'));
+ zellij.send_stdin(&keys::key('['));
+
+ let grid_snapshot = zellij.wait_until("beta pane moved into the left tab", |grid_snapshot| {
+ grid_snapshot.status_bar_appears()
+ && grid_snapshot.contains("alpha")
+ && grid_snapshot.contains("beta")
+ && !grid_snapshot.contains("Tab #2")
+ });
+ assert_snapshot!(normalized(&grid_snapshot));
+ zellij.quit();
+}
+
#[test]
fn open_new_tab() {
let mut zellij = start_zellij();
@@ -64,7 +273,7 @@ fn open_new_tab() {
let grid_snapshot = zellij.wait_until("second tab steady in normal mode", |grid_snapshot| {
grid_snapshot.status_bar_appears()
&& grid_snapshot.contains("Tab #2")
- && grid_snapshot.cursor_is_at(FIRST_PANE_PROMPT_X, PROMPT_ROW)
+ && grid_snapshot.cursor_is_at(col(3).row(2))
});
assert_snapshot!(normalized(&grid_snapshot));
zellij.quit();
@@ -76,8 +285,8 @@ fn close_tab() {
claim_first_terminal_and_wait_for_prompt(&zellij);
open_new_tab_and_wait_for_prompt(&zellij, "Tab #2");
- zellij.send_stdin(&keys::TAB_MODE);
- zellij.send_stdin(&keys::CLOSE_TAB_IN_TAB_MODE);
+ zellij.send_stdin(&keys::ctrl('t'));
+ zellij.send_stdin(&keys::key('x'));
let grid_snapshot = zellij.wait_until(
"second tab closed, only first tab remains",
@@ -85,7 +294,7 @@ fn close_tab() {
grid_snapshot.status_bar_appears()
&& grid_snapshot.contains("Tab #1")
&& !grid_snapshot.contains("Tab #2")
- && grid_snapshot.cursor_is_at(FIRST_PANE_PROMPT_X, PROMPT_ROW)
+ && grid_snapshot.cursor_is_at(col(3).row(2))
},
);
assert_snapshot!(normalized(&grid_snapshot));
@@ -97,8 +306,8 @@ fn undo_rename_tab() {
let mut zellij = start_zellij();
claim_first_terminal_and_wait_for_prompt(&zellij);
- zellij.send_stdin(&keys::TAB_MODE);
- zellij.send_stdin(&keys::RENAME_TAB_MODE);
+ zellij.send_stdin(&keys::ctrl('t'));
+ zellij.send_stdin(&keys::key('r'));
zellij.send_stdin(b"aa");
zellij.send_stdin(&keys::ESC);
zellij.send_stdin(&keys::ESC);
@@ -117,7 +326,7 @@ fn move_tab_to_left() {
open_new_tab_and_wait_for_prompt(&zellij, "Tab #2");
open_new_tab_and_wait_for_prompt(&zellij, "Tab #3");
- zellij.send_stdin(&keys::MOVE_TAB_LEFT);
+ zellij.send_stdin(&keys::alt('i'));
let grid_snapshot = zellij.wait_until("third tab moved one position left", |grid_snapshot| {
tabs_in_order(grid_snapshot, &["Tab #1", "Tab #3", "Tab #2"])
@@ -133,8 +342,8 @@ fn move_tab_to_right() {
open_new_tab_and_wait_for_prompt(&zellij, "Tab #2");
open_new_tab_and_wait_for_prompt(&zellij, "Tab #3");
- zellij.send_stdin(&keys::MOVE_FOCUS_LEFT_IN_NORMAL_MODE);
- zellij.send_stdin(&keys::MOVE_TAB_RIGHT);
+ zellij.send_stdin(&keys::alt('h'));
+ zellij.send_stdin(&keys::alt('o'));
let grid_snapshot = zellij.wait_until("second tab moved one position right", |grid_snapshot| {
tabs_in_order(grid_snapshot, &["Tab #1", "Tab #3", "Tab #2"])
@@ -150,15 +359,15 @@ fn move_tab_to_left_until_it_wraps_around() {
open_new_tab_and_wait_for_prompt(&zellij, "Tab #2");
open_new_tab_and_wait_for_prompt(&zellij, "Tab #3");
- zellij.send_stdin(&keys::MOVE_TAB_LEFT);
+ zellij.send_stdin(&keys::alt('i'));
zellij.wait_until("third tab moved one position left", |grid_snapshot| {
tabs_in_order(grid_snapshot, &["Tab #1", "Tab #3", "Tab #2"])
});
- zellij.send_stdin(&keys::MOVE_TAB_LEFT);
+ zellij.send_stdin(&keys::alt('i'));
zellij.wait_until("third tab moved to the beginning", |grid_snapshot| {
tabs_in_order(grid_snapshot, &["Tab #3", "Tab #1", "Tab #2"])
});
- zellij.send_stdin(&keys::MOVE_TAB_LEFT);
+ zellij.send_stdin(&keys::alt('i'));
let grid_snapshot = zellij.wait_until("third tab wrapped to the end", |grid_snapshot| {
tabs_in_order(grid_snapshot, &["Tab #1", "Tab #2", "Tab #3"])
@@ -174,7 +383,7 @@ fn move_tab_to_right_until_it_wraps_around() {
open_new_tab_and_wait_for_prompt(&zellij, "Tab #2");
open_new_tab_and_wait_for_prompt(&zellij, "Tab #3");
- zellij.send_stdin(&keys::MOVE_TAB_RIGHT);
+ zellij.send_stdin(&keys::alt('o'));
let grid_snapshot = zellij.wait_until("third tab wrapped to the beginning", |grid_snapshot| {
tabs_in_order(grid_snapshot, &["Tab #3", "Tab #1", "Tab #2"])