[go: nahoru, domu]

blob: 940059db5cf3191213416fb56a738970c6ee3cd1 [file] [log] [blame]
Qingqing Dengd6cf9a92020-06-10 17:12:25 -07001/*
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
Louis Pullen-Freilichca6eca22020-07-20 00:31:45 +010017package androidx.compose.foundation.text.selection
Qingqing Dengd6cf9a92020-06-10 17:12:25 -070018
19import androidx.test.filters.SmallTest
20import androidx.ui.core.Constraints
21import androidx.ui.core.LayoutDirection
Qingqing Deng9e91bf72020-06-22 12:04:18 -070022import androidx.ui.core.clipboard.ClipboardManager
Qingqing Dengd693a5c2020-06-22 11:25:04 -070023import androidx.ui.core.hapticfeedback.HapticFeedback
24import androidx.ui.core.hapticfeedback.HapticFeedbackType
Qingqing Denge4b8cc52020-07-06 16:21:14 -070025import androidx.ui.core.texttoolbar.TextToolbar
Louis Pullen-Freilichf434a132020-07-22 14:19:24 +010026import androidx.compose.ui.geometry.Rect
27import androidx.compose.ui.geometry.Offset
Louis Pullen-Freilichab194752020-07-21 22:21:22 +010028import androidx.compose.ui.text.input.OffsetMap
29import androidx.compose.ui.text.input.TextFieldValue
30import androidx.compose.ui.text.AnnotatedString
Louis Pullen-Freilichca6eca22020-07-20 00:31:45 +010031import androidx.compose.foundation.text.TextFieldState
Louis Pullen-Freilichab194752020-07-21 22:21:22 +010032import androidx.compose.ui.text.TextLayoutInput
33import androidx.compose.ui.text.TextRange
34import androidx.compose.ui.text.TextStyle
35import androidx.compose.ui.text.style.ResolvedTextDirection
36import androidx.compose.ui.text.style.TextOverflow
Qingqing Dengd6cf9a92020-06-10 17:12:25 -070037import androidx.ui.unit.Density
38import com.google.common.truth.Truth.assertThat
39import com.nhaarman.mockitokotlin2.any
Qingqing Deng40519572020-07-10 13:25:36 -070040import com.nhaarman.mockitokotlin2.isNull
Qingqing Dengd6cf9a92020-06-10 17:12:25 -070041import com.nhaarman.mockitokotlin2.mock
Qingqing Deng9e91bf72020-06-22 12:04:18 -070042import com.nhaarman.mockitokotlin2.spy
43import com.nhaarman.mockitokotlin2.times
44import com.nhaarman.mockitokotlin2.verify
Qingqing Dengd6cf9a92020-06-10 17:12:25 -070045import com.nhaarman.mockitokotlin2.whenever
46import org.junit.Before
47import org.junit.Test
48import org.junit.runner.RunWith
49import org.junit.runners.JUnit4
50
51@SmallTest
52@RunWith(JUnit4::class)
53class TextFieldSelectionManagerTest {
54 private val text = "Hello World"
55 private val density = Density(density = 1f)
56 private val offsetMap = OffsetMap.identityOffsetMap
57 private var value = TextFieldValue(text)
58 private val lambda: (TextFieldValue) -> Unit = { value = it }
Qingqing Deng9e91bf72020-06-22 12:04:18 -070059 private val spyLambda = spy(lambda)
Qingqing Dengd6cf9a92020-06-10 17:12:25 -070060 private val state = TextFieldState(mock())
61
62 private val dragBeginPosition = Offset.Zero
63 private val dragDistance = Offset(300f, 15f)
64 private val beginOffset = 0
Qingqing Deng48b32a02020-06-22 17:06:27 -070065 private val dragOffset = text.indexOf('r')
66 private val fakeTextRange = TextRange(0, "Hello".length)
Qingqing Dengd6cf9a92020-06-10 17:12:25 -070067 private val dragTextRange = TextRange("Hello".length + 1, text.length)
68
69 private val manager = TextFieldSelectionManager()
70
Qingqing Deng9e91bf72020-06-22 12:04:18 -070071 private val clipboardManager = mock<ClipboardManager>()
Qingqing Denge4b8cc52020-07-06 16:21:14 -070072 private val textToolbar = mock<TextToolbar>()
Qingqing Dengd693a5c2020-06-22 11:25:04 -070073 private val hapticFeedback = mock<HapticFeedback>()
Qingqing Deng9e91bf72020-06-22 12:04:18 -070074
Qingqing Dengd6cf9a92020-06-10 17:12:25 -070075 @Before
76 fun setup() {
77 manager.offsetMap = offsetMap
78 manager.onValueChange = lambda
79 manager.state = state
80 manager.value = value
Qingqing Deng9e91bf72020-06-22 12:04:18 -070081 manager.clipboardManager = clipboardManager
Qingqing Denge4b8cc52020-07-06 16:21:14 -070082 manager.textToolbar = textToolbar
Qingqing Dengd693a5c2020-06-22 11:25:04 -070083 manager.hapticFeedBack = hapticFeedback
Qingqing Dengd6cf9a92020-06-10 17:12:25 -070084
85 state.layoutResult = mock()
86 whenever(state.layoutResult!!.layoutInput).thenReturn(
87 TextLayoutInput(
88 text = AnnotatedString(text),
89 style = TextStyle.Default,
90 placeholders = mock(),
91 maxLines = 2,
92 softWrap = true,
93 overflow = TextOverflow.Ellipsis,
94 density = density,
95 layoutDirection = LayoutDirection.Ltr,
96 resourceLoader = mock(),
97 constraints = Constraints()
98 )
99 )
100 whenever(state.layoutResult!!.getOffsetForPosition(dragBeginPosition)).thenReturn(
101 beginOffset
102 )
103 whenever(state.layoutResult!!.getOffsetForPosition(dragDistance)).thenReturn(dragOffset)
Qingqing Deng48b32a02020-06-22 17:06:27 -0700104 whenever(state.layoutResult!!.getWordBoundary(beginOffset)).thenReturn(fakeTextRange)
Qingqing Dengd6cf9a92020-06-10 17:12:25 -0700105 whenever(state.layoutResult!!.getWordBoundary(dragOffset)).thenReturn(dragTextRange)
Siyamed Sinir5e075172020-06-24 12:41:28 -0700106 whenever(state.layoutResult!!.getBidiRunDirection(any()))
107 .thenReturn(ResolvedTextDirection.Ltr)
Qingqing Denge4b8cc52020-07-06 16:21:14 -0700108 whenever(state.layoutResult!!.getBoundingBox(any())).thenReturn(Rect.zero)
Qingqing Dengd6cf9a92020-06-10 17:12:25 -0700109 }
110
111 @Test
112 fun TextFieldSelectionManager_init() {
113 assertThat(manager.offsetMap).isEqualTo(offsetMap)
114 assertThat(manager.onValueChange).isEqualTo(lambda)
115 assertThat(manager.state).isEqualTo(state)
116 assertThat(manager.value).isEqualTo(value)
117 }
118
119 @Test
120 fun TextFieldSelectionManager_longPressDragObserver_onLongPress() {
121 manager.longPressDragObserver.onLongPress(dragBeginPosition)
122
123 assertThat(state.selectionIsOn).isTrue()
Qingqing Deng48b32a02020-06-22 17:06:27 -0700124 assertThat(value.selection).isEqualTo(fakeTextRange)
Qingqing Dengd693a5c2020-06-22 11:25:04 -0700125 verify(
126 hapticFeedback,
127 times(1)
128 ).performHapticFeedback(HapticFeedbackType.TextHandleMove)
Qingqing Dengd6cf9a92020-06-10 17:12:25 -0700129 }
130
131 @Test
132 fun TextFieldSelectionManager_longPressDragObserver_onDrag() {
133 manager.longPressDragObserver.onLongPress(dragBeginPosition)
134 manager.longPressDragObserver.onDrag(dragDistance)
135
136 assertThat(value.selection).isEqualTo(TextRange(0, text.length))
Qingqing Dengd693a5c2020-06-22 11:25:04 -0700137 verify(
138 hapticFeedback,
139 times(2)
140 ).performHapticFeedback(HapticFeedbackType.TextHandleMove)
Qingqing Dengd6cf9a92020-06-10 17:12:25 -0700141 }
Qingqing Deng9e91bf72020-06-22 12:04:18 -0700142
143 @Test
Qingqing Deng48b32a02020-06-22 17:06:27 -0700144 fun TextFieldSelectionManager_handleDragObserver_onStart_startHandle() {
145 manager.handleDragObserver(isStartHandle = true).onStart(Offset.Zero)
146
147 assertThat(state.draggingHandle).isTrue()
148 verify(spyLambda, times(0)).invoke(any())
149 verify(
150 hapticFeedback,
151 times(0)
152 ).performHapticFeedback(HapticFeedbackType.TextHandleMove)
153 }
154
155 @Test
156 fun TextFieldSelectionManager_handleDragObserver_onStart_endHandle() {
157 manager.handleDragObserver(isStartHandle = false).onStart(Offset.Zero)
158
159 assertThat(state.draggingHandle).isTrue()
160 verify(spyLambda, times(0)).invoke(any())
161 verify(
162 hapticFeedback,
163 times(0)
164 ).performHapticFeedback(HapticFeedbackType.TextHandleMove)
165 }
166
167 @Test
168 fun TextFieldSelectionManager_handleDragObserver_onDrag_startHandle() {
169 manager.value = TextFieldValue(text = text, selection = TextRange(0, "Hello".length))
170
171 val result = manager.handleDragObserver(isStartHandle = true).onDrag(dragDistance)
172
173 assertThat(result).isEqualTo(dragDistance)
174 assertThat(value.selection).isEqualTo(TextRange(dragOffset, "Hello".length))
175 verify(
176 hapticFeedback,
177 times(1)
178 ).performHapticFeedback(HapticFeedbackType.TextHandleMove)
179 }
180
181 @Test
182 fun TextFieldSelectionManager_handleDragObserver_onDrag_endHandle() {
183 manager.value = TextFieldValue(text = text, selection = TextRange(0, "Hello".length))
184
185 val result = manager.handleDragObserver(isStartHandle = false).onDrag(dragDistance)
186
187 assertThat(result).isEqualTo(dragDistance)
188 assertThat(value.selection).isEqualTo(TextRange(0, dragOffset))
189 verify(
190 hapticFeedback,
191 times(1)
192 ).performHapticFeedback(HapticFeedbackType.TextHandleMove)
193 }
194
195 @Test
196 fun TextFieldSelectionManager_handleDragObserver_onStop() {
197 manager.handleDragObserver(false).onStart(Offset.Zero)
198 manager.handleDragObserver(false).onDrag(Offset.Zero)
199
200 manager.handleDragObserver(false).onStop(Offset.Zero)
201
202 assertThat(state.draggingHandle).isFalse()
203 verify(
204 hapticFeedback,
205 times(0)
206 ).performHapticFeedback(HapticFeedbackType.TextHandleMove)
207 }
208
209 @Test
Qingqing Deng9e91bf72020-06-22 12:04:18 -0700210 fun copy_selection_collapse() {
211 manager.value = TextFieldValue(text = text, selection = TextRange(4, 4))
212
213 manager.copy()
214
215 verify(clipboardManager, times(0)).setText(any())
216 }
217
218 @Test
219 fun copy_selection_not_null() {
220 manager.value = TextFieldValue(text = text, selection = TextRange(0, "Hello".length))
221
222 manager.copy()
223
224 verify(clipboardManager, times(1)).setText(AnnotatedString("Hello"))
225 assertThat(value.selection).isEqualTo(TextRange("Hello".length, "Hello".length))
226 assertThat(state.selectionIsOn).isFalse()
227 }
228
229 @Test
230 fun paste_clipBoardManager_null() {
231 manager.clipboardManager = null
232
233 manager.paste()
234
235 verify(spyLambda, times(0)).invoke(any())
236 }
237
238 @Test
239 fun paste_clipBoardManager_empty() {
240 whenever(clipboardManager.getText()).thenReturn(null)
241
242 manager.paste()
243
244 verify(spyLambda, times(0)).invoke(any())
245 }
246
247 @Test
248 fun paste_clipBoardManager_not_empty() {
249 whenever(clipboardManager.getText()).thenReturn(AnnotatedString("Hello"))
250 manager.value = TextFieldValue(
251 text = text,
252 selection = TextRange("Hel".length, "Hello Wo".length)
253 )
254
255 manager.paste()
256
257 assertThat(value.text).isEqualTo("HelHellorld")
258 assertThat(value.selection).isEqualTo(TextRange("Hello Wo".length, "Hello Wo".length))
259 assertThat(state.selectionIsOn).isFalse()
260 }
261
262 @Test
263 fun cut_selection_collapse() {
264 manager.value = TextFieldValue(text = text, selection = TextRange(4, 4))
265
266 manager.cut()
267
268 verify(clipboardManager, times(0)).setText(any())
269 }
270
271 @Test
272 fun cut_selection_not_null() {
273 manager.value = TextFieldValue(
274 text = text + text,
Qingqing Deng48b32a02020-06-22 17:06:27 -0700275 selection = TextRange("Hello".length, text.length)
276 )
Qingqing Deng9e91bf72020-06-22 12:04:18 -0700277
278 manager.cut()
279
280 verify(clipboardManager, times(1)).setText(AnnotatedString(" World"))
281 assertThat(value.text).isEqualTo("HelloHello World")
282 assertThat(value.selection).isEqualTo(TextRange("Hello".length, "Hello".length))
283 assertThat(state.selectionIsOn).isFalse()
284 }
Qingqing Denge4b8cc52020-07-06 16:21:14 -0700285
286 @Test
Qingqing Deng40519572020-07-10 13:25:36 -0700287 fun showSelectionToolbar_trigger_textToolbar_showMenu_Clipboard_empty_not_show_paste() {
Qingqing Denge4b8cc52020-07-06 16:21:14 -0700288 manager.value = TextFieldValue(
289 text = text + text,
290 selection = TextRange("Hello".length, text.length)
291 )
292
293 manager.showSelectionToolbar()
294
Qingqing Deng40519572020-07-10 13:25:36 -0700295 verify(textToolbar, times(1)).showMenu(any(), any(), isNull(), any())
296 }
297
298 @Test
299 fun showSelectionToolbar_trigger_textToolbar_showMenu_selection_collapse_not_show_copy_cut() {
300 whenever(clipboardManager.getText()).thenReturn(AnnotatedString(text))
301 manager.value = TextFieldValue(
302 text = text + text,
303 selection = TextRange(0, 0)
304 )
305
306 manager.showSelectionToolbar()
307
308 verify(textToolbar, times(1)).showMenu(any(), isNull(), any(), isNull())
Qingqing Denge4b8cc52020-07-06 16:21:14 -0700309 }
Qingqing Deng9e91bf72020-06-22 12:04:18 -0700310}