diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml
index 357dfd7b5..46b86d224 100644
--- a/config/detekt/baseline.xml
+++ b/config/detekt/baseline.xml
@@ -158,6 +158,7 @@
MagicNumber:StepDelegate.kt$StepDelegate$25
MagicNumber:StepDelegate.kt$StepDelegate$5
MagicNumber:StepDelegate.kt$StepDelegate$50
+ MagicNumber:StepQuizCodeBlanksReducer.kt$StepQuizCodeBlanksReducer$2
MagicNumber:StepQuizCodeBlanksReducer.kt$StepQuizCodeBlanksReducer$47580L
MagicNumber:StudyPlanActivityAdapterDelegate.kt$StudyPlanActivityAdapterDelegate.ViewHolder$100f
MagicNumber:SubscriptionSyncLoading.kt$0.5f
@@ -205,7 +206,6 @@
ModifierReused:LeaderboardPlaceInfo.kt$Row( modifier = modifier, horizontalArrangement = Arrangement.SpaceBetween ) { Text( text = placeNumber.toString(), style = MaterialTheme.typography.body2, color = colorResource(id = R.color.color_on_surface_alpha_60), modifier = Modifier.align(Alignment.CenterVertically) ) if (placeNumber in 1..3) { Image( painter = painterResource( id = when (placeNumber) { 1 -> org.hyperskill.app.android.R.drawable.ic_leaderboard_first_place 2 -> org.hyperskill.app.android.R.drawable.ic_leaderboard_second_place 3 -> org.hyperskill.app.android.R.drawable.ic_leaderboard_third_place else -> error("Place icon should not be visible for the place number $placeNumber") } ), contentDescription = null, modifier = modifier .requiredSize(24.dp) .align(Alignment.CenterVertically) ) } }
ModifierWithoutDefault:BadgeImage.kt$modifier
NestedBlockDepth:AuthSocialWebViewClient.kt$AuthSocialWebViewClient$override fun shouldOverrideUrlLoading( view: WebView?, request: WebResourceRequest? ): Boolean
- NestedBlockDepth:StepQuizCodeBlanksReducer.kt$StepQuizCodeBlanksReducer$private fun setCodeBlockIsActive(codeBlock: CodeBlock, isActive: Boolean): CodeBlock
NestedBlockDepth:StepQuizCodeBlanksViewStateMapper.kt$StepQuizCodeBlanksViewStateMapper$private fun mapContentState( state: StepQuizCodeBlanksFeature.State.Content ): StepQuizCodeBlanksViewState.Content
PreviewPublic:BadgeCard.kt$BadgeCardPreview
PreviewPublic:BadgeCard.kt$LastLevelBadgeCardPreview
diff --git a/iosHyperskillApp/iosHyperskillApp.xcodeproj/project.pbxproj b/iosHyperskillApp/iosHyperskillApp.xcodeproj/project.pbxproj
index 3d14111a9..0fd2617ca 100644
--- a/iosHyperskillApp/iosHyperskillApp.xcodeproj/project.pbxproj
+++ b/iosHyperskillApp/iosHyperskillApp.xcodeproj/project.pbxproj
@@ -584,6 +584,8 @@
2CEEE03328916A3D00282849 /* ProblemOfDayViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CEEE03228916A3D00282849 /* ProblemOfDayViewModel.swift */; };
2CEEE03528916A6800282849 /* ProblemOfDayOutputProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CEEE03428916A6800282849 /* ProblemOfDayOutputProtocol.swift */; };
2CEEE03728917F1100282849 /* TimeIntervalExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CEEE03628917F1100282849 /* TimeIntervalExtensions.swift */; };
+ 2CEFEBE22C8AD43F0069567E /* StepQuizCodeBlanksElifStatementView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CEFEBE12C8AD43F0069567E /* StepQuizCodeBlanksElifStatementView.swift */; };
+ 2CEFEBE42C8AD5280069567E /* StepQuizCodeBlanksElseStatementView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CEFEBE32C8AD5280069567E /* StepQuizCodeBlanksElseStatementView.swift */; };
2CF0B4E629F9CEAF009C2A2D /* StudyPlanSectionActivitiesList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CF0B4E529F9CEAF009C2A2D /* StudyPlanSectionActivitiesList.swift */; };
2CF2DA3A27EC5B2D0055426D /* Assembly.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CF2DA3927EC5B2D0055426D /* Assembly.swift */; };
2CF34F912C2E8EAE0054477E /* CommentsContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CF34F902C2E8EAE0054477E /* CommentsContentView.swift */; };
@@ -1390,6 +1392,8 @@
2CEEE03228916A3D00282849 /* ProblemOfDayViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProblemOfDayViewModel.swift; sourceTree = ""; };
2CEEE03428916A6800282849 /* ProblemOfDayOutputProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProblemOfDayOutputProtocol.swift; sourceTree = ""; };
2CEEE03628917F1100282849 /* TimeIntervalExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimeIntervalExtensions.swift; sourceTree = ""; };
+ 2CEFEBE12C8AD43F0069567E /* StepQuizCodeBlanksElifStatementView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StepQuizCodeBlanksElifStatementView.swift; sourceTree = ""; };
+ 2CEFEBE32C8AD5280069567E /* StepQuizCodeBlanksElseStatementView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StepQuizCodeBlanksElseStatementView.swift; sourceTree = ""; };
2CF0B4E529F9CEAF009C2A2D /* StudyPlanSectionActivitiesList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StudyPlanSectionActivitiesList.swift; sourceTree = ""; };
2CF2DA3927EC5B2D0055426D /* Assembly.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Assembly.swift; sourceTree = ""; };
2CF34F902C2E8EAE0054477E /* CommentsContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommentsContentView.swift; sourceTree = ""; };
@@ -3639,6 +3643,8 @@
2CBEE4C52C87003A004486E8 /* Conditions */ = {
isa = PBXGroup;
children = (
+ 2CEFEBE12C8AD43F0069567E /* StepQuizCodeBlanksElifStatementView.swift */,
+ 2CEFEBE32C8AD5280069567E /* StepQuizCodeBlanksElseStatementView.swift */,
2CBEE4C62C870059004486E8 /* StepQuizCodeBlanksIfStatementView.swift */,
);
path = Conditions;
@@ -5235,6 +5241,7 @@
2C0DB90728644F2C001EA35E /* CodeEditorView.swift in Sources */,
2CDA98452944590800ADE539 /* ProfileStatisticsView.swift in Sources */,
2C8DD40E2AFB907000FD5359 /* ShareStreakAction.swift in Sources */,
+ 2CEFEBE22C8AD43F0069567E /* StepQuizCodeBlanksElifStatementView.swift in Sources */,
2C7CB6822ADFDB45006F78DA /* UIFont+SizeOfString.swift in Sources */,
2CC4AAF1280DB513002276A0 /* WebOAuthService.swift in Sources */,
2CF2DA3A27EC5B2D0055426D /* Assembly.swift in Sources */,
@@ -5583,6 +5590,7 @@
2C93AF2529B34FE6004639E0 /* StepQuizPyCharmAssembly.swift in Sources */,
2CDA98412944512D00ADE539 /* ProfileSkeletonView.swift in Sources */,
2CEDE70729965B4D0032D399 /* RestartApplicationLocalNotification.swift in Sources */,
+ 2CEFEBE42C8AD5280069567E /* StepQuizCodeBlanksElseStatementView.swift in Sources */,
2CACBCBC2B7A12F1006D3AB2 /* UsersInterviewWidgetView.swift in Sources */,
2CDA98432944524D00ADE539 /* HomeSkeletonView.swift in Sources */,
2C9D493D29F07015000599AB /* StudyPlanSectionErrorView.swift in Sources */,
diff --git a/iosHyperskillApp/iosHyperskillApp/Sources/Modules/StepQuizSubmodules/StepQuizCodeBlanks/Views/CodeBlocks/Conditions/StepQuizCodeBlanksElifStatementView.swift b/iosHyperskillApp/iosHyperskillApp/Sources/Modules/StepQuizSubmodules/StepQuizCodeBlanks/Views/CodeBlocks/Conditions/StepQuizCodeBlanksElifStatementView.swift
new file mode 100644
index 000000000..8f4c66aec
--- /dev/null
+++ b/iosHyperskillApp/iosHyperskillApp/Sources/Modules/StepQuizSubmodules/StepQuizCodeBlanks/Views/CodeBlocks/Conditions/StepQuizCodeBlanksElifStatementView.swift
@@ -0,0 +1,73 @@
+import shared
+import SwiftUI
+
+struct StepQuizCodeBlanksElifStatementView: View {
+ let elifStatementItem: StepQuizCodeBlanksViewStateCodeBlockItemElifStatement
+
+ let onChildTap: (StepQuizCodeBlanksViewStateCodeBlockChildItem) -> Void
+
+ var body: some View {
+ ScrollView(.horizontal, showsIndicators: false) {
+ HStack(alignment: .center, spacing: LayoutInsets.smallInset) {
+ Text("elif")
+ .font(StepQuizCodeBlanksAppearance.blankFont)
+ .foregroundColor(StepQuizCodeBlanksAppearance.blankTextColor)
+
+ ForEach(elifStatementItem.children, id: \.id) { child in
+ StepQuizCodeBlanksCodeBlockChildView(child: child, action: onChildTap)
+ }
+
+ Text(":")
+ .font(StepQuizCodeBlanksAppearance.blankFont)
+ .foregroundColor(StepQuizCodeBlanksAppearance.blankTextColor)
+ }
+ .padding(.horizontal, LayoutInsets.defaultInset)
+ .padding(.vertical, LayoutInsets.smallInset)
+ .background(Color(ColorPalette.violet400Alpha7))
+ .cornerRadius(StepQuizCodeBlanksAppearance.cornerRadius)
+ .padding(.horizontal)
+ }
+ .scrollBounceBehaviorBasedOnSize(axes: .horizontal)
+ }
+}
+
+#if DEBUG
+#Preview {
+ VStack {
+ StepQuizCodeBlanksElifStatementView(
+ elifStatementItem: StepQuizCodeBlanksViewStateCodeBlockItemElifStatement(
+ id: 0,
+ indentLevel: 0,
+ children: [
+ StepQuizCodeBlanksViewStateCodeBlockChildItem(id: 0, isActive: true, value: nil)
+ ]
+ ),
+ onChildTap: { _ in }
+ )
+
+ StepQuizCodeBlanksElifStatementView(
+ elifStatementItem: StepQuizCodeBlanksViewStateCodeBlockItemElifStatement(
+ id: 0,
+ indentLevel: 0,
+ children: [
+ StepQuizCodeBlanksViewStateCodeBlockChildItem(id: 0, isActive: true, value: "x")
+ ]
+ ),
+ onChildTap: { _ in }
+ )
+
+ StepQuizCodeBlanksElifStatementView(
+ elifStatementItem: StepQuizCodeBlanksViewStateCodeBlockItemElifStatement(
+ id: 0,
+ indentLevel: 0,
+ children: [
+ StepQuizCodeBlanksViewStateCodeBlockChildItem(id: 0, isActive: false, value: "x"),
+ StepQuizCodeBlanksViewStateCodeBlockChildItem(id: 1, isActive: true, value: nil)
+ ]
+ ),
+ onChildTap: { _ in }
+ )
+ }
+ .padding()
+}
+#endif
diff --git a/iosHyperskillApp/iosHyperskillApp/Sources/Modules/StepQuizSubmodules/StepQuizCodeBlanks/Views/CodeBlocks/Conditions/StepQuizCodeBlanksElseStatementView.swift b/iosHyperskillApp/iosHyperskillApp/Sources/Modules/StepQuizSubmodules/StepQuizCodeBlanks/Views/CodeBlocks/Conditions/StepQuizCodeBlanksElseStatementView.swift
new file mode 100644
index 000000000..91a75e90e
--- /dev/null
+++ b/iosHyperskillApp/iosHyperskillApp/Sources/Modules/StepQuizSubmodules/StepQuizCodeBlanks/Views/CodeBlocks/Conditions/StepQuizCodeBlanksElseStatementView.swift
@@ -0,0 +1,45 @@
+import shared
+import SwiftUI
+
+struct StepQuizCodeBlanksElseStatementView: View {
+ let elseStatementItem: StepQuizCodeBlanksViewStateCodeBlockItemElseStatement
+
+ var body: some View {
+ Text("else:")
+ .font(StepQuizCodeBlanksAppearance.blankFont)
+ .foregroundColor(StepQuizCodeBlanksAppearance.blankTextColor)
+ .padding(.horizontal, LayoutInsets.defaultInset)
+ .padding(.vertical, LayoutInsets.smallInset)
+ .frame(minHeight: StepQuizCodeBlanksCodeBlockChildTextView.Appearance.minHeight)
+ .background(Color(ColorPalette.violet400Alpha7))
+ .addBorder(
+ color: elseStatementItem.isActive ? StepQuizCodeBlanksAppearance.activeBorderColor : .clear,
+ width: elseStatementItem.isActive ? 1 : 0,
+ cornerRadius: StepQuizCodeBlanksAppearance.cornerRadius
+ )
+ .padding(.horizontal)
+ .animation(.default, value: elseStatementItem.isActive)
+ }
+}
+
+#if DEBUG
+#Preview {
+ StepQuizCodeBlanksElseStatementView(
+ elseStatementItem: StepQuizCodeBlanksViewStateCodeBlockItemElseStatement(
+ id: 0,
+ indentLevel: 0,
+ isActive: true
+ )
+ )
+}
+
+#Preview {
+ StepQuizCodeBlanksElseStatementView(
+ elseStatementItem: StepQuizCodeBlanksViewStateCodeBlockItemElseStatement(
+ id: 0,
+ indentLevel: 0,
+ isActive: false
+ )
+ )
+}
+#endif
diff --git a/iosHyperskillApp/iosHyperskillApp/Sources/Modules/StepQuizSubmodules/StepQuizCodeBlanks/Views/CodeBlocks/StepQuizCodeBlanksCodeBlocksView.swift b/iosHyperskillApp/iosHyperskillApp/Sources/Modules/StepQuizSubmodules/StepQuizCodeBlanks/Views/CodeBlocks/StepQuizCodeBlanksCodeBlocksView.swift
index 79f8d394f..9b5e4c634 100644
--- a/iosHyperskillApp/iosHyperskillApp/Sources/Modules/StepQuizSubmodules/StepQuizCodeBlanks/Views/CodeBlocks/StepQuizCodeBlanksCodeBlocksView.swift
+++ b/iosHyperskillApp/iosHyperskillApp/Sources/Modules/StepQuizSubmodules/StepQuizCodeBlanks/Views/CodeBlocks/StepQuizCodeBlanksCodeBlocksView.swift
@@ -78,6 +78,15 @@ struct StepQuizCodeBlanksCodeBlocksView: View {
ifStatementItem: ifStatementItem,
onChildTap: onChildTap
)
+ case .elifStatement(let elifStatementItem):
+ StepQuizCodeBlanksElifStatementView(
+ elifStatementItem: elifStatementItem,
+ onChildTap: onChildTap
+ )
+ case .elseStatement(let elseStatementItem):
+ StepQuizCodeBlanksElseStatementView(
+ elseStatementItem: elseStatementItem
+ )
}
}
}
diff --git a/shared/src/commonMain/kotlin/org/hyperskill/app/step_quiz_code_blanks/domain/model/CodeBlock.kt b/shared/src/commonMain/kotlin/org/hyperskill/app/step_quiz_code_blanks/domain/model/CodeBlock.kt
index 241028bbd..1266f9783 100644
--- a/shared/src/commonMain/kotlin/org/hyperskill/app/step_quiz_code_blanks/domain/model/CodeBlock.kt
+++ b/shared/src/commonMain/kotlin/org/hyperskill/app/step_quiz_code_blanks/domain/model/CodeBlock.kt
@@ -27,9 +27,15 @@ sealed class CodeBlock {
internal fun areAllChildrenUnselected(): Boolean =
children.all { it is CodeBlockChild.SelectSuggestion && it.selectedSuggestion == null }
+ internal fun areAllChildrenSelected(): Boolean =
+ children.all { it is CodeBlockChild.SelectSuggestion && it.selectedSuggestion != null }
+
internal fun hasAnySelectedChild(): Boolean =
children.any { it is CodeBlockChild.SelectSuggestion && it.selectedSuggestion != null }
+ internal fun hasAnyUnselectedChild(): Boolean =
+ children.any { it is CodeBlockChild.SelectSuggestion && it.selectedSuggestion == null }
+
internal data class Blank(
override val isActive: Boolean,
override val indentLevel: Int = 0,
@@ -38,7 +44,7 @@ sealed class CodeBlock {
override val children: List = emptyList()
override val analyticRepresentation: String
- get() = "Blank(isActive=$isActive, suggestions=$suggestions)"
+ get() = "Blank(isActive=$isActive, indentLevel=$indentLevel, suggestions=$suggestions)"
override fun toReplyString(): String = ""
}
@@ -52,7 +58,7 @@ sealed class CodeBlock {
override val suggestions: List = emptyList()
override val analyticRepresentation: String =
- "Print(children=$children)"
+ "Print(indentLevel=$indentLevel, children=$children)"
override fun toReplyString(): String =
buildString {
@@ -63,7 +69,7 @@ sealed class CodeBlock {
}
override fun toString(): String =
- "Print(children=$children)"
+ "Print(indentLevel=$indentLevel, children=$children)"
}
internal data class Variable(
@@ -81,7 +87,7 @@ sealed class CodeBlock {
override val suggestions: List = emptyList()
override val analyticRepresentation: String
- get() = "Variable(children=$children)"
+ get() = "Variable(indentLevel=$indentLevel, children=$children)"
override fun toReplyString(): String =
buildString {
@@ -92,7 +98,7 @@ sealed class CodeBlock {
}
override fun toString(): String =
- "Variable(children=$children)"
+ "Variable(indentLevel=$indentLevel, children=$children)"
}
internal data class IfStatement(
@@ -104,7 +110,7 @@ sealed class CodeBlock {
override val suggestions: List = emptyList()
override val analyticRepresentation: String
- get() = "IfStatement(children=$children)"
+ get() = "IfStatement(indentLevel=$indentLevel, children=$children)"
override fun toReplyString(): String =
buildString {
@@ -115,7 +121,46 @@ sealed class CodeBlock {
}
override fun toString(): String =
- "IfStatement(children=$children)"
+ "IfStatement(indentLevel=$indentLevel, children=$children)"
+ }
+
+ internal data class ElifStatement(
+ override val indentLevel: Int = 0,
+ override val children: List
+ ) : CodeBlock() {
+ override val isActive: Boolean = false
+
+ override val suggestions: List = emptyList()
+
+ override val analyticRepresentation: String
+ get() = "ElifStatement(indentLevel=$indentLevel, children=$children)"
+
+ override fun toReplyString(): String =
+ buildString {
+ append("elif ")
+ append(joinChildrenToReplyString(children))
+ append(":")
+ }
+
+ override fun toString(): String =
+ "ElifStatement(indentLevel=$indentLevel, children=$children)"
+ }
+
+ internal data class ElseStatement(
+ override val isActive: Boolean,
+ override val indentLevel: Int = 0
+ ) : CodeBlock() {
+ override val suggestions: List = emptyList()
+
+ override val children: List = emptyList()
+
+ override val analyticRepresentation: String
+ get() = "ElseStatement(isActive=$isActive, indentLevel=$indentLevel)"
+
+ override fun toReplyString(): String = "else:"
+
+ override fun toString(): String =
+ "ElseStatement(isActive=$isActive, indentLevel=$indentLevel)"
}
}
@@ -144,10 +189,12 @@ internal fun CodeBlock.Companion.joinChildrenToReplyString(children: List): CodeBlock =
when (this) {
- is CodeBlock.Blank -> this
+ is CodeBlock.Blank,
+ is CodeBlock.ElseStatement -> this
is CodeBlock.Print -> copy(children = children.cast())
is CodeBlock.Variable -> copy(children = children.cast())
is CodeBlock.IfStatement -> copy(children = children.cast())
+ is CodeBlock.ElifStatement -> copy(children = children.cast())
}
internal fun CodeBlock.updatedIndentLevel(indentLevel: Int): CodeBlock =
@@ -156,4 +203,6 @@ internal fun CodeBlock.updatedIndentLevel(indentLevel: Int): CodeBlock =
is CodeBlock.Print -> copy(indentLevel = indentLevel)
is CodeBlock.Variable -> copy(indentLevel = indentLevel)
is CodeBlock.IfStatement -> copy(indentLevel = indentLevel)
+ is CodeBlock.ElifStatement -> copy(indentLevel = indentLevel)
+ is CodeBlock.ElseStatement -> copy(indentLevel = indentLevel)
}
\ No newline at end of file
diff --git a/shared/src/commonMain/kotlin/org/hyperskill/app/step_quiz_code_blanks/domain/model/Suggestion.kt b/shared/src/commonMain/kotlin/org/hyperskill/app/step_quiz_code_blanks/domain/model/Suggestion.kt
index 0d95eaa5d..9ff56fa42 100644
--- a/shared/src/commonMain/kotlin/org/hyperskill/app/step_quiz_code_blanks/domain/model/Suggestion.kt
+++ b/shared/src/commonMain/kotlin/org/hyperskill/app/step_quiz_code_blanks/domain/model/Suggestion.kt
@@ -26,6 +26,20 @@ sealed class Suggestion {
"IfStatement(text='$text')"
}
+ data object ElifStatement : Suggestion() {
+ override val text: String = "elif"
+
+ override val analyticRepresentation: String =
+ "ElifStatement(text='$text')"
+ }
+
+ data object ElseStatement : Suggestion() {
+ override val text: String = "else"
+
+ override val analyticRepresentation: String =
+ "ElseStatement(text='$text')"
+ }
+
data class ConstantString(
override val text: String
) : Suggestion() {
diff --git a/shared/src/commonMain/kotlin/org/hyperskill/app/step_quiz_code_blanks/presentation/StepQuizCodeBlanksFeature.kt b/shared/src/commonMain/kotlin/org/hyperskill/app/step_quiz_code_blanks/presentation/StepQuizCodeBlanksFeature.kt
index 8cc1fe4e3..11857df8f 100644
--- a/shared/src/commonMain/kotlin/org/hyperskill/app/step_quiz_code_blanks/presentation/StepQuizCodeBlanksFeature.kt
+++ b/shared/src/commonMain/kotlin/org/hyperskill/app/step_quiz_code_blanks/presentation/StepQuizCodeBlanksFeature.kt
@@ -37,6 +37,9 @@ object StepQuizCodeBlanksFeature {
internal val codeBlanksVariablesSuggestions: List =
step.codeBlanksVariablesSuggestions()
+ internal val codeBlanksVariablesAndStringsSuggestions: List =
+ codeBlanksVariablesSuggestions + codeBlanksStringsSuggestions
+
internal val codeBlanksOperationsSuggestions: List =
step.codeBlanksOperationsSuggestions()
}
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 31f62faa4..be8532bdc 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
@@ -21,8 +21,8 @@ import org.hyperskill.app.step_quiz_code_blanks.presentation.StepQuizCodeBlanksF
import org.hyperskill.app.step_quiz_code_blanks.presentation.StepQuizCodeBlanksFeature.Message
import org.hyperskill.app.step_quiz_code_blanks.presentation.StepQuizCodeBlanksFeature.OnboardingState
import org.hyperskill.app.step_quiz_code_blanks.presentation.StepQuizCodeBlanksFeature.State
-import ru.nobird.app.core.model.cast
import ru.nobird.app.core.model.mutate
+import ru.nobird.app.core.model.slice
import ru.nobird.app.presentation.redux.reducer.StateReducer
private typealias StepQuizCodeBlanksReducerResult = Pair>
@@ -89,8 +89,7 @@ class StepQuizCodeBlanksReducer(
children = listOf(
CodeBlockChild.SelectSuggestion(
isActive = true,
- suggestions = state.codeBlanksVariablesSuggestions +
- state.codeBlanksStringsSuggestions,
+ suggestions = state.codeBlanksVariablesAndStringsSuggestions,
selectedSuggestion = null
)
)
@@ -117,16 +116,33 @@ class StepQuizCodeBlanksReducer(
children = listOf(
CodeBlockChild.SelectSuggestion(
isActive = true,
- suggestions = state.codeBlanksVariablesSuggestions +
- state.codeBlanksStringsSuggestions,
+ suggestions = state.codeBlanksVariablesAndStringsSuggestions,
selectedSuggestion = null
)
)
)
- else -> activeCodeBlock
+ Suggestion.ElifStatement ->
+ CodeBlock.ElifStatement(
+ indentLevel = activeCodeBlock.indentLevel,
+ children = listOf(
+ CodeBlockChild.SelectSuggestion(
+ isActive = true,
+ suggestions = state.codeBlanksVariablesAndStringsSuggestions,
+ selectedSuggestion = null
+ )
+ )
+ )
+ Suggestion.ElseStatement ->
+ CodeBlock.ElseStatement(
+ isActive = false,
+ indentLevel = activeCodeBlock.indentLevel
+ )
+ is Suggestion.ConstantString -> activeCodeBlock
}
+
is CodeBlock.Print,
- is CodeBlock.IfStatement -> {
+ is CodeBlock.IfStatement,
+ is CodeBlock.ElifStatement -> {
activeCodeBlock.activeChildIndex()?.let { activeChildIndex ->
val activeChild = activeCodeBlock.children[activeChildIndex] as CodeBlockChild.SelectSuggestion
val newChildren = activeCodeBlock.children
@@ -138,10 +154,10 @@ class StepQuizCodeBlanksReducer(
)
)
}
- .cast>()
activeCodeBlock.updatedChildren(newChildren)
} ?: activeCodeBlock
}
+
is CodeBlock.Variable -> {
activeCodeBlock.activeChildIndex()?.let { activeChildIndex ->
activeCodeBlock.copy(
@@ -168,13 +184,35 @@ class StepQuizCodeBlanksReducer(
)
} ?: activeCodeBlock
}
+
+ is CodeBlock.ElseStatement -> activeCodeBlock
}
- val newCodeBlocks = state.codeBlocks.mutate { set(activeCodeBlockIndex, newCodeBlock) }
+ val newCodeBlocks = state.codeBlocks.mutate {
+ set(activeCodeBlockIndex, newCodeBlock)
+
+ if (newCodeBlock is CodeBlock.ElseStatement && activeCodeBlock !== newCodeBlock) {
+ val blankInsertIndex = activeCodeBlockIndex + 1
+ val blankIndentLevel = newCodeBlock.indentLevel + 1
+ add(
+ blankInsertIndex,
+ createBlankCodeBlock(
+ isActive = true,
+ indentLevel = blankIndentLevel,
+ suggestions = getSuggestionsForBlankCodeBlock(
+ index = blankInsertIndex,
+ indentLevel = blankIndentLevel,
+ codeBlocks = this,
+ isVariableSuggestionAvailable = state.isVariableSuggestionsAvailable
+ )
+ )
+ )
+ }
+ }
val isFulfilledOnboardingPrintCodeBlock =
state.onboardingState is OnboardingState.HighlightSuggestions &&
- activeCodeBlock is CodeBlock.Print && activeCodeBlock.children.any { it.selectedSuggestion == null } &&
- newCodeBlock is CodeBlock.Print && newCodeBlock.children.all { it.selectedSuggestion != null }
+ activeCodeBlock is CodeBlock.Print && activeCodeBlock.hasAnyUnselectedChild() &&
+ newCodeBlock is CodeBlock.Print && newCodeBlock.areAllChildrenSelected()
val (onboardingState, onboardingActions) =
if (isFulfilledOnboardingPrintCodeBlock) {
OnboardingState.HighlightCallToActionButton to
@@ -250,7 +288,8 @@ class StepQuizCodeBlanksReducer(
val newChildren = when (targetCodeBlock) {
is CodeBlock.Print,
is CodeBlock.Variable,
- is CodeBlock.IfStatement -> {
+ is CodeBlock.IfStatement,
+ is CodeBlock.ElifStatement -> {
targetCodeBlock.children.mapIndexed { index, child ->
require(child is CodeBlockChild.SelectSuggestion)
if (index == message.codeBlockChildItem.id) {
@@ -260,7 +299,9 @@ class StepQuizCodeBlanksReducer(
}
}
}
- else -> null
+ null,
+ is CodeBlock.Blank,
+ is CodeBlock.ElseStatement -> null
}
val newCodeBlocks = state.codeBlocks.mutate {
@@ -317,17 +358,27 @@ class StepQuizCodeBlanksReducer(
}
removeAt(activeCodeBlockIndex)
}
- val replaceActiveCodeWithBlank = {
+ val replaceActiveCodeBlockWithBlank = {
set(
activeCodeBlockIndex,
createBlankCodeBlock(
isActive = true,
indentLevel = activeCodeBlock.indentLevel,
- isVariableSuggestionAvailable = state.isVariableSuggestionsAvailable
+ suggestions = getSuggestionsForBlankCodeBlock(
+ index = activeCodeBlockIndex,
+ indentLevel = activeCodeBlock.indentLevel,
+ codeBlocks = this,
+ isVariableSuggestionAvailable = state.isVariableSuggestionsAvailable
+ )
)
)
}
+ val isNextCodeBlockHasSameIndentLevelOrTrue = state.codeBlocks
+ .getOrNull(activeCodeBlockIndex + 1)
+ ?.let { it.indentLevel == activeCodeBlock.indentLevel }
+ ?: true
+
when (activeCodeBlock) {
is CodeBlock.Blank -> {
if (state.codeBlocks.size > 1) {
@@ -370,7 +421,7 @@ class StepQuizCodeBlanksReducer(
removeActiveCodeBlockAndSetNextActive()
else ->
- replaceActiveCodeWithBlank()
+ replaceActiveCodeBlockWithBlank()
}
}
is CodeBlock.Variable -> {
@@ -409,51 +460,63 @@ class StepQuizCodeBlanksReducer(
if (state.codeBlocks.size > 1) {
removeActiveCodeBlockAndSetNextActive()
} else {
- replaceActiveCodeWithBlank()
+ replaceActiveCodeBlockWithBlank()
}
}
}
- is CodeBlock.IfStatement -> {
+ is CodeBlock.IfStatement,
+ is CodeBlock.ElifStatement -> {
val activeChildIndex = activeCodeBlock.activeChildIndex() ?: return@mutate
- val activeChild = activeCodeBlock.children[activeChildIndex]
-
- val nextCodeBlock = state.codeBlocks.getOrNull(activeCodeBlockIndex + 1)
+ val activeChild = activeCodeBlock.children[activeChildIndex] as CodeBlockChild.SelectSuggestion
when {
- activeChild.selectedSuggestion != null ->
+ activeChild.selectedSuggestion != null -> {
+ val newChildren = activeCodeBlock.children.mutate {
+ set(
+ activeChildIndex,
+ activeChild.copy(selectedSuggestion = null)
+ )
+ }
set(
activeCodeBlockIndex,
- activeCodeBlock.copy(
- children = activeCodeBlock.children.mutate {
- set(
- activeChildIndex,
- activeChild.copy(selectedSuggestion = null)
- )
- }
- )
+ activeCodeBlock.updatedChildren(newChildren)
)
+ }
- activeChildIndex > 0 ->
+ activeChildIndex > 0 -> {
+ val newChildren = activeCodeBlock.children.mutate {
+ val previousChildIndex = activeChildIndex - 1
+ val previousChild = this[previousChildIndex] as CodeBlockChild.SelectSuggestion
+ set(
+ previousChildIndex,
+ previousChild.copy(isActive = true)
+ )
+
+ removeAt(activeChildIndex)
+ }
set(
activeCodeBlockIndex,
- activeCodeBlock.copy(
- children = activeCodeBlock.children.mutate {
- set(
- activeChildIndex - 1,
- this[activeChildIndex - 1].copy(isActive = true)
- )
- removeAt(activeChildIndex)
- }
- )
+ activeCodeBlock.updatedChildren(newChildren)
)
+ }
(activeChildIndex == 0 || activeCodeBlock.areAllChildrenUnselected()) &&
- (nextCodeBlock?.let { it.indentLevel == activeCodeBlock.indentLevel } ?: true) ->
+ isNextCodeBlockHasSameIndentLevelOrTrue -> {
if (state.codeBlocks.size > 1) {
removeActiveCodeBlockAndSetNextActive()
} else {
- replaceActiveCodeWithBlank()
+ replaceActiveCodeBlockWithBlank()
}
+ }
+ }
+ }
+ is CodeBlock.ElseStatement -> {
+ if (isNextCodeBlockHasSameIndentLevelOrTrue) {
+ if (state.codeBlocks.size > 1) {
+ removeActiveCodeBlockAndSetNextActive()
+ } else {
+ replaceActiveCodeBlockWithBlank()
+ }
}
}
}
@@ -484,7 +547,9 @@ class StepQuizCodeBlanksReducer(
return if (activeCodeBlock != null) {
val indentLevel =
when (activeCodeBlock) {
- is CodeBlock.IfStatement -> activeCodeBlock.indentLevel + 1
+ is CodeBlock.IfStatement,
+ is CodeBlock.ElifStatement,
+ is CodeBlock.ElseStatement -> activeCodeBlock.indentLevel + 1
else -> activeCodeBlock.indentLevel
}
@@ -493,15 +558,23 @@ class StepQuizCodeBlanksReducer(
activeCodeBlockIndex,
setCodeBlockIsActive(codeBlock = state.codeBlocks[activeCodeBlockIndex], isActive = false)
)
+
+ val insertIndex = activeCodeBlockIndex + 1
add(
- activeCodeBlockIndex + 1,
+ insertIndex,
createBlankCodeBlock(
isActive = true,
indentLevel = indentLevel,
- isVariableSuggestionAvailable = state.isVariableSuggestionsAvailable
+ suggestions = getSuggestionsForBlankCodeBlock(
+ index = insertIndex,
+ indentLevel = indentLevel,
+ codeBlocks = this,
+ isVariableSuggestionAvailable = state.isVariableSuggestionsAvailable
+ )
)
)
}
+
state.copy(codeBlocks = newCodeBlocks) to actions
} else {
state to actions
@@ -534,7 +607,8 @@ class StepQuizCodeBlanksReducer(
val newChildren = when (activeCodeBlock) {
is CodeBlock.Print,
is CodeBlock.Variable,
- is CodeBlock.IfStatement -> {
+ is CodeBlock.IfStatement,
+ is CodeBlock.ElifStatement -> {
activeCodeBlock.activeChildIndex()?.let { activeChildIndex ->
val activeChild = activeCodeBlock.children[activeChildIndex] as CodeBlockChild.SelectSuggestion
@@ -561,15 +635,14 @@ class StepQuizCodeBlanksReducer(
selectedSuggestion = null
)
- activeCodeBlock.children
- .mutate {
- set(activeChildIndex, activeChild.copy(isActive = false))
- add(activeChildIndex + 1, newChild)
- }
- .cast>()
+ activeCodeBlock.children.mutate {
+ set(activeChildIndex, activeChild.copy(isActive = false))
+ add(activeChildIndex + 1, newChild)
+ }
}
}
- else -> null
+ is CodeBlock.Blank,
+ is CodeBlock.ElseStatement -> null
}
val newCodeBlocks = state.codeBlocks.mutate {
@@ -612,7 +685,18 @@ class StepQuizCodeBlanksReducer(
codeBlocks = state.codeBlocks.mutate {
set(
activeCodeBlockIndex,
- activeCodeBlock.updatedIndentLevel(newIndentLevel)
+ when (activeCodeBlock) {
+ is CodeBlock.Blank -> activeCodeBlock.copy(
+ indentLevel = newIndentLevel,
+ suggestions = getSuggestionsForBlankCodeBlock(
+ index = activeCodeBlockIndex,
+ indentLevel = newIndentLevel,
+ codeBlocks = this,
+ isVariableSuggestionAvailable = state.isVariableSuggestionsAvailable
+ )
+ )
+ else -> activeCodeBlock.updatedIndentLevel(newIndentLevel)
+ }
)
}
) to actions
@@ -621,9 +705,11 @@ class StepQuizCodeBlanksReducer(
private fun setCodeBlockIsActive(codeBlock: CodeBlock, isActive: Boolean): CodeBlock =
when (codeBlock) {
is CodeBlock.Blank -> codeBlock.copy(isActive = isActive)
+ is CodeBlock.ElseStatement -> codeBlock.copy(isActive = isActive)
is CodeBlock.Print,
is CodeBlock.Variable,
- is CodeBlock.IfStatement -> {
+ is CodeBlock.IfStatement,
+ is CodeBlock.ElifStatement -> {
if (isActive) {
if (codeBlock.activeChild() != null) {
codeBlock
@@ -651,17 +737,51 @@ class StepQuizCodeBlanksReducer(
private fun createBlankCodeBlock(
isActive: Boolean,
indentLevel: Int,
- isVariableSuggestionAvailable: Boolean
+ suggestions: List
): CodeBlock.Blank =
CodeBlock.Blank(
isActive = isActive,
indentLevel = indentLevel,
- suggestions = if (isVariableSuggestionAvailable) {
+ suggestions = suggestions
+ )
+
+ private fun getSuggestionsForBlankCodeBlock(
+ index: Int = -1,
+ indentLevel: Int = 0,
+ codeBlocks: List = emptyList(),
+ isVariableSuggestionAvailable: Boolean
+ ): List =
+ when {
+ areElifAndElseStatementsSuggestionsAvailable(index, indentLevel, codeBlocks) ->
+ listOf(Suggestion.Print, Suggestion.Variable, Suggestion.ElifStatement, Suggestion.ElseStatement)
+
+ isVariableSuggestionAvailable ->
listOf(Suggestion.Print, Suggestion.Variable, Suggestion.IfStatement)
- } else {
+
+ else ->
listOf(Suggestion.Print)
- }
- )
+ }
+
+ internal fun areElifAndElseStatementsSuggestionsAvailable(
+ index: Int,
+ indentLevel: Int,
+ codeBlocks: List
+ ): Boolean {
+ if (index < 2 || codeBlocks.isEmpty()) {
+ return false
+ }
+
+ val previousCodeBlock = codeBlocks
+ .slice(to = index)
+ .reversed()
+ .firstOrNull { it.indentLevel == indentLevel }
+
+ return when (previousCodeBlock) {
+ is CodeBlock.IfStatement,
+ is CodeBlock.ElifStatement -> true
+ else -> false
+ }
+ }
private fun createInitialCodeBlocks(step: Step): List =
if (step.id == 47580L) {
@@ -714,7 +834,9 @@ class StepQuizCodeBlanksReducer(
createBlankCodeBlock(
isActive = true,
indentLevel = 0,
- isVariableSuggestionAvailable = StepQuizCodeBlanksFeature.isVariableSuggestionsAvailable(step)
+ suggestions = getSuggestionsForBlankCodeBlock(
+ isVariableSuggestionAvailable = StepQuizCodeBlanksFeature.isVariableSuggestionsAvailable(step)
+ )
)
)
} else {
@@ -722,7 +844,9 @@ class StepQuizCodeBlanksReducer(
createBlankCodeBlock(
isActive = true,
indentLevel = 0,
- isVariableSuggestionAvailable = StepQuizCodeBlanksFeature.isVariableSuggestionsAvailable(step)
+ suggestions = getSuggestionsForBlankCodeBlock(
+ isVariableSuggestionAvailable = StepQuizCodeBlanksFeature.isVariableSuggestionsAvailable(step)
+ )
)
)
}
diff --git a/shared/src/commonMain/kotlin/org/hyperskill/app/step_quiz_code_blanks/view/mapper/StepQuizCodeBlanksViewStateMapper.kt b/shared/src/commonMain/kotlin/org/hyperskill/app/step_quiz_code_blanks/view/mapper/StepQuizCodeBlanksViewStateMapper.kt
index 899f8f17d..fb7f1d6b9 100644
--- a/shared/src/commonMain/kotlin/org/hyperskill/app/step_quiz_code_blanks/view/mapper/StepQuizCodeBlanksViewStateMapper.kt
+++ b/shared/src/commonMain/kotlin/org/hyperskill/app/step_quiz_code_blanks/view/mapper/StepQuizCodeBlanksViewStateMapper.kt
@@ -23,10 +23,13 @@ object StepQuizCodeBlanksViewStateMapper {
val suggestions =
when (activeCodeBlock) {
- is CodeBlock.Blank -> activeCodeBlock.suggestions
+ is CodeBlock.Blank ->
+ activeCodeBlock.suggestions
+
is CodeBlock.Print,
is CodeBlock.Variable,
- is CodeBlock.IfStatement ->
+ is CodeBlock.IfStatement,
+ is CodeBlock.ElifStatement ->
(activeCodeBlock.activeChild() as? CodeBlockChild.SelectSuggestion)?.let {
if (it.selectedSuggestion == null) {
it.suggestions
@@ -34,7 +37,9 @@ object StepQuizCodeBlanksViewStateMapper {
emptyList()
}
}
- null -> emptyList()
+
+ null,
+ is CodeBlock.ElseStatement -> emptyList()
} ?: emptyList()
val isDeleteButtonEnabled =
@@ -55,13 +60,16 @@ object StepQuizCodeBlanksViewStateMapper {
}
} ?: false
}
- is CodeBlock.IfStatement -> {
+ is CodeBlock.IfStatement,
+ is CodeBlock.ElifStatement -> {
activeCodeBlock.activeChildIndex()?.let { activeChildIndex ->
+ val activeChild = activeCodeBlock.children[activeChildIndex] as CodeBlockChild.SelectSuggestion
+
when {
activeChildIndex > 0 ->
true
- activeCodeBlock.children[activeChildIndex].selectedSuggestion != null ->
+ activeChild.selectedSuggestion != null ->
true
else ->
@@ -70,13 +78,17 @@ object StepQuizCodeBlanksViewStateMapper {
}
} ?: false
}
+ is CodeBlock.ElseStatement ->
+ codeBlocks.getOrNull(activeCodeBlockIndex + 1)
+ ?.let { it.indentLevel == activeCodeBlock.indentLevel } ?: true
null -> false
}
val isSpaceButtonHidden = if (state.codeBlanksOperationsSuggestions.isNotEmpty()) {
when (activeCodeBlock) {
is CodeBlock.Print,
- is CodeBlock.IfStatement -> {
+ is CodeBlock.IfStatement,
+ is CodeBlock.ElifStatement -> {
val activeChild = activeCodeBlock.activeChild() as? CodeBlockChild.SelectSuggestion
activeChild?.selectedSuggestion == null
}
@@ -88,17 +100,26 @@ object StepQuizCodeBlanksViewStateMapper {
true
}
}
- else -> true
+ null,
+ is CodeBlock.Blank,
+ is CodeBlock.ElseStatement -> true
}
} else {
true
}
+ val isPreviousCodeBlockCondition =
+ when (activeCodeBlockIndex?.let { state.codeBlocks.getOrNull(it - 1) }) {
+ is CodeBlock.IfStatement,
+ is CodeBlock.ElifStatement,
+ is CodeBlock.ElseStatement -> true
+ else -> false
+ }
val isDecreaseIndentLevelButtonHidden =
when {
activeCodeBlock == null -> true
activeCodeBlock.indentLevel < 1 -> true
- state.codeBlocks.getOrNull(activeCodeBlockIndex - 1) is CodeBlock.IfStatement -> true
+ isPreviousCodeBlockCondition -> true
else -> false
}
@@ -141,6 +162,18 @@ object StepQuizCodeBlanksViewStateMapper {
indentLevel = codeBlock.indentLevel,
children = codeBlock.children.mapIndexed(::mapCodeBlockChild)
)
+ is CodeBlock.ElifStatement ->
+ StepQuizCodeBlanksViewState.CodeBlockItem.ElifStatement(
+ id = index,
+ indentLevel = codeBlock.indentLevel,
+ children = codeBlock.children.mapIndexed(::mapCodeBlockChild)
+ )
+ is CodeBlock.ElseStatement ->
+ StepQuizCodeBlanksViewState.CodeBlockItem.ElseStatement(
+ id = index,
+ indentLevel = codeBlock.indentLevel,
+ isActive = codeBlock.isActive
+ )
}
private fun mapCodeBlockChild(
diff --git a/shared/src/commonMain/kotlin/org/hyperskill/app/step_quiz_code_blanks/view/model/StepQuizCodeBlanksViewState.kt b/shared/src/commonMain/kotlin/org/hyperskill/app/step_quiz_code_blanks/view/model/StepQuizCodeBlanksViewState.kt
index 0cddcf03b..2bf809df5 100644
--- a/shared/src/commonMain/kotlin/org/hyperskill/app/step_quiz_code_blanks/view/model/StepQuizCodeBlanksViewState.kt
+++ b/shared/src/commonMain/kotlin/org/hyperskill/app/step_quiz_code_blanks/view/model/StepQuizCodeBlanksViewState.kt
@@ -59,6 +59,20 @@ sealed interface StepQuizCodeBlanksViewState {
override val indentLevel: Int = 0,
override val children: List
) : CodeBlockItem
+
+ data class ElifStatement(
+ override val id: Int,
+ override val indentLevel: Int = 0,
+ override val children: List
+ ) : CodeBlockItem
+
+ data class ElseStatement(
+ override val id: Int,
+ override val indentLevel: Int = 0,
+ val isActive: Boolean
+ ) : CodeBlockItem {
+ override val children: List = emptyList()
+ }
}
data class CodeBlockChildItem(
diff --git a/shared/src/commonTest/kotlin/org/hyperskill/step_quiz_code_blanks/presentation/StepQuizCodeBlanksReducerCodeBlockChildClickedTest.kt b/shared/src/commonTest/kotlin/org/hyperskill/step_quiz_code_blanks/presentation/StepQuizCodeBlanksReducerCodeBlockChildClickedTest.kt
index feea44ebf..9c864bf95 100644
--- a/shared/src/commonTest/kotlin/org/hyperskill/step_quiz_code_blanks/presentation/StepQuizCodeBlanksReducerCodeBlockChildClickedTest.kt
+++ b/shared/src/commonTest/kotlin/org/hyperskill/step_quiz_code_blanks/presentation/StepQuizCodeBlanksReducerCodeBlockChildClickedTest.kt
@@ -53,6 +53,43 @@ class StepQuizCodeBlanksReducerCodeBlockChildClickedTest {
assertContainsCodeBlockChildClickedAnalyticEvent(actions)
}
+ @Test
+ fun `CodeBlockChildClicked should not update state if target code block is Blank`() {
+ val initialState = StepQuizCodeBlanksFeature.State.Content.stub(
+ codeBlocks = listOf(
+ CodeBlock.Blank(
+ isActive = false,
+ suggestions = emptyList()
+ )
+ )
+ )
+
+ val message = StepQuizCodeBlanksFeature.Message.CodeBlockChildClicked(
+ codeBlockItem = StepQuizCodeBlanksViewState.CodeBlockItem.Blank(id = 0, isActive = false),
+ codeBlockChildItem = StepQuizCodeBlanksViewState.CodeBlockChildItem(id = 0, isActive = false, value = null)
+ )
+ val (state, actions) = reducer.reduce(initialState, message)
+
+ assertEquals(initialState, state)
+ assertContainsCodeBlockChildClickedAnalyticEvent(actions)
+ }
+
+ @Test
+ fun `CodeBlockChildClicked should not update state if target code block is ElseStatement`() {
+ val initialState = StepQuizCodeBlanksFeature.State.Content.stub(
+ codeBlocks = listOf(CodeBlock.ElseStatement(isActive = false))
+ )
+
+ val message = StepQuizCodeBlanksFeature.Message.CodeBlockChildClicked(
+ codeBlockItem = StepQuizCodeBlanksViewState.CodeBlockItem.ElseStatement(id = 0, isActive = false),
+ codeBlockChildItem = StepQuizCodeBlanksViewState.CodeBlockChildItem(id = 0, isActive = false, value = null)
+ )
+ val (state, actions) = reducer.reduce(initialState, message)
+
+ assertEquals(initialState, state)
+ assertContainsCodeBlockChildClickedAnalyticEvent(actions)
+ }
+
@Test
fun `CodeBlockChildClicked should update state to activate the clicked Variable child`() {
val initialState = StepQuizCodeBlanksFeature.State.Content.stub(
@@ -153,6 +190,86 @@ class StepQuizCodeBlanksReducerCodeBlockChildClickedTest {
assertContainsCodeBlockChildClickedAnalyticEvent(actions)
}
+ @Test
+ fun `CodeBlockChildClicked should update state to activate the clicked IfStatement child`() {
+ val initialState = StepQuizCodeBlanksFeature.State.Content.stub(
+ codeBlocks = listOf(
+ CodeBlock.IfStatement(
+ children = listOf(
+ CodeBlockChild.SelectSuggestion(
+ isActive = false,
+ suggestions = emptyList(),
+ selectedSuggestion = null
+ )
+ )
+ )
+ )
+ )
+
+ val message = StepQuizCodeBlanksFeature.Message.CodeBlockChildClicked(
+ codeBlockItem = StepQuizCodeBlanksViewState.CodeBlockItem.IfStatement(id = 0, children = emptyList()),
+ codeBlockChildItem = StepQuizCodeBlanksViewState.CodeBlockChildItem(id = 0, isActive = false, value = null)
+ )
+ val (state, actions) = reducer.reduce(initialState, message)
+
+ val expectedState = initialState.copy(
+ codeBlocks = listOf(
+ CodeBlock.IfStatement(
+ children = listOf(
+ CodeBlockChild.SelectSuggestion(
+ isActive = true,
+ suggestions = emptyList(),
+ selectedSuggestion = null
+ )
+ )
+ )
+ )
+ )
+
+ assertEquals(expectedState, state)
+ assertContainsCodeBlockChildClickedAnalyticEvent(actions)
+ }
+
+ @Test
+ fun `CodeBlockChildClicked should update state to activate the clicked ElifStatement child`() {
+ val initialState = StepQuizCodeBlanksFeature.State.Content.stub(
+ codeBlocks = listOf(
+ CodeBlock.ElifStatement(
+ children = listOf(
+ CodeBlockChild.SelectSuggestion(
+ isActive = false,
+ suggestions = emptyList(),
+ selectedSuggestion = null
+ )
+ )
+ )
+ )
+ )
+
+ val message = StepQuizCodeBlanksFeature.Message.CodeBlockChildClicked(
+ codeBlockItem = StepQuizCodeBlanksViewState.CodeBlockItem.ElifStatement(id = 0, children = emptyList()),
+ codeBlockChildItem = StepQuizCodeBlanksViewState.CodeBlockChildItem(id = 0, isActive = false, value = null)
+ )
+ val (state, actions) = reducer.reduce(initialState, message)
+
+ val expectedState = initialState.copy(
+ codeBlocks = listOf(
+ CodeBlock.ElifStatement(
+ children = listOf(
+ CodeBlockChild.SelectSuggestion(
+ isActive = true,
+ suggestions = emptyList(),
+ selectedSuggestion = null
+ )
+ )
+ )
+ )
+ )
+
+ assertEquals(expectedState, state)
+ assertContainsCodeBlockChildClickedAnalyticEvent(actions)
+ }
+
private fun assertContainsCodeBlockChildClickedAnalyticEvent(actions: Set) {
assertTrue {
actions.any {
diff --git a/shared/src/commonTest/kotlin/org/hyperskill/step_quiz_code_blanks/presentation/StepQuizCodeBlanksReducerCodeBlockClickedTest.kt b/shared/src/commonTest/kotlin/org/hyperskill/step_quiz_code_blanks/presentation/StepQuizCodeBlanksReducerCodeBlockClickedTest.kt
index 7a88b7b36..84deb40e1 100644
--- a/shared/src/commonTest/kotlin/org/hyperskill/step_quiz_code_blanks/presentation/StepQuizCodeBlanksReducerCodeBlockClickedTest.kt
+++ b/shared/src/commonTest/kotlin/org/hyperskill/step_quiz_code_blanks/presentation/StepQuizCodeBlanksReducerCodeBlockClickedTest.kt
@@ -26,6 +26,36 @@ class StepQuizCodeBlanksReducerCodeBlockClickedTest {
assertTrue(actions.isEmpty())
}
+ @Test
+ fun `CodeBlockClicked should not update state if no target code block`() {
+ val initialState = StepQuizCodeBlanksFeature.State.Content.stub(
+ codeBlocks = listOf(CodeBlock.Blank(isActive = true, suggestions = emptyList()))
+ )
+
+ val message = StepQuizCodeBlanksFeature.Message.CodeBlockClicked(
+ codeBlockItem = StepQuizCodeBlanksViewState.CodeBlockItem.Blank(id = 1, isActive = true)
+ )
+ val (state, actions) = reducer.reduce(initialState, message)
+
+ assertEquals(initialState, state)
+ assertContainsCodeBlockClickedAnalyticEvent(actions)
+ }
+
+ @Test
+ fun `CodeBlockClicked should not update state if target code block is active`() {
+ val initialState = StepQuizCodeBlanksFeature.State.Content.stub(
+ codeBlocks = listOf(CodeBlock.Blank(isActive = true, suggestions = emptyList()))
+ )
+
+ val message = StepQuizCodeBlanksFeature.Message.CodeBlockClicked(
+ codeBlockItem = StepQuizCodeBlanksViewState.CodeBlockItem.Blank(id = 0, isActive = true)
+ )
+ val (state, actions) = reducer.reduce(initialState, message)
+
+ assertEquals(initialState, state)
+ assertContainsCodeBlockClickedAnalyticEvent(actions)
+ }
+
@Test
fun `CodeBlockClicked should update active Print code block`() {
val initialState = StepQuizCodeBlanksFeature.State.Content.stub(
@@ -64,7 +94,32 @@ class StepQuizCodeBlanksReducerCodeBlockClickedTest {
}
@Test
- fun `CodeBlockClicked should update active Variable code block`() {
+ fun `CodeBlockClicked should update active ElseStatement code block`() {
+ val initialState = StepQuizCodeBlanksFeature.State.Content.stub(
+ codeBlocks = listOf(
+ CodeBlock.Blank(isActive = false, suggestions = emptyList()),
+ CodeBlock.ElseStatement(isActive = true)
+ )
+ )
+
+ val message = StepQuizCodeBlanksFeature.Message.CodeBlockClicked(
+ codeBlockItem = StepQuizCodeBlanksViewState.CodeBlockItem.Blank(id = 0, isActive = false)
+ )
+ val (state, actions) = reducer.reduce(initialState, message)
+
+ val expectedState = initialState.copy(
+ codeBlocks = listOf(
+ CodeBlock.Blank(isActive = true, suggestions = emptyList()),
+ CodeBlock.ElseStatement(isActive = false)
+ )
+ )
+
+ assertEquals(expectedState, state)
+ assertContainsCodeBlockClickedAnalyticEvent(actions)
+ }
+
+ @Test
+ fun `CodeBlockClicked should update active Variable code block to not active`() {
val initialState = StepQuizCodeBlanksFeature.State.Content.stub(
codeBlocks = listOf(
CodeBlock.Print(
@@ -118,6 +173,61 @@ class StepQuizCodeBlanksReducerCodeBlockClickedTest {
assertContainsCodeBlockClickedAnalyticEvent(actions)
}
+ @Test
+ fun `CodeBlockClicked should update not active Variable code block to active`() {
+ val initialState = StepQuizCodeBlanksFeature.State.Content.stub(
+ codeBlocks = listOf(
+ CodeBlock.Print(
+ children = listOf(
+ CodeBlockChild.SelectSuggestion(
+ isActive = true, suggestions = emptyList(), selectedSuggestion = null
+ )
+ )
+ ),
+ CodeBlock.Variable(
+ children = listOf(
+ CodeBlockChild.SelectSuggestion(
+ isActive = false, suggestions = emptyList(), selectedSuggestion = null
+ ),
+ CodeBlockChild.SelectSuggestion(
+ isActive = false, suggestions = emptyList(), selectedSuggestion = null
+ )
+ )
+ )
+ )
+ )
+
+ val message = StepQuizCodeBlanksFeature.Message.CodeBlockClicked(
+ codeBlockItem = StepQuizCodeBlanksViewState.CodeBlockItem.Blank(id = 1, isActive = false)
+ )
+ val (state, actions) = reducer.reduce(initialState, message)
+
+ val expectedState = initialState.copy(
+ codeBlocks = listOf(
+ CodeBlock.Print(
+ children = listOf(
+ CodeBlockChild.SelectSuggestion(
+ isActive = false, suggestions = emptyList(), selectedSuggestion = null
+ )
+ )
+ ),
+ CodeBlock.Variable(
+ children = listOf(
+ CodeBlockChild.SelectSuggestion(
+ isActive = true, suggestions = emptyList(), selectedSuggestion = null
+ ),
+ CodeBlockChild.SelectSuggestion(
+ isActive = false, suggestions = emptyList(), selectedSuggestion = null
+ )
+ )
+ )
+ )
+ )
+
+ assertEquals(expectedState, state)
+ assertContainsCodeBlockClickedAnalyticEvent(actions)
+ }
+
private fun assertContainsCodeBlockClickedAnalyticEvent(actions: Set) {
assertTrue {
actions.any {
diff --git a/shared/src/commonTest/kotlin/org/hyperskill/step_quiz_code_blanks/presentation/StepQuizCodeBlanksReducerDecreaseIndentLevelButtonClickedTest.kt b/shared/src/commonTest/kotlin/org/hyperskill/step_quiz_code_blanks/presentation/StepQuizCodeBlanksReducerDecreaseIndentLevelButtonClickedTest.kt
index 63c0f39d7..9e16f08b9 100644
--- a/shared/src/commonTest/kotlin/org/hyperskill/step_quiz_code_blanks/presentation/StepQuizCodeBlanksReducerDecreaseIndentLevelButtonClickedTest.kt
+++ b/shared/src/commonTest/kotlin/org/hyperskill/step_quiz_code_blanks/presentation/StepQuizCodeBlanksReducerDecreaseIndentLevelButtonClickedTest.kt
@@ -6,12 +6,26 @@ import kotlin.test.assertTrue
import org.hyperskill.app.step.domain.model.StepRoute
import org.hyperskill.app.step_quiz_code_blanks.domain.analytic.StepQuizCodeBlanksClickedDecreaseIndentLevelHyperskillAnalyticEvent
import org.hyperskill.app.step_quiz_code_blanks.domain.model.CodeBlock
+import org.hyperskill.app.step_quiz_code_blanks.domain.model.Suggestion
import org.hyperskill.app.step_quiz_code_blanks.presentation.StepQuizCodeBlanksFeature
import org.hyperskill.app.step_quiz_code_blanks.presentation.StepQuizCodeBlanksReducer
class StepQuizCodeBlanksReducerDecreaseIndentLevelButtonClickedTest {
private val reducer = StepQuizCodeBlanksReducer(StepRoute.Learn.Step(1, null))
+ @Test
+ fun `DecreaseIndentLevelButtonClicked should not update state if state is not Content`() {
+ val initialState = StepQuizCodeBlanksFeature.State.Idle
+
+ val (state, actions) = reducer.reduce(
+ initialState,
+ StepQuizCodeBlanksFeature.Message.DecreaseIndentLevelButtonClicked
+ )
+
+ assertEquals(initialState, state)
+ assertTrue(actions.isEmpty())
+ }
+
@Test
fun `DecreaseIndentLevelButtonClicked should not update state if no active code block`() {
val initialState = StepQuizCodeBlanksFeature.State.Content.stub(
@@ -54,7 +68,44 @@ class StepQuizCodeBlanksReducerDecreaseIndentLevelButtonClickedTest {
)
val expectedState = initialState.copy(
- codeBlocks = listOf(CodeBlock.Blank(isActive = true, indentLevel = 0, suggestions = emptyList()))
+ codeBlocks = listOf(
+ CodeBlock.Blank(
+ isActive = true,
+ indentLevel = 0,
+ suggestions = listOf(Suggestion.Print)
+ )
+ )
+ )
+
+ assertEquals(expectedState, state)
+ assertContainsDecreaseIndentLevelAnalyticEvent(actions)
+ }
+
+ @Test
+ fun `DecreaseIndentLevelButtonClicked should decrease indent level by 1 and update suggestions for Blank`() {
+ val initialState = StepQuizCodeBlanksFeature.State.Content.stub(
+ codeBlocks = listOf(
+ CodeBlock.Blank(
+ isActive = true,
+ indentLevel = 1,
+ suggestions = emptyList()
+ )
+ )
+ )
+
+ val (state, actions) = reducer.reduce(
+ initialState,
+ StepQuizCodeBlanksFeature.Message.DecreaseIndentLevelButtonClicked
+ )
+
+ val expectedState = initialState.copy(
+ codeBlocks = listOf(
+ CodeBlock.Blank(
+ isActive = true,
+ indentLevel = 0,
+ suggestions = listOf(Suggestion.Print)
+ )
+ )
)
assertEquals(expectedState, state)
@@ -78,7 +129,7 @@ class StepQuizCodeBlanksReducerDecreaseIndentLevelButtonClickedTest {
val expectedState = initialState.copy(
codeBlocks = listOf(
CodeBlock.Blank(isActive = false, indentLevel = 3, suggestions = emptyList()),
- CodeBlock.Blank(isActive = true, indentLevel = 1, suggestions = emptyList())
+ CodeBlock.Blank(isActive = true, indentLevel = 1, suggestions = listOf(Suggestion.Print))
)
)
diff --git a/shared/src/commonTest/kotlin/org/hyperskill/step_quiz_code_blanks/presentation/StepQuizCodeBlanksReducerDeleteButtonClickedTest.kt b/shared/src/commonTest/kotlin/org/hyperskill/step_quiz_code_blanks/presentation/StepQuizCodeBlanksReducerDeleteButtonClickedTest.kt
index 2ab0d37fe..c01e8a93e 100644
--- a/shared/src/commonTest/kotlin/org/hyperskill/step_quiz_code_blanks/presentation/StepQuizCodeBlanksReducerDeleteButtonClickedTest.kt
+++ b/shared/src/commonTest/kotlin/org/hyperskill/step_quiz_code_blanks/presentation/StepQuizCodeBlanksReducerDeleteButtonClickedTest.kt
@@ -62,43 +62,6 @@ class StepQuizCodeBlanksReducerDeleteButtonClickedTest {
assertContainsDeleteButtonClickedAnalyticEvent(actions)
}
- @Test
- fun `DeleteButtonClicked should clear suggestion if active Print code block has selected suggestion`() {
- val suggestion = Suggestion.ConstantString("suggestion")
- val initialState = StepQuizCodeBlanksFeature.State.Content.stub(
- codeBlocks = listOf(
- CodeBlock.Print(
- children = listOf(
- CodeBlockChild.SelectSuggestion(
- isActive = true,
- suggestions = listOf(suggestion),
- selectedSuggestion = suggestion
- )
- )
- )
- )
- )
-
- val (state, actions) = reducer.reduce(initialState, StepQuizCodeBlanksFeature.Message.DeleteButtonClicked)
-
- val expectedState = initialState.copy(
- codeBlocks = listOf(
- CodeBlock.Print(
- children = listOf(
- CodeBlockChild.SelectSuggestion(
- isActive = true,
- suggestions = listOf(suggestion),
- selectedSuggestion = null
- )
- )
- )
- )
- )
-
- assertEquals(expectedState, state)
- assertContainsDeleteButtonClickedAnalyticEvent(actions)
- }
-
@Test
fun `DeleteButtonClicked should set next code block as active if no code block before deleted`() {
val initialStates = listOf(
@@ -402,6 +365,84 @@ class StepQuizCodeBlanksReducerDeleteButtonClickedTest {
}
}
+ @Test
+ fun `DeleteButtonClicked should clear suggestion if active Print code block has selected suggestion`() {
+ val suggestion = Suggestion.ConstantString("suggestion")
+ val initialState = StepQuizCodeBlanksFeature.State.Content.stub(
+ codeBlocks = listOf(
+ CodeBlock.Print(
+ children = listOf(
+ CodeBlockChild.SelectSuggestion(
+ isActive = true,
+ suggestions = listOf(suggestion),
+ selectedSuggestion = suggestion
+ )
+ )
+ )
+ )
+ )
+
+ val (state, actions) = reducer.reduce(initialState, StepQuizCodeBlanksFeature.Message.DeleteButtonClicked)
+
+ val expectedState = initialState.copy(
+ codeBlocks = listOf(
+ CodeBlock.Print(
+ children = listOf(
+ CodeBlockChild.SelectSuggestion(
+ isActive = true,
+ suggestions = listOf(suggestion),
+ selectedSuggestion = null
+ )
+ )
+ )
+ )
+ )
+
+ assertEquals(expectedState, state)
+ assertContainsDeleteButtonClickedAnalyticEvent(actions)
+ }
+
+ @Test
+ fun `DeleteButtonClicked should remove child for Print code block`() {
+ val initialState = StepQuizCodeBlanksFeature.State.Content.stub(
+ codeBlocks = listOf(
+ CodeBlock.Print(
+ children = listOf(
+ CodeBlockChild.SelectSuggestion(
+ isActive = false,
+ suggestions = emptyList(),
+ selectedSuggestion = Suggestion.ConstantString("suggestion")
+ ),
+ CodeBlockChild.SelectSuggestion(
+ isActive = true,
+ suggestions = emptyList(),
+ selectedSuggestion = null
+ )
+ )
+ )
+ )
+ )
+
+ val (state, actions) = reducer.reduce(initialState, StepQuizCodeBlanksFeature.Message.DeleteButtonClicked)
+
+ val expectedState = initialState.copy(
+ codeBlocks = listOf(
+ CodeBlock.Print(
+ children = listOf(
+ CodeBlockChild.SelectSuggestion(
+ isActive = true,
+ suggestions = emptyList(),
+ selectedSuggestion = Suggestion.ConstantString("suggestion")
+ )
+ )
+ )
+ )
+ )
+
+ assertEquals(expectedState, state)
+ assertContainsDeleteButtonClickedAnalyticEvent(actions)
+ }
+
@Test
fun `DeleteButtonClicked should replace single Print code block with Blank`() {
val initialState = StepQuizCodeBlanksFeature.State.Content.stub(
@@ -428,6 +469,94 @@ class StepQuizCodeBlanksReducerDeleteButtonClickedTest {
assertContainsDeleteButtonClickedAnalyticEvent(actions)
}
+ @Test
+ fun `DeleteButtonClicked should clear suggestion if active Variable code block has selected suggestion`() {
+ val suggestion = Suggestion.ConstantString("suggestion")
+ val initialState = StepQuizCodeBlanksFeature.State.Content.stub(
+ codeBlocks = listOf(
+ CodeBlock.Variable(
+ children = listOf(
+ CodeBlockChild.SelectSuggestion(
+ isActive = true,
+ suggestions = listOf(suggestion),
+ selectedSuggestion = suggestion
+ )
+ )
+ )
+ )
+ )
+
+ val (state, actions) = reducer.reduce(initialState, StepQuizCodeBlanksFeature.Message.DeleteButtonClicked)
+
+ val expectedState = initialState.copy(
+ codeBlocks = listOf(
+ CodeBlock.Variable(
+ children = listOf(
+ CodeBlockChild.SelectSuggestion(
+ isActive = true,
+ suggestions = listOf(suggestion),
+ selectedSuggestion = null
+ )
+ )
+ )
+ )
+ )
+
+ assertEquals(expectedState, state)
+ assertContainsDeleteButtonClickedAnalyticEvent(actions)
+ }
+
+ @Test
+ fun `DeleteButtonClicked should remove child for Variable code block`() {
+ val initialState = StepQuizCodeBlanksFeature.State.Content.stub(
+ codeBlocks = listOf(
+ CodeBlock.Variable(
+ children = listOf(
+ CodeBlockChild.SelectSuggestion(
+ isActive = false,
+ suggestions = emptyList(),
+ selectedSuggestion = null
+ ),
+ CodeBlockChild.SelectSuggestion(
+ isActive = false,
+ suggestions = emptyList(),
+ selectedSuggestion = Suggestion.ConstantString("suggestion")
+ ),
+ CodeBlockChild.SelectSuggestion(
+ isActive = true,
+ suggestions = emptyList(),
+ selectedSuggestion = null
+ )
+ )
+ )
+ )
+ )
+
+ val (state, actions) = reducer.reduce(initialState, StepQuizCodeBlanksFeature.Message.DeleteButtonClicked)
+
+ val expectedState = initialState.copy(
+ codeBlocks = listOf(
+ CodeBlock.Variable(
+ children = listOf(
+ CodeBlockChild.SelectSuggestion(
+ isActive = false,
+ suggestions = emptyList(),
+ selectedSuggestion = null
+ ),
+ CodeBlockChild.SelectSuggestion(
+ isActive = true,
+ suggestions = emptyList(),
+ selectedSuggestion = Suggestion.ConstantString("suggestion")
+ )
+ )
+ )
+ )
+ )
+
+ assertEquals(expectedState, state)
+ assertContainsDeleteButtonClickedAnalyticEvent(actions)
+ }
+
@Test
fun `DeleteButtonClicked should replace single Variable code block with Blank`() {
val initialState = StepQuizCodeBlanksFeature.State.Content.stub(
@@ -459,6 +588,336 @@ class StepQuizCodeBlanksReducerDeleteButtonClickedTest {
assertContainsDeleteButtonClickedAnalyticEvent(actions)
}
+ @Test
+ fun `DeleteButtonClicked should remove Variable code block and set previous active`() {
+ val initialState = StepQuizCodeBlanksFeature.State.Content.stub(
+ codeBlocks = listOf(
+ CodeBlock.Blank(isActive = false, suggestions = emptyList()),
+ CodeBlock.Variable(
+ children = listOf(
+ CodeBlockChild.SelectSuggestion(
+ isActive = true,
+ suggestions = emptyList(),
+ selectedSuggestion = null
+ ),
+ CodeBlockChild.SelectSuggestion(
+ isActive = false,
+ suggestions = emptyList(),
+ selectedSuggestion = null
+ )
+ )
+ )
+ )
+ )
+
+ val (state, actions) = reducer.reduce(initialState, StepQuizCodeBlanksFeature.Message.DeleteButtonClicked)
+
+ val expectedState = initialState.copy(
+ codeBlocks = listOf(CodeBlock.Blank(isActive = true, suggestions = emptyList()))
+ )
+
+ assertEquals(expectedState, state)
+ assertContainsDeleteButtonClickedAnalyticEvent(actions)
+ }
+
+ @Test
+ fun `DeleteButtonClicked should remove Variable code block and set next active`() {
+ val initialState = StepQuizCodeBlanksFeature.State.Content.stub(
+ codeBlocks = listOf(
+ CodeBlock.Variable(
+ children = listOf(
+ CodeBlockChild.SelectSuggestion(
+ isActive = true,
+ suggestions = emptyList(),
+ selectedSuggestion = null
+ ),
+ CodeBlockChild.SelectSuggestion(
+ isActive = false,
+ suggestions = emptyList(),
+ selectedSuggestion = null
+ )
+ )
+ ),
+ CodeBlock.Blank(isActive = false, suggestions = emptyList())
+ )
+ )
+
+ val (state, actions) = reducer.reduce(initialState, StepQuizCodeBlanksFeature.Message.DeleteButtonClicked)
+
+ val expectedState = initialState.copy(
+ codeBlocks = listOf(CodeBlock.Blank(isActive = true, suggestions = emptyList()))
+ )
+
+ assertEquals(expectedState, state)
+ assertContainsDeleteButtonClickedAnalyticEvent(actions)
+ }
+
+ @Test
+ fun `DeleteButtonClicked should clear suggestion if active IfStatement code block has selected suggestion`() {
+ val suggestion = Suggestion.ConstantString("suggestion")
+ val initialState = StepQuizCodeBlanksFeature.State.Content.stub(
+ codeBlocks = listOf(
+ CodeBlock.IfStatement(
+ children = listOf(
+ CodeBlockChild.SelectSuggestion(
+ isActive = true,
+ suggestions = listOf(suggestion),
+ selectedSuggestion = suggestion
+ )
+ )
+ )
+ )
+ )
+
+ val (state, actions) = reducer.reduce(initialState, StepQuizCodeBlanksFeature.Message.DeleteButtonClicked)
+
+ val expectedState = initialState.copy(
+ codeBlocks = listOf(
+ CodeBlock.IfStatement(
+ children = listOf(
+ CodeBlockChild.SelectSuggestion(
+ isActive = true,
+ suggestions = listOf(suggestion),
+ selectedSuggestion = null
+ )
+ )
+ )
+ )
+ )
+
+ assertEquals(expectedState, state)
+ assertContainsDeleteButtonClickedAnalyticEvent(actions)
+ }
+
+ @Test
+ fun `DeleteButtonClicked should remove child for IfStatement code block`() {
+ val initialState = StepQuizCodeBlanksFeature.State.Content.stub(
+ codeBlocks = listOf(
+ CodeBlock.IfStatement(
+ children = listOf(
+ CodeBlockChild.SelectSuggestion(
+ isActive = false,
+ suggestions = emptyList(),
+ selectedSuggestion = Suggestion.ConstantString("suggestion")
+ ),
+ CodeBlockChild.SelectSuggestion(
+ isActive = true,
+ suggestions = emptyList(),
+ selectedSuggestion = null
+ )
+ )
+ )
+ )
+ )
+
+ val (state, actions) = reducer.reduce(initialState, StepQuizCodeBlanksFeature.Message.DeleteButtonClicked)
+
+ val expectedState = initialState.copy(
+ codeBlocks = listOf(
+ CodeBlock.IfStatement(
+ children = listOf(
+ CodeBlockChild.SelectSuggestion(
+ isActive = true,
+ suggestions = emptyList(),
+ selectedSuggestion = Suggestion.ConstantString("suggestion")
+ )
+ )
+ )
+ )
+ )
+
+ assertEquals(expectedState, state)
+ assertContainsDeleteButtonClickedAnalyticEvent(actions)
+ }
+
+ @Test
+ fun `DeleteButtonClicked should replace single IfStatement code block with Blank`() {
+ val initialState = StepQuizCodeBlanksFeature.State.Content.stub(
+ codeBlocks = listOf(
+ CodeBlock.IfStatement(
+ children = listOf(
+ CodeBlockChild.SelectSuggestion(
+ isActive = true,
+ suggestions = emptyList(),
+ selectedSuggestion = null
+ )
+ )
+ )
+ )
+ )
+
+ val (state, actions) = reducer.reduce(initialState, StepQuizCodeBlanksFeature.Message.DeleteButtonClicked)
+
+ val expectedState = initialState.copy(
+ codeBlocks = listOf(CodeBlock.Blank(isActive = true, suggestions = listOf(Suggestion.Print)))
+ )
+
+ assertEquals(expectedState, state)
+ assertContainsDeleteButtonClickedAnalyticEvent(actions)
+ }
+
+ /* ktlint-disable */
+ @Test
+ fun `DeleteButtonClicked should not replace single IfStatement code block with Blank when next code block has different indent level`() {
+ val initialState = StepQuizCodeBlanksFeature.State.Content.stub(
+ codeBlocks = listOf(
+ CodeBlock.IfStatement(
+ indentLevel = 0,
+ children = listOf(
+ CodeBlockChild.SelectSuggestion(
+ isActive = true,
+ suggestions = emptyList(),
+ selectedSuggestion = null
+ )
+ )
+ ),
+ CodeBlock.Blank(indentLevel = 1, isActive = false, suggestions = emptyList())
+ )
+ )
+
+ val (state, actions) = reducer.reduce(initialState, StepQuizCodeBlanksFeature.Message.DeleteButtonClicked)
+
+ assertEquals(initialState, state)
+ assertContainsDeleteButtonClickedAnalyticEvent(actions)
+ }
+
+ @Test
+ fun `DeleteButtonClicked should remove IfStatement code block and set previous active`() {
+ val initialState = StepQuizCodeBlanksFeature.State.Content.stub(
+ codeBlocks = listOf(
+ CodeBlock.Blank(isActive = false, suggestions = emptyList()),
+ CodeBlock.IfStatement(
+ children = listOf(
+ CodeBlockChild.SelectSuggestion(
+ isActive = true,
+ suggestions = emptyList(),
+ selectedSuggestion = null
+ ),
+ CodeBlockChild.SelectSuggestion(
+ isActive = false,
+ suggestions = emptyList(),
+ selectedSuggestion = null
+ )
+ )
+ )
+ )
+ )
+
+ val (state, actions) = reducer.reduce(initialState, StepQuizCodeBlanksFeature.Message.DeleteButtonClicked)
+
+ val expectedState = initialState.copy(
+ codeBlocks = listOf(CodeBlock.Blank(isActive = true, suggestions = emptyList()))
+ )
+
+ assertEquals(expectedState, state)
+ assertContainsDeleteButtonClickedAnalyticEvent(actions)
+ }
+
+ @Test
+ fun `DeleteButtonClicked should remove IfStatement code block and set next active`() {
+ val initialState = StepQuizCodeBlanksFeature.State.Content.stub(
+ codeBlocks = listOf(
+ CodeBlock.IfStatement(
+ children = listOf(
+ CodeBlockChild.SelectSuggestion(
+ isActive = true,
+ suggestions = emptyList(),
+ selectedSuggestion = null
+ ),
+ CodeBlockChild.SelectSuggestion(
+ isActive = false,
+ suggestions = emptyList(),
+ selectedSuggestion = null
+ )
+ )
+ ),
+ CodeBlock.Blank(isActive = false, suggestions = emptyList())
+ )
+ )
+
+ val (state, actions) = reducer.reduce(initialState, StepQuizCodeBlanksFeature.Message.DeleteButtonClicked)
+
+ val expectedState = initialState.copy(
+ codeBlocks = listOf(CodeBlock.Blank(isActive = true, suggestions = emptyList()))
+ )
+
+ assertEquals(expectedState, state)
+ assertContainsDeleteButtonClickedAnalyticEvent(actions)
+ }
+
+ /* ktlint-disable */
+ @Test
+ fun `DeleteButtonClicked should not replace single ElseStatement code block with Blank when next code block has different indent level`() {
+ val initialState = StepQuizCodeBlanksFeature.State.Content.stub(
+ codeBlocks = listOf(
+ CodeBlock.ElseStatement(
+ indentLevel = 0,
+ isActive = true
+ ),
+ CodeBlock.Blank(indentLevel = 1, isActive = false, suggestions = emptyList())
+ )
+ )
+
+ val (state, actions) = reducer.reduce(initialState, StepQuizCodeBlanksFeature.Message.DeleteButtonClicked)
+
+ assertEquals(initialState, state)
+ assertContainsDeleteButtonClickedAnalyticEvent(actions)
+ }
+
+ @Test
+ fun `DeleteButtonClicked should replace single ElseStatement code block with Blank`() {
+ val initialState = StepQuizCodeBlanksFeature.State.Content.stub(
+ codeBlocks = listOf(CodeBlock.ElseStatement(isActive = true))
+ )
+
+ val (state, actions) = reducer.reduce(initialState, StepQuizCodeBlanksFeature.Message.DeleteButtonClicked)
+
+ val expectedState = initialState.copy(
+ codeBlocks = listOf(CodeBlock.Blank(isActive = true, suggestions = listOf(Suggestion.Print)))
+ )
+
+ assertEquals(expectedState, state)
+ assertContainsDeleteButtonClickedAnalyticEvent(actions)
+ }
+
+ @Test
+ fun `DeleteButtonClicked should remove ElseStatement code block and set previous active`() {
+ val initialState = StepQuizCodeBlanksFeature.State.Content.stub(
+ codeBlocks = listOf(
+ CodeBlock.Blank(isActive = false, suggestions = emptyList()),
+ CodeBlock.ElseStatement(isActive = true)
+ )
+ )
+
+ val (state, actions) = reducer.reduce(initialState, StepQuizCodeBlanksFeature.Message.DeleteButtonClicked)
+
+ val expectedState = initialState.copy(
+ codeBlocks = listOf(CodeBlock.Blank(isActive = true, suggestions = emptyList()))
+ )
+
+ assertEquals(expectedState, state)
+ assertContainsDeleteButtonClickedAnalyticEvent(actions)
+ }
+
+ @Test
+ fun `DeleteButtonClicked should remove ElseStatement code block and set next active`() {
+ val initialState = StepQuizCodeBlanksFeature.State.Content.stub(
+ codeBlocks = listOf(
+ CodeBlock.ElseStatement(isActive = true),
+ CodeBlock.Blank(isActive = false, suggestions = emptyList())
+ )
+ )
+
+ val (state, actions) = reducer.reduce(initialState, StepQuizCodeBlanksFeature.Message.DeleteButtonClicked)
+
+ val expectedState = initialState.copy(
+ codeBlocks = listOf(CodeBlock.Blank(isActive = true, suggestions = emptyList()))
+ )
+
+ assertEquals(expectedState, state)
+ assertContainsDeleteButtonClickedAnalyticEvent(actions)
+ }
+
private fun assertContainsDeleteButtonClickedAnalyticEvent(actions: Set) {
assertTrue {
actions.any {
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
new file mode 100644
index 000000000..a9ff044f7
--- /dev/null
+++ b/shared/src/commonTest/kotlin/org/hyperskill/step_quiz_code_blanks/presentation/StepQuizCodeBlanksReducerElifAndElseStatementsSuggestionsAvailabilityTest.kt
@@ -0,0 +1,74 @@
+package org.hyperskill.step_quiz_code_blanks.presentation
+
+import kotlin.test.Test
+import kotlin.test.assertFalse
+import kotlin.test.assertTrue
+import org.hyperskill.app.step.domain.model.StepRoute
+import org.hyperskill.app.step_quiz_code_blanks.domain.model.CodeBlock
+import org.hyperskill.app.step_quiz_code_blanks.presentation.StepQuizCodeBlanksReducer
+
+class StepQuizCodeBlanksReducerElifAndElseStatementsSuggestionsAvailabilityTest {
+ private val reducer = StepQuizCodeBlanksReducer(StepRoute.Learn.Step(1, null))
+
+ @Test
+ fun `Should return false if index is less than 2`() {
+ val result =
+ reducer.areElifAndElseStatementsSuggestionsAvailable(index = 1, indentLevel = 0, codeBlocks = emptyList())
+ assertFalse(result)
+ }
+
+ @Test
+ fun `Should return false if codeBlocks is empty`() {
+ val result =
+ reducer.areElifAndElseStatementsSuggestionsAvailable(index = 2, indentLevel = 0, codeBlocks = emptyList())
+ assertFalse(result)
+ }
+
+ @Test
+ fun `Should return false if no previous code block at same indent level`() {
+ val codeBlocks = listOf(
+ CodeBlock.IfStatement(indentLevel = 0, children = emptyList()),
+ CodeBlock.Print(indentLevel = 1, children = emptyList())
+ )
+ val result =
+ reducer.areElifAndElseStatementsSuggestionsAvailable(index = 2, indentLevel = 1, codeBlocks = codeBlocks)
+ assertFalse(result)
+ }
+
+ @Test
+ fun `Should return true if previous code block is IfStatement at same indent level`() {
+ val codeBlocks = listOf(
+ CodeBlock.IfStatement(indentLevel = 0, children = emptyList()),
+ CodeBlock.Print(indentLevel = 1, children = emptyList())
+ )
+ val result =
+ reducer.areElifAndElseStatementsSuggestionsAvailable(index = 2, indentLevel = 0, codeBlocks = codeBlocks)
+ assertTrue(result)
+ }
+
+ @Test
+ fun `Should return true if previous code block is IfStatement at same indent level nested`() {
+ val codeBlocks = listOf(
+ CodeBlock.IfStatement(indentLevel = 0, children = emptyList()),
+ CodeBlock.Print(indentLevel = 1, children = emptyList()),
+ CodeBlock.IfStatement(indentLevel = 1, children = emptyList()),
+ CodeBlock.Print(indentLevel = 2, children = emptyList())
+ )
+ val result =
+ reducer.areElifAndElseStatementsSuggestionsAvailable(index = 4, indentLevel = 1, codeBlocks = codeBlocks)
+ assertTrue(result)
+ }
+
+ @Test
+ fun `Should return true if previous code block is ElifStatement at same indent level`() {
+ val codeBlocks = listOf(
+ CodeBlock.IfStatement(indentLevel = 0, children = emptyList()),
+ CodeBlock.Print(indentLevel = 1, children = emptyList()),
+ CodeBlock.ElifStatement(indentLevel = 0, children = emptyList()),
+ CodeBlock.Print(indentLevel = 1, children = emptyList())
+ )
+ val result =
+ reducer.areElifAndElseStatementsSuggestionsAvailable(index = 4, indentLevel = 0, codeBlocks = codeBlocks)
+ assertTrue(result)
+ }
+}
\ No newline at end of file
diff --git a/shared/src/commonTest/kotlin/org/hyperskill/step_quiz_code_blanks/presentation/StepQuizCodeBlanksReducerEnterButtonClickedTest.kt b/shared/src/commonTest/kotlin/org/hyperskill/step_quiz_code_blanks/presentation/StepQuizCodeBlanksReducerEnterButtonClickedTest.kt
index 1f52b1479..b25d8cd19 100644
--- a/shared/src/commonTest/kotlin/org/hyperskill/step_quiz_code_blanks/presentation/StepQuizCodeBlanksReducerEnterButtonClickedTest.kt
+++ b/shared/src/commonTest/kotlin/org/hyperskill/step_quiz_code_blanks/presentation/StepQuizCodeBlanksReducerEnterButtonClickedTest.kt
@@ -24,7 +24,7 @@ class StepQuizCodeBlanksReducerEnterButtonClickedTest {
}
@Test
- fun `EnterButtonClicked should log analytic event and not update state if no active code block`() {
+ fun `EnterButtonClicked should not update state if no active code block`() {
val initialState = StepQuizCodeBlanksFeature.State.Content.stub(
codeBlocks = listOf(
CodeBlock.Blank(
@@ -41,7 +41,7 @@ class StepQuizCodeBlanksReducerEnterButtonClickedTest {
}
@Test
- fun `EnterButtonClicked should log analytic event and add new active Blank block if active code block exists`() {
+ fun `EnterButtonClicked should append new active Blank block if active code block exists`() {
val initialState = StepQuizCodeBlanksFeature.State.Content.stub(
codeBlocks = listOf(
CodeBlock.Blank(
@@ -103,6 +103,41 @@ class StepQuizCodeBlanksReducerEnterButtonClickedTest {
assertContainsEnterButtonClickedAnalyticEvent(actions)
}
+ @Test
+ fun `EnterButtonClicked should add Blank with next indentLevel if active code block is condition`() {
+ val codeBlocks = listOf(
+ CodeBlock.IfStatement(
+ children = listOf(
+ CodeBlockChild.SelectSuggestion(
+ isActive = true,
+ suggestions = emptyList(),
+ selectedSuggestion = null
+ )
+ )
+ ),
+ CodeBlock.ElifStatement(
+ children = listOf(
+ CodeBlockChild.SelectSuggestion(
+ isActive = true,
+ suggestions = emptyList(),
+ selectedSuggestion = null
+ )
+ )
+ ),
+ CodeBlock.ElseStatement(isActive = true)
+ )
+
+ codeBlocks.forEach { codeBlock ->
+ val initialState = StepQuizCodeBlanksFeature.State.Content.stub(codeBlocks = listOf(codeBlock))
+
+ val (state, actions) = reducer.reduce(initialState, StepQuizCodeBlanksFeature.Message.EnterButtonClicked)
+
+ assertTrue(state is StepQuizCodeBlanksFeature.State.Content)
+ assertEquals(1, state.codeBlocks[1].indentLevel)
+ assertContainsEnterButtonClickedAnalyticEvent(actions)
+ }
+ }
+
private fun assertContainsEnterButtonClickedAnalyticEvent(actions: Set) {
assertTrue {
actions.any {
diff --git a/shared/src/commonTest/kotlin/org/hyperskill/step_quiz_code_blanks/presentation/StepQuizCodeBlanksReducerSpaceButtonClickedTest.kt b/shared/src/commonTest/kotlin/org/hyperskill/step_quiz_code_blanks/presentation/StepQuizCodeBlanksReducerSpaceButtonClickedTest.kt
index b1295d945..3c8166d55 100644
--- a/shared/src/commonTest/kotlin/org/hyperskill/step_quiz_code_blanks/presentation/StepQuizCodeBlanksReducerSpaceButtonClickedTest.kt
+++ b/shared/src/commonTest/kotlin/org/hyperskill/step_quiz_code_blanks/presentation/StepQuizCodeBlanksReducerSpaceButtonClickedTest.kt
@@ -27,7 +27,7 @@ class StepQuizCodeBlanksReducerSpaceButtonClickedTest {
}
@Test
- fun `SpaceButtonClicked should not update state if active Print block has no active child`() {
+ fun `SpaceButtonClicked should not update state if no active code block`() {
val initialState = StepQuizCodeBlanksFeature.State.Content.stub(
codeBlocks = listOf(
CodeBlock.Print(
@@ -48,6 +48,35 @@ class StepQuizCodeBlanksReducerSpaceButtonClickedTest {
assertContainsSpaceButtonClickedAnalyticEvent(actions)
}
+ @Test
+ fun `SpaceButtonClicked should not update state if active code block is Blank`() {
+ val initialState = StepQuizCodeBlanksFeature.State.Content.stub(
+ codeBlocks = listOf(
+ CodeBlock.Blank(
+ isActive = true,
+ suggestions = emptyList()
+ )
+ )
+ )
+
+ val (state, actions) = reducer.reduce(initialState, StepQuizCodeBlanksFeature.Message.SpaceButtonClicked)
+
+ assertEquals(initialState, state)
+ assertContainsSpaceButtonClickedAnalyticEvent(actions)
+ }
+
+ @Test
+ fun `SpaceButtonClicked should not update state if active code block is ElseStatement`() {
+ val initialState = StepQuizCodeBlanksFeature.State.Content.stub(
+ codeBlocks = listOf(CodeBlock.ElseStatement(isActive = true))
+ )
+
+ val (state, actions) = reducer.reduce(initialState, StepQuizCodeBlanksFeature.Message.SpaceButtonClicked)
+
+ assertEquals(initialState, state)
+ assertContainsSpaceButtonClickedAnalyticEvent(actions)
+ }
+
@Test
fun `SpaceButtonClicked should add a new child to active Print code block`() {
val initialState = StepQuizCodeBlanksFeature.State.Content.stub(
diff --git a/shared/src/commonTest/kotlin/org/hyperskill/step_quiz_code_blanks/presentation/StepQuizCodeBlanksReducerSuggestionClickedTest.kt b/shared/src/commonTest/kotlin/org/hyperskill/step_quiz_code_blanks/presentation/StepQuizCodeBlanksReducerSuggestionClickedTest.kt
index 5eedd6de7..5e3970aff 100644
--- a/shared/src/commonTest/kotlin/org/hyperskill/step_quiz_code_blanks/presentation/StepQuizCodeBlanksReducerSuggestionClickedTest.kt
+++ b/shared/src/commonTest/kotlin/org/hyperskill/step_quiz_code_blanks/presentation/StepQuizCodeBlanksReducerSuggestionClickedTest.kt
@@ -61,7 +61,7 @@ class StepQuizCodeBlanksReducerSuggestionClickedTest {
}
@Test
- fun `SuggestionClicked should update active Blank code block to Print if suggestion exists`() {
+ fun `SuggestionClicked should update Blank to Print`() {
val initialState = StepQuizCodeBlanksFeature.State.Content.stub(
codeBlocks = listOf(
CodeBlock.Blank(
@@ -93,7 +93,7 @@ class StepQuizCodeBlanksReducerSuggestionClickedTest {
}
@Test
- fun `SuggestionClicked should update active Blank code block to Variable if suggestion exists`() {
+ fun `SuggestionClicked should update Blank to Variable`() {
val initialState = StepQuizCodeBlanksFeature.State.Content.stub(
codeBlocks = listOf(
CodeBlock.Blank(
@@ -129,6 +129,102 @@ class StepQuizCodeBlanksReducerSuggestionClickedTest {
assertContainsSuggestionClickedAnalyticEvent(actions)
}
+ @Test
+ fun `SuggestionClicked should update Blank to IfStatement`() {
+ val initialState = StepQuizCodeBlanksFeature.State.Content.stub(
+ codeBlocks = listOf(
+ CodeBlock.Blank(
+ isActive = true,
+ suggestions = listOf(Suggestion.Print, Suggestion.IfStatement)
+ )
+ )
+ )
+
+ val message = StepQuizCodeBlanksFeature.Message.SuggestionClicked(Suggestion.IfStatement)
+ val (state, actions) = reducer.reduce(initialState, message)
+
+ val expectedState = initialState.copy(
+ codeBlocks = listOf(
+ CodeBlock.IfStatement(
+ children = listOf(
+ CodeBlockChild.SelectSuggestion(
+ isActive = true,
+ suggestions = initialState.codeBlanksVariablesAndStringsSuggestions,
+ selectedSuggestion = null
+ )
+ )
+ )
+ )
+ )
+
+ assertEquals(expectedState, state)
+ assertContainsSuggestionClickedAnalyticEvent(actions)
+ }
+
+ @Test
+ fun `SuggestionClicked should update Blank to ElifStatement`() {
+ val initialState = StepQuizCodeBlanksFeature.State.Content.stub(
+ codeBlocks = listOf(
+ CodeBlock.Blank(
+ isActive = true,
+ suggestions = listOf(Suggestion.Print, Suggestion.ElifStatement)
+ )
+ )
+ )
+
+ val message = StepQuizCodeBlanksFeature.Message.SuggestionClicked(Suggestion.ElifStatement)
+ val (state, actions) = reducer.reduce(initialState, message)
+
+ val expectedState = initialState.copy(
+ codeBlocks = listOf(
+ CodeBlock.ElifStatement(
+ children = listOf(
+ CodeBlockChild.SelectSuggestion(
+ isActive = true,
+ suggestions = initialState.codeBlanksVariablesAndStringsSuggestions,
+ selectedSuggestion = null
+ )
+ )
+ )
+ )
+ )
+
+ assertEquals(expectedState, state)
+ assertContainsSuggestionClickedAnalyticEvent(actions)
+ }
+
+ @Test
+ fun `SuggestionClicked should update Blank to ElseStatement and add Blank with next indentLevel`() {
+ val initialState = StepQuizCodeBlanksFeature.State.Content.stub(
+ codeBlocks = listOf(
+ CodeBlock.Blank(
+ isActive = true,
+ suggestions = listOf(Suggestion.Print, Suggestion.ElseStatement)
+ )
+ )
+ )
+
+ val message = StepQuizCodeBlanksFeature.Message.SuggestionClicked(Suggestion.ElseStatement)
+ val (state, actions) = reducer.reduce(initialState, message)
+
+ val expectedState = initialState.copy(
+ codeBlocks = listOf(
+ CodeBlock.ElseStatement(
+ isActive = false,
+ indentLevel = 0
+ ),
+ CodeBlock.Blank(
+ isActive = true,
+ indentLevel = 1,
+ suggestions = listOf(Suggestion.Print)
+ )
+ )
+ )
+
+ assertEquals(expectedState, state)
+ assertContainsSuggestionClickedAnalyticEvent(actions)
+ }
+
@Test
fun `SuggestionClicked should update Print code block with selected suggestion`() {
val suggestion = Suggestion.ConstantString("suggestion")
@@ -154,6 +250,56 @@ class StepQuizCodeBlanksReducerSuggestionClickedTest {
assertContainsSuggestionClickedAnalyticEvent(actions)
}
+ @Test
+ fun `SuggestionClicked should update IfStatement code block with selected suggestion`() {
+ val suggestion = Suggestion.ConstantString("suggestion")
+ val initialState = StepQuizCodeBlanksFeature.State.Content.stub(
+ codeBlocks = listOf(
+ CodeBlock.IfStatement(
+ children = listOf(
+ CodeBlockChild.SelectSuggestion(
+ isActive = true,
+ suggestions = listOf(suggestion),
+ selectedSuggestion = null
+ )
+ )
+ )
+ )
+ )
+
+ val message = StepQuizCodeBlanksFeature.Message.SuggestionClicked(suggestion)
+ val (state, actions) = reducer.reduce(initialState, message)
+
+ assertTrue(state is StepQuizCodeBlanksFeature.State.Content)
+ assertEquals(suggestion, (state.codeBlocks[0] as CodeBlock.IfStatement).children[0].selectedSuggestion)
+ assertContainsSuggestionClickedAnalyticEvent(actions)
+ }
+
+ @Test
+ fun `SuggestionClicked should update ElifStatement code block with selected suggestion`() {
+ val suggestion = Suggestion.ConstantString("suggestion")
+ val initialState = StepQuizCodeBlanksFeature.State.Content.stub(
+ codeBlocks = listOf(
+ CodeBlock.ElifStatement(
+ children = listOf(
+ CodeBlockChild.SelectSuggestion(
+ isActive = true,
+ suggestions = listOf(suggestion),
+ selectedSuggestion = null
+ )
+ )
+ )
+ )
+ )
+
+ val message = StepQuizCodeBlanksFeature.Message.SuggestionClicked(suggestion)
+ val (state, actions) = reducer.reduce(initialState, message)
+
+ assertTrue(state is StepQuizCodeBlanksFeature.State.Content)
+ assertEquals(suggestion, (state.codeBlocks[0] as CodeBlock.ElifStatement).children[0].selectedSuggestion)
+ assertContainsSuggestionClickedAnalyticEvent(actions)
+ }
+
@Test
fun `SuggestionClicked should update Variable code block with selected suggestion for name`() {
val suggestion = Suggestion.ConstantString("suggestion")
@@ -252,6 +398,20 @@ class StepQuizCodeBlanksReducerSuggestionClickedTest {
assertContainsSuggestionClickedAnalyticEvent(actions)
}
+ @Test
+ fun `SuggestionClicked should not update ElseStatement code block with selected suggestion`() {
+ val suggestion = Suggestion.ConstantString("suggestion")
+ val initialState = StepQuizCodeBlanksFeature.State.Content.stub(
+ codeBlocks = listOf(CodeBlock.ElseStatement(isActive = true))
+ )
+
+ val message = StepQuizCodeBlanksFeature.Message.SuggestionClicked(suggestion)
+ val (state, actions) = reducer.reduce(initialState, message)
+
+ assertEquals(initialState, state)
+ assertContainsSuggestionClickedAnalyticEvent(actions)
+ }
+
private fun assertContainsSuggestionClickedAnalyticEvent(actions: Set) {
assertTrue {
actions.any {