feat: separate draft pull requests into their own tab

This commit is contained in:
Valdemar Lorenzen
2026-10-21 10:25:08 +02:00
parent cce3b1e469
commit 6a359cc351
9 changed files with 179 additions and 22 deletions
+80 -1
View File
@@ -445,7 +445,12 @@ fn pull_request_categories_keep_query_and_select_loaded_rows_before_completion()
let mut state = DiffState::pull_requests("origin".into());
assert_eq!(
state.categories(),
[Category::Open, Category::Closed, Category::All]
[
Category::Open,
Category::Closed,
Category::Drafts,
Category::All
]
);
state.take_request();
state.handle(Input::Char('\t'));
@@ -464,6 +469,7 @@ fn pull_request_categories_keep_query_and_select_loaded_rows_before_completion()
assert!(state.pull_request_partial());
assert_eq!(state.entries().next().unwrap().label, "#2 Fix Ünicode");
state.handle(Input::Char('\t'));
state.handle(Input::Char('\t'));
state.apply_result(
1,
Ok(pull_page(
@@ -498,6 +504,79 @@ fn pull_request_categories_keep_query_and_select_loaded_rows_before_completion()
));
}
#[test]
fn pull_request_drafts_filter_independently_and_share_open_loading_and_retry() {
let mut state = DiffState::pull_requests("origin".into());
state.take_request();
let mut page = pull_page(
false,
&[
(1, "Ready", "2024-01-01T00:00:00Z"),
(2, "Draft Ünicode", "2024-01-02T00:00:00Z"),
],
true,
);
if let Outcome::PullRequestPage {
result: Ok(page), ..
} = &mut page
{
page.entries[1].0.category = Category::Drafts;
}
state.apply_result(1, Ok(page));
state.apply_result(
1,
Ok(pull_page(
true,
&[(3, "Closed", "2024-01-03T00:00:00Z")],
false,
)),
);
let labels = |state: &DiffState| {
state
.entries()
.map(|entry| entry.label.clone())
.collect::<Vec<_>>()
};
assert_eq!(labels(&state), ["#1 Ready"]);
state.handle(Input::BackTab);
assert_eq!(
labels(&state),
["#3 Closed", "#2 Draft Ünicode", "#1 Ready"]
);
state.handle(Input::BackTab);
assert_eq!(labels(&state), ["#2 Draft Ünicode"]);
assert!(state.pull_request_partial());
state.apply_result(
1,
Ok(Outcome::PullRequestPage {
closed: false,
result: Err("open stream unavailable".into()),
}),
);
assert!(
state
.pull_request_notice()
.unwrap()
.contains("open stream unavailable")
);
state.handle(Input::Ctrl('r'));
assert_eq!(state.pull_request_retries, [1, 0]);
state.apply_result(1, Ok(pull_page(false, &[], false)));
assert!(!state.pull_request_partial());
for ch in "Ünicode Alice".chars() {
state.handle(Input::Char(ch));
}
assert_eq!(labels(&state), ["#2 Draft Ünicode"]);
state.handle(Input::Enter);
assert!(matches!(
state.take_request(),
Some(Operation::ResolvePullRequest(Revision {
target: RevisionTarget::PullRequest { number: 2, .. },
..
}))
));
}
#[test]
fn pull_request_all_streams_sort_deduplicate_and_preserve_selection_identity() {
let mut state = DiffState::pull_requests("origin".into());
+36
View File
@@ -7,6 +7,42 @@ fn parse(url: &str, provider: Option<&str>) -> Result<Forge, String> {
})
}
#[test]
fn draft_summaries_keep_open_closed_and_merged_states_distinct() {
for provider in [Provider::Github, Provider::Gitlab, Provider::Gitea] {
for (closed, merged, expected) in [
(false, false, "draft"),
(true, false, "closed"),
(true, true, "merged"),
] {
let mut value = json!({
"number": 7, "iid": 7, "title": "Unfinished work", "draft": true,
"state": if merged && provider == Provider::Gitlab { "merged" }
else if closed { "closed" }
else if provider == Provider::Gitlab { "opened" } else { "open" },
"merged": merged,
"merged_at": if merged { Some("2024-01-02T00:00:00Z") } else { None },
"user": null, "author": null, "created_at": "2024-01-01T00:00:00Z"
});
let entry = summary(provider, &value, Some((1, 2))).unwrap();
assert!(entry.draft);
assert_eq!(entry.closed, closed);
assert_eq!(
entry.description,
format!("deleted user · {expected} · +1 -2")
);
if !closed {
value["draft"] = json!(false);
assert!(!summary(provider, &value, None).unwrap().draft);
value.as_object_mut().unwrap().remove("draft");
assert!(!summary(provider, &value, None).unwrap().draft);
value["draft"] = json!("true");
assert!(summary(provider, &value, None).is_err());
}
}
}
}
#[test]
fn pull_request_sizes_include_zero_and_preserve_unknown_statistics() {
for (url, provider) in [
+10 -1
View File
@@ -3508,7 +3508,11 @@ fn pull_request_picker_renders_all_rows_without_a_pinned_offset() {
},
label: format!("#{index:02} Pull request"),
description: "open · main ← topic".into(),
category: Category::Open,
category: if index == 1 {
Category::Drafts
} else {
Category::Open
},
},
format!("2026-01-{:02}T00:00:00Z", 20 - index),
)
@@ -3548,6 +3552,11 @@ fn pull_request_picker_renders_all_rows_without_a_pinned_offset() {
);
render_diff(&mut terminal, &diff, &mut panels);
assert!(screen_text(&terminal).contains("No matching pull requests"));
diff.handle(Input::Char('\t'));
render_diff(&mut terminal, &diff, &mut panels);
let text = screen_text(&terminal);
assert!(text.contains("> #01 Pull request"));
assert!(!text.contains("#00 Pull request") && !text.contains("#19 Pull request"));
}
}