diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index d423ebb078..495fa527bb 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -9,7 +9,7 @@ repositories { } dependencies { - implementation("com.diffplug.spotless:spotless-plugin-gradle:6.6.0") + implementation("com.diffplug.spotless:spotless-plugin-gradle:6.21.0") implementation("com.android.tools.build:gradle:8.1.1") diff --git a/buildSrc/src/main/kotlin/SpotlessConfig.kt b/buildSrc/src/main/kotlin/SpotlessConfig.kt index 6f699f22bc..9869d97e61 100644 --- a/buildSrc/src/main/kotlin/SpotlessConfig.kt +++ b/buildSrc/src/main/kotlin/SpotlessConfig.kt @@ -19,7 +19,7 @@ import org.gradle.kotlin.dsl.apply import org.gradle.kotlin.dsl.configure fun Project.configureSpotless() { - val ktlintVersion = "0.41.0" + val ktlintVersion = "0.50.0" val ktlintOptions = mapOf("indent_size" to "2", "continuation_indent_size" to "2") apply(plugin = Plugins.BuildPlugins.spotless) configure { @@ -32,10 +32,10 @@ fun Project.configureSpotless() { ktfmt().googleStyle() licenseHeaderFile( "${project.rootProject.projectDir}/license-header.txt", - "package|import|class|object|sealed|open|interface|abstract " + "package|import|class|object|sealed|open|interface|abstract ", // It is necessary to tell spotless the top level of a file in order to apply config to it // See: https://github.com/diffplug/spotless/issues/135 - ) + ) toggleOffOn() } kotlinGradle { @@ -49,16 +49,5 @@ fun Project.configureSpotless() { prettier(mapOf("prettier" to "2.0.5", "@prettier/plugin-xml" to "0.13.0")) .config(mapOf("parser" to "xml", "tabWidth" to 4)) } - // Creates one off SpotlessApply task for generated files - com.diffplug.gradle.spotless.KotlinExtension(this).apply { - target("**/*_Generated.kt") - ktlint(ktlintVersion).userData(ktlintOptions) - ktfmt().googleStyle() - licenseHeaderFile( - "${project.rootProject.projectDir}/license-header.txt", - "package|import|class|object|sealed|open|interface|abstract " - ) - createIndependentApplyTask("spotlessGenerated") - } } } diff --git a/datacapture/src/main/java/com/google/android/fhir/datacapture/expressions/EnabledAnswerOptionsEvaluator.kt b/datacapture/src/main/java/com/google/android/fhir/datacapture/expressions/EnabledAnswerOptionsEvaluator.kt index fa11cca5ab..73a46304cc 100644 --- a/datacapture/src/main/java/com/google/android/fhir/datacapture/expressions/EnabledAnswerOptionsEvaluator.kt +++ b/datacapture/src/main/java/com/google/android/fhir/datacapture/expressions/EnabledAnswerOptionsEvaluator.kt @@ -48,7 +48,7 @@ import org.hl7.fhir.r4.model.ValueSet * @param questionnaireResponse the [QuestionnaireResponse] related to the [Questionnaire] * @param xFhirQueryResolver the [XFhirQueryResolver] to resolve resources based on the X-FHIR-Query * @param externalValueSetResolver the [ExternalAnswerValueSetResolver] to resolve value sets - * externally/outside of the [Questionnaire] + * externally/outside of the [Questionnaire] * @param questionnaireItemParentMap the [Map] of items parent * @param questionnaireLaunchContextMap the [Map] of launchContext names to their resource values */ @@ -68,7 +68,7 @@ internal class EnabledAnswerOptionsEvaluator( questionnaire, questionnaireResponse, questionnaireItemParentMap, - questionnaireLaunchContextMap + questionnaireLaunchContextMap, ) private val answerValueSetMap = @@ -94,13 +94,19 @@ internal class EnabledAnswerOptionsEvaluator( questionnaireResponseItem: QuestionnaireResponseItemComponent, ): Pair< List, - List + List, > { + val resolvedAnswerOptions = + answerOptions( + questionnaireItem, + questionnaireResponseItem, + questionnaireResponse, + questionnaireItemParentMap, + ) - val resolvedAnswerOptions = answerOptions(questionnaireItem) - - if (questionnaireItem.answerOptionsToggleExpressions.isEmpty()) + if (questionnaireItem.answerOptionsToggleExpressions.isEmpty()) { return Pair(resolvedAnswerOptions, emptyList()) + } val enabledQuestionnaireAnswerOptions = evaluateAnswerOptionsToggleExpressions( @@ -121,29 +127,37 @@ internal class EnabledAnswerOptionsEvaluator( /** * In a `choice` or `open-choice` type question, the answer options are defined in one of the * three elements in the questionnaire: - * * - `Questionnaire.item.answerOption`: a list of permitted answers to the question * - `Questionnaire.item.answerValueSet`: a reference to a value set containing a list of - * permitted answers to the question + * permitted answers to the question * - `Extension answer-expression`: an expression based extension which defines the x-fhir-query - * or fhirpath to evaluate permitted answer options + * or fhirpath to evaluate permitted answer options * * Returns the answer options defined in one of the sources above. If the answer options are * defined in `Questionnaire.item.answerValueSet`, the answer value set will be expanded. */ private suspend fun answerOptions( questionnaireItem: QuestionnaireItemComponent, + questionnaireResponseItem: QuestionnaireResponseItemComponent, + questionnaireResponse: QuestionnaireResponse, + questionnaireItemParentMap: Map, ): List = when { questionnaireItem.answerOption.isNotEmpty() -> questionnaireItem.answerOption !questionnaireItem.answerValueSet.isNullOrEmpty() -> resolveAnswerValueSet(questionnaireItem.answerValueSet) - questionnaireItem.answerExpression != null -> resolveAnswerExpression(questionnaireItem) + questionnaireItem.answerExpression != null -> + resolveAnswerExpression( + questionnaireItem, + questionnaireResponseItem, + questionnaireResponse, + questionnaireItemParentMap, + ) else -> emptyList() } private suspend fun resolveAnswerValueSet( - uri: String + uri: String, ): List { // If cache hit, return it if (answerValueSetMap.contains(uri)) { @@ -164,7 +178,7 @@ internal class EnabledAnswerOptionsEvaluator( .filterNot { it.abstract || it.inactive } .map { component -> Questionnaire.QuestionnaireItemAnswerOptionComponent( - Coding(component.system, component.code, component.display) + Coding(component.system, component.code, component.display), ) } } @@ -185,6 +199,9 @@ internal class EnabledAnswerOptionsEvaluator( // https://build.fhir.org/ig/HL7/sdc/expressions.html#x-fhir-query-enhancements private suspend fun resolveAnswerExpression( item: QuestionnaireItemComponent, + responseItem: QuestionnaireResponseItemComponent, + questionnaireResponse: QuestionnaireResponse, + questionnaireItemParentMap: Map, ): List { // Check cache first for database queries val answerExpression = item.answerExpression ?: return emptyList() @@ -208,16 +225,16 @@ internal class EnabledAnswerOptionsEvaluator( options } ?: error( - "XFhirQueryResolver cannot be null. Please provide the XFhirQueryResolver via DataCaptureConfig." + "XFhirQueryResolver cannot be null. Please provide the XFhirQueryResolver via DataCaptureConfig.", ) } answerExpression.isFhirPath -> { - val data = fhirPathEngine.evaluate(questionnaireResponse, answerExpression.expression) + val data = expressionEvaluator.evaluateExpression(item, responseItem, answerExpression) item.extractAnswerOptions(data) } else -> throw UnsupportedOperationException( - "${answerExpression.language} not supported for answer-expression yet" + "${answerExpression.language} not supported for answer-expression yet", ) } } @@ -232,18 +249,19 @@ internal class EnabledAnswerOptionsEvaluator( .map { val (expression, toggleOptions) = it val evaluationResult = - if (expression.isFhirPath) + if (expression.isFhirPath) { fhirPathEngine.convertToBoolean( expressionEvaluator.evaluateExpression( item, questionnaireResponseItem, expression, - ) + ), ) - else + } else { throw UnsupportedOperationException( - "${expression.language} not supported yet for answer-options-toggle-expression" + "${expression.language} not supported yet for answer-options-toggle-expression", ) + } evaluationResult to toggleOptions } .partition { it.first } diff --git a/datacapture/src/test/java/com/google/android/fhir/datacapture/QuestionnaireViewModelTest.kt b/datacapture/src/test/java/com/google/android/fhir/datacapture/QuestionnaireViewModelTest.kt index b6a4c190d0..e24202e82b 100644 --- a/datacapture/src/test/java/com/google/android/fhir/datacapture/QuestionnaireViewModelTest.kt +++ b/datacapture/src/test/java/com/google/android/fhir/datacapture/QuestionnaireViewModelTest.kt @@ -141,8 +141,10 @@ class QuestionnaireViewModelTest { state = SavedStateHandle() check( ApplicationProvider.getApplicationContext() - is DataCaptureConfig.Provider - ) { "Few tests require a custom application class that implements DataCaptureConfig.Provider" } + is DataCaptureConfig.Provider, + ) { + "Few tests require a custom application class that implements DataCaptureConfig.Provider" + } ReflectionHelpers.setStaticField(DataCapture::class.java, "configuration", null) } @@ -160,7 +162,7 @@ class QuestionnaireViewModelTest { assertThat(errorMessage) .isEqualTo( - "Neither EXTRA_QUESTIONNAIRE_JSON_URI nor EXTRA_QUESTIONNAIRE_JSON_STRING is supplied." + "Neither EXTRA_QUESTIONNAIRE_JSON_URI nor EXTRA_QUESTIONNAIRE_JSON_STRING is supplied.", ) } @@ -177,7 +179,7 @@ class QuestionnaireViewModelTest { viewModel.getQuestionnaireResponse(), QuestionnaireResponse().apply { this.questionnaire = "http://www.sample-org/FHIR/Resources/Questionnaire/a-questionnaire" - } + }, ) } @@ -191,7 +193,7 @@ class QuestionnaireViewModelTest { linkId = "a-link-id" text = "Yes or no?" type = Questionnaire.QuestionnaireItemType.BOOLEAN - } + }, ) } @@ -204,9 +206,9 @@ class QuestionnaireViewModelTest { QuestionnaireResponse.QuestionnaireResponseItemComponent().apply { linkId = "a-link-id" text = "Yes or no?" - } + }, ) - } + }, ) } @@ -225,9 +227,9 @@ class QuestionnaireViewModelTest { linkId = "another-link-id" text = "Name?" type = Questionnaire.QuestionnaireItemType.STRING - } + }, ) - } + }, ) } @@ -244,11 +246,11 @@ class QuestionnaireViewModelTest { QuestionnaireResponse.QuestionnaireResponseItemComponent().apply { linkId = "another-link-id" text = "Name?" - } + }, ) - } + }, ) - } + }, ) } @@ -261,7 +263,7 @@ class QuestionnaireViewModelTest { Questionnaire.QuestionnaireItemComponent().apply { linkId = "a-link-id" text = "Basic question" - } + }, ) } @@ -269,7 +271,7 @@ class QuestionnaireViewModelTest { QuestionnaireResponse().apply { id = "a-questionnaire-response" addItem( - QuestionnaireResponse.QuestionnaireResponseItemComponent().apply { linkId = "a-link-id" } + QuestionnaireResponse.QuestionnaireResponseItemComponent().apply { linkId = "a-link-id" }, ) } @@ -296,9 +298,9 @@ class QuestionnaireViewModelTest { mutableListOf( Questionnaire.QuestionnaireItemInitialComponent().apply { value = BooleanType(false) - } + }, ) - } + }, ) } @@ -314,11 +316,11 @@ class QuestionnaireViewModelTest { addAnswer( QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent().apply { value = BooleanType(false) - } + }, ) - } + }, ) - } + }, ) } @@ -334,9 +336,9 @@ class QuestionnaireViewModelTest { type = Questionnaire.QuestionnaireItemType.GROUP initial = mutableListOf( - Questionnaire.QuestionnaireItemInitialComponent().setValue(BooleanType(true)) + Questionnaire.QuestionnaireItemInitialComponent().setValue(BooleanType(true)), ) - } + }, ) } @@ -346,7 +348,7 @@ class QuestionnaireViewModelTest { assertThat(errorMessage) .isEqualTo( - "Questionnaire item a-link-id has initial value(s) and is a group or display item. See rule que-8 at https://www.hl7.org/fhir/questionnaire-definitions.html#Questionnaire.item.initial." + "Questionnaire item a-link-id has initial value(s) and is a group or display item. See rule que-8 at https://www.hl7.org/fhir/questionnaire-definitions.html#Questionnaire.item.initial.", ) } @@ -362,9 +364,9 @@ class QuestionnaireViewModelTest { type = Questionnaire.QuestionnaireItemType.DISPLAY initial = mutableListOf( - Questionnaire.QuestionnaireItemInitialComponent().setValue(BooleanType(true)) + Questionnaire.QuestionnaireItemInitialComponent().setValue(BooleanType(true)), ) - } + }, ) } @@ -374,7 +376,7 @@ class QuestionnaireViewModelTest { assertThat(errorMessage) .isEqualTo( - "Questionnaire item a-link-id has initial value(s) and is a group or display item. See rule que-8 at https://www.hl7.org/fhir/questionnaire-definitions.html#Questionnaire.item.initial." + "Questionnaire item a-link-id has initial value(s) and is a group or display item. See rule que-8 at https://www.hl7.org/fhir/questionnaire-definitions.html#Questionnaire.item.initial.", ) } @@ -392,9 +394,9 @@ class QuestionnaireViewModelTest { initial = mutableListOf( Questionnaire.QuestionnaireItemInitialComponent().setValue(BooleanType(true)), - Questionnaire.QuestionnaireItemInitialComponent().setValue(BooleanType(true)) + Questionnaire.QuestionnaireItemInitialComponent().setValue(BooleanType(true)), ) - } + }, ) } @@ -410,16 +412,16 @@ class QuestionnaireViewModelTest { addAnswer( QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent().apply { value = BooleanType(true) - } + }, ) addAnswer( QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent().apply { value = BooleanType(true) - } + }, ) - } + }, ) - } + }, ) } @@ -437,9 +439,9 @@ class QuestionnaireViewModelTest { initial = mutableListOf( Questionnaire.QuestionnaireItemInitialComponent().setValue(BooleanType(true)), - Questionnaire.QuestionnaireItemInitialComponent().setValue(BooleanType(true)) + Questionnaire.QuestionnaireItemInitialComponent().setValue(BooleanType(true)), ) - } + }, ) } @@ -449,7 +451,7 @@ class QuestionnaireViewModelTest { assertThat(errorMessage) .isEqualTo( - "Questionnaire item a-link-id can only have multiple initial values for repeating items. See rule que-13 at https://www.hl7.org/fhir/questionnaire-definitions.html#Questionnaire.item.initial." + "Questionnaire item a-link-id can only have multiple initial values for repeating items. See rule que-13 at https://www.hl7.org/fhir/questionnaire-definitions.html#Questionnaire.item.initial.", ) } @@ -498,7 +500,7 @@ class QuestionnaireViewModelTest { assertThat(errorMessage) .isEqualTo( - "Mismatching Questionnaire http://www.sample-org/FHIR/Resources/Questionnaire/questionnaire-1 and QuestionnaireResponse (for Questionnaire Questionnaire/a-questionnaire)" + "Mismatching Questionnaire http://www.sample-org/FHIR/Resources/Questionnaire/questionnaire-1 and QuestionnaireResponse (for Questionnaire Questionnaire/a-questionnaire)", ) } @@ -512,7 +514,7 @@ class QuestionnaireViewModelTest { linkId = "a-link-id" text = "Basic question" type = Questionnaire.QuestionnaireItemType.BOOLEAN - } + }, ) } val questionnaireResponse = @@ -524,9 +526,9 @@ class QuestionnaireViewModelTest { addAnswer( QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent().apply { value = BooleanType(true) - } + }, ) - } + }, ) } @@ -537,13 +539,13 @@ class QuestionnaireViewModelTest { QuestionnaireResponse.QuestionnaireResponseItemComponent().apply { linkId = "a-link-id" text = "Basic question" - } + }, ) } assertResourceEquals( createQuestionnaireViewModel(questionnaire, questionnaireResponse).getQuestionnaireResponse(), - expectedQuestionnaireResponse + expectedQuestionnaireResponse, ) } @@ -557,7 +559,7 @@ class QuestionnaireViewModelTest { linkId = "a-link-id" text = "Basic question" type = Questionnaire.QuestionnaireItemType.BOOLEAN - } + }, ) } val questionnaireResponse = @@ -569,9 +571,9 @@ class QuestionnaireViewModelTest { addAnswer( QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent().apply { value = StringType("true") - } + }, ) - } + }, ) } @@ -596,7 +598,7 @@ class QuestionnaireViewModelTest { text = "Basic question which allows multiple answers" type = Questionnaire.QuestionnaireItemType.STRING repeats = true - } + }, ) } val questionnaireResponse = @@ -609,14 +611,14 @@ class QuestionnaireViewModelTest { addAnswer( QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent().apply { value = StringType("string 1") - } + }, ) addAnswer( QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent().apply { value = StringType("string 2") - } + }, ) - } + }, ) } @@ -636,7 +638,7 @@ class QuestionnaireViewModelTest { text = "Basic question" type = Questionnaire.QuestionnaireItemType.BOOLEAN repeats = false - } + }, ) } val questionnaireResponse = @@ -648,14 +650,14 @@ class QuestionnaireViewModelTest { addAnswer( QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent().apply { value = BooleanType(true) - } + }, ) addAnswer( QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent().apply { value = BooleanType(false) - } + }, ) - } + }, ) } @@ -684,9 +686,9 @@ class QuestionnaireViewModelTest { linkId = "another-link-id" text = "Is this true?" type = Questionnaire.QuestionnaireItemType.BOOLEAN - } + }, ) - } + }, ) } val questionnaireResponse = @@ -703,11 +705,11 @@ class QuestionnaireViewModelTest { addAnswer( QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent().apply { value = BooleanType(true) - } + }, ) - } + }, ) - } + }, ) } @@ -731,9 +733,9 @@ class QuestionnaireViewModelTest { linkId = "another-link-id" text = "Name?" type = Questionnaire.QuestionnaireItemType.STRING - } + }, ) - } + }, ) } val questionnaireResponse = @@ -753,13 +755,13 @@ class QuestionnaireViewModelTest { addAnswer( QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent().apply { value = StringType("a-name") - } + }, ) - } + }, ) - } + }, ) - } + }, ) } @@ -782,9 +784,9 @@ class QuestionnaireViewModelTest { Questionnaire.QuestionnaireItemComponent().apply { linkId = "nested-question" type = Questionnaire.QuestionnaireItemType.BOOLEAN - } + }, ) - } + }, ) } val questionnaireResponse = @@ -799,11 +801,11 @@ class QuestionnaireViewModelTest { addAnswer( QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent().apply { value = BooleanType(true) - } + }, ) - } + }, ) - } + }, ) addItem( QuestionnaireResponse.QuestionnaireResponseItemComponent().apply { @@ -814,11 +816,11 @@ class QuestionnaireViewModelTest { addAnswer( QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent().apply { value = BooleanType(false) - } + }, ) - } + }, ) - } + }, ) } @@ -843,9 +845,9 @@ class QuestionnaireViewModelTest { answerOption = listOf( Questionnaire.QuestionnaireItemAnswerOptionComponent(testOption1), - Questionnaire.QuestionnaireItemAnswerOptionComponent(testOption2) + Questionnaire.QuestionnaireItemAnswerOptionComponent(testOption2), ) - } + }, ) } val questionnaireResponse = @@ -858,9 +860,9 @@ class QuestionnaireViewModelTest { addAnswer( QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent().apply { value = testOption1 - } + }, ) - } + }, ) } @@ -881,9 +883,9 @@ class QuestionnaireViewModelTest { type = Questionnaire.QuestionnaireItemType.GROUP initial = mutableListOf( - Questionnaire.QuestionnaireItemInitialComponent().apply { value = valueCoding } + Questionnaire.QuestionnaireItemInitialComponent().apply { value = valueCoding }, ) - } + }, ) } val questionnaireResponse = @@ -895,9 +897,9 @@ class QuestionnaireViewModelTest { addAnswer( QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent().apply { value = StringType("") - } + }, ) - } + }, ) } createQuestionnaireViewModel(questionnaire, questionnaireResponse) @@ -929,7 +931,8 @@ class QuestionnaireViewModelTest { } ] } - """.trimIndent() + """ + .trimIndent() val questionnaireResponseString = """ @@ -941,7 +944,8 @@ class QuestionnaireViewModelTest { } ] } - """.trimIndent() + """ + .trimIndent() val expectedResponseString = """ @@ -963,7 +967,8 @@ class QuestionnaireViewModelTest { } ] } - """.trimIndent() + """ + .trimIndent() state.set(EXTRA_QUESTIONNAIRE_JSON_STRING, questionnaireString) state.set(EXTRA_QUESTIONNAIRE_RESPONSE_JSON_STRING, questionnaireResponseString) @@ -1008,7 +1013,8 @@ class QuestionnaireViewModelTest { } ] } - """.trimIndent() + """ + .trimIndent() val questionnaireResponseString = """ @@ -1045,7 +1051,8 @@ class QuestionnaireViewModelTest { } ] } - """.trimIndent() + """ + .trimIndent() val expectedResponseString = """ @@ -1086,7 +1093,8 @@ class QuestionnaireViewModelTest { } ] } - """.trimIndent() + """ + .trimIndent() state.set(EXTRA_QUESTIONNAIRE_JSON_STRING, questionnaireString) state.set(EXTRA_QUESTIONNAIRE_RESPONSE_JSON_STRING, questionnaireResponseString) @@ -1120,9 +1128,9 @@ class QuestionnaireViewModelTest { linkId = "another-link-id" text = "Name?" type = Questionnaire.QuestionnaireItemType.STRING - } + }, ) - } + }, ) } @@ -1154,7 +1162,7 @@ class QuestionnaireViewModelTest { text = "Name?" type = Questionnaire.QuestionnaireItemType.STRING required = true - } + }, ) } val viewModel = createQuestionnaireViewModel(questionnaire) @@ -1176,7 +1184,7 @@ class QuestionnaireViewModelTest { text = "Name?" type = Questionnaire.QuestionnaireItemType.STRING required = true - } + }, ) } @@ -1186,7 +1194,7 @@ class QuestionnaireViewModelTest { question.clearAnswer() assertThat( - viewModel.getQuestionnaireItemViewItemList().single().asQuestion().validationResult + viewModel.getQuestionnaireItemViewItemList().single().asQuestion().validationResult, ) .isEqualTo(Invalid(listOf("Missing answer for required field."))) } @@ -1202,7 +1210,7 @@ class QuestionnaireViewModelTest { linkId = "question-1" type = Questionnaire.QuestionnaireItemType.BOOLEAN addInitial().apply { value = BooleanType(false) } - } + }, ) addItem( Questionnaire.QuestionnaireItemComponent().apply { @@ -1213,7 +1221,7 @@ class QuestionnaireViewModelTest { question = "question-1" operator = Questionnaire.QuestionnaireItemOperator.EQUAL } - } + }, ) } val viewModel = createQuestionnaireViewModel(questionnaire) @@ -1223,7 +1231,8 @@ class QuestionnaireViewModelTest { .getQuestionnaireItemViewItemList() .single() .asQuestion() - .questionnaireItem.linkId + .questionnaireItem + .linkId, ) .isEqualTo("question-1") } @@ -1239,7 +1248,7 @@ class QuestionnaireViewModelTest { linkId = "question-1" type = Questionnaire.QuestionnaireItemType.BOOLEAN addInitial().apply { value = BooleanType(true) } - } + }, ) addItem( Questionnaire.QuestionnaireItemComponent().apply { @@ -1250,7 +1259,7 @@ class QuestionnaireViewModelTest { question = "question-1" operator = Questionnaire.QuestionnaireItemOperator.EQUAL } - } + }, ) } val viewModel = createQuestionnaireViewModel(questionnaire) @@ -1276,7 +1285,7 @@ class QuestionnaireViewModelTest { text = "a question" type = Questionnaire.QuestionnaireItemType.BOOLEAN addExtension(hiddenExtension) - } + }, ) } @@ -1302,7 +1311,7 @@ class QuestionnaireViewModelTest { setValue(BooleanType(false)) } addInitial().apply { value = BooleanType(true) } - } + }, ) } val serializedQuestionnaire = printer.encodeResourceToString(questionnaire) @@ -1315,7 +1324,8 @@ class QuestionnaireViewModelTest { .getQuestionnaireItemViewItemList() .single() .asQuestion() - .questionnaireItem.linkId + .questionnaireItem + .linkId, ) .isEqualTo("a-boolean-item-1") } @@ -1342,9 +1352,9 @@ class QuestionnaireViewModelTest { linkId = "page1-1" type = Questionnaire.QuestionnaireItemType.BOOLEAN text = "Question on page 1" - } + }, ) - } + }, ) addItem( Questionnaire.QuestionnaireItemComponent().apply { @@ -1356,9 +1366,9 @@ class QuestionnaireViewModelTest { linkId = "page2-1" type = Questionnaire.QuestionnaireItemType.BOOLEAN text = "Question on page 2" - } + }, ) - } + }, ) } val viewModel = createQuestionnaireViewModel(questionnaire) @@ -1370,10 +1380,10 @@ class QuestionnaireViewModelTest { pages = listOf( QuestionnairePage(0, enabled = true, hidden = false), - QuestionnairePage(1, enabled = true, hidden = false) + QuestionnairePage(1, enabled = true, hidden = false), ), - currentPageIndex = 0 - ) + currentPageIndex = 0, + ), ) assertThat(state.items).hasSize(2) state.items[0].asQuestion().questionnaireItem.let { groupItem -> @@ -1402,9 +1412,9 @@ class QuestionnaireViewModelTest { linkId = "page1-1" type = Questionnaire.QuestionnaireItemType.BOOLEAN text = "Question on page 1" - } + }, ) - } + }, ) addItem( Questionnaire.QuestionnaireItemComponent().apply { @@ -1416,9 +1426,9 @@ class QuestionnaireViewModelTest { linkId = "page2-1" type = Questionnaire.QuestionnaireItemType.BOOLEAN text = "Question on page 2" - } + }, ) - } + }, ) } val viewModel = createQuestionnaireViewModel(questionnaire) @@ -1426,7 +1436,7 @@ class QuestionnaireViewModelTest { viewModel.goToNextPage() assertThat( - (viewModel.questionnaireStateFlow.value.displayMode as DisplayMode.EditMode).pagination + (viewModel.questionnaireStateFlow.value.displayMode as DisplayMode.EditMode).pagination, ) .isEqualTo( QuestionnairePagination( @@ -1434,11 +1444,11 @@ class QuestionnaireViewModelTest { pages = listOf( QuestionnairePage(0, enabled = true, hidden = false), - QuestionnairePage(1, enabled = true, hidden = false) + QuestionnairePage(1, enabled = true, hidden = false), ), currentPageIndex = 1, - showSubmitButton = true - ) + showSubmitButton = true, + ), ) } } @@ -1458,9 +1468,9 @@ class QuestionnaireViewModelTest { linkId = "page1-1" type = Questionnaire.QuestionnaireItemType.BOOLEAN text = "Question on page 1" - } + }, ) - } + }, ) addItem( Questionnaire.QuestionnaireItemComponent().apply { @@ -1472,9 +1482,9 @@ class QuestionnaireViewModelTest { linkId = "page2-1" type = Questionnaire.QuestionnaireItemType.BOOLEAN text = "Question on page 2" - } + }, ) - } + }, ) } val viewModel = createQuestionnaireViewModel(questionnaire) @@ -1483,7 +1493,7 @@ class QuestionnaireViewModelTest { viewModel.goToPreviousPage() assertThat( - (viewModel.questionnaireStateFlow.value.displayMode as DisplayMode.EditMode).pagination + (viewModel.questionnaireStateFlow.value.displayMode as DisplayMode.EditMode).pagination, ) .isEqualTo( QuestionnairePagination( @@ -1491,10 +1501,10 @@ class QuestionnaireViewModelTest { pages = listOf( QuestionnairePage(0, enabled = true, hidden = false), - QuestionnairePage(1, enabled = true, hidden = false) + QuestionnairePage(1, enabled = true, hidden = false), ), - currentPageIndex = 0 - ) + currentPageIndex = 0, + ), ) } } @@ -1514,9 +1524,9 @@ class QuestionnaireViewModelTest { linkId = "page1-1" type = Questionnaire.QuestionnaireItemType.BOOLEAN text = "Question on page 1" - } + }, ) - } + }, ) addItem( Questionnaire.QuestionnaireItemComponent().apply { @@ -1528,14 +1538,14 @@ class QuestionnaireViewModelTest { linkId = "page2-1" type = Questionnaire.QuestionnaireItemType.BOOLEAN text = "Question on page 2" - } + }, ) addEnableWhen().apply { answer = BooleanType(true) question = "page1-1" operator = Questionnaire.QuestionnaireItemOperator.EQUAL } - } + }, ) addItem( Questionnaire.QuestionnaireItemComponent().apply { @@ -1547,16 +1557,16 @@ class QuestionnaireViewModelTest { linkId = "page3-1" type = Questionnaire.QuestionnaireItemType.BOOLEAN text = "Question on page 3" - } + }, ) - } + }, ) } val viewModel = createQuestionnaireViewModel(questionnaire) viewModel.runViewModelBlocking { viewModel.goToNextPage() assertThat( - (viewModel.questionnaireStateFlow.value.displayMode as DisplayMode.EditMode).pagination + (viewModel.questionnaireStateFlow.value.displayMode as DisplayMode.EditMode).pagination, ) .isEqualTo( QuestionnairePagination( @@ -1568,8 +1578,8 @@ class QuestionnaireViewModelTest { QuestionnairePage(2, enabled = true, hidden = false), ), currentPageIndex = 2, - showSubmitButton = true - ) + showSubmitButton = true, + ), ) } } @@ -1590,9 +1600,9 @@ class QuestionnaireViewModelTest { linkId = "page1-1" type = Questionnaire.QuestionnaireItemType.BOOLEAN text = "Question on page 1" - } + }, ) - } + }, ) addItem( Questionnaire.QuestionnaireItemComponent().apply { @@ -1604,9 +1614,9 @@ class QuestionnaireViewModelTest { linkId = "page2-1" type = Questionnaire.QuestionnaireItemType.BOOLEAN text = "Question on page 2" - } + }, ) - } + }, ) addItem( Questionnaire.QuestionnaireItemComponent().apply { @@ -1618,15 +1628,15 @@ class QuestionnaireViewModelTest { linkId = "page3-1" type = Questionnaire.QuestionnaireItemType.BOOLEAN text = "Question on page 3" - } + }, ) - } + }, ) } val viewModel = createQuestionnaireViewModel(questionnaire) viewModel.runViewModelBlocking { assertThat( - (viewModel.questionnaireStateFlow.value.displayMode as DisplayMode.EditMode).pagination + (viewModel.questionnaireStateFlow.value.displayMode as DisplayMode.EditMode).pagination, ) .isEqualTo( QuestionnairePagination( @@ -1635,10 +1645,10 @@ class QuestionnaireViewModelTest { listOf( QuestionnairePage(0, enabled = true, hidden = true), QuestionnairePage(1, enabled = true, hidden = false), - QuestionnairePage(2, enabled = true, hidden = false) + QuestionnairePage(2, enabled = true, hidden = false), ), - currentPageIndex = 1 - ) + currentPageIndex = 1, + ), ) } } @@ -1658,9 +1668,9 @@ class QuestionnaireViewModelTest { linkId = "page1-1" type = Questionnaire.QuestionnaireItemType.BOOLEAN text = "Question on page 1" - } + }, ) - } + }, ) addItem( Questionnaire.QuestionnaireItemComponent().apply { @@ -1673,9 +1683,9 @@ class QuestionnaireViewModelTest { linkId = "page2-1" type = Questionnaire.QuestionnaireItemType.BOOLEAN text = "Question on page 2" - } + }, ) - } + }, ) addItem( Questionnaire.QuestionnaireItemComponent().apply { @@ -1687,16 +1697,16 @@ class QuestionnaireViewModelTest { linkId = "page3-1" type = Questionnaire.QuestionnaireItemType.BOOLEAN text = "Question on page 3" - } + }, ) - } + }, ) } val viewModel = createQuestionnaireViewModel(questionnaire) viewModel.runViewModelBlocking { viewModel.goToNextPage() assertThat( - (viewModel.questionnaireStateFlow.value.displayMode as DisplayMode.EditMode).pagination + (viewModel.questionnaireStateFlow.value.displayMode as DisplayMode.EditMode).pagination, ) .isEqualTo( QuestionnairePagination( @@ -1705,11 +1715,11 @@ class QuestionnaireViewModelTest { listOf( QuestionnairePage(0, enabled = true, hidden = false), QuestionnairePage(1, enabled = true, hidden = true), - QuestionnairePage(2, enabled = true, hidden = false) + QuestionnairePage(2, enabled = true, hidden = false), ), currentPageIndex = 2, - showSubmitButton = true - ) + showSubmitButton = true, + ), ) } } @@ -1741,9 +1751,9 @@ class QuestionnaireViewModelTest { linkId = "page1-1" type = Questionnaire.QuestionnaireItemType.BOOLEAN text = "Question on page 1" - } + }, ) - } + }, ) addItem( Questionnaire.QuestionnaireItemComponent().apply { @@ -1755,9 +1765,9 @@ class QuestionnaireViewModelTest { linkId = "page2-1" type = Questionnaire.QuestionnaireItemType.BOOLEAN text = "Question on page 2" - } + }, ) - } + }, ) } val viewModel = createQuestionnaireViewModel(questionnaire) @@ -1765,15 +1775,15 @@ class QuestionnaireViewModelTest { viewModel.goToNextPage() assertThat(questionnaire.entryMode).isEqualTo(EntryMode.PRIOR_EDIT) assertThat( - (viewModel.questionnaireStateFlow.value.displayMode as DisplayMode.EditMode).pagination + (viewModel.questionnaireStateFlow.value.displayMode as DisplayMode.EditMode).pagination, ) .isEqualTo( QuestionnairePagination( isPaginated = true, pages = viewModel.pages!!, currentPageIndex = 1, - showSubmitButton = true - ) + showSubmitButton = true, + ), ) } } @@ -1799,9 +1809,9 @@ class QuestionnaireViewModelTest { linkId = "page1-1" type = Questionnaire.QuestionnaireItemType.BOOLEAN text = "Question on page 1" - } + }, ) - } + }, ) addItem( Questionnaire.QuestionnaireItemComponent().apply { @@ -1813,9 +1823,9 @@ class QuestionnaireViewModelTest { linkId = "page2-1" type = Questionnaire.QuestionnaireItemType.BOOLEAN text = "Question on page 2" - } + }, ) - } + }, ) } val viewModel = createQuestionnaireViewModel(questionnaire) @@ -1825,14 +1835,14 @@ class QuestionnaireViewModelTest { assertThat(questionnaire.entryMode).isEqualTo(EntryMode.PRIOR_EDIT) assertThat( - (viewModel.questionnaireStateFlow.value.displayMode as DisplayMode.EditMode).pagination + (viewModel.questionnaireStateFlow.value.displayMode as DisplayMode.EditMode).pagination, ) .isEqualTo( QuestionnairePagination( isPaginated = true, pages = viewModel.pages!!, - currentPageIndex = 0 - ) + currentPageIndex = 0, + ), ) } } @@ -1858,9 +1868,9 @@ class QuestionnaireViewModelTest { linkId = "page1-1" type = Questionnaire.QuestionnaireItemType.BOOLEAN required = true - } + }, ) - } + }, ) addItem( Questionnaire.QuestionnaireItemComponent().apply { @@ -1871,23 +1881,23 @@ class QuestionnaireViewModelTest { Questionnaire.QuestionnaireItemComponent().apply { linkId = "page2-1" type = Questionnaire.QuestionnaireItemType.BOOLEAN - } + }, ) - } + }, ) } val viewModel = createQuestionnaireViewModel(questionnaire) viewModel.runViewModelBlocking { viewModel.goToNextPage() assertThat( - (viewModel.questionnaireStateFlow.value.displayMode as DisplayMode.EditMode).pagination + (viewModel.questionnaireStateFlow.value.displayMode as DisplayMode.EditMode).pagination, ) .isEqualTo( QuestionnairePagination( isPaginated = true, pages = viewModel.pages!!, - currentPageIndex = 0 - ) + currentPageIndex = 0, + ), ) } } @@ -1912,9 +1922,9 @@ class QuestionnaireViewModelTest { Questionnaire.QuestionnaireItemComponent().apply { linkId = "page1-1" type = Questionnaire.QuestionnaireItemType.BOOLEAN - } + }, ) - } + }, ) addItem( Questionnaire.QuestionnaireItemComponent().apply { @@ -1926,9 +1936,9 @@ class QuestionnaireViewModelTest { linkId = "page2-1" required = true type = Questionnaire.QuestionnaireItemType.BOOLEAN - } + }, ) - } + }, ) } val viewModel = createQuestionnaireViewModel(questionnaire, enableReviewPage = true) @@ -1936,15 +1946,15 @@ class QuestionnaireViewModelTest { viewModel.goToNextPage() viewModel.setReviewMode(true) assertThat( - (viewModel.questionnaireStateFlow.value.displayMode as DisplayMode.EditMode).pagination + (viewModel.questionnaireStateFlow.value.displayMode as DisplayMode.EditMode).pagination, ) .isEqualTo( QuestionnairePagination( isPaginated = true, pages = viewModel.pages!!, currentPageIndex = 1, - showReviewButton = true - ) + showReviewButton = true, + ), ) } } @@ -1970,9 +1980,9 @@ class QuestionnaireViewModelTest { linkId = "page1-1" type = Questionnaire.QuestionnaireItemType.BOOLEAN text = "Question on page 1" - } + }, ) - } + }, ) addItem( Questionnaire.QuestionnaireItemComponent().apply { @@ -1984,9 +1994,9 @@ class QuestionnaireViewModelTest { linkId = "page2-1" type = Questionnaire.QuestionnaireItemType.BOOLEAN text = "Question on page 2" - } + }, ) - } + }, ) } val viewModel = createQuestionnaireViewModel(questionnaire) @@ -2019,9 +2029,9 @@ class QuestionnaireViewModelTest { linkId = "page1-1" type = Questionnaire.QuestionnaireItemType.BOOLEAN text = "Question on page 1" - } + }, ) - } + }, ) addItem( Questionnaire.QuestionnaireItemComponent().apply { @@ -2033,9 +2043,9 @@ class QuestionnaireViewModelTest { linkId = "page2-1" type = Questionnaire.QuestionnaireItemType.BOOLEAN text = "Question on page 2" - } + }, ) - } + }, ) } val viewModel = createQuestionnaireViewModel(questionnaire) @@ -2063,9 +2073,9 @@ class QuestionnaireViewModelTest { linkId = "page1-1" type = Questionnaire.QuestionnaireItemType.BOOLEAN text = "Question on page 1" - } + }, ) - } + }, ) addItem( Questionnaire.QuestionnaireItemComponent().apply { @@ -2077,9 +2087,9 @@ class QuestionnaireViewModelTest { linkId = "page2-1" type = Questionnaire.QuestionnaireItemType.BOOLEAN text = "Question on page 2" - } + }, ) - } + }, ) } val viewModel = createQuestionnaireViewModel(questionnaire) @@ -2106,9 +2116,9 @@ class QuestionnaireViewModelTest { linkId = "page1-1" type = Questionnaire.QuestionnaireItemType.BOOLEAN text = "Question on page 1" - } + }, ) - } + }, ) addItem( Questionnaire.QuestionnaireItemComponent().apply { @@ -2120,9 +2130,9 @@ class QuestionnaireViewModelTest { linkId = "page2-1" type = Questionnaire.QuestionnaireItemType.BOOLEAN text = "Question on page 2" - } + }, ) - } + }, ) } val viewModel = createQuestionnaireViewModel(questionnaire) @@ -2156,9 +2166,9 @@ class QuestionnaireViewModelTest { linkId = "page1-1" type = Questionnaire.QuestionnaireItemType.BOOLEAN text = "Question on page 1" - } + }, ) - } + }, ) addItem( Questionnaire.QuestionnaireItemComponent().apply { @@ -2170,9 +2180,9 @@ class QuestionnaireViewModelTest { linkId = "page2-1" type = Questionnaire.QuestionnaireItemType.BOOLEAN text = "Question on page 2" - } + }, ) - } + }, ) } val viewModel = createQuestionnaireViewModel(questionnaire) @@ -2180,15 +2190,15 @@ class QuestionnaireViewModelTest { viewModel.goToNextPage() assertThat( - (viewModel.questionnaireStateFlow.value.displayMode as DisplayMode.EditMode).pagination + (viewModel.questionnaireStateFlow.value.displayMode as DisplayMode.EditMode).pagination, ) .isEqualTo( QuestionnairePagination( isPaginated = true, pages = viewModel.pages!!, currentPageIndex = 1, - showSubmitButton = true - ) + showSubmitButton = true, + ), ) } } @@ -2214,9 +2224,9 @@ class QuestionnaireViewModelTest { linkId = "page1-1" type = Questionnaire.QuestionnaireItemType.BOOLEAN required = true - } + }, ) - } + }, ) addItem( Questionnaire.QuestionnaireItemComponent().apply { @@ -2227,23 +2237,23 @@ class QuestionnaireViewModelTest { Questionnaire.QuestionnaireItemComponent().apply { linkId = "page2-1" type = Questionnaire.QuestionnaireItemType.BOOLEAN - } + }, ) - } + }, ) } val viewModel = createQuestionnaireViewModel(questionnaire) viewModel.runViewModelBlocking { viewModel.goToNextPage() assertThat( - (viewModel.questionnaireStateFlow.value.displayMode as DisplayMode.EditMode).pagination + (viewModel.questionnaireStateFlow.value.displayMode as DisplayMode.EditMode).pagination, ) .isEqualTo( QuestionnairePagination( isPaginated = true, pages = viewModel.pages!!, - currentPageIndex = 0 - ) + currentPageIndex = 0, + ), ) } } @@ -2269,9 +2279,9 @@ class QuestionnaireViewModelTest { linkId = "page1-1" type = Questionnaire.QuestionnaireItemType.BOOLEAN text = "Question on page 1" - } + }, ) - } + }, ) addItem( Questionnaire.QuestionnaireItemComponent().apply { @@ -2283,38 +2293,38 @@ class QuestionnaireViewModelTest { linkId = "page2-1" type = Questionnaire.QuestionnaireItemType.BOOLEAN text = "Question on page 2" - } + }, ) - } + }, ) } val viewModel = createQuestionnaireViewModel(questionnaire) viewModel.runViewModelBlocking { viewModel.goToNextPage() assertThat( - (viewModel.questionnaireStateFlow.value.displayMode as DisplayMode.EditMode).pagination + (viewModel.questionnaireStateFlow.value.displayMode as DisplayMode.EditMode).pagination, ) .isEqualTo( QuestionnairePagination( isPaginated = true, pages = viewModel.pages!!, currentPageIndex = 1, - showSubmitButton = true - ) + showSubmitButton = true, + ), ) viewModel.goToPreviousPage() assertThat( - (viewModel.questionnaireStateFlow.value.displayMode as DisplayMode.EditMode).pagination + (viewModel.questionnaireStateFlow.value.displayMode as DisplayMode.EditMode).pagination, ) .isEqualTo( QuestionnairePagination( isPaginated = true, pages = viewModel.pages!!, currentPageIndex = 1, - showSubmitButton = true - ) + showSubmitButton = true, + ), ) } } @@ -2335,13 +2345,14 @@ class QuestionnaireViewModelTest { Questionnaire.QuestionnaireItemComponent().apply { linkId = "a-link-id" type = Questionnaire.QuestionnaireItemType.BOOLEAN - } + }, ) } val viewModel = createQuestionnaireViewModel(questionnaire, enableReviewPage = false) assertThat( (viewModel.questionnaireStateFlow.first().displayMode as DisplayMode.EditMode) - .pagination.showReviewButton + .pagination + .showReviewButton, ) .isFalse() } @@ -2357,18 +2368,19 @@ class QuestionnaireViewModelTest { Questionnaire.QuestionnaireItemComponent().apply { linkId = "a-link-id" type = Questionnaire.QuestionnaireItemType.BOOLEAN - } + }, ) } val viewModel = createQuestionnaireViewModel( questionnaire, enableReviewPage = false, - showReviewPageFirst = true + showReviewPageFirst = true, ) assertThat( (viewModel.questionnaireStateFlow.first().displayMode as DisplayMode.EditMode) - .pagination.showReviewButton + .pagination + .showReviewButton, ) .isFalse() } @@ -2384,13 +2396,14 @@ class QuestionnaireViewModelTest { Questionnaire.QuestionnaireItemComponent().apply { linkId = "a-link-id" type = Questionnaire.QuestionnaireItemType.BOOLEAN - } + }, ) } val viewModel = createQuestionnaireViewModel(questionnaire, enableReviewPage = true) assertThat( (viewModel.questionnaireStateFlow.first().displayMode as DisplayMode.EditMode) - .pagination.showReviewButton + .pagination + .showReviewButton, ) .isTrue() } @@ -2406,14 +2419,14 @@ class QuestionnaireViewModelTest { Questionnaire.QuestionnaireItemComponent().apply { linkId = "a-link-id" type = Questionnaire.QuestionnaireItemType.BOOLEAN - } + }, ) } val viewModel = createQuestionnaireViewModel( questionnaire, enableReviewPage = true, - showSubmitButton = true + showSubmitButton = true, ) viewModel.setReviewMode(true) assertThat(viewModel.questionnaireStateFlow.first().displayMode) @@ -2433,19 +2446,19 @@ class QuestionnaireViewModelTest { Questionnaire.QuestionnaireItemComponent().apply { linkId = "a-link-id" type = Questionnaire.QuestionnaireItemType.BOOLEAN - } + }, ) } val viewModel = createQuestionnaireViewModel( questionnaire, enableReviewPage = true, - showReviewPageFirst = true + showReviewPageFirst = true, ) assertThat( (viewModel.questionnaireStateFlow.first().displayMode as DisplayMode.ReviewMode) - .showEditButton + .showEditButton, ) .isTrue() } @@ -2467,9 +2480,9 @@ class QuestionnaireViewModelTest { linkId = "page1-1" type = Questionnaire.QuestionnaireItemType.BOOLEAN text = "Question on page 1" - } + }, ) - } + }, ) addItem( Questionnaire.QuestionnaireItemComponent().apply { @@ -2481,9 +2494,9 @@ class QuestionnaireViewModelTest { linkId = "page2-1" type = Questionnaire.QuestionnaireItemType.BOOLEAN text = "Question on page 2" - } + }, ) - } + }, ) } val viewModel = createQuestionnaireViewModel(questionnaire, enableReviewPage = false) @@ -2491,7 +2504,8 @@ class QuestionnaireViewModelTest { viewModel.goToNextPage() assertThat( (viewModel.questionnaireStateFlow.value.displayMode as DisplayMode.EditMode) - .pagination.showReviewButton + .pagination + .showReviewButton, ) .isFalse() } @@ -2514,9 +2528,9 @@ class QuestionnaireViewModelTest { linkId = "page1-1" type = Questionnaire.QuestionnaireItemType.BOOLEAN text = "Question on page 1" - } + }, ) - } + }, ) addItem( Questionnaire.QuestionnaireItemComponent().apply { @@ -2528,16 +2542,17 @@ class QuestionnaireViewModelTest { linkId = "page2-1" type = Questionnaire.QuestionnaireItemType.BOOLEAN text = "Question on page 2" - } + }, ) - } + }, ) } val viewModel = createQuestionnaireViewModel(questionnaire, enableReviewPage = false) viewModel.runViewModelBlocking { assertThat( (viewModel.questionnaireStateFlow.value.displayMode as DisplayMode.EditMode) - .pagination.showReviewButton + .pagination + .showReviewButton, ) .isFalse() } @@ -2559,9 +2574,9 @@ class QuestionnaireViewModelTest { linkId = "page1-1" type = Questionnaire.QuestionnaireItemType.BOOLEAN text = "Question on page 1" - } + }, ) - } + }, ) addItem( Questionnaire.QuestionnaireItemComponent().apply { @@ -2573,9 +2588,9 @@ class QuestionnaireViewModelTest { linkId = "page2-1" type = Questionnaire.QuestionnaireItemType.BOOLEAN text = "Question on page 2" - } + }, ) - } + }, ) } val viewModel = createQuestionnaireViewModel(questionnaire, enableReviewPage = true) @@ -2583,7 +2598,8 @@ class QuestionnaireViewModelTest { viewModel.goToNextPage() assertThat( (viewModel.questionnaireStateFlow.value.displayMode as DisplayMode.EditMode) - .pagination.showReviewButton + .pagination + .showReviewButton, ) .isTrue() } @@ -2606,9 +2622,9 @@ class QuestionnaireViewModelTest { linkId = "page1-1" type = Questionnaire.QuestionnaireItemType.BOOLEAN text = "Question on page 1" - } + }, ) - } + }, ) addItem( Questionnaire.QuestionnaireItemComponent().apply { @@ -2620,16 +2636,17 @@ class QuestionnaireViewModelTest { linkId = "page2-1" type = Questionnaire.QuestionnaireItemType.BOOLEAN text = "Question on page 2" - } + }, ) - } + }, ) } val viewModel = createQuestionnaireViewModel(questionnaire, enableReviewPage = true) viewModel.runViewModelBlocking { assertThat( (viewModel.questionnaireStateFlow.value.displayMode as DisplayMode.EditMode) - .pagination.showReviewButton + .pagination + .showReviewButton, ) .isTrue() } @@ -2645,14 +2662,15 @@ class QuestionnaireViewModelTest { Questionnaire.QuestionnaireItemComponent().apply { linkId = "a-link-id" type = Questionnaire.QuestionnaireItemType.BOOLEAN - } + }, ) } val viewModel = createQuestionnaireViewModel(questionnaire, enableReviewPage = true) viewModel.setReviewMode(false) assertThat( (viewModel.questionnaireStateFlow.first().displayMode as DisplayMode.EditMode) - .pagination.showReviewButton + .pagination + .showReviewButton, ) .isTrue() } @@ -2668,7 +2686,7 @@ class QuestionnaireViewModelTest { Questionnaire.QuestionnaireItemComponent().apply { linkId = "a-link-id" type = Questionnaire.QuestionnaireItemType.BOOLEAN - } + }, ) } val viewModel = createQuestionnaireViewModel(questionnaire, enableReviewPage = true) @@ -2676,7 +2694,7 @@ class QuestionnaireViewModelTest { assertThat( (viewModel.questionnaireStateFlow.first().displayMode as DisplayMode.ReviewMode) - .showEditButton + .showEditButton, ) .isTrue() } @@ -2698,14 +2716,14 @@ class QuestionnaireViewModelTest { Questionnaire.QuestionnaireItemComponent().apply { linkId = "a-link-id" type = Questionnaire.QuestionnaireItemType.BOOLEAN - } + }, ) } val viewModel = createQuestionnaireViewModel(questionnaire, readOnlyMode = true) assertThat( (viewModel.questionnaireStateFlow.first().displayMode as DisplayMode.ReviewMode) - .showEditButton + .showEditButton, ) .isFalse() } @@ -2726,13 +2744,14 @@ class QuestionnaireViewModelTest { Questionnaire.QuestionnaireItemComponent().apply { linkId = "a-link-id" type = Questionnaire.QuestionnaireItemType.BOOLEAN - } + }, ) } val viewModel = createQuestionnaireViewModel(questionnaire, showSubmitButton = false) assertThat( (viewModel.questionnaireStateFlow.first().displayMode as DisplayMode.EditMode) - .pagination.showSubmitButton + .pagination + .showSubmitButton, ) .isFalse() } @@ -2746,13 +2765,14 @@ class QuestionnaireViewModelTest { Questionnaire.QuestionnaireItemComponent().apply { linkId = "a-link-id" type = Questionnaire.QuestionnaireItemType.BOOLEAN - } + }, ) } val viewModel = createQuestionnaireViewModel(questionnaire, showSubmitButton = true) assertThat( (viewModel.questionnaireStateFlow.first().displayMode as DisplayMode.EditMode) - .pagination.showSubmitButton + .pagination + .showSubmitButton, ) .isTrue() } @@ -2766,13 +2786,14 @@ class QuestionnaireViewModelTest { Questionnaire.QuestionnaireItemComponent().apply { linkId = "a-link-id" type = Questionnaire.QuestionnaireItemType.BOOLEAN - } + }, ) } val viewModel = createQuestionnaireViewModel(questionnaire, showSubmitButton = null) assertThat( (viewModel.questionnaireStateFlow.first().displayMode as DisplayMode.EditMode) - .pagination.showSubmitButton + .pagination + .showSubmitButton, ) .isTrue() } @@ -2794,9 +2815,11 @@ class QuestionnaireViewModelTest { text = "Basic question" type = Questionnaire.QuestionnaireItemType.BOOLEAN addInitial( - Questionnaire.QuestionnaireItemInitialComponent().apply { value = BooleanType(false) } + Questionnaire.QuestionnaireItemInitialComponent().apply { + value = BooleanType(false) + }, ) - } + }, ) } val viewModel = createQuestionnaireViewModel(questionnaire) @@ -2811,11 +2834,11 @@ class QuestionnaireViewModelTest { addAnswer( QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent().apply { value = BooleanType(false) - } + }, ) - } + }, ) - } + }, ) } @@ -2832,14 +2855,16 @@ class QuestionnaireViewModelTest { Extension(ToolingExtensions.EXT_TRANSLATION).apply { addExtension(Extension("lang", StringType("en-US"))) addExtension(Extension("content", StringType("Basic Question"))) - } + }, ) } type = Questionnaire.QuestionnaireItemType.BOOLEAN addInitial( - Questionnaire.QuestionnaireItemInitialComponent().apply { value = BooleanType(false) } + Questionnaire.QuestionnaireItemInitialComponent().apply { + value = BooleanType(false) + }, ) - } + }, ) } val viewModel = createQuestionnaireViewModel(questionnaire) @@ -2854,11 +2879,11 @@ class QuestionnaireViewModelTest { addAnswer( QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent().apply { value = BooleanType(false) - } + }, ) - } + }, ) - } + }, ) } @@ -2881,11 +2906,11 @@ class QuestionnaireViewModelTest { mutableListOf( Questionnaire.QuestionnaireItemInitialComponent().apply { value = StringType("Test Value") - } + }, ) - } + }, ) - } + }, ) } val viewModel = createQuestionnaireViewModel(questionnaire) @@ -2905,13 +2930,13 @@ class QuestionnaireViewModelTest { listOf( QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent().apply { value = StringType("Test Value") - } + }, ) - } + }, ) - } + }, ) - } + }, ) } @@ -2925,7 +2950,7 @@ class QuestionnaireViewModelTest { linkId = "question-1" type = Questionnaire.QuestionnaireItemType.BOOLEAN addInitial().apply { value = BooleanType(false) } - } + }, ) addItem( Questionnaire.QuestionnaireItemComponent().apply { @@ -2936,7 +2961,7 @@ class QuestionnaireViewModelTest { question = "question-1" operator = Questionnaire.QuestionnaireItemOperator.EQUAL } - } + }, ) } state.set(EXTRA_QUESTIONNAIRE_JSON_STRING, printer.encodeResourceToString(questionnaire)) @@ -2952,11 +2977,11 @@ class QuestionnaireViewModelTest { addAnswer( QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent().apply { value = BooleanType(false) - } + }, ) - } + }, ) - } + }, ) } @@ -2970,7 +2995,7 @@ class QuestionnaireViewModelTest { linkId = "question-1" type = Questionnaire.QuestionnaireItemType.BOOLEAN addInitial().apply { value = BooleanType(true) } - } + }, ) addItem( Questionnaire.QuestionnaireItemComponent().apply { @@ -2981,7 +3006,7 @@ class QuestionnaireViewModelTest { question = "question-1" operator = Questionnaire.QuestionnaireItemOperator.EQUAL } - } + }, ) } state.set(EXTRA_QUESTIONNAIRE_JSON_STRING, printer.encodeResourceToString(questionnaire)) @@ -2997,14 +3022,16 @@ class QuestionnaireViewModelTest { addAnswer( QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent().apply { value = BooleanType(true) - } + }, ) - } + }, ) addItem( - QuestionnaireResponse.QuestionnaireResponseItemComponent().apply { linkId = "question-2" } + QuestionnaireResponse.QuestionnaireResponseItemComponent().apply { + linkId = "question-2" + }, ) - } + }, ) } @@ -3017,7 +3044,7 @@ class QuestionnaireViewModelTest { Questionnaire.QuestionnaireItemComponent().apply { linkId = "question-1" type = Questionnaire.QuestionnaireItemType.BOOLEAN - } + }, ) addItem( Questionnaire.QuestionnaireItemComponent().apply { @@ -3028,7 +3055,7 @@ class QuestionnaireViewModelTest { question = "question-1" operator = Questionnaire.QuestionnaireItemOperator.EQUAL } - } + }, ) addItem( Questionnaire.QuestionnaireItemComponent().apply { @@ -3039,7 +3066,7 @@ class QuestionnaireViewModelTest { question = "question-1" operator = Questionnaire.QuestionnaireItemOperator.EQUAL } - } + }, ) } state.set(EXTRA_QUESTIONNAIRE_JSON_STRING, printer.encodeResourceToString(questionnaire)) @@ -3053,9 +3080,9 @@ class QuestionnaireViewModelTest { addAnswer( QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent().apply { value = BooleanType(false) - } + }, ) - } + }, ) addItem( QuestionnaireResponse.QuestionnaireResponseItemComponent().apply { @@ -3063,14 +3090,14 @@ class QuestionnaireViewModelTest { addAnswer( QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent().apply { value = BooleanType(true) - } + }, ) - } + }, ) } state.set( EXTRA_QUESTIONNAIRE_RESPONSE_JSON_STRING, - printer.encodeResourceToString(questionnaireResponse) + printer.encodeResourceToString(questionnaireResponse), ) val viewModel = QuestionnaireViewModel(context, state) @@ -3088,7 +3115,7 @@ class QuestionnaireViewModelTest { Questionnaire.QuestionnaireItemComponent().apply { linkId = "question-1" type = Questionnaire.QuestionnaireItemType.BOOLEAN - } + }, ) addItem( Questionnaire.QuestionnaireItemComponent().apply { @@ -3099,7 +3126,7 @@ class QuestionnaireViewModelTest { question = "question-1" operator = Questionnaire.QuestionnaireItemOperator.EQUAL } - } + }, ) addItem( Questionnaire.QuestionnaireItemComponent().apply { @@ -3110,7 +3137,7 @@ class QuestionnaireViewModelTest { question = "question-2" operator = Questionnaire.QuestionnaireItemOperator.EQUAL } - } + }, ) } @@ -3123,9 +3150,9 @@ class QuestionnaireViewModelTest { addAnswer( QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent().apply { value = BooleanType(true) - } + }, ) - } + }, ) addItem( QuestionnaireResponse.QuestionnaireResponseItemComponent().apply { @@ -3133,9 +3160,9 @@ class QuestionnaireViewModelTest { addAnswer( QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent().apply { value = BooleanType(true) - } + }, ) - } + }, ) addItem( QuestionnaireResponse.QuestionnaireResponseItemComponent().apply { @@ -3143,9 +3170,9 @@ class QuestionnaireViewModelTest { addAnswer( QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent().apply { value = BooleanType(true) - } + }, ) - } + }, ) } @@ -3173,7 +3200,7 @@ class QuestionnaireViewModelTest { Questionnaire.QuestionnaireItemComponent().apply { linkId = "question-1" type = Questionnaire.QuestionnaireItemType.BOOLEAN - } + }, ) addItem( Questionnaire.QuestionnaireItemComponent().apply { @@ -3184,7 +3211,7 @@ class QuestionnaireViewModelTest { question = "question-1" operator = Questionnaire.QuestionnaireItemOperator.EQUAL } - } + }, ) addItem( Questionnaire.QuestionnaireItemComponent().apply { @@ -3195,7 +3222,7 @@ class QuestionnaireViewModelTest { question = "question-2" operator = Questionnaire.QuestionnaireItemOperator.EQUAL } - } + }, ) } @@ -3208,9 +3235,9 @@ class QuestionnaireViewModelTest { addAnswer( QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent().apply { value = BooleanType(true) - } + }, ) - } + }, ) addItem( QuestionnaireResponse.QuestionnaireResponseItemComponent().apply { @@ -3218,9 +3245,9 @@ class QuestionnaireViewModelTest { addAnswer( QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent().apply { value = BooleanType(true) - } + }, ) - } + }, ) addItem( QuestionnaireResponse.QuestionnaireResponseItemComponent().apply { @@ -3228,9 +3255,9 @@ class QuestionnaireViewModelTest { addAnswer( QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent().apply { value = BooleanType(true) - } + }, ) - } + }, ) } @@ -3248,9 +3275,9 @@ class QuestionnaireViewModelTest { addItem( QuestionnaireResponse.QuestionnaireResponseItemComponent().apply { linkId = "question-1" - } + }, ) - } + }, ) // Setting the answer of "question-1" to true should enable question-2 that in turn enables @@ -3260,7 +3287,7 @@ class QuestionnaireViewModelTest { .setAnswer( QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent().apply { value = BooleanType(true) - } + }, ) assertResourceEquals(viewModel.getQuestionnaireResponse(), questionnaireResponse) @@ -3426,7 +3453,8 @@ class QuestionnaireViewModelTest { ] } - """.trimIndent() + """ + .trimIndent() val questionnaireResponseString = """ @@ -3529,7 +3557,8 @@ class QuestionnaireViewModelTest { ] } - """.trimIndent() + """ + .trimIndent() val expectedResponseString = """ @@ -3623,7 +3652,8 @@ class QuestionnaireViewModelTest { ] } - """.trimIndent() + """ + .trimIndent() state.set(EXTRA_QUESTIONNAIRE_JSON_STRING, questionnaireString) state.set(EXTRA_QUESTIONNAIRE_RESPONSE_JSON_STRING, questionnaireResponseString) @@ -3657,9 +3687,9 @@ class QuestionnaireViewModelTest { linkId = "a-nested-item" text = "Basic question" type = Questionnaire.QuestionnaireItemType.BOOLEAN - } + }, ) - } + }, ) } val questionnaireResponse = @@ -3675,11 +3705,11 @@ class QuestionnaireViewModelTest { addAnswer( QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent().apply { this.value = valueBooleanType.setValue(false) - } + }, ) - } + }, ) - } + }, ) } val viewModel = createQuestionnaireViewModel(questionnaire) @@ -3690,7 +3720,7 @@ class QuestionnaireViewModelTest { .setAnswer( QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent().apply { this.value = valueBooleanType.setValue(false) - } + }, ) assertResourceEquals(viewModel.getQuestionnaireResponse(), questionnaireResponse) @@ -3713,16 +3743,16 @@ class QuestionnaireViewModelTest { linkId = "nested-item-a" text = "Basic question" type = Questionnaire.QuestionnaireItemType.BOOLEAN - } + }, ) addItem( Questionnaire.QuestionnaireItemComponent().apply { linkId = "another-nested-item-a" text = "Basic question" type = Questionnaire.QuestionnaireItemType.BOOLEAN - } + }, ) - } + }, ) addItem( Questionnaire.QuestionnaireItemComponent().apply { @@ -3735,16 +3765,16 @@ class QuestionnaireViewModelTest { linkId = "nested-item-b" text = "Basic question" type = Questionnaire.QuestionnaireItemType.BOOLEAN - } + }, ) addItem( Questionnaire.QuestionnaireItemComponent().apply { linkId = "another-nested-item-b" text = "Basic question" type = Questionnaire.QuestionnaireItemType.BOOLEAN - } + }, ) - } + }, ) } val viewModel = createQuestionnaireViewModel(questionnaire) @@ -3769,8 +3799,9 @@ class QuestionnaireViewModelTest { item = repeatedGroupA() .asQuestion() - .questionnaireItem.getNestedQuestionnaireResponseItems() - } + .questionnaireItem + .getNestedQuestionnaireResponseItems() + }, ) repeatedGroupB() .asQuestion() @@ -3779,15 +3810,16 @@ class QuestionnaireViewModelTest { item = repeatedGroupB() .asQuestion() - .questionnaireItem.getNestedQuestionnaireResponseItems() - } + .questionnaireItem + .getNestedQuestionnaireResponseItems() + }, ) } assertThat( viewModel.getQuestionnaireItemViewItemList().map { it.asQuestion().questionnaireItem.linkId - } + }, ) .containsExactly( "repeated-group-a", @@ -3799,7 +3831,7 @@ class QuestionnaireViewModelTest { "nested-item-b", "another-nested-item-b", "nested-item-b", - "another-nested-item-b" + "another-nested-item-b", ) .inOrder() @@ -3815,15 +3847,15 @@ class QuestionnaireViewModelTest { QuestionnaireResponse.QuestionnaireResponseItemComponent().apply { linkId = "nested-item-a" text = "Basic question" - } + }, ) addItem( QuestionnaireResponse.QuestionnaireResponseItemComponent().apply { linkId = "another-nested-item-a" text = "Basic question" - } + }, ) - } + }, ) addItem( QuestionnaireResponse.QuestionnaireResponseItemComponent().apply { @@ -3833,15 +3865,15 @@ class QuestionnaireViewModelTest { QuestionnaireResponse.QuestionnaireResponseItemComponent().apply { linkId = "nested-item-a" text = "Basic question" - } + }, ) addItem( QuestionnaireResponse.QuestionnaireResponseItemComponent().apply { linkId = "another-nested-item-a" text = "Basic question" - } + }, ) - } + }, ) addItem( QuestionnaireResponse.QuestionnaireResponseItemComponent().apply { @@ -3851,15 +3883,15 @@ class QuestionnaireViewModelTest { QuestionnaireResponse.QuestionnaireResponseItemComponent().apply { linkId = "nested-item-b" text = "Basic question" - } + }, ) addItem( QuestionnaireResponse.QuestionnaireResponseItemComponent().apply { linkId = "another-nested-item-b" text = "Basic question" - } + }, ) - } + }, ) addItem( QuestionnaireResponse.QuestionnaireResponseItemComponent().apply { @@ -3869,17 +3901,17 @@ class QuestionnaireViewModelTest { QuestionnaireResponse.QuestionnaireResponseItemComponent().apply { linkId = "nested-item-b" text = "Basic question" - } + }, ) addItem( QuestionnaireResponse.QuestionnaireResponseItemComponent().apply { linkId = "another-nested-item-b" text = "Basic question" - } + }, ) - } + }, ) - } + }, ) } } @@ -3904,11 +3936,11 @@ class QuestionnaireViewModelTest { linkId = "a-nested-nested-boolean-item" text = "Nested nested question" type = Questionnaire.QuestionnaireItemType.BOOLEAN - } + }, ) - } + }, ) - } + }, ) } @@ -3934,17 +3966,17 @@ class QuestionnaireViewModelTest { text = "Nested nested question" addAnswer( QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent() - .apply { this.value = valueBooleanType.setValue(false) } + .apply { this.value = valueBooleanType.setValue(false) }, ) - } + }, ) - } + }, ) - } + }, ) - } + }, ) - } + }, ) } @@ -3958,7 +3990,7 @@ class QuestionnaireViewModelTest { .setAnswer( QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent().apply { this.value = valueBooleanType.setValue(false) - } + }, ) items = viewModel.getQuestionnaireItemViewItemList().map { it.asQuestion() } @@ -3970,7 +4002,7 @@ class QuestionnaireViewModelTest { .setAnswer( QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent().apply { this.value = valueBooleanType.setValue(false) - } + }, ) items = viewModel.getQuestionnaireItemViewItemList().map { it.asQuestion() } @@ -3982,7 +4014,7 @@ class QuestionnaireViewModelTest { .setAnswer( QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent().apply { this.value = valueBooleanType.setValue(false) - } + }, ) assertResourceEquals(viewModel.getQuestionnaireResponse(), questionnaireResponse) @@ -4010,7 +4042,7 @@ class QuestionnaireViewModelTest { system = CODE_SYSTEM_YES_NO code = "Y" display = "Yes" - } + }, ) addContains( @@ -4018,7 +4050,7 @@ class QuestionnaireViewModelTest { system = CODE_SYSTEM_YES_NO code = "N" display = "No" - } + }, ) addContains( @@ -4026,16 +4058,16 @@ class QuestionnaireViewModelTest { system = CODE_SYSTEM_YES_NO code = "asked-unknown" display = "Don't Know" - } + }, ) } - } + }, ) addItem( Questionnaire.QuestionnaireItemComponent().apply { linkId = "q-yesnodontknow" answerValueSet = "#$valueSetId" - } + }, ) } @@ -4061,8 +4093,7 @@ class QuestionnaireViewModelTest { valueSetResolverExternal = object : ExternalAnswerValueSetResolver { override suspend fun resolve(uri: String): List { - - return if (uri == CODE_SYSTEM_YES_NO) + return if (uri == CODE_SYSTEM_YES_NO) { listOf( Coding().apply { system = CODE_SYSTEM_YES_NO @@ -4078,11 +4109,13 @@ class QuestionnaireViewModelTest { system = CODE_SYSTEM_YES_NO code = "asked-unknown" display = "Don't Know" - } + }, ) - else emptyList() + } else { + emptyList() + } } - } + }, ) val questionnaire = @@ -4092,7 +4125,7 @@ class QuestionnaireViewModelTest { Questionnaire.QuestionnaireItemComponent().apply { linkId = "q-codesystemyesno" answerValueSet = CODE_SYSTEM_YES_NO - } + }, ) } @@ -4125,7 +4158,7 @@ class QuestionnaireViewModelTest { xFhirQueryResolver = { xFhirQuery -> searchString = xFhirQuery emptyList() - } + }, ) val patientId = "123" @@ -4136,7 +4169,7 @@ class QuestionnaireViewModelTest { extension = listOf( Extension( - "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-launchContext" + "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-launchContext", ) .apply { addExtension( @@ -4144,11 +4177,11 @@ class QuestionnaireViewModelTest { Coding( "http://hl7.org/fhir/uv/sdc/CodeSystem/launchContext", "patient", - "Patient" - ) + "Patient", + ), ) addExtension("type", CodeType("Patient")) - } + }, ) addItem( Questionnaire.QuestionnaireItemComponent().apply { @@ -4162,16 +4195,16 @@ class QuestionnaireViewModelTest { Expression().apply { this.expression = "Observation?subject={{%patient.id}}" this.language = Expression.ExpressionLanguage.APPLICATION_XFHIRQUERY.toCode() - } - ) + }, + ), ) - } + }, ) } state.set(EXTRA_QUESTIONNAIRE_JSON_STRING, printer.encodeResourceToString(questionnaire)) state.set( EXTRA_QUESTIONNAIRE_LAUNCH_CONTEXT_JSON_STRINGS, - listOf(printer.encodeResourceToString(patient)) + listOf(printer.encodeResourceToString(patient)), ) val viewModel = QuestionnaireViewModel(context, state) @@ -4212,18 +4245,18 @@ class QuestionnaireViewModelTest { Expression().apply { this.expression = "Practitioner?active=true" this.language = Expression.ExpressionLanguage.APPLICATION_XFHIRQUERY.toCode() - } + }, ), Extension( - "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-choiceColumn" + "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-choiceColumn", ) .apply { this.addExtension(Extension("path", StringType("id"))) this.addExtension(Extension("label", StringType("name"))) this.addExtension(Extension("forDisplay", BooleanType(true))) - } + }, ) - } + }, ) } state.set(EXTRA_QUESTIONNAIRE_JSON_STRING, printer.encodeResourceToString(questionnaire)) @@ -4256,18 +4289,18 @@ class QuestionnaireViewModelTest { Expression().apply { this.expression = "Practitioner?active=true" this.language = Expression.ExpressionLanguage.APPLICATION_XFHIRQUERY.toCode() - } + }, ), Extension( - "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-choiceColumn" + "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-choiceColumn", ) .apply { this.addExtension(Extension("path", StringType("id"))) this.addExtension(Extension("label", StringType("name"))) this.addExtension(Extension("forDisplay", BooleanType(true))) - } + }, ) - } + }, ) } state.set(EXTRA_QUESTIONNAIRE_JSON_STRING, printer.encodeResourceToString(questionnaire)) @@ -4278,55 +4311,166 @@ class QuestionnaireViewModelTest { } assertThat(exception.message) .isEqualTo( - "XFhirQueryResolver cannot be null. Please provide the XFhirQueryResolver via DataCaptureConfig." + "XFhirQueryResolver cannot be null. Please provide the XFhirQueryResolver via DataCaptureConfig.", ) } - // ==================================================================== // - // // - // Answer Options Toggle Expression // - // // - // ==================================================================== // @Test - fun `only answer options evaluating to true in answerOptionsToggleExpression occurrences should be enabled on initial load`() = + fun `should return questionnaire item answer options for answer expression with fhirpath variable`() = runTest { val questionnaire = Questionnaire().apply { addItem( - Questionnaire.QuestionnaireItemComponent().apply { - linkId = "b" + QuestionnaireItemComponent().apply { + linkId = "a" + text = "Question 1" type = Questionnaire.QuestionnaireItemType.CHOICE - text = "Select an option" - addExtension( - Extension(EXTENSION_ANSWER_OPTION_TOGGLE_EXPRESSION_URL).apply { - addExtension( - Extension( - EXTENSION_ANSWER_OPTION_TOGGLE_EXPRESSION_OPTION, - Coding().apply { - code = "option1" - display = "Option 1" - } - ) - ) - addExtension( - Extension( - EXTENSION_ANSWER_OPTION_TOGGLE_EXPRESSION_OPTION, - Coding().apply { - code = "option3" - display = "Option 3" - } - ) - ) - addExtension( - Extension( - EXTENSION_ANSWER_OPTION_TOGGLE_EXPRESSION, - Expression().apply { - this.expression = "false" - this.language = "text/fhirpath" - } - ) + repeats = true + initial = + listOf( + Questionnaire.QuestionnaireItemInitialComponent(Coding("test", "1", "One")), + Questionnaire.QuestionnaireItemInitialComponent(Coding("test", "2", "Two")), + ) + }, + ) + addItem( + QuestionnaireItemComponent().apply { + linkId = "b" + text = "Question 2" + type = Questionnaire.QuestionnaireItemType.STRING + extension = + listOf( + Extension( + "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-answerExpression", + Expression().apply { + this.expression = "%resource.item[0].answer.value.select(%VAR1 + code)" + this.language = Expression.ExpressionLanguage.TEXT_FHIRPATH.toCode() + }, + ), + Extension( + "http://hl7.org/fhir/StructureDefinition/variable", + Expression().apply { + this.name = "VAR1" + this.expression = "'Class '" + this.language = Expression.ExpressionLanguage.TEXT_FHIRPATH.toCode() + }, + ), + ) + }, + ) + } + + state.set(EXTRA_QUESTIONNAIRE_JSON_STRING, printer.encodeResourceToString(questionnaire)) + val viewModel = QuestionnaireViewModel(context, state) + + viewModel.runViewModelBlocking { + val viewItem = + viewModel + .getQuestionnaireItemViewItemList() + .map { it.asQuestion() } + .single { it.questionnaireItem.linkId == "b" } + assertThat(viewItem.enabledAnswerOptions.map { it.valueStringType.value }) + .containsExactly("Class 1", "Class 2") + } + } + + @Test + fun `should return questionnaire item answer options for answer expression with fhirpath supplement context`() = + runTest { + val questionnaire = + Questionnaire().apply { + addItem( + QuestionnaireItemComponent().apply { + linkId = "a" + text = "Question 1" + type = Questionnaire.QuestionnaireItemType.CHOICE + repeats = true + initial = + listOf( + Questionnaire.QuestionnaireItemInitialComponent(Coding("test", "1", "One")), + Questionnaire.QuestionnaireItemInitialComponent(Coding("test", "2", "Two")), + ) + }, + ) + addItem( + QuestionnaireItemComponent().apply { + linkId = "b" + text = "Question 2" + type = Questionnaire.QuestionnaireItemType.STRING + extension = + listOf( + Extension( + "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-answerExpression", + Expression().apply { + this.expression = + "%resource.item[0].answer.value.select('Code ' + code + '-' + %context.linkId)" + this.language = Expression.ExpressionLanguage.TEXT_FHIRPATH.toCode() + }, + ), + ) + }, + ) + } + + state.set(EXTRA_QUESTIONNAIRE_JSON_STRING, printer.encodeResourceToString(questionnaire)) + val viewModel = QuestionnaireViewModel(context, state) + + viewModel.runViewModelBlocking { + val viewItem = + viewModel + .getQuestionnaireItemViewItemList() + .map { it.asQuestion() } + .single { it.questionnaireItem.linkId == "b" } + assertThat(viewItem.enabledAnswerOptions.map { it.valueStringType.value }) + .containsExactly("Code 1-b", "Code 2-b") + } + } + + // ==================================================================== // + // // + // Answer Options Toggle Expression // + // // + // ==================================================================== // + @Test + fun `only answer options evaluating to true in answerOptionsToggleExpression occurrences should be enabled on initial load`() = + runTest { + val questionnaire = + Questionnaire().apply { + addItem( + Questionnaire.QuestionnaireItemComponent().apply { + linkId = "b" + type = Questionnaire.QuestionnaireItemType.CHOICE + text = "Select an option" + addExtension( + Extension(EXTENSION_ANSWER_OPTION_TOGGLE_EXPRESSION_URL).apply { + addExtension( + Extension( + EXTENSION_ANSWER_OPTION_TOGGLE_EXPRESSION_OPTION, + Coding().apply { + code = "option1" + display = "Option 1" + }, + ), ) - } + addExtension( + Extension( + EXTENSION_ANSWER_OPTION_TOGGLE_EXPRESSION_OPTION, + Coding().apply { + code = "option3" + display = "Option 3" + }, + ), + ) + addExtension( + Extension( + EXTENSION_ANSWER_OPTION_TOGGLE_EXPRESSION, + Expression().apply { + this.expression = "false" + this.language = "text/fhirpath" + }, + ), + ) + }, ) addExtension( Extension(EXTENSION_ANSWER_OPTION_TOGGLE_EXPRESSION_URL).apply { @@ -4336,8 +4480,8 @@ class QuestionnaireViewModelTest { Coding().apply { code = "option2" display = "Option 2" - } - ) + }, + ), ) addExtension( Extension( @@ -4345,10 +4489,10 @@ class QuestionnaireViewModelTest { Expression().apply { this.expression = "true" this.language = "text/fhirpath" - } - ) + }, + ), ) - } + }, ) answerOption = listOf( @@ -4372,9 +4516,9 @@ class QuestionnaireViewModelTest { code = "option3" display = "Option 3" } - } + }, ) - } + }, ) } @@ -4401,7 +4545,7 @@ class QuestionnaireViewModelTest { type = Questionnaire.QuestionnaireItemType.BOOLEAN text = "Is Married?" addInitial().apply { value = BooleanType(false) } - } + }, ) addItem( @@ -4417,8 +4561,8 @@ class QuestionnaireViewModelTest { Coding().apply { code = "option1" display = "Option 1" - } - ) + }, + ), ) addExtension( Extension( @@ -4426,8 +4570,8 @@ class QuestionnaireViewModelTest { Coding().apply { code = "option3" display = "Option 3" - } - ) + }, + ), ) addExtension( Extension( @@ -4436,10 +4580,10 @@ class QuestionnaireViewModelTest { this.expression = "%resource.repeat(item).where(linkId='a' and answer.empty().not()).select(answer.value)" this.language = "text/fhirpath" - } - ) + }, + ), ) - } + }, ) addExtension( Extension(EXTENSION_ANSWER_OPTION_TOGGLE_EXPRESSION_URL).apply { @@ -4449,8 +4593,8 @@ class QuestionnaireViewModelTest { Coding().apply { code = "option2" display = "Option 2" - } - ) + }, + ), ) addExtension( Extension( @@ -4459,10 +4603,10 @@ class QuestionnaireViewModelTest { this.expression = "%resource.repeat(item).where(linkId='a' and answer.empty().not()).select(answer.value.not())" this.language = "text/fhirpath" - } - ) + }, + ), ) - } + }, ) answerOption = listOf( @@ -4486,9 +4630,9 @@ class QuestionnaireViewModelTest { code = "option3" display = "Option 3" } - } + }, ) - } + }, ) } @@ -4509,7 +4653,7 @@ class QuestionnaireViewModelTest { .setAnswer( QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent().apply { value = BooleanType(true) - } + }, ) assertThat( @@ -4517,7 +4661,9 @@ class QuestionnaireViewModelTest { .getQuestionnaireResponse() .item .single { it.linkId == "a" } - .answerFirstRep.value.primitiveValue() + .answerFirstRep + .value + .primitiveValue(), ) .isEqualTo(BooleanType(true).primitiveValue()) @@ -4550,8 +4696,8 @@ class QuestionnaireViewModelTest { Coding().apply { code = "option1" display = "Option 1" - } - ) + }, + ), ) addExtension( Extension( @@ -4559,8 +4705,8 @@ class QuestionnaireViewModelTest { Coding().apply { code = "option3" display = "Option 3" - } - ) + }, + ), ) addExtension( Extension( @@ -4568,10 +4714,10 @@ class QuestionnaireViewModelTest { Expression().apply { this.expression = "false" this.language = "text/fhirpath" - } - ) + }, + ), ) - } + }, ) addExtension( Extension(EXTENSION_ANSWER_OPTION_TOGGLE_EXPRESSION_URL).apply { @@ -4581,8 +4727,8 @@ class QuestionnaireViewModelTest { Coding().apply { code = "option2" display = "Option 2" - } - ) + }, + ), ) addExtension( Extension( @@ -4590,10 +4736,10 @@ class QuestionnaireViewModelTest { Expression().apply { this.expression = "true" this.language = "text/fhirpath" - } - ) + }, + ), ) - } + }, ) answerOption = listOf( @@ -4631,9 +4777,9 @@ class QuestionnaireViewModelTest { code = "option5" display = "Option 5" } - } + }, ) - } + }, ) } val viewModel = createQuestionnaireViewModel(questionnaire) @@ -4667,8 +4813,8 @@ class QuestionnaireViewModelTest { Coding().apply { code = "option2" display = "Option 2" - } - ) + }, + ), ) addExtension( Extension( @@ -4676,8 +4822,8 @@ class QuestionnaireViewModelTest { Coding().apply { code = "option75" display = "Option 75" - } - ) + }, + ), ) addExtension( Extension( @@ -4685,10 +4831,10 @@ class QuestionnaireViewModelTest { Expression().apply { this.expression = "true" this.language = "text/fhirpath" - } - ) + }, + ), ) - } + }, ) addExtension( Extension(EXTENSION_ANSWER_OPTION_TOGGLE_EXPRESSION_URL).apply { @@ -4698,8 +4844,8 @@ class QuestionnaireViewModelTest { Coding().apply { code = "option1" display = "Option 1" - } - ) + }, + ), ) addExtension( Extension( @@ -4707,8 +4853,8 @@ class QuestionnaireViewModelTest { Coding().apply { code = "option3" display = "Option 3" - } - ) + }, + ), ) addExtension( Extension( @@ -4716,10 +4862,10 @@ class QuestionnaireViewModelTest { Expression().apply { this.expression = "false" this.language = "text/fhirpath" - } - ) + }, + ), ) - } + }, ) answerOption = listOf( @@ -4743,9 +4889,9 @@ class QuestionnaireViewModelTest { code = "option3" display = "Option 3" } - } + }, ) - } + }, ) } @@ -4780,8 +4926,8 @@ class QuestionnaireViewModelTest { Coding().apply { code = "option1" display = "Option 1" - } - ) + }, + ), ) addExtension( Extension( @@ -4789,10 +4935,10 @@ class QuestionnaireViewModelTest { Expression().apply { this.expression = "false" this.language = "text/fhirpath" - } - ) + }, + ), ) - } + }, ) addExtension( Extension(EXTENSION_ANSWER_OPTION_TOGGLE_EXPRESSION_URL).apply { @@ -4802,8 +4948,8 @@ class QuestionnaireViewModelTest { Coding().apply { code = "option1" display = "Option 1" - } - ) + }, + ), ) addExtension( Extension( @@ -4811,10 +4957,10 @@ class QuestionnaireViewModelTest { Expression().apply { this.expression = "true" this.language = "text/fhirpath" - } - ) + }, + ), ) - } + }, ) answerOption = listOf( @@ -4831,9 +4977,9 @@ class QuestionnaireViewModelTest { code = "option2" display = "Option 2" } - } + }, ) - } + }, ) } @@ -4859,9 +5005,9 @@ class QuestionnaireViewModelTest { name = "textVal" language = "text/fhirpath" expression = "10" - } + }, ) - } + }, ) addItem( Questionnaire.QuestionnaireItemComponent().apply { @@ -4876,8 +5022,8 @@ class QuestionnaireViewModelTest { Coding().apply { code = "option1" display = "Option 1" - } - ) + }, + ), ) addExtension( Extension( @@ -4885,10 +5031,10 @@ class QuestionnaireViewModelTest { Expression().apply { this.expression = "%textVal > 10" this.language = "text/fhirpath" - } - ) + }, + ), ) - } + }, ) addExtension( @@ -4899,8 +5045,8 @@ class QuestionnaireViewModelTest { Coding().apply { code = "option2" display = "Option 2" - } - ) + }, + ), ) addExtension( Extension( @@ -4908,10 +5054,10 @@ class QuestionnaireViewModelTest { Expression().apply { this.expression = "%textVal = 10" this.language = "text/fhirpath" - } - ) + }, + ), ) - } + }, ) addAnswerOption( @@ -4921,7 +5067,7 @@ class QuestionnaireViewModelTest { code = "option1" display = "Option 1" } - } + }, ) addAnswerOption( Questionnaire.QuestionnaireItemAnswerOptionComponent().apply { @@ -4930,9 +5076,9 @@ class QuestionnaireViewModelTest { code = "option2" display = "Option 2" } - } + }, ) - } + }, ) } @@ -4959,7 +5105,7 @@ class QuestionnaireViewModelTest { type = Questionnaire.QuestionnaireItemType.BOOLEAN text = "Is Married?" addInitial().apply { value = BooleanType(false) } - } + }, ) addItem( Questionnaire.QuestionnaireItemComponent().apply { @@ -4974,8 +5120,8 @@ class QuestionnaireViewModelTest { Coding().apply { code = "option1" display = "Option 1" - } - ) + }, + ), ) addExtension( Extension( @@ -4984,10 +5130,10 @@ class QuestionnaireViewModelTest { this.expression = "%resource.repeat(item).where(linkId='a' and answer.empty().not()).select(answer.value.not())" this.language = "text/fhirpath" - } - ) + }, + ), ) - } + }, ) addExtension( @@ -4998,8 +5144,8 @@ class QuestionnaireViewModelTest { Coding().apply { code = "option2" display = "Option 2" - } - ) + }, + ), ) addExtension( Extension( @@ -5008,10 +5154,10 @@ class QuestionnaireViewModelTest { this.expression = "%resource.repeat(item).where(linkId='a' and answer.empty().not()).select(answer.value)" this.language = "text/fhirpath" - } - ) + }, + ), ) - } + }, ) addAnswerOption( @@ -5021,7 +5167,7 @@ class QuestionnaireViewModelTest { code = "option1" display = "Option 1" } - } + }, ) addAnswerOption( Questionnaire.QuestionnaireItemAnswerOptionComponent().apply { @@ -5030,9 +5176,9 @@ class QuestionnaireViewModelTest { code = "option2" display = "Option 2" } - } + }, ) - } + }, ) } val viewModel = createQuestionnaireViewModel(questionnaire) @@ -5050,7 +5196,7 @@ class QuestionnaireViewModelTest { code = "option1" display = "Option 1" } - } + }, ) } assertThat( @@ -5058,7 +5204,9 @@ class QuestionnaireViewModelTest { .getQuestionnaireResponse() .item .single { it.linkId == "b" } - .answerFirstRep.valueCoding.code + .answerFirstRep + .valueCoding + .code, ) .isEqualTo("option1") // change answer to depended question to trigger re-evaluation of @@ -5071,7 +5219,7 @@ class QuestionnaireViewModelTest { setAnswer( QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent().apply { value = BooleanType(true) - } + }, ) } @@ -5111,19 +5259,19 @@ class QuestionnaireViewModelTest { this.language = "text/fhirpath" this.expression = "%resource.repeat(item).where(linkId='a-age-years' and answer.empty().not()).select(today() - answer.value)" - } + }, ) } - } + }, ) addItem( Questionnaire.QuestionnaireItemComponent().apply { linkId = "a-age-years" type = Questionnaire.QuestionnaireItemType.QUANTITY addInitial( - Questionnaire.QuestionnaireItemInitialComponent(Quantity.fromUcum("1", "year")) + Questionnaire.QuestionnaireItemInitialComponent(Quantity.fromUcum("1", "year")), ) - } + }, ) } @@ -5134,7 +5282,9 @@ class QuestionnaireViewModelTest { .getQuestionnaireResponse() .item .single { it.linkId == "a-birthdate" } - .answerFirstRep.value.asStringValue() + .answerFirstRep + .value + .asStringValue(), ) .isEqualTo(DateType(Date()).apply { add(Calendar.YEAR, -1) }.asStringValue()) @@ -5143,7 +5293,10 @@ class QuestionnaireViewModelTest { .getQuestionnaireResponse() .item .single { it.linkId == "a-age-years" } - .answerFirstRep.valueQuantity.value.toString() + .answerFirstRep + .valueQuantity + .value + .toString(), ) .isEqualTo("1") } @@ -5166,16 +5319,16 @@ class QuestionnaireViewModelTest { this.language = "text/fhirpath" this.expression = "%resource.repeat(item).where(linkId='a-age-years' and answer.empty().not()).select(today() - answer.value)" - } + }, ) } - } + }, ) addItem( Questionnaire.QuestionnaireItemComponent().apply { linkId = "a-age-years" type = Questionnaire.QuestionnaireItemType.INTEGER - } + }, ) } @@ -5200,14 +5353,14 @@ class QuestionnaireViewModelTest { listOf( QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent().apply { this.value = Quantity.fromUcum("2", "years") - } + }, ), - null + null, ) } assertThat( - birthdateItem.getQuestionnaireResponseItem().answer.first().valueDateType.valueAsString + birthdateItem.getQuestionnaireResponseItem().answer.first().valueDateType.valueAsString, ) .isEqualTo(DateType(Date()).apply { add(Calendar.YEAR, -2) }.valueAsString) } @@ -5230,16 +5383,16 @@ class QuestionnaireViewModelTest { this.language = "text/fhirpath" this.expression = "%resource.repeat(item).where(linkId='a-age-years' and answer.empty().not()).select(today() - answer.value)" - } + }, ) } - } + }, ) addItem( Questionnaire.QuestionnaireItemComponent().apply { linkId = "a-age-years" type = Questionnaire.QuestionnaireItemType.INTEGER - } + }, ) } @@ -5258,14 +5411,14 @@ class QuestionnaireViewModelTest { listOf( QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent().apply { this.value = birthdateValue - } + }, ), - null + null, ) } assertThat( - birthdateItem.getQuestionnaireResponseItem().answer.first().valueDateType.valueAsString + birthdateItem.getQuestionnaireResponseItem().answer.first().valueDateType.valueAsString, ) .isEqualTo(birthdateValue.valueAsString) @@ -5280,14 +5433,14 @@ class QuestionnaireViewModelTest { listOf( QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent().apply { this.value = Quantity.fromUcum("2", "years") - } + }, ), - null + null, ) } assertThat( - birthdateItem.getQuestionnaireResponseItem().answer.first().valueDateType.valueAsString + birthdateItem.getQuestionnaireResponseItem().answer.first().valueDateType.valueAsString, ) .isEqualTo(birthdateValue.valueAsString) } @@ -5304,8 +5457,8 @@ class QuestionnaireViewModelTest { type = Questionnaire.QuestionnaireItemType.DATE addInitial( Questionnaire.QuestionnaireItemInitialComponent( - DateType(Date()).apply { add(Calendar.YEAR, -2) } - ) + DateType(Date()).apply { add(Calendar.YEAR, -2) }, + ), ) addExtension().apply { url = EXTENSION_CALCULATED_EXPRESSION_URL @@ -5314,10 +5467,10 @@ class QuestionnaireViewModelTest { this.language = "text/fhirpath" this.expression = "%resource.repeat(item).where(linkId='a-age-years' and answer.empty().not()).select(today() - answer.value)" - } + }, ) } - } + }, ) addItem( @@ -5331,10 +5484,10 @@ class QuestionnaireViewModelTest { this.language = "text/fhirpath" this.expression = "today().toString().substring(0, 4).toInteger() - %resource.repeat(item).where(linkId='a-birthdate').answer.value.toString().substring(0, 4).toInteger()" - } + }, ) } - } + }, ) } @@ -5360,8 +5513,8 @@ class QuestionnaireViewModelTest { type = Questionnaire.QuestionnaireItemType.DATE addInitial( Questionnaire.QuestionnaireItemInitialComponent( - DateType(Date()).apply { add(Calendar.YEAR, -2) } - ) + DateType(Date()).apply { add(Calendar.YEAR, -2) }, + ), ) addExtension().apply { url = EXTENSION_CALCULATED_EXPRESSION_URL @@ -5370,10 +5523,10 @@ class QuestionnaireViewModelTest { this.language = "text/fhirpath" this.expression = "%resource.repeat(item).where(linkId='a-age-years' and answer.empty().not()).select(today() - answer.value)" - } + }, ) } - } + }, ) .addItem() .apply { @@ -5396,10 +5549,10 @@ class QuestionnaireViewModelTest { this.language = "text/fhirpath" this.expression = "today().toString().substring(0, 4).toInteger() - %resource.repeat(item).where(linkId='a-birthdate').answer.value.toString().substring(0, 4).toInteger()" - } + }, ) } - } + }, ) } @@ -5436,9 +5589,9 @@ class QuestionnaireViewModelTest { linkId = "nested-display-question" text = "subtitle text" type = Questionnaire.QuestionnaireItemType.DISPLAY - } + }, ) - } + }, ) } state.set(EXTRA_QUESTIONNAIRE_JSON_STRING, printer.encodeResourceToString(questionnaire)) @@ -5446,7 +5599,7 @@ class QuestionnaireViewModelTest { val viewModel = QuestionnaireViewModel(context, state) viewModel.runViewModelBlocking { assertThat( - viewModel.getQuestionnaireItemViewItemList().last().asQuestion().questionnaireItem.linkId + viewModel.getQuestionnaireItemViewItemList().last().asQuestion().questionnaireItem.linkId, ) .isEqualTo("nested-display-question") } @@ -5465,9 +5618,9 @@ class QuestionnaireViewModelTest { Coding().apply { code = EXTENSION_DISPLAY_CATEGORY_INSTRUCTIONS system = EXTENSION_DISPLAY_CATEGORY_SYSTEM - } + }, ) - } + }, ) } @@ -5486,9 +5639,9 @@ class QuestionnaireViewModelTest { text = "subtitle text" type = Questionnaire.QuestionnaireItemType.DISPLAY extension = listOf(displayCategoryExtension) - } + }, ) - } + }, ) } state.set(EXTRA_QUESTIONNAIRE_JSON_STRING, printer.encodeResourceToString(questionnaire)) @@ -5500,7 +5653,8 @@ class QuestionnaireViewModelTest { .getQuestionnaireItemViewItemList() .last() .asQuestion() - .questionnaireItem.linkId + .questionnaireItem + .linkId, ) .isEqualTo("parent-question") } @@ -5519,9 +5673,9 @@ class QuestionnaireViewModelTest { Coding().apply { code = "flyover" system = EXTENSION_ITEM_CONTROL_SYSTEM - } + }, ) - } + }, ) } @@ -5540,9 +5694,9 @@ class QuestionnaireViewModelTest { text = "flyover text" type = Questionnaire.QuestionnaireItemType.DISPLAY extension = listOf(itemControlExtensionWithFlyOverCode) - } + }, ) - } + }, ) } state.set(EXTRA_QUESTIONNAIRE_JSON_STRING, printer.encodeResourceToString(questionnaire)) @@ -5554,7 +5708,8 @@ class QuestionnaireViewModelTest { .getQuestionnaireItemViewItemList() .last() .asQuestion() - .questionnaireItem.linkId + .questionnaireItem + .linkId, ) .isEqualTo("parent-question") } @@ -5573,9 +5728,9 @@ class QuestionnaireViewModelTest { Coding().apply { code = DisplayItemControlType.HELP.extensionCode system = EXTENSION_ITEM_CONTROL_SYSTEM - } + }, ) - } + }, ) } @@ -5594,9 +5749,9 @@ class QuestionnaireViewModelTest { text = "help description" type = Questionnaire.QuestionnaireItemType.DISPLAY extension = listOf(itemControlExtensionWithHelpCode) - } + }, ) - } + }, ) } state.set(EXTRA_QUESTIONNAIRE_JSON_STRING, printer.encodeResourceToString(questionnaire)) @@ -5608,7 +5763,8 @@ class QuestionnaireViewModelTest { .getQuestionnaireItemViewItemList() .last() .asQuestion() - .questionnaireItem.linkId + .questionnaireItem + .linkId, ) .isEqualTo("parent-question") } @@ -5635,9 +5791,9 @@ class QuestionnaireViewModelTest { linkId = "page1-1" type = Questionnaire.QuestionnaireItemType.BOOLEAN text = "Question on page 1" - } + }, ) - } + }, ) addItem( Questionnaire.QuestionnaireItemComponent().apply { @@ -5649,9 +5805,9 @@ class QuestionnaireViewModelTest { linkId = "page2-1" type = Questionnaire.QuestionnaireItemType.BOOLEAN text = "Question on page 2" - } + }, ) - } + }, ) } val viewModel = createQuestionnaireViewModel(questionnaire) @@ -5673,7 +5829,7 @@ class QuestionnaireViewModelTest { language = Expression.ExpressionLanguage.TEXT_FHIRPATH.toCode() expression = "%resource.repeat(item).where(linkId='1' and answer.empty().not()).select(answer.value) = ${if (it) "true" else "false"}" - } + }, ) } } @@ -5687,9 +5843,9 @@ class QuestionnaireViewModelTest { Coding().apply { code = EXTENSION_DISPLAY_CATEGORY_INSTRUCTIONS system = EXTENSION_DISPLAY_CATEGORY_SYSTEM - } + }, ) - } + }, ) } val questionnaire: (List) -> Questionnaire = { @@ -5710,7 +5866,7 @@ class QuestionnaireViewModelTest { linkId = "1.1" text = "Text when no is selected" type = Questionnaire.QuestionnaireItemType.DISPLAY - } + }, ) .addItem( Questionnaire.QuestionnaireItemComponent().apply { @@ -5718,16 +5874,16 @@ class QuestionnaireViewModelTest { linkId = "1.2" text = "Text when yes is selected" type = Questionnaire.QuestionnaireItemType.DISPLAY - } + }, ) - } + }, ) } } state.set( EXTRA_QUESTIONNAIRE_JSON_STRING, - printer.encodeResourceToString(questionnaire(emptyList())) + printer.encodeResourceToString(questionnaire(emptyList())), ) // empty initial value @@ -5736,7 +5892,7 @@ class QuestionnaireViewModelTest { assertThat(viewModel.getQuestionnaireItemViewItemList().size).isEqualTo(1) // enabledDisplayItems is 0 when no choice is present assertThat( - viewModel.getQuestionnaireItemViewItemList()[0].asQuestion().enabledDisplayItems.size + viewModel.getQuestionnaireItemViewItemList()[0].asQuestion().enabledDisplayItems.size, ) .isEqualTo(0) } @@ -5747,10 +5903,10 @@ class QuestionnaireViewModelTest { printer.encodeResourceToString( questionnaire( listOf( - Questionnaire.QuestionnaireItemInitialComponent().apply { value = BooleanType(false) } - ) - ) - ) + Questionnaire.QuestionnaireItemInitialComponent().apply { value = BooleanType(false) }, + ), + ), + ), ) viewModel = QuestionnaireViewModel(context, state) @@ -5771,10 +5927,10 @@ class QuestionnaireViewModelTest { printer.encodeResourceToString( questionnaire( listOf( - Questionnaire.QuestionnaireItemInitialComponent().apply { value = BooleanType(true) } - ) - ) - ) + Questionnaire.QuestionnaireItemInitialComponent().apply { value = BooleanType(true) }, + ), + ), + ), ) viewModel = QuestionnaireViewModel(context, state) @@ -5799,7 +5955,7 @@ class QuestionnaireViewModelTest { Questionnaire.QuestionnaireItemComponent().apply { linkId = "a-age" type = Questionnaire.QuestionnaireItemType.INTEGER - } + }, ) addItem( Questionnaire.QuestionnaireItemComponent().apply { @@ -5812,10 +5968,10 @@ class QuestionnaireViewModelTest { this.language = "text/fhirpath" this.expression = "%resource.repeat(item).where(linkId='a-age' and answer.empty().not()).select('Notes for child of age ' + answer.value.toString() + ' years')" - } + }, ) } - } + }, ) } @@ -5832,7 +5988,8 @@ class QuestionnaireViewModelTest { viewModel.questionnaireStateFlow.collect { descriptionResponseItem = it.items - .find { it.asQuestion().questionnaireItem.linkId == "a-description" }!!.asQuestion() + .find { it.asQuestion().questionnaireItem.linkId == "a-description" }!! + .asQuestion() this@launch.cancel() } } @@ -5850,14 +6007,14 @@ class QuestionnaireViewModelTest { listOf( QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent().apply { this.value = IntegerType(2) - } + }, ), - null + null, ) } assertThat( - ageItemUpdated.getQuestionnaireResponseItem().answer.first().valueIntegerType.value + ageItemUpdated.getQuestionnaireResponseItem().answer.first().valueIntegerType.value, ) .isEqualTo(2) @@ -5883,7 +6040,7 @@ class QuestionnaireViewModelTest { name = "A" language = "text/fhirpath" expression = "1" - } + }, ) } addItem( @@ -5897,7 +6054,7 @@ class QuestionnaireViewModelTest { name = "B" language = "text/fhirpath" expression = "2" - } + }, ) } textElement.addExtension().apply { @@ -5906,10 +6063,10 @@ class QuestionnaireViewModelTest { Expression().apply { this.language = "text/fhirpath" this.expression = "'Sum of variables is ' + ( %A + %B ).toString() " - } + }, ) } - } + }, ) } @@ -5931,14 +6088,14 @@ class QuestionnaireViewModelTest { enableReviewPage: Boolean = false, showReviewPageFirst: Boolean = false, readOnlyMode: Boolean = false, - showSubmitButton: Boolean? = null + showSubmitButton: Boolean? = null, ): QuestionnaireViewModel { state.set(EXTRA_QUESTIONNAIRE_JSON_STRING, printer.encodeResourceToString(questionnaire)) questionnaireResponse?.let { state.set( EXTRA_QUESTIONNAIRE_RESPONSE_JSON_STRING, - printer.encodeResourceToString(questionnaireResponse) + printer.encodeResourceToString(questionnaireResponse), ) } enableReviewPage.let { state.set(EXTRA_ENABLE_REVIEW_PAGE, it) } @@ -5955,7 +6112,7 @@ class QuestionnaireViewModelTest { private fun QuestionnaireViewItem.getQuestionnaireResponseItem() = ReflectionHelpers.getField( this, - "questionnaireResponseItem" + "questionnaireResponseItem", ) /** @@ -5982,8 +6139,8 @@ class QuestionnaireViewModelTest { Coding().apply { code = "page" system = EXTENSION_ITEM_CONTROL_SYSTEM - } - ) + }, + ), ) } diff --git a/engine/src/main/java/com/google/android/fhir/sync/download/ResourceParamsBasedDownloadWorkManager.kt b/engine/src/main/java/com/google/android/fhir/sync/download/ResourceParamsBasedDownloadWorkManager.kt index de7052104f..94c95c0b23 100644 --- a/engine/src/main/java/com/google/android/fhir/sync/download/ResourceParamsBasedDownloadWorkManager.kt +++ b/engine/src/main/java/com/google/android/fhir/sync/download/ResourceParamsBasedDownloadWorkManager.kt @@ -102,30 +102,30 @@ class ResourceParamsBasedDownloadWorkManager( throw FHIRException(response.issueFirstRep.diagnostics) } - return if (response is Bundle && response.type == Bundle.BundleType.SEARCHSET) { - response.link - .firstOrNull { component -> component.relation == "next" } - ?.url?.let { next -> urlOfTheNextPagesToDownloadForAResource.add(next) } - - response.entry - .map { it.resource } - .also { resources -> - resources - .groupBy { it.resourceType } - .entries.map { map -> - map.value - .filter { it.meta.lastUpdated != null } - .let { - context.saveLastUpdatedTimestamp( - map.key, - it.maxOfOrNull { it.meta.lastUpdated }?.toTimeZoneString() - ) - } - } - } - } else { - emptyList() + if ((response !is Bundle || response.type != Bundle.BundleType.SEARCHSET)) { + return emptyList() } + + response.link + .firstOrNull { component -> component.relation == "next" } + ?.url?.let { next -> urlOfTheNextPagesToDownloadForAResource.add(next) } + + return response.entry + .map { it.resource } + .also { resources -> + resources + .groupBy { it.resourceType } + .entries.map { map -> + map.value + .filter { it.meta.lastUpdated != null } + .let { + context.saveLastUpdatedTimestamp( + map.key, + it.maxOfOrNull { it.meta.lastUpdated }?.toTimeZoneString() + ) + } + } + } } interface TimestampContext { diff --git a/engine/src/main/java/com/google/android/fhir/sync/upload/request/TransactionBundleGenerator.kt b/engine/src/main/java/com/google/android/fhir/sync/upload/request/TransactionBundleGenerator.kt index 2f4a698b5f..b068c4a222 100644 --- a/engine/src/main/java/com/google/android/fhir/sync/upload/request/TransactionBundleGenerator.kt +++ b/engine/src/main/java/com/google/android/fhir/sync/upload/request/TransactionBundleGenerator.kt @@ -76,7 +76,7 @@ class TransactionBundleGenerator( httpVerbToUseForCreate: Bundle.HTTPVerb, httpVerbToUseForUpdate: Bundle.HTTPVerb, generatedBundleSize: Int, - useETagForUpload: Boolean + useETagForUpload: Boolean, ): TransactionBundleGenerator { val createFunction = diff --git a/engine/src/test/java/com/google/android/fhir/sync/download/ResourceParamsBasedDownloadWorkManagerTest.kt b/engine/src/test/java/com/google/android/fhir/sync/download/ResourceParamsBasedDownloadWorkManagerTest.kt index 53dd0d79d2..e59cb98ae1 100644 --- a/engine/src/test/java/com/google/android/fhir/sync/download/ResourceParamsBasedDownloadWorkManagerTest.kt +++ b/engine/src/test/java/com/google/android/fhir/sync/download/ResourceParamsBasedDownloadWorkManagerTest.kt @@ -17,11 +17,14 @@ package com.google.android.fhir.sync.download import com.google.android.fhir.logicalId +import com.google.android.fhir.sync.DownloadRequest import com.google.android.fhir.sync.SyncDataParams import com.google.android.fhir.sync.UrlDownloadRequest import com.google.common.truth.Truth.assertThat -import kotlinx.coroutines.test.runBlockingTest +import kotlinx.coroutines.test.StandardTestDispatcher +import kotlinx.coroutines.test.runTest import org.hl7.fhir.exceptions.FHIRException +import org.hl7.fhir.r4.model.Binary import org.hl7.fhir.r4.model.Bundle import org.hl7.fhir.r4.model.OperationOutcome import org.hl7.fhir.r4.model.Patient @@ -35,78 +38,80 @@ import org.robolectric.RobolectricTestRunner class ResourceParamsBasedDownloadWorkManagerTest { @Test - fun getNextRequestUrl_shouldReturnNextResourceUrls() = runBlockingTest { - val downloadManager = - ResourceParamsBasedDownloadWorkManager( - mapOf( - ResourceType.Patient to mapOf(Patient.ADDRESS_CITY.paramName to "NAIROBI"), - ResourceType.Immunization to emptyMap(), - ResourceType.Observation to emptyMap(), - ), - TestResourceParamsBasedDownloadWorkManagerContext("2022-03-20") - ) + fun getNextRequestUrl_shouldReturnNextResourceUrls() = + runTest(StandardTestDispatcher()) { + val downloadManager = + ResourceParamsBasedDownloadWorkManager( + mapOf( + ResourceType.Patient to mapOf(Patient.ADDRESS_CITY.paramName to "NAIROBI"), + ResourceType.Immunization to emptyMap(), + ResourceType.Observation to emptyMap(), + ), + TestResourceParamsBasedDownloadWorkManagerContext("2022-03-20") + ) - val urlsToDownload = mutableListOf() - do { - val url = downloadManager.getNextRequest()?.let { (it as UrlDownloadRequest).url } - if (url != null) { - urlsToDownload.add(url) - } - } while (url != null) + val urlsToDownload = mutableListOf() + do { + val url = downloadManager.getNextRequest()?.let { (it as UrlDownloadRequest).url } + if (url != null) { + urlsToDownload.add(url) + } + } while (url != null) - assertThat(urlsToDownload) - .containsExactly( - "Patient?address-city=NAIROBI&_sort=_lastUpdated&_lastUpdated=gt2022-03-20", - "Observation?_sort=_lastUpdated&_lastUpdated=gt2022-03-20", - "Immunization?_sort=_lastUpdated&_lastUpdated=gt2022-03-20" - ) - } + assertThat(urlsToDownload) + .containsExactly( + "Patient?address-city=NAIROBI&_sort=_lastUpdated&_lastUpdated=gt2022-03-20", + "Observation?_sort=_lastUpdated&_lastUpdated=gt2022-03-20", + "Immunization?_sort=_lastUpdated&_lastUpdated=gt2022-03-20" + ) + } @Test - fun getNextRequestUrl_shouldReturnResourceAndPageUrlsAsNextUrls() = runBlockingTest { - val downloadManager = - ResourceParamsBasedDownloadWorkManager( - mapOf(ResourceType.Patient to emptyMap(), ResourceType.Observation to emptyMap()), - TestResourceParamsBasedDownloadWorkManagerContext("2022-03-20") - ) + fun getNextRequestUrl_shouldReturnResourceAndPageUrlsAsNextUrls() = + runTest(StandardTestDispatcher()) { + val downloadManager = + ResourceParamsBasedDownloadWorkManager( + mapOf(ResourceType.Patient to emptyMap(), ResourceType.Observation to emptyMap()), + TestResourceParamsBasedDownloadWorkManagerContext("2022-03-20") + ) - val urlsToDownload = mutableListOf() - do { - val url = downloadManager.getNextRequest()?.let { (it as UrlDownloadRequest).url } - if (url != null) { - urlsToDownload.add(url) - } - // Call process response so that It can add the next page url to be downloaded next. - when (url) { - "Patient?_sort=_lastUpdated&_lastUpdated=gt2022-03-20", - "Observation?_sort=_lastUpdated&_lastUpdated=gt2022-03-20" -> { - downloadManager.processResponse( - Bundle().apply { - type = Bundle.BundleType.SEARCHSET - addLink( - Bundle.BundleLinkComponent().apply { - relation = "next" - this.url = "http://url-to-next-page?token=pageToken" - } - ) - } - ) + val urlsToDownload = mutableListOf() + do { + val url = downloadManager.getNextRequest()?.let { (it as UrlDownloadRequest).url } + if (url != null) { + urlsToDownload.add(url) } - } - } while (url != null) - - assertThat(urlsToDownload) - .containsExactly( - "Patient?_sort=_lastUpdated&_lastUpdated=gt2022-03-20", - "http://url-to-next-page?token=pageToken", - "Observation?_sort=_lastUpdated&_lastUpdated=gt2022-03-20", - "http://url-to-next-page?token=pageToken" - ) - } + // Call process response so that It can add the next page url to be downloaded next. + when (url) { + "Patient?_sort=_lastUpdated&_lastUpdated=gt2022-03-20", + "Observation?_sort=_lastUpdated&_lastUpdated=gt2022-03-20" -> { + downloadManager.processResponse( + Bundle().apply { + type = Bundle.BundleType.SEARCHSET + addLink( + Bundle.BundleLinkComponent().apply { + relation = "next" + this.url = "http://url-to-next-page?token=pageToken" + } + ) + } + ) + } + } + } while (url != null) + + assertThat(urlsToDownload) + .containsExactly( + "Patient?_sort=_lastUpdated&_lastUpdated=gt2022-03-20", + "http://url-to-next-page?token=pageToken", + "Observation?_sort=_lastUpdated&_lastUpdated=gt2022-03-20", + "http://url-to-next-page?token=pageToken" + ) + } @Test fun getNextRequestUrl_withLastUpdatedTimeProvidedInContext_ShouldAppendGtPrefixToLastUpdatedSearchParam() = - runBlockingTest { + runTest(StandardTestDispatcher()) { val downloadManager = ResourceParamsBasedDownloadWorkManager( mapOf(ResourceType.Patient to emptyMap()), @@ -118,7 +123,7 @@ class ResourceParamsBasedDownloadWorkManagerTest { @Test fun getNextRequestUrl_withLastUpdatedSyncParamProvided_shouldReturnUrlWithExactProvidedLastUpdatedSyncParam() = - runBlockingTest { + runTest(StandardTestDispatcher()) { val downloadManager = ResourceParamsBasedDownloadWorkManager( mapOf( @@ -136,7 +141,7 @@ class ResourceParamsBasedDownloadWorkManagerTest { @Test fun getNextRequestUrl_withLastUpdatedSyncParamHavingGtPrefix_shouldReturnUrlWithExactProvidedLastUpdatedSyncParam() = - runBlockingTest { + runTest(StandardTestDispatcher()) { val downloadManager = ResourceParamsBasedDownloadWorkManager( mapOf(ResourceType.Patient to mapOf(SyncDataParams.LAST_UPDATED_KEY to "gt2022-06-28")), @@ -148,7 +153,7 @@ class ResourceParamsBasedDownloadWorkManagerTest { @Test fun getNextRequestUrl_withNullUpdatedTimeStamp_shouldReturnUrlWithoutLastUpdatedQueryParam() = - runBlockingTest { + runTest(StandardTestDispatcher()) { val downloadManager = ResourceParamsBasedDownloadWorkManager( mapOf(ResourceType.Patient to mapOf(Patient.ADDRESS_CITY.paramName to "NAIROBI")), @@ -160,7 +165,7 @@ class ResourceParamsBasedDownloadWorkManagerTest { @Test fun getNextRequestUrl_withEmptyUpdatedTimeStamp_shouldReturnUrlWithoutLastUpdatedQueryParam() = - runBlockingTest { + runTest(StandardTestDispatcher()) { val downloadManager = ResourceParamsBasedDownloadWorkManager( mapOf(ResourceType.Patient to mapOf(Patient.ADDRESS_CITY.paramName to "NAIROBI")), @@ -171,82 +176,32 @@ class ResourceParamsBasedDownloadWorkManagerTest { } @Test - fun `getSummaryRequestUrls should return resource summary urls`() = runBlockingTest { - val downloadManager = - ResourceParamsBasedDownloadWorkManager( - mapOf( - ResourceType.Patient to mapOf(Patient.ADDRESS_CITY.paramName to "NAIROBI"), - ResourceType.Immunization to emptyMap(), - ResourceType.Observation to emptyMap(), - ), - TestResourceParamsBasedDownloadWorkManagerContext("2022-03-20") - ) - - val urls = downloadManager.getSummaryRequestUrls() - - assertThat(urls.map { it.key }) - .containsExactly(ResourceType.Patient, ResourceType.Immunization, ResourceType.Observation) - assertThat(urls.map { it.value }) - .containsExactly( - "Patient?address-city=NAIROBI&_sort=_lastUpdated&_lastUpdated=gt2022-03-20&_summary=count", - "Immunization?_sort=_lastUpdated&_lastUpdated=gt2022-03-20&_summary=count", - "Observation?_sort=_lastUpdated&_lastUpdated=gt2022-03-20&_summary=count" - ) - } - - @Test - fun processResponse_withBundleTypeSearchSet_shouldReturnPatient() = runBlockingTest { - val downloadManager = - ResourceParamsBasedDownloadWorkManager( - emptyMap(), - NoOpResourceParamsBasedDownloadWorkManagerContext - ) - val response = - Bundle().apply { - type = Bundle.BundleType.SEARCHSET - addEntry( - Bundle.BundleEntryComponent().apply { - resource = Patient().apply { id = "Patient-Id-001" } - } - ) - addEntry( - Bundle.BundleEntryComponent().apply { - resource = Patient().apply { id = "Patient-Id-002" } - } + fun `getSummaryRequestUrls should return resource summary urls`() = + runTest(StandardTestDispatcher()) { + val downloadManager = + ResourceParamsBasedDownloadWorkManager( + mapOf( + ResourceType.Patient to mapOf(Patient.ADDRESS_CITY.paramName to "NAIROBI"), + ResourceType.Immunization to emptyMap(), + ResourceType.Observation to emptyMap(), + ), + TestResourceParamsBasedDownloadWorkManagerContext("2022-03-20") ) - } - val resources = downloadManager.processResponse(response) - assertThat(resources.map { it.logicalId }).containsExactly("Patient-Id-001", "Patient-Id-002") - } - @Test - fun processResponse_withTransactionResponseBundle_shouldReturnEmptyList() = runBlockingTest { - val downloadManager = - ResourceParamsBasedDownloadWorkManager( - emptyMap(), - NoOpResourceParamsBasedDownloadWorkManagerContext - ) - val response = - Bundle().apply { - type = Bundle.BundleType.TRANSACTIONRESPONSE - addEntry( - Bundle.BundleEntryComponent().apply { - resource = Patient().apply { id = "Patient-Id-001" } - } - ) - addEntry( - Bundle.BundleEntryComponent().apply { - resource = Patient().apply { id = "Patient-Id-002" } - } - ) - } + val urls = downloadManager.getSummaryRequestUrls() - val resources = downloadManager.processResponse(response) - assertThat(resources).hasSize(0) - } + assertThat(urls.map { it.key }) + .containsExactly(ResourceType.Patient, ResourceType.Immunization, ResourceType.Observation) + assertThat(urls.map { it.value }) + .containsExactly( + "Patient?address-city=NAIROBI&_sort=_lastUpdated&_lastUpdated=gt2022-03-20&_summary=count", + "Immunization?_sort=_lastUpdated&_lastUpdated=gt2022-03-20&_summary=count", + "Observation?_sort=_lastUpdated&_lastUpdated=gt2022-03-20&_summary=count" + ) + } @Test - fun processResponse_withOperationOutcome_shouldThrowException() { + fun `processResponse should throw exception including diagnostics from operation outcome`() { val downloadManager = ResourceParamsBasedDownloadWorkManager( emptyMap(), @@ -263,10 +218,125 @@ class ResourceParamsBasedDownloadWorkManagerTest { val exception = assertThrows(FHIRException::class.java) { - runBlockingTest { downloadManager.processResponse(response) } + runTest(StandardTestDispatcher()) { downloadManager.processResponse(response) } } + assertThat(exception.localizedMessage).isEqualTo("Server couldn't fulfil the request.") } + + @Test + fun `processResponse should return empty list for resource that is not a bundle`() = + runTest(StandardTestDispatcher()) { + val downloadManager = + ResourceParamsBasedDownloadWorkManager( + emptyMap(), + NoOpResourceParamsBasedDownloadWorkManagerContext + ) + val response = Binary().apply { contentType = "application/json" } + + assertThat(downloadManager.processResponse(response)).isEmpty() + } + + @Test + fun `processResponse should return empty list for bundle that is not a search set`() = + runTest(StandardTestDispatcher()) { + val downloadManager = + ResourceParamsBasedDownloadWorkManager( + emptyMap(), + NoOpResourceParamsBasedDownloadWorkManagerContext + ) + val response = + Bundle().apply { + type = Bundle.BundleType.TRANSACTIONRESPONSE + addEntry( + Bundle.BundleEntryComponent().apply { + resource = Patient().apply { id = "Patient-Id-001" } + } + ) + addEntry( + Bundle.BundleEntryComponent().apply { + resource = Patient().apply { id = "Patient-Id-002" } + } + ) + } + + assertThat(downloadManager.processResponse(response)).isEmpty() + } + + @Test + fun `processResponse should return resources for bundle search set`() = + runTest(StandardTestDispatcher()) { + val downloadManager = + ResourceParamsBasedDownloadWorkManager( + emptyMap(), + NoOpResourceParamsBasedDownloadWorkManagerContext + ) + val response = + Bundle().apply { + type = Bundle.BundleType.SEARCHSET + addEntry( + Bundle.BundleEntryComponent().apply { + resource = Patient().apply { id = "Patient-Id-001" } + } + ) + addEntry( + Bundle.BundleEntryComponent().apply { + resource = Patient().apply { id = "Patient-Id-002" } + } + ) + } + + assertThat(downloadManager.processResponse(response).map { it.logicalId }) + .containsExactly("Patient-Id-001", "Patient-Id-002") + } + + @Test + fun `processResponse should add next request`() = + runTest(StandardTestDispatcher()) { + val downloadManager = + ResourceParamsBasedDownloadWorkManager( + emptyMap(), + NoOpResourceParamsBasedDownloadWorkManagerContext + ) + val response = + Bundle().apply { + type = Bundle.BundleType.SEARCHSET + link = + mutableListOf( + Bundle.BundleLinkComponent().apply { + relation = "next" + url = "next_url" + } + ) + } + + downloadManager.processResponse(response) + + assertThat(downloadManager.getNextRequest()).isEqualTo(DownloadRequest.of("next_url")) + } + + @Test + fun `processResponse should not add next request if next url is missing`() = + runTest(StandardTestDispatcher()) { + val downloadManager = + ResourceParamsBasedDownloadWorkManager( + emptyMap(), + NoOpResourceParamsBasedDownloadWorkManagerContext + ) + val response = + Bundle().apply { + type = Bundle.BundleType.SEARCHSET + addEntry( + Bundle.BundleEntryComponent().apply { + resource = Patient().apply { id = "Patient-Id-001" } + } + ) + } + + downloadManager.processResponse(response) + + assertThat(downloadManager.getNextRequest()).isNull() + } } val NoOpResourceParamsBasedDownloadWorkManagerContext = diff --git a/engine/src/test/java/com/google/android/fhir/sync/upload/UploaderImplTest.kt b/engine/src/test/java/com/google/android/fhir/sync/upload/UploaderImplTest.kt index 49b75a8eb7..bc2da80244 100644 --- a/engine/src/test/java/com/google/android/fhir/sync/upload/UploaderImplTest.kt +++ b/engine/src/test/java/com/google/android/fhir/sync/upload/UploaderImplTest.kt @@ -40,7 +40,17 @@ import org.robolectric.RobolectricTestRunner class UploaderImplTest { @Test - fun `upload Bundle transaction should emit Success`() = runBlocking { + fun `upload should start`() = runBlocking { + val result = + UploaderImpl(BundleDataSource { Bundle() }, SquashedChangesUploadWorkManager()) + .upload(localChanges) + .toList() + + assertThat(result.first()).isInstanceOf(UploadState.Started::class.java) + } + + @Test + fun `upload should succeed if response is transaction response`() = runBlocking { val result = UploaderImpl( BundleDataSource { Bundle().apply { type = Bundle.BundleType.TRANSACTIONRESPONSE } }, @@ -59,17 +69,7 @@ class UploaderImplTest { } @Test - fun `upload Bundle transaction should emit Started state`() = runBlocking { - val result = - UploaderImpl(BundleDataSource { Bundle() }, SquashedChangesUploadWorkManager()) - .upload(localChanges) - .toList() - - assertThat(result.first()).isInstanceOf(UploadState.Started::class.java) - } - - @Test - fun `upload Bundle Transaction server error should emit Failure`() = runBlocking { + fun `upload should fail if response is operation outcome with issue`() = runBlocking { val result = UploaderImpl( BundleDataSource { @@ -93,7 +93,36 @@ class UploaderImplTest { } @Test - fun `upload Bundle transaction error during upload should emit Failure`() = runBlocking { + fun `upload should fail if response is empty operation outcome`() = runBlocking { + val result = + UploaderImpl( + BundleDataSource { OperationOutcome() }, + SquashedChangesUploadWorkManager(), + ) + .upload(localChanges) + .toList() + + assertThat(result).hasSize(2) + assertThat(result.last()).isInstanceOf(UploadState.Failure::class.java) + } + + @Test + fun `upload should fail if response is neither transaction response nor operation outcome`() = + runBlocking { + val result = + UploaderImpl( + BundleDataSource { Bundle().apply { type = Bundle.BundleType.SEARCHSET } }, + SquashedChangesUploadWorkManager(), + ) + .upload(localChanges) + .toList() + + assertThat(result).hasSize(2) + assertThat(result.last()).isInstanceOf(UploadState.Failure::class.java) + } + + @Test + fun `upload should fail if there is connection exception`() = runBlocking { val result = UploaderImpl( BundleDataSource { throw ConnectException("Failed to connect to server.") }, @@ -105,6 +134,7 @@ class UploaderImplTest { assertThat(result).hasSize(2) assertThat(result.last()).isInstanceOf(UploadState.Failure::class.java) } + companion object { val localChanges = listOf( diff --git a/engine/src/test/java/com/google/android/fhir/sync/upload/request/TransactionBundleGeneratorTest.kt b/engine/src/test/java/com/google/android/fhir/sync/upload/request/TransactionBundleGeneratorTest.kt index e9db8c43cf..56e41e72f9 100644 --- a/engine/src/test/java/com/google/android/fhir/sync/upload/request/TransactionBundleGeneratorTest.kt +++ b/engine/src/test/java/com/google/android/fhir/sync/upload/request/TransactionBundleGeneratorTest.kt @@ -27,6 +27,7 @@ import org.hl7.fhir.r4.model.Bundle import org.hl7.fhir.r4.model.HumanName import org.hl7.fhir.r4.model.Patient import org.hl7.fhir.r4.model.ResourceType +import org.junit.Assert.assertThrows import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner @@ -292,4 +293,146 @@ class TransactionBundleGeneratorTest { assertThat(result.first().resource.entry[0].request.ifMatch).isNull() assertThat(result.first().resource.entry[1].request.ifMatch).isNull() } + + @Test + fun `getGenerator() with supported Bundle HTTPVerbs should return TransactionBundleGenerator`() = + runBlocking { + val generator = + TransactionBundleGenerator.Factory.getGenerator( + Bundle.HTTPVerb.PUT, + Bundle.HTTPVerb.PATCH, + generatedBundleSize = 500, + useETagForUpload = true, + ) + + assertThat(generator).isInstanceOf(TransactionBundleGenerator::class.java) + } + + @Test + fun `getGenerator() should through exception for create by DELETE`() { + val exception = + assertThrows(IllegalArgumentException::class.java) { + runBlocking { + TransactionBundleGenerator.Factory.getGenerator( + Bundle.HTTPVerb.DELETE, + Bundle.HTTPVerb.PATCH, + generatedBundleSize = 500, + useETagForUpload = true, + ) + } + } + assertThat(exception.localizedMessage).isEqualTo("Creation using DELETE is not supported.") + } + + @Test + fun `getGenerator() should through exception for create by GET`() { + val exception = + assertThrows(IllegalArgumentException::class.java) { + runBlocking { + TransactionBundleGenerator.Factory.getGenerator( + Bundle.HTTPVerb.GET, + Bundle.HTTPVerb.PATCH, + generatedBundleSize = 500, + useETagForUpload = true, + ) + } + } + assertThat(exception.localizedMessage).isEqualTo("Creation using GET is not supported.") + } + + @Test + fun `getGenerator() should through exception for create by PATCH`() { + val exception = + assertThrows(IllegalArgumentException::class.java) { + runBlocking { + TransactionBundleGenerator.Factory.getGenerator( + Bundle.HTTPVerb.PATCH, + Bundle.HTTPVerb.PATCH, + generatedBundleSize = 500, + useETagForUpload = true, + ) + } + } + assertThat(exception.localizedMessage).isEqualTo("Creation using PATCH is not supported.") + } + + @Test + fun `getGenerator() should through exception for create by POST`() { + val exception = + assertThrows(IllegalArgumentException::class.java) { + runBlocking { + TransactionBundleGenerator.Factory.getGenerator( + Bundle.HTTPVerb.POST, + Bundle.HTTPVerb.PATCH, + generatedBundleSize = 500, + useETagForUpload = true, + ) + } + } + assertThat(exception.localizedMessage).isEqualTo("Creation using POST is not supported.") + } + + @Test + fun `getGenerator() should through exception for update by DELETE`() { + val exception = + assertThrows(IllegalArgumentException::class.java) { + runBlocking { + TransactionBundleGenerator.Factory.getGenerator( + Bundle.HTTPVerb.PUT, + Bundle.HTTPVerb.DELETE, + generatedBundleSize = 500, + useETagForUpload = true, + ) + } + } + assertThat(exception.localizedMessage).isEqualTo("Update using DELETE is not supported.") + } + + @Test + fun `getGenerator() should through exception for update by GET`() { + val exception = + assertThrows(IllegalArgumentException::class.java) { + runBlocking { + TransactionBundleGenerator.Factory.getGenerator( + Bundle.HTTPVerb.PUT, + Bundle.HTTPVerb.GET, + generatedBundleSize = 500, + useETagForUpload = true, + ) + } + } + assertThat(exception.localizedMessage).isEqualTo("Update using GET is not supported.") + } + + @Test + fun `getGenerator() should through exception for update by POST`() { + val exception = + assertThrows(IllegalArgumentException::class.java) { + runBlocking { + TransactionBundleGenerator.Factory.getGenerator( + Bundle.HTTPVerb.PUT, + Bundle.HTTPVerb.POST, + generatedBundleSize = 500, + useETagForUpload = true, + ) + } + } + assertThat(exception.localizedMessage).isEqualTo("Update using POST is not supported.") + } + + @Test + fun `getGenerator() should through exception for update by PUT`() { + val exception = + assertThrows(IllegalArgumentException::class.java) { + runBlocking { + TransactionBundleGenerator.Factory.getGenerator( + Bundle.HTTPVerb.PUT, + Bundle.HTTPVerb.PUT, + generatedBundleSize = 500, + useETagForUpload = true, + ) + } + } + assertThat(exception.localizedMessage).isEqualTo("Update using PUT is not supported.") + } }