Files
MailUI4Agents/client/electron/test/harmony-presets.test.mjs
JianFeeeee 5e74e0312e test(预设): 补行为判据 —— "清单里有、渲染器没有"必须红(值判据只挡住一半)
pi 2026-09-14 指出:我那条"两端预设清单 id/顺序逐项相等"只挡得住**一半** ——
它挡"清单漏一档",挡不住"清单有、渲染器没有"(第 7 档两边都加、忘了给
`layersFor` 加分支 ⇒ 值判据全绿,用户看到的却是 aurora)。症状与"信息对等缺口"
一模一样,只是路径换了。核对:他说得对,`layersFor` 的兜底就是 `aurora`,
所以丢分支是**静默**的。

新增 `test/harmony-presets.test.mjs`(4 条,`--experimental-strip-types` 直接执行
纯逻辑 `model/Wallpaper.ts` —— 与 `harmony-logic` 同一先例,**能真跑**):

1. 六档 × 两主题:不抛异常、非空、每层 kind/色标/网格字段有效;
2. ★ **清单里有、渲染器没有 → 红**:手法是拿一个不存在的 id 调一次当作"兜底长相",
   任何一档丢了分支就会与兜底逐字节相同 ⇒ 红。**并写明已知边界**:`aurora` 自己
   就是兜底档,它"丢分支"在行为上不可观测(由值判据 + 第 3 条兜住);
3. 六档两两不同(复制分支没改参数 → 红);
4. 深色档必须与浅色档不同("深色没换色" → 红)。

**不需要设备 ⇒ 按 pi 的提醒不登记进 `static` 欠账**(那会虚增余额)。
两条变异都验过:删 `mesh` 分支 → 行为判据报"画出来的和兜底档一模一样";
清单加第 7 档不加分支 → **值判据与行为判据同时红**(两条一起才闭合)。
安装相位 14/14 绿。

另按 pi 要求处理两处登记:
· §7.12 那行"px ↔ 档位"改成**核实后的真相**:`bg_blur` 在鸿蒙侧只有搬运没有消费
  (`Appearance.ts` clamp 存下、`AppearanceStore` 同步,**没有任何页面读它**),
  所以不存在映射表可钉 —— 钉一张不存在的表是假判据;并写明"若将来开始消费它,
  那时必须补映射判据",作为"人工约定、当前无判据"的登记处。
· 预设 id/顺序那条**标清钉的是产品要求**(顺序=用户看到的选项次序),理由写进判据注释。
2026-09-14 17:05:15 +08:00

108 lines
5.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* 预设的**行为**判据:每一档都真的画得出来。
*
* 为什么需要它(pi 2026-09-14 指出):`appearance-defaults.test.mjs` 那条
* "鸿蒙 `PRESET_IDS` 与 WebUI `PRESETS` 的 id 与顺序逐项一致" 只挡得住**一半** ——
* 它挡"清单漏一档",**挡不住"清单有、渲染器没有"**:将来两边都加了第 7 档、
* 却忘了给 `layersFor` 加分支,那条判据**全绿**,而用户看到的是"无背景"
* ——正是"信息对等缺口"的同一个症状,只是路径换了。
*
* 所以这里是**两条配对**:值判据管"清单对不对",行为判据管"每一档真的画得出来"。
* 这与命中区那处(数值在 `NavItems.ts`、判据直接 import)是同一个配对方式。
*
* **能真跑**,不是形态判据:`model/Wallpaper.ts` 是纯逻辑(无 `@ohos` 依赖、
* 类型可擦除),用 node 的 `--experimental-strip-types` 直接执行它 —— 与
* `harmony-logic.test.mjs` 同一个先例。因此它**不需要设备**,
* 也**不该**登记进 `static` 欠账(那会虚增余额)。
*/
import { join } from 'node:path';
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { pathToFileURL } from 'node:url';
const ROOT = '/home/program/agentmail';
const WALLPAPER_TS = join(ROOT, 'client/harmony/entry/src/main/ets/model/Wallpaper.ts');
// 直接执行 `.ets` 树里的**纯逻辑** `.ts`(有 `@ohos` 依赖的文件不能这样跑)
const W = await import(pathToFileURL(WALLPAPER_TS).href);
const IDS = W.PRESET_IDS;
const THEMES = [false, true];
const themeName = d => (d ? '深色' : '浅色');
/** 层栈的稳定序列化(只取有意义字段,便于比较与报错) */
const shapeOf = layers => JSON.stringify(layers.map(l => ({
kind: l.kind,
cx: l.cx, cy: l.cy, radius: l.radius, angle: l.angle,
colors: l.colors, stops: l.stops,
lineColor: l.lineColor, lineAlpha: l.lineAlpha, step: l.step
})));
test('六档 × 两种主题:都画得出来(不抛异常、非空、每层字段有效)', () => {
assert.ok(Array.isArray(IDS) && IDS.length >= 6, `预设清单要能取到(实际 ${IDS?.length})`);
for (const id of IDS) {
for (const dark of THEMES) {
let layers;
assert.doesNotThrow(() => { layers = W.layersFor(id, dark); },
`${id}(${themeName(dark)})调用就抛了 —— 用户看到的是"无背景"`);
assert.ok(Array.isArray(layers) && layers.length >= 1,
`${id}(${themeName(dark)})返回空层栈 ⇒ 这一档画出来是空白`);
for (const [i, l] of layers.entries()) {
assert.ok(['radial', 'linear', 'grid'].includes(l.kind),
`${id}(${themeName(dark)})第 ${i} 层的 kind 认不出:${l.kind}`);
if (l.kind === 'grid') {
assert.ok(typeof l.step === 'number' && l.step > 0, `${id} 网格档缺 step`);
assert.ok(typeof l.lineColor === 'string' && l.lineColor.length > 0, `${id} 网格档缺线色`);
} else {
assert.ok(Array.isArray(l.colors) && l.colors.length >= 2 && l.colors.every(c => typeof c === 'string' && c.length > 0),
`${id}(${themeName(dark)})第 ${i} 层的色标不完整:${JSON.stringify(l.colors)}`);
}
}
}
}
});
/*
* ★ 这条是 pi 那半边的直接对应物:**"清单里有、渲染器里没有"必须红。**
*
* 手法:拿一个**不存在的 id** 调一次 —— `normalizePreset` 会把它落到默认档
* (aurora),于是"兜底长什么样"就有了可比较的对象。任何一档如果**丢了分支**,
* 它会静默落到同一个兜底上 ⇒ 与兜底逐字节相同 ⇒ 红。
*
* **已知边界(写出来,不假装全覆盖)**:`aurora` 自己**就是**那个兜底档,
* 所以它"丢了分支"在行为上不可观测(结果一样)。这条判据能测到的是
* **另外五档**;aurora 的存在性由值判据(清单相等)与下面那条"六档两两不同"兜住。
*/
test('★ 清单里有、渲染器里没有 → 红(每一档都必须与"兜底档"不同)', () => {
for (const dark of THEMES) {
const fallback = shapeOf(W.layersFor('__这个 id 不存在__', dark));
const aurora = shapeOf(W.layersFor('aurora', dark));
assert.equal(fallback, aurora, `自检:认不出的 id 应当落到默认档 aurora(${themeName(dark)})`);
for (const id of IDS) {
if (id === 'aurora') continue; // 见上面的"已知边界"
assert.notEqual(shapeOf(W.layersFor(id, dark)), fallback,
`${id}(${themeName(dark)})画出来的和"兜底档"一模一样 ⇒ 它没有自己的渲染分支` +
`(清单里有、渲染器没有:用户看到的是 aurora,不是他选的那档)`);
}
}
});
test('六档两两不同(复制粘贴分支没改参数 → 红)', () => {
for (const dark of THEMES) {
const seen = new Map();
for (const id of IDS) {
const s = shapeOf(W.layersFor(id, dark));
assert.ok(!seen.has(s),
`${id} 与 ${seen.get(s)} 在${themeName(dark)}下画出来完全相同 —— 复制分支后忘了改参数`);
seen.set(s, id);
}
}
});
test('深色档必须与浅色档不同("深色没换色" → 红)', () => {
for (const id of IDS) {
assert.notEqual(shapeOf(W.layersFor(id, true)), shapeOf(W.layersFor(id, false)),
`${id} 的深色档与浅色档完全一样 ⇒ 深色模式下这一档没有换色`);
}
});