diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f1671685..10c400984 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ ## Pending changes - [#83](https://github.com/bumble-tech/appyx/issues/83) – **Breaking change**: `RoutingSource` renamed to `NavModel`. All subclasses, fields, package names, etc., any mentions of the word follow suit. - +- [#91](https://github.com/bumble-tech/appyx/pull/91) – **Fixed**: Spotlight next and previous operations crash fix ## 1.0-alpha04 diff --git a/core/src/main/java/com/bumble/appyx/navmodel/spotlight/operation/Next.kt b/core/src/main/java/com/bumble/appyx/navmodel/spotlight/operation/Next.kt index 834925141..dfbed3451 100644 --- a/core/src/main/java/com/bumble/appyx/navmodel/spotlight/operation/Next.kt +++ b/core/src/main/java/com/bumble/appyx/navmodel/spotlight/operation/Next.kt @@ -12,7 +12,7 @@ import kotlinx.parcelize.Parcelize class Next : SpotlightOperation { override fun isApplicable(elements: RoutingElements) = - elements.any { it.fromState == INACTIVE_AFTER } + elements.any { it.fromState == INACTIVE_AFTER && it.targetState == INACTIVE_AFTER } override fun invoke(elements: RoutingElements): RoutingElements { val nextKey = diff --git a/core/src/main/java/com/bumble/appyx/navmodel/spotlight/operation/Previous.kt b/core/src/main/java/com/bumble/appyx/navmodel/spotlight/operation/Previous.kt index 2cb128cae..60c4fd415 100644 --- a/core/src/main/java/com/bumble/appyx/navmodel/spotlight/operation/Previous.kt +++ b/core/src/main/java/com/bumble/appyx/navmodel/spotlight/operation/Previous.kt @@ -12,7 +12,7 @@ import kotlinx.parcelize.Parcelize class Previous : SpotlightOperation { override fun isApplicable(elements: RoutingElements) = - elements.any { it.fromState == INACTIVE_BEFORE } + elements.any { it.fromState == INACTIVE_BEFORE && it.targetState == INACTIVE_BEFORE } override fun invoke( elements: RoutingElements diff --git a/core/src/test/java/com/bumble/appyx/navmodel/spotlight/SpotlightTestHelper.kt b/core/src/test/java/com/bumble/appyx/navmodel/spotlight/SpotlightTestHelper.kt new file mode 100644 index 000000000..d12a4f7c7 --- /dev/null +++ b/core/src/test/java/com/bumble/appyx/navmodel/spotlight/SpotlightTestHelper.kt @@ -0,0 +1,19 @@ +package com.bumble.appyx.navmodel.spotlight + +import com.bumble.appyx.core.navigation.Operation +import com.bumble.appyx.core.navigation.RoutingKey +import com.bumble.appyx.navmodel.spotlight.Spotlight.TransitionState +import com.bumble.appyx.navmodel.spotlight.operation.Routing + +internal fun spotlightElement( + element: T, + key: RoutingKey = RoutingKey(routing = element), + fromState: TransitionState, + targetState: TransitionState, + operation: Operation = Operation.Noop() +) = SpotlightElement( + key = key, + fromState = fromState, + targetState = targetState, + operation = operation +) diff --git a/core/src/test/java/com/bumble/appyx/navmodel/spotlight/operation/NextTest.kt b/core/src/test/java/com/bumble/appyx/navmodel/spotlight/operation/NextTest.kt new file mode 100644 index 000000000..a74c588c6 --- /dev/null +++ b/core/src/test/java/com/bumble/appyx/navmodel/spotlight/operation/NextTest.kt @@ -0,0 +1,54 @@ +package com.bumble.appyx.navmodel.spotlight.operation + +import com.bumble.appyx.navmodel.spotlight.Spotlight.TransitionState.ACTIVE +import com.bumble.appyx.navmodel.spotlight.Spotlight.TransitionState.INACTIVE_AFTER +import com.bumble.appyx.navmodel.spotlight.Spotlight.TransitionState.INACTIVE_BEFORE +import com.bumble.appyx.navmodel.spotlight.operation.Routing.Routing1 +import com.bumble.appyx.navmodel.spotlight.spotlightElement +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Test + +internal class NextTest { + + @Test + fun `Given last element is in transition When next called Then operation is not applicable`() { + val firstElement = spotlightElement( + element = Routing1, + fromState = INACTIVE_AFTER, + targetState = INACTIVE_BEFORE, + ) + val lastElement = spotlightElement( + element = Routing1, + fromState = INACTIVE_AFTER, + targetState = ACTIVE, + ) + val elements = listOf(firstElement, lastElement) + val operation = Next() + + val applicable = operation.isApplicable(elements) + + assertFalse(applicable) + } + + @Test + fun `Given last element is not in transition When next called Then operation is applicable`() { + val firstElement = spotlightElement( + element = Routing1, + fromState = ACTIVE, + targetState = ACTIVE, + ) + val lastElement = spotlightElement( + element = Routing1, + fromState = INACTIVE_AFTER, + targetState = INACTIVE_AFTER, + ) + val elements = listOf(firstElement, lastElement) + val operation = Next() + + val applicable = operation.isApplicable(elements) + + assertTrue(applicable) + } + +} diff --git a/core/src/test/java/com/bumble/appyx/navmodel/spotlight/operation/PreviousTest.kt b/core/src/test/java/com/bumble/appyx/navmodel/spotlight/operation/PreviousTest.kt new file mode 100644 index 000000000..452fca6cb --- /dev/null +++ b/core/src/test/java/com/bumble/appyx/navmodel/spotlight/operation/PreviousTest.kt @@ -0,0 +1,54 @@ +package com.bumble.appyx.navmodel.spotlight.operation + +import com.bumble.appyx.navmodel.spotlight.Spotlight.TransitionState.ACTIVE +import com.bumble.appyx.navmodel.spotlight.Spotlight.TransitionState.INACTIVE_AFTER +import com.bumble.appyx.navmodel.spotlight.Spotlight.TransitionState.INACTIVE_BEFORE +import com.bumble.appyx.navmodel.spotlight.operation.Routing.Routing1 +import com.bumble.appyx.navmodel.spotlight.spotlightElement +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Test + +internal class PreviousTest { + + @Test + fun `Given first element is in transition When previous called Then operation is not applicable`() { + val firstElement = spotlightElement( + element = Routing1, + fromState = INACTIVE_AFTER, + targetState = INACTIVE_BEFORE, + ) + val lastElement = spotlightElement( + element = Routing1, + fromState = ACTIVE, + targetState = INACTIVE_BEFORE, + ) + val elements = listOf(firstElement, lastElement) + val operation = Previous() + + val applicable = operation.isApplicable(elements) + + assertFalse(applicable) + } + + @Test + fun `Given first element is not in transition When previous called Then operation is applicable`() { + val firstElement = spotlightElement( + element = Routing1, + fromState = ACTIVE, + targetState = ACTIVE, + ) + val lastElement = spotlightElement( + element = Routing1, + fromState = INACTIVE_BEFORE, + targetState = INACTIVE_BEFORE, + ) + val elements = listOf(firstElement, lastElement) + val operation = Previous() + + val applicable = operation.isApplicable(elements) + + assertTrue(applicable) + } + +} diff --git a/core/src/test/java/com/bumble/appyx/navmodel/spotlight/operation/Routing.kt b/core/src/test/java/com/bumble/appyx/navmodel/spotlight/operation/Routing.kt new file mode 100644 index 000000000..ecfa285b8 --- /dev/null +++ b/core/src/test/java/com/bumble/appyx/navmodel/spotlight/operation/Routing.kt @@ -0,0 +1,7 @@ +package com.bumble.appyx.navmodel.spotlight.operation + +internal sealed class Routing { + object Routing1 : Routing() + object Routing2 : Routing() + object Routing3 : Routing() +}