mirror of
https://github.com/zhigang1992/react-native.git
synced 2026-04-03 22:48:25 +08:00
Summary: So `PanReponder.onPanResponderRelease/onPanResponderTerminate` receive a `gestureState` object containing a `onPanResponderTerminate.vx/vy` property. On Android and iOS, they appear to be orders of magnitude different, which appear to be due to the different scale of timestamps that are used when generating touch events. This pull request fixes the timestamps to be milliseconds on both platforms (since I assume iOS is the more authoritative one, and is the one that `react-native-viewpager`'s vx thresholds written written to compare against.) As far as I can tell, the RN code doesn't use the `vx/vy` properties, so they should be okay. And looks like the RN code only cares about relative values of `startTimestamp/currentTimestamp/previousTimestamp` though, so should be fine too. it's quite possible there will be downstream android breakage with this change, particularly for those who are already compensating for the RN discrepancy. Closes https://github.com/facebook/react-native/pull/8199 Differential Revision: D3528215 Pulled By: davidaurelio fbshipit-source-id: d81732e50a5ece2168e8347309d8d52a0db42951
201 lines
7.0 KiB
Java
201 lines
7.0 KiB
Java
/**
|
|
* Copyright (c) 2015-present, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*/
|
|
|
|
package com.facebook.react;
|
|
|
|
import java.util.Date;
|
|
|
|
import android.util.DisplayMetrics;
|
|
import android.view.MotionEvent;
|
|
|
|
import com.facebook.react.bridge.Arguments;
|
|
import com.facebook.react.bridge.ReactContext;
|
|
import com.facebook.react.bridge.ReactApplicationContext;
|
|
import com.facebook.react.bridge.CatalystInstance;
|
|
import com.facebook.react.bridge.ReactTestHelper;
|
|
import com.facebook.react.bridge.JavaOnlyArray;
|
|
import com.facebook.react.bridge.JavaOnlyMap;
|
|
import com.facebook.react.bridge.WritableArray;
|
|
import com.facebook.react.common.SystemClock;
|
|
import com.facebook.react.uimanager.UIManagerModule;
|
|
import com.facebook.react.uimanager.DisplayMetricsHolder;
|
|
import com.facebook.react.uimanager.events.Event;
|
|
import com.facebook.react.uimanager.events.EventDispatcher;
|
|
import com.facebook.react.uimanager.events.RCTEventEmitter;
|
|
|
|
import org.junit.Before;
|
|
import org.junit.Test;
|
|
import org.junit.runner.RunWith;
|
|
import org.junit.Rule;
|
|
import org.mockito.ArgumentCaptor;
|
|
import org.mockito.invocation.InvocationOnMock;
|
|
import org.mockito.stubbing.Answer;
|
|
import org.powermock.api.mockito.PowerMockito;
|
|
import org.powermock.core.classloader.annotations.PrepareForTest;
|
|
import org.powermock.core.classloader.annotations.PowerMockIgnore;
|
|
import org.powermock.modules.junit4.rule.PowerMockRule;
|
|
import org.robolectric.RobolectricTestRunner;
|
|
import org.robolectric.RuntimeEnvironment;
|
|
|
|
import static org.fest.assertions.api.Assertions.assertThat;
|
|
import static org.mockito.Matchers.any;
|
|
import static org.mockito.Matchers.eq;
|
|
import static org.mockito.Mockito.mock;
|
|
import static org.mockito.Mockito.reset;
|
|
import static org.mockito.Mockito.verify;
|
|
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
|
import static org.mockito.Mockito.when;
|
|
|
|
@PrepareForTest({Arguments.class, SystemClock.class})
|
|
@RunWith(RobolectricTestRunner.class)
|
|
@PowerMockIgnore({"org.mockito.*", "org.robolectric.*", "android.*"})
|
|
public class RootViewTest {
|
|
|
|
@Rule
|
|
public PowerMockRule rule = new PowerMockRule();
|
|
|
|
private ReactContext mReactContext;
|
|
private CatalystInstance mCatalystInstanceMock;
|
|
|
|
@Before
|
|
public void setUp() {
|
|
final long ts = SystemClock.nanoTime();
|
|
PowerMockito.mockStatic(Arguments.class);
|
|
PowerMockito.when(Arguments.createArray()).thenAnswer(new Answer<Object>() {
|
|
@Override
|
|
public Object answer(InvocationOnMock invocation) throws Throwable {
|
|
return new JavaOnlyArray();
|
|
}
|
|
});
|
|
PowerMockito.when(Arguments.createMap()).thenAnswer(new Answer<Object>() {
|
|
@Override
|
|
public Object answer(InvocationOnMock invocation) throws Throwable {
|
|
return new JavaOnlyMap();
|
|
}
|
|
});
|
|
PowerMockito.mockStatic(SystemClock.class);
|
|
PowerMockito.when(SystemClock.nanoTime()).thenAnswer(new Answer<Object>() {
|
|
@Override
|
|
public Object answer(InvocationOnMock invocation) throws Throwable {
|
|
return ts;
|
|
}
|
|
});
|
|
|
|
mCatalystInstanceMock = ReactTestHelper.createMockCatalystInstance();
|
|
mReactContext = new ReactApplicationContext(RuntimeEnvironment.application);
|
|
mReactContext.initializeWithInstance(mCatalystInstanceMock);
|
|
DisplayMetricsHolder.initDisplayMetricsIfNotInitialized(mReactContext);
|
|
|
|
UIManagerModule uiManagerModuleMock = mock(UIManagerModule.class);
|
|
when(mCatalystInstanceMock.getNativeModule(UIManagerModule.class))
|
|
.thenReturn(uiManagerModuleMock);
|
|
}
|
|
|
|
@Test
|
|
public void testTouchEmitter() {
|
|
ReactInstanceManager instanceManager = mock(ReactInstanceManager.class);
|
|
when(instanceManager.getCurrentReactContext()).thenReturn(mReactContext);
|
|
|
|
UIManagerModule uiManager = mock(UIManagerModule.class);
|
|
EventDispatcher eventDispatcher = mock(EventDispatcher.class);
|
|
RCTEventEmitter eventEmitterModuleMock = mock(RCTEventEmitter.class);
|
|
when(mCatalystInstanceMock.getNativeModule(UIManagerModule.class))
|
|
.thenReturn(uiManager);
|
|
when(uiManager.getEventDispatcher()).thenReturn(eventDispatcher);
|
|
|
|
int rootViewId = 7;
|
|
|
|
ReactRootView rootView = new ReactRootView(mReactContext);
|
|
rootView.setId(rootViewId);
|
|
rootView.startReactApplication(instanceManager, "");
|
|
rootView.simulateAttachForTesting();
|
|
|
|
long ts = SystemClock.nanoTime();
|
|
|
|
// Test ACTION_DOWN event
|
|
rootView.onTouchEvent(
|
|
MotionEvent.obtain(100, ts, MotionEvent.ACTION_DOWN, 0, 0, 0));
|
|
|
|
ArgumentCaptor<Event> downEventCaptor = ArgumentCaptor.forClass(Event.class);
|
|
verify(eventDispatcher).dispatchEvent(downEventCaptor.capture());
|
|
verifyNoMoreInteractions(eventDispatcher);
|
|
|
|
downEventCaptor.getValue().dispatch(eventEmitterModuleMock);
|
|
|
|
ArgumentCaptor<JavaOnlyArray> downActionTouchesArgCaptor =
|
|
ArgumentCaptor.forClass(JavaOnlyArray.class);
|
|
verify(eventEmitterModuleMock).receiveTouches(
|
|
eq("topTouchStart"),
|
|
downActionTouchesArgCaptor.capture(),
|
|
any(JavaOnlyArray.class));
|
|
verifyNoMoreInteractions(eventEmitterModuleMock);
|
|
|
|
assertThat(downActionTouchesArgCaptor.getValue().size()).isEqualTo(1);
|
|
assertThat(downActionTouchesArgCaptor.getValue().getMap(0)).isEqualTo(
|
|
JavaOnlyMap.of(
|
|
"pageX",
|
|
0.,
|
|
"pageY",
|
|
0.,
|
|
"locationX",
|
|
0.,
|
|
"locationY",
|
|
0.,
|
|
"target",
|
|
rootViewId,
|
|
"timeStamp",
|
|
(double) ts,
|
|
"identifier",
|
|
0.));
|
|
|
|
// Test ACTION_UP event
|
|
reset(eventEmitterModuleMock, eventDispatcher);
|
|
|
|
ArgumentCaptor<Event> upEventCaptor = ArgumentCaptor.forClass(Event.class);
|
|
ArgumentCaptor<JavaOnlyArray> upActionTouchesArgCaptor =
|
|
ArgumentCaptor.forClass(JavaOnlyArray.class);
|
|
|
|
rootView.onTouchEvent(
|
|
MotionEvent.obtain(50, ts, MotionEvent.ACTION_UP, 0, 0, 0));
|
|
verify(eventDispatcher).dispatchEvent(upEventCaptor.capture());
|
|
verifyNoMoreInteractions(eventDispatcher);
|
|
|
|
upEventCaptor.getValue().dispatch(eventEmitterModuleMock);
|
|
verify(eventEmitterModuleMock).receiveTouches(
|
|
eq("topTouchEnd"),
|
|
upActionTouchesArgCaptor.capture(),
|
|
any(WritableArray.class));
|
|
verifyNoMoreInteractions(eventEmitterModuleMock);
|
|
|
|
assertThat(upActionTouchesArgCaptor.getValue().size()).isEqualTo(1);
|
|
assertThat(upActionTouchesArgCaptor.getValue().getMap(0)).isEqualTo(
|
|
JavaOnlyMap.of(
|
|
"pageX",
|
|
0.,
|
|
"pageY",
|
|
0.,
|
|
"locationX",
|
|
0.,
|
|
"locationY",
|
|
0.,
|
|
"target",
|
|
rootViewId,
|
|
"timeStamp",
|
|
(double) ts,
|
|
"identifier",
|
|
0.));
|
|
|
|
// Test other action
|
|
reset(eventDispatcher);
|
|
rootView.onTouchEvent(
|
|
MotionEvent.obtain(50, new Date().getTime(), MotionEvent.ACTION_HOVER_MOVE, 0, 0, 0));
|
|
verifyNoMoreInteractions(eventDispatcher);
|
|
}
|
|
}
|