const assert = require('node:assert/strict'); const { readFileSync } = require('node:fs'); const { test } = require('node:test'); const ts = require('typescript'); function loadSource(relativePath) { return ts.createSourceFile( relativePath, readFileSync(`${__dirname}/../src/${relativePath}`, 'utf8'), ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX, ).getFullText(); } const editPage = loadSource('pages/accounts/$id/edit.tsx'); const fingerprintFields = loadSource('components/accounts/FingerprintFields.tsx'); const createPage = loadSource('pages/accounts/new.tsx'); test('账号资料表单按三行分组:登录用户名/更新密码/业务状态、实名三项、账号备注', () => { // 第一行:登录用户名、更新密码、业务状态在同一个 Row 中按序出现 const firstRow = editPage.indexOf(''); const businessStatus = editPage.indexOf('name="business_status"'); const secondRow = editPage.indexOf('', firstRow + 1); assert.ok(firstRow > -1 && businessStatus > firstRow && businessStatus < secondRow, '登录用户名/更新密码/业务状态应同属第一行'); assert.ok(editPage.includes('name="login_username"') && editPage.includes('name="password"'), '第一行应包含登录用户名与更新密码'); // 第二行:实名状态、实名姓名、实名证件号 const noteItem = editPage.indexOf('name="note"'); assert.ok(secondRow > firstRow && secondRow < noteItem, '实名三项应与第一行分行'); assert.ok( editPage.includes('name="real_name_status"') && editPage.includes('name="real_name"') && editPage.includes('name="identity_number"'), '第二行应包含实名状态、实名姓名、实名证件号', ); // 账号备注独占一行(不包在 Row/Col 中) assert.ok( !editPage.slice(editPage.lastIndexOf('', noteItem), noteItem).includes('name="note"'), '账号备注不应在 Row 内', ); }); test('指纹浏览器环境独立卡片单独保存,不混入账号资料保存', () => { assert.ok(editPage.includes('title="指纹浏览器环境"'), '指纹环境应为独立卡片'); assert.ok(editPage.includes('>保存指纹<') || editPage.includes('保存指纹'), '指纹卡片应有独立保存按钮'); assert.match(editPage, /\/creator\/accounts\/\$\{encodeURIComponent\(selected\.id\)\}\/fingerprint/); assert.ok(editPage.includes(''), '指纹字段应复用共享组件'); assert.ok(editPage.includes('fpForm.validateFields()'), '指纹保存前应先校验表单'); }); test('指纹保存后按运行状态提示:运行中重启 / 未运行下次启动生效', () => { assert.ok(editPage.includes("result?.runtime_id ? '指纹已保存,运行环境已用新配置重启。' : '指纹已保存,将在环境下次启动时生效。'")); }); test('编辑页加载时从账号详情内联 environment.fingerprint 回显指纹', () => { assert.ok(editPage.includes("getOne('accounts', selectedID)"), '编辑页应请求账号详情'); assert.match(editPage, /fingerprintFormValues\(detail\?\.environment\?\.fingerprint\)/); }); test('账号资料保存保留现有冷却时间,不再发送 NaN', () => { assert.ok( editPage.includes('cooldown_seconds: selected.cooldown_seconds'), 'cooldown_seconds 应透传当前值,避免 Number(undefined)=NaN 被 400 拒绝', ); assert.ok(!editPage.includes('Number(values.cooldown_seconds)'), '不得再从缺失的表单字段计算冷却时间'); }); test('共享指纹字段:载荷只含非空项,disable_spoofing 以逗号合并', () => { assert.match(fingerprintFields, /fp\.disable_spoofing\?\.length\) payload\.disable_spoofing = fp\.disable_spoofing\.join\(','\)/); assert.match(fingerprintFields, /if \(fp\.timezone\?\.trim\(\)\) payload\.timezone = fp\.timezone\.trim\(\)/); assert.match(fingerprintFields, /payload\.hardware_concurrency = fp\.hardware_concurrency/); }); test('共享指纹字段:回显把逗号字符串拆回多选数组', () => { assert.match(fingerprintFields, /String\(fp\.disable_spoofing\)\.split\(','\)/); }); test('创建页与编辑页共用同一指纹字段组件', () => { assert.ok(createPage.includes("from '@/components/accounts/FingerprintFields'")); assert.ok(createPage.includes('')); assert.match(createPage, /fingerprintPayload\(values\.fingerprint\)/); });