Merge branch 'jira-2871' into 'master'
[DEV-2871] Added DocumentProgramFormatter to format program data by step, group and field metadata See merge request floss/liza!31master
commit
48cae2bb4d
|
@ -0,0 +1,218 @@
|
||||||
|
/**
|
||||||
|
* Formats program bucket data
|
||||||
|
*
|
||||||
|
* Copyright (C) 2018 R-T Specialty, LLC.
|
||||||
|
*
|
||||||
|
* This file is part of the Liza Data Collection Framework.
|
||||||
|
*
|
||||||
|
* liza is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const Class = require( 'easejs' ).Class;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats program bucket data
|
||||||
|
*
|
||||||
|
* This takes a document and formats the bucket data in a
|
||||||
|
* structured manner of steps, groups and fields metadata
|
||||||
|
*/
|
||||||
|
module.exports = Class( 'DocumentProgramFormatter',
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Current program
|
||||||
|
*
|
||||||
|
* @type {Program}
|
||||||
|
*/
|
||||||
|
'private _program': null,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs classification matching on fields
|
||||||
|
*
|
||||||
|
* A field will have a positive match for a given index if all of its
|
||||||
|
* classes match
|
||||||
|
*
|
||||||
|
* @type {FieldClassMatcher}
|
||||||
|
*/
|
||||||
|
'private _class_matcher': null,
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize document formatter
|
||||||
|
*
|
||||||
|
* @param {Program} program active program
|
||||||
|
* @param {FieldClassMatcher} class_matcher class/field matcher
|
||||||
|
*/
|
||||||
|
constructor( program, class_matcher )
|
||||||
|
{
|
||||||
|
this._program = program;
|
||||||
|
this._class_matcher = class_matcher;
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats document data into structure similar to program
|
||||||
|
*
|
||||||
|
* @param {Bucket} bucket document bucket
|
||||||
|
*
|
||||||
|
* @return {Promise.<Object>} a promise of a data object
|
||||||
|
*/
|
||||||
|
'public format'( bucket )
|
||||||
|
{
|
||||||
|
return new Promise( ( resolve, reject ) =>
|
||||||
|
{
|
||||||
|
const cmatch = this._program.classify( bucket.getData() );
|
||||||
|
|
||||||
|
this._class_matcher.match( cmatch, ( matches ) =>
|
||||||
|
{
|
||||||
|
const len = this._program.steps.length;
|
||||||
|
const data = this._parseSteps( len, bucket, matches );
|
||||||
|
|
||||||
|
resolve( data );
|
||||||
|
} );
|
||||||
|
|
||||||
|
} );
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses step data
|
||||||
|
*
|
||||||
|
* @param {Number} len step length
|
||||||
|
* @param {Bucket} bucket document bucket
|
||||||
|
* @param {Object} matches field matches
|
||||||
|
*
|
||||||
|
* @return {Object} step data
|
||||||
|
*/
|
||||||
|
'private _parseSteps'( len, bucket, matches )
|
||||||
|
{
|
||||||
|
const data = { steps: [] };
|
||||||
|
|
||||||
|
for ( let i = 1; i < len; i++ )
|
||||||
|
{
|
||||||
|
const step = {};
|
||||||
|
const step_groups = this._program.steps[ i ].groups;
|
||||||
|
|
||||||
|
const groups = this._parseGroups( step_groups, bucket, matches );
|
||||||
|
|
||||||
|
step.title = this._program.steps[ i ].title;
|
||||||
|
step.groups = groups;
|
||||||
|
|
||||||
|
data.steps.push( step );
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses group data
|
||||||
|
*
|
||||||
|
* @param {Array} step_groups array of group data
|
||||||
|
* @param {Bucket} bucket document bucket
|
||||||
|
* @param {Object} matches field matches
|
||||||
|
*
|
||||||
|
* @return {Array} array of groups
|
||||||
|
*/
|
||||||
|
'private _parseGroups'( step_groups, bucket, matches )
|
||||||
|
{
|
||||||
|
return step_groups.map( group_id =>
|
||||||
|
{
|
||||||
|
const fields = this._program.groupExclusiveFields[ group_id ];
|
||||||
|
const { title, link } = this._program.groups[ group_id ];
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: group_id,
|
||||||
|
title: title || "",
|
||||||
|
link: link || "",
|
||||||
|
questions: this._parseFields( fields, bucket, matches ),
|
||||||
|
};
|
||||||
|
} );
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses fields/question data
|
||||||
|
*
|
||||||
|
* @param {Array} fields array of field data
|
||||||
|
* @param {Bucket} bucket document bucket
|
||||||
|
* @param {Object} matches field matches
|
||||||
|
*
|
||||||
|
* @return {Array} array of questions
|
||||||
|
*/
|
||||||
|
'private _parseFields'( fields, bucket, matches )
|
||||||
|
{
|
||||||
|
const questions = [];
|
||||||
|
|
||||||
|
for ( let field in fields )
|
||||||
|
{
|
||||||
|
const field_id = fields[ field ];
|
||||||
|
|
||||||
|
// Don't include fields that are not in program.fields
|
||||||
|
if ( typeof this._program.fields[ field_id ] === "undefined" )
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const value = bucket.getDataByName( field_id );
|
||||||
|
const { label, type } = this._program.fields[ field_id ];
|
||||||
|
|
||||||
|
questions.push( {
|
||||||
|
id: field_id,
|
||||||
|
label: label || "",
|
||||||
|
value: value,
|
||||||
|
type: type || "",
|
||||||
|
applicable: this._getApplicable( matches, field_id, value ),
|
||||||
|
} );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return questions;
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine when a field is shown by index
|
||||||
|
* Map boolean values of [0, 1] to [true, false]
|
||||||
|
*
|
||||||
|
* @param {Object} matches field matches
|
||||||
|
* @param {String} field_id id of field
|
||||||
|
* @param {Object} field_value field object
|
||||||
|
*
|
||||||
|
* @return {Array.<boolean>} array of booleans
|
||||||
|
*/
|
||||||
|
'private _getApplicable'( matches, field_id, field_value )
|
||||||
|
{
|
||||||
|
// If object is undefined, default to array of true
|
||||||
|
if ( typeof matches[ field_id ] === "undefined" )
|
||||||
|
{
|
||||||
|
return field_value.map( _ => true );
|
||||||
|
}
|
||||||
|
|
||||||
|
const indexes = matches[ field_id ].indexes;
|
||||||
|
const all_match = matches[ field_id ].all;
|
||||||
|
|
||||||
|
if ( indexes.length > 0 )
|
||||||
|
{
|
||||||
|
// Map indexes of 0, 1 to true, false
|
||||||
|
return indexes.map( x => !!x );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return field_value.map( _ => all_match );
|
||||||
|
}
|
||||||
|
},
|
||||||
|
} );
|
|
@ -1,7 +1,7 @@
|
||||||
/**
|
/**
|
||||||
* Contains FieldClassMatcher class
|
* Reduce field predicate results into vectors and flags
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017 R-T Specialty, LLC.
|
* Copyright (C) 2018 R-T Specialty, LLC.
|
||||||
*
|
*
|
||||||
* This file is part of the Liza Data Collection Framework.
|
* This file is part of the Liza Data Collection Framework.
|
||||||
*
|
*
|
||||||
|
@ -19,12 +19,21 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var Class = require( 'easejs' ).Class;
|
'use strict';
|
||||||
|
|
||||||
|
const { Class } = require( 'easejs' );
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates match sets for field based on their classifications and a given
|
* Generate match vector for fields given field predicates and
|
||||||
* classification set
|
* classification results
|
||||||
|
*
|
||||||
|
* TODO: Support for multiple predicates on fields is for
|
||||||
|
* backwards-compatibility with older classification systems; newer systems
|
||||||
|
* generate a single classification representing the visibility of the
|
||||||
|
* field, allowing the classification reduction complexity and logic to stay
|
||||||
|
* within TAME. Much of the complexity in this class can therefore be
|
||||||
|
* removed in the future.
|
||||||
*/
|
*/
|
||||||
module.exports = Class( 'FieldClassMatcher',
|
module.exports = Class( 'FieldClassMatcher',
|
||||||
{
|
{
|
||||||
|
@ -40,7 +49,7 @@ module.exports = Class( 'FieldClassMatcher',
|
||||||
*
|
*
|
||||||
* @param {Object.<Array.<string>>} fields field names and their classes
|
* @param {Object.<Array.<string>>} fields field names and their classes
|
||||||
*/
|
*/
|
||||||
__construct: function( fields )
|
constructor( fields )
|
||||||
{
|
{
|
||||||
this._fields = fields;
|
this._fields = fields;
|
||||||
},
|
},
|
||||||
|
@ -58,31 +67,55 @@ module.exports = Class( 'FieldClassMatcher',
|
||||||
*
|
*
|
||||||
* @return {FieldClassMatcher} self
|
* @return {FieldClassMatcher} self
|
||||||
*/
|
*/
|
||||||
'public match': function( classes, callback )
|
'public match'( classes, callback )
|
||||||
{
|
{
|
||||||
var cmatch = {};
|
// currently not asynchronous, but leaves open the possibility
|
||||||
cmatch.__classes = classes;
|
callback(
|
||||||
|
Object.keys( this._fields ).reduce(
|
||||||
|
( cmatch, id ) =>
|
||||||
|
{
|
||||||
|
cmatch[ id ] = this._reduceFieldMatches(
|
||||||
|
this._fields[ id ],
|
||||||
|
classes
|
||||||
|
), cmatch;
|
||||||
|
|
||||||
for ( var field in this._fields )
|
return cmatch;
|
||||||
{
|
},
|
||||||
var cur = this._fields[ field ],
|
{ __classes: classes }
|
||||||
vis = [],
|
)
|
||||||
all = true,
|
);
|
||||||
hasall = false;
|
|
||||||
|
|
||||||
if ( cur.length === 0 )
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reduce field class matches to a vector
|
||||||
|
*
|
||||||
|
* All field predicates in FIELDC will be reduced and combined into a
|
||||||
|
* single vector representing the visibility of each index.
|
||||||
|
*
|
||||||
|
* @param {Array} fieldc field predicate class names
|
||||||
|
* @param {Object} classes cmatch results
|
||||||
|
*
|
||||||
|
* @return {Object} all, any, indexes
|
||||||
|
*/
|
||||||
|
'private _reduceFieldMatches'( fieldc, classes )
|
||||||
{
|
{
|
||||||
continue;
|
const vis = [];
|
||||||
}
|
|
||||||
|
let all = true;
|
||||||
|
let hasall = false;
|
||||||
|
|
||||||
// determine if we have a match based on the given classifications
|
// determine if we have a match based on the given classifications
|
||||||
for ( var c in cur )
|
for ( let c in fieldc )
|
||||||
{
|
{
|
||||||
// if the indexes property is a scalar, then it applies to all
|
// if the indexes property is a scalar, then it applies to all
|
||||||
// indexes
|
// indexes
|
||||||
var data = ( classes[ cur[ c ] ] || {} ),
|
const data = ( classes[ fieldc[ c ] ] || {} );
|
||||||
thisall = ( typeof data.indexes !== 'object' ),
|
const thisall = !Array.isArray( data.indexes );
|
||||||
alltrue = ( !thisall || data.indexes === 1 );
|
|
||||||
|
let alltrue = ( !thisall || data.indexes === 1 );
|
||||||
|
|
||||||
// if no indexes apply for the given classification (it may be a
|
// if no indexes apply for the given classification (it may be a
|
||||||
// pure boolean), then this variable will be true if any only if
|
// pure boolean), then this variable will be true if any only if
|
||||||
|
@ -95,14 +128,14 @@ module.exports = Class( 'FieldClassMatcher',
|
||||||
// of visibility, that encountering a scalar will still manage
|
// of visibility, that encountering a scalar will still manage
|
||||||
// to affect previous results even if it is the last
|
// to affect previous results even if it is the last
|
||||||
// classification that we are checking
|
// classification that we are checking
|
||||||
var indexes = ( thisall ) ? vis : data.indexes;
|
const indexes = ( thisall ) ? vis : data.indexes;
|
||||||
|
|
||||||
for ( var i in indexes )
|
for ( let i in indexes )
|
||||||
{
|
{
|
||||||
// default to visible; note that, if we've encountered any
|
// default to visible; note that, if we've encountered any
|
||||||
// "all index" situations (scalars), then we must only be
|
// "all index" situations (scalars), then we must only be
|
||||||
// true if the scalar value was true
|
// true if the scalar value was true
|
||||||
vis[ i ] = (
|
vis[ i ] = +(
|
||||||
( !hasall || all )
|
( !hasall || all )
|
||||||
&& ( ( vis[ i ] === undefined )
|
&& ( ( vis[ i ] === undefined )
|
||||||
? 1
|
? 1
|
||||||
|
@ -112,40 +145,18 @@ module.exports = Class( 'FieldClassMatcher',
|
||||||
( thisall ) ? data.indexes : data.indexes[ i ]
|
( thisall ) ? data.indexes : data.indexes[ i ]
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
// all are true unless one is false (duh?)
|
|
||||||
alltrue = !!( alltrue && vis[ i ] );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
alltrue = alltrue && vis.every( x => x );
|
||||||
all = ( all && alltrue );
|
all = ( all && alltrue );
|
||||||
}
|
}
|
||||||
|
|
||||||
// default 'any' to 'all'; this will have the effect of saying "yes
|
|
||||||
// there are matches, but we don't care what" if there are no
|
|
||||||
// indexes associated with the match, implying that all indexes
|
|
||||||
// should match
|
|
||||||
var any = all;
|
|
||||||
for ( var i = 0, len = vis.length; i < len; i++ )
|
|
||||||
{
|
|
||||||
if ( vis[ i ] )
|
|
||||||
{
|
|
||||||
any = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// store the classification match data for assertions, etc
|
// store the classification match data for assertions, etc
|
||||||
cmatch[ field ] = {
|
return {
|
||||||
all: all,
|
all: all,
|
||||||
any: any,
|
any: all || vis.some( x => !!x ),
|
||||||
indexes: vis
|
indexes: vis
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
// currently not asynchronous, but leaves open the possibility
|
|
||||||
callback( cmatch );
|
|
||||||
|
|
||||||
return this;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
@ -164,27 +175,13 @@ module.exports = Class( 'FieldClassMatcher',
|
||||||
*
|
*
|
||||||
* @return {number} 0 if false otherwise 1 for true
|
* @return {number} 0 if false otherwise 1 for true
|
||||||
*/
|
*/
|
||||||
'private _reduceMatch': function( result )
|
'private _reduceMatch'( result )
|
||||||
{
|
{
|
||||||
if ( ( result === undefined )
|
if ( !Array.isArray( result ) )
|
||||||
|| ( result === null )
|
|
||||||
|| ( result.length === undefined )
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
return result;
|
return !!result;
|
||||||
}
|
}
|
||||||
|
|
||||||
var ret = false,
|
return +result.some( x => this._reduceMatch( x ) );
|
||||||
i = result.length;
|
|
||||||
|
|
||||||
// reduce with logical or
|
|
||||||
while( i-- )
|
|
||||||
{
|
|
||||||
// recurse just in case we have another array of values
|
|
||||||
ret = ret || this._reduceMatch( result[ i ] );
|
|
||||||
}
|
|
||||||
|
|
||||||
return +ret;
|
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
|
|
@ -53,6 +53,14 @@ const {
|
||||||
DataApiManager,
|
DataApiManager,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
document: {
|
||||||
|
DocumentProgramFormatter,
|
||||||
|
},
|
||||||
|
|
||||||
|
field: {
|
||||||
|
FieldClassMatcher,
|
||||||
|
},
|
||||||
|
|
||||||
server: {
|
server: {
|
||||||
DocumentServer,
|
DocumentServer,
|
||||||
|
|
||||||
|
@ -450,6 +458,23 @@ function doRoute( program, request, data, resolve, reject )
|
||||||
} );
|
} );
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
else if ( cmd == 'progdata' )
|
||||||
|
{
|
||||||
|
acquireReadLock( quote_id, request, function()
|
||||||
|
{
|
||||||
|
handleRequest( function( quote )
|
||||||
|
{
|
||||||
|
const response = UserResponse( request );
|
||||||
|
const bucket = quote.getBucket();
|
||||||
|
const class_matcher = FieldClassMatcher( program.whens );
|
||||||
|
|
||||||
|
DocumentProgramFormatter( program, class_matcher )
|
||||||
|
.format( bucket )
|
||||||
|
.then( data => response.ok( data ) )
|
||||||
|
.catch( e => response.error( e ) );
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
}
|
||||||
else if ( cmd === 'mkrev' )
|
else if ( cmd === 'mkrev' )
|
||||||
{
|
{
|
||||||
// the database operation for this is atomic and disjoint from
|
// the database operation for this is atomic and disjoint from
|
||||||
|
|
|
@ -0,0 +1,287 @@
|
||||||
|
/**
|
||||||
|
* Test of DocumentProgramFormatter
|
||||||
|
*
|
||||||
|
* Copyright (C) 2018 R-T Specialty, LLC.
|
||||||
|
*
|
||||||
|
* This file is part of liza.
|
||||||
|
*
|
||||||
|
* liza is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const chai = require( 'chai' );
|
||||||
|
const expect = chai.expect;
|
||||||
|
|
||||||
|
const {
|
||||||
|
document: {
|
||||||
|
DocumentProgramFormatter: Sut,
|
||||||
|
},
|
||||||
|
} = require( '../../' );
|
||||||
|
|
||||||
|
chai.use( require( 'chai-as-promised' ) );
|
||||||
|
|
||||||
|
|
||||||
|
describe( 'DocumentProgramFormatter', () =>
|
||||||
|
{
|
||||||
|
it( "formats bucket data by steps, groups and fields", () =>
|
||||||
|
{
|
||||||
|
const bucket_data = {
|
||||||
|
alcohol_shown: [ "foo", "" ],
|
||||||
|
alcohol_not_shown: [ "" ],
|
||||||
|
ecigs_shown_twice: [ "foo", "bar" ],
|
||||||
|
ecigs_not_shown: [ "" ],
|
||||||
|
field_no_label: [ "" ],
|
||||||
|
field_no_vis: [ "true" ],
|
||||||
|
field_empty_array_any_true: [ "bar" ],
|
||||||
|
field_empty_array_any_false: [ "" ]
|
||||||
|
};
|
||||||
|
|
||||||
|
const expected_object = {
|
||||||
|
steps: [
|
||||||
|
{
|
||||||
|
title: "Manage Quote",
|
||||||
|
groups: []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "General Information",
|
||||||
|
groups: [
|
||||||
|
{
|
||||||
|
id: "group_one",
|
||||||
|
title: "Group One",
|
||||||
|
link: "locations",
|
||||||
|
questions: [
|
||||||
|
{
|
||||||
|
id: "alcohol_shown",
|
||||||
|
label: "Alcohol?",
|
||||||
|
value: [ "foo", "" ],
|
||||||
|
type: "noyes",
|
||||||
|
applicable: [ true, false ]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "alcohol_not_shown",
|
||||||
|
label: "No alcohol?",
|
||||||
|
value: [ "" ],
|
||||||
|
type: "noyes",
|
||||||
|
applicable: [ false ]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "field_no_vis",
|
||||||
|
label: "Is this field found in FieldMatcher?",
|
||||||
|
value: [ "true" ],
|
||||||
|
type: "noyes",
|
||||||
|
applicable: [ true ]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "group_two",
|
||||||
|
title: "",
|
||||||
|
link: "",
|
||||||
|
questions: [
|
||||||
|
{
|
||||||
|
id: "ecigs_shown_twice",
|
||||||
|
label: "Two ecig answers?",
|
||||||
|
value: [ "foo", "bar" ],
|
||||||
|
type: "noyes",
|
||||||
|
applicable: [ true, true ]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "ecigs_not_shown",
|
||||||
|
label: "No ecigs?",
|
||||||
|
value: [ "" ],
|
||||||
|
type: "noyes",
|
||||||
|
applicable: [ false ]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "field_empty_array_any_true",
|
||||||
|
label: "Empty match array?",
|
||||||
|
value: [ "bar" ],
|
||||||
|
type: "noyes",
|
||||||
|
applicable: [ true ]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "field_empty_array_any_false",
|
||||||
|
label: "Empty match array and 'any' is false?",
|
||||||
|
value: [ "" ],
|
||||||
|
type: "noyes",
|
||||||
|
applicable: [ false ]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
const bucket = createStubBucket( bucket_data );
|
||||||
|
const program = createStubProgram();
|
||||||
|
const class_matcher = createStubClassMatcher();
|
||||||
|
|
||||||
|
return expect(
|
||||||
|
Sut( program, class_matcher ).format( bucket )
|
||||||
|
).to.eventually.deep.equal( expected_object );
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
|
||||||
|
|
||||||
|
function createStubClassMatcher()
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
match( _, callback )
|
||||||
|
{
|
||||||
|
callback({
|
||||||
|
"__classes": {
|
||||||
|
'--vis-foo': { is: true, indexes: [1,0] },
|
||||||
|
},
|
||||||
|
"alcohol_shown": {
|
||||||
|
"all": false,
|
||||||
|
"any": true,
|
||||||
|
"indexes": [1, 0]
|
||||||
|
},
|
||||||
|
"alcohol_not_shown": {
|
||||||
|
"all": false,
|
||||||
|
"any": false,
|
||||||
|
"indexes": [0]
|
||||||
|
},
|
||||||
|
"ecigs_shown_twice": {
|
||||||
|
"all": false,
|
||||||
|
"any": true,
|
||||||
|
"indexes": [1, 1]
|
||||||
|
},
|
||||||
|
"ecigs_not_shown": {
|
||||||
|
"all": false,
|
||||||
|
"any": false,
|
||||||
|
"indexes": []
|
||||||
|
},
|
||||||
|
"field_empty_array_any_false": {
|
||||||
|
"all": false,
|
||||||
|
"any": false,
|
||||||
|
"indexes": []
|
||||||
|
},
|
||||||
|
"field_empty_array_any_true": {
|
||||||
|
"all": true,
|
||||||
|
"any": true,
|
||||||
|
"indexes": []
|
||||||
|
},
|
||||||
|
}) ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function createStubBucket( metadata )
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
getDataByName: name => metadata[ name ],
|
||||||
|
getData()
|
||||||
|
{
|
||||||
|
return metadata;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function createStubProgram()
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
steps: [
|
||||||
|
{
|
||||||
|
title: "Index 0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Manage Quote",
|
||||||
|
groups: []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "General Information",
|
||||||
|
groups: [ 'group_one', 'group_two' ]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
classify( bucket_data )
|
||||||
|
{
|
||||||
|
return {}
|
||||||
|
},
|
||||||
|
groups:
|
||||||
|
{
|
||||||
|
'group_one':
|
||||||
|
{
|
||||||
|
title: "Group One",
|
||||||
|
link: "locations"
|
||||||
|
},
|
||||||
|
'group_two': {},
|
||||||
|
},
|
||||||
|
fields:
|
||||||
|
{
|
||||||
|
alcohol_shown:
|
||||||
|
{
|
||||||
|
label: "Alcohol?",
|
||||||
|
type: "noyes",
|
||||||
|
required: "true",
|
||||||
|
},
|
||||||
|
alcohol_not_shown:
|
||||||
|
{
|
||||||
|
label: "No alcohol?",
|
||||||
|
type: "noyes",
|
||||||
|
required: "true"
|
||||||
|
},
|
||||||
|
ecigs_shown_twice:
|
||||||
|
{
|
||||||
|
label: "Two ecig answers?",
|
||||||
|
type: "noyes",
|
||||||
|
required: "true"
|
||||||
|
},
|
||||||
|
ecigs_not_shown:
|
||||||
|
{
|
||||||
|
label: "No ecigs?",
|
||||||
|
type: "noyes",
|
||||||
|
required: "true"
|
||||||
|
},
|
||||||
|
field_empty_array_any_true:
|
||||||
|
{
|
||||||
|
label: "Empty match array?",
|
||||||
|
type: "noyes",
|
||||||
|
required: "true"
|
||||||
|
},
|
||||||
|
field_empty_array_any_false:
|
||||||
|
{
|
||||||
|
label: "Empty match array and 'any' is false?",
|
||||||
|
type: "noyes",
|
||||||
|
required: "true"
|
||||||
|
},
|
||||||
|
field_no_vis:
|
||||||
|
{
|
||||||
|
label: "Is this field found in FieldMatcher?",
|
||||||
|
type: "noyes",
|
||||||
|
required: "true"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
groupExclusiveFields:
|
||||||
|
{
|
||||||
|
'group_one': [
|
||||||
|
"alcohol_shown",
|
||||||
|
"alcohol_not_shown",
|
||||||
|
"field_no_label",
|
||||||
|
"field_no_vis"
|
||||||
|
],
|
||||||
|
'group_two': [
|
||||||
|
"ecigs_shown_twice",
|
||||||
|
"ecigs_not_shown",
|
||||||
|
"field_empty_array_any_true",
|
||||||
|
"field_empty_array_any_false"
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,485 @@
|
||||||
|
/**
|
||||||
|
* Test case for FieldVisibilityEventHandler
|
||||||
|
*
|
||||||
|
* Copyright (C) 2018 R-T Specialty, LLC.
|
||||||
|
*
|
||||||
|
* This file is part of the Liza Data Collection Framework
|
||||||
|
*
|
||||||
|
* Liza is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const expect = require( 'chai' ).expect;
|
||||||
|
|
||||||
|
const { FieldClassMatcher: Sut } = require( '../../' ).field;
|
||||||
|
|
||||||
|
|
||||||
|
describe( 'FieldClassMatcher', () =>
|
||||||
|
{
|
||||||
|
it( "echoes provided classes as __classes", done =>
|
||||||
|
{
|
||||||
|
const classes = { foo: [ 1 ], bar: 0 };
|
||||||
|
|
||||||
|
Sut( {} ).match( classes, result =>
|
||||||
|
{
|
||||||
|
expect( result.__classes ).to.deep.equal( classes );
|
||||||
|
done();
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
|
||||||
|
|
||||||
|
[
|
||||||
|
{
|
||||||
|
label: "does nothing with no fields or classes",
|
||||||
|
fields: {},
|
||||||
|
classes: {},
|
||||||
|
expected: {},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
label: "sets all on >0 scalar cmatch",
|
||||||
|
fields: { foo: [ 'foowhen' ] },
|
||||||
|
classes: { foowhen: { indexes: 1 } },
|
||||||
|
expected: {
|
||||||
|
foo: {
|
||||||
|
all: true,
|
||||||
|
any: true,
|
||||||
|
indexes: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
label: "sets none on 0 scalar cmatch",
|
||||||
|
fields: { foo: [ 'foowhen' ] },
|
||||||
|
classes: { foowhen: { indexes: 0 } },
|
||||||
|
expected: {
|
||||||
|
foo: {
|
||||||
|
all: false,
|
||||||
|
any: false,
|
||||||
|
indexes: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
label: "sets all on all vector cmatch",
|
||||||
|
fields: { foo: [ 'foowhen' ] },
|
||||||
|
classes: { foowhen: { indexes: [ 1, 1 ] } },
|
||||||
|
expected: {
|
||||||
|
foo: {
|
||||||
|
all: true,
|
||||||
|
any: true,
|
||||||
|
indexes: [ 1, 1 ],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
label: "sets any on partial vector cmatch",
|
||||||
|
fields: { foo: [ 'foowhen' ] },
|
||||||
|
classes: { foowhen: { indexes: [ 0, 1 ] } },
|
||||||
|
expected: {
|
||||||
|
foo: {
|
||||||
|
all: false,
|
||||||
|
any: true,
|
||||||
|
indexes: [ 0, 1 ],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
label: "sets nothing on empty vector cmatch",
|
||||||
|
fields: { foo: [ 'foowhen' ] },
|
||||||
|
classes: { foowhen: { indexes: [ 0, 0 ] } },
|
||||||
|
expected: {
|
||||||
|
foo: {
|
||||||
|
all: false,
|
||||||
|
any: false,
|
||||||
|
indexes: [ 0, 0 ],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
// bad class data
|
||||||
|
{
|
||||||
|
label: "handles missing class as if scalar 0",
|
||||||
|
fields: { foo: [ 'noexist' ] },
|
||||||
|
classes: {},
|
||||||
|
expected: {
|
||||||
|
foo: {
|
||||||
|
all: false,
|
||||||
|
any: false,
|
||||||
|
indexes: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// multiple classes
|
||||||
|
{
|
||||||
|
label: "logical ANDs multiple vector classes (any)",
|
||||||
|
fields: { foo: [ 'foowhen1', 'foowhen2' ] },
|
||||||
|
classes: {
|
||||||
|
foowhen1: { indexes: [ 1, 0 ] },
|
||||||
|
foowhen2: { indexes: [ 1, 1 ] },
|
||||||
|
},
|
||||||
|
expected: {
|
||||||
|
foo: {
|
||||||
|
all: false,
|
||||||
|
any: true,
|
||||||
|
indexes: [ 1, 0 ],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "logical ANDs multiple vector classes (all)",
|
||||||
|
fields: { foo: [ 'foowhen1', 'foowhen2' ] },
|
||||||
|
classes: {
|
||||||
|
foowhen1: { indexes: [ 1, 1 ] },
|
||||||
|
foowhen2: { indexes: [ 1, 1 ] },
|
||||||
|
},
|
||||||
|
expected: {
|
||||||
|
foo: {
|
||||||
|
all: true,
|
||||||
|
any: true,
|
||||||
|
indexes: [ 1, 1 ],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "logical ANDs multiple vector classes (none)",
|
||||||
|
fields: { foo: [ 'foowhen1', 'foowhen2' ] },
|
||||||
|
classes: {
|
||||||
|
foowhen1: { indexes: [ 0, 0 ] },
|
||||||
|
foowhen2: { indexes: [ 0, 0 ] },
|
||||||
|
},
|
||||||
|
expected: {
|
||||||
|
foo: {
|
||||||
|
all: false,
|
||||||
|
any: false,
|
||||||
|
indexes: [ 0, 0 ],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
label: "assumes match when ANDing varying lengths (any)",
|
||||||
|
fields: { foo: [ 'foowhen1', 'foowhen2' ] },
|
||||||
|
classes: {
|
||||||
|
foowhen1: { indexes: [ 0 ] },
|
||||||
|
foowhen2: { indexes: [ 0, 1 ] },
|
||||||
|
},
|
||||||
|
expected: {
|
||||||
|
foo: {
|
||||||
|
all: false,
|
||||||
|
any: true,
|
||||||
|
indexes: [ 0, 1 ],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "assumes match when ANDing varying lengths (all)",
|
||||||
|
fields: { foo: [ 'foowhen1', 'foowhen2' ] },
|
||||||
|
classes: {
|
||||||
|
foowhen1: { indexes: [ 1 ] },
|
||||||
|
foowhen2: { indexes: [ 1, 1 ] },
|
||||||
|
},
|
||||||
|
expected: {
|
||||||
|
foo: {
|
||||||
|
all: true,
|
||||||
|
any: true,
|
||||||
|
indexes: [ 1, 1 ],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "assumes match when ANDing varying lengths (none)",
|
||||||
|
fields: { foo: [ 'foowhen1', 'foowhen2' ] },
|
||||||
|
classes: {
|
||||||
|
foowhen1: { indexes: [ 0 ] },
|
||||||
|
foowhen2: { indexes: [ 0, 0 ] },
|
||||||
|
},
|
||||||
|
expected: {
|
||||||
|
foo: {
|
||||||
|
all: false,
|
||||||
|
any: false,
|
||||||
|
indexes: [ 0, 0 ],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
label: "logically ANDs scalar with vector (any)",
|
||||||
|
fields: { foo: [ 'foowhen1', 'foowhen2' ] },
|
||||||
|
classes: {
|
||||||
|
foowhen1: { indexes: 1 },
|
||||||
|
foowhen2: { indexes: [ 0, 1, 0 ] },
|
||||||
|
},
|
||||||
|
expected: {
|
||||||
|
foo: {
|
||||||
|
all: false,
|
||||||
|
any: true,
|
||||||
|
indexes: [ 0, 1, 0 ],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "logically ANDs scalar with vector (all)",
|
||||||
|
fields: { foo: [ 'foowhen1', 'foowhen2' ] },
|
||||||
|
classes: {
|
||||||
|
foowhen1: { indexes: 1 },
|
||||||
|
foowhen2: { indexes: [ 1, 1 ] },
|
||||||
|
},
|
||||||
|
expected: {
|
||||||
|
foo: {
|
||||||
|
all: true,
|
||||||
|
any: true,
|
||||||
|
indexes: [ 1, 1 ],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "logically ANDs scalar with vector (none)",
|
||||||
|
fields: { foo: [ 'foowhen1', 'foowhen2' ] },
|
||||||
|
classes: {
|
||||||
|
foowhen1: { indexes: 0 },
|
||||||
|
foowhen2: { indexes: [ 1, 1 ] },
|
||||||
|
},
|
||||||
|
expected: {
|
||||||
|
foo: {
|
||||||
|
all: false,
|
||||||
|
any: false,
|
||||||
|
indexes: [ 0, 0 ],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
label: "logically ANDs scalar with vector (rev, any)",
|
||||||
|
fields: { foo: [ 'foowhen1', 'foowhen2' ] },
|
||||||
|
classes: {
|
||||||
|
foowhen1: { indexes: [ 0, 1, 0 ] },
|
||||||
|
foowhen2: { indexes: 1 },
|
||||||
|
},
|
||||||
|
expected: {
|
||||||
|
foo: {
|
||||||
|
all: false,
|
||||||
|
any: true,
|
||||||
|
indexes: [ 0, 1, 0 ],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "logically ANDs scalar with vector (rev, all)",
|
||||||
|
fields: { foo: [ 'foowhen1', 'foowhen2' ] },
|
||||||
|
classes: {
|
||||||
|
foowhen1: { indexes: [ 1, 1 ] },
|
||||||
|
foowhen2: { indexes: 1 },
|
||||||
|
},
|
||||||
|
expected: {
|
||||||
|
foo: {
|
||||||
|
all: true,
|
||||||
|
any: true,
|
||||||
|
indexes: [ 1, 1 ],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "logically ANDs scalar with vector (rev, none)",
|
||||||
|
fields: { foo: [ 'foowhen1', 'foowhen2' ] },
|
||||||
|
classes: {
|
||||||
|
foowhen1: { indexes: [ 1, 1 ] },
|
||||||
|
foowhen2: { indexes: 0 },
|
||||||
|
},
|
||||||
|
expected: {
|
||||||
|
foo: {
|
||||||
|
all: false,
|
||||||
|
any: false,
|
||||||
|
indexes: [ 0, 0 ],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
label: "logically ANDs scalar with scalar (all)",
|
||||||
|
fields: { foo: [ 'foowhen1', 'foowhen2' ] },
|
||||||
|
classes: {
|
||||||
|
foowhen1: { indexes: 1 },
|
||||||
|
foowhen2: { indexes: 1 },
|
||||||
|
},
|
||||||
|
expected: {
|
||||||
|
foo: {
|
||||||
|
all: true,
|
||||||
|
any: true,
|
||||||
|
indexes: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "logically ANDs scalar with scalar (none 0)",
|
||||||
|
fields: { foo: [ 'foowhen1', 'foowhen2' ] },
|
||||||
|
classes: {
|
||||||
|
foowhen1: { indexes: 0 },
|
||||||
|
foowhen2: { indexes: 0 },
|
||||||
|
},
|
||||||
|
expected: {
|
||||||
|
foo: {
|
||||||
|
all: false,
|
||||||
|
any: false,
|
||||||
|
indexes: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "logically ANDs scalar with scalar (none 1)",
|
||||||
|
fields: { foo: [ 'foowhen1', 'foowhen2' ] },
|
||||||
|
classes: {
|
||||||
|
foowhen1: { indexes: 0 },
|
||||||
|
foowhen2: { indexes: 1 },
|
||||||
|
},
|
||||||
|
expected: {
|
||||||
|
foo: {
|
||||||
|
all: false,
|
||||||
|
any: false,
|
||||||
|
indexes: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
// matrix cmatch
|
||||||
|
{
|
||||||
|
label: "logically ORs matrix cmatch (single)",
|
||||||
|
fields: { foo: [ 'foowhen' ] },
|
||||||
|
classes: {
|
||||||
|
foowhen: { indexes: [ [ 1 ] ] },
|
||||||
|
},
|
||||||
|
expected: {
|
||||||
|
foo: {
|
||||||
|
all: true,
|
||||||
|
any: true,
|
||||||
|
indexes: [ 1 ],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "logically ORs matrix cmatch (any)",
|
||||||
|
fields: { foo: [ 'foowhen' ] },
|
||||||
|
classes: {
|
||||||
|
foowhen: { indexes: [ [ 1 ], [ 0, 1 ], [ 0, 1 ], [ 0, 0 ] ] },
|
||||||
|
},
|
||||||
|
expected: {
|
||||||
|
foo: {
|
||||||
|
all: false,
|
||||||
|
any: true,
|
||||||
|
indexes: [ 1, 1, 1, 0 ],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "logically ORs matrix cmatch (all)",
|
||||||
|
fields: { foo: [ 'foowhen' ] },
|
||||||
|
classes: {
|
||||||
|
foowhen: { indexes: [ [ 1 ], [ 0, 1 ], [ 0, 1 ] ] },
|
||||||
|
},
|
||||||
|
expected: {
|
||||||
|
foo: {
|
||||||
|
all: true,
|
||||||
|
any: true,
|
||||||
|
indexes: [ 1, 1, 1 ],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "logically AND's logically OR'd matrix cmatches",
|
||||||
|
fields: { foo: [ 'foowhen1', 'foowhen2' ] },
|
||||||
|
classes: {
|
||||||
|
foowhen1: { indexes: [ [ 0 ], [ 0, 1 ], [ 1 ] ] },
|
||||||
|
foowhen2: { indexes: [ [ 0 ], [ 0, 1 ], [ 0 ] ] },
|
||||||
|
},
|
||||||
|
expected: {
|
||||||
|
foo: {
|
||||||
|
all: false,
|
||||||
|
any: true,
|
||||||
|
indexes: [ 0, 1, 0 ],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// arbitrary nesting
|
||||||
|
{
|
||||||
|
label: "logically ORs arbitrarily nested vectors",
|
||||||
|
fields: { foo: [ 'foowhen' ] },
|
||||||
|
classes: {
|
||||||
|
foowhen: { indexes: [ [ [ 0 ], [ 0, 1 ] ], [ [ [ 0 ] ] ] ] },
|
||||||
|
},
|
||||||
|
expected: {
|
||||||
|
foo: {
|
||||||
|
all: false,
|
||||||
|
any: true,
|
||||||
|
indexes: [ 1, 0 ],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
// multiple fields
|
||||||
|
{
|
||||||
|
label: "sets multiple fields",
|
||||||
|
fields: { foo: [ 'foowhen' ], bar: [ 'barwhen' ] },
|
||||||
|
classes: {
|
||||||
|
foowhen: { indexes: [ 0, 0 ] },
|
||||||
|
barwhen: { indexes: [ 1, 1 ] },
|
||||||
|
},
|
||||||
|
expected: {
|
||||||
|
foo: {
|
||||||
|
all: false,
|
||||||
|
any: false,
|
||||||
|
indexes: [ 0, 0 ],
|
||||||
|
},
|
||||||
|
bar: {
|
||||||
|
all: true,
|
||||||
|
any: true,
|
||||||
|
indexes: [ 1, 1 ],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
label: "sets all for fields with no predicates",
|
||||||
|
fields: { foo: [] },
|
||||||
|
classes: {},
|
||||||
|
expected: {
|
||||||
|
foo: {
|
||||||
|
all: true,
|
||||||
|
any: true,
|
||||||
|
indexes: [],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
].forEach( ( { label, fields, classes, expected } ) =>
|
||||||
|
{
|
||||||
|
it( label, done =>
|
||||||
|
{
|
||||||
|
// no use in specifying this above every time
|
||||||
|
expected.__classes = classes;
|
||||||
|
|
||||||
|
Sut( fields ).match( classes, result =>
|
||||||
|
{
|
||||||
|
expect( result ).to.deep.equal( expected );
|
||||||
|
done();
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
} );
|
Loading…
Reference in New Issue