From e142ba4ca349acc9c20a91af83902cc277a3e0e9 Mon Sep 17 00:00:00 2001 From: cketti Date: Wed, 6 Mar 2024 17:22:39 +0100 Subject: [PATCH] Ignore line breaks in single line text inputs --- .../atom/textfield/TextFieldCommon.kt | 6 ++ .../atom/textfield/TextFieldOutlined.kt | 2 +- .../TextFieldOutlinedEmailAddress.kt | 2 +- .../textfield/TextFieldOutlinedPassword.kt | 4 +- .../TextFieldOutlinedEmailAddressKtTest.kt | 50 ++++++++++ .../atom/textfield/TextFieldOutlinedKtTest.kt | 88 ++++++++++++++++++ .../TextFieldOutlinedPasswordKtTest.kt | 78 ++++++++++++++++ .../atom/textfield/TextInputTextFieldTest.kt | 91 ------------------- 8 files changed, 226 insertions(+), 95 deletions(-) create mode 100644 core/ui/compose/designsystem/src/test/kotlin/app/k9mail/core/ui/compose/designsystem/atom/textfield/TextFieldOutlinedEmailAddressKtTest.kt create mode 100644 core/ui/compose/designsystem/src/test/kotlin/app/k9mail/core/ui/compose/designsystem/atom/textfield/TextFieldOutlinedKtTest.kt delete mode 100644 core/ui/compose/designsystem/src/test/kotlin/app/k9mail/core/ui/compose/designsystem/atom/textfield/TextInputTextFieldTest.kt diff --git a/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/atom/textfield/TextFieldCommon.kt b/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/atom/textfield/TextFieldCommon.kt index f34727a97..7497bc952 100644 --- a/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/atom/textfield/TextFieldCommon.kt +++ b/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/atom/textfield/TextFieldCommon.kt @@ -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, diff --git a/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/atom/textfield/TextFieldOutlined.kt b/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/atom/textfield/TextFieldOutlined.kt index f91b92459..b272c2944 100644 --- a/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/atom/textfield/TextFieldOutlined.kt +++ b/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/atom/textfield/TextFieldOutlined.kt @@ -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), diff --git a/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/atom/textfield/TextFieldOutlinedEmailAddress.kt b/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/atom/textfield/TextFieldOutlinedEmailAddress.kt index 2f06322a4..4a354205b 100644 --- a/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/atom/textfield/TextFieldOutlinedEmailAddress.kt +++ b/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/atom/textfield/TextFieldOutlinedEmailAddress.kt @@ -22,7 +22,7 @@ fun TextFieldOutlinedEmailAddress( ) { MaterialOutlinedTextField( value = value, - onValueChange = onValueChange, + onValueChange = stripLineBreaks(onValueChange), modifier = modifier, enabled = isEnabled, label = selectLabel(label, isRequired), diff --git a/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/atom/textfield/TextFieldOutlinedPassword.kt b/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/atom/textfield/TextFieldOutlinedPassword.kt index b07f850fe..e779b69aa 100644 --- a/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/atom/textfield/TextFieldOutlinedPassword.kt +++ b/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/atom/textfield/TextFieldOutlinedPassword.kt @@ -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), diff --git a/core/ui/compose/designsystem/src/test/kotlin/app/k9mail/core/ui/compose/designsystem/atom/textfield/TextFieldOutlinedEmailAddressKtTest.kt b/core/ui/compose/designsystem/src/test/kotlin/app/k9mail/core/ui/compose/designsystem/atom/textfield/TextFieldOutlinedEmailAddressKtTest.kt new file mode 100644 index 000000000..f519ae901 --- /dev/null +++ b/core/ui/compose/designsystem/src/test/kotlin/app/k9mail/core/ui/compose/designsystem/atom/textfield/TextFieldOutlinedEmailAddressKtTest.kt @@ -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") + } +} diff --git a/core/ui/compose/designsystem/src/test/kotlin/app/k9mail/core/ui/compose/designsystem/atom/textfield/TextFieldOutlinedKtTest.kt b/core/ui/compose/designsystem/src/test/kotlin/app/k9mail/core/ui/compose/designsystem/atom/textfield/TextFieldOutlinedKtTest.kt new file mode 100644 index 000000000..bc5c0c201 --- /dev/null +++ b/core/ui/compose/designsystem/src/test/kotlin/app/k9mail/core/ui/compose/designsystem/atom/textfield/TextFieldOutlinedKtTest.kt @@ -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") + } +} diff --git a/core/ui/compose/designsystem/src/test/kotlin/app/k9mail/core/ui/compose/designsystem/atom/textfield/TextFieldOutlinedPasswordKtTest.kt b/core/ui/compose/designsystem/src/test/kotlin/app/k9mail/core/ui/compose/designsystem/atom/textfield/TextFieldOutlinedPasswordKtTest.kt index ffddca54e..36ae93bf7 100644 --- a/core/ui/compose/designsystem/src/test/kotlin/app/k9mail/core/ui/compose/designsystem/atom/textfield/TextFieldOutlinedPasswordKtTest.kt +++ b/core/ui/compose/designsystem/src/test/kotlin/app/k9mail/core/ui/compose/designsystem/atom/textfield/TextFieldOutlinedPasswordKtTest.kt @@ -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), diff --git a/core/ui/compose/designsystem/src/test/kotlin/app/k9mail/core/ui/compose/designsystem/atom/textfield/TextInputTextFieldTest.kt b/core/ui/compose/designsystem/src/test/kotlin/app/k9mail/core/ui/compose/designsystem/atom/textfield/TextInputTextFieldTest.kt deleted file mode 100644 index af6eff666..000000000 --- a/core/ui/compose/designsystem/src/test/kotlin/app/k9mail/core/ui/compose/designsystem/atom/textfield/TextInputTextFieldTest.kt +++ /dev/null @@ -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 = 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, - ) - }, - ), - ) - } -}