From cab6693a4f54b47c17341b93810a013b20c438e7 Mon Sep 17 00:00:00 2001 From: Nandor Kraszlan Date: Mon, 25 Jan 2021 22:58:42 +0000 Subject: [PATCH] Added dynamic directives and accessor --- package-lock.json | 4 +-- package.json | 2 +- rollup.config.js | 7 ++--- src/index.ts | 27 ++++++++++++++---- src/lib/ACL.ts | 10 +++---- tests/.eslintrc.js | 7 +++++ tests/helpers.ts | 25 +++++++++++++++++ tests/unit/ACL.test.ts | 2 +- tests/vue/ACL.test.ts | 53 ++++++++++++++++++++++++++---------- tests/vue/Directives.test.ts | 25 +---------------- types/config.d.ts | 11 +++++++- 11 files changed, 114 insertions(+), 59 deletions(-) create mode 100644 tests/helpers.ts diff --git a/package-lock.json b/package-lock.json index 95b93f1..bcbb61d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { "name": "vacl", - "version": "0.1.4", + "version": "0.1.5", "lockfileVersion": 2, "requires": true, "packages": { "": { - "version": "0.1.4", + "version": "0.1.5", "license": "MIT", "devDependencies": { "@olavoparno/jest-badges-readme": "^1.5.1", diff --git a/package.json b/package.json index 1bdba95..18c7ae1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vacl", - "version": "0.1.4", + "version": "0.1.5", "description": "A Lightweight Typescript ACL directives library for Vue 3", "files": [ "dist" diff --git a/rollup.config.js b/rollup.config.js index d6afbda..922468c 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -2,7 +2,6 @@ import typescript from 'rollup-plugin-typescript2'; import pkg from './package.json'; import { terser } from 'rollup-plugin-terser'; import bundleSize from 'rollup-plugin-bundle-size'; -import path from "path"; export default { input: 'src/index.ts', @@ -10,7 +9,8 @@ export default { { file: pkg.main, format: 'cjs', - sourcemap: true + sourcemap: true, + exports: 'default' }, { file: pkg.module, @@ -21,9 +21,6 @@ export default { external: [ ...Object.keys(pkg.dependencies || {}) ], - alias: { - '@/': path.resolve(__dirname, '/src/') - }, plugins: [ typescript({ typescript: require('typescript'), diff --git a/src/index.ts b/src/index.ts index 6751ad7..5ccfd44 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,15 +1,30 @@ +/* eslint-disable @typescript-eslint/no-unsafe-member-access */ import { can, cannot, has, hasnt } from './lib/Directives'; import type { App } from '@vue/runtime-core'; -import type { Config } from '../types/config'; +import type { Config, Directives } from '../types/config'; import ACL from './lib/ACL'; +const defaults: Required = { + roles: [], + permissions: [], + forceRemove: false, + accessor: '$vacl', + directives: { + can: 'can', + cannot: 'cannot', + has: 'has', + hasnt: 'hasnt' + } as Required +}; + export default { install: (app: App, options?: Config): void => { - const acl = app.config.globalProperties.$vacl = new ACL(options); + const directives = { ...defaults.directives, ...Object(options?.directives) }; + const acl = app.config.globalProperties[options?.accessor ?? defaults.accessor] = new ACL(options); - app.directive('can', can(acl)); - app.directive('cannot', cannot(acl)); - app.directive('has', has(acl)); - app.directive('hasnt', hasnt(acl)); + app.directive(directives.can, can(acl)); + app.directive(directives.cannot, cannot(acl)); + app.directive(directives.has, has(acl)); + app.directive(directives.hasnt, hasnt(acl)); } }; diff --git a/src/lib/ACL.ts b/src/lib/ACL.ts index 78a81a3..5dfd30b 100644 --- a/src/lib/ACL.ts +++ b/src/lib/ACL.ts @@ -14,7 +14,7 @@ export default class ACL { * * @returns {ACL} */ - public constructor(config: Config | null = null) { + public constructor(config?: Config) { this.roles = new Set(config?.roles ?? []); this.permissions = new Set(config?.permissions ?? []); this.forceRemove = config?.forceRemove ?? false; @@ -41,7 +41,7 @@ export default class ACL { /** * Set the roles. * - * @param {Roles} roles + * @param {string[]} roles * * @returns {ACL} */ @@ -54,7 +54,7 @@ export default class ACL { /** * Set the permissions. * - * @param {Permissions} permissions + * @param {string[]} permissions * * @returns {ACL} */ @@ -67,7 +67,7 @@ export default class ACL { /** * Add roles to the existing store. * - * @param {Roles | string} roles + * @param {string | string[]} roles * * @returns {ACL} */ @@ -80,7 +80,7 @@ export default class ACL { /** * Add permissions to the existing store. * - * @param {Permissions | string} permissions + * @param {string | string[]} permissions * * @returns {ACL} */ diff --git a/tests/.eslintrc.js b/tests/.eslintrc.js index 070fd55..dae501b 100644 --- a/tests/.eslintrc.js +++ b/tests/.eslintrc.js @@ -24,6 +24,13 @@ module.exports = { "jest/prefer-expect-assertions": "off", "jest/no-hooks": "off", "jest/prefer-called-with": "off", + "jest/no-restricted-matchers": [ + "error", + { + "toBeFalsy": 'Use toBe(false) instead to avoid unexpected type coercion.', + "toBeTruthy": 'Use toBe(true) instead to avoid unexpected type coercion.', + } + ] // "jest/valid-title": ["error", { // mustMatch: { // it: '^should ' diff --git a/tests/helpers.ts b/tests/helpers.ts new file mode 100644 index 0000000..272e6b7 --- /dev/null +++ b/tests/helpers.ts @@ -0,0 +1,25 @@ +import type ACL from '@/lib/ACL'; +import type { VueWrapper } from '@vue/test-utils'; +import { shallowMount } from '@vue/test-utils'; +import { can, cannot, has, hasnt } from '@/lib/Directives'; + +/** + * Create a test wrapper. + * + * @param {ACL} acl + * @param {string} template + * + * @returns {VueWrapper} + */ +export function getWrapper(acl: ACL, template: string): VueWrapper { + return shallowMount({ template }, { + global: { + directives: { + can: can(acl), + cannot: cannot(acl), + has: has(acl), + hasnt: hasnt(acl) + } + } + }); +} diff --git a/tests/unit/ACL.test.ts b/tests/unit/ACL.test.ts index 26dd56b..22a85f5 100644 --- a/tests/unit/ACL.test.ts +++ b/tests/unit/ACL.test.ts @@ -1,6 +1,6 @@ import ACL from '@/lib/ACL'; -describe('aCL', () => { +describe('acl', () => { it('can_be_instantiated', () => { const acl = new ACL(); diff --git a/tests/vue/ACL.test.ts b/tests/vue/ACL.test.ts index 172b935..93344ca 100644 --- a/tests/vue/ACL.test.ts +++ b/tests/vue/ACL.test.ts @@ -1,32 +1,57 @@ -import { mount } from '@vue/test-utils'; +import { shallowMount, VueWrapper } from '@vue/test-utils'; import { createApp, h } from 'vue'; import ACL from '@/lib/ACL'; import Vacl from '@/index'; -describe('reactivity', () => { +describe('vue', () => { + const component = { + render() { + return h('div'); + } + }; + it('can be installed correctly', () => { - const app = createApp({ - template: '
' - }).use(Vacl); + const app = createApp(component).use(Vacl); expect(app.config.globalProperties.$vacl).toBeInstanceOf(ACL); }); it('provides the acl globally to all components', () => { - const component = { - render() { - return h('div'); + const wrapper = shallowMount(component, { + global: { + plugins: [[Vacl, { permissions: ['view'] }]] } - }; + }); + + expect(wrapper.vm.$vacl).toBeInstanceOf(ACL); + expect(wrapper.vm.$vacl.can('view')).toBe(true); + expect(wrapper.vm.$vacl.has('admin')).toBe(false); + }); - const app = mount(component, { + it('should be able to configure global accessor name', () => { + const wrapper = shallowMount(component, { global: { - plugins: [[Vacl, { permissions: ['view'] }]] + plugins: [[Vacl, { accessor: '$acl' }]] } }); - expect(app.vm.$vacl).toBeInstanceOf(ACL); - expect(app.vm.$vacl.can('view')).toBeTruthy(); - expect(app.vm.$vacl.has('admin')).toBeFalsy(); + expect(wrapper.vm.$acl).toBeInstanceOf(ACL); + }); + + it('should be able to set the directive names from the config', () => { + const div = document.createElement('div'); + div.id = 'id'; + document.body.appendChild(div); + + const app = createApp({ + template: `
+
` + }) + .use(Vacl, { directives: { can: 'able' }, permissions: ['view'] }); + + const wrapper = new VueWrapper(app, app.mount('#id')); + + expect(wrapper.find('[data-test="visible"]').isVisible()).toBe(true); + expect(wrapper.find('[data-test="hidden"]').isVisible()).toBe(false); }); }); diff --git a/tests/vue/Directives.test.ts b/tests/vue/Directives.test.ts index e346fc2..3274643 100644 --- a/tests/vue/Directives.test.ts +++ b/tests/vue/Directives.test.ts @@ -1,7 +1,5 @@ -import type { VueWrapper } from '@vue/test-utils'; -import { mount } from '@vue/test-utils'; import ACL from '@/lib/ACL'; -import { can, cannot, has, hasnt } from '@/lib/Directives'; +import { getWrapper } from '../helpers'; /****************************************************************************** * PERMISSIONS @@ -215,24 +213,3 @@ describe('dom removal', () => { // expect(incorrectType).toThrow("Value passed to v-can should be a non-empty string. \"1\" passed."); // }); // }); - -/** - * Create a test wrapper. - * - * @param {ACL} acl - * @param {string} template - * - * @returns {VueWrapper} - */ -function getWrapper(acl: ACL, template: string): VueWrapper { - return mount({ template }, { - global: { - directives: { - can: can(acl), - cannot: cannot(acl), - has: has(acl), - hasnt: hasnt(acl) - } - } - }); -} diff --git a/types/config.d.ts b/types/config.d.ts index 11ba79d..2c1c56f 100644 --- a/types/config.d.ts +++ b/types/config.d.ts @@ -2,12 +2,21 @@ import type ACL from '../src/lib/ACL'; declare module '@vue/runtime-core' { interface ComponentCustomProperties { - $vacl: ACL; + [key: string]: ACL | any; } } +export type Directives = { + can?: string, + cannot?: string, + has?: string, + hasnt?: string +} + export type Config = { roles?: string[]; permissions?: string[]; forceRemove?: boolean; + accessor?: string + directives?: Directives };