use super::*;
use crate::item_data::ItemData;
use std::time::{Duration, Instant};
fn setup(ctx: TestContext) -> TestContext {
commit(&ctx.dir, "third commit", "");
commit(&ctx.dir, "second commit", "");
commit(&ctx.dir, "first commit", "");
with_renderer(ctx)
}
#[test]
fn limit_prompt() {
snapshot!(setup(setup_clone!()), "l-n-n");
}
#[test]
fn limit_set_10() {
snapshot!(setup(setup_clone!()), "l-n-n10<enter>");
}
#[test]
fn limit_invalid() {
snapshot!(setup(setup_clone!()), "l-n-nfff<enter>");
}
#[test]
fn limit_2_commits() {
snapshot!(setup(setup_clone!()), "l-n-n2<enter>l");
}
#[test]
fn limit_2_commits_other() {
snapshot!(setup(setup_clone!()), "l-n-n2<enter>l");
}
#[test]
fn grep_prompt() {
snapshot!(setup(setup_clone!()), "l-F");
}
#[test]
fn grep_set_example() {
snapshot!(setup(setup_clone!()), "l-Fexample<enter>");
}
#[test]
fn grep_second() {
snapshot!(setup(setup_clone!()), "l-Fsecond<enter>l");
}
#[test]
fn grep_no_match() {
snapshot!(setup(setup_clone!()), "l-Fdoesntexist<enter>l");
}
#[test]
fn grep_second_other() {
snapshot!(setup(setup_clone!()), "l-Fsecond<enter>omain<enter>");
}
#[test]
fn log_other_prompt() {
snapshot!(setup(setup_clone!()), "lljlo");
}
#[test]
fn log_other() {
snapshot!(setup(setup_clone!()), "lljlo<enter>");
}
#[test]
fn log_other_input() {
snapshot!(setup(setup_clone!()), "lomain~1<enter>");
}
#[test]
fn log_other_invalid() {
snapshot!(setup(setup_clone!()), "lo <enter>");
}
#[test]
fn log_empty_branch() {
let mut ctx = setup_clone!();
run(&ctx.dir, &["rm", "-rf", ".git"]);
run(&ctx.dir, &["rm", "initial-file"]);
run(&ctx.dir, &["git", "init", "--initial-branch=main"]);
let mut app = ctx.init_app();
ctx.update(&mut app, keys("ll"));
insta::assert_snapshot!(ctx.redact_buffer());
}
pub(super) fn with_log_args(mut ctx: TestContext, args: &[&str]) -> TestContext {
ctx.config().general.log_args = args.iter().map(|arg| arg.to_string()).collect();
ctx
}
pub(super) const MULTI_ROW_LOG_ARGS: &[&str] = &[
"--date=short",
"--format=%n\u{25b8} %h %an %ad%C(auto)%d%C(reset)%n %s",
];
fn git_command_of(app: &crate::app::App) -> Option<String> {
app.screen()
.git_command
.as_ref()
.map(|git| git.borrow().line(None))
}
#[test]
fn the_log_is_the_output_of_git_log() {
let mut ctx = setup(setup_clone!());
let mut app = ctx.init_app();
ctx.update(&mut app, keys("l-Fsecond<enter>l"));
assert_eq!(
git_command_of(&app),
Some("git log -n256 --extended-regexp --grep=second".into())
);
}
#[test]
fn log_args_are_given_to_git_log() {
let mut ctx = with_log_args(setup(setup_clone!()), &["--format=logged %s"]);
let mut app = ctx.init_app();
ctx.update(&mut app, keys("ll"));
let buffer = ctx.redact_buffer();
assert!(buffer.contains("logged add second commit"), "{buffer}");
}
#[test]
fn the_log_is_drawn_before_the_renderer_finishes() {
let mut ctx = setup_clone!();
for i in 1..=40 {
commit(&ctx.dir, &format!("file-{i:02}"), "");
}
ctx.config().general.diff_renderer.command = super::pager::pausing_renderer();
let started = Instant::now();
let mut app = ctx.init_app_streaming_with_args(Default::default());
ctx.update(&mut app, keys("ll"));
assert!(
started.elapsed() < Duration::from_secs(2),
"{:?}",
started.elapsed()
);
assert!(ctx.redact_buffer().contains("add file-40"));
}
#[test]
fn log_move_down_steps_a_whole_commit() {
snapshot!(
with_log_args(setup(setup_clone!()), MULTI_ROW_LOG_ARGS),
"lljj"
);
}
#[test]
fn log_move_next_section_steps_a_whole_commit() {
snapshot!(
with_log_args(setup(setup_clone!()), MULTI_ROW_LOG_ARGS),
"ll<alt+j><alt+j>"
);
}
#[test]
fn log_move_prev_section_steps_a_whole_commit() {
snapshot!(
with_log_args(setup(setup_clone!()), MULTI_ROW_LOG_ARGS),
"lljj<alt+k>"
);
}
#[test]
fn log_fold_hides_the_commit_rows() {
snapshot!(
with_log_args(setup(setup_clone!()), MULTI_ROW_LOG_ARGS),
"ll<tab>"
);
}
#[test]
fn log_show_acts_on_the_selected_commit() {
snapshot!(setup(setup_clone!()), "llj<enter>");
}
#[test]
fn log_copy_hash_of_the_selected_commit() {
snapshot!(setup(setup_clone!()), "lljy");
}
#[test]
fn log_with_stat() {
snapshot!(
with_log_args(
setup(setup_clone!()),
&["--stat", "--format=\u{25b8} %h %s"]
),
"llj"
);
}
#[test]
fn log_divider_is_not_the_cursor_line() {
let mut ctx = with_log_args(setup(setup_clone!()), &["--format=\u{25b8} %h %s"]);
ctx.config().general.diff_renderer.command = [
"awk",
r#"/^▸ /{printf "\033]1717;1;C;;;%s\033\\\n─────\n", $2} {print}"#,
]
.map(String::from)
.to_vec();
snapshot!(ctx, "llj");
}
#[test]
fn log_keeps_the_first_commit_when_the_renderer_greets_on_its_line() {
let mut ctx = with_log_args(setup(setup_clone!()), &["--format=\u{25b8} %h %s"]);
ctx.config().general.diff_renderer.command = [
"awk",
r#"BEGIN{printf "\033]1717;1\033\\"} /^▸ /{printf "\033]1717;1;C;;;%s\033\\", $2} {print}"#,
]
.map(String::from)
.to_vec();
let mut app = ctx.init_app();
ctx.update(&mut app, keys("ll"));
insta::assert_snapshot!(ctx.redact_buffer());
}
#[test]
fn paging_lands_on_a_commit_of_the_new_page() {
let mut ctx = with_renderer(setup_clone!());
for i in 1..=40 {
commit(&ctx.dir, &format!("file-{i:02}"), "");
}
let mut app = ctx.init_app();
ctx.update(&mut app, keys("ll"));
let top = selected_summary(&app);
ctx.update(&mut app, keys("<pagedown>"));
let paged = selected_summary(&app);
assert_ne!(top, paged, "the cursor stayed on the first commit");
assert!(
ctx.redact_buffer().contains(&paged),
"selected {paged} is off screen:\n{}",
ctx.redact_buffer()
);
ctx.update(&mut app, keys("<pageup>"));
assert_eq!(top, selected_summary(&app), "paging back missed the commit");
}
fn selected_summary(app: &crate::app::App) -> String {
match &app.state.screens.last().unwrap().get_selected_item().data {
ItemData::Commit { summary, .. } => summary.clone(),
data => panic!("not a commit: {data:?}"),
}
}