test: migrate Compose UI tests from androidTest to commonTest (#5091)

This commit is contained in:
James Rich 2026-04-12 15:20:00 -05:00 committed by GitHub
parent 4156acf297
commit a11dee42a7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
27 changed files with 296 additions and 597 deletions

View file

@ -16,28 +16,46 @@
*/
package org.meshtastic.core.ui.component
import androidx.compose.ui.test.ExperimentalTestApi
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.junit4.v2.createComposeRule
import androidx.compose.ui.test.onNodeWithText
import org.junit.Rule
import org.junit.Test
import androidx.compose.ui.test.runComposeUiTest
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.resetMain
import kotlinx.coroutines.test.setMain
import org.meshtastic.core.ui.util.AlertManager
import kotlin.test.AfterTest
import kotlin.test.BeforeTest
import kotlin.test.Test
@OptIn(ExperimentalTestApi::class, ExperimentalCoroutinesApi::class)
class AlertHostTest {
@get:Rule val composeTestRule = createComposeRule()
private val testDispatcher = UnconfinedTestDispatcher()
@BeforeTest
fun setUp() {
Dispatchers.setMain(testDispatcher)
}
@AfterTest
fun tearDown() {
Dispatchers.resetMain()
}
@Test
fun alertHost_showsDialog_whenAlertIsTriggered() {
fun alertHost_showsDialog_whenAlertIsTriggered() = runComposeUiTest {
val alertManager = AlertManager()
val title = "Alert Title"
val message = "Alert Message"
composeTestRule.setContent { AlertHost(alertManager = alertManager) }
setContent { AlertHost(alertManager = alertManager) }
alertManager.showAlert(title = title, message = message)
composeTestRule.onNodeWithText(title).assertIsDisplayed()
composeTestRule.onNodeWithText(message).assertIsDisplayed()
onNodeWithText(title).assertIsDisplayed()
onNodeWithText(message).assertIsDisplayed()
}
}

View file

@ -18,27 +18,25 @@ package org.meshtastic.core.ui.component
import androidx.compose.material3.Text
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.test.assertDoesNotExist
import androidx.compose.ui.test.ExperimentalTestApi
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.junit4.v2.createComposeRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import org.junit.Rule
import org.junit.Test
import androidx.compose.ui.test.runComposeUiTest
import org.meshtastic.core.ui.util.LocalBarcodeScannerSupported
import org.meshtastic.core.ui.util.LocalNfcScannerSupported
import org.meshtastic.proto.SharedContact
import org.meshtastic.proto.User
import kotlin.test.Test
@OptIn(ExperimentalTestApi::class)
class ImportFabUiTest {
@get:Rule val composeTestRule = createComposeRule()
@Test
fun importFab_expands_onButtonClick_whenSupported() {
fun importFab_expands_onButtonClick_whenSupported() = runComposeUiTest {
val testTag = "import_fab"
composeTestRule.setContent {
setContent {
CompositionLocalProvider(
LocalBarcodeScannerSupported provides true,
LocalNfcScannerSupported provides true,
@ -48,18 +46,18 @@ class ImportFabUiTest {
}
// Expand the FAB
composeTestRule.onNodeWithTag(testTag).performClick()
onNodeWithTag(testTag).performClick()
// Verify menu items are visible using their tags
composeTestRule.onNodeWithTag("nfc_import").assertIsDisplayed()
composeTestRule.onNodeWithTag("qr_import").assertIsDisplayed()
composeTestRule.onNodeWithTag("url_import").assertIsDisplayed()
onNodeWithTag("nfc_import").assertIsDisplayed()
onNodeWithTag("qr_import").assertIsDisplayed()
onNodeWithTag("url_import").assertIsDisplayed()
}
@Test
fun importFab_hidesNfcAndQr_whenNotSupported() {
fun importFab_hidesNfcAndQr_whenNotSupported() = runComposeUiTest {
val testTag = "import_fab"
composeTestRule.setContent {
setContent {
CompositionLocalProvider(
LocalBarcodeScannerSupported provides false,
LocalNfcScannerSupported provides false,
@ -69,41 +67,41 @@ class ImportFabUiTest {
}
// Expand the FAB
composeTestRule.onNodeWithTag(testTag).performClick()
onNodeWithTag(testTag).performClick()
// Verify menu items are visible using their tags
composeTestRule.onNodeWithTag("nfc_import").assertDoesNotExist()
composeTestRule.onNodeWithTag("qr_import").assertDoesNotExist()
composeTestRule.onNodeWithTag("url_import").assertIsDisplayed()
onNodeWithTag("nfc_import").assertDoesNotExist()
onNodeWithTag("qr_import").assertDoesNotExist()
onNodeWithTag("url_import").assertIsDisplayed()
}
@Test
fun importFab_showsUrlDialog_whenUrlItemClicked() {
fun importFab_showsUrlDialog_whenUrlItemClicked() = runComposeUiTest {
val testTag = "import_fab"
composeTestRule.setContent { MeshtasticImportFAB(onImport = {}, isContactContext = true, testTag = testTag) }
setContent { MeshtasticImportFAB(onImport = {}, isContactContext = true, testTag = testTag) }
composeTestRule.onNodeWithTag(testTag).performClick()
composeTestRule.onNodeWithTag("url_import").performClick()
onNodeWithTag(testTag).performClick()
onNodeWithTag("url_import").performClick()
// The URL dialog should be shown.
// We'll search for its title indirectly or check if an AlertDialog appeared.
}
@Test
fun importFab_showsShareChannels_whenCallbackProvided() {
fun importFab_showsShareChannels_whenCallbackProvided() = runComposeUiTest {
val testTag = "import_fab"
composeTestRule.setContent {
setContent {
MeshtasticImportFAB(onImport = {}, onShareChannels = {}, isContactContext = false, testTag = testTag)
}
composeTestRule.onNodeWithTag(testTag).performClick()
composeTestRule.onNodeWithTag("share_channels").assertIsDisplayed()
onNodeWithTag(testTag).performClick()
onNodeWithTag("share_channels").assertIsDisplayed()
}
@Test
fun importFab_showsSharedContactDialog_whenProvided() {
fun importFab_showsSharedContactDialog_whenProvided() = runComposeUiTest {
val contact = SharedContact(user = User(long_name = "Suzume Goddess"), node_num = 1)
composeTestRule.setContent {
setContent {
MeshtasticImportFAB(
onImport = {},
sharedContact = contact,
@ -113,6 +111,6 @@ class ImportFabUiTest {
}
// Check if goddess is here
composeTestRule.onNodeWithText("Importing Suzume Goddess").assertIsDisplayed()
onNodeWithText("Importing Suzume Goddess").assertIsDisplayed()
}
}

View file

@ -18,22 +18,21 @@ package org.meshtastic.core.ui.util
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.test.ExperimentalTestApi
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.junit4.v2.createComposeRule
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import org.junit.Rule
import org.junit.Test
import androidx.compose.ui.test.runComposeUiTest
import kotlin.test.Test
import kotlin.test.assertTrue
@OptIn(ExperimentalTestApi::class)
class AlertManagerUiTest {
@get:Rule val composeTestRule = createComposeRule()
private val alertManager = AlertManager()
@Test
fun alertManager_showsAlert_whenRequested() {
composeTestRule.setContent {
fun alertManager_showsAlert_whenRequested() = runComposeUiTest {
val alertManager = AlertManager()
setContent {
val alertData by alertManager.currentAlert.collectAsState()
alertData?.let { data -> AlertPreviewRenderer(data) }
}
@ -43,29 +42,24 @@ class AlertManagerUiTest {
alertManager.showAlert(title = title, message = message)
composeTestRule.onNodeWithText(title).assertIsDisplayed()
composeTestRule.onNodeWithText(message).assertIsDisplayed()
onNodeWithText(title).assertIsDisplayed()
onNodeWithText(message).assertIsDisplayed()
}
@Test
fun alertManager_confirmButton_triggersCallbackAndDismisses() {
fun alertManager_confirmButton_triggersCallbackAndDismisses() = runComposeUiTest {
val alertManager = AlertManager()
var confirmClicked = false
composeTestRule.setContent {
setContent {
val alertData by alertManager.currentAlert.collectAsState()
alertData?.let { data -> AlertPreviewRenderer(data) }
}
alertManager.showAlert(title = "Confirm Title", onConfirm = { confirmClicked = true })
// Default confirm text is "Okay" from resources, but AlertPreviewRenderer uses it
// We'll search for the text "Okay" (assuming it matches the resource value)
// Since we are in a test, we might need to use a hardcoded string or a resource
// But for this test, let's just use the confirmText parameter to be sure
alertManager.showAlert(title = "Confirm Title", confirmText = "Yes", onConfirm = { confirmClicked = true })
composeTestRule.onNodeWithText("Yes").performClick()
onNodeWithText("Yes").performClick()
assert(confirmClicked)
composeTestRule.onNodeWithText("Confirm Title").assertDoesNotExist()
assertTrue(confirmClicked)
onNodeWithText("Confirm Title").assertDoesNotExist()
}
}