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

feat: Implement functionality of the contactme module #12

Merged
merged 1 commit into from
Dec 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -68,14 +68,13 @@ android {
dependencies {
implementation(project(":feature:home"))
implementation(project(":feature:photo"))

// TODO Wei
// implementation(project(":feature:contactme"))
implementation(project(":feature:contactme"))

implementation(project(":core:designsystem"))
implementation(project(":core:common"))
implementation(project(":core:data"))
implementation(project(":core:model"))
// TODO Wei
// implementation(project(":core:datastore"))

androidTestImplementation(project(":core:designsystem"))
Expand Down
8 changes: 8 additions & 0 deletions app/src/main/java/com/wei/picquest/navigation/PqNavHost.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import androidx.compose.ui.Modifier
import androidx.navigation.compose.NavHost
import androidx.window.layout.DisplayFeature
import com.wei.picquest.core.designsystem.ui.DeviceOrientation
import com.wei.picquest.feature.contactme.contactme.navigation.contactMeGraph
import com.wei.picquest.feature.home.home.navigation.homeGraph
import com.wei.picquest.feature.home.home.navigation.homeRoute
import com.wei.picquest.feature.photo.photolibrary.navigation.photoLibraryGraph
Expand Down Expand Up @@ -44,5 +45,12 @@ fun PqNavHost(
photoLibraryGraph(navController = navController)
},
)
contactMeGraph(
navController = navController,
contentType = contentType,
displayFeatures = displayFeatures,
navigationType = navigationType,
nestedGraphs = { },
)
}
}
9 changes: 6 additions & 3 deletions app/src/main/java/com/wei/picquest/ui/PqAppState.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import com.wei.picquest.core.designsystem.ui.PqNavigationType
import com.wei.picquest.core.designsystem.ui.currentDeviceOrientation
import com.wei.picquest.core.designsystem.ui.isBookPosture
import com.wei.picquest.core.designsystem.ui.isSeparating
import com.wei.picquest.feature.contactme.contactme.navigation.contactMeRoute
import com.wei.picquest.feature.contactme.contactme.navigation.navigateToContactMe
import com.wei.picquest.feature.home.home.navigation.homeRoute
import com.wei.picquest.feature.home.home.navigation.navigateToHome
import com.wei.picquest.feature.photo.photosearch.navigation.navigateToPhotoSearch
Expand Down Expand Up @@ -152,6 +154,7 @@ class PqAppState(
@Composable get() = when (currentDestination?.route) {
homeRoute -> TopLevelDestination.HOME
photoSearchRoute -> TopLevelDestination.PHOTO
contactMeRoute -> TopLevelDestination.CONTACT_ME
else -> null
}

Expand Down Expand Up @@ -203,9 +206,9 @@ class PqAppState(
topLevelNavOptions,
)

// TopLevelDestination.CONTACT_ME -> navController.navigateToContactMe(
// topLevelNavOptions,
// )
TopLevelDestination.CONTACT_ME -> navController.navigateToContactMe(
topLevelNavOptions,
)

else -> showFunctionalityNotAvailablePopup.value = true
}
Expand Down
1 change: 1 addition & 0 deletions feature/contactme/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
12 changes: 12 additions & 0 deletions feature/contactme/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
plugins {
alias(libs.plugins.pq.android.feature)
alias(libs.plugins.pq.android.library.compose)
alias(libs.plugins.pq.android.hilt)
}

android {
namespace = "com.wei.picquest.feature.contactme"
}

dependencies {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
package com.wei.picquest.feature.contactme.contactme

import androidx.activity.ComponentActivity
import androidx.annotation.StringRes
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.junit4.AndroidComposeTestRule
import androidx.compose.ui.test.onNodeWithContentDescription
import androidx.compose.ui.test.performClick
import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.window.layout.DisplayFeature
import com.google.common.truth.Truth
import com.wei.picquest.core.designsystem.theme.PqTheme
import com.wei.picquest.core.designsystem.ui.PqContentType
import com.wei.picquest.feature.contactme.R
import com.wei.picquest.feature.contactme.contactme.utilities.EMAIL
import com.wei.picquest.feature.contactme.contactme.utilities.LINKEDIN_URL
import com.wei.picquest.feature.contactme.contactme.utilities.NAME_ENG
import com.wei.picquest.feature.contactme.contactme.utilities.NAME_TW
import com.wei.picquest.feature.contactme.contactme.utilities.PHONE
import com.wei.picquest.feature.contactme.contactme.utilities.POSITION
import com.wei.picquest.feature.contactme.contactme.utilities.TIME_ZONE
import kotlin.properties.ReadOnlyProperty

/**
* Screen Robot for [ContactMeScreenTest].
*
* 遵循此模型,找到測試使用者介面元素、檢查其屬性、和透過測試規則執行動作:
* composeTestRule{.finder}{.assertion}{.action}
*
* Testing cheatsheet:
* https://developer.android.com/jetpack/compose/testing-cheatsheet
*/
internal fun contactMeScreenRobot(
composeTestRule: AndroidComposeTestRule<ActivityScenarioRule<ComponentActivity>, ComponentActivity>,
func: ContactMeScreenRobot.() -> Unit,
) = ContactMeScreenRobot(composeTestRule).apply(func)

internal open class ContactMeScreenRobot(
private val composeTestRule: AndroidComposeTestRule<ActivityScenarioRule<ComponentActivity>, ComponentActivity>,
) {
private fun AndroidComposeTestRule<*, *>.stringResource(@StringRes resId: Int) =
ReadOnlyProperty<Any?, String> { _, _ -> activity.getString(resId) }

private val profilePictureDescription by composeTestRule.stringResource(R.string.profile_picture)
private val linkedinString by composeTestRule.stringResource(R.string.linkedin)
private val emailString by composeTestRule.stringResource(R.string.email)
private val timezoneString by composeTestRule.stringResource(R.string.timezone)
private val callDescription by composeTestRule.stringResource(R.string.call)

private val profilePicture by lazy {
composeTestRule.onNodeWithContentDescription(
profilePictureDescription.format(testUiState.nameEng),
useUnmergedTree = true,
)
}
private val name by lazy {
composeTestRule.onNodeWithContentDescription(
testUiState.nameEng,
useUnmergedTree = true,
)
}
private val position by lazy {
composeTestRule.onNodeWithContentDescription(
testUiState.position,
useUnmergedTree = true,
)
}
private val linkedin by lazy {
composeTestRule.onNodeWithContentDescription(
linkedinString,
useUnmergedTree = true,
)
}
private val linkedinValue by lazy {
composeTestRule.onNodeWithContentDescription(
testUiState.linkedinUrl,
useUnmergedTree = true,
)
}
private val email by lazy {
composeTestRule.onNodeWithContentDescription(
emailString,
useUnmergedTree = true,
)
}
private val emailValue by lazy {
composeTestRule.onNodeWithContentDescription(
testUiState.email,
useUnmergedTree = true,
)
}
private val timeZone by lazy {
composeTestRule.onNodeWithContentDescription(
timezoneString,
useUnmergedTree = true,
)
}
private val timeZoneValue by lazy {
composeTestRule.onNodeWithContentDescription(
testUiState.timeZone,
useUnmergedTree = true,
)
}
private val call by lazy {
composeTestRule.onNodeWithContentDescription(
callDescription.format(testUiState.nameEng),
useUnmergedTree = true,
)
}

private var callClicked: Boolean = false

fun setContactMeScreenContent(
contentType: PqContentType,
displayFeature: List<DisplayFeature>,
) {
composeTestRule.setContent {
PqTheme {
ContactMeScreen(
uiStates = testUiState,
contentType = contentType,
displayFeatures = displayFeature,
onPhoneClick = { callClicked = true },
)
}
}
}

fun verifyProfilePictureDisplayed() {
profilePicture.assertExists().assertIsDisplayed()
}

fun verifyNameDisplayed() {
name.assertExists().assertIsDisplayed()
}

fun verifyPositionDisplayed() {
position.assertExists().assertIsDisplayed()
}

fun verifyCallDisplayed() {
call.assertExists().assertIsDisplayed()
}

fun verifyLinkedinExists() {
linkedin.assertExists()
}

fun verifyLinkedinValueExists() {
linkedinValue.assertExists()
}

fun verifyEmailExists() {
email.assertExists()
}

fun verifyEmailValueExists() {
emailValue.assertExists()
}

fun verifyTimeZoneExists() {
timeZone.assertExists()
}

fun verifyTimeZoneValueExists() {
timeZoneValue.assertExists()
}

infix fun call(func: ContactMeScreenCallRobot.() -> Unit): ContactMeScreenCallRobot {
call.assertExists().performClick()
return contactMeScreenCallRobot(composeTestRule) {
setIsCallClicked(callClicked)
func()
}
}
}

internal fun contactMeScreenCallRobot(
composeTestRule: AndroidComposeTestRule<ActivityScenarioRule<ComponentActivity>, ComponentActivity>,
func: ContactMeScreenCallRobot.() -> Unit,
) = ContactMeScreenCallRobot(composeTestRule).apply(func)

internal open class ContactMeScreenCallRobot(
private val composeTestRule: AndroidComposeTestRule<ActivityScenarioRule<ComponentActivity>, ComponentActivity>,
) {

private var isCallClicked: Boolean = false

fun setIsCallClicked(backClicked: Boolean) {
isCallClicked = backClicked
}

fun isCall() {
Truth.assertThat(isCallClicked).isTrue()
}
}

val testUiState = ContactMeViewState(
nameTw = NAME_TW,
nameEng = NAME_ENG,
position = POSITION,
phone = PHONE,
linkedinUrl = LINKEDIN_URL,
email = EMAIL,
timeZone = TIME_ZONE,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package com.wei.picquest.feature.contactme.contactme

import androidx.activity.ComponentActivity
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.window.layout.DisplayFeature
import com.wei.picquest.core.designsystem.ui.PqContentType
import org.junit.Rule
import org.junit.Test

/**
* UI tests for [ContactMeScreen] composable.
*/
class ContactMeScreenTest {

/**
* 通常我們使用 createComposeRule(),作為 composeTestRule
*
* 但若測試案例需查找資源檔 e.g. R.string.welcome。
* 使用 createAndroidComposeRule<ComponentActivity>(),作為 composeTestRule
*/
@get:Rule
val composeTestRule = createAndroidComposeRule<ComponentActivity>()

@Test
fun checkElementsVisibility_afterOpeningTheScreen() {
contactMeScreenRobot(composeTestRule) {
setContactMeScreenContent(
contentType = PqContentType.SINGLE_PANE,
displayFeature = emptyList<DisplayFeature>(),
)

verifyProfilePictureDisplayed()
verifyNameDisplayed()
verifyPositionDisplayed()
verifyCallDisplayed()
verifyLinkedinExists()
verifyLinkedinValueExists()
verifyEmailExists()
verifyEmailValueExists()
verifyTimeZoneExists()
verifyTimeZoneValueExists()
}
}

@Test
fun checkCallButtonAction_afterPress() {
contactMeScreenRobot(composeTestRule) {
setContactMeScreenContent(
contentType = PqContentType.SINGLE_PANE,
displayFeature = emptyList<DisplayFeature>(),
)
} call {
isCall()
}
}
}
4 changes: 4 additions & 0 deletions feature/contactme/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

</manifest>
Loading