用户令(信息架构):这一页从「下载」改称「版本管理」,版面从「一排 tab + 平铺列表」 改成**页面内三栏**:版本类型栏 + 主版本栏 + 列表/详情,并加搜索框。 - **三栏几何**(`pages/layout/rails.rs`,纯函数 + 数值表):类型栏 140/64、主版本栏 140/64、 间距 24、列表列下限 424(行宽红线)。四档由**可用宽**裁决:Wide ≥888 / Narrow ≥676 / Compact ≥600 / Chips <600,三个门槛都是 `栏宽 + 间距 + 424` 算出来的,不是拍的 - **主版本分组规则**(`download/group.rs`):正式版/预发布按 `major.minor`(`1.21.9-pre3` → `1.21`)、 周更按年桶(`24w14a` → `24w`)、远古版**没有主版本**(`rd-132211`/`a1.2.6` → 第二级只有「全部」); 判据用真实版本样本钉住,不按「第一个点」切 - **跨类型搜索**(`search_bar` + 清空 + 命中数):查询词写进 `AppState.download_query`(页面无状态, 只能这么活),`hits` 是**跨分类**读数;三条口径分开:「有命中」/「没有匹配」/「命中在别的类型里」 - 两级侧栏都用 `navigation::list_item` / `tabs` / `chip` / `buttons::icon`:Tab 可达、hover/pressed/ 选中态全部来自组件,页面不自己写状态层 - 右栏展开(M104 `content_width` 852)⇒ 可用宽 716 ⇒ Narrow 档,列表 464(任务书那句 492 漏扣 96px 的页边距与栏间距,报告里给了完整数值表) - 文案 11 条中英进 `crates/i18n`(新域宏接在装配链末尾 ⇒ `Key::ALL` 只追加,金表只追加) - 护栏:两层侧栏都存在 + 列表按第二级选中态过滤(变异可证)、三档门槛、424 下限扫描、 版本行单行宽账、搜索条真的装着 - `install.rs` 只动了「已装」分支(加一句「已安装」解释禁用按钮),没碰 M105 的选项区 取证:`DevDocs/artifacts/m107/`(10 张:三档 × 浅深 + 搜索命中 + 空 + 异地 + 远古版)
107 lines
4.3 KiB
Rust
107 lines
4.3 KiB
Rust
//! 下载页的**文案守门**(M105 从 `download/tests.rs` 搬出:那边加了安装选项的用例之后顶破 400 行)。
|
||
//!
|
||
//! 两条各守一件事:死 key(目录里有、页面没用)与「模型说的是目录的语言」。
|
||
|
||
use unlucky_i18n::{Key, Lang};
|
||
use unlucky_presenter::dto::VersionKind;
|
||
|
||
use super::*;
|
||
|
||
// --------------------------------------------------------------- 文案(i18n)
|
||
|
||
/// 每个 `Download*` key 都**真的被页面引用**(死 key 守门,M94 r1 / P2-6)。
|
||
///
|
||
/// 复审抓到的 `DownloadSelected` 就是这条要拦的东西:目录里定义了、页面没人用。所以这里把页面
|
||
/// 源码用 `include_str!` 编进来扫一遍。**抓不住什么也说清**:反向(引用了不存在的 key)是编译错误,
|
||
/// 不需要测试;译文对不对、两套语言都填了没由 `i18n` 自己的 `built_in_catalogs_have_every_key` 守;
|
||
/// 它只看字面出现,用宏拼 key 名能骗过它。
|
||
#[test]
|
||
fn every_download_key_is_referenced_by_the_page() {
|
||
const SOURCES: [&str; 11] = [
|
||
include_str!("../../download.rs"),
|
||
include_str!("../model.rs"),
|
||
// M107:分类枚举搬进 `model/category.rs`(`Category::label_key` 在那儿,漏扫会误判死 key)
|
||
include_str!("../model/category.rs"),
|
||
include_str!("../rows.rs"),
|
||
// M107:两级侧栏与分组规则(`VersionRailTitle` / `VersionMajorAll` 的落点)
|
||
include_str!("../rails.rs"),
|
||
include_str!("../group.rs"),
|
||
include_str!("../install.rs"),
|
||
include_str!("../states.rs"),
|
||
include_str!("../demo.rs"),
|
||
// M105:安装选项区的两条填页线(漏扫的话新 key 会被判成死 key)
|
||
include_str!("../options/model.rs"),
|
||
include_str!("../options/view.rs"),
|
||
];
|
||
|
||
let mut checked = 0;
|
||
for key in Key::ALL
|
||
.iter()
|
||
.filter(|key| key.name().starts_with("Download") || key.name().starts_with("Version"))
|
||
{
|
||
let needle = format!("Key::{}", key.name());
|
||
assert!(
|
||
SOURCES.iter().any(|source| source.contains(&needle)),
|
||
"{needle} 在目录里定义了,但下载页没有任何地方用它(死 key,删掉或接上)"
|
||
);
|
||
checked += 1;
|
||
}
|
||
assert!(
|
||
checked > 20,
|
||
"下载页的 key 不该被成批删掉(只查到 {checked} 条)"
|
||
);
|
||
assert!(
|
||
checked > 50,
|
||
"M107 之后这一页的 key(Download* + Version*)不该少于 50 条,只查到 {checked}"
|
||
);
|
||
}
|
||
|
||
/// 页面级的文案全部按 `Catalog` 走:中文目录出中文,英文目录出英文,**且不含 core 的中文业务串**。
|
||
///
|
||
/// 抓得住:页面里又出现硬编码英文/中文字面量、或把 `InstallStage::label()` / `progress.message`
|
||
/// (core 的中文串)画到界面上。
|
||
/// 抓不住:目录里那条译文本身写错了。
|
||
#[test]
|
||
fn the_model_speaks_the_catalog_language() {
|
||
let versions = vec![version("1.21.9", VersionKind::Release, false)];
|
||
let state = state_with(versions);
|
||
|
||
let en = Download::from_snapshot(
|
||
&state,
|
||
Category::All,
|
||
&InstallDraft::default(),
|
||
Lang::En.catalog(),
|
||
);
|
||
let zh = Download::from_snapshot(
|
||
&state,
|
||
Category::All,
|
||
&InstallDraft::default(),
|
||
Lang::ZhCn.catalog(),
|
||
);
|
||
|
||
assert_eq!(en.rows[0].supporting, "Release · released 2026-09-20");
|
||
assert_eq!(zh.rows[0].supporting, "正式版 · 发布于 2026-09-20");
|
||
|
||
let progress = InstallProgressView {
|
||
version: "1.21.9".to_string(),
|
||
stage: InstallStage::Planning,
|
||
message: "正在解析版本清单…".to_string(),
|
||
..InstallProgressView::new()
|
||
};
|
||
let install_en = crate::pages::download::model::InstallView::of(&progress, Lang::En.catalog());
|
||
assert_eq!(install_en.stage_label, "Planning");
|
||
assert_eq!(install_en.detail, "Waiting for the first file…");
|
||
assert!(
|
||
!install_en.stage_label.contains('解'),
|
||
"core 的中文阶段串不许直接上屏"
|
||
);
|
||
assert!(
|
||
!install_en.detail.contains('正'),
|
||
"`InstallProgressView::message` 是中文业务串,页面不许画它"
|
||
);
|
||
|
||
let install_zh =
|
||
crate::pages::download::model::InstallView::of(&progress, Lang::ZhCn.catalog());
|
||
assert_eq!(install_zh.stage_label, "解析版本");
|
||
}
|