MBL-2723 continued: After login, open pledge manager webview (#2450)

* MBL-2723 start PM webview on login viewmodel

* Move toolbar title logic out of SplashScreenActivity and into ActivityExt

* Translations

* Remove FPO

* Wrong toolbar title displayed due to feature flag

* Let WebViewActivity observe currentUser before loading url

* Add comments

* Add viewmodel

* Add and fix tests

* Clean up

* Have the WebViewEvent interface live in the ViewModel file

* Refactor with isa and fix tests

---------

Co-authored-by: Isabel Martin <arkariang@gmail.com>
This commit is contained in:
Yun Cheng
2025-12-03 09:26:19 -05:00
committed by GitHub
parent dda4c2b278
commit eedbfcb77c
17 changed files with 214 additions and 76 deletions

View File

@@ -741,6 +741,7 @@
"Retry": "Retry",
"Retry_or_select_another_method": "Retry or select another method.",
"Reusability_and_recyclability": "Reusability and recyclability",
"Review_edits_made_to_your_order": "Review edits made to your order",
"reward_amount_plus_shipping_cost_each": "%{reward_amount} + %{shipping_cost} shipping each",
"Reward_delivered": "Reward delivered?",
"Reward_estimated_for_delivery_in_date": "<b>Reward</b> estimated for delivery in %{delivery_date}",
@@ -3435,6 +3436,7 @@
"Retry": "Erneut versuchen",
"Retry_or_select_another_method": "Versuche es erneut oder wähle eine andere Karte.",
"Reusability_and_recyclability": "Wiederverwertung und Recyclingfähigkeit",
"Review_edits_made_to_your_order": "Änderungen an deiner Bestellung prüfen",
"reward_amount_plus_shipping_cost_each": "Jeweils %{reward_amount} + %{shipping_cost} Versandkosten",
"Reward_delivered": "Belohnung versandt?",
"Reward_estimated_for_delivery_in_date": "Voraussichtliche Lieferung der <b>Belohnung</b> im %{delivery_date}",
@@ -6129,6 +6131,7 @@
"Retry": "Volver a intentar",
"Retry_or_select_another_method": "Vuelve a intentarlo o selecciona otro método.",
"Reusability_and_recyclability": "Reutilización y reciclabilidad",
"Review_edits_made_to_your_order": "Revisa las modificaciones realizadas en tu pedido",
"reward_amount_plus_shipping_cost_each": "%{reward_amount} + %{shipping_cost} de envío cada una",
"Reward_delivered": "¿Recompensa entregada?",
"Reward_estimated_for_delivery_in_date": "<b>Recompensa</b> estimada para entregarse en %{delivery_date}",
@@ -8823,6 +8826,7 @@
"Retry": "Réessayer",
"Retry_or_select_another_method": "Veuillez réessayer ou choisir un autre moyen de paiement.",
"Reusability_and_recyclability": "Recyclage et valorisation des déchets",
"Review_edits_made_to_your_order": "Vérifions les modifications apportées à votre commande",
"reward_amount_plus_shipping_cost_each": "%{reward_amount} + %{shipping_cost} de frais de port par unité",
"Reward_delivered": "Récompense livrée ?",
"Reward_estimated_for_delivery_in_date": "<b>Récompense</b> prévue pour %{delivery_date}",
@@ -11517,6 +11521,7 @@
"Retry": "Retry",
"Retry_or_select_another_method": "Retry or select another method.",
"Reusability_and_recyclability": "Reusability and recyclability",
"Review_edits_made_to_your_order": "Review edits made to your order",
"reward_amount_plus_shipping_cost_each": "%{reward_amount} + %{shipping_cost} shipping each",
"Reward_delivered": "Reward delivered?",
"Reward_estimated_for_delivery_in_date": "<b>Reward</b> estimated for delivery in %{delivery_date}",
@@ -14211,6 +14216,7 @@
"Retry": "もう一度試す",
"Retry_or_select_another_method": "再試行するか別のお支払い方法を選択してください。",
"Reusability_and_recyclability": "再利用可能性とリサイクル可能性",
"Review_edits_made_to_your_order": "ご注文への編集内容を確認してください",
"reward_amount_plus_shipping_cost_each": "%{reward_amount} + 送料各 %{shipping_cost} ",
"Reward_delivered": "リワードを受け取りましたか?",
"Reward_estimated_for_delivery_in_date": "<b>リワード</b>の配達予定日は%{delivery_date}",
@@ -16905,6 +16911,7 @@
"Retry": "Retry",
"Retry_or_select_another_method": "Retry or select another method.",
"Reusability_and_recyclability": "Reusability and recyclability",
"Review_edits_made_to_your_order": "Review edits made to your order",
"reward_amount_plus_shipping_cost_each": "%{reward_amount} + %{shipping_cost} shipping each",
"Reward_delivered": "Reward delivered?",
"Reward_estimated_for_delivery_in_date": "<b>Reward</b> estimated for delivery in %{delivery_date}",

View File

@@ -32,7 +32,6 @@ object IntentKey {
const val SURVEY_RESPONSE = "com.kickstarter.kickstarter.survey_response"
const val NOTIFICATION_SURVEY_RESPONSE = "com.kickstarter.kickstarter.survey_response"
const val DEEPLINK_SURVEY_RESPONSE = "com.kickstarter.kickstarter.deeplink_survey_response"
const val DEEPLINK_PM_ORDER_EDIT = "com.kickstarter.kickstarter.deeplink_pm_order_edit"
const val NOTIFICATION_PLEDGE_REDEMPTION = "com.kickstarter.kickstarter.notification_pledge_redeption"
const val TOOLBAR_TITLE = "com.kickstarter.kickstarter.intent_toolbar_title"
const val TRACKING_CLIENT_TYPE_TAG = "com.kickstarter.kickstarter.intent_tracking_client_tag"

View File

@@ -282,7 +282,7 @@ class LoginToutActivity : ComponentActivity() {
private fun finishWithSuccessfulResult() {
setResult(RESULT_OK)
goToSurveyIfSurveyPresent()
goToSurveyIfSurveyPresent() // TODO: Remove in MBL-2879
finish()
}

View File

@@ -470,7 +470,7 @@ class ProjectPageActivity :
this.viewModel.outputs.openPledgeManagerWebview()
.observeOn(AndroidSchedulers.mainThread())
.subscribe { startWebViewActivity(it, getString(R.string.Pledge_manager)) }
.subscribe { startWebViewActivity(it) }
.addToDisposable(disposables)
this.viewModel.outputs.projectMedia()

View File

@@ -18,7 +18,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.unit.dp
import androidx.core.net.toUri
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.core.view.WindowCompat
import com.kickstarter.R
@@ -34,8 +33,6 @@ import com.kickstarter.libs.utils.UrlUtils.saveFlag
import com.kickstarter.libs.utils.extensions.addToDisposable
import com.kickstarter.libs.utils.extensions.getEnvironment
import com.kickstarter.libs.utils.extensions.getProjectIntent
import com.kickstarter.libs.utils.extensions.isPMOrderEditUri
import com.kickstarter.libs.utils.extensions.isPMUri
import com.kickstarter.libs.utils.extensions.path
import com.kickstarter.models.SurveyResponse
import com.kickstarter.ui.IntentKey
@@ -173,15 +170,9 @@ class SplashScreenActivity : AppCompatActivity() {
viewModel.outputs.startPMWebview()
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
val uri = it.first
val isLoggedIn = it.second
.subscribe { uri ->
if (isLoggedIn) {
startPMActivity(uri.toString())
} else {
startLoginForPMOrderEdit(uri.toString())
}
startPMActivity(uri.toString())
}.addToDisposable(disposables)
statsigClient.updateExperimentUser()
@@ -284,26 +275,9 @@ class SplashScreenActivity : AppCompatActivity() {
finish()
}
private fun startLoginForPMOrderEdit(url: String) {
val intent = Intent(this, LoginToutActivity::class.java)
.putExtra(IntentKey.LOGIN_REASON, LoginReason.DEFAULT)
.putExtra(IntentKey.DEEPLINK_PM_ORDER_EDIT, url)
startActivityForResult(intent, ActivityRequestCodes.LOGIN_FLOW)
}
private fun startPMActivity(url: String) {
ApplicationUtils.startNewDiscoveryActivity(this)
val uri = url.toUri()
val webEndpoint = environment.webEndpoint()
val toolbarTitle = when {
uri.isPMUri(webEndpoint) -> getString(R.string.Pledge_manager)
uri.isPMOrderEditUri(webEndpoint) -> getString(R.string.fpo_review_edits)
else -> getString(R.string.Pledge_manager)
}
startWebViewActivity(url, toolbarTitle = toolbarTitle)
startWebViewActivity(url)
finish()
}

View File

@@ -1,19 +1,38 @@
package com.kickstarter.ui.activities
import android.content.Intent
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.addCallback
import androidx.activity.viewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.kickstarter.databinding.WebViewLayoutBinding
import com.kickstarter.libs.ActivityRequestCodes
import com.kickstarter.libs.utils.extensions.getEnvironment
import com.kickstarter.ui.IntentKey
import com.kickstarter.ui.data.LoginReason
import com.kickstarter.ui.extensions.finishWithAnimation
import com.kickstarter.ui.views.KSWebView
import com.kickstarter.utils.WindowInsetsUtil
import com.kickstarter.viewmodels.WebViewEvent
import com.kickstarter.viewmodels.WebViewViewModel
import kotlinx.coroutines.launch
class WebViewActivity : ComponentActivity() {
private lateinit var binding: WebViewLayoutBinding
private lateinit var viewModelFactory: WebViewViewModel.Factory
private val viewModel: WebViewViewModel by viewModels {
viewModelFactory
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
this.getEnvironment()?.let { env ->
viewModelFactory = WebViewViewModel.Factory(env)
}
binding = WebViewLayoutBinding.inflate(layoutInflater)
WindowInsetsUtil.manageEdgeToEdge(
window,
@@ -38,9 +57,6 @@ class WebViewActivity : ComponentActivity() {
}
})
val url = intent.getStringExtra(IntentKey.URL)
url?.let { binding.webView.loadUrl(it) }
onBackPressedDispatcher.addCallback {
if (binding.webView.canGoBack()) {
binding.webView.goBack()
@@ -48,5 +64,34 @@ class WebViewActivity : ComponentActivity() {
finishWithAnimation()
}
}
observeCurrentUserEvents()
}
// Check if the user is logged in.
// If no, start the LoginToutActivity. Wait for its result within the same backstack.
// Once LoginToutActivity is finished successfully WebViewActivity can proceed to load the url to the webview.
private fun observeCurrentUserEvents() {
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.webViewUIState.collect { event ->
when (event) {
WebViewEvent.SHOW_LOGIN -> startLoginToutActivity()
WebViewEvent.LOAD_WEBVIEW -> loadUrl()
}
}
}
}
}
private fun loadUrl() {
val url = intent.getStringExtra(IntentKey.URL)
url?.let { binding.webView.loadUrl(it) }
}
private fun startLoginToutActivity() {
val intent = Intent(this, LoginToutActivity::class.java)
.putExtra(IntentKey.LOGIN_REASON, LoginReason.STAR_PROJECT)
startActivityForResult(intent, ActivityRequestCodes.LOGIN_FLOW)
}
}

View File

@@ -11,6 +11,7 @@ import android.view.View
import android.view.inputmethod.InputMethodManager
import androidx.activity.result.ActivityResultLauncher
import androidx.annotation.AnimRes
import androidx.core.net.toUri
import androidx.fragment.app.Fragment
import androidx.lifecycle.Lifecycle
import com.google.android.play.core.review.ReviewInfo
@@ -32,6 +33,8 @@ import com.kickstarter.libs.utils.extensions.getRootCommentsActivityIntent
import com.kickstarter.libs.utils.extensions.getSearchIntent
import com.kickstarter.libs.utils.extensions.getUpdatesActivityIntent
import com.kickstarter.libs.utils.extensions.getVideoActivityIntent
import com.kickstarter.libs.utils.extensions.isPMOrderEditUri
import com.kickstarter.libs.utils.extensions.isPMUri
import com.kickstarter.libs.utils.extensions.reduceProjectPayload
import com.kickstarter.libs.utils.extensions.withData
import com.kickstarter.models.Project
@@ -329,7 +332,14 @@ fun Activity.startBackingDetailsWebViewActivity(url: String) {
)
}
fun Activity.startWebViewActivity(url: String, toolbarTitle: String) {
fun Activity.startWebViewActivity(url: String) {
val uri = url.toUri()
val toolbarTitle = when {
uri.isPMUri(url) -> getString(R.string.Pledge_manager)
uri.isPMOrderEditUri(url, isFFEnabled = true) -> getString(R.string.Review_edits_made_to_your_order)
else -> getString(R.string.Pledge_manager)
}
startActivity(
Intent(this, WebViewActivity::class.java)
.putExtra(IntentKey.URL, url)

View File

@@ -113,7 +113,7 @@ interface SplashScreenViewModel {
fun startProjectSurvey(): Observable<Pair<Uri, Boolean>>
fun startPMWebview(): Observable<Pair<Uri, Boolean>>
fun startPMWebview(): Observable<Uri>
/** Emits a Project and RefTag pair when we should start the [com.kickstarter.ui.activities.PreLaunchProjectPageActivity]. */
fun startPreLaunchProjectActivity(): Observable<Pair<Uri, Project>>
@@ -132,7 +132,7 @@ interface SplashScreenViewModel {
private val startProjectActivityToSave = BehaviorSubject.create<Uri>()
private val startProjectSurvey = BehaviorSubject.create<Pair<Uri, Boolean>>()
private val startPMWebview = BehaviorSubject.create<Pair<Uri, Boolean>>()
private val startPMWebview = BehaviorSubject.create<Uri>()
private val updateUserPreferences = BehaviorSubject.create<Boolean>()
private val finishDeeplinkActivity = BehaviorSubject.create<Unit>()
private val apolloClient = requireNotNull(environment.apolloClientV2())
@@ -379,10 +379,6 @@ interface SplashScreenViewModel {
it.isPMOrderEditUri(webEndpoint, ffClient.getBoolean(FlagKey.ANDROID_EDIT_ORDER))
}
.map { appendRefTagIfNone(it) }
.withLatestFrom(this.currentUser.isLoggedIn) { url, isLoggedIn ->
return@withLatestFrom Pair(url, isLoggedIn)
}
.filter { it.second.isTrue() }
.subscribe {
startPMWebview.onNext(it)
}.addToDisposable(disposables)
@@ -537,7 +533,7 @@ interface SplashScreenViewModel {
override fun startProjectSurvey(): Observable<Pair<Uri, Boolean>> = startProjectSurvey
override fun startPMWebview(): Observable<Pair<Uri, Boolean>> = startPMWebview
override fun startPMWebview(): Observable<Uri> = startPMWebview
override fun startPreLaunchProjectActivity(): Observable<Pair<Uri, Project>> = startPreLaunchProjectActivity
}

View File

@@ -0,0 +1,64 @@
package com.kickstarter.viewmodels
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import com.kickstarter.libs.Environment
import com.kickstarter.models.User
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import kotlinx.coroutines.plus
import kotlinx.coroutines.rx2.asFlow
import kotlin.coroutines.EmptyCoroutineContext
enum class WebViewEvent {
SHOW_LOGIN,
LOAD_WEBVIEW
}
class WebViewViewModel(
val environment: Environment,
testDispatcher: CoroutineDispatcher? = null
) : ViewModel() {
private val currentUser = requireNotNull(environment.currentUserV2()).observable()
private val scope = viewModelScope + (testDispatcher ?: EmptyCoroutineContext)
private val _mutableWebViewUIState = MutableStateFlow(WebViewEvent.SHOW_LOGIN)
val webViewUIState = _mutableWebViewUIState.asStateFlow()
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(),
initialValue = WebViewEvent.SHOW_LOGIN
)
init {
scope.launch {
currentUser.asFlow()
.collect {
emitCurrentState(it.getValue())
}
}
}
suspend fun emitCurrentState(user: User?) {
if (user != null) {
_mutableWebViewUIState.emit(WebViewEvent.LOAD_WEBVIEW)
} else {
_mutableWebViewUIState.emit(WebViewEvent.SHOW_LOGIN)
}
}
class Factory(
private val environment: Environment,
private val testDispatcher: CoroutineDispatcher? = null
) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return WebViewViewModel(environment, testDispatcher) as T
}
}
}

View File

@@ -659,6 +659,7 @@ Antippen und erneut versuchen</string>
<string name="Retry" formatted="false">Erneut versuchen</string>
<string name="Retry_or_select_another_method" formatted="false">Versuche es erneut oder wähle eine andere Karte.</string>
<string name="Reusability_and_recyclability" formatted="false">Wiederverwertung und Recyclingfähigkeit</string>
<string name="Review_edits_made_to_your_order" formatted="false">Änderungen an deiner Bestellung prüfen</string>
<string name="Reward_Surveys_few" formatted="false">%{reward_survey_count} Befragungen</string>
<string name="Reward_Surveys_many" formatted="false">%{reward_survey_count} Befragungen</string>
<string name="Reward_Surveys_one" formatted="false">Befragung</string>

View File

@@ -660,6 +660,7 @@ Toca para intentar de nuevo.</string>
<string name="Retry" formatted="false">Volver a intentar</string>
<string name="Retry_or_select_another_method" formatted="false">Vuelve a intentarlo o selecciona otro método.</string>
<string name="Reusability_and_recyclability" formatted="false">Reutilización y reciclabilidad</string>
<string name="Review_edits_made_to_your_order" formatted="false">Revisa las modificaciones realizadas en tu pedido</string>
<string name="Reward_Surveys_few" formatted="false">%{reward_survey_count} cuestionarios</string>
<string name="Reward_Surveys_many" formatted="false">%{reward_survey_count} cuestionarios</string>
<string name="Reward_Surveys_one" formatted="false">Cuestionario</string>

View File

@@ -660,6 +660,7 @@ n\'ont rien soutenu.</string>
<string name="Retry" formatted="false">Réessayer</string>
<string name="Retry_or_select_another_method" formatted="false">Veuillez réessayer ou choisir un autre moyen de paiement.</string>
<string name="Reusability_and_recyclability" formatted="false">Recyclage et valorisation des déchets</string>
<string name="Review_edits_made_to_your_order" formatted="false">Vérifions les modifications apportées à votre commande</string>
<string name="Reward_Surveys_few" formatted="false">%{reward_survey_count} questionnaires</string>
<string name="Reward_Surveys_many" formatted="false">%{reward_survey_count} questionnaires</string>
<string name="Reward_Surveys_one" formatted="false">Questionnaire</string>

View File

@@ -658,6 +658,7 @@
<string name="Retry" formatted="false">もう一度試す</string>
<string name="Retry_or_select_another_method" formatted="false">再試行するか別のお支払い方法を選択してください。</string>
<string name="Reusability_and_recyclability" formatted="false">再利用可能性とリサイクル可能性</string>
<string name="Review_edits_made_to_your_order" formatted="false">ご注文への編集内容を確認してください</string>
<string name="Reward_Surveys_few" formatted="false">%{reward_survey_count} リワードのサーベイ</string>
<string name="Reward_Surveys_many" formatted="false">%{reward_survey_count} リワードのサーベイ</string>
<string name="Reward_Surveys_one" formatted="false">リワードサーベイ</string>

View File

@@ -103,7 +103,4 @@
<!-- FPO's for PPO v2 -->
<string name="fpo_the_creator_has_not_collected_your_address_please_contact_them_to_resolve_the_issue" formatted="false">The creator has not collected your address. Please contact them to resolve the issue.</string>
<!-- FPO's for order edit -->
<string name="fpo_review_edits" formatted="false">Review edits made to your order</string>
</resources>

View File

@@ -662,6 +662,7 @@ daring ideas.</string>
<string name="Retry" formatted="false">Retry</string>
<string name="Retry_or_select_another_method" formatted="false">Retry or select another method.</string>
<string name="Reusability_and_recyclability" formatted="false">Reusability and recyclability</string>
<string name="Review_edits_made_to_your_order" formatted="false">Review edits made to your order</string>
<string name="Reward_Surveys_few" formatted="false">%{reward_survey_count} Reward Surveys</string>
<string name="Reward_Surveys_many" formatted="false">%{reward_survey_count} Reward Surveys</string>
<string name="Reward_Surveys_one" formatted="false">Reward Survey</string>

View File

@@ -51,7 +51,7 @@ class DeepLinkViewModelTest : KSRobolectricTestCase() {
private val startProjectActivityForCommentToUpdate = TestSubscriber<Uri>()
private val startPreLaunchProjectActivity = TestSubscriber<Pair<Uri, Project>>()
private val startProjectSurveyActivity = TestSubscriber<Pair<Uri, Boolean>>()
private val startPMOrderEditWebview = TestSubscriber<Pair<Uri, Boolean>>()
private val startPMOrderEditWebview = TestSubscriber<Uri>()
private val finishDeeplinkActivity = TestSubscriber<Unit>()
private val disposables = CompositeDisposable()
@@ -978,7 +978,7 @@ class DeepLinkViewModelTest : KSRobolectricTestCase() {
}
@Test
fun testPMOrderEditDeeplink_startsPMOrderEditWebview_featureFlagEnabled_UserLoggedIn() {
fun testPMOrderEditDeeplink_startsPMOrderEditWebview_featureFlagEnabled() {
val url = "https://www.kickstarter.com/projects/1768690592/reclaimed-coffee-video-game/order_edits/5/checkout"
val mockFeatureFlagClient: MockFeatureFlagClient =
object : MockFeatureFlagClient() {
@@ -1007,34 +1007,6 @@ class DeepLinkViewModelTest : KSRobolectricTestCase() {
startPMOrderEditWebview.assertValueCount(1)
}
@Test
fun testPMOrderEditDeeplink_startsPMOrderEditWebview_featureFlagEnabled_UserLoggedOut() {
val url = "https://www.kickstarter.com/projects/1768690592/reclaimed-coffee-video-game/order_edits/5/checkout"
val mockFeatureFlagClient: MockFeatureFlagClient =
object : MockFeatureFlagClient() {
override fun getBoolean(FlagKey: FlagKey): Boolean {
return true
}
}
val env = environment()
.toBuilder()
.currentUserV2(MockCurrentUserV2()) // using empty constructor means no user logged in
.featureFlagClient(mockFeatureFlagClient)
.build()
setUpEnvironment(intent = intentWithData(url), environment = env)
startBrowser.assertNoValues()
startDiscoveryActivity.assertNoValues()
startProjectActivity.assertNoValues()
startProjectActivityForCheckout.assertNoValues()
startProjectActivityForComment.assertNoValues()
startProjectActivityToSave.assertNoValues()
startPreLaunchProjectActivity.assertNoValues()
startProjectSurveyActivity.assertNoValues()
startPMOrderEditWebview.assertNoValues()
}
private fun mockApiSetBacking(backing: Backing, completed: Boolean): MockApiClientV2 {
return object : MockApiClientV2() {
override fun postBacking(

View File

@@ -0,0 +1,69 @@
package com.kickstarter.viewmodels
import com.kickstarter.KSRobolectricTestCase
import com.kickstarter.libs.Environment
import com.kickstarter.libs.MockCurrentUserV2
import com.kickstarter.mock.factories.UserFactory
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.advanceUntilIdle
import kotlinx.coroutines.test.runTest
import org.junit.Test
class WebViewViewModelTest : KSRobolectricTestCase() {
lateinit var vm: WebViewViewModel
fun setUpEnvironment(
environment: Environment? = null,
dispatcher: CoroutineDispatcher
) {
this.vm = WebViewViewModel.Factory(environment ?: environment(), dispatcher).create(WebViewViewModel::class.java)
}
@OptIn(ExperimentalCoroutinesApi::class)
@Test
fun emitsLoginEventWhenUserIsLoggedOut() = runTest {
val env = environment()
.toBuilder()
.currentUserV2(MockCurrentUserV2())
.build()
val dispatcher = UnconfinedTestDispatcher(testScheduler)
val webViewEvent = mutableListOf<WebViewEvent>()
backgroundScope.launch(dispatcher) {
setUpEnvironment(env, dispatcher)
vm.webViewUIState.toList(webViewEvent)
}
advanceUntilIdle()
assertEquals(1, webViewEvent.size)
assertEquals(webViewEvent.first(), WebViewEvent.SHOW_LOGIN)
}
@OptIn(ExperimentalCoroutinesApi::class)
@Test
fun emitsLoginEventWhenUserIsLoggedIn() = runTest {
val env = environment()
.toBuilder()
.currentUserV2(MockCurrentUserV2(UserFactory.user()))
.build()
val dispatcher = UnconfinedTestDispatcher(testScheduler)
val webViewEvent = mutableListOf<WebViewEvent>()
backgroundScope.launch(dispatcher) {
setUpEnvironment(env, dispatcher)
vm.webViewUIState.toList(webViewEvent)
}
advanceUntilIdle()
assertEquals(2, webViewEvent.size)
assertEquals(webViewEvent.first(), WebViewEvent.SHOW_LOGIN) // initial
assertEquals(webViewEvent.last(), WebViewEvent.LOAD_WEBVIEW) // actual
}
}