Ignore line breaks in single line text inputs

This commit is contained in:
cketti 2024-03-06 17:22:39 +01:00
parent 87947e1e77
commit e142ba4ca3
8 changed files with 226 additions and 95 deletions

View file

@ -5,6 +5,12 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
import app.k9mail.core.ui.compose.theme.PreviewWithThemes
private val LINE_BREAK = "[\\r\\n]".toRegex()
internal fun stripLineBreaks(onValueChange: (String) -> Unit): (String) -> Unit = { value ->
onValueChange(value.replace(LINE_BREAK, replacement = ""))
}
internal fun selectLabel(
label: String?,
isRequired: Boolean,

View file

@ -26,7 +26,7 @@ fun TextFieldOutlined(
) {
MaterialOutlinedTextField(
value = value,
onValueChange = onValueChange,
onValueChange = if (isSingleLine) stripLineBreaks(onValueChange) else onValueChange,
modifier = modifier,
enabled = isEnabled,
label = selectLabel(label, isRequired),

View file

@ -22,7 +22,7 @@ fun TextFieldOutlinedEmailAddress(
) {
MaterialOutlinedTextField(
value = value,
onValueChange = onValueChange,
onValueChange = stripLineBreaks(onValueChange),
modifier = modifier,
enabled = isEnabled,
label = selectLabel(label, isRequired),

View file

@ -35,7 +35,7 @@ fun TextFieldOutlinedPassword(
MaterialOutlinedTextField(
value = value,
onValueChange = onValueChange,
onValueChange = stripLineBreaks(onValueChange),
modifier = modifier,
enabled = isEnabled,
label = selectLabel(label, isRequired),
@ -70,7 +70,7 @@ fun TextFieldOutlinedPassword(
) {
MaterialOutlinedTextField(
value = value,
onValueChange = onValueChange,
onValueChange = stripLineBreaks(onValueChange),
modifier = modifier,
enabled = isEnabled,
label = selectLabel(label, isRequired),

View file

@ -0,0 +1,50 @@
package app.k9mail.core.ui.compose.designsystem.atom.textfield
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.performTextInput
import app.k9mail.core.ui.compose.testing.ComposeTest
import assertk.assertThat
import assertk.assertions.isEqualTo
import org.junit.Test
private const val TEST_TAG = "TextFieldOutlinedEmailAddress"
class TextFieldOutlinedEmailAddressKtTest : ComposeTest() {
@Test
fun `should call onValueChange when value changes`() = runComposeTest {
var value = "initial"
setContent {
TextFieldOutlinedEmailAddress(
value = value,
onValueChange = { value = it },
modifier = Modifier.testTag(TEST_TAG),
)
}
onNodeWithTag(TEST_TAG).performClick()
onNodeWithTag(TEST_TAG).performTextInput(" + added text")
assertThat(value).isEqualTo("initial + added text")
}
@Test
fun `should strip line breaks before onValueChange is called`() = runComposeTest {
var value = ""
setContent {
TextFieldOutlinedEmailAddress(
value = value,
onValueChange = { value = it },
modifier = Modifier.testTag(TEST_TAG),
)
}
onNodeWithTag(TEST_TAG).performClick()
onNodeWithTag(TEST_TAG).performTextInput("one\n two")
assertThat(value).isEqualTo("one two")
}
}

View file

@ -0,0 +1,88 @@
package app.k9mail.core.ui.compose.designsystem.atom.textfield
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.performTextInput
import app.k9mail.core.ui.compose.testing.ComposeTest
import assertk.assertThat
import assertk.assertions.isEqualTo
import org.junit.Test
private const val TEST_TAG = "TextFieldOutlined"
class TextFieldOutlinedKtTest : ComposeTest() {
@Test
fun `should call onValueChange when value changes with isSingleLine = false`() = runComposeTest {
var value = "initial"
setContent {
TextFieldOutlined(
value = value,
onValueChange = { value = it },
isSingleLine = false,
modifier = Modifier.testTag(TEST_TAG),
)
}
onNodeWithTag(TEST_TAG).performClick()
onNodeWithTag(TEST_TAG).performTextInput(" + added text")
assertThat(value).isEqualTo("initial + added text")
}
@Test
fun `should call onValueChange when value changes with isSingleLine = true`() = runComposeTest {
var value = "initial"
setContent {
TextFieldOutlined(
value = value,
onValueChange = { value = it },
isSingleLine = true,
modifier = Modifier.testTag(TEST_TAG),
)
}
onNodeWithTag(TEST_TAG).performClick()
onNodeWithTag(TEST_TAG).performTextInput(" + added text")
assertThat(value).isEqualTo("initial + added text")
}
@Test
fun `should allow line breaks when isSingleLine = false`() = runComposeTest {
var value = ""
setContent {
TextFieldOutlined(
value = value,
onValueChange = { value = it },
isSingleLine = false,
modifier = Modifier.testTag(TEST_TAG),
)
}
onNodeWithTag(TEST_TAG).performClick()
onNodeWithTag(TEST_TAG).performTextInput("one\ntwo")
assertThat(value).isEqualTo("one\ntwo")
}
@Test
fun `should strip line breaks before onValueChange is called when isSingleLine = true`() = runComposeTest {
var value = ""
setContent {
TextFieldOutlined(
value = value,
onValueChange = { value = it },
isSingleLine = true,
modifier = Modifier.testTag(TEST_TAG),
)
}
onNodeWithTag(TEST_TAG).performClick()
onNodeWithTag(TEST_TAG).performTextInput("one\n two")
assertThat(value).isEqualTo("one two")
}
}

View file

@ -1,18 +1,24 @@
package app.k9mail.core.ui.compose.designsystem.atom.textfield
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.test.SemanticsNodeInteraction
import androidx.compose.ui.test.SemanticsNodeInteractionsProvider
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.onNodeWithContentDescription
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 app.k9mail.core.ui.compose.designsystem.R
import app.k9mail.core.ui.compose.testing.ComposeTest
import assertk.assertThat
import assertk.assertions.isEqualTo
import assertk.assertions.isTrue
import org.junit.Test
private const val PASSWORD = "Password input"
private const val TEST_TAG = "TextFieldOutlinedPassword"
class TextFieldOutlinedPasswordKtTest : ComposeTest() {
@ -131,6 +137,78 @@ class TextFieldOutlinedPasswordKtTest : ComposeTest() {
onNodeWithText(PASSWORD).assertDoesNotExist()
}
@Test
fun `variant 1 should call onValueChange when value changes`() = runComposeTest {
var value = "initial"
setContent {
TextFieldOutlinedPassword(
value = value,
onValueChange = { value = it },
modifier = Modifier.testTag(TEST_TAG),
)
}
onNodeWithTag(TEST_TAG).performClick()
onNodeWithTag(TEST_TAG).performTextInput(" + added text")
assertThat(value).isEqualTo("initial + added text")
}
@Test
fun `variant 2 should call onValueChange when value changes`() = runComposeTest {
var value = "initial"
setContent {
TextFieldOutlinedPassword(
value = value,
onValueChange = { value = it },
isPasswordVisible = false,
onPasswordVisibilityToggleClicked = {},
modifier = Modifier.testTag(TEST_TAG),
)
}
onNodeWithTag(TEST_TAG).performClick()
onNodeWithTag(TEST_TAG).performTextInput(" + added text")
assertThat(value).isEqualTo("initial + added text")
}
@Test
fun `variant 1 should strip line breaks before onValueChange is called`() = runComposeTest {
var value = ""
setContent {
TextFieldOutlinedPassword(
value = value,
onValueChange = { value = it },
modifier = Modifier.testTag(TEST_TAG),
)
}
onNodeWithTag(TEST_TAG).performClick()
onNodeWithTag(TEST_TAG).performTextInput("one\n two")
assertThat(value).isEqualTo("one two")
}
@Test
fun `variant 2 should strip line breaks before onValueChange is called`() = runComposeTest {
var value = ""
setContent {
TextFieldOutlinedPassword(
value = value,
onValueChange = { value = it },
isPasswordVisible = false,
onPasswordVisibilityToggleClicked = {},
modifier = Modifier.testTag(TEST_TAG),
)
}
onNodeWithTag(TEST_TAG).performClick()
onNodeWithTag(TEST_TAG).performTextInput("one\n two")
assertThat(value).isEqualTo("one two")
}
private fun SemanticsNodeInteractionsProvider.onShowPasswordNode(): SemanticsNodeInteraction {
return onNodeWithContentDescription(
getString(R.string.designsystem_atom_password_textfield_show_password),

View file

@ -1,91 +0,0 @@
package app.k9mail.core.ui.compose.designsystem.atom.textfield
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.performTextInput
import app.k9mail.core.ui.compose.testing.ComposeTest
import assertk.assertThat
import assertk.assertions.isEqualTo
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.ParameterizedRobolectricTestRunner
data class TextInputTextFieldTestData(
val name: String,
val input: String,
val content: @Composable (
value: String,
onValueChange: (String) -> Unit,
modifier: Modifier,
) -> Unit,
)
@RunWith(ParameterizedRobolectricTestRunner::class)
class TextInputTextFieldTest(
data: TextInputTextFieldTestData,
) : ComposeTest() {
private val testSubjectName = data.name
private val testSubject = data.content
private val testInput = data.input
@Test
fun `should call onValueChange when value changes`() = runComposeTest {
var value = testInput
setContent {
testSubject(
value,
{ value = it },
Modifier.testTag(testSubjectName),
)
}
onNodeWithTag(testSubjectName).performClick()
onNodeWithTag(testSubjectName).performTextInput(" + added text")
assertThat(value).isEqualTo("$testInput + added text")
}
companion object {
@JvmStatic
@ParameterizedRobolectricTestRunner.Parameters(name = "{0}")
fun data(): List<TextInputTextFieldTestData> = listOf(
TextInputTextFieldTestData(
name = "TextFieldOutlined",
input = "value",
content = { value, onValueChange: (String) -> Unit, modifier ->
TextFieldOutlined(
value = value,
onValueChange = onValueChange,
modifier = modifier,
)
},
),
TextInputTextFieldTestData(
name = "TextFieldOutlinedPassword",
input = "value",
content = { value, onValueChange: (String) -> Unit, modifier ->
TextFieldOutlinedPassword(
value = value,
onValueChange = onValueChange,
modifier = modifier,
)
},
),
TextInputTextFieldTestData(
name = "TextFieldOutlinedEmail",
input = "value",
content = { value, onValueChange: (String) -> Unit, modifier ->
TextFieldOutlinedEmailAddress(
value = value,
onValueChange = onValueChange,
modifier = modifier,
)
},
),
)
}
}