Files
CyberStrikeAI/web/static/js/form-validation.test.cjs
T

63 lines
3.1 KiB
JavaScript

const fs = require('node:fs');
const vm = require('node:vm');
const test = require('node:test');
const assert = require('node:assert/strict');
const taskSource = fs.readFileSync('web/static/js/tasks.js','utf8');
function concurrencyHarness(value) {
let calls = 0, message, focused = false;
const input = {value, focus() { focused = true; }};
const ctx = {_bqInlineSaving:false, batchQueuesState:{currentQueueId:'q'},
document:{getElementById:() => input}, _t:x => x, alert:x => {message=x;},
apiFetch:async () => {calls++;return {ok:true,json:async () => ({queue:{title:'title'}})};},
showBatchQueueDetail() {},refreshBatchQueues() {},console};
vm.createContext(ctx);
const start = taskSource.indexOf('async function saveInlineConcurrency(');
vm.runInContext(taskSource.slice(start,taskSource.indexOf('// --- 单条执行 ---',start)),ctx);
return {ctx,calls:() => calls,message:() => message,focused:() => focused};
}
test('invalid concurrency leaves the editor open and makes no API calls', async () => {
for (const value of ['0','9','1.5','2abc','']) {
const h=concurrencyHarness(value);
await h.ctx.saveInlineConcurrency();
assert.equal(h.calls(),0);
assert.equal(h.message(),'tasks.concurrencyInvalid');
assert.equal(h.focused(),true);
assert.equal(h.ctx._bqInlineSaving,false);
}
});
test('valid concurrency updates and refreshes normally',async () => {
for (const value of ['1','8']) {
const h=concurrencyHarness(value);
await h.ctx.saveInlineConcurrency();
assert.equal(h.calls(),2);
assert.equal(h.message(),undefined);
}
});
test('blank project name stops before making a save request',async () => {
const source=fs.readFileSync('web/static/js/projects.js','utf8');
let calls=0,message;
const ctx={currentProjectId:'p',requireProjectWrite:() => true,
document:{getElementById:() => ({value:' '})},tp:x => x,alert:x => {message=x;},apiFetch:() => {calls++;}};
vm.createContext(ctx);
const start=source.indexOf('async function saveProjectSettings(');
vm.runInContext(source.slice(start,source.indexOf('function findProjectById(',start)),ctx);
await ctx.saveProjectSettings();
assert.equal(calls,0);
assert.equal(message,'projects.enterProjectName');
});
test('RBAC creation buttons use the existing permission visibility guard',() => {
const html=fs.readFileSync('web/templates/index.html','utf8');
const buttons=html.match(/<button\b[^>]*onclick="openRbac(?:Role|User)Modal\(\)"[^>]*>/g);
assert.ok(buttons.length>=3);
for (const b of buttons) assert.match(b,/data-require-permission="rbac:write"/);
const auth=fs.readFileSync('web/static/js/auth.js','utf8');
const ctx={permissionAllowedForElement:() => false};
vm.createContext(ctx);
const start=auth.indexOf('function applyPermissionElement(');
vm.runInContext(auth.slice(start,auth.indexOf('let permissionClickGuardInstalled',start)),ctx);
const button={disabled:false,hidden:false,getAttribute:key => key==='data-require-permission'?'rbac:write':null,classList:{toggle() {}},setAttribute() {}};
ctx.applyPermissionElement(button);
assert.equal(button.hidden,true);
assert.equal(button.disabled,true);
});