Skip to content

Commit

Permalink
Merge pull request #68 from PeriodPals/fix/alert/ui-tests
Browse files Browse the repository at this point in the history
Fix/alert/UI tests: Fix and complete UI tests for AlertScreen
  • Loading branch information
charliemangano authored Oct 28, 2024
2 parents 3bec511 + 19d5f28 commit 2eea42f
Show file tree
Hide file tree
Showing 4 changed files with 167 additions and 67 deletions.
2 changes: 1 addition & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -221,8 +221,8 @@ dependencies {

/// Mockito for android testing
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.mockito.android)
androidTestImplementation(libs.mockito.kotlin)
androidTestImplementation(libs.dexmaker.mockito.inline)

// Mockito for unit testing
testImplementation(libs.mockito.kotlin)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,55 +1,125 @@
package com.android.periodpals.ui.alert

import androidx.compose.material3.MaterialTheme
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.assertIsNotDisplayed
import androidx.compose.ui.test.assertTextEquals
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.performTextInput
import androidx.navigation.compose.rememberNavController
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.periodpals.ui.navigation.NavigationActions
import com.android.periodpals.ui.navigation.Route
import com.android.periodpals.ui.navigation.Screen
import com.android.periodpals.ui.navigation.TopLevelDestination
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.mock
import org.mockito.Mockito.`when`
import org.mockito.kotlin.any
import org.mockito.kotlin.never
import org.mockito.kotlin.verify

@RunWith(AndroidJUnit4::class)
class AlertScreenTest {

private lateinit var navigationActions: NavigationActions
@get:Rule val composeTestRule = createComposeRule()

@Before
fun setUp() {
navigationActions = mock(NavigationActions::class.java)

`when`(navigationActions.currentRoute()).thenReturn(Route.ALERT)

composeTestRule.setContent { AlertScreen(navigationActions) }
}

@Test
fun displayAllComponents() {
composeTestRule.setContent {
MaterialTheme { AlertScreen(NavigationActions(rememberNavController())) }
}
fun allComponentsAreDisplayed() {

composeTestRule.onNodeWithTag("alertInstruction").assertIsDisplayed()
composeTestRule.onNodeWithTag("alertProduct").assertIsDisplayed()
composeTestRule.onNodeWithTag("alertUrgency").assertIsDisplayed()
composeTestRule.onNodeWithTag("alertLocation").assertIsDisplayed()
composeTestRule.onNodeWithTag("alertMessage").assertIsDisplayed()
composeTestRule.onNodeWithTag("bottomNavigationMenu").assertIsDisplayed()
composeTestRule.onNodeWithTag("topBar").assertIsDisplayed()
composeTestRule.onNodeWithTag("goBackButton").assertIsNotDisplayed()
composeTestRule
.onNodeWithTag("alertSubmit")
.assertIsDisplayed()
.assertTextEquals("Ask for Help")
}

@Test
fun interactWithComponents() {
composeTestRule.setContent {
MaterialTheme { AlertScreen(NavigationActions(rememberNavController())) }
}
fun createValidAlert() {

composeTestRule.onNodeWithTag("alertProduct").performClick()
composeTestRule.onNodeWithTag("Pads").performClick()
// composeTestRule.onNodeWithTag("alertProduct").assertTextEquals("Pads")
composeTestRule.onNodeWithText("Pads").performClick()

composeTestRule.onNodeWithTag("alertUrgency").performClick()
composeTestRule.onNodeWithTag("!! Medium").performClick()
// composeTestRule.onNodeWithTag("alertUrgency").assertTextEquals("!! Medium")
composeTestRule.onNodeWithText("!! Medium").performClick()

composeTestRule.onNodeWithTag("alertLocation").performTextInput("Rolex")
composeTestRule.onNodeWithTag("alertMessage").performTextInput("I need help finding a tampon")

// Cannot test navigation actions
// composeTestRule.onNodeWithTag("alertSubmit").performClick()
composeTestRule.onNodeWithTag("alertSubmit").performClick()
verify(navigationActions).navigateTo(Screen.ALERT_LIST)
}

@Test
fun createInvalidAlertNoProduct() {

composeTestRule.onNodeWithTag("alertUrgency").performClick()
composeTestRule.waitForIdle()
composeTestRule.onNodeWithText("!! Medium").performClick()
composeTestRule.waitForIdle()

composeTestRule.onNodeWithTag("alertLocation").performTextInput("Rolex")
composeTestRule.onNodeWithTag("alertMessage").performTextInput("I need help finding a tampon")

composeTestRule.onNodeWithTag("alertSubmit").performClick()
verify(navigationActions, never()).navigateTo(any<TopLevelDestination>())
verify(navigationActions, never()).navigateTo(any<String>())
}

@Test
fun createInvalidAlertNoUrgencyLevel() {

composeTestRule.onNodeWithTag("alertProduct").performClick()
composeTestRule.waitForIdle()
composeTestRule.onNodeWithText("Pads").performClick()
composeTestRule.waitForIdle()

composeTestRule.onNodeWithTag("alertLocation").performTextInput("Rolex")
composeTestRule.onNodeWithTag("alertMessage").performTextInput("I need help finding a tampon")

composeTestRule.onNodeWithTag("alertSubmit").performClick()
verify(navigationActions, never()).navigateTo(any<TopLevelDestination>())
verify(navigationActions, never()).navigateTo(any<String>())
}

@Test
fun createInvalidAlertNoLocation() {

composeTestRule.onNodeWithTag("alertProduct").performClick()
composeTestRule.waitForIdle()
composeTestRule.onNodeWithText("Pads").performClick()
composeTestRule.waitForIdle()

composeTestRule.onNodeWithTag("alertUrgency").performClick()
composeTestRule.waitForIdle()
composeTestRule.onNodeWithText("!! Medium").performClick()
composeTestRule.waitForIdle()

composeTestRule.onNodeWithTag("alertMessage").performTextInput("I need help finding a tampon")

composeTestRule.onNodeWithTag("alertSubmit").performClick()
verify(navigationActions, never()).navigateTo(any<TopLevelDestination>())
verify(navigationActions, never()).navigateTo(any<String>())
}
}
128 changes: 78 additions & 50 deletions app/src/main/java/com/android/periodpals/ui/alert/AlertScreen.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.android.periodpals.ui.alert

import android.widget.Toast
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
Expand All @@ -24,6 +25,7 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
Expand All @@ -35,74 +37,102 @@ import com.android.periodpals.ui.navigation.TopAppBar

@Composable
fun AlertScreen(navigationActions: NavigationActions) {
val context = LocalContext.current
var location by remember { mutableStateOf("") }
var message by remember { mutableStateOf("") }
val (productIsSelected, setProductIsSelected) = remember { mutableStateOf(false) }
val (urgencyIsSelected, setUrgencyIsSelected) = remember { mutableStateOf(false) }

// TODO("TOP APP BAR and BOTTOM NAVIGATION")
Scaffold(
modifier = Modifier.testTag("alertScreen"),
bottomBar = {
BottomNavigationMenu(
onTabSelect = { route -> navigationActions.navigateTo(route) },
tabList = LIST_TOP_LEVEL_DESTINATION,
selectedItem = navigationActions.currentRoute())
},
topBar = {
TopAppBar(
title = "Create Alert",
selectedItem = navigationActions.currentRoute(),
)
},
topBar = { TopAppBar(title = "Create Alert") },
content = { paddingValues ->
Column(
modifier = Modifier.fillMaxSize().padding(30.dp).padding(paddingValues),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.SpaceEvenly) {
// Text Instruction
Text(
"Push a notification to users near you! If they are available and have the products you need, they'll be able to help you!",
modifier = Modifier.testTag("alertInstruction"),
textAlign = TextAlign.Center,
style = MaterialTheme.typography.titleSmall)
verticalArrangement = Arrangement.SpaceEvenly,
) {
// Text Instruction
Text(
"Push a notification to users near you! If they are available and have the products you need, they'll be able to help you!",
modifier = Modifier.testTag("alertInstruction"),
textAlign = TextAlign.Center,
style = MaterialTheme.typography.titleSmall,
)

// Product
ExposedDropdownMenuSample(
listOf("Tampons", "Pads", "No Preference"), "Product Needed", "alertProduct")
// Product
ExposedDropdownMenuSample(
listOf("Tampons", "Pads", "No Preference"),
"Product Needed",
"alertProduct",
setProductIsSelected,
)

// Urgency
ExposedDropdownMenuSample(
listOf("!!! High", "!! Medium", "! Low"), "Urgency level", "alertUrgency")
// Urgency
ExposedDropdownMenuSample(
listOf("!!! High", "!! Medium", "! Low"),
"Urgency level",
"alertUrgency",
setUrgencyIsSelected,
)

// Location
OutlinedTextField(
value = location,
onValueChange = { location = it },
label = { Text("Location") },
placeholder = { Text("Enter your location") },
modifier = Modifier.fillMaxWidth().testTag("alertLocation"))
// Location
OutlinedTextField(
value = location,
onValueChange = { location = it },
label = { Text("Location") },
placeholder = { Text("Enter your location") },
modifier = Modifier.fillMaxWidth().testTag("alertLocation"),
)

// Message
OutlinedTextField(
value = message,
onValueChange = { message = it },
label = { Text("Message") },
placeholder = { Text("Write a message for the other users") },
modifier = Modifier.fillMaxWidth().height(150.dp).testTag("alertMessage"))
// Message
OutlinedTextField(
value = message,
onValueChange = { message = it },
label = { Text("Message") },
placeholder = { Text("Write a message for the other users") },
modifier = Modifier.fillMaxWidth().height(150.dp).testTag("alertMessage"),
)

// Submit Button
Button(
onClick = { navigationActions.navigateTo(Screen.ALERT_LIST) },
modifier =
Modifier.width(300.dp).height(100.dp).testTag("alertSubmit").padding(16.dp),
) {
Text("Ask for Help", style = MaterialTheme.typography.headlineMedium)
}
}
})
// Submit Button
Button(
onClick = {
if (location.isEmpty()) {
Toast.makeText(context, "Please enter a location", Toast.LENGTH_SHORT).show()
} else if (!productIsSelected) {
Toast.makeText(context, "Please select a product", Toast.LENGTH_SHORT).show()
} else if (!urgencyIsSelected) {
Toast.makeText(context, "Please select an urgency level", Toast.LENGTH_SHORT)
.show()
} else {
navigationActions.navigateTo(Screen.ALERT_LIST)
}
},
modifier =
Modifier.width(300.dp).height(100.dp).testTag("alertSubmit").padding(16.dp),
) {
Text("Ask for Help", style = MaterialTheme.typography.headlineMedium)
}
}
},
)
}

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ExposedDropdownMenuSample(list: List<String>, label: String, testTag: String) {
fun ExposedDropdownMenuSample(
list: List<String>,
label: String,
testTag: String,
setIsSelected: (Boolean) -> Unit,
) {
var options = list
var expanded by remember { mutableStateOf(false) }
var text by remember { mutableStateOf("Please choose one option") }
Expand All @@ -116,23 +146,21 @@ fun ExposedDropdownMenuSample(list: List<String>, label: String, testTag: String
modifier = Modifier.menuAnchor(),
value = text,
onValueChange = {},
readOnly = true,
singleLine = true,
readOnly = true,
label = { Text(label) },
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) },
colors = ExposedDropdownMenuDefaults.textFieldColors(),
)
ExposedDropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false },
) {
ExposedDropdownMenu(expanded = expanded, onDismissRequest = { expanded = false }) {
options.forEach { option ->
DropdownMenuItem(
modifier = Modifier.testTag(option),
modifier = Modifier,
text = { Text(option) },
onClick = {
text = option
expanded = false
setIsSelected(true)
},
contentPadding = ExposedDropdownMenuDefaults.ItemContentPadding,
)
Expand Down
2 changes: 2 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ agp = "8.6.1"
androidxNavigationCompose = "2.5.3"
bomVersion = "3.0.0"
compose = "1.0.0-beta01"
dexmakerMockitoInline = "2.28.4"
imagepicker = "2.1"
json = "20240303"
kotlin = "2.0.0"
Expand Down Expand Up @@ -101,6 +102,7 @@ androidx-ui-tooling = { module = "androidx.compose.ui:ui-tooling", version.ref =
androidx-ui-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview", version.ref = "ui" }
compose = { module = "com.github.bumptech.glide:compose", version.ref = "compose" }
core-ktx = { module = "com.google.android.play:core-ktx", version.ref = "coreKtxVersion" }
dexmaker-mockito-inline = { module = "com.linkedin.dexmaker:dexmaker-mockito-inline", version.ref = "dexmakerMockitoInline" }
github-postgrest-kt = { module = "io.github.jan-tennert.supabase:postgrest-kt" }
imagepicker = { module = "com.github.dhaval2404:imagepicker", version.ref = "imagepicker" }
json = { module = "org.json:json", version.ref = "json" }
Expand Down

0 comments on commit 2eea42f

Please sign in to comment.