Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/alert/UI tests: Fix and complete UI tests for AlertScreen #68

Merged
merged 10 commits into from
Oct 28, 2024
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
charliemangano marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -1,26 +1,42 @@
package com.android.periodpals.ui.alert

import androidx.compose.material3.MaterialTheme
import androidx.compose.ui.test.assertIsDisplayed
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)
}

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

composeTestRule.onNodeWithTag("alertInstruction").assertIsDisplayed()
composeTestRule.onNodeWithTag("alertProduct").assertIsDisplayed()
Expand All @@ -34,22 +50,74 @@ class AlertScreenTest {
}

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

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

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

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

composeTestRule.onNodeWithTag("alertSubmit").performClick()
verify(navigationActions).navigateTo(Screen.ALERT_LIST)
}

@Test
fun createInvalidAlertNoProduct() {
composeTestRule.setContent { AlertScreen(navigationActions) }

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

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, never()).navigateTo(any<TopLevelDestination>())
verify(navigationActions, never()).navigateTo(any<String>())
charliemangano marked this conversation as resolved.
Show resolved Hide resolved
}

@Test
fun createInvalidAlertNoUrgencyLevel() {
composeTestRule.setContent { AlertScreen(navigationActions) }

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.setContent { AlertScreen(navigationActions) }

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) }
charliemangano marked this conversation as resolved.
Show resolved Hide resolved

// 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
Loading