#[allow(clippy::wildcard_imports)]
use super::super::*;
#[allow(clippy::wildcard_imports)]
use super::helpers::*;
#[test]
fn opening_an_issue_with_a_mention_shows_the_notice() {
let (_tmp, mut app, number) = app_with_mention("blocked on @alice");
app.open_issue_by_number(number).unwrap();
assert!(matches!(app.mode, Mode::MentionNotice));
let view = app.mentions.as_ref().expect("the notice is open");
assert_eq!(view.rows.len(), 1);
assert_eq!(view.rows[0].body, "alice");
assert!(
view.rows[0].context.contains("@alice"),
"{:?}",
view.rows[0].context
);
let text = frame_text(&app, 100, 20);
assert!(text.contains("Mentions for you"), "{text}");
assert!(
text.contains("@alice") || text.contains("blocked on @alice"),
"{text}"
);
}
#[test]
fn an_issue_with_no_mentions_for_the_user_opens_no_notice() {
let (_tmp, mut app, number) = app_with_mention("cc @someone-else");
app.open_issue_by_number(number).unwrap();
assert!(
matches!(app.mode, Mode::Detail),
"no notice for a stranger's mention"
);
assert!(app.mentions.is_none());
}
#[test]
fn ignoring_a_mention_writes_nothing_and_shows_it_again() {
let (_tmp, mut app, number) = app_with_mention("blocked on @alice");
app.open_issue_by_number(number).unwrap();
app.handle_key(press(KeyCode::Char('i'))).unwrap();
assert!(
matches!(app.mode, Mode::Detail),
"ignore returns to the pane"
);
assert!(app.mentions.is_none());
recount_mentions(&mut app);
assert_eq!(app.unread_mentions, 1);
app.open_issue_by_number(number).unwrap();
assert!(matches!(app.mode, Mode::MentionNotice));
}
#[test]
fn marking_a_mention_read_persists_and_is_not_shown_again() {
let (_tmp, mut app, number) = app_with_mention("blocked on @alice");
app.open_issue_by_number(number).unwrap();
app.handle_key(press(KeyCode::Char('r'))).unwrap();
assert!(
app.mentions.is_none(),
"the last pending mention closes the notice"
);
assert!(matches!(app.mode, Mode::Detail));
assert_eq!(app.unread_mentions, 0, "the count is recomputed");
app.open_issue_by_number(number).unwrap();
assert!(
matches!(app.mode, Mode::Detail),
"a read mention is not shown again"
);
}
#[test]
fn jumping_to_a_mention_scrolls_the_detail_pane_to_it() {
let body = "one\ntwo\nthree\nfour\nfive\nsix\nblocked on @alice\n";
let (_tmp, mut app, number) = app_with_mention(body);
app.open_issue_by_number(number).unwrap();
let detail = app.detail.as_mut().expect("a detail pane");
detail.max_scroll.set(50);
detail.scroll_offset = 0;
app.handle_key(press(KeyCode::Enter)).unwrap();
assert!(app.mentions.is_none(), "the notice closes on jump");
assert_eq!(
app.detail.as_ref().unwrap().scroll_offset,
6,
"scrolled to the mention"
);
}
#[test]
fn the_indicator_row_appears_with_unread_mentions_and_goes_when_read() {
let (_tmp, mut app, number) = app_with_mention("blocked on @alice");
assert_eq!(mention_row_height(&app), 1);
let text = frame_text(&app, 100, 20);
assert!(text.contains("1 unread mention"), "{text}");
assert!(text.contains("press i to open"), "{text}");
app.handle_key(press(KeyCode::Char('i'))).unwrap();
assert!(matches!(app.mode, Mode::Mentions));
app.handle_key(press(KeyCode::Char('r'))).unwrap();
assert_eq!(app.unread_mentions, 0);
assert_eq!(mention_row_height(&app), 0);
let text = frame_text(&app, 100, 20);
assert!(!text.contains("unread mention"), "{text}");
let _ = number;
}
#[test]
fn no_unread_mentions_means_no_indicator_row() {
let (_tmp, app, _number) = app_with_mention("nothing addressed here");
assert_eq!(mention_row_height(&app), 0);
let text = frame_text(&app, 100, 20);
assert!(!text.contains("unread mention"), "{text}");
}
#[test]
fn the_board_chord_opens_the_mentions_view_across_issues() {
let (_tmp, mut app) = make_app();
let first = seed_issue(&mut app, "blocked on @alice");
let _second = seed_issue(&mut app, "also blocked on @alice");
app.mention_candidates = vec!["alice".to_string()];
recount_mentions(&mut app);
assert_eq!(app.unread_mentions, 2);
app.handle_key(press(KeyCode::Char('i'))).unwrap();
assert!(
matches!(app.mode, Mode::Mentions),
"the chord opens the view"
);
let rows = app
.mentions_view
.as_ref()
.expect("the view is open")
.rows
.len();
assert_eq!(rows, 2, "both issues are listed");
let text = frame_text(&app, 100, 20);
assert!(text.contains("Pending mentions"), "{text}");
app.handle_key(press(KeyCode::Enter)).unwrap();
assert!(matches!(app.mode, Mode::Detail));
assert!(
app.mentions.is_none(),
"the opened issue's own notice does not preempt"
);
assert_eq!(app.detail.as_ref().unwrap().issue.number, first);
}
#[test]
fn the_mentions_view_reports_nothing_to_show_rather_than_opening_empty() {
let (_tmp, mut app, _number) = app_with_mention("nothing addressed here");
app.handle_key(press(KeyCode::Char('i'))).unwrap();
assert!(matches!(app.mode, Mode::Board));
assert!(app.mentions_view.is_none());
assert_eq!(app.status_msg.as_deref(), Some("no pending mentions"));
}
#[test]
fn mentions_disabled_means_no_notice_no_count_and_no_indicator() {
let (_tmp, mut app, number) = app_with_mention("blocked on @alice");
assert_eq!(app.unread_mentions, 1, "the mention is there to begin with");
app.store.config.mentions.enabled = false;
app.mention_candidates = vec!["alice".to_string()];
recount_mentions(&mut app);
assert_eq!(app.unread_mentions, 0);
assert_eq!(mention_row_height(&app), 0);
app.open_issue_by_number(number).unwrap();
assert!(
matches!(app.mode, Mode::Detail),
"no notice when the feature is off"
);
app.mode = Mode::Board;
app.handle_key(press(KeyCode::Char('i'))).unwrap();
assert!(matches!(app.mode, Mode::Board));
assert!(
app.status_msg
.as_deref()
.is_some_and(|m| m.contains("disabled")),
"{:?}",
app.status_msg
);
}