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

Testing/discovery screen2 #93

Merged
merged 10 commits into from
Apr 12, 2024
9 changes: 8 additions & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ dependencies {
globalTestImplementation(libs.kaspresso)
globalTestImplementation(libs.kaspresso.compose)


//Hilt
androidTestImplementation(libs.hilt.android.testing)
kaptAndroidTest(libs.hilt.android.compiler)
Expand Down Expand Up @@ -206,7 +207,13 @@ dependencies {
implementation(libs.hilt.android)
kapt(libs.hilt.android.compiler)

testImplementation("io.mockk:mockk:1.13.10")
globalTestImplementation(libs.mockk)

//Android Testing
androidTestImplementation(libs.hilt.android.testing)
kaptAndroidTest(libs.hilt.android.compiler)


}

tasks.withType<Test> {
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,17 @@ package com.lastaoutdoor.lasta.ui.screen.discovery

import androidx.activity.compose.setContent
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.assertTextContains
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.navigation.compose.composable
import androidx.compose.ui.test.performClick
import com.lastaoutdoor.lasta.data.api.FakeOutdoorActivityRepository
import com.lastaoutdoor.lasta.data.model.activity.ActivityType
import com.lastaoutdoor.lasta.data.model.activity.OutdoorActivity
import com.lastaoutdoor.lasta.di.AppModule
import com.lastaoutdoor.lasta.ui.MainActivity
import com.lastaoutdoor.lasta.viewmodel.DiscoveryScreenViewModel
import dagger.hilt.android.testing.BindValue
import dagger.hilt.android.testing.HiltAndroidRule
import dagger.hilt.android.testing.HiltAndroidTest
import dagger.hilt.android.testing.UninstallModules
Expand All @@ -18,28 +24,106 @@ import org.junit.Test
@UninstallModules(AppModule::class)
class DiscoveryScreenTest {

// Allow Hilt to inject dependencies
@get:Rule(order = 0) val hiltRule = HiltAndroidRule(this)

// Create a compose rule
@get:Rule(order = 1) val composeRule = createAndroidComposeRule<MainActivity>()

// Bind the fake view model to the test
@BindValue
val discoveryScreenViewModel: DiscoveryScreenViewModel =
DiscoveryScreenViewModel(FakeOutdoorActivityRepository())

// Set up the test
@Before
fun setUp() {
hiltRule.inject()
}

// Test if discovery screen is displayed
@Test
fun discoveryScreen_isDisplayed() {
composeRule.activity.setContent { DiscoveryScreen() }
composeRule.onNodeWithTag("discoveryScreen").assertIsDisplayed()
}

// Test if discovery content is displayed
@Test
fun discoveryScreen_hasContent() {
composeRule.activity.setContent { DiscoveryScreen() }
composeRule.onNodeWithTag("discoveryContent").assertIsDisplayed()
}

// Test if discovery screen has list
@Test
fun discoveryScreen_hasList() {
composeRule.activity.setContent { DiscoveryScreen() }
composeRule.onNodeWithTag("outdoorActivityList").assertIsDisplayed()
}

// Test if discovery screen has floating action button
@Test
fun discoveryScreen_hasFloatingActionButton() {
composeRule.activity.setContent { DiscoveryScreen() }
composeRule.onNodeWithTag("floatingActionButtons").assertIsDisplayed()
}

// Test if discovery screen has outdoor activity item and valid buttons
@Test
fun discoveryScreen_hasOutdoorActivityItem_andValidButtons() {
discoveryScreenViewModel.climbingActivities =
mutableListOf(OutdoorActivity(ActivityType.CLIMBING, 1, 1.0f, "1 hour", "Climbing"))
composeRule.activity.setContent {
val navController = androidx.navigation.compose.rememberNavController()
com.lastaoutdoor.lasta.ui.theme.LastaTheme {
androidx.navigation.compose.NavHost(
navController = navController, startDestination = "DiscoveryScreen") {
composable(route = "DiscoveryScreen") {
com.lastaoutdoor.lasta.ui.screen.discovery.DiscoveryScreen()
}
}
}
OutdoorActivityList(outdoorActivities = discoveryScreenViewModel.climbingActivities)
}
composeRule.onNodeWithTag("moreInfoButton").performClick()
composeRule.onNodeWithTag("mapButton").performClick()
composeRule.onNodeWithTag("startButton").performClick()
composeRule.onNodeWithTag("outdoorActivityItem").assertExists()
composeRule.onNodeWithTag("outdoorActivityItem").assertIsDisplayed()
}

// Test if activity dialog with valid info shows
@Test
fun discoveryScreen_isDisplayed() {
composeRule.onNodeWithTag("Discovery").assertIsDisplayed()
fun activityDialog_isDisplayed() {
val act = OutdoorActivity(ActivityType.CLIMBING, 1, 1.0f, "1 hour", "Zurich")
composeRule.activity.setContent {
ActivityDialog(
onDismissRequest = { /*dismiss dialog on clicking "Ok"*/
discoveryScreenViewModel.displayDialog.value = false
},
outdoorActivity = act)
}
composeRule.onNodeWithTag("okButton").performClick()
composeRule.onNodeWithTag("locationText").assertTextContains("Location: Zurich")
composeRule.onNodeWithTag("durationText").assertTextContains("Duration: 1 hour")
composeRule.onNodeWithTag("difficultyText").assertTextContains("Difficulty: 1")
composeRule.onNodeWithTag("activityDialog").assertIsDisplayed()
}

// Test if activity dialog with empty info shows
@Test
fun activityDialog_isDisplayed_emptyInfo() {
val act = OutdoorActivity(ActivityType.CLIMBING, 0, 1.0f, "", "")
composeRule.activity.setContent {
ActivityDialog(
onDismissRequest = { /*dismiss dialog on clicking "Ok"*/
discoveryScreenViewModel.displayDialog.value = false
},
outdoorActivity = act)
}
composeRule.onNodeWithTag("locationText").assertTextContains("No available location")
composeRule.onNodeWithTag("durationText").assertTextContains("No available duration")
composeRule.onNodeWithTag("difficultyText").assertTextContains("No available difficulty")
composeRule.onNodeWithTag("activityDialog").assertIsDisplayed()
}

// Test that setting dialog.value to true calls ActivityDialog
@Test
fun showDialog_isCalled() {
discoveryScreenViewModel.displayDialog.value = true
composeRule.activity.setContent { DiscoveryContent() }
composeRule.onNodeWithTag("activityDialog").assertIsDisplayed()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,12 @@ import androidx.activity.compose.setContent
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import com.lastaoutdoor.lasta.di.AppModule
import com.lastaoutdoor.lasta.ui.MainActivity
import com.lastaoutdoor.lasta.ui.screen.discovery.DiscoveryScreen
import com.lastaoutdoor.lasta.ui.theme.LastaTheme
import dagger.hilt.android.testing.HiltAndroidRule
import dagger.hilt.android.testing.HiltAndroidTest
import dagger.hilt.android.testing.UninstallModules
import org.junit.Assert.*
import org.junit.Before
import org.junit.Rule
import org.junit.Test
Expand Down Expand Up @@ -45,6 +40,6 @@ class MapScreenTest {

@Test
fun discoveryScreen_isDisplayed() {
composeRule.onNodeWithTag("Discovery").assertIsDisplayed()
composeRule.onNodeWithTag("discoveryScreen").assertIsDisplayed()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import androidx.compose.material3.Button
import androidx.compose.material3.Card
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
Expand All @@ -43,23 +42,22 @@ import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Dialog
import androidx.hilt.navigation.compose.hiltViewModel
import com.lastaoutdoor.lasta.R
import com.lastaoutdoor.lasta.data.model.activity.ActivityType
import com.lastaoutdoor.lasta.data.model.activity.OutdoorActivity
import com.lastaoutdoor.lasta.viewmodel.DiscoveryScreenViewModel

@Composable
fun DiscoveryScreen() {
/** this is called when discovery button is clicked */
Scaffold(
modifier = Modifier.testTag("Discovery"),
modifier = Modifier.testTag("discoveryScreen"),
floatingActionButton = { FloatingActionButtons() }) { innerPadding ->
Column(modifier = Modifier.padding(innerPadding)) { DiscoveryContent() }
}
}

@Composable
fun DiscoveryContent(discoveryScreenViewModel: DiscoveryScreenViewModel = hiltViewModel()) {
Column {
Column(modifier = Modifier.testTag("discoveryContent")) {
// link this with database or API depending from which we fetch
// OutdoorActivityList(outdoorActivityViewModel)

Expand All @@ -83,7 +81,7 @@ fun DiscoveryContent(discoveryScreenViewModel: DiscoveryScreenViewModel = hiltVi
@Composable
fun FloatingActionButtons() {
Column(
modifier = Modifier.padding(16.dp),
modifier = Modifier.padding(16.dp).testTag("floatingActionButtons"),
verticalArrangement = Arrangement.Bottom,
horizontalAlignment = Alignment.End) {
FloatingActionButton(onClick = { /*TODO*/}, modifier = Modifier.padding(bottom = 8.dp)) {
Expand All @@ -98,17 +96,19 @@ fun FloatingActionButtons() {
@Composable
fun OutdoorActivityList(outdoorActivities: List<OutdoorActivity>) {
/** Our list of activities which is lazy in order to display only the first ones. */
LazyColumn(modifier = Modifier.fillMaxSize(), contentPadding = PaddingValues(16.dp)) {
items(outdoorActivities) { outdoorActivity -> OutdoorActivityItem(outdoorActivity) }
}
LazyColumn(
modifier = Modifier.fillMaxSize().testTag("outdoorActivityList"),
contentPadding = PaddingValues(16.dp)) {
items(outdoorActivities) { outdoorActivity -> OutdoorActivityItem(outdoorActivity) }
}
}

@Composable
fun OutdoorActivityItem(
outdoorActivity: OutdoorActivity,
discoveryScreenViewModel: DiscoveryScreenViewModel = hiltViewModel()
) {
Card(modifier = Modifier.padding(8.dp)) {
Card(modifier = Modifier.padding(8.dp).testTag("outdoorActivityItem")) {
Column(modifier = Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) {
Row(
// modifier = Modifier.fillMaxWidth(),
Expand All @@ -129,7 +129,8 @@ fun OutdoorActivityItem(
// set its theme from themes.xml

modifier =
Modifier.border(
Modifier.testTag("startButton")
.border(
width = 1.dp,
color = Color(0xFFFF7009),
shape = RoundedCornerShape(size = 20.dp))
Expand Down Expand Up @@ -174,8 +175,8 @@ fun OutdoorActivityItem(
shape = RoundedCornerShape(size = 20.dp))
// .width(104.dp)
// .height(30.dp)
.background(
color = Color(0xFF6609FF), shape = RoundedCornerShape(size = 20.dp))) {
.background(color = Color(0xFF6609FF), shape = RoundedCornerShape(size = 20.dp))
.testTag("moreInfoButton")) {
Text(
text = "MORE INFO",
style =
Expand All @@ -190,7 +191,8 @@ fun OutdoorActivityItem(
Button(
onClick = { /* Switch to map view and see location of activity */},
modifier =
Modifier.border(
Modifier.testTag("mapButton")
.border(
width = 1.dp,
color = Color(0xFF0989FF),
shape = RoundedCornerShape(size = 20.dp))
Expand All @@ -213,27 +215,12 @@ fun OutdoorActivityItem(
}
}

@Composable
fun OutdoorActivityExample() {
MaterialTheme {
OutdoorActivityItem(OutdoorActivity(ActivityType.HIKING, 3, 5.0f, "2 hours", "Zurich"))
}
}

@Composable
fun OutdoorActivityListExample() {
MaterialTheme {
OutdoorActivityList(
List(10) { index -> OutdoorActivity(ActivityType.HIKING, 3, 5.0f, "2 hours", "Zurich") })
}
}

/** The composable for the "more info" dialog box */
@Composable
fun ActivityDialog(onDismissRequest: () -> Unit, outdoorActivity: OutdoorActivity) {
Dialog(onDismissRequest = { onDismissRequest() }) {
Card(
modifier = Modifier.fillMaxWidth().height(400.dp).padding(16.dp),
modifier = Modifier.fillMaxWidth().height(400.dp).padding(16.dp).testTag("activityDialog"),
shape = RoundedCornerShape(16.dp),
) {
Column(
Expand All @@ -244,20 +231,20 @@ fun ActivityDialog(onDismissRequest: () -> Unit, outdoorActivity: OutdoorActivit
Text(
text =
if (outdoorActivity.locationName != "") "Location: " + outdoorActivity.locationName
else "No available Location",
modifier = Modifier.padding(16.dp),
else "No available location",
modifier = Modifier.padding(16.dp).testTag("locationText"),
)
Text(
text =
if (outdoorActivity.duration != "") "Duration: " + outdoorActivity.duration
else "No available duration",
modifier = Modifier.padding(16.dp),
modifier = Modifier.padding(16.dp).testTag("durationText"),
)
Text(
text =
if (outdoorActivity.difficulty != 0) "Difficulty: ${outdoorActivity.difficulty}"
else "No available difficulty",
modifier = Modifier.padding(16.dp),
modifier = Modifier.padding(16.dp).testTag("difficultyText"),
)

Row(
Expand All @@ -266,7 +253,7 @@ fun ActivityDialog(onDismissRequest: () -> Unit, outdoorActivity: OutdoorActivit
) {
TextButton(
onClick = { onDismissRequest() },
modifier = Modifier.padding(8.dp),
modifier = Modifier.padding(8.dp).testTag("okButton"),
) {
Text("Ok")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class DiscoveryScreenViewModel
@Inject
constructor(private val repository: OutdoorActivityRepository) : ViewModel() {

var climbingActivities: ArrayList<OutdoorActivity> = ArrayList()
var climbingActivities: MutableList<OutdoorActivity> = mutableListOf()

fun fetchClimbingActivities(
rad: Double = 10000.0,
Expand All @@ -36,7 +36,7 @@ constructor(private val repository: OutdoorActivityRepository) : ViewModel() {
// start and join the thread, since we need the result before continuing
climbingThread.start()
climbingThread.join()

climbingActivities.clear()
climbingNodes.forEach { node ->
climbingActivities.add(
OutdoorActivity(
Expand Down