Skip to content

Commit

Permalink
Merge branch 'LF-2831-output-empty-expression' into 'master'
Browse files Browse the repository at this point in the history
LF-2831 - Added empty output expression validation

See merge request lfor/rule-editor!4
  • Loading branch information
wamorn committed Mar 14, 2024
2 parents c164835 + 63ccb1c commit 1fa3c74
Show file tree
Hide file tree
Showing 52 changed files with 3,430 additions and 1,169 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

This project follows [Semantic Versioning](http://semver.org/).

## [3.1.17] 2024-01-26
### Added
- Added expression validation
- Implemented logic to disable the 'Save' button if validation fails

## [3.1.16] 2024-01-17
### Changed
- Converted the Rule Editor demo to display in a dialog
Expand Down
567 changes: 310 additions & 257 deletions cypress/integration/custom_extension_variable_type.ts

Large diffs are not rendered by default.

3 changes: 1 addition & 2 deletions cypress/integration/demo.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -390,8 +390,7 @@ describe('Rule editor demo', () => {

// The Scoring Item panel should now display
cy.get('lhc-select-scoring-items').should('exist');
cy.get('div.scoring-items-selection-body')
.within(() => {
cy.get('div.scoring-items-selection-body').within(() => {
cy.get('div.items-tree tree-node').should('have.length', 17);
cy.get('.angular-tree-component [type="checkbox"]').as('checkboxes');

Expand Down
550 changes: 550 additions & 0 deletions cypress/integration/expression_validation.ts

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions cypress/integration/fhirpath_to_easy_path_conversion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -393,8 +393,9 @@ describe('Rule editor', () => {
.should('be.visible')
.should('not.have.class', 'field-error');

// Type 'a' into the expression
cy.get('#simple-expression-2').type('a');
// Type 'a' into the expression, there should be no error
cy.get('#simple-expression-2').type('a')
.should('not.have.class', 'field-error');

// The 'Save' button should be enabled.
cy.get('#export').should('not.have.class', 'disabled');
Expand Down
102 changes: 87 additions & 15 deletions cypress/integration/spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,28 @@ describe('Rule editor', () => {
cy.get('#variable-type-0').select('Easy Path Expression');
cy.get('#simple-expression-0').type('1 + 1');

// The Output Expression should have no error, the Save button should be enabled
cy.get('#simple-expression-final').should('not.have.class', 'field-error');
cy.get('#expression-error > p').should('not.exist');
cy.get('#export').should('not.have.class', 'disabled');

// Save (Export) should output the questionnaire for the given Variable Type
cy.get('#export').click();

// There is an error in the Output Expression
cy.get('#simple-expression-final').should('have.class', 'field-error');
cy.get('#final-expression-section #expression-error > p').should('contain.text', 'Expression is required.');
// As a result, the Save button is disabled
cy.get('#export').should('have.class', 'disabled');

// Fix the expression in the Output Expression section
cy.get('#simple-expression-final').clear().type('1 + 1');

// The Output Expression should no longer have error, the Save button should be enabled
cy.get('#simple-expression-final').should('not.have.class', 'field-error');
cy.get('#expression-error > p').should('not.exist');
cy.get('#export').should('not.have.class', 'disabled');

// Save (Export) should output the questionnaire for the given Variable Type
cy.get('#export').click();

Expand Down Expand Up @@ -1164,18 +1186,18 @@ describe('Rule editor', () => {

// Confirm that variable c is available for Output expression
cy.get('#simple-expression-final').clear().type('a + b + c');
cy.get('lhc-syntax-preview>div>div>pre').should('not.have.text', 'Not valid');
cy.get('#final-expression-section lhc-syntax-preview > div > div > pre').should('not.have.text', 'Not valid');

// Delete variable b
cy.get('#remove-variable-1').click();
cy.get('#variables-section .variable-row').should('have.length', 2);

// Confirm that variable b is no longer available for Output expression
cy.get('lhc-syntax-preview>div>div>pre').should('contain.text', 'Not valid');
cy.get('#final-expression-section #expression-error > p').should('contain.text', 'Invalid expression.');

// Confirm that expression without variable b is valid
cy.get('#simple-expression-final').clear().type('a + c');
cy.get('lhc-syntax-preview>div>div>pre').should('not.have.text', 'Not valid');
cy.get('#final-expression-section lhc-syntax-preview > div > div > pre').should('not.have.text', 'Not valid');

});

Expand Down Expand Up @@ -1253,11 +1275,11 @@ describe('Rule editor', () => {
});

describe('PHQ9 score calculation', () => {
beforeEach(() => {
cy.get('#questionnaire-select').select('PHQ9 (no FHIRPath)');
});

it('should display the calculate sum prompt', () => {
cy.intercept('/phq9.json').as('phq9');
cy.get('select#questionnaire-select').select('PHQ9 (no FHIRPath)');
cy.wait('@phq9');

// The demo has '(/39156-5) selected by default
cy.get('#question').should('contain.value', '(/39156-5)');
// Click the button to edit the expression
Expand All @@ -1270,6 +1292,10 @@ describe('Rule editor', () => {
});

it('should hide the calculate sum prompt if click no', () => {
cy.intercept('/phq9.json').as('phq9');
cy.get('select#questionnaire-select').select('PHQ9 (no FHIRPath)');
cy.wait('@phq9');

// The demo has '(/39156-5) selected by default
cy.get('#question').should('contain.value', '(/39156-5)');
// Click the button to edit the expression
Expand All @@ -1295,6 +1321,10 @@ describe('Rule editor', () => {
});

it('should display the scoring items selection', () => {
cy.intercept('/phq9.json').as('phq9');
cy.get('select#questionnaire-select').select('PHQ9 (no FHIRPath)');
cy.wait('@phq9');

// The demo has '(/39156-5) selected by default
cy.get('#question').should('contain.value', '(/39156-5)');
// Click the button to edit the expression
Expand All @@ -1317,6 +1347,10 @@ describe('Rule editor', () => {
});

it('should be able to select/unselect all items', () => {
cy.intercept('/phq9.json').as('phq9');
cy.get('select#questionnaire-select').select('PHQ9 (no FHIRPath)');
cy.wait('@phq9');

// The demo has '(/39156-5) selected by default
cy.get('#question').should('contain.value', '(/39156-5)');
// Click the button to edit the expression
Expand Down Expand Up @@ -1344,6 +1378,10 @@ describe('Rule editor', () => {
});

it('should not select items if the "Unselect All" button is clicked with zero selected items', () => {
cy.intercept('/phq9.json').as('phq9');
cy.get('select#questionnaire-select').select('PHQ9 (no FHIRPath)');
cy.wait('@phq9');

// The demo has '(/39156-5) selected by default
cy.get('#question').should('contain.value', '(/39156-5)');
// Click the button to edit the expression
Expand Down Expand Up @@ -1372,6 +1410,10 @@ describe('Rule editor', () => {
});

it('should be able to select/unselect individual item', () => {
cy.intercept('/phq9.json').as('phq9');
cy.get('select#questionnaire-select').select('PHQ9 (no FHIRPath)');
cy.wait('@phq9');

// The demo has '(/39156-5) selected by default
cy.get('#question').should('contain.value', '(/39156-5)');
// Click the button to edit the expression
Expand Down Expand Up @@ -1409,6 +1451,10 @@ describe('Rule editor', () => {
});

it('should hide the scoring items selection if click Cancel', () => {
cy.intercept('/phq9.json').as('phq9');
cy.get('select#questionnaire-select').select('PHQ9 (no FHIRPath)');
cy.wait('@phq9');

// The demo has '(/39156-5) selected by default
cy.get('#question').should('contain.value', '(/39156-5)');
// Click the button to edit the expression
Expand Down Expand Up @@ -1444,6 +1490,10 @@ describe('Rule editor', () => {
});

it('should be able to export score with selected individual items', () => {
cy.intercept('/phq9.json').as('phq9');
cy.get('select#questionnaire-select').select('PHQ9 (no FHIRPath)');
cy.wait('@phq9');

// The demo has '(/39156-5) selected by default
cy.get('#question').should('contain.value', '(/39156-5)');
// Click the button to edit the expression
Expand Down Expand Up @@ -1480,6 +1530,10 @@ describe('Rule editor', () => {
});

it('should be able to export score with all items', () => {
cy.intercept('/phq9.json').as('phq9');
cy.get('select#questionnaire-select').select('PHQ9 (no FHIRPath)');
cy.wait('@phq9');

// The demo has '(/39156-5) selected by default
cy.get('#question').should('contain.value', '(/39156-5)');
// Click the button to edit the expression
Expand Down Expand Up @@ -2065,7 +2119,6 @@ describe('Rule editor', () => {
expect(parsedData.item[6].extension[7].valueExpression.expression).to.have.string(variable9Exp);
});
});

});

describe('Query support', () => {
Expand Down Expand Up @@ -2151,8 +2204,11 @@ describe('Rule editor', () => {
cy.get('#case-output-2').should('have.value', 'overweight');
cy.get('.default').should('have.value', 'obese');

cy.get('lhc-case-statements > lhc-syntax-preview').contains(
cy.get('lhc-case-statements lhc-syntax-preview > div > div > pre').should('contain.text',
`iif(%bmi<18.5,'underweight',iif(%bmi<25,'normal',iif(%bmi<30,'overweight','obese')))`);

// the 'Save' button should be enabled
cy.get('#export').should('not.have.class', 'disabled');
});

it('should display the FHIRPath case editor when importing questionnaire with FHIRPath in final expression', () => {
Expand All @@ -2178,8 +2234,12 @@ describe('Rule editor', () => {
cy.get('#case-output-2').should('have.value', `'overweight'`);
cy.get('.default').should('have.value', `'obese'`);

cy.get('lhc-case-statements > lhc-syntax-preview').contains(
`iif(%bmi<18.5,'underweight',iif(%bmi<25,'normal',iif(%bmi<30,'overweight','obese')))`);
// Check the output expression
cy.get('lhc-case-statements lhc-syntax-preview > div > div > pre').should('contain.text',
`iif(%bmi<18.5,'underweight',iif(%bmi<25,'normal',iif(%bmi<30,'overweight','obese')))`);

// the 'Save' button should be enabled
cy.get('#export').should('not.have.class', 'disabled');
});

it('should be able to add cases to a questionnaire that does not have them', () => {
Expand All @@ -2199,11 +2259,20 @@ describe('Rule editor', () => {

cy.get('#case-statements').should('not.be.checked');
cy.get('#case-statements').check();
// 'Use expressions (string if unchecked)' checkbox should be checked
cy.get('#output-expressions').should('be.checked');
cy.get('#output-expressions').uncheck();

// Preview should not show up initially
cy.get('lhc-case-statements > lhc-syntax-preview').should('not.exist');
cy.get('lhc-case-statements lhc-syntax-preview').should('not.exist');

// The case condition, case output, and default case should contain no errors
cy.get('#case-condition-0').should('not.have.class', 'field-error');
cy.get('#case-output-0').should('not.have.class', 'field-error');
cy.get('.default').should('not.have.class', 'field-error');

// the 'Save' button should be enabled
cy.get('#export').should('not.have.class', 'disabled');

// Add a conditions and outputs
cy.get('#case-condition-0').type('bmi<18.5');
Expand All @@ -2214,12 +2283,15 @@ describe('Rule editor', () => {
cy.get('#add-case').click();
cy.get('#case-condition-2').type('bmi<30');
cy.get('#case-output-2').type('overweight');
cy.get('.default').type('obese');
// Add a default value
cy.get('.default').type('obese');

// Check the output expression
cy.get('lhc-case-statements > lhc-syntax-preview').contains(
cy.get('lhc-case-statements lhc-syntax-preview > div > div > pre').should('contain.text',
`iif(%bmi<18.5,'underweight',iif(%bmi<25,'normal',iif(%bmi<30,'overweight','obese')))`);

// the 'Save' button should be enabled
cy.get('#export').should('not.have.class', 'disabled');
});

it('should reset case statements when switching between 2 case statements questionnaire', () => {
Expand Down
84 changes: 84 additions & 0 deletions docs/3rdpartylicenses.txt
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,9 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


@lhncbc/ucum-lhc
SEE LICENSE IN LICENSE.md

@ungap/custom-elements
ISC
ISC License
Expand All @@ -107,6 +110,9 @@ OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.


antlr4
BSD-3-Clause

autocomplete-lhc
See LICENSE.md
# LICENSE
Expand Down Expand Up @@ -138,6 +144,14 @@ THIS SOFTWARE IS PROVIDED BY THE OWNER AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


date-fns
MIT
# License

date-fns is licensed under the [MIT license](http://kossnocorp.mit-license.org).
Read more about MIT at [TLDRLegal](https://tldrlegal.com/license/mit-license).


easy-path-expressions
SEE LICENSE IN LICENSE.md

Expand Down Expand Up @@ -166,6 +180,76 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


fhirpath
SEE LICENSE in LICENSE.md
# LICENSE

The software, fhirpath.js, was jointly developed by the Lister Hill National
Center for Biomedical Communications (LHNCBC), a research and development
division of the U.S. National Library of Medicine (NLM), and [email protected]
(Health Samurai).

The following terms and conditions are based on the BSD open-source license.

If publishing research that used this software, please include a citation that
acknowledges it.

Redistribution and use in source and binary forms, with or without modification,
are permitted for commercial and non-commercial purposes and products alike,
provided that the following conditions are met:

* Redistributions of source code shall retain this license file, including the
list of contributing developers and organizations, this list of conditions and
the following disclaimer.
* Redistributions in binary form shall include this license file, including the
list of contributing developers and organizations, this list of conditions and
the following disclaimer, in the documentation and/or other materials provided
with the distribution.
* Neither the names of the National Library of Medicine (NLM), the Lister Hill
National Center for Biomedical Communications (LHNCBC), the National
Institutes of Health (NIH), nor the names of any of the software contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS "AS IS" AND ANY EXPRESS
OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
OF SUCH DAMAGE.


is-finite
MIT
MIT License

Copyright (c) Sindre Sorhus <[email protected]> (sindresorhus.com)

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


is-integer
WTFPL OR ISC
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) 2004 Sam Hocevar <[email protected]>

Everyone is permitted to copy and distribute verbatim or modified copies of this license document, and changing it is allowed as long as the name is changed.

DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

0. You just DO WHAT THE FUCK YOU WANT TO.


mobx
MIT
The MIT License (MIT)
Expand Down
Loading

0 comments on commit 1fa3c74

Please sign in to comment.