diff --git a/app/src/androidTest/java/com/plainstudio/stackcasino/navigation/StackNavHostTest.kt b/app/src/androidTest/java/com/plainstudio/stackcasino/navigation/StackNavHostTest.kt index 9905da2..a44a0cd 100644 --- a/app/src/androidTest/java/com/plainstudio/stackcasino/navigation/StackNavHostTest.kt +++ b/app/src/androidTest/java/com/plainstudio/stackcasino/navigation/StackNavHostTest.kt @@ -1,25 +1,41 @@ package com.plainstudio.stackcasino.navigation -import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.navigation.compose.ComposeNavigator import androidx.navigation.testing.TestNavHostController -import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.plainstudio.stackcasino.HiltTestActivity +import dagger.hilt.android.testing.HiltAndroidRule +import dagger.hilt.android.testing.HiltAndroidTest import org.junit.Assert.assertEquals import org.junit.Rule import org.junit.Test +import org.junit.rules.RuleChain import org.junit.runner.RunWith /** - * Smoke-validates that every [Route] declared in the sealed hierarchy is - * actually registered in the nav graph and reachable from the start - * destination. If a route is added to [Route] without a matching + * Smoke-validates that every [Route] declared in the sealed hierarchy + * is actually registered in the nav graph and reachable from the + * start destination. If a route is added to [Route] without a matching * `composable(...)` block in [StackNavHost], the call to [navigate] * here throws and the test fails. + * + * Runs against [HiltTestActivity] (not the default `ComponentActivity` + * the bare `createComposeRule()` would spin up) because several + * destinations call `hiltViewModel()` which only resolves on an + * `@AndroidEntryPoint` host. */ +@HiltAndroidTest @RunWith(AndroidJUnit4::class) class StackNavHostTest { + private val hiltRule = HiltAndroidRule(this) + private val composeRule = createAndroidComposeRule() + + // Hilt has to inject the test before the Compose rule mounts the + // activity, otherwise hiltViewModel() inside the first composed + // screen has no graph to pull from. @get:Rule - val composeRule = createComposeRule() + val ruleChain: RuleChain = RuleChain.outerRule(hiltRule).around(composeRule) private lateinit var navController: TestNavHostController @@ -27,8 +43,8 @@ class StackNavHostTest { fun every_static_route_is_reachable() { composeRule.setContent { navController = - TestNavHostController(ApplicationProvider.getApplicationContext()).apply { - navigatorProvider.addNavigator(androidx.navigation.compose.ComposeNavigator()) + TestNavHostController(composeRule.activity).apply { + navigatorProvider.addNavigator(ComposeNavigator()) } StackNavHost(navController = navController, startDestination = Route.Login.path) } @@ -52,7 +68,7 @@ class StackNavHostTest { ) staticTargets.forEach { route -> - composeRule.runOnUiThread { navController.navigate(route.path) } + composeRule.runOnUiThread { navController.navigate(route.defaultPath) } composeRule.waitForIdle() assertEquals( "Navigation to ${route.path} did not land on the expected destination.", @@ -66,8 +82,8 @@ class StackNavHostTest { fun parametric_routes_resolve_with_arguments() { composeRule.setContent { navController = - TestNavHostController(ApplicationProvider.getApplicationContext()).apply { - navigatorProvider.addNavigator(androidx.navigation.compose.ComposeNavigator()) + TestNavHostController(composeRule.activity).apply { + navigatorProvider.addNavigator(ComposeNavigator()) } StackNavHost(navController = navController, startDestination = Route.Login.path) } diff --git a/app/src/debug/AndroidManifest.xml b/app/src/debug/AndroidManifest.xml new file mode 100644 index 0000000..60e50b3 --- /dev/null +++ b/app/src/debug/AndroidManifest.xml @@ -0,0 +1,17 @@ + + + + + + + + diff --git a/app/src/debug/java/com/plainstudio/stackcasino/HiltTestActivity.kt b/app/src/debug/java/com/plainstudio/stackcasino/HiltTestActivity.kt new file mode 100644 index 0000000..13eb651 --- /dev/null +++ b/app/src/debug/java/com/plainstudio/stackcasino/HiltTestActivity.kt @@ -0,0 +1,23 @@ +package com.plainstudio.stackcasino + +import androidx.activity.ComponentActivity +import dagger.hilt.android.AndroidEntryPoint + +/** + * Bare-bones `@AndroidEntryPoint` shell used as the host activity for + * Compose tests that mount screens calling `hiltViewModel()`. The + * default `createComposeRule()` spins up a plain `ComponentActivity` + * which Hilt cannot wire (it throws "Given component holder class + * androidx.activity.ComponentActivity does not implement interface + * dagger.hilt.internal.GeneratedComponent..."), so any Compose test + * that needs the Hilt graph has to launch through this activity via + * `createAndroidComposeRule()`. + * + * Lives in `src/debug/` (not `src/androidTest/`) because Android's + * instrumentation runner resolves activities through the target APK's + * PackageManager: a class declared only in the test APK would fail + * with "Unable to resolve activity". Production release builds drop + * the activity entirely since the `debug/` sourceset is variant-scoped. + */ +@AndroidEntryPoint +class HiltTestActivity : ComponentActivity() diff --git a/app/src/main/res/drawable/ic_brand_splash.xml b/app/src/main/res/drawable/ic_brand_splash.xml new file mode 100644 index 0000000..252bb87 --- /dev/null +++ b/app/src/main/res/drawable/ic_brand_splash.xml @@ -0,0 +1,28 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml index 07d5da9..44277e9 100644 --- a/app/src/main/res/drawable/ic_launcher_background.xml +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -1,170 +1,16 @@ + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/drawable/ic_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml index 2b068d1..ac3bfdc 100644 --- a/app/src/main/res/drawable/ic_launcher_foreground.xml +++ b/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -1,30 +1,27 @@ + - - - - - - - - - \ No newline at end of file + android:fillColor="#00000000" + android:strokeColor="#8B5CF6" + android:strokeWidth="3" + android:pathData="M32,32 h44 v44 h-44 z" /> + + + diff --git a/app/src/main/res/drawable/ic_launcher_monochrome.xml b/app/src/main/res/drawable/ic_launcher_monochrome.xml new file mode 100644 index 0000000..efc18b0 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_monochrome.xml @@ -0,0 +1,26 @@ + + + + + + diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml index 6f3b755..b070c76 100644 --- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -2,5 +2,5 @@ - + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml index 6f3b755..b070c76 100644 --- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -2,5 +2,5 @@ - + \ No newline at end of file diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index 39518bb..a134db5 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -17,7 +17,7 @@ postSplashScreenTheme once the activity is ready. -->