From 0d1bf863c000ab3dd6c3bb2ae8646cf71eef13a9 Mon Sep 17 00:00:00 2001 From: Isaac Udy Date: Fri, 15 Nov 2024 20:48:02 +1300 Subject: [PATCH] Updated enro-test to prefer "assert" methods in all cases, added functionality to deliver a result directly to a particular navigation key, and a few other minor documentation and function naming updates --- .../src/main/java/dev/enro/test/EnroTest.kt | 1 + .../java/dev/enro/test/EnroTestAssertions.kt | 83 +++++++++++++- ...igationInstruction.deliverResultForTest.kt | 37 ++++++ .../NavigationKey.deliverResultForTest.kt | 62 +++++++++++ .../TestNavigationContainer.assertActive.kt | 68 +++++++++++- ...TestNavigationContainer.assertBackstack.kt | 13 +++ .../TestNavigationContainer.assertContains.kt | 67 +++++++++++ ...vigationContainer.expectOpenInstruction.kt | 3 + .../dev/enro/test/TestNavigationContainer.kt | 12 ++ .../test/TestNavigationHandle.assertClosed.kt | 8 +- ...NavigationHandle.assertClosedWithResult.kt | 21 +++- ...tNavigationHandle.assertContainerExists.kt | 46 ++++++++ .../test/TestNavigationHandle.assertOpened.kt | 36 +++++- ...avigationHandle.assertOpenedInstruction.kt | 105 ++++++++++++++++++ .../TestNavigationHandle.expectContainer.kt | 3 + .../TestNavigationHandle.expectInstruction.kt | 3 + .../dev/enro/test/TestNavigationHandle.kt | 8 ++ .../enro/test/extensions/ResultExtensions.kt | 10 ++ .../dev/enro/test/CreateResultChannelTest.kt | 2 +- .../java/dev/enro/test/EnroTestJvmTest.kt | 95 ++++++++-------- .../test/java/dev/enro/test/EnroTestTest.kt | 86 +++++++------- libs.versions.toml | 2 +- 22 files changed, 662 insertions(+), 109 deletions(-) create mode 100644 enro-test/src/main/java/dev/enro/test/NavigationInstruction.deliverResultForTest.kt create mode 100644 enro-test/src/main/java/dev/enro/test/NavigationKey.deliverResultForTest.kt create mode 100644 enro-test/src/main/java/dev/enro/test/TestNavigationHandle.assertContainerExists.kt create mode 100644 enro-test/src/main/java/dev/enro/test/TestNavigationHandle.assertOpenedInstruction.kt diff --git a/enro-test/src/main/java/dev/enro/test/EnroTest.kt b/enro-test/src/main/java/dev/enro/test/EnroTest.kt index a0c88f68..749ff855 100644 --- a/enro-test/src/main/java/dev/enro/test/EnroTest.kt +++ b/enro-test/src/main/java/dev/enro/test/EnroTest.kt @@ -45,6 +45,7 @@ object EnroTest { fun uninstallNavigationController() { EnroViewModelNavigationHandleProvider.clearAllForTest() + TestNavigationHandle.allInstructions.clear() navigationController?.apply { setConfig( config.copy( diff --git a/enro-test/src/main/java/dev/enro/test/EnroTestAssertions.kt b/enro-test/src/main/java/dev/enro/test/EnroTestAssertions.kt index a605ac97..9dd63109 100644 --- a/enro-test/src/main/java/dev/enro/test/EnroTestAssertions.kt +++ b/enro-test/src/main/java/dev/enro/test/EnroTestAssertions.kt @@ -1,5 +1,7 @@ package dev.enro.test +import dev.enro.core.NavigationKey + class EnroTestAssertionException(message: String) : AssertionError(message) @PublishedApi @@ -13,7 +15,7 @@ data class EnroAssertionContext( ) @PublishedApi -internal fun Any?.shouldBeEqualTo(expected: Any?, message: EnroAssertionContext.() -> String) { +internal fun T.shouldBeEqualTo(expected: Any?, message: EnroAssertionContext.() -> String): T { if (this != expected) { val assertionContext = EnroAssertionContext( expected = expected, @@ -21,10 +23,11 @@ internal fun Any?.shouldBeEqualTo(expected: Any?, message: EnroAssertionContext. ) throw EnroTestAssertionException(message(assertionContext)) } + return this } @PublishedApi -internal fun Any?.shouldNotBeEqualTo(expected: Any?, message: EnroAssertionContext.() -> String) { +internal fun T.shouldNotBeEqualTo(expected: Any?, message: EnroAssertionContext.() -> String): T { if (this == expected) { val assertionContext = EnroAssertionContext( expected = expected, @@ -32,4 +35,80 @@ internal fun Any?.shouldNotBeEqualTo(expected: Any?, message: EnroAssertionConte ) throw EnroTestAssertionException(message(assertionContext)) } + return this +} + +@PublishedApi +internal fun T.shouldMatchPredicate(predicate: (T) -> Boolean, message: EnroAssertionContext.() -> String): T { + val predicateResult = predicate(this) + if (!predicateResult) { + val assertionContext = EnroAssertionContext( + expected = null, + actual = this + ) + throw EnroTestAssertionException(message(assertionContext)) + } + return this +} + +@PublishedApi +internal fun T.shouldNotMatchPredicate(predicate: (T) -> Boolean, message: EnroAssertionContext.() -> String): T { + val predicateResult = predicate(this) + if (predicateResult) { + val assertionContext = EnroAssertionContext( + expected = null, + actual = this + ) + throw EnroTestAssertionException(message(assertionContext)) + } + return this +} + +@PublishedApi +internal fun T?.shouldMatchPredicateNotNull(predicate: (T) -> Boolean, message: EnroAssertionContext.() -> String): T { + if (this == null) { + throw EnroTestAssertionException("Expected a non-null value, but was null.") + } + + val predicateResult = predicate(this) + if (!predicateResult) { + val assertionContext = EnroAssertionContext( + expected = null, + actual = this + ) + throw EnroTestAssertionException(message(assertionContext)) + } + return this +} + +@PublishedApi +internal inline fun Any?.shouldBeInstanceOf(): T { + if (this == null) { + throw EnroTestAssertionException("Expected a non-null value, but was null.") + } + + val isCorrectType = this is T + if (!isCorrectType) { + val assertionContext = EnroAssertionContext( + expected = T::class, + actual = this::class + ) + throw EnroTestAssertionException("Expected type ${T::class.simpleName}, but was ${this::class.simpleName}") + } + return this as T +} + +@PublishedApi +internal fun Any?.shouldBeInstanceOf( + cls: Class, +) : T { + if (this == null) { + throw EnroTestAssertionException("Expected a non-null value, but was null.") + } + + val isCorrectType = cls.isAssignableFrom(this::class.java) + if (!isCorrectType) { + throw EnroTestAssertionException("Expected type ${cls.simpleName}, but was ${this::class.simpleName}") + } + return this as T } \ No newline at end of file diff --git a/enro-test/src/main/java/dev/enro/test/NavigationInstruction.deliverResultForTest.kt b/enro-test/src/main/java/dev/enro/test/NavigationInstruction.deliverResultForTest.kt new file mode 100644 index 00000000..58264471 --- /dev/null +++ b/enro-test/src/main/java/dev/enro/test/NavigationInstruction.deliverResultForTest.kt @@ -0,0 +1,37 @@ +@file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") +package dev.enro.test + +import dev.enro.core.NavigationInstruction +import dev.enro.core.NavigationKey +import dev.enro.core.result.EnroResult +import dev.enro.core.result.internal.PendingResult + +/** + * Given a NavigationInstruction.Open, this function will deliver a result to the instruction. This is useful for testing + * the behavior of a screen/ViewModel that expects a result. + */ +fun NavigationInstruction.Open<*>.deliverResultForTest(type: Class, result: T) { + val navigationController = EnroTest.getCurrentNavigationController() + val resultId = internal.resultId!! + + val navigationKey = internal.resultKey ?: navigationKey + + val pendingResult = PendingResult.Result( + resultChannelId = resultId, + instruction = this, + navigationKey = navigationKey as NavigationKey.WithResult, + resultType = type.kotlin, + result = result + ) + EnroResult + .from(navigationController) + .addPendingResult(pendingResult) +} + +/** + * Given a NavigationInstruction.Open, this function will deliver a result to the instruction. This is useful for testing + * the behavior of a screen/ViewModel that expects a result. + */ +inline fun NavigationInstruction.Open<*>.deliverResultForTest(result: T) { + deliverResultForTest(T::class.java, result) +} \ No newline at end of file diff --git a/enro-test/src/main/java/dev/enro/test/NavigationKey.deliverResultForTest.kt b/enro-test/src/main/java/dev/enro/test/NavigationKey.deliverResultForTest.kt new file mode 100644 index 00000000..ed85d167 --- /dev/null +++ b/enro-test/src/main/java/dev/enro/test/NavigationKey.deliverResultForTest.kt @@ -0,0 +1,62 @@ +@file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") +package dev.enro.test + +import dev.enro.core.NavigationInstruction +import dev.enro.core.NavigationKey +import dev.enro.core.result.EnroResult +import dev.enro.core.result.internal.PendingResult + +/** + * Given a NavigationKey.WithResult, this function will attempt to find the related NavigationInstruction that opened that + * NavigationKey, and deliver a result to that instruction. This is useful for testing the behavior of a screen/ViewModel + * that expects a result. + */ +fun NavigationKey.WithResult.deliverResultForTest( + type: Class, + result: T, +) { + val exactInstruction = TestNavigationHandle.allInstructions + .filterIsInstance>() + .firstOrNull { + System.identityHashCode(it.navigationKey) == System.identityHashCode(this) + } + val fuzzyInstructions = TestNavigationHandle.allInstructions + .filterIsInstance>() + .filter { + it.navigationKey == this + } + if (fuzzyInstructions.isEmpty()) { + throw EnroTestAssertionException("No instruction was found for NavigationKey $this") + } + val instruction = when { + exactInstruction != null -> exactInstruction + fuzzyInstructions.size == 1 -> fuzzyInstructions.first() + else -> { + throw EnroTestAssertionException("No instruction was found for NavigationKey $this") + } + } + val navigationController = EnroTest.getCurrentNavigationController() + val resultId = instruction.internal.resultId!! + val navigationKey = instruction.internal.resultKey ?: instruction.navigationKey + + @Suppress("UNCHECKED_CAST") + val pendingResult = PendingResult.Result( + resultChannelId = resultId, + instruction = instruction, + navigationKey = navigationKey as NavigationKey.WithResult, + resultType = type.kotlin, + result = result + ) + EnroResult + .from(navigationController) + .addPendingResult(pendingResult) +} + +/** + * Given a NavigationKey.WithResult, this function will attempt to find the related NavigationInstruction that opened that + * NavigationKey, and deliver a result to that instruction. This is useful for testing the behavior of a screen/ViewModel + * that expects a result. + */ +inline fun NavigationKey.WithResult.deliverResultForTest(result: T) { + deliverResultForTest(T::class.java, result) +} \ No newline at end of file diff --git a/enro-test/src/main/java/dev/enro/test/TestNavigationContainer.assertActive.kt b/enro-test/src/main/java/dev/enro/test/TestNavigationContainer.assertActive.kt index 265a442e..fd35b8e0 100644 --- a/enro-test/src/main/java/dev/enro/test/TestNavigationContainer.assertActive.kt +++ b/enro-test/src/main/java/dev/enro/test/TestNavigationContainer.assertActive.kt @@ -9,11 +9,27 @@ import dev.enro.core.container.NavigationContainerContext * NavigationInstruction [instruction] */ fun NavigationContainerContext.assertActive( - instruction: NavigationInstruction.Open<*> + instruction: NavigationInstruction.Open<*>, ) { - backstack.active.shouldBeEqualTo(instruction) { - "Active NavigationInstruction does not match expected value.\n\tExpected: $expected\n\tActual: $actual" - } + backstack.active + .shouldBeEqualTo(instruction) { + "Active NavigationInstruction does not match expected value.\n\tExpected: $expected\n\tActual: $actual" + } +} + +/** + * Asserts that the active NavigationInstruction in the NavigationContainerContext matches the provided predicate + * + * @return The active NavigationInstruction that matches the predicate + */ +fun NavigationContainerContext.assertActive( + predicate: (NavigationInstruction.Open<*>) -> Boolean, +): NavigationInstruction.Open<*> { + backstack.active + .shouldMatchPredicateNotNull(predicate) { + "Active NavigationInstruction does not match predicate.\n\tWas: $actual" + } + .let { return it } } /** @@ -21,13 +37,30 @@ fun NavigationContainerContext.assertActive( * the provided NavigationKey [key] */ fun NavigationContainerContext.assertActive( - key: NavigationKey + key: NavigationKey, ) { backstack.active?.navigationKey.shouldBeEqualTo(key) { "Active NavigationInstruction's NavigationKey does not match expected value.\n\tExpected: $expected\n\tActual: $actual" } } +/** + * Asserts that the active NavigationInstruction in the NavigationContainerContext has a NavigationKey that matches the + * provided type T and the provided predicate + * + * @return The active NavigationInstruction's NavigationKey that matches the predicate + */ +inline fun NavigationContainerContext.assertActive( + noinline predicate: (T) -> Boolean = { true } +) : T { + backstack.active?.navigationKey + .shouldBeInstanceOf() + .shouldMatchPredicateNotNull(predicate) { + "Active NavigationInstruction's NavigationKey does not match predicate.\n\tWas: $actual" + } + .let { return it } +} + /** * Asserts that the active NavigationInstruction in the NavigationContainerContext is not equal to the * provided NavigationInstruction [instruction] @@ -40,6 +73,17 @@ fun NavigationContainerContext.assertNotActive( } } +/** + * Asserts that the active NavigationInstruction in the NavigationContainerContext does not match the provided predicate + */ +fun NavigationContainerContext.assertInstructionNotActive( + predicate: (NavigationInstruction.Open<*>) -> Boolean +) { + backstack.active.shouldNotBeEqualTo(predicate) { + "Active NavigationInstruction should not be active.\n\tActive: $expected" + } +} + /** * Asserts that the active NavigationInstruction in the NavigationContainerContext has a NavigationKey that is not equal * to the provided NavigationKey [key] @@ -50,4 +94,18 @@ fun NavigationContainerContext.assertNotActive( backstack.active?.navigationKey.shouldNotBeEqualTo(key) { "Active NavigationInstruction's NavigationKey should not be active.\n\tActive: $expected" } +} + +/** + * Asserts that the active NavigationInstruction in the NavigationContainerContext has a NavigationKey that does not match the + * provided type T and the provided predicate + */ +inline fun NavigationContainerContext.assertNotActive( + noinline predicate: (T) -> Boolean = { true } +) { + val activeKey = backstack.active?.navigationKey + if (activeKey !is T) return + activeKey.shouldNotMatchPredicate(predicate) { + "Active NavigationInstruction's NavigationKey should not match predicate.\n\tWas: $actual" + } } \ No newline at end of file diff --git a/enro-test/src/main/java/dev/enro/test/TestNavigationContainer.assertBackstack.kt b/enro-test/src/main/java/dev/enro/test/TestNavigationContainer.assertBackstack.kt index 979511b1..24fe6695 100644 --- a/enro-test/src/main/java/dev/enro/test/TestNavigationContainer.assertBackstack.kt +++ b/enro-test/src/main/java/dev/enro/test/TestNavigationContainer.assertBackstack.kt @@ -22,3 +22,16 @@ fun NavigationContainerContext.assertBackstackEquals( } } } + +/** + * Asserts that the NavigationContainerContext's backstack matches the provided predicate + */ +fun NavigationContainerContext.assertBackstackMatches( + predicate: (NavigationBackstack) -> Boolean +) { + val actualBackstack = this.backstack + + actualBackstack.shouldMatchPredicateNotNull(predicate) { + "NavigationContainer's backstack did not match predicate\n\tActual backstack: $actualBackstack" + } +} \ No newline at end of file diff --git a/enro-test/src/main/java/dev/enro/test/TestNavigationContainer.assertContains.kt b/enro-test/src/main/java/dev/enro/test/TestNavigationContainer.assertContains.kt index e0f620a6..d3ed0d60 100644 --- a/enro-test/src/main/java/dev/enro/test/TestNavigationContainer.assertContains.kt +++ b/enro-test/src/main/java/dev/enro/test/TestNavigationContainer.assertContains.kt @@ -19,6 +19,24 @@ fun NavigationContainerContext.assertContains( } } +/** + * Asserts that the NavigationContainerContext's backstack contains at least one NavigationInstruction that matches the + * provided predicate. + * + * @return The first NavigationInstruction that matches the predicate + */ +fun NavigationContainerContext.assertContains( + predicate: (NavigationInstruction.Open<*>) -> Boolean, +) : NavigationInstruction.Open<*> { + backstack.firstOrNull(predicate) + .shouldNotBeEqualTo( + null, + ) { + "NavigationContainer's backstack does not contain expected NavigationInstruction.\n\tBackstack: $backstack" + } + .let { return it!! } +} + /** * Asserts that the NavigationContainerContext's backstack contains at least one NavigationInstruction that has a * NavigationKey that is equal to the provided NavigationKey [key] @@ -36,6 +54,24 @@ fun NavigationContainerContext.assertContains( } } +/** + * Asserts that the NavigationContainerContext's backstack contains at least one NavigationInstruction that has a + * NavigationKey that of type T and matches the provided predicate + * + * @return The first NavigationKey that matches the predicate + */ +inline fun NavigationContainerContext.assertContains( + noinline predicate: (T) -> Boolean = { true } +) : T { + val backstackAsNavigationKeys = backstack.map { it.navigationKey } + val found = backstackAsNavigationKeys + .firstOrNull { it is T && predicate(it) } + if (found == null) { + throw EnroTestAssertionException("NavigationContainer's backstack does not contain NavigationKey matching predicate.\n\tExpected NavigationKey type: ${T::class}\n\tBackstack: $backstackAsNavigationKeys") + } + return found as T +} + /** * Asserts that the NavigationContainerContext's backstack does not contain a NavigationInstruction that is equal to * the provided NavigationInstruction [instruction] @@ -51,6 +87,21 @@ fun NavigationContainerContext.assertDoesNotContain( } } +/** + * Asserts that the NavigationContainerContext's backstack does not contain a NavigationInstruction that matches the provided + * predicate + */ +fun NavigationContainerContext.assertDoesNotContain( + predicate: (NavigationInstruction.Open<*>) -> Boolean, +) { + backstack.firstOrNull(predicate) + .shouldBeEqualTo( + null, + ) { + "NavigationContainer's backstack should not contain NavigationInstruction matching predicate.\n\tBackstack: $backstack" + } +} + /** * Asserts that the NavigationContainerContext's backstack does not contain an instruction that has a NavigationKey that is * equal to the provided NavigationKey [key] @@ -65,4 +116,20 @@ fun NavigationContainerContext.assertDoesNotContain( ) { "NavigationContainer's backstack should not contain NavigationKey.\n\tNavigationInstruction: $expected\n\tBackstack: $backstackAsNavigationKeys" } +} + +/** + * Asserts that the NavigationContainerContext's backstack does not contain an instruction that has a NavigationKey that is + * of type T and matches the provided predicate + */ +inline fun NavigationContainerContext.assertDoesNotContainer( + noinline predicate: (T) -> Boolean = { true } +) { + val backstackAsNavigationKeys = backstack.map { it.navigationKey } + backstack.firstOrNull { it is T && predicate(it) } + .shouldBeEqualTo( + null, + ) { + "NavigationContainer's backstack should not contain NavigationKey matching predicate.\n\tBackstack: $backstackAsNavigationKeys" + } } \ No newline at end of file diff --git a/enro-test/src/main/java/dev/enro/test/TestNavigationContainer.expectOpenInstruction.kt b/enro-test/src/main/java/dev/enro/test/TestNavigationContainer.expectOpenInstruction.kt index d1bf055d..2543cf21 100644 --- a/enro-test/src/main/java/dev/enro/test/TestNavigationContainer.expectOpenInstruction.kt +++ b/enro-test/src/main/java/dev/enro/test/TestNavigationContainer.expectOpenInstruction.kt @@ -7,6 +7,7 @@ import dev.enro.core.container.NavigationContainerContext * Asserts that the NavigationContainerContext's backstack contains a NavigationInstruction with a NavigationKey of type [T] * that matches the provided filter, and then returns that NavigationInstruction */ +@Deprecated("Use assertContains instead") fun NavigationContainerContext.expectOpenInstruction( type: Class, filter: (T) -> Boolean = { true } @@ -33,6 +34,7 @@ fun NavigationContainerContext.expectOpenInstruction( * Asserts that the NavigationContainerContext's backstack contains a NavigationInstruction with a NavigationKey of type [T] * that matches the provided filter, and then returns that NavigationInstruction */ +@Deprecated("Use assertContains instead") inline fun NavigationContainerContext.expectOpenInstruction(noinline filter: (T) -> Boolean = { true }): NavigationInstruction.Open<*> { return expectOpenInstruction(T::class.java, filter) } @@ -41,6 +43,7 @@ inline fun NavigationContainerContext.expectOpenInstruction(no * Asserts that the NavigationContainerContext's backstack contains a NavigationInstruction with a NavigationKey * that is equal to the provided key, and then returns that NavigationInstruction */ +@Deprecated("Use assertContains instead") inline fun NavigationContainerContext.expectOpenInstruction(key: T): NavigationInstruction.Open<*> { return expectOpenInstruction(T::class.java) { it == key } } \ No newline at end of file diff --git a/enro-test/src/main/java/dev/enro/test/TestNavigationContainer.kt b/enro-test/src/main/java/dev/enro/test/TestNavigationContainer.kt index 21509650..08e13e8f 100644 --- a/enro-test/src/main/java/dev/enro/test/TestNavigationContainer.kt +++ b/enro-test/src/main/java/dev/enro/test/TestNavigationContainer.kt @@ -3,6 +3,7 @@ package dev.enro.test import android.os.Bundle import androidx.core.os.bundleOf import dev.enro.core.NavigationContainerKey +import dev.enro.core.NavigationInstruction import dev.enro.core.container.NavigationBackstack import dev.enro.core.container.NavigationContainerContext import dev.enro.core.container.emptyBackstack @@ -18,6 +19,17 @@ public class TestNavigationContainer( override val backstack: NavigationBackstack get() = backstackFlow.value override fun setBackstack(backstack: NavigationBackstack) { + val backstackIds = backstack + .map { it.instructionId } + .toSet() + + TestNavigationHandle.allInstructions.apply { + removeAll { + it is NavigationInstruction.Open<*> && it.instructionId in backstackIds + } + addAll(backstack) + } + mutableBackstackFlow.value = backstack } diff --git a/enro-test/src/main/java/dev/enro/test/TestNavigationHandle.assertClosed.kt b/enro-test/src/main/java/dev/enro/test/TestNavigationHandle.assertClosed.kt index 627bc830..db81102c 100644 --- a/enro-test/src/main/java/dev/enro/test/TestNavigationHandle.assertClosed.kt +++ b/enro-test/src/main/java/dev/enro/test/TestNavigationHandle.assertClosed.kt @@ -5,7 +5,7 @@ import dev.enro.core.NavigationInstruction /** * Asserts that the NavigationHandle has received a RequestClose instruction */ -fun TestNavigationHandle<*>.assertRequestedClose() { +fun TestNavigationHandle<*>.assertRequestedClose() : NavigationInstruction.RequestClose { val instruction = instructions .filterIsInstance() .lastOrNull() @@ -13,18 +13,22 @@ fun TestNavigationHandle<*>.assertRequestedClose() { instruction.shouldNotBeEqualTo(null) { "NavigationHandle was expected to have executed a RequestClose instruction, but no RequestClose instruction was found" } + return instruction!! } /** * Asserts that the NavigationHandle has received a Close instruction + * + * @return the Close instruction that was executed */ -fun TestNavigationHandle<*>.assertClosed() { +fun TestNavigationHandle<*>.assertClosed() : NavigationInstruction.Close { val instruction = instructions.filterIsInstance() .lastOrNull() instruction.shouldNotBeEqualTo(null) { "NavigationHandle was expected to have executed a Close instruction, but no Close instruction was found" } + return instruction!! } /** diff --git a/enro-test/src/main/java/dev/enro/test/TestNavigationHandle.assertClosedWithResult.kt b/enro-test/src/main/java/dev/enro/test/TestNavigationHandle.assertClosedWithResult.kt index f698e135..477731e7 100644 --- a/enro-test/src/main/java/dev/enro/test/TestNavigationHandle.assertClosedWithResult.kt +++ b/enro-test/src/main/java/dev/enro/test/TestNavigationHandle.assertClosedWithResult.kt @@ -5,11 +5,13 @@ import kotlin.reflect.KClass /** * Asserts that the NavigationHandle has executed a Close.WithResult instruction, and that the result matches the provided predicate + * + * @return the result of the Close.WithResult instruction */ fun TestNavigationHandle<*>.assertClosedWithResult( type: KClass, predicate: (T) -> Boolean = { true }, -) { +) : T { val instruction = instructions.filterIsInstance() .lastOrNull() @@ -29,14 +31,17 @@ fun TestNavigationHandle<*>.assertClosedWithResult( predicate(result).shouldBeEqualTo(true) { "NavigationHandle's Close.WithResult did not match the provided predicate\n\tResult: $result" } + return result } /** * Asserts that the NavigationHandle has executed a Close.WithResult instruction, and that the result matches the provided predicate + * + * @return the result of the Close.WithResult instruction */ inline fun TestNavigationHandle<*>.assertClosedWithResult( predicate: (T) -> Boolean = { true }, -) { +) : T { val instruction = instructions.filterIsInstance() .lastOrNull() @@ -56,6 +61,7 @@ inline fun TestNavigationHandle<*>.assertClosedWithResult( predicate(result).shouldBeEqualTo(true) { "NavigationHandle's Close.WithResult did not match the provided predicate\n\tResult: $result" } + return result } /** @@ -81,6 +87,7 @@ fun TestNavigationHandle<*>.assertClosedWithResult( /** * Asserts that the NavigationHandle has not executed a Close.WithResult instruction */ +@Deprecated("Use assertNotClosed or assertClosedWithNoResult") fun TestNavigationHandle<*>.assertNotClosedWithResult() { val instruction = instructions.filterIsInstance() .lastOrNull() @@ -88,4 +95,14 @@ fun TestNavigationHandle<*>.assertNotClosedWithResult() { instruction.shouldBeEqualTo(null) { "NavigationHandle should not have executed a Close.WithResult instruction, but a Close.WithResult instruction was found" } +} + +/** + * Asserts that the NavigationHandle has executed a Close instruction, but not a Close.WithResult instruction + */ +fun TestNavigationHandle<*>.assertClosedWithNoResult() { + val closeInstruction = assertClosed() + if (closeInstruction is NavigationInstruction.Close.WithResult) { + enroAssertionError("NavigationHandle was closed with result:\n\t${closeInstruction.result}") + } } \ No newline at end of file diff --git a/enro-test/src/main/java/dev/enro/test/TestNavigationHandle.assertContainerExists.kt b/enro-test/src/main/java/dev/enro/test/TestNavigationHandle.assertContainerExists.kt new file mode 100644 index 00000000..8440d962 --- /dev/null +++ b/enro-test/src/main/java/dev/enro/test/TestNavigationHandle.assertContainerExists.kt @@ -0,0 +1,46 @@ +package dev.enro.test + +import dev.enro.core.NavigationContainerKey +import dev.enro.core.container.NavigationContainerContext +import dev.enro.core.onActiveContainer +import dev.enro.core.onContainer +import dev.enro.core.onParentContainer + +/** + * Asserts that the NavigationHandle has a parent container, and then returns the NavigationContainerContext associated + * with that container, which can be used for further assertions. + */ +fun TestNavigationHandle<*>.assertParentContainerExists(): NavigationContainerContext { + var container: NavigationContainerContext? = null + onParentContainer { container = this@onParentContainer } + container.shouldNotBeEqualTo(null) { + "NavigationHandle does not have a parent container" + } + return requireNotNull(container) +} + +/** + * Asserts that the NavigationHandle has an active container, and then returns the NavigationContainerContext associated + * with that container, which can be used for further assertions. + */ +fun TestNavigationHandle<*>.assertActiveContainerExists(): NavigationContainerContext { + var container: NavigationContainerContext? = null + onActiveContainer { container = this@onActiveContainer } + container.shouldNotBeEqualTo(null) { + "NavigationHandle does not have an active container" + } + return requireNotNull(container) +} + +/** + * Asserts that the NavigationHandle has a container with the provided NavigationContainerKey [key], and then returns + * the NavigationContainerContext associated with that container, which can be used for further assertions. + */ +fun TestNavigationHandle<*>.assertContainerExists(key: NavigationContainerKey): NavigationContainerContext { + var container: NavigationContainerContext? = null + onContainer(key) { container = this@onContainer } + container.shouldNotBeEqualTo(null) { + "NavigationHandle does not have a container with key $key" + } + return requireNotNull(container) +} \ No newline at end of file diff --git a/enro-test/src/main/java/dev/enro/test/TestNavigationHandle.assertOpened.kt b/enro-test/src/main/java/dev/enro/test/TestNavigationHandle.assertOpened.kt index 77d3989a..bf51dd79 100644 --- a/enro-test/src/main/java/dev/enro/test/TestNavigationHandle.assertOpened.kt +++ b/enro-test/src/main/java/dev/enro/test/TestNavigationHandle.assertOpened.kt @@ -2,6 +2,7 @@ package dev.enro.test import dev.enro.core.NavigationDirection import dev.enro.core.NavigationInstruction +import dev.enro.core.NavigationKey /** * This method asserts that the last navigation instruction the NavigationHandle executed was a NavigationInstruction.Open @@ -10,7 +11,11 @@ import dev.enro.core.NavigationInstruction * If you want to assert that any NavigationInstruction.Open was executed, and don't care whether the instruction was the * last instruction or not, use [assertAnyOpened]. */ -fun TestNavigationHandle<*>.assertOpened(type: Class, direction: NavigationDirection? = null): T { +fun TestNavigationHandle<*>.assertOpened( + type: Class, + direction: NavigationDirection? = null, + predicate: (T) -> Boolean = { true } +): T { val openInstructions = instructions.filterIsInstance>() if (openInstructions.isEmpty()) { enroAssertionError("NavigationHandle has not executed any NavigationInstruction.Open") @@ -25,6 +30,11 @@ fun TestNavigationHandle<*>.assertOpened(type: Class, direction: Na "NavigationHandle was expected to have executed a NavigationInstruction.Open with a NavigationDirection of $direction, but the NavigationDirection was ${instruction.navigationDirection}" } } + instruction.navigationKey + .shouldBeInstanceOf(type) + .shouldMatchPredicate(predicate) { + "NavigationHandle was expected to have executed a NavigationInstruction.Open with a NavigationKey that matched the provided predicate, but the NavigationKey did not match the predicate" + } return instruction.navigationKey as T } @@ -35,7 +45,7 @@ fun TestNavigationHandle<*>.assertOpened(type: Class, direction: Na * If you want to assert that any NavigationInstruction.Open was executed, and don't care whether the instruction was the * last instruction or not, use [assertAnyOpened]. */ -inline fun TestNavigationHandle<*>.assertOpened(direction: NavigationDirection? = null): T { +inline fun TestNavigationHandle<*>.assertOpened(direction: NavigationDirection? = null): T { return assertOpened(T::class.java, direction) } @@ -46,13 +56,20 @@ inline fun TestNavigationHandle<*>.assertOpened(direction: Nav * * If you care about ordering, and you want to assert on the last NavigationInstruction.Open executed, use [assertOpened]. */ -fun TestNavigationHandle<*>.assertAnyOpened(type: Class, direction: NavigationDirection? = null): T { +fun TestNavigationHandle<*>.assertAnyOpened( + type: Class, + direction: NavigationDirection? = null, + predicate: (T) -> Boolean = { true } +): T { val openInstructions = instructions.filterIsInstance>() if (openInstructions.isEmpty()) { enroAssertionError("NavigationHandle has not executed any NavigationInstruction.Open") } - val instruction = openInstructions.lastOrNull { type.isAssignableFrom(it.navigationKey::class.java) } + val instruction = openInstructions.lastOrNull { + type.isAssignableFrom(it.navigationKey::class.java) && + runCatching { predicate(it.navigationKey as T) }.getOrDefault(false) + } if (instruction == null) { enroAssertionError("NavigationHandle has not executed any NavigationInstruction.Open with a NavigationKey of type $type") } @@ -74,8 +91,15 @@ fun TestNavigationHandle<*>.assertAnyOpened(type: Class, direction: * * If you care about ordering, and you want to assert on the last NavigationInstruction.Open executed, use [assertOpened]. */ -inline fun TestNavigationHandle<*>.assertAnyOpened(direction: NavigationDirection? = null): T { - return assertAnyOpened(T::class.java, direction) +inline fun TestNavigationHandle<*>.assertAnyOpened( + direction: NavigationDirection? = null, + noinline predicate: (T) -> Boolean = { true } +): T { + return assertAnyOpened( + type = T::class.java, + direction = direction, + predicate = predicate + ) } /** diff --git a/enro-test/src/main/java/dev/enro/test/TestNavigationHandle.assertOpenedInstruction.kt b/enro-test/src/main/java/dev/enro/test/TestNavigationHandle.assertOpenedInstruction.kt new file mode 100644 index 00000000..29c49190 --- /dev/null +++ b/enro-test/src/main/java/dev/enro/test/TestNavigationHandle.assertOpenedInstruction.kt @@ -0,0 +1,105 @@ +package dev.enro.test + +import dev.enro.core.NavigationDirection +import dev.enro.core.NavigationInstruction +import dev.enro.core.NavigationKey + +/** + * This method asserts that the last navigation instruction the NavigationHandle executed was a NavigationInstruction.Open + * with a NavigationKey of the provided type [T] and direction (if the direction parameter was not null). + * + * If you want to assert that any NavigationInstruction.Open was executed, and don't care whether the instruction was the + * last instruction or not, use [assertAnyInstructionOpened]. + */ +fun TestNavigationHandle<*>.assertInstructionOpened( + type: Class, + direction: NavigationDirection? = null, + predicate: (T) -> Boolean = { true } +): NavigationInstruction.Open<*> { + val openInstructions = instructions.filterIsInstance>() + if (openInstructions.isEmpty()) { + enroAssertionError("NavigationHandle has not executed any NavigationInstruction.Open") + } + + val instruction = openInstructions.last() + type.isAssignableFrom(instruction.navigationKey::class.java).shouldBeEqualTo(true) { + "NavigationHandle was expected to have executed a NavigationInstruction.Open with a NavigationKey of type $type, but the NavigationKey was of type ${instruction.navigationKey::class.java}" + } + if (direction != null) { + instruction.navigationDirection.shouldBeEqualTo(direction) { + "NavigationHandle was expected to have executed a NavigationInstruction.Open with a NavigationDirection of $direction, but the NavigationDirection was ${instruction.navigationDirection}" + } + } + instruction.navigationKey + .shouldBeInstanceOf(type) + .shouldMatchPredicate(predicate) { + "NavigationHandle was expected to have executed a NavigationInstruction.Open with a NavigationKey that matched the provided predicate, but the NavigationKey did not match the predicate" + } + return instruction +} + +/** + * This method asserts that the last navigation instruction the NavigationHandle executed was a NavigationInstruction.Open + * with a NavigationKey of the provided type [T] and direction (if the direction parameter was not null). + * + * If you want to assert that any NavigationInstruction.Open was executed, and don't care whether the instruction was the + * last instruction or not, use [assertAnyInstructionOpened]. + */ +inline fun TestNavigationHandle<*>.assertInstructionOpened( + direction: NavigationDirection? = null +): NavigationInstruction.Open<*> { + return assertInstructionOpened(T::class.java, direction) +} + +/** + * This method asserts that the NavigationHandle has executed a NavigationInstruction.Open with a NavigationKey of the + * provided type [T] and direction (if the direction parameter was not null). This method does not care about the order + * of the instructions executed by the NavigationHandle. + * + * If you care about ordering, and you want to assert on the last NavigationInstruction.Open executed, use [assertInstructionOpened]. + */ +fun TestNavigationHandle<*>.assertAnyInstructionOpened( + type: Class, + direction: NavigationDirection? = null, + predicate: (T) -> Boolean = { true } +): NavigationInstruction.Open<*> { + val openInstructions = instructions.filterIsInstance>() + + if (openInstructions.isEmpty()) { + enroAssertionError("NavigationHandle has not executed any NavigationInstruction.Open") + } + val instruction = openInstructions.lastOrNull { + type.isAssignableFrom(it.navigationKey::class.java) && + runCatching { predicate(it.navigationKey as T) }.getOrDefault(false) + } + if (instruction == null) { + enroAssertionError("NavigationHandle has not executed any NavigationInstruction.Open with a NavigationKey of type $type") + } + type.isAssignableFrom(instruction.navigationKey::class.java).shouldBeEqualTo(true) { + "NavigationHandle was expected to have executed a NavigationInstruction.Open with a NavigationKey of type $type, but the NavigationKey was of type ${instruction.navigationKey::class.java}" + } + if (direction != null) { + instruction.navigationDirection.shouldBeEqualTo(direction) { + "NavigationHandle was expected to have executed a NavigationInstruction.Open with a NavigationDirection of $direction, but the NavigationDirection was ${instruction.navigationDirection}" + } + } + return instruction +} + +/** + * This method asserts that the NavigationHandle has executed a NavigationInstruction.Open with a NavigationKey of the + * provided type [T] and direction (if the direction parameter was not null). This method does not care about the order + * of the instructions executed by the NavigationHandle. + * + * If you care about ordering, and you want to assert on the last NavigationInstruction.Open executed, use [assertInstructionOpened]. + */ +inline fun TestNavigationHandle<*>.assertAnyInstructionOpened( + direction: NavigationDirection? = null, + noinline predicate: (T) -> Boolean = { true } +): NavigationInstruction.Open<*> { + return assertAnyInstructionOpened( + type = T::class.java, + direction = direction, + predicate = predicate + ) +} \ No newline at end of file diff --git a/enro-test/src/main/java/dev/enro/test/TestNavigationHandle.expectContainer.kt b/enro-test/src/main/java/dev/enro/test/TestNavigationHandle.expectContainer.kt index 5a893357..cb5df3b4 100644 --- a/enro-test/src/main/java/dev/enro/test/TestNavigationHandle.expectContainer.kt +++ b/enro-test/src/main/java/dev/enro/test/TestNavigationHandle.expectContainer.kt @@ -10,6 +10,7 @@ import dev.enro.core.onParentContainer * Asserts that the NavigationHandle has a parent container, and then returns the NavigationContainerContext associated * with that container, which can be used for further assertions. */ +@Deprecated("Use assertParentContainerExists instead") fun TestNavigationHandle<*>.expectParentContainer(): NavigationContainerContext { var container: NavigationContainerContext? = null onParentContainer { container = this@onParentContainer } @@ -23,6 +24,7 @@ fun TestNavigationHandle<*>.expectParentContainer(): NavigationContainerContext * Asserts that the NavigationHandle has an active container, and then returns the NavigationContainerContext associated * with that container, which can be used for further assertions. */ +@Deprecated("Use assertActiveContainerExists instead") fun TestNavigationHandle<*>.expectActiveContainer(): NavigationContainerContext { var container: NavigationContainerContext? = null onActiveContainer { container = this@onActiveContainer } @@ -36,6 +38,7 @@ fun TestNavigationHandle<*>.expectActiveContainer(): NavigationContainerContext * Asserts that the NavigationHandle has a container with the provided NavigationContainerKey [key], and then returns * the NavigationContainerContext associated with that container, which can be used for further assertions. */ +@Deprecated("Use assertActiveContainerExists instead") fun TestNavigationHandle<*>.expectContainer(key: NavigationContainerKey): NavigationContainerContext { var container: NavigationContainerContext? = null onContainer(key) { container = this@onContainer } diff --git a/enro-test/src/main/java/dev/enro/test/TestNavigationHandle.expectInstruction.kt b/enro-test/src/main/java/dev/enro/test/TestNavigationHandle.expectInstruction.kt index e07a984a..368962d2 100644 --- a/enro-test/src/main/java/dev/enro/test/TestNavigationHandle.expectInstruction.kt +++ b/enro-test/src/main/java/dev/enro/test/TestNavigationHandle.expectInstruction.kt @@ -11,6 +11,7 @@ fun TestNavigationHandle<*>.expectCloseInstruction() { * Asserts that the NavigationHandle has received a NavigationInstruction with a NavigationKey that is assignable to type [T] and * which matches the provided filter, and then returns that NavigationInstruction. */ +@Deprecated("Use assertAnyInstructionOpened instead") fun TestNavigationHandle<*>.expectOpenInstruction( type: Class, filter: (T) -> Boolean = { true } @@ -41,6 +42,7 @@ fun TestNavigationHandle<*>.expectOpenInstruction( * Asserts that the NavigationHandle has received a NavigationInstruction with a NavigationKey that is assignable to type [T] and * which matches the provided filter, and then returns that NavigationInstruction. */ +@Deprecated("Use assertAnyInstructionOpened instead") inline fun TestNavigationHandle<*>.expectOpenInstruction(noinline filter: (T) -> Boolean = { true }): NavigationInstruction.Open<*> { return expectOpenInstruction(T::class.java, filter) } @@ -49,6 +51,7 @@ inline fun TestNavigationHandle<*>.expectOpenInstruction(noinl * Asserts that the NavigationHandle has received a NavigationInstruction with a NavigationKey that is equal to the provided * NavigationKey [key], and then returns that NavigationInstruction. */ +@Deprecated("Use assertAnyInstructionOpened instead") inline fun TestNavigationHandle<*>.expectOpenInstruction(key: T): NavigationInstruction.Open<*> { return expectOpenInstruction(T::class.java) { it == key } } \ No newline at end of file diff --git a/enro-test/src/main/java/dev/enro/test/TestNavigationHandle.kt b/enro-test/src/main/java/dev/enro/test/TestNavigationHandle.kt index d0fd1e24..618c3748 100644 --- a/enro-test/src/main/java/dev/enro/test/TestNavigationHandle.kt +++ b/enro-test/src/main/java/dev/enro/test/TestNavigationHandle.kt @@ -42,8 +42,16 @@ class TestNavigationHandle( } override fun executeInstruction(navigationInstruction: NavigationInstruction) { + if (instructions.lastOrNull() is NavigationInstruction.Close) { + throw IllegalStateException("TestNavigationHandle has received a close instruction and can no longer execute instructions") + } + allInstructions.add(navigationInstruction) navigationHandle.executeInstruction(navigationInstruction) } + + companion object { + internal val allInstructions = mutableListOf() + } } /** diff --git a/enro-test/src/main/java/dev/enro/test/extensions/ResultExtensions.kt b/enro-test/src/main/java/dev/enro/test/extensions/ResultExtensions.kt index 6194d5d0..0f0d0426 100644 --- a/enro-test/src/main/java/dev/enro/test/extensions/ResultExtensions.kt +++ b/enro-test/src/main/java/dev/enro/test/extensions/ResultExtensions.kt @@ -8,6 +8,11 @@ import dev.enro.core.result.EnroResult import dev.enro.core.result.internal.PendingResult import dev.enro.test.EnroTest +/** + * Given a NavigationInstruction.Open, this function will deliver a result to the instruction. This is useful for testing + * the behavior of a screen/ViewModel that expects a result. + */ +@Deprecated("Use deliverResultForTest instead") fun NavigationInstruction.Open<*>.sendResultForTest(type: Class, result: T) { val navigationController = EnroTest.getCurrentNavigationController() val resultId = internal.resultId!! @@ -26,6 +31,11 @@ fun NavigationInstruction.Open<*>.sendResultForTest(type: Class, re .addPendingResult(pendingResult) } +/** + * Given a NavigationInstruction.Open, this function will deliver a result to the instruction. This is useful for testing + * the behavior of a screen/ViewModel that expects a result. + */ +@Deprecated("Use deliverResultForTest instead") inline fun NavigationInstruction.Open<*>.sendResultForTest(result: T) { sendResultForTest(T::class.java, result) } diff --git a/enro/src/test/java/dev/enro/test/CreateResultChannelTest.kt b/enro/src/test/java/dev/enro/test/CreateResultChannelTest.kt index 0eabd73f..9388a8cf 100644 --- a/enro/src/test/java/dev/enro/test/CreateResultChannelTest.kt +++ b/enro/src/test/java/dev/enro/test/CreateResultChannelTest.kt @@ -41,7 +41,7 @@ class CreateResultChannelTest { @Test fun resultChannelsAreUniquelyIdentifiableWithinViewModel_keyWithRepeatedLambda() { - val createResultChannel = createTestNavigationHandle(ResultChannelTestKey()) + createTestNavigationHandle(ResultChannelTestKey()) .dependencyScope .get() diff --git a/enro/src/test/java/dev/enro/test/EnroTestJvmTest.kt b/enro/src/test/java/dev/enro/test/EnroTestJvmTest.kt index 8011adb5..91d229da 100644 --- a/enro/src/test/java/dev/enro/test/EnroTestJvmTest.kt +++ b/enro/src/test/java/dev/enro/test/EnroTestJvmTest.kt @@ -6,12 +6,13 @@ import dev.enro.core.onContainer import dev.enro.core.onParentContainer import dev.enro.core.requestClose import dev.enro.test.extensions.putNavigationHandleForViewModel -import dev.enro.test.extensions.sendResultForTest -import org.junit.Assert -import org.junit.Assert.* +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertTrue +import org.junit.Assert.fail import org.junit.Rule import org.junit.Test -import java.util.* +import java.util.UUID class EnroTestJvmTest { @@ -31,7 +32,7 @@ class EnroTestJvmTest { @Test fun whenPutNavigationHandleForTesting_andViewModelIsCreated_theViewModelIsCreatedSuccessfully() { - val navigationHandle = putNavigationHandleForViewModel(TestTestNavigationKey()) + putNavigationHandleForViewModel(TestTestNavigationKey()) val viewModel = factory.create(TestTestViewModel::class.java) assertNotNull(viewModel) } @@ -54,9 +55,9 @@ class EnroTestJvmTest { assertNotNull(viewModel) viewModel.openStringOne() - val instruction = navigationHandle.expectOpenInstruction() + val instruction = navigationHandle.assertAnyInstructionOpened() navigationHandle.assertOpened() - instruction.sendResultForTest("wow") + instruction.deliverResultForTest("wow") assertEquals("wow", viewModel.stringOneResult) } @@ -74,7 +75,7 @@ class EnroTestJvmTest { assertEquals(id, key.id) runCatching { navigationHandle.assertNoneOpened() - }.onSuccess { Assert.fail() } + }.onSuccess { fail() } } @Test @@ -85,17 +86,17 @@ class EnroTestJvmTest { viewModel.openStringOne() - navigationHandle.expectOpenInstruction() - .sendResultForTest("first") + navigationHandle.assertAnyInstructionOpened() + .deliverResultForTest("first") - navigationHandle.expectOpenInstruction() - .sendResultForTest("second") + navigationHandle.assertAnyInstructionOpened() + .deliverResultForTest("second") - navigationHandle.expectOpenInstruction() - .sendResultForTest(1) + navigationHandle.assertAnyInstructionOpened() + .deliverResultForTest(1) - navigationHandle.expectOpenInstruction() - .sendResultForTest(2) + navigationHandle.assertAnyInstructionOpened() + .deliverResultForTest(2) assertEquals("first", viewModel.stringOneResult) assertEquals("second", viewModel.stringTwoResult) @@ -104,15 +105,14 @@ class EnroTestJvmTest { runCatching { navigationHandle.assertNoneOpened() - }.onSuccess { Assert.fail() } + }.onSuccess { fail() } navigationHandle.assertAnyOpened() navigationHandle.assertAnyOpened() - navigationHandle.expectCloseInstruction() navigationHandle.assertClosed() runCatching { navigationHandle.assertNotClosed() - }.onSuccess { Assert.fail() } + }.onSuccess { fail() } } @Test @@ -125,11 +125,11 @@ class EnroTestJvmTest { viewModel.sendResult(expectedResult) runCatching { - navigationHandle.assertNoResultDelivered() + navigationHandle.assertNotClosed() }.onSuccess { fail() } - navigationHandle.assertResultDelivered(expectedResult) - navigationHandle.assertResultDelivered { it == expectedResult } - val result = navigationHandle.assertResultDelivered() + navigationHandle.assertClosedWithResult(expectedResult) + navigationHandle.assertClosedWithResult { it == expectedResult } + val result = navigationHandle.assertClosedWithResult() assertEquals(expectedResult, result) } @@ -141,16 +141,14 @@ class EnroTestJvmTest { val expectedResult = UUID.randomUUID().toString() runCatching { - navigationHandle.assertResultDelivered(expectedResult) + navigationHandle.assertClosedWithResult(expectedResult) }.onSuccess { fail() } runCatching { - navigationHandle.assertResultDelivered() + navigationHandle.assertClosedWithResult() }.onSuccess { fail() } - navigationHandle.assertNoResultDelivered() + navigationHandle.assertNotClosed() } - - @Test fun givenViewModel_whenContainerOperationIsPerformedOnParentContainer_thenParentContainerIsUpdated() { val navigationHandle = putNavigationHandleForViewModel(TestTestNavigationKey()) @@ -161,8 +159,8 @@ class EnroTestJvmTest { val expectedKey = TestTestKeyWithData(expectedId) viewModel.parentContainerOperation(expectedId) - navigationHandle.expectParentContainer().assertContains(expectedKey) - navigationHandle.expectParentContainer().assertActive(expectedKey) + navigationHandle.assertParentContainerExists().assertContains(expectedKey) + navigationHandle.assertParentContainerExists().assertActive(expectedKey) assertEquals(expectedKey, parentContainer.backstack.last().navigationKey) navigationHandle.onParentContainer { @@ -180,8 +178,8 @@ class EnroTestJvmTest { val expectedKey = TestTestKeyWithData(expectedId) viewModel.activeContainerOperation(expectedId) - navigationHandle.expectActiveContainer().assertContains(expectedKey) - navigationHandle.expectActiveContainer().assertActive(expectedKey) + navigationHandle.assertActiveContainerExists().assertContains(expectedKey) + navigationHandle.assertActiveContainerExists().assertActive(expectedKey) assertEquals(expectedKey, activeContainer.backstack.last().navigationKey) navigationHandle.onActiveContainer { @@ -199,8 +197,8 @@ class EnroTestJvmTest { val expectedKey = TestTestKeyWithData(expectedId) viewModel.specificContainerOperation(expectedId) - navigationHandle.expectContainer(testContainerKey).assertContains(expectedKey) - navigationHandle.expectContainer(testContainerKey).assertActive(expectedKey) + navigationHandle.assertContainerExists(testContainerKey).assertContains(expectedKey) + navigationHandle.assertContainerExists(testContainerKey).assertActive(expectedKey) assertEquals(expectedKey, childContainer.backstack.last().navigationKey) navigationHandle.onContainer(testContainerKey) { @@ -211,7 +209,8 @@ class EnroTestJvmTest { @Test fun givenFlowViewModel_whenFlowIsExecuted_thenFlowCompletesAsExpected() { val navigationHandle = putNavigationHandleForViewModel(FlowTestKey) - val viewModel = factory.create(FlowViewModel::class.java) + factory.create(FlowViewModel::class.java) + val expected = FlowData( first = UUID.randomUUID().toString(), second = UUID.randomUUID().toString(), @@ -220,26 +219,26 @@ class EnroTestJvmTest { ) navigationHandle - .expectActiveContainer() - .expectOpenInstruction { it.id == "first" } - .sendResultForTest(expected.first) + .assertActiveContainerExists() + .assertContains { it.id == "first" } + .deliverResultForTest(expected.first) navigationHandle - .expectActiveContainer() - .expectOpenInstruction { it.id == "second" } - .sendResultForTest(expected.second) + .assertActiveContainerExists() + .assertContains { it.id == "second" } + .deliverResultForTest(expected.second) navigationHandle - .expectActiveContainer() - .expectOpenInstruction { it.id == "bottomSheet" } - .sendResultForTest(expected.bottomSheet) + .assertActiveContainerExists() + .assertContains { it.id == "bottomSheet" } + .deliverResultForTest(expected.bottomSheet) navigationHandle - .expectActiveContainer() - .expectOpenInstruction { it.id == "third" } - .sendResultForTest(expected.third) + .assertActiveContainerExists() + .assertContains { it.id == "third" } + .deliverResultForTest(expected.third) - val result = navigationHandle.assertResultDelivered() + val result = navigationHandle.assertClosedWithResult() assertEquals(expected, result) } } diff --git a/enro/src/test/java/dev/enro/test/EnroTestTest.kt b/enro/src/test/java/dev/enro/test/EnroTestTest.kt index 5a507b0e..3d31478d 100644 --- a/enro/src/test/java/dev/enro/test/EnroTestTest.kt +++ b/enro/src/test/java/dev/enro/test/EnroTestTest.kt @@ -7,12 +7,14 @@ import dev.enro.core.onContainer import dev.enro.core.onParentContainer import dev.enro.core.requestClose import dev.enro.test.extensions.putNavigationHandleForViewModel -import dev.enro.test.extensions.sendResultForTest -import org.junit.Assert.* +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertTrue +import org.junit.Assert.fail import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import java.util.* +import java.util.UUID @RunWith(AndroidJUnit4::class) class EnroTestTest { @@ -33,7 +35,7 @@ class EnroTestTest { @Test fun whenPutNavigationHandleForTesting_andViewModelIsCreated_theViewModelIsCreatedSuccessfully() { - val navigationHandle = putNavigationHandleForViewModel(TestTestNavigationKey()) + putNavigationHandleForViewModel(TestTestNavigationKey()) val viewModel = factory.create(TestTestViewModel::class.java) assertNotNull(viewModel) } @@ -56,9 +58,9 @@ class EnroTestTest { assertNotNull(viewModel) viewModel.openStringOne() - val instruction = navigationHandle.expectOpenInstruction() + val instruction = navigationHandle.assertAnyInstructionOpened() navigationHandle.assertOpened() - instruction.sendResultForTest("wow") + instruction.deliverResultForTest("wow") assertEquals("wow", viewModel.stringOneResult) } @@ -87,17 +89,17 @@ class EnroTestTest { viewModel.openStringOne() - navigationHandle.expectOpenInstruction() - .sendResultForTest("first") + navigationHandle.assertAnyInstructionOpened() + .deliverResultForTest("first") - navigationHandle.expectOpenInstruction() - .sendResultForTest("second") + navigationHandle.assertAnyInstructionOpened() + .deliverResultForTest("second") - navigationHandle.expectOpenInstruction() - .sendResultForTest(1) + navigationHandle.assertAnyInstructionOpened() + .deliverResultForTest(1) - navigationHandle.expectOpenInstruction() - .sendResultForTest(2) + navigationHandle.assertAnyInstructionOpened() + .deliverResultForTest(2) assertEquals("first", viewModel.stringOneResult) assertEquals("second", viewModel.stringTwoResult) @@ -110,7 +112,6 @@ class EnroTestTest { navigationHandle.assertAnyOpened() navigationHandle.assertAnyOpened() - navigationHandle.expectCloseInstruction() navigationHandle.assertClosed() runCatching { navigationHandle.assertNotClosed() @@ -127,11 +128,11 @@ class EnroTestTest { viewModel.sendResult(expectedResult) runCatching { - navigationHandle.assertNoResultDelivered() + navigationHandle.assertNotClosed() }.onSuccess { fail() } - navigationHandle.assertResultDelivered(expectedResult) - navigationHandle.assertResultDelivered { it == expectedResult } - val result = navigationHandle.assertResultDelivered() + navigationHandle.assertClosedWithResult(expectedResult) + navigationHandle.assertClosedWithResult { it == expectedResult } + val result = navigationHandle.assertClosedWithResult() assertEquals(expectedResult, result) } @@ -143,12 +144,12 @@ class EnroTestTest { val expectedResult = UUID.randomUUID().toString() runCatching { - navigationHandle.assertResultDelivered(expectedResult) + navigationHandle.assertClosedWithResult(expectedResult) }.onSuccess { fail() } runCatching { - navigationHandle.assertResultDelivered() + navigationHandle.assertClosedWithResult() }.onSuccess { fail() } - navigationHandle.assertNoResultDelivered() + navigationHandle.assertNotClosed() } @Test @@ -161,8 +162,8 @@ class EnroTestTest { val expectedKey = TestTestKeyWithData(expectedId) viewModel.parentContainerOperation(expectedId) - navigationHandle.expectParentContainer().assertContains(expectedKey) - navigationHandle.expectParentContainer().assertActive(expectedKey) + navigationHandle.assertParentContainerExists().assertContains(expectedKey) + navigationHandle.assertParentContainerExists().assertActive(expectedKey) assertEquals(expectedKey, parentContainer.backstack.last().navigationKey) navigationHandle.onParentContainer { @@ -180,8 +181,8 @@ class EnroTestTest { val expectedKey = TestTestKeyWithData(expectedId) viewModel.activeContainerOperation(expectedId) - navigationHandle.expectActiveContainer().assertContains(expectedKey) - navigationHandle.expectActiveContainer().assertActive(expectedKey) + navigationHandle.assertActiveContainerExists().assertContains(expectedKey) + navigationHandle.assertActiveContainerExists().assertActive(expectedKey) assertEquals(expectedKey, activeContainer.backstack.last().navigationKey) navigationHandle.onActiveContainer { @@ -199,8 +200,8 @@ class EnroTestTest { val expectedKey = TestTestKeyWithData(expectedId) viewModel.specificContainerOperation(expectedId) - navigationHandle.expectContainer(testContainerKey).assertContains(expectedKey) - navigationHandle.expectContainer(testContainerKey).assertActive(expectedKey) + navigationHandle.assertContainerExists(testContainerKey).assertContains(expectedKey) + navigationHandle.assertContainerExists(testContainerKey).assertActive(expectedKey) assertEquals(expectedKey, childContainer.backstack.last().navigationKey) navigationHandle.onContainer(testContainerKey) { @@ -211,7 +212,8 @@ class EnroTestTest { @Test fun givenFlowViewModel_whenFlowIsExecuted_thenFlowCompletesAsExpected() { val navigationHandle = putNavigationHandleForViewModel(FlowTestKey) - val viewModel = factory.create(FlowViewModel::class.java) + factory.create(FlowViewModel::class.java) + val expected = FlowData( first = UUID.randomUUID().toString(), second = UUID.randomUUID().toString(), @@ -220,26 +222,26 @@ class EnroTestTest { ) navigationHandle - .expectActiveContainer() - .expectOpenInstruction { it.id == "first" } - .sendResultForTest(expected.first) + .assertActiveContainerExists() + .assertContains { it.id == "first" } + .deliverResultForTest(expected.first) navigationHandle - .expectActiveContainer() - .expectOpenInstruction { it.id == "second" } - .sendResultForTest(expected.second) + .assertActiveContainerExists() + .assertContains { it.id == "second" } + .deliverResultForTest(expected.second) navigationHandle - .expectActiveContainer() - .expectOpenInstruction { it.id == "bottomSheet" } - .sendResultForTest(expected.bottomSheet) + .assertActiveContainerExists() + .assertContains { it.id == "bottomSheet" } + .deliverResultForTest(expected.bottomSheet) navigationHandle - .expectActiveContainer() - .expectOpenInstruction { it.id == "third" } - .sendResultForTest(expected.third) + .assertActiveContainerExists() + .assertContains { it.id == "third" } + .deliverResultForTest(expected.third) - val result = navigationHandle.assertResultDelivered() + val result = navigationHandle.assertClosedWithResult() assertEquals(expected, result) } } \ No newline at end of file diff --git a/libs.versions.toml b/libs.versions.toml index e305de84..1e78bd3b 100644 --- a/libs.versions.toml +++ b/libs.versions.toml @@ -49,7 +49,7 @@ hilt-compiler = "androidx.hilt:hilt-compiler:1.2.0" leakcanary = "com.squareup.leakcanary:leakcanary-android:2.14" testing-junit = "junit:junit:4.13.2" -testing-robolectric = "org.robolectric:robolectric:4.8.1" +testing-robolectric = "org.robolectric:robolectric:4.12.2" testing-androidx-junit = "androidx.test.ext:junit:1.2.1" testing-androidx-core = "androidx.test:core:1.6.1"