Skip to content

Commit

Permalink
chore: merge with main
Browse files Browse the repository at this point in the history
  • Loading branch information
francelu committed Oct 14, 2024
2 parents b257014 + 17719dd commit 194c844
Show file tree
Hide file tree
Showing 11 changed files with 644 additions and 78 deletions.
17 changes: 11 additions & 6 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ plugins {
alias(libs.plugins.androidApplication)
alias(libs.plugins.jetbrainsKotlinAndroid)
alias(libs.plugins.ktfmt)
//alias(libs.plugins.sonar)
// alias(libs.plugins.sonar)
alias(libs.plugins.compose.compiler)
id("jacoco")

Expand Down Expand Up @@ -104,18 +104,18 @@ sonar {
// Each path may be absolute or relative to the project base directory.
property(
"sonar.junit.reportPaths",
"${project.layout.buildDirectory.get()}/test-results/testDebugunitTest/"
"${project.layout.buildDirectory.get()}/test-results/testDebugunitTest/",
)
// Paths to xml files with Android Lint issues. If the main flavor is changed, this file will
// have to be changed too.
property(
"sonar.androidLint.reportPaths",
"${project.layout.buildDirectory.get()}/reports/lint-results-debug.xml"
"${project.layout.buildDirectory.get()}/reports/lint-results-debug.xml",
)
// Paths to JaCoCo XML coverage report files.
property(
"sonar.coverage.jacoco.xmlReportPaths",
"${project.layout.buildDirectory.get()}/reports/jacoco/jacocoTestReport/jacocoTestReport.xml"
"${project.layout.buildDirectory.get()}/reports/jacoco/jacocoTestReport/jacocoTestReport.xml",
)
}
}
Expand Down Expand Up @@ -162,6 +162,7 @@ dependencies {
implementation(libs.material)
implementation(libs.androidx.lifecycle.runtime.ktx)
implementation(platform(libs.compose.bom))
implementation(libs.androidx.navigation.compose.v282)
implementation(libs.androidx.espresso.intents)
implementation(libs.androidx.espresso.core)

Expand Down Expand Up @@ -197,8 +198,11 @@ dependencies {
// ---------- Robolectric ------------
testImplementation(libs.robolectric)

// Hourglass, eye icons and others
// Material Icons
implementation(libs.androidx.material.icons.extended)
// Mockito for unit testing
testImplementation(libs.mockito.kotlin)
testImplementation(libs.mockito.core.v540)
}

tasks.withType<Test> {
Expand Down Expand Up @@ -239,5 +243,6 @@ tasks.register("jacocoTestReport", JacocoReport::class) {
fileTree(project.layout.buildDirectory.get()) {
include("outputs/unit_test_code_coverage/debugUnitTest/testDebugUnitTest.exec")
include("outputs/code_coverage/debugAndroidTest/connected/*/coverage.ec")
})
}
)
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package com.android.periodpals.ui.navigation

import android.annotation.SuppressLint
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.assertIsNotSelected
import androidx.compose.ui.test.assertIsSelected
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.performClick
import org.junit.Rule
import org.junit.Test

class BottomNavigationMenuTest {

@get:Rule val composeTestRule = createComposeRule()

@Test
fun bottomNavigationMenu_displaysAllTabs() {
composeTestRule.setContent {
BottomNavigationMenu(
onTabSelect = {}, tabList = LIST_TOP_LEVEL_DESTINATION, selectedItem = "Map")
}

composeTestRule.onNodeWithTag("bottomNavigationMenu").assertIsDisplayed()
LIST_TOP_LEVEL_DESTINATION.forEach { tab ->
composeTestRule.onNodeWithTag(tab.textId).assertIsDisplayed()
}
}

@SuppressLint("UnrememberedMutableState")
@Test
fun bottomNavigationMenu_clickOnTab_changesSelection() {
var selectedTab by mutableStateOf(Route.MAP) // Initially selected tab is "MAP"

// Set the composable content with an initial selected tab
composeTestRule.setContent {
BottomNavigationMenu(
onTabSelect = { selectedTab = it.route },
tabList = LIST_TOP_LEVEL_DESTINATION,
selectedItem = selectedTab)
}

// Initially, verify that "MAP" is selected
composeTestRule.onNodeWithTag("Map").assertIsSelected()

// Perform a click on the "Alert" tab
composeTestRule.onNodeWithTag("Alert").performClick()

// Now check that the "Alert" tab is selected
composeTestRule.onNodeWithTag("Alert").assertIsSelected()

// Optionally, check that the previously selected "Map" tab is no longer selected
composeTestRule.onNodeWithTag("Map").assertIsNotSelected()
}

@Test
fun bottomNavigationMenu_iconAndLabelAreDisplayedCorrectly() {
composeTestRule.setContent {
BottomNavigationMenu(
onTabSelect = {}, tabList = LIST_TOP_LEVEL_DESTINATION, selectedItem = "Profile")
}

LIST_TOP_LEVEL_DESTINATION.forEach { tab ->
composeTestRule.onNodeWithTag(tab.textId).assertIsDisplayed()
composeTestRule.onNodeWithTag(tab.textId).assertIsDisplayed()
}
}

@Test
fun bottomNavigationMenu_initialSelectionIsCorrect() {
composeTestRule.setContent {
BottomNavigationMenu(
onTabSelect = {}, tabList = LIST_TOP_LEVEL_DESTINATION, selectedItem = "Timer")
}

composeTestRule.onNodeWithTag("Timer").assertIsSelected()
}

@Test
fun bottomNavigationMenu_selectingSameTabDoesNotCrash() {
var selectedTab = Route.ALERT_LIST

composeTestRule.setContent {
BottomNavigationMenu(
onTabSelect = { selectedTab = it.route },
tabList = LIST_TOP_LEVEL_DESTINATION,
selectedItem = selectedTab)
}

composeTestRule.onNodeWithTag("Alert List").performClick()
composeTestRule.onNodeWithTag("Alert List").assertIsSelected()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package com.android.periodpals.ui.navigation

import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.assertIsNotDisplayed
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.performClick
import org.junit.Assert.assertThrows
import org.junit.Rule
import org.junit.Test

class TopAppBarTest {

@get:Rule val composeTestRule = createComposeRule()

@Test
fun topAppBar_displaysTitle() {
composeTestRule.setContent { TopAppBar(title = "Tampon Timer") }

composeTestRule.onNodeWithTag("topBar").assertIsDisplayed()
composeTestRule.onNodeWithTag("screenTitle").assertIsDisplayed()
}

@Test
fun topAppBar_displaysBackButton() {
composeTestRule.setContent { TopAppBar(title = "Tampon Timer", backButton = true) }

composeTestRule.onNodeWithTag("topBar").assertIsDisplayed()
composeTestRule.onNodeWithTag("goBackButton").assertIsDisplayed()
}

@Test
fun topAppBar_backButtonClick() {
var backButtonClicked = false

composeTestRule.setContent {
TopAppBar(
title = "Tampon Timer",
backButton = true,
onBackButtonClick = { backButtonClicked = true })
}

composeTestRule.onNodeWithTag("goBackButton").performClick()
assert(backButtonClicked)
}

@Test
fun topAppBar_noBackButton() {
composeTestRule.setContent { TopAppBar(title = "Tampon Timer", backButton = false) }

composeTestRule.onNodeWithTag("topBar").assertIsDisplayed()
composeTestRule.onNodeWithTag("goBackButton").assertDoesNotExist()
}

@Test
fun topAppBar_backButtonTrue_onBackButtonClickNull_throwsException() {
val exception =
assertThrows(IllegalArgumentException::class.java) {
composeTestRule.setContent {
TopAppBar(title = "Test Title", backButton = true, onBackButtonClick = null)
}
}
assert(exception.message == "onBackButtonClick must be provided when backButton is true")
}

@Test
fun topAppBar_backButtonTrue_onBackButtonClickNotNull_doesNotThrowException() {
composeTestRule.setContent {
TopAppBar(title = "Test Title", backButton = true, onBackButtonClick = { /* Do nothing */})
}
composeTestRule.onNodeWithTag("topBar").assertIsDisplayed()
composeTestRule.onNodeWithTag("goBackButton").assertIsDisplayed()
}

@Test
fun topAppBar_backButtonFalse_onBackButtonClickNull_doesNotThrowException() {
composeTestRule.setContent {
TopAppBar(title = "Test Title", backButton = false, onBackButtonClick = null)
}
composeTestRule.onNodeWithTag("topBar").assertIsDisplayed()
composeTestRule.onNodeWithTag("goBackButton").assertIsNotDisplayed()
}

@Test
fun topAppBar_backButtonFalse_onBackButtonClickNotNull_doesNotThrowException() {
composeTestRule.setContent {
TopAppBar(title = "Test Title", backButton = false, onBackButtonClick = { /* Do nothing */})
}
composeTestRule.onNodeWithTag("topBar").assertIsDisplayed()
composeTestRule.onNodeWithTag("goBackButton").assertIsNotDisplayed()
}
}
74 changes: 71 additions & 3 deletions app/src/main/java/com/android/periodpals/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
package com.android.periodpals

// import androidx.navigation.compose.NavHost
// import androidx.navigation.compose.composable
// import androidx.navigation.navigation
// import com.android.periodpals.ui.navigation.Route
// import com.android.periodpals.ui.navigation.Screen
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
Expand All @@ -18,7 +23,8 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.pointer.PointerIcon.Companion.Text
import androidx.compose.ui.unit.dp
import com.android.periodpals.ui.profile.CreateProfile
import androidx.navigation.compose.rememberNavController
import com.android.periodpals.ui.navigation.NavigationActions
import com.android.periodpals.ui.theme.PeriodPalsAppTheme
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
Expand All @@ -32,14 +38,76 @@ class MainActivity : ComponentActivity() {
PeriodPalsAppTheme {
// A surface container using the 'background' color from the theme
Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background) {
// CountriesList()
CreateProfile()
PeriodPalsApp()
}
}
}
}
}

@Composable
fun PeriodPalsApp() {
val navController = rememberNavController()
val navigationActions = NavigationActions(navController)

CountriesList()

// TODO: Uncomment what has been implemented

// NavHost(navController = navController, startDestination = Route.AUTH) {
// // Authentication
// navigation(
// startDestination = Screen.AUTH,
// route = Route.AUTH,
// ) {
// composable(Screen.AUTH) { SignInScreen(navigationActions) }
// composable(Screen.REGISTER) { RegisterScreen(navigationActions) }
// composable(Screen.CREATE_PROFILE) { CreateProfileScreen(navigationActions) }
// }
//
// // Alert push notifications
// navigation(
// startDestination = Screen.ALERT,
// route = Route.ALERT,
// ) {
// composable(Screen.ALERT) { AlertScreen(navigationActions) }
// }
//
// // Notifications received or pushed
// navigation(
// startDestination = Screen.ALERT_LIST,
// route = Route.ALERT_LIST,
// ) {
// composable(Screen.ALERT_LIST) { AlertListScreen(navigationActions) }
// }
//
// // Map
// navigation(
// startDestination = Screen.MAP,
// route = Route.MAP,
// ) {
// composable(Screen.MAP) { MapScreen(navigationActions) }
// }
//
// // Timer
// navigation(
// startDestination = Screen.TIMER,
// route = Route.TIMER,
// ) {
// composable(Screen.TIMER) { TimerScreen(navigationActions) }
// }
//
// // Profile
// navigation(
// startDestination = Screen.PROFILE,
// route = Route.PROFILE,
// ) {
// composable(Screen.PROFILE) { ProfileScreen(navigationActions) }
// composable(Screen.EDIT_PROFILE) { EditProfileScreen(navigationActions) }
// }
// }
}

@Composable
fun CountriesList(dispatcher: CoroutineDispatcher = Dispatchers.IO) {
var countries by remember { mutableStateOf<List<Country>>(listOf()) }
Expand Down
Loading

0 comments on commit 194c844

Please sign in to comment.