This commit is contained in:
commit
5ec99ce227
668 files changed
+164448
No files matched your search
@@ -0,0 +1,93 @@
|
||||
/**
|
||||
* `web/i18n.js` 的品牌文案渲染回归(2026-09-19,档案 134 配套)。
|
||||
*
|
||||
* 为什么单独钉这一条:品牌标识从 DeepSeek 图形换成了**文字**(中文「能力网络」(2026-09-19
|
||||
* 定名)/英文及其他语言 `CapabilityNet`)。词条漏配或 key 写错时,i18n 会**静默回退英文**
|
||||
* —— 页面上看不出报错,只有中文用户看到英文名。这类"静默失败"必须靠测试钉住。
|
||||
*
|
||||
* 做法:用 `node:vm` 跑**仓库里那份真实的 `web/i18n.js`**(不是复制一遍逻辑),
|
||||
* 配一个够用的最小 DOM 桩,断言六种语言解析路径下的**实际渲染结果**。
|
||||
*
|
||||
* ⚠️ 桩里必须注入 `URLSearchParams` / `URL`:`vm` 上下文只含 ECMAScript 内置、
|
||||
* 不含 Web API;少了它们 `new URLSearchParams(location.search)` 会抛 ReferenceError,
|
||||
* 被 i18n.js 的 try/catch 吞掉 ⇒ 表现为"`?lang=` 不生效"的**假失败**(第一版踩过)。
|
||||
*/
|
||||
import { test } from 'node:test'
|
||||
import assert from 'node:assert/strict'
|
||||
import { readFileSync } from 'node:fs'
|
||||
import vm from 'node:vm'
|
||||
|
||||
const SRC = readFileSync(new URL('../web/i18n.js', import.meta.url), 'utf8')
|
||||
|
||||
/** 造一个够 i18n.js 用的最小元素桩(只需要 getAttribute / textContent)。 */
|
||||
function makeEl(attrs = {}) {
|
||||
return {
|
||||
_attrs: { ...attrs },
|
||||
textContent: attrs.__text ?? '',
|
||||
className: '',
|
||||
innerHTML: '',
|
||||
style: {},
|
||||
getAttribute(name) {
|
||||
return Object.prototype.hasOwnProperty.call(this._attrs, name) ? this._attrs[name] : null
|
||||
},
|
||||
setAttribute(name, value) {
|
||||
this._attrs[name] = value
|
||||
},
|
||||
appendChild() {},
|
||||
addEventListener() {},
|
||||
querySelectorAll() {
|
||||
return []
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/** 在最小 DOM 桩里跑一次 i18n.js,返回品牌节点的实际渲染结果与解析出的语言。 */
|
||||
function renderBrand({ search = '', cookie = '', language = 'en-US' } = {}) {
|
||||
const brandNode = makeEl({ 'data-i18n': 'brand.name', __text: 'CapabilityNet' })
|
||||
const sandbox = {
|
||||
console,
|
||||
setTimeout: () => {},
|
||||
URLSearchParams,
|
||||
URL,
|
||||
document: {
|
||||
readyState: 'complete',
|
||||
cookie,
|
||||
documentElement: makeEl(),
|
||||
querySelectorAll(selector) {
|
||||
return selector === '[data-i18n]' ? [brandNode] : []
|
||||
},
|
||||
createElement: () => makeEl(),
|
||||
addEventListener: () => {},
|
||||
},
|
||||
navigator: { language, languages: [language] },
|
||||
location: { search, href: `https://example.net/login.html${search}`, reload: () => {} },
|
||||
window: {},
|
||||
}
|
||||
sandbox.window = sandbox
|
||||
vm.createContext(sandbox)
|
||||
vm.runInContext(SRC, sandbox, { filename: 'web/i18n.js' })
|
||||
return { brand: brandNode.textContent, lang: sandbox.window.I18N.lang }
|
||||
}
|
||||
|
||||
test('品牌文案:中文显示「能力网络」,英文及其他语言显示 CapabilityNet', () => {
|
||||
const cases = [
|
||||
['默认(浏览器 en-US)', { language: 'en-US' }, 'CapabilityNet'],
|
||||
['中文(浏览器 zh-CN)', { language: 'zh-CN' }, '能力网络'],
|
||||
['中文(?lang=zh)', { search: '?lang=zh', language: 'en-US' }, '能力网络'],
|
||||
['英文(?lang=en 覆盖中文浏览器)', { search: '?lang=en', language: 'zh-CN' }, 'CapabilityNet'],
|
||||
['cookie dsh_lang=zh', { cookie: 'dsh_lang=zh', language: 'en-US' }, '能力网络'],
|
||||
['未支持语言(ja)⇒ 回退英文', { language: 'ja-JP' }, 'CapabilityNet'],
|
||||
]
|
||||
for (const [label, opts, expected] of cases) {
|
||||
const got = renderBrand(opts)
|
||||
assert.equal(got.brand, expected, label)
|
||||
}
|
||||
})
|
||||
|
||||
test('品牌文案:不会退化成裸 key、不留空', () => {
|
||||
for (const language of ['en-US', 'zh-CN', 'ja-JP', 'de-DE']) {
|
||||
const { brand } = renderBrand({ language })
|
||||
assert.notEqual(brand, 'brand.name', `裸 key 泄露(${language})`)
|
||||
assert.ok(brand.trim() !== '', `空文案(${language})`)
|
||||
}
|
||||
})
|
||||
Reference in new issue
Block a user