[go: nahoru, domu]

blob: 7ea10dd54e82335d678c78266341053fc4c5f19b [file] [log] [blame]
/*
* Copyright 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.compose.foundation
import androidx.compose.Composable
import androidx.compose.getValue
import androidx.compose.mutableStateOf
import androidx.compose.setValue
import androidx.test.filters.SmallTest
import androidx.ui.core.Modifier
import androidx.ui.core.gesture.scrollorientationlocking.Orientation
import androidx.ui.core.testTag
import androidx.compose.foundation.gestures.draggable
import androidx.ui.geometry.Offset
import androidx.compose.foundation.layout.Stack
import androidx.compose.foundation.layout.preferredSize
import androidx.ui.test.center
import androidx.ui.test.createComposeRule
import androidx.ui.test.performGesture
import androidx.ui.test.performPartialGesture
import androidx.ui.test.onNodeWithTag
import androidx.ui.test.runOnIdle
import androidx.ui.test.down
import androidx.ui.test.moveBy
import androidx.ui.test.swipe
import androidx.ui.test.swipeWithVelocity
import androidx.ui.test.up
import androidx.ui.test.size
import androidx.ui.unit.dp
import androidx.ui.unit.milliseconds
import com.google.common.truth.Truth.assertThat
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
@SmallTest
@RunWith(JUnit4::class)
class DraggableTest {
@get:Rule
val composeTestRule = createComposeRule()
private val draggableBoxTag = "dragTag"
@Test
fun draggable_horizontalDrag() {
var total = 0f
setDraggableContent {
Modifier.draggable(Orientation.Horizontal) { total += it }
}
onNodeWithTag(draggableBoxTag).performGesture {
this.swipe(
start = this.center,
end = Offset(this.center.x + 100f, this.center.y),
duration = 100.milliseconds
)
}
val lastTotal = runOnIdle {
assertThat(total).isGreaterThan(0)
total
}
onNodeWithTag(draggableBoxTag).performGesture {
this.swipe(
start = this.center,
end = Offset(this.center.x, this.center.y + 100f),
duration = 100.milliseconds
)
}
runOnIdle {
assertThat(total).isEqualTo(lastTotal)
}
onNodeWithTag(draggableBoxTag).performGesture {
this.swipe(
start = this.center,
end = Offset(this.center.x - 100f, this.center.y),
duration = 100.milliseconds
)
}
runOnIdle {
assertThat(total).isLessThan(0.01f)
}
}
@Test
fun draggable_verticalDrag() {
var total = 0f
setDraggableContent {
Modifier.draggable(Orientation.Vertical) { total += it }
}
onNodeWithTag(draggableBoxTag).performGesture {
this.swipe(
start = this.center,
end = Offset(this.center.x, this.center.y + 100f),
duration = 100.milliseconds
)
}
val lastTotal = runOnIdle {
assertThat(total).isGreaterThan(0)
total
}
onNodeWithTag(draggableBoxTag).performGesture {
this.swipe(
start = this.center,
end = Offset(this.center.x + 100f, this.center.y),
duration = 100.milliseconds
)
}
runOnIdle {
assertThat(total).isEqualTo(lastTotal)
}
onNodeWithTag(draggableBoxTag).performGesture {
this.swipe(
start = this.center,
end = Offset(this.center.x, this.center.y - 100f),
duration = 100.milliseconds
)
}
runOnIdle {
assertThat(total).isLessThan(0.01f)
}
}
@Test
fun draggable_startStop() {
var startTrigger = 0f
var stopTrigger = 0f
setDraggableContent {
Modifier.draggable(
Orientation.Horizontal,
onDragStarted = { startTrigger += 1 },
onDragStopped = { stopTrigger += 1 }
) {}
}
runOnIdle {
assertThat(startTrigger).isEqualTo(0)
assertThat(stopTrigger).isEqualTo(0)
}
onNodeWithTag(draggableBoxTag).performGesture {
this.swipe(
start = this.center,
end = Offset(this.center.x + 100f, this.center.y),
duration = 100.milliseconds
)
}
runOnIdle {
assertThat(startTrigger).isEqualTo(1)
assertThat(stopTrigger).isEqualTo(1)
}
}
@Test
fun draggable_disabledWontCallLambda() {
var total = 0f
val enabled = mutableStateOf(true)
setDraggableContent {
Modifier.draggable(Orientation.Horizontal, enabled = enabled.value) {
total += it
}
}
onNodeWithTag(draggableBoxTag).performGesture {
this.swipe(
start = this.center,
end = Offset(this.center.x + 100f, this.center.y),
duration = 100.milliseconds
)
}
val prevTotal = runOnIdle {
assertThat(total).isGreaterThan(0f)
enabled.value = false
total
}
onNodeWithTag(draggableBoxTag).performGesture {
this.swipe(
start = this.center,
end = Offset(this.center.x + 100f, this.center.y),
duration = 100.milliseconds
)
}
runOnIdle {
assertThat(total).isEqualTo(prevTotal)
}
}
@Test
fun draggable_velocityProxy() {
var velocityTriggered = 0f
setDraggableContent {
Modifier.draggable(
Orientation.Horizontal,
onDragStopped = {
velocityTriggered = it
}
) {}
}
onNodeWithTag(draggableBoxTag).performGesture {
this.swipeWithVelocity(
start = this.center,
end = Offset(this.center.x + 100f, this.center.y),
endVelocity = 112f,
duration = 100.milliseconds
)
}
runOnIdle {
assertThat(velocityTriggered - 112f).isLessThan(0.1f)
}
}
@Test
fun draggable_startWithoutSlop_ifAnimating() {
var total = 0f
setDraggableContent {
Modifier.draggable(Orientation.Horizontal, startDragImmediately = true) {
total += it
}
}
onNodeWithTag(draggableBoxTag).performGesture {
this.swipe(
start = this.center,
end = Offset(this.center.x + 100f, this.center.y),
duration = 100.milliseconds
)
}
runOnIdle {
// should be exactly 100 as there's no slop
assertThat(total).isEqualTo(100f)
}
}
@Test
fun draggable_cancel_callsDragStop() {
var total = 0f
var dragStopped = 0f
setDraggableContent {
if (total < 20f) {
Modifier.draggable(
Orientation.Horizontal,
onDragStopped = { dragStopped += 1 },
startDragImmediately = true
) { total += it }
} else Modifier
}
onNodeWithTag(draggableBoxTag).performGesture {
this.swipe(
start = this.center,
end = Offset(this.center.x + 100f, this.center.y),
duration = 100.milliseconds
)
}
runOnIdle {
// should be exactly 100 as there's no slop
assertThat(total).isGreaterThan(0f)
assertThat(dragStopped).isEqualTo(1f)
}
}
@Test
fun draggable_noNestedDrag() {
var innerDrag = 0f
var outerDrag = 0f
composeTestRule.setContent {
Stack {
Box(gravity = ContentGravity.Center,
modifier = Modifier
.testTag(draggableBoxTag)
.preferredSize(300.dp)
.draggable(Orientation.Horizontal) {
outerDrag += it
}
) {
Box(modifier = Modifier
.preferredSize(300.dp)
.draggable(Orientation.Horizontal) { delta ->
innerDrag += delta / 2
}
)
}
}
}
onNodeWithTag(draggableBoxTag).performGesture {
this.swipe(
start = this.center,
end = Offset(this.center.x + 200f, this.center.y),
duration = 300.milliseconds
)
}
runOnIdle {
assertThat(innerDrag).isGreaterThan(0f)
// draggable doesn't participate in nested scrolling, so outer should receive 0 events
assertThat(outerDrag).isEqualTo(0f)
}
}
@Test
fun draggable_interactionState() {
val interactionState = InteractionState()
setDraggableContent {
Modifier.draggable(
Orientation.Horizontal,
interactionState = interactionState
) {}
}
runOnIdle {
assertThat(interactionState.value).doesNotContain(Interaction.Dragged)
}
onNodeWithTag(draggableBoxTag)
.performPartialGesture {
down(Offset(size.width / 4f, size.height / 2f))
moveBy(Offset(size.width / 2f, 0f))
}
runOnIdle {
assertThat(interactionState.value).contains(Interaction.Dragged)
}
onNodeWithTag(draggableBoxTag)
.performPartialGesture {
up()
}
runOnIdle {
assertThat(interactionState.value).doesNotContain(Interaction.Dragged)
}
}
@Test
fun draggable_interactionState_resetWhenDisposed() {
val interactionState = InteractionState()
var emitDraggableBox by mutableStateOf(true)
composeTestRule.setContent {
Stack {
if (emitDraggableBox) {
Box(modifier = Modifier
.testTag(draggableBoxTag)
.preferredSize(100.dp)
.draggable(
orientation = Orientation.Horizontal,
interactionState = interactionState
) {}
)
}
}
}
runOnIdle {
assertThat(interactionState.value).doesNotContain(Interaction.Dragged)
}
onNodeWithTag(draggableBoxTag)
.performPartialGesture {
down(Offset(size.width / 4f, size.height / 2f))
moveBy(Offset(size.width / 2f, 0f))
}
runOnIdle {
assertThat(interactionState.value).contains(Interaction.Dragged)
}
// Dispose draggable
runOnIdle {
emitDraggableBox = false
}
runOnIdle {
assertThat(interactionState.value).doesNotContain(Interaction.Dragged)
}
}
private fun setDraggableContent(draggableFactory: @Composable () -> Modifier) {
composeTestRule.setContent {
Stack {
val draggable = draggableFactory()
Box(
modifier = Modifier
.testTag(draggableBoxTag)
.preferredSize(100.dp)
.plus(draggable)
)
}
}
}
}