Add some ProfileFragmentViewModel logic and tests

This commit is contained in:
Ammar Githam 2021-06-13 20:52:03 +09:00
parent 1d9eb43442
commit 70ffac3025
5 changed files with 122 additions and 17 deletions

View File

@ -242,9 +242,10 @@ dependencies {
testImplementation 'org.junit.jupiter:junit-jupiter:5.7.2'
testImplementation "androidx.test.ext:junit-ktx:1.1.2"
testImplementation "androidx.test:core-ktx:1.3.0"
testImplementation "androidx.arch.core:core-testing:2.1.0"
testImplementation "org.robolectric:robolectric:4.5.1"
androidTestImplementation 'org.junit.jupiter:junit-jupiter:5.7.1'
androidTestImplementation 'org.junit.jupiter:junit-jupiter:5.7.2'
androidTestImplementation 'androidx.test:core:1.3.0'
androidTestImplementation 'com.android.support:support-annotations:28.0.0'
androidTestImplementation 'com.android.support.test:runner:1.0.2'

View File

@ -382,7 +382,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
shouldRefresh = false;
return root;
}
// appStateViewModel.getCurrentUserLiveData().observe(getViewLifecycleOwner(), user -> viewModel.setCurrentUser(user));
appStateViewModel.getCurrentUserLiveData().observe(getViewLifecycleOwner(), user -> viewModel.setCurrentUser(user));
binding = FragmentProfileBinding.inflate(inflater, container, false);
root = binding.getRoot();
profileDetailsBinding = binding.header;

View File

@ -1,13 +1,12 @@
package awais.instagrabber.viewmodels
import android.os.Bundle
import android.util.Log
import androidx.lifecycle.*
import androidx.savedstate.SavedStateRegistryOwner
import awais.instagrabber.db.repositories.AccountRepository
import awais.instagrabber.db.repositories.FavoriteRepository
import awais.instagrabber.models.Resource
import awais.instagrabber.repositories.responses.User
import awais.instagrabber.utils.extensions.TAG
import awais.instagrabber.webservices.*
class ProfileFragmentViewModel(
@ -20,17 +19,44 @@ class ProfileFragmentViewModel(
accountRepository: AccountRepository,
favoriteRepository: FavoriteRepository,
) : ViewModel() {
private val _profile = MutableLiveData<User?>()
val profile: LiveData<User?> = _profile
val username: LiveData<String> = Transformations.map(profile) { return@map it?.username ?: "" }
private val _profile = MutableLiveData<Resource<User?>>(Resource.loading(null))
private val _isLoggedIn = MutableLiveData(false)
var currentUser: User? = null
var isLoggedIn = false
get() = currentUser != null
private set
val profile: LiveData<Resource<User?>> = _profile
/**
* Username of profile without '`@`'
*/
val username: LiveData<String> = Transformations.map(profile) {
return@map when (it.status) {
Resource.Status.LOADING, Resource.Status.ERROR -> ""
Resource.Status.SUCCESS -> it.data?.username ?: ""
}
}
val isLoggedIn: LiveData<Boolean> = _isLoggedIn
var currentUser: Resource<User?>? = null
set(value) {
_isLoggedIn.postValue(value?.data != null)
// if no profile, and value is valid, set it as profile
val profileValue = profile.value
if (
profileValue?.status != Resource.Status.LOADING
&& profileValue?.data == null
&& value?.status == Resource.Status.SUCCESS
&& value.data != null
) {
_profile.postValue(Resource.success(value.data))
}
field = value
}
init {
Log.d(TAG, "${state.keys()} $userRepository $friendshipRepository $storiesRepository $mediaRepository")
// Log.d(TAG, "${state.keys()} $userRepository $friendshipRepository $storiesRepository $mediaRepository")
val usernameFromState = state.get<String?>("username")
if (usernameFromState.isNullOrBlank()) {
_profile.postValue(Resource.success(null))
}
}
}

View File

@ -0,0 +1,41 @@
package awais.instagrabber
import androidx.annotation.VisibleForTesting
import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
import java.util.concurrent.TimeoutException
@VisibleForTesting(otherwise = VisibleForTesting.NONE)
fun <T> LiveData<T>.getOrAwaitValue(
time: Long = 2,
timeUnit: TimeUnit = TimeUnit.SECONDS,
afterObserve: () -> Unit = {}
): T {
var data: T? = null
val latch = CountDownLatch(1)
val observer = object : Observer<T> {
override fun onChanged(o: T?) {
data = o
latch.countDown()
this@getOrAwaitValue.removeObserver(this)
}
}
this.observeForever(observer)
try {
afterObserve.invoke()
// Don't wait indefinitely if the LiveData is not set.
if (!latch.await(time, timeUnit)) {
throw TimeoutException("LiveData value was never set.")
}
} finally {
this.removeObserver(observer)
}
@Suppress("UNCHECKED_CAST")
return data as T
}

View File

@ -1,5 +1,6 @@
package awais.instagrabber.viewmodels
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import androidx.lifecycle.SavedStateHandle
import androidx.test.ext.junit.runners.AndroidJUnit4
import awais.instagrabber.common.*
@ -7,19 +8,49 @@ import awais.instagrabber.db.datasources.AccountDataSource
import awais.instagrabber.db.datasources.FavoriteDataSource
import awais.instagrabber.db.repositories.AccountRepository
import awais.instagrabber.db.repositories.FavoriteRepository
import awais.instagrabber.getOrAwaitValue
import awais.instagrabber.models.Resource
import awais.instagrabber.repositories.responses.User
import awais.instagrabber.webservices.*
import org.junit.Rule
import org.junit.Test
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertNull
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
internal class ProfileFragmentViewModelTest {
@get:Rule
var instantExecutorRule = InstantTaskExecutorRule()
@Test
fun testNoUsernameNoCurrentUser() {
val state = SavedStateHandle(
mutableMapOf<String, Any>(
"username" to ""
)
val accountDataSource = AccountDataSource(AccountDaoAdapter())
val viewModel = ProfileFragmentViewModel(
SavedStateHandle(),
UserRepository(UserServiceAdapter()),
FriendshipRepository(FriendshipServiceAdapter()),
StoriesRepository(StoriesServiceAdapter()),
MediaRepository(MediaServiceAdapter()),
GraphQLRepository(GraphQLServiceAdapter()),
AccountRepository(accountDataSource),
FavoriteRepository(FavoriteDataSource(FavoriteDaoAdapter()))
)
assertEquals(false, viewModel.isLoggedIn.getOrAwaitValue())
assertNull(viewModel.profile.getOrAwaitValue().data)
assertEquals("", viewModel.username.getOrAwaitValue())
viewModel.currentUser = Resource.success(null)
assertEquals(false, viewModel.isLoggedIn.getOrAwaitValue())
}
@Test
fun testNoUsernameWithCurrentUser() {
// val state = SavedStateHandle(
// mutableMapOf<String, Any?>(
// "username" to "test"
// )
// )
val userRepository = UserRepository(UserServiceAdapter())
val friendshipRepository = FriendshipRepository(FriendshipServiceAdapter())
val storiesRepository = StoriesRepository(StoriesServiceAdapter())
@ -29,7 +60,7 @@ internal class ProfileFragmentViewModelTest {
val accountRepository = AccountRepository(accountDataSource)
val favoriteRepository = FavoriteRepository(FavoriteDataSource(FavoriteDaoAdapter()))
val viewModel = ProfileFragmentViewModel(
state,
SavedStateHandle(),
userRepository,
friendshipRepository,
storiesRepository,
@ -38,5 +69,11 @@ internal class ProfileFragmentViewModelTest {
accountRepository,
favoriteRepository
)
assertEquals(false, viewModel.isLoggedIn.getOrAwaitValue())
assertNull(viewModel.profile.getOrAwaitValue().data)
val user = User()
viewModel.currentUser = Resource.success(user)
assertEquals(true, viewModel.isLoggedIn.getOrAwaitValue())
assertEquals(user, viewModel.profile.getOrAwaitValue().data)
}
}