From a079b82da10259dd53cbb8ec2af955d7f617e460 Mon Sep 17 00:00:00 2001 From: David Chopin Date: Mon, 31 Jul 2023 14:46:18 -0500 Subject: [PATCH] Made it so that visibleIf is respected. Does not yet handle multiple conditions chained with AND or OR - DC --- lib/data/condition/visibility_helper.dart | 145 +++++ lib/ui/elements/matrix_dropdown.dart | 61 ++- lib/ui/elements/matrix_dynamic.dart | 65 ++- lib/ui/survey_configuration.dart | 3 +- lib/ui/survey_element_factory.dart | 12 +- lib/ui/survey_page_widget.dart | 36 +- .../condition/visibility_helper_test.dart | 508 ++++++++++++++++++ 7 files changed, 761 insertions(+), 69 deletions(-) create mode 100644 lib/data/condition/visibility_helper.dart create mode 100644 test/data/condition/visibility_helper_test.dart diff --git a/lib/data/condition/visibility_helper.dart b/lib/data/condition/visibility_helper.dart new file mode 100644 index 00000000..3d4057f2 --- /dev/null +++ b/lib/data/condition/visibility_helper.dart @@ -0,0 +1,145 @@ +import 'package:flutter_survey_js/survey.dart'; + +class VisibilityHelper { + bool isElementVisible( + {required Question element, + required Map surveyResponse}) { + final VisibilityHelper visibilityHelper = VisibilityHelper(); + bool isVisible = true; + String? visibleIf = element.visibleIf?.replaceAll(' ', ''); + if (visibleIf == null) { + return true; + } + void findMatch(Map surveyResponse) { + if (!_curlyBraceRegExp.hasMatch(visibleIf!)) { + return; + } + final match = _curlyBraceRegExp.firstMatch(visibleIf!); + final matchedString = + match?.group(0)!.replaceAll('{', '').replaceAll('}', ''); + String comparisonOperator = + visibilityHelper.determineComparisonOperatorString( + matchedElementName: match!, visibleIf: visibleIf!); + + dynamic rightOperand = visibilityHelper.determineRightOperand( + visibleIf: visibleIf!, + matchedElementName: match, + comparisonOperatorLength: comparisonOperator.length); + + visibleIf = visibleIf!.replaceFirst(_curlyBraceRegExp, ''); + + if (rightOperand is num) { + if (surveyResponse[matchedString] == null) { + isVisible = false; + return; + } + switch (comparisonOperator) { + case '=': + if (surveyResponse[matchedString] != rightOperand) { + isVisible = false; + return; + } + break; + case '<': + if (surveyResponse[matchedString] >= rightOperand) { + isVisible = false; + return; + } + break; + case '<=': + if (surveyResponse[matchedString] > rightOperand) { + isVisible = false; + return; + } + break; + case '>': + if (surveyResponse[matchedString] <= rightOperand) { + isVisible = false; + return; + } + break; + case '>=': + if (surveyResponse[matchedString] < rightOperand) { + isVisible = false; + return; + } + break; + } + } else { + rightOperand = rightOperand.substring(1, rightOperand.length - 1); + if (surveyResponse[matchedString] != '$rightOperand') { + isVisible = false; + return; + } + } + findMatch(surveyResponse); + } + + findMatch(surveyResponse); + return isVisible; + } + + final RegExp _curlyBraceRegExp = RegExp(r'{(.*?)}'); + final List _comparisonOperatorSymbols = ['=', '<', '>']; + + String determineComparisonOperatorString( + {required String visibleIf, required RegExpMatch matchedElementName}) { + String comparisonOperator = ''; + void evaluateChar() { + final String charToCheck = visibleIf.substring( + matchedElementName.end + comparisonOperator.length, + matchedElementName.end + comparisonOperator.length + 1); + if (_comparisonOperatorSymbols.contains(charToCheck)) { + comparisonOperator += charToCheck; + evaluateChar(); + } + } + + evaluateChar(); + return comparisonOperator; + } + + dynamic determineRightOperand( + {required String visibleIf, + required RegExpMatch matchedElementName, + required int comparisonOperatorLength}) { + bool rightOperandIsNum = false; + String rightOperand = ''; + + void evaluateChar() { + if ((matchedElementName.end + + comparisonOperatorLength + + rightOperand.length + + 1) > + visibleIf.length) { + return; + } + final String charToCheck = visibleIf.substring( + matchedElementName.end + + comparisonOperatorLength + + rightOperand.length, + matchedElementName.end + + comparisonOperatorLength + + rightOperand.length + + 1); + if (rightOperand.isEmpty) { + if (charToCheck != '\'') { + rightOperandIsNum = true; + } + } else { + if ((rightOperandIsNum && int.tryParse(charToCheck) != null)) { + return; + } + if (charToCheck == '\'') { + rightOperand += charToCheck; + return; + } + } + rightOperand += charToCheck; + evaluateChar(); + } + + evaluateChar(); + return rightOperandIsNum ? int.parse(rightOperand) : rightOperand; + } +} diff --git a/lib/ui/elements/matrix_dropdown.dart b/lib/ui/elements/matrix_dropdown.dart index fb4922ab..b2c063dc 100644 --- a/lib/ui/elements/matrix_dropdown.dart +++ b/lib/ui/elements/matrix_dropdown.dart @@ -56,34 +56,41 @@ class MatrixDropdownElement extends StatelessWidget { TableCell( verticalAlignment: TableCellVerticalAlignment.middle, child: Text(row.castToItemvalue().text ?? "")), - ...(matrix.columns?.toList() ?? []).map((column) { - final q = matrixDropdownColumnToQuestion(matrix, column); - final v = questionToValidators(q); + ...(matrix.columns?.toList() ?? []) + .map((column) { + final q = matrixDropdownColumnToQuestion(matrix, column); + final v = questionToValidators(q); + final element = SurveyConfiguration.of(context)! + .factory + .resolve(context, q, + configuration: + const ElementConfiguration(hasTitle: false)); - return TableCell( - verticalAlignment: TableCellVerticalAlignment.middle, - child: ReactiveNestedForm( - formControlName: - row.castToItemvalue().value!.toString(), - child: Builder( - builder: (context) { - final fg = ReactiveForm.of(context) as FormGroup; - final c = fg.control(column.name!); - //TODO runner - // //concat validators - // final newV = HashSet.of( - // [...c.validators, ...v]).toList(); - c.setValidators(v); - return SurveyConfiguration.of(context)! - .factory - .resolve( - context, q, - configuration: const ElementConfiguration( - hasTitle: false)); - }, - ), - )); - }).toList() + return element == null + ? null + : TableCell( + verticalAlignment: + TableCellVerticalAlignment.middle, + child: ReactiveNestedForm( + formControlName: + row.castToItemvalue().value!.toString(), + child: Builder( + builder: (context) { + final fg = + ReactiveForm.of(context) as FormGroup; + final c = fg.control(column.name!); + //TODO runner + // //concat validators + // final newV = HashSet.of( + // [...c.validators, ...v]).toList(); + c.setValidators(v); + return element; + }, + ), + )); + }) + .toList() + .removeWhere((element) => element == null) as List ])); }); diff --git a/lib/ui/elements/matrix_dynamic.dart b/lib/ui/elements/matrix_dynamic.dart index 87a0046f..4fdcfe77 100644 --- a/lib/ui/elements/matrix_dynamic.dart +++ b/lib/ui/elements/matrix_dynamic.dart @@ -1,12 +1,12 @@ import 'package:flutter/material.dart'; import 'package:flutter_survey_js/generated/l10n.dart'; import 'package:flutter_survey_js/ui/custom_scroll_behavior.dart'; -import 'package:flutter_survey_js/ui/reactive/reactive_wrap_form_array.dart'; -import 'package:flutter_survey_js/ui/survey_configuration.dart'; -import 'package:flutter_survey_js_model/flutter_survey_js_model.dart' as s; import 'package:flutter_survey_js/ui/form_control.dart'; import 'package:flutter_survey_js/ui/reactive/reactive_nested_form.dart'; +import 'package:flutter_survey_js/ui/reactive/reactive_wrap_form_array.dart'; +import 'package:flutter_survey_js/ui/survey_configuration.dart'; import 'package:flutter_survey_js/ui/validators.dart'; +import 'package:flutter_survey_js_model/flutter_survey_js_model.dart' as s; import 'package:reactive_forms/reactive_forms.dart'; import 'matrix_dropdown_base.dart'; @@ -89,33 +89,40 @@ class MatrixDynamicElement extends StatelessWidget { ) : null, children: [ - ...(matrix.columns?.toList() ?? []).map((column) { - final q = matrixDropdownColumnToQuestion(matrix, column); - final v = questionToValidators(q); + ...(matrix.columns?.toList() ?? []) + .map((column) { + final q = matrixDropdownColumnToQuestion(matrix, column); + final v = questionToValidators(q); + final element = SurveyConfiguration.of(context)! + .factory + .resolve(context, q, + configuration: + const ElementConfiguration(hasTitle: false)); - return TableCell( - verticalAlignment: TableCellVerticalAlignment.middle, - child: ReactiveNestedForm( - formGroup: c, - child: Builder( - builder: (context) { - final fg = ReactiveForm.of(context) as FormGroup; - final c = fg.control(column.name!); - //concat validators - // final newV = HashSet.of( - // [...c.validators, ...v]).toList(); - //TODO runner - c.setValidators(v); - return SurveyConfiguration.of(context)! - .factory - .resolve( - context, q, - configuration: const ElementConfiguration( - hasTitle: false)); - }, - ), - )); - }).toList(), + return element == null + ? null + : TableCell( + verticalAlignment: + TableCellVerticalAlignment.middle, + child: ReactiveNestedForm( + formGroup: c, + child: Builder( + builder: (context) { + final fg = + ReactiveForm.of(context) as FormGroup; + final c = fg.control(column.name!); + //concat validators + // final newV = HashSet.of( + // [...c.validators, ...v]).toList(); + //TODO runner + c.setValidators(v); + return element; + }, + ), + )); + }) + .toList() + .removeWhere((element) => element == null) as List, TableCell( child: SizedBox( child: Padding( diff --git a/lib/ui/survey_configuration.dart b/lib/ui/survey_configuration.dart index 54ca0faf..533bb9fc 100644 --- a/lib/ui/survey_configuration.dart +++ b/lib/ui/survey_configuration.dart @@ -1,11 +1,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_survey_js/ui/elements/question_title.dart'; import 'package:flutter_survey_js/ui/survey_element_factory.dart'; - import 'package:flutter_survey_js_model/flutter_survey_js_model.dart' as s; import 'package:reactive_forms/reactive_forms.dart'; -typedef SurveyElementBuilder = Widget Function( +typedef SurveyElementBuilder = Widget? Function( BuildContext context, s.Elementbase element, {ElementConfiguration? configuration}); typedef SurveyFormControlBuilder = AbstractControl Function( diff --git a/lib/ui/survey_element_factory.dart b/lib/ui/survey_element_factory.dart index cc299f53..d091ad83 100644 --- a/lib/ui/survey_element_factory.dart +++ b/lib/ui/survey_element_factory.dart @@ -1,12 +1,13 @@ import 'package:flutter/material.dart'; +import 'package:flutter_survey_js/data/condition/visibility_helper.dart'; import 'package:flutter_survey_js/ui/elements/boolean.dart'; import 'package:flutter_survey_js/ui/elements/comment.dart'; import 'package:flutter_survey_js/ui/elements/matrix_dropdown.dart'; import 'package:flutter_survey_js/ui/elements/panel.dart'; -import 'package:flutter_survey_js/ui/reactive/always_update_form_array.dart'; import 'package:flutter_survey_js/ui/reactive/reactive.dart'; import 'package:flutter_survey_js/ui/reactive/reactive_signature_string.dart'; import 'package:flutter_survey_js/ui/survey_configuration.dart'; +import 'package:flutter_survey_js/ui/survey_widget.dart'; import 'package:flutter_survey_js/ui/validators.dart'; import 'package:flutter_survey_js_model/flutter_survey_js_model.dart' as s; import 'package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart'; @@ -183,8 +184,15 @@ class SurveyElementFactory { } // resolve resolve widet from element - Widget resolve(BuildContext context, s.Elementbase element, + Widget? resolve(BuildContext context, s.Elementbase element, {ElementConfiguration? configuration}) { + if (element is s.Question) { + if (!VisibilityHelper().isElementVisible( + element: element, + surveyResponse: SurveyProvider.of(context).formGroup.value)) { + return null; + } + } var res = _map[element.type]; if (res == null) { //unsupported element diff --git a/lib/ui/survey_page_widget.dart b/lib/ui/survey_page_widget.dart index 75d57672..b89d05f8 100644 --- a/lib/ui/survey_page_widget.dart +++ b/lib/ui/survey_page_widget.dart @@ -1,10 +1,11 @@ import 'dart:math'; import 'package:flutter/material.dart'; -import 'package:flutter_survey_js/ui/survey_configuration.dart'; +import 'package:flutter_survey_js/data/condition/visibility_helper.dart'; import 'package:flutter_survey_js_model/flutter_survey_js_model.dart' as s; import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; +import '../survey.dart'; import 'panel_title.dart'; class SurveyPageWidget extends StatefulWidget { @@ -134,21 +135,38 @@ class SurveyPageWidgetState extends State { itemBuilder: (context, index) { if (index < widget.page.elementsOrQuestions!.length && index >= 0) { - return SurveyConfiguration.of(context)!.factory.resolve( - context, - widget - .page.elementsOrQuestions![index].realElement); + final surveyElement = SurveyConfiguration.of(context)! + .factory + .resolve( + context, + widget.page.elementsOrQuestions![index] + .realElement); + return surveyElement ?? + Container( + width: double.infinity, + ); } else { return Container( width: double.infinity, - // child: Image.asset( - // 'assets/images/decision.jpg', - // fit: BoxFit.fill, - // ), ); } }, separatorBuilder: (BuildContext context, int index) { + final SurveyQuestionsInner element = + widget.page.elements![index]; + if (element is s.Question) { + if (VisibilityHelper().isElementVisible( + element: element as s.Question, + surveyResponse: + SurveyProvider.of(context).formGroup.value)) { + return SurveyConfiguration.of(context)! + .separatorBuilder(context); + } else { + return Container( + width: double.infinity, + ); + } + } return SurveyConfiguration.of(context)! .separatorBuilder(context); }, diff --git a/test/data/condition/visibility_helper_test.dart b/test/data/condition/visibility_helper_test.dart new file mode 100644 index 00000000..e5676c53 --- /dev/null +++ b/test/data/condition/visibility_helper_test.dart @@ -0,0 +1,508 @@ +import 'package:flutter_survey_js/data/condition/visibility_helper.dart'; +import 'package:flutter_survey_js/survey.dart'; +import 'package:flutter_test/flutter_test.dart'; + +main() { + late VisibilityHelper testObject; + final regex = RegExp(r'{(.*?)}'); + + setUp(() { + testObject = VisibilityHelper(); + }); + + group('determineComparisonOperatorString', () { + test('identifies =', () { + final String visibleIf = "{kids}=3"; + final RegExpMatch matchedElementName = regex.firstMatch(visibleIf)!; + final comparisonOperator = testObject.determineComparisonOperatorString( + visibleIf: visibleIf, matchedElementName: matchedElementName); + expect(comparisonOperator, '='); + }); + test('identifies <', () { + final String visibleIf = "{kids}<3"; + final RegExpMatch matchedElementName = regex.firstMatch(visibleIf)!; + final comparisonOperator = testObject.determineComparisonOperatorString( + visibleIf: visibleIf, matchedElementName: matchedElementName); + expect(comparisonOperator, '<'); + }); + test('identifies <=', () { + final String visibleIf = "{kids}<=3"; + final RegExpMatch matchedElementName = regex.firstMatch(visibleIf)!; + final comparisonOperator = testObject.determineComparisonOperatorString( + visibleIf: visibleIf, matchedElementName: matchedElementName); + expect(comparisonOperator, '<='); + }); + test('identifies >', () { + final String visibleIf = "{kids}>3"; + final RegExpMatch matchedElementName = regex.firstMatch(visibleIf)!; + final comparisonOperator = testObject.determineComparisonOperatorString( + visibleIf: visibleIf, matchedElementName: matchedElementName); + expect(comparisonOperator, '>'); + }); + test('identifies >=', () { + final String visibleIf = "{kids}>=3"; + final RegExpMatch matchedElementName = regex.firstMatch(visibleIf)!; + final comparisonOperator = testObject.determineComparisonOperatorString( + visibleIf: visibleIf, matchedElementName: matchedElementName); + expect(comparisonOperator, '>='); + }); + }); + + group('determineRightOperand', () { + test('identifies int values', () { + final String visibleIf = "{kids}>=3"; + final RegExpMatch matchedElementName = regex.firstMatch(visibleIf)!; + final rightOperand = testObject.determineRightOperand( + visibleIf: visibleIf, + matchedElementName: matchedElementName, + comparisonOperatorLength: 2); + expect(rightOperand.runtimeType, int); + expect(rightOperand, 3); + }); + + test('identifies String values', () { + final String visibleIf = "{haveKids}='Yes'"; + final RegExpMatch matchedElementName = regex.firstMatch(visibleIf)!; + final rightOperand = testObject.determineRightOperand( + visibleIf: visibleIf, + matchedElementName: matchedElementName, + comparisonOperatorLength: 1); + expect(rightOperand.runtimeType, String); + expect(rightOperand, '\'Yes\''); + }); + }); + + const json = { + "showQuestionNumbers": "off", + "questions": [ + { + "type": "radiogroup", + "name": "haveKids", + "title": "Do you have any children?", + "isRequired": true, + "choices": ["Yes", "No"], + "colCount": 0 + }, + { + "type": "dropdown", + "name": "kids", + "title": "How many children do you have?", + "visibleIf": "{haveKids}='Yes'", + "isRequired": true, + "choices": [1, 2, 3, 4, 5] + }, + { + "type": "dropdown", + "name": "kid1Age", + "title": "The first child's age:", + "visibleIf": "{kids} >= 1", + "isRequired": true, + "choices": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16] + }, + { + "type": "dropdown", + "name": "kid2Age", + "title": "The second child's age:", + "visibleIf": "{kids} >= 2", + "isRequired": true, + "startWithNewLine": false, + "choices": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16] + }, + { + "type": "dropdown", + "name": "kid3Age", + "title": "The third child's age:", + "visibleIf": "{kids} >= 3", + "isRequired": true, + "startWithNewLine": false, + "choices": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16] + }, + { + "type": "dropdown", + "name": "kid4Age", + "title": "The fourth child's age:", + "visibleIf": "{kids} >= 4", + "isRequired": true, + "startWithNewLine": false, + "choices": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16] + }, + { + "type": "dropdown", + "name": "kid5Age", + "title": "The fifth child's age:", + "visibleIf": "{kids} >= 5", + "isRequired": true, + "startWithNewLine": false, + "choices": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16] + } + ] + }; + group('kids', () { + test('is not visible if `haveKids` is \'No\'', () { + final survey = surveyFromJson(json); + final Question kidsQuestion = survey?.questions + ?.firstWhere( + (element) => (element.realElement as Question).name == 'kids') + .realElement as Question; + final currentResponse = { + 'haveKids': 'No', + 'kids': null, + 'kid1Age': null, + 'kid2Age': null, + 'kid3Age': null, + 'kid4Age': null, + 'kid5Age': null + }; + expect( + testObject.isElementVisible( + element: kidsQuestion, surveyResponse: currentResponse), + false); + }); + + test('is visible if `haveKids` is \'Yes\'', () { + final survey = surveyFromJson(json); + final Question kidsQuestion = survey?.questions + ?.firstWhere( + (element) => (element.realElement as Question).name == 'kids') + .realElement as Question; + final currentResponse = { + 'haveKids': 'Yes', + 'kids': null, + 'kid1Age': null, + 'kid2Age': null, + 'kid3Age': null, + 'kid4Age': null, + 'kid5Age': null + }; + expect( + testObject.isElementVisible( + element: kidsQuestion, surveyResponse: currentResponse), + true); + }); + }); + + group('kid1Age', () { + test('is not visible if `kids` is `null`', () { + final survey = surveyFromJson(json); + final Question kidsQuestion = survey?.questions + ?.firstWhere( + (element) => (element.realElement as Question).name == 'kid1Age') + .realElement as Question; + final currentResponse = { + 'haveKids': 'Yes', + 'kids': null, + 'kid1Age': null, + 'kid2Age': null, + 'kid3Age': null, + 'kid4Age': null, + 'kid5Age': null + }; + expect( + testObject.isElementVisible( + element: kidsQuestion, surveyResponse: currentResponse), + false); + }); + + test('is not visible if `kids` is 0', () { + final survey = surveyFromJson(json); + final Question kidsQuestion = survey?.questions + ?.firstWhere( + (element) => (element.realElement as Question).name == 'kid1Age') + .realElement as Question; + final currentResponse = { + 'haveKids': 'Yes', + 'kids': 0, + 'kid1Age': null, + 'kid2Age': null, + 'kid3Age': null, + 'kid4Age': null, + 'kid5Age': null + }; + expect( + testObject.isElementVisible( + element: kidsQuestion, surveyResponse: currentResponse), + false); + }); + + test('is visible if `kids` is 1', () { + final survey = surveyFromJson(json); + final Question kidsQuestion = survey?.questions + ?.firstWhere( + (element) => (element.realElement as Question).name == 'kid1Age') + .realElement as Question; + final currentResponse = { + 'haveKids': 'Yes', + 'kids': 1, + 'kid1Age': null, + 'kid2Age': null, + 'kid3Age': null, + 'kid4Age': null, + 'kid5Age': null + }; + expect( + testObject.isElementVisible( + element: kidsQuestion, surveyResponse: currentResponse), + true); + }); + }); + + group('kid2Age', () { + test('is not visible if `kids` is `null`', () { + final survey = surveyFromJson(json); + final Question kidsQuestion = survey?.questions + ?.firstWhere( + (element) => (element.realElement as Question).name == 'kid2Age') + .realElement as Question; + final currentResponse = { + 'haveKids': 'Yes', + 'kids': null, + 'kid1Age': null, + 'kid2Age': null, + 'kid3Age': null, + 'kid4Age': null, + 'kid5Age': null + }; + expect( + testObject.isElementVisible( + element: kidsQuestion, surveyResponse: currentResponse), + false); + }); + + test('is not visible if `kids` is 1', () { + final survey = surveyFromJson(json); + final Question kidsQuestion = survey?.questions + ?.firstWhere( + (element) => (element.realElement as Question).name == 'kid2Age') + .realElement as Question; + final currentResponse = { + 'haveKids': 'Yes', + 'kids': 1, + 'kid1Age': null, + 'kid2Age': null, + 'kid3Age': null, + 'kid4Age': null, + 'kid5Age': null + }; + expect( + testObject.isElementVisible( + element: kidsQuestion, surveyResponse: currentResponse), + false); + }); + + test('is visible if `kids` is 2', () { + final survey = surveyFromJson(json); + final Question kidsQuestion = survey?.questions + ?.firstWhere( + (element) => (element.realElement as Question).name == 'kid2Age') + .realElement as Question; + final currentResponse = { + 'haveKids': 'Yes', + 'kids': 2, + 'kid1Age': null, + 'kid2Age': null, + 'kid3Age': null, + 'kid4Age': null, + 'kid5Age': null + }; + expect( + testObject.isElementVisible( + element: kidsQuestion, surveyResponse: currentResponse), + true); + }); + }); + + group('kid3Age', () { + test('is not visible if `kids` is `null`', () { + final survey = surveyFromJson(json); + final Question kidsQuestion = survey?.questions + ?.firstWhere( + (element) => (element.realElement as Question).name == 'kid3Age') + .realElement as Question; + final currentResponse = { + 'haveKids': 'Yes', + 'kids': null, + 'kid1Age': null, + 'kid2Age': null, + 'kid3Age': null, + 'kid4Age': null, + 'kid5Age': null + }; + expect( + testObject.isElementVisible( + element: kidsQuestion, surveyResponse: currentResponse), + false); + }); + + test('is not visible if `kids` is 2', () { + final survey = surveyFromJson(json); + final Question kidsQuestion = survey?.questions + ?.firstWhere( + (element) => (element.realElement as Question).name == 'kid3Age') + .realElement as Question; + final currentResponse = { + 'haveKids': 'Yes', + 'kids': 2, + 'kid1Age': null, + 'kid2Age': null, + 'kid3Age': null, + 'kid4Age': null, + 'kid5Age': null + }; + expect( + testObject.isElementVisible( + element: kidsQuestion, surveyResponse: currentResponse), + false); + }); + + test('is visible if `kids` is 3', () { + final survey = surveyFromJson(json); + final Question kidsQuestion = survey?.questions + ?.firstWhere( + (element) => (element.realElement as Question).name == 'kid3Age') + .realElement as Question; + final currentResponse = { + 'haveKids': 'Yes', + 'kids': 3, + 'kid1Age': null, + 'kid2Age': null, + 'kid3Age': null, + 'kid4Age': null, + 'kid5Age': null + }; + expect( + testObject.isElementVisible( + element: kidsQuestion, surveyResponse: currentResponse), + true); + }); + }); + + group('kid4Age', () { + test('is not visible if `kids` is `null`', () { + final survey = surveyFromJson(json); + final Question kidsQuestion = survey?.questions + ?.firstWhere( + (element) => (element.realElement as Question).name == 'kid4Age') + .realElement as Question; + final currentResponse = { + 'haveKids': 'Yes', + 'kids': null, + 'kid1Age': null, + 'kid2Age': null, + 'kid3Age': null, + 'kid4Age': null, + 'kid5Age': null + }; + expect( + testObject.isElementVisible( + element: kidsQuestion, surveyResponse: currentResponse), + false); + }); + + test('is not visible if `kids` is 3', () { + final survey = surveyFromJson(json); + final Question kidsQuestion = survey?.questions + ?.firstWhere( + (element) => (element.realElement as Question).name == 'kid4Age') + .realElement as Question; + final currentResponse = { + 'haveKids': 'Yes', + 'kids': 3, + 'kid1Age': null, + 'kid2Age': null, + 'kid3Age': null, + 'kid4Age': null, + 'kid5Age': null + }; + expect( + testObject.isElementVisible( + element: kidsQuestion, surveyResponse: currentResponse), + false); + }); + + test('is visible if `kids` is 4', () { + final survey = surveyFromJson(json); + final Question kidsQuestion = survey?.questions + ?.firstWhere( + (element) => (element.realElement as Question).name == 'kid4Age') + .realElement as Question; + final currentResponse = { + 'haveKids': 'Yes', + 'kids': 4, + 'kid1Age': null, + 'kid2Age': null, + 'kid3Age': null, + 'kid4Age': null, + 'kid5Age': null + }; + expect( + testObject.isElementVisible( + element: kidsQuestion, surveyResponse: currentResponse), + true); + }); + }); + + group('kid5Age', () { + test('is not visible if `kids` is `null`', () { + final survey = surveyFromJson(json); + final Question kidsQuestion = survey?.questions + ?.firstWhere( + (element) => (element.realElement as Question).name == 'kid5Age') + .realElement as Question; + final currentResponse = { + 'haveKids': 'Yes', + 'kids': null, + 'kid1Age': null, + 'kid2Age': null, + 'kid3Age': null, + 'kid4Age': null, + 'kid5Age': null + }; + expect( + testObject.isElementVisible( + element: kidsQuestion, surveyResponse: currentResponse), + false); + }); + + test('is not visible if `kids` is 4', () { + final survey = surveyFromJson(json); + final Question kidsQuestion = survey?.questions + ?.firstWhere( + (element) => (element.realElement as Question).name == 'kid5Age') + .realElement as Question; + final currentResponse = { + 'haveKids': 'Yes', + 'kids': 4, + 'kid1Age': null, + 'kid2Age': null, + 'kid3Age': null, + 'kid4Age': null, + 'kid5Age': null + }; + expect( + testObject.isElementVisible( + element: kidsQuestion, surveyResponse: currentResponse), + false); + }); + + test('is visible if `kids` is 5', () { + final survey = surveyFromJson(json); + final Question kidsQuestion = survey?.questions + ?.firstWhere( + (element) => (element.realElement as Question).name == 'kid5Age') + .realElement as Question; + final currentResponse = { + 'haveKids': 'Yes', + 'kids': 5, + 'kid1Age': null, + 'kid2Age': null, + 'kid3Age': null, + 'kid4Age': null, + 'kid5Age': null + }; + expect( + testObject.isElementVisible( + element: kidsQuestion, surveyResponse: currentResponse), + true); + }); + }); +}