|
|
@@ -3,14 +3,11 @@ import {format} from '@e22m4u/js-format';
|
|
|
import {projectData} from './project-data.js';
|
|
|
|
|
|
describe('projectData', function () {
|
|
|
- it('should require the parameter "schemaOrFactory" to be a valid value', function () {
|
|
|
- const throwable = v => () => projectData(v, {});
|
|
|
+ it('should require the options argument to be an object', function () {
|
|
|
+ const throwable = v => () => projectData({}, 10, v);
|
|
|
const error = s =>
|
|
|
- format(
|
|
|
- 'Projection schema must be an Object, a Function ' +
|
|
|
- 'or a non-empty String, but %s was given.',
|
|
|
- s,
|
|
|
- );
|
|
|
+ format('Projection options must be an Object, but %s was given.', s);
|
|
|
+ expect(throwable('str')).to.throw(error('"str"'));
|
|
|
expect(throwable('')).to.throw(error('""'));
|
|
|
expect(throwable(10)).to.throw(error('10'));
|
|
|
expect(throwable(0)).to.throw(error('0'));
|
|
|
@@ -18,48 +15,57 @@ describe('projectData', function () {
|
|
|
expect(throwable(false)).to.throw(error('false'));
|
|
|
expect(throwable([])).to.throw(error('Array'));
|
|
|
expect(throwable(null)).to.throw(error('null'));
|
|
|
- expect(throwable(undefined)).to.throw(error('undefined'));
|
|
|
- projectData({}, {});
|
|
|
- projectData(() => ({}), {});
|
|
|
- projectData('mySchema', {}, {resolver: () => ({})});
|
|
|
+ throwable({})();
|
|
|
+ throwable(undefined)();
|
|
|
});
|
|
|
|
|
|
- it('should require the parameter "options" to be an object', function () {
|
|
|
- const throwable = v => () => projectData({}, {}, v);
|
|
|
+ it('should require the strict option to be a boolean', function () {
|
|
|
+ const throwable = v => () => projectData({}, 10, {strict: v});
|
|
|
const error = s =>
|
|
|
- format('Parameter "options" must be an Object, but %s was given.', s);
|
|
|
+ format(
|
|
|
+ 'Projection option "strict" must be a Boolean, but %s was given.',
|
|
|
+ s,
|
|
|
+ );
|
|
|
expect(throwable('str')).to.throw(error('"str"'));
|
|
|
expect(throwable('')).to.throw(error('""'));
|
|
|
expect(throwable(10)).to.throw(error('10'));
|
|
|
expect(throwable(0)).to.throw(error('0'));
|
|
|
- expect(throwable(true)).to.throw(error('true'));
|
|
|
- expect(throwable(false)).to.throw(error('false'));
|
|
|
expect(throwable([])).to.throw(error('Array'));
|
|
|
+ expect(throwable({})).to.throw(error('Object'));
|
|
|
expect(throwable(null)).to.throw(error('null'));
|
|
|
- throwable({})();
|
|
|
+ throwable(true)();
|
|
|
+ throwable(false)();
|
|
|
throwable(undefined)();
|
|
|
});
|
|
|
|
|
|
- it('should require the option "strict" to be a boolean', function () {
|
|
|
- const throwable = v => () => projectData({}, {}, {strict: v});
|
|
|
+ it('should require the scope option to be a non-empty string', function () {
|
|
|
+ const throwable = v => () => projectData({}, 10, {scope: v});
|
|
|
const error = s =>
|
|
|
- format('Option "strict" must be a Boolean, but %s was given.', s);
|
|
|
- expect(throwable('str')).to.throw(error('"str"'));
|
|
|
+ format(
|
|
|
+ 'Projection option "scope" must be a non-empty String, ' +
|
|
|
+ 'but %s was given.',
|
|
|
+ s,
|
|
|
+ );
|
|
|
expect(throwable('')).to.throw(error('""'));
|
|
|
expect(throwable(10)).to.throw(error('10'));
|
|
|
expect(throwable(0)).to.throw(error('0'));
|
|
|
+ expect(throwable(true)).to.throw(error('true'));
|
|
|
+ expect(throwable(false)).to.throw(error('false'));
|
|
|
expect(throwable([])).to.throw(error('Array'));
|
|
|
expect(throwable({})).to.throw(error('Object'));
|
|
|
expect(throwable(null)).to.throw(error('null'));
|
|
|
- throwable(true)();
|
|
|
- throwable(false)();
|
|
|
+ throwable('str')();
|
|
|
throwable(undefined)();
|
|
|
});
|
|
|
|
|
|
- it('should require the option "scope" to be a non-empty string', function () {
|
|
|
- const throwable = v => () => projectData({}, {}, {scope: v});
|
|
|
+ it('should require the resolver option to be a function', function () {
|
|
|
+ const throwable = v => () => projectData({}, 10, {resolver: v});
|
|
|
const error = s =>
|
|
|
- format('Option "scope" must be a non-empty String, but %s was given.', s);
|
|
|
+ format(
|
|
|
+ 'Projection option "resolver" must be a Function, but %s was given.',
|
|
|
+ s,
|
|
|
+ );
|
|
|
+ expect(throwable('str')).to.throw(error('"str"'));
|
|
|
expect(throwable('')).to.throw(error('""'));
|
|
|
expect(throwable(10)).to.throw(error('10'));
|
|
|
expect(throwable(0)).to.throw(error('0'));
|
|
|
@@ -68,317 +74,259 @@ describe('projectData', function () {
|
|
|
expect(throwable([])).to.throw(error('Array'));
|
|
|
expect(throwable({})).to.throw(error('Object'));
|
|
|
expect(throwable(null)).to.throw(error('null'));
|
|
|
- throwable('str')();
|
|
|
+ throwable(() => ({}))();
|
|
|
throwable(undefined)();
|
|
|
});
|
|
|
|
|
|
- it('should require the option "resolver" to be a Function', function () {
|
|
|
- const throwable = v => () => projectData({}, {}, {resolver: v});
|
|
|
+ it('should project with the schema factory', function () {
|
|
|
+ let invoked = 0;
|
|
|
+ const factory = () => {
|
|
|
+ invoked++;
|
|
|
+ return {foo: true, bar: false};
|
|
|
+ };
|
|
|
+ const res = projectData(factory, {foo: 10, bar: 20});
|
|
|
+ expect(res).to.be.eql({foo: 10});
|
|
|
+ expect(invoked).to.be.eq(1);
|
|
|
+ });
|
|
|
+
|
|
|
+ it('should require a factory value to be an object or a non-empty string', function () {
|
|
|
+ const throwable = v => () => projectData(() => v, {});
|
|
|
const error = s =>
|
|
|
- format('Option "resolver" must be a Function, but %s was given.', s);
|
|
|
+ format(
|
|
|
+ 'Schema factory must return an Object ' +
|
|
|
+ 'or a non-empty String, but %s was given.',
|
|
|
+ s,
|
|
|
+ );
|
|
|
expect(throwable('')).to.throw(error('""'));
|
|
|
expect(throwable(10)).to.throw(error('10'));
|
|
|
expect(throwable(0)).to.throw(error('0'));
|
|
|
expect(throwable(true)).to.throw(error('true'));
|
|
|
expect(throwable(false)).to.throw(error('false'));
|
|
|
expect(throwable([])).to.throw(error('Array'));
|
|
|
- expect(throwable({})).to.throw(error('Object'));
|
|
|
+ expect(throwable(undefined)).to.throw(error('undefined'));
|
|
|
expect(throwable(null)).to.throw(error('null'));
|
|
|
- throwable(() => undefined)();
|
|
|
- throwable(undefined)();
|
|
|
+ expect(throwable(() => undefined)).to.throw(error('Function'));
|
|
|
+ projectData(() => ({}), {});
|
|
|
+ projectData(() => 'str', {}, {resolver: () => ({})});
|
|
|
});
|
|
|
|
|
|
- it('should return a non-object and non-array data as is', function () {
|
|
|
- const fn = v => projectData({foo: true}, v);
|
|
|
- expect(fn('str')).to.be.eql('str');
|
|
|
- expect(fn('')).to.be.eql('');
|
|
|
- expect(fn(10)).to.be.eql(10);
|
|
|
- expect(fn(0)).to.be.eql(0);
|
|
|
- expect(fn(true)).to.be.eql(true);
|
|
|
- expect(fn(false)).to.be.eql(false);
|
|
|
- expect(fn(null)).to.be.eql(null);
|
|
|
- expect(fn(undefined)).to.be.eql(undefined);
|
|
|
+ it('should resolve the schema name by the given resolver', function () {
|
|
|
+ let invoked = 0;
|
|
|
+ const resolver = name => {
|
|
|
+ invoked++;
|
|
|
+ expect(name).to.be.eql('mySchema');
|
|
|
+ return {foo: true, bar: false};
|
|
|
+ };
|
|
|
+ const res = projectData('mySchema', {foo: 10, bar: 20}, {resolver});
|
|
|
+ expect(res).to.be.eql({foo: 10});
|
|
|
+ expect(invoked).to.be.eq(1);
|
|
|
});
|
|
|
|
|
|
- it('should apply projection for each array element', function () {
|
|
|
- const schema = {foo: true, bar: false};
|
|
|
- const data = [
|
|
|
- {foo: 10, bar: 20, baz: 30},
|
|
|
- {foo: 40, bar: 50, baz: 60},
|
|
|
- ];
|
|
|
- const res = projectData(schema, data);
|
|
|
- expect(res).to.be.eql([
|
|
|
- {foo: 10, baz: 30},
|
|
|
- {foo: 40, baz: 60},
|
|
|
- ]);
|
|
|
+ it('should require the resolver option when the schema name is given', function () {
|
|
|
+ const throwable = () => projectData('mySchema', {});
|
|
|
+ expect(throwable).to.throw(
|
|
|
+ 'Projection option "resolver" is required to resolve "mySchema" schema.',
|
|
|
+ );
|
|
|
});
|
|
|
|
|
|
- it('should add fields without rules by default', function () {
|
|
|
- const res = projectData({}, {foo: 10, bar: 20});
|
|
|
- expect(res).to.be.eql({foo: 10, bar: 20});
|
|
|
+ it('should require a resolver result to be an object', function () {
|
|
|
+ const throwable = v => () =>
|
|
|
+ projectData('mySchema', {}, {resolver: () => v});
|
|
|
+ const error = s =>
|
|
|
+ format('Schema resolver must return an Object, but %s was given.', s);
|
|
|
+ expect(throwable('str')).to.throw(error('"str"'));
|
|
|
+ expect(throwable('')).to.throw(error('""'));
|
|
|
+ expect(throwable(10)).to.throw(error('10'));
|
|
|
+ expect(throwable(0)).to.throw(error('0'));
|
|
|
+ expect(throwable(true)).to.throw(error('true'));
|
|
|
+ expect(throwable(false)).to.throw(error('false'));
|
|
|
+ expect(throwable([])).to.throw(error('Array'));
|
|
|
+ expect(throwable(undefined)).to.throw(error('undefined'));
|
|
|
+ expect(throwable(null)).to.throw(error('null'));
|
|
|
+ expect(throwable(() => undefined)).to.throw(error('Function'));
|
|
|
+ throwable({})();
|
|
|
});
|
|
|
|
|
|
- it('should project fields by a boolean value', function () {
|
|
|
- const res = projectData({foo: true, bar: false}, {foo: 10, bar: 20});
|
|
|
+ it('should resolver the schema name from the factory function', function () {
|
|
|
+ let invoked = 0;
|
|
|
+ const resolver = name => {
|
|
|
+ invoked++;
|
|
|
+ expect(name).to.be.eql('mySchema');
|
|
|
+ return {foo: true, bar: false};
|
|
|
+ };
|
|
|
+ const res = projectData(() => 'mySchema', {foo: 10, bar: 20}, {resolver});
|
|
|
expect(res).to.be.eql({foo: 10});
|
|
|
+ expect(invoked).to.be.eq(1);
|
|
|
});
|
|
|
|
|
|
- it('should project fields by the select option', function () {
|
|
|
- const res = projectData(
|
|
|
- {foo: {select: true}, bar: {select: false}},
|
|
|
- {foo: 10, bar: 20},
|
|
|
- );
|
|
|
- expect(res).to.be.eql({foo: 10});
|
|
|
+ it('should resolve the named schema in the the nested object', function () {
|
|
|
+ let invoked = 0;
|
|
|
+ const resolver = name => {
|
|
|
+ invoked++;
|
|
|
+ if (name === 'schema1') {
|
|
|
+ return {foo: true, bar: {schema: 'schema2'}};
|
|
|
+ } else if (name === 'schema2') {
|
|
|
+ return {baz: true, qux: false};
|
|
|
+ }
|
|
|
+ };
|
|
|
+ const data = {foo: 10, bar: {baz: 20, qux: 30}};
|
|
|
+ const res = projectData('schema1', data, {resolver});
|
|
|
+ expect(res).to.be.eql({foo: 10, bar: {baz: 20}});
|
|
|
+ expect(invoked).to.be.eq(2);
|
|
|
});
|
|
|
|
|
|
- it('should ignore scope-related rules by default', function () {
|
|
|
- const res = projectData(
|
|
|
- {foo: {scopes: {input: false, output: false}}},
|
|
|
- {foo: 10},
|
|
|
+ it('should validate the given schema in the shallow mode', function () {
|
|
|
+ const schema1 = {foo: '?'};
|
|
|
+ const schema2 = {foo: true, bar: {schema: {baz: '?'}}};
|
|
|
+ expect(() => projectData(schema1, {foo: 10})).to.throw(
|
|
|
+ 'Property options must be an Object or a Boolean, but "?" was given.',
|
|
|
);
|
|
|
+ const res = projectData(schema2, {foo: 10});
|
|
|
expect(res).to.be.eql({foo: 10});
|
|
|
- });
|
|
|
-
|
|
|
- it('should create nested projection by the schema option', function () {
|
|
|
- const res = projectData(
|
|
|
- {foo: true, bar: false, qux: {schema: {abc: true, def: false}}},
|
|
|
- {foo: 10, bar: 20, qux: {abc: 30, def: 40}},
|
|
|
+ expect(() => projectData(schema2, {bar: {baz: 20}})).to.throw(
|
|
|
+ 'Property options must be an Object or a Boolean, but "?" was given.',
|
|
|
);
|
|
|
- expect(res).to.be.eql({foo: 10, qux: {abc: 30}});
|
|
|
});
|
|
|
|
|
|
- describe('schema factory', function () {
|
|
|
- it('should throw an error if the schema factory returns an invalid value', function () {
|
|
|
- const throwable = v => () => projectData(() => v, {});
|
|
|
- const error = s =>
|
|
|
- format(
|
|
|
- 'Projection schema factory must return an Object ' +
|
|
|
- 'or a non-empty String, but %s was given.',
|
|
|
- s,
|
|
|
- );
|
|
|
- expect(throwable('')).to.throw(error('""'));
|
|
|
- expect(throwable(10)).to.throw(error('10'));
|
|
|
- expect(throwable(0)).to.throw(error('0'));
|
|
|
- expect(throwable(true)).to.throw(error('true'));
|
|
|
- expect(throwable(false)).to.throw(error('false'));
|
|
|
- expect(throwable([])).to.throw(error('Array'));
|
|
|
- expect(throwable(null)).to.throw(error('null'));
|
|
|
- expect(throwable(undefined)).to.throw(error('undefined'));
|
|
|
- projectData(() => ({}), {});
|
|
|
- projectData(() => 'mySchema', {}, {resolver: () => ({})});
|
|
|
- });
|
|
|
-
|
|
|
- it('should resolve a schema object from the given factory', function () {
|
|
|
- let invoked = 0;
|
|
|
- const factory = () => {
|
|
|
- invoked++;
|
|
|
- return {foo: true, bar: false};
|
|
|
- };
|
|
|
- const res = projectData(factory, {foo: 10, bar: 20});
|
|
|
- expect(res).to.be.eql({foo: 10});
|
|
|
- expect(invoked).to.be.eq(1);
|
|
|
- });
|
|
|
-
|
|
|
- it('should use the schema factory in the nested schema', function () {
|
|
|
- let invoked = 0;
|
|
|
- const factory = () => {
|
|
|
- invoked++;
|
|
|
- return {baz: true, qux: false};
|
|
|
- };
|
|
|
- const res = projectData(
|
|
|
- {foo: true, bar: {schema: factory}},
|
|
|
- {foo: 10, bar: {baz: 20, qux: 30}},
|
|
|
- );
|
|
|
- expect(res).to.be.eql({foo: 10, bar: {baz: 20}});
|
|
|
- expect(invoked).to.be.eq(1);
|
|
|
- });
|
|
|
+ it('should return primitive value as is', function () {
|
|
|
+ expect(projectData({}, 'str')).to.be.eq('str');
|
|
|
+ expect(projectData({}, '')).to.be.eq('');
|
|
|
+ expect(projectData({}, 10)).to.be.eq(10);
|
|
|
+ expect(projectData({}, 0)).to.be.eq(0);
|
|
|
+ expect(projectData({}, true)).to.be.eq(true);
|
|
|
+ expect(projectData({}, false)).to.be.eq(false);
|
|
|
+ expect(projectData({}, undefined)).to.be.eq(undefined);
|
|
|
+ expect(projectData({}, null)).to.be.eq(null);
|
|
|
});
|
|
|
|
|
|
- describe('named schema', function () {
|
|
|
- it('should throw an error if the schema resolver returns an invalid value', function () {
|
|
|
- const throwable = v => () =>
|
|
|
- projectData('mySchema', {}, {resolver: () => v});
|
|
|
- const error = s =>
|
|
|
- format(
|
|
|
- 'Projection schema resolver must return an Object, but %s was given.',
|
|
|
- s,
|
|
|
- );
|
|
|
- expect(throwable('str')).to.throw(error('"str"'));
|
|
|
- expect(throwable('')).to.throw(error('""'));
|
|
|
- expect(throwable(10)).to.throw(error('10'));
|
|
|
- expect(throwable(0)).to.throw(error('0'));
|
|
|
- expect(throwable(true)).to.throw(error('true'));
|
|
|
- expect(throwable(false)).to.throw(error('false'));
|
|
|
- expect(throwable([])).to.throw(error('Array'));
|
|
|
- expect(throwable(null)).to.throw(error('null'));
|
|
|
- expect(throwable(undefined)).to.throw(error('undefined'));
|
|
|
- throwable({})();
|
|
|
- });
|
|
|
+ it('should project an array items', function () {
|
|
|
+ const list = [{foo: 10, bar: 20, baz: 30}, {qux: 30}];
|
|
|
+ const expectedList = [{foo: 10, baz: 30}, {qux: 30}];
|
|
|
+ const res = projectData({foo: true, bar: false}, list);
|
|
|
+ expect(res).to.be.eql(expectedList);
|
|
|
+ });
|
|
|
|
|
|
- it('should throw an error if no schema resolver is provided when a schema name is given', function () {
|
|
|
- const throwable = () => projectData('mySchema', {});
|
|
|
- expect(throwable).to.throw(
|
|
|
- 'Unable to resolve the projection schema "mySchema" ' +
|
|
|
- 'without a provided resolver.',
|
|
|
- );
|
|
|
- });
|
|
|
+ it('should project an array items in the strict mode', function () {
|
|
|
+ const list = [{foo: 10, bar: 20, baz: 30}, {qux: 30}];
|
|
|
+ const expectedList = [{foo: 10}, {}];
|
|
|
+ const res = projectData({foo: true, bar: false}, list, {strict: true});
|
|
|
+ expect(res).to.be.eql(expectedList);
|
|
|
+ });
|
|
|
|
|
|
- it('should pass the schema name to the schema resolver and project the given data', function () {
|
|
|
- let invoked = 0;
|
|
|
- const resolver = key => {
|
|
|
- expect(key).to.be.eq('mySchema');
|
|
|
- invoked++;
|
|
|
- return {foo: true, bar: false};
|
|
|
- };
|
|
|
- const res = projectData('mySchema', {foo: 10, bar: 20}, {resolver});
|
|
|
- expect(res).to.be.eql({foo: 10});
|
|
|
- expect(invoked).to.be.eq(1);
|
|
|
- });
|
|
|
+ it('should exclude properties without rules when the strict mode is enabled', function () {
|
|
|
+ const res = projectData(
|
|
|
+ {foo: true, bar: false},
|
|
|
+ {foo: 10, bar: 20, baz: 30},
|
|
|
+ {strict: true},
|
|
|
+ );
|
|
|
+ expect(res).to.be.eql({foo: 10});
|
|
|
+ });
|
|
|
|
|
|
- it('should use the schema resolver in the nested schema', function () {
|
|
|
- let invoked = 0;
|
|
|
- const resolver = key => {
|
|
|
- expect(key).to.be.eq('mySchema');
|
|
|
- invoked++;
|
|
|
- return {baz: true, qux: false};
|
|
|
- };
|
|
|
- const res = projectData(
|
|
|
- {foo: true, bar: {schema: 'mySchema'}},
|
|
|
- {foo: 10, bar: {baz: 20, qux: 30}},
|
|
|
- {resolver},
|
|
|
- );
|
|
|
- expect(res).to.be.eql({foo: 10, bar: {baz: 20}});
|
|
|
- expect(invoked).to.be.eq(1);
|
|
|
- });
|
|
|
+ it('should ignore prototype properties', function () {
|
|
|
+ const data = Object.create({baz: 30});
|
|
|
+ data.foo = 10;
|
|
|
+ data.bar = 20;
|
|
|
+ expect(data).to.be.eql({foo: 10, bar: 20, baz: 30});
|
|
|
+ const res = projectData({foo: true, bar: false}, data);
|
|
|
+ expect(res).to.be.eql({foo: 10});
|
|
|
+ });
|
|
|
|
|
|
- it('should use the schema name from the schema factory', function () {
|
|
|
- let invoked = 0;
|
|
|
- const resolver = key => {
|
|
|
- expect(key).to.be.eq('mySchema');
|
|
|
- invoked++;
|
|
|
- return {foo: true, bar: false};
|
|
|
- };
|
|
|
- const res = projectData(() => 'mySchema', {foo: 10, bar: 20}, {resolver});
|
|
|
- expect(res).to.be.eql({foo: 10});
|
|
|
- expect(invoked).to.be.eq(1);
|
|
|
- });
|
|
|
+ it('should project the property by a boolean rule', function () {
|
|
|
+ const res = projectData({foo: true, bar: false}, {foo: 10, bar: 20});
|
|
|
+ expect(res).to.be.eql({foo: 10});
|
|
|
});
|
|
|
|
|
|
- describe('strict mode', function () {
|
|
|
- it('should preserve fields not defined in the schema when the strict option is false', function () {
|
|
|
- const res = projectData({}, {foo: 10});
|
|
|
- expect(res).to.be.eql({foo: 10});
|
|
|
- });
|
|
|
+ it('should project the property by the select option', function () {
|
|
|
+ const res = projectData(
|
|
|
+ {foo: {select: true}, bar: {select: false}},
|
|
|
+ {foo: 10, bar: 20},
|
|
|
+ );
|
|
|
+ expect(res).to.be.eql({foo: 10});
|
|
|
+ });
|
|
|
|
|
|
- it('should remove fields without rules when the strict mode is enabled', function () {
|
|
|
- const res = projectData({}, {foo: 10}, {strict: true});
|
|
|
- expect(res).to.be.eql({});
|
|
|
- });
|
|
|
+ it('should ignore scope options when no active scope is provided', function () {
|
|
|
+ const schema = {
|
|
|
+ foo: {select: true, scopes: {input: false}},
|
|
|
+ bar: {select: false, scopes: {output: true}},
|
|
|
+ };
|
|
|
+ const res = projectData(schema, {foo: 10, bar: 20});
|
|
|
+ expect(res).to.be.eql({foo: 10});
|
|
|
+ });
|
|
|
|
|
|
- it('should project fields by a boolean value', function () {
|
|
|
- const res = projectData(
|
|
|
- {foo: true, bar: false},
|
|
|
- {foo: 10, bar: 20},
|
|
|
- {strict: true},
|
|
|
- );
|
|
|
- expect(res).to.be.eql({foo: 10});
|
|
|
- });
|
|
|
+ it('should project the active scope by the boolean rule', function () {
|
|
|
+ const schema = {
|
|
|
+ foo: {scopes: {input: true}},
|
|
|
+ bar: {scopes: {input: false}},
|
|
|
+ };
|
|
|
+ const res = projectData(schema, {foo: 10, bar: 20}, {scope: 'input'});
|
|
|
+ expect(res).to.be.eql({foo: 10});
|
|
|
+ });
|
|
|
|
|
|
- it('should project fields by the select option', function () {
|
|
|
- const res = projectData(
|
|
|
- {foo: {select: true}, bar: {select: false}},
|
|
|
- {foo: 10, bar: 20},
|
|
|
- {strict: true},
|
|
|
- );
|
|
|
- expect(res).to.be.eql({foo: 10});
|
|
|
- });
|
|
|
+ it('should project the active scope by the select option', function () {
|
|
|
+ const schema = {
|
|
|
+ foo: {scopes: {input: {select: true}}},
|
|
|
+ bar: {scopes: {input: {select: false}}},
|
|
|
+ };
|
|
|
+ const res = projectData(schema, {foo: 10, bar: 20}, {scope: 'input'});
|
|
|
+ expect(res).to.be.eql({foo: 10});
|
|
|
+ });
|
|
|
|
|
|
- it('should propagate the strict mode to nested schema', function () {
|
|
|
- const res = projectData(
|
|
|
- {foo: false, bar: {select: true, schema: {baz: true}}},
|
|
|
- {foo: 10, bar: {baz: 20, qux: 30}},
|
|
|
- {strict: true},
|
|
|
- );
|
|
|
- expect(res).to.be.eql({bar: {baz: 20}});
|
|
|
- });
|
|
|
+ it('should prioritize the scope rule over the general options', function () {
|
|
|
+ const schema = {
|
|
|
+ foo: {select: false, scopes: {input: true}},
|
|
|
+ bar: {select: true, scopes: {input: false}},
|
|
|
+ };
|
|
|
+ const res = projectData(schema, {foo: 10, bar: 20}, {scope: 'input'});
|
|
|
+ expect(res).to.be.eql({foo: 10});
|
|
|
+ });
|
|
|
|
|
|
- it('should ignore inherited fields', function () {
|
|
|
- const res = projectData(
|
|
|
- {bar: true, toString: true},
|
|
|
- {bar: 10},
|
|
|
- {strict: true},
|
|
|
- );
|
|
|
- expect(res).to.be.eql({bar: 10});
|
|
|
- });
|
|
|
+ it('should ignore scope options not matched with the active scope', function () {
|
|
|
+ const schema = {
|
|
|
+ foo: {scopes: {input: true, output: false}},
|
|
|
+ bar: {scopes: {input: false, output: true}},
|
|
|
+ };
|
|
|
+ const res = projectData(schema, {foo: 10, bar: 20}, {scope: 'input'});
|
|
|
+ expect(res).to.be.eql({foo: 10});
|
|
|
});
|
|
|
|
|
|
- describe('projection scope', function () {
|
|
|
- it('should apply scope-specific selection rule by a boolean value', function () {
|
|
|
- const schema = {
|
|
|
- foo: {
|
|
|
- select: false,
|
|
|
- scopes: {
|
|
|
- input: true,
|
|
|
- },
|
|
|
- },
|
|
|
- bar: true,
|
|
|
- };
|
|
|
- const data = {foo: 10, bar: 20};
|
|
|
- const res1 = projectData(schema, data);
|
|
|
- const res2 = projectData(schema, data, {scope: 'input'});
|
|
|
- expect(res1).to.be.eql({bar: 20});
|
|
|
- expect(res2).to.be.eql({foo: 10, bar: 20});
|
|
|
- });
|
|
|
+ it('should exclude properties without selection rule in the strict mode when the active scope is provided', function () {
|
|
|
+ const schema = {
|
|
|
+ foo: {scopes: {input: true}},
|
|
|
+ bar: {scopes: {input: false}},
|
|
|
+ baz: {scopes: {output: true}},
|
|
|
+ };
|
|
|
+ const data = {foo: 10, bar: 20, baz: 30, qux: 40};
|
|
|
+ const res = projectData(schema, data, {strict: true, scope: 'input'});
|
|
|
+ expect(res).to.be.eql({foo: 10});
|
|
|
+ });
|
|
|
|
|
|
- it('should apply scope-specific selection rule by the select option', function () {
|
|
|
- const schema = {
|
|
|
- foo: {
|
|
|
- select: false,
|
|
|
- scopes: {
|
|
|
- input: {select: true},
|
|
|
- },
|
|
|
- },
|
|
|
- bar: {select: true},
|
|
|
- };
|
|
|
- const data = {foo: 10, bar: 20};
|
|
|
- const res1 = projectData(schema, data);
|
|
|
- const res2 = projectData(schema, data, {scope: 'input'});
|
|
|
- expect(res1).to.be.eql({bar: 20});
|
|
|
- expect(res2).to.be.eql({foo: 10, bar: 20});
|
|
|
- });
|
|
|
+ it('should prioritize the scope options over the general options in the strict mode', function () {
|
|
|
+ const schema = {
|
|
|
+ foo: {select: false, scopes: {input: true}},
|
|
|
+ bar: {select: false, scopes: {input: {select: true}}},
|
|
|
+ };
|
|
|
+ const data = {foo: 10, bar: 20, baz: 30};
|
|
|
+ const res = projectData(schema, data, {strict: true, scope: 'input'});
|
|
|
+ expect(res).to.be.eql({foo: 10, bar: 20});
|
|
|
+ });
|
|
|
|
|
|
- it('should fallback to general rule if scope rule is missing', function () {
|
|
|
- const schema = {
|
|
|
- foo: {
|
|
|
- select: true,
|
|
|
- scopes: {
|
|
|
- output: {select: false},
|
|
|
- },
|
|
|
- },
|
|
|
- };
|
|
|
- const data = {foo: 10};
|
|
|
- const res = projectData(schema, data, {scope: 'input'});
|
|
|
- expect(res).to.be.eql({foo: 10});
|
|
|
- });
|
|
|
+ it('should project the nested object by the given schema', function () {
|
|
|
+ const schema = {
|
|
|
+ foo: true,
|
|
|
+ bar: {schema: {baz: true, qux: false}},
|
|
|
+ };
|
|
|
+ const data = {foo: 10, bar: {baz: 20, qux: 30, buz: 40}};
|
|
|
+ const res = projectData(schema, data, {scope: 'input'});
|
|
|
+ expect(res).to.be.eql({foo: 10, bar: {baz: 20, buz: 40}});
|
|
|
+ });
|
|
|
|
|
|
- it('should fallback to the general rule if the scope options exists but lacks the select option', function () {
|
|
|
- const schema = {
|
|
|
- foo: {
|
|
|
- select: true,
|
|
|
- scopes: {
|
|
|
- input: {},
|
|
|
- },
|
|
|
- },
|
|
|
- bar: {
|
|
|
- select: false,
|
|
|
- scopes: {
|
|
|
- input: {},
|
|
|
- },
|
|
|
- },
|
|
|
- };
|
|
|
- const data = {foo: 10, bar: 20};
|
|
|
- const res = projectData(schema, data, {scope: 'input'});
|
|
|
- expect(res).to.be.eql({foo: 10});
|
|
|
- });
|
|
|
+ it('should exclude nested properties without rules in the strict mode', function () {
|
|
|
+ const schema = {
|
|
|
+ foo: true,
|
|
|
+ bar: {select: true, schema: {baz: true, qux: false}},
|
|
|
+ };
|
|
|
+ const data = {foo: 10, bar: {baz: 20, qux: 30, buz: 40}};
|
|
|
+ const res = projectData(schema, data, {strict: true, scope: 'input'});
|
|
|
+ expect(res).to.be.eql({foo: 10, bar: {baz: 20}});
|
|
|
});
|
|
|
});
|