quinjet 0.0.40

A fast, live, keyboard-first Git source-control interface for the terminal
use super::*;

#[test]
fn pull_request_files_create_pending_line_reviews() {
    let mut app = App::new("/tmp/repo", "repo");
    let now = Instant::now();
    app.view = View::PullRequests;
    app.focus = Focus::Content;
    app.pull_request = Some(pull_request(12, "Review me", "acme/widget"));
    app.pull_request_section = PullRequestSection::Files;
    app.pull_request_file_view = PullRequestFileView::SingleFile;
    app.pull_request_single_file = Some(PathBuf::from("src/lib.rs"));
    app.document = crate::git::diff::parse_diff(
        b"diff --git a/src/lib.rs b/src/lib.rs\n--- a/src/lib.rs\n+++ b/src/lib.rs\n@@ -1 +1 @@\n-old\n+new\n",
        "src/lib.rs",
        Some(Path::new("src/lib.rs")),
        false,
    );

    app.handle_key(KeyEvent::new(KeyCode::Char('j'), KeyModifiers::NONE), now);
    app.handle_key(KeyEvent::new(KeyCode::Char('c'), KeyModifiers::NONE), now);
    assert!(matches!(
        app.modal,
        Some(Modal::PullRequestReviewComment { .. })
    ));
    app.handle_paste("Use the shared parser here");
    let effects = app.handle_key(KeyEvent::new(KeyCode::Enter, KeyModifiers::CONTROL), now);

    assert!(matches!(
        effects.as_slice(),
        [AppEffect::Git(command)] if matches!(
            command.as_ref(),
            WorkerCommand::OperatePullRequestReview {
                operation: PullRequestReviewOperation::AddThread {
                    path,
                    line: Some(1),
                    side: Some(PullRequestReviewSide::Left),
                    body,
                    ..
                },
                ..
            } if path == Path::new("src/lib.rs") && body == "Use the shared parser here"
        )
    ));
}

#[test]
fn pull_request_review_threads_render_below_their_diff_line() {
    let mut app = App::new("/tmp/repo", "repo");
    app.pull_request_file_view = PullRequestFileView::SingleFile;
    app.pull_request_single_file = Some(PathBuf::from("src/lib.rs"));
    app.document = crate::git::diff::parse_diff(
        b"diff --git a/src/lib.rs b/src/lib.rs\n--- a/src/lib.rs\n+++ b/src/lib.rs\n@@ -0,0 +1 @@\n+new\n",
        "src/lib.rs",
        Some(Path::new("src/lib.rs")),
        false,
    );
    app.pull_request_review_generation = 4;

    app.handle_worker_event(
        WorkerEvent::PullRequestReview {
            generation: 4,
            result: Ok(PullRequestReviewSnapshot {
                pull_request_id: "PR_1".to_owned(),
                head_oid: "head".to_owned(),
                review_decision: None,
                pending_review: None,
                threads: vec![PullRequestReviewThread {
                    id: "thread-1".to_owned(),
                    path: PathBuf::from("src/lib.rs"),
                    side: PullRequestReviewSide::Right,
                    line: Some(1),
                    original_line: Some(1),
                    start_side: None,
                    start_line: None,
                    original_start_line: None,
                    subject: PullRequestReviewThreadSubject::Line,
                    is_resolved: false,
                    is_outdated: false,
                    resolved_by: None,
                    viewer_can_reply: true,
                    viewer_can_resolve: true,
                    viewer_can_unresolve: false,
                    comments: Vec::new(),
                    comments_truncated: false,
                }],
                truncated: false,
            }),
        },
        Instant::now(),
    );

    let added = app
        .document
        .lines
        .iter()
        .position(|line| line.kind == DiffLineKind::Added);
    let review = app
        .document
        .lines
        .iter()
        .position(|line| line.kind == DiffLineKind::Review);
    assert_eq!(review, added.map(|index| index + 1));
}

#[test]
fn clicking_a_review_thread_opens_permission_aware_actions() {
    use ratatui::Terminal;
    use ratatui::backend::TestBackend;

    let mut app = App::new("/tmp/repo", "repo");
    app.view = View::PullRequests;
    app.focus = Focus::Content;
    app.pull_request_section = PullRequestSection::Files;
    app.pull_request_file_view = PullRequestFileView::SingleFile;
    app.pull_request_single_file = Some(PathBuf::from("src/lib.rs"));
    app.document = crate::git::diff::parse_diff(
        b"diff --git a/src/lib.rs b/src/lib.rs\n--- a/src/lib.rs\n+++ b/src/lib.rs\n@@ -0,0 +1 @@\n+new\n",
        "src/lib.rs",
        Some(Path::new("src/lib.rs")),
        false,
    );
    app.pull_request_review = PullRequestReviewSnapshot {
        pull_request_id: "PR_1".to_owned(),
        head_oid: "head".to_owned(),
        review_decision: None,
        pending_review: None,
        threads: vec![PullRequestReviewThread {
            id: "thread-1".to_owned(),
            path: PathBuf::from("src/lib.rs"),
            side: PullRequestReviewSide::Right,
            line: Some(1),
            original_line: Some(1),
            start_side: None,
            start_line: None,
            original_start_line: None,
            subject: PullRequestReviewThreadSubject::Line,
            is_resolved: false,
            is_outdated: false,
            resolved_by: None,
            viewer_can_reply: true,
            viewer_can_resolve: true,
            viewer_can_unresolve: false,
            comments: vec![crate::git::github::PullRequestReviewComment {
                id: "comment-1".to_owned(),
                author: "reviewer".to_owned(),
                body: "Use the shared parser".to_owned(),
                created_at: String::new(),
                updated_at: String::new(),
                url: "https://github.com/acme/widget/pull/1#discussion_r1".to_owned(),
                state: "PENDING".to_owned(),
                viewer_did_author: true,
                viewer_can_update: true,
                viewer_can_delete: true,
            }],
            comments_truncated: false,
        }],
        truncated: false,
    };
    app.decorate_pull_request_review();
    let mut terminal = Terminal::new(TestBackend::new(140, 32)).unwrap();
    terminal
        .draw(|frame| crate::ui::draw(frame, &mut app, &Theme::default()))
        .unwrap();
    let hit = app.geometry.content_review_hits.first().unwrap().clone();
    app.handle_mouse(
        MouseEvent {
            kind: MouseEventKind::Down(MouseButton::Left),
            column: hit.area.x,
            row: hit.area.y,
            modifiers: KeyModifiers::NONE,
        },
        Instant::now(),
    );

    let Some(Modal::PullRequestReviewThreadActions { items, .. }) = app.modal else {
        panic!("review actions did not open");
    };
    assert!(matches!(
        items.as_slice(),
        [
            PullRequestReviewThreadAction::Reply { .. },
            PullRequestReviewThreadAction::CopyComment { .. },
            PullRequestReviewThreadAction::OpenComment { .. },
            PullRequestReviewThreadAction::EditComment { .. },
            PullRequestReviewThreadAction::DeleteComment { .. },
            PullRequestReviewThreadAction::Resolve { .. }
        ]
    ));
}

#[test]
fn editing_a_review_thread_action_updates_the_comment() {
    let mut app = App::new("/tmp/repo", "repo");
    app.pull_request = Some(pull_request(12, "Review me", "acme/widget"));
    app.modal = Some(Modal::PullRequestReviewThreadActions {
        items: vec![PullRequestReviewThreadAction::EditComment {
            comment_id: "comment-1".to_owned(),
            body: "Original".to_owned(),
        }],
        selected: 0,
    });
    app.handle_key(
        KeyEvent::new(KeyCode::Enter, KeyModifiers::NONE),
        Instant::now(),
    );
    let Some(Modal::PullRequestReviewComment { input, .. }) = app.modal.as_mut() else {
        panic!("review editor did not open");
    };
    input.value = "Updated".to_owned();
    input.cursor = input.value.len();
    let effects = app.handle_key(
        KeyEvent::new(KeyCode::Enter, KeyModifiers::CONTROL),
        Instant::now(),
    );

    assert!(matches!(
        effects.as_slice(),
        [AppEffect::Git(command)] if matches!(
            command.as_ref(),
            WorkerCommand::OperatePullRequestReview {
                operation: PullRequestReviewOperation::UpdateComment {
                    comment_id,
                    body,
                },
                ..
            } if comment_id == "comment-1" && body == "Updated"
        )
    ));
}