mirror of
https://github.com/InsanusMokrassar/MicroUtils.git
synced 2025-09-06 08:40:19 +00:00
add tests for paged loading components
This commit is contained in:
42
common/compose/src/jvmTest/kotlin/LoadableComponentTests.kt
Normal file
42
common/compose/src/jvmTest/kotlin/LoadableComponentTests.kt
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.test.ExperimentalTestApi
|
||||||
|
import androidx.compose.ui.test.runComposeUiTest
|
||||||
|
import dev.inmo.micro_utils.common.compose.LoadableComponent
|
||||||
|
import dev.inmo.micro_utils.coroutines.SpecialMutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
|
import kotlinx.coroutines.flow.filter
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
|
import kotlinx.coroutines.flow.firstOrNull
|
||||||
|
import org.jetbrains.annotations.TestOnly
|
||||||
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
|
class LoadableComponentTests {
|
||||||
|
@OptIn(ExperimentalTestApi::class)
|
||||||
|
@Test
|
||||||
|
@TestOnly
|
||||||
|
fun testSimpleLoad() = runComposeUiTest {
|
||||||
|
val loadingFlow = SpecialMutableStateFlow<Int>(0)
|
||||||
|
val loadedFlow = SpecialMutableStateFlow<Int>(0)
|
||||||
|
setContent {
|
||||||
|
LoadableComponent<Int>({
|
||||||
|
loadingFlow.filter { it == 1 }.first()
|
||||||
|
}) {
|
||||||
|
assert(dataState.value.data == 1)
|
||||||
|
remember {
|
||||||
|
loadedFlow.value = 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
waitForIdle()
|
||||||
|
|
||||||
|
assertTrue(loadedFlow.value == 0)
|
||||||
|
|
||||||
|
loadingFlow.value = 1
|
||||||
|
|
||||||
|
waitForIdle()
|
||||||
|
|
||||||
|
assertTrue(loadedFlow.value == 2)
|
||||||
|
}
|
||||||
|
}
|
@@ -19,11 +19,14 @@ class InfinityPagedComponentContext<T> internal constructor(
|
|||||||
) {
|
) {
|
||||||
internal val iterationState: MutableState<Pair<Int, Pagination>> = mutableStateOf(0 to SimplePagination(page, size))
|
internal val iterationState: MutableState<Pair<Int, Pagination>> = mutableStateOf(0 to SimplePagination(page, size))
|
||||||
internal val dataState: MutableState<List<T>?> = mutableStateOf(null)
|
internal val dataState: MutableState<List<T>?> = mutableStateOf(null)
|
||||||
|
internal var lastPageLoaded = false
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads the next page of data. If the current page is the last one, the function returns early.
|
* Loads the next page of data. If the current page is the last one, the function returns early.
|
||||||
*/
|
*/
|
||||||
fun loadNext() {
|
fun loadNext() {
|
||||||
|
if (lastPageLoaded) return
|
||||||
|
|
||||||
iterationState.value = iterationState.value.let {
|
iterationState.value = iterationState.value.let {
|
||||||
if ((dataState.value as? PaginationResult<*>) ?.isLastPage == true) return
|
if ((dataState.value as? PaginationResult<*>) ?.isLastPage == true) return
|
||||||
(it.first + 1) to it.second.nextPage()
|
(it.first + 1) to it.second.nextPage()
|
||||||
@@ -35,6 +38,7 @@ class InfinityPagedComponentContext<T> internal constructor(
|
|||||||
*/
|
*/
|
||||||
fun reload() {
|
fun reload() {
|
||||||
dataState.value = null
|
dataState.value = null
|
||||||
|
lastPageLoaded = false
|
||||||
iterationState.value = iterationState.value.let {
|
iterationState.value = iterationState.value.let {
|
||||||
(it.first + 1) to (it.second.firstPage())
|
(it.first + 1) to (it.second.firstPage())
|
||||||
}
|
}
|
||||||
@@ -59,8 +63,12 @@ internal fun <T> InfinityPagedComponent(
|
|||||||
) {
|
) {
|
||||||
val context = remember { InfinityPagedComponentContext<T>(page, size) }
|
val context = remember { InfinityPagedComponentContext<T>(page, size) }
|
||||||
|
|
||||||
LaunchedEffect(context.iterationState.value) {
|
LaunchedEffect(context.iterationState.value.first) {
|
||||||
context.dataState.value = (context.dataState.value ?: emptyList()) + loader(context, context.iterationState.value.second).results
|
val paginationResult = loader(context, context.iterationState.value.second)
|
||||||
|
if (paginationResult.isLastPage) {
|
||||||
|
context.lastPageLoaded = true
|
||||||
|
}
|
||||||
|
context.dataState.value = (context.dataState.value ?: emptyList()) + paginationResult.results
|
||||||
}
|
}
|
||||||
|
|
||||||
context.dataState.value ?.let {
|
context.dataState.value ?.let {
|
||||||
@@ -79,11 +87,10 @@ internal fun <T> InfinityPagedComponent(
|
|||||||
@Composable
|
@Composable
|
||||||
fun <T> InfinityPagedComponent(
|
fun <T> InfinityPagedComponent(
|
||||||
pageInfo: Pagination,
|
pageInfo: Pagination,
|
||||||
loader: suspend PagedComponentContext<T>.(Pagination) -> PaginationResult<T>,
|
loader: suspend InfinityPagedComponentContext<T>.(Pagination) -> PaginationResult<T>,
|
||||||
block: @Composable PagedComponentContext<T>.(PaginationResult<T>) -> Unit
|
block: @Composable InfinityPagedComponentContext<T>.(List<T>?) -> Unit
|
||||||
) {
|
) {
|
||||||
PagedComponent(
|
InfinityPagedComponent(
|
||||||
null,
|
|
||||||
pageInfo.page,
|
pageInfo.page,
|
||||||
pageInfo.size,
|
pageInfo.size,
|
||||||
loader,
|
loader,
|
||||||
@@ -91,25 +98,6 @@ fun <T> InfinityPagedComponent(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Overloaded composable function for an infinitely paged component.
|
|
||||||
*
|
|
||||||
* @param T The type of the paginated data.
|
|
||||||
* @param initialPage Initial page number.
|
|
||||||
* @param size Number of items per page.
|
|
||||||
* @param loader Suspended function that loads paginated data.
|
|
||||||
* @param block Composable function that renders the UI with the loaded data.
|
|
||||||
*/
|
|
||||||
@Composable
|
|
||||||
fun <T> InfinityPagedComponent(
|
|
||||||
initialPage: Int,
|
|
||||||
size: Int,
|
|
||||||
loader: suspend PagedComponentContext<T>.(Pagination) -> PaginationResult<T>,
|
|
||||||
block: @Composable PagedComponentContext<T>.(PaginationResult<T>) -> Unit
|
|
||||||
) {
|
|
||||||
PagedComponent(null, initialPage, size, loader, block)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Overloaded composable function for an infinitely paged component.
|
* Overloaded composable function for an infinitely paged component.
|
||||||
*
|
*
|
||||||
@@ -121,8 +109,8 @@ fun <T> InfinityPagedComponent(
|
|||||||
@Composable
|
@Composable
|
||||||
fun <T> InfinityPagedComponent(
|
fun <T> InfinityPagedComponent(
|
||||||
size: Int,
|
size: Int,
|
||||||
loader: suspend PagedComponentContext<T>.(Pagination) -> PaginationResult<T>,
|
loader: suspend InfinityPagedComponentContext<T>.(Pagination) -> PaginationResult<T>,
|
||||||
block: @Composable PagedComponentContext<T>.(PaginationResult<T>) -> Unit
|
block: @Composable InfinityPagedComponentContext<T>.(List<T>?) -> Unit
|
||||||
) {
|
) {
|
||||||
PagedComponent(0, size, loader, block)
|
InfinityPagedComponent(0, size, loader, block)
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,47 @@
|
|||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.ui.test.ExperimentalTestApi
|
||||||
|
import androidx.compose.ui.test.runComposeUiTest
|
||||||
|
import dev.inmo.micro_utils.pagination.*
|
||||||
|
import dev.inmo.micro_utils.pagination.compose.InfinityPagedComponent
|
||||||
|
import dev.inmo.micro_utils.pagination.compose.PagedComponent
|
||||||
|
import org.jetbrains.annotations.TestOnly
|
||||||
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertContentEquals
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
class InfinityPagedComponentTests {
|
||||||
|
@OptIn(ExperimentalTestApi::class)
|
||||||
|
@Test
|
||||||
|
@TestOnly
|
||||||
|
fun testSimpleLoad() = runComposeUiTest {
|
||||||
|
var expectedList = listOf<Int>()
|
||||||
|
setContent {
|
||||||
|
InfinityPagedComponent<Int>(
|
||||||
|
size = 1,
|
||||||
|
loader = {
|
||||||
|
PaginationResult(
|
||||||
|
page = it.page,
|
||||||
|
size = it.size,
|
||||||
|
results = (it.firstIndex .. it.lastIndex).toList(),
|
||||||
|
objectsNumber = 3
|
||||||
|
).also {
|
||||||
|
expectedList += it.results
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
assertEquals(expectedList, it)
|
||||||
|
|
||||||
|
LaunchedEffect(it ?.size) {
|
||||||
|
loadNext()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
waitForIdle()
|
||||||
|
|
||||||
|
assertContentEquals(
|
||||||
|
listOf(0, 1, 2),
|
||||||
|
expectedList
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
64
pagination/compose/src/jvmTest/kotlin/PagedComponentTests.kt
Normal file
64
pagination/compose/src/jvmTest/kotlin/PagedComponentTests.kt
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.ui.test.ExperimentalTestApi
|
||||||
|
import androidx.compose.ui.test.runComposeUiTest
|
||||||
|
import dev.inmo.micro_utils.pagination.*
|
||||||
|
import dev.inmo.micro_utils.pagination.compose.PagedComponent
|
||||||
|
import org.jetbrains.annotations.TestOnly
|
||||||
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
class PagedComponentTests {
|
||||||
|
@OptIn(ExperimentalTestApi::class)
|
||||||
|
@Test
|
||||||
|
@TestOnly
|
||||||
|
fun testSimpleLoad() = runComposeUiTest {
|
||||||
|
var expectedPage = PaginationResult(
|
||||||
|
page = 0,
|
||||||
|
size = 1,
|
||||||
|
results = listOf(0),
|
||||||
|
objectsNumber = 3
|
||||||
|
)
|
||||||
|
var previousPage = expectedPage
|
||||||
|
setContent {
|
||||||
|
PagedComponent<Int>(
|
||||||
|
initialPage = 0,
|
||||||
|
size = 1,
|
||||||
|
loader = {
|
||||||
|
previousPage = expectedPage
|
||||||
|
expectedPage = PaginationResult(
|
||||||
|
page = it.page,
|
||||||
|
size = it.size,
|
||||||
|
results = (it.firstIndex .. it.lastIndex).toList(),
|
||||||
|
objectsNumber = 3
|
||||||
|
)
|
||||||
|
expectedPage
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
assertEquals(expectedPage, it)
|
||||||
|
assertEquals(expectedPage.results, it.results)
|
||||||
|
|
||||||
|
if (it.isLastPage || it.page < previousPage.page) {
|
||||||
|
if (it.isFirstPage) {
|
||||||
|
// do nothing - end of test
|
||||||
|
} else {
|
||||||
|
loadPrevious()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
loadNext()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
waitForIdle()
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
PaginationResult(
|
||||||
|
page = 0,
|
||||||
|
size = 1,
|
||||||
|
results = listOf(0),
|
||||||
|
objectsNumber = 3
|
||||||
|
),
|
||||||
|
expectedPage
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user