feat: add splash screen and login auth flow
This commit is contained in:
@@ -1,16 +0,0 @@
|
||||
package space.hackenslacker.kanbn4droid.app
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class ExampleInstrumentedTest {
|
||||
@Test
|
||||
fun useAppContext() {
|
||||
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
||||
assertEquals("space.hackenslacker.kanbn4droid.app", appContext.packageName)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
package space.hackenslacker.kanbn4droid.app
|
||||
|
||||
import androidx.test.core.app.ActivityScenario
|
||||
import androidx.test.espresso.Espresso.onView
|
||||
import androidx.test.espresso.action.ViewActions.click
|
||||
import androidx.test.espresso.action.ViewActions.closeSoftKeyboard
|
||||
import androidx.test.espresso.action.ViewActions.replaceText
|
||||
import androidx.test.espresso.assertion.ViewAssertions.matches
|
||||
import androidx.test.espresso.intent.Intents
|
||||
import androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent
|
||||
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
|
||||
import androidx.test.espresso.matcher.ViewMatchers.withId
|
||||
import androidx.test.espresso.matcher.ViewMatchers.withText
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import org.junit.After
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import space.hackenslacker.kanbn4droid.app.auth.ApiKeyStore
|
||||
import space.hackenslacker.kanbn4droid.app.auth.AuthResult
|
||||
import space.hackenslacker.kanbn4droid.app.auth.KanbnApiClient
|
||||
import space.hackenslacker.kanbn4droid.app.auth.SessionStore
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class LoginFlowTest {
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MainActivity.dependencies.clear()
|
||||
Intents.init()
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
Intents.release()
|
||||
MainActivity.dependencies.clear()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun showsLoginWithDefaultUrlWhenNoStoredSession() {
|
||||
MainActivity.dependencies.sessionStoreFactory = { InMemorySessionStore() }
|
||||
MainActivity.dependencies.apiKeyStoreFactory = { InMemoryApiKeyStore(null) }
|
||||
MainActivity.dependencies.apiClientFactory = { FakeApiClient(AuthResult.Success) }
|
||||
|
||||
ActivityScenario.launch(MainActivity::class.java)
|
||||
|
||||
onView(withId(R.id.baseUrlInput)).check(matches(withText("https://kan.bn/")))
|
||||
onView(withId(R.id.apiKeyInput)).check(matches(isDisplayed()))
|
||||
onView(withId(R.id.signInButton)).check(matches(isDisplayed()))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun autoLoginSuccessSkipsLoginAndNavigatesToBoards() {
|
||||
val sessionStore = InMemorySessionStore("https://kan.bn/")
|
||||
MainActivity.dependencies.sessionStoreFactory = { sessionStore }
|
||||
MainActivity.dependencies.apiKeyStoreFactory = { InMemoryApiKeyStore("kan_key") }
|
||||
MainActivity.dependencies.apiClientFactory = { FakeApiClient(AuthResult.Success) }
|
||||
|
||||
ActivityScenario.launch(MainActivity::class.java)
|
||||
|
||||
Intents.intended(hasComponent(BoardsPlaceholderActivity::class.java.name))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun invalidStoredSessionFallsBackToLoginAndKeepsUrl() {
|
||||
val sessionStore = InMemorySessionStore("https://kan.bn/")
|
||||
MainActivity.dependencies.sessionStoreFactory = { sessionStore }
|
||||
MainActivity.dependencies.apiKeyStoreFactory = { InMemoryApiKeyStore("bad-key") }
|
||||
MainActivity.dependencies.apiClientFactory = {
|
||||
FakeApiClient(AuthResult.Failure("Authentication failed. Check your API key."))
|
||||
}
|
||||
|
||||
ActivityScenario.launch(MainActivity::class.java)
|
||||
|
||||
onView(withId(R.id.baseUrlInput)).check(matches(withText("https://kan.bn/")))
|
||||
onView(withId(R.id.signInButton)).check(matches(isDisplayed()))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun manualSignInSuccessNavigatesAndStoresSession() {
|
||||
val sessionStore = InMemorySessionStore()
|
||||
val keyStore = InMemoryApiKeyStore(null)
|
||||
MainActivity.dependencies.sessionStoreFactory = { sessionStore }
|
||||
MainActivity.dependencies.apiKeyStoreFactory = { keyStore }
|
||||
MainActivity.dependencies.apiClientFactory = { FakeApiClient(AuthResult.Success) }
|
||||
|
||||
ActivityScenario.launch(MainActivity::class.java)
|
||||
|
||||
onView(withId(R.id.baseUrlInput)).perform(replaceText("https://kan.bn"), closeSoftKeyboard())
|
||||
onView(withId(R.id.apiKeyInput)).perform(replaceText("kan_new"), closeSoftKeyboard())
|
||||
onView(withId(R.id.signInButton)).perform(click())
|
||||
|
||||
Intents.intended(hasComponent(BoardsPlaceholderActivity::class.java.name))
|
||||
assertEquals("https://kan.bn/", sessionStore.getBaseUrl())
|
||||
assertEquals("kan_new", keyStore.savedKey)
|
||||
}
|
||||
|
||||
private class InMemorySessionStore(
|
||||
private var baseUrl: String? = null,
|
||||
) : SessionStore {
|
||||
override fun getBaseUrl(): String? = baseUrl
|
||||
|
||||
override fun saveBaseUrl(url: String) {
|
||||
baseUrl = url
|
||||
}
|
||||
|
||||
override fun clearBaseUrl() {
|
||||
baseUrl = null
|
||||
}
|
||||
}
|
||||
|
||||
private class InMemoryApiKeyStore(
|
||||
private var key: String?,
|
||||
) : ApiKeyStore {
|
||||
var savedKey: String? = null
|
||||
|
||||
override suspend fun saveApiKey(baseUrl: String, apiKey: String): Result<Unit> {
|
||||
key = apiKey
|
||||
savedKey = apiKey
|
||||
return Result.success(Unit)
|
||||
}
|
||||
|
||||
override suspend fun getApiKey(baseUrl: String): Result<String?> = Result.success(key)
|
||||
|
||||
override suspend fun invalidateApiKey(baseUrl: String): Result<Unit> {
|
||||
key = null
|
||||
return Result.success(Unit)
|
||||
}
|
||||
}
|
||||
|
||||
private class FakeApiClient(
|
||||
private val result: AuthResult,
|
||||
) : KanbnApiClient {
|
||||
override suspend fun healthCheck(baseUrl: String, apiKey: String): AuthResult = result
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user