use escriba_buffer::BufferSet;
use escriba_core::Mode;
use escriba_keymap::Key;
use escriba_runtime::EditorState;
const CONFLICTED: &str = "\
fn a() {}
<<<<<<< HEAD
let x = 1;
=======
let x = 2;
>>>>>>> branch
fn b() {}
";
fn editor() -> EditorState {
let mut bufs = BufferSet::new();
let id = bufs.scratch(CONFLICTED);
let mut st = EditorState::new_with_buffer(bufs, id);
st.dismiss_splash();
st
}
fn ex(st: &mut EditorState, line: &str) {
st.on_key(&Key::Char(':'));
for c in line.chars() {
st.on_key(&Key::Char(c));
}
st.on_key(&Key::Enter);
if st.modal.mode() == Mode::Command {
st.on_key(&Key::Esc);
}
}
fn text(st: &EditorState) -> String {
st.buffers
.get(st.active)
.map(|b| b.to_string())
.unwrap_or_default()
}
#[test]
fn walking_lands_on_the_conflict() {
let mut st = editor();
ex(&mut st, "conflict.next");
assert_eq!(st.cursor().line, 1, "the <<<<<<< line");
}
#[test]
fn choosing_ours_removes_the_markers_and_keeps_the_first_half() {
let mut st = editor();
for _ in 0..3 {
st.on_key(&Key::Char('j'));
}
ex(&mut st, "conflict.choose-ours");
let t = text(&st);
assert!(t.contains("let x = 1;"), "ours survives: {t:?}");
assert!(!t.contains("let x = 2;"), "theirs is gone: {t:?}");
assert!(
!t.contains("<<<<<<<") && !t.contains("=======") && !t.contains(">>>>>>>"),
"no markers left: {t:?}"
);
assert!(
t.contains("fn a() {}") && t.contains("fn b() {}"),
"context intact: {t:?}"
);
}
#[test]
fn choosing_theirs_keeps_the_second_half() {
let mut st = editor();
for _ in 0..3 {
st.on_key(&Key::Char('j'));
}
ex(&mut st, "conflict.choose-theirs");
let t = text(&st);
assert!(
t.contains("let x = 2;") && !t.contains("let x = 1;"),
"{t:?}"
);
}
#[test]
fn choosing_both_keeps_ours_then_theirs() {
let mut st = editor();
for _ in 0..3 {
st.on_key(&Key::Char('j'));
}
ex(&mut st, "conflict.choose-both");
let t = text(&st);
let ours = t.find("let x = 1;").expect("ours");
let theirs = t.find("let x = 2;").expect("theirs");
assert!(
ours < theirs,
"ours comes first, as it does in the file: {t:?}"
);
assert!(!t.contains("======="), "{t:?}");
}
#[test]
fn resolving_outside_a_conflict_declines_rather_than_editing() {
let mut st = editor();
let before = text(&st);
ex(&mut st, "conflict.choose-ours");
assert_eq!(text(&st), before, "the buffer is untouched");
assert!(
st.messages.iter().any(|m| m.contains("not inside")),
"and it says why: {:?}",
st.messages,
);
}
#[test]
fn a_clean_buffer_reports_no_conflicts() {
let mut bufs = BufferSet::new();
let id = bufs.scratch("fn a() {}\n");
let mut st = EditorState::new_with_buffer(bufs, id);
st.dismiss_splash();
ex(&mut st, "conflict.next");
assert!(
st.messages.iter().any(|m| m.contains("no merge conflicts")),
"{:?}",
st.messages,
);
}
#[test]
fn resolving_is_undoable() {
let mut st = editor();
let before = text(&st);
for _ in 0..3 {
st.on_key(&Key::Char('j'));
}
ex(&mut st, "conflict.choose-ours");
assert_ne!(text(&st), before);
st.on_key(&Key::Char('u'));
assert_eq!(text(&st), before, "one undo restores the conflict");
}