Files
react-native/ReactAndroid/src/test/java/com/facebook/react/RootViewTest.java
Mike Lambert 6779d13dcb Reverted commit D3528215
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
2016-07-07 08:16:26 -07:00

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);
}
}