Files
Scoreboard/console/main/userpaths.test.js
T
AskaEth 407abec3b0 fix: 大屏背景图被兜底渐变盖住 + 配置/规则也搬到用户数据目录
1) 大屏背景图不生效:App.vue 里 .bg 与 .bg-fallback 都是 absolute inset:0,
   而兜底渐变是不透明的且在图片之后绘制 → 配了背景图也只看得到渐变。
   改成 v-if/v-else 互斥(用无头 Chromium 真渲染对照验证:修复后取到图片像素,
   回退版本只剩渐变)。新增 background-layer.test.ts 锁住层次关系。

2) 规则/config 仍在安装目录,覆盖安装/卸载会连配置一起删:现在三样都落在
   %LOCALAPPDATA%\ScoreboardConsole\(与数据目录同一基座):
   - console\config.json(Console 读写都用这份)
   - config.yaml(Console 用绝对路径 -config 传给服务端与 passwd)
   - rules\(首次从安装目录复制过去,之后只认用户目录那份)
   首次运行复制迁移、已存在不覆盖、不可写则回落安装目录并记日志(不影响启动);
   开发态行为不变。备份脚本与文档同步。
   注意 resolveExecutable 必须显式传部署包根目录,否则会去用户目录找 scoreboard.exe。

自检:服务端 11 包全绿;console 语法 16 文件 + node:test 20/20;
前端 vue-tsc + build、node --test 69/69。
2026-09-10 10:06:21 +08:00

200 lines
9.1 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use strict'
const assert = require('node:assert/strict')
const fs = require('node:fs')
const os = require('node:os')
const path = require('node:path')
const test = require('node:test')
const {
resolveUserRoot,
planUserPaths,
prepareUserPaths,
withConfigArg
} = require('./userpaths')
// 用绝对路径字符串拼测试目录,避免依赖运行平台的盘符写法
const INSTALL = path.resolve('/scoreboard-install')
const LOCAL = path.resolve('/localappdata')
const USER_ROOT = path.join(LOCAL, 'ScoreboardConsole')
function makeRoot(t) {
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'scoreboard-userpaths-'))
t.after(() => fs.rmSync(root, { recursive: true, force: true }))
return root
}
function writeFile(file, text) {
fs.mkdirSync(path.dirname(file), { recursive: true })
fs.writeFileSync(file, text)
}
function readFile(file) {
return fs.readFileSync(file, 'utf8')
}
// 打包态 + 有 %LOCALAPPDATA% 是「接管」的唯一组合
function planFor({ installDir = INSTALL, env = { LOCALAPPDATA: LOCAL }, isPackaged = true } = {}) {
return planUserPaths({ installDir, userRoot: resolveUserRoot(env), isPackaged })
}
test('打包态三项都落到用户根下,与数据目录同一基座', () => {
const plan = planFor()
assert.equal(plan.mode, 'user')
assert.equal(plan.root, USER_ROOT)
assert.equal(plan.items.consoleConfig.target, path.join(USER_ROOT, 'console', 'config.json'))
assert.equal(plan.items.serverConfig.target, path.join(USER_ROOT, 'config.yaml'))
assert.equal(plan.items.rulesDir.target, path.join(USER_ROOT, 'rules'))
})
test('开发态不接管,三项都指向安装目录里随包的那份', () => {
const plan = planFor({ isPackaged: false })
assert.equal(plan.mode, 'install')
assert.equal(plan.reason, 'dev')
assert.equal(plan.items.consoleConfig.target, path.join(INSTALL, 'config.json'))
assert.equal(plan.items.serverConfig.target, path.join(INSTALL, 'config.yaml'))
assert.equal(plan.items.rulesDir.target, path.join(INSTALL, 'rules'))
})
test('拿不到 %LOCALAPPDATA% 时回落安装目录并记日志', () => {
const plan = planFor({ env: {} })
assert.equal(plan.mode, 'install')
assert.equal(plan.reason, 'no-localappdata')
const lines = []
const paths = prepareUserPaths({ plan, log: (line) => lines.push(line) })
assert.equal(paths.consoleConfig, path.join(INSTALL, 'config.json'))
assert.equal(paths.rulesDir, path.join(INSTALL, 'rules'))
assert.equal(paths.serverConfig, null)
assert.match(lines.join('\n'), /拿不到 %LOCALAPPDATA%/)
})
test('首次运行把随包的三项复制过去,安装目录那份留作模板', (t) => {
const root = makeRoot(t)
const installDir = path.join(root, 'install')
const userRoot = path.join(root, 'localappdata', 'ScoreboardConsole')
writeFile(path.join(installDir, 'config.json'), '{"server":{"port":19541}}')
writeFile(path.join(installDir, 'config.yaml'), 'server:\n port: 19541\n')
writeFile(path.join(installDir, 'rules', 'cup.yaml'), 'format: scoreboard-rules\n')
writeFile(path.join(installDir, 'rules', 'README.md'), '说明\n')
const lines = []
const paths = prepareUserPaths({
plan: planUserPaths({ installDir, userRoot, isPackaged: true }),
log: (line) => lines.push(line)
})
assert.equal(paths.consoleConfig, path.join(userRoot, 'console', 'config.json'))
assert.equal(paths.serverConfig, path.join(userRoot, 'config.yaml'))
assert.equal(paths.rulesDir, path.join(userRoot, 'rules'))
assert.equal(readFile(paths.consoleConfig), '{"server":{"port":19541}}')
assert.equal(readFile(paths.serverConfig), 'server:\n port: 19541\n')
assert.equal(readFile(path.join(userRoot, 'rules', 'cup.yaml')), 'format: scoreboard-rules\n')
assert.equal(readFile(path.join(userRoot, 'rules', 'README.md')), '说明\n')
// 复制不是移动:安装目录那三份必须原样留着当模板
assert.equal(readFile(path.join(installDir, 'config.json')), '{"server":{"port":19541}}')
assert.equal(readFile(path.join(installDir, 'config.yaml')), 'server:\n port: 19541\n')
assert.equal(fs.existsSync(path.join(installDir, 'rules', 'cup.yaml')), true)
assert.equal(lines.filter((line) => line.startsWith('已把 ')).length, 3)
})
test('用户目录已有那份就一律用它,不覆盖也不重拷', (t) => {
const root = makeRoot(t)
const installDir = path.join(root, 'install')
const userRoot = path.join(root, 'localappdata', 'ScoreboardConsole')
writeFile(path.join(installDir, 'config.json'), '{"server":{"port":1111}}')
writeFile(path.join(installDir, 'config.yaml'), 'server:\n port: 1111\n')
writeFile(path.join(installDir, 'rules', 'cup.yaml'), 'template\n')
writeFile(path.join(userRoot, 'console', 'config.json'), '{"server":{"port":2222}}')
writeFile(path.join(userRoot, 'config.yaml'), 'server:\n port: 2222\n')
writeFile(path.join(userRoot, 'rules', 'mine.yaml'), 'mine\n')
const lines = []
const paths = prepareUserPaths({
plan: planUserPaths({ installDir, userRoot, isPackaged: true }),
log: (line) => lines.push(line)
})
assert.equal(readFile(paths.consoleConfig), '{"server":{"port":2222}}')
assert.equal(readFile(paths.serverConfig), 'server:\n port: 2222\n')
assert.equal(readFile(path.join(userRoot, 'rules', 'mine.yaml')), 'mine\n')
// 用户目录里的 rules\ 已存在(哪怕内容和模板不同)→ 不补拷模板里的 cup.yaml
assert.equal(fs.existsSync(path.join(userRoot, 'rules', 'cup.yaml')), false)
assert.equal(lines.filter((line) => line.startsWith('已把 ')).length, 0)
})
test('用户目录写不进去时记日志并回落到安装目录,不抛异常', (t) => {
const root = makeRoot(t)
const installDir = path.join(root, 'install')
writeFile(path.join(installDir, 'config.json'), '{}')
writeFile(path.join(installDir, 'config.yaml'), 'server: {}\n')
writeFile(path.join(installDir, 'rules', 'cup.yaml'), 'template\n')
// 用户根的位置被一个文件占着:mkdir/rename 必然失败(ENOTDIR)
const userRoot = path.join(root, 'blocker')
writeFile(userRoot, 'not a dir')
const lines = []
const paths = prepareUserPaths({
plan: planUserPaths({ installDir, userRoot, isPackaged: true }),
log: (line) => lines.push(line)
})
assert.equal(paths.consoleConfig, path.join(installDir, 'config.json'))
assert.equal(paths.serverConfig, null)
assert.equal(paths.rulesDir, path.join(installDir, 'rules'))
assert.equal(readFile(paths.consoleConfig), '{}')
assert.equal(readFile(path.join(installDir, 'rules', 'cup.yaml')), 'template\n')
assert.equal(lines.filter((line) => line.includes('继续用安装目录里的')).length, 3)
})
test('两边都没有的那一份跳过复制,路径仍指向用户目录', (t) => {
const root = makeRoot(t)
const installDir = path.join(root, 'install')
const userRoot = path.join(root, 'localappdata', 'ScoreboardConsole')
fs.mkdirSync(installDir, { recursive: true })
const lines = []
const paths = prepareUserPaths({
plan: planUserPaths({ installDir, userRoot, isPackaged: true }),
log: (line) => lines.push(line)
})
// 不接管就没人去读用户目录,服务端会按 config.Load 的默认值跑
assert.equal(paths.serverConfig, path.join(userRoot, 'config.yaml'))
assert.equal(fs.existsSync(paths.serverConfig), false)
assert.equal(lines.filter((line) => line.includes('本次跳过复制')).length, 3)
})
test('规则目录拷贝到位后不留临时目录,重复运行不重做', (t) => {
const root = makeRoot(t)
const installDir = path.join(root, 'install')
const userRoot = path.join(root, 'localappdata', 'ScoreboardConsole')
writeFile(path.join(installDir, 'config.json'), '{}\n')
writeFile(path.join(installDir, 'config.yaml'), 'server: {}\n')
writeFile(path.join(installDir, 'rules', 'cup.yaml'), 'template\n')
const plan = planUserPaths({ installDir, userRoot, isPackaged: true })
const first = prepareUserPaths({ plan, log: () => {} })
assert.deepEqual(fs.readdirSync(userRoot).sort(), ['config.yaml', 'console', 'rules'])
assert.deepEqual(fs.readdirSync(first.rulesDir), ['cup.yaml'])
// 第二次启动:目标都在了,不再复制(用户改过的内容不会被模板顶掉)
writeFile(path.join(userRoot, 'config.yaml'), 'server:\n port: 3333\n')
const second = prepareUserPaths({ plan, log: () => {} })
assert.deepEqual(second, first)
assert.equal(readFile(path.join(userRoot, 'config.yaml')), 'server:\n port: 3333\n')
})
test('-config 的值换成解析出的绝对路径,其余参数原样保留', () => {
assert.deepEqual(withConfigArg(['-config', 'config.yaml'], '/u/config.yaml'), ['-config', '/u/config.yaml'])
assert.deepEqual(
withConfigArg(['-config', 'config.yaml', '-project', 'cup'], '/u/config.yaml'),
['-config', '/u/config.yaml', '-project', 'cup']
)
// 参数里没写 -config 就补一条,否则用户目录里那份永远不会被读到
assert.deepEqual(withConfigArg(['-project', 'cup'], '/u/config.yaml'), ['-project', 'cup', '-config', '/u/config.yaml'])
// 不接管(开发态)原样返回
assert.deepEqual(withConfigArg(['-config', 'config.yaml'], null), ['-config', 'config.yaml'])
assert.deepEqual(withConfigArg(null, '/u/config.yaml'), ['-config', '/u/config.yaml'])
})