Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

enketo-validate

Package Overview
Dependencies
Maintainers
1
Versions
42
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

enketo-validate - npm Package Compare versions

Comparing version 1.0.3 to 1.1.0

appveyor.yml

12

CHANGELOG.md

@@ -5,2 +5,14 @@ ## Change Log

[1.1.0] - 2018-01-30
---------------------
##### Removed
- Separate OC build
##### Added
- Custom OC rule for external clinical data.
- Rule to require calculations without form control to have readonly="true" attribute.
##### Changed
- The `--oc` flag will now run all OC customizations. No separate build/binary required any more.
[1.0.3] - 2017-01-03

@@ -7,0 +19,0 @@ ---------------------

24

package.json
{
"name": "enketo-validate",
"version": "1.0.3",
"version": "1.1.0",
"description": "An XForm validator around Enketo's form engine",

@@ -8,4 +8,3 @@ "main": "src/validator.js",

"test": "mocha test/spec/*.spec.js",
"build": "browserify src/FormModel.js > build/FormModel-bundle.js",
"oc-build": "browserify -g aliasify src/FormModel.js > build/FormModel-bundle.js"
"install": "browserify src/FormModel.js > build/FormModel-bundle.js && browserify -g aliasify src/FormModel.js > build/FormModel-bundle-oc.js"
},

@@ -29,14 +28,15 @@ "repository": {

"dependencies": {
"commander": "^2.12.2",
"enketo-xslt": "^1.15.5",
"jsdom": "^11.5.1",
"aliasify": "^2.1.0",
"browserify": "^15.2.0",
"commander": "^2.13.0",
"enketo-core": "4.44.4",
"enketo-xpathjs-oc": "git+https://github.com/OpenClinica/enketo-xpathjs-oc.git#3279e19",
"enketo-xslt": "^1.16.1",
"jsdom": "^11.6.2",
"libxslt": "0.6.5"
},
"devDependencies": {
"aliasify": "^2.1.0",
"browserify": "^14.5.0",
"chai": "^4.1.2",
"enketo-core": "^4.42.3",
"enketo-xpathjs-oc": "git+https://github.com/OpenClinica/enketo-xpathjs-oc.git",
"mocha": "^4.1.0"
"mocha": "^5.0.0",
"pkg": "^4.3.0"
},

@@ -48,2 +48,2 @@ "aliasify": {

}
}
}

@@ -18,4 +18,5 @@ Enketo Validate [![npm version](https://badge.fury.io/js/enketo-validate.svg)](http://badge.fury.io/js/enketo-validate) [![Build Status](https://travis-ci.org/enketo/enketo-validate.svg?branch=master)](https://travis-ci.org/enketo/enketo-validate) [![Dependency Status](https://david-dm.org/enketo/enketo-validate/status.svg)](https://david-dm.org/enketo/enketo-validate) [![devDependency Status](https://david-dm.org/enketo/enketo-validate/dev-status.svg)](https://david-dm.org/enketo/enketo-validate?type=dev)

1. install nodeJS 6+
2. clone repo
3. `npm install --production`
2. install build tools for native modules with `apt-get install build-essential`
3. clone repo
4. `npm install --production`

@@ -48,3 +49,3 @@ #### Command-line Use

let result = validator.validate( xformStr );
let result = validator.validate( xformStr, options );

@@ -70,15 +71,2 @@ // The result has the following format:

In the future, some ideas to extend validation further are:
* Check itemsets.
* Check more thoroughly whether XForms syntax is valid using an XML Schema.
* Check whether all itext elements referred to anywhere exist in model.
### Using a custom XPath Evaluator
The following example shows how to swap Enketo's XPath evaluator with OpenClinica's custom XPath evaluator in command-line mode:
1. Instead of using `npm install --production` do `npm install` which which will also install devDepencies.
2. Run `npm run oc-build`. This will replace the bundle file in the /build folder.
### Funding

@@ -85,0 +73,0 @@

@@ -5,3 +5,3 @@ 'use strict';

let validate = ( xformStr, options ) => {
let validate = ( xformStr, options = {} ) => {
let warnings = [];

@@ -19,2 +19,6 @@ let errors = [];

xform.checkStructure( warnings, errors );
xform.checkRules( warnings, errors );
if ( options.openclinica ) {
xform.checkOpenClinicaRules( warnings, errors );
}
}

@@ -73,2 +77,2 @@

validate: validate
};
};

@@ -15,2 +15,3 @@ 'use strict';

constructor( xformStr, options = {} ) {
this.options = options;
if ( !xformStr || !xformStr.trim() ) {

@@ -22,13 +23,21 @@ throw 'Empty form.';

this.doc = this.dom.window.document;
this.debug = !!options.debug;
}
get binds() {
return this.doc.querySelectorAll( 'bind' );
this._binds = this._binds || [ ...this.doc.querySelectorAll( 'bind' ) ];
return this._binds;
}
get bindsWithCalc() {
this._bindsWithCalc = this._bindsWithCalc || [ ...this.doc.querySelectorAll( 'bind[calculate]' ) ];
return this._bindsWithCalc;
}
// The reason this is not included in the constructor is to separate different types of errors,
// and keep the constructor just for XML parse errors.
parseModel() {
const scriptContent = fs.readFileSync( path.join( __dirname, '../build/FormModel-bundle.js' ), { encoding: 'utf-8' } );
// Be careful here, the pkg module to create binaries is surprisingly sophisticated, but the paths cannot be dynamic.
const scriptContent = this.options.openclinica ?
fs.readFileSync( path.join( __dirname, '../build/FormModel-bundle-oc.js' ), { encoding: 'utf-8' } ) :
fs.readFileSync( path.join( __dirname, '../build/FormModel-bundle.js' ), { encoding: 'utf-8' } );

@@ -155,6 +164,38 @@ // This window is not to be confused with this.dom.window which contains the XForm.

if ( this.doc.querySelector( 'group:not([ref]) > repeat' ) ) {
warnings.push( 'Found <repeat> that has a parent <group> without a ref attribute. If the repeat has relevant logic, this will make the form very slow.' );
warnings.push( 'Found <repeat> that has a parent <group> without a ref attribute. ' +
'If the repeat has relevant logic, this will make the form very slow.' );
}
}
checkRules( warnings, errors ) {
// Check for use of form controls with calculations that are not readonly
this.bindsWithCalc
.filter( this._withFormControl.bind( this ) )
.filter( bind => {
const readonly = bind.getAttribute( 'readonly' );
// TODO: the check for true() should be probably be done in XPath,
// using XPath boolean conversion rules.
return !readonly || readonly.trim() !== 'true()';
} )
.map( this._nodeNames.bind( this ) )
.forEach( nodeName => errors.push( `Question "${nodeName}" has a calculation is not set to readonly.` ) );
}
checkOpenClinicaRules( warnings, errors ) {
const OC_NS = 'http://openclinica.org/xforms';
const CLINICALDATA_REF = /instance\(\s*(["'])((?:(?!\1)clinicaldata))\1\s*\)/;
// Check for use of external data in instance "clinicaldata"
this.bindsWithCalc
.filter( this._withoutFormControl.bind( this ) )
.filter( bind => {
// If both are true we have found an error (in an efficient manner)
return CLINICALDATA_REF.test( bind.getAttribute( 'calculate' ) ) &&
bind.getAttributeNS( OC_NS, 'external' ) !== 'clinicaldata';
} )
.map( this._nodeNames.bind( this ) )
.forEach( nodeName => errors.push( `Found calculation for "${nodeName}" that refers to ` +
'external clinicaldata without the required "external" attribute in the correct namespace.' ) );
}
/*

@@ -208,4 +249,29 @@ * Obtain an isolated "browser" window context and optionally, run a script in this context.

/**
* Determines whether bind element has corresponding input form control.
*
* @param {Element} bind The XForm <bind> element
* @returns {boolean}
* @memberof XForm
*/
_withFormControl( bind ) {
const nodeset = bind.getAttribute( 'nodeset' );
// We are not checking for <group> and <repeat>,
// as the purpose of this function is to identify calculations without form control
return !!this.doc.querySelector( `input[ref="${nodeset}"], select[ref="${nodeset}"], ` +
`select1[ref="${nodeset}"], trigger[ref="${nodeset}"]` );
}
_withoutFormControl( bind ) {
return !this._withFormControl( bind );
}
_nodeNames( bind ) {
const path = bind.getAttribute( 'nodeset' );
return path.substring( path.lastIndexOf( '/' ) + 1 );
}
_cleanXmlDomParserError( error ) {
if ( this.debug ) {
console.log( 'this.options', this.options );
if ( this.options.debug ) {
return error;

@@ -218,3 +284,3 @@ }

_cleanXPathException( error ) {
if ( this.debug ) {
if ( this.options.debug ) {
return error;

@@ -237,2 +303,2 @@ }

XForm: XForm
};
};

@@ -87,2 +87,12 @@ /* eslint-env mocha */

describe( 'validated with custom OpenClinica rules', () => {
const xf1 = loadXForm( 'openclinica.xml' );
const result1 = validator.validate( xf1, { openclinica: true } );
it( 'outputs errors for calculations without form control that refer to external ' +
'clinicaldata instance but do not have the oc:external="clinicaldata" bind', () => {
expect( result1.errors.length ).to.equal( 7 );
expect( arrContains( result1.errors, /refers to external clinicaldata without the required "external" attribute/i ) ).to.equal( true );
} );
} );
} );

@@ -103,6 +103,21 @@ /* eslint-env mocha */

xdescribe( 'with comment-status() calls', () => {
// Needs to fail here as we're not in openclinica mode.
describe( 'with comment-status() calls', () => {
it( 'should not throw an error message', () => {
const expr = 'comment-status(/data/a)';
const evaluationFn = () => xf.enketoEvaluate( expr );
expect( evaluationFn ).to.throw();
} );
} );
} );
describe( 'XPath expressions (in custom OpenClinica evaluator)', () => {
const xf = new XForm( loadXForm( 'model-only.xml' ), { openclinica: true } );
describe( 'with comment-status() calls', () => {
it( 'should not throw an error message', () => {
const expr = 'comment-status(/data/a)';
const evaluationFn = () => xf.enketoEvaluate( expr );
expect( evaluationFn ).not.to.throw();

@@ -109,0 +124,0 @@ } );

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc