feat: 为高频变更 JS 文件补充 smoke test 并建立 js-test-gate Rule

新增测试:
- api.test.js: 28 个用例覆盖 VpsFmt 纯工具函数(OAuth 归一化、倒计时文案、费用文案、徽章样式)
- app.test.js: 15 个用例覆盖 NAVS/VIEW_MAP/VALID_VIEWS 常量和 _viewFromHash 路由解析
- modals.test.js: 3 个用例验证 9 个模态框组件定义完整性

规则与钩子:
- 新建 .qoder/rules/js-test-gate.md:编辑 static/js/ 下 JS 文件后运行 vitest
- 更新 post-edit-check.sh:JS 编辑后 node --check + vitest(排除 vendored 文件)

部署闸门 check_js_tests(update.sh)已有 vitest run 步骤,现在测试覆盖从 1/7 文件提升到 4/7
This commit is contained in:
gouki
2026-10-15 21:20:52 +00:00
parent 90695c82fd
commit a63fcb786f
5 changed files with 539 additions and 2 deletions
+62
View File
@@ -0,0 +1,62 @@
/**
* modals.js smoke test
*
* 验证 9 个 Vue 模态框组件均已正确定义(含 template + setup)。
* modals.js 只有 const XModal = { template, setup } 声明,
* setup() 内的外部引用(store / Fmt / Vue)只在 Vue 挂载时执行,
* 因此 VM 沙箱无需注入完整 mock 即可验证组件结构。
*/
import { describe, it, expect, beforeAll } from 'vitest';
import { readFileSync } from 'node:fs';
import { resolve } from 'node:path';
import vm from 'node:vm';
let ctx;
const EXPECTED_MODALS = [
'AssetModal',
'ProviderModal',
'AccountModal',
'AccountsViewModal',
'CredentialModal',
'ActionModal',
'DnsModal',
'ConfirmModal',
'ResultModal',
];
beforeAll(() => {
ctx = vm.createContext({ console });
ctx.window = ctx;
let code = readFileSync(
resolve(import.meta.dirname, '..', 'modals.js'),
'utf-8',
);
// 同 store.test.js 约定:顶格 const → globalThis,使测试可通过 ctx.X 访问
code = code.replace(/^const\s+(?!\{)(\w+)\s*=/gm, 'globalThis.$1 =');
vm.runInContext(code, ctx);
});
describe('modals.js 组件定义完整性', () => {
it('全部 9 个模态框组件均已定义', () => {
for (const name of EXPECTED_MODALS) {
expect(ctx[name], `${name} 未定义`).toBeDefined();
}
});
it('每个组件均包含 template 字符串和 setup 函数', () => {
for (const name of EXPECTED_MODALS) {
const comp = ctx[name];
expect(typeof comp.template, `${name}.template 非字符串`).toBe('string');
expect(comp.template.length, `${name}.template 为空`).toBeGreaterThan(10);
expect(typeof comp.setup, `${name}.setup 非函数`).toBe('function');
}
});
it('template 包含 v-if 条件渲染(模态框显隐控制)', () => {
for (const name of EXPECTED_MODALS) {
expect(ctx[name].template, `${name}.template 缺少 v-if`).toContain('v-if');
}
});
});