use super::*;
use crate::route::{code, pulls, search};
fn failed(state: &mut State, key: DataKey) {
update(
state,
Msg::Fetched {
key,
result: Err(crate::api::ApiError::Network("timed out".into())),
cached_at: None,
source: None,
},
);
}
#[test]
fn a_failed_readme_says_why_under_the_files() {
let mut state = state();
let _ = state.push(Route::Code(code::Repo(ghtui())));
let overview = Data::Repo(Box::new(fixtures::overview()));
fetched(&mut state, DataKey::Repo(ghtui()), overview);
failed(&mut state, DataKey::Readme(ghtui()));
let text = page_text(&state);
assert!(text.contains("Cargo.toml"), "{text}");
let why = "Couldn't load the README: network error: timed out. r tries again.";
assert!(text.contains(why), "{text}");
assert!(state.overview(&ghtui()).is_some());
}
#[test]
fn a_loading_conversation_stays_on_the_timeline() {
let mut state = with_home();
let pr = PrRef::parse("gold-silver-copper/ghtui#12").unwrap();
let route = Route::pr(pr.clone());
let _ = state.push(route.clone());
crate::fixtures::pr_fetched(&mut state, &pr, Ok(pr_detail()));
let lines: Vec<String> = (state.build_page(&route, 100, NOW).lines.iter())
.map(crate::ui::page::PageLine::text)
.collect();
let at = lines
.iter()
.position(|l| l.contains("Loading the conversation…"));
let above = at.and_then(|at| lines.get(at.checked_sub(1)?));
assert_eq!(above.map(|l| l.trim()), Some("│"), "{lines:#?}");
}
#[test]
fn a_search_in_flight_says_searching() {
let mut state = with_repo();
let route = Route::Search(search::Search {
kind: SearchKind::Repos,
query: "ghtui".into(),
});
let _ = state.push(route.clone());
let page = state.build_page(&route, 100, NOW);
let text: Vec<String> = page
.lines
.iter()
.map(crate::ui::page::PageLine::text)
.collect();
assert!(text.iter().any(|l| l.contains("Searching…")), "{text:#?}");
}
#[test]
fn every_route_says_why_when_its_needs_fail() {
fn err<T>() -> Result<T, crate::api::ApiError> {
Err(crate::api::ApiError::Network("timed out".into()))
}
let urls = [
"https://github.com/",
"https://github.com/o/r",
"https://github.com/o/r/tree/main/src",
"https://github.com/o/r/blob/main/src/main.rs",
"https://github.com/o/r/blame/main/src/main.rs",
"https://github.com/o/r/issues",
"https://github.com/o/r/pulls",
"https://github.com/search?q=tui&type=repositories",
"https://github.com/o/r/issues/1",
"https://github.com/o/r/pull/2",
"https://github.com/o/r/pull/2/commits",
"https://github.com/o/r/pull/2/checks",
"https://github.com/o/r/stargazers",
"https://github.com/o/r/watchers",
"https://github.com/o/r/forks",
"https://github.com/o/r/releases",
"https://github.com/o/r/releases/tag/v1",
"https://github.com/o/r/tags",
"https://github.com/o/r/branches",
"https://github.com/o/r/milestones",
"https://github.com/o/r/milestone/1",
"https://github.com/o/r/labels",
"https://github.com/o/r/fork",
"https://github.com/new",
"https://github.com/o/r/milestones/new",
"https://github.com/o/r/milestones/1/edit",
"https://github.com/o/r/edit/main/README.md",
"https://github.com/o/r/compare/a...b",
"https://github.com/o/r/wiki",
"https://github.com/o/r/security/advisories",
"https://github.com/o/r/security/advisories/GHSA-2222-3333-4444",
"https://github.com/o/r/settings",
"https://github.com/o/r/settings/branches",
"https://github.com/o/r/settings/access",
"https://github.com/o/r/settings/hooks",
"https://github.com/o/r/settings/hooks/11",
"https://github.com/o/r/settings/keys",
"https://github.com/orgs/o/teams",
"https://github.com/orgs/o/teams/t",
"https://gist.github.com/o/0123456789abcdef0123",
"https://gist.github.com/o",
"https://github.com/o/r/deployments",
"https://github.com/o/r/discussions",
"https://github.com/o/r/discussions/3",
"https://github.com/o/r/actions",
"https://github.com/o/r/actions/runs/4",
"https://github.com/o/r/actions/runs/4/job/5",
"https://github.com/o/r/actions/workflows/ci.yml",
"https://github.com/o/r/commit/0123456789abcdef0123456789abcdef01234567/checks",
"https://github.com/o/r/commits/main",
"https://github.com/o/r/commit/0123456789abcdef0123456789abcdef01234567",
"https://github.com/o",
"https://github.com/o?tab=followers",
"https://github.com/o?tab=repositories",
"https://github.com/orgs/o/projects",
"https://github.com/o/r/projects",
"https://github.com/orgs/o/projects/1",
"https://github.com/orgs/o/projects/1/views/2?pane=issue&itemId=1003",
];
for url in urls {
let crate::route::Target::Page(route) = crate::route::Dest::from_url(url).target else {
panic!("{url} isn't a page");
};
let route = route.split(None).unwrap_or(route);
let all = state().needs(&route);
for failing in &all {
let mut state = state();
let _ = state.push(route.clone());
for key in all.iter().cloned() {
let fail = &key == failing;
let msg = Msg::Fetched {
result: if fail {
err()
} else {
Ok(crate::fixtures::sample(&key))
},
key,
cached_at: None,
source: None,
};
update(&mut state, msg);
}
let text = page_text(&state);
let at = format!("{url}, {failing:?} failing:\n{text}");
assert!(!text.contains("Loading"), "{at}");
assert_eq!(
text.contains("timed out"),
!decoration(&route, failing),
"{at}"
);
}
}
}
fn decoration(route: &Route, need: &DataKey) -> bool {
use DataKey as K;
crate::browse::is_header(route, need)
|| matches!(
(route, need),
(_, K::LastCommits(..) | K::Clone(_))
| (Route::Code(code::CommitChecks { .. }), K::Commit(..))
| (
Route::Pulls(pulls::Pr {
tab: PrTab::Checks,
..
}),
K::PrActivity(_)
)
)
}