Skip to content

Commit d15747a

Browse files
committed
refactor: Move UI tests to commonMain for multiplatform support
- Relocate UI test case files and screen objects from the `test-jvm` module to the `test/commonMain` source set. - Update test cases to use `kotlinx.coroutines.test.TestResult` as the return type instead of `Unit`. - Replace JVM-specific dependencies and utilities with their Kotlin Multiplatform equivalents: - `org.junit.Assert.assertEquals` is replaced with `kotlin.test.assertEquals`. - `org.koin.java.KoinJavaComponent` is replaced with `org.koin.mp.KoinPlatform`. - `java.util.UUID` is replaced with `kotlin.uuid.Uuid`. - `kotlinx.coroutines.runBlocking` is replaced with a custom `runBlockingAll` utility for multiplatform compatibility. - `Thread.sleep` is replaced with `kotlinx.coroutines.delay`. - Add necessary dependencies to the `ui/test/build.gradle.kts` file for the `commonMain` source set, including `koin-core`, `turbine`, and project modules. - Annotate test screen classes with `@JvmInline` for performance optimization.
1 parent 2709147 commit d15747a

24 files changed

+101
-67
lines changed

ui/test/build.gradle.kts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
)
66

77
import org.jetbrains.compose.ExperimentalComposeLibrary
8-
import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
98
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
9+
import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
1010
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
1111
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSetTree
1212

@@ -33,13 +33,23 @@ kotlin {
3333
sourceSets {
3434
val commonMain by getting {
3535
dependencies {
36+
implementation(kotlin("test"))
37+
implementation(projects.core.domain)
38+
implementation(projects.core.presentation)
39+
implementation(projects.ui.shared)
3640
implementation(compose.uiTest)
3741
implementation(compose.material3)
42+
implementation(compose.materialIconsExtended)
43+
implementation(compose.components.resources)
44+
implementation(libs.material.theme.prefs)
45+
implementation(libs.turbine)
46+
implementation(project.dependencies.platform(libs.koin.bom))
47+
implementation(libs.koin.core)
48+
implementation(libs.kermit)
3849
}
3950
}
4051
val commonTest by getting {
4152
dependencies {
42-
implementation(kotlin("test"))
4353
}
4454
}
4555
val jvmTest by getting {

ui/test-jvm/src/main/kotlin/com/softartdev/notedelight/DbTestEncryptor.kt renamed to ui/test/src/commonMain/kotlin/com/softartdev/notedelight/DbTestEncryptor.kt

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,22 @@ package com.softartdev.notedelight
33
import co.touchlab.kermit.Logger
44
import com.softartdev.notedelight.model.PlatformSQLiteState
55
import com.softartdev.notedelight.repository.SafeRepo
6-
import kotlinx.coroutines.runBlocking
7-
import org.koin.java.KoinJavaComponent.inject
6+
import com.softartdev.notedelight.util.runBlockingAll
7+
import kotlinx.coroutines.delay
8+
import org.koin.mp.KoinPlatform
9+
import kotlin.time.Duration.Companion.seconds
810

911
object DbTestEncryptor : () -> Unit {
1012
private val logger = Logger.withTag(this@DbTestEncryptor::class.simpleName.toString())
1113
const val PASSWORD = "password"
1214

13-
override fun invoke() = runBlocking {
14-
val safeRepo: SafeRepo by inject(SafeRepo::class.java)
15+
override fun invoke() = runBlockingAll {
16+
val safeRepo: SafeRepo by KoinPlatform.getKoin().inject()
1517
while (safeRepo.databaseState == PlatformSQLiteState.DOES_NOT_EXIST) {
1618
safeRepo.buildDbIfNeed()
1719
val count: Long = safeRepo.noteDAO.count()
1820
logger.d { "notes count = $count" }
19-
Thread.sleep(1000)
21+
delay(duration = 1.seconds)
2022
logger.d { "databaseState = ${safeRepo.databaseState.name}" }
2123
}
2224
safeRepo.encrypt(StringBuilder(PASSWORD))

ui/test-jvm/src/main/kotlin/com/softartdev/notedelight/ext.kt renamed to ui/test/src/commonMain/kotlin/com/softartdev/notedelight/ext.kt

File renamed without changes.

ui/test-jvm/src/main/kotlin/com/softartdev/notedelight/ui/BaseTestCase.kt renamed to ui/test/src/commonMain/kotlin/com/softartdev/notedelight/ui/BaseTestCase.kt

File renamed without changes.

ui/test-jvm/src/main/kotlin/com/softartdev/notedelight/ui/cases/CrudTestCase.kt renamed to ui/test/src/commonMain/kotlin/com/softartdev/notedelight/ui/cases/CrudTestCase.kt

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
@file:OptIn(ExperimentalTestApi::class)
1+
@file:OptIn(ExperimentalTestApi::class, ExperimentalUuidApi::class)
22

33
package com.softartdev.notedelight.ui.cases
44

@@ -9,14 +9,16 @@ import androidx.compose.ui.test.performTextInput
99
import com.softartdev.notedelight.ui.BaseTestCase
1010
import com.softartdev.notedelight.ui.screen.MainTestScreen.Companion.noteItemTitleText
1111
import com.softartdev.notedelight.waitUntilDisplayed
12+
import kotlinx.coroutines.test.TestResult
1213
import kotlinx.coroutines.test.runTest
13-
import java.util.UUID
14+
import kotlin.uuid.ExperimentalUuidApi
15+
import kotlin.uuid.Uuid
1416

1517
class CrudTestCase(
1618
composeUiTest: ComposeUiTest
17-
) : () -> Unit, BaseTestCase(composeUiTest) {
19+
) : () -> TestResult, BaseTestCase(composeUiTest) {
1820

19-
private val actualNoteText = UUID.randomUUID().toString().substring(0, 30)
21+
private val actualNoteText = Uuid.random().toString().substring(0, 30)
2022

2123
override fun invoke() = runTest {
2224
mainTestScreen {

ui/test-jvm/src/main/kotlin/com/softartdev/notedelight/ui/cases/EditTitleAfterCreateTestCase.kt renamed to ui/test/src/commonMain/kotlin/com/softartdev/notedelight/ui/cases/EditTitleAfterCreateTestCase.kt

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
@file:OptIn(ExperimentalTestApi::class)
1+
@file:OptIn(ExperimentalTestApi::class, ExperimentalUuidApi::class)
22

33
package com.softartdev.notedelight.ui.cases
44

@@ -10,17 +10,19 @@ import androidx.compose.ui.test.performTextInput
1010
import androidx.compose.ui.test.performTextReplacement
1111
import com.softartdev.notedelight.ui.BaseTestCase
1212
import com.softartdev.notedelight.ui.screen.MainTestScreen.Companion.noteItemTitleText
13+
import com.softartdev.notedelight.util.runBlockingAll
1314
import com.softartdev.notedelight.waitUntilDisplayed
14-
import kotlinx.coroutines.runBlocking
15+
import kotlinx.coroutines.test.TestResult
1516
import kotlinx.coroutines.test.runTest
1617
import notedelight.ui.shared.generated.resources.Res
1718
import notedelight.ui.shared.generated.resources.enter_title
1819
import org.jetbrains.compose.resources.getString
19-
import java.util.UUID
20+
import kotlin.uuid.ExperimentalUuidApi
21+
import kotlin.uuid.Uuid
2022

2123
class EditTitleAfterCreateTestCase(
2224
composeUiTest: ComposeUiTest
23-
) : () -> Unit, BaseTestCase(composeUiTest) {
25+
) : () -> TestResult, BaseTestCase(composeUiTest) {
2426

2527
private val actualNoteTitle = "title"
2628

@@ -29,7 +31,7 @@ class EditTitleAfterCreateTestCase(
2931
composeUiTest.waitUntilDisplayed("fab", blockSNI = ::fabSNI)
3032
fabSNI.performClick()
3133
noteScreen {
32-
val actualNoteText = UUID.randomUUID().toString().substring(0, 30)
34+
val actualNoteText = Uuid.random().toString().substring(0, 30)
3335
textFieldSNI.performTextInput(actualNoteText)
3436
editTitleMenuButtonSNI.performClick()
3537
editTitleDialog {
@@ -38,7 +40,7 @@ class EditTitleAfterCreateTestCase(
3840
yesDialogButtonSNI.performClick()
3941
}
4042
composeUiTest
41-
.onNodeWithContentDescription(label = runBlocking { getString(Res.string.enter_title) })
43+
.onNodeWithContentDescription(label = runBlockingAll { getString(Res.string.enter_title) })
4244
.assertDoesNotExist()
4345
saveNoteMenuButtonSNI.performClick()
4446
backButtonSNI.performClick()

ui/test-jvm/src/main/kotlin/com/softartdev/notedelight/ui/cases/EditTitleAfterSaveTestCase.kt renamed to ui/test/src/commonMain/kotlin/com/softartdev/notedelight/ui/cases/EditTitleAfterSaveTestCase.kt

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
@file:OptIn(ExperimentalTestApi::class)
1+
@file:OptIn(ExperimentalTestApi::class, ExperimentalUuidApi::class)
22

33
package com.softartdev.notedelight.ui.cases
44

@@ -10,19 +10,21 @@ import androidx.compose.ui.test.performTextInput
1010
import androidx.compose.ui.test.performTextReplacement
1111
import com.softartdev.notedelight.ui.BaseTestCase
1212
import com.softartdev.notedelight.ui.screen.MainTestScreen.Companion.noteItemTitleText
13+
import com.softartdev.notedelight.util.runBlockingAll
1314
import com.softartdev.notedelight.waitUntilDisplayed
14-
import kotlinx.coroutines.runBlocking
15+
import kotlinx.coroutines.test.TestResult
1516
import kotlinx.coroutines.test.runTest
1617
import notedelight.ui.shared.generated.resources.Res
1718
import notedelight.ui.shared.generated.resources.enter_title
1819
import org.jetbrains.compose.resources.getString
19-
import java.util.UUID
20+
import kotlin.uuid.ExperimentalUuidApi
21+
import kotlin.uuid.Uuid
2022

2123
class EditTitleAfterSaveTestCase(
2224
composeUiTest: ComposeUiTest
23-
) : () -> Unit, BaseTestCase(composeUiTest) {
25+
) : () -> TestResult, BaseTestCase(composeUiTest) {
2426

25-
private val actualNoteText = UUID.randomUUID().toString().substring(0, 30)
27+
private val actualNoteText = Uuid.random().toString().substring(0, 30)
2628
private val actualNoteTitle = "title"
2729

2830
override fun invoke() = runTest {
@@ -44,7 +46,7 @@ class EditTitleAfterSaveTestCase(
4446
yesDialogButtonSNI.performClick()
4547
}
4648
composeUiTest
47-
.onNodeWithContentDescription(label = runBlocking { getString(Res.string.enter_title) })
49+
.onNodeWithContentDescription(label = runBlockingAll { getString(Res.string.enter_title) })
4850
.assertDoesNotExist()
4951
saveNoteMenuButtonSNI.performClick()
5052
backButtonSNI.performClick()

ui/test-jvm/src/main/kotlin/com/softartdev/notedelight/ui/cases/FlowAfterCryptTestCase.kt renamed to ui/test/src/commonMain/kotlin/com/softartdev/notedelight/ui/cases/FlowAfterCryptTestCase.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,15 @@ import com.softartdev.notedelight.ui.BaseTestCase
1515
import com.softartdev.notedelight.ui.screen.MainTestScreen
1616
import com.softartdev.notedelight.waitAssert
1717
import com.softartdev.notedelight.waitUntilDisplayed
18+
import kotlinx.coroutines.test.TestResult
1819
import kotlinx.coroutines.test.runTest
1920

2021
class FlowAfterCryptTestCase(
2122
composeUiTest: ComposeUiTest,
2223
private val pressBack: () -> Unit,
2324
private val closeSoftKeyboard: () -> Unit,
2425
private val password: String = "password"
25-
) : () -> Unit, BaseTestCase(composeUiTest) {
26+
) : () -> TestResult, BaseTestCase(composeUiTest) {
2627
private val logger = Logger.withTag("FlowAfterCryptTestCase")
2728
private val titleText = "Lorem"
2829

ui/test-jvm/src/main/kotlin/com/softartdev/notedelight/ui/cases/LocaleTestCase.kt renamed to ui/test/src/commonMain/kotlin/com/softartdev/notedelight/ui/cases/LocaleTestCase.kt

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,23 +11,24 @@ import com.softartdev.notedelight.ASSERT_WAIT_TIMEOUT_MILLIS
1111
import com.softartdev.notedelight.interactor.LocaleInteractor
1212
import com.softartdev.notedelight.model.LanguageEnum
1313
import com.softartdev.notedelight.ui.BaseTestCase
14+
import com.softartdev.notedelight.util.runBlockingAll
1415
import com.softartdev.notedelight.waitUntilDisplayed
1516
import com.softartdev.notedelight.waitUntilSelected
16-
import junit.framework.TestCase.assertEquals
17-
import kotlinx.coroutines.runBlocking
17+
import kotlinx.coroutines.test.TestResult
1818
import kotlinx.coroutines.test.runTest
1919
import notedelight.ui.shared.generated.resources.Res
2020
import notedelight.ui.shared.generated.resources.choose_language
2121
import org.jetbrains.compose.resources.getString
22-
import org.koin.java.KoinJavaComponent
22+
import org.koin.mp.KoinPlatform
23+
import kotlin.test.assertEquals
2324

2425
class LocaleTestCase(
2526
composeUiTest: ComposeUiTest,
2627
private val pressBack: () -> Unit
27-
) : () -> Unit, BaseTestCase(composeUiTest) {
28+
) : () -> TestResult, BaseTestCase(composeUiTest) {
2829

2930
override fun invoke() = runTest {
30-
val localeInteractor: LocaleInteractor = KoinJavaComponent.get(LocaleInteractor::class.java)
31+
val localeInteractor: LocaleInteractor = KoinPlatform.getKoin().get()
3132
mainTestScreen {
3233
composeUiTest.waitUntilDisplayed("settingsMenuButton", blockSNI = ::settingsMenuButtonSNI)
3334
settingsMenuButtonSNI.performClick()
@@ -42,7 +43,7 @@ class LocaleTestCase(
4243
yesDialogButtonSNI.performClick()
4344
}
4445
composeUiTest.waitUntilDoesNotExist(
45-
matcher = hasText(runBlocking { getString(Res.string.choose_language) }),
46+
matcher = hasText(runBlockingAll { getString(Res.string.choose_language) }),
4647
timeoutMillis = ASSERT_WAIT_TIMEOUT_MILLIS,
4748
)
4849
assertEquals(languageEnum, localeInteractor.languageEnum)

ui/test-jvm/src/main/kotlin/com/softartdev/notedelight/ui/cases/PrepopulateDbTestCase.kt renamed to ui/test/src/commonMain/kotlin/com/softartdev/notedelight/ui/cases/PrepopulateDbTestCase.kt

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,30 +9,31 @@ import com.softartdev.notedelight.db.NoteDAO
99
import com.softartdev.notedelight.model.Note
1010
import com.softartdev.notedelight.ui.BaseTestCase
1111
import com.softartdev.notedelight.usecase.note.CreateNoteUseCase
12+
import kotlinx.coroutines.test.TestResult
1213
import kotlinx.coroutines.test.runTest
13-
import org.junit.Assert
14-
import org.koin.java.KoinJavaComponent
14+
import org.koin.mp.KoinPlatform
15+
import kotlin.test.assertEquals
1516
import kotlin.time.Duration.Companion.minutes
1617

1718
class PrepopulateDbTestCase(
1819
composeUiTest: ComposeUiTest
19-
) : () -> Unit, BaseTestCase(composeUiTest) {
20+
) : () -> TestResult, BaseTestCase(composeUiTest) {
2021

21-
private val noteDAO: NoteDAO by KoinJavaComponent.inject(NoteDAO::class.java)
22-
private val createNoteUseCase: CreateNoteUseCase by KoinJavaComponent.inject(CreateNoteUseCase::class.java)
22+
private val noteDAO: NoteDAO by KoinPlatform.getKoin().inject()
23+
private val createNoteUseCase: CreateNoteUseCase by KoinPlatform.getKoin().inject()
2324

2425
override fun invoke() = runTest(timeout = 1.minutes) {
2526
noteDAO.listFlow.test {
2627
var notes: List<Note> = awaitItem()
27-
Assert.assertEquals(0, notes.size)
28+
assertEquals(0, notes.size)
2829

2930
for (num in 1..250) {
3031
val loremIpsum = LOREM_IPSUM.repeat(num)
3132
createNoteUseCase(title = "Title #$num", text = loremIpsum)
3233
composeUiTest.awaitIdle()
3334

3435
notes = awaitItem()
35-
Assert.assertEquals(num, notes.size)
36+
assertEquals(num, notes.size)
3637
}
3738
}
3839
}

0 commit comments

Comments
 (0)