diff options
| author | Aram Drevekenin <aram@poor.dev> | 2021-12-20 17:31:07 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-12-20 17:31:07 +0100 |
| commit | ca8438b0aa0e5e9aa1c69f0217c275d20f269a8f (patch) | |
| tree | 97ee4922eb41415d54d3cb183ad9dbef66bd7186 /src | |
| parent | chore(release): v0.22.1 (diff) | |
| download | sl0ppij-ca8438b0aa0e5e9aa1c69f0217c275d20f269a8f.tar.gz sl0ppij-ca8438b0aa0e5e9aa1c69f0217c275d20f269a8f.tar.xz sl0ppij-ca8438b0aa0e5e9aa1c69f0217c275d20f269a8f.zip | |
feat(collaboration): implement multiple users (#957)
* work
* feat(collaboration): implement multiple users
* style(cleanup): some leftovers
Diffstat (limited to 'src')
8 files changed, 524 insertions, 21 deletions
diff --git a/src/tests/e2e/cases.rs b/src/tests/e2e/cases.rs index 0585c373..1457c0a9 100644 --- a/src/tests/e2e/cases.rs +++ b/src/tests/e2e/cases.rs @@ -178,7 +178,6 @@ pub fn cannot_split_terminals_vertically_when_active_terminal_is_too_small() { name: "Make sure only one pane appears", instruction: |remote_terminal: RemoteTerminal| -> bool { let mut step_is_complete = false; - // if remote_terminal.cursor_position_is(3, 2) && remote_terminal.snapshot_contains("...") if remote_terminal.cursor_position_is(3, 2) { // ... is the truncated tip line step_is_complete = true; @@ -928,7 +927,7 @@ pub fn detach_and_attach_session() { let last_snapshot = loop { RemoteRunner::kill_running_sessions(fake_win_size); drop(()); - let mut runner = RemoteRunner::new(fake_win_size) + let mut runner = RemoteRunner::new_mirrored_session(fake_win_size) .add_step(Step { name: "Split pane to the right", instruction: |mut remote_terminal: RemoteTerminal| -> bool { @@ -1261,20 +1260,21 @@ pub fn mirrored_sessions() { // then make sure they were also reflected (mirrored) in the first runner afterwards RemoteRunner::kill_running_sessions(fake_win_size); drop(()); - let mut first_runner = RemoteRunner::new_with_session_name(fake_win_size, session_name) - .dont_panic() - .add_step(Step { - name: "Wait for app to load", - instruction: |mut remote_terminal: RemoteTerminal| -> bool { - let mut step_is_complete = false; - if remote_terminal.status_bar_appears() - && remote_terminal.cursor_position_is(3, 2) - { - step_is_complete = true; - } - step_is_complete - }, - }); + let mut first_runner = + RemoteRunner::new_with_session_name(fake_win_size, session_name, true) + .dont_panic() + .add_step(Step { + name: "Wait for app to load", + instruction: |mut remote_terminal: RemoteTerminal| -> bool { + let mut step_is_complete = false; + if remote_terminal.status_bar_appears() + && remote_terminal.cursor_position_is(3, 2) + { + step_is_complete = true; + } + step_is_complete + }, + }); first_runner.run_all_steps(); let mut second_runner = RemoteRunner::new_existing_session(fake_win_size, session_name) @@ -1398,6 +1398,286 @@ pub fn mirrored_sessions() { #[test] #[ignore] +pub fn multiple_users_in_same_pane_and_tab() { + let fake_win_size = Size { + cols: 120, + rows: 24, + }; + let mut test_attempts = 10; + let session_name = "multiple_users_in_same_pane_and_tab"; + let (first_runner_snapshot, second_runner_snapshot) = loop { + // here we connect with one runner, then connect with another, perform some actions and + // then make sure they were also reflected (mirrored) in the first runner afterwards + RemoteRunner::kill_running_sessions(fake_win_size); + drop(()); + let mut first_runner = + RemoteRunner::new_with_session_name(fake_win_size, session_name, false) + .dont_panic() + .add_step(Step { + name: "Wait for app to load", + instruction: |mut remote_terminal: RemoteTerminal| -> bool { + let mut step_is_complete = false; + if remote_terminal.status_bar_appears() + && remote_terminal.cursor_position_is(3, 2) + { + step_is_complete = true; + } + step_is_complete + }, + }); + first_runner.run_all_steps(); + + let mut second_runner = RemoteRunner::new_existing_session(fake_win_size, session_name) + .dont_panic() + .add_step(Step { + name: "Wait for app to load", + instruction: |mut remote_terminal: RemoteTerminal| -> bool { + let mut step_is_complete = false; + if remote_terminal.status_bar_appears() + && remote_terminal.cursor_position_is(3, 2) + { + step_is_complete = true; + } + step_is_complete + }, + }); + second_runner.run_all_steps(); + + if first_runner.test_timed_out || second_runner.test_timed_out { + test_attempts -= 1; + continue; + } + let second_runner_snapshot = second_runner.take_snapshot_after(Step { + name: "take snapshot after", + instruction: |remote_terminal: RemoteTerminal| -> bool { + let mut step_is_complete = false; + if remote_terminal.cursor_position_is(3, 2) + && remote_terminal.snapshot_contains("MY FOCUS") + { + // cursor is back in the first tab + step_is_complete = true; + } + step_is_complete + }, + }); + let first_runner_snapshot = first_runner.take_snapshot_after(Step { + name: "take snapshot after", + instruction: |remote_terminal: RemoteTerminal| -> bool { + let mut step_is_complete = false; + if remote_terminal.cursor_position_is(3, 2) + && remote_terminal.snapshot_contains("MY FOCUS") + { + // cursor is back in the first tab + step_is_complete = true; + } + step_is_complete + }, + }); + + if (first_runner.test_timed_out || second_runner.test_timed_out) && test_attempts >= 0 { + test_attempts -= 1; + continue; + } else { + break (first_runner_snapshot, second_runner_snapshot); + } + }; + assert_snapshot!(first_runner_snapshot); + assert_snapshot!(second_runner_snapshot); +} + +#[test] +#[ignore] +pub fn multiple_users_in_different_panes_and_same_tab() { + let fake_win_size = Size { + cols: 120, + rows: 24, + }; + let mut test_attempts = 10; + let session_name = "multiple_users_in_same_pane_and_tab"; + let (first_runner_snapshot, second_runner_snapshot) = loop { + // here we connect with one runner, then connect with another, perform some actions and + // then make sure they were also reflected (mirrored) in the first runner afterwards + RemoteRunner::kill_running_sessions(fake_win_size); + drop(()); + let mut first_runner = + RemoteRunner::new_with_session_name(fake_win_size, session_name, false) + .dont_panic() + .add_step(Step { + name: "Wait for app to load", + instruction: |mut remote_terminal: RemoteTerminal| -> bool { + let mut step_is_complete = false; + if remote_terminal.status_bar_appears() + && remote_terminal.cursor_position_is(3, 2) + { + step_is_complete = true; + } + step_is_complete + }, + }); + first_runner.run_all_steps(); + + let mut second_runner = RemoteRunner::new_existing_session(fake_win_size, session_name) + .dont_panic() + .add_step(Step { + name: "Split pane to the right", + instruction: |mut remote_terminal: RemoteTerminal| -> bool { + let mut step_is_complete = false; + if remote_terminal.status_bar_appears() + && remote_terminal.cursor_position_is(3, 2) + { + remote_terminal.send_key(&PANE_MODE); + remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE); + // back to normal mode after split + remote_terminal.send_key(&ENTER); + step_is_complete = true; + } + step_is_complete + }, + }); + second_runner.run_all_steps(); + + if first_runner.test_timed_out || second_runner.test_timed_out { + test_attempts -= 1; + continue; + } + + let second_runner_snapshot = second_runner.take_snapshot_after(Step { + name: "take snapshot after", + instruction: |remote_terminal: RemoteTerminal| -> bool { + let mut step_is_complete = false; + if remote_terminal.cursor_position_is(63, 2) && remote_terminal.tip_appears() { + // cursor is in the newly opened second pane + step_is_complete = true; + } + step_is_complete + }, + }); + + let first_runner_snapshot = first_runner.take_snapshot_after(Step { + name: "take snapshot after", + instruction: |remote_terminal: RemoteTerminal| -> bool { + let mut step_is_complete = false; + if remote_terminal.cursor_position_is(3, 2) + && remote_terminal.snapshot_contains("││$") + { + // cursor is back in the first tab + step_is_complete = true; + } + step_is_complete + }, + }); + + if (first_runner.test_timed_out || second_runner.test_timed_out) && test_attempts >= 0 { + test_attempts -= 1; + continue; + } else { + break (first_runner_snapshot, second_runner_snapshot); + } + }; + assert_snapshot!(first_runner_snapshot); + assert_snapshot!(second_runner_snapshot); +} + +#[test] +#[ignore] +pub fn multiple_users_in_different_tabs() { + let fake_win_size = Size { + cols: 120, + rows: 24, + }; + let mut test_attempts = 10; + let session_name = "multiple_users_in_different_tabs"; + let (first_runner_snapshot, second_runner_snapshot) = loop { + // here we connect with one runner, then connect with another, perform some actions and + // then make sure they were also reflected (mirrored) in the first runner afterwards + RemoteRunner::kill_running_sessions(fake_win_size); + drop(()); + let mut first_runner = + RemoteRunner::new_with_session_name(fake_win_size, session_name, false) + .dont_panic() + .add_step(Step { + name: "Wait for app to load", + instruction: |mut remote_terminal: RemoteTerminal| -> bool { + let mut step_is_complete = false; + if remote_terminal.status_bar_appears() + && remote_terminal.cursor_position_is(3, 2) + { + step_is_complete = true; + } + step_is_complete + }, + }); + first_runner.run_all_steps(); + + let mut second_runner = RemoteRunner::new_existing_session(fake_win_size, session_name) + .dont_panic() + .add_step(Step { + name: "Open new tab", + instruction: |mut remote_terminal: RemoteTerminal| -> bool { + let mut step_is_complete = false; + if remote_terminal.cursor_position_is(3, 2) && remote_terminal.tip_appears() { + // cursor is in the newly opened second pane + remote_terminal.send_key(&TAB_MODE); + remote_terminal.send_key(&NEW_TAB_IN_TAB_MODE); + // back to normal mode after split + remote_terminal.send_key(&ENTER); + step_is_complete = true; + } + step_is_complete + }, + }); + second_runner.run_all_steps(); + + if first_runner.test_timed_out || second_runner.test_timed_out { + test_attempts -= 1; + continue; + } + + let second_runner_snapshot = second_runner.take_snapshot_after(Step { + name: "Wait for new tab to open", + instruction: |remote_terminal: RemoteTerminal| -> bool { + let mut step_is_complete = false; + if remote_terminal.cursor_position_is(3, 2) + && remote_terminal.tip_appears() + && remote_terminal.snapshot_contains("Tab #2") + && remote_terminal.status_bar_appears() + { + // cursor is in the newly opened second tab + step_is_complete = true; + } + step_is_complete + }, + }); + + let first_runner_snapshot = first_runner.take_snapshot_after(Step { + name: "Wait for new tab to open", + instruction: |remote_terminal: RemoteTerminal| -> bool { + let mut step_is_complete = false; + if remote_terminal.cursor_position_is(3, 2) + && remote_terminal.tip_appears() + && remote_terminal.snapshot_contains("Tab #2") + && remote_terminal.status_bar_appears() + { + // cursor is in the newly opened second tab + step_is_complete = true; + } + step_is_complete + }, + }); + + if (first_runner.test_timed_out || second_runner.test_timed_out) && test_attempts >= 0 { + test_attempts -= 1; + continue; + } else { + break (first_runner_snapshot, second_runner_snapshot); + } + }; + assert_snapshot!(first_runner_snapshot); + assert_snapshot!(second_runner_snapshot); +} + +#[test] +#[ignore] pub fn bracketed_paste() { let fake_win_size = Size { cols: 120, diff --git a/src/tests/e2e/remote_runner.rs b/src/tests/e2e/remote_runner.rs index 205710f1..feaeecda 100644 --- a/src/tests/e2e/remote_runner.rs +++ b/src/tests/e2e/remote_runner.rs @@ -67,13 +67,27 @@ fn start_zellij(channel: &mut ssh2::Channel) { channel.flush().unwrap(); } -fn start_zellij_in_session(channel: &mut ssh2::Channel, session_name: &str) { +fn start_zellij_mirrored_session(channel: &mut ssh2::Channel) { stop_zellij(channel); channel .write_all( format!( - "{} --session {}\n", - ZELLIJ_EXECUTABLE_LOCATION, session_name + "{} --session {} options --mirror-session true\n", + ZELLIJ_EXECUTABLE_LOCATION, SESSION_NAME + ) + .as_bytes(), + ) + .unwrap(); + channel.flush().unwrap(); +} + +fn start_zellij_in_session(channel: &mut ssh2::Channel, session_name: &str, mirrored: bool) { + stop_zellij(channel); + channel + .write_all( + format!( + "{} --session {} options --mirror-session {}\n", + ZELLIJ_EXECUTABLE_LOCATION, session_name, mirrored ) .as_bytes(), ) @@ -333,13 +347,48 @@ impl RemoteRunner { reader_thread, } } + pub fn new_mirrored_session(win_size: Size) -> Self { + let sess = ssh_connect(); + let mut channel = sess.channel_session().unwrap(); + let mut rows = Dimension::fixed(win_size.rows); + let mut cols = Dimension::fixed(win_size.cols); + rows.set_inner(win_size.rows); + cols.set_inner(win_size.cols); + let pane_geom = PaneGeom { + x: 0, + y: 0, + rows, + cols, + }; + setup_remote_environment(&mut channel, win_size); + start_zellij_mirrored_session(&mut channel); + let channel = Arc::new(Mutex::new(channel)); + let last_snapshot = Arc::new(Mutex::new(String::new())); + let cursor_coordinates = Arc::new(Mutex::new((0, 0))); + sess.set_blocking(false); + let reader_thread = + read_from_channel(&channel, &last_snapshot, &cursor_coordinates, &pane_geom); + RemoteRunner { + steps: vec![], + channel, + currently_running_step: None, + current_step_index: 0, + retries_left: RETRIES, + retry_pause_ms: 100, + test_timed_out: false, + panic_on_no_retries_left: true, + last_snapshot, + cursor_coordinates, + reader_thread, + } + } pub fn kill_running_sessions(win_size: Size) { let sess = ssh_connect(); let mut channel = sess.channel_session().unwrap(); setup_remote_environment(&mut channel, win_size); start_zellij(&mut channel); } - pub fn new_with_session_name(win_size: Size, session_name: &str) -> Self { + pub fn new_with_session_name(win_size: Size, session_name: &str, mirrored: bool) -> Self { // notice that this method does not have a timeout, so use with caution! let sess = ssh_connect_without_timeout(); let mut channel = sess.channel_session().unwrap(); @@ -354,7 +403,7 @@ impl RemoteRunner { cols, }; setup_remote_environment(&mut channel, win_size); - start_zellij_in_session(&mut channel, session_name); + start_zellij_in_session(&mut channel, session_name, mirrored); let channel = Arc::new(Mutex::new(channel)); let last_snapshot = Arc::new(Mutex::new(String::new())); let cursor_coordinates = Arc::new(Mutex::new((0, 0))); diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_different_panes_and_same_tab-2.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_different_panes_and_same_tab-2.snap new file mode 100644 index 00000000..5868eb44 --- /dev/null +++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_different_panes_and_same_tab-2.snap @@ -0,0 +1,29 @@ +--- +source: src/tests/e2e/cases.rs +expression: second_runner_snapshot + +--- + Zellij (multiple_users_in_same_pane_and_tab) Tab #1 [ ] +┌ Pane #1 ───────────┤ FOCUSED USER: ├───────────────────┐┌ Pane #2 ──────────────┤ MY FOCUS ├───────────────────────┐ +│$ ││$ █ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +└──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘ + Ctrl + <g> LOCK <p> PANE <t> TAB <n> RESIZE <h> MOVE <s> SCROLL <o> SESSION <q> QUIT + Tip: Alt + <n> => new pane. Alt + <[] or hjkl> => navigate. Alt + <+-> => resize pane. diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_different_panes_and_same_tab.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_different_panes_and_same_tab.snap new file mode 100644 index 00000000..e9355a44 --- /dev/null +++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_different_panes_and_same_tab.snap @@ -0,0 +1,29 @@ +--- +source: src/tests/e2e/cases.rs +expression: first_runner_snapshot + +--- + Zellij (multiple_users_in_same_pane_and_tab) Tab #1 [ ] +┌ Pane #1 ──────────────┤ MY FOCUS ├───────────────────────┐┌ Pane #2 ───────────┤ FOCUSED USER: ├───────────────────┐ +│$ █ ││$ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +└──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘ + Ctrl + <g> LOCK <p> PANE <t> TAB <n> RESIZE <h> MOVE <s> SCROLL <o> SESSION <q> QUIT + Tip: Alt + <n> => new pane. Alt + <[] or hjkl> => navigate. Alt + <+-> => resize pane. diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_different_tabs-2.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_different_tabs-2.snap new file mode 100644 index 00000000..7b983dfd --- /dev/null +++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_different_tabs-2.snap @@ -0,0 +1,29 @@ +--- +source: src/tests/e2e/cases.rs +expression: second_runner_snapshot + +--- + Zellij (multiple_users_in_different_tabs) Tab #1 [ ] Tab #2 +┌ Pane #1 ────────────────────────────────────────────┤ MY FOCUS ├─────────────────────────────────────────────────────┐ +│$ █ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + Ctrl + <g> LOCK <p> PANE <t> TAB <n> RESIZE <h> MOVE <s> SCROLL <o> SESSION <q> QUIT + Tip: Alt + <n> => new pane. Alt + <[] or hjkl> => navigate. Alt + <+-> => resize pane. diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_different_tabs.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_different_tabs.snap new file mode 100644 index 00000000..4a1926d6 --- /dev/null +++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_different_tabs.snap @@ -0,0 +1,29 @@ +--- +source: src/tests/e2e/cases.rs +expression: first_runner_snapshot + +--- + Zellij (multiple_users_in_different_tabs) Tab #1 Tab #2 [ ] +┌ Pane #1 ────────────────────────────────────────────┤ MY FOCUS ├─────────────────────────────────────────────────────┐ +│$ █ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + Ctrl + <g> LOCK <p> PANE <t> TAB <n> RESIZE <h> MOVE <s> SCROLL <o> SESSION <q> QUIT + Tip: Alt + <n> => new pane. Alt + <[] or hjkl> => navigate. Alt + <+-> => resize pane. diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_same_pane_and_tab-2.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_same_pane_and_tab-2.snap new file mode 100644 index 00000000..92d189c4 --- /dev/null +++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_same_pane_and_tab-2.snap @@ -0,0 +1,29 @@ +--- +source: src/tests/e2e/cases.rs +expression: second_runner_snapshot + +--- + Zellij (multiple_users_in_same_pane_and_tab) Tab #1 [ ] +┌ Pane #1 ─────────────────────────────────────────┤ MY FOCUS AND: ├─────────────────────────────────────────────────┐ +│$ █ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + Ctrl + <g> LOCK <p> PANE <t> TAB <n> RESIZE <h> MOVE <s> SCROLL <o> SESSION <q> QUIT + Tip: Alt + <n> => new pane. Alt + <[] or hjkl> => navigate. Alt + <+-> => resize pane. diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_same_pane_and_tab.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_same_pane_and_tab.snap new file mode 100644 index 00000000..9944ee81 --- /dev/null +++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__multiple_users_in_same_pane_and_tab.snap @@ -0,0 +1,29 @@ +--- +source: src/tests/e2e/cases.rs +expression: first_runner_snapshot + +--- + Zellij (multiple_users_in_same_pane_and_tab) Tab #1 [ ] +┌ Pane #1 ─────────────────────────────────────────┤ MY FOCUS AND: ├─────────────────────────────────────────────────┐ +│$ █ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + Ctrl + <g> LOCK <p> PANE <t> TAB <n> RESIZE <h> MOVE <s> SCROLL <o> SESSION <q> QUIT + Tip: Alt + <n> => new pane. Alt + <[] or hjkl> => navigate. Alt + <+-> => resize pane. |
