From 8b7fdfdb8d421b08651297d6eaf41eaae3dbd474 Mon Sep 17 00:00:00 2001 From: Matthew Gramigna Date: Thu, 6 Oct 2022 10:31:06 -0400 Subject: [PATCH] Add prettier config and format source code (#28) --- .eslintrc.json | 41 ++++---- .github/workflows/ci-workflow.yml | 7 +- .prettierignore | 2 + .prettierrc | 8 ++ package.json | 8 +- src/fhir.js | 129 ++++++++++++++---------- src/index.js | 2 +- src/load.js | 156 ++++++++++++++++++++++-------- test/dstu2_test.js | 147 +++++++++++++++++++--------- test/r401_test.js | 134 +++++++++++++++++-------- test/r4_test.js | 155 ++++++++++++++++++++--------- test/stu3_test.js | 148 +++++++++++++++++++--------- yarn.lock | 10 ++ 13 files changed, 652 insertions(+), 295 deletions(-) create mode 100644 .prettierignore create mode 100644 .prettierrc diff --git a/.eslintrc.json b/.eslintrc.json index 611209e..a712dcd 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,23 +1,20 @@ { - "parserOptions": { - "ecmaVersion": 6, - "sourceType": "module", - "ecmaFeatures": { - "experimentalObjectRestSpread": true - } - }, - "rules": { - "indent": ["error", 2], - "linebreak-style": ["error", "unix"], - "no-console": ["warn", { "allow": ["warn", "error"] }], - "no-unused-vars": ["error", { "vars": "all", "args": "none" }], - "quotes": ["error", "single", { "allowTemplateLiterals": true }], - "semi": ["error", "always"] - }, - "env": { - "es6": true, - "node": true, - "mocha": true - }, - "extends": ["eslint:recommended"] -} \ No newline at end of file + "parserOptions": { + "ecmaVersion": 6, + "sourceType": "module", + "ecmaFeatures": { + "experimentalObjectRestSpread": true + } + }, + "rules": { + "linebreak-style": ["error", "unix"], + "no-console": ["warn", { "allow": ["warn", "error"] }], + "no-unused-vars": ["error", { "vars": "all", "args": "none" }] + }, + "env": { + "es6": true, + "node": true, + "mocha": true + }, + "extends": ["eslint:recommended", "prettier"] +} diff --git a/.github/workflows/ci-workflow.yml b/.github/workflows/ci-workflow.yml index 64e90a2..99788cd 100644 --- a/.github/workflows/ci-workflow.yml +++ b/.github/workflows/ci-workflow.yml @@ -4,7 +4,7 @@ on: [push, pull_request] jobs: build: - name: Checkout, install, lint, build and test + name: Checkout, install, lint, prettier, build and test runs-on: ubuntu-latest strategy: matrix: @@ -45,6 +45,11 @@ jobs: env: CI: true + - name: Check prettier formatting + run: yarn prettier + env: + CI: true + - name: Build the source code run: yarn build env: diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..d1212a5 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,2 @@ +lib +src/modelInfos diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..0243ce1 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,8 @@ +{ + "trailingComma": "none", + "arrowParens": "avoid", + "endOfLine": "auto", + "printWidth": 100, + "tabWidth": 2, + "singleQuote": true +} diff --git a/package.json b/package.json index 53cc95c..11cd1e2 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,9 @@ "test:watch": "npm test -- --watch", "test:debug": "./node_modules/.bin/mocha --inspect --debug-brk --reporter spec --recursive", "lint": "./node_modules/.bin/eslint .", - "lint:fix": "./node_modules/.bin/eslint . --fix" + "lint:fix": "./node_modules/.bin/eslint . --fix", + "prettier": "prettier --check \"**/*.js\"", + "prettier:fix": "prettier --write \"**/*.js\"" }, "dependencies": { "xml2js": "~0.4.23" @@ -28,7 +30,9 @@ "chai": "^4.3.4", "cql-execution": ">=1.3.0", "eslint": "^8.3.0", - "mocha": "9.2.0" + "eslint-config-prettier": "^8.5.0", + "mocha": "9.2.0", + "prettier": "^2.7.1" }, "peerDependencies": { "cql-execution": ">=1.3.0" diff --git a/src/fhir.js b/src/fhir.js index 9336bf8..66fa097 100644 --- a/src/fhir.js +++ b/src/fhir.js @@ -41,9 +41,11 @@ class FHIRWrapper { } _typeCastIsAllowed(currentClass, targetClass) { - return (targetClass == currentClass) || - (currentClass.parentClasses().includes(targetClass)) || // upcasting, safe - (targetClass.parentClasses().includes(currentClass)); // downcasting, unsafe but allowed + return ( + targetClass == currentClass || + currentClass.parentClasses().includes(targetClass) || // upcasting, safe + targetClass.parentClasses().includes(currentClass) // downcasting, unsafe but allowed + ); } } @@ -108,7 +110,7 @@ class FHIRObject { Object.defineProperties(this, { _json: { value: json, enumerable: false }, _typeInfo: { value: typeInfo, enumerable: false }, - _modelInfo: { value: modelInfo, enumerable: false }, + _modelInfo: { value: modelInfo, enumerable: false } }); if (typeInfo == null) { console.error(`Failed to locate typeInfo for ${json}`); @@ -127,7 +129,7 @@ class FHIRObject { for (const name of elementNames) { Object.defineProperty(this, name, { - get: function() { + get: function () { return this.get(name); }, enumerable: true @@ -151,7 +153,7 @@ class FHIRObject { return; } - const choicePropertyName = function(element, choice) { + const choicePropertyName = function (element, choice) { return `${element.name}${choice.name[0].toUpperCase()}${choice.name.slice(1)}`; }; @@ -161,7 +163,9 @@ class FHIRObject { // property is a choice (e.g., medication). In this case we need to find the matchin choice // and use it. We don't want other choices, even if they're in the data. property = root; // keep the explicit name - typeSpecifier = element.typeSpecifier.choices.find(c => property === choicePropertyName(element, c)); + typeSpecifier = element.typeSpecifier.choices.find( + c => property === choicePropertyName(element, c) + ); } else { property = element.name; typeSpecifier = element.typeSpecifier; @@ -184,7 +188,9 @@ class FHIRObject { if (typeSpecifier.namespace === 'System') { // TODO: If there is a suffix, we need to drill into the CQL system type! if (suffix != null) { - console.error(`Traversing into CQL system types isn't supported: ${this._typeInfo.name}.${root}.${suffix}.`); + console.error( + `Traversing into CQL system types isn't supported: ${this._typeInfo.name}.${root}.${suffix}.` + ); return; } return toSystemObject(this._json[property], typeSpecifier.name); @@ -234,7 +240,12 @@ class FHIRObject { // Required by cql-execution API (but not currently used in FHIR data model) getDateOrInterval(field) { let dateOrIvl = this.get(field); - if (!(dateOrIvl instanceof cql.DateTime) && !(dateOrIvl instanceof cql.Interval) && dateOrIvl && dateOrIvl.value) { + if ( + !(dateOrIvl instanceof cql.DateTime) && + !(dateOrIvl instanceof cql.Interval) && + dateOrIvl && + dateOrIvl.value + ) { dateOrIvl = dateOrIvl.value; } return dateOrIvl; @@ -242,7 +253,7 @@ class FHIRObject { _is(typeSpecifier) { return this._typeHierarchy().some( - (t) => t.type === typeSpecifier.type && t.name == typeSpecifier.name + t => t.type === typeSpecifier.type && t.name == typeSpecifier.name ); } @@ -269,16 +280,20 @@ class FHIRObject { } // TODO: This currently doesn't include System types in the hierarchy. We should fix this in the parentClasses // function, but until then, we know that everything eventually inherits from System.Any, so force that here: - typeHierarchy.push({name: '{urn:hl7-org:elm-types:r1}Any', type: 'NamedTypeSpecifier' }); + typeHierarchy.push({ name: '{urn:hl7-org:elm-types:r1}Any', type: 'NamedTypeSpecifier' }); return typeHierarchy; } - getTypeInfo() { return this._typeInfo; } + getTypeInfo() { + return this._typeInfo; + } } class Patient extends FHIRObject { constructor(bundle, modelInfo) { - const patientClass = modelInfo.patientClassIdentifier ? modelInfo.patientClassIdentifier : modelInfo.patientClassName; + const patientClass = modelInfo.patientClassIdentifier + ? modelInfo.patientClassIdentifier + : modelInfo.patientClassName; const resourceType = modelInfo.patientClassName.replace(/^FHIR\./, ''); const ptEntry = bundle.entry.find(e => e.resource && e.resource.resourceType == resourceType); const ptClass = modelInfo.findClass(patientClass); @@ -301,11 +316,13 @@ class Patient extends FHIRObject { return []; } const resourceType = classInfo.name.replace(/^FHIR\./, ''); - const records = this._bundle.entry.filter( e => { - return e.resource && e.resource.resourceType == resourceType; - }).map( e => { - return new FHIRObject(e.resource, classInfo, this._modelInfo); - }); + const records = this._bundle.entry + .filter(e => { + return e.resource && e.resource.resourceType == resourceType; + }) + .map(e => { + return new FHIRObject(e.resource, classInfo, this._modelInfo); + }); return records; } } @@ -330,8 +347,8 @@ function getPropertyFromJSON(json, property, typeSpecifier, modelInfo) { // Special handling for FHIR ids and extensions on primitives. if (isFHIRPrimitiveOrListOfFHIRPrimitives(typeSpecifier, modelInfo)) { // Normalize (or copy) to arrays to better share code between lists and non-lists - const valueArr = (typeSpecifier.isList && Array.isArray(value)) ? [...value] : [value]; - const extraArr = (typeSpecifier.isList && Array.isArray(extra)) ? [...extra] : [extra]; + const valueArr = typeSpecifier.isList && Array.isArray(value) ? [...value] : [value]; + const extraArr = typeSpecifier.isList && Array.isArray(extra) ? [...extra] : [extra]; // Make sure arrays are of same length for easier processing while (valueArr.length > extraArr.length) { extraArr.push(undefined); @@ -341,7 +358,7 @@ function getPropertyFromJSON(json, property, typeSpecifier, modelInfo) { } const data = []; - for (let i=0; i < valueArr.length; i++) { + for (let i = 0; i < valueArr.length; i++) { let item = {}; if (typeof valueArr[i] !== 'undefined') { item.value = valueArr[i]; @@ -364,13 +381,21 @@ function getPropertyFromJSON(json, property, typeSpecifier, modelInfo) { function isFHIRPrimitiveOrListOfFHIRPrimitives(typeSpecifier, modelInfo) { if (typeSpecifier.isNamed) { // If its namespace is FHIR and its name starts w/ a lowercase letter, it's a FHIR primitive - if (typeSpecifier.namespace === 'FHIR' && typeSpecifier.name[0].toLowerCase() === typeSpecifier.name[0]) { + if ( + typeSpecifier.namespace === 'FHIR' && + typeSpecifier.name[0].toLowerCase() === typeSpecifier.name[0] + ) { return true; } // The FHIR modelinfo represents code elements as a unique class type with a single string 'value'. // e.g., Goal's 'status' element has type 'GoalStatus', which just has a string value element. const typeInfo = modelInfo.findClass(typeSpecifier.fqn); - if (typeInfo && typeInfo.baseTypeSpecifier && typeInfo.baseTypeSpecifier.fqn === 'FHIR.Element' && typeInfo.elements.length === 1) { + if ( + typeInfo && + typeInfo.baseTypeSpecifier && + typeInfo.baseTypeSpecifier.fqn === 'FHIR.Element' && + typeInfo.elements.length === 1 + ) { const property = typeInfo.findElement('value'); return property && property.typeSpecifier.fqn === 'System.String'; } @@ -394,30 +419,30 @@ function toSystemObject(data, name) { } switch (name) { - case 'Boolean': - case 'Decimal': - case 'Integer': - case 'String': - return data; - case 'Code': - case 'Concept': - case 'Quantity': - // Currently, these aren't used as leaf nodes in the FHIR model infos! - return; - case 'DateTime': - // CQL DateTime doesn't support 'Z' right now, so account for that. - return cql.DateTime.parse(data.replace('Z', '+00:00')); - case 'Date': - // cql-execution v1.3.2 currently doesn't export the new Date class, so we need to use this workaround - return cql.DateTime.parse(data) != null ? cql.DateTime.parse(data).getDate() : undefined; - case 'Time': { - // CQL DateTime doesn't support 'Z' right now, so account for that. In addition Time should not - // have an offset, so clear the offset as well. - // NOTE: Current CQL execution treats time as a DateTime w/ date fixed to 0000-01-01. - const time = cql.DateTime.parse(`0000-01-01T${data.replace('Z', '+00:00')}`); - time.timezoneOffset = null; - return time; - } + case 'Boolean': + case 'Decimal': + case 'Integer': + case 'String': + return data; + case 'Code': + case 'Concept': + case 'Quantity': + // Currently, these aren't used as leaf nodes in the FHIR model infos! + return; + case 'DateTime': + // CQL DateTime doesn't support 'Z' right now, so account for that. + return cql.DateTime.parse(data.replace('Z', '+00:00')); + case 'Date': + // cql-execution v1.3.2 currently doesn't export the new Date class, so we need to use this workaround + return cql.DateTime.parse(data) != null ? cql.DateTime.parse(data).getDate() : undefined; + case 'Time': { + // CQL DateTime doesn't support 'Z' right now, so account for that. In addition Time should not + // have an offset, so clear the offset as well. + // NOTE: Current CQL execution treats time as a DateTime w/ date fixed to 0000-01-01. + const time = cql.DateTime.parse(`0000-01-01T${data.replace('Z', '+00:00')}`); + time.timezoneOffset = null; + return time; + } } } @@ -453,7 +478,8 @@ function toFHIRObject(data, typeSpecifier, modelInfo, suffix) { toFHIRObject(data.low, typeSpecifier.pointType, modelInfo), toFHIRObject(data.high, typeSpecifier.pointType, modelInfo), data.lowClosed, - data.highClosed); + data.highClosed + ); } return; } @@ -483,7 +509,12 @@ function toCode(f) { return codings.length === 1 ? codings[0] : codings; } } else if (typeName === 'Coding') { - return new cql.Code(f.code ? f.code.value : f.code, f.system ? f.system.value : f.system, f.version ? f.version.value : f.version, f.display ? f.display.value : f.display); + return new cql.Code( + f.code ? f.code.value : f.code, + f.system ? f.system.value : f.system, + f.version ? f.version.value : f.version, + f.display ? f.display.value : f.display + ); } else if (typeName === 'code') { return f.value; } diff --git a/src/index.js b/src/index.js index 6dcb03b..1cedafa 100644 --- a/src/index.js +++ b/src/index.js @@ -1 +1 @@ -module.exports = require('./fhir'); \ No newline at end of file +module.exports = require('./fhir'); diff --git a/src/load.js b/src/load.js index 9c53186..ea05d8a 100644 --- a/src/load.js +++ b/src/load.js @@ -49,7 +49,10 @@ class ModelInfo { this._classesByName = new Map(); for (const t of xml.typeInfo) { - if (t.$ != null && (stripNS(t.$.type) === 'ClassInfo' || stripNS(t.$.type) === 'ProfileInfo')) { + if ( + t.$ != null && + (stripNS(t.$.type) === 'ClassInfo' || stripNS(t.$.type) === 'ProfileInfo') + ) { const classInfo = new ClassInfo(t, this); if (classInfo.label != null) { this._classesByLabel.set(classInfo.label, classInfo); @@ -64,16 +67,36 @@ class ModelInfo { } } - get name() { return this._name; } - get version() { return this._version; } - get url() { return this._url; } - get schemaLocation() { return this._schemaLocation; } - get targetQualifier() { return this._targetQualifier; } - get patientClassName() { return this._patientClassName; } - get patientClassIdentifier() { return this._patientClassIdentifier; } - get patientBirthDatePropertyName() { return this._patientBirthDatePropertyName; } - get caseSensitive() { return this._caseSensitive; } - get strictRetrieveTyping() { return this._strictRetrieveTyping; } + get name() { + return this._name; + } + get version() { + return this._version; + } + get url() { + return this._url; + } + get schemaLocation() { + return this._schemaLocation; + } + get targetQualifier() { + return this._targetQualifier; + } + get patientClassName() { + return this._patientClassName; + } + get patientClassIdentifier() { + return this._patientClassIdentifier; + } + get patientBirthDatePropertyName() { + return this._patientBirthDatePropertyName; + } + get caseSensitive() { + return this._caseSensitive; + } + get strictRetrieveTyping() { + return this._strictRetrieveTyping; + } findClass(klass) { // First check label, then identifier, then name @@ -93,7 +116,9 @@ class ModelInfo { // Last ditch effort by name: if it starts with the model prefix (e.g., FHIR.Patient) then remove it; OR if it // doesn't start with the model prefix (e.g. Patient), add it. - const modKlassName = klassName.startsWith(`${this.name}.`) ? klassName.slice(this.name.length+1) : `${this.name}.${klassName}`; + const modKlassName = klassName.startsWith(`${this.name}.`) + ? klassName.slice(this.name.length + 1) + : `${this.name}.${klassName}`; return this._classesByName.get(modKlassName); } } @@ -118,15 +143,33 @@ class ClassInfo { this._parentClasses = null; //lazy loaded } - get namespace() { return this._namespace; } - get name() { return this._name; } - get identifier() { return this._identifier; } - get label() { return this._label; } - get isRetrievable() { return this._isRetrievable; } - get primaryCodePath() { return this._primaryCodePath; } - get baseTypeSpecifier() { return this._baseTypeSpecifier; } - get modelInfo() { return this._modelInfo; } - get elements() { return Array.from(this._elementsByName.values()); } + get namespace() { + return this._namespace; + } + get name() { + return this._name; + } + get identifier() { + return this._identifier; + } + get label() { + return this._label; + } + get isRetrievable() { + return this._isRetrievable; + } + get primaryCodePath() { + return this._primaryCodePath; + } + get baseTypeSpecifier() { + return this._baseTypeSpecifier; + } + get modelInfo() { + return this._modelInfo; + } + get elements() { + return Array.from(this._elementsByName.values()); + } // @return NamedTypeSpecifier parentClasses() { @@ -154,20 +197,27 @@ class ClassInfo { findElement(el, allowExplicitChoice = false) { let element = this._elementsByName.get(el); // TODO: Should we add support for when the base type is a System type? - if (element == null && this.baseTypeSpecifier != null && this.baseTypeSpecifier.namespace !== 'System') { + if ( + element == null && + this.baseTypeSpecifier != null && + this.baseTypeSpecifier.namespace !== 'System' + ) { element = this._modelInfo.findClass(this.baseTypeSpecifier.fqn).findElement(el); } if (element == null && allowExplicitChoice) { // Now go through the name checking possible combinations of name and type for explicit choices // E.g., medicationCodeableConcept -> medication / CodeableConcept -> medicationCodeable / Concept - for (let i=0; i < el.length; i++) { + for (let i = 0; i < el.length; i++) { if (/^[A-Z]$/.test(el[i])) { const name = el.slice(0, i); const potential = this.findElement(name, false); if (potential != null && potential.typeSpecifier && potential.typeSpecifier.isChoice) { const explicitType = el.slice(i); const typeMatchesChoice = potential.typeSpecifier.choices.find(c => { - return c.name === explicitType || c.name === `${explicitType[0].toLowerCase()}${explicitType.slice(1)}`; + return ( + c.name === explicitType || + c.name === `${explicitType[0].toLowerCase()}${explicitType.slice(1)}` + ); }); if (typeMatchesChoice) { element = potential; @@ -190,9 +240,15 @@ class ClassElement { this._modelInfo = modelInfo; } - get name() { return this._name; } - get typeSpecifier() { return this._typeSpecifier; } - get isProhibited() { return this._isProhibited; } + get name() { + return this._name; + } + get typeSpecifier() { + return this._typeSpecifier; + } + get isProhibited() { + return this._isProhibited; + } } const NAMED_TYPE_NAME = 'NamedTypeSpecifier'; @@ -203,10 +259,18 @@ class NamedTypeSpecifier { this._namespace = namespace; } - get isNamed() { return true; } - get name() { return this._name; } - get namespace() { return this._namespace; } - get fqn() { return this.namespace == null ? this.name : `${this.namespace}.${this.name}`; } + get isNamed() { + return true; + } + get name() { + return this._name; + } + get namespace() { + return this._namespace; + } + get fqn() { + return this.namespace == null ? this.name : `${this.namespace}.${this.name}`; + } } const LIST_TYPE_NAME = 'ListTypeSpecifier'; @@ -216,8 +280,12 @@ class ListTypeSpecifier { this._elementType = elementType; } - get isList() { return true; } - get elementType() { return this._elementType; } + get isList() { + return true; + } + get elementType() { + return this._elementType; + } } const INTERVAL_TYPE_NAME = 'IntervalTypeSpecifier'; @@ -227,8 +295,12 @@ class IntervalTypeSpecifier { this._pointType = pointType; } - get isInterval() { return true; } - get pointType() { return this._pointType; } + get isInterval() { + return true; + } + get pointType() { + return this._pointType; + } } const CHOICE_TYPE_NAME = 'ChoiceTypeSpecifier'; @@ -238,8 +310,12 @@ class ChoiceTypeSpecifier { this._choices = choices; } - get isChoice() { return true; } - get choices() { return this._choices; } + get isChoice() { + return true; + } + get choices() { + return this._choices; + } } function getTypeSpecifierFromXML(xml, ...prefixes) { @@ -247,13 +323,13 @@ function getTypeSpecifierFromXML(xml, ...prefixes) { // loop through prefixes looking for type property (e.g., type, elementType, pointType, etc.) if (xml.$) { - for (let i=0; type == null && i < prefixes.length; i++) { + for (let i = 0; type == null && i < prefixes.length; i++) { type = prefixes[i] === '' ? stripNS(xml.$.type) : stripNS(xml.$[`${prefixes[i]}Type`]); } } // loop through prefixes looking for typeSpecifier property (e.g., typeSpecifier, elementTypeSpecifier, etc.) - for (let i=0; typeSpecifier == null && i < prefixes.length; i++) { + for (let i = 0; typeSpecifier == null && i < prefixes.length; i++) { typeSpecifier = prefixes[i] === '' ? xml.typeSpecifier : xml[`${prefixes[i]}TypeSpecifier`]; } if (typeSpecifier && typeSpecifier.length > 0) { @@ -308,4 +384,4 @@ function stripNS(str) { return str.replace(/.*:/, ''); } -module.exports = load; \ No newline at end of file +module.exports = load; diff --git a/test/dstu2_test.js b/test/dstu2_test.js index a881607..80f2c95 100644 --- a/test/dstu2_test.js +++ b/test/dstu2_test.js @@ -1,6 +1,6 @@ const cql = require('cql-execution'); const cqlfhir = require('../src/index'); -const {expect} = require('chai'); +const { expect } = require('chai'); const conditionResource = require('./fixtures/dstu2/Condition_f201.json'); const patientMyron = require('./fixtures/dstu2/Myron933_Ondricka197_a901d2b4-30a8-41b9-b94a-f44561d8f809.json'); @@ -55,7 +55,11 @@ describe('#FHIRWrapper_DSTU2', () => { }); it('should error if requested type is incompatible', () => { - expect(function(){fhirWrapper.wrap(conditionResource, 'Observation');}).to.throw('Incompatible types: FHIR resourceType is Condition which cannot be cast as Observation'); + expect(function () { + fhirWrapper.wrap(conditionResource, 'Observation'); + }).to.throw( + 'Incompatible types: FHIR resourceType is Condition which cannot be cast as Observation' + ); }); it('should wrap a fhir resource to the type specified if real type unknown', () => { @@ -71,10 +75,7 @@ describe('#DSTU2', () => { }); beforeEach(() => { - patientSource.loadBundles([ - patientMyron, - patientShawnee - ]); + patientSource.loadBundles([patientMyron, patientShawnee]); }); afterEach(() => patientSource.reset()); @@ -108,14 +109,16 @@ describe('#DSTU2', () => { expect(patientSource.currentPatient()).to.not.equal(patientSource.currentPatient()); }); - it('should find patient birthDate', () =>{ + it('should find patient birthDate', () => { const pt = patientSource.currentPatient(); // cql-execution v1.3.2 currently doesn't export the new Date class, so we need to use the .getDate() workaround - expect(compact(pt.get('birthDate'))).to.deep.equal({ value: new cql.DateTime.parse('1975-02-25').getDate() }); + expect(compact(pt.get('birthDate'))).to.deep.equal({ + value: new cql.DateTime.parse('1975-02-25').getDate() + }); expect(pt.get('birthDate.value')).to.deep.equal(new cql.DateTime.parse('1975-02-25').getDate()); }); - it('should find patient extensions', () =>{ + it('should find patient extensions', () => { const pt = patientSource.currentPatient(); const extensions = pt.get('extension'); expect(extensions).to.have.length(7); @@ -139,7 +142,7 @@ describe('#DSTU2', () => { }); }); - it('should find records by type name (e.g., Condition)', () =>{ + it('should find records by type name (e.g., Condition)', () => { const pt = patientSource.currentPatient(); const conditions = pt.findRecords('Condition'); expect(conditions).to.have.length(9); @@ -148,7 +151,7 @@ describe('#DSTU2', () => { expect(paymentReconciliations).to.be.empty; }); - it('should find records by model name and type name (e.g., FHIR.Condition)', () =>{ + it('should find records by model name and type name (e.g., FHIR.Condition)', () => { const pt = patientSource.currentPatient(); const conditions = pt.findRecords('FHIR.Condition'); expect(conditions).to.have.length(9); @@ -157,7 +160,7 @@ describe('#DSTU2', () => { expect(paymentReconciliations).to.be.empty; }); - it('should find records by model URL and type name (e.g., {http://hl7.org/fhir}Condition)', () =>{ + it('should find records by model URL and type name (e.g., {http://hl7.org/fhir}Condition)', () => { const pt = patientSource.currentPatient(); const conditions = pt.findRecords('{http://hl7.org/fhir}Condition'); expect(conditions).to.have.length(9); @@ -166,7 +169,7 @@ describe('#DSTU2', () => { expect(paymentReconciliations).to.be.empty; }); - it('should find a single record', () =>{ + it('should find a single record', () => { const pt = patientSource.currentPatient(); const condition = pt.findRecord('Condition'); expect(condition.getTypeInfo().name).to.equal('FHIR.Condition'); @@ -177,19 +180,27 @@ describe('#DSTU2', () => { it('should support getId', () => { const pt = patientSource.currentPatient(); - const procedure = pt.findRecords('Procedure').find(p => p.getId() === '9d6f2ff7-f7f2-4dbb-a271-8a8c3c501e18'); + const procedure = pt + .findRecords('Procedure') + .find(p => p.getId() === '9d6f2ff7-f7f2-4dbb-a271-8a8c3c501e18'); expect(procedure.getId()).to.equal('9d6f2ff7-f7f2-4dbb-a271-8a8c3c501e18'); }); it('should support getCode', () => { const pt = patientSource.currentPatient(); - const procedure = pt.findRecords('Procedure').find(p => p.getId() === '9d6f2ff7-f7f2-4dbb-a271-8a8c3c501e18'); - expect(procedure.getCode('code')).to.deep.equal(new cql.Code('117015009', 'http://snomed.info/sct', undefined, 'Throat culture (procedure) ')); + const procedure = pt + .findRecords('Procedure') + .find(p => p.getId() === '9d6f2ff7-f7f2-4dbb-a271-8a8c3c501e18'); + expect(procedure.getCode('code')).to.deep.equal( + new cql.Code('117015009', 'http://snomed.info/sct', undefined, 'Throat culture (procedure) ') + ); }); it('should support getDate (DateTime)', () => { const pt = patientSource.currentPatient(); - const condition = pt.findRecords('Encounter').find(p => p.getId() === 'df950a26-42f3-4db1-93b5-50ba3cec264e'); + const condition = pt + .findRecords('Encounter') + .find(p => p.getId() === 'df950a26-42f3-4db1-93b5-50ba3cec264e'); const periodStart = condition.getDate('period.start.value'); expect(periodStart.isDateTime).to.be.true; expect(periodStart).to.deep.equal(cql.DateTime.parse('1994-07-19T09:18:56-04:00')); @@ -197,7 +208,9 @@ describe('#DSTU2', () => { it('should support getDate (Date)', () => { const pt = patientSource.currentPatient(); - const condition = pt.findRecords('Condition').find(p => p.getId() === '3c57b73b-6f28-45e7-9729-b681a1ec4156'); + const condition = pt + .findRecords('Condition') + .find(p => p.getId() === '3c57b73b-6f28-45e7-9729-b681a1ec4156'); const dateRecorded = condition.getDate('dateRecorded.value'); expect(dateRecorded.isDate).to.be.true; expect(dateRecorded).to.deep.equal(cql.DateTime.parse('1994-07-19').getDate()); @@ -205,7 +218,9 @@ describe('#DSTU2', () => { it('should support getDateOrInterval (DateTime)', () => { const pt = patientSource.currentPatient(); - const condition = pt.findRecords('Encounter').find(p => p.getId() === 'df950a26-42f3-4db1-93b5-50ba3cec264e'); + const condition = pt + .findRecords('Encounter') + .find(p => p.getId() === 'df950a26-42f3-4db1-93b5-50ba3cec264e'); const periodStart = condition.getDateOrInterval('period.start.value'); expect(periodStart.isDateTime).to.be.true; expect(periodStart).to.deep.equal(cql.DateTime.parse('1994-07-19T09:18:56-04:00')); @@ -213,7 +228,9 @@ describe('#DSTU2', () => { it('should support getDateOrInterval (Date)', () => { const pt = patientSource.currentPatient(); - const condition = pt.findRecords('Condition').find(p => p.getId() === '3c57b73b-6f28-45e7-9729-b681a1ec4156'); + const condition = pt + .findRecords('Condition') + .find(p => p.getId() === '3c57b73b-6f28-45e7-9729-b681a1ec4156'); const dateRecorded = condition.getDateOrInterval('dateRecorded.value'); expect(dateRecorded.isDate).to.be.true; expect(dateRecorded).to.deep.equal(cql.DateTime.parse('1994-07-19').getDate()); @@ -221,37 +238,55 @@ describe('#DSTU2', () => { it('should support dot-separated-paths', () => { const pt = patientSource.currentPatient(); - const procedure = pt.findRecords('Procedure').find(p => p.getId() === '9d6f2ff7-f7f2-4dbb-a271-8a8c3c501e18'); - expect(procedure.get('performedPeriod.start.value')).to.deep.equal(cql.DateTime.parse('2009-03-01T08:18:56-05:00')); + const procedure = pt + .findRecords('Procedure') + .find(p => p.getId() === '9d6f2ff7-f7f2-4dbb-a271-8a8c3c501e18'); + expect(procedure.get('performedPeriod.start.value')).to.deep.equal( + cql.DateTime.parse('2009-03-01T08:18:56-05:00') + ); }); it('should support getting booleans', () => { const pt = patientSource.currentPatient(); - const immunization = pt.findRecords('Immunization').find(p => p.getId() === '52bc722f-e5a3-4be2-bdf3-1001d9078165'); + const immunization = pt + .findRecords('Immunization') + .find(p => p.getId() === '52bc722f-e5a3-4be2-bdf3-1001d9078165'); expect(immunization.get('wasNotGiven.value')).to.be.false; }); it('should support getting decimals', () => { const pt = patientSource.currentPatient(); - const daly = pt.get('extension').find(e => e.url && e.url.value === 'http://synthetichealth.github.io/synthea/disability-adjusted-life-years'); + const daly = pt + .get('extension') + .find( + e => + e.url && + e.url.value === 'http://synthetichealth.github.io/synthea/disability-adjusted-life-years' + ); expect(daly.get('valueDecimal.value')).to.deep.equal(1.6468635978517043); }); it('should support getting integers', () => { const pt = patientSource.currentPatient(); - const claim = pt.findRecords('Claim').find(p => p.getId() === '391f6bb9-0a79-439b-b84f-3a3a7364c8db'); + const claim = pt + .findRecords('Claim') + .find(p => p.getId() === '391f6bb9-0a79-439b-b84f-3a3a7364c8db'); expect(claim.get('diagnosis')[0].get('sequence.value')).to.equal(1); }); it('should support getting strings', () => { const pt = patientSource.currentPatient(); - const procedure = pt.findRecords('Procedure').find(p => p.getId() === '9d6f2ff7-f7f2-4dbb-a271-8a8c3c501e18'); + const procedure = pt + .findRecords('Procedure') + .find(p => p.getId() === '9d6f2ff7-f7f2-4dbb-a271-8a8c3c501e18'); expect(procedure.get('status.value')).to.deep.equal('completed'); }); it('should support getting dateTimes', () => { const pt = patientSource.currentPatient(); - const condition = pt.findRecords('Encounter').find(p => p.getId() === 'df950a26-42f3-4db1-93b5-50ba3cec264e'); + const condition = pt + .findRecords('Encounter') + .find(p => p.getId() === 'df950a26-42f3-4db1-93b5-50ba3cec264e'); const periodStart = condition.get('period.start.value'); expect(periodStart.isDateTime).to.be.true; expect(periodStart).to.deep.equal(cql.DateTime.parse('1994-07-19T09:18:56-04:00')); @@ -259,7 +294,9 @@ describe('#DSTU2', () => { it('should support getting dates', () => { const pt = patientSource.currentPatient(); - const condition = pt.findRecords('Condition').find(p => p.getId() === '3c57b73b-6f28-45e7-9729-b681a1ec4156'); + const condition = pt + .findRecords('Condition') + .find(p => p.getId() === '3c57b73b-6f28-45e7-9729-b681a1ec4156'); const dateRecorded = condition.get('dateRecorded.value'); expect(dateRecorded.isDate).to.be.true; expect(dateRecorded).to.deep.equal(cql.DateTime.parse('1994-07-19').getDate()); @@ -267,7 +304,9 @@ describe('#DSTU2', () => { it('should support getting times', () => { const pt = patientSource.currentPatient(); - const observation = pt.findRecords('Observation').find(p => p.getId() === '9c15c801-6bb5-47a7-a9db-8bad0cb6aa68'); + const observation = pt + .findRecords('Observation') + .find(p => p.getId() === '9c15c801-6bb5-47a7-a9db-8bad0cb6aa68'); const valueTime = observation.get('valueTime.value'); expect(valueTime.isTime()).to.be.true; expect(valueTime).to.deep.equal(cql.DateTime.parse('0000-01-01T18:23:47.376-05:00').getTime()); @@ -275,25 +314,33 @@ describe('#DSTU2', () => { it('should support getting an option of a choice', () => { const pt = patientSource.currentPatient(); - const condition = pt.findRecords('Condition').find(p => p.getId() === '3c57b73b-6f28-45e7-9729-b681a1ec4156'); + const condition = pt + .findRecords('Condition') + .find(p => p.getId() === '3c57b73b-6f28-45e7-9729-b681a1ec4156'); // In DSTU2, you just fully spell out the choice (e.g., onset[x] datetime is retrieved as onsetDateTime) - expect(condition.get('onsetDateTime.value')).to.deep.equal(cql.DateTime.parse('1994-07-19T09:18:56-04:00')); + expect(condition.get('onsetDateTime.value')).to.deep.equal( + cql.DateTime.parse('1994-07-19T09:18:56-04:00') + ); }); it('should support id and extension on primitives', () => { const pt = patientSource.currentPatient(); - const goal = pt.findRecords('Goal').find(p => p.getId() === 'ba9ddc45-9800-4537-97db-df84891597e9'); + const goal = pt + .findRecords('Goal') + .find(p => p.getId() === 'ba9ddc45-9800-4537-97db-df84891597e9'); expect(goal.get('status.id')).to.equal('12345'); - expect(compact(goal.get('status.extension'))).to.deep.equal([ { - url: { value: 'http://example.org/fhir/StructureDefinition/originalText' }, - valueCode: { value: 'progressing' } - }]); + expect(compact(goal.get('status.extension'))).to.deep.equal([ + { + url: { value: 'http://example.org/fhir/StructureDefinition/originalText' }, + valueCode: { value: 'progressing' } + } + ]); }); it('should support id on list of primitives', () => { const pt = patientSource.currentPatient(); expect(compact(pt.get('address')[0].get('line'))).to.deep.equal([ - { value: '172 O\'Keefe Station' }, + { value: "172 O'Keefe Station" }, { id: '2468', value: 'Floor 5' }, { value: 'Apt. C' } ]); @@ -306,20 +353,29 @@ describe('#DSTU2', () => { { name: '{http://hl7.org/fhir}Condition', type: 'NamedTypeSpecifier' }, { name: '{http://hl7.org/fhir}DomainResource', type: 'NamedTypeSpecifier' }, { name: '{http://hl7.org/fhir}Resource', type: 'NamedTypeSpecifier' }, - { name: '{urn:hl7-org:elm-types:r1}Any', type: 'NamedTypeSpecifier' }, + { name: '{urn:hl7-org:elm-types:r1}Any', type: 'NamedTypeSpecifier' } ]); }); it('should support _is', () => { const pt = patientSource.currentPatient(); const condition = pt.findRecord('Condition'); - expect(condition._is({name: '{http://hl7.org/fhir}Condition', type: 'NamedTypeSpecifier'})).to.be.true; - expect(condition._is({name: '{http://hl7.org/fhir}DomainResource', type: 'NamedTypeSpecifier'})).to.be.true; - expect(condition._is({name: '{http://hl7.org/fhir}Resource', type: 'NamedTypeSpecifier'})).to.be.true; - expect(condition._is({name: '{urn:hl7-org:elm-types:r1}Any', type: 'NamedTypeSpecifier'})).to.be.true; - expect(condition._is({name: '{http://some.other.model.org}Condition', type: 'NamedTypeSpecifier'})).to.be.false; - expect(condition._is({name: '{http://hl7.org/fhir}Observation', type: 'NamedTypeSpecifier'})).to.be.false; - expect(condition._is({name: '{http://hl7.org/fhir}Condition', type: 'IntervalTypeSpecifier'})).to.be.false; + expect(condition._is({ name: '{http://hl7.org/fhir}Condition', type: 'NamedTypeSpecifier' })).to + .be.true; + expect( + condition._is({ name: '{http://hl7.org/fhir}DomainResource', type: 'NamedTypeSpecifier' }) + ).to.be.true; + expect(condition._is({ name: '{http://hl7.org/fhir}Resource', type: 'NamedTypeSpecifier' })).to + .be.true; + expect(condition._is({ name: '{urn:hl7-org:elm-types:r1}Any', type: 'NamedTypeSpecifier' })).to + .be.true; + expect( + condition._is({ name: '{http://some.other.model.org}Condition', type: 'NamedTypeSpecifier' }) + ).to.be.false; + expect(condition._is({ name: '{http://hl7.org/fhir}Observation', type: 'NamedTypeSpecifier' })) + .to.be.false; + expect(condition._is({ name: '{http://hl7.org/fhir}Condition', type: 'IntervalTypeSpecifier' })) + .to.be.false; }); }); @@ -335,7 +391,6 @@ function compact(obj) { if (value !== undefined) { compacted[prop] = compact(value); } - } return compacted; } diff --git a/test/r401_test.js b/test/r401_test.js index 7a27448..a7742de 100644 --- a/test/r401_test.js +++ b/test/r401_test.js @@ -1,6 +1,6 @@ const cql = require('cql-execution'); const cqlfhir = require('../src/index'); -const {expect} = require('chai'); +const { expect } = require('chai'); const conditionResource = require('./fixtures/r4/Condition_f201.json'); const patientLuna = require('./fixtures/r4/Luna60_McCullough561_6662f0ca-b617-4e02-8f55-7275e9f49aa0.json'); @@ -55,7 +55,11 @@ describe('#FHIRWrapper_R4 v4.0.1', () => { }); it('should error if requested type is incompatible', () => { - expect(function(){fhirWrapper.wrap(conditionResource, 'Observation');}).to.throw('Incompatible types: FHIR resourceType is Condition which cannot be cast as Observation'); + expect(function () { + fhirWrapper.wrap(conditionResource, 'Observation'); + }).to.throw( + 'Incompatible types: FHIR resourceType is Condition which cannot be cast as Observation' + ); }); it('should wrap a fhir resource to the type specified if real type unknown', () => { @@ -71,10 +75,7 @@ describe('#R4 v4.0.1', () => { }); beforeEach(() => { - patientSource.loadBundles([ - patientLuna, - patientJohnnie - ]); + patientSource.loadBundles([patientLuna, patientJohnnie]); }); afterEach(() => patientSource.reset()); @@ -108,14 +109,16 @@ describe('#R4 v4.0.1', () => { expect(patientSource.currentPatient()).to.not.equal(patientSource.currentPatient()); }); - it('should find patient birthDate', () =>{ + it('should find patient birthDate', () => { const pt = patientSource.currentPatient(); // cql-execution v1.3.2 currently doesn't export the new Date class, so we need to use the .getDate() workaround - expect(compact(pt.get('birthDate'))).to.deep.equal({ value: new cql.DateTime.parse('2008-11-06').getDate() }); + expect(compact(pt.get('birthDate'))).to.deep.equal({ + value: new cql.DateTime.parse('2008-11-06').getDate() + }); expect(pt.get('birthDate.value')).to.deep.equal(new cql.DateTime.parse('2008-11-06').getDate()); }); - it('should find patient extensions', () =>{ + it('should find patient extensions', () => { const pt = patientSource.currentPatient(); const extensions = pt.get('extension'); expect(extensions).to.have.length(7); @@ -143,7 +146,7 @@ describe('#R4 v4.0.1', () => { }); }); - it('should find records by type name (e.g., Condition)', () =>{ + it('should find records by type name (e.g., Condition)', () => { const pt = patientSource.currentPatient(); const conditions = pt.findRecords('Condition'); expect(conditions).to.have.length(8); @@ -152,7 +155,7 @@ describe('#R4 v4.0.1', () => { expect(paymentReconciliations).to.be.empty; }); - it('should find records by model name and type name (e.g., FHIR.Condition)', () =>{ + it('should find records by model name and type name (e.g., FHIR.Condition)', () => { const pt = patientSource.currentPatient(); const conditions = pt.findRecords('FHIR.Condition'); expect(conditions).to.have.length(8); @@ -161,7 +164,7 @@ describe('#R4 v4.0.1', () => { expect(paymentReconciliations).to.be.empty; }); - it('should find records by model URL and type name (e.g., {http://hl7.org/fhir}Condition)', () =>{ + it('should find records by model URL and type name (e.g., {http://hl7.org/fhir}Condition)', () => { const pt = patientSource.currentPatient(); const conditions = pt.findRecords('{http://hl7.org/fhir}Condition'); expect(conditions).to.have.length(8); @@ -170,7 +173,7 @@ describe('#R4 v4.0.1', () => { expect(paymentReconciliations).to.be.empty; }); - it('should find a single record', () =>{ + it('should find a single record', () => { const pt = patientSource.currentPatient(); const condition = pt.findRecord('Condition'); expect(condition.getTypeInfo().name).to.equal('Condition'); @@ -181,19 +184,32 @@ describe('#R4 v4.0.1', () => { it('should support getId', () => { const pt = patientSource.currentPatient(); - const procedure = pt.findRecords('Procedure').find(p => p.getId() === '03c48dbb-2e69-451d-877a-b3397a9f3d26'); + const procedure = pt + .findRecords('Procedure') + .find(p => p.getId() === '03c48dbb-2e69-451d-877a-b3397a9f3d26'); expect(procedure.getId()).to.equal('03c48dbb-2e69-451d-877a-b3397a9f3d26'); }); it('should support getCode', () => { const pt = patientSource.currentPatient(); - const procedure = pt.findRecords('Procedure').find(p => p.getId() === '03c48dbb-2e69-451d-877a-b3397a9f3d26'); - expect(procedure.getCode('code')).to.deep.equal(new cql.Code('428191000124101', 'http://snomed.info/sct', undefined, 'Documentation of current medications')); + const procedure = pt + .findRecords('Procedure') + .find(p => p.getId() === '03c48dbb-2e69-451d-877a-b3397a9f3d26'); + expect(procedure.getCode('code')).to.deep.equal( + new cql.Code( + '428191000124101', + 'http://snomed.info/sct', + undefined, + 'Documentation of current medications' + ) + ); }); it('should support getDate (DateTime)', () => { const pt = patientSource.currentPatient(); - const medReq = pt.findRecords('MedicationRequest').find(p => p.getId() === '622c5788-3028-41fd-a8cb-164f868d4322'); + const medReq = pt + .findRecords('MedicationRequest') + .find(p => p.getId() === '622c5788-3028-41fd-a8cb-164f868d4322'); const authoredOn = medReq.getDate('authoredOn.value'); expect(authoredOn.isDateTime).to.be.true; expect(authoredOn).to.deep.equal(cql.DateTime.parse('2009-12-01T11:18:29-05:00')); @@ -208,7 +224,9 @@ describe('#R4 v4.0.1', () => { it('should support getDateOrInterval (DateTime)', () => { const pt = patientSource.currentPatient(); - const medReq = pt.findRecords('MedicationRequest').find(p => p.getId() === '622c5788-3028-41fd-a8cb-164f868d4322'); + const medReq = pt + .findRecords('MedicationRequest') + .find(p => p.getId() === '622c5788-3028-41fd-a8cb-164f868d4322'); const authoredOn = medReq.getDateOrInterval('authoredOn.value'); expect(authoredOn.isDateTime).to.be.true; expect(authoredOn).to.deep.equal(cql.DateTime.parse('2009-12-01T11:18:29-05:00')); @@ -223,37 +241,51 @@ describe('#R4 v4.0.1', () => { it('should support dot-separated-paths', () => { const pt = patientSource.currentPatient(); - const procedure = pt.findRecords('Procedure').find(p => p.getId() === '03c48dbb-2e69-451d-877a-b3397a9f3d26'); - expect(procedure.get('subject.reference.value')).to.deep.equal('urn:uuid:356a0ab8-5592-4ec5-8c3a-9a4d0857b793'); + const procedure = pt + .findRecords('Procedure') + .find(p => p.getId() === '03c48dbb-2e69-451d-877a-b3397a9f3d26'); + expect(procedure.get('subject.reference.value')).to.deep.equal( + 'urn:uuid:356a0ab8-5592-4ec5-8c3a-9a4d0857b793' + ); }); it('should support getting booleans', () => { const pt = patientSource.currentPatient(); - const immunization = pt.findRecords('Immunization').find(p => p.getId() === '2a986005-b7c9-464c-9da8-0a3220dd8721'); + const immunization = pt + .findRecords('Immunization') + .find(p => p.getId() === '2a986005-b7c9-464c-9da8-0a3220dd8721'); expect(immunization.get('primarySource.value')).to.be.true; }); it('should support getting decimals', () => { const pt = patientSource.currentPatient(); - const claim = pt.findRecords('Claim').find(p => p.getId() === '58cd648a-5f4d-4306-bef4-49ec64c88c63'); + const claim = pt + .findRecords('Claim') + .find(p => p.getId() === '58cd648a-5f4d-4306-bef4-49ec64c88c63'); expect(claim.get('total.value.value')).to.equal(687.08); }); it('should support getting integers', () => { const pt = patientSource.currentPatient(); - const claim = pt.findRecords('Claim').find(p => p.getId() === '58cd648a-5f4d-4306-bef4-49ec64c88c63'); + const claim = pt + .findRecords('Claim') + .find(p => p.getId() === '58cd648a-5f4d-4306-bef4-49ec64c88c63'); expect(claim.get('item')[0].get('sequence.value')).to.equal(1); }); it('should support getting strings', () => { const pt = patientSource.currentPatient(); - const procedure = pt.findRecords('Procedure').find(p => p.getId() === '03c48dbb-2e69-451d-877a-b3397a9f3d26'); + const procedure = pt + .findRecords('Procedure') + .find(p => p.getId() === '03c48dbb-2e69-451d-877a-b3397a9f3d26'); expect(procedure.get('status.value')).to.deep.equal('completed'); }); it('should support getting dateTimes', () => { const pt = patientSource.currentPatient(); - const medReq = pt.findRecords('MedicationRequest').find(p => p.getId() === '622c5788-3028-41fd-a8cb-164f868d4322'); + const medReq = pt + .findRecords('MedicationRequest') + .find(p => p.getId() === '622c5788-3028-41fd-a8cb-164f868d4322'); const authoredOn = medReq.getDate('authoredOn.value'); expect(authoredOn.isDateTime).to.be.true; expect(authoredOn).to.deep.equal(cql.DateTime.parse('2009-12-01T11:18:29-05:00')); @@ -268,7 +300,9 @@ describe('#R4 v4.0.1', () => { it('should support getting times', () => { const pt = patientSource.currentPatient(); - const observation = pt.findRecords('Observation').find(p => p.getId() === '9c15c801-6bb5-47a7-a9db-8bad0cb6aa68'); + const observation = pt + .findRecords('Observation') + .find(p => p.getId() === '9c15c801-6bb5-47a7-a9db-8bad0cb6aa68'); const valueTime = observation.get('value.value'); expect(valueTime.isTime()).to.be.true; expect(valueTime).to.deep.equal(cql.DateTime.parse('0000-01-01T18:23:47.376-05:00').getTime()); @@ -276,19 +310,27 @@ describe('#R4 v4.0.1', () => { it('should support getting an option of a choice', () => { const pt = patientSource.currentPatient(); - const condition = pt.findRecords('Condition').find(p => p.getId() === '9934bc4f-58af-4ecf-bb70-b7cc31987fc5'); + const condition = pt + .findRecords('Condition') + .find(p => p.getId() === '9934bc4f-58af-4ecf-bb70-b7cc31987fc5'); // In R4, you use the stub of the choice (e.g., onset[x] datetime is retrieved as onset) - expect(condition.get('onset.value')).to.deep.equal(cql.DateTime.parse('2009-08-09T12:18:29-04:00')); + expect(condition.get('onset.value')).to.deep.equal( + cql.DateTime.parse('2009-08-09T12:18:29-04:00') + ); }); it('should support id and extension on primitives', () => { const pt = patientSource.currentPatient(); - const encounter = pt.findRecords('Encounter').find(p => p.getId() === '9d911534-10d8-4dc2-91f1-d7aeed828af8'); + const encounter = pt + .findRecords('Encounter') + .find(p => p.getId() === '9d911534-10d8-4dc2-91f1-d7aeed828af8'); expect(encounter.get('status.id')).to.equal('12345'); - expect(compact(encounter.get('status.extension'))).to.deep.equal([ { - url: { value: 'http://example.org/fhir/StructureDefinition/originalText' }, - value: { value: 'completed' } - }]); + expect(compact(encounter.get('status.extension'))).to.deep.equal([ + { + url: { value: 'http://example.org/fhir/StructureDefinition/originalText' }, + value: { value: 'completed' } + } + ]); }); it('should support id on list of primitives', () => { @@ -307,20 +349,29 @@ describe('#R4 v4.0.1', () => { { name: '{http://hl7.org/fhir}Condition', type: 'NamedTypeSpecifier' }, { name: '{http://hl7.org/fhir}DomainResource', type: 'NamedTypeSpecifier' }, { name: '{http://hl7.org/fhir}Resource', type: 'NamedTypeSpecifier' }, - { name: '{urn:hl7-org:elm-types:r1}Any', type: 'NamedTypeSpecifier' }, + { name: '{urn:hl7-org:elm-types:r1}Any', type: 'NamedTypeSpecifier' } ]); }); it('should support _is', () => { const pt = patientSource.currentPatient(); const condition = pt.findRecord('Condition'); - expect(condition._is({name: '{http://hl7.org/fhir}Condition', type: 'NamedTypeSpecifier'})).to.be.true; - expect(condition._is({name: '{http://hl7.org/fhir}DomainResource', type: 'NamedTypeSpecifier'})).to.be.true; - expect(condition._is({name: '{http://hl7.org/fhir}Resource', type: 'NamedTypeSpecifier'})).to.be.true; - expect(condition._is({name: '{urn:hl7-org:elm-types:r1}Any', type: 'NamedTypeSpecifier'})).to.be.true; - expect(condition._is({name: '{http://some.other.model.org}Condition', type: 'NamedTypeSpecifier'})).to.be.false; - expect(condition._is({name: '{http://hl7.org/fhir}Observation', type: 'NamedTypeSpecifier'})).to.be.false; - expect(condition._is({name: '{http://hl7.org/fhir}Condition', type: 'IntervalTypeSpecifier'})).to.be.false; + expect(condition._is({ name: '{http://hl7.org/fhir}Condition', type: 'NamedTypeSpecifier' })).to + .be.true; + expect( + condition._is({ name: '{http://hl7.org/fhir}DomainResource', type: 'NamedTypeSpecifier' }) + ).to.be.true; + expect(condition._is({ name: '{http://hl7.org/fhir}Resource', type: 'NamedTypeSpecifier' })).to + .be.true; + expect(condition._is({ name: '{urn:hl7-org:elm-types:r1}Any', type: 'NamedTypeSpecifier' })).to + .be.true; + expect( + condition._is({ name: '{http://some.other.model.org}Condition', type: 'NamedTypeSpecifier' }) + ).to.be.false; + expect(condition._is({ name: '{http://hl7.org/fhir}Observation', type: 'NamedTypeSpecifier' })) + .to.be.false; + expect(condition._is({ name: '{http://hl7.org/fhir}Condition', type: 'IntervalTypeSpecifier' })) + .to.be.false; }); }); @@ -336,7 +387,6 @@ function compact(obj) { if (value !== undefined) { compacted[prop] = compact(value); } - } return compacted; } diff --git a/test/r4_test.js b/test/r4_test.js index da63ccd..88c941c 100644 --- a/test/r4_test.js +++ b/test/r4_test.js @@ -1,6 +1,6 @@ const cql = require('cql-execution'); const cqlfhir = require('../src/index'); -const {expect} = require('chai'); +const { expect } = require('chai'); const conditionResource = require('./fixtures/r4/Condition_f201.json'); const patientLuna = require('./fixtures/r4/Luna60_McCullough561_6662f0ca-b617-4e02-8f55-7275e9f49aa0.json'); @@ -55,7 +55,11 @@ describe('#FHIRWrapper_R4 v4.0.0', () => { }); it('should error if requested type is incompatible', () => { - expect(function(){fhirWrapper.wrap(conditionResource, 'Observation');}).to.throw('Incompatible types: FHIR resourceType is Condition which cannot be cast as Observation'); + expect(function () { + fhirWrapper.wrap(conditionResource, 'Observation'); + }).to.throw( + 'Incompatible types: FHIR resourceType is Condition which cannot be cast as Observation' + ); }); it('should wrap a fhir resource to the type specified if real type unknown', () => { @@ -71,10 +75,7 @@ describe('#R4 v4.0.0', () => { }); beforeEach(() => { - patientSource.loadBundles([ - patientLuna, - patientJohnnie - ]); + patientSource.loadBundles([patientLuna, patientJohnnie]); }); afterEach(() => patientSource.reset()); @@ -108,14 +109,16 @@ describe('#R4 v4.0.0', () => { expect(patientSource.currentPatient()).to.not.equal(patientSource.currentPatient()); }); - it('should find patient birthDate', () =>{ + it('should find patient birthDate', () => { const pt = patientSource.currentPatient(); // cql-execution v1.3.2 currently doesn't export the new Date class, so we need to use the .getDate() workaround - expect(compact(pt.get('birthDate'))).to.deep.equal({ value: new cql.DateTime.parse('2008-11-06').getDate() }); + expect(compact(pt.get('birthDate'))).to.deep.equal({ + value: new cql.DateTime.parse('2008-11-06').getDate() + }); expect(pt.get('birthDate.value')).to.deep.equal(new cql.DateTime.parse('2008-11-06').getDate()); }); - it('should find patient extensions', () =>{ + it('should find patient extensions', () => { const pt = patientSource.currentPatient(); const extensions = pt.get('extension'); expect(extensions).to.have.length(7); @@ -143,7 +146,7 @@ describe('#R4 v4.0.0', () => { }); }); - it('should find records by type name (e.g., Condition)', () =>{ + it('should find records by type name (e.g., Condition)', () => { const pt = patientSource.currentPatient(); const conditions = pt.findRecords('Condition'); expect(conditions).to.have.length(8); @@ -152,7 +155,7 @@ describe('#R4 v4.0.0', () => { expect(paymentReconciliations).to.be.empty; }); - it('should find records by model name and type name (e.g., FHIR.Condition)', () =>{ + it('should find records by model name and type name (e.g., FHIR.Condition)', () => { const pt = patientSource.currentPatient(); const conditions = pt.findRecords('FHIR.Condition'); expect(conditions).to.have.length(8); @@ -161,7 +164,7 @@ describe('#R4 v4.0.0', () => { expect(paymentReconciliations).to.be.empty; }); - it('should find records by model URL and type name (e.g., {http://hl7.org/fhir}Condition)', () =>{ + it('should find records by model URL and type name (e.g., {http://hl7.org/fhir}Condition)', () => { const pt = patientSource.currentPatient(); const conditions = pt.findRecords('{http://hl7.org/fhir}Condition'); expect(conditions).to.have.length(8); @@ -170,7 +173,7 @@ describe('#R4 v4.0.0', () => { expect(paymentReconciliations).to.be.empty; }); - it('should find a single record', () =>{ + it('should find a single record', () => { const pt = patientSource.currentPatient(); const condition = pt.findRecord('Condition'); expect(condition.getTypeInfo().name).to.equal('Condition'); @@ -181,19 +184,32 @@ describe('#R4 v4.0.0', () => { it('should support getId', () => { const pt = patientSource.currentPatient(); - const procedure = pt.findRecords('Procedure').find(p => p.getId() === '03c48dbb-2e69-451d-877a-b3397a9f3d26'); + const procedure = pt + .findRecords('Procedure') + .find(p => p.getId() === '03c48dbb-2e69-451d-877a-b3397a9f3d26'); expect(procedure.getId()).to.equal('03c48dbb-2e69-451d-877a-b3397a9f3d26'); }); it('should support getCode', () => { const pt = patientSource.currentPatient(); - const procedure = pt.findRecords('Procedure').find(p => p.getId() === '03c48dbb-2e69-451d-877a-b3397a9f3d26'); - expect(procedure.getCode('code')).to.deep.equal(new cql.Code('428191000124101', 'http://snomed.info/sct', undefined, 'Documentation of current medications')); + const procedure = pt + .findRecords('Procedure') + .find(p => p.getId() === '03c48dbb-2e69-451d-877a-b3397a9f3d26'); + expect(procedure.getCode('code')).to.deep.equal( + new cql.Code( + '428191000124101', + 'http://snomed.info/sct', + undefined, + 'Documentation of current medications' + ) + ); }); it('should support getDate (DateTime)', () => { const pt = patientSource.currentPatient(); - const medReq = pt.findRecords('MedicationRequest').find(p => p.getId() === '622c5788-3028-41fd-a8cb-164f868d4322'); + const medReq = pt + .findRecords('MedicationRequest') + .find(p => p.getId() === '622c5788-3028-41fd-a8cb-164f868d4322'); const authoredOn = medReq.getDate('authoredOn.value'); expect(authoredOn.isDateTime).to.be.true; expect(authoredOn).to.deep.equal(cql.DateTime.parse('2009-12-01T11:18:29-05:00')); @@ -208,7 +224,9 @@ describe('#R4 v4.0.0', () => { it('should support getDateOrInterval (DateTime)', () => { const pt = patientSource.currentPatient(); - const medReq = pt.findRecords('MedicationRequest').find(p => p.getId() === '622c5788-3028-41fd-a8cb-164f868d4322'); + const medReq = pt + .findRecords('MedicationRequest') + .find(p => p.getId() === '622c5788-3028-41fd-a8cb-164f868d4322'); const authoredOn = medReq.getDateOrInterval('authoredOn.value'); expect(authoredOn.isDateTime).to.be.true; expect(authoredOn).to.deep.equal(cql.DateTime.parse('2009-12-01T11:18:29-05:00')); @@ -223,37 +241,51 @@ describe('#R4 v4.0.0', () => { it('should support dot-separated-paths', () => { const pt = patientSource.currentPatient(); - const procedure = pt.findRecords('Procedure').find(p => p.getId() === '03c48dbb-2e69-451d-877a-b3397a9f3d26'); - expect(procedure.get('subject.reference.value')).to.deep.equal('urn:uuid:356a0ab8-5592-4ec5-8c3a-9a4d0857b793'); + const procedure = pt + .findRecords('Procedure') + .find(p => p.getId() === '03c48dbb-2e69-451d-877a-b3397a9f3d26'); + expect(procedure.get('subject.reference.value')).to.deep.equal( + 'urn:uuid:356a0ab8-5592-4ec5-8c3a-9a4d0857b793' + ); }); it('should support getting booleans', () => { const pt = patientSource.currentPatient(); - const immunization = pt.findRecords('Immunization').find(p => p.getId() === '2a986005-b7c9-464c-9da8-0a3220dd8721'); + const immunization = pt + .findRecords('Immunization') + .find(p => p.getId() === '2a986005-b7c9-464c-9da8-0a3220dd8721'); expect(immunization.get('primarySource.value')).to.be.true; }); it('should support getting decimals', () => { const pt = patientSource.currentPatient(); - const claim = pt.findRecords('Claim').find(p => p.getId() === '58cd648a-5f4d-4306-bef4-49ec64c88c63'); + const claim = pt + .findRecords('Claim') + .find(p => p.getId() === '58cd648a-5f4d-4306-bef4-49ec64c88c63'); expect(claim.get('total.value.value')).to.equal(687.08); }); it('should support getting integers', () => { const pt = patientSource.currentPatient(); - const claim = pt.findRecords('Claim').find(p => p.getId() === '58cd648a-5f4d-4306-bef4-49ec64c88c63'); + const claim = pt + .findRecords('Claim') + .find(p => p.getId() === '58cd648a-5f4d-4306-bef4-49ec64c88c63'); expect(claim.get('item')[0].get('sequence.value')).to.equal(1); }); it('should support getting strings', () => { const pt = patientSource.currentPatient(); - const procedure = pt.findRecords('Procedure').find(p => p.getId() === '03c48dbb-2e69-451d-877a-b3397a9f3d26'); + const procedure = pt + .findRecords('Procedure') + .find(p => p.getId() === '03c48dbb-2e69-451d-877a-b3397a9f3d26'); expect(procedure.get('status.value')).to.deep.equal('completed'); }); it('should support getting dateTimes', () => { const pt = patientSource.currentPatient(); - const medReq = pt.findRecords('MedicationRequest').find(p => p.getId() === '622c5788-3028-41fd-a8cb-164f868d4322'); + const medReq = pt + .findRecords('MedicationRequest') + .find(p => p.getId() === '622c5788-3028-41fd-a8cb-164f868d4322'); const authoredOn = medReq.getDate('authoredOn.value'); expect(authoredOn.isDateTime).to.be.true; expect(authoredOn).to.deep.equal(cql.DateTime.parse('2009-12-01T11:18:29-05:00')); @@ -268,7 +300,9 @@ describe('#R4 v4.0.0', () => { it('should support getting times', () => { const pt = patientSource.currentPatient(); - const observation = pt.findRecords('Observation').find(p => p.getId() === '9c15c801-6bb5-47a7-a9db-8bad0cb6aa68'); + const observation = pt + .findRecords('Observation') + .find(p => p.getId() === '9c15c801-6bb5-47a7-a9db-8bad0cb6aa68'); const valueTime = observation.get('value.value'); expect(valueTime.isTime()).to.be.true; expect(valueTime).to.deep.equal(cql.DateTime.parse('0000-01-01T18:23:47.376-05:00').getTime()); @@ -276,24 +310,39 @@ describe('#R4 v4.0.0', () => { it('should support getting an option of a choice', () => { const pt = patientSource.currentPatient(); - const condition = pt.findRecords('Condition').find(p => p.getId() === '9934bc4f-58af-4ecf-bb70-b7cc31987fc5'); + const condition = pt + .findRecords('Condition') + .find(p => p.getId() === '9934bc4f-58af-4ecf-bb70-b7cc31987fc5'); // In R4, you use the stub of the choice (e.g., onset[x] datetime is retrieved as onset) - expect(condition.get('onset.value')).to.deep.equal(cql.DateTime.parse('2009-08-09T12:18:29-04:00')); + expect(condition.get('onset.value')).to.deep.equal( + cql.DateTime.parse('2009-08-09T12:18:29-04:00') + ); }); it('should support getting an option of a choice using explicit name', () => { // This was needed because the ModelInfo used to indicate MedicationRequest's primaryCodePath as medicationCodeableConcept! // Keep it in case anyone tries to load an older ModelInfo that still does that. const pt = patientSource.currentPatient(); - const medReq = pt.findRecords('MedicationRequest').find(p => p.getId() === '622c5788-3028-41fd-a8cb-164f868d4322'); + const medReq = pt + .findRecords('MedicationRequest') + .find(p => p.getId() === '622c5788-3028-41fd-a8cb-164f868d4322'); const code = medReq.getCode('medicationCodeableConcept'); - expect(code).to.deep.equal(new cql.Code('309097', 'http://www.nlm.nih.gov/research/umls/rxnorm', undefined, 'Cefuroxime 250 MG Oral Tablet')); + expect(code).to.deep.equal( + new cql.Code( + '309097', + 'http://www.nlm.nih.gov/research/umls/rxnorm', + undefined, + 'Cefuroxime 250 MG Oral Tablet' + ) + ); }); it('should not return wrong type if explicit choice name was requested but data used different type', () => { // This is needed because the ModelInfo indicates MedicationRequest's primaryCodePath as medicationCodeableConcept! const pt = patientSource.currentPatient(); - const medReq = pt.findRecords('MedicationRequest').find(p => p.getId() === '622c5788-3028-41fd-a8cb-164f868d4322'); + const medReq = pt + .findRecords('MedicationRequest') + .find(p => p.getId() === '622c5788-3028-41fd-a8cb-164f868d4322'); const ref = medReq.getCode('medicationReference'); expect(ref).to.be.undefined; }); @@ -301,19 +350,25 @@ describe('#R4 v4.0.0', () => { it('should return undefined when attempting to get the code on a choice that is a reference', () => { // This was a bug that surfaced when testing some CQL against Synthea patients that use medicationReference const pt = patientSource.currentPatient(); - const medReq = pt.findRecords('MedicationRequest').find(p => p.getId() === '622c5788-3028-41fd-a8cb-164f868d4323'); + const medReq = pt + .findRecords('MedicationRequest') + .find(p => p.getId() === '622c5788-3028-41fd-a8cb-164f868d4323'); const ref = medReq.getCode('medication'); expect(ref).to.be.undefined; }); it('should support id and extension on primitives', () => { const pt = patientSource.currentPatient(); - const encounter = pt.findRecords('Encounter').find(p => p.getId() === '9d911534-10d8-4dc2-91f1-d7aeed828af8'); + const encounter = pt + .findRecords('Encounter') + .find(p => p.getId() === '9d911534-10d8-4dc2-91f1-d7aeed828af8'); expect(encounter.get('status.id')).to.equal('12345'); - expect(compact(encounter.get('status.extension'))).to.deep.equal([ { - url: { value: 'http://example.org/fhir/StructureDefinition/originalText' }, - value: { value: 'completed' } - }]); + expect(compact(encounter.get('status.extension'))).to.deep.equal([ + { + url: { value: 'http://example.org/fhir/StructureDefinition/originalText' }, + value: { value: 'completed' } + } + ]); }); it('should support id on list of primitives', () => { @@ -332,20 +387,29 @@ describe('#R4 v4.0.0', () => { { name: '{http://hl7.org/fhir}Condition', type: 'NamedTypeSpecifier' }, { name: '{http://hl7.org/fhir}DomainResource', type: 'NamedTypeSpecifier' }, { name: '{http://hl7.org/fhir}Resource', type: 'NamedTypeSpecifier' }, - { name: '{urn:hl7-org:elm-types:r1}Any', type: 'NamedTypeSpecifier' }, + { name: '{urn:hl7-org:elm-types:r1}Any', type: 'NamedTypeSpecifier' } ]); }); it('should support _is', () => { const pt = patientSource.currentPatient(); const condition = pt.findRecord('Condition'); - expect(condition._is({name: '{http://hl7.org/fhir}Condition', type: 'NamedTypeSpecifier'})).to.be.true; - expect(condition._is({name: '{http://hl7.org/fhir}DomainResource', type: 'NamedTypeSpecifier'})).to.be.true; - expect(condition._is({name: '{http://hl7.org/fhir}Resource', type: 'NamedTypeSpecifier'})).to.be.true; - expect(condition._is({name: '{urn:hl7-org:elm-types:r1}Any', type: 'NamedTypeSpecifier'})).to.be.true; - expect(condition._is({name: '{http://some.other.model.org}Condition', type: 'NamedTypeSpecifier'})).to.be.false; - expect(condition._is({name: '{http://hl7.org/fhir}Observation', type: 'NamedTypeSpecifier'})).to.be.false; - expect(condition._is({name: '{http://hl7.org/fhir}Condition', type: 'IntervalTypeSpecifier'})).to.be.false; + expect(condition._is({ name: '{http://hl7.org/fhir}Condition', type: 'NamedTypeSpecifier' })).to + .be.true; + expect( + condition._is({ name: '{http://hl7.org/fhir}DomainResource', type: 'NamedTypeSpecifier' }) + ).to.be.true; + expect(condition._is({ name: '{http://hl7.org/fhir}Resource', type: 'NamedTypeSpecifier' })).to + .be.true; + expect(condition._is({ name: '{urn:hl7-org:elm-types:r1}Any', type: 'NamedTypeSpecifier' })).to + .be.true; + expect( + condition._is({ name: '{http://some.other.model.org}Condition', type: 'NamedTypeSpecifier' }) + ).to.be.false; + expect(condition._is({ name: '{http://hl7.org/fhir}Observation', type: 'NamedTypeSpecifier' })) + .to.be.false; + expect(condition._is({ name: '{http://hl7.org/fhir}Condition', type: 'IntervalTypeSpecifier' })) + .to.be.false; }); }); @@ -361,7 +425,6 @@ function compact(obj) { if (value !== undefined) { compacted[prop] = compact(value); } - } return compacted; } diff --git a/test/stu3_test.js b/test/stu3_test.js index f50641a..fd63048 100644 --- a/test/stu3_test.js +++ b/test/stu3_test.js @@ -1,6 +1,6 @@ const cql = require('cql-execution'); const cqlfhir = require('../src/index'); -const {expect} = require('chai'); +const { expect } = require('chai'); const conditionResource = require('./fixtures/stu3/Condition_f201.json'); const patientMyron = require('./fixtures/stu3/Myron933_Ondricka197_a901d2b4-30a8-41b9-b94a-f44561d8f809.json'); @@ -55,7 +55,11 @@ describe('#FHIRWrapper_STU3', () => { }); it('should error if requested type is incompatible', () => { - expect(function(){fhirWrapper.wrap(conditionResource, 'Observation');}).to.throw('Incompatible types: FHIR resourceType is Condition which cannot be cast as Observation'); + expect(function () { + fhirWrapper.wrap(conditionResource, 'Observation'); + }).to.throw( + 'Incompatible types: FHIR resourceType is Condition which cannot be cast as Observation' + ); }); it('should wrap a fhir resource to the type specified if real type unknown', () => { @@ -71,10 +75,7 @@ describe('#STU3', () => { }); beforeEach(() => { - patientSource.loadBundles([ - patientMyron, - patientShawnee - ]); + patientSource.loadBundles([patientMyron, patientShawnee]); }); afterEach(() => patientSource.reset()); @@ -108,14 +109,16 @@ describe('#STU3', () => { expect(patientSource.currentPatient()).to.not.equal(patientSource.currentPatient()); }); - it('should find patient birthDate', () =>{ + it('should find patient birthDate', () => { const pt = patientSource.currentPatient(); // cql-execution v1.3.2 currently doesn't export the new Date class, so we need to use the .getDate() workaround - expect(compact(pt.get('birthDate'))).to.deep.equal({ value: new cql.DateTime.parse('1975-02-25').getDate() }); + expect(compact(pt.get('birthDate'))).to.deep.equal({ + value: new cql.DateTime.parse('1975-02-25').getDate() + }); expect(pt.get('birthDate.value')).to.deep.equal(new cql.DateTime.parse('1975-02-25').getDate()); }); - it('should find patient extensions', () =>{ + it('should find patient extensions', () => { const pt = patientSource.currentPatient(); const extensions = pt.get('extension'); expect(extensions).to.have.length(11); @@ -143,7 +146,7 @@ describe('#STU3', () => { }); }); - it('should find records by type name (e.g., Condition)', () =>{ + it('should find records by type name (e.g., Condition)', () => { const pt = patientSource.currentPatient(); const conditions = pt.findRecords('Condition'); expect(conditions).to.have.length(9); @@ -152,7 +155,7 @@ describe('#STU3', () => { expect(paymentReconciliations).to.be.empty; }); - it('should find records by model name and type name (e.g., FHIR.Condition)', () =>{ + it('should find records by model name and type name (e.g., FHIR.Condition)', () => { const pt = patientSource.currentPatient(); const conditions = pt.findRecords('FHIR.Condition'); expect(conditions).to.have.length(9); @@ -161,7 +164,7 @@ describe('#STU3', () => { expect(paymentReconciliations).to.be.empty; }); - it('should find records by model URL and type name (e.g., {http://hl7.org/fhir}Condition)', () =>{ + it('should find records by model URL and type name (e.g., {http://hl7.org/fhir}Condition)', () => { const pt = patientSource.currentPatient(); const conditions = pt.findRecords('{http://hl7.org/fhir}Condition'); expect(conditions).to.have.length(9); @@ -170,7 +173,7 @@ describe('#STU3', () => { expect(paymentReconciliations).to.be.empty; }); - it('should find a single record', () =>{ + it('should find a single record', () => { const pt = patientSource.currentPatient(); const condition = pt.findRecord('Condition'); expect(condition.getTypeInfo().name).to.equal('Condition'); @@ -181,19 +184,27 @@ describe('#STU3', () => { it('should support getId', () => { const pt = patientSource.currentPatient(); - const procedure = pt.findRecords('Procedure').find(p => p.getId() === 'f38a480b-f352-4c1f-aca2-b6612a110530'); + const procedure = pt + .findRecords('Procedure') + .find(p => p.getId() === 'f38a480b-f352-4c1f-aca2-b6612a110530'); expect(procedure.getId()).to.equal('f38a480b-f352-4c1f-aca2-b6612a110530'); }); it('should support getCode', () => { const pt = patientSource.currentPatient(); - const procedure = pt.findRecords('Procedure').find(p => p.getId() === 'f38a480b-f352-4c1f-aca2-b6612a110530'); - expect(procedure.getCode('code')).to.deep.equal(new cql.Code('117015009', 'http://snomed.info/sct', undefined, 'Throat culture (procedure) ')); + const procedure = pt + .findRecords('Procedure') + .find(p => p.getId() === 'f38a480b-f352-4c1f-aca2-b6612a110530'); + expect(procedure.getCode('code')).to.deep.equal( + new cql.Code('117015009', 'http://snomed.info/sct', undefined, 'Throat culture (procedure) ') + ); }); it('should support getDate (DateTime)', () => { const pt = patientSource.currentPatient(); - const condition = pt.findRecords('MedicationRequest').find(p => p.getId() === 'a87a2346-f826-40db-95ff-0660786460c0'); + const condition = pt + .findRecords('MedicationRequest') + .find(p => p.getId() === 'a87a2346-f826-40db-95ff-0660786460c0'); const periodStart = condition.getDate('authoredOn.value'); expect(periodStart.isDateTime).to.be.true; expect(periodStart).to.deep.equal(cql.DateTime.parse('2008-11-11T08:18:56-05:00')); @@ -208,7 +219,9 @@ describe('#STU3', () => { it('should support getDateOrInterval (DateTime)', () => { const pt = patientSource.currentPatient(); - const condition = pt.findRecords('MedicationRequest').find(p => p.getId() === 'a87a2346-f826-40db-95ff-0660786460c0'); + const condition = pt + .findRecords('MedicationRequest') + .find(p => p.getId() === 'a87a2346-f826-40db-95ff-0660786460c0'); const periodStart = condition.getDateOrInterval('authoredOn.value'); expect(periodStart.isDateTime).to.be.true; expect(periodStart).to.deep.equal(cql.DateTime.parse('2008-11-11T08:18:56-05:00')); @@ -223,37 +236,51 @@ describe('#STU3', () => { it('should support dot-separated-paths', () => { const pt = patientSource.currentPatient(); - const procedure = pt.findRecords('Procedure').find(p => p.getId() === 'f38a480b-f352-4c1f-aca2-b6612a110530'); - expect(procedure.get('subject.reference.value')).to.deep.equal('urn:uuid:d02dafab-dd3b-43cf-ae5b-c29b9789cb1c'); + const procedure = pt + .findRecords('Procedure') + .find(p => p.getId() === 'f38a480b-f352-4c1f-aca2-b6612a110530'); + expect(procedure.get('subject.reference.value')).to.deep.equal( + 'urn:uuid:d02dafab-dd3b-43cf-ae5b-c29b9789cb1c' + ); }); it('should support getting booleans', () => { const pt = patientSource.currentPatient(); - const immunization = pt.findRecords('Immunization').find(p => p.getId() === 'b61579e1-1c2b-49e1-8be7-b95b11f1ca70'); + const immunization = pt + .findRecords('Immunization') + .find(p => p.getId() === 'b61579e1-1c2b-49e1-8be7-b95b11f1ca70'); expect(immunization.get('notGiven.value')).to.be.false; }); it('should support getting decimals', () => { const pt = patientSource.currentPatient(); - const claim = pt.findRecords('Claim').find(p => p.getId() === 'c04752c4-38ab-464a-8c97-b4e755d15e36'); + const claim = pt + .findRecords('Claim') + .find(p => p.getId() === 'c04752c4-38ab-464a-8c97-b4e755d15e36'); expect(claim.get('total.value.value')).to.equal(265.52); }); it('should support getting integers', () => { const pt = patientSource.currentPatient(); - const claim = pt.findRecords('Claim').find(p => p.getId() === 'c04752c4-38ab-464a-8c97-b4e755d15e36'); + const claim = pt + .findRecords('Claim') + .find(p => p.getId() === 'c04752c4-38ab-464a-8c97-b4e755d15e36'); expect(claim.get('item')[0].get('sequence.value')).to.equal(1); }); it('should support getting strings', () => { const pt = patientSource.currentPatient(); - const procedure = pt.findRecords('Procedure').find(p => p.getId() === 'f38a480b-f352-4c1f-aca2-b6612a110530'); + const procedure = pt + .findRecords('Procedure') + .find(p => p.getId() === 'f38a480b-f352-4c1f-aca2-b6612a110530'); expect(procedure.get('status.value')).to.deep.equal('completed'); }); it('should support getting dateTimes', () => { const pt = patientSource.currentPatient(); - const condition = pt.findRecords('MedicationRequest').find(p => p.getId() === 'a87a2346-f826-40db-95ff-0660786460c0'); + const condition = pt + .findRecords('MedicationRequest') + .find(p => p.getId() === 'a87a2346-f826-40db-95ff-0660786460c0'); const periodStart = condition.get('authoredOn.value'); expect(periodStart.isDateTime).to.be.true; expect(periodStart).to.deep.equal(cql.DateTime.parse('2008-11-11T08:18:56-05:00')); @@ -268,7 +295,9 @@ describe('#STU3', () => { it('should support getting times', () => { const pt = patientSource.currentPatient(); - const observation = pt.findRecords('Observation').find(p => p.getId() === '9c15c801-6bb5-47a7-a9db-8bad0cb6aa68'); + const observation = pt + .findRecords('Observation') + .find(p => p.getId() === '9c15c801-6bb5-47a7-a9db-8bad0cb6aa68'); const valueTime = observation.get('value.value'); expect(valueTime.isTime()).to.be.true; expect(valueTime).to.deep.equal(cql.DateTime.parse('0000-01-01T18:23:47.376-05:00').getTime()); @@ -276,41 +305,60 @@ describe('#STU3', () => { it('should support getting an option of a choice', () => { const pt = patientSource.currentPatient(); - const condition = pt.findRecords('Condition').find(p => p.getId() === '9aae38e5-44ff-4f6e-8228-4898cdfa0833'); + const condition = pt + .findRecords('Condition') + .find(p => p.getId() === '9aae38e5-44ff-4f6e-8228-4898cdfa0833'); // In STU3, you use the stub of the choice (e.g., onset[x] datetime is retrieved as onset) - expect(condition.get('onset.value')).to.deep.equal(cql.DateTime.parse('1994-07-19T09:18:56-04:00')); + expect(condition.get('onset.value')).to.deep.equal( + cql.DateTime.parse('1994-07-19T09:18:56-04:00') + ); }); it('should support getting an option of a choice using explicit name', () => { // This is needed because the ModelInfo indicates MedicationRequest's primaryCodePath as medicationCodeableConcept! const pt = patientSource.currentPatient(); - const condition = pt.findRecords('MedicationRequest').find(p => p.getId() === 'a87a2346-f826-40db-95ff-0660786460c0'); + const condition = pt + .findRecords('MedicationRequest') + .find(p => p.getId() === 'a87a2346-f826-40db-95ff-0660786460c0'); const code = condition.getCode('medicationCodeableConcept'); - expect(code).to.deep.equal(new cql.Code('308192', 'http://www.nlm.nih.gov/research/umls/rxnorm', undefined, 'Amoxicillin 500 MG Oral Tablet')); + expect(code).to.deep.equal( + new cql.Code( + '308192', + 'http://www.nlm.nih.gov/research/umls/rxnorm', + undefined, + 'Amoxicillin 500 MG Oral Tablet' + ) + ); }); it('should not return wrong type if explicit choice name was requested but data used different type', () => { // This is needed because the ModelInfo indicates MedicationRequest's primaryCodePath as medicationCodeableConcept! const pt = patientSource.currentPatient(); - const condition = pt.findRecords('MedicationRequest').find(p => p.getId() === 'a87a2346-f826-40db-95ff-0660786460c0'); + const condition = pt + .findRecords('MedicationRequest') + .find(p => p.getId() === 'a87a2346-f826-40db-95ff-0660786460c0'); const code = condition.getCode('medicationReference'); expect(code).to.be.undefined; }); it('should support id and extension on primitives', () => { const pt = patientSource.currentPatient(); - const goal = pt.findRecords('Goal').find(p => p.getId() === '1ed18813-c964-4f2d-8467-d8b351fe051c'); + const goal = pt + .findRecords('Goal') + .find(p => p.getId() === '1ed18813-c964-4f2d-8467-d8b351fe051c'); expect(goal.get('status.id')).to.equal('12345'); - expect(compact(goal.get('status.extension'))).to.deep.equal([ { - url: { value: 'http://example.org/fhir/StructureDefinition/originalText' }, - value: { value: 'progressing' } - }]); + expect(compact(goal.get('status.extension'))).to.deep.equal([ + { + url: { value: 'http://example.org/fhir/StructureDefinition/originalText' }, + value: { value: 'progressing' } + } + ]); }); it('should support id on list of primitives', () => { const pt = patientSource.currentPatient(); expect(compact(pt.get('address')[0].get('line'))).to.deep.equal([ - { value: '172 O\'Keefe Station' }, + { value: "172 O'Keefe Station" }, { id: '2468', value: 'Floor 5' }, { value: 'Apt. C' } ]); @@ -323,20 +371,29 @@ describe('#STU3', () => { { name: '{http://hl7.org/fhir}Condition', type: 'NamedTypeSpecifier' }, { name: '{http://hl7.org/fhir}DomainResource', type: 'NamedTypeSpecifier' }, { name: '{http://hl7.org/fhir}Resource', type: 'NamedTypeSpecifier' }, - { name: '{urn:hl7-org:elm-types:r1}Any', type: 'NamedTypeSpecifier' }, + { name: '{urn:hl7-org:elm-types:r1}Any', type: 'NamedTypeSpecifier' } ]); }); it('should support _is', () => { const pt = patientSource.currentPatient(); const condition = pt.findRecord('Condition'); - expect(condition._is({name: '{http://hl7.org/fhir}Condition', type: 'NamedTypeSpecifier'})).to.be.true; - expect(condition._is({name: '{http://hl7.org/fhir}DomainResource', type: 'NamedTypeSpecifier'})).to.be.true; - expect(condition._is({name: '{http://hl7.org/fhir}Resource', type: 'NamedTypeSpecifier'})).to.be.true; - expect(condition._is({name: '{urn:hl7-org:elm-types:r1}Any', type: 'NamedTypeSpecifier'})).to.be.true; - expect(condition._is({name: '{http://some.other.model.org}Condition', type: 'NamedTypeSpecifier'})).to.be.false; - expect(condition._is({name: '{http://hl7.org/fhir}Observation', type: 'NamedTypeSpecifier'})).to.be.false; - expect(condition._is({name: '{http://hl7.org/fhir}Condition', type: 'IntervalTypeSpecifier'})).to.be.false; + expect(condition._is({ name: '{http://hl7.org/fhir}Condition', type: 'NamedTypeSpecifier' })).to + .be.true; + expect( + condition._is({ name: '{http://hl7.org/fhir}DomainResource', type: 'NamedTypeSpecifier' }) + ).to.be.true; + expect(condition._is({ name: '{http://hl7.org/fhir}Resource', type: 'NamedTypeSpecifier' })).to + .be.true; + expect(condition._is({ name: '{urn:hl7-org:elm-types:r1}Any', type: 'NamedTypeSpecifier' })).to + .be.true; + expect( + condition._is({ name: '{http://some.other.model.org}Condition', type: 'NamedTypeSpecifier' }) + ).to.be.false; + expect(condition._is({ name: '{http://hl7.org/fhir}Observation', type: 'NamedTypeSpecifier' })) + .to.be.false; + expect(condition._is({ name: '{http://hl7.org/fhir}Condition', type: 'IntervalTypeSpecifier' })) + .to.be.false; }); }); @@ -352,7 +409,6 @@ function compact(obj) { if (value !== undefined) { compacted[prop] = compact(value); } - } return compacted; } diff --git a/yarn.lock b/yarn.lock index af25de7..0d1d816 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1367,6 +1367,11 @@ escape-string-regexp@^1.0.5: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= +eslint-config-prettier@^8.5.0: + version "8.5.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz#5a81680ec934beca02c7b1a61cf8ca34b66feab1" + integrity sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q== + eslint-scope@^7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.1.0.tgz#c1f6ea30ac583031f203d65c73e723b01298f153" @@ -2023,6 +2028,11 @@ prelude-ls@^1.2.1: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== +prettier@^2.7.1: + version "2.7.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.7.1.tgz#e235806850d057f97bb08368a4f7d899f7760c64" + integrity sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g== + process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"