fix(app-iced, presenter): M114 r4 —— 登录面板 Close 不再是死按钮 + 面板态删掉原文字段(把「画原文」变成编译错)

复审 r2 P2-1:`Account::Close` 只在 `phase.busy()` 时派 `CancelDeviceLogin`,而
`Page::build` 只要非 Idle 就强制回 Microsoft ⇒ Failed/Succeeded 下 Close 点了没反应。
- 判据改成「两条轨任一非 Idle 就取消」;host 侧 `CancelDeviceLogin` 本来就把两条轨收成 Idle
- mock 补 `AppCommand::CancelDeviceLogin` 分支(按真 host 契约发 Idle),否则 mock 下不可验
- 新增 headless 回归闸:失败/成功/第三方轨三种态 → Close → `panel == None`
  (变异实证:判据退回 `busy()` ⇒ 该用例红,打印 left: Microsoft / right: None)

复审 r2 P2-2:`DevicePanel`/`AuthlibPanel` 删掉 `message`/`detail` 字段。
视图没有材料可画,「把后端原文画上屏」从测试问题变成**类型错误**;
`tests_text.rs` 的 docstring 改成真实边界(只覆盖模型 + i18n 查表,视图靠类型 + 出图)。

顺带(塔看图标出):alert 的 `Code: …` 与标题同排,省一整行高度,
避免 1180×760 把 All accounts 第三行挤到折线下;新增 `closed` 取证场景出「关掉之后」的对照图。
This commit is contained in:
AskaEth committed 2026-11-05 05:10:47 +08:00
1 parent bc71834bc3
commit f3e20b670e
9 files changed
+188 -81

No files matched your search

@@ -1,8 +1,17 @@
//! 「**页面会画什么**」的取样器:把视图模型 + 视图层对码的 i18n 查表结果拼成一串。
//! 「**模型这一层会给出什么**」的取样器:视图模型字段 + **按码做的 i18n 查表**,拼成一串。
//!
//! ## 边界(复审 r2 P2-2 订正,别把这条读成「视图覆盖」)
//!
//! 这是**平行实现**:它自己调 `catalog` 查表,**不调用** `login::panel` / `forms::status_line`,
//! 也不遍历 iced 的元素树。所以它证明的是「模型 + i18n 表里没有中文」,**不证明**「视图真的照它画」。
//!
//! 视图层靠两道别的闸:
//! 1. **类型**:`DevicePanel` / `AuthlibPanel` 里**没有** `message`/`detail` 字段(r3 删掉了),
//! 想画后端原文是编译错误,不是测试问题;
//! 2. **出图**:`DevDocs/artifacts/m114/after/*` 由人(tower/复审)逐张看。
//!
//! 为什么单独一个文件:`pages/account/tests.rs` 贴着 400 行硬线,而这段是**纯取样**
//! (不给任何断言)——断言都在 `tests.rs` 里。语言泄漏(复审 r1 P1-1)与凭据红线两条判据
//! 都依赖它:只有把「模型字段 + i18n 文案」一起取样,才能证明「英文界面里没有中文」。
//! (不给任何断言)——断言都在 `tests.rs` 里。
use unlucky_i18n::{Key, Lang};
use unlucky_presenter::dto::AccountNoticeCode;
@@ -47,13 +56,12 @@ pub(super) fn on_screen(page: &Page) -> String {
}
match &page.device {
DevicePanel::Hidden => {}
DevicePanel::Requesting { .. } => out.push_str(catalog.text(Key::AccountDeviceRequesting)),
DevicePanel::Requesting => out.push_str(catalog.text(Key::AccountDeviceRequesting)),
DevicePanel::Code {
code,
link,
remaining,
expired,
..
} => {
out.push_str(code);
out.push_str(link);
@@ -65,7 +73,7 @@ pub(super) fn on_screen(page: &Page) -> String {
}
}
DevicePanel::Browser { link, .. } => out.push_str(link),
DevicePanel::Failed { failure, .. } => {
DevicePanel::Failed { failure } => {
out.push_str(catalog.text(failure_body_key(*failure)));
out.push_str(&catalog.render(Key::AccountFailureCode, &[("code", failure.code())]));
}
@@ -75,9 +83,9 @@ pub(super) fn on_screen(page: &Page) -> String {
}
match &page.authlib {
AuthlibPanel::Idle => {}
AuthlibPanel::Running { .. } => out.push_str(catalog.text(Key::AccountServerChecking)),
AuthlibPanel::Ready { server_name, .. } => out.push_str(server_name),
AuthlibPanel::Failed { failure, .. } => out.push_str(failure.code()),
AuthlibPanel::Running => out.push_str(catalog.text(Key::AccountServerChecking)),
AuthlibPanel::Ready { server_name } => out.push_str(server_name),
AuthlibPanel::Failed { failure } => out.push_str(failure.code()),
AuthlibPanel::Succeeded { name } => out.push_str(name),
}
out