Skip to content

Instantly share code, notes, and snippets.

@Judrummer
Last active November 17, 2019 05:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Judrummer/bcceab5498251ab2a8dfd90ea921c99e to your computer and use it in GitHub Desktop.
Save Judrummer/bcceab5498251ab2a8dfd90ea921c99e to your computer and use it in GitHub Desktop.
Kotlin Multiplatform Workshop
package com.kithub.core.data
import android.content.Context.MODE_PRIVATE
import androidx.core.content.edit
import com.kithub.core.AndroidAppContext
actual class UserPreferenceImpl : UserPreference {
private val sharedPreference
get() = AndroidAppContext.app.getSharedPreferences("UserPreference", MODE_PRIVATE)
override var username: String
get() = sharedPreference.getString("username", "").orEmpty()
set(value) {
sharedPreference.edit {
putString("username", value)
}
}
}
package com.kithub.core.data
import platform.Foundation.NSUserDefaults
import platform.Foundation.setValue
actual class UserPreferenceImpl : UserPreference {
private val userDefaults get() = NSUserDefaults(suiteName = "UserPreference")
override var username: String
get() = userDefaults.stringForKey("username").orEmpty()
set(value) {
userDefaults.setValue(value, forKey = "username")
}
}
package com.kithub.core.viewmodel.repodetail
import com.kithub.core.data.GithubApi
import com.kithub.core.data.UserPreference
import com.kithub.core.viewmodel.KViewModel
import kotlinx.coroutines.launch
data class RepoDetailViewState(
val loading: Boolean = true,
val name: String = "",
val descriptionText: String = "",
val language: String = "",
val sshUrl: String = "",
val starCount: Int = 0,
val contributors: List<ContributorItem> = emptyList()
)
data class ContributorItem(
val name: String = "",
val contributions: Int = 0,
val avatarUrl: String = ""
)
class RepoDetailViewModel(
private val userPreference: UserPreference,
private val githubApi: GithubApi
) : KViewModel<RepoDetailViewState>(RepoDetailViewState()) {
fun refresh(repoName: String) {
launch {
try {
setState { copy(loading = true) }
val contributors = githubApi.getContributors(userPreference.username, repoName)
val repoDetail = githubApi.getUserRepoDetail(userPreference.username, repoName)
setState {
copy(loading = false,
name = repoDetail.name.orEmpty(),
descriptionText = repoDetail.description.orEmpty(),
language = repoDetail.language.orEmpty(),
starCount = repoDetail.stargazersCount ?: 0,
sshUrl = repoDetail.sshUrl.orEmpty(),
contributors = contributors.map {
ContributorItem(
name = it.login.orEmpty(),
avatarUrl = it.avatarUrl.orEmpty(),
contributions = it.contributions ?: 0
)
})
}
} catch (e: Throwable) {
setState { copy(loading = false) }
setError(e)
}
}
}
}
package com.kithub.core.viewmodel.repodetail
import com.kithub.core.assertState
import com.kithub.core.common.coroutine.ApplicationDispatcher
import com.kithub.core.common.coroutine.TestDispatcher
import com.kithub.core.common.kreactive.test
import com.kithub.core.data.ContributorEntity
import com.kithub.core.data.GithubApi
import com.kithub.core.data.RepoDetailEntity
import com.kithub.core.data.UserPreference
import io.mockk.coEvery
import io.mockk.every
import io.mockk.mockk
import kotlinx.coroutines.InternalCoroutinesApi
import org.junit.Test
import kotlin.test.assertTrue
@UseExperimental(InternalCoroutinesApi::class)
class RepoDetailViewModelTest {
val userPreference = mockk<UserPreference>(relaxed = true)
val githubApi = mockk<GithubApi>(relaxed = true)
val viewModel = RepoDetailViewModel(userPreference, githubApi)
val state = viewModel.state.test()
val error = viewModel.error.test()
init {
ApplicationDispatcher = TestDispatcher
}
@Test
fun refresh() {
val repoDetailEntity = RepoDetailEntity(
name = "ktor",
description = "http library",
language = "kotlin",
sshUrl = "ssh.url",
stargazersCount = 2000
)
val contributorEntities = listOf(
ContributorEntity(
login = "alice",
contributions = 120,
avatarUrl = "avatar.url"
)
)
every { userPreference.username } returns "kotlin"
coEvery { githubApi.getContributors("kotlin", "ktor") } returns contributorEntities
coEvery { githubApi.getUserRepoDetail("kotlin", "ktor") } returns repoDetailEntity
viewModel.refresh("ktor")
assertTrue { error.values.isEmpty() }
state.assertState(
RepoDetailViewState(),
{ copy(loading = true) },
{
copy(
loading = false,
name = "ktor",
descriptionText = "http library",
starCount = 2000,
sshUrl = "ssh.url",
language = "kotlin",
contributors = listOf(
ContributorItem(
name = "alice",
contributions = 120,
avatarUrl = "avatar.url"
)
)
)
}
)
}
}
package com.kithub.core.viewmodel.repolist
import com.kithub.core.data.GithubApi
import com.kithub.core.data.UserPreference
import com.kithub.core.viewmodel.KViewModel
import kotlinx.coroutines.launch
data class RepoListViewState(
val loading: Boolean = true,
val username: String = "",
val displayName: String = "",
val avatarUrl: String = "",
val repos: List<RepoItem> = emptyList()
)
data class RepoItem(
val name: String,
val descriptionText: String, // Don't use val description: String because iOS can't see it
val starCount: Int
)
class RepoListViewModel(
private val userPreference: UserPreference,
private val githubApi: GithubApi
) : KViewModel<RepoListViewState>(RepoListViewState()) {
fun refresh() {
launch {
try {
setState { copy(loading = true) }
val userEntity = githubApi.getUser(username = userPreference.username)
val repoEntities = githubApi.getUserRepos(username = userPreference.username)
setState {
copy(
loading = false,
username = userEntity.login ?: "",
displayName = userEntity.name.orEmpty(),
avatarUrl = userEntity.avatarUrl.orEmpty(),
repos = repoEntities.map {
RepoItem(
name = it.name.orEmpty(),
descriptionText = it.description.orEmpty(),
starCount = it.stargazersCount ?: 0
)
}
)
}
} catch (e: Throwable) {
setState { copy(loading = false) }
setError(e)
}
}
}
fun logout() {
userPreference.username = ""
}
}
package com.kithub.core.viewmodel.repolist
import com.kithub.core.assertState
import com.kithub.core.common.coroutine.ApplicationDispatcher
import com.kithub.core.common.coroutine.TestDispatcher
import com.kithub.core.common.kreactive.test
import com.kithub.core.data.GithubApi
import com.kithub.core.data.RepoEntity
import com.kithub.core.data.UserEntity
import com.kithub.core.data.UserPreference
import io.mockk.coEvery
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import kotlinx.coroutines.InternalCoroutinesApi
import kotlin.test.Test
import kotlin.test.assertTrue
@UseExperimental(InternalCoroutinesApi::class)
class RepoListViewModelTest {
val userPreference = mockk<UserPreference>(relaxed = true)
val githubApi = mockk<GithubApi>(relaxed = true)
val viewModel = RepoListViewModel(userPreference, githubApi)
val state = viewModel.state.test()
val error = viewModel.error.test()
init {
ApplicationDispatcher = TestDispatcher
}
@Test
fun refresh() {
val userEntity = UserEntity(
login = "kotlin",
name = "Kotlin Lang",
avatarUrl = "www.google.com"
)
val repoEntities = listOf(
RepoEntity(
name = "ktor",
description = "Http library",
stargazersCount = 9999
)
)
every { userPreference.username } returns "kotlin"
coEvery { githubApi.getUser("kotlin") } returns userEntity
coEvery { githubApi.getUserRepos("kotlin") } returns repoEntities
viewModel.refresh()
assertTrue { error.values.isEmpty() }
state.assertState(RepoListViewState(),
{ copy(loading = true) },
{
copy(
loading = false,
username = "kotlin",
avatarUrl = "www.google.com",
displayName = "Kotlin Lang",
repos = listOf(
RepoItem(
name = "ktor",
descriptionText = "Http library",
starCount = 9999
)
)
)
}
)
}
@Test
fun logout() {
viewModel.logout()
verify { userPreference.username = "" }
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment