127 lines
3.6 KiB
JavaScript
127 lines
3.6 KiB
JavaScript
import assert from "node:assert/strict"
|
|
import test from "node:test"
|
|
import { readFile } from "node:fs/promises"
|
|
import ts from "typescript"
|
|
import vm from "node:vm"
|
|
|
|
async function loadModule() {
|
|
const source = await readFile(
|
|
new URL("./support-conversation-i18n.ts", import.meta.url),
|
|
"utf8"
|
|
)
|
|
const compiled = ts.transpileModule(source, {
|
|
compilerOptions: {
|
|
target: ts.ScriptTarget.ES2017,
|
|
module: ts.ModuleKind.CommonJS,
|
|
},
|
|
fileName: "support-conversation-i18n.ts",
|
|
})
|
|
const messages = {}
|
|
const sandbox = {
|
|
exports: {},
|
|
module: { exports: {} },
|
|
require: (name) => {
|
|
if (name === "@/i18n/messages") {
|
|
return {
|
|
translateCurrentMessage: (key) => messages[key] ?? key,
|
|
translateMessage: (_locale, key) => messages[key] ?? key,
|
|
}
|
|
}
|
|
throw new Error(`unexpected require: ${name}`)
|
|
},
|
|
}
|
|
sandbox.exports = sandbox.module.exports
|
|
vm.runInNewContext(compiled.outputText, sandbox)
|
|
return { module: sandbox.module.exports, messages }
|
|
}
|
|
|
|
const LOCALES = ["zh-CN", "en-US", "ru-RU"]
|
|
|
|
async function readMessages(locale) {
|
|
const raw = await readFile(
|
|
new URL(`../messages/${locale}.json`, import.meta.url),
|
|
"utf8"
|
|
)
|
|
return JSON.parse(raw)
|
|
}
|
|
|
|
test("every conversation status maps to a key that exists in all locales", async () => {
|
|
const { module, messages } = await loadModule()
|
|
const catalogues = Object.fromEntries(
|
|
await Promise.all(
|
|
LOCALES.map(async (locale) => [locale, await readMessages(locale)])
|
|
)
|
|
)
|
|
|
|
for (const locale of LOCALES) {
|
|
for (const status of [1, 2, 3, 4]) {
|
|
const key = module.getConversationStatusMessageKey(status)
|
|
const expected = lookup(catalogues[locale], key)
|
|
messages[key] = expected
|
|
assert.ok(expected, `missing ${key} in ${locale}`)
|
|
assert.equal(
|
|
module.getConversationStatusLabel(status),
|
|
expected,
|
|
`status ${status} is not localized for ${locale}`
|
|
)
|
|
}
|
|
}
|
|
})
|
|
|
|
test("no two statuses share a label in any locale", async () => {
|
|
const catalogues = Object.fromEntries(
|
|
await Promise.all(LOCALES.map(async (locale) => [locale, await readMessages(locale)]))
|
|
)
|
|
|
|
for (const locale of LOCALES) {
|
|
const labels = [1, 2, 3, 4].map((status) =>
|
|
lookup(catalogues[locale], statusKey(status))
|
|
)
|
|
assert.equal(
|
|
new Set(labels).size,
|
|
labels.length,
|
|
`duplicate status labels in ${locale}: ${labels.join(" / ")}`
|
|
)
|
|
}
|
|
})
|
|
|
|
// The helper deals in fully qualified "supportChat.x" keys while the catalogues
|
|
// are already scoped to the supportChat namespace.
|
|
function lookup(catalogue, key) {
|
|
return key.split(".").reduce((node, part) => node?.[part], catalogue)
|
|
}
|
|
|
|
function statusKey(status) {
|
|
return {
|
|
1: "supportChat.statusAiServing",
|
|
2: "supportChat.statusPending",
|
|
3: "supportChat.statusActive",
|
|
4: "supportChat.statusClosed",
|
|
}[status]
|
|
}
|
|
|
|
test("an unknown status still resolves to a defined label", async () => {
|
|
const { module, messages } = await loadModule()
|
|
const en = await readMessages("en-US")
|
|
messages[module.getConversationStatusMessageKey(99)] =
|
|
en.supportChat.statusClosed
|
|
|
|
assert.equal(
|
|
module.getConversationStatusLabel(99),
|
|
en.supportChat.statusClosed
|
|
)
|
|
assert.equal(
|
|
module.getConversationStatusMessageKey(0),
|
|
module.getConversationStatusMessageKey(4)
|
|
)
|
|
})
|
|
|
|
test("the fallback request title is localized everywhere", async () => {
|
|
const { module, messages } = await loadModule()
|
|
messages["supportChat.requestFallbackTitle"] = "Support request"
|
|
|
|
assert.equal(
|
|
module.getConversationFallbackTitle("ru-RU"),
|
|
"Support request"
|
|
)
|
|
}) |