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 detailPage = loadSource('pages/accounts/$id/index.tsx'); const fingerprintFields = loadSource('components/accounts/FingerprintFields.tsx'); const createPage = loadSource('components/accounts/AccountCreateModal.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\(','\)/); }); for (const [pageName, page] of [['详情页', detailPage], ['编辑页', editPage]]) { test(`${pageName}账号标题不保留上下外边距`, () => { assert.match(page, /Typography\.Title level=\{5\} style=\{\{ margin: 0 \}\}/); }); } test('账号创建成功通知列表,不跳转页面', () => { assert.match(createPage, /onSuccess\(\)/); assert.doesNotMatch(createPage, /history\./); }); test('资料保存不承载独立登录弹窗的处理中状态', () => { assert.match(editPage, /htmlType="submit" loading=\{busy\}/); assert.match(editPage, /const \[loginOpen, setLoginOpen\] = useState\(false\)/); assert.match(editPage, /onClick=\{\(\) => setLoginOpen\(true\)\}/); assert.match(editPage, / { assert.doesNotMatch(createPage, /platform_account_key/); assert.match(editPage, /label="UID"/); assert.match(editPage, /copyable>\{selected\.platform_account_key\}/); assert.match(editPage, /首次登录核验后自动绑定/); assert.doesNotMatch(editPage, /name="platform_account_key"/); assert.match(detailPage, /label="UID"/); }); test('创建弹窗与编辑页共用同一指纹字段组件', () => { assert.ok(createPage.includes("from '@/components/accounts/FingerprintFields'")); assert.ok(createPage.includes('')); assert.match(createPage, /fingerprintPayload\(values\.fingerprint\)/); });