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

SpeziViews #140

Merged
merged 32 commits into from
Nov 24, 2024
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
aaf29df
SpeziViews
pauljohanneskraft Nov 9, 2024
11efb05
intermediate
pauljohanneskraft Nov 10, 2024
5fa1c8d
Update SuspendButton implementation
pauljohanneskraft Nov 10, 2024
37159b8
Update tests for PersonalInfo
pauljohanneskraft Nov 11, 2024
f3f516b
Finish up SpeziViews
pauljohanneskraft Nov 11, 2024
306b46b
Rename test
pauljohanneskraft Nov 11, 2024
dc8133b
Fix import issues
pauljohanneskraft Nov 11, 2024
4258420
Remove import issues
pauljohanneskraft Nov 11, 2024
e3baa1b
Add import
pauljohanneskraft Nov 11, 2024
0a94bc5
Move files into the correct repositories
pauljohanneskraft Nov 11, 2024
40601b4
Rename package
pauljohanneskraft Nov 11, 2024
728e534
Remove nonimported usages of PersonNameComponents
pauljohanneskraft Nov 11, 2024
7928eb2
Update previews
pauljohanneskraft Nov 11, 2024
d974d16
fix imports
pauljohanneskraft Nov 11, 2024
5143693
detekt
pauljohanneskraft Nov 11, 2024
2c8e57b
Fix tests
pauljohanneskraft Nov 11, 2024
10a2543
fix
pauljohanneskraft Nov 12, 2024
5c2d056
Update
pauljohanneskraft Nov 18, 2024
6f5b66c
review adaptions
pauljohanneskraft Nov 18, 2024
e02bad3
tests
pauljohanneskraft Nov 18, 2024
0a0fc2f
adapt tests
pauljohanneskraft Nov 18, 2024
35f5076
update
pauljohanneskraft Nov 19, 2024
307d84c
Adapt contact
pauljohanneskraft Nov 19, 2024
fcda9c7
Add modifier parameters
pauljohanneskraft Nov 19, 2024
088e66f
Fix tests
pauljohanneskraft Nov 19, 2024
2207da9
detekt
pauljohanneskraft Nov 19, 2024
c84e748
update
pauljohanneskraft Nov 19, 2024
d184b64
adapt tests
pauljohanneskraft Nov 19, 2024
58dc269
small update
pauljohanneskraft Nov 19, 2024
858c75f
Make sure to only trigger validation after the first input
pauljohanneskraft Nov 19, 2024
f3af7e3
detekt
pauljohanneskraft Nov 19, 2024
ace70d4
Add onSubmit
pauljohanneskraft Nov 19, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ package edu.stanford.bdh.engagehf.contact.data

import com.google.firebase.firestore.DocumentSnapshot
import edu.stanford.spezi.core.design.component.StringResource
import edu.stanford.spezi.core.design.views.personalInfo.PersonNameComponents
import edu.stanford.spezi.modules.contact.model.Contact
import edu.stanford.spezi.modules.contact.model.ContactOption
import edu.stanford.spezi.modules.contact.model.PersonNameComponents
import edu.stanford.spezi.modules.contact.model.call
import edu.stanford.spezi.modules.contact.model.email
import javax.inject.Inject
Expand All @@ -19,11 +19,12 @@ class ContactDocumentToContactMapper @Inject constructor() {
}
val components = contactName.split(", ")
val nameComponents = components.firstOrNull()?.split(" ")
val personNameComponents = PersonNameComponents(
givenName = nameComponents?.getOrNull(0),
familyName = nameComponents?.drop(1)
?.joinToString(" ") // assigning everything besides given name here
)
val personNameComponents =
PersonNameComponents(
givenName = nameComponents?.getOrNull(0),
familyName = nameComponents?.drop(1)
?.joinToString(" ") // assigning everything besides given name here
)
val title = components.lastOrNull()
val contactEmail = document.getString(CONTACT_EMAIL_FIELD)
val phone = document.getString(CONTACT_PHONE_FIELD)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@
import edu.stanford.spezi.core.design.theme.SpeziTheme
import edu.stanford.spezi.core.design.theme.TextStyles
import edu.stanford.spezi.core.design.theme.ThemePreviews
import edu.stanford.spezi.core.design.views.personalInfo.PersonNameComponents
import edu.stanford.spezi.core.notification.R
import edu.stanford.spezi.modules.contact.ContactComposable
import edu.stanford.spezi.modules.contact.model.Contact
import edu.stanford.spezi.modules.contact.model.ContactOption
import edu.stanford.spezi.modules.contact.model.PersonNameComponents
import edu.stanford.spezi.modules.contact.model.call
import edu.stanford.spezi.modules.contact.model.email
import edu.stanford.spezi.modules.contact.model.website
Expand Down Expand Up @@ -109,7 +109,10 @@
ContactScreenViewModel.UiState.Error("An error occurred"),
ContactScreenViewModel.UiState.ContactLoaded(
contact = Contact(
name = PersonNameComponents(givenName = "Leland", familyName = "Stanford"),
name = PersonNameComponents(
givenName = "Leland",
familyName = "Stanford"

Check warning on line 114 in app/src/main/kotlin/edu/stanford/bdh/engagehf/contact/ui/ContactScreen.kt

View check run for this annotation

Codecov / codecov/patch

app/src/main/kotlin/edu/stanford/bdh/engagehf/contact/ui/ContactScreen.kt#L112-L114

Added lines #L112 - L114 were not covered by tests
),
image = ImageResource.Vector(Icons.Default.AccountBox),
title = StringResource("University Founder"),
description = StringResource(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package edu.stanford.bdh.engagehf.contact.data
import com.google.common.truth.Truth.assertThat
import com.google.firebase.firestore.DocumentSnapshot
import edu.stanford.spezi.core.design.component.StringResource
import edu.stanford.spezi.modules.contact.model.PersonNameComponents
import edu.stanford.spezi.core.design.views.personalInfo.PersonNameComponents
import io.mockk.every
import io.mockk.mockk
import org.junit.Test
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package edu.stanford.spezi.core.design.personalInfo

import androidx.compose.ui.test.junit4.createComposeRule
import edu.stanford.spezi.core.design.personalInfo.composables.NameFieldsTestComposable
import edu.stanford.spezi.core.design.personalInfo.simulators.NameFieldsTestSimulator
import org.junit.Before
import org.junit.Rule
import org.junit.Test

class NameFieldsTest {

@get:Rule
val composeTestRule = createComposeRule()

@Before
fun init() {
composeTestRule.setContent {
NameFieldsTestComposable()
}
}

@Test
fun testNameFields() {
nameFields {
assertTextExists("First Name")
assertTextExists("Last Name")

enterText("enter your first name", "Leland")
enterText("enter your last name", "Stanford")
}
}

private fun nameFields(block: NameFieldsTestSimulator.() -> Unit) {
NameFieldsTestSimulator(composeTestRule).apply(block)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package edu.stanford.spezi.core.design.personalInfo

import androidx.compose.ui.test.junit4.createComposeRule
import edu.stanford.spezi.core.design.personalInfo.composables.UserProfileTestComposable
import edu.stanford.spezi.core.design.personalInfo.simulators.UserProfileTestSimulator
import org.junit.Before
import org.junit.Rule
import org.junit.Test

class UserProfileTest {

@get:Rule
val composeTestRule = createComposeRule()

@Before
fun init() {
composeTestRule.setContent {
UserProfileTestComposable()
}
}

@Test
fun testUserProfile() {
userProfile {
assertUserInitialsExists(true, "PS")
assertUserInitialsExists(true, "LS")
waitUntilUserInitialsDisappear("LS")
assertImageExists()
}
}

private fun userProfile(block: UserProfileTestSimulator.() -> Unit) {
UserProfileTestSimulator(composeTestRule).apply(block)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package edu.stanford.spezi.core.design.personalInfo.composables

import androidx.compose.foundation.layout.Column
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import edu.stanford.spezi.core.design.component.StringResource
import edu.stanford.spezi.core.design.views.personalInfo.PersonNameComponents
import edu.stanford.spezi.core.design.views.personalInfo.fields.NameFieldRow

@Composable
fun NameFieldsTestComposable() {
val name = remember { mutableStateOf(PersonNameComponents()) }

Column {
NameFieldRow(StringResource("First Name"), name, PersonNameComponents::givenName) {
Text("enter your first name")
}

HorizontalDivider()

NameFieldRow(StringResource("Last Name"), name, PersonNameComponents::familyName) {
Text("enter your last name")
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package edu.stanford.spezi.core.design.personalInfo.composables

import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.height
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Person
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import edu.stanford.spezi.core.design.component.ImageResource
import edu.stanford.spezi.core.design.views.personalInfo.PersonNameComponents
import edu.stanford.spezi.core.design.views.personalInfo.UserProfileComposable
import kotlinx.coroutines.delay
import kotlin.time.Duration.Companion.seconds

@Composable
fun UserProfileTestComposable() {
Column {
UserProfileComposable(
PersonNameComponents(
givenName = "Paul",
familyName = "Schmiedmayer"
),
Modifier.height(100.dp),
)
UserProfileComposable(
PersonNameComponents(
givenName = "Leland",
familyName = "Stanford"
),
Modifier.height(200.dp),
) {
delay(0.5.seconds)
ImageResource.Vector(Icons.Default.Person)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package edu.stanford.spezi.core.design.personalInfo.simulators

import androidx.compose.ui.test.junit4.ComposeTestRule
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performTextInput

class NameFieldsTestSimulator(
private val composeTestRule: ComposeTestRule,
) {

fun assertTextExists(text: String) {
composeTestRule
.onNodeWithText(text)
.assertExists()
}

fun enterText(placeholder: String, text: String) {
composeTestRule
.onNodeWithText(placeholder)
.performTextInput(text)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package edu.stanford.spezi.core.design.personalInfo.simulators

import androidx.compose.ui.test.assertCountEquals
import androidx.compose.ui.test.junit4.ComposeTestRule
import androidx.compose.ui.test.onAllNodesWithContentDescription
import androidx.compose.ui.test.onAllNodesWithText
import androidx.compose.ui.test.onNodeWithText

class UserProfileTestSimulator(
private val composeTestRule: ComposeTestRule,
) {

fun assertUserInitialsExists(exists: Boolean, text: String) {
val node = composeTestRule
.onNodeWithText(text)
if (exists) {
node.assertExists()
} else {
node.assertDoesNotExist()
}
}

fun waitUntilUserInitialsDisappear(text: String, timeoutMillis: Long = 1_000) {
composeTestRule.waitUntil(timeoutMillis = timeoutMillis) {
composeTestRule.onAllNodesWithText(text)
.fetchSemanticsNodes().isEmpty()
}
}

fun assertImageExists() {
composeTestRule
.onAllNodesWithContentDescription("")
.assertCountEquals(1)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package edu.stanford.spezi.core.design.validation

import androidx.compose.ui.test.junit4.createComposeRule
import edu.stanford.spezi.core.design.validation.composables.FocusValidationRules
import edu.stanford.spezi.core.design.validation.simulators.FocusValidationRulesSimulator
import org.junit.Before
import org.junit.Rule
import org.junit.Test

class FocusValidationRulesTest {

@get:Rule
val composeTestRule = createComposeRule()

@Before
fun init() {
composeTestRule.setContent {
FocusValidationRules()
}
}

@Test
fun testFocusValidationRules() {
focusValidationRules {
assertHasEngines(true)
assertInputValid(false)
assertPasswordMessageExists(false)
assertEmptyMessageExists(false)
clickValidateButton()
assertLastState(false)
assertPasswordMessageExists(true)
assertEmptyMessageExists(true)
}
}

private fun focusValidationRules(block: FocusValidationRulesSimulator.() -> Unit) {
FocusValidationRulesSimulator(composeTestRule).apply(block)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package edu.stanford.spezi.core.design.validation.composables

import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.material3.Button
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import edu.stanford.spezi.core.design.component.StringResource
import edu.stanford.spezi.core.design.views.validation.Validate
import edu.stanford.spezi.core.design.views.validation.ValidationRule
import edu.stanford.spezi.core.design.views.validation.minimalPassword
import edu.stanford.spezi.core.design.views.validation.nonEmpty
import edu.stanford.spezi.core.design.views.validation.state.ReceiveValidation
import edu.stanford.spezi.core.design.views.validation.state.ValidationContext
import edu.stanford.spezi.core.design.views.validation.views.VerifiableTextField

enum class Field {
INPUT, NON_EMPTY_INPUT
}

@Composable
fun FocusValidationRules() {
val input = remember { mutableStateOf("") }
val nonEmptyInput = remember { mutableStateOf("") }
val validationContext = remember { mutableStateOf(ValidationContext()) }
val lastValid = remember { mutableStateOf<Boolean?>(null) }
val switchFocus = remember { mutableStateOf(false) }

ReceiveValidation(validationContext) {
Column {
Text("Has Engines: ${if (!validationContext.value.isEmpty) "Yes" else "No"}")
Text("Input Valid: ${if (validationContext.value.allInputValid) "Yes" else "No"}")
lastValid.value?.let { lastValid ->
Text("Last state: ${if (lastValid) "valid" else "invalid"}")
}
Button(
onClick = {
val newLastValid = validationContext.value
.validateHierarchy(switchFocus.value)
lastValid.value = newLastValid
}
) {
Text("Validate")
}
Row {
Text("Switch Focus")
Switch(switchFocus.value, onCheckedChange = { switchFocus.value = it })
}

Validate(input.value, rules = listOf(ValidationRule.minimalPassword)) {
VerifiableTextField(StringResource(Field.INPUT.name), input)
}

Validate(nonEmptyInput.value, rules = listOf(ValidationRule.nonEmpty)) {
VerifiableTextField(StringResource(Field.NON_EMPTY_INPUT.name), nonEmptyInput)
}
}
}
}
Loading
Loading