/** * 两个客户端必须用**同一份设计词表**。 * * 用户下一步要求:「同步 ui 设计到客户端」。同步的第一件事不是把每个页面重画一遍, * 而是两边共用同一套令牌(颜色/圆角/玻璃透明度)—— 否则每加一个页面就重抄一遍色值, * 两个客户端会越走越远,而且这种漂移**没有任何判据会红**。 * * 这里只断言"两边对同一件事的取值一致",不断言实现方式(WebUI 用 CSS 变量、 * 鸿蒙用 ArkTS 常量,本来就该不同)。 */ import { test } from 'node:test'; import assert from 'node:assert/strict'; import { readFileSync } from 'node:fs'; import { dirname, join } from 'node:path'; import { fileURLToPath } from 'node:url'; const HERE = dirname(fileURLToPath(import.meta.url)); const ROOT = join(HERE, '..', '..', '..'); const web = readFileSync(join(ROOT, 'client/electron/src/index.css'), 'utf8'); const harmony = readFileSync( join(ROOT, 'client/harmony/entry/src/main/ets/common/Theme.ets'), 'utf8' ); const hex = h => h.toUpperCase(); test('品牌蓝一致', () => { // WebUI: --c-blue-600 应该是 37 99 235 = #2563EB const webBlue = web.match(/--c-blue-600:\s*(\d+)\s+(\d+)\s+(\d+)/); assert.ok(webBlue, 'WebUI 要有 --c-blue-600'); const asHex = hex( '#' + [webBlue[1], webBlue[2], webBlue[3]] .map(n => Number(n).toString(16).padStart(2, '0')) .join('') ); const harmonyBlue = harmony.match(/accent: string = '(#[0-9A-Fa-f]{6})'/); assert.ok(harmonyBlue, '鸿蒙要有 accent'); assert.equal(hex(harmonyBlue[1]), asHex, `品牌蓝不一致:WebUI ${asHex} vs 鸿蒙 ${harmonyBlue[1]}`); }); test('卡片圆角一致(WebUI 0.875rem = 14px)', () => { assert.match(web, /--radius-card:\s*0\.875rem/); assert.match(harmony, /radiusCard: number = 14/); }); test('导航玻璃不透明度一致(都是 0.72)', () => { assert.match(web, /--nav-bg:\s*255 255 255 \/ 0\.72/); // 0.72 × 255 ≈ 184 = 0xB8(鸿蒙用 #AARRGGBB,顺序与 CSS 不同) assert.match(harmony, /navBgLight: string = '#B8FFFFFF'/); }); test('★ 判据自检:把鸿蒙的品牌蓝改成别的必须判红', () => { const mutated = harmony.replace(/accent: string = '#2563EB'/, "accent: string = '#FF0000'"); const m = mutated.match(/accent: string = '(#[0-9A-Fa-f]{6})'/); assert.notEqual(hex(m[1]), '#2563EB'); }); test('鸿蒙的令牌文件说明了与 WebUI 的对应关系(不是凭空一套)', () => { assert.match(harmony, /与 WebUI 的令牌\*\*一一对应|对应 WebUI/); });