aboutsummaryrefslogtreecommitdiffstats
path: root/zellij-integration-tests/src
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 /zellij-integration-tests/src
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
Diffstat (limited to 'zellij-integration-tests/src')
-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
5 files changed, 102 insertions, 60 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()