Files
Unlucky-Launcher/crates/app-iced/src/pages/content/tests.rs
T
AskaEth f9877a89d8 fix(app-iced): 下载页页头「内容库」选中态反转(M229 复审 F3)
`pages/content.rs::section_switcher` 把 `on_content` 写死成 `true`,而下载页也调它,
于是版本管理页的页头把「内容库」画成选中、「版本管理」画成未选中。

改成由调用方给位置:下载页传 false、内容库页自己传 true;并加一条源码层面的调用点断言
(iced 的 Element 不可内省,判据只有一行,写错必红)。

不是 M229 引入的缺陷(M109 就有),按复审要求单独提交,不与收官换肤混在一起。
2026-11-11 01:49:14 +08:00

120 lines
4.3 KiB
Rust

//! 内容库页的守门(M109):演示数据、四类内容、页面自留状态的默认值。
//!
//! 视图模型的判定在 `model/tests.rs`,栏几何在 `layout.rs`,这里只管「演示数据真的装得满」——
//! 出图靠它,装不满就说明 `demo()` 与 `Content` 的字段对不上。
use unlucky_i18n::Lang;
use unlucky_presenter::dto::ContentKind;
use super::model::{PageState, Status};
use super::{demo, Content};
#[test]
fn the_page_state_starts_on_mods_with_nothing_selected() {
let page = PageState::default();
assert_eq!(page.kind, ContentKind::Mod);
assert!(page.selected.is_none());
assert!(page.target.is_none());
assert!(page.draft.is_empty());
}
#[test]
fn every_kind_gets_a_full_demo_screen() {
// 四种内容各出一屏(状态矩阵的前 8 张靠这一条)
for kind in ContentKind::ALL {
let model: Content = demo(kind, Lang::En);
assert_eq!(model.kind, kind, "{} 的演示数据换了类型", kind.slug());
assert_eq!(model.status, Status::Ready, "{}", kind.slug());
assert_eq!(model.hits.len(), 4, "{} 的命中项不足", kind.slug());
assert!(
model.selected.is_some(),
"演示数据要选中一条(详情面板才有内容)"
);
assert!(model.total > 0);
assert!(model.has_more, "演示数据要能画「加载更多」");
// 整合包**不**在这一条里断(它天生不可装:`ContentKind::Modpack.dir()` 是 `None`),
// 由下一条用例专钉。
if kind != ContentKind::Modpack {
assert!(
model.can_install(),
"默认那一屏应当是「能装」的({})",
kind.slug()
);
}
}
}
#[test]
fn the_modpack_screen_is_browsable_but_never_installable() {
// 整合包:检索能看,装入一律禁用(M111 未落地)——演示图上必须看得到原因
let model = demo(ContentKind::Modpack, Lang::En);
assert!(model.selected.is_some());
assert!(!model.can_install());
assert_eq!(
model.blocker_reasons(Lang::En.catalog()).len(),
1,
"整合包只有一条原因(导入管线未落地)"
);
}
#[test]
fn the_installable_kinds_block_only_when_the_target_disagrees() {
for kind in [
ContentKind::Mod,
ContentKind::Resourcepack,
ContentKind::Shader,
] {
let model = demo(kind, Lang::En);
assert!(
model.blocker_reasons(Lang::En.catalog()).is_empty(),
"{} 的默认演示项应当可装",
kind.slug()
);
}
}
#[test]
fn the_demo_selection_is_always_one_of_the_hits() {
for kind in ContentKind::ALL {
let model = demo(kind, Lang::En);
let selected = model.selected.clone().expect("有选中项");
assert!(
model.hits.iter().any(|hit| hit.id == selected),
"{} 的选中项不在命中列表里(详情面板会是空的)",
kind.slug()
);
}
}
/// M191:「全部成功」那一屏(默认态)**没有**重试面板——出图与判据同源,不是手搓的。
#[test]
fn the_default_demo_screen_has_no_retry_surface() {
for kind in ContentKind::ALL {
let model = demo(kind, Lang::En);
assert!(
model.retry.is_none(),
"{} 默认态不该有重试面板",
kind.slug()
);
}
}
/// M229 复审 F3 的回归门:页头一级切换的选中态由**调用方所在页**决定,不是写死「内容库」。
///
/// 抓得住:`download.rs` 又传回 `true`(下载页页头的「内容库」被画成选中,正是 F3 的现场)。
/// 抓不住:像素长什么样(那要截图);iced 的 `Element` 不可内省,所以这条从源码层面钉住调用点——
/// 判据只有一行,读的人也不会误解。
#[test]
fn the_download_header_marks_versions_selected_not_content() {
let download = include_str!("../download.rs");
assert!(
download.contains("section_switcher(ui, catalog, false)"),
"下载页是「版本管理」那一侧,必须传 false(传 true = F3 复发)"
);
let content_view = include_str!("view.rs");
assert!(
content_view.contains("section_switcher(ui, catalog, true)"),
"内容库页自己那一份必须传 true"
);
}