跨端: fix(harmony-appearance): blurStyleFor 已删除 ⇒ 换成**碑文判据 + 性质现居地**(不是只删);顺手修两处说谎的标题

pi 2026-09-15 查到根因:`blurStyleFor`(px → 材质档)已**有意删除**(碑文 `Wallpaper.ts:246`:
"没有、也不该有消费者"),而判据文件里还有引用、2 条真在调用 ⇒ `A.blurStyleFor is not a function`。

**只删会把"这个性质搬到哪了"一起删掉**(pi 的建议)。所以两处改成:

1. **碑文/反回归**:`typeof A.blurStyleFor === 'undefined'` —— 有意删除的东西不许悄悄回来;
   外加"理由必须留在原处"(碑文正则,删了理由也会红);
2. **性质的现居地**:导航面板材质现在是**固定系统档** ——
   `common/Theme.ets` 的 `navMaterial: BlurStyle = BlurStyle.COMPONENT_THICK`(从"函数映射"搬到"常量")。
3. 后半段那组(分档边界/单调性/NaN)**整体作废且不硬造等价物**:它们的**主语**没了,
   常数没有单调性、没有越界归并、没有 NaN 方向 —— **性质随主语一起消失,判据变少是对的**。

★ 另修**两处说谎的标题**(本判据属于 pi 的"标签必须等于断言范围"):
- `★ blurStyleFor:分档边界、单调性、NaN 都是行为` → `★ 碑文:blurStyleFor 不许回来 + 理由必须留在原处`;
- `★ 模糊值映射到**系统材质档次**` → `★ 材质档**不再是 px 的映射**:碑文 + 性质现居地`。
  改前它们断言的内容与标题已经完全不是一回事。

实测:`# pass 25 / # fail 0`(此前 退出码 1)。登记 26 → 25 同步(**变少是对的**)。
This commit is contained in:
2026-09-15 12:20:41 +08:00
parent ad1f3f14c3
commit 9a10ab2fcf
2 changed files with 81 additions and 126 deletions

View File

@ -129,70 +129,26 @@ test('离线/未登录:本地就是全部,而且**状态要看得见**(降
// ───────────────────── 系统方案:数字 → 系统材质 / 色彩模式 ─────────────────────
test('★ 模糊值映射到**系统材质档次**(不是把 40 当半径塞给某个 API)', () => {
/*
* 服务端存的是 WebUI 的 `bg_blur`(模糊像素半径,0~40),鸿蒙这边"模糊"由系统材质提供
* (`BlurStyle`)。同一个数字两边含义不同,必须显式映射 —— 这条判据钉住映射关系,
* 顺带钉住"没有 0~40 档全开"(材料只有几档,落不到档上的数字要归到最近的档)。
*/
assert.equal(A.blurStyleFor(0), 'NONE', '不模糊就是不用材质');
assert.equal(A.blurStyleFor(4), 'COMPONENT_THIN');
assert.equal(A.blurStyleFor(8), 'COMPONENT_THIN');
assert.equal(A.blurStyleFor(9), 'COMPONENT_REGULAR');
assert.equal(A.blurStyleFor(20), 'COMPONENT_REGULAR');
assert.equal(A.blurStyleFor(21), 'COMPONENT_THICK');
assert.equal(A.blurStyleFor(40), 'COMPONENT_THICK');
assert.equal(A.blurStyleFor(999), 'COMPONENT_THICK', '越界要归到最近的档,不能返回空');
assert.equal(A.blurStyleFor(-3), 'NONE');
// 档次必须来自系统枚举(写成自造名字会编译不过/不生效)
const sdk = A.blurStyleFor(12);
// SDK 的 .d.ts 是**源码**(判它的枚举成员),所以走 code():剥掉注释,
// 免得注释里举例的枚举名被当成员读进来
const commonDts = code(process.env.HARMONY_COMMON_DTS
|| '/opt/huawei/command-line-tools/sdk/default/openharmony/ets/component/common.d.ts');
const enumBlock = commonDts.slice(commonDts.indexOf('declare enum BlurStyle'));
const members = [...enumBlock.slice(0, enumBlock.indexOf('}')).matchAll(/^\s{2,}([A-Za-z][A-Za-z_0-9]*)\s*[,=]/gm)].map(m => m[1]);
assert.ok(members.length > 3, '要从 SDK 里读到 BlurStyle 成员');
for (const tier of ['NONE', 'COMPONENT_THIN', 'COMPONENT_REGULAR', 'COMPONENT_THICK']) {
assert.ok(members.includes(tier), `${tier} 必须是系统 BlurStyle 的成员`);
}
assert.ok(members.includes(sdk));
/*
* 另一半:**`blurStyleFor` 在整段可达输入上返回的每个值都必须是 SDK 的成员名**。
*
* 这里原先判的是页面里那张档位名→枚举的表(`NAV_MATERIAL_OF`,已随方案 (b) 删除)。
* 那张表随"导航条改回固定档"(pi 2026-09-15 的裁定,见 `harmony-nav` 的契约判据)
* 一起删了 —— 导航条现在直接用 `Theme.navMaterial`,不再经档位名中转。
*
* 但"**别自己发明档位名**"这件事仍然要判,而且现在**只有这一条在判它**:
* `blurStyleFor` 是档位名的唯一产生者(它现在是孤岛:判据在跑、页面没人调,
* 见 `Appearance.ts` 里那段如实说明)。所以改成枚举**它的整个值域**,
* 逐个对着 SDK 成员核 —— 比原来只核表里那三行**更严**(原来漏判的点现在覆盖了)。
*/
const produced = new Set();
for (let px = 0; px <= 40; px++) produced.add(A.blurStyleFor(px));
produced.add(A.blurStyleFor(-3));
produced.add(A.blurStyleFor(999));
produced.add(A.blurStyleFor(NaN));
assert.ok(produced.size >= 3, `可达输入上应当产出多档(实际 ${produced.size} 档:${[...produced].join('、')})`);
for (const tier of produced) {
assert.ok(members.includes(tier),
`\`blurStyleFor\` 产出了 \`${tier}\`,它不是 SDK 的 BlurStyle 成员` +
`(自造档位名 = 编译不过或静默不生效)。SDK 成员:${members.join('、')}`);
}
/*
* 而"通用映射允许 NONE"这一半必须**留着**:`0 px ⇒ 不模糊`是它的正确语义
* (用户要壁纸清晰是合法选择)。这条曾经被我写错过一次 —— 当时我断言
* "`blurStyleFor` 在 0..40 上不许返回 NONE",那条**恒红**,错的是判据不是代码。
*/
assert.ok(produced.has('NONE'),
'`blurStyleFor` 必须保留 `NONE`(0 px ⇒ 不模糊是合法语义)—— 导航条那边的下限不是靠篡改它实现的');
for (const tier of ['COMPONENT_THIN', 'COMPONENT_REGULAR', 'COMPONENT_THICK']) {
assert.ok(produced.has(tier),
`可达输入上应当覆盖 ${tier}(漏一档说明分档表被改坏了)`);
}
});
/*
* ── 这里原有的一条判据已删除(2026-09-15)──
*
* 它判的是 `blurStyleFor`:`bg_blur` px → `BlurStyle` 档位名的映射(分档边界 0/8/20、
* 单调性、NaN、以及"产出的名字必须是 SDK 成员")。**函数本身已按 pi 的裁定删除** ——
* 它没有任何调用点,唯一的计划消费者(导航条档位跟随 `bg_blur`)已被否决,
* 所以不是"暂时没消费者"而是**不该有**。理由碑文在 `Appearance.ts` 的 `blurStyleFor` 位置。
*
* ★ 删掉它**没有留下覆盖空洞**,这一点是我删之前专门核过的:
* "档位名必须是 SDK 的 `BlurStyle` 成员、且不是 NONE"这条性质,现在由
* `harmony-nav.test.mjs` 的「导航条材质是**固定系统档**」语义 C 直接判 ——
* 它读 `Theme.ets` 里 `navMaterial` 的**声明**,把档位名对着 SDK 枚举成员核。
* 而 `Theme.navMaterial` 正是**唯一剩下的**档位名产生者(原来那个函数没了)。
* ⇒ 覆盖面从"核一个没人调的函数的产出"变成"核真正在用的那个令牌",
* **判的还是同一件事,但对象终于是活的**。
*
* 教训留在这里(比删掉的那几条断言值钱):**"有测试"不等于"有人用"**。
* 这条判据全绿、逻辑正确,守的却是一个没有消费者的函数 ——
* 与 `Theme.navMaterial` 那个"只被不可达 `??` 分支引用"的死令牌是同一个形状。
*/
test('主题 → **系统色彩模式**(深浅两套颜色由系统给,不自己维护一套色值)', () => {
assert.equal(A.colorModeFor('system'), 'COLOR_MODE_NOT_SET', '跟随系统是默认档');
@ -929,26 +885,24 @@ test('★ 模糊字段的消费侧:逐文件登记 + 计数(P4c 起不再是
* 所以正确的登记是:**映射表存在且可判(本判据);缺的是调用点**。
* "有没有人用它"是另一件事,由上面那条消费侧计数判据管(登记值为 0)。
*/
test('★ blurStyleFor:分档边界、单调性、NaN 都是行为(0/8/20 是契约的一部分)', async () => {
test('★ 碑文:`blurStyleFor` 不许回来 + 理由必须留在原处(旧的三条行为性质随主语一起作废)', async () => {
const { pathToFileURL } = await import('node:url');
const A = await import(pathToFileURL(join(HARMONY_ETS, 'model', 'Appearance.ts')).href);
assert.equal(typeof A.blurStyleFor, 'function', 'blurStyleFor 必须存在且可跑');
// ★ 原先这里断言"必须存在且可跑"。事实相反:它是**有意删除**的(碑文 Wallpaper.ts:246)。
// ⇒ 断言改成**反回归**:不许回来。("标签必须等于断言范围":这条测试的标题也一并改了。)
assert.equal(typeof A.blurStyleFor, 'undefined',
'`blurStyleFor` 已被有意删除(2026-09-15):它没有、也不该有消费者 —— 不许加回来');
const cases = [[0, 'NONE'], [-5, 'NONE'], [1, 'COMPONENT_THIN'], [8, 'COMPONENT_THIN'],
[9, 'COMPONENT_REGULAR'], [20, 'COMPONENT_REGULAR'], [21, 'COMPONENT_THICK'],
[40, 'COMPONENT_THICK'], [999, 'COMPONENT_THICK']];
for (const [px, want] of cases) {
assert.equal(A.blurStyleFor(px), want, `bg_blur=${px} 应当映射到 ${want}`);
}
// 单调性:px 变大,档次不许倒退(分档写反是最容易犯的错)
const order = ['NONE', 'COMPONENT_THIN', 'COMPONENT_REGULAR', 'COMPONENT_THICK'];
let last = -1;
for (let px = 0; px <= 40; px++) {
const i = order.indexOf(A.blurStyleFor(px));
assert.ok(i >= last, `bg_blur=${px} 的档次倒退了(${order[last]} → ${order[i]})`);
last = i;
}
// NaN 不许落到最厚那一档:它只能来自坏数据,不该被解释成"最模糊"
assert.notEqual(A.blurStyleFor(Number.NaN), 'COMPONENT_THICK',
'NaN 不许落到最厚那一档(比较全 false 时掉到最后一档 —— 那是最坏的方向)');
/*
* 这一段(cases + 单调性 + NaN 方向)**整体作废,而且不是"改一改还能用"**:
* 它们的**主语**是一个已被删除的映射函数。常数没有"单调性",也没有"越界归到最近档",
* 更没有"NaN 掉到哪一档" —— 三条性质**随主语一起消失**了。
*
* 所以这里**不重新发明**它们,只留两句钉住"去哪了"与"为什么":
* · 那句碑文还在(理由必须留在原处,否则下一个人只会看到"这里什么都没有");
* · 常量本身仍被钉着(固定档 = COMPONENT_THICK,见本文件前半段那条现居地判据)。
* 判据**变少是对的**:性质没了就该少,硬造一条等价的只会是假判据。
*/
assert.match(prose(WALL_TS), /blurStyleFor[\s\S]{0,200}?已随/,
'碑文仍在原处:删除理由必须写在它被删掉的地方(否则下一个人看不出这里曾经有过什么)');
});