diff --git a/enro-test/src/main/java/dev/enro/test/EnroTestAssertions.kt b/enro-test/src/main/java/dev/enro/test/EnroTestAssertions.kt new file mode 100644 index 00000000..a605ac97 --- /dev/null +++ b/enro-test/src/main/java/dev/enro/test/EnroTestAssertions.kt @@ -0,0 +1,35 @@ +package dev.enro.test + +class EnroTestAssertionException(message: String) : AssertionError(message) + +@PublishedApi +internal fun enroAssertionError(message: String): Nothing { + throw EnroTestAssertionException(message) +} + +data class EnroAssertionContext( + val expected: Any?, + val actual: Any?, +) + +@PublishedApi +internal fun Any?.shouldBeEqualTo(expected: Any?, message: EnroAssertionContext.() -> String) { + if (this != expected) { + val assertionContext = EnroAssertionContext( + expected = expected, + actual = this + ) + throw EnroTestAssertionException(message(assertionContext)) + } +} + +@PublishedApi +internal fun Any?.shouldNotBeEqualTo(expected: Any?, message: EnroAssertionContext.() -> String) { + if (this == expected) { + val assertionContext = EnroAssertionContext( + expected = expected, + actual = this + ) + throw EnroTestAssertionException(message(assertionContext)) + } +} \ No newline at end of file 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 new file mode 100644 index 00000000..c540121a --- /dev/null +++ b/enro-test/src/main/java/dev/enro/test/TestNavigationHandle.assertClosedWithResult.kt @@ -0,0 +1,92 @@ +package dev.enro.test + +import dev.enro.core.NavigationInstruction +import dev.enro.core.NavigationKey +import kotlin.reflect.KClass + +/** + * Asserts that the NavigationHandle has executed a Close.WithResult instruction, and that the result matches the provided predicate + */ +fun TestNavigationHandle>.assertClosedWithResult( + type: KClass, + predicate: (T) -> Boolean = { true }, +) { + val instruction = instructions.filterIsInstance() + .lastOrNull() + + instruction.shouldNotBeEqualTo(null) { + "NavigationHandle was expected to have executed a Close.WithResult instruction, but no Close.WithResult instruction was found" + } + requireNotNull(instruction) + + val result = instruction.result + val isAssignable = type.isInstance(result) + isAssignable.shouldBeEqualTo(true) { + "NavigationHandle's Close.WithResult was expected to be assignable to ${type}, but was of type ${instruction.result::class}" + } + @Suppress("UNCHECKED_CAST") + result as T + + predicate(result).shouldBeEqualTo(true) { + "NavigationHandle's Close.WithResult did not match the provided predicate\n\tResult: $result" + } +} + +/** + * Asserts that the NavigationHandle has executed a Close.WithResult instruction, and that the result matches the provided predicate + */ +inline fun TestNavigationHandle>.assertClosedWithResult( + predicate: (T) -> Boolean = { true }, +) { + val instruction = instructions.filterIsInstance() + .lastOrNull() + + instruction.shouldNotBeEqualTo(null) { + "NavigationHandle was expected to have executed a Close.WithResult instruction, but no Close.WithResult instruction was found" + } + requireNotNull(instruction) + + val result = instruction.result + val isAssignable = T::class.isInstance(result) + isAssignable.shouldBeEqualTo(true) { + "NavigationHandle's Close.WithResult was expected to be assignable to ${T::class}, but was of type ${instruction.result::class}" + } + @Suppress("UNCHECKED_CAST") + result as T + + predicate(result).shouldBeEqualTo(true) { + "NavigationHandle's Close.WithResult did not match the provided predicate\n\tResult: $result" + } +} + +/** + * Asserts that the NavigationHandle has executed a Close.WithResult instruction, and that the result is equal to [expected] + */ +fun TestNavigationHandle>.assertClosedWithResult( + expected: T, +) { + val instruction = instructions.filterIsInstance() + .lastOrNull() + + instruction.shouldNotBeEqualTo(null) { + "NavigationHandle was expected to have executed a Close.WithResult instruction, but no Close.WithResult instruction was found" + } + requireNotNull(instruction) + + val result = instruction.result + result.shouldBeEqualTo(expected) { + "NavigationHandle's Close.WithResult was expected to be $expected, but was $result" + } +} + +/** + * Asserts that the NavigationHandle has not executed a Close.WithResult instruction + */ +fun TestNavigationHandle>.assertNotClosedWithResult() { + val instruction = instructions.filterIsInstance() + .lastOrNull() + + instruction.shouldBeEqualTo(null) { + "NavigationHandle should not have executed a Close.WithResult instruction, but a Close.WithResult instruction was found" + } +} \ No newline at end of file