-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Modify configuration files to support Kotlin & Jetpack Compose As discussed this morning, we plan on moving to Kotlin and Jetpack compose. This commit marks the beginning of the modification to make it. * Update minSdk to comply with firebase requirement Changes: * Change from minSdk:24 to minSdk:28 to comply with the firebase realtime database requirements * Delete old java activity file and write basic test Changes: * Remove `GreetingActivity.java` and `MainActivity.java` and dependencies * Write simple `ComposeActvitityTest.kt` * Authentication This commit adds firebase, firebase auth, auth ui and also new interfaces for users and authenticator along with some tests. FirebaseAuthenticator#authenticate should be tested in a valid Android environment with a login activity. * Better test * Compose migration (#26) * Modify configuration files to support Kotlin & Jetpack Compose As discussed this morning, we plan on moving to Kotlin and Jetpack compose. This commit marks the beginning of the modification to make it. * Update minSdk to comply with firebase requirement Changes: * Change from minSdk:24 to minSdk:28 to comply with the firebase realtime database requirements * Delete old java activity file and write basic test Changes: * Remove `GreetingActivity.java` and `MainActivity.java` and dependencies * Write simple `ComposeActvitityTest.kt` * fix: Support for coverage with Kotlin * Added Mockito * Fixed the build.gradle with proper versions * 🔥 💯 :triump: base 😡 --------- Co-authored-by: BoyeGuillaume <guillaume.boye@epfl.ch> * Basic interface * Theme and basic navigation * Add login flow * Try of testing navigation * Add navigation quick test * Move navigation into appropriate package and refactor * Begin of LoginActivityTest * Add test for LoginActivity * LoginActivity.kt refactor * fix: authenticate of FirebaseAuthenticator does not abort authenticate when logged-in * Navigation test * Delete a line in LoginScreen (cc) and adds seed for theme * Delete a line in LoginScreen (cc) again * Add missing UI test dependency in build.gradle * Should be ok to merge with master * Should be ok to merge with master --------- Co-authored-by: BoyeGuillaume <guillaume.boye@epfl.ch>
- Loading branch information
1 parent
f8d80dd
commit 8d1b6a0
Showing
21 changed files
with
863 additions
and
90 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
100 changes: 100 additions & 0 deletions
100
app/src/androidTest/java/com/github/geohunt/app/ui/LoginActivityTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
package com.github.geohunt.app.ui | ||
|
||
import android.graphics.Bitmap | ||
import androidx.activity.ComponentActivity | ||
import androidx.compose.ui.test.hasTestTag | ||
import androidx.compose.ui.test.junit4.createAndroidComposeRule | ||
import androidx.compose.ui.test.onNodeWithText | ||
import androidx.compose.ui.test.performClick | ||
import androidx.test.core.app.launchActivity | ||
import androidx.test.espresso.intent.Intents | ||
import androidx.test.espresso.intent.Intents.assertNoUnverifiedIntents | ||
import androidx.test.espresso.intent.Intents.intended | ||
import androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent | ||
import androidx.test.espresso.intent.matcher.IntentMatchers.hasExtra | ||
import androidx.test.ext.junit.runners.AndroidJUnit4 | ||
import com.github.geohunt.app.LoginActivity | ||
import com.github.geohunt.app.MainActivity | ||
import com.github.geohunt.app.authentication.Authenticator | ||
import com.github.geohunt.app.mocks.MockLazyRef | ||
import com.github.geohunt.app.model.LazyRef | ||
import com.github.geohunt.app.model.database.api.Challenge | ||
import com.github.geohunt.app.model.database.api.User | ||
import org.hamcrest.Matchers.* | ||
import org.junit.Rule | ||
import org.junit.Test | ||
import org.junit.runner.RunWith | ||
import java.util.concurrent.CompletableFuture | ||
|
||
@RunWith(AndroidJUnit4::class) | ||
class LoginActivityTest { | ||
@get:Rule val composeTestRule = createAndroidComposeRule<MainActivity>() | ||
|
||
@Test | ||
fun opensHomeActivityWhenLoggedIn() { | ||
Authenticator.authInstance.set(MockAuthenticator(MockUser("hello"))) | ||
|
||
Intents.init() | ||
launchActivity<LoginActivity>() | ||
intended(allOf(hasComponent(MainActivity::class.java.name))) | ||
Intents.release() | ||
} | ||
|
||
@Test | ||
fun doesNothingIfNotSignedIn() { | ||
Authenticator.authInstance.set(MockAuthenticator(null)) | ||
Intents.init() | ||
|
||
launchActivity<LoginActivity>() | ||
intended(allOf(hasComponent(LoginActivity::class.java.name))) | ||
assertNoUnverifiedIntents() | ||
Intents.release() | ||
} | ||
|
||
@Test | ||
fun titleOfAppIsShown() { | ||
Authenticator.authInstance.set(MockAuthenticator(null)) | ||
launchActivity<LoginActivity>() | ||
composeTestRule.onNodeWithText("GeoHunt").assertExists("Title of app does not appear on log in") | ||
} | ||
|
||
@Test | ||
fun clickingOnButtonTriggersSignIn() { | ||
val cf = CompletableFuture<Void>() | ||
Authenticator.authInstance.set(MockAuthenticator(null) { | ||
cf.complete(null) | ||
return@MockAuthenticator CompletableFuture.completedFuture(null) | ||
}) | ||
Intents.init() | ||
|
||
launchActivity<LoginActivity>() | ||
composeTestRule.onNode(hasTestTag("signin-btn")).performClick() | ||
intended(allOf( | ||
hasComponent(LoginActivity::class.java.name), | ||
hasExtra("login", any(Any::class.java)))) | ||
Intents.release() | ||
assert(cf.isDone) | ||
} | ||
|
||
class MockUser( | ||
override var displayName: String? = null, | ||
override val uid: String = "1", | ||
override val profilePicture: LazyRef<Bitmap> = MockLazyRef("1") { TODO() }, | ||
override val challenges: List<LazyRef<Challenge>> = emptyList(), | ||
override val hunts: List<LazyRef<Challenge>> = emptyList(), | ||
override var score: Number = 1 | ||
) : User | ||
|
||
class MockAuthenticator(override val user: User?, | ||
val authenticateCb: (a: ComponentActivity) -> CompletableFuture<User> = { | ||
CompletableFuture.completedFuture(null) | ||
}) : Authenticator { | ||
override fun authenticate(activity: ComponentActivity): CompletableFuture<User> { | ||
return authenticateCb(activity) | ||
} | ||
|
||
override fun signOut(activity: ComponentActivity): CompletableFuture<Void> { | ||
TODO("Not yet implemented") | ||
} | ||
} | ||
} |
56 changes: 56 additions & 0 deletions
56
app/src/androidTest/java/com/github/geohunt/app/ui/components/navigation/TestNavigation.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
package com.github.geohunt.app.components.navigation | ||
|
||
import androidx.compose.ui.platform.LocalContext | ||
import androidx.compose.ui.test.assertIsSelected | ||
import androidx.compose.ui.test.hasTestTag | ||
import androidx.compose.ui.test.junit4.createComposeRule | ||
import androidx.compose.ui.test.performClick | ||
import androidx.navigation.compose.ComposeNavigator | ||
import androidx.navigation.testing.TestNavHostController | ||
import com.github.geohunt.app.ui.components.navigation.NavigationBar | ||
import com.github.geohunt.app.ui.components.navigation.NavigationController | ||
import com.github.geohunt.app.ui.components.navigation.Route | ||
import org.junit.Before | ||
import org.junit.Rule | ||
import org.junit.Test | ||
|
||
class TestNavigation { | ||
|
||
@get:Rule | ||
val composeTestRule = createComposeRule() | ||
|
||
private lateinit var navController: TestNavHostController | ||
|
||
@Before | ||
fun setUp() { | ||
composeTestRule.setContent { | ||
navController = TestNavHostController(LocalContext.current) | ||
navController.navigatorProvider.addNavigator(ComposeNavigator()) | ||
NavigationController(navController = navController) | ||
NavigationBar(navController = navController) | ||
} | ||
} | ||
|
||
@Test | ||
fun startRouteIsHome() { | ||
assert(Route.Home.route == navController.currentBackStackEntry?.destination?.route) | ||
} | ||
|
||
@Test | ||
fun clickingOnButtonSelectsIt() { | ||
for (route in Route.values()) { | ||
val node = composeTestRule.onNode(hasTestTag("navbtn-" + route.route)) | ||
node.performClick() | ||
node.assertIsSelected() | ||
} | ||
} | ||
|
||
@Test | ||
fun clickingOnButtonRedirects() { | ||
for (route in Route.values()) { | ||
val node = composeTestRule.onNode(hasTestTag("navbtn-" + route.route)) | ||
node.performClick() | ||
assert(navController.currentBackStackEntry?.destination?.route == route.route) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
package com.github.geohunt.app | ||
|
||
import android.content.Context | ||
import android.content.Intent | ||
import android.os.Bundle | ||
import androidx.activity.ComponentActivity | ||
import androidx.activity.compose.setContent | ||
import androidx.compose.foundation.layout.* | ||
import androidx.compose.material.Button | ||
import androidx.compose.material.MaterialTheme | ||
import androidx.compose.material.Surface | ||
import androidx.compose.material.Text | ||
import androidx.compose.runtime.Composable | ||
import androidx.compose.ui.Alignment | ||
import androidx.compose.ui.Modifier | ||
import androidx.compose.ui.graphics.Brush | ||
import androidx.compose.ui.platform.testTag | ||
import androidx.compose.ui.res.stringResource | ||
import androidx.compose.ui.text.ExperimentalTextApi | ||
import androidx.compose.ui.text.TextStyle | ||
import androidx.compose.ui.text.style.TextAlign | ||
import androidx.compose.ui.unit.dp | ||
import com.github.geohunt.app.authentication.Authenticator | ||
import com.github.geohunt.app.ui.theme.GeoHuntTheme | ||
import com.github.geohunt.app.ui.theme.md_theme_light_primary | ||
import com.github.geohunt.app.ui.theme.seed | ||
|
||
class LoginActivity : ComponentActivity() { | ||
override fun onCreate(savedInstanceState: Bundle?) { | ||
super.onCreate(savedInstanceState) | ||
|
||
val authenticator: Authenticator = Authenticator.authInstance.get() | ||
|
||
authenticator.user?.let { loggedIn() } | ||
|
||
if (intent.hasExtra("login")) { | ||
authenticator.authenticate(this@LoginActivity).thenAccept { | ||
it?.let { loggedIn() } | ||
} | ||
} | ||
|
||
setContent { | ||
GeoHuntTheme { | ||
// A surface container using the 'background' color from the theme | ||
Surface( | ||
modifier = Modifier.fillMaxSize(), | ||
color = MaterialTheme.colors.background | ||
) { | ||
LoginScreen(context = this@LoginActivity) | ||
} | ||
} | ||
} | ||
} | ||
|
||
private fun loggedIn() { | ||
startActivity( | ||
Intent(this@LoginActivity, MainActivity::class.java) | ||
) | ||
} | ||
} | ||
|
||
@OptIn(ExperimentalTextApi::class) | ||
@Composable | ||
fun LoginScreen(context: Context) { | ||
Column( | ||
modifier = Modifier.padding(20.dp), | ||
verticalArrangement = Arrangement.Center, | ||
horizontalAlignment = Alignment.CenterHorizontally | ||
) { | ||
Text( | ||
text = stringResource(id = R.string.app_name), | ||
textAlign = TextAlign.Center, | ||
style = TextStyle( | ||
brush = Brush.linearGradient(listOf(md_theme_light_primary, seed)) | ||
) | ||
) | ||
|
||
Spacer(modifier = Modifier.height(20.dp)) | ||
|
||
Button(modifier = Modifier.testTag("signin-btn"), onClick = { | ||
val intent = Intent(context, LoginActivity::class.java) | ||
intent.putExtra("login", 1) | ||
context.startActivity(intent) | ||
}) { | ||
Text(stringResource(id = R.string.sign_in)) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
package com.github.geohunt.app | ||
|
||
import android.os.Bundle | ||
import androidx.activity.ComponentActivity | ||
import androidx.activity.compose.setContent | ||
import androidx.compose.foundation.layout.fillMaxSize | ||
import androidx.compose.foundation.layout.padding | ||
import androidx.compose.material.MaterialTheme | ||
import androidx.compose.material.Scaffold | ||
import androidx.compose.material.Surface | ||
import androidx.compose.runtime.Composable | ||
import androidx.compose.ui.Modifier | ||
import androidx.compose.ui.draw.shadow | ||
import androidx.compose.ui.unit.Dp | ||
import androidx.navigation.compose.rememberNavController | ||
import com.github.geohunt.app.ui.components.navigation.NavigationBar | ||
import com.github.geohunt.app.ui.components.navigation.NavigationController | ||
import com.github.geohunt.app.ui.theme.GeoHuntTheme | ||
|
||
class MainActivity : ComponentActivity() { | ||
override fun onCreate(savedInstanceState: Bundle?) { | ||
super.onCreate(savedInstanceState) | ||
setContent { | ||
GeoHuntTheme { | ||
// A surface container using the 'background' color from the theme | ||
Surface( | ||
modifier = Modifier.fillMaxSize(), | ||
color = MaterialTheme.colors.background | ||
) { | ||
MainComposable() | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
@Composable | ||
fun MainComposable() { | ||
val navController = rememberNavController() | ||
Scaffold( | ||
bottomBar = { | ||
Surface(modifier = Modifier.shadow(Dp(9f))) { | ||
NavigationBar(navController = navController) | ||
} | ||
} | ||
) { padding -> | ||
NavigationController(navController = navController, Modifier.padding(padding)) | ||
} | ||
} |
32 changes: 32 additions & 0 deletions
32
app/src/main/java/com/github/geohunt/app/authentication/Authenticator.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package com.github.geohunt.app.authentication | ||
|
||
import androidx.activity.ComponentActivity | ||
import com.github.geohunt.app.model.database.api.User | ||
import com.github.geohunt.app.utility.Singleton | ||
import java.util.concurrent.CompletableFuture | ||
|
||
interface Authenticator { | ||
|
||
val user: User? | ||
|
||
/** | ||
* Begins an authentication phase from the given activity. | ||
* Returns a future that will be completed when the user will be logged in. | ||
* If the authentication fails, the completable future will fail. | ||
* @param activity The activity that requests the connection | ||
* @return A completable future that follows the above description | ||
*/ | ||
fun authenticate(activity: ComponentActivity): CompletableFuture<User> | ||
|
||
/** | ||
* Signs out the user. | ||
* @param activity The activity that requests the signing out | ||
* @return The completable future is finished when the signing out is finished. | ||
*/ | ||
fun signOut(activity: ComponentActivity): CompletableFuture<Void> | ||
|
||
companion object { | ||
val authInstance: Singleton<Authenticator> = Singleton(FirebaseAuthenticator()) | ||
} | ||
|
||
} |
Oops, something went wrong.