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/顺序那条**标清钉的是产品要求**(顺序=用户看到的选项次序),理由写进判据注释。
This commit is contained in:
107
client/electron/test/harmony-presets.test.mjs
Normal file
107
client/electron/test/harmony-presets.test.mjs
Normal file
@ -0,0 +1,107 @@
|
||||
/*
|
||||
* 预设的**行为**判据:每一档都真的画得出来。
|
||||
*
|
||||
* 为什么需要它(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} 的深色档与浅色档完全一样 ⇒ 深色模式下这一档没有换色`);
|
||||
}
|
||||
});
|
||||
@ -64,6 +64,9 @@ const SUITE = [
|
||||
['test/theme.test.mjs', [], 30],
|
||||
['test/background.test.mjs', [], 42],
|
||||
['test/cross-client-theme.test.mjs', [], 15],
|
||||
// 预设的**行为**判据:每一档都真的画得出来(能真跑,不需要设备 ⇒ 不进 static 欠账)。
|
||||
// 与 appearance-defaults 那条「清单 id/顺序相等」配对:值判据管清单,行为判据管渲染器。
|
||||
['test/harmony-presets.test.mjs', ['--experimental-strip-types', '--no-warnings'], 4],
|
||||
['test/harmony-logic.test.mjs', ['--experimental-strip-types', '--no-warnings'], 28],
|
||||
['test/harmony-system-api.test.mjs', [], 5],
|
||||
// P4 外观同步:跑 model/Appearance.ts(纯逻辑),所以也要 strip-types
|
||||
|
||||
Reference in New Issue
Block a user