From a385483cd6accb7c58764f3b4715c1f144c661b8 Mon Sep 17 00:00:00 2001 From: eyelidlessness Date: Wed, 10 Jul 2024 14:48:46 -0700 Subject: [PATCH] Scenario: add test for skipping validation of non-relevant nodes (at node and form level) --- .../src/assertion/extensions/answers.ts | 2 +- .../src/assertion/extensions/node-state.ts | 7 ++ packages/scenario/test/validity-state.test.ts | 87 +++++++++++++++++++ 3 files changed, 95 insertions(+), 1 deletion(-) diff --git a/packages/scenario/src/assertion/extensions/answers.ts b/packages/scenario/src/assertion/extensions/answers.ts index 6f0afd05..ff52281b 100644 --- a/packages/scenario/src/assertion/extensions/answers.ts +++ b/packages/scenario/src/assertion/extensions/answers.ts @@ -118,7 +118,7 @@ const answerExtensions = extendExpect(expect, { return new UnreachableError(expected); } - return pass || new InspectableComparisonError(condition, expected, 'be'); + return pass || new InspectableComparisonError(actual, expected, 'be'); } ), diff --git a/packages/scenario/src/assertion/extensions/node-state.ts b/packages/scenario/src/assertion/extensions/node-state.ts index 82fc97f9..86f7a48e 100644 --- a/packages/scenario/src/assertion/extensions/node-state.ts +++ b/packages/scenario/src/assertion/extensions/node-state.ts @@ -71,6 +71,13 @@ const nodeStateExtensions = extendExpect(expect, { currentState: { relevant: false }, }), + toBeRequired: new StaticConditionExpectExtension(assertEngineNode, { + currentState: { required: true }, + }), + toBeOptional: new StaticConditionExpectExtension(assertEngineNode, { + currentState: { required: false }, + }), + /** * **PORTING NOTES** * diff --git a/packages/scenario/test/validity-state.test.ts b/packages/scenario/test/validity-state.test.ts index 7eab39e0..aa847586 100644 --- a/packages/scenario/test/validity-state.test.ts +++ b/packages/scenario/test/validity-state.test.ts @@ -16,6 +16,7 @@ import { AnswerResult, Scenario } from '../src/jr/Scenario.ts'; import { r } from '../src/jr/resource/ResourcePathHelper.ts'; import { ANSWER_CONSTRAINT_VIOLATED, + ANSWER_OK, ANSWER_REQUIRED_BUT_EMPTY, } from '../src/jr/validation/ValidateOutcome.ts'; @@ -446,3 +447,89 @@ describe('Validity messages', () => { expect(result).toHaveConstraintMessage(null); }); }); + +describe('Validation and relevance', () => { + it('does not produce validation errors for non-relevant questions', async () => { + const scenario = await Scenario.init( + 'Validation and relevance', + html( + head( + title('Validation and relevance'), + // prettier-ignore + model( + mainInstance( + t('data id="validation-and-relevance"', + t('a', 'default value'), + t('b', 'A'), + t('validated-when-relevant') + ) + ), + bind('/data/a'), + bind('/data/b'), + bind('/data/validated-when-relevant') + .relevant("/data/a != 'default value'") + .required('/data/b > 10') + ) + ), + body(input('/data/a'), input('/data/b'), input('/data/validated-when-relevant')) + ) + ); + + // Sanity check: initially non-relevant and optional + expect(scenario.getInstanceNode('/data/validated-when-relevant')).toBeNonRelevant(); + expect(scenario.getInstanceNode('/data/validated-when-relevant')).toBeOptional(); + + // Sanity check: no validation errors while optional + expect(scenario.answerOf('/data/validated-when-relevant')).toHaveValidityStatus( + AnswerResult.OK + ); + + // Make required condition effective + scenario.answer('/data/b', 11); + + // Sanity check: still non-relevant, now required + expect(scenario.getInstanceNode('/data/validated-when-relevant')).toBeNonRelevant(); + expect(scenario.getInstanceNode('/data/validated-when-relevant')).toBeRequired(); + + // Check form has no validation error while non-relevant + let validate = scenario.getValidationOutcome(); + + expect(validate.failedPrompt).toBeNull(); + expect(validate.outcome).toBe(ANSWER_OK); + + // Check answer/node has no validation error while non-relevant + expect(scenario.answerOf('/data/validated-when-relevant')).toHaveValidityStatus( + AnswerResult.OK + ); + + // Make relevant, sanity check + scenario.answer('/data/a', 'another value, not the default!'); + expect(scenario.getInstanceNode('/data/validated-when-relevant')).toBeRelevant(); + + // Check validation error while relevant + validate = scenario.getValidationOutcome(); + + expect(validate.failedPrompt).toBe(scenario.indexOf('/data/validated-when-relevant')); + expect(validate.outcome).toBe(ANSWER_REQUIRED_BUT_EMPTY); + + // Check answer/node has no validation error while non-relevant + expect(scenario.answerOf('/data/validated-when-relevant')).toHaveValidityStatus( + AnswerResult.REQUIRED_BUT_EMPTY + ); + + // Make non-relevant again + scenario.answer('/data/a', 'default value'); + expect(scenario.getInstanceNode('/data/validated-when-relevant')).toBeNonRelevant(); + + // Check form has no validation error again + validate = scenario.getValidationOutcome(); + + expect(validate.failedPrompt).toBeNull(); + expect(validate.outcome).toBe(ANSWER_OK); + + // Check answer/node has no validation error again + expect(scenario.answerOf('/data/validated-when-relevant')).toHaveValidityStatus( + AnswerResult.OK + ); + }); +});