diff --git a/iosHyperskillApp/iosHyperskillApp/Sources/Extensions/Shared/Model/BlockOptionsExtensions.swift b/iosHyperskillApp/iosHyperskillApp/Sources/Extensions/Shared/Model/BlockOptionsExtensions.swift index 8b4aaa2d5..eacf1f3ae 100644 --- a/iosHyperskillApp/iosHyperskillApp/Sources/Extensions/Shared/Model/BlockOptionsExtensions.swift +++ b/iosHyperskillApp/iosHyperskillApp/Sources/Extensions/Shared/Model/BlockOptionsExtensions.swift @@ -14,6 +14,7 @@ extension Block.Options { codeBlanksStrings: [String]? = nil, codeBlanksVariables: [String]? = nil, codeBlanksOperations: [String]? = nil, + codeBlanksAvailableConditions: Set? = nil, codeBlanksEnabled: Bool? = nil, codeBlanksTemplateString: String? = nil ) { @@ -28,6 +29,7 @@ extension Block.Options { codeBlanksStrings: codeBlanksStrings, codeBlanksVariables: codeBlanksVariables, codeBlanksOperations: codeBlanksOperations, + codeBlanksAvailableConditions: codeBlanksAvailableConditions, codeBlanksEnabled: codeBlanksEnabled.flatMap(KotlinBoolean.init(value:)), codeBlanksTemplateString: codeBlanksTemplateString ) diff --git a/shared/src/commonMain/kotlin/org/hyperskill/app/step/domain/model/Block.kt b/shared/src/commonMain/kotlin/org/hyperskill/app/step/domain/model/Block.kt index 57d2b5fc0..db254cd03 100644 --- a/shared/src/commonMain/kotlin/org/hyperskill/app/step/domain/model/Block.kt +++ b/shared/src/commonMain/kotlin/org/hyperskill/app/step/domain/model/Block.kt @@ -38,6 +38,8 @@ data class Block( val codeBlanksVariables: List? = null, @SerialName("code_blanks_operations") val codeBlanksOperations: List? = null, + @SerialName("code_blanks_available_conditions") + val codeBlanksAvailableConditions: Set? = null, @SerialName("code_blanks_enabled") val codeBlanksEnabled: Boolean? = null, @SerialName("code_blanks_template") diff --git a/shared/src/commonMain/kotlin/org/hyperskill/app/step_quiz_code_blanks/domain/model/template/CodeBlanksTemplateMapper.kt b/shared/src/commonMain/kotlin/org/hyperskill/app/step_quiz_code_blanks/domain/model/template/CodeBlanksTemplateMapper.kt index eef0e448c..ec0d157dd 100644 --- a/shared/src/commonMain/kotlin/org/hyperskill/app/step_quiz_code_blanks/domain/model/template/CodeBlanksTemplateMapper.kt +++ b/shared/src/commonMain/kotlin/org/hyperskill/app/step_quiz_code_blanks/domain/model/template/CodeBlanksTemplateMapper.kt @@ -50,7 +50,8 @@ internal object CodeBlanksTemplateMapper { indentLevel = entry.indentLevel, isDeleteForbidden = entry.isDeleteForbidden, suggestions = StepQuizCodeBlanksResolver.getSuggestionsForBlankCodeBlock( - isVariableSuggestionAvailable = StepQuizCodeBlanksResolver.isVariableSuggestionsAvailable(step) + isVariableSuggestionAvailable = StepQuizCodeBlanksResolver.isVariableSuggestionsAvailable(step), + availableConditions = step.block.options.codeBlanksAvailableConditions ?: emptySet() ) ) CodeBlockTemplateEntryType.PRINT -> @@ -194,7 +195,8 @@ internal object CodeBlanksTemplateMapper { indentLevel = 0, isDeleteForbidden = false, suggestions = StepQuizCodeBlanksResolver.getSuggestionsForBlankCodeBlock( - isVariableSuggestionAvailable = StepQuizCodeBlanksResolver.isVariableSuggestionsAvailable(step) + isVariableSuggestionAvailable = StepQuizCodeBlanksResolver.isVariableSuggestionsAvailable(step), + availableConditions = step.block.options.codeBlanksAvailableConditions ?: emptySet() ) ) ) diff --git a/shared/src/commonMain/kotlin/org/hyperskill/app/step_quiz_code_blanks/presentation/StepQuizCodeBlanksReducer.kt b/shared/src/commonMain/kotlin/org/hyperskill/app/step_quiz_code_blanks/presentation/StepQuizCodeBlanksReducer.kt index ce3e9db99..2a4ef46c6 100644 --- a/shared/src/commonMain/kotlin/org/hyperskill/app/step_quiz_code_blanks/presentation/StepQuizCodeBlanksReducer.kt +++ b/shared/src/commonMain/kotlin/org/hyperskill/app/step_quiz_code_blanks/presentation/StepQuizCodeBlanksReducer.kt @@ -200,7 +200,8 @@ class StepQuizCodeBlanksReducer( index = blankInsertIndex, indentLevel = blankIndentLevel, codeBlocks = this, - isVariableSuggestionAvailable = state.isVariableSuggestionsAvailable + isVariableSuggestionAvailable = state.isVariableSuggestionsAvailable, + availableConditions = state.availableConditions ) ) ) @@ -357,7 +358,8 @@ class StepQuizCodeBlanksReducer( index = activeCodeBlockIndex, indentLevel = activeCodeBlock.indentLevel, codeBlocks = this, - isVariableSuggestionAvailable = state.isVariableSuggestionsAvailable + isVariableSuggestionAvailable = state.isVariableSuggestionsAvailable, + availableConditions = state.availableConditions ) ) ) @@ -564,7 +566,8 @@ class StepQuizCodeBlanksReducer( index = insertIndex, indentLevel = indentLevel, codeBlocks = this, - isVariableSuggestionAvailable = state.isVariableSuggestionsAvailable + isVariableSuggestionAvailable = state.isVariableSuggestionsAvailable, + availableConditions = state.availableConditions ) ) ) @@ -699,7 +702,8 @@ class StepQuizCodeBlanksReducer( index = activeCodeBlockIndex, indentLevel = newIndentLevel, codeBlocks = this, - isVariableSuggestionAvailable = state.isVariableSuggestionsAvailable + isVariableSuggestionAvailable = state.isVariableSuggestionsAvailable, + availableConditions = state.availableConditions ) ) else -> activeCodeBlock.updatedIndentLevel(newIndentLevel) @@ -749,7 +753,8 @@ class StepQuizCodeBlanksReducer( isActive = true, indentLevel = 0, suggestions = StepQuizCodeBlanksResolver.getSuggestionsForBlankCodeBlock( - isVariableSuggestionAvailable = StepQuizCodeBlanksResolver.isVariableSuggestionsAvailable(step) + isVariableSuggestionAvailable = StepQuizCodeBlanksResolver.isVariableSuggestionsAvailable(step), + availableConditions = step.block.options.codeBlanksAvailableConditions ?: emptySet() ) ) ) diff --git a/shared/src/commonMain/kotlin/org/hyperskill/app/step_quiz_code_blanks/presentation/StepQuizCodeBlanksResolver.kt b/shared/src/commonMain/kotlin/org/hyperskill/app/step_quiz_code_blanks/presentation/StepQuizCodeBlanksResolver.kt index 670cb4732..ecc0a2a8b 100644 --- a/shared/src/commonMain/kotlin/org/hyperskill/app/step_quiz_code_blanks/presentation/StepQuizCodeBlanksResolver.kt +++ b/shared/src/commonMain/kotlin/org/hyperskill/app/step_quiz_code_blanks/presentation/StepQuizCodeBlanksResolver.kt @@ -15,14 +15,33 @@ internal object StepQuizCodeBlanksResolver { index: Int = -1, indentLevel: Int = 0, codeBlocks: List = emptyList(), - isVariableSuggestionAvailable: Boolean + isVariableSuggestionAvailable: Boolean, + availableConditions: Set ): List = when { - areElifAndElseStatementsSuggestionsAvailable(index, indentLevel, codeBlocks) -> - listOf(Suggestion.Print, Suggestion.Variable, Suggestion.ElifStatement, Suggestion.ElseStatement) + areElifAndElseStatementsSuggestionsAvailable(index, indentLevel, codeBlocks, availableConditions) -> + buildList { + add(Suggestion.Print) + add(Suggestion.Variable) + + if (availableConditions.contains(Suggestion.ElifStatement.text)) { + add(Suggestion.ElifStatement) + } + + if (availableConditions.contains(Suggestion.ElseStatement.text)) { + add(Suggestion.ElseStatement) + } + } isVariableSuggestionAvailable -> - listOf(Suggestion.Print, Suggestion.Variable, Suggestion.IfStatement) + buildList { + add(Suggestion.Print) + add(Suggestion.Variable) + + if (availableConditions.contains(Suggestion.IfStatement.text)) { + add(Suggestion.IfStatement) + } + } else -> listOf(Suggestion.Print) @@ -31,9 +50,14 @@ internal object StepQuizCodeBlanksResolver { fun areElifAndElseStatementsSuggestionsAvailable( index: Int, indentLevel: Int, - codeBlocks: List + codeBlocks: List, + availableConditions: Set ): Boolean { - if (index < MINIMUM_POSSIBLE_INDEX_FOR_ELIF_AND_ELSE_STATEMENTS || codeBlocks.isEmpty()) { + if ( + index < MINIMUM_POSSIBLE_INDEX_FOR_ELIF_AND_ELSE_STATEMENTS || + codeBlocks.isEmpty() || + availableConditions.isEmpty() + ) { return false } diff --git a/shared/src/commonMain/kotlin/org/hyperskill/app/step_quiz_code_blanks/presentation/StepQuizCodeBlanksStateExtensions.kt b/shared/src/commonMain/kotlin/org/hyperskill/app/step_quiz_code_blanks/presentation/StepQuizCodeBlanksStateExtensions.kt index a55f0f21c..d6b5bf2eb 100644 --- a/shared/src/commonMain/kotlin/org/hyperskill/app/step_quiz_code_blanks/presentation/StepQuizCodeBlanksStateExtensions.kt +++ b/shared/src/commonMain/kotlin/org/hyperskill/app/step_quiz_code_blanks/presentation/StepQuizCodeBlanksStateExtensions.kt @@ -13,6 +13,9 @@ internal val StepQuizCodeBlanksFeature.State.isVariableSuggestionsAvailable: Boo StepQuizCodeBlanksResolver.isVariableSuggestionsAvailable(it) } ?: false +internal val StepQuizCodeBlanksFeature.State.Content.availableConditions: Set + get() = step.block.options.codeBlanksAvailableConditions ?: emptySet() + fun StepQuizCodeBlanksFeature.State.Content.createReply(): Reply = Reply.code( code = buildString { diff --git a/shared/src/commonTest/kotlin/org/hyperskill/step_quiz_code_blanks/presentation/StepQuizCodeBlanksReducerElifAndElseStatementsSuggestionsAvailabilityTest.kt b/shared/src/commonTest/kotlin/org/hyperskill/step_quiz_code_blanks/presentation/StepQuizCodeBlanksReducerElifAndElseStatementsSuggestionsAvailabilityTest.kt index 726b291af..671fc6501 100644 --- a/shared/src/commonTest/kotlin/org/hyperskill/step_quiz_code_blanks/presentation/StepQuizCodeBlanksReducerElifAndElseStatementsSuggestionsAvailabilityTest.kt +++ b/shared/src/commonTest/kotlin/org/hyperskill/step_quiz_code_blanks/presentation/StepQuizCodeBlanksReducerElifAndElseStatementsSuggestionsAvailabilityTest.kt @@ -7,12 +7,17 @@ import org.hyperskill.app.step_quiz_code_blanks.domain.model.CodeBlock import org.hyperskill.app.step_quiz_code_blanks.presentation.StepQuizCodeBlanksResolver class StepQuizCodeBlanksReducerElifAndElseStatementsSuggestionsAvailabilityTest { + companion object { + private val availableConditions = setOf("if", "elif", "else") + } + @Test fun `Should return false if index is less than 2`() { val result = StepQuizCodeBlanksResolver.areElifAndElseStatementsSuggestionsAvailable( index = 1, indentLevel = 0, - codeBlocks = emptyList() + codeBlocks = emptyList(), + availableConditions = availableConditions ) assertFalse(result) } @@ -22,7 +27,23 @@ class StepQuizCodeBlanksReducerElifAndElseStatementsSuggestionsAvailabilityTest val result = StepQuizCodeBlanksResolver.areElifAndElseStatementsSuggestionsAvailable( index = 2, indentLevel = 0, - codeBlocks = emptyList() + codeBlocks = emptyList(), + availableConditions = availableConditions + ) + assertFalse(result) + } + + @Test + fun `Should return false if availableConditions is empty`() { + val codeBlocks = listOf( + CodeBlock.IfStatement(indentLevel = 0, children = emptyList()), + CodeBlock.Print(indentLevel = 1, children = emptyList()) + ) + val result = StepQuizCodeBlanksResolver.areElifAndElseStatementsSuggestionsAvailable( + index = 2, + indentLevel = 0, + codeBlocks = codeBlocks, + availableConditions = emptySet() ) assertFalse(result) } @@ -36,7 +57,8 @@ class StepQuizCodeBlanksReducerElifAndElseStatementsSuggestionsAvailabilityTest val result = StepQuizCodeBlanksResolver.areElifAndElseStatementsSuggestionsAvailable( index = 2, indentLevel = 1, - codeBlocks = codeBlocks + codeBlocks = codeBlocks, + availableConditions = availableConditions ) assertFalse(result) } @@ -50,7 +72,8 @@ class StepQuizCodeBlanksReducerElifAndElseStatementsSuggestionsAvailabilityTest val result = StepQuizCodeBlanksResolver.areElifAndElseStatementsSuggestionsAvailable( index = 2, indentLevel = 0, - codeBlocks = codeBlocks + codeBlocks = codeBlocks, + availableConditions = availableConditions ) assertTrue(result) } @@ -66,7 +89,8 @@ class StepQuizCodeBlanksReducerElifAndElseStatementsSuggestionsAvailabilityTest val result = StepQuizCodeBlanksResolver.areElifAndElseStatementsSuggestionsAvailable( index = 4, indentLevel = 1, - codeBlocks = codeBlocks + codeBlocks = codeBlocks, + availableConditions = availableConditions ) assertTrue(result) } @@ -82,7 +106,8 @@ class StepQuizCodeBlanksReducerElifAndElseStatementsSuggestionsAvailabilityTest val result = StepQuizCodeBlanksResolver.areElifAndElseStatementsSuggestionsAvailable( index = 4, indentLevel = 0, - codeBlocks = codeBlocks + codeBlocks = codeBlocks, + availableConditions = availableConditions ) assertTrue(result) } diff --git a/shared/src/commonTest/kotlin/org/hyperskill/step_quiz_code_blanks/presentation/StepQuizCodeBlanksReducerInitializeTest.kt b/shared/src/commonTest/kotlin/org/hyperskill/step_quiz_code_blanks/presentation/StepQuizCodeBlanksReducerInitializeTest.kt index 52f60c050..1c48397cb 100644 --- a/shared/src/commonTest/kotlin/org/hyperskill/step_quiz_code_blanks/presentation/StepQuizCodeBlanksReducerInitializeTest.kt +++ b/shared/src/commonTest/kotlin/org/hyperskill/step_quiz_code_blanks/presentation/StepQuizCodeBlanksReducerInitializeTest.kt @@ -15,28 +15,42 @@ class StepQuizCodeBlanksReducerInitializeTest { private val reducer = StepQuizCodeBlanksReducer.stub() @Test - fun `Initialize should return Content state with active Blank and Print and Variable and If suggestions`() { - val step = Step.stub( - id = 1, - block = Block.stub(options = Block.Options(codeBlanksVariables = listOf("a", "b"))) + fun `Initialize should return Content state with active Blank and correct suggestions`() { + val blockOptions = listOf( + Block.Options(codeBlanksVariables = listOf("a", "b")), + Block.Options( + codeBlanksVariables = listOf("a", "b"), + codeBlanksAvailableConditions = setOf("if", "elif", "else") + ) + ) + val expectedSuggestions = listOf( + listOf(Suggestion.Print, Suggestion.Variable), + listOf(Suggestion.Print, Suggestion.Variable, Suggestion.IfStatement) ) - val message = StepQuizCodeBlanksFeature.InternalMessage.Initialize(step) - val (state, actions) = reducer.reduce(StepQuizCodeBlanksFeature.State.Idle, message) + blockOptions.forEachIndexed { index, options -> + val step = Step.stub( + id = 1, + block = Block.stub(options = options) + ) - val expectedState = StepQuizCodeBlanksFeature.State.Content( - step = step, - codeBlocks = listOf( - CodeBlock.Blank( - isActive = true, - suggestions = listOf(Suggestion.Print, Suggestion.Variable, Suggestion.IfStatement) + val message = StepQuizCodeBlanksFeature.InternalMessage.Initialize(step) + val (state, actions) = reducer.reduce(StepQuizCodeBlanksFeature.State.Idle, message) + + val expectedState = StepQuizCodeBlanksFeature.State.Content( + step = step, + codeBlocks = listOf( + CodeBlock.Blank( + isActive = true, + suggestions = expectedSuggestions[index] + ) ) ) - ) - assertTrue(state is StepQuizCodeBlanksFeature.State.Content) - assertEquals(expectedState.codeBlocks, state.codeBlocks) - assertTrue(actions.isEmpty()) + assertTrue(state is StepQuizCodeBlanksFeature.State.Content) + assertEquals(expectedState.codeBlocks, state.codeBlocks) + assertTrue(actions.isEmpty()) + } } @Test