-
-
Notifications
You must be signed in to change notification settings - Fork 276
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(extra): adds
permittedAttributesOf
This function allows to extract permitted fields from Ability rules Relates to #18
- Loading branch information
Showing
8 changed files
with
131 additions
and
43 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
import { AbilityBuilder } from '../src' | ||
import { permittedFieldsOf } from '../src/extra' | ||
import { Post } from './spec_helper' | ||
|
||
describe('permittedFieldsOf', () => { | ||
it('returns an empty array for `Ability` with empty rules', () => { | ||
const ability = AbilityBuilder.define(() => {}) | ||
expect(permittedFieldsOf(ability, 'read', 'Post')).to.be.empty | ||
}) | ||
|
||
it('returns an empty array if none of rules contain fields', () => { | ||
const ability = AbilityBuilder.define(can => can('read', 'Post')) | ||
|
||
expect(permittedFieldsOf(ability, 'read', 'Post')).to.be.empty | ||
}) | ||
|
||
it('returns a unique array of fields if there are duplicated fields across fields', () => { | ||
const ability = AbilityBuilder.define(can => { | ||
can('read', 'Post', ['title']) | ||
can('read', 'Post', ['title', 'description'], { id: 1 }) | ||
}) | ||
const fields = permittedFieldsOf(ability, 'read', 'Post') | ||
|
||
expect(fields).to.have.length(2) | ||
expect(fields).to.have.all.members(['title', 'description']) | ||
}) | ||
|
||
it('returns unique fields for array which contains direct and inverted rules', () => { | ||
const ability = AbilityBuilder.define((can, cannot) => { | ||
can('read', 'Post', ['title', 'description']) | ||
cannot('read', 'Post', ['description']) | ||
}) | ||
const fields = permittedFieldsOf(ability, 'read', 'Post') | ||
|
||
expect(fields).to.have.length(1) | ||
expect(fields).to.have.all.members(['title']) | ||
}) | ||
|
||
it('allows to provide an option `fieldsFrom` which extract fields from rule', () => { | ||
const ability = AbilityBuilder.define(can => can('read', 'Post')) | ||
const fields = permittedFieldsOf(ability, 'read', 'Post', { | ||
fieldsFrom: rule => rule.fields || ['title'] | ||
}) | ||
|
||
expect(fields).to.deep.equal(['title']) | ||
}) | ||
|
||
describe('when `subject` is an instance', () => { | ||
let ability | ||
|
||
beforeEach(() => { | ||
ability = AbilityBuilder.define((can, cannot) => { | ||
can('read', 'Post', ['title']) | ||
can('read', 'Post', ['title', 'description'], { id: 1 }) | ||
cannot('read', 'Post', ['description'], { private: true }) | ||
}) | ||
}) | ||
|
||
it('allows to return fields for specific instance', () => { | ||
const post = new Post({ title: 'does not match conditions' }) | ||
const fields = permittedFieldsOf(ability, 'read', post) | ||
|
||
expect(fields).to.deep.equal(['title']) | ||
}) | ||
|
||
it('allows to return fields for subject instance which matches specified rule conditions', () => { | ||
const post = new Post({ id: 1, title: 'matches conditions' }) | ||
const fields = permittedFieldsOf(ability, 'read', post) | ||
|
||
expect(fields).to.deep.equal(['title', 'description']) | ||
}) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,32 +1,32 @@ | ||
(function(factory) { | ||
if (typeof require === 'function' && typeof module !== 'undefined') { | ||
require('chai').use(require('chai-spies')); | ||
factory(require('chai'), global); | ||
} else if (typeof window === 'object') { | ||
window.global = window; | ||
factory(window.chai, window); | ||
} | ||
})(function(chai) { | ||
chai.Assertion.addMethod('allow', function(action, subject, field) { | ||
const subjectRepresantation = prettifyObject(subject) | ||
this.assert( | ||
this._obj.can(action, subject, field), | ||
`expected ability to allow ${action} on ${subjectRepresantation}`, | ||
`expected ability to not allow ${action} on ${subjectRepresantation}` | ||
); | ||
}); | ||
import chai from 'chai' | ||
import spies from 'chai-spies' | ||
|
||
function prettifyObject(object) { | ||
if (!object || typeof object === 'string') { | ||
return object; | ||
} | ||
chai.use(spies) | ||
|
||
if (typeof object === 'function') { | ||
return object.name; | ||
} | ||
chai.Assertion.addMethod('allow', function(action, subject, field) { | ||
const subjectRepresantation = prettifyObject(subject) | ||
this.assert( | ||
this._obj.can(action, subject, field), | ||
`expected ability to allow ${action} on ${subjectRepresantation}`, | ||
`expected ability to not allow ${action} on ${subjectRepresantation}` | ||
); | ||
}); | ||
|
||
const attrs = JSON.stringify(object); | ||
return `${object.constructor.name} { ${attrs[0] === '{' ? attrs.slice(1, -1) : attrs} }` | ||
function prettifyObject(object) { | ||
if (!object || typeof object === 'string') { | ||
return object; | ||
} | ||
}); | ||
|
||
if (typeof object === 'function') { | ||
return object.name; | ||
} | ||
|
||
const attrs = JSON.stringify(object); | ||
return `${object.constructor.name} { ${attrs[0] === '{' ? attrs.slice(1, -1) : attrs} }` | ||
} | ||
|
||
export class Post { | ||
constructor(attrs) { | ||
Object.assign(this, attrs) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters