enketo-validate
Advanced tools
Comparing version 1.0.3 to 1.1.0
@@ -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 @@ --------------------- |
{ | ||
"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
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Git dependency
Supply chain riskContains a dependency which resolves to a remote git URL. Dependencies fetched from git URLs are not immutable can be used to inject untrusted code or reduce the likelihood of a reproducible install.
Found 1 instance in 1 package
Install scripts
Supply chain riskInstall scripts are run when the package is installed. The majority of malware in npm is hidden in install scripts.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
1345212
3
39
39601
8
80
2
8
+ Addedaliasify@^2.1.0
+ Addedbrowserify@^15.2.0
+ Addedenketo-core@4.44.4
+ Addedenketo-xpathjs-oc@git+https://github.com/OpenClinica/enketo-xpathjs-oc.git#3279e19
+ AddedJSONStream@1.3.5(transitive)
+ Addedacorn@7.4.1(transitive)
+ Addedacorn-node@1.8.2(transitive)
+ Addedacorn-walk@7.2.0(transitive)
+ Addedaliasify@2.1.0(transitive)
+ Addedasn1.js@4.10.1(transitive)
+ Addedassert@1.5.1(transitive)
+ Addedbalanced-match@1.0.2(transitive)
+ Addedbase64-js@1.5.1(transitive)
+ Addedbn.js@4.12.15.2.1(transitive)
+ Addedbootstrap-datepicker@1.7.1(transitive)
+ Addedbootstrap-slider-basic@3.3.0(transitive)
+ Addedbrace-expansion@1.1.11(transitive)
+ Addedbrorand@1.1.0(transitive)
+ Addedbrowser-pack@6.1.0(transitive)
+ Addedbrowser-resolve@1.11.3(transitive)
+ Addedbrowserify@15.2.0(transitive)
+ Addedbrowserify-aes@1.2.0(transitive)
+ Addedbrowserify-cipher@1.0.1(transitive)
+ Addedbrowserify-des@1.0.2(transitive)
+ Addedbrowserify-rsa@4.1.1(transitive)
+ Addedbrowserify-sign@4.2.3(transitive)
+ Addedbrowserify-transform-tools@1.7.0(transitive)
+ Addedbrowserify-zlib@0.2.0(transitive)
+ Addedbuffer@5.7.1(transitive)
+ Addedbuffer-from@1.1.2(transitive)
+ Addedbuffer-xor@1.0.3(transitive)
+ Addedbuiltin-status-codes@3.0.0(transitive)
+ Addedcached-path-relative@1.1.0(transitive)
+ Addedcall-bind@1.0.7(transitive)
+ Addedcipher-base@1.0.6(transitive)
+ Addedcombine-source-map@0.8.0(transitive)
+ Addedconcat-map@0.0.1(transitive)
+ Addedconcat-stream@1.5.21.6.2(transitive)
+ Addedconsole-browserify@1.2.0(transitive)
+ Addedconstants-browserify@1.0.0(transitive)
+ Addedconvert-source-map@1.1.3(transitive)
+ Addedcore-util-is@1.0.3(transitive)
+ Addedcreate-ecdh@4.0.4(transitive)
+ Addedcreate-hash@1.2.0(transitive)
+ Addedcreate-hmac@1.1.7(transitive)
+ Addedcrypto-browserify@3.12.1(transitive)
+ Addeddash-ast@1.0.0(transitive)
+ Addeddefine-data-property@1.1.4(transitive)
+ Addeddefine-properties@1.2.1(transitive)
+ Addeddefined@1.0.1(transitive)
+ Addeddeps-sort@2.0.1(transitive)
+ Addeddes.js@1.1.0(transitive)
+ Addeddetective@5.2.1(transitive)
+ Addeddiffie-hellman@5.0.3(transitive)
+ Addeddomain-browser@1.1.7(transitive)
+ Addedduplexer2@0.1.4(transitive)
+ Addedelliptic@6.6.1(transitive)
+ Addedenketo-core@4.44.4(transitive)
+ Addedenketo-xpathjs@1.6.2(transitive)
+ Addedes-define-property@1.0.0(transitive)
+ Addedes-errors@1.3.0(transitive)
+ Addedevents@1.1.1(transitive)
+ Addedevp_bytestokey@1.0.3(transitive)
+ Addedfalafel@2.2.5(transitive)
+ Addedfast-safe-stringify@2.1.1(transitive)
+ Addedfs.realpath@1.0.0(transitive)
+ Addedfunction-bind@1.1.2(transitive)
+ Addedget-assigned-identifiers@1.2.0(transitive)
+ Addedget-intrinsic@1.2.4(transitive)
+ Addedglob@7.2.3(transitive)
+ Addedgopd@1.1.0(transitive)
+ Addedhas@1.0.4(transitive)
+ Addedhas-property-descriptors@1.0.2(transitive)
+ Addedhas-proto@1.0.3(transitive)
+ Addedhas-symbols@1.0.3(transitive)
+ Addedhash-base@3.0.5(transitive)
+ Addedhash.js@1.1.7(transitive)
+ Addedhasown@2.0.2(transitive)
+ Addedhmac-drbg@1.0.1(transitive)
+ Addedhtmlescape@1.1.1(transitive)
+ Addedhttps-browserify@1.0.0(transitive)
+ Addedieee754@1.2.1(transitive)
+ Addedimmediate@3.0.6(transitive)
+ Addedindexof@0.0.1(transitive)
+ Addedinflight@1.0.6(transitive)
+ Addedinherits@2.0.32.0.4(transitive)
+ Addedinline-source-map@0.6.3(transitive)
+ Addedinsert-module-globals@7.2.1(transitive)
+ Addedis-buffer@1.1.6(transitive)
+ Addedis-core-module@2.15.1(transitive)
+ Addedisarray@1.0.02.0.5(transitive)
+ Addedjquery@3.3.1(transitive)
+ Addedjquery-touchswipe@1.6.19(transitive)
+ Addedjson-stable-stringify@0.0.1(transitive)
+ Addedjsonify@0.0.1(transitive)
+ Addedjsonparse@1.3.1(transitive)
+ Addedlabeled-stream-splicer@2.0.2(transitive)
+ Addedleaflet@0.7.7(transitive)
+ Addedlie@3.2.0(transitive)
+ Addedlodash.memoize@3.0.4(transitive)
+ Addedmd5.js@1.3.5(transitive)
+ Addedmergexml@1.1.2(transitive)
+ Addedmiller-rabin@4.0.1(transitive)
+ Addedminimalistic-assert@1.0.1(transitive)
+ Addedminimalistic-crypto-utils@1.0.1(transitive)
+ Addedminimatch@3.1.2(transitive)
+ Addedminimist@1.2.8(transitive)
+ Addedmkdirp@0.5.6(transitive)
+ Addedmodule-deps@5.0.1(transitive)
+ Addedobject-inspect@1.13.3(transitive)
+ Addedobject-keys@1.1.1(transitive)
+ Addedobject.assign@4.1.5(transitive)
+ Addedonce@1.4.0(transitive)
+ Addedos-browserify@0.3.0(transitive)
+ Addedpako@1.0.11(transitive)
+ Addedparents@1.0.1(transitive)
+ Addedparse-asn1@5.1.7(transitive)
+ Addedpath-browserify@0.0.1(transitive)
+ Addedpath-is-absolute@1.0.1(transitive)
+ Addedpath-parse@1.0.7(transitive)
+ Addedpath-platform@0.11.15(transitive)
+ Addedpbkdf2@3.1.2(transitive)
+ Addedprocess@0.11.10(transitive)
+ Addedprocess-nextick-args@1.0.72.0.1(transitive)
+ Addedpublic-encrypt@4.0.3(transitive)
+ Addedpunycode@1.4.1(transitive)
+ Addedqs@6.13.1(transitive)
+ Addedquerystring-es3@0.2.1(transitive)
+ Addedrandombytes@2.1.0(transitive)
+ Addedrandomfill@1.0.4(transitive)
+ Addedread-only-stream@2.0.0(transitive)
+ Addedreadable-stream@2.0.62.3.8(transitive)
+ Addedresolve@1.1.71.22.8(transitive)
+ Addedripemd160@2.0.2(transitive)
+ Addedsafe-buffer@5.1.2(transitive)
+ Addedset-function-length@1.2.2(transitive)
+ Addedsha.js@2.4.11(transitive)
+ Addedshasum@1.0.2(transitive)
+ Addedshasum-object@1.0.0(transitive)
+ Addedshell-quote@1.8.2(transitive)
+ Addedside-channel@1.0.6(transitive)
+ Addedsignature_pad@2.3.2(transitive)
+ Addedsimple-concat@1.0.1(transitive)
+ Addedsource-map@0.5.7(transitive)
+ Addedstream-browserify@2.0.2(transitive)
+ Addedstream-combiner2@1.1.1(transitive)
+ Addedstream-http@2.8.3(transitive)
+ Addedstream-splicer@2.0.1(transitive)
+ Addedstring_decoder@0.10.311.0.31.1.1(transitive)
+ Addedsubarg@1.0.0(transitive)
+ Addedsupports-preserve-symlinks-flag@1.0.0(transitive)
+ Addedsyntax-error@1.4.0(transitive)
+ Addedthrough@2.3.8(transitive)
+ Addedthrough2@2.0.5(transitive)
+ Addedtimers-browserify@1.4.2(transitive)
+ Addedto-arraybuffer@1.0.1(transitive)
+ Addedtty-browserify@0.0.1(transitive)
+ Addedtypedarray@0.0.60.0.7(transitive)
+ Addedumd@3.0.3(transitive)
+ Addedundeclared-identifiers@1.1.3(transitive)
+ Addedurl@0.11.4(transitive)
+ Addedutil@0.10.4(transitive)
+ Addedutil-deprecate@1.0.2(transitive)
+ Addedvm-browserify@0.0.4(transitive)
+ Addedwrappy@1.0.2(transitive)
+ Addedxtend@4.0.2(transitive)
Updatedcommander@^2.13.0
Updatedenketo-xslt@^1.16.1
Updatedjsdom@^11.6.2