Files
danyang-match/ui-tests/tabbar-icons.mjs
T

123 lines
6.0 KiB
JavaScript
Raw Normal View History

2026-09-10 22:00:17 +08:00
import assert from 'node:assert/strict'
import { readFileSync, existsSync } from 'node:fs'
2026-09-10 22:00:17 +08:00
import { join } from 'node:path'
const repoRoot = process.cwd()
const root = join(repoRoot, 'miniapp', 'assets', 'tabbar')
2026-09-10 22:00:17 +08:00
const icons = {
home: '房子',
discover: '爱心',
message: '对话气泡',
activity: '日历',
mine: '人物',
2026-09-10 22:00:17 +08:00
}
// ── 1. SVG 源文件:主题化标题 + 两态配色 ──────────────────────────────
2026-09-10 22:00:17 +08:00
for (const [name, title] of Object.entries(icons)) {
const normal = readFileSync(join(root, 'svg', `${name}.svg`), 'utf8')
const active = readFileSync(join(root, 'svg', `${name}-active.svg`), 'utf8')
assert.match(normal, new RegExp(`<title>${title}</title>`), `${name} 缺少主题化标题`)
assert.match(normal, /#47528C/i, `${name} 默认态颜色错误`)
assert.match(active, /#F4005A/i, `${name} 选中态颜色错误`)
2026-09-10 22:00:17 +08:00
// ── 2. PNG 规格:正方形、1x/2x/3x、透明 RGBA ────────────────────────
2026-09-10 22:00:17 +08:00
for (const suffix of ['', '_2x', '_3x']) {
const expectedSize = suffix === '' ? 81 : suffix === '_2x' ? 162 : 243
for (const state of ['', '-active']) {
const png = readFileSync(join(root, `${name}${state}${suffix}.png`))
assert.equal(png.readUInt32BE(16), expectedSize)
assert.equal(png.readUInt32BE(20), expectedSize)
assert.equal(png[25], 6, `${name}${state}${suffix} 必须为透明 RGBA PNG`)
}
}
}
// ── 3. app.json ↔ 磁盘一致性 ─────────────────────────────────────────
// 历史缺陷:tabBar 图标只被 app.json 引用(不经 WXML/JS),
// 一旦配置漂移,不会有任何 WXML 侧症状,只会「底部导航整排变破图」。
const appJsonPath = join(repoRoot, 'miniapp', 'app.json')
const appJson = JSON.parse(readFileSync(appJsonPath, 'utf8'))
const tabList = appJson.tabBar?.list ?? []
assert.equal(tabList.length, 5, 'tabBar.list 必须为 5 项(微信支持 2~5)')
// 文字与图标同色:color 必须等于 SVG 默认态色,selectedColor 必须等于选中态色,
// 否则会出现「图标是藏青蓝/玫红、文字却是别的颜色」的割裂。
assert.equal(appJson.tabBar?.color, '#47528C', 'tabBar.color 必须与图标默认态同色(#47528C)')
assert.equal(appJson.tabBar?.selectedColor, '#F4005A', 'tabBar.selectedColor 必须与图标选中态同色(#F4005A)')
for (const item of tabList) {
assert.ok(appJson.pages.includes(item.pagePath), `tabBar pagePath 未在 pages 中注册: ${item.pagePath}`)
for (const key of ['iconPath', 'selectedIconPath']) {
const rel = item[key]
assert.ok(rel, `tabBar ${item.pagePath} 缺少 ${key}`)
assert.ok(!rel.startsWith('/'), `tabBar ${key} 不应以 / 开头(微信按小程序根解析): ${rel}`)
assert.ok(
existsSync(join(repoRoot, 'miniapp', rel)),
`tabBar ${key} 指向不存在的文件: ${rel}`,
)
}
}
// ── 4. 「无依赖文件过滤」必须关闭(tabBar 图标整排变破图的根因)────────
// 微信开发者工具自 1.05.2112301 起默认开启该能力:它按 WXML/JS 依赖分析,
// app.json 的 tabBar 图标不被视为依赖 → 被从开发包中剔除 → 图标全部加载失败。
// 官方缓解方式即 ignoreDevUnusedFiles / ignoreUploadUnusedFiles 置 false。
const CONFIGS = [
'project.config.json',
'miniapp/project.config.json',
'project.private.config.json',
'miniapp/project.private.config.json',
]
const requiredOff = ['project.config.json', 'miniapp/project.config.json']
for (const rel of CONFIGS) {
const p = join(repoRoot, rel)
if (!existsSync(p)) continue // 私有配置被 gitignore,缺失属正常
const setting = JSON.parse(readFileSync(p, 'utf8')).setting ?? {}
assert.notEqual(
setting.ignoreDevUnusedFiles, true,
`${rel} 开启了无依赖文件过滤(ignoreDevUnusedFiles),会剔除 app.json 独占引用的 tabBar 图标,导致底部导航整排破图`,
)
assert.notEqual(
setting.ignoreUploadUnusedFiles, true,
`${rel} 开启了上传期无依赖文件过滤(ignoreUploadUnusedFiles),体验版/正式版会丢失 tabBar 图标`,
)
}
for (const rel of requiredOff) {
const p = join(repoRoot, rel)
assert.ok(existsSync(p), `缺少共享配置 ${rel}`)
const setting = JSON.parse(readFileSync(p, 'utf8')).setting ?? {}
assert.equal(
setting.ignoreDevUnusedFiles, false,
`${rel} 必须显式关闭 ignoreDevUnusedFiles(新克隆默认值为 true 会复现破图)`,
)
assert.equal(
setting.ignoreUploadUnusedFiles, false,
`${rel} 必须显式关闭 ignoreUploadUnusedFiles`,
)
}
// ── 5. 静态预览必须改写资源路径 ──────────────────────────────────────
// WXML/WXSS 写的是小程序根绝对路径 /assets/...,浏览器 file:// 打开会解析到
// 文件系统根 → 全部 404(表现为破图)。预览产出必须改写为相对路径,
// 并复刻 app.json 原生 tabBar(它不在任何 WXML 里)。
const previewSrc = readFileSync(join(repoRoot, 'scripts', 'preview-miniapp.mjs'), 'utf8')
assert.match(previewSrc, /const rewriteAssetUrls\s*=/, '预览脚本缺少资源路径改写器')
assert.match(
previewSrc, /writeFileSync\(outFile,\s*rewriteAssetUrls\(html\)/,
'预览脚本写盘时未应用资源路径改写 → file:// 下图片会全部 404',
)
assert.match(previewSrc, /function renderTabbar\(/, '预览脚本缺少原生 tabBar 复刻')
const previewHtml = join(repoRoot, 'artifacts', 'ui-redesign', 'miniapp', 'preview.html')
if (existsSync(previewHtml)) {
const html = readFileSync(previewHtml, 'utf8')
const absoluteSrc = (html.match(/src=["']\/assets\//g) || []).length
assert.equal(absoluteSrc, 0, `preview.html 仍有 ${absoluteSrc} 处根绝对资源路径,浏览器打开会破图`)
assert.match(html, /class="mini-tabbar"/, 'preview.html 缺少复刻的底部 tabBar')
}
2026-09-10 22:00:17 +08:00
console.log('Tabbar icon contracts passed')