Files
pulse-signage/test/rbac-permissions.test.js
lzstealth a5bf8e6f7f
Publish Docker Image / build-and-push-existing-registry (./build/Dockerfile, web, pulse-signage-web) (push) Successful in 1m16s
Publish Docker Image / build-and-push-existing-registry (./build/Dockerfile.player, player, pulse-signage-player) (push) Successful in 33s
Release 2.8.1
2026-08-16 22:08:32 +01:00

130 lines
6.7 KiB
JavaScript

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, /<h3 class="card-title">Permissions<\/h3>[\s\S]*<div class="card-body p-0">[\s\S]*accordion accordion-flush/);
assert.match(rbacFormTemplate, /card card-outline card-secondary admin-form-card overflow-hidden[\s\S]*<h3 class="card-title">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, /<col style="width: 5\.5rem;" \/>/);
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.'));
});