From 7a3345fdeabeea7f25d329de559ddbc87602a742 Mon Sep 17 00:00:00 2001 From: mtgriego Date: Wed, 25 Oct 2023 09:53:36 -0700 Subject: [PATCH] [MBL-796] Update RewardViewHolderVIewmodel to RX2 (#1885) * Update RewardViewHolderVIewmodel to RX2 * lint and clear disposables * more lint --- .../ui/viewholders/RewardViewHolder.kt | 180 +++++---- .../viewmodels/RewardViewHolderViewModel.kt | 153 ++------ .../RewardViewHolderViewModelTest.kt | 363 +++++++++++++----- 3 files changed, 396 insertions(+), 300 deletions(-) diff --git a/app/src/main/java/com/kickstarter/ui/viewholders/RewardViewHolder.kt b/app/src/main/java/com/kickstarter/ui/viewholders/RewardViewHolder.kt index b61b8e1d38..73c4252610 100644 --- a/app/src/main/java/com/kickstarter/ui/viewholders/RewardViewHolder.kt +++ b/app/src/main/java/com/kickstarter/ui/viewholders/RewardViewHolder.kt @@ -1,7 +1,6 @@ package com.kickstarter.ui.viewholders import android.annotation.SuppressLint -import android.content.Intent import android.util.Pair import android.view.View import androidx.core.view.isGone @@ -9,24 +8,21 @@ import androidx.recyclerview.widget.LinearLayoutManager import com.jakewharton.rxbinding.view.RxView import com.kickstarter.R import com.kickstarter.databinding.ItemRewardBinding -import com.kickstarter.libs.rx.transformers.Transformers.observeForUI +import com.kickstarter.libs.rx.transformers.Transformers.observeForUIV2 import com.kickstarter.libs.utils.NumberUtils import com.kickstarter.libs.utils.RewardItemDecorator import com.kickstarter.libs.utils.RewardUtils import com.kickstarter.libs.utils.RewardViewUtils -import com.kickstarter.libs.utils.TransitionUtils.slideInFromRight -import com.kickstarter.libs.utils.TransitionUtils.transition import com.kickstarter.libs.utils.ViewUtils +import com.kickstarter.libs.utils.extensions.addToDisposable import com.kickstarter.libs.utils.extensions.isNotNull import com.kickstarter.libs.utils.extensions.isTrue -import com.kickstarter.models.Project import com.kickstarter.models.Reward -import com.kickstarter.ui.IntentKey -import com.kickstarter.ui.activities.BackingActivity import com.kickstarter.ui.adapters.RewardItemsAdapter import com.kickstarter.ui.data.ProjectData import com.kickstarter.viewmodels.RewardViewHolderViewModel -import rx.android.schedulers.AndroidSchedulers +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.disposables.CompositeDisposable class RewardViewHolder(private val binding: ItemRewardBinding, val delegate: Delegate?, private val inset: Boolean = false) : KSViewHolder(binding.root) { @@ -39,152 +35,153 @@ class RewardViewHolder(private val binding: ItemRewardBinding, val delegate: Del private val currencyConversionString = context().getString(R.string.About_reward_amount) private val remainingRewardsString = context().getString(R.string.Left_count_left_few) + private val disposables = CompositeDisposable() init { val rewardItemAdapter = setUpRewardItemsAdapter() this.viewModel.outputs.conversionIsGone() - .compose(bindToLifecycle()) - .compose(observeForUI()) - .subscribe(ViewUtils.setGone(this.binding.rewardConversionTextView)) + .compose(observeForUIV2()) + .subscribe { this.binding.rewardConversionTextView.isGone = it } + .addToDisposable(disposables) this.viewModel.outputs.conversion() - .compose(bindToLifecycle()) - .compose(observeForUI()) + .compose(observeForUIV2()) .subscribe { setConversionTextView(it) } + .addToDisposable(disposables) this.viewModel.outputs.descriptionForNoReward() - .compose(bindToLifecycle()) - .compose(observeForUI()) + .compose(observeForUIV2()) .subscribe { this.binding.rewardDescriptionTextView.setText(it) } + .addToDisposable(disposables) this.viewModel.outputs.descriptionForReward() - .compose(bindToLifecycle()) - .compose(observeForUI()) + .compose(observeForUIV2()) .subscribe { this.binding.rewardDescriptionTextView.text = it } + .addToDisposable(disposables) this.viewModel.outputs.descriptionIsGone() - .compose(bindToLifecycle()) - .compose(observeForUI()) - .subscribe { ViewUtils.setGone(this.binding.rewardDescriptionContainer, it) } + .compose(observeForUIV2()) + .subscribe { this.binding.rewardDescriptionContainer.isGone = it } + .addToDisposable(disposables) this.viewModel.outputs.buttonIsEnabled() - .compose(bindToLifecycle()) - .compose(observeForUI()) + .compose(observeForUIV2()) .subscribe { this.binding.rewardPledgeButton.isEnabled = it } + .addToDisposable(disposables) this.viewModel.outputs.remainingIsGone() - .compose(bindToLifecycle()) - .compose(observeForUI()) - .subscribe(ViewUtils.setGone(this.binding.rewardRemainingTextView)) + .compose(observeForUIV2()) + .subscribe { this.binding.rewardRemainingTextView.isGone = it } + .addToDisposable(disposables) this.viewModel.outputs.limitContainerIsGone() - .compose(bindToLifecycle()) - .compose(observeForUI()) + .compose(observeForUIV2()) .subscribe { ViewUtils.setGone(this.binding.rewardLimitContainer, it) } + .addToDisposable(disposables) this.viewModel.outputs.remaining() - .compose(bindToLifecycle()) - .compose(observeForUI()) + .compose(observeForUIV2()) .subscribe { setRemainingRewardsTextView(it) } + .addToDisposable(disposables) this.viewModel.outputs.buttonCTA() - .compose(bindToLifecycle()) - .compose(observeForUI()) + .compose(observeForUIV2()) .subscribe { this.binding.rewardPledgeButton.setText(it) } + .addToDisposable(disposables) this.viewModel.outputs.shippingSummary() - .compose(bindToLifecycle()) - .compose(observeForUI()) + .compose(observeForUIV2()) .subscribe { setShippingSummaryText(it) } + .addToDisposable(disposables) this.viewModel.outputs.shippingSummaryIsGone() - .compose(bindToLifecycle()) .observeOn(AndroidSchedulers.mainThread()) .subscribe { this.binding.rewardShippingSummary.isGone = it } + .addToDisposable(disposables) this.viewModel.outputs.minimumAmountTitle() - .compose(bindToLifecycle()) - .compose(observeForUI()) + .compose(observeForUIV2()) .subscribe { this.binding.rewardMinimumTextView.text = it } + .addToDisposable(disposables) this.viewModel.outputs.reward() - .compose(bindToLifecycle()) - .compose(observeForUI()) + .compose(observeForUIV2()) .subscribe { this.binding.rewardEndingTextView.text = formattedExpirationString(it) } + .addToDisposable(disposables) this.viewModel.outputs.endDateSectionIsGone() - .compose(bindToLifecycle()) - .compose(observeForUI()) - .subscribe { ViewUtils.setGone(this.binding.rewardEndingTextView, it) } + .compose(observeForUIV2()) + .subscribe { this.binding.rewardEndingTextView.isGone = it } + .addToDisposable(disposables) this.viewModel.outputs.rewardItems() - .compose(bindToLifecycle()) - .compose(observeForUI()) + .compose(observeForUIV2()) .subscribe { rewardItemAdapter.rewardsItems(it) } + .addToDisposable(disposables) this.viewModel.outputs.rewardItemsAreGone() - .compose(bindToLifecycle()) - .compose(observeForUI()) - .subscribe(ViewUtils.setGone(this.binding.rewardsItemSection)) + .compose(observeForUIV2()) + .subscribe { this.binding.rewardsItemSection.isGone = it } + .addToDisposable(disposables) this.viewModel.outputs.titleForNoReward() - .compose(bindToLifecycle()) - .compose(observeForUI()) + .compose(observeForUIV2()) .subscribe { this.binding.rewardTitleTextView.setText(it) } + .addToDisposable(disposables) this.viewModel.outputs.titleForReward() - .compose(bindToLifecycle()) - .compose(observeForUI()) + .compose(observeForUIV2()) .subscribe { this.binding.rewardTitleTextView.text = it } + .addToDisposable(disposables) this.viewModel.outputs.titleIsGone() - .compose(bindToLifecycle()) - .compose(observeForUI()) - .subscribe { ViewUtils.setGone(this.binding.rewardTitleTextView, it) } + .compose(observeForUIV2()) + .subscribe { this.binding.rewardTitleTextView.isGone = it } + .addToDisposable(disposables) this.viewModel.outputs.showFragment() - .compose(bindToLifecycle()) - .compose(observeForUI()) + .compose(observeForUIV2()) .subscribe { this.delegate?.rewardClicked(it.second) } + .addToDisposable(disposables) this.viewModel.outputs.buttonIsGone() - .compose(bindToLifecycle()) - .compose(observeForUI()) + .compose(observeForUIV2()) .subscribe { setPledgeButtonVisibility(it) } + .addToDisposable(disposables) this.viewModel.outputs.backersCount() - .compose(bindToLifecycle()) - .compose(observeForUI()) + .compose(observeForUIV2()) .subscribe { setBackersCountTextView(it) } + .addToDisposable(disposables) this.viewModel.outputs.backersCountIsGone() - .compose(bindToLifecycle()) - .compose(observeForUI()) - .subscribe { ViewUtils.setGone(this.binding.rewardBackersCount, it) } + .compose(observeForUIV2()) + .subscribe { this.binding.rewardBackersCount.isGone = it } + .addToDisposable(disposables) this.viewModel.outputs.estimatedDelivery() - .compose(bindToLifecycle()) - .compose(observeForUI()) + .compose(observeForUIV2()) .subscribe { this.binding.rewardEstimatedDelivery.text = it } + .addToDisposable(disposables) this.viewModel.outputs.estimatedDeliveryIsGone() - .compose(bindToLifecycle()) - .compose(observeForUI()) - .subscribe { ViewUtils.setGone(this.binding.rewardEstimatedDeliverySection, it) } + .compose(observeForUIV2()) + .subscribe { this.binding.rewardEstimatedDeliverySection.isGone = it } + .addToDisposable(disposables) this.viewModel.outputs.isMinimumPledgeAmountGone() - .compose(bindToLifecycle()) - .compose(observeForUI()) + .compose(observeForUIV2()) .subscribe { - ViewUtils.setGone(this.binding.rewardConversionTextView, it) - ViewUtils.setGone(this.binding.rewardMinimumTextView, it) + this.binding.rewardConversionTextView.isGone = it + this.binding.rewardMinimumTextView.isGone = it } + .addToDisposable(disposables) RxView.clicks(this.binding.rewardPledgeButton) .compose(bindToLifecycle()) @@ -192,33 +189,33 @@ class RewardViewHolder(private val binding: ItemRewardBinding, val delegate: Del this.viewModel.outputs.hasAddOnsAvailable() .filter { it.isNotNull() } - .compose(bindToLifecycle()) - .compose(observeForUI()) + .compose(observeForUIV2()) .subscribe { - ViewUtils.setGone(this.binding.rewardAddOnsAvailable, !it) + this.binding.rewardAddOnsAvailable.isGone = !it } + .addToDisposable(disposables) this.viewModel.outputs.selectedRewardTagIsGone() - .compose(bindToLifecycle()) - .compose(observeForUI()) + .compose(observeForUIV2()) .subscribe { isGone -> if (!isGone) this.binding.rewardSelectedRewardTag.visibility = View.VISIBLE - else ViewUtils.setGone(this.binding.rewardSelectedRewardTag, true) + else this.binding.rewardSelectedRewardTag.isGone = true } + .addToDisposable(disposables) this.viewModel.outputs.localPickUpIsGone() - .compose(bindToLifecycle()) .observeOn(AndroidSchedulers.mainThread()) .subscribe { this.binding.localPickupContainer.localPickupGroup.isGone = it } + .addToDisposable(disposables) this.viewModel.outputs.localPickUpName() - .compose(bindToLifecycle()) .observeOn(AndroidSchedulers.mainThread()) .subscribe { this.binding.localPickupContainer.localPickupLocation.text = it } + .addToDisposable(disposables) } override fun bindData(data: Any?) { @@ -264,11 +261,16 @@ class RewardViewHolder(private val binding: ItemRewardBinding, val delegate: Del } } - private fun setRemainingRewardsTextView(remaining: String) { - this.binding.rewardRemainingTextView.text = this.ksString.format( - this.remainingRewardsString, - "left_count", remaining - ) + private fun setRemainingRewardsTextView(remaining: Int) { + if (remaining > 0) { + this.binding.rewardRemainingTextView.isGone = false + this.binding.rewardRemainingTextView.text = this.ksString.format( + this.remainingRewardsString, + "left_count", NumberUtils.format(remaining) + ) + } else { + this.binding.rewardRemainingTextView.isGone = true + } } private fun setShippingSummaryText(stringResAndLocationName: Pair) { @@ -287,12 +289,8 @@ class RewardViewHolder(private val binding: ItemRewardBinding, val delegate: Del return rewardItemAdapter } - private fun startBackingActivity(project: Project) { - val context = context() - val intent = Intent(context, BackingActivity::class.java) - .putExtra(IntentKey.PROJECT, project) - - context.startActivity(intent) - transition(context, slideInFromRight()) + override fun destroy() { + viewModel.onCleared() + super.destroy() } } diff --git a/app/src/main/java/com/kickstarter/viewmodels/RewardViewHolderViewModel.kt b/app/src/main/java/com/kickstarter/viewmodels/RewardViewHolderViewModel.kt index 67fa54846a..5361028fa6 100644 --- a/app/src/main/java/com/kickstarter/viewmodels/RewardViewHolderViewModel.kt +++ b/app/src/main/java/com/kickstarter/viewmodels/RewardViewHolderViewModel.kt @@ -2,17 +2,16 @@ package com.kickstarter.viewmodels import android.text.SpannableString import android.util.Pair -import androidx.annotation.NonNull import androidx.annotation.VisibleForTesting import com.kickstarter.R -import com.kickstarter.libs.ActivityViewModel import com.kickstarter.libs.Environment import com.kickstarter.libs.rx.transformers.Transformers.combineLatestPair -import com.kickstarter.libs.rx.transformers.Transformers.takeWhen +import com.kickstarter.libs.rx.transformers.Transformers.takeWhenV2 import com.kickstarter.libs.utils.DateTimeUtils -import com.kickstarter.libs.utils.NumberUtils +import com.kickstarter.libs.utils.KsOptional import com.kickstarter.libs.utils.RewardUtils import com.kickstarter.libs.utils.RewardViewUtils +import com.kickstarter.libs.utils.extensions.addToDisposable import com.kickstarter.libs.utils.extensions.isBacked import com.kickstarter.libs.utils.extensions.isNotNull import com.kickstarter.libs.utils.extensions.isNull @@ -24,11 +23,11 @@ import com.kickstarter.models.User import com.kickstarter.ui.data.PledgeData import com.kickstarter.ui.data.PledgeFlowContext import com.kickstarter.ui.data.ProjectData -import com.kickstarter.ui.viewholders.RewardViewHolder +import io.reactivex.Observable +import io.reactivex.disposables.CompositeDisposable +import io.reactivex.subjects.BehaviorSubject +import io.reactivex.subjects.PublishSubject import org.joda.time.DateTime -import rx.Observable -import rx.subjects.BehaviorSubject -import rx.subjects.PublishSubject import java.math.RoundingMode interface RewardViewHolderViewModel { @@ -66,7 +65,7 @@ interface RewardViewHolderViewModel { fun descriptionForNoReward(): Observable /** Emits the reward's description. */ - fun descriptionForReward(): Observable + fun descriptionForReward(): Observable /** Emits `true` if the reward description is empty and should be hidden in the UI. */ fun descriptionIsGone(): Observable @@ -87,7 +86,7 @@ interface RewardViewHolderViewModel { fun minimumAmountTitle(): Observable /** Emits the remaining count of the reward. */ - fun remaining(): Observable + fun remaining(): Observable /** Emits `true` if the remaining count should be hidden, `false` otherwise. */ fun remainingIsGone(): Observable @@ -117,7 +116,7 @@ interface RewardViewHolderViewModel { fun titleForNoReward(): Observable /** Emits the reward's title when `isReward` is true. */ - fun titleForReward(): Observable + fun titleForReward(): Observable /** Emits if the reward has add-Ons available */ fun hasAddOnsAvailable(): Observable @@ -135,13 +134,9 @@ interface RewardViewHolderViewModel { fun localPickUpName(): Observable } - class ViewModel(@NonNull environment: Environment) : - ActivityViewModel(environment), Inputs, Outputs { - private val currentUser = requireNotNull(environment.currentUser()) + class ViewModel(environment: Environment) : Inputs, Outputs { + private val currentUser = requireNotNull(environment.currentUserV2()) private val ksCurrency = requireNotNull(environment.ksCurrency()) - private val ffClient = requireNotNull(environment.featureFlagClient()) - private val sharedPreferences = requireNotNull(environment.sharedPreferences()) - private val apolloClient = requireNotNull(environment.apolloClient()) private val projectDataAndReward = PublishSubject.create>() private val rewardClicked = PublishSubject.create() @@ -154,14 +149,14 @@ interface RewardViewHolderViewModel { private val conversion = BehaviorSubject.create() private val conversionIsGone = BehaviorSubject.create() private val descriptionForNoReward = BehaviorSubject.create() - private val descriptionForReward = BehaviorSubject.create() + private val descriptionForReward = BehaviorSubject.create() private val descriptionIsGone = BehaviorSubject.create() private val endDateSectionIsGone = BehaviorSubject.create() private val estimatedDelivery = BehaviorSubject.create() private val estimatedDeliveryIsGone = BehaviorSubject.create() private val limitContainerIsGone = BehaviorSubject.create() private val minimumAmountTitle = PublishSubject.create() - private val remaining = BehaviorSubject.create() + private val remaining = BehaviorSubject.create() private val remainingIsGone = BehaviorSubject.create() private val reward = BehaviorSubject.create() private val rewardItems = BehaviorSubject.create>() @@ -170,7 +165,7 @@ interface RewardViewHolderViewModel { private val shippingSummaryIsGone = BehaviorSubject.create() private val showFragment = PublishSubject.create>() private val titleForNoReward = BehaviorSubject.create() - private val titleForReward = BehaviorSubject.create() + private val titleForReward = BehaviorSubject.create() private val titleIsGone = BehaviorSubject.create() private val addOnsAvailable = BehaviorSubject.create() private val isMinimumPledgeAmountGone = BehaviorSubject.create() @@ -184,6 +179,8 @@ interface RewardViewHolderViewModel { val inputs: Inputs = this val outputs: Outputs = this + val disposables = CompositeDisposable() + init { val reward = this.projectDataAndReward @@ -206,32 +203,29 @@ interface RewardViewHolderViewModel { this.ksCurrency ) } - .compose(bindToLifecycle()) .subscribe(this.minimumAmountTitle) val userCreatedProject = this.currentUser.observable() - .compose>(combineLatestPair(project)) - .map { it.first?.id() == it.second.creator().id() } + .compose?, Project>>(combineLatestPair(project)) + .map { it.first?.getValue()?.id() == it.second.creator().id() } projectAndReward .compose, Boolean>>(combineLatestPair(userCreatedProject)) .map { buttonIsGone(it.first.first, it.first.second, it.second) } .distinctUntilChanged() - .compose(bindToLifecycle()) .subscribe(this.buttonIsGone) projectAndReward .map { RewardViewUtils.pledgeButtonText(it.first, it.second) } .distinctUntilChanged() - .compose(bindToLifecycle()) .subscribe { this.buttonCTA.onNext(it) } + .addToDisposable(disposables) projectAndReward .map { it.first } .map { it.currency() == it.currentCurrency() } - .compose(bindToLifecycle()) .subscribe(this.conversionIsGone) projectAndReward @@ -244,7 +238,6 @@ interface RewardViewHolderViewModel { true ) } - .compose(bindToLifecycle()) .subscribe(this.conversion) projectAndReward @@ -256,86 +249,74 @@ interface RewardViewHolderViewModel { else -> R.string.Back_it_because_you_believe_in_it } } - .compose(bindToLifecycle()) .subscribe(this.descriptionForNoReward) reward .filter { RewardUtils.isReward(it) } + .filter { it.description().isNotNull() } .map { it.description() } - .compose(bindToLifecycle()) .subscribe(this.descriptionForReward) projectAndReward .map { RewardUtils.isReward(it.second) && it.second.description().isNullOrEmpty() } .distinctUntilChanged() - .compose(bindToLifecycle()) .subscribe(this.descriptionIsGone) projectAndReward .map { shouldContinueFlow(it.first, it.second) } .distinctUntilChanged() - .compose(bindToLifecycle()) .subscribe { this.buttonIsEnabled.onNext(it) } + .addToDisposable(disposables) projectAndReward .map { it.first.isLive && RewardUtils.isLimited(it.second) } .map { it.negate() } .distinctUntilChanged() - .compose(bindToLifecycle()) .subscribe(this.remainingIsGone) reward .filter { RewardUtils.isLimited(it) } - .filter { it.remaining().isNotNull() } - .map { it.remaining() } - .map { remaining -> remaining?.let { NumberUtils.format(it) } } - .compose(bindToLifecycle()) + .map { it.remaining() ?: -1 } .subscribe(this.remaining) reward .filter { RewardUtils.isItemized(it) } .map { it.rewardsItems() } - .compose(bindToLifecycle()) .subscribe(this.rewardItems) reward .map { RewardUtils.isItemized(it) } .map { it.negate() } .distinctUntilChanged() - .compose(bindToLifecycle()) .subscribe(this.rewardItemsAreGone) reward - .compose(bindToLifecycle()) .subscribe(this.reward) reward .map { it.hasAddons() } - .compose(bindToLifecycle()) .subscribe(this.addOnsAvailable) projectAndReward .map { expirationDateIsGone(it.first, it.second) } .distinctUntilChanged() - .compose(bindToLifecycle()) .subscribe(this.endDateSectionIsGone) projectAndReward .filter { shouldContinueFlow(it.first, it.second) && it.first.isLive } - .compose>(takeWhen(this.rewardClicked)) - .compose(bindToLifecycle()) + .compose>(takeWhenV2(this.rewardClicked)) .subscribe(this.showFragment) this.projectDataAndReward .filter { it.first.project().isLive && !it.first.project().isBacking() } - .compose>(takeWhen(this.rewardClicked)) + .compose(takeWhenV2(this.rewardClicked)) .map { PledgeData.with(PledgeFlowContext.NEW_PLEDGE, it.first, it.second) } - .compose(bindToLifecycle()) .subscribe { - this.analyticEvents.trackSelectRewardCTA(it) + environment.analytics()?.trackSelectRewardCTA(it) } + .addToDisposable(disposables) projectAndReward .filter { RewardUtils.isNoReward(it.second) } @@ -346,33 +327,29 @@ interface RewardViewHolderViewModel { else -> R.string.Pledge_without_a_reward } } - .compose(bindToLifecycle()) .subscribe(this.titleForNoReward) projectAndReward .map { it.first.backing()?.isBacked(it.second) ?: false } - .compose(bindToLifecycle()) .subscribe { this.selectedRewardTagIsGone.onNext(!it) } + .addToDisposable(disposables) reward .filter { RewardUtils.isReward(it) } .map { it.title() } - .compose(bindToLifecycle()) .subscribe(this.titleForReward) reward .map { RewardUtils.isReward(it) && it.title().isNullOrEmpty() } .distinctUntilChanged() - .compose(bindToLifecycle()) .subscribe(this.titleIsGone) reward .filter { RewardUtils.isShippable(it) } .map { RewardUtils.shippingSummary(it) } .filter { it.isNotNull() } - .compose(bindToLifecycle()) .subscribe(this.shippingSummary) reward @@ -380,24 +357,22 @@ interface RewardViewHolderViewModel { .map { RewardUtils.isLocalPickup(it) } - .compose(bindToLifecycle()) .subscribe { this.localPickUpIsGone.onNext(!it) } + .addToDisposable(disposables) reward .filter { !RewardUtils.isShippable(it) } .filter { RewardUtils.isLocalPickup(it) } .map { it.localReceiptLocation()?.displayableName() } .filter { it.isNotNull() } - .compose(bindToLifecycle()) .subscribe(this.localPickUpName) projectAndReward .map { it.first.isLive && RewardUtils.isShippable(it.second) } .map { it.negate() } .distinctUntilChanged() - .compose(bindToLifecycle()) .subscribe(this.shippingSummaryIsGone) Observable.combineLatest( @@ -407,36 +382,30 @@ interface RewardViewHolderViewModel { this.addOnsAvailable ) { endDateGone, remainingGone, shippingGone, addOnsAvailable -> endDateGone && remainingGone && shippingGone && !addOnsAvailable } .distinctUntilChanged() - .compose(bindToLifecycle()) .subscribe(this.limitContainerIsGone) reward .map { RewardUtils.isNoReward(it) || !RewardUtils.hasBackers(it) } .distinctUntilChanged() - .compose(bindToLifecycle()) .subscribe(this.backersCountIsGone) reward .filter { RewardUtils.isReward(it) && RewardUtils.hasBackers(it) } .map { it.backersCount() as Int } - .compose(bindToLifecycle()) .subscribe(this.backersCount) reward .map { RewardUtils.isNoReward(it) || it.estimatedDeliveryOn().isNull() } .distinctUntilChanged() - .compose(bindToLifecycle()) .subscribe(this.estimatedDeliveryIsGone) reward .filter { RewardUtils.isReward(it) && it.estimatedDeliveryOn().isNotNull() } .map { it.estimatedDeliveryOn() } .map { DateTimeUtils.estimatedDeliveryOn(it) } - .compose(bindToLifecycle()) .subscribe(this.estimatedDelivery) reward.map { RewardUtils.isNoReward(it) } - .compose(bindToLifecycle()) .subscribe(this.isMinimumPledgeAmountGone) } @@ -459,25 +428,6 @@ interface RewardViewHolderViewModel { } } - /** - * In case the `suggested_no_reward_amount` is active and the selected reward is no reward - * update the value with one of the variants. Otherwise return the reward as it is - * - * @param rewardAndVariant contains the selected reward and the variant value - * - * @return reward if modified value if needed - */ - private fun updateReward(rewardAndVariant: Pair): Reward { - val reward = rewardAndVariant.first - - val updatedMinimum = rewardAndVariant.second?.let { - if (RewardUtils.isNoReward(reward)) it.toDouble() - else reward.minimum() - } ?: reward.minimum() - - return reward.toBuilder().minimum(updatedMinimum).build() - } - private fun buttonIsGone( project: Project, reward: Reward, @@ -498,9 +448,7 @@ interface RewardViewHolderViewModel { } } - private fun hasBackedAddOns(project: Project) = !project.backing()?.addOns().isNullOrEmpty() - - private fun isSelectable(@NonNull project: Project, @NonNull reward: Reward): Boolean { + private fun isSelectable(project: Project, reward: Reward): Boolean { if (project.backing()?.isBacked(reward) == true) { return false } @@ -508,7 +456,7 @@ interface RewardViewHolderViewModel { return RewardUtils.isAvailable(project, reward) } - override fun configureWith(@NonNull projectData: ProjectData, @NonNull reward: Reward) { + override fun configureWith(projectData: ProjectData, reward: Reward) { this.projectDataAndReward.onNext(Pair.create(projectData, reward)) } @@ -516,96 +464,71 @@ interface RewardViewHolderViewModel { this.rewardClicked.onNext(position) } - @NonNull override fun backersCount(): Observable = this.backersCount - @NonNull override fun backersCountIsGone(): Observable = this.backersCountIsGone - @NonNull override fun buttonCTA(): Observable = this.buttonCTA - @NonNull override fun buttonIsEnabled(): Observable = this.buttonIsEnabled - @NonNull override fun buttonIsGone(): Observable = this.buttonIsGone - @NonNull override fun conversion(): Observable = this.conversion - @NonNull override fun conversionIsGone(): Observable = this.conversionIsGone - @NonNull override fun descriptionForNoReward(): Observable = this.descriptionForNoReward - @NonNull - override fun descriptionForReward(): Observable = this.descriptionForReward + override fun descriptionForReward(): Observable = this.descriptionForReward - @NonNull override fun descriptionIsGone(): Observable = this.descriptionIsGone - @NonNull override fun endDateSectionIsGone(): Observable = this.endDateSectionIsGone - @NonNull override fun estimatedDelivery(): Observable = this.estimatedDelivery - @NonNull override fun estimatedDeliveryIsGone(): Observable = this.estimatedDeliveryIsGone - @NonNull override fun limitContainerIsGone(): Observable = this.limitContainerIsGone - @NonNull override fun minimumAmountTitle(): Observable = this.minimumAmountTitle - @NonNull - override fun remaining(): Observable = this.remaining + override fun remaining(): Observable = this.remaining - @NonNull override fun remainingIsGone(): Observable = this.remainingIsGone - @NonNull override fun reward(): Observable = this.reward - @NonNull override fun rewardItems(): Observable> = this.rewardItems - @NonNull override fun rewardItemsAreGone(): Observable = this.rewardItemsAreGone - @NonNull override fun shippingSummary(): Observable> = this.shippingSummary - @NonNull override fun shippingSummaryIsGone(): Observable = this.shippingSummaryIsGone - @NonNull override fun showFragment(): Observable> = this.showFragment - @NonNull override fun titleForNoReward(): Observable = this.titleForNoReward - @NonNull - override fun titleForReward(): Observable = this.titleForReward + override fun titleForReward(): Observable = this.titleForReward - @NonNull override fun titleIsGone(): Observable = this.titleIsGone - @NonNull override fun hasAddOnsAvailable(): Observable = this.addOnsAvailable - @NonNull override fun isMinimumPledgeAmountGone(): Observable = this.isMinimumPledgeAmountGone - @NonNull override fun selectedRewardTagIsGone(): Observable = this.selectedRewardTagIsGone override fun localPickUpIsGone(): Observable = this.localPickUpIsGone override fun localPickUpName(): Observable = this.localPickUpName + + fun onCleared() { + disposables.clear() + } } } diff --git a/app/src/test/java/com/kickstarter/viewmodels/RewardViewHolderViewModelTest.kt b/app/src/test/java/com/kickstarter/viewmodels/RewardViewHolderViewModelTest.kt index 2638bad922..73e630c154 100644 --- a/app/src/test/java/com/kickstarter/viewmodels/RewardViewHolderViewModelTest.kt +++ b/app/src/test/java/com/kickstarter/viewmodels/RewardViewHolderViewModelTest.kt @@ -2,12 +2,12 @@ package com.kickstarter.viewmodels import android.content.SharedPreferences import android.util.Pair -import androidx.annotation.NonNull import com.kickstarter.KSRobolectricTestCase import com.kickstarter.R import com.kickstarter.libs.Environment -import com.kickstarter.libs.MockCurrentUser +import com.kickstarter.libs.MockCurrentUserV2 import com.kickstarter.libs.utils.EventName +import com.kickstarter.libs.utils.extensions.addToDisposable import com.kickstarter.mock.factories.BackingFactory import com.kickstarter.mock.factories.LocationFactory import com.kickstarter.mock.factories.ProjectDataFactory @@ -18,10 +18,12 @@ import com.kickstarter.models.Project import com.kickstarter.models.Reward import com.kickstarter.models.RewardsItem import com.kickstarter.ui.SharedPreferenceKey +import io.reactivex.disposables.CompositeDisposable +import io.reactivex.subscribers.TestSubscriber import org.joda.time.DateTime +import org.junit.After import org.junit.Test import org.mockito.Mockito -import rx.observers.TestSubscriber import java.math.RoundingMode class RewardViewHolderViewModelTest : KSRobolectricTestCase() { @@ -35,14 +37,14 @@ class RewardViewHolderViewModelTest : KSRobolectricTestCase() { private val conversion = TestSubscriber.create() private val conversionIsGone = TestSubscriber.create() private val descriptionForNoReward = TestSubscriber() - private val descriptionForReward = TestSubscriber() + private val descriptionForReward = TestSubscriber() private val descriptionIsGone = TestSubscriber() private val endDateSectionIsGone = TestSubscriber() private val estimatedDelivery = TestSubscriber() private val estimatedDeliveryIsGone = TestSubscriber() private val limitContainerIsGone = TestSubscriber() private val minimumAmountTitle = TestSubscriber() - private val remaining = TestSubscriber() + private val remaining = TestSubscriber() private val remainingIsGone = TestSubscriber() private val reward = TestSubscriber() private val rewardItems = TestSubscriber>() @@ -58,38 +60,69 @@ class RewardViewHolderViewModelTest : KSRobolectricTestCase() { private val localPickUpIsGone = TestSubscriber() private val localPickUpName = TestSubscriber() - private fun setUpEnvironment(@NonNull environment: Environment) { + private val disposables = CompositeDisposable() + + private fun setUpEnvironment(environment: Environment) { this.vm = RewardViewHolderViewModel.ViewModel(environment) - this.vm.outputs.backersCount().subscribe(this.backersCount) - this.vm.outputs.backersCountIsGone().subscribe(this.backersCountIsGone) - this.vm.outputs.buttonCTA().subscribe(this.buttonCTA) - this.vm.outputs.buttonIsEnabled().subscribe(this.buttonIsEnabled) - this.vm.outputs.buttonIsGone().subscribe(this.buttonIsGone) - this.vm.outputs.conversion().subscribe(this.conversion) - this.vm.outputs.conversionIsGone().subscribe(this.conversionIsGone) - this.vm.outputs.descriptionForNoReward().subscribe(this.descriptionForNoReward) - this.vm.outputs.descriptionForReward().subscribe(this.descriptionForReward) - this.vm.outputs.descriptionIsGone().subscribe(this.descriptionIsGone) - this.vm.outputs.endDateSectionIsGone().subscribe(this.endDateSectionIsGone) - this.vm.outputs.estimatedDelivery().subscribe(this.estimatedDelivery) - this.vm.outputs.estimatedDeliveryIsGone().subscribe(this.estimatedDeliveryIsGone) - this.vm.outputs.remaining().subscribe(this.remaining) - this.vm.outputs.remainingIsGone().subscribe(this.remainingIsGone) - this.vm.outputs.limitContainerIsGone().subscribe(this.limitContainerIsGone) - this.vm.outputs.minimumAmountTitle().map { it.toString() }.subscribe(this.minimumAmountTitle) - this.vm.outputs.reward().subscribe(this.reward) - this.vm.outputs.rewardItems().subscribe(this.rewardItems) - this.vm.outputs.rewardItemsAreGone().subscribe(this.rewardItemsAreGone) - this.vm.outputs.shippingSummary().subscribe(this.shippingSummary) - this.vm.outputs.shippingSummaryIsGone().subscribe(this.shippingSummaryIsGone) - this.vm.outputs.showFragment().subscribe(this.showPledgeFragment) - this.vm.outputs.titleForNoReward().subscribe(this.titleForNoReward) - this.vm.outputs.titleForReward().subscribe(this.titleForReward) - this.vm.outputs.titleIsGone().subscribe(this.titleIsGone) - this.vm.outputs.hasAddOnsAvailable().subscribe(this.hasAddonsAvailable) - this.vm.outputs.selectedRewardTagIsGone().subscribe(this.selectedRewardTagIsGone) - this.vm.outputs.localPickUpName().subscribe(this.localPickUpName) - this.vm.outputs.localPickUpIsGone().subscribe(this.localPickUpIsGone) + this.vm.outputs.backersCount().subscribe { this.backersCount.onNext(it) } + .addToDisposable(disposables) + this.vm.outputs.backersCountIsGone().subscribe { this.backersCountIsGone.onNext(it) } + .addToDisposable(disposables) + this.vm.outputs.buttonCTA().subscribe { this.buttonCTA.onNext(it) } + .addToDisposable(disposables) + this.vm.outputs.buttonIsEnabled().subscribe { this.buttonIsEnabled.onNext(it) } + .addToDisposable(disposables) + this.vm.outputs.buttonIsGone().subscribe { this.buttonIsGone.onNext(it) } + .addToDisposable(disposables) + this.vm.outputs.conversion().subscribe { this.conversion.onNext(it) } + .addToDisposable(disposables) + this.vm.outputs.conversionIsGone().subscribe { this.conversionIsGone.onNext(it) } + .addToDisposable(disposables) + this.vm.outputs.descriptionForNoReward() + .subscribe { this.descriptionForNoReward.onNext(it) }.addToDisposable(disposables) + this.vm.outputs.descriptionForReward().subscribe { this.descriptionForReward.onNext(it) } + .addToDisposable(disposables) + this.vm.outputs.descriptionIsGone().subscribe { this.descriptionIsGone.onNext(it) } + .addToDisposable(disposables) + this.vm.outputs.endDateSectionIsGone().subscribe { this.endDateSectionIsGone.onNext(it) } + .addToDisposable(disposables) + this.vm.outputs.estimatedDelivery().subscribe { this.estimatedDelivery.onNext(it) } + .addToDisposable(disposables) + this.vm.outputs.estimatedDeliveryIsGone() + .subscribe { this.estimatedDeliveryIsGone.onNext(it) }.addToDisposable(disposables) + this.vm.outputs.remaining().subscribe { this.remaining.onNext(it) } + .addToDisposable(disposables) + this.vm.outputs.remainingIsGone().subscribe { this.remainingIsGone.onNext(it) } + .addToDisposable(disposables) + this.vm.outputs.limitContainerIsGone().subscribe { this.limitContainerIsGone.onNext(it) } + .addToDisposable(disposables) + this.vm.outputs.minimumAmountTitle().map { it.toString() } + .subscribe { this.minimumAmountTitle.onNext(it) }.addToDisposable(disposables) + this.vm.outputs.reward().subscribe { this.reward.onNext(it) }.addToDisposable(disposables) + this.vm.outputs.rewardItems().subscribe { this.rewardItems.onNext(it) } + .addToDisposable(disposables) + this.vm.outputs.rewardItemsAreGone().subscribe { this.rewardItemsAreGone.onNext(it) } + .addToDisposable(disposables) + this.vm.outputs.shippingSummary().subscribe { this.shippingSummary.onNext(it) } + .addToDisposable(disposables) + this.vm.outputs.shippingSummaryIsGone().subscribe { this.shippingSummaryIsGone.onNext(it) } + .addToDisposable(disposables) + this.vm.outputs.showFragment().subscribe { this.showPledgeFragment.onNext(it) } + .addToDisposable(disposables) + this.vm.outputs.titleForNoReward().subscribe { this.titleForNoReward.onNext(it) } + .addToDisposable(disposables) + this.vm.outputs.titleForReward().subscribe { this.titleForReward.onNext(it) } + .addToDisposable(disposables) + this.vm.outputs.titleIsGone().subscribe { this.titleIsGone.onNext(it) } + .addToDisposable(disposables) + this.vm.outputs.hasAddOnsAvailable().subscribe { this.hasAddonsAvailable.onNext(it) } + .addToDisposable(disposables) + this.vm.outputs.selectedRewardTagIsGone() + .subscribe { this.selectedRewardTagIsGone.onNext(it) }.addToDisposable(disposables) + this.vm.outputs.localPickUpName().subscribe { this.localPickUpName.onNext(it) } + .addToDisposable(disposables) + this.vm.outputs.localPickUpIsGone().subscribe { this.localPickUpIsGone.onNext(it) } + .addToDisposable(disposables) } @Test @@ -143,7 +176,10 @@ class RewardViewHolderViewModelTest : KSRobolectricTestCase() { fun testBackersCount_whenNoReward() { setUpEnvironment(environment()) - this.vm.inputs.configureWith(ProjectDataFactory.project(ProjectFactory.project()), RewardFactory.noReward()) + this.vm.inputs.configureWith( + ProjectDataFactory.project(ProjectFactory.project()), + RewardFactory.noReward() + ) this.backersCount.assertNoValues() this.backersCountIsGone.assertValue(true) @@ -153,7 +189,10 @@ class RewardViewHolderViewModelTest : KSRobolectricTestCase() { fun testButtonUIOutputs_whenProjectIsLiveAndUnbacked_availableReward() { setUpEnvironment(environment()) - this.vm.inputs.configureWith(ProjectDataFactory.project(ProjectFactory.project()), RewardFactory.reward()) + this.vm.inputs.configureWith( + ProjectDataFactory.project(ProjectFactory.project()), + RewardFactory.reward() + ) this.buttonIsGone.assertValue(false) this.buttonCTA.assertValue(R.string.Select) } @@ -162,17 +201,23 @@ class RewardViewHolderViewModelTest : KSRobolectricTestCase() { fun testButtonUIOutputs_whenProjectIsLiveAndUnbacked_noReward() { setUpEnvironment(environment()) - this.vm.inputs.configureWith(ProjectDataFactory.project(ProjectFactory.project()), RewardFactory.noReward()) + this.vm.inputs.configureWith( + ProjectDataFactory.project(ProjectFactory.project()), + RewardFactory.noReward() + ) this.buttonIsGone.assertValue(false) this.buttonIsEnabled.assertValue(true) - this.buttonCTA.assertValuesAndClear(R.string.Select) + this.buttonCTA.assertValue(R.string.Select) } @Test fun testButtonUIOutputs_whenProjectIsLiveAndUnbacked_soldOutReward() { setUpEnvironment(environment()) - this.vm.inputs.configureWith(ProjectDataFactory.project(ProjectFactory.project()), RewardFactory.limitReached()) + this.vm.inputs.configureWith( + ProjectDataFactory.project(ProjectFactory.project()), + RewardFactory.limitReached() + ) this.buttonIsGone.assertValue(false) this.buttonIsEnabled.assertValue(false) this.buttonCTA.assertValue(R.string.No_longer_available) @@ -247,9 +292,12 @@ class RewardViewHolderViewModelTest : KSRobolectricTestCase() { fun testButtonUIOutputs_whenProjectIsLiveAndUnbacked_expiredReward() { setUpEnvironment(environment()) - this.vm.inputs.configureWith(ProjectDataFactory.project(ProjectFactory.project()), RewardFactory.ended()) + this.vm.inputs.configureWith( + ProjectDataFactory.project(ProjectFactory.project()), + RewardFactory.ended() + ) this.buttonIsGone.assertValue(false) - this.buttonCTA.assertValuesAndClear(R.string.No_longer_available) + this.buttonCTA.assertValue(R.string.No_longer_available) } @Test @@ -258,9 +306,12 @@ class RewardViewHolderViewModelTest : KSRobolectricTestCase() { val backedLiveProject = ProjectFactory.backedProjectWithAddOns() val rw = backedLiveProject.backing()?.reward() - this.vm.inputs.configureWith(ProjectDataFactory.project(backedLiveProject), requireNotNull(rw)) + this.vm.inputs.configureWith( + ProjectDataFactory.project(backedLiveProject), + requireNotNull(rw) + ) this.buttonIsGone.assertValue(false) - this.buttonCTA.assertValuesAndClear(R.string.Continue) + this.buttonCTA.assertValue(R.string.Continue) } @Test @@ -271,7 +322,7 @@ class RewardViewHolderViewModelTest : KSRobolectricTestCase() { val rw = RewardFactory.reward() this.vm.inputs.configureWith(ProjectDataFactory.project(backedLiveProject), rw) this.buttonIsGone.assertValue(false) - this.buttonCTA.assertValuesAndClear(R.string.Select) + this.buttonCTA.assertValue(R.string.Select) } @Test @@ -280,7 +331,10 @@ class RewardViewHolderViewModelTest : KSRobolectricTestCase() { val backedLiveProject = ProjectFactory.backedProjectWithAddOns() val rw = backedLiveProject.backing()?.reward() - this.vm.inputs.configureWith(ProjectDataFactory.project(backedLiveProject), requireNotNull(rw)) + this.vm.inputs.configureWith( + ProjectDataFactory.project(backedLiveProject), + requireNotNull(rw) + ) this.buttonIsGone.assertValue(false) this.buttonIsEnabled.assertValue(true) } @@ -315,7 +369,10 @@ class RewardViewHolderViewModelTest : KSRobolectricTestCase() { val backedLiveProject = ProjectFactory.backedProjectWithRewardAndAddOnsLimitReached() val rw = backedLiveProject.backing()?.reward() - this.vm.inputs.configureWith(ProjectDataFactory.project(backedLiveProject), requireNotNull(rw)) + this.vm.inputs.configureWith( + ProjectDataFactory.project(backedLiveProject), + requireNotNull(rw) + ) this.buttonIsGone.assertValue(false) this.buttonIsEnabled.assertValue(true) } @@ -331,7 +388,7 @@ class RewardViewHolderViewModelTest : KSRobolectricTestCase() { ?: RewardFactory.reward() ) this.buttonIsGone.assertValue(false) - this.buttonCTA.assertValuesAndClear(R.string.Selected) + this.buttonCTA.assertValue(R.string.Selected) } @Test @@ -352,7 +409,10 @@ class RewardViewHolderViewModelTest : KSRobolectricTestCase() { setUpEnvironment(environment()) val backedLiveProject = ProjectFactory.backedProject() - this.vm.inputs.configureWith(ProjectDataFactory.project(backedLiveProject), RewardFactory.reward()) + this.vm.inputs.configureWith( + ProjectDataFactory.project(backedLiveProject), + RewardFactory.reward() + ) this.selectedRewardTagIsGone.assertValue(true) } @@ -369,16 +429,22 @@ class RewardViewHolderViewModelTest : KSRobolectricTestCase() { fun testButtonUIOutputs_whenProjectIsLiveAndBacked_availableReward() { setUpEnvironment(environment()) - this.vm.inputs.configureWith(ProjectDataFactory.project(ProjectFactory.backedProject()), RewardFactory.reward()) + this.vm.inputs.configureWith( + ProjectDataFactory.project(ProjectFactory.backedProject()), + RewardFactory.reward() + ) this.buttonIsGone.assertValue(false) - this.buttonCTA.assertValuesAndClear(R.string.Select) + this.buttonCTA.assertValue(R.string.Select) } @Test fun testButtonUIOutputs_whenProjectIsLiveAndBacked_soldOutReward() { setUpEnvironment(environment()) - this.vm.inputs.configureWith(ProjectDataFactory.project(ProjectFactory.backedProject()), RewardFactory.limitReached()) + this.vm.inputs.configureWith( + ProjectDataFactory.project(ProjectFactory.backedProject()), + RewardFactory.limitReached() + ) this.buttonIsGone.assertValue(false) this.buttonCTA.assertValue(R.string.No_longer_available) } @@ -387,9 +453,12 @@ class RewardViewHolderViewModelTest : KSRobolectricTestCase() { fun testButtonUIOutputs_whenProjectIsLiveAndBacked_soldOutReward_andUnbacked() { setUpEnvironment(environment()) - this.vm.inputs.configureWith(ProjectDataFactory.project(ProjectFactory.backedProject()), RewardFactory.ended()) + this.vm.inputs.configureWith( + ProjectDataFactory.project(ProjectFactory.backedProject()), + RewardFactory.ended() + ) this.buttonIsGone.assertValue(false) - this.buttonCTA.assertValuesAndClear(R.string.No_longer_available) + this.buttonCTA.assertValue(R.string.No_longer_available) } @Test @@ -400,9 +469,12 @@ class RewardViewHolderViewModelTest : KSRobolectricTestCase() { .toBuilder() .state(Project.STATE_SUCCESSFUL) .build() - this.vm.inputs.configureWith(ProjectDataFactory.project(successfulProject), RewardFactory.reward()) + this.vm.inputs.configureWith( + ProjectDataFactory.project(successfulProject), + RewardFactory.reward() + ) this.buttonIsGone.assertValue(true) - this.buttonCTA.assertValuesAndClear(R.string.No_longer_available) + this.buttonCTA.assertValue(R.string.No_longer_available) } @Test @@ -413,9 +485,12 @@ class RewardViewHolderViewModelTest : KSRobolectricTestCase() { .toBuilder() .state(Project.STATE_SUCCESSFUL) .build() - this.vm.inputs.configureWith(ProjectDataFactory.project(backedSuccessfulProject), RewardFactory.reward()) + this.vm.inputs.configureWith( + ProjectDataFactory.project(backedSuccessfulProject), + RewardFactory.reward() + ) this.buttonIsGone.assertValue(true) - this.buttonCTA.assertValuesAndClear(R.string.No_longer_available) + this.buttonCTA.assertValue(R.string.No_longer_available) } @Test @@ -426,9 +501,12 @@ class RewardViewHolderViewModelTest : KSRobolectricTestCase() { .state(Project.STATE_SUCCESSFUL) .build() - this.vm.inputs.configureWith(ProjectDataFactory.project(backedSuccessfulProject), RewardFactory.noReward()) + this.vm.inputs.configureWith( + ProjectDataFactory.project(backedSuccessfulProject), + RewardFactory.noReward() + ) this.buttonIsGone.assertValue(true) - this.buttonCTA.assertValuesAndClear(R.string.No_longer_available) + this.buttonCTA.assertValue(R.string.No_longer_available) } @Test @@ -456,7 +534,10 @@ class RewardViewHolderViewModelTest : KSRobolectricTestCase() { .state(Project.STATE_SUCCESSFUL) .build() - this.vm.inputs.configureWith(ProjectDataFactory.project(backedNoRewardSuccessfulProject), RewardFactory.noReward()) + this.vm.inputs.configureWith( + ProjectDataFactory.project(backedNoRewardSuccessfulProject), + RewardFactory.noReward() + ) this.buttonIsGone.assertValue(false) this.buttonCTA.assertValue(R.string.Selected) } @@ -472,7 +553,7 @@ class RewardViewHolderViewModelTest : KSRobolectricTestCase() { val environment = environment() .toBuilder() - .currentUser(MockCurrentUser(creator)) + .currentUserV2(MockCurrentUserV2(creator)) .build() setUpEnvironment(environment) @@ -492,7 +573,7 @@ class RewardViewHolderViewModelTest : KSRobolectricTestCase() { val environment = environment() .toBuilder() - .currentUser(MockCurrentUser(creator)) + .currentUserV2(MockCurrentUserV2(creator)) .build() setUpEnvironment(environment) @@ -540,7 +621,13 @@ class RewardViewHolderViewModelTest : KSRobolectricTestCase() { .build() this.vm.inputs.configureWith(ProjectDataFactory.project(caProject), reward) - this.conversion.assertValue(expectedConvertedCurrency(environment, caProject, convertedMinimum)) + this.conversion.assertValue( + expectedConvertedCurrency( + environment, + caProject, + convertedMinimum + ) + ) this.conversionIsGone.assertValue(false) } @@ -549,7 +636,10 @@ class RewardViewHolderViewModelTest : KSRobolectricTestCase() { setUpEnvironment(environment()) // Reward with empty description - this.vm.inputs.configureWith(ProjectDataFactory.project(ProjectFactory.project()), RewardFactory.noDescription()) + this.vm.inputs.configureWith( + ProjectDataFactory.project(ProjectFactory.project()), + RewardFactory.noDescription() + ) this.descriptionForNoReward.assertNoValues() this.descriptionForReward.assertValue("") this.descriptionIsGone.assertValue(true) @@ -566,7 +656,7 @@ class RewardViewHolderViewModelTest : KSRobolectricTestCase() { .build() this.vm.inputs.configureWith(ProjectDataFactory.project(ProjectFactory.project()), reward) this.descriptionForNoReward.assertNoValues() - this.descriptionForReward.assertValue(null) + this.descriptionForReward.assertNoValues() this.descriptionIsGone.assertValue(true) } @@ -587,7 +677,10 @@ class RewardViewHolderViewModelTest : KSRobolectricTestCase() { setUpEnvironment(environment()) // No reward - this.vm.inputs.configureWith(ProjectDataFactory.project(ProjectFactory.project()), RewardFactory.noReward()) + this.vm.inputs.configureWith( + ProjectDataFactory.project(ProjectFactory.project()), + RewardFactory.noReward() + ) this.descriptionForNoReward.assertValue(R.string.Back_it_because_you_believe_in_it) this.descriptionForReward.assertNoValues() this.descriptionIsGone.assertValue(false) @@ -606,7 +699,10 @@ class RewardViewHolderViewModelTest : KSRobolectricTestCase() { .toBuilder() .backing(noRewardBacking) .build() - this.vm.inputs.configureWith(ProjectDataFactory.project(backedProject), RewardFactory.noReward()) + this.vm.inputs.configureWith( + ProjectDataFactory.project(backedProject), + RewardFactory.noReward() + ) this.descriptionForNoReward.assertValue(R.string.Thanks_for_bringing_this_project_one_step_closer_to_becoming_a_reality) this.descriptionForReward.assertNoValues() this.descriptionIsGone.assertValue(false) @@ -636,7 +732,10 @@ class RewardViewHolderViewModelTest : KSRobolectricTestCase() { this.vm.inputs.configureWith(ProjectDataFactory.project(project), expiringReward) this.endDateSectionIsGone.assertValues(true, false) - this.vm.inputs.configureWith(ProjectDataFactory.project(ProjectFactory.successfulProject()), expiringReward) + this.vm.inputs.configureWith( + ProjectDataFactory.project(ProjectFactory.successfulProject()), + expiringReward + ) this.endDateSectionIsGone.assertValues(true, false, true) } @@ -672,7 +771,10 @@ class RewardViewHolderViewModelTest : KSRobolectricTestCase() { fun testEstimatedDelivery_whenNoReward() { setUpEnvironment(environment()) - this.vm.inputs.configureWith(ProjectDataFactory.project(ProjectFactory.project()), RewardFactory.noReward()) + this.vm.inputs.configureWith( + ProjectDataFactory.project(ProjectFactory.project()), + RewardFactory.noReward() + ) this.estimatedDelivery.assertNoValues() this.estimatedDeliveryIsGone.assertValue(true) @@ -728,7 +830,12 @@ class RewardViewHolderViewModelTest : KSRobolectricTestCase() { val liveProject = ProjectFactory.project().toBuilder().sendMetaCapiEvents(true).build() var sharedPreferences: SharedPreferences = Mockito.mock(SharedPreferences::class.java) - Mockito.`when`(sharedPreferences.getBoolean(SharedPreferenceKey.CONSENT_MANAGEMENT_PREFERENCE, false)).thenReturn(false) + Mockito.`when`( + sharedPreferences.getBoolean( + SharedPreferenceKey.CONSENT_MANAGEMENT_PREFERENCE, + false + ) + ).thenReturn(false) setUpEnvironment( environment().toBuilder() @@ -766,27 +873,42 @@ class RewardViewHolderViewModelTest : KSRobolectricTestCase() { setUpEnvironment(environment()) // A reward from a live project that is available should be enabled. - this.vm.inputs.configureWith(ProjectDataFactory.project(ProjectFactory.project()), RewardFactory.reward()) + this.vm.inputs.configureWith( + ProjectDataFactory.project(ProjectFactory.project()), + RewardFactory.reward() + ) this.buttonIsEnabled.assertValue(true) // A reward from a successful project should not be enabled. - this.vm.inputs.configureWith(ProjectDataFactory.project(ProjectFactory.successfulProject()), RewardFactory.reward()) + this.vm.inputs.configureWith( + ProjectDataFactory.project(ProjectFactory.successfulProject()), + RewardFactory.reward() + ) this.buttonIsEnabled.assertValues(true, false) // A backed reward from a live project should not be enabled. val backedLiveProject = ProjectFactory.backedProject() - this.vm.inputs.configureWith(ProjectDataFactory.project(backedLiveProject), backedLiveProject.backing()?.reward()!!) + this.vm.inputs.configureWith( + ProjectDataFactory.project(backedLiveProject), + backedLiveProject.backing()?.reward()!! + ) this.buttonIsEnabled.assertValues(true, false, true, false) // A backed reward from an ended project should not be enabled. val backedSuccessfulProject = ProjectFactory.backedProject().toBuilder() .state(Project.STATE_SUCCESSFUL) .build() - this.vm.inputs.configureWith(ProjectDataFactory.project(backedSuccessfulProject), backedSuccessfulProject.backing()?.reward()!!) + this.vm.inputs.configureWith( + ProjectDataFactory.project(backedSuccessfulProject), + backedSuccessfulProject.backing()?.reward()!! + ) this.buttonIsEnabled.assertValues(true, false, true, false) // A reward with its limit reached should not be enabled. - this.vm.inputs.configureWith(ProjectDataFactory.project(ProjectFactory.project()), RewardFactory.limitReached()) + this.vm.inputs.configureWith( + ProjectDataFactory.project(ProjectFactory.project()), + RewardFactory.limitReached() + ) this.buttonIsEnabled.assertValues(true, false, true, false, true, false) } @@ -813,7 +935,10 @@ class RewardViewHolderViewModelTest : KSRobolectricTestCase() { val project = ProjectFactory.project() setUpEnvironment(environment()) - this.vm.inputs.configureWith(ProjectDataFactory.project(project), RewardFactory.endingSoon()) + this.vm.inputs.configureWith( + ProjectDataFactory.project(project), + RewardFactory.endingSoon() + ) this.limitContainerIsGone.assertValue(false) } @@ -822,7 +947,10 @@ class RewardViewHolderViewModelTest : KSRobolectricTestCase() { val project = ProjectFactory.project() setUpEnvironment(environment()) - this.vm.inputs.configureWith(ProjectDataFactory.project(project), RewardFactory.rewardWithShipping()) + this.vm.inputs.configureWith( + ProjectDataFactory.project(project), + RewardFactory.rewardWithShipping() + ) this.limitContainerIsGone.assertValue(false) } @@ -831,7 +959,10 @@ class RewardViewHolderViewModelTest : KSRobolectricTestCase() { val project = ProjectFactory.successfulProject() setUpEnvironment(environment()) - this.vm.inputs.configureWith(ProjectDataFactory.project(project), RewardFactory.rewardWithShipping()) + this.vm.inputs.configureWith( + ProjectDataFactory.project(project), + RewardFactory.rewardWithShipping() + ) this.limitContainerIsGone.assertValue(true) } @@ -864,14 +995,20 @@ class RewardViewHolderViewModelTest : KSRobolectricTestCase() { // When reward is limited, quantity should be shown. this.vm.inputs.configureWith(ProjectDataFactory.project(project), RewardFactory.limited()) - this.remaining.assertValue("5") + this.remaining.assertValue(5) this.remainingIsGone.assertValue(false) // When reward's limit has been reached, don't show quantity. - this.vm.inputs.configureWith(ProjectDataFactory.project(project), RewardFactory.limitReached()) + this.vm.inputs.configureWith( + ProjectDataFactory.project(project), + RewardFactory.limitReached() + ) this.remainingIsGone.assertValues(false, true) - this.vm.inputs.configureWith(ProjectDataFactory.project(ProjectFactory.successfulProject()), RewardFactory.limitReached()) + this.vm.inputs.configureWith( + ProjectDataFactory.project(ProjectFactory.successfulProject()), + RewardFactory.limitReached() + ) this.remainingIsGone.assertValues(false, true) // When reward has no limit, don't show quantity (distinct until changed). @@ -931,7 +1068,8 @@ class RewardViewHolderViewModelTest : KSRobolectricTestCase() { val project = ProjectFactory.project() setUpEnvironment(environment()) - val rewardWithShipping = RewardFactory.singleLocationShipping(LocationFactory.nigeria().displayableName()) + val rewardWithShipping = + RewardFactory.singleLocationShipping(LocationFactory.nigeria().displayableName()) this.vm.inputs.configureWith(ProjectDataFactory.project(project), rewardWithShipping) this.shippingSummary.assertValue(Pair(R.string.location_name_only, "Nigeria")) this.shippingSummaryIsGone.assertValues(false) @@ -970,9 +1108,12 @@ class RewardViewHolderViewModelTest : KSRobolectricTestCase() { val rewardWithNoTitle = RewardFactory.reward().toBuilder() .title(null) .build() - this.vm.inputs.configureWith(ProjectDataFactory.project(ProjectFactory.project()), rewardWithNoTitle) + this.vm.inputs.configureWith( + ProjectDataFactory.project(ProjectFactory.project()), + rewardWithNoTitle + ) this.titleIsGone.assertValue(true) - this.titleForReward.assertValue(null) + this.titleForReward.assertNoValues() this.titleForNoReward.assertNoValues() } @@ -980,7 +1121,10 @@ class RewardViewHolderViewModelTest : KSRobolectricTestCase() { fun testTitleOutputs_whenReward_hasTitle() { setUpEnvironment(environment()) - this.vm.inputs.configureWith(ProjectDataFactory.project(ProjectFactory.project()), RewardFactory.reward()) + this.vm.inputs.configureWith( + ProjectDataFactory.project(ProjectFactory.project()), + RewardFactory.reward() + ) this.titleIsGone.assertValue(false) this.titleForReward.assertValue("Digital Bundle") this.titleForNoReward.assertNoValues() @@ -990,7 +1134,10 @@ class RewardViewHolderViewModelTest : KSRobolectricTestCase() { fun testTitleOutputs_whenNoReward() { setUpEnvironment(environment()) - this.vm.inputs.configureWith(ProjectDataFactory.project(ProjectFactory.project()), RewardFactory.noReward()) + this.vm.inputs.configureWith( + ProjectDataFactory.project(ProjectFactory.project()), + RewardFactory.noReward() + ) this.titleIsGone.assertValues(false) this.titleForReward.assertNoValues() this.titleForNoReward.assertValue(R.string.Pledge_without_a_reward) @@ -1000,7 +1147,10 @@ class RewardViewHolderViewModelTest : KSRobolectricTestCase() { fun testReward_HasAddOnsAvailable() { setUpEnvironment(environment()) - this.vm.inputs.configureWith(ProjectDataFactory.project(ProjectFactory.project()), RewardFactory.reward().toBuilder().hasAddons(true).build()) + this.vm.inputs.configureWith( + ProjectDataFactory.project(ProjectFactory.project()), + RewardFactory.reward().toBuilder().hasAddons(true).build() + ) this.hasAddonsAvailable.assertValue(true) } @@ -1008,7 +1158,10 @@ class RewardViewHolderViewModelTest : KSRobolectricTestCase() { fun testReward_No_HasAddOnsAvailable() { setUpEnvironment(environment()) - this.vm.inputs.configureWith(ProjectDataFactory.project(ProjectFactory.project()), RewardFactory.reward().toBuilder().hasAddons(false).build()) + this.vm.inputs.configureWith( + ProjectDataFactory.project(ProjectFactory.project()), + RewardFactory.reward().toBuilder().hasAddons(false).build() + ) this.hasAddonsAvailable.assertValue(false) } @@ -1025,7 +1178,10 @@ class RewardViewHolderViewModelTest : KSRobolectricTestCase() { .toBuilder() .backing(noRewardBacking) .build() - this.vm.inputs.configureWith(ProjectDataFactory.project(backedProject), RewardFactory.noReward()) + this.vm.inputs.configureWith( + ProjectDataFactory.project(backedProject), + RewardFactory.noReward() + ) this.titleIsGone.assertValue(false) this.titleForReward.assertNoValues() this.titleForNoReward.assertValue(R.string.You_pledged_without_a_reward) @@ -1061,9 +1217,28 @@ class RewardViewHolderViewModelTest : KSRobolectricTestCase() { this.localPickUpIsGone.assertNoValues() } - private fun expectedConvertedCurrency(environment: Environment, project: Project, amount: Double): String = - requireNotNull(environment.ksCurrency()).format(amount, project, true, RoundingMode.HALF_UP, true) + private fun expectedConvertedCurrency( + environment: Environment, + project: Project, + amount: Double + ): String = + requireNotNull(environment.ksCurrency()).format( + amount, + project, + true, + RoundingMode.HALF_UP, + true + ) - private fun expectedCurrency(environment: Environment, project: Project, amount: Double): String = + private fun expectedCurrency( + environment: Environment, + project: Project, + amount: Double + ): String = requireNotNull(environment.ksCurrency()).format(amount, project, RoundingMode.HALF_UP) + + @After + fun clear() { + disposables.clear() + } }