blob: d49277a9ac8453eb02952de6cfc9ea29496dcb38 [file] [log] [blame]
Filip Pavlis5b2a1402020-05-11 15:12:36 +01001/*
2 * Copyright 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Filip Pavlisbc81dcd2020-10-21 19:25:11 +010017package androidx.compose.ui.test
Filip Pavlis5b2a1402020-05-11 15:12:36 +010018
Louis Pullen-Freilichddda7be2020-07-17 18:28:12 +010019import androidx.compose.foundation.ExperimentalFoundationApi
Matvei Malkov575d0df2020-10-28 22:15:09 +030020import androidx.compose.foundation.text.BasicTextField
Ralston Da Silva82855d62021-02-03 00:11:06 -080021import androidx.compose.foundation.text.KeyboardActions
Siyamed Sinir89516532020-10-30 19:09:45 -070022import androidx.compose.foundation.text.KeyboardOptions
Adam Powell948672232020-07-28 12:06:47 -070023import androidx.compose.runtime.Composable
24import androidx.compose.runtime.mutableStateOf
25import androidx.compose.runtime.remember
26import androidx.compose.ui.Modifier
27import androidx.compose.ui.platform.testTag
Filip Pavlis2bf78912020-10-26 18:01:47 +000028import androidx.compose.ui.test.junit4.createComposeRule
Matvei Malkov575d0df2020-10-28 22:15:09 +030029import androidx.compose.ui.test.util.BoundaryNode
30import androidx.compose.ui.test.util.expectErrorMessageStartsWith
Louis Pullen-Freilichab194752020-07-21 22:21:22 +010031import androidx.compose.ui.text.input.ImeAction
Matvei Malkov575d0df2020-10-28 22:15:09 +030032import androidx.test.ext.junit.runners.AndroidJUnit4
Adam Powell948672232020-07-28 12:06:47 -070033import androidx.test.filters.MediumTest
Filip Pavlis5b2a1402020-05-11 15:12:36 +010034import com.google.common.truth.Truth.assertThat
35import org.junit.Rule
36import org.junit.Test
37import org.junit.runner.RunWith
Filip Pavlis5b2a1402020-05-11 15:12:36 +010038
39@MediumTest
Jelle Fresen828e83b2020-10-20 12:01:28 +010040@RunWith(AndroidJUnit4::class)
Filip Pavlis5b2a1402020-05-11 15:12:36 +010041class TextActionsTest {
42
Filip Pavlis66f9c892020-05-14 12:00:08 +010043 private val fieldTag = "Field"
44
Filip Pavlis5b2a1402020-05-11 15:12:36 +010045 @get:Rule
Filip Pavlisa76b5bd72020-09-03 12:54:27 +010046 val rule = createComposeRule()
Filip Pavlis5b2a1402020-05-11 15:12:36 +010047
48 @Composable
Matvei Malkov790b3112020-07-15 14:09:37 +010049 @OptIn(ExperimentalFoundationApi::class)
Filip Pavlis66f9c892020-05-14 12:00:08 +010050 fun TextFieldUi(
Siyamed Sinir1cd8bb92021-01-08 01:41:47 -080051 imeAction: ImeAction = ImeAction.Default,
Ralston Da Silva82855d62021-02-03 00:11:06 -080052 keyboardActions: KeyboardActions = KeyboardActions.Default,
Filip Pavlis66f9c892020-05-14 12:00:08 +010053 textCallback: (String) -> Unit = {}
54 ) {
Siyamed Sinir663ecc02020-11-16 22:51:52 -080055 val state = remember { mutableStateOf("") }
Matvei Malkov575d0df2020-10-28 22:15:09 +030056 BasicTextField(
Alexandre Elias5c8252f2020-05-27 15:52:40 -070057 modifier = Modifier.testTag(fieldTag),
58 value = state.value,
Siyamed Sinir89516532020-10-30 19:09:45 -070059 keyboardOptions = KeyboardOptions(imeAction = imeAction),
Ralston Da Silva82855d62021-02-03 00:11:06 -080060 keyboardActions = keyboardActions,
Alexandre Elias5c8252f2020-05-27 15:52:40 -070061 onValueChange = {
62 state.value = it
Siyamed Sinir663ecc02020-11-16 22:51:52 -080063 textCallback(it)
Alexandre Elias5c8252f2020-05-27 15:52:40 -070064 }
65 )
Filip Pavlis5b2a1402020-05-11 15:12:36 +010066 }
67
68 @Test
69 fun sendText_clearText() {
70 var lastSeenText = ""
Filip Pavlisa76b5bd72020-09-03 12:54:27 +010071 rule.setContent {
Filip Pavlis5b2a1402020-05-11 15:12:36 +010072 TextFieldUi {
73 lastSeenText = it
74 }
75 }
76
Filip Pavlisa76b5bd72020-09-03 12:54:27 +010077 rule.onNodeWithTag(fieldTag)
Filip Pavlis659ea722020-07-13 14:14:32 +010078 .performTextInput("Hello!")
Filip Pavlis5b2a1402020-05-11 15:12:36 +010079
Filip Pavlisa76b5bd72020-09-03 12:54:27 +010080 rule.runOnIdle {
Filip Pavlis5b2a1402020-05-11 15:12:36 +010081 assertThat(lastSeenText).isEqualTo("Hello!")
82 }
83
Filip Pavlisa76b5bd72020-09-03 12:54:27 +010084 rule.onNodeWithTag(fieldTag)
Filip Pavlisa0382342021-02-09 14:56:23 +000085 .performTextClearance()
Filip Pavlis5b2a1402020-05-11 15:12:36 +010086
Filip Pavlisa76b5bd72020-09-03 12:54:27 +010087 rule.runOnIdle {
Filip Pavlis5b2a1402020-05-11 15:12:36 +010088 assertThat(lastSeenText).isEqualTo("")
89 }
90 }
91
92 @Test
93 fun sendTextTwice_shouldAppend() {
94 var lastSeenText = ""
Filip Pavlisa76b5bd72020-09-03 12:54:27 +010095 rule.setContent {
Filip Pavlis5b2a1402020-05-11 15:12:36 +010096 TextFieldUi {
97 lastSeenText = it
98 }
99 }
100
Filip Pavlisa76b5bd72020-09-03 12:54:27 +0100101 rule.onNodeWithTag(fieldTag)
Filip Pavlis659ea722020-07-13 14:14:32 +0100102 .performTextInput("Hello ")
Filip Pavlis5b2a1402020-05-11 15:12:36 +0100103
Filip Pavlisa76b5bd72020-09-03 12:54:27 +0100104 rule.onNodeWithTag(fieldTag)
Filip Pavlisa0382342021-02-09 14:56:23 +0000105 .performTextInput("world!")
Filip Pavlis5b2a1402020-05-11 15:12:36 +0100106
Filip Pavlisa76b5bd72020-09-03 12:54:27 +0100107 rule.runOnIdle {
Filip Pavlis5b2a1402020-05-11 15:12:36 +0100108 assertThat(lastSeenText).isEqualTo("Hello world!")
109 }
110 }
111
Filip Pavlis66f9c892020-05-14 12:00:08 +0100112 // @Test - not always appends, seems to be flaky
113 fun sendTextTwice_shouldAppend_ver2() {
Filip Pavlis5b2a1402020-05-11 15:12:36 +0100114 var lastSeenText = ""
Filip Pavlisa76b5bd72020-09-03 12:54:27 +0100115 rule.setContent {
Filip Pavlis5b2a1402020-05-11 15:12:36 +0100116 TextFieldUi {
117 lastSeenText = it
118 }
119 }
120
Filip Pavlisa76b5bd72020-09-03 12:54:27 +0100121 rule.onNodeWithTag(fieldTag)
Filip Pavlis659ea722020-07-13 14:14:32 +0100122 .performTextInput("Hello")
Filip Pavlis5b2a1402020-05-11 15:12:36 +0100123
Filip Pavlis66f9c892020-05-14 12:00:08 +0100124 // This helps. So there must be some timing issue.
125 // Thread.sleep(3000)
126
Filip Pavlisa76b5bd72020-09-03 12:54:27 +0100127 rule.onNodeWithTag(fieldTag)
Filip Pavlisa0382342021-02-09 14:56:23 +0000128 .performTextInput(" world!")
Filip Pavlis5b2a1402020-05-11 15:12:36 +0100129
Filip Pavlisa76b5bd72020-09-03 12:54:27 +0100130 rule.runOnIdle {
Filip Pavlis5b2a1402020-05-11 15:12:36 +0100131 assertThat(lastSeenText).isEqualTo("Hello world!")
132 }
133 }
134
135 @Test
Filip Pavlis66f9c892020-05-14 12:00:08 +0100136 fun replaceText() {
137 var lastSeenText = ""
Filip Pavlisa76b5bd72020-09-03 12:54:27 +0100138 rule.setContent {
Filip Pavlis66f9c892020-05-14 12:00:08 +0100139 TextFieldUi {
140 lastSeenText = it
141 }
142 }
143
Filip Pavlisa76b5bd72020-09-03 12:54:27 +0100144 rule.onNodeWithTag(fieldTag)
Filip Pavlis659ea722020-07-13 14:14:32 +0100145 .performTextInput("Hello")
Filip Pavlis66f9c892020-05-14 12:00:08 +0100146
Filip Pavlisa76b5bd72020-09-03 12:54:27 +0100147 rule.runOnIdle {
Filip Pavlis66f9c892020-05-14 12:00:08 +0100148 assertThat(lastSeenText).isEqualTo("Hello")
149 }
150
Filip Pavlisa76b5bd72020-09-03 12:54:27 +0100151 rule.onNodeWithTag(fieldTag)
Filip Pavlisa0382342021-02-09 14:56:23 +0000152 .performTextReplacement("world")
Filip Pavlis66f9c892020-05-14 12:00:08 +0100153
Filip Pavlisa76b5bd72020-09-03 12:54:27 +0100154 rule.runOnIdle {
Filip Pavlis66f9c892020-05-14 12:00:08 +0100155 assertThat(lastSeenText).isEqualTo("world")
156 }
157 }
158
159 @Test
160 fun sendImeAction_search() {
Ralston Da Silva82855d62021-02-03 00:11:06 -0800161 var actionPerformed = false
Filip Pavlisa76b5bd72020-09-03 12:54:27 +0100162 rule.setContent {
Jeff Gastonad2fc532020-09-18 15:10:46 -0400163 TextFieldUi(
164 imeAction = ImeAction.Search,
Ralston Da Silva82855d62021-02-03 00:11:06 -0800165 keyboardActions = KeyboardActions(onSearch = { actionPerformed = true })
Jeff Gastonad2fc532020-09-18 15:10:46 -0400166 )
Filip Pavlis66f9c892020-05-14 12:00:08 +0100167 }
Ralston Da Silva82855d62021-02-03 00:11:06 -0800168 assertThat(actionPerformed).isFalse()
Filip Pavlis66f9c892020-05-14 12:00:08 +0100169
Filip Pavlisa76b5bd72020-09-03 12:54:27 +0100170 rule.onNodeWithTag(fieldTag)
Filip Pavlis659ea722020-07-13 14:14:32 +0100171 .performImeAction()
Filip Pavlis66f9c892020-05-14 12:00:08 +0100172
Filip Pavlisa76b5bd72020-09-03 12:54:27 +0100173 rule.runOnIdle {
Ralston Da Silva82855d62021-02-03 00:11:06 -0800174 assertThat(actionPerformed).isTrue()
Filip Pavlis66f9c892020-05-14 12:00:08 +0100175 }
176 }
177
178 @Test
179 fun sendImeAction_actionNotDefined_shouldFail() {
Ralston Da Silva82855d62021-02-03 00:11:06 -0800180 var actionPerformed = false
Filip Pavlisa76b5bd72020-09-03 12:54:27 +0100181 rule.setContent {
Jeff Gastonad2fc532020-09-18 15:10:46 -0400182 TextFieldUi(
Siyamed Sinir1cd8bb92021-01-08 01:41:47 -0800183 imeAction = ImeAction.Default,
Ralston Da Silva4b76bcb2021-02-03 14:55:03 -0800184 keyboardActions = KeyboardActions { actionPerformed = true }
Jeff Gastonad2fc532020-09-18 15:10:46 -0400185 )
Filip Pavlis66f9c892020-05-14 12:00:08 +0100186 }
Ralston Da Silva82855d62021-02-03 00:11:06 -0800187 assertThat(actionPerformed).isFalse()
Filip Pavlis66f9c892020-05-14 12:00:08 +0100188
Jeff Gastonad2fc532020-09-18 15:10:46 -0400189 expectErrorMessageStartsWith(
190 "" +
Filip Pavlis659ea722020-07-13 14:14:32 +0100191 "Failed to perform IME action as current node does not specify any.\n" +
Filip Pavlis66f9c892020-05-14 12:00:08 +0100192 "Semantics of the node:"
193 ) {
Filip Pavlisa76b5bd72020-09-03 12:54:27 +0100194 rule.onNodeWithTag(fieldTag)
Filip Pavlis659ea722020-07-13 14:14:32 +0100195 .performImeAction()
Filip Pavlis66f9c892020-05-14 12:00:08 +0100196 }
197 }
198
199 @Test
200 fun sendImeAction_inputNotSupported_shouldFail() {
Filip Pavlisa76b5bd72020-09-03 12:54:27 +0100201 rule.setContent {
Filip Pavlis66f9c892020-05-14 12:00:08 +0100202 BoundaryNode(testTag = "node")
203 }
204
Jeff Gastonad2fc532020-09-18 15:10:46 -0400205 expectErrorMessageStartsWith(
206 "" +
Filip Pavlis659ea722020-07-13 14:14:32 +0100207 "Failed to perform IME action.\n" +
Filip Pavlis13bdbe72020-11-06 17:35:47 +0000208 "Failed to assert the following: (SetText is defined)\n" +
Filip Pavlis66f9c892020-05-14 12:00:08 +0100209 "Semantics of the node:"
210 ) {
Filip Pavlisa76b5bd72020-09-03 12:54:27 +0100211 rule.onNodeWithTag("node")
Filip Pavlis659ea722020-07-13 14:14:32 +0100212 .performImeAction()
Filip Pavlis66f9c892020-05-14 12:00:08 +0100213 }
214 }
Filip Pavlis5b2a1402020-05-11 15:12:36 +0100215}