//! 控件树那一节:九个状态 × 四档宽度都要能构图(M105/M107 合流后 `tests.rs` 到 433 行, //! 按 AGENTS.md §3-1 拆出来)。 //! //! 只测「能构建出树」——**抓不住**画出来长什么样(像素由 `DevDocs/artifacts/m107/` 的图负责)。 use unlucky_i18n::Lang; use unlucky_presenter::dto::{InstallProgressView, InstallStage, VersionKind}; use crate::pages::download::model::{Download, InstallView, VersionRow}; use crate::pages::download::{view, Category, Message}; use crate::pages::layout::{frame_metrics, PaneKit}; use super::{ui, version}; /// 九个状态都能构建出控件树,且四档宽度都能构建(不 panic、消息类型对得上)。 /// /// 抓得住:某个状态分支漏了返回值、或消息类型接错(编译期就红);空态/加载态忘了给内容。 /// 抓不住:**画出来长什么样**——像素由 `DevDocs/artifacts/m107/` 的图负责。 #[test] fn every_state_builds_a_tree_at_every_width() { let ui = ui(); let catalog = Lang::En.catalog(); let loading = Download::loading(); let empty = Download { loaded: true, ..Download::loading() }; let ready = Download { options: None, rows: vec![VersionRow::of( &version("1.21.9", VersionKind::Release, false), catalog, )], category: Category::Release, selected: Some("1.21.9".to_string()), install: None, loaded: true, // M107 的五个新字段:桶 / 查询 / 命中数 / 第二级选中 / 折叠态 ..Download::default() }; let installing = Download { install: Some(InstallView::of( &InstallProgressView { version: "1.21.9".to_string(), stage: InstallStage::Downloading, files_done: 128, files_total: 512, ..InstallProgressView::new() }, catalog, )), ..ready.clone() }; let failed = Download { install: Some(InstallView::of( &InstallProgressView { version: "1.21.9".to_string(), stage: InstallStage::Failed, ..InstallProgressView::new() }, catalog, )), ..ready.clone() }; // M107:搜索 / 第二级选中 / 搜不到 / 手动折叠四个新增状态的取样 let searching = Download { query: "1.21".to_string(), hits: 1, ..ready.clone() }; let majored = Download { selected_major: Some("1.21".to_string()), ..ready.clone() }; let no_match = Download { query: "zzz".to_string(), hits: 0, ..ready.clone() }; let folded = Download { type_rail_folded: true, ..ready.clone() }; // 四档宽度:1180 / 1024(Wide 门槛)/ 900(Narrow)/ 700(Chips) for width in [1180.0, 1024.0, 900.0, 700.0] { let metrics = frame_metrics(PaneKit::Single, width); for model in [ loading.clone(), empty.clone(), ready.clone(), installing.clone(), failed.clone(), searching.clone(), majored.clone(), no_match.clone(), folded.clone(), ] { let _: iced::Element<'_, Message> = view(&ui, metrics, &model, Lang::En); } } } /// 安装失败的模型:可读文案 + 出口都有(不静默)。 #[test] fn a_failed_install_stays_visible() { let model = InstallView::of( &InstallProgressView { version: "1.21.9".to_string(), stage: InstallStage::Failed, ..InstallProgressView::new() }, Lang::En.catalog(), ); assert!(model.failed()); assert!(model.terminal()); assert!(!model.running(), "失败之后不再给「取消安装」"); assert_eq!(model.stage_label, "Install failed"); } /// 演示数据自己也得守规矩(它是出图的基线):八行、四个主版本桶、英文目录下不出现中文类型词。 #[test] fn the_demo_data_is_sane() { let catalog = Lang::En.catalog(); let demo = crate::pages::download::demo(catalog); assert!(demo.loaded); assert_eq!(demo.rows.len(), 8); assert_eq!(demo.category, Category::All, "混合列表没有单个分类该亮"); assert!(demo.query.is_empty(), "缺省演示没有搜索词"); assert_eq!( demo.buckets .iter() .map(|bucket| bucket.key.as_deref()) .collect::>(), vec![Some("24w"), Some("1.21"), Some("1.20"), None], "第二级侧栏的桶顺序与内容" ); assert!(demo .rows .iter() .all(|row| row.supporting.contains("released"))); assert!(demo.rows.iter().all(|row| !row.supporting.contains('版'))); }