diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index b82bec9ff..2fb22579f 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -1,4 +1,4 @@ -name: Jest unit tests +name: Codi unit tests on: push: diff --git a/package.json b/package.json index f0b4c021a..7527e8cfe 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "node": ">=18.x" }, "scripts": { - "test": "jest", + "test": "codi tests", "_build": "npx esbuild ./lib/mapp.mjs ./lib/ui.mjs --bundle --minify --tree-shaking=false --sourcemap --format=iife --outdir=./public/js/lib", "version": "node version.js", "generate-docs-mapp": "jsdoc --configure jsdoc_mapp.json --verbose", @@ -29,6 +29,7 @@ }, "devDependencies": { "clean-jsdoc-theme": "^4.2.17", + "codi-test-framework": "^0.0.8", "cookie-parser": "^1.4.5", "dotenv": "^16.4.5", "esbuild": "^0.19.11", @@ -38,7 +39,6 @@ "eslint-plugin-n": "^15.6.0", "eslint-plugin-promise": "^6.1.1", "express": "^4.18.3", - "jest": "^29.5.0", "jsdocs": "^0.0.1", "uhtml": "^3.1.0" } diff --git a/tests/mod/utils/merge.test.mjs b/tests/mod/utils/merge.test.mjs new file mode 100644 index 000000000..932d57bf8 --- /dev/null +++ b/tests/mod/utils/merge.test.mjs @@ -0,0 +1,81 @@ +import { describe, it, assertDeepEqual } from 'codi-test-framework'; +import mergeDeep from '../../../mod/utils/merge.js' + +describe('mergeDeep', () => { + + it('should merge objects deeply', () => { + const target = { + name: 'Rob', + age: 28, + address: { + street: '6 fourteenth street', + city: 'Johannesburg', + }, + hobbies: ['squash', 'guitar'], + }; + + const source = { + name: 'Rob', + age: 28, + address: { + street: '6 fourteenth street', + city: 'Johannesburg', + }, + hobbies: ['cooking'], + }; + + const expected = { + name: 'Rob', + age: 28, + address: { + street: '6 fourteenth street', + city: 'Johannesburg', + }, + hobbies: ['squash', 'guitar', 'cooking'], + }; + + const mergedObj = mergeDeep(target, source); + + assertDeepEqual(mergedObj, expected) + }); + + it('should merge arrays deeply', () => { + const target = { + fruits: ['apple', 'banana'], + }; + + const source = { + fruits: ['banana', 'orange'], + }; + + const expected = { + fruits: ['apple', 'banana','banana', 'orange'], + }; + + const mergedObj = mergeDeep(target, source); + + assertDeepEqual(mergedObj, expected) + }); + + it('should handle merging with null or undefined values', () => { + const target = { + name: 'John', + age: 30, + }; + + const source1 = null; + const source2 = undefined; + + const expected = { + name: 'John', + age: 30, + }; + + const mergedObj1 = mergeDeep(target, source1); + const mergedObj2 = mergeDeep(target, source2); + + assertDeepEqual(mergedObj1, expected); + assertDeepEqual(mergedObj2, expected); + }); + +}); diff --git a/tests/mod/utils/roles.test.mjs b/tests/mod/utils/roles.test.mjs new file mode 100644 index 000000000..6c47c0ee8 --- /dev/null +++ b/tests/mod/utils/roles.test.mjs @@ -0,0 +1,105 @@ +import { describe, it, assertEqual, assertDeepEqual } from 'codi-test-framework'; +import { check, filter, get } from '../../../mod/utils/roles.js'; + +describe('Roles Module', () => { + describe('check()', () => { + it('should return the object if no roles are assigned', () => { + const obj = { name: 'John' }; + const result = check(obj, ['user']); + assertEqual(result, obj); + }); + + it('should return the object if the asterisk role is present', () => { + const obj = { name: 'John', roles: { '*': true } }; + const result = check(obj, ['user']); + assertEqual(result, obj); + }); + + it('should return false if user roles are not provided or not an array', () => { + const obj = { name: 'John', roles: { user: true } }; + const result1 = check(obj, null); + const result2 = check(obj, 'user'); + assertEqual(result1, false); + assertEqual(result2, false); + }); + + it('should return false if a negated role is included in user roles', () => { + const obj = { name: 'John', roles: { '!admin': true } }; + const result = check(obj, ['user', 'admin']); + assertEqual(result, false); + }); + + it('should return the object if all roles are negated and not included in user roles', () => { + const obj = { name: 'John', roles: { '!admin': true, '!superuser': true } }; + const result = check(obj, ['user']); + assertEqual(result, obj); + }); + + it('should return the object if a positive role is included in user roles', () => { + const obj = { name: 'John', roles: { user: true, admin: true } }; + const result = check(obj, ['user']); + assertEqual(result, obj); + }); + + it('should return false if no positive role is included in user roles', () => { + const obj = { name: 'John', roles: { admin: true } }; + const result = check(obj, ['user']); + assertEqual(result, false); + }); + }); + + describe('filter()', () => { + it('should return undefined if the layer has no roles', () => { + const layer = { name: 'Layer' }; + const result = filter(layer, ['user']); + assertEqual(result, undefined); + }); + + it('should return undefined if user roles are not an array', () => { + const layer = { name: 'Layer', roles: { user: { filter: { status: 'active' } } } }; + const result = filter(layer, 'user'); + assertEqual(result, undefined); + }); + + it('should return the role filter for matching roles', () => { + const layer = { name: 'Layer', roles: { user: { filter: { status: 'active' } } } }; + const result = filter(layer, ['user']); + assertDeepEqual(result, { user: { status: 'active' } }); + }); + + it('should return the role filter for negated roles not included in user roles', () => { + const layer = { name: 'Layer', roles: { '!admin': { filter: { status: 'active' } } } }; + const result = filter(layer, ['user']); + assertDeepEqual(result, { '!admin': { status: 'active' } }); + }); + }); + + describe('get()', () => { + it('should return an array of unique roles from the object tree', () => { + const obj = { + roles: { user: true, admin: true }, + foo: { roles: { superuser: true } }, + }; + const result = get(obj); + assertDeepEqual(result, ['user', 'admin', 'superuser']); + }); + + it('should remove the negation prefix from roles', () => { + const obj = { + roles: { '!user': true, admin: true }, + foo: { roles: { '!superuser': true } }, + }; + const result = get(obj); + assertDeepEqual(result, ['user', 'admin', 'superuser']); + }); + + it('should exclude the restricted asterisk role', () => { + const obj = { + roles: { '*': true, user: true }, + foo: { roles: { admin: true } }, + }; + const result = get(obj); + assertDeepEqual(result, ['user', 'admin']); + }); + }); +}); \ No newline at end of file