const test = require('node:test'); const assert = require('node:assert/strict'); const fs = require('node:fs'); const rbacPermissionsScript = fs.readFileSync(require.resolve('../src/web/public/js/rbac-permissions.js'), 'utf8'); const rbacFormTemplate = fs.readFileSync(require.resolve('../src/web/views/settings/rbac/form.hbs'), 'utf8'); const rbacRoutesSource = fs.readFileSync(require.resolve('../src/web/routes/admin/rbac.js'), 'utf8'); const { buildPermissionSections, buildRbacEditViewModel } = require('../src/web/routes/settings/rbac/form-view-model'); const rbacFormViewModel = fs.readFileSync(require.resolve('../src/web/routes/settings/rbac/form-view-model.js'), 'utf8'); const rbacSource = fs.readFileSync(require.resolve('../src/rbac.js'), 'utf8'); test('rbac form exposes bulk permission controls', () => { assert.match(rbacFormTemplate, /name="permissions_present" value="1"/); assert.match(rbacFormTemplate, /name="user_ids_present\[\]" value="\{\{id\}\}"/); assert.match(rbacFormTemplate, /accordion accordion-flush/); assert.match(rbacFormTemplate, /rbac-permissions-table-head/); assert.ok(!rbacFormTemplate.includes('table-light')); assert.match(rbacFormTemplate, /

Permissions<\/h3>[\s\S]*
[\s\S]*accordion accordion-flush/); assert.match(rbacFormTemplate, /card card-outline card-secondary admin-form-card overflow-hidden[\s\S]*

Permissions<\/h3>/); assert.ok(!rbacFormTemplate.includes('card card-outline card-secondary overflow-hidden')); assert.match(rbacFormTemplate, /accordion-item overflow-hidden" data-permission-section/); assert.match(rbacFormTemplate, /accordion-body p-0/); assert.match(rbacFormTemplate, /data-bs-parent="#role-permissions-accordion"/); assert.match(rbacFormTemplate, /table-layout: fixed; width: 100%;/); assert.match(rbacFormTemplate, //); assert.match(rbacFormTemplate, /class="text-center"/); assert.match(rbacFormTemplate, /data-permission-row-toggle/); assert.ok(rbacFormTemplate.includes('text-reset')); assert.ok(rbacFormTemplate.includes('color: inherit;')); assert.ok(rbacFormTemplate.includes('text-decoration-style: dotted')); assert.ok(rbacFormTemplate.includes('aria-label="Role actions"')); assert.ok(!rbacFormTemplate.includes('aria-label="Permission actions"')); assert.ok(!rbacFormTemplate.includes('showSaveAndClose=false')); assert.ok(!rbacFormTemplate.includes('showSaveAndNew=false')); assert.ok(!rbacFormTemplate.includes('Select none')); assert.ok(!rbacFormTemplate.includes('permission categories')); assert.ok(!rbacFormTemplate.includes('btn-link')); assert.ok(!rbacFormTemplate.includes('rounded-bottom-3 overflow-hidden')); assert.ok(!rbacFormTemplate.includes('table-responsive rounded-bottom-3 overflow-hidden')); assert.ok(rbacFormViewModel.includes("label: 'Read'")); assert.ok(rbacFormViewModel.includes("label: 'Create'")); assert.ok(rbacFormViewModel.includes("label: 'Update'")); assert.ok(rbacFormViewModel.includes("label: 'Delete'")); assert.ok(rbacFormViewModel.includes("label: 'Allow'")); assert.ok(rbacFormViewModel.includes('left.resourceOrder')); assert.ok(rbacRoutesSource.includes('resourceOrder: Number(permission.resourceOrder) || 999')); assert.ok(rbacFormViewModel.includes('section.actions = actionDefinitions.map')); assert.ok(rbacFormViewModel.includes('data-async-save-new-url="/settings/roles/new"')); assert.ok(rbacFormViewModel.includes('data-async-save-close-url="/settings/roles" data-async-save-new-url="/settings/roles/new"')); assert.ok(rbacFormViewModel.includes('cancelConfirmMessage: "You\'ve made changes. Are you sure you want to leave this page?"')); assert.match(rbacFormTemplate, /cancelConfirmMessage=cancelConfirmMessage/); assert.ok(rbacSource.includes("sectionName: 'Main navigation'")); assert.ok(rbacSource.includes("permissions: [")); assert.ok(rbacSource.includes("resourceOrder: resource.order")); }); test('rbac permissions script supports bulk permission selection', () => { assert.ok(rbacPermissionsScript.includes('function setRowCheckboxes(row, checked)')); assert.ok(rbacPermissionsScript.includes('function toggleRowCheckboxes(row)')); assert.ok(rbacPermissionsScript.includes('data-permission-row-toggle')); assert.ok(rbacPermissionsScript.includes("toggleRowCheckboxes(findPermissionRow(targetId));")); assert.match(rbacPermissionsScript, /toggleRowCheckboxes\(findPermissionRow\(targetId\)\);\s*markPermissionFormDirty\(control\);/); assert.ok(!rbacPermissionsScript.includes('data-permission-select-none')); assert.ok(!rbacPermissionsScript.includes('data-permission-section-select-none')); }); test('rbac accordion sections follow configured order', () => { const sections = buildPermissionSections([ { categoryName: 'Settings', sectionIndex: 0, sectionOrder: 40, resourceIndex: 1, resourceOrder: 20, title: 'Later resource', permissions: [] }, { categoryName: 'Main navigation', sectionIndex: 3, sectionOrder: 10, resourceIndex: 0, resourceOrder: 20, title: 'Clients', permissions: [] }, { categoryName: 'Main navigation', sectionIndex: 0, sectionOrder: 10, resourceIndex: 1, resourceOrder: 10, title: 'Dashboard', permissions: [] } ]); assert.deepEqual(sections.map(function (section) { return section.title; }), ['Main navigation', 'Settings']); assert.deepEqual(sections[0].groups.map(function (group) { return group.title; }), ['Dashboard', 'Clients']); }); test('rbac duplicate route exists', () => { const rbacRoutes = fs.readFileSync(require.resolve('../src/web/routes/admin/rbac.js'), 'utf8'); const duplicateHelpers = fs.readFileSync(require.resolve('../src/web/routes/settings/rbac/duplicate.js'), 'utf8'); assert.match(rbacRoutes, /\/settings\/roles\/:id\/duplicate/); assert.ok(rbacRoutes.includes("requirePermission('rbac.create')")); assert.ok(duplicateHelpers.includes('buildDuplicateRoleName')); assert.ok(duplicateHelpers.includes('buildDuplicateRole')); }); test('built-in Super Admin role cannot be deleted but can be renamed', () => { const model = buildRbacEditViewModel({ id: 1, role_key: 'super-admin', name: 'Super Admin', inUse: false }, '', null, [], [], null); assert.equal(model.footerDeleteDisabled, true); assert.equal(model.role.isProtected, true); assert.equal(model.footerDeleteTitle, 'The built-in Super Admin role cannot be deleted.'); assert.equal(model.footerDeleteConfirmMessage, 'Delete Super Admin?'); assert.ok(!rbacFormTemplate.includes('The built-in role name cannot be changed.')); assert.ok(!rbacFormTemplate.includes('The built-in role description cannot be changed.')); });