initial commit taken from gitlab.lrz.de

This commit is contained in:
privatereese
2018-08-24 18:09:42 +02:00
parent ae54ed4c48
commit fc05486403
28494 changed files with 2159823 additions and 0 deletions

3
node_modules/react-native/ReactAndroid/.npmignore generated vendored Normal file
View File

@@ -0,0 +1,3 @@
# Make sure we never publish ReactAndroid/build (Gradle output) to npm.
# The folder is huge (> 100MB)!
build/

View File

@@ -0,0 +1,23 @@
Here's how to test the whole dev experience end-to-end. This will be eventually merged into the [Getting Started guide](https://facebook.github.io/react-native/docs/getting-started.html).
Assuming you have the [Android SDK](https://developer.android.com/sdk/installing/index.html) installed, run `android` to open the Android SDK Manager.
Make sure you have the following installed:
- Android SDK version 23
- SDK build tools version 23
- Android Support Repository 17 (for Android Support Library)
Follow steps on https://github.com/facebook/react-native/blob/master/react-native-cli/CONTRIBUTING.md, but be sure to bump the version of react-native in package.json to some version > 0.9 (latest published npm version) or set up proxying properly for react-native
- From the react-native-android repo:
- `./gradlew :ReactAndroid:installArchives`
- *Assuming you already have android-jsc installed to local maven repo, no steps included here*
- `react-native init ProjectName`
- Open up your Android emulator (Genymotion is recommended)
- `cd ProjectName`
- `react-native run-android`
In case the app crashed:
- Run `adb logcat` and try to find a Java exception

7
node_modules/react-native/ReactAndroid/README.md generated vendored Normal file
View File

@@ -0,0 +1,7 @@
# Building React Native for Android
See the [docs on the website](https://facebook.github.io/react-native/docs/android-building-from-source.html).
# Running tests
When you submit a pull request CircleCI will automatically run all tests. To run tests locally, see [Testing](https://facebook.github.io/react-native/docs/testing.html).

306
node_modules/react-native/ReactAndroid/build.gradle generated vendored Normal file
View File

@@ -0,0 +1,306 @@
// Copyright 2015-present Facebook. All Rights Reserved.
apply plugin: 'com.android.library'
apply plugin: 'maven'
apply plugin: 'de.undercouch.download'
import de.undercouch.gradle.tasks.download.Download
import org.apache.tools.ant.taskdefs.condition.Os
import org.apache.tools.ant.filters.ReplaceTokens
// We download various C++ open-source dependencies into downloads.
// We then copy both the downloaded code and our custom makefiles and headers into third-party-ndk.
// After that we build native code from src/main/jni with module path pointing at third-party-ndk.
def downloadsDir = new File("$buildDir/downloads")
def thirdPartyNdkDir = new File("$buildDir/third-party-ndk")
// You need to have following folders in this directory:
// - boost_1_63_0
// - double-conversion-1.1.1
// - folly-deprecate-dynamic-initializer
// - glog-0.3.3
// - jsc-headers
def dependenciesPath = System.getenv("REACT_NATIVE_DEPENDENCIES")
// The Boost library is a very large download (>100MB).
// If Boost is already present on your system, define the REACT_NATIVE_BOOST_PATH env variable
// and the build will use that.
def boostPath = dependenciesPath ?: System.getenv("REACT_NATIVE_BOOST_PATH")
task createNativeDepsDirectories {
downloadsDir.mkdirs()
thirdPartyNdkDir.mkdirs()
}
task downloadBoost(dependsOn: createNativeDepsDirectories, type: Download) {
src 'https://github.com/react-native-community/boost-for-react-native/releases/download/v1.63.0-0/boost_1_63_0.tar.gz'
onlyIfNewer true
overwrite false
dest new File(downloadsDir, 'boost_1_63_0.tar.gz')
}
task prepareBoost(dependsOn: boostPath ? [] : [downloadBoost], type: Copy) {
from boostPath ?: tarTree(resources.gzip(downloadBoost.dest))
from 'src/main/jni/third-party/boost/Android.mk'
include 'Android.mk', 'boost_1_63_0/boost/**/*.hpp', 'boost/boost/**/*.hpp'
includeEmptyDirs = false
into "$thirdPartyNdkDir/boost"
doLast {
file("$thirdPartyNdkDir/boost/boost").renameTo("$thirdPartyNdkDir/boost/boost_1_63_0")
}
}
task downloadDoubleConversion(dependsOn: createNativeDepsDirectories, type: Download) {
src 'https://github.com/google/double-conversion/archive/v1.1.1.tar.gz'
onlyIfNewer true
overwrite false
dest new File(downloadsDir, 'double-conversion-1.1.1.tar.gz')
}
task prepareDoubleConversion(dependsOn: dependenciesPath ? [] : [downloadDoubleConversion], type: Copy) {
from dependenciesPath ?: tarTree(downloadDoubleConversion.dest)
from 'src/main/jni/third-party/double-conversion/Android.mk'
include 'double-conversion-1.1.1/src/**/*', 'Android.mk'
filesMatching('*/src/**/*', {fname -> fname.path = "double-conversion/${fname.name}"})
includeEmptyDirs = false
into "$thirdPartyNdkDir/double-conversion"
}
task downloadFolly(dependsOn: createNativeDepsDirectories, type: Download) {
src 'https://github.com/facebook/folly/archive/v2016.09.26.00.tar.gz'
onlyIfNewer true
overwrite false
dest new File(downloadsDir, 'folly-2016.09.26.00.tar.gz');
}
task prepareFolly(dependsOn: dependenciesPath ? [] : [downloadFolly], type: Copy) {
from dependenciesPath ?: tarTree(downloadFolly.dest)
from 'src/main/jni/third-party/folly/Android.mk'
include 'folly-2016.09.26.00/folly/**/*', 'Android.mk'
eachFile {fname -> fname.path = (fname.path - "folly-2016.09.26.00/")}
includeEmptyDirs = false
into "$thirdPartyNdkDir/folly"
}
task downloadGlog(dependsOn: createNativeDepsDirectories, type: Download) {
src 'https://github.com/google/glog/archive/v0.3.3.tar.gz'
onlyIfNewer true
overwrite false
dest new File(downloadsDir, 'glog-0.3.3.tar.gz')
}
// Prepare glog sources to be compiled, this task will perform steps that normally should've been
// executed by automake. This way we can avoid dependencies on make/automake
task prepareGlog(dependsOn: dependenciesPath ? [] : [downloadGlog], type: Copy) {
from dependenciesPath ?: tarTree(downloadGlog.dest)
from 'src/main/jni/third-party/glog/'
include 'glog-0.3.3/src/**/*', 'Android.mk', 'config.h'
includeEmptyDirs = false
filesMatching('**/*.h.in') {
filter(ReplaceTokens, tokens: [
ac_cv_have_unistd_h: '1',
ac_cv_have_stdint_h: '1',
ac_cv_have_systypes_h: '1',
ac_cv_have_inttypes_h: '1',
ac_cv_have_libgflags: '0',
ac_google_start_namespace: 'namespace google {',
ac_cv_have_uint16_t: '1',
ac_cv_have_u_int16_t: '1',
ac_cv_have___uint16: '0',
ac_google_end_namespace: '}',
ac_cv_have___builtin_expect: '1',
ac_google_namespace: 'google',
ac_cv___attribute___noinline: '__attribute__ ((noinline))',
ac_cv___attribute___noreturn: '__attribute__ ((noreturn))',
ac_cv___attribute___printf_4_5: '__attribute__((__format__ (__printf__, 4, 5)))'
])
it.path = (it.name - '.in')
}
into "$thirdPartyNdkDir/glog"
}
task downloadJSCHeaders(type: Download) {
// in sync with webkit SVN revision 174650
def jscAPIBaseURL = 'https://raw.githubusercontent.com/WebKit/webkit/38b15a3ba3c1b0798f2036f7cea36ffdc096202e/Source/JavaScriptCore/API/'
def jscHeaderFiles = ['JavaScript.h', 'JSBase.h', 'JSContextRef.h', 'JSObjectRef.h', 'JSRetainPtr.h', 'JSStringRef.h', 'JSValueRef.h', 'WebKitAvailability.h']
def output = new File(downloadsDir, 'jsc')
output.mkdirs()
src(jscHeaderFiles.collect { headerName -> "$jscAPIBaseURL$headerName" })
onlyIfNewer true
overwrite false
dest output
}
// Create Android.mk library module based on so files from mvn + include headers fetched from webkit.org
task prepareJSC(dependsOn: dependenciesPath ? [] : [downloadJSCHeaders]) << {
copy {
from zipTree(configurations.compile.fileCollection { dep -> dep.name == 'android-jsc' }.singleFile)
from dependenciesPath ? "$dependenciesPath/jsc-headers" : {downloadJSCHeaders.dest}
from 'src/main/jni/third-party/jsc/Android.mk'
include 'jni/**/*.so', '*.h', 'Android.mk'
filesMatching('*.h', { fname -> fname.path = "JavaScriptCore/${fname.path}"})
into "$thirdPartyNdkDir/jsc";
}
}
def getNdkBuildName() {
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
return "ndk-build.cmd"
} else {
return "ndk-build"
}
}
def findNdkBuildFullPath() {
// we allow to provide full path to ndk-build tool
if (hasProperty('ndk.command')) {
return property('ndk.command')
}
// or just a path to the containing directory
if (hasProperty('ndk.path')) {
def ndkDir = property('ndk.path')
return new File(ndkDir, getNdkBuildName()).getAbsolutePath()
}
if (System.getenv('ANDROID_NDK') != null) {
def ndkDir = System.getenv('ANDROID_NDK')
return new File(ndkDir, getNdkBuildName()).getAbsolutePath()
}
def ndkDir = android.hasProperty('plugin') ? android.plugin.ndkFolder :
plugins.getPlugin('com.android.library').hasProperty('sdkHandler') ?
plugins.getPlugin('com.android.library').sdkHandler.getNdkFolder() :
android.ndkDirectory.absolutePath
if (ndkDir) {
return new File(ndkDir, getNdkBuildName()).getAbsolutePath()
}
return null
}
def getNdkBuildFullPath() {
def ndkBuildFullPath = findNdkBuildFullPath()
if (ndkBuildFullPath == null) {
throw new GradleScriptException(
"ndk-build binary cannot be found, check if you've set " +
"\$ANDROID_NDK environment variable correctly or if ndk.dir is " +
"setup in local.properties",
null)
}
if (!new File(ndkBuildFullPath).canExecute()) {
throw new GradleScriptException(
"ndk-build binary " + ndkBuildFullPath + " doesn't exist or isn't executable.\n" +
"Check that the \$ANDROID_NDK environment variable, or ndk.dir in local.properties, is set correctly.\n" +
"(On Windows, make sure you escape backslashes in local.properties or use forward slashes, e.g. C:\\\\ndk or C:/ndk rather than C:\\ndk)",
null)
}
return ndkBuildFullPath
}
task buildReactNdkLib(dependsOn: [prepareJSC, prepareBoost, prepareDoubleConversion, prepareFolly, prepareGlog], type: Exec) {
inputs.file('src/main/jni/react')
outputs.dir("$buildDir/react-ndk/all")
commandLine getNdkBuildFullPath(),
'NDK_PROJECT_PATH=null',
"NDK_APPLICATION_MK=$projectDir/src/main/jni/Application.mk",
'NDK_OUT=' + temporaryDir,
"NDK_LIBS_OUT=$buildDir/react-ndk/all",
"THIRD_PARTY_NDK_DIR=$buildDir/third-party-ndk",
"REACT_COMMON_DIR=$projectDir/../ReactCommon",
'-C', file('src/main/jni/react/jni').absolutePath,
'--jobs', project.hasProperty("jobs") ? project.property("jobs") : Runtime.runtime.availableProcessors()
}
task cleanReactNdkLib(type: Exec) {
commandLine getNdkBuildFullPath(),
"NDK_APPLICATION_MK=$projectDir/src/main/jni/Application.mk",
"THIRD_PARTY_NDK_DIR=$buildDir/third-party-ndk",
"REACT_COMMON_DIR=$projectDir/../ReactCommon",
'-C', file('src/main/jni/react/jni').absolutePath,
'clean'
}
task packageReactNdkLibs(dependsOn: buildReactNdkLib, type: Copy) {
from "$buildDir/react-ndk/all"
exclude '**/libjsc.so'
into "$buildDir/react-ndk/exported"
}
task packageReactNdkLibsForBuck(dependsOn: packageReactNdkLibs, type: Copy) {
from "$buildDir/react-ndk/exported"
into "src/main/jni/prebuilt/lib"
}
android {
compileSdkVersion 23
buildToolsVersion "23.0.1"
defaultConfig {
minSdkVersion 16
targetSdkVersion 22
versionCode 1
versionName "1.0"
ndk {
moduleName "reactnativejni"
}
buildConfigField 'boolean', 'IS_INTERNAL_BUILD', 'false'
buildConfigField 'int', 'EXOPACKAGE_FLAGS', '0'
testApplicationId "com.facebook.react.tests.gradle"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
sourceSets.main {
jni.srcDirs = []
jniLibs.srcDir "$buildDir/react-ndk/exported"
res.srcDirs = ['src/main/res/devsupport', 'src/main/res/shell', 'src/main/res/views/modal', 'src/main/res/views/uimanager']
java {
srcDirs = ['src/main/java', 'src/main/libraries/soloader/java', 'src/main/jni/first-party/fb/jni/java']
exclude 'com/facebook/react/processing'
exclude 'com/facebook/react/module/processing'
}
}
tasks.withType(JavaCompile) {
compileTask -> compileTask.dependsOn packageReactNdkLibs
}
clean.dependsOn cleanReactNdkLib
lintOptions {
abortOnError false
}
packagingOptions {
exclude 'META-INF/NOTICE'
exclude 'META-INF/LICENSE'
}
}
dependencies {
compile fileTree(dir: 'src/main/third-party/java/infer-annotations/', include: ['*.jar'])
compile 'javax.inject:javax.inject:1'
compile 'com.android.support:appcompat-v7:23.0.1'
compile 'com.facebook.fbui.textlayoutbuilder:textlayoutbuilder:1.0.0'
compile 'com.facebook.fresco:fresco:1.3.0'
compile 'com.facebook.fresco:imagepipeline-okhttp3:1.3.0'
compile 'com.facebook.soloader:soloader:0.1.0'
compile 'com.google.code.findbugs:jsr305:3.0.0'
compile 'com.squareup.okhttp3:okhttp:3.6.0'
compile 'com.squareup.okhttp3:okhttp-urlconnection:3.6.0'
compile 'com.squareup.okio:okio:1.13.0'
compile 'org.webkit:android-jsc:r174650'
testCompile "junit:junit:${JUNIT_VERSION}"
testCompile "org.powermock:powermock-api-mockito:${POWERMOCK_VERSION}"
testCompile "org.powermock:powermock-module-junit4-rule:${POWERMOCK_VERSION}"
testCompile "org.powermock:powermock-classloading-xstream:${POWERMOCK_VERSION}"
testCompile "org.mockito:mockito-core:${MOCKITO_CORE_VERSION}"
testCompile "org.easytesting:fest-assert-core:${FEST_ASSERT_CORE_VERSION}"
testCompile "org.robolectric:robolectric:${ROBOLECTRIC_VERSION}"
androidTestCompile fileTree(dir: 'src/main/third-party/java/buck-android-support/', include: ['*.jar'])
androidTestCompile 'com.android.support.test:runner:0.3'
androidTestCompile "org.mockito:mockito-core:${MOCKITO_CORE_VERSION}"
}
apply from: 'release.gradle'

View File

@@ -0,0 +1,14 @@
VERSION_NAME=0.55.4
GROUP=com.facebook.react
POM_NAME=ReactNative
POM_ARTIFACT_ID=react-native
POM_PACKAGING=aar
android.useDeprecatedNdk=true
MOCKITO_CORE_VERSION=1.10.19
POWERMOCK_VERSION=1.6.2
ROBOLECTRIC_VERSION=3.0
JUNIT_VERSION=4.12
FEST_ASSERT_CORE_VERSION=2.0M10

22
node_modules/react-native/ReactAndroid/libs/BUCK generated vendored Normal file
View File

@@ -0,0 +1,22 @@
android_prebuilt_aar(
name = "appcompat",
aar = ":appcompat-binary-aar",
visibility = ["//ReactAndroid/..."],
)
remote_file(
name = "appcompat-binary-aar",
sha1 = "7d659f671541394a8bc2b9f909950aa2a5ec87ff",
url = "mvn:com.android.support:appcompat-v7:aar:23.0.1",
)
android_prebuilt_aar(
name = "android-jsc",
aar = ":android-jsc-aar",
)
remote_file(
name = "android-jsc-aar",
sha1 = "880cedd93f43e0fc841f01f2fa185a63d9230f85",
url = "mvn:org.webkit:android-jsc:aar:r174650",
)

133
node_modules/react-native/ReactAndroid/release.gradle generated vendored Normal file
View File

@@ -0,0 +1,133 @@
// Copyright 2015-present Facebook. All Rights Reserved.
apply plugin: 'maven'
apply plugin: 'signing'
// Gradle tasks for publishing to maven
// 1) To install in local maven repo use :installArchives task
// 2) To upload artifact to maven central use: :uploadArchives (you'd need to have the permission to do that)
def isReleaseBuild() {
return VERSION_NAME.contains('SNAPSHOT') == false
}
def getRepositoryUrl() {
return project.hasProperty('repositoryUrl') ? property('repositoryUrl') : 'https://oss.sonatype.org/service/local/staging/deploy/maven2/'
}
def getRepositoryUsername() {
return project.hasProperty('repositoryUsername') ? property('repositoryUsername') : ''
}
def getRepositoryPassword() {
return project.hasProperty('repositoryPassword') ? property('repositoryPassword') : ''
}
def configureReactNativePom(def pom) {
pom.project {
name POM_NAME
artifactId POM_ARTIFACT_ID
packaging POM_PACKAGING
description 'A framework for building native apps with React'
url 'https://github.com/facebook/react-native'
scm {
url 'https://github.com/facebook/react-native.git'
connection 'scm:git:https://github.com/facebook/react-native.git'
developerConnection 'scm:git:git@github.com:facebook/react-native.git'
}
licenses {
license {
name 'MIT License'
url 'https://github.com/facebook/react-native/blob/master/LICENSE'
distribution 'repo'
}
}
developers {
developer {
id 'facebook'
name 'Facebook'
}
}
}
}
if (JavaVersion.current().isJava8Compatible()) {
allprojects {
tasks.withType(Javadoc) {
options.addStringOption('Xdoclint:none', '-quiet')
}
}
}
afterEvaluate { project ->
task androidJavadoc(type: Javadoc) {
source = android.sourceSets.main.java.srcDirs
classpath += files(android.bootClasspath)
classpath += files(project.getConfigurations().getByName('compile').asList())
include '**/*.java'
exclude '**/ReactBuildConfig.java'
}
task androidJavadocJar(type: Jar, dependsOn: androidJavadoc) {
classifier = 'javadoc'
from androidJavadoc.destinationDir
}
task androidSourcesJar(type: Jar) {
classifier = 'sources'
from android.sourceSets.main.java.srcDirs
include '**/*.java'
}
android.libraryVariants.all { variant ->
def name = variant.name.capitalize()
task "jar${name}"(type: Jar, dependsOn: variant.javaCompile) {
from variant.javaCompile.destinationDir
}
}
artifacts {
archives androidSourcesJar
archives androidJavadocJar
}
version = VERSION_NAME
group = GROUP
signing {
required { isReleaseBuild() && gradle.taskGraph.hasTask('uploadArchives') }
sign configurations.archives
}
uploadArchives {
configuration = configurations.archives
repositories.mavenDeployer {
beforeDeployment {
MavenDeployment deployment -> signing.signPom(deployment)
}
repository(url: getRepositoryUrl()) {
authentication(
userName: getRepositoryUsername(),
password: getRepositoryPassword())
}
configureReactNativePom pom
}
}
task installArchives(type: Upload) {
configuration = configurations.archives
repositories.mavenDeployer {
// Deploy to react-native/android, ready to publish to npm
repository url: "file://${projectDir}/../android"
configureReactNativePom pom
}
}
}

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.facebook.react.tests.gradle"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk android:targetSdkVersion="7" />
<supports-screens android:anyDensity="true" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!-- needed for screenshot tests -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application
android:hardwareAccelerated="false">
<activity
android:name="com.facebook.react.testing.ReactAppTestActivity"
android:theme="@style/Theme.ReactNative.AppCompat.Light.NoActionBar.FullScreen"
/>
</application>
</manifest>

View File

@@ -0,0 +1,7 @@
load("//ReactNative:DEFS.bzl", "rn_android_resource")
rn_android_resource(
name = "assets",
assets = ".",
visibility = ["PUBLIC"],
)

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.facebook.react.tests">
<supports-screens android:anyDensity="true" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!-- needed for screenshot tests -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application
android:hardwareAccelerated="false">
<uses-library android:name="android.test.runner" />
<activity
android:name="com.facebook.react.testing.ReactAppTestActivity"
android:theme="@style/Theme.ReactNative.AppCompat.Light.NoActionBar.FullScreen">
</activity>
</application>
<instrumentation
android:name="android.support.test.runner.AndroidJUnitRunner"
android:targetPackage="com.facebook.react.tests"
android:label="Buck runs instrumentation tests"/>
</manifest>

View File

@@ -0,0 +1,24 @@
load("//ReactNative:DEFS.bzl", "rn_android_binary", "react_native_dep", "react_native_integration_tests_target", "react_native_target", "KEYSTORE_TARGET")
# We are running instrumentation tests in simple mode: app code and instrumentation are in the same APK
# Currently you need to run these commands to execute tests:
#
# node local-cli/cli.js bundle --platform android --dev true --entry-file ReactAndroid/src/androidTest/js/TestBundle.js --bundle-output ReactAndroid/src/androidTest/assets/AndroidTestBundle.js
# gradle :ReactAndroid:packageReactNdkLibsForBuck
# buck install ReactAndroid/src/androidTest/buck-runner:instrumentation-tests
# ./scripts/run-android-instrumentation-tests.sh com.facebook.react.tests
rn_android_binary(
name = "instrumentation-tests",
keystore = KEYSTORE_TARGET,
manifest = "AndroidManifest.xml",
deps = [
react_native_dep("android_res/com/facebook/catalyst/appcompat:appcompat"),
react_native_dep("libraries/soloader/java/com/facebook/soloader:soloader"),
react_native_dep("third-party/java/testing-support-lib:exposed-instrumentation-api"),
react_native_integration_tests_target("assets:assets"),
react_native_integration_tests_target("java/com/facebook/react/tests:tests"),
react_native_target("java/com/facebook/react/devsupport:devsupport"),
react_native_target("jni/prebuilt:android-jsc"),
react_native_target("jni/prebuilt:reactnative-libs"),
],
)

View File

@@ -0,0 +1,112 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.testing;
import java.util.ArrayList;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import com.facebook.react.bridge.BaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.testing.ReactInstanceSpecForTest;
import com.facebook.react.testing.ReactAppInstrumentationTestCase;
/**
* Shared by {@link ReactScrollViewTestCase} and {@link ReactHorizontalScrollViewTestCase}.
* See also ScrollViewTestModule.js
*/
public abstract class AbstractScrollViewTestCase extends ReactAppInstrumentationTestCase {
protected ScrollListenerModule mScrollListenerModule;
protected static interface ScrollViewTestModule extends JavaScriptModule {
public void scrollTo(float destX, float destY);
}
@Override
protected void tearDown() throws Exception {
waitForBridgeAndUIIdle(60000);
super.tearDown();
}
@Override
protected ReactInstanceSpecForTest createReactInstanceSpecForTest() {
mScrollListenerModule = new ScrollListenerModule();
return super.createReactInstanceSpecForTest()
.addNativeModule(mScrollListenerModule);
}
// See ScrollViewListenerModule.js
protected static class ScrollListenerModule extends BaseJavaModule {
private final ArrayList<Double> mXOffsets = new ArrayList<Double>();
private final ArrayList<Double> mYOffsets = new ArrayList<Double>();
private final ArrayList<Integer> mItemsPressed = new ArrayList<Integer>();
private final Semaphore mScrollSignaler = new Semaphore(0);
private boolean mScrollBeginDragCalled;
private boolean mScrollEndDragCalled;
// Matches ScrollViewListenerModule.js
@Override
public String getName() {
return "ScrollListener";
}
@ReactMethod
public void onScroll(double x, double y) {
mXOffsets.add(x);
mYOffsets.add(y);
mScrollSignaler.release();
}
@ReactMethod
public void onItemPress(int itemNumber) {
mItemsPressed.add(itemNumber);
}
@ReactMethod
public void onScrollBeginDrag(double x, double y) {
mScrollBeginDragCalled = true;
}
@ReactMethod
public void onScrollEndDrag(double x, double y) {
mScrollEndDragCalled = true;
}
public void waitForScrollIdle() {
while (true) {
try {
boolean gotScrollSignal = mScrollSignaler.tryAcquire(1000, TimeUnit.MILLISECONDS);
if (!gotScrollSignal) {
return;
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
public ArrayList<Double> getXOffsets() {
return mXOffsets;
}
public ArrayList<Double> getYOffsets() {
return mYOffsets;
}
public ArrayList<Integer> getItemsPressed() {
return mItemsPressed;
}
public boolean dragEventsMatch() {
return mScrollBeginDragCalled && mScrollEndDragCalled;
}
}
}

View File

@@ -0,0 +1,56 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.testing;
import javax.annotation.Nullable;
import com.facebook.react.bridge.BaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
/**
* NativeModule for tests that allows assertions from JS to propagate to Java.
*/
public class AssertModule extends BaseJavaModule {
private boolean mGotSuccess;
private boolean mGotFailure;
private @Nullable String mFirstFailureStackTrace;
@Override
public String getName() {
return "Assert";
}
@ReactMethod
public void fail(String stackTrace) {
if (!mGotFailure) {
mGotFailure = true;
mFirstFailureStackTrace = stackTrace;
}
}
@ReactMethod
public void success() {
mGotSuccess = true;
}
/**
* Allows the user of this module to verify that asserts are actually being called from JS and
* that none of them failed.
*/
public void verifyAssertsAndReset() {
assertFalse("First failure: " + mFirstFailureStackTrace, mGotFailure);
assertTrue("Received no assertions during the test!", mGotSuccess);
mGotFailure = false;
mGotSuccess = false;
mFirstFailureStackTrace = null;
}
}

View File

@@ -0,0 +1,38 @@
load("//ReactNative:DEFS.bzl", "rn_android_library", "react_native_dep", "react_native_integration_tests_target", "react_native_target")
rn_android_library(
name = "testing",
srcs = glob(
["**/*.java"],
excludes = [
"idledetection/**/*.java",
"network/**/*.java",
],
),
visibility = [
"PUBLIC",
],
deps = [
react_native_dep("libraries/soloader/java/com/facebook/soloader:soloader"),
react_native_dep("third-party/android/support/v4:lib-support-v4"),
react_native_dep("third-party/java/buck-android-support:buck-android-support"),
react_native_dep("third-party/java/infer-annotations:infer-annotations"),
react_native_dep("third-party/java/jsr-305:jsr-305"),
react_native_dep("third-party/java/junit:junit"),
react_native_dep("third-party/java/mockito:mockito"),
react_native_dep("third-party/java/testing-support-lib:runner"),
react_native_integration_tests_target("java/com/facebook/react/testing/idledetection:idledetection"),
react_native_integration_tests_target("java/com/facebook/react/testing/network:network"),
react_native_target("java/com/facebook/react:react"),
react_native_target("java/com/facebook/react/bridge:bridge"),
react_native_target("java/com/facebook/react/common:common"),
react_native_target("java/com/facebook/react/devsupport:interfaces"),
react_native_target("java/com/facebook/react/module/annotations:annotations"),
react_native_target("java/com/facebook/react/module/model:model"),
react_native_target("java/com/facebook/react/modules/core:core"),
react_native_target("java/com/facebook/react/modules/debug:interfaces"),
react_native_target("java/com/facebook/react/shell:shell"),
react_native_target("java/com/facebook/react/uimanager:uimanager"),
react_native_target("res:uimanager"),
],
)

View File

@@ -0,0 +1,57 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.testing;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.BaseJavaModule;
import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.WritableMap;
/**
* Dummy implementation of storage module, used for testing
*/
public final class FakeAsyncLocalStorage extends BaseJavaModule {
private static WritableMap errorMessage;
static {
errorMessage = Arguments.createMap();
errorMessage.putString("message", "Fake Async Local Storage");
}
@Override
public String getName() {
return "AsyncSQLiteDBStorage";
}
@ReactMethod
public void multiGet(final ReadableArray keys, final Callback callback) {
callback.invoke(errorMessage, null);
}
@ReactMethod
public void multiSet(final ReadableArray keyValueArray, final Callback callback) {
callback.invoke(errorMessage);
}
@ReactMethod
public void multiRemove(final ReadableArray keys, final Callback callback) {
callback.invoke(errorMessage);
}
@ReactMethod
public void clear(Callback callback) {
callback.invoke(errorMessage);
}
@ReactMethod
public void getAllKeys(final Callback callback) {
callback.invoke(errorMessage, null);
}
}

View File

@@ -0,0 +1,51 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.testing;
import javax.annotation.Nullable;
import com.facebook.react.bridge.BaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
/**
* Dummy implementation of storage module, used for testing
*/
public final class FakeWebSocketModule extends BaseJavaModule {
@Override
public String getName() {
return "WebSocketModule";
}
@Override
public boolean canOverrideExistingModule() {
return true;
}
@ReactMethod
public void connect(
final String url,
@Nullable final ReadableArray protocols,
@Nullable final ReadableMap headers,
final int id) {
}
@ReactMethod
public void close(int code, String reason, int id) {
}
@ReactMethod
public void send(String message, int id) {
}
@ReactMethod
public void sendBinary(String base64String, int id) {
}
}

View File

@@ -0,0 +1,42 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.testing;
import java.util.List;
import android.view.View;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.uimanager.ViewManager;
import com.facebook.react.ReactPackage;
/**
* This class wraps {@class ReactInstanceSpecForTest} in {@class ReactPackage} interface.
* TODO(6788898): Refactor test code to use ReactPackage instead of SpecForTest
*/
public class InstanceSpecForTestPackage implements ReactPackage {
private final ReactInstanceSpecForTest mSpecForTest;
public InstanceSpecForTestPackage(ReactInstanceSpecForTest specForTest) {
mSpecForTest = specForTest;
}
@Override
public List<NativeModule> createNativeModules(
ReactApplicationContext catalystApplicationContext) {
return mSpecForTest.getExtraNativeModulesForTest();
}
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return mSpecForTest.getExtraViewManagers();
}
}

View File

@@ -0,0 +1,55 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.testing;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import com.facebook.react.bridge.BaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
/**
* Native module provides single method {@link #record} which records its single int argument
* in calls array
*/
public class IntRecordingModule extends BaseJavaModule {
private final List<Integer> mCalls = new ArrayList<>();
private final CountDownLatch mCountDownLatch = new CountDownLatch(1);
@Override
public String getName() {
return "Recording";
}
@ReactMethod
public void record(int i) {
mCalls.add(i);
mCountDownLatch.countDown();
}
public void reset() {
mCalls.clear();
}
public List<Integer> getCalls() {
return mCalls;
}
public void waitForFirstCall() {
try {
if (!mCountDownLatch.await(15000, TimeUnit.MILLISECONDS)) {
throw new RuntimeException("Timed out waiting for first call");
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -0,0 +1,58 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.testing;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import com.facebook.react.bridge.BaseJavaModule;
import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.ReactMethod;
/**
* This class is used to verify that some JS integration tests have completed successfully.
* The JS integration tests can be started from a ReactIntegrationTestCase and upon
* finishing successfully the {@link JSIntegrationTestChecker#testDone()} method will be called.
* To verify if the test has completed successfully, call {#link JSIntegrationTestChecker#await()}
* to wait for the test to run, and {#link JSIntegrationTestChecker#isTestDone()} to check if it
* completed successfully.
*/
public class JSIntegrationTestChecker extends BaseJavaModule {
private final CountDownLatch mLatch;
public JSIntegrationTestChecker() {
mLatch = new CountDownLatch(1);
}
@Override
public String getName() {
return "TestModule";
}
@ReactMethod
public void markTestCompleted() {
mLatch.countDown();
}
@ReactMethod
public void verifySnapshot(Callback callback) {
}
public boolean await(long ms) {
try {
return mLatch.await(ms, TimeUnit.MILLISECONDS);
} catch (InterruptedException ignore) {
return false;
}
}
public boolean isTestDone() {
return mLatch.getCount() == 0;
}
}

View File

@@ -0,0 +1,46 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.testing;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.List;
/**
* Custom implementation of {@link org.junit.runners.model.MultipleFailureException} that includes
* stack information of collected exception as a part of the message.
*/
public class MultipleFailureException extends org.junit.runners.model.MultipleFailureException {
public MultipleFailureException(List<Throwable> errors) {
super(errors);
}
@Override
public String getMessage() {
StringBuilder sb = new StringBuilder();
List<Throwable> errors = getFailures();
sb.append(String.format("There were %d errors:", errors.size()));
int i = 0;
for (Throwable e : errors) {
sb.append(String.format("%n---- Error #%d", i));
sb.append("\n" + getStackTraceAsString(e));
i++;
}
sb.append("\n");
return sb.toString();
}
private static String getStackTraceAsString(Throwable throwable) {
StringWriter stringWriter = new StringWriter();
throwable.printStackTrace(new PrintWriter(stringWriter));
return stringWriter.toString();
}
}

View File

@@ -0,0 +1,165 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.testing;
import javax.annotation.Nullable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import android.graphics.Bitmap;
import android.test.ActivityInstrumentationTestCase2;
import android.view.View;
import android.view.ViewGroup;
import com.facebook.infer.annotation.Assertions;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.testing.idledetection.IdleWaiter;
/**
* Base class for instrumentation tests that runs React based react application in UI mode
*/
public abstract class ReactAppInstrumentationTestCase extends
ActivityInstrumentationTestCase2<ReactAppTestActivity> implements IdleWaiter {
public ReactAppInstrumentationTestCase() {
super(ReactAppTestActivity.class);
}
@Override
protected void setUp() throws Exception {
super.setUp();
final ReactAppTestActivity activity = getActivity();
try {
runTestOnUiThread(new Runnable() {
@Override
public void run() {
activity.loadApp(
getReactApplicationKeyUnderTest(),
createReactInstanceSpecForTest(),
getEnableDevSupport());
}
});
} catch (Throwable t) {
throw new Exception("Unable to load react app", t);
}
waitForBridgeAndUIIdle();
assertTrue("Layout never occurred!", activity.waitForLayout(5000));
waitForBridgeAndUIIdle();
}
@Override
protected void tearDown() throws Exception {
ReactAppTestActivity activity = getActivity();
super.tearDown();
activity.waitForDestroy(5000);
}
public ViewGroup getRootView() {
return (ViewGroup) getActivity().getRootView();
}
/**
* This method isn't safe since it doesn't factor in layout-only view removal. Use
* {@link #getViewByTestId(String)} instead.
*/
@Deprecated
public <T extends View> T getViewAtPath(int... path) {
return ReactTestHelper.getViewAtPath((ViewGroup) getRootView().getParent(), path);
}
public <T extends View> T getViewByTestId(String testID) {
return (T) ReactTestHelper
.getViewWithReactTestId((ViewGroup) getRootView().getParent(), testID);
}
public SingleTouchGestureGenerator createGestureGenerator() {
return new SingleTouchGestureGenerator(getRootView(), this);
}
public void waitForBridgeAndUIIdle() {
getActivity().waitForBridgeAndUIIdle();
}
public void waitForBridgeAndUIIdle(long timeoutMs) {
getActivity().waitForBridgeAndUIIdle(timeoutMs);
}
protected Bitmap getScreenshot() {
// Wait for the UI to settle. If the UI is doing animations, this may be unsafe!
getInstrumentation().waitForIdleSync();
final CountDownLatch latch = new CountDownLatch(1);
final BitmapHolder bitmapHolder = new BitmapHolder();
final Runnable getScreenshotRunnable = new Runnable() {
private static final int MAX_TRIES = 1000;
// This is the constant used in the support library for APIs that don't have Choreographer
private static final int FRAME_DELAY_MS = 10;
private int mNumRuns = 0;
@Override
public void run() {
mNumRuns++;
ReactAppTestActivity activity = getActivity();
if (!activity.isScreenshotReady()) {
if (mNumRuns > MAX_TRIES) {
throw new RuntimeException(
"Waited " + MAX_TRIES + " frames to get screenshot but it's still not ready!");
}
activity.postDelayed(this, FRAME_DELAY_MS);
return;
}
bitmapHolder.bitmap = getActivity().getCurrentScreenshot();
latch.countDown();
}
};
getActivity().runOnUiThread(getScreenshotRunnable);
try {
if (!latch.await(5000, TimeUnit.MILLISECONDS)) {
throw new RuntimeException("Timed out waiting for screenshot runnable to run!");
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return Assertions.assertNotNull(bitmapHolder.bitmap);
}
/**
* Implement this method to provide application key to be launched. List of available
* application is located in TestBundle.js file
*/
protected abstract String getReactApplicationKeyUnderTest();
protected boolean getEnableDevSupport() {
return false;
}
/**
* Override this method to provide extra native modules to be loaded before the app starts
*/
protected ReactInstanceSpecForTest createReactInstanceSpecForTest() {
return new ReactInstanceSpecForTest();
}
protected ReactContext getReactContext() {
return getActivity().getReactContext();
}
/**
* Helper class to pass the bitmap between execution scopes in {@link #getScreenshot()}.
*/
private static class BitmapHolder {
public @Nullable volatile Bitmap bitmap;
}
}

View File

@@ -0,0 +1,276 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.testing;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.view.View;
import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
import com.facebook.infer.annotation.Assertions;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactInstanceManagerBuilder;
import com.facebook.react.ReactRootView;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.common.LifecycleState;
import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
import com.facebook.react.modules.core.PermissionAwareActivity;
import com.facebook.react.modules.core.PermissionListener;
import com.facebook.react.shell.MainReactPackage;
import com.facebook.react.testing.idledetection.ReactBridgeIdleSignaler;
import com.facebook.react.testing.idledetection.ReactIdleDetectionUtil;
import com.facebook.react.uimanager.UIImplementationProvider;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nullable;
public class ReactAppTestActivity extends FragmentActivity
implements DefaultHardwareBackBtnHandler, PermissionAwareActivity {
private static final String DEFAULT_BUNDLE_NAME = "AndroidTestBundle.js";
private static final int ROOT_VIEW_ID = 8675309;
// we need a bigger timeout for CI builds because they run on a slow emulator
private static final long IDLE_TIMEOUT_MS = 120000;
private CountDownLatch mLayoutEvent = new CountDownLatch(1);
private @Nullable ReactBridgeIdleSignaler mBridgeIdleSignaler;
private ScreenshotingFrameLayout mScreenshotingFrameLayout;
private final CountDownLatch mDestroyCountDownLatch = new CountDownLatch(1);
private @Nullable ReactInstanceManager mReactInstanceManager;
private @Nullable ReactRootView mReactRootView;
private LifecycleState mLifecycleState = LifecycleState.BEFORE_RESUME;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
overridePendingTransition(0, 0);
// We wrap screenshot layout in another FrameLayout in order to handle custom dimensions of the
// screenshot view set through {@link #setScreenshotDimensions}
FrameLayout rootView = new FrameLayout(this);
setContentView(rootView);
mScreenshotingFrameLayout = new ScreenshotingFrameLayout(this);
mScreenshotingFrameLayout.setId(ROOT_VIEW_ID);
rootView.addView(mScreenshotingFrameLayout);
mReactRootView = new ReactRootView(this);
mScreenshotingFrameLayout.addView(mReactRootView);
}
@Override
protected void onPause() {
super.onPause();
mLifecycleState = LifecycleState.BEFORE_RESUME;
overridePendingTransition(0, 0);
if (mReactInstanceManager != null) {
mReactInstanceManager.onHostPause();
}
}
@Override
protected void onResume() {
super.onResume();
mLifecycleState = LifecycleState.RESUMED;
if (mReactInstanceManager != null) {
mReactInstanceManager.onHostResume(this, this);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
mDestroyCountDownLatch.countDown();
if (mReactInstanceManager != null) {
mReactInstanceManager.destroy();
mReactInstanceManager = null;
}
if (mReactRootView != null) {
mReactRootView.unmountReactApplication();
mReactRootView = null;
}
mScreenshotingFrameLayout.clean();
}
public void waitForDestroy(long timeoutMs) throws InterruptedException {
mDestroyCountDownLatch.await(timeoutMs, TimeUnit.MILLISECONDS);
}
public void loadApp(String appKey, ReactInstanceSpecForTest spec, boolean enableDevSupport) {
loadApp(appKey, spec, null, DEFAULT_BUNDLE_NAME, enableDevSupport);
}
public void loadApp(String appKey, ReactInstanceSpecForTest spec, String bundleName) {
loadApp(appKey, spec, null, bundleName, false /* = useDevSupport */);
}
public void loadApp(
String appKey,
ReactInstanceSpecForTest spec,
String bundleName,
UIImplementationProvider uiImplementationProvider) {
loadApp(appKey, spec, null, bundleName, false /* = useDevSupport */, uiImplementationProvider);
}
public void resetRootViewForScreenshotTests() {
if (mReactInstanceManager != null) {
mReactInstanceManager.destroy();
mReactInstanceManager = null;
}
if (mReactRootView != null) {
mReactRootView.unmountReactApplication();
}
mReactRootView = new ReactRootView(this);
mScreenshotingFrameLayout.removeAllViews();
mScreenshotingFrameLayout.clean();
mScreenshotingFrameLayout.addView(mReactRootView);
}
public void loadApp(
String appKey,
ReactInstanceSpecForTest spec,
@Nullable Bundle initialProps,
String bundleName,
boolean useDevSupport) {
loadApp(appKey, spec, initialProps, bundleName, useDevSupport, null);
}
public void loadApp(
String appKey,
ReactInstanceSpecForTest spec,
@Nullable Bundle initialProps,
String bundleName,
boolean useDevSupport,
UIImplementationProvider uiImplementationProvider) {
final CountDownLatch currentLayoutEvent = mLayoutEvent = new CountDownLatch(1);
mBridgeIdleSignaler = new ReactBridgeIdleSignaler();
ReactInstanceManagerBuilder builder =
ReactTestHelper.getReactTestFactory()
.getReactInstanceManagerBuilder()
.setApplication(getApplication())
.setBundleAssetName(bundleName);
if (!spec.getAlternativeReactPackagesForTest().isEmpty()) {
builder.addPackages(spec.getAlternativeReactPackagesForTest());
} else {
builder.addPackage(new MainReactPackage());
}
builder
.addPackage(new InstanceSpecForTestPackage(spec))
// By not setting a JS module name, we force the bundle to be always loaded from
// assets, not the devserver, even if dev mode is enabled (such as when testing redboxes).
// This makes sense because we never run the devserver in tests.
//.setJSMainModuleName()
.setUseDeveloperSupport(useDevSupport)
.setBridgeIdleDebugListener(mBridgeIdleSignaler)
.setInitialLifecycleState(mLifecycleState)
.setUIImplementationProvider(uiImplementationProvider);
mReactInstanceManager = builder.build();
mReactInstanceManager.onHostResume(this, this);
Assertions.assertNotNull(mReactRootView).getViewTreeObserver().addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
currentLayoutEvent.countDown();
}
});
Assertions.assertNotNull(mReactRootView)
.startReactApplication(mReactInstanceManager, appKey, initialProps);
}
public boolean waitForLayout(long millis) throws InterruptedException {
return mLayoutEvent.await(millis, TimeUnit.MILLISECONDS);
}
public void waitForBridgeAndUIIdle() {
waitForBridgeAndUIIdle(IDLE_TIMEOUT_MS);
}
public void waitForBridgeAndUIIdle(long timeoutMs) {
ReactIdleDetectionUtil.waitForBridgeAndUIIdle(
Assertions.assertNotNull(mBridgeIdleSignaler),
getReactContext(),
timeoutMs);
}
public View getRootView() {
return Assertions.assertNotNull(mReactRootView);
}
public ReactContext getReactContext() {
return waitForReactContext();
}
// Because react context is created asynchronously, we may have to wait until it is available.
// It's simpler than exposing synchronosition mechanism to notify listener than react context
// creation has completed.
private ReactContext waitForReactContext() {
Assertions.assertNotNull(mReactInstanceManager);
try {
while (true) {
ReactContext reactContext = mReactInstanceManager.getCurrentReactContext();
if (reactContext != null) {
return reactContext;
}
Thread.sleep(100);
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
public void postDelayed(Runnable r, int delayMS) {
getRootView().postDelayed(r, delayMS);
}
/**
* Does not ensure that this is run on the UI thread or that the UI Looper is idle like
* {@link ReactAppInstrumentationTestCase#getScreenshot()}. You probably want to use that
* instead.
*/
public Bitmap getCurrentScreenshot() {
return mScreenshotingFrameLayout.getLastDrawnBitmap();
}
public boolean isScreenshotReady() {
return mScreenshotingFrameLayout.isScreenshotReady();
}
public void setScreenshotDimensions(int width, int height) {
mScreenshotingFrameLayout.setLayoutParams(new FrameLayout.LayoutParams(width, height));
}
@Override
public void invokeDefaultOnBackPressed() {
super.onBackPressed();
}
@Override
public void onRequestPermissionsResult(
int requestCode,
String[] permissions,
int[] grantResults) {
}
@Override
public void requestPermissions(
String[] permissions, int requestCode, PermissionListener listener) {}
}

View File

@@ -0,0 +1,76 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.testing;
import android.annotation.SuppressLint;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.uimanager.ViewManager;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* A spec that allows a test to add additional NativeModules/JS modules to the ReactInstance. This
* can also be used to stub out existing native modules by adding another module with the same name
* as a built-in module.
*/
@SuppressLint("JavatestsIncorrectFolder")
public class ReactInstanceSpecForTest {
private final List<NativeModule> mNativeModules =
new ArrayList<NativeModule>(Arrays.asList(new FakeWebSocketModule()));
private final List<Class<? extends JavaScriptModule>> mJSModuleSpecs = new ArrayList<>();
private final List<ViewManager> mViewManagers = new ArrayList<>();
private final ArrayList<ReactPackage> mReactPackages = new ArrayList<>();
public ReactInstanceSpecForTest addNativeModule(NativeModule module) {
mNativeModules.add(module);
return this;
}
public ReactInstanceSpecForTest setPackage(ReactPackage reactPackage) {
if (!mReactPackages.isEmpty()) {
throw new IllegalStateException(
"setPackage is not allowed after addPackages. " + reactPackage);
}
mReactPackages.add(reactPackage);
return this;
}
public ReactInstanceSpecForTest addPackages(List<ReactPackage> reactPackages) {
mReactPackages.addAll(reactPackages);
return this;
}
public ReactInstanceSpecForTest addViewManager(ViewManager viewManager) {
mViewManagers.add(viewManager);
return this;
}
public List<NativeModule> getExtraNativeModulesForTest() {
return mNativeModules;
}
public ReactPackage getAlternativeReactPackageForTest() {
if (mReactPackages.size() > 1) {
throw new IllegalStateException(
"Multiple packages were added - use getAlternativeReactPackagesForTest instead.");
}
return mReactPackages.get(0);
}
public List<ReactPackage> getAlternativeReactPackagesForTest() {
return mReactPackages;
}
public List<ViewManager> getExtraViewManagers() {
return mViewManagers;
}
}

View File

@@ -0,0 +1,210 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.testing;
import com.facebook.react.modules.core.ReactChoreographer;
import javax.annotation.Nullable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import android.app.Application;
import android.support.test.InstrumentationRegistry;
import android.test.AndroidTestCase;
import android.view.View;
import android.view.ViewGroup;
import com.facebook.infer.annotation.Assertions;
import com.facebook.react.bridge.BaseJavaModule;
import com.facebook.react.bridge.CatalystInstance;
import com.facebook.react.bridge.LifecycleEventListener;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.SoftAssertions;
import com.facebook.react.bridge.UiThreadUtil;
import com.facebook.react.common.futures.SimpleSettableFuture;
import com.facebook.react.devsupport.interfaces.DevSupportManager;
import com.facebook.react.modules.core.Timing;
import com.facebook.react.testing.idledetection.ReactBridgeIdleSignaler;
import com.facebook.react.testing.idledetection.ReactIdleDetectionUtil;
import com.facebook.soloader.SoLoader;
import static org.mockito.Mockito.mock;
/**
* Use this class for writing integration tests of catalyst. This class will run all JNI call
* within separate android looper, thus you don't need to care about starting your own looper.
*
* Keep in mind that all JS remote method calls and script load calls are asynchronous and you
* should not expect them to return results immediately.
*
* In order to write catalyst integration:
* 1) Make {@link ReactIntegrationTestCase} a base class of your test case
* 2) Use {@link ReactTestHelper#catalystInstanceBuilder()}
* instead of {@link com.facebook.react.bridge.CatalystInstanceImpl.Builder} to build catalyst
* instance for testing purposes
*
*/
public abstract class ReactIntegrationTestCase extends AndroidTestCase {
// we need a bigger timeout for CI builds because they run on a slow emulator
private static final long IDLE_TIMEOUT_MS = 60000;
private @Nullable CatalystInstance mInstance;
private @Nullable ReactBridgeIdleSignaler mBridgeIdleSignaler;
private @Nullable ReactApplicationContext mReactContext;
@Override
public ReactApplicationContext getContext() {
if (mReactContext == null) {
mReactContext = new ReactApplicationContext(super.getContext());
Assertions.assertNotNull(mReactContext);
}
return mReactContext;
}
public void shutDownContext() {
if (mInstance != null) {
final ReactContext contextToDestroy = mReactContext;
mReactContext = null;
mInstance = null;
final SimpleSettableFuture<Void> semaphore = new SimpleSettableFuture<>();
UiThreadUtil.runOnUiThread(new Runnable() {
@Override
public void run() {
if (contextToDestroy != null) {
contextToDestroy.destroy();
}
semaphore.set(null);
}
});
semaphore.getOrThrow();
}
}
/**
* This method isn't safe since it doesn't factor in layout-only view removal. Use
* {@link #getViewByTestId} instead.
*/
@Deprecated
public <T extends View> T getViewAtPath(ViewGroup rootView, int... path) {
return ReactTestHelper.getViewAtPath(rootView, path);
}
public <T extends View> T getViewByTestId(ViewGroup rootView, String testID) {
return (T) ReactTestHelper.getViewWithReactTestId(rootView, testID);
}
public static class Event {
private final CountDownLatch mLatch;
public Event() {
this(1);
}
public Event(int counter) {
mLatch = new CountDownLatch(counter);
}
public void occur() {
mLatch.countDown();
}
public boolean didOccur() {
return mLatch.getCount() == 0;
}
public boolean await(long millis) {
try {
return mLatch.await(millis, TimeUnit.MILLISECONDS);
} catch (InterruptedException ignore) {
return false;
}
}
}
/**
* Timing module needs to be created on the main thread so that it gets the correct Choreographer.
*/
protected Timing createTimingModule() {
final SimpleSettableFuture<Timing> simpleSettableFuture = new SimpleSettableFuture<Timing>();
UiThreadUtil.runOnUiThread(
new Runnable() {
@Override
public void run() {
ReactChoreographer.initialize();
Timing timing = new Timing(getContext(), mock(DevSupportManager.class));
simpleSettableFuture.set(timing);
}
});
try {
return simpleSettableFuture.get(5000, TimeUnit.MILLISECONDS);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public void initializeWithInstance(CatalystInstance instance) {
mInstance = instance;
mBridgeIdleSignaler = new ReactBridgeIdleSignaler();
mInstance.addBridgeIdleDebugListener(mBridgeIdleSignaler);
getContext().initializeWithInstance(mInstance);
}
public boolean waitForBridgeIdle(long millis) {
return Assertions.assertNotNull(mBridgeIdleSignaler).waitForIdle(millis);
}
public void waitForIdleSync() {
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
}
public void waitForBridgeAndUIIdle() {
ReactIdleDetectionUtil.waitForBridgeAndUIIdle(
Assertions.assertNotNull(mBridgeIdleSignaler),
getContext(),
IDLE_TIMEOUT_MS);
}
@Override
protected void setUp() throws Exception {
super.setUp();
SoLoader.init(getContext(), /* native exopackage */ false);
}
@Override
protected void tearDown() throws Exception {
super.tearDown();
shutDownContext();
}
protected static void initializeJavaModule(final BaseJavaModule javaModule) {
final Semaphore semaphore = new Semaphore(0);
UiThreadUtil.runOnUiThread(
new Runnable() {
@Override
public void run() {
javaModule.initialize();
if (javaModule instanceof LifecycleEventListener) {
((LifecycleEventListener) javaModule).onHostResume();
}
semaphore.release();
}
});
try {
SoftAssertions.assertCondition(
semaphore.tryAcquire(5000, TimeUnit.MILLISECONDS),
"Timed out initializing timing module");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -0,0 +1,53 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.testing;
import com.facebook.react.modules.debug.interfaces.DeveloperSettings;
public class ReactSettingsForTests implements DeveloperSettings {
@Override
public boolean isFpsDebugEnabled() {
return false;
}
@Override
public boolean isAnimationFpsDebugEnabled() {
return false;
}
@Override
public boolean isJSDevModeEnabled() {
return true;
}
@Override
public boolean isJSMinifyEnabled() {
return false;
}
@Override
public boolean isElementInspectorEnabled() {
return false;
}
@Override
public boolean isNuclideJSDebugEnabled() {
return false;
}
@Override
public boolean isRemoteJSDebugEnabled() {
return false;
}
@Override
public void setRemoteJSDebugEnabled(boolean remoteJSDebugEnabled) {
}
}

View File

@@ -0,0 +1,38 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.testing;
import com.facebook.buck.android.support.exopackage.ApplicationLike;
import com.facebook.buck.android.support.exopackage.ExopackageApplication;
import com.facebook.react.common.build.ReactBuildConfig;
import com.facebook.soloader.SoLoader;
/**
* Application class for the Catalyst Launcher to allow it to work as an exopackage.
*
* Any app-specific code that should run before secondary dex files are loaded
* (like setting up crash reporting) should go in onBaseContextAttached.
* Anything that should run after secondary dex should go in CatalystApplicationImpl.onCreate.
*/
public class ReactTestAppShell extends ExopackageApplication<ApplicationLike> {
public ReactTestAppShell() {
super("com.facebook.react.testing.ReactTestApplicationImpl", ReactBuildConfig.EXOPACKAGE_FLAGS);
}
@Override
protected void onBaseContextAttached() {
// This is a terrible hack. Don't copy it.
// It's unfortunate that Instagram does the same thing.
// We need to do this here because internal apps use SoLoader,
// and Open Source Buck uses ExopackageSoLoader.
// If you feel the need to copy this, we should refactor it
// into an FB-specific subclass of ExopackageApplication.
SoLoader.init(this, (ReactBuildConfig.EXOPACKAGE_FLAGS & 2) != 0);
}
}

View File

@@ -0,0 +1,23 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.testing;
import android.app.Application;
import com.facebook.buck.android.support.exopackage.DefaultApplicationLike;
public class ReactTestApplicationImpl extends DefaultApplicationLike {
public ReactTestApplicationImpl() {
super();
}
public ReactTestApplicationImpl(Application application) {
super(application);
}
}

View File

@@ -0,0 +1,25 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.testing;
import android.content.Context;
import com.facebook.react.ReactInstanceManagerBuilder;
import com.facebook.react.bridge.CatalystInstance;
import com.facebook.react.bridge.NativeModule;
public interface ReactTestFactory {
public static interface ReactInstanceEasyBuilder {
ReactInstanceEasyBuilder setContext(Context context);
ReactInstanceEasyBuilder addNativeModule(NativeModule module);
CatalystInstance build();
}
ReactInstanceEasyBuilder getCatalystInstanceBuilder();
ReactInstanceManagerBuilder getReactInstanceManagerBuilder();
}

View File

@@ -0,0 +1,211 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.testing;
import javax.annotation.Nullable;
import android.app.Instrumentation;
import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.view.View;
import android.view.ViewGroup;
import com.facebook.infer.annotation.Assertions;
import com.facebook.react.NativeModuleRegistryBuilder;
import com.facebook.react.R;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactInstanceManagerBuilder;
import com.facebook.react.bridge.CatalystInstance;
import com.facebook.react.bridge.JavaScriptModuleRegistry;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.NativeModuleCallExceptionHandler;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.WritableNativeMap;
import com.facebook.react.bridge.queue.ReactQueueConfigurationSpec;
import com.facebook.react.bridge.CatalystInstanceImpl;
import com.facebook.react.bridge.JSBundleLoader;
import com.facebook.react.bridge.JSCJavaScriptExecutorFactory;
import com.facebook.react.bridge.JavaScriptExecutor;
import com.facebook.react.modules.core.ReactChoreographer;
import com.android.internal.util.Predicate;
public class ReactTestHelper {
private static class DefaultReactTestFactory implements ReactTestFactory {
private static class ReactInstanceEasyBuilderImpl implements ReactInstanceEasyBuilder {
private NativeModuleRegistryBuilder mNativeModuleRegistryBuilder;
private @Nullable Context mContext;
@Override
public ReactInstanceEasyBuilder setContext(Context context) {
mContext = context;
return this;
}
@Override
public ReactInstanceEasyBuilder addNativeModule(NativeModule nativeModule) {
if (mNativeModuleRegistryBuilder == null) {
mNativeModuleRegistryBuilder = new NativeModuleRegistryBuilder(
(ReactApplicationContext) mContext,
null,
false);
}
Assertions.assertNotNull(nativeModule);
mNativeModuleRegistryBuilder.addNativeModule(nativeModule);
return this;
}
@Override
public CatalystInstance build() {
if (mNativeModuleRegistryBuilder == null) {
mNativeModuleRegistryBuilder = new NativeModuleRegistryBuilder(
(ReactApplicationContext) mContext,
null,
false);
}
JavaScriptExecutor executor = null;
try {
executor = new JSCJavaScriptExecutorFactory("ReactTestHelperApp", "ReactTestHelperDevice").create();
} catch (Exception e) {
throw new RuntimeException(e);
}
return new CatalystInstanceImpl.Builder()
.setReactQueueConfigurationSpec(ReactQueueConfigurationSpec.createDefault())
.setJSExecutor(executor)
.setRegistry(mNativeModuleRegistryBuilder.build())
.setJSBundleLoader(JSBundleLoader.createAssetLoader(
mContext,
"assets://AndroidTestBundle.js",
false/* Asynchronous */))
.setNativeModuleCallExceptionHandler(
new NativeModuleCallExceptionHandler() {
@Override
public void handleException(Exception e) {
throw new RuntimeException(e);
}
})
.build();
}
}
@Override
public ReactInstanceEasyBuilder getCatalystInstanceBuilder() {
return new ReactInstanceEasyBuilderImpl();
}
@Override
public ReactInstanceManagerBuilder getReactInstanceManagerBuilder() {
return ReactInstanceManager.builder();
}
}
public static ReactTestFactory getReactTestFactory() {
Instrumentation inst = InstrumentationRegistry.getInstrumentation();
if (!(inst instanceof ReactTestFactory)) {
return new DefaultReactTestFactory();
}
return (ReactTestFactory) inst;
}
public static ReactTestFactory.ReactInstanceEasyBuilder catalystInstanceBuilder(
final ReactIntegrationTestCase testCase) {
final ReactTestFactory.ReactInstanceEasyBuilder builder =
getReactTestFactory().getCatalystInstanceBuilder();
ReactTestFactory.ReactInstanceEasyBuilder postBuilder =
new ReactTestFactory.ReactInstanceEasyBuilder() {
@Override
public ReactTestFactory.ReactInstanceEasyBuilder setContext(Context context) {
builder.setContext(context);
return this;
}
@Override
public ReactTestFactory.ReactInstanceEasyBuilder addNativeModule(NativeModule module) {
builder.addNativeModule(module);
return this;
}
@Override
public CatalystInstance build() {
final CatalystInstance instance = builder.build();
testCase.initializeWithInstance(instance);
instance.runJSBundle();
InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
@Override
public void run() {
ReactChoreographer.initialize();
instance.initialize();
}
});
testCase.waitForBridgeAndUIIdle();
return instance;
}
};
postBuilder.setContext(testCase.getContext());
return postBuilder;
}
/**
* Gets the view at given path in the UI hierarchy, ignoring modals.
*/
public static <T extends View> T getViewAtPath(ViewGroup rootView, int... path) {
// The application root element is wrapped in a helper view in order
// to be able to display modals. See renderApplication.js.
ViewGroup appWrapperView = rootView;
View view = appWrapperView.getChildAt(0);
for (int i = 0; i < path.length; i++) {
view = ((ViewGroup) view).getChildAt(path[i]);
}
return (T) view;
}
/**
* Gets the view with a given react test ID in the UI hierarchy. React test ID is currently
* propagated into view content description.
*/
public static View getViewWithReactTestId(View rootView, String testId) {
return findChild(rootView, hasTagValue(testId));
}
public static String getTestId(View view) {
return view.getTag(R.id.react_test_id) instanceof String
? (String) view.getTag(R.id.react_test_id)
: null;
}
private static View findChild(View root, Predicate<View> predicate) {
if (predicate.apply(root)) {
return root;
}
if (root instanceof ViewGroup) {
ViewGroup viewGroup = (ViewGroup) root;
for (int i = 0; i < viewGroup.getChildCount(); i++) {
View child = viewGroup.getChildAt(i);
View result = findChild(child, predicate);
if (result != null) {
return result;
}
}
}
return null;
}
private static Predicate<View> hasTagValue(final String tagValue) {
return new Predicate<View>() {
@Override
public boolean apply(View view) {
Object tag = getTestId(view);
return tag != null && tag.equals(tagValue);
}
};
}
}

View File

@@ -0,0 +1,78 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.testing;
import javax.annotation.Nullable;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.os.Looper;
import android.widget.FrameLayout;
/**
* A FrameLayout that allows you to access the result of the last time its hierarchy was drawn. It
* accomplishes this by drawing its hierarchy into a software Canvas, saving the resulting Bitmap
* and then drawing that Bitmap to the actual Canvas provided by the system.
*/
public class ScreenshotingFrameLayout extends FrameLayout {
private @Nullable Bitmap mBitmap;
private Canvas mCanvas;
public ScreenshotingFrameLayout(Context context) {
super(context);
mCanvas = new Canvas();
}
@Override
protected void dispatchDraw(Canvas canvas) {
if (mBitmap == null) {
mBitmap = createNewBitmap(canvas);
mCanvas.setBitmap(mBitmap);
} else if (mBitmap.getWidth() != canvas.getWidth() ||
mBitmap.getHeight() != canvas.getHeight()) {
mBitmap.recycle();
mBitmap = createNewBitmap(canvas);
mCanvas.setBitmap(mBitmap);
}
super.dispatchDraw(mCanvas);
canvas.drawBitmap(mBitmap, 0, 0, null);
}
public void clean() {
if (mBitmap != null) {
mBitmap.recycle();
mBitmap = null;
}
mCanvas.setBitmap(null);
}
private static Bitmap createNewBitmap(Canvas canvas) {
return Bitmap.createBitmap(canvas.getWidth(), canvas.getHeight(), Bitmap.Config.ARGB_8888);
}
public Bitmap getLastDrawnBitmap() {
if (mBitmap == null) {
throw new RuntimeException("View has not been drawn yet!");
}
if (Looper.getMainLooper() != Looper.myLooper()) {
throw new RuntimeException(
"Must access screenshots from main thread or you may get partially drawn Bitmaps");
}
if (!isScreenshotReady()) {
throw new RuntimeException("Trying to get screenshot, but the view is dirty or needs layout");
}
return Bitmap.createBitmap(mBitmap);
}
public boolean isScreenshotReady() {
return !isDirty() && !isLayoutRequested();
}
}

View File

@@ -0,0 +1,162 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.testing;
import android.os.SystemClock;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import com.facebook.react.testing.idledetection.IdleWaiter;
/**
* Provides methods for generating touch events and dispatching them directly to a given view.
* Events scenarios are based on {@link android.test.TouchUtils} but they get gets dispatched
* directly through the view hierarchy using {@link View#dispatchTouchEvent} method instead of
* using instrumentation API.
* <p>
* All the events for a gesture are dispatched immediately which makes tests run very fast.
* The eventTime for each event is still set correctly. Android's gesture recognizers check
* eventTime in order to figure out gesture speed, and therefore scroll vs fling is recognized.
*/
public class SingleTouchGestureGenerator {
private static final long DEFAULT_DELAY_MS = 20;
private View mDispatcherView;
private IdleWaiter mIdleWaiter;
private long mLastDownTime;
private long mEventTime;
private float mLastX;
private float mLastY;
private ViewConfiguration mViewConfig;
public SingleTouchGestureGenerator(View view, IdleWaiter idleWaiter) {
mDispatcherView = view;
mIdleWaiter = idleWaiter;
mViewConfig = ViewConfiguration.get(view.getContext());
}
private SingleTouchGestureGenerator dispatchEvent(
final int action,
final float x,
final float y,
long eventTime) {
mEventTime = eventTime;
if (action == MotionEvent.ACTION_DOWN) {
mLastDownTime = eventTime;
}
mLastX = x;
mLastY = y;
mDispatcherView.post(
new Runnable() {
@Override
public void run() {
MotionEvent event = MotionEvent.obtain(mLastDownTime, mEventTime, action, x, y, 0);
mDispatcherView.dispatchTouchEvent(event);
event.recycle();
}
});
mIdleWaiter.waitForBridgeAndUIIdle();
return this;
}
private float getViewCenterX(View view) {
int[] xy = new int[2];
view.getLocationOnScreen(xy);
int viewWidth = view.getWidth();
return xy[0] + (viewWidth / 2.0f);
}
private float getViewCenterY(View view) {
int[] xy = new int[2];
view.getLocationOnScreen(xy);
int viewHeight = view.getHeight();
return xy[1] + (viewHeight / 2.0f);
}
public SingleTouchGestureGenerator startGesture(float x, float y) {
return dispatchEvent(MotionEvent.ACTION_DOWN, x, y, SystemClock.uptimeMillis());
}
public SingleTouchGestureGenerator startGesture(View view) {
return startGesture(getViewCenterX(view), getViewCenterY(view));
}
private SingleTouchGestureGenerator dispatchDelayedEvent(
int action,
float x,
float y,
long delay) {
return dispatchEvent(action, x, y, mEventTime + delay);
}
public SingleTouchGestureGenerator endGesture(float x, float y, long delay) {
return dispatchDelayedEvent(MotionEvent.ACTION_UP, x, y, delay);
}
public SingleTouchGestureGenerator endGesture(float x, float y) {
return endGesture(x, y, DEFAULT_DELAY_MS);
}
public SingleTouchGestureGenerator endGesture() {
return endGesture(mLastX, mLastY);
}
public SingleTouchGestureGenerator moveGesture(float x, float y, long delay) {
return dispatchDelayedEvent(MotionEvent.ACTION_MOVE, x, y, delay);
}
public SingleTouchGestureGenerator moveBy(float dx, float dy, long delay) {
return moveGesture(mLastX + dx, mLastY + dy, delay);
}
public SingleTouchGestureGenerator moveBy(float dx, float dy) {
return moveBy(dx, dy, DEFAULT_DELAY_MS);
}
public SingleTouchGestureGenerator clickViewAt(float x, float y) {
float touchSlop = mViewConfig.getScaledTouchSlop();
return startGesture(x, y).moveBy(touchSlop / 2.0f, touchSlop / 2.0f).endGesture();
}
public SingleTouchGestureGenerator drag(
float fromX,
float fromY,
float toX,
float toY,
int stepCount,
long totalDelay) {
float xStep = (toX - fromX) / stepCount;
float yStep = (toY - fromY) / stepCount;
float x = fromX;
float y = fromY;
for (int i = 0; i < stepCount; i++) {
x += xStep;
y += yStep;
moveGesture(x, y, totalDelay / stepCount);
}
return this;
}
public SingleTouchGestureGenerator dragTo(float toX, float toY, int stepCount, long totalDelay) {
return drag(mLastX, mLastY, toX, toY, stepCount, totalDelay);
}
public SingleTouchGestureGenerator dragTo(View view, int stepCount, long totalDelay) {
return dragTo(getViewCenterX(view), getViewCenterY(view), stepCount, totalDelay);
}
public SingleTouchGestureGenerator dragTo(View view, int stepCount) {
return dragTo(view, stepCount, stepCount * DEFAULT_DELAY_MS);
}
}

View File

@@ -0,0 +1,41 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.testing;
import java.util.ArrayList;
import java.util.List;
import com.facebook.react.bridge.BaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
/**
* Native module provides single method {@link #record} which records its single string argument
* in calls array
*/
public class StringRecordingModule extends BaseJavaModule {
private final List<String> mCalls = new ArrayList<String>();
@Override
public String getName() {
return "Recording";
}
@ReactMethod
public void record(String text) {
mCalls.add(text);
}
public void reset() {
mCalls.clear();
}
public List<String> getCalls() {
return mCalls;
}
}

View File

@@ -0,0 +1,14 @@
load("//ReactNative:DEFS.bzl", "rn_android_library", "react_native_dep", "react_native_target")
rn_android_library(
name = "idledetection",
srcs = glob(["**/*.java"]),
visibility = [
"PUBLIC",
],
deps = [
react_native_dep("third-party/java/testing-support-lib:runner"),
react_native_target("java/com/facebook/react/bridge:bridge"),
react_native_target("java/com/facebook/react/modules/core:core"),
],
)

View File

@@ -0,0 +1,16 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.testing.idledetection;
/**
* Interface for something that knows how to wait for bridge and UI idle.
*/
public interface IdleWaiter {
void waitForBridgeAndUIIdle();
}

View File

@@ -0,0 +1,61 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.testing.idledetection;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import com.facebook.react.bridge.NotThreadSafeBridgeIdleDebugListener;
/**
* Utility class that uses {@link NotThreadSafeBridgeIdleDebugListener} interface to allow callers
* to wait for the bridge to be idle.
*/
public class ReactBridgeIdleSignaler implements NotThreadSafeBridgeIdleDebugListener {
// Starts at 1 since bridge starts idle. The logic here is that the semaphore is only acquirable
// if the bridge is idle.
private final Semaphore mBridgeIdleSemaphore = new Semaphore(1);
private volatile boolean mIsBridgeIdle = true;
@Override
public void onTransitionToBridgeIdle() {
mIsBridgeIdle = true;
mBridgeIdleSemaphore.release();
}
@Override
public void onTransitionToBridgeBusy() {
mIsBridgeIdle = false;
try {
if (!mBridgeIdleSemaphore.tryAcquire(15000, TimeUnit.MILLISECONDS)) {
throw new RuntimeException(
"Timed out waiting to acquire the test idle listener semaphore. Deadlock?");
}
} catch (InterruptedException e) {
throw new RuntimeException("Got interrupted", e);
}
}
public boolean isBridgeIdle() {
return mIsBridgeIdle;
}
public boolean waitForIdle(long millis) {
try {
if (mBridgeIdleSemaphore.tryAcquire(millis, TimeUnit.MILLISECONDS)) {
mBridgeIdleSemaphore.release();
return true;
}
return false;
} catch (InterruptedException e) {
throw new RuntimeException("Got interrupted", e);
}
}
}

View File

@@ -0,0 +1,124 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.testing.idledetection;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import android.app.Instrumentation;
import android.os.SystemClock;
import android.support.test.InstrumentationRegistry;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.UiThreadUtil;
import com.facebook.react.modules.core.ChoreographerCompat;
public class ReactIdleDetectionUtil {
/**
* Waits for both the UI thread and bridge to be idle. It determines this by waiting for the
* bridge to become idle, then waiting for the UI thread to become idle, then checking if the
* bridge is idle again (if the bridge was idle before and is still idle after running the UI
* thread to idle, then there are no more events to process in either place).
* <p/>
* Also waits for any Choreographer callbacks to run after the initial sync since things like UI
* events are initiated from Choreographer callbacks.
*/
public static void waitForBridgeAndUIIdle(
ReactBridgeIdleSignaler idleSignaler,
final ReactContext reactContext,
long timeoutMs) {
UiThreadUtil.assertNotOnUiThread();
long startTime = SystemClock.uptimeMillis();
waitInner(idleSignaler, timeoutMs);
long timeToWait = Math.max(1, timeoutMs - (SystemClock.uptimeMillis() - startTime));
waitForChoreographer(timeToWait);
waitForJSIdle(reactContext);
timeToWait = Math.max(1, timeoutMs - (SystemClock.uptimeMillis() - startTime));
waitInner(idleSignaler, timeToWait);
timeToWait = Math.max(1, timeoutMs - (SystemClock.uptimeMillis() - startTime));
waitForChoreographer(timeToWait);
}
private static void waitForChoreographer(long timeToWait) {
final int waitFrameCount = 2;
final CountDownLatch latch = new CountDownLatch(1);
UiThreadUtil.runOnUiThread(
new Runnable() {
@Override
public void run() {
ChoreographerCompat.getInstance().postFrameCallback(
new ChoreographerCompat.FrameCallback() {
private int frameCount = 0;
@Override
public void doFrame(long frameTimeNanos) {
frameCount++;
if (frameCount == waitFrameCount) {
latch.countDown();
} else {
ChoreographerCompat.getInstance().postFrameCallback(this);
}
}
});
}
});
try {
if (!latch.await(timeToWait, TimeUnit.MILLISECONDS)) {
throw new RuntimeException("Timed out waiting for Choreographer");
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static void waitForJSIdle(ReactContext reactContext) {
if (!reactContext.hasActiveCatalystInstance()) {
return;
}
final CountDownLatch latch = new CountDownLatch(1);
reactContext.runOnJSQueueThread(
new Runnable() {
@Override
public void run() {
latch.countDown();
}
});
try {
if (!latch.await(5000, TimeUnit.MILLISECONDS)) {
throw new RuntimeException("Timed out waiting for JS thread");
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static void waitInner(ReactBridgeIdleSignaler idleSignaler, long timeToWait) {
// TODO gets broken in gradle, do we need it?
Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
long startTime = SystemClock.uptimeMillis();
boolean bridgeWasIdle = false;
while (SystemClock.uptimeMillis() - startTime < timeToWait) {
boolean bridgeIsIdle = idleSignaler.isBridgeIdle();
if (bridgeIsIdle && bridgeWasIdle) {
return;
}
bridgeWasIdle = bridgeIsIdle;
long newTimeToWait = Math.max(1, timeToWait - (SystemClock.uptimeMillis() - startTime));
idleSignaler.waitForIdle(newTimeToWait);
instrumentation.waitForIdleSync();
}
throw new RuntimeException("Timed out waiting for bridge and UI idle!");
}
}

View File

@@ -0,0 +1,16 @@
load("//ReactNative:DEFS.bzl", "rn_android_library", "react_native_dep", "react_native_target")
rn_android_library(
name = "network",
srcs = glob(["**/*.java"]),
visibility = [
"PUBLIC",
],
deps = [
react_native_dep("third-party/android/support/v4:lib-support-v4"),
react_native_dep("third-party/java/jsr-305:jsr-305"),
react_native_target("java/com/facebook/react/bridge:bridge"),
react_native_target("java/com/facebook/react/module/annotations:annotations"),
react_native_target("java/com/facebook/react/modules/core:core"),
],
)

View File

@@ -0,0 +1,151 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.testing.network;
import javax.annotation.Nullable;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.module.annotations.ReactModule;
import com.facebook.react.modules.core.DeviceEventManagerModule;
/**
* Mock Networking module that records last request received by {@link #sendRequest} method and
* returns response code and body that should be set with {@link #setResponse}
*/
@ReactModule(name = "Networking", canOverrideExistingModule = true)
public class NetworkRecordingModuleMock extends ReactContextBaseJavaModule {
public int mRequestCount = 0;
public @Nullable String mRequestMethod;
public @Nullable String mRequestURL;
public @Nullable ReadableArray mRequestHeaders;
public @Nullable ReadableMap mRequestData;
public int mLastRequestId;
public boolean mAbortedRequest;
private boolean mCompleteRequest;
public NetworkRecordingModuleMock(ReactApplicationContext reactContext) {
this(reactContext, true);
}
public NetworkRecordingModuleMock(ReactApplicationContext reactContext, boolean completeRequest) {
super(reactContext);
mCompleteRequest = completeRequest;
}
public static interface RequestListener {
public void onRequest(String method, String url, ReadableArray header, ReadableMap data);
}
private int mResponseCode;
private @Nullable String mResponseBody;
private @Nullable RequestListener mRequestListener;
public void setResponse(int code, String body) {
mResponseCode = code;
mResponseBody = body;
}
public void setRequestListener(RequestListener requestListener) {
mRequestListener = requestListener;
}
@Override
public final String getName() {
return "Networking";
}
private void fireReactCallback(
Callback callback,
int status,
@Nullable String headers,
@Nullable String body) {
callback.invoke(status, headers, body);
}
@ReactMethod
public final void sendRequest(
String method,
String url,
int requestId,
ReadableArray headers,
ReadableMap data,
final String responseType,
boolean incrementalUpdates,
int timeout,
boolean withCredentials) {
mLastRequestId = requestId;
mRequestCount++;
mRequestMethod = method;
mRequestURL = url;
mRequestHeaders = headers;
mRequestData = data;
if (mRequestListener != null) {
mRequestListener.onRequest(method, url, headers, data);
}
if (mCompleteRequest) {
onResponseReceived(requestId, mResponseCode, null);
onDataReceived(requestId, mResponseBody);
onRequestComplete(requestId, null);
}
}
@ReactMethod
public void abortRequest(int requestId) {
mLastRequestId = requestId;
mAbortedRequest = true;
}
@Override
public boolean canOverrideExistingModule() {
return true;
}
public void setCompleteRequest(boolean completeRequest) {
mCompleteRequest = completeRequest;
}
private void onDataReceived(int requestId, String data) {
WritableArray args = Arguments.createArray();
args.pushInt(requestId);
args.pushString(data);
getEventEmitter().emit("didReceiveNetworkData", args);
}
private void onRequestComplete(int requestId, @Nullable String error) {
WritableArray args = Arguments.createArray();
args.pushInt(requestId);
args.pushString(error);
getEventEmitter().emit("didCompleteNetworkResponse", args);
}
private void onResponseReceived(int requestId, int code, WritableMap headers) {
WritableArray args = Arguments.createArray();
args.pushInt(requestId);
args.pushInt(code);
args.pushMap(headers);
getEventEmitter().emit("didReceiveNetworkResponse", args);
}
private DeviceEventManagerModule.RCTDeviceEventEmitter getEventEmitter() {
return getReactApplicationContext()
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class);
}
}

View File

@@ -0,0 +1,39 @@
load("//ReactNative:DEFS.bzl", "rn_android_library", "react_native_dep", "react_native_integration_tests_target", "react_native_target")
rn_android_library(
name = "tests",
srcs = glob(["**/*.java"]),
visibility = [
"PUBLIC",
],
deps = [
react_native_dep("third-party/android/support/v4:lib-support-v4"),
react_native_dep("third-party/java/jsr-305:jsr-305"),
react_native_dep("third-party/java/junit:junit"),
react_native_integration_tests_target("java/com/facebook/react/testing:testing"),
react_native_integration_tests_target("java/com/facebook/react/testing/idledetection:idledetection"),
react_native_target("java/com/facebook/react:react"),
react_native_target("java/com/facebook/react/bridge:bridge"),
react_native_target("java/com/facebook/react/common:common"),
react_native_target("java/com/facebook/react/module/annotations:annotations"),
react_native_target("java/com/facebook/react/modules/appstate:appstate"),
react_native_target("java/com/facebook/react/modules/core:core"),
react_native_target("java/com/facebook/react/modules/datepicker:datepicker"),
react_native_target("java/com/facebook/react/modules/deviceinfo:deviceinfo"),
react_native_target("java/com/facebook/react/modules/share:share"),
react_native_target("java/com/facebook/react/modules/systeminfo:systeminfo"),
react_native_target("java/com/facebook/react/modules/timepicker:timepicker"),
react_native_target("java/com/facebook/react/touch:touch"),
react_native_target("java/com/facebook/react/uimanager:uimanager"),
react_native_target("java/com/facebook/react/uimanager/annotations:annotations"),
react_native_target("java/com/facebook/react/uimanager/util:util"),
react_native_target("java/com/facebook/react/views/picker:picker"),
react_native_target("java/com/facebook/react/views/progressbar:progressbar"),
react_native_target("java/com/facebook/react/views/scroll:scroll"),
react_native_target("java/com/facebook/react/views/slider:slider"),
react_native_target("java/com/facebook/react/views/swiperefresh:swiperefresh"),
react_native_target("java/com/facebook/react/views/text:text"),
react_native_target("java/com/facebook/react/views/textinput:textinput"),
react_native_target("java/com/facebook/react/views/view:view"),
],
)

View File

@@ -0,0 +1,129 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.tests;
import com.facebook.react.testing.ReactInstanceSpecForTest;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.uimanager.UIManagerModule;
import com.facebook.react.testing.AssertModule;
import com.facebook.react.testing.ReactAppInstrumentationTestCase;
/**
* Tests for {@link UIManagerModule#measure}, {@link UIManagerModule#measureLayout}, and
* {@link UIManagerModule#measureLayoutRelativeToParent}. Tests measurement for views in the
* following hierarchy:
*
* +---------------------------------------------+
* | A |
* | |
* | +-----------+ +---------+ |
* | | B | | D | |
* | | +---+ | | | |
* | | | C | | | | |
* | | | | | +---------+ |
* | | +---+ | |
* | +-----------+ |
* | |
* | |
* | |
* +---------------------------------------------+
*
* View locations and dimensions:
* A - (0,0) to (500, 500) (500x500)
* B - (50,80) to (250, 380) (200x300)
* C - (150,150) to (200, 300) (50x150)
* D - (400,100) to (450, 300) (50x200)
*/
public class CatalystMeasureLayoutTest extends ReactAppInstrumentationTestCase {
private static interface MeasureLayoutTestModule extends JavaScriptModule {
public void verifyMeasureOnViewA();
public void verifyMeasureOnViewC();
public void verifyMeasureLayoutCRelativeToA();
public void verifyMeasureLayoutCRelativeToB();
public void verifyMeasureLayoutCRelativeToSelf();
public void verifyMeasureLayoutRelativeToParentOnViewA();
public void verifyMeasureLayoutRelativeToParentOnViewB();
public void verifyMeasureLayoutRelativeToParentOnViewC();
public void verifyMeasureLayoutDRelativeToB();
public void verifyMeasureLayoutNonExistentTag();
public void verifyMeasureLayoutNonExistentAncestor();
public void verifyMeasureLayoutRelativeToParentNonExistentTag();
}
private MeasureLayoutTestModule mTestJSModule;
private AssertModule mAssertModule;
@Override
protected void setUp() throws Exception {
super.setUp();
mTestJSModule = getReactContext().getJSModule(MeasureLayoutTestModule.class);
}
@Override
protected String getReactApplicationKeyUnderTest() {
return "MeasureLayoutTestApp";
}
@Override
protected ReactInstanceSpecForTest createReactInstanceSpecForTest() {
mAssertModule = new AssertModule();
return super.createReactInstanceSpecForTest()
.addNativeModule(mAssertModule);
}
private void waitForBridgeIdleAndVerifyAsserts() {
waitForBridgeAndUIIdle();
mAssertModule.verifyAssertsAndReset();
}
public void testMeasure() {
mTestJSModule.verifyMeasureOnViewA();
waitForBridgeIdleAndVerifyAsserts();
mTestJSModule.verifyMeasureOnViewC();
waitForBridgeIdleAndVerifyAsserts();
}
public void testMeasureLayout() {
mTestJSModule.verifyMeasureLayoutCRelativeToA();
waitForBridgeIdleAndVerifyAsserts();
mTestJSModule.verifyMeasureLayoutCRelativeToB();
waitForBridgeIdleAndVerifyAsserts();
mTestJSModule.verifyMeasureLayoutCRelativeToSelf();
waitForBridgeIdleAndVerifyAsserts();
}
public void testMeasureLayoutRelativeToParent() {
mTestJSModule.verifyMeasureLayoutRelativeToParentOnViewA();
waitForBridgeIdleAndVerifyAsserts();
mTestJSModule.verifyMeasureLayoutRelativeToParentOnViewB();
waitForBridgeIdleAndVerifyAsserts();
mTestJSModule.verifyMeasureLayoutRelativeToParentOnViewC();
waitForBridgeIdleAndVerifyAsserts();
}
public void testMeasureLayoutCallsErrorCallbackWhenViewIsNotChildOfAncestor() {
mTestJSModule.verifyMeasureLayoutDRelativeToB();
waitForBridgeIdleAndVerifyAsserts();
}
public void testMeasureLayoutCallsErrorCallbackWhenViewDoesNotExist() {
mTestJSModule.verifyMeasureLayoutNonExistentTag();
waitForBridgeIdleAndVerifyAsserts();
}
public void testMeasureLayoutCallsErrorCallbackWhenAncestorDoesNotExist() {
mTestJSModule.verifyMeasureLayoutNonExistentAncestor();
waitForBridgeIdleAndVerifyAsserts();
}
public void testMeasureLayoutRelativeToParentCallsErrorCallbackWhenViewDoesNotExist() {
mTestJSModule.verifyMeasureLayoutRelativeToParentNonExistentTag();
waitForBridgeIdleAndVerifyAsserts();
}
}

View File

@@ -0,0 +1,655 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.tests;
import java.util.List;
import android.view.MotionEvent;
import com.facebook.react.testing.ReactInstanceSpecForTest;
import com.facebook.react.testing.ReactAppInstrumentationTestCase;
import com.facebook.react.testing.StringRecordingModule;
/**
* Test case for verifying that multitouch events are directed to the React's view touch handlers
* properly
*/
public class CatalystMultitouchHandlingTestCase extends ReactAppInstrumentationTestCase {
private final StringRecordingModule mRecordingModule = new StringRecordingModule();
@Override
protected String getReactApplicationKeyUnderTest() {
return "MultitouchHandlingTestAppModule";
}
@Override
protected ReactInstanceSpecForTest createReactInstanceSpecForTest() {
return new ReactInstanceSpecForTest()
.addNativeModule(mRecordingModule);
}
/**
* In this test case we send pre-recorded stream of pinch out gesture and verify that we have
* recorded important touch events in JS module
*/
public void testMultitouchEvents() throws InterruptedException {
generateRecordedPinchTouchEvents();
waitForBridgeAndUIIdle();
// Expect to receive at least 5 events (DOWN for each pointer, UP for each pointer and at least
// one MOVE event with both pointers down)
List<String> calls = mRecordingModule.getCalls();
int moveWithBothPointersEventIndex = -1;
int startEventIndex = -1;
int startExtraPointerEventIndex = -1;
int endEventIndex = -1;
int endExtraPointerEventIndex = -1;
for (int i = 0; i < calls.size(); i++) {
String call = calls.get(i);
if (call.equals("start;ExtraPointer")) {
assertEquals(-1, startExtraPointerEventIndex);
startExtraPointerEventIndex = i;
} else if (call.equals("end;ExtraPointer")) {
assertEquals(-1, endExtraPointerEventIndex);
endExtraPointerEventIndex = i;
} else if (call.equals("start;1")) {
assertEquals(-1, startEventIndex);
startEventIndex = i;
} else if (call.equals("end;0")) {
assertEquals(-1, endEventIndex);
endEventIndex = i;
} else if (call.equals("move;2")) {
// this will happen more than once, let's just capture the last occurrence
moveWithBothPointersEventIndex = i;
}
}
assertEquals(0, startEventIndex);
assertTrue(-1 != startExtraPointerEventIndex);
assertTrue(-1 != moveWithBothPointersEventIndex);
assertTrue(-1 != endExtraPointerEventIndex);
assertTrue(startExtraPointerEventIndex < moveWithBothPointersEventIndex);
assertTrue(endExtraPointerEventIndex > moveWithBothPointersEventIndex);
assertEquals(calls.size() - 1, endEventIndex);
}
private MotionEvent.PointerProperties createPointerProps(int id, int toolType) {
MotionEvent.PointerProperties pointerProps = new MotionEvent.PointerProperties();
pointerProps.id = id;
pointerProps.toolType = toolType;
return pointerProps;
}
private MotionEvent.PointerCoords createPointerCoords(float x, float y) {
MotionEvent.PointerCoords pointerCoords = new MotionEvent.PointerCoords();
pointerCoords.x = x;
pointerCoords.y = y;
return pointerCoords;
}
private void dispatchEvent(
final int action,
final long start,
final long when,
final int pointerCount,
final MotionEvent.PointerProperties[] pointerProps,
final MotionEvent.PointerCoords[] pointerCoords) {
getRootView().post(
new Runnable() {
@Override
public void run() {
MotionEvent event =
MotionEvent.obtain(start, when, action, pointerCount, pointerProps, pointerCoords, 0, 0, 1.0f, 1.0f, 0, 0, 0, 0);
getRootView().dispatchTouchEvent(event);
event.recycle();
}
});
getInstrumentation().waitForIdleSync();
}
/**
* This method "replay" multi-touch gesture recorded with modified TouchesHelper class that
* generated this piece of code (see https://phabricator.fb.com/P19756940).
* This is not intended to be copied/reused and once we need to have more multitouch gestures
* in instrumentation tests we should either:
* - implement nice generator similar to {@link SingleTouchGestureGenerator}
* - implement gesture recorded that will record touch data using arbitrary format and then read
* this recorded touch sequence during tests instead of generating code like this
*/
private void generateRecordedPinchTouchEvents() {
// START OF GENERATED CODE
{
MotionEvent.PointerProperties[] pointerProps = new MotionEvent.PointerProperties[1];
MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[1];
pointerProps[0] = createPointerProps(0, 1);
pointerCoords[0] = createPointerCoords(268.0f, 347.0f);
dispatchEvent(MotionEvent.ACTION_DOWN, 446560605, 446560605, 1, pointerProps, pointerCoords);
}
{
MotionEvent.PointerProperties[] pointerProps = new MotionEvent.PointerProperties[1];
MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[1];
pointerProps[0] = createPointerProps(0, 1);
pointerCoords[0] = createPointerCoords(267.0f, 346.0f);
dispatchEvent(MotionEvent.ACTION_MOVE, 446560605, 446560630, 1, pointerProps, pointerCoords);
}
{
MotionEvent.PointerProperties[] pointerProps = new MotionEvent.PointerProperties[2];
MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[2];
pointerProps[0] = createPointerProps(0, 1);
pointerCoords[0] = createPointerCoords(267.0f, 346.0f);
pointerProps[1] = createPointerProps(1, 1);
pointerCoords[1] = createPointerCoords(225.0f, 542.0f);
dispatchEvent(MotionEvent.ACTION_POINTER_DOWN | (1 << MotionEvent.ACTION_POINTER_INDEX_SHIFT), 446560605, 446560630, 2, pointerProps, pointerCoords);
}
{
MotionEvent.PointerProperties[] pointerProps = new MotionEvent.PointerProperties[2];
MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[2];
pointerProps[0] = createPointerProps(0, 1);
pointerCoords[0] = createPointerCoords(266.0f, 345.0f);
pointerProps[1] = createPointerProps(1, 1);
pointerCoords[1] = createPointerCoords(225.0f, 542.0f);
dispatchEvent(MotionEvent.ACTION_MOVE, 446560605, 446560647, 2, pointerProps, pointerCoords);
}
{
MotionEvent.PointerProperties[] pointerProps = new MotionEvent.PointerProperties[2];
MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[2];
pointerProps[0] = createPointerProps(0, 1);
pointerCoords[0] = createPointerCoords(265.0f, 344.0f);
pointerProps[1] = createPointerProps(1, 1);
pointerCoords[1] = createPointerCoords(224.0f, 541.0f);
dispatchEvent(MotionEvent.ACTION_MOVE, 446560605, 446560664, 2, pointerProps, pointerCoords);
}
{
MotionEvent.PointerProperties[] pointerProps = new MotionEvent.PointerProperties[2];
MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[2];
pointerProps[0] = createPointerProps(0, 1);
pointerCoords[0] = createPointerCoords(264.0f, 342.0f);
pointerProps[1] = createPointerProps(1, 1);
pointerCoords[1] = createPointerCoords(223.0f, 540.0f);
dispatchEvent(MotionEvent.ACTION_MOVE, 446560605, 446560681, 2, pointerProps, pointerCoords);
}
{
MotionEvent.PointerProperties[] pointerProps = new MotionEvent.PointerProperties[2];
MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[2];
pointerProps[0] = createPointerProps(0, 1);
pointerCoords[0] = createPointerCoords(263.0f, 340.0f);
pointerProps[1] = createPointerProps(1, 1);
pointerCoords[1] = createPointerCoords(222.0f, 539.0f);
dispatchEvent(MotionEvent.ACTION_MOVE, 446560605, 446560698, 2, pointerProps, pointerCoords);
}
{
MotionEvent.PointerProperties[] pointerProps = new MotionEvent.PointerProperties[2];
MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[2];
pointerProps[0] = createPointerProps(0, 1);
pointerCoords[0] = createPointerCoords(262.0f, 337.0f);
pointerProps[1] = createPointerProps(1, 1);
pointerCoords[1] = createPointerCoords(221.0f, 538.0f);
dispatchEvent(MotionEvent.ACTION_MOVE, 446560605, 446560714, 2, pointerProps, pointerCoords);
}
{
MotionEvent.PointerProperties[] pointerProps = new MotionEvent.PointerProperties[2];
MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[2];
pointerProps[0] = createPointerProps(0, 1);
pointerCoords[0] = createPointerCoords(262.0f, 333.0f);
pointerProps[1] = createPointerProps(1, 1);
pointerCoords[1] = createPointerCoords(220.0f, 537.0f);
dispatchEvent(MotionEvent.ACTION_MOVE, 446560605, 446560731, 2, pointerProps, pointerCoords);
}
{
MotionEvent.PointerProperties[] pointerProps = new MotionEvent.PointerProperties[2];
MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[2];
pointerProps[0] = createPointerProps(0, 1);
pointerCoords[0] = createPointerCoords(261.0f, 328.0f);
pointerProps[1] = createPointerProps(1, 1);
pointerCoords[1] = createPointerCoords(219.0f, 536.0f);
dispatchEvent(MotionEvent.ACTION_MOVE, 446560605, 446560748, 2, pointerProps, pointerCoords);
}
{
MotionEvent.PointerProperties[] pointerProps = new MotionEvent.PointerProperties[2];
MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[2];
pointerProps[0] = createPointerProps(0, 1);
pointerCoords[0] = createPointerCoords(260.0f, 321.0f);
pointerProps[1] = createPointerProps(1, 1);
pointerCoords[1] = createPointerCoords(218.0f, 536.0f);
dispatchEvent(MotionEvent.ACTION_MOVE, 446560605, 446560765, 2, pointerProps, pointerCoords);
}
{
MotionEvent.PointerProperties[] pointerProps = new MotionEvent.PointerProperties[2];
MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[2];
pointerProps[0] = createPointerProps(0, 1);
pointerCoords[0] = createPointerCoords(260.0f, 313.0f);
pointerProps[1] = createPointerProps(1, 1);
pointerCoords[1] = createPointerCoords(216.0f, 536.0f);
dispatchEvent(MotionEvent.ACTION_MOVE, 446560605, 446560781, 2, pointerProps, pointerCoords);
}
{
MotionEvent.PointerProperties[] pointerProps = new MotionEvent.PointerProperties[2];
MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[2];
pointerProps[0] = createPointerProps(0, 1);
pointerCoords[0] = createPointerCoords(260.0f, 304.0f);
pointerProps[1] = createPointerProps(1, 1);
pointerCoords[1] = createPointerCoords(214.0f, 537.0f);
dispatchEvent(MotionEvent.ACTION_MOVE, 446560605, 446560798, 2, pointerProps, pointerCoords);
}
{
MotionEvent.PointerProperties[] pointerProps = new MotionEvent.PointerProperties[2];
MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[2];
pointerProps[0] = createPointerProps(0, 1);
pointerCoords[0] = createPointerCoords(260.0f, 295.0f);
pointerProps[1] = createPointerProps(1, 1);
pointerCoords[1] = createPointerCoords(211.0f, 539.0f);
dispatchEvent(MotionEvent.ACTION_MOVE, 446560605, 446560815, 2, pointerProps, pointerCoords);
}
{
MotionEvent.PointerProperties[] pointerProps = new MotionEvent.PointerProperties[2];
MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[2];
pointerProps[0] = createPointerProps(0, 1);
pointerCoords[0] = createPointerCoords(261.0f, 285.0f);
pointerProps[1] = createPointerProps(1, 1);
pointerCoords[1] = createPointerCoords(208.0f, 542.0f);
dispatchEvent(MotionEvent.ACTION_MOVE, 446560605, 446560832, 2, pointerProps, pointerCoords);
}
{
MotionEvent.PointerProperties[] pointerProps = new MotionEvent.PointerProperties[2];
MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[2];
pointerProps[0] = createPointerProps(0, 1);
pointerCoords[0] = createPointerCoords(264.0f, 274.0f);
pointerProps[1] = createPointerProps(1, 1);
pointerCoords[1] = createPointerCoords(203.0f, 547.0f);
dispatchEvent(MotionEvent.ACTION_MOVE, 446560605, 446560849, 2, pointerProps, pointerCoords);
}
{
MotionEvent.PointerProperties[] pointerProps = new MotionEvent.PointerProperties[2];
MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[2];
pointerProps[0] = createPointerProps(0, 1);
pointerCoords[0] = createPointerCoords(266.0f, 264.0f);
pointerProps[1] = createPointerProps(1, 1);
pointerCoords[1] = createPointerCoords(199.0f, 551.0f);
dispatchEvent(MotionEvent.ACTION_MOVE, 446560605, 446560865, 2, pointerProps, pointerCoords);
}
{
MotionEvent.PointerProperties[] pointerProps = new MotionEvent.PointerProperties[2];
MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[2];
pointerProps[0] = createPointerProps(0, 1);
pointerCoords[0] = createPointerCoords(269.0f, 254.0f);
pointerProps[1] = createPointerProps(1, 1);
pointerCoords[1] = createPointerCoords(194.0f, 556.0f);
dispatchEvent(MotionEvent.ACTION_MOVE, 446560605, 446560882, 2, pointerProps, pointerCoords);
}
{
MotionEvent.PointerProperties[] pointerProps = new MotionEvent.PointerProperties[2];
MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[2];
pointerProps[0] = createPointerProps(0, 1);
pointerCoords[0] = createPointerCoords(273.0f, 245.0f);
pointerProps[1] = createPointerProps(1, 1);
pointerCoords[1] = createPointerCoords(190.0f, 561.0f);
dispatchEvent(MotionEvent.ACTION_MOVE, 446560605, 446560899, 2, pointerProps, pointerCoords);
}
{
MotionEvent.PointerProperties[] pointerProps = new MotionEvent.PointerProperties[2];
MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[2];
pointerProps[0] = createPointerProps(0, 1);
pointerCoords[0] = createPointerCoords(276.0f, 236.0f);
pointerProps[1] = createPointerProps(1, 1);
pointerCoords[1] = createPointerCoords(186.0f, 567.0f);
dispatchEvent(MotionEvent.ACTION_MOVE, 446560605, 446560916, 2, pointerProps, pointerCoords);
}
{
MotionEvent.PointerProperties[] pointerProps = new MotionEvent.PointerProperties[2];
MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[2];
pointerProps[0] = createPointerProps(0, 1);
pointerCoords[0] = createPointerCoords(280.0f, 227.0f);
pointerProps[1] = createPointerProps(1, 1);
pointerCoords[1] = createPointerCoords(183.0f, 573.0f);
dispatchEvent(MotionEvent.ACTION_MOVE, 446560605, 446560933, 2, pointerProps, pointerCoords);
}
{
MotionEvent.PointerProperties[] pointerProps = new MotionEvent.PointerProperties[2];
MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[2];
pointerProps[0] = createPointerProps(0, 1);
pointerCoords[0] = createPointerCoords(283.0f, 219.0f);
pointerProps[1] = createPointerProps(1, 1);
pointerCoords[1] = createPointerCoords(181.0f, 579.0f);
dispatchEvent(MotionEvent.ACTION_MOVE, 446560605, 446560949, 2, pointerProps, pointerCoords);
}
{
MotionEvent.PointerProperties[] pointerProps = new MotionEvent.PointerProperties[2];
MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[2];
pointerProps[0] = createPointerProps(0, 1);
pointerCoords[0] = createPointerCoords(287.0f, 211.0f);
pointerProps[1] = createPointerProps(1, 1);
pointerCoords[1] = createPointerCoords(179.0f, 584.0f);
dispatchEvent(MotionEvent.ACTION_MOVE, 446560605, 446560966, 2, pointerProps, pointerCoords);
}
{
MotionEvent.PointerProperties[] pointerProps = new MotionEvent.PointerProperties[2];
MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[2];
pointerProps[0] = createPointerProps(0, 1);
pointerCoords[0] = createPointerCoords(291.0f, 202.0f);
pointerProps[1] = createPointerProps(1, 1);
pointerCoords[1] = createPointerCoords(177.0f, 589.0f);
dispatchEvent(MotionEvent.ACTION_MOVE, 446560605, 446560983, 2, pointerProps, pointerCoords);
}
{
MotionEvent.PointerProperties[] pointerProps = new MotionEvent.PointerProperties[2];
MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[2];
pointerProps[0] = createPointerProps(0, 1);
pointerCoords[0] = createPointerCoords(296.0f, 193.0f);
pointerProps[1] = createPointerProps(1, 1);
pointerCoords[1] = createPointerCoords(175.0f, 593.0f);
dispatchEvent(MotionEvent.ACTION_MOVE, 446560605, 446561000, 2, pointerProps, pointerCoords);
}
{
MotionEvent.PointerProperties[] pointerProps = new MotionEvent.PointerProperties[2];
MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[2];
pointerProps[0] = createPointerProps(0, 1);
pointerCoords[0] = createPointerCoords(301.0f, 184.0f);
pointerProps[1] = createPointerProps(1, 1);
pointerCoords[1] = createPointerCoords(174.0f, 598.0f);
dispatchEvent(MotionEvent.ACTION_MOVE, 446560605, 446561016, 2, pointerProps, pointerCoords);
}
{
MotionEvent.PointerProperties[] pointerProps = new MotionEvent.PointerProperties[2];
MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[2];
pointerProps[0] = createPointerProps(0, 1);
pointerCoords[0] = createPointerCoords(307.0f, 176.0f);
pointerProps[1] = createPointerProps(1, 1);
pointerCoords[1] = createPointerCoords(173.0f, 603.0f);
dispatchEvent(MotionEvent.ACTION_MOVE, 446560605, 446561033, 2, pointerProps, pointerCoords);
}
{
MotionEvent.PointerProperties[] pointerProps = new MotionEvent.PointerProperties[2];
MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[2];
pointerProps[0] = createPointerProps(0, 1);
pointerCoords[0] = createPointerCoords(313.0f, 168.0f);
pointerProps[1] = createPointerProps(1, 1);
pointerCoords[1] = createPointerCoords(172.0f, 608.0f);
dispatchEvent(MotionEvent.ACTION_MOVE, 446560605, 446561050, 2, pointerProps, pointerCoords);
}
{
MotionEvent.PointerProperties[] pointerProps = new MotionEvent.PointerProperties[2];
MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[2];
pointerProps[0] = createPointerProps(0, 1);
pointerCoords[0] = createPointerCoords(317.0f, 160.0f);
pointerProps[1] = createPointerProps(1, 1);
pointerCoords[1] = createPointerCoords(171.0f, 613.0f);
dispatchEvent(MotionEvent.ACTION_MOVE, 446560605, 446561067, 2, pointerProps, pointerCoords);
}
{
MotionEvent.PointerProperties[] pointerProps = new MotionEvent.PointerProperties[2];
MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[2];
pointerProps[0] = createPointerProps(0, 1);
pointerCoords[0] = createPointerCoords(320.0f, 154.0f);
pointerProps[1] = createPointerProps(1, 1);
pointerCoords[1] = createPointerCoords(170.0f, 619.0f);
dispatchEvent(MotionEvent.ACTION_MOVE, 446560605, 446561084, 2, pointerProps, pointerCoords);
}
{
MotionEvent.PointerProperties[] pointerProps = new MotionEvent.PointerProperties[2];
MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[2];
pointerProps[0] = createPointerProps(0, 1);
pointerCoords[0] = createPointerCoords(323.0f, 149.0f);
pointerProps[1] = createPointerProps(1, 1);
pointerCoords[1] = createPointerCoords(169.0f, 624.0f);
dispatchEvent(MotionEvent.ACTION_MOVE, 446560605, 446561100, 2, pointerProps, pointerCoords);
}
{
MotionEvent.PointerProperties[] pointerProps = new MotionEvent.PointerProperties[2];
MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[2];
pointerProps[0] = createPointerProps(0, 1);
pointerCoords[0] = createPointerCoords(325.0f, 145.0f);
pointerProps[1] = createPointerProps(1, 1);
pointerCoords[1] = createPointerCoords(168.0f, 628.0f);
dispatchEvent(MotionEvent.ACTION_MOVE, 446560605, 446561117, 2, pointerProps, pointerCoords);
}
{
MotionEvent.PointerProperties[] pointerProps = new MotionEvent.PointerProperties[2];
MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[2];
pointerProps[0] = createPointerProps(0, 1);
pointerCoords[0] = createPointerCoords(328.0f, 141.0f);
pointerProps[1] = createPointerProps(1, 1);
pointerCoords[1] = createPointerCoords(167.0f, 632.0f);
dispatchEvent(MotionEvent.ACTION_MOVE, 446560605, 446561134, 2, pointerProps, pointerCoords);
}
{
MotionEvent.PointerProperties[] pointerProps = new MotionEvent.PointerProperties[2];
MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[2];
pointerProps[0] = createPointerProps(0, 1);
pointerCoords[0] = createPointerCoords(331.0f, 137.0f);
pointerProps[1] = createPointerProps(1, 1);
pointerCoords[1] = createPointerCoords(166.0f, 636.0f);
dispatchEvent(MotionEvent.ACTION_MOVE, 446560605, 446561151, 2, pointerProps, pointerCoords);
}
{
MotionEvent.PointerProperties[] pointerProps = new MotionEvent.PointerProperties[2];
MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[2];
pointerProps[0] = createPointerProps(0, 1);
pointerCoords[0] = createPointerCoords(334.0f, 134.0f);
pointerProps[1] = createPointerProps(1, 1);
pointerCoords[1] = createPointerCoords(165.0f, 639.0f);
dispatchEvent(MotionEvent.ACTION_MOVE, 446560605, 446561167, 2, pointerProps, pointerCoords);
}
{
MotionEvent.PointerProperties[] pointerProps = new MotionEvent.PointerProperties[2];
MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[2];
pointerProps[0] = createPointerProps(0, 1);
pointerCoords[0] = createPointerCoords(337.0f, 131.0f);
pointerProps[1] = createPointerProps(1, 1);
pointerCoords[1] = createPointerCoords(164.0f, 643.0f);
dispatchEvent(MotionEvent.ACTION_MOVE, 446560605, 446561184, 2, pointerProps, pointerCoords);
}
{
MotionEvent.PointerProperties[] pointerProps = new MotionEvent.PointerProperties[2];
MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[2];
pointerProps[0] = createPointerProps(0, 1);
pointerCoords[0] = createPointerCoords(338.0f, 128.0f);
pointerProps[1] = createPointerProps(1, 1);
pointerCoords[1] = createPointerCoords(164.0f, 646.0f);
dispatchEvent(MotionEvent.ACTION_MOVE, 446560605, 446561201, 2, pointerProps, pointerCoords);
}
{
MotionEvent.PointerProperties[] pointerProps = new MotionEvent.PointerProperties[2];
MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[2];
pointerProps[0] = createPointerProps(0, 1);
pointerCoords[0] = createPointerCoords(340.0f, 126.0f);
pointerProps[1] = createPointerProps(1, 1);
pointerCoords[1] = createPointerCoords(164.0f, 649.0f);
dispatchEvent(MotionEvent.ACTION_MOVE, 446560605, 446561218, 2, pointerProps, pointerCoords);
}
{
MotionEvent.PointerProperties[] pointerProps = new MotionEvent.PointerProperties[2];
MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[2];
pointerProps[0] = createPointerProps(0, 1);
pointerCoords[0] = createPointerCoords(341.0f, 124.0f);
pointerProps[1] = createPointerProps(1, 1);
pointerCoords[1] = createPointerCoords(163.0f, 652.0f);
dispatchEvent(MotionEvent.ACTION_MOVE, 446560605, 446561234, 2, pointerProps, pointerCoords);
}
{
MotionEvent.PointerProperties[] pointerProps = new MotionEvent.PointerProperties[2];
MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[2];
pointerProps[0] = createPointerProps(0, 1);
pointerCoords[0] = createPointerCoords(342.0f, 122.0f);
pointerProps[1] = createPointerProps(1, 1);
pointerCoords[1] = createPointerCoords(163.0f, 655.0f);
dispatchEvent(MotionEvent.ACTION_MOVE, 446560605, 446561251, 2, pointerProps, pointerCoords);
}
{
MotionEvent.PointerProperties[] pointerProps = new MotionEvent.PointerProperties[2];
MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[2];
pointerProps[0] = createPointerProps(0, 1);
pointerCoords[0] = createPointerCoords(343.0f, 120.0f);
pointerProps[1] = createPointerProps(1, 1);
pointerCoords[1] = createPointerCoords(162.0f, 659.0f);
dispatchEvent(MotionEvent.ACTION_MOVE, 446560605, 446561268, 2, pointerProps, pointerCoords);
}
{
MotionEvent.PointerProperties[] pointerProps = new MotionEvent.PointerProperties[2];
MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[2];
pointerProps[0] = createPointerProps(0, 1);
pointerCoords[0] = createPointerCoords(344.0f, 118.0f);
pointerProps[1] = createPointerProps(1, 1);
pointerCoords[1] = createPointerCoords(161.0f, 664.0f);
dispatchEvent(MotionEvent.ACTION_MOVE, 446560605, 446561285, 2, pointerProps, pointerCoords);
}
{
MotionEvent.PointerProperties[] pointerProps = new MotionEvent.PointerProperties[2];
MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[2];
pointerProps[0] = createPointerProps(0, 1);
pointerCoords[0] = createPointerCoords(345.0f, 116.0f);
pointerProps[1] = createPointerProps(1, 1);
pointerCoords[1] = createPointerCoords(160.0f, 667.0f);
dispatchEvent(MotionEvent.ACTION_MOVE, 446560605, 446561302, 2, pointerProps, pointerCoords);
}
{
MotionEvent.PointerProperties[] pointerProps = new MotionEvent.PointerProperties[2];
MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[2];
pointerProps[0] = createPointerProps(0, 1);
pointerCoords[0] = createPointerCoords(346.0f, 115.0f);
pointerProps[1] = createPointerProps(1, 1);
pointerCoords[1] = createPointerCoords(158.0f, 670.0f);
dispatchEvent(MotionEvent.ACTION_MOVE, 446560605, 446561318, 2, pointerProps, pointerCoords);
}
{
MotionEvent.PointerProperties[] pointerProps = new MotionEvent.PointerProperties[2];
MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[2];
pointerProps[0] = createPointerProps(0, 1);
pointerCoords[0] = createPointerCoords(347.0f, 114.0f);
pointerProps[1] = createPointerProps(1, 1);
pointerCoords[1] = createPointerCoords(157.0f, 673.0f);
dispatchEvent(MotionEvent.ACTION_MOVE, 446560605, 446561335, 2, pointerProps, pointerCoords);
}
{
MotionEvent.PointerProperties[] pointerProps = new MotionEvent.PointerProperties[2];
MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[2];
pointerProps[0] = createPointerProps(0, 1);
pointerCoords[0] = createPointerCoords(348.0f, 113.0f);
pointerProps[1] = createPointerProps(1, 1);
pointerCoords[1] = createPointerCoords(156.0f, 676.0f);
dispatchEvent(MotionEvent.ACTION_MOVE, 446560605, 446561352, 2, pointerProps, pointerCoords);
}
{
MotionEvent.PointerProperties[] pointerProps = new MotionEvent.PointerProperties[2];
MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[2];
pointerProps[0] = createPointerProps(0, 1);
pointerCoords[0] = createPointerCoords(348.0f, 112.0f);
pointerProps[1] = createPointerProps(1, 1);
pointerCoords[1] = createPointerCoords(155.0f, 677.0f);
dispatchEvent(MotionEvent.ACTION_MOVE, 446560605, 446561369, 2, pointerProps, pointerCoords);
}
{
MotionEvent.PointerProperties[] pointerProps = new MotionEvent.PointerProperties[2];
MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[2];
pointerProps[0] = createPointerProps(0, 1);
pointerCoords[0] = createPointerCoords(349.0f, 111.0f);
pointerProps[1] = createPointerProps(1, 1);
pointerCoords[1] = createPointerCoords(154.0f, 678.0f);
dispatchEvent(MotionEvent.ACTION_MOVE, 446560605, 446561386, 2, pointerProps, pointerCoords);
}
{
MotionEvent.PointerProperties[] pointerProps = new MotionEvent.PointerProperties[2];
MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[2];
pointerProps[0] = createPointerProps(0, 1);
pointerCoords[0] = createPointerCoords(349.0f, 110.0f);
pointerProps[1] = createPointerProps(1, 1);
pointerCoords[1] = createPointerCoords(153.0f, 679.0f);
dispatchEvent(MotionEvent.ACTION_MOVE, 446560605, 446561402, 2, pointerProps, pointerCoords);
}
{
MotionEvent.PointerProperties[] pointerProps = new MotionEvent.PointerProperties[2];
MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[2];
pointerProps[0] = createPointerProps(0, 1);
pointerCoords[0] = createPointerCoords(349.0f, 109.0f);
pointerProps[1] = createPointerProps(1, 1);
pointerCoords[1] = createPointerCoords(152.0f, 680.0f);
dispatchEvent(MotionEvent.ACTION_MOVE, 446560605, 446561419, 2, pointerProps, pointerCoords);
}
{
MotionEvent.PointerProperties[] pointerProps = new MotionEvent.PointerProperties[2];
MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[2];
pointerProps[0] = createPointerProps(0, 1);
pointerCoords[0] = createPointerCoords(349.0f, 110.0f);
pointerProps[1] = createPointerProps(1, 1);
pointerCoords[1] = createPointerCoords(151.0f, 680.0f);
dispatchEvent(MotionEvent.ACTION_MOVE, 446560605, 446561435, 2, pointerProps, pointerCoords);
}
{
MotionEvent.PointerProperties[] pointerProps = new MotionEvent.PointerProperties[2];
MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[2];
pointerProps[0] = createPointerProps(0, 1);
pointerCoords[0] = createPointerCoords(349.0f, 110.0f);
pointerProps[1] = createPointerProps(1, 1);
pointerCoords[1] = createPointerCoords(151.0f, 680.0f);
dispatchEvent(MotionEvent.ACTION_POINTER_UP | (0 << MotionEvent.ACTION_POINTER_INDEX_SHIFT), 446560605, 446561443, 2, pointerProps, pointerCoords);
}
{
MotionEvent.PointerProperties[] pointerProps = new MotionEvent.PointerProperties[1];
MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[1];
pointerProps[0] = createPointerProps(0, 1);
pointerCoords[0] = createPointerCoords(151.0f, 680.0f);
dispatchEvent(MotionEvent.ACTION_UP, 446560605, 446561451, 1, pointerProps, pointerCoords);
}
// END OF GENERATED CODE
}
}

View File

@@ -0,0 +1,760 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.tests;
import com.facebook.react.bridge.BaseJavaModule;
import com.facebook.react.bridge.CatalystInstance;
import com.facebook.react.bridge.Dynamic;
import com.facebook.react.bridge.InvalidIteratorException;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.bridge.NoSuchKeyException;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.ReadableMapKeySetIterator;
import com.facebook.react.bridge.ReadableNativeMap;
import com.facebook.react.bridge.ReadableType;
import com.facebook.react.bridge.UiThreadUtil;
import com.facebook.react.bridge.UnexpectedNativeTypeException;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.WritableNativeMap;
import com.facebook.react.modules.appstate.AppStateModule;
import com.facebook.react.modules.deviceinfo.DeviceInfoModule;
import com.facebook.react.modules.systeminfo.AndroidInfoModule;
import com.facebook.react.testing.FakeWebSocketModule;
import com.facebook.react.testing.ReactIntegrationTestCase;
import com.facebook.react.testing.ReactTestHelper;
import com.facebook.react.uimanager.UIImplementationProvider;
import com.facebook.react.uimanager.UIManagerModule;
import com.facebook.react.uimanager.ViewManager;
import com.facebook.react.views.view.ReactViewManager;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.junit.Ignore;
/**
* Integration test to verify passing various types of parameters from JS to Java works
*
* TODO: we should run these tests with isBlockingSynchronousMethod = true as well,
* since they currently use a completely different codepath
*/
@Ignore("Fix prop types and view managers.")
public class CatalystNativeJSToJavaParametersTestCase extends ReactIntegrationTestCase {
private interface TestJSToJavaParametersModule extends JavaScriptModule {
void returnBasicTypes();
void returnBoxedTypes();
void returnDynamicTypes();
void returnArrayWithBasicTypes();
void returnNestedArray();
void returnArrayWithMaps();
void returnMapWithBasicTypes();
void returnNestedMap();
void returnMapWithArrays();
void returnArrayWithStringDoubleIntMapArrayBooleanNull();
void returnMapWithStringDoubleIntMapArrayBooleanNull();
void returnMapForMerge1();
void returnMapForMerge2();
void returnMapWithMultibyteUTF8CharacterString();
void returnArrayWithMultibyteUTF8CharacterString();
void returnArrayWithLargeInts();
void returnMapWithLargeInts();
}
private RecordingTestModule mRecordingTestModule;
private CatalystInstance mCatalystInstance;
@Override
protected void setUp() throws Exception {
super.setUp();
List<ViewManager> viewManagers = Arrays.<ViewManager>asList(
new ReactViewManager());
final UIManagerModule mUIManager =
new UIManagerModule(getContext(), viewManagers, new UIImplementationProvider(), 0);
UiThreadUtil.runOnUiThread(
new Runnable() {
@Override
public void run() {
mUIManager.onHostResume();
}
});
waitForIdleSync();
mRecordingTestModule = new RecordingTestModule();
mCatalystInstance = ReactTestHelper.catalystInstanceBuilder(this)
.addNativeModule(mRecordingTestModule)
.addNativeModule(new AndroidInfoModule(getContext()))
.addNativeModule(new DeviceInfoModule(getContext()))
.addNativeModule(new AppStateModule(getContext()))
.addNativeModule(new FakeWebSocketModule())
.addNativeModule(mUIManager)
.build();
}
public void testBasicTypes() {
mCatalystInstance.getJSModule(TestJSToJavaParametersModule.class).returnBasicTypes();
waitForBridgeAndUIIdle();
List<Object[]> basicTypesCalls = mRecordingTestModule.getBasicTypesCalls();
assertEquals(1, basicTypesCalls.size());
Object[] args = basicTypesCalls.get(0);
assertEquals("foo", args[0]);
assertEquals(3.14, args[1]);
assertEquals(true, args[2]);
assertNull(args[3]);
}
public void testBoxedTypes() {
mCatalystInstance.getJSModule(TestJSToJavaParametersModule.class).returnBoxedTypes();
waitForBridgeAndUIIdle();
List<Object[]> boxedTypesCalls = mRecordingTestModule.getBoxedTypesCalls();
assertEquals(1, boxedTypesCalls.size());
Object[] args = boxedTypesCalls.get(0);
assertEquals(Integer.valueOf(42), args[0]);
assertEquals(Double.valueOf(3.14), args[1]);
assertEquals(Boolean.valueOf(true), args[2]);
}
public void testDynamicType() {
mCatalystInstance.getJSModule(TestJSToJavaParametersModule.class).returnDynamicTypes();
waitForBridgeAndUIIdle();
List<Dynamic> dynamicCalls = mRecordingTestModule.getDynamicCalls();
assertEquals(2, dynamicCalls.size());
assertEquals("foo", dynamicCalls.get(0).asString());
assertEquals(3.14, dynamicCalls.get(1).asDouble());
}
public void testArrayWithBasicTypes() {
mCatalystInstance.getJSModule(TestJSToJavaParametersModule.class).returnArrayWithBasicTypes();
waitForBridgeAndUIIdle();
List<ReadableArray> calls = mRecordingTestModule.getArrayCalls();
assertEquals(1, calls.size());
ReadableArray array = calls.get(0);
assertNotNull(array);
assertEquals(5, array.size());
assertFalse(array.isNull(0));
assertEquals("foo", array.getString(0));
assertFalse(array.isNull(1));
assertEquals(3.14, array.getDouble(1));
assertFalse(array.isNull(2));
assertEquals(-111, array.getInt(2));
assertFalse(array.isNull(3));
assertTrue(array.getBoolean(3));
assertTrue(array.isNull(4));
assertEquals(null, array.getString(4));
assertEquals(null, array.getMap(4));
assertEquals(null, array.getArray(4));
}
public void testNestedArray() {
mCatalystInstance.getJSModule(TestJSToJavaParametersModule.class).returnNestedArray();
waitForBridgeAndUIIdle();
List<ReadableArray> calls = mRecordingTestModule.getArrayCalls();
assertEquals(1, calls.size());
ReadableArray array = calls.get(0);
assertNotNull(array);
assertEquals(2, array.size());
assertEquals("we", array.getString(0));
assertFalse(array.isNull(1));
ReadableArray subArray = array.getArray(1);
assertEquals(2, subArray.size());
assertEquals("have", subArray.getString(0));
subArray = subArray.getArray(1);
assertEquals(2, subArray.size());
assertEquals("to", subArray.getString(0));
subArray = subArray.getArray(1);
assertEquals(2, subArray.size());
assertEquals("go", subArray.getString(0));
subArray = subArray.getArray(1);
assertEquals(1, subArray.size());
assertEquals("deeper", subArray.getString(0));
}
public void testArrayWithMaps() {
mCatalystInstance.getJSModule(TestJSToJavaParametersModule.class).returnArrayWithMaps();
waitForBridgeAndUIIdle();
List<ReadableArray> calls = mRecordingTestModule.getArrayCalls();
assertEquals(1, calls.size());
ReadableArray array = calls.get(0);
assertEquals(2, array.size());
assertFalse(array.isNull(0));
ReadableMap m1 = array.getMap(0);
ReadableMap m2 = array.getMap(1);
assertEquals("m1v1", m1.getString("m1k1"));
assertEquals("m1v2", m1.getString("m1k2"));
assertEquals("m2v1", m2.getString("m2k1"));
}
public void testMapWithBasicTypes() {
mCatalystInstance.getJSModule(TestJSToJavaParametersModule.class).returnMapWithBasicTypes();
waitForBridgeAndUIIdle();
List<ReadableMap> calls = mRecordingTestModule.getMapCalls();
assertEquals(1, calls.size());
ReadableMap map = calls.get(0);
assertNotNull(map);
assertTrue(map.hasKey("stringKey"));
assertFalse(map.isNull("stringKey"));
assertEquals("stringValue", map.getString("stringKey"));
assertTrue(map.hasKey("doubleKey"));
assertFalse(map.isNull("doubleKey"));
assertTrue(Math.abs(3.14 - map.getDouble("doubleKey")) < .0001);
assertTrue(map.hasKey("intKey"));
assertFalse(map.isNull("intKey"));
assertEquals(-11, map.getInt("intKey"));
assertTrue(map.hasKey("booleanKey"));
assertFalse(map.isNull("booleanKey"));
assertTrue(map.getBoolean("booleanKey"));
assertTrue(map.hasKey("nullKey"));
assertTrue(map.isNull("nullKey"));
assertNull(map.getString("nullKey"));
assertNull(map.getMap("nullKey"));
assertNull(map.getArray("nullKey"));
assertFalse(map.hasKey("nonExistentKey"));
}
public void testNestedMap() {
mCatalystInstance.getJSModule(TestJSToJavaParametersModule.class).returnNestedMap();
waitForBridgeAndUIIdle();
List<ReadableMap> calls = mRecordingTestModule.getMapCalls();
assertEquals(1, calls.size());
ReadableMap map = calls.get(0);
assertNotNull(map);
assertTrue(map.hasKey("weHaveToGoDeeper"));
assertFalse(map.isNull("weHaveToGoDeeper"));
ReadableMap nestedMap = map.getMap("weHaveToGoDeeper");
assertTrue(nestedMap.hasKey("inception"));
assertTrue(nestedMap.getBoolean("inception"));
}
public void testMapParameterWithArrays() throws InterruptedException {
mCatalystInstance.getJSModule(TestJSToJavaParametersModule.class).returnMapWithArrays();
waitForBridgeAndUIIdle();
List<ReadableMap> calls = mRecordingTestModule.getMapCalls();
assertEquals(1, calls.size());
ReadableMap map = calls.get(0);
assertNotNull(map);
ReadableArray arrayParameter;
assertTrue(map.hasKey("empty"));
arrayParameter = map.getArray("empty");
assertNotNull(arrayParameter);
assertEquals(0, arrayParameter.size());
assertTrue(map.hasKey("ints"));
assertFalse(map.isNull("ints"));
arrayParameter = map.getArray("ints");
assertNotNull(arrayParameter);
assertEquals(2, arrayParameter.size());
assertEquals(43, arrayParameter.getInt(0));
assertEquals(44, arrayParameter.getInt(1));
assertTrue(map.hasKey("mixed"));
arrayParameter = map.getArray("mixed");
assertNotNull(arrayParameter);
assertEquals(3, arrayParameter.size());
assertEquals(77, arrayParameter.getInt(0));
assertEquals("string", arrayParameter.getString(1));
ReadableArray nestedArray = arrayParameter.getArray(2);
assertEquals(2, nestedArray.size());
}
public void testMapParameterDump() {
mCatalystInstance.getJSModule(TestJSToJavaParametersModule.class).returnMapWithBasicTypes();
mCatalystInstance.getJSModule(TestJSToJavaParametersModule.class).returnNestedMap();
mCatalystInstance.getJSModule(TestJSToJavaParametersModule.class).returnMapWithArrays();
waitForBridgeAndUIIdle();
List<ReadableMap> calls = mRecordingTestModule.getMapCalls();
assertEquals(3, calls.size());
// App should not crash while generating debug string representation of arguments
assertNotNull(calls.get(0).toString());
assertNotNull(calls.get(1).toString());
assertNotNull(calls.get(2).toString());
}
public void testGetTypeFromArray() {
mCatalystInstance.getJSModule(TestJSToJavaParametersModule.class)
.returnArrayWithStringDoubleIntMapArrayBooleanNull();
waitForBridgeAndUIIdle();
List<ReadableArray> calls = mRecordingTestModule.getArrayCalls();
assertEquals(1, calls.size());
ReadableArray array = calls.get(0);
assertEquals(ReadableType.String, array.getType(0));
assertEquals(ReadableType.Number, array.getType(1));
assertEquals(ReadableType.Number, array.getType(2));
assertEquals(ReadableType.Map, array.getType(3));
assertEquals(ReadableType.Array, array.getType(4));
assertEquals(ReadableType.Boolean, array.getType(5));
assertEquals(ReadableType.Null, array.getType(6));
}
public void testGetTypeFromMap() {
mCatalystInstance.getJSModule(TestJSToJavaParametersModule.class)
.returnMapWithStringDoubleIntMapArrayBooleanNull();
waitForBridgeAndUIIdle();
List<ReadableMap> calls = mRecordingTestModule.getMapCalls();
assertEquals(1, calls.size());
ReadableMap map = calls.get(0);
assertEquals(ReadableType.String, map.getType("string"));
assertEquals(ReadableType.Number, map.getType("double"));
assertEquals(ReadableType.Number, map.getType("int"));
assertEquals(ReadableType.Map, map.getType("map"));
assertEquals(ReadableType.Array, map.getType("array"));
assertEquals(ReadableType.Boolean, map.getType("boolean"));
assertEquals(ReadableType.Null, map.getType("null"));
}
public void testGetWrongTypeFromArray() {
mCatalystInstance.getJSModule(TestJSToJavaParametersModule.class)
.returnArrayWithStringDoubleIntMapArrayBooleanNull();
waitForBridgeAndUIIdle();
List<ReadableArray> calls = mRecordingTestModule.getArrayCalls();
assertEquals(1, calls.size());
ReadableArray array = calls.get(0);
assertUnexpectedTypeExceptionThrown(array, 0, "boolean");
assertUnexpectedTypeExceptionThrown(array, 1, "string");
assertUnexpectedTypeExceptionThrown(array, 2, "array");
assertUnexpectedTypeExceptionThrown(array, 3, "double");
assertUnexpectedTypeExceptionThrown(array, 4, "map");
assertUnexpectedTypeExceptionThrown(array, 5, "array");
}
public void testGetWrongTypeFromMap() {
mCatalystInstance.getJSModule(TestJSToJavaParametersModule.class)
.returnMapWithStringDoubleIntMapArrayBooleanNull();
waitForBridgeAndUIIdle();
List<ReadableMap> calls = mRecordingTestModule.getMapCalls();
assertEquals(1, calls.size());
ReadableMap map = calls.get(0);
assertUnexpectedTypeExceptionThrown(map, "string", "double");
assertUnexpectedTypeExceptionThrown(map, "double", "map");
assertUnexpectedTypeExceptionThrown(map, "int", "boolean");
assertUnexpectedTypeExceptionThrown(map, "map", "array");
assertUnexpectedTypeExceptionThrown(map, "array", "boolean");
assertUnexpectedTypeExceptionThrown(map, "boolean", "string");
}
public void testArrayOutOfBoundsExceptionThrown() {
mCatalystInstance.getJSModule(TestJSToJavaParametersModule.class).returnArrayWithBasicTypes();
waitForBridgeAndUIIdle();
List<ReadableArray> calls = mRecordingTestModule.getArrayCalls();
assertEquals(1, calls.size());
ReadableArray array = calls.get(0);
assertNotNull(array);
assertArrayOutOfBoundsExceptionThrown(array, -1, "boolean");
assertArrayOutOfBoundsExceptionThrown(array, -1, "string");
assertArrayOutOfBoundsExceptionThrown(array, -1, "double");
assertArrayOutOfBoundsExceptionThrown(array, -1, "int");
assertArrayOutOfBoundsExceptionThrown(array, -1, "map");
assertArrayOutOfBoundsExceptionThrown(array, -1, "array");
assertArrayOutOfBoundsExceptionThrown(array, 10, "boolean");
assertArrayOutOfBoundsExceptionThrown(array, 10, "string");
assertArrayOutOfBoundsExceptionThrown(array, 10, "double");
assertArrayOutOfBoundsExceptionThrown(array, 10, "int");
assertArrayOutOfBoundsExceptionThrown(array, 10, "map");
assertArrayOutOfBoundsExceptionThrown(array, 10, "array");
}
public void testNoSuchKeyExceptionThrown() {
mCatalystInstance.getJSModule(TestJSToJavaParametersModule.class).returnMapWithBasicTypes();
waitForBridgeAndUIIdle();
List<ReadableMap> calls = mRecordingTestModule.getMapCalls();
assertEquals(1, calls.size());
ReadableMap map = calls.get(0);
assertNotNull(map);
assertNoSuchKeyExceptionThrown(map, "noSuchKey", "double");
assertNoSuchKeyExceptionThrown(map, "noSuchKey", "int");
assertNoSuchKeyExceptionThrown(map, "noSuchKey", "map");
assertNoSuchKeyExceptionThrown(map, "noSuchKey", "array");
assertNoSuchKeyExceptionThrown(map, "noSuchKey", "boolean");
assertNoSuchKeyExceptionThrown(map, "noSuchKey", "string");
}
public void testIntOutOfRangeThrown() {
mCatalystInstance.getJSModule(TestJSToJavaParametersModule.class).returnArrayWithLargeInts();
mCatalystInstance.getJSModule(TestJSToJavaParametersModule.class).returnMapWithLargeInts();
waitForBridgeAndUIIdle();
assertEquals(1, mRecordingTestModule.getArrayCalls().size());
assertEquals(1, mRecordingTestModule.getMapCalls().size());
ReadableArray array = mRecordingTestModule.getArrayCalls().get(0);
assertNotNull(array);
ReadableMap map = mRecordingTestModule.getMapCalls().get(0);
assertNotNull(map);
assertEquals(ReadableType.Number, array.getType(0));
assertUnexpectedTypeExceptionThrown(array, 0, "int");
assertEquals(ReadableType.Number, array.getType(1));
assertUnexpectedTypeExceptionThrown(array, 1, "int");
assertEquals(ReadableType.Number, map.getType("first"));
assertUnexpectedTypeExceptionThrown(map, "first", "int");
assertEquals(ReadableType.Number, map.getType("second"));
assertUnexpectedTypeExceptionThrown(map, "second", "int");
}
public void testMapMerging() {
mCatalystInstance.getJSModule(TestJSToJavaParametersModule.class).returnMapForMerge1();
mCatalystInstance.getJSModule(TestJSToJavaParametersModule.class).returnMapForMerge2();
waitForBridgeAndUIIdle();
List<ReadableMap> maps = mRecordingTestModule.getMapCalls();
assertEquals(2, maps.size());
WritableMap dest = new WritableNativeMap();
dest.merge(maps.get(0));
dest.merge(maps.get(1));
assertTrue(dest.hasKey("a"));
assertTrue(dest.hasKey("b"));
assertTrue(dest.hasKey("c"));
assertTrue(dest.hasKey("d"));
assertTrue(dest.hasKey("e"));
assertTrue(dest.hasKey("f"));
assertTrue(dest.hasKey("newkey"));
assertEquals("overwrite", dest.getString("a"));
assertEquals(41, dest.getInt("b"));
assertEquals("string", dest.getString("c"));
assertEquals(77, dest.getInt("d"));
assertTrue(dest.isNull("e"));
assertEquals(3, dest.getArray("f").size());
assertEquals("newvalue", dest.getString("newkey"));
}
public void testMapAccessibleAfterMerge() {
mCatalystInstance.getJSModule(TestJSToJavaParametersModule.class).returnMapForMerge1();
mCatalystInstance.getJSModule(TestJSToJavaParametersModule.class).returnMapForMerge2();
waitForBridgeAndUIIdle();
List<ReadableMap> maps = mRecordingTestModule.getMapCalls();
assertEquals(2, maps.size());
WritableMap dest = new WritableNativeMap();
dest.merge(maps.get(0));
dest.merge(maps.get(1));
ReadableMap source = maps.get(1);
assertTrue(source.hasKey("a"));
assertTrue(source.hasKey("d"));
assertTrue(source.hasKey("e"));
assertTrue(source.hasKey("f"));
assertTrue(source.hasKey("newkey"));
assertFalse(source.hasKey("b"));
assertFalse(source.hasKey("c"));
assertEquals("overwrite", source.getString("a"));
assertEquals(77, source.getInt("d"));
assertTrue(source.isNull("e"));
assertEquals(3, source.getArray("f").size());
assertEquals("newvalue", source.getString("newkey"));
}
public void testMapIterateOverMapWithBasicTypes() {
mCatalystInstance.getJSModule(TestJSToJavaParametersModule.class).returnMapWithBasicTypes();
waitForBridgeAndUIIdle();
List<ReadableMap> calls = mRecordingTestModule.getMapCalls();
assertEquals(1, calls.size());
ReadableNativeMap map = (ReadableNativeMap) calls.get(0);
assertNotNull(map);
ReadableMapKeySetIterator mapIterator = map.keySetIterator();
Set<String> keys = new HashSet<String>();
while (mapIterator.hasNextKey()) {
keys.add(mapIterator.nextKey());
}
Set<String> expectedKeys = new HashSet<String>(
Arrays.asList("stringKey", "doubleKey", "intKey", "booleanKey", "nullKey"));
assertEquals(keys, expectedKeys);
}
public void testMapIterateOverNestedMaps() {
mCatalystInstance.getJSModule(TestJSToJavaParametersModule.class).returnNestedMap();
waitForBridgeAndUIIdle();
List<ReadableMap> calls = mRecordingTestModule.getMapCalls();
assertEquals(1, calls.size());
ReadableNativeMap map = (ReadableNativeMap) calls.get(0);
assertNotNull(map);
ReadableMapKeySetIterator firstLevelIterator = map.keySetIterator();
String firstLevelKey = firstLevelIterator.nextKey();
assertEquals(firstLevelKey, "weHaveToGoDeeper");
ReadableNativeMap secondMap = map.getMap("weHaveToGoDeeper");
ReadableMapKeySetIterator secondLevelIterator = secondMap.keySetIterator();
String secondLevelKey = secondLevelIterator.nextKey();
assertEquals(secondLevelKey, "inception");
assertTrue(secondMap.getBoolean(secondLevelKey));
}
public void testInvalidIteratorExceptionThrown() {
mCatalystInstance.getJSModule(TestJSToJavaParametersModule.class).returnMapWithBasicTypes();
waitForBridgeAndUIIdle();
List<ReadableMap> calls = mRecordingTestModule.getMapCalls();
assertEquals(1, calls.size());
ReadableNativeMap map = (ReadableNativeMap) calls.get(0);
assertNotNull(map);
ReadableMapKeySetIterator mapIterator = map.keySetIterator();
while (mapIterator.hasNextKey()) {
mapIterator.nextKey();
}
assertInvalidIteratorExceptionThrown(mapIterator);
}
public void testStringWithMultibyteUTF8Characters() {
TestJSToJavaParametersModule jsModule =
mCatalystInstance.getJSModule(TestJSToJavaParametersModule.class);
jsModule.returnMapWithMultibyteUTF8CharacterString();
jsModule.returnArrayWithMultibyteUTF8CharacterString();
waitForBridgeAndUIIdle();
List<ReadableMap> maps = mRecordingTestModule.getMapCalls();
assertEquals(1, maps.size());
ReadableMap map = maps.get(0);
assertEquals("a", map.getString("one-byte"));
assertEquals("\u00A2", map.getString("two-bytes"));
assertEquals("\u20AC", map.getString("three-bytes"));
assertEquals("\uD83D\uDE1C", map.getString("four-bytes"));
assertEquals(
"\u017C\u00F3\u0142\u0107 g\u0119\u015Bl\u0105 \u6211 \uD83D\uDE0E ja\u017A\u0107",
map.getString("mixed"));
List<ReadableArray> arrays = mRecordingTestModule.getArrayCalls();
assertEquals(1, arrays.size());
ReadableArray array = arrays.get(0);
assertEquals("a", array.getString(0));
assertEquals("\u00A2", array.getString(1));
assertEquals("\u20AC", array.getString(2));
assertEquals("\uD83D\uDE1C", array.getString(3));
assertEquals(
"\u017C\u00F3\u0142\u0107 g\u0119\u015Bl\u0105 \u6211 \uD83D\uDE0E ja\u017A\u0107",
array.getString(4));
}
private void assertUnexpectedTypeExceptionThrown(
ReadableArray array,
int index,
String typeToAskFor) {
boolean gotException = false;
try {
arrayGetByType(array, index, typeToAskFor);
} catch (UnexpectedNativeTypeException expected) {
gotException = true;
}
assertTrue(gotException);
}
private void assertUnexpectedTypeExceptionThrown(
ReadableMap map,
String key,
String typeToAskFor) {
boolean gotException = false;
try {
mapGetByType(map, key, typeToAskFor);
} catch (UnexpectedNativeTypeException expected) {
gotException = true;
}
assertTrue(gotException);
}
private void assertArrayOutOfBoundsExceptionThrown(
ReadableArray array,
int index,
String typeToAskFor) {
boolean gotException = false;
try {
arrayGetByType(array, index, typeToAskFor);
} catch (ArrayIndexOutOfBoundsException expected) {
gotException = true;
}
assertTrue(gotException);
}
private void assertNoSuchKeyExceptionThrown(
ReadableMap map,
String key,
String typeToAskFor) {
boolean gotException = false;
try {
mapGetByType(map, key, typeToAskFor);
} catch (NoSuchKeyException expected) {
gotException = true;
}
assertTrue(gotException);
}
private static void assertInvalidIteratorExceptionThrown(
ReadableMapKeySetIterator iterator) {
boolean gotException = false;
try {
iterator.nextKey();
} catch (InvalidIteratorException expected) {
gotException = true;
}
assertTrue(gotException);
}
private void arrayGetByType(ReadableArray array, int index, String typeToAskFor) {
if (typeToAskFor.equals("double")) {
array.getDouble(index);
} else if (typeToAskFor.equals("int")) {
array.getInt(index);
} else if (typeToAskFor.equals("string")) {
array.getString(index);
} else if (typeToAskFor.equals("array")) {
array.getArray(index);
} else if (typeToAskFor.equals("map")) {
array.getMap(index);
} else if (typeToAskFor.equals("boolean")) {
array.getBoolean(index);
} else {
throw new RuntimeException("Unknown type: " + typeToAskFor);
}
}
private void mapGetByType(ReadableMap map, String key, String typeToAskFor) {
if (typeToAskFor.equals("double")) {
map.getDouble(key);
} else if (typeToAskFor.equals("int")) {
map.getInt(key);
} else if (typeToAskFor.equals("string")) {
map.getString(key);
} else if (typeToAskFor.equals("array")) {
map.getArray(key);
} else if (typeToAskFor.equals("map")) {
map.getMap(key);
} else if (typeToAskFor.equals("boolean")) {
map.getBoolean(key);
} else {
throw new RuntimeException("Unknown type: " + typeToAskFor);
}
}
private static class RecordingTestModule extends BaseJavaModule {
private final List<Object[]> mBasicTypesCalls = new ArrayList<Object[]>();
private final List<Object[]> mBoxedTypesCalls = new ArrayList<Object[]>();
private final List<ReadableArray> mArrayCalls = new ArrayList<ReadableArray>();
private final List<ReadableMap> mMapCalls = new ArrayList<ReadableMap>();
private final List<Dynamic> mDynamicCalls = new ArrayList<Dynamic>();
@Override
public String getName() {
return "Recording";
}
@ReactMethod
public void receiveBasicTypes(String s, double d, boolean b, String nullableString) {
mBasicTypesCalls.add(new Object[]{s, d, b, nullableString});
}
@ReactMethod
public void receiveBoxedTypes(Integer i, Double d, Boolean b) {
mBoxedTypesCalls.add(new Object[]{i, d, b});
}
@ReactMethod
public void receiveArray(ReadableArray array) {
mArrayCalls.add(array);
}
@ReactMethod
public void receiveMap(ReadableMap map) {
mMapCalls.add(map);
}
@ReactMethod
public void receiveDynamic(Dynamic dynamic) {
mDynamicCalls.add(dynamic);
}
public List<Object[]> getBasicTypesCalls() {
return mBasicTypesCalls;
}
public List<Object[]> getBoxedTypesCalls() {
return mBoxedTypesCalls;
}
public List<ReadableArray> getArrayCalls() {
return mArrayCalls;
}
public List<ReadableMap> getMapCalls() {
return mMapCalls;
}
public List<Dynamic> getDynamicCalls() {
return mDynamicCalls;
}
}
}

View File

@@ -0,0 +1,382 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.tests;
import com.facebook.react.bridge.CatalystInstance;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.bridge.ObjectAlreadyConsumedException;
import com.facebook.react.bridge.UiThreadUtil;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.WritableNativeArray;
import com.facebook.react.bridge.WritableNativeMap;
import com.facebook.react.modules.appstate.AppStateModule;
import com.facebook.react.modules.deviceinfo.DeviceInfoModule;
import com.facebook.react.testing.AssertModule;
import com.facebook.react.testing.FakeWebSocketModule;
import com.facebook.react.testing.ReactIntegrationTestCase;
import com.facebook.react.testing.ReactTestHelper;
import com.facebook.react.uimanager.UIImplementationProvider;
import com.facebook.react.uimanager.UIManagerModule;
import com.facebook.react.uimanager.ViewManager;
import com.facebook.react.views.view.ReactViewManager;
import java.util.Arrays;
import java.util.List;
import org.junit.Ignore;
/**
* Test marshalling arguments from Java to JS to appropriate native classes.
*/
@Ignore("Fix prop types and view managers.")
public class CatalystNativeJavaToJSArgumentsTestCase extends ReactIntegrationTestCase {
private interface TestJavaToJSArgumentsModule extends JavaScriptModule {
void receiveBasicTypes(String s, double d, boolean b, String nullString);
void receiveArrayWithBasicTypes(WritableArray array);
void receiveNestedArray(WritableArray nestedArray);
void receiveArrayWithMaps(WritableArray arrayWithMaps);
void receiveMapWithBasicTypes(WritableMap map);
void receiveNestedMap(WritableMap nestedMap);
void receiveMapWithArrays(WritableMap mapWithArrays);
void receiveMapAndArrayWithNullValues(
WritableMap map,
WritableArray array);
void receiveMapWithMultibyteUTF8CharacterString(WritableMap map);
void receiveArrayWithMultibyteUTF8CharacterString(WritableArray array);
}
private AssertModule mAssertModule;
private CatalystInstance mInstance;
@Override
protected void setUp() throws Exception {
super.setUp();
List<ViewManager> viewManagers = Arrays.<ViewManager>asList(
new ReactViewManager());
final UIManagerModule mUIManager =
new UIManagerModule(getContext(), viewManagers, new UIImplementationProvider(), 0);
UiThreadUtil.runOnUiThread(
new Runnable() {
@Override
public void run() {
mUIManager.onHostResume();
}
});
waitForIdleSync();
mAssertModule = new AssertModule();
mInstance = ReactTestHelper.catalystInstanceBuilder(this)
.addNativeModule(mAssertModule)
.addNativeModule(new DeviceInfoModule(getContext()))
.addNativeModule(new AppStateModule(getContext()))
.addNativeModule(new FakeWebSocketModule())
.addNativeModule(mUIManager)
.build();
}
public void testBasicTypes() {
mInstance.getJSModule(TestJavaToJSArgumentsModule.class)
.receiveBasicTypes("foo", 3.14, true, null);
waitForBridgeAndUIIdle();
mAssertModule.verifyAssertsAndReset();
}
public void testArrayWithBasicTypes() {
WritableNativeArray array = new WritableNativeArray();
array.pushString("red panda");
array.pushDouble(1.19);
array.pushBoolean(true);
array.pushNull();
mInstance.getJSModule(TestJavaToJSArgumentsModule.class).receiveArrayWithBasicTypes(array);
waitForBridgeAndUIIdle();
mAssertModule.verifyAssertsAndReset();
}
public void testNestedArray() {
WritableNativeArray level1 = new WritableNativeArray();
WritableNativeArray level2 = new WritableNativeArray();
WritableNativeArray level3 = new WritableNativeArray();
level3.pushString("level3");
level2.pushString("level2");
level2.pushArray(level3);
level1.pushString("level1");
level1.pushArray(level2);
mInstance.getJSModule(TestJavaToJSArgumentsModule.class).receiveNestedArray(level1);
waitForBridgeAndUIIdle();
mAssertModule.verifyAssertsAndReset();
}
public void testArrayWithMaps() {
WritableNativeMap m1 = new WritableNativeMap();
WritableNativeMap m2 = new WritableNativeMap();
m1.putString("m1k1", "m1v1");
m1.putString("m1k2", "m1v2");
m2.putString("m2k1", "m2v1");
WritableNativeArray array = new WritableNativeArray();
array.pushMap(m1);
array.pushMap(m2);
mInstance.getJSModule(TestJavaToJSArgumentsModule.class).receiveArrayWithMaps(array);
waitForBridgeAndUIIdle();
mAssertModule.verifyAssertsAndReset();
}
public void testMapWithBasicTypes() {
WritableNativeMap map = new WritableNativeMap();
map.putString("stringKey", "stringValue");
map.putDouble("doubleKey", 3.14);
map.putBoolean("booleanKey", true);
map.putNull("nullKey");
mInstance.getJSModule(TestJavaToJSArgumentsModule.class).receiveMapWithBasicTypes(map);
waitForBridgeAndUIIdle();
mAssertModule.verifyAssertsAndReset();
}
public void testNestedMap() {
WritableNativeMap map = new WritableNativeMap();
WritableNativeMap nestedMap = new WritableNativeMap();
nestedMap.putString("animals", "foxes");
map.putMap("nestedMap", nestedMap);
mInstance.getJSModule(TestJavaToJSArgumentsModule.class).receiveNestedMap(map);
waitForBridgeAndUIIdle();
mAssertModule.verifyAssertsAndReset();
}
public void testMapWithArrays() {
WritableNativeMap map = new WritableNativeMap();
WritableNativeArray a1 = new WritableNativeArray();
WritableNativeArray a2 = new WritableNativeArray();
a1.pushDouble(3);
a1.pushDouble(1);
a1.pushDouble(4);
a2.pushDouble(1);
a2.pushDouble(9);
map.putArray("array1", a1);
map.putArray("array2", a2);
mInstance.getJSModule(TestJavaToJSArgumentsModule.class).receiveMapWithArrays(map);
waitForBridgeAndUIIdle();
mAssertModule.verifyAssertsAndReset();
}
public void testMapWithNullStringValue() {
WritableNativeMap map = new WritableNativeMap();
map.putString("string", null);
map.putArray("array", null);
map.putMap("map", null);
WritableNativeArray array = new WritableNativeArray();
array.pushString(null);
array.pushArray(null);
array.pushMap(null);
mInstance.getJSModule(TestJavaToJSArgumentsModule.class)
.receiveMapAndArrayWithNullValues(map, array);
waitForBridgeAndUIIdle();
mAssertModule.verifyAssertsAndReset();
}
public void testStringWithMultibyteUTF8Characters() {
TestJavaToJSArgumentsModule jsModule = mInstance.getJSModule(TestJavaToJSArgumentsModule.class);
WritableNativeMap map = new WritableNativeMap();
map.putString("two-bytes", "\u00A2");
map.putString("three-bytes", "\u20AC");
map.putString("four-bytes", "\uD83D\uDE1C");
map.putString(
"mixed",
"\u017C\u00F3\u0142\u0107 g\u0119\u015Bl\u0105 \u6211 \uD83D\uDE0E ja\u017A\u0107");
jsModule.receiveMapWithMultibyteUTF8CharacterString(map);
waitForBridgeAndUIIdle();
mAssertModule.verifyAssertsAndReset();
WritableArray array = new WritableNativeArray();
array.pushString("\u00A2");
array.pushString("\u20AC");
array.pushString("\uD83D\uDE1C");
array.pushString(
"\u017C\u00F3\u0142\u0107 g\u0119\u015Bl\u0105 \u6211 \uD83D\uDE0E ja\u017A\u0107");
jsModule.receiveArrayWithMultibyteUTF8CharacterString(array);
waitForBridgeAndUIIdle();
mAssertModule.verifyAssertsAndReset();
}
public void testThrowWhenArrayReusedInArray() {
boolean gotException = false;
try {
WritableNativeArray array1 = new WritableNativeArray();
WritableNativeArray array2 = new WritableNativeArray();
WritableNativeArray child = new WritableNativeArray();
array1.pushArray(child);
array2.pushArray(child);
} catch (ObjectAlreadyConsumedException e) {
gotException = true;
}
assertTrue(gotException);
}
public void testThrowWhenArrayReusedInMap() {
boolean gotException = false;
try {
WritableNativeMap map1 = new WritableNativeMap();
WritableNativeMap map2 = new WritableNativeMap();
WritableNativeArray child = new WritableNativeArray();
map1.putArray("child", child);
map2.putArray("child", child);
} catch (ObjectAlreadyConsumedException e) {
gotException = true;
}
assertTrue(gotException);
}
public void testThrowWhenMapReusedInArray() {
boolean gotException = false;
try {
WritableNativeArray array1 = new WritableNativeArray();
WritableNativeArray array2 = new WritableNativeArray();
WritableNativeMap child = new WritableNativeMap();
array1.pushMap(child);
array2.pushMap(child);
} catch (ObjectAlreadyConsumedException e) {
gotException = true;
}
assertTrue(gotException);
}
public void testThrowWhenMapReusedInMap() {
boolean gotException = false;
try {
WritableNativeMap map1 = new WritableNativeMap();
WritableNativeMap map2 = new WritableNativeMap();
WritableNativeMap child = new WritableNativeMap();
map1.putMap("child", child);
map2.putMap("child", child);
} catch (ObjectAlreadyConsumedException e) {
gotException = true;
}
assertTrue(gotException);
}
public void testThrowWhenAddToConsumedArray() {
WritableNativeArray array = new WritableNativeArray();
WritableNativeArray parent = new WritableNativeArray();
parent.pushArray(array);
boolean gotException = false;
try {
array.pushNull();
} catch (ObjectAlreadyConsumedException e) {
gotException = true;
}
assertTrue(gotException);
gotException = false;
try {
array.pushBoolean(true);
} catch (ObjectAlreadyConsumedException e) {
gotException = true;
}
assertTrue(gotException);
gotException = false;
try {
array.pushDouble(1);
} catch (ObjectAlreadyConsumedException e) {
gotException = true;
}
assertTrue(gotException);
gotException = false;
try {
array.pushString("foo");
} catch (ObjectAlreadyConsumedException e) {
gotException = true;
}
assertTrue(gotException);
gotException = false;
try {
array.pushArray(new WritableNativeArray());
} catch (ObjectAlreadyConsumedException e) {
gotException = true;
}
assertTrue(gotException);
gotException = false;
try {
array.pushMap(new WritableNativeMap());
} catch (ObjectAlreadyConsumedException e) {
gotException = true;
}
assertTrue(gotException);
}
public void testThrowWhenAddToConsumedMap() {
WritableNativeMap map = new WritableNativeMap();
WritableNativeArray parent = new WritableNativeArray();
parent.pushMap(map);
boolean gotException = false;
try {
map.putNull("key");
} catch (ObjectAlreadyConsumedException e) {
gotException = true;
}
assertTrue(gotException);
gotException = false;
try {
map.putBoolean("key", true);
} catch (ObjectAlreadyConsumedException e) {
gotException = true;
}
assertTrue(gotException);
gotException = false;
try {
map.putDouble("key", 1);
} catch (ObjectAlreadyConsumedException e) {
gotException = true;
}
assertTrue(gotException);
gotException = false;
try {
map.putString("key", "foo");
} catch (ObjectAlreadyConsumedException e) {
gotException = true;
}
assertTrue(gotException);
gotException = false;
try {
map.putArray("key", new WritableNativeArray());
} catch (ObjectAlreadyConsumedException e) {
gotException = true;
}
assertTrue(gotException);
gotException = false;
try {
map.putMap("key", new WritableNativeMap());
} catch (ObjectAlreadyConsumedException e) {
gotException = true;
}
assertTrue(gotException);
}
}

View File

@@ -0,0 +1,164 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.tests;
import com.facebook.react.bridge.BaseJavaModule;
import com.facebook.react.bridge.CatalystInstance;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.WritableNativeArray;
import com.facebook.react.bridge.WritableNativeMap;
import com.facebook.react.module.annotations.ReactModule;
import com.facebook.react.modules.appstate.AppStateModule;
import com.facebook.react.modules.deviceinfo.DeviceInfoModule;
import com.facebook.react.testing.AssertModule;
import com.facebook.react.testing.FakeWebSocketModule;
import com.facebook.react.testing.ReactIntegrationTestCase;
import com.facebook.react.testing.ReactTestHelper;
import com.facebook.react.uimanager.UIImplementationProvider;
import com.facebook.react.uimanager.UIManagerModule;
import com.facebook.react.uimanager.ViewManager;
import java.util.ArrayList;
import org.junit.Ignore;
/**
* Test marshalling return values from Java to JS
*/
@Ignore("Fix prop types and view managers.")
public class CatalystNativeJavaToJSReturnValuesTestCase extends ReactIntegrationTestCase {
private interface TestJavaToJSReturnValuesModule extends JavaScriptModule {
void callMethod(String methodName, String expectedReturnType, String expectedJSON);
void triggerException();
}
@ReactModule(name = "TestModule")
private static class TestModule extends BaseJavaModule {
@Override
public String getName() {
return "TestModule";
}
@ReactMethod(isBlockingSynchronousMethod = true)
boolean getBoolean() {
return true;
}
@ReactMethod(isBlockingSynchronousMethod = true)
Boolean getBoxedBoolean() {
return Boolean.valueOf(true);
}
@ReactMethod(isBlockingSynchronousMethod = true)
int getInt() {
return 42;
}
@ReactMethod(isBlockingSynchronousMethod = true)
Integer getBoxedInt() {
return Integer.valueOf(42);
}
@ReactMethod(isBlockingSynchronousMethod = true)
double getDouble() {
return 3.14159;
}
@ReactMethod(isBlockingSynchronousMethod = true)
Double getBoxedDouble() {
return Double.valueOf(3.14159);
}
@ReactMethod(isBlockingSynchronousMethod = true)
String getString() {
return "Hello world!";
}
@ReactMethod(isBlockingSynchronousMethod = true)
WritableArray getArray() {
WritableArray arr = new WritableNativeArray();
arr.pushString("a");
arr.pushBoolean(true);
return arr;
}
@ReactMethod(isBlockingSynchronousMethod = true)
WritableMap getMap() {
WritableMap map = new WritableNativeMap();
map.putBoolean("a", true);
map.putBoolean("b", false);
return map;
}
@ReactMethod(isBlockingSynchronousMethod = true)
boolean triggerException() {
throw new RuntimeException("Exception triggered");
}
}
private AssertModule mAssertModule;
private CatalystInstance mInstance;
@Override
protected void setUp() throws Exception {
super.setUp();
final UIManagerModule mUIManager =
new UIManagerModule(
getContext(), new ArrayList<ViewManager>(), new UIImplementationProvider(), 0);
mAssertModule = new AssertModule();
mInstance = ReactTestHelper.catalystInstanceBuilder(this)
.addNativeModule(mAssertModule)
.addNativeModule(new DeviceInfoModule(getContext()))
.addNativeModule(new AppStateModule(getContext()))
.addNativeModule(new FakeWebSocketModule())
.addNativeModule(mUIManager)
.addNativeModule(new TestModule())
.build();
}
public void testGetPrimitives() {
TestJavaToJSReturnValuesModule m = mInstance.getJSModule(TestJavaToJSReturnValuesModule.class);
// jboolean is actually an unsigned char, so we don't get JS booleans
m.callMethod("getBoolean", "number", "1");
m.callMethod("getBoxedBoolean", "number", "1");
m.callMethod("getInt", "number", "42");
m.callMethod("getBoxedInt", "number", "42");
m.callMethod("getDouble", "number", "3.14159");
m.callMethod("getBoxedDouble", "number", "3.14159");
waitForBridgeAndUIIdle();
mAssertModule.verifyAssertsAndReset();
}
public void testObjectTypes() {
TestJavaToJSReturnValuesModule m = mInstance.getJSModule(TestJavaToJSReturnValuesModule.class);
m.callMethod("getString", "string", "\"Hello world!\"");
m.callMethod("getArray", "object", "[\"a\",true]");
m.callMethod("getMap", "object", "{\"b\":false,\"a\":true}");
waitForBridgeAndUIIdle();
mAssertModule.verifyAssertsAndReset();
}
public void testThrowsException() {
TestJavaToJSReturnValuesModule m = mInstance.getJSModule(TestJavaToJSReturnValuesModule.class);
m.triggerException();
waitForBridgeAndUIIdle();
mAssertModule.verifyAssertsAndReset();
}
}

View File

@@ -0,0 +1,296 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.tests;
import java.util.ArrayList;
import java.util.List;
import android.content.Context;
import javax.annotation.Nullable;
import android.widget.ScrollView;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.testing.ReactAppInstrumentationTestCase;
import com.facebook.react.testing.ReactInstanceSpecForTest;
import com.facebook.react.uimanager.PixelUtil;
import com.facebook.react.uimanager.annotations.ReactProp;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.views.view.ReactViewGroup;
import com.facebook.react.views.view.ReactViewManager;
import org.junit.Assert;
import org.junit.Ignore;
/**
* Integration test for {@code removeClippedSubviews} property that verify correct scrollview
* behavior
*/
public class CatalystSubviewsClippingTestCase extends ReactAppInstrumentationTestCase {
private interface SubviewsClippingTestModule extends JavaScriptModule {
void renderClippingSample1();
void renderClippingSample2();
void renderScrollViewTest();
void renderUpdatingSample1(boolean update1, boolean update2);
void renderUpdatingSample2(boolean update);
}
private final List<String> mEvents = new ArrayList<>();
@Override
protected String getReactApplicationKeyUnderTest() {
return "SubviewsClippingTestApp";
}
@Override
protected ReactInstanceSpecForTest createReactInstanceSpecForTest() {
return super.createReactInstanceSpecForTest()
.addViewManager(new ClippableViewManager(mEvents));
}
/**
* In this test view are layout in a following way:
* +-----------------------------+
* | |
* | +---------------------+ |
* | | inner1 | |
* | +---------------------+ |
* | +-------------------------+ |
* | | outer (clip=true) | |
* | | +---------------------+ | |
* | | | inner2 | | |
* | | +---------------------+ | |
* | | | |
* | +-------------------------+ |
* | +---------------------+ |
* | | inner3 | |
* | +---------------------+ |
* | |
* +-----------------------------+
*
* We expect only outer and inner2 to be attached
*/
public void XtestOneLevelClippingInView() throws Throwable {
mEvents.clear();
getReactContext().getJSModule(SubviewsClippingTestModule.class).renderClippingSample1();
waitForBridgeAndUIIdle();
Assert.assertArrayEquals(new String[]{"Attach_outer", "Attach_inner2"}, mEvents.toArray());
}
/**
* In this test view are layout in a following way:
* +-----------------------------+
* | outer (clip=true) |
* | |
* | |
* | |
* | +-----------------------------+
* | | complexInner (clip=true) |
* | | +----------+ | +---------+ |
* | | | inner1 | | | inner2 | |
* | | | | | | | |
* | | +----------+ | +---------+ |
* +--------------+--------------+ |
* | +----------+ +---------+ |
* | | inner3 | | inner4 | |
* | | | | | |
* | +----------+ +---------+ |
* | |
* +-----------------------------+
*
* We expect outer, complexInner & inner1 to be attached
*/
public void XtestTwoLevelClippingInView() throws Throwable {
mEvents.clear();
getReactContext().getJSModule(SubviewsClippingTestModule.class).renderClippingSample2();
waitForBridgeAndUIIdle();
Assert.assertArrayEquals(
new String[]{"Attach_outer", "Attach_complexInner", "Attach_inner1"},
mEvents.toArray());
}
/**
* This test verifies that we update clipped subviews appropriately when some of them gets
* re-layouted.
*
* In this test scenario we render clipping view ("outer") with two subviews, one is outside and
* clipped and one is inside (absolutely positioned). By updating view props we first change the
* height of the first element so that it should intersect with clipping "outer" view. Then we
* update top position of the second view so that is should go off screen.
*/
public void testClippingAfterLayoutInner() {
SubviewsClippingTestModule subviewsClippingTestModule =
getReactContext().getJSModule(SubviewsClippingTestModule.class);
mEvents.clear();
subviewsClippingTestModule.renderUpdatingSample1(false, false);
waitForBridgeAndUIIdle();
Assert.assertArrayEquals(new String[]{"Attach_outer", "Attach_inner2"}, mEvents.toArray());
mEvents.clear();
subviewsClippingTestModule.renderUpdatingSample1(true, false);
waitForBridgeAndUIIdle();
Assert.assertArrayEquals(new String[]{"Attach_inner1"}, mEvents.toArray());
mEvents.clear();
subviewsClippingTestModule.renderUpdatingSample1(true, true);
waitForBridgeAndUIIdle();
Assert.assertArrayEquals(new String[]{"Detach_inner2"}, mEvents.toArray());
}
/**
* This test verifies that we update clipping views appropriately when parent view layout changes
* in a way that affects clipping.
*
* In this test we render clipping view ("outer") set to be 100x100dp with inner view that is
* absolutely positioned out of the clipping area of the parent view. Then we resize parent view
* so that inner view should be visible.
*/
public void testClippingAfterLayoutParent() {
SubviewsClippingTestModule subviewsClippingTestModule =
getReactContext().getJSModule(SubviewsClippingTestModule.class);
mEvents.clear();
subviewsClippingTestModule.renderUpdatingSample2(false);
waitForBridgeAndUIIdle();
Assert.assertArrayEquals(new String[]{"Attach_outer"}, mEvents.toArray());
mEvents.clear();
subviewsClippingTestModule.renderUpdatingSample2(true);
waitForBridgeAndUIIdle();
Assert.assertArrayEquals(new String[]{"Attach_inner"}, mEvents.toArray());
}
public void testOneLevelClippingInScrollView() throws Throwable {
getReactContext().getJSModule(SubviewsClippingTestModule.class).renderScrollViewTest();
waitForBridgeAndUIIdle();
// Only 3 first views should be attached at the beginning
Assert.assertArrayEquals(new String[]{"Attach_0", "Attach_1", "Attach_2"}, mEvents.toArray());
mEvents.clear();
// We scroll down such that first view get out of the bounds, we expect the first view to be
// detached and 4th view to get attached
scrollToDpInUIThread(120);
Assert.assertArrayEquals(new String[]{"Detach_0", "Attach_3"}, mEvents.toArray());
}
public void testTwoLevelClippingInScrollView() throws Throwable {
getReactContext().getJSModule(SubviewsClippingTestModule.class).renderScrollViewTest();
waitForBridgeAndUIIdle();
final int complexViewOffset = 4 * 120 - 300;
// Step 1
// We scroll down such that first "complex" view is clipped & just below the bottom of the
// scroll view
scrollToDpInUIThread(complexViewOffset);
mEvents.clear();
// Step 2
// Scroll a little bit so that "complex" view is visible, but it's inner views are not
scrollToDpInUIThread(complexViewOffset + 5);
Assert.assertArrayEquals(new String[]{"Attach_C0"}, mEvents.toArray());
mEvents.clear();
// Step 3
// Scroll even more so that first subview of "complex" view is visible, view 1 will get off
// screen
scrollToDpInUIThread(complexViewOffset + 100);
Assert.assertArrayEquals(new String[]{"Detach_1", "Attach_C0.1"}, mEvents.toArray());
mEvents.clear();
// Step 4
// Scroll even more to reveal second subview of "complex" view
scrollToDpInUIThread(complexViewOffset + 150);
Assert.assertArrayEquals(new String[]{"Attach_C0.2"}, mEvents.toArray());
mEvents.clear();
// Step 5
// Scroll back to previous position (Step 3), second view should get detached
scrollToDpInUIThread(complexViewOffset + 100);
Assert.assertArrayEquals(new String[]{"Detach_C0.2"}, mEvents.toArray());
mEvents.clear();
// Step 6
// Scroll back to Step 2, complex view should be visible but all subviews should be detached
scrollToDpInUIThread(complexViewOffset + 5);
Assert.assertArrayEquals(new String[]{"Attach_1", "Detach_C0.1"}, mEvents.toArray());
mEvents.clear();
// Step 7
// Scroll back to Step 1, complex view should be gone
scrollToDpInUIThread(complexViewOffset);
Assert.assertArrayEquals(new String[]{"Detach_C0"}, mEvents.toArray());
}
private void scrollToDpInUIThread(final int yPositionInDP) throws Throwable {
final ScrollView mainScrollView = getViewByTestId("scroll_view");
runTestOnUiThread(
new Runnable() {
@Override
public void run() {
mainScrollView.scrollTo(0, (int) PixelUtil.toPixelFromDIP(yPositionInDP));
}
});
waitForBridgeAndUIIdle();
}
private static class ClippableView extends ReactViewGroup {
private String mClippableViewID;
private final List<String> mEvents;
public ClippableView(Context context, List<String> events) {
super(context);
mEvents = events;
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
mEvents.add("Attach_" + mClippableViewID);
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
mEvents.add("Detach_" + mClippableViewID);
}
public void setClippableViewID(String clippableViewID) {
mClippableViewID = clippableViewID;
}
}
private static class ClippableViewManager extends ReactViewManager {
private final List<String> mEvents;
public ClippableViewManager(List<String> events) {
mEvents = events;
}
@Override
public String getName() {
return "ClippableView";
}
@Override
public ReactViewGroup createViewInstance(ThemedReactContext context) {
return new ClippableView(context, mEvents);
}
@ReactProp(name = "clippableViewID")
public void setClippableViewId(ReactViewGroup view, @Nullable String clippableViewId) {
((ClippableView) view).setClippableViewID(clippableViewId);
}
}
}

View File

@@ -0,0 +1,157 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.tests;
import android.view.View;
import com.facebook.react.testing.ReactInstanceSpecForTest;
import com.facebook.react.testing.ReactAppInstrumentationTestCase;
import com.facebook.react.testing.SingleTouchGestureGenerator;
import com.facebook.react.testing.StringRecordingModule;
/**
* This test is to verify that touch events bubbles up to the right handler. We emulate couple
* of different gestures on top of the application reflecting following layout:
*
* +---------------------------------------------------------------------------------------+
* | |
* | +----------------------------------------------------------------------------------+ |
* | | +-------------+ +----------------+ | |
* | | | +---+ | | | | |
* | | | | A | | | | | |
* | | | +---+ | | C | | |
* | | | {B} | | | | |
* | | | | {D} | | | |
* | | +-------------+ +----------------+ | |
* | | | |
* | | | |
* | +----------------------------------------------------------------------------------+ |
* |
* | +----------------------------------------------------------------------------------+ |
* | | | |
* | | | |
* | | | |
* | | {E} | |
* | | | |
* | | | |
* | +----------------------------------------------------------------------------------+ |
* +---------------------------------------------------------------------------------------+
*
* Then in each test case we either tap the center of a particular view (from A to E) or we start
* a gesture in one view and end it with another.
* View with names in brackets (e.g. {D}) have touch handlers set whereas all other views are not
* declared to handler touch events.
*/
public class CatalystTouchBubblingTestCase extends ReactAppInstrumentationTestCase {
private final StringRecordingModule mRecordingModule = new StringRecordingModule();
@Override
protected String getReactApplicationKeyUnderTest() {
return "TouchBubblingTestAppModule";
}
/**
* 1) Simulate touch event at view A, expect {B} touch handler to fire
* 2) Simulate touch event at view C, expect {D} touch handler to fire
*/
public void testSimpleClickAtInnerElements() {
mRecordingModule.reset();
View innerButton = getViewByTestId("A");
assertNotNull(innerButton);
createGestureGenerator().startGesture(innerButton).endGesture();
waitForBridgeAndUIIdle();
assertEquals(1, mRecordingModule.getCalls().size());
assertEquals("inner", mRecordingModule.getCalls().get(0));
mRecordingModule.reset();
innerButton = getViewByTestId("C");
assertNotNull(innerButton);
createGestureGenerator().startGesture(innerButton).endGesture();
waitForBridgeAndUIIdle();
assertEquals(1, mRecordingModule.getCalls().size());
assertEquals("outer", mRecordingModule.getCalls().get(0));
}
/**
* 1) Start touch at view A, then drag and release on view {B} (but outside of A), expect {B}'s
* touch handler to fire
* 2) Do the same with view C and {D}
*/
public void testDownOnInnerUpOnTouchableParent() {
View innerButton = getViewByTestId("A");
View touchableParent = getViewByTestId("B");
SingleTouchGestureGenerator gestureGenerator = createGestureGenerator();
gestureGenerator.startGesture(innerButton);
// wait for tapped view measurements
waitForBridgeAndUIIdle();
gestureGenerator.dragTo(touchableParent, 15).endGesture();
waitForBridgeAndUIIdle();
assertEquals(1, mRecordingModule.getCalls().size());
assertEquals("inner", mRecordingModule.getCalls().get(0));
// Do same with second inner view
mRecordingModule.reset();
touchableParent = getViewByTestId("D");
innerButton = getViewByTestId("C");
gestureGenerator = createGestureGenerator();
gestureGenerator.startGesture(innerButton);
// wait for tapped view measurements
waitForBridgeAndUIIdle();
gestureGenerator.dragTo(touchableParent, 15).endGesture();
waitForBridgeAndUIIdle();
assertEquals(1, mRecordingModule.getCalls().size());
assertEquals("outer", mRecordingModule.getCalls().get(0));
}
/**
* Start gesture at view A, then drag and release on view {E}. Expect no touch handlers to fire
*/
public void testDragOutOfTouchable() {
View outsideView = getViewByTestId("E");
View innerButton = getViewByTestId("A");
SingleTouchGestureGenerator gestureGenerator = createGestureGenerator();
gestureGenerator.startGesture(innerButton);
// wait for tapped view measurements
waitForBridgeAndUIIdle();
gestureGenerator.dragTo(outsideView, 15).endGesture();
waitForBridgeAndUIIdle();
assertTrue(mRecordingModule.getCalls().isEmpty());
}
/**
* In this scenario we start gesture at view A (has two touchable parents {B} and {D}) then we
* drag and release gesture on view {D}, but outside of {B}. We expect no touch handler to fire
*/
public void testNoEventWhenDragOutOfFirstTouchableParentToItsTouchableParent() {
View topLevelTouchable = getViewByTestId("C");
View innerButton = getViewByTestId("A");
SingleTouchGestureGenerator gestureGenerator = createGestureGenerator();
gestureGenerator.startGesture(innerButton);
// wait for tapped view measurements
waitForBridgeAndUIIdle();
gestureGenerator.dragTo(topLevelTouchable, 15).endGesture();
waitForBridgeAndUIIdle();
assertTrue(mRecordingModule.getCalls().isEmpty());
}
@Override
protected ReactInstanceSpecForTest createReactInstanceSpecForTest() {
return new ReactInstanceSpecForTest()
.addNativeModule(mRecordingModule);
}
}

View File

@@ -0,0 +1,252 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.tests;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.TextView;
import com.facebook.react.ReactRootView;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.bridge.UiThreadUtil;
import com.facebook.react.modules.appstate.AppStateModule;
import com.facebook.react.modules.deviceinfo.DeviceInfoModule;
import com.facebook.react.modules.systeminfo.AndroidInfoModule;
import com.facebook.react.testing.FakeWebSocketModule;
import com.facebook.react.testing.ReactIntegrationTestCase;
import com.facebook.react.testing.ReactTestHelper;
import com.facebook.react.uimanager.PixelUtil;
import com.facebook.react.uimanager.UIImplementationProvider;
import com.facebook.react.uimanager.UIManagerModule;
import com.facebook.react.uimanager.ViewManager;
import com.facebook.react.views.text.ReactRawTextManager;
import com.facebook.react.views.text.ReactTextViewManager;
import com.facebook.react.views.view.ReactViewManager;
import java.util.Arrays;
import java.util.List;
/**
* Test case for basic {@link UIManagerModule} functionality.
*/
public class CatalystUIManagerTestCase extends ReactIntegrationTestCase {
private interface UIManagerTestModule extends JavaScriptModule {
void renderFlexTestApplication(int rootTag);
void renderFlexWithTextApplication(int rootTag);
void renderAbsolutePositionTestApplication(int rootTag);
void renderAbsolutePositionBottomRightTestApplication(int rootTag);
void renderCenteredTextViewTestApplication(int rootTag, String text);
void renderUpdatePositionInListTestApplication(int rootTag);
void flushUpdatePositionInList();
}
private UIManagerTestModule jsModule;
private UIManagerModule uiManager;
private int inPixelRounded(int val) {
return Math.round(PixelUtil.toPixelFromDIP(val));
}
private boolean isWithinRange(float value, float lower, float upper) {
return value >= lower && value <= upper;
}
private ReactRootView createRootView() {
ReactRootView rootView = new ReactRootView(getContext());
final DisplayMetrics metrics = getContext().getResources().getDisplayMetrics();
rootView.setLayoutParams(
new FrameLayout.LayoutParams(metrics.widthPixels, metrics.heightPixels));
uiManager.addRootView(rootView);
// We add the root view by posting to the main thread so wait for that to complete so that the
// root view tag is added to the view
waitForIdleSync();
return rootView;
}
@Override
protected void setUp() throws Exception {
super.setUp();
List<ViewManager> viewManagers = Arrays.<ViewManager>asList(
new ReactViewManager(),
new ReactTextViewManager(),
new ReactRawTextManager());
uiManager =
new UIManagerModule(getContext(), viewManagers, new UIImplementationProvider(), 0);
UiThreadUtil.runOnUiThread(new Runnable() {
@Override
public void run() {
uiManager.onHostResume();
}
});
waitForIdleSync();
jsModule = ReactTestHelper.catalystInstanceBuilder(this)
.addNativeModule(uiManager)
.addNativeModule(new AndroidInfoModule(getContext()))
.addNativeModule(new DeviceInfoModule(getContext()))
.addNativeModule(new AppStateModule(getContext()))
.addNativeModule(new FakeWebSocketModule())
.build()
.getJSModule(UIManagerTestModule.class);
}
public void testFlexUIRendered() {
FrameLayout rootView = createRootView();
jsModule.renderFlexTestApplication(rootView.getId());
waitForBridgeAndUIIdle();
assertEquals(1, rootView.getChildCount());
ViewGroup container = getViewByTestId(rootView, "container");
assertEquals(inPixelRounded(200), container.getWidth());
assertEquals(inPixelRounded(200), container.getHeight());
assertEquals(2, container.getChildCount());
View child0 = container.getChildAt(0);
assertEquals(inPixelRounded(100), child0.getWidth());
assertEquals(inPixelRounded(200), child0.getHeight());
View child1 = container.getChildAt(1);
assertEquals(inPixelRounded(100), child1.getWidth());
assertEquals(inPixelRounded(200), child1.getHeight());
}
// TODO t13583009
// Breaks OSS CI but runs fine locally
// Find what could be different and make the test independent of env
// public void testFlexWithTextViews() {
// FrameLayout rootView = createRootView();
// jsModule.renderFlexWithTextApplication(rootView.getId());
// waitForBridgeAndUIIdle();
//
// assertEquals(1, rootView.getChildCount());
//
// ViewGroup container = getViewByTestId(rootView, "container");
// assertEquals(inPixelRounded(300), container.getHeight());
// assertEquals(1, container.getChildCount());
//
// ViewGroup row = (ViewGroup) container.getChildAt(0);
// assertEquals(inPixelRounded(300), row.getHeight());
// assertEquals(2, row.getChildCount());
//
// // Text measurement adds padding that isn't completely dependent on density so we can't easily
// // get an exact value here
// float approximateExpectedTextHeight = inPixelRounded(19);
// View leftText = row.getChildAt(0);
// assertTrue(
// isWithinRange(
// leftText.getHeight(),
// approximateExpectedTextHeight - PixelUtil.toPixelFromDIP(1),
// approximateExpectedTextHeight + PixelUtil.toPixelFromDIP(1)));
// assertEquals(row.getWidth() / 2 - inPixelRounded(20), leftText.getWidth());
// assertEquals(inPixelRounded(290), (leftText.getTop() + leftText.getHeight()));
//
// View rightText = row.getChildAt(1);
// assertTrue(
// isWithinRange(
// rightText.getHeight(),
// approximateExpectedTextHeight - PixelUtil.toPixelFromDIP(1),
// approximateExpectedTextHeight + PixelUtil.toPixelFromDIP(1)));
// assertEquals(leftText.getWidth(), rightText.getWidth());
// assertEquals(leftText.getTop(), rightText.getTop());
// assertEquals(leftText.getWidth() + inPixelRounded(30), rightText.getLeft());
// }
public void testAbsolutePositionUIRendered() {
FrameLayout rootView = createRootView();
jsModule.renderAbsolutePositionTestApplication(rootView.getId());
waitForBridgeAndUIIdle();
assertEquals(1, rootView.getChildCount());
View absoluteView = getViewByTestId(rootView, "absolute");
assertEquals(inPixelRounded(50), absoluteView.getWidth());
assertEquals(inPixelRounded(60), absoluteView.getHeight());
assertEquals(inPixelRounded(10), absoluteView.getLeft());
assertEquals(inPixelRounded(15), absoluteView.getTop());
}
public void testUpdatePositionInList() {
FrameLayout rootView = createRootView();
jsModule.renderUpdatePositionInListTestApplication(rootView.getId());
waitForBridgeAndUIIdle();
ViewGroup containerView = getViewByTestId(rootView, "container");
View c0 = containerView.getChildAt(0);
View c1 = containerView.getChildAt(1);
View c2 = containerView.getChildAt(2);
assertEquals(inPixelRounded(10), c0.getHeight());
assertEquals(inPixelRounded(0), c0.getTop());
assertEquals(inPixelRounded(10), c1.getHeight());
assertEquals(inPixelRounded(10), c1.getTop());
assertEquals(inPixelRounded(10), c2.getHeight());
assertEquals(inPixelRounded(20), c2.getTop());
// Let's make the second elements 50px height instead of 10px
jsModule.flushUpdatePositionInList();
waitForBridgeAndUIIdle();
assertEquals(inPixelRounded(10), c0.getHeight());
assertEquals(inPixelRounded(0), c0.getTop());
assertEquals(inPixelRounded(50), c1.getHeight());
assertEquals(inPixelRounded(10), c1.getTop());
assertEquals(inPixelRounded(10), c2.getHeight());
assertEquals(inPixelRounded(60), c2.getTop());
}
public void testAbsolutePositionBottomRightUIRendered() {
FrameLayout rootView = createRootView();
jsModule.renderAbsolutePositionBottomRightTestApplication(rootView.getId());
waitForBridgeAndUIIdle();
assertEquals(1, rootView.getChildCount());
ViewGroup containerView = getViewByTestId(rootView, "container");
View absoluteView = containerView.getChildAt(0);
assertEquals(inPixelRounded(50), absoluteView.getWidth());
assertEquals(inPixelRounded(60), absoluteView.getHeight());
assertEquals(inPixelRounded(100 - 50 - 10), Math.round(absoluteView.getLeft()));
assertEquals(inPixelRounded(100 - 60 - 15), Math.round(absoluteView.getTop()));
}
public void _testCenteredText(String text) {
ReactRootView rootView = new ReactRootView(getContext());
int rootTag = uiManager.addRootView(rootView);
jsModule.renderCenteredTextViewTestApplication(rootTag, text);
waitForBridgeAndUIIdle();
TextView textView = getViewByTestId(rootView, "text");
// text view should be centered
String msg = "text `" + text + "` is not centered";
assertTrue(msg, textView.getLeft() > 0.1);
assertTrue(msg, textView.getTop() > 0.1);
assertEquals(
msg,
(int) Math.ceil((inPixelRounded(200) - textView.getWidth()) * 0.5f),
textView.getLeft());
assertEquals(
msg,
(int) Math.ceil((inPixelRounded(100) - textView.getHeight()) * 0.5f),
textView.getTop());
}
public void testCenteredTextCases() {
String[] cases = new String[] {
"test",
"with whitespace",
};
for (String text : cases) {
_testCenteredText(text);
}
}
}

View File

@@ -0,0 +1,175 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.tests;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import android.app.DatePickerDialog;
import android.content.DialogInterface;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.Fragment;
import android.widget.DatePicker;
import com.facebook.react.bridge.BaseJavaModule;
import com.facebook.react.testing.ReactInstanceSpecForTest;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.WritableNativeMap;
import com.facebook.react.modules.datepicker.DatePickerDialogModule;
import com.facebook.react.testing.ReactAppInstrumentationTestCase;
/**
* Test case for {@link DatePickerDialogModule} options and callbacks.
*/
public class DatePickerDialogTestCase extends ReactAppInstrumentationTestCase {
private static interface DatePickerDialogTestModule extends JavaScriptModule {
public void showDatePickerDialog(WritableMap options);
}
private static class DatePickerDialogRecordingModule extends BaseJavaModule {
private final List<Integer[]> mDates = new ArrayList<Integer[]>();
private int mDismissed = 0;
private int mErrors = 0;
@Override
public String getName() {
return "DatePickerDialogRecordingModule";
}
@ReactMethod
public void recordDate(int year, int month, int day) {
mDates.add(new Integer[] {year, month, day});
}
@ReactMethod
public void recordDismissed() {
mDismissed++;
}
@ReactMethod
public void recordError() {
mErrors++;
}
public List<Integer[]> getDates() {
return new ArrayList<Integer[]>(mDates);
}
public int getDismissed() {
return mDismissed;
}
public int getErrors() {
return mErrors;
}
}
final DatePickerDialogRecordingModule mRecordingModule = new DatePickerDialogRecordingModule();
@Override
protected ReactInstanceSpecForTest createReactInstanceSpecForTest() {
return super.createReactInstanceSpecForTest()
.addNativeModule(mRecordingModule);
}
@Override
protected String getReactApplicationKeyUnderTest() {
return "DatePickerDialogTestApp";
}
private static long getDateInMillis(int year, int month, int date) {
final Calendar c = Calendar.getInstance();
c.set(Calendar.YEAR, year);
c.set(Calendar.MONTH, month);
c.set(Calendar.DATE, date);
return c.getTimeInMillis();
}
private DatePickerDialogTestModule getTestModule() {
return getReactContext().getCatalystInstance().getJSModule(DatePickerDialogTestModule.class);
}
private DialogFragment showDialog(WritableMap options) {
getTestModule().showDatePickerDialog(options);
waitForBridgeAndUIIdle();
getInstrumentation().waitForIdleSync();
return (DialogFragment) getActivity().getSupportFragmentManager()
.findFragmentByTag(DatePickerDialogModule.FRAGMENT_TAG);
}
public void testShowBasicDatePicker() {
final Fragment datePickerFragment = showDialog(null);
assertNotNull(datePickerFragment);
}
public void testPresetDate() {
final WritableMap options = new WritableNativeMap();
options.putDouble("date", getDateInMillis(2020, 5, 6));
final DialogFragment datePickerFragment = showDialog(options);
final DatePicker datePicker =
((DatePickerDialog) datePickerFragment.getDialog()).getDatePicker();
assertEquals(2020, datePicker.getYear());
assertEquals(5, datePicker.getMonth());
assertEquals(6, datePicker.getDayOfMonth());
}
public void testCallback() throws Throwable {
final WritableMap options = new WritableNativeMap();
options.putDouble("date", getDateInMillis(2020, 5, 6));
final DialogFragment datePickerFragment = showDialog(options);
runTestOnUiThread(
new Runnable() {
@Override
public void run() {
((DatePickerDialog) datePickerFragment.getDialog())
.getButton(DialogInterface.BUTTON_POSITIVE).performClick();
}
});
getInstrumentation().waitForIdleSync();
waitForBridgeAndUIIdle();
assertEquals(0, mRecordingModule.getErrors());
assertEquals(1, mRecordingModule.getDates().size());
assertEquals(2020, (int) mRecordingModule.getDates().get(0)[0]);
assertEquals(5, (int) mRecordingModule.getDates().get(0)[1]);
assertEquals(6, (int) mRecordingModule.getDates().get(0)[2]);
}
public void testDismissCallback() throws Throwable {
final DialogFragment datePickerFragment = showDialog(null);
runTestOnUiThread(
new Runnable() {
@Override
public void run() {
datePickerFragment.getDialog().dismiss();
}
});
getInstrumentation().waitForIdleSync();
waitForBridgeAndUIIdle();
assertEquals(0, mRecordingModule.getErrors());
assertEquals(0, mRecordingModule.getDates().size());
assertEquals(1, mRecordingModule.getDismissed());
}
}

View File

@@ -0,0 +1,128 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.tests;
import android.os.Bundle;
import android.test.ActivityInstrumentationTestCase2;
import com.facebook.react.bridge.BaseJavaModule;
import com.facebook.react.testing.FakeWebSocketModule;
import com.facebook.react.testing.ReactInstanceSpecForTest;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.testing.ReactAppTestActivity;
/**
* Simple test case for passing initial props to the root React application.
*/
public class InitialPropsTestCase extends
ActivityInstrumentationTestCase2<ReactAppTestActivity> {
public static final String DEFAULT_JS_BUNDLE = "AndroidTestBundle.js";
private static class RecordingModule extends BaseJavaModule {
private int mCount = 0;
private ReadableMap mProps;
@Override
public String getName() {
return "InitialPropsRecordingModule";
}
@ReactMethod
public void recordProps(ReadableMap props) {
mProps = props;
mCount++;
}
public int getCount() {
return mCount;
}
public ReadableMap getProps() {
return mProps;
}
}
private RecordingModule mRecordingModule;
public InitialPropsTestCase() {
super(ReactAppTestActivity.class);
}
@Override
protected void setUp() throws Exception {
super.setUp();
mRecordingModule = new RecordingModule();
}
public void testInitialProps() throws Throwable {
final ReactAppTestActivity activity = getActivity();
runTestOnUiThread(
new Runnable() {
@Override
public void run() {
ReactInstanceSpecForTest catalystInstanceSpec = new ReactInstanceSpecForTest();
catalystInstanceSpec.addNativeModule(new FakeWebSocketModule());
catalystInstanceSpec.addNativeModule(mRecordingModule);
Bundle props = new Bundle();
props.putString("key1", "string");
props.putInt("key2", 5);
props.putDouble("key3", 5.5);
props.putFloat("key4", 5.6f);
props.putBoolean("key5", true);
props.putStringArray("key6", new String[]{"one", "two", "three"});
props.putIntArray("key7", new int[]{1, 2, 3});
props.putDoubleArray("key8", new double[]{1.5, 2.5, 3.5});
props.putFloatArray("key9", new float[]{1.6f, 2.6f, 3.6f});
props.putBooleanArray("key10", new boolean[]{true, false});
activity.loadApp(
"InitialPropsTestApp",
catalystInstanceSpec,
props,
DEFAULT_JS_BUNDLE,
false);
}
});
activity.waitForBridgeAndUIIdle();
assertEquals(1, mRecordingModule.getCount());
ReadableMap props = mRecordingModule.getProps();
assertEquals("string", props.getString("key1"));
assertEquals(5, props.getInt("key2"));
assertEquals(5.5, props.getDouble("key3"));
assertEquals(5.6f, (float) props.getDouble("key4"));
assertEquals(true, props.getBoolean("key5"));
ReadableArray stringArray = props.getArray("key6");
assertEquals("one", stringArray.getString(0));
assertEquals("two", stringArray.getString(1));
assertEquals("three", stringArray.getString(2));
ReadableArray intArray = props.getArray("key7");
assertEquals(1, intArray.getInt(0));
assertEquals(2, intArray.getInt(1));
assertEquals(3, intArray.getInt(2));
ReadableArray doubleArray = props.getArray("key8");
assertEquals(1.5, doubleArray.getDouble(0));
assertEquals(2.5, doubleArray.getDouble(1));
assertEquals(3.5, doubleArray.getDouble(2));
ReadableArray floatArray = props.getArray("key9");
assertEquals(1.6f, (float) floatArray.getDouble(0));
assertEquals(2.6f, (float) floatArray.getDouble(1));
assertEquals(3.6f, (float) floatArray.getDouble(2));
ReadableArray booleanArray = props.getArray("key10");
assertEquals(true, booleanArray.getBoolean(0));
assertEquals(false, booleanArray.getBoolean(1));
}
}

View File

@@ -0,0 +1,107 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.tests;
import java.util.Arrays;
import java.util.List;
import com.facebook.react.testing.FakeWebSocketModule;
import com.facebook.react.testing.ReactIntegrationTestCase;
import com.facebook.react.testing.ReactTestHelper;
import com.facebook.react.testing.StringRecordingModule;
import com.facebook.react.bridge.CatalystInstance;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.bridge.UiThreadUtil;
import com.facebook.react.modules.appstate.AppStateModule;
import com.facebook.react.modules.deviceinfo.DeviceInfoModule;
import com.facebook.react.uimanager.UIImplementationProvider;
import com.facebook.react.uimanager.UIManagerModule;
import com.facebook.react.uimanager.ViewManager;
import com.facebook.react.views.view.ReactViewManager;
/**
* Test locale-based functionality of JS VM
*/
public class JSLocaleTest extends ReactIntegrationTestCase {
private interface TestJSLocaleModule extends JavaScriptModule {
void toUpper(String string);
void toLower(String string);
}
StringRecordingModule mStringRecordingModule;
private CatalystInstance mInstance;
@Override
protected void setUp() throws Exception {
super.setUp();
List<ViewManager> viewManagers = Arrays.<ViewManager>asList(
new ReactViewManager());
final UIManagerModule mUIManager = new UIManagerModule(
getContext(),
viewManagers,
new UIImplementationProvider(),
0);
UiThreadUtil.runOnUiThread(
new Runnable() {
@Override
public void run() {
mUIManager.onHostResume();
}
});
waitForIdleSync();
mStringRecordingModule = new StringRecordingModule();
mInstance = ReactTestHelper.catalystInstanceBuilder(this)
.addNativeModule(mStringRecordingModule)
.addNativeModule(mUIManager)
.addNativeModule(new DeviceInfoModule(getContext()))
.addNativeModule(new AppStateModule(getContext()))
.addNativeModule(new FakeWebSocketModule())
.build();
}
public void testToUpper() {
TestJSLocaleModule testModule = mInstance.getJSModule(TestJSLocaleModule.class);
waitForBridgeAndUIIdle();
testModule.toUpper("test");
testModule.toUpper("W niżach mógł zjeść truflę koń bądź psy");
testModule.toUpper("Шеф взъярён тчк щипцы с эхом гудбай Жюль");
testModule.toUpper("Γαζίες καὶ μυρτιὲς δὲν θὰ βρῶ πιὰ στὸ χρυσαφὶ ξέφωτο");
testModule.toUpper("chinese: 幓 厏吪吙 鈊釿閍 碞碠粻 曮禷");
waitForBridgeAndUIIdle();
String[] answers = mStringRecordingModule.getCalls().toArray(new String[0]);
assertEquals("TEST", answers[0]);
assertEquals("W NIŻACH MÓGŁ ZJEŚĆ TRUFLĘ KOŃ BĄDŹ PSY", answers[1]);
assertEquals("ШЕФ ВЗЪЯРЁН ТЧК ЩИПЦЫ С ЭХОМ ГУДБАЙ ЖЮЛЬ", answers[2]);
assertEquals("ΓΑΖΊΕΣ ΚΑῚ ΜΥΡΤΙῈΣ ΔῈΝ ΘᾺ ΒΡΩ͂ ΠΙᾺ ΣΤῸ ΧΡΥΣΑΦῚ ΞΈΦΩΤΟ", answers[3]);
assertEquals("CHINESE: 幓 厏吪吙 鈊釿閍 碞碠粻 曮禷", answers[4]);
}
public void testToLower() {
TestJSLocaleModule testModule = mInstance.getJSModule(TestJSLocaleModule.class);
testModule.toLower("TEST");
testModule.toLower("W NIŻACH MÓGŁ ZJEŚĆ TRUFLĘ KOŃ BĄDŹ psy");
testModule.toLower("ШЕФ ВЗЪЯРЁН ТЧК ЩИПЦЫ С ЭХОМ ГУДБАЙ ЖЮЛЬ");
testModule.toLower("ΓΑΖΊΕΣ ΚΑῚ ΜΥΡΤΙῈΣ ΔῈΝ ΘᾺ ΒΡΩ͂ ΠΙᾺ ΣΤῸ ΧΡΥΣΑΦῚ ΞΈΦΩΤΟ");
testModule.toLower("CHINESE: 幓 厏吪吙 鈊釿閍 碞碠粻 曮禷");
waitForBridgeAndUIIdle();
String[] answers = mStringRecordingModule.getCalls().toArray(new String[0]);
assertEquals("test", answers[0]);
assertEquals("w niżach mógł zjeść truflę koń bądź psy", answers[1]);
assertEquals("шеф взъярён тчк щипцы с эхом гудбай жюль", answers[2]);
assertEquals("γαζίες καὶ μυρτιὲς δὲν θὰ βρῶ πιὰ στὸ χρυσαφὶ ξέφωτο", answers[3]);
assertEquals("chinese: 幓 厏吪吙 鈊釿閍 碞碠粻 曮禷", answers[4]);
}
}

View File

@@ -0,0 +1,67 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.tests;
import android.widget.ScrollView;
import com.facebook.react.uimanager.PixelUtil;
import com.facebook.react.testing.ReactAppInstrumentationTestCase;
import com.facebook.react.testing.SingleTouchGestureGenerator;
/**
* Test case to verify that JSResponder flow work correctly.
*
* In a single test case scenario we have a view with pan gesture recognizer containing a scrollview
* We verify that by vertical drags affects a scrollview while horizontal drags are suppose to
* be recognized by pan responder and setJSResponder should be triggered resulting in scrollview
* events being intercepted.
*/
public class JSResponderTestCase extends ReactAppInstrumentationTestCase {
@Override
protected String getReactApplicationKeyUnderTest() {
return "JSResponderTestApp";
}
public void testResponderLocksScrollView() {
ScrollView scrollView = getViewByTestId("scroll_view");
assertNotNull(scrollView);
assertEquals(0, scrollView.getScrollY());
float inpx40dp = PixelUtil.toPixelFromDIP(40f);
float inpx100dp = PixelUtil.toPixelFromDIP(100f);
SingleTouchGestureGenerator gestureGenerator = createGestureGenerator();
gestureGenerator
.startGesture(30, 30 + inpx100dp)
.dragTo(30 + inpx40dp, 30, 10, 1200)
.endGesture(180, 100);
waitForBridgeAndUIIdle();
assertTrue("Expected to scroll by at least 80 dp", scrollView.getScrollY() >= inpx100dp * .8f);
int previousScroll = scrollView.getScrollY();
gestureGenerator
.startGesture(30, 30 + inpx100dp)
.dragTo(30 + inpx40dp, 30 + inpx100dp, 10, 1200);
waitForBridgeAndUIIdle();
gestureGenerator
.dragTo(30 + inpx40dp, 30, 10, 1200)
.endGesture();
waitForBridgeAndUIIdle();
assertEquals("Expected not to scroll", scrollView.getScrollY(), previousScroll);
}
}

View File

@@ -0,0 +1,42 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.tests;
import com.facebook.react.testing.ReactAppInstrumentationTestCase;
import com.facebook.react.testing.ReactInstanceSpecForTest;
import com.facebook.react.testing.StringRecordingModule;
/**
* Simple test to verify that layout events (onLayout) propagate to JS from native.
*/
public class LayoutEventsTestCase extends ReactAppInstrumentationTestCase {
private StringRecordingModule mStringRecordingModule;
@Override
protected String getReactApplicationKeyUnderTest() {
return "LayoutEventsTestApp";
}
/**
* Creates a UI in JS and verifies the onLayout handler is called.
*/
public void testOnLayoutCalled() {
assertEquals(3, mStringRecordingModule.getCalls().size());
assertEquals("10,10-100x100", mStringRecordingModule.getCalls().get(0));
assertEquals("10,10-50x50", mStringRecordingModule.getCalls().get(1));
assertEquals("0,0-50x50", mStringRecordingModule.getCalls().get(2));
}
@Override
protected ReactInstanceSpecForTest createReactInstanceSpecForTest() {
mStringRecordingModule = new StringRecordingModule();
return super.createReactInstanceSpecForTest()
.addNativeModule(mStringRecordingModule);
}
}

View File

@@ -0,0 +1,92 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.tests;
import java.util.Arrays;
import java.util.List;
import android.view.View;
import com.facebook.react.testing.ReactAppInstrumentationTestCase;
import com.facebook.react.uimanager.util.ReactFindViewUtil;
/**
* Tests that the 'nativeID' property can be set on various views.
* The 'nativeID' property is used to reference react managed views from native code.
*/
public class NativeIdTestCase extends ReactAppInstrumentationTestCase {
@Override
protected String getReactApplicationKeyUnderTest() {
return "NativeIdTestApp";
}
private final List<String> viewTags = Arrays.asList(
"Image",
"Text",
"TouchableBounce",
"TouchableHighlight",
"TouchableOpacity",
"TouchableWithoutFeedback",
"TextInput",
"View");
private boolean mViewFound;
@Override
protected void setUp() throws Exception {
mViewFound = false;
ReactFindViewUtil.addViewListener(new ReactFindViewUtil.OnViewFoundListener() {
@Override
public String getNativeId() {
return viewTags.get(0);
}
@Override
public void onViewFound(View view) {
mViewFound = true;
}
});
super.setUp();
}
public void testPropertyIsSetForViews() {
for (String nativeId : viewTags) {
View viewWithTag = ReactFindViewUtil.findView(
getActivity().getRootView(),
nativeId);
assertNotNull(
"View with nativeID " + nativeId + " was not found. Check NativeIdTestModule.js.",
viewWithTag);
}
}
public void testViewListener() {
assertTrue("OnViewFound callback was never invoked", mViewFound);
}
public void testFindView() {
mViewFound = false;
ReactFindViewUtil.findView(
getActivity().getRootView(),
new ReactFindViewUtil.OnViewFoundListener() {
@Override
public String getNativeId() {
return viewTags.get(0);
}
@Override
public void onViewFound(View view) {
mViewFound = true;
}
});
assertTrue(
"OnViewFound callback should have successfully been invoked synchronously",
mViewFound);
}
}

View File

@@ -0,0 +1,130 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.tests;
import android.content.res.Resources;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ProgressBar;
import com.facebook.react.ReactRootView;
import com.facebook.react.bridge.CatalystInstance;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.bridge.UiThreadUtil;
import com.facebook.react.modules.appstate.AppStateModule;
import com.facebook.react.modules.deviceinfo.DeviceInfoModule;
import com.facebook.react.modules.systeminfo.AndroidInfoModule;
import com.facebook.react.testing.FakeWebSocketModule;
import com.facebook.react.testing.ReactIntegrationTestCase;
import com.facebook.react.testing.ReactTestHelper;
import com.facebook.react.uimanager.UIImplementationProvider;
import com.facebook.react.uimanager.UIManagerModule;
import com.facebook.react.uimanager.ViewManager;
import com.facebook.react.views.progressbar.ReactProgressBarViewManager;
import com.facebook.react.views.view.ReactViewManager;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
/**
* Test to verify that Progress bar renders as a view of the right size
*/
public class ProgressBarTestCase extends ReactIntegrationTestCase {
// Has same order of progressBars in ProgressBarTestModule
private static final String[] styleList =
new String[] {"Horizontal", "Small", "Large", "Inverse", "SmallInverse", "LargeInverse"};
private static final HashMap<String, Integer> styles = new HashMap<String, Integer>();
static {
styles.put("Horizontal", android.R.attr.progressBarStyleHorizontal);
styles.put("Small", android.R.attr.progressBarStyleSmall);
styles.put("Large", android.R.attr.progressBarStyleLarge);
styles.put("Inverse", android.R.attr.progressBarStyleInverse);
styles.put("SmallInverse", android.R.attr.progressBarStyleSmallInverse);
styles.put("LargeInverse", android.R.attr.progressBarStyleLargeInverse);
}
private static interface ProgressBarTestModule extends JavaScriptModule {
public void renderProgressBarApplication(int rootTag);
}
private UIManagerModule mUIManager;
private CatalystInstance mInstance;
private ReactRootView mRootView;
@Override
protected void setUp() throws Exception {
super.setUp();
List<ViewManager> viewManagers = Arrays.<ViewManager>asList(
new ReactViewManager(),
new ReactProgressBarViewManager());
mUIManager =
new UIManagerModule(getContext(), viewManagers, new UIImplementationProvider(), 0);
UiThreadUtil.runOnUiThread(
new Runnable() {
@Override
public void run() {
mUIManager.onHostResume();
}
});
waitForIdleSync();
mInstance = ReactTestHelper.catalystInstanceBuilder(this)
.addNativeModule(mUIManager)
.addNativeModule(new AndroidInfoModule(getContext()))
.addNativeModule(new DeviceInfoModule(getContext()))
.addNativeModule(new AppStateModule(getContext()))
.addNativeModule(new FakeWebSocketModule())
.build();
mRootView = new ReactRootView(getContext());
DisplayMetrics metrics = getContext().getResources().getDisplayMetrics();
mRootView.setLayoutParams(
new FrameLayout.LayoutParams(metrics.widthPixels, metrics.heightPixels));
int rootTag = mUIManager.addRootView(mRootView);
mInstance.getJSModule(ProgressBarTestModule.class).renderProgressBarApplication(rootTag);
waitForBridgeAndUIIdle();
}
/**
* Test that the sizes of the progressBars are setup correctly
*/
public void testProgressBarSizes() {
for (String style : styleList) {
ProgressBar newProgressBar =
new ProgressBar(getContext(), null, styles.get(style));
final int spec = View.MeasureSpec.makeMeasureSpec(
ViewGroup.LayoutParams.WRAP_CONTENT,
View.MeasureSpec.UNSPECIFIED);
newProgressBar.measure(spec, spec);
final int expectedHeight = newProgressBar.getMeasuredHeight();
// verify correct size of view containing ProgressBar
View viewContainingProgressBar = getViewByTestId(mRootView, style);
assertEquals(expectedHeight, viewContainingProgressBar.getHeight());
assertTrue(((ViewGroup) viewContainingProgressBar).getChildAt(0) instanceof ProgressBar);
}
}
public void testProgressBarWidth() {
View viewContainingProgressBar = getViewByTestId(mRootView, "Horizontal200");
assertEquals(viewContainingProgressBar.getWidth(), dpToPixels(200));
ProgressBar progressBar = (ProgressBar) ((ViewGroup) viewContainingProgressBar).getChildAt(0);
assertEquals(progressBar.getWidth(), dpToPixels(200));
}
private int dpToPixels(int dp) {
Resources r = getContext().getResources();
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, r.getDisplayMetrics());
}
}

View File

@@ -0,0 +1,133 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.tests;
import java.util.ArrayList;
import android.view.View;
import android.widget.HorizontalScrollView;
import com.facebook.react.testing.AbstractScrollViewTestCase;
import com.facebook.react.testing.SingleTouchGestureGenerator;
import com.facebook.react.uimanager.PixelUtil;
/**
* Integration test for horizontal ScrollView.
* See ScrollViewTestModule.js
*/
public class ReactHorizontalScrollViewTestCase extends AbstractScrollViewTestCase {
@Override
protected String getReactApplicationKeyUnderTest() {
return "HorizontalScrollViewTestApp";
}
private void dragLeft() {
dragLeft(200);
}
private void dragLeft(int durationMs) {
createGestureGenerator()
.startGesture(150, 50)
.dragTo(50, 60, 10, durationMs)
.endGesture(50, 60);
}
public void testScrolling() {
HorizontalScrollView scrollView = getViewAtPath(0);
assertNotNull(scrollView);
assertEquals(0, scrollView.getScrollX());
dragLeft();
assertTrue("Expected to scroll by at least 50 pixels", scrollView.getScrollX() >= 50);
}
public void testScrollEvents() {
HorizontalScrollView scrollView = getViewAtPath(0);
dragLeft();
waitForBridgeAndUIIdle();
mScrollListenerModule.waitForScrollIdle();
waitForBridgeAndUIIdle();
ArrayList<Double> xOffsets = mScrollListenerModule.getXOffsets();
assertFalse("Expected to receive at least one scroll event", xOffsets.isEmpty());
assertTrue("Expected offset to be greater than 0", xOffsets.get(xOffsets.size() - 1) > 0);
assertTrue(
"Expected no item click event fired",
mScrollListenerModule.getItemsPressed().isEmpty());
assertEquals(
"Expected last offset to be offset of scroll view",
PixelUtil.toDIPFromPixel(scrollView.getScrollX()),
xOffsets.get(xOffsets.size() - 1).doubleValue(),
1e-5);
}
public void testScrollAndClick() throws Exception {
SingleTouchGestureGenerator gestureGenerator = createGestureGenerator();
// Slowly drag the ScrollView to prevent fling
dragLeft(15000);
waitForBridgeAndUIIdle();
getInstrumentation().waitForIdleSync();
// Find visible item to be clicked
View visibleItem = null;
int visibleItemNumber = 0;
for (; visibleItemNumber < 100; visibleItemNumber++) {
visibleItem = getViewAtPath(0, 0, visibleItemNumber);
int pos[] = new int[2];
visibleItem.getLocationInWindow(pos);
if (pos[0] >= 0) {
break;
}
}
// Click first visible item
gestureGenerator.startGesture(visibleItem).endGesture();
waitForBridgeAndUIIdle();
ArrayList<Double> xOffsets = mScrollListenerModule.getXOffsets();
ArrayList<Integer> itemIds = mScrollListenerModule.getItemsPressed();
assertFalse("Expected to receive at least one scroll event", xOffsets.isEmpty());
assertTrue("Expected offset to be greater than 0", xOffsets.get(xOffsets.size() - 1) > 0);
assertEquals("Expected to receive exactly one item click event", 1, itemIds.size());
assertEquals(visibleItemNumber, (int) itemIds.get(0));
}
/**
* Verify that 'scrollTo' command makes ScrollView start scrolling
*/
public void testScrollToCommand() throws Exception {
HorizontalScrollView scrollView = getViewAtPath(0);
ScrollViewTestModule jsModule =
getReactContext().getCatalystInstance().getJSModule(ScrollViewTestModule.class);
assertEquals(0, scrollView.getScrollX());
jsModule.scrollTo(300, 0);
waitForBridgeAndUIIdle();
getInstrumentation().waitForIdleSync();
// Unfortunately we need to use timeouts here in order to wait for scroll animation to happen
// there is no better way (yet) for waiting for scroll animation to finish
long timeout = 10000;
long interval = 50;
long start = System.currentTimeMillis();
while (System.currentTimeMillis() - start < timeout) {
if (scrollView.getScrollX() > 0) {
break;
}
Thread.sleep(interval);
}
assertNotSame(0, scrollView.getScrollX());
}
}

View File

@@ -0,0 +1,206 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.tests;
import java.util.ArrayList;
import java.util.List;
import android.graphics.Color;
import android.widget.Spinner;
import android.widget.SpinnerAdapter;
import android.widget.TextView;
import com.facebook.react.bridge.BaseJavaModule;
import com.facebook.react.testing.ReactInstanceSpecForTest;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.views.picker.ReactDialogPickerManager;
import com.facebook.react.views.picker.ReactDropdownPickerManager;
import com.facebook.react.views.picker.ReactPicker;
import com.facebook.react.views.picker.ReactPickerManager;
import com.facebook.react.testing.ReactAppInstrumentationTestCase;
/**
* Integration test for {@link ReactDialogPickerManager} and {@link ReactDropdownPickerManager}
* (and, implicitly, {@link ReactPickerManager}). Tests basic properties, events and switching
* between spinner modes (which changes the used manager).
*/
public class ReactPickerTestCase extends ReactAppInstrumentationTestCase {
private static interface PickerAndroidTestModule extends JavaScriptModule {
public void selectItem(int position);
public void setMode(String mode);
public void setPrimaryColor(String color);
}
public static class PickerAndroidRecordingModule extends BaseJavaModule {
private final List<Integer> mSelections = new ArrayList<Integer>();
@Override
public String getName() {
return "PickerAndroidRecordingModule";
}
@ReactMethod
public void recordSelection(int position) {
mSelections.add(position);
}
public List<Integer> getSelections() {
return new ArrayList<Integer>(mSelections);
}
}
private PickerAndroidRecordingModule mRecordingModule;
@Override
protected String getReactApplicationKeyUnderTest() {
return "PickerAndroidTestApp";
}
@Override
protected ReactInstanceSpecForTest createReactInstanceSpecForTest() {
mRecordingModule = new PickerAndroidRecordingModule();
return super.createReactInstanceSpecForTest()
.addNativeModule(mRecordingModule);
}
public void testBasicProperties() {
ReactPicker spinner = getViewAtPath(0, 0);
SpinnerAdapter adapter = spinner.getAdapter();
assertEquals(Spinner.MODE_DIALOG, spinner.getMode());
assertEquals("prompt", spinner.getPrompt());
assertNotNull(adapter);
assertEquals(3, adapter.getCount());
assertEquals("item1", ((TextView) adapter.getView(0, null, null)).getText());
assertEquals("item2", ((TextView) adapter.getView(1, null, null)).getText());
assertEquals("item3", ((TextView) adapter.getView(2, null, null)).getText());
assertEquals(1, spinner.getSelectedItemPosition());
// test colors
assertEquals(Color.RED, ((TextView) adapter.getView(0, null, null)).getCurrentTextColor());
assertEquals(Color.GREEN, ((TextView) adapter.getView(1, null, null)).getCurrentTextColor());
assertEquals(Color.BLUE, ((TextView) adapter.getView(2, null, null)).getCurrentTextColor());
assertEquals(
Color.RED,
((TextView) adapter.getDropDownView(0, null, null)).getCurrentTextColor());
assertEquals(
Color.GREEN,
((TextView) adapter.getDropDownView(1, null, null)).getCurrentTextColor());
assertEquals(
Color.BLUE,
((TextView) adapter.getDropDownView(2, null, null)).getCurrentTextColor());
getTestModule().setPrimaryColor("black");
waitForBridgeAndUIIdle();
assertEquals(Color.BLACK, ((TextView) adapter.getView(0, null, null)).getCurrentTextColor());
assertEquals(Color.BLACK, ((TextView) adapter.getView(1, null, null)).getCurrentTextColor());
assertEquals(Color.BLACK, ((TextView) adapter.getView(2, null, null)).getCurrentTextColor());
assertEquals(
Color.RED,
((TextView) adapter.getDropDownView(0, null, null)).getCurrentTextColor());
assertEquals(
Color.GREEN,
((TextView) adapter.getDropDownView(1, null, null)).getCurrentTextColor());
assertEquals(
Color.BLUE,
((TextView) adapter.getDropDownView(2, null, null)).getCurrentTextColor());
}
public void testDropdownPicker() {
ReactPicker spinner = getViewAtPath(0, 1);
assertEquals(Spinner.MODE_DROPDOWN, spinner.getMode());
}
public void testDisabledPicker() {
ReactPicker spinner = getViewAtPath(0, 2);
assertFalse(spinner.isEnabled());
}
public void testUpdateSelectedItem() {
ReactPicker spinner = getViewAtPath(0, 0);
assertEquals(1, spinner.getSelectedItemPosition());
getTestModule().selectItem(2);
waitForBridgeAndUIIdle();
getInstrumentation().waitForIdleSync();
assertEquals(2, spinner.getSelectedItemPosition());
}
public void testUpdateMode() {
ReactPicker spinner = getViewAtPath(0, 1);
assertEquals(Spinner.MODE_DROPDOWN, spinner.getMode());
getTestModule().setMode("dialog");
waitForBridgeAndUIIdle();
getInstrumentation().waitForIdleSync();
// changing the spinner mode in JS actually creates a new component on the native side, as
// there's no way to change the mode once you have constructed a Spinner.
ReactPicker newPicker = getViewAtPath(0, 1);
assertTrue(spinner != newPicker);
assertEquals(Spinner.MODE_DIALOG, newPicker.getMode());
}
public void testOnSelect() throws Throwable {
runTestOnUiThread(
new Runnable() {
@Override
public void run() {
ReactPicker spinner = getViewAtPath(0, 0);
spinner.setSelection(2);
}
});
getInstrumentation().waitForIdleSync();
waitForBridgeAndUIIdle();
List<Integer> selections = mRecordingModule.getSelections();
assertEquals(1, selections.size());
assertEquals(2, (int) selections.get(0));
}
public void testOnSelectSequence() throws Throwable {
updateFirstSpinnerAndCheckLastSpinnerMatches(0);
updateFirstSpinnerAndCheckLastSpinnerMatches(2);
updateFirstSpinnerAndCheckLastSpinnerMatches(0);
updateFirstSpinnerAndCheckLastSpinnerMatches(2);
}
private void updateFirstSpinnerAndCheckLastSpinnerMatches(
final int indexToSelect
) throws Throwable {
// The last spinner has the same selected value as the first one.
// Test that user selection is propagated correctly to JS, to setState, and to Spinners.
runTestOnUiThread(
new Runnable() {
@Override
public void run() {
ReactPicker spinner = getViewAtPath(0, 0);
spinner.setSelection(indexToSelect);
}
});
getInstrumentation().waitForIdleSync();
waitForBridgeAndUIIdle();
ReactPicker spinnerInSync = getViewAtPath(0, 3);
assertEquals(
"Picker selection was not updated correctly via setState.",
indexToSelect,
spinnerInSync.getSelectedItemPosition());
}
private PickerAndroidTestModule getTestModule() {
return getReactContext().getCatalystInstance().getJSModule(PickerAndroidTestModule.class);
}
}

View File

@@ -0,0 +1,105 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.tests;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import com.facebook.react.testing.ReactInstanceSpecForTest;
import com.facebook.react.testing.ReactAppInstrumentationTestCase;
import com.facebook.react.testing.StringRecordingModule;
import com.facebook.react.ReactRootView;
import org.junit.Ignore;
/**
* Integration test for {@link ReactRootView}.
*/
public class ReactRootViewTestCase extends ReactAppInstrumentationTestCase {
private StringRecordingModule mRecordingModule;
@Override
protected String getReactApplicationKeyUnderTest() {
return "CatalystRootViewTestApp";
}
@Ignore("t6596940: fix intermittently failing test")
public void testResizeRootView() throws Throwable {
final ReactRootView rootView = (ReactRootView) getRootView();
final View childView = rootView.getChildAt(0);
assertEquals(rootView.getWidth(), childView.getWidth());
final int newWidth = rootView.getWidth() / 2;
runTestOnUiThread(
new Runnable() {
@Override
public void run() {
rootView.setLayoutParams(new FrameLayout.LayoutParams(
newWidth,
ViewGroup.LayoutParams.MATCH_PARENT));
}
});
getInstrumentation().waitForIdleSync();
waitForBridgeAndUIIdle();
assertEquals(newWidth, childView.getWidth());
}
/**
* Verify that removing the root view from hierarchy will trigger subviews removal both on JS and
* native side
*/
public void testRemoveRootView() throws Throwable {
final ReactRootView rootView = (ReactRootView) getRootView();
assertEquals(1, rootView.getChildCount());
runTestOnUiThread(
new Runnable() {
@Override
public void run() {
ViewGroup parent = (ViewGroup) rootView.getParent();
parent.removeView(rootView);
// removing from parent should not remove child views, child views should be removed as
// an effect of native call to UIManager.removeRootView
assertEquals(1, rootView.getChildCount());
}
});
getInstrumentation().waitForIdleSync();
waitForBridgeAndUIIdle();
assertEquals("root component should not be automatically unmounted", 0, mRecordingModule.getCalls().size());
assertEquals(1, rootView.getChildCount());
runTestOnUiThread(
new Runnable() {
@Override
public void run() {
rootView.unmountReactApplication();
}
});
waitForBridgeAndUIIdle();
assertEquals(1, mRecordingModule.getCalls().size());
assertEquals("RootComponentWillUnmount", mRecordingModule.getCalls().get(0));
assertEquals(0, rootView.getChildCount());
}
@Override
protected ReactInstanceSpecForTest createReactInstanceSpecForTest() {
mRecordingModule = new StringRecordingModule();
return new ReactInstanceSpecForTest()
.addNativeModule(mRecordingModule);
}
}

View File

@@ -0,0 +1,135 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.tests;
import java.util.ArrayList;
import android.view.View;
import android.widget.ScrollView;
import com.facebook.react.testing.AbstractScrollViewTestCase;
import com.facebook.react.testing.SingleTouchGestureGenerator;
import com.facebook.react.uimanager.PixelUtil;
/**
* Integration test for vertical ScrollView.
* See ScrollViewTestModule.js
*/
public class ReactScrollViewTestCase extends AbstractScrollViewTestCase {
@Override
protected String getReactApplicationKeyUnderTest() {
return "ScrollViewTestApp";
}
private void dragUp() {
dragUp(200);
}
private void dragUp(int durationMs) {
createGestureGenerator()
.startGesture(200, 200)
.dragTo(180, 100, 10, durationMs)
.endGesture(180, 100);
}
public void testScrolling() {
ScrollView scrollView = getViewAtPath(0);
assertNotNull(scrollView);
assertEquals(0, scrollView.getScrollY());
dragUp();
assertTrue("Expected to scroll by at least 50 pixels", scrollView.getScrollY() >= 50);
}
public void testScrollEvents() {
ScrollView scrollView = getViewAtPath(0);
dragUp();
waitForBridgeAndUIIdle();
mScrollListenerModule.waitForScrollIdle();
waitForBridgeAndUIIdle();
ArrayList<Double> yOffsets = mScrollListenerModule.getYOffsets();
assertFalse("Expected to receive at least one scroll event", yOffsets.isEmpty());
assertTrue("Expected offset to be greater than 0", yOffsets.get(yOffsets.size() - 1) > 0);
assertTrue(
"Expected no item click event fired",
mScrollListenerModule.getItemsPressed().isEmpty());
assertEquals(
"Expected last offset to be offset of scroll view",
PixelUtil.toDIPFromPixel(scrollView.getScrollY()),
yOffsets.get(yOffsets.size() - 1).doubleValue(),
1e-5);
assertTrue("Begin and End Drag should be called", mScrollListenerModule.dragEventsMatch());
}
public void testScrollAndClick() throws Exception {
SingleTouchGestureGenerator gestureGenerator = createGestureGenerator();
// Slowly drag the ScrollView to prevent fling
dragUp(15000);
waitForBridgeAndUIIdle();
getInstrumentation().waitForIdleSync();
// Find visible item to be clicked
View visibleItem = null;
int visibleItemNumber = 0;
for (; visibleItemNumber < 100; visibleItemNumber++) {
visibleItem = getViewAtPath(0, 0, visibleItemNumber);
int pos[] = new int[2];
visibleItem.getLocationInWindow(pos);
if (pos[1] >= 0) {
break;
}
}
// Click first visible item
gestureGenerator.startGesture(visibleItem).endGesture();
waitForBridgeAndUIIdle();
ArrayList<Double> yOffsets = mScrollListenerModule.getYOffsets();
ArrayList<Integer> itemIds = mScrollListenerModule.getItemsPressed();
assertFalse("Expected to receive at least one scroll event", yOffsets.isEmpty());
assertTrue("Expected offset to be greater than 0", yOffsets.get(yOffsets.size() - 1) > 0);
assertEquals("Expected to receive exactly one item click event", 1, itemIds.size());
assertEquals(visibleItemNumber, (int) itemIds.get(0));
}
/**
* Verify that 'scrollTo' command makes ScrollView start scrolling
*/
public void testScrollToCommand() throws Exception {
ScrollView scrollView = getViewAtPath(0);
ScrollViewTestModule jsModule =
getReactContext().getCatalystInstance().getJSModule(ScrollViewTestModule.class);
assertEquals(0, scrollView.getScrollY());
jsModule.scrollTo(0, 300);
waitForBridgeAndUIIdle();
getInstrumentation().waitForIdleSync();
// Unfortunately we need to use timeouts here in order to wait for scroll animation to happen
// there is no better way (yet) for waiting for scroll animation to finish
long timeout = 10000;
long interval = 50;
long start = System.currentTimeMillis();
while (System.currentTimeMillis() - start < timeout) {
if (scrollView.getScrollY() > 0) {
break;
}
Thread.sleep(interval);
}
assertNotSame(0, scrollView.getScrollY());
assertFalse("Drag should not be called with scrollTo", mScrollListenerModule.dragEventsMatch());
}
}

View File

@@ -0,0 +1,102 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.tests;
import android.view.View;
import com.facebook.react.testing.ReactAppInstrumentationTestCase;
import com.facebook.react.testing.ReactInstanceSpecForTest;
import com.facebook.react.bridge.BaseJavaModule;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.views.swiperefresh.ReactSwipeRefreshLayout;
/**
* Test case for {@link ReactSwipeRefreshLayout}.
*/
public class ReactSwipeRefreshLayoutTestCase extends ReactAppInstrumentationTestCase {
private class SwipeRefreshLayoutRecordingModule extends BaseJavaModule {
private int mCount = 0;
@Override
public String getName() {
return "SwipeRefreshLayoutRecordingModule";
}
@ReactMethod
public void onRefresh() {
mCount++;
}
public int getCount() {
return mCount;
}
}
private interface SwipeRefreshLayoutTestModule extends JavaScriptModule {
void setRows(int rows);
}
private final SwipeRefreshLayoutRecordingModule mRecordingModule =
new SwipeRefreshLayoutRecordingModule();
@Override
protected String getReactApplicationKeyUnderTest() {
return "SwipeRefreshLayoutTestApp";
}
@Override
protected ReactInstanceSpecForTest createReactInstanceSpecForTest() {
return super.createReactInstanceSpecForTest()
.addNativeModule(mRecordingModule);
}
public void testRefreshNoScroll() {
View refreshLayout = getViewAtPath(0);
createGestureGenerator()
.startGesture(refreshLayout.getWidth() / 2, 10)
.dragTo(refreshLayout.getWidth() / 2, refreshLayout.getHeight() / 2, 100, 1000)
.endGesture();
waitForBridgeAndUIIdle();
assertEquals(1, mRecordingModule.getCount());
}
public void testRefreshScroll() {
View refreshLayout = getViewAtPath(0);
getReactContext().getJSModule(SwipeRefreshLayoutTestModule.class).setRows(100);
createGestureGenerator()
.startGesture(refreshLayout.getWidth() / 2, 10)
.dragTo(refreshLayout.getWidth() / 2, refreshLayout.getHeight() / 2, 100, 1000)
.endGesture();
waitForBridgeAndUIIdle();
assertEquals(1, mRecordingModule.getCount());
}
public void testNoRefreshAfterScroll() {
View refreshLayout = getViewAtPath(0);
getReactContext().getJSModule(SwipeRefreshLayoutTestModule.class).setRows(100);
createGestureGenerator()
.startGesture(refreshLayout.getWidth() / 2, refreshLayout.getHeight() / 2)
.dragTo(refreshLayout.getWidth() / 2, 10, 100, 1000)
.endGesture();
waitForBridgeAndUIIdle();
createGestureGenerator()
.startGesture(refreshLayout.getWidth() / 2, 10)
.dragTo(refreshLayout.getWidth() / 2, refreshLayout.getHeight() / 2, 100, 1000)
.endGesture();
waitForBridgeAndUIIdle();
assertEquals(0, mRecordingModule.getCount());
}
}

View File

@@ -0,0 +1,107 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.tests;
import java.util.ArrayList;
import java.util.List;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Instrumentation.ActivityMonitor;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentFilter.MalformedMimeTypeException;
import android.support.v4.app.DialogFragment;
import com.facebook.react.bridge.BaseJavaModule;
import com.facebook.react.testing.ReactInstanceSpecForTest;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.WritableNativeMap;
import com.facebook.react.modules.share.ShareModule;
import com.facebook.react.testing.ReactAppInstrumentationTestCase;
/**
* Test case for {@link ShareModule}.
*/
public class ShareTestCase extends ReactAppInstrumentationTestCase {
private static interface ShareTestModule extends JavaScriptModule {
public void showShareDialog(WritableMap content, WritableMap options);
}
private static class ShareRecordingModule extends BaseJavaModule {
private int mOpened = 0;
private int mErrors = 0;
@Override
public String getName() {
return "ShareRecordingModule";
}
@ReactMethod
public void recordOpened() {
mOpened++;
}
@ReactMethod
public void recordError() {
mErrors++;
}
public int getOpened() {
return mOpened;
}
public int getErrors() {
return mErrors;
}
}
final ShareRecordingModule mRecordingModule = new ShareRecordingModule();
@Override
protected ReactInstanceSpecForTest createReactInstanceSpecForTest() {
return super.createReactInstanceSpecForTest()
.addNativeModule(mRecordingModule);
}
@Override
protected String getReactApplicationKeyUnderTest() {
return "ShareTestApp";
}
private ShareTestModule getTestModule() {
return getReactContext().getCatalystInstance().getJSModule(ShareTestModule.class);
}
public void testShowBasicShareDialog() {
final WritableMap content = new WritableNativeMap();
content.putString("message", "Hello, ReactNative!");
final WritableMap options = new WritableNativeMap();
IntentFilter intentFilter = new IntentFilter(Intent.ACTION_CHOOSER);
intentFilter.addCategory(Intent.CATEGORY_DEFAULT);
ActivityMonitor monitor = getInstrumentation().addMonitor(intentFilter, null, true);
getTestModule().showShareDialog(content, options);
waitForBridgeAndUIIdle();
getInstrumentation().waitForIdleSync();
assertEquals(1, monitor.getHits());
assertEquals(1, mRecordingModule.getOpened());
assertEquals(0, mRecordingModule.getErrors());
}
}

View File

@@ -0,0 +1,52 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.tests;
import java.util.Arrays;
import java.util.List;
import android.view.View;
import com.facebook.react.views.picker.ReactDropdownPickerManager;
import com.facebook.react.testing.ReactAppInstrumentationTestCase;
import com.facebook.react.testing.ReactInstanceSpecForTest;
import com.facebook.react.testing.ReactTestHelper;
/**
* Tests that the 'testID' property can be set on various views.
* The 'testID' property is used to locate views in UI tests.
*/
public class TestIdTestCase extends ReactAppInstrumentationTestCase {
@Override
protected String getReactApplicationKeyUnderTest() {
return "TestIdTestApp";
}
private final List<String> viewTags = Arrays.asList(
"Image",
"Text",
"TouchableBounce",
"TouchableHighlight",
"TouchableOpacity",
"TouchableWithoutFeedback",
"TextInput",
"View"
);
public void testPropertyIsSetForViews() {
for (String tag : viewTags) {
View viewWithTag = ReactTestHelper.getViewWithReactTestId(
getActivity().getRootView(),
tag);
assertNotNull(
"View with testID tag " + tag + " was not found. Check TestIdTestModule.js.",
viewWithTag);
}
}
}

View File

@@ -0,0 +1,257 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.tests;
import android.graphics.Color;
import android.text.style.ForegroundColorSpan;
import android.util.TypedValue;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
import android.widget.EditText;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.testing.ReactAppInstrumentationTestCase;
import com.facebook.react.testing.ReactInstanceSpecForTest;
import com.facebook.react.testing.StringRecordingModule;
import com.facebook.react.uimanager.PixelUtil;
import com.facebook.react.views.textinput.ReactEditText;
/**
* Test to verify that TextInput renders correctly
*/
public class TextInputTestCase extends ReactAppInstrumentationTestCase {
private final StringRecordingModule mRecordingModule = new StringRecordingModule();
private interface TextInputTestModule extends JavaScriptModule {
void setValueRef(String ref, String value);
}
/**
* Test that the actual height of the text input is not dependant on the font size of the text
* within.
*/
public void testTextInputMeasurements() {
View textInputViewHeightSet = getViewByTestId("textInput1");
EditText textInputViewNoHeight = getViewByTestId("textInput2");
int expectedHeight = Math.round(PixelUtil.toPixelFromDIP(30));
assertEquals(expectedHeight, textInputViewHeightSet.getHeight());
EditText editText = new EditText(textInputViewNoHeight.getContext());
editText.setTextSize(
TypedValue.COMPLEX_UNIT_PX,
(float) Math.ceil(PixelUtil.toPixelFromSP(21.f)));
editText.setPadding(0, 0, 0, 0);
int measureSpec = View.MeasureSpec.makeMeasureSpec(
ViewGroup.LayoutParams.WRAP_CONTENT,
View.MeasureSpec.UNSPECIFIED);
editText.measure(measureSpec, measureSpec);
assertEquals(editText.getMeasuredHeight(), textInputViewNoHeight.getHeight());
}
/**
* Test that the cursor moves to the end of the word.
*/
public void testTextInputCursorPosition() throws Throwable {
final EditText textInputWithText = getViewByTestId("textInput3");
runTestOnUiThread(
new Runnable() {
@Override
public void run() {
textInputWithText.setSelection(3);
}
});
getReactContext().getJSModule(TextInputTestModule.class)
.setValueRef("textInput3", "Some other value");
waitForBridgeAndUIIdle();
assertEquals(4, textInputWithText.getSelectionStart());
assertEquals(4, textInputWithText.getSelectionEnd());
}
/**
* Test that the colors are applied to new text
*/
public void testTextInputColors() throws Throwable {
String testIDs[] = new String[] {"textInput4", "textInput5", "textInput6"};
for (String testID : testIDs) {
getReactContext().getJSModule(TextInputTestModule.class).setValueRef(testID, "NewText");
}
waitForBridgeAndUIIdle();
for (String testID : testIDs) {
ReactEditText reactEditText = getViewByTestId(testID);
assertEquals(
Color.GREEN,
reactEditText.getText().getSpans(0, 1, ForegroundColorSpan.class)[0]
.getForegroundColor());
}
}
public void testOnSubmitEditing() throws Throwable {
String testId = "onSubmitTextInput";
ReactEditText reactEditText = getViewByTestId(testId);
fireEditorActionAndCheckRecording(reactEditText, EditorInfo.IME_ACTION_GO);
fireEditorActionAndCheckRecording(reactEditText, EditorInfo.IME_ACTION_DONE);
fireEditorActionAndCheckRecording(reactEditText, EditorInfo.IME_ACTION_NEXT);
fireEditorActionAndCheckRecording(reactEditText, EditorInfo.IME_ACTION_PREVIOUS);
fireEditorActionAndCheckRecording(reactEditText, EditorInfo.IME_ACTION_SEARCH);
fireEditorActionAndCheckRecording(reactEditText, EditorInfo.IME_ACTION_SEND);
fireEditorActionAndCheckRecording(reactEditText, EditorInfo.IME_ACTION_UNSPECIFIED);
fireEditorActionAndCheckRecording(reactEditText, EditorInfo.IME_ACTION_NONE);
}
private void fireEditorActionAndCheckRecording(final ReactEditText reactEditText,
final int actionId) throws Throwable {
fireEditorActionAndCheckRecording(reactEditText, actionId, true);
fireEditorActionAndCheckRecording(reactEditText, actionId, false);
}
private void fireEditorActionAndCheckRecording(final ReactEditText reactEditText,
final int actionId,
final boolean blurOnSubmit) throws Throwable {
mRecordingModule.reset();
runTestOnUiThread(
new Runnable() {
@Override
public void run() {
reactEditText.requestFocusFromJS();
reactEditText.setBlurOnSubmit(blurOnSubmit);
reactEditText.onEditorAction(actionId);
}
});
waitForBridgeAndUIIdle();
assertEquals(1, mRecordingModule.getCalls().size());
assertEquals(!blurOnSubmit, reactEditText.isFocused());
}
/**
* Test that the mentions input has colors displayed correctly.
* Removed for being flaky in open source, December 2016
public void testMetionsInputColors() throws Throwable {
EventDispatcher eventDispatcher =
getReactContext().getNativeModule(UIManagerModule.class).getEventDispatcher();
ReactEditText reactEditText = getViewByTestId("tokenizedInput");
String newText = "#Things and more #things";
int contentWidth = reactEditText.getWidth();
int contentHeight = reactEditText.getHeight();
int start = 0;
int count = newText.length();
eventDispatcher.dispatchEvent(
new ReactTextChangedEvent(
reactEditText.getId(),
newText.toString(),
(int) PixelUtil.toDIPFromPixel(contentWidth),
(int) PixelUtil.toDIPFromPixel(contentHeight),
reactEditText.incrementAndGetEventCounter()));
eventDispatcher.dispatchEvent(
new ReactTextInputEvent(
reactEditText.getId(),
newText.toString(),
"",
start,
start + count - 1));
waitForBridgeAndUIIdle();
ForegroundColorSpan[] spans = reactEditText
.getText().getSpans(0, reactEditText.getText().length(), ForegroundColorSpan.class);
assertEquals(2, spans.length);
assertEquals(spans[0].getForegroundColor(), spans[1].getForegroundColor());
assertEquals(0, reactEditText.getText().getSpanStart(spans[1]));
assertEquals(7, reactEditText.getText().getSpanEnd(spans[1]));
assertEquals(newText.length() - 7, reactEditText.getText().getSpanStart(spans[0]));
assertEquals(newText.length(), reactEditText.getText().getSpanEnd(spans[0]));
String moreText = "andsuch ";
String previousText = newText;
newText += moreText;
count = moreText.length();
start = previousText.length();
eventDispatcher.dispatchEvent(
new ReactTextChangedEvent(
reactEditText.getId(),
newText.toString(),
(int) PixelUtil.toDIPFromPixel(contentWidth),
(int) PixelUtil.toDIPFromPixel(contentHeight),
reactEditText.incrementAndGetEventCounter()));
eventDispatcher.dispatchEvent(
new ReactTextInputEvent(
reactEditText.getId(),
moreText,
"",
start,
start + count - 1));
waitForBridgeAndUIIdle();
spans = reactEditText.getText()
.getSpans(0, reactEditText.getText().length(), ForegroundColorSpan.class);
assertEquals(2, spans.length);
assertEquals(spans[0].getForegroundColor(), spans[1].getForegroundColor());
assertEquals(0, reactEditText.getText().getSpanStart(spans[1]));
assertEquals(7, reactEditText.getText().getSpanEnd(spans[1]));
assertEquals(newText.length() - 15, reactEditText.getText().getSpanStart(spans[0]));
assertEquals(newText.length() - 1, reactEditText.getText().getSpanEnd(spans[0]));
moreText = "morethings";
previousText = newText;
newText += moreText;
count = moreText.length();
start = previousText.length();
eventDispatcher.dispatchEvent(
new ReactTextChangedEvent(
reactEditText.getId(),
newText.toString(),
(int) PixelUtil.toDIPFromPixel(contentWidth),
(int) PixelUtil.toDIPFromPixel(contentHeight),
reactEditText.incrementAndGetEventCounter()));
eventDispatcher.dispatchEvent(
new ReactTextInputEvent(
reactEditText.getId(),
moreText,
"",
start,
start + count - 1));
waitForBridgeAndUIIdle();
spans = reactEditText.getText()
.getSpans(0, reactEditText.getText().length(), ForegroundColorSpan.class);
assertEquals(spans[0].getForegroundColor(), spans[1].getForegroundColor());
assertEquals(2, spans.length);
assertEquals(0, reactEditText.getText().getSpanStart(spans[1]));
assertEquals(7, reactEditText.getText().getSpanEnd(spans[1]));
assertEquals(newText.length() - 25, reactEditText.getText().getSpanStart(spans[0]));
assertEquals(newText.length() - 11, reactEditText.getText().getSpanEnd(spans[0]));
}
*/
@Override
protected ReactInstanceSpecForTest createReactInstanceSpecForTest() {
return super.createReactInstanceSpecForTest()
.addNativeModule(mRecordingModule);
}
@Override
protected String getReactApplicationKeyUnderTest() {
return "TextInputTestApp";
}
}

View File

@@ -0,0 +1,159 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.tests;
import java.util.ArrayList;
import java.util.List;
import android.app.TimePickerDialog;
import android.content.DialogInterface;
import android.support.v4.app.DialogFragment;
import com.facebook.react.bridge.BaseJavaModule;
import com.facebook.react.testing.ReactInstanceSpecForTest;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.WritableNativeMap;
import com.facebook.react.modules.timepicker.TimePickerDialogModule;
import com.facebook.react.testing.ReactAppInstrumentationTestCase;
/**
* Test case for {@link TimePickerDialogModule} options and callbacks.
*/
public class TimePickerDialogTestCase extends ReactAppInstrumentationTestCase {
private static interface TimePickerDialogTestModule extends JavaScriptModule {
public void showTimePickerDialog(WritableMap options);
}
private static class TimePickerDialogRecordingModule extends BaseJavaModule {
private final List<Integer[]> mTimes = new ArrayList<Integer[]>();
private int mDismissed = 0;
private int mErrors = 0;
@Override
public String getName() {
return "TimePickerDialogRecordingModule";
}
@ReactMethod
public void recordTime(int hour, int minute) {
mTimes.add(new Integer[] {hour, minute});
}
@ReactMethod
public void recordDismissed() {
mDismissed++;
}
@ReactMethod
public void recordError() {
mErrors++;
}
public List<Integer[]> getTimes() {
return new ArrayList<Integer[]>(mTimes);
}
public int getDismissed() {
return mDismissed;
}
public int getErrors() {
return mErrors;
}
}
final TimePickerDialogRecordingModule mRecordingModule = new TimePickerDialogRecordingModule();
@Override
protected ReactInstanceSpecForTest createReactInstanceSpecForTest() {
return super.createReactInstanceSpecForTest()
.addNativeModule(mRecordingModule);
}
@Override
protected String getReactApplicationKeyUnderTest() {
return "TimePickerDialogTestApp";
}
private TimePickerDialogTestModule getTestModule() {
return getReactContext().getCatalystInstance().getJSModule(TimePickerDialogTestModule.class);
}
private DialogFragment showDialog(WritableMap options) {
getTestModule().showTimePickerDialog(options);
waitForBridgeAndUIIdle();
getInstrumentation().waitForIdleSync();
return (DialogFragment) getActivity().getSupportFragmentManager()
.findFragmentByTag(TimePickerDialogModule.FRAGMENT_TAG);
}
public void testShowBasicTimePicker() {
final DialogFragment fragment = showDialog(null);
assertNotNull(fragment);
}
public void testPresetTimeAndCallback() throws Throwable {
final WritableMap options = new WritableNativeMap();
options.putInt("hour", 4);
options.putInt("minute", 5);
final DialogFragment fragment = showDialog(options);
List<Integer[]> recordedTimes = mRecordingModule.getTimes();
assertEquals(0, recordedTimes.size());
runTestOnUiThread(
new Runnable() {
@Override
public void run() {
((TimePickerDialog) fragment.getDialog())
.getButton(DialogInterface.BUTTON_POSITIVE).performClick();
}
});
getInstrumentation().waitForIdleSync();
waitForBridgeAndUIIdle();
assertEquals(0, mRecordingModule.getErrors());
assertEquals(0, mRecordingModule.getDismissed());
recordedTimes = mRecordingModule.getTimes();
assertEquals(1, recordedTimes.size());
assertEquals(4, (int) recordedTimes.get(0)[0]);
assertEquals(5, (int) recordedTimes.get(0)[1]);
}
public void testDismissCallback() throws Throwable {
final DialogFragment fragment = showDialog(null);
assertEquals(0, mRecordingModule.getDismissed());
runTestOnUiThread(
new Runnable() {
@Override
public void run() {
fragment.getDialog().dismiss();
}
});
getInstrumentation().waitForIdleSync();
waitForBridgeAndUIIdle();
assertEquals(0, mRecordingModule.getErrors());
assertEquals(0, mRecordingModule.getTimes().size());
assertEquals(1, mRecordingModule.getDismissed());
}
}

View File

@@ -0,0 +1,150 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.tests;
import android.graphics.Color;
import android.view.View;
import android.view.ViewGroup;
import com.facebook.react.ReactRootView;
import com.facebook.react.bridge.CatalystInstance;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.bridge.UiThreadUtil;
import com.facebook.react.modules.appstate.AppStateModule;
import com.facebook.react.modules.deviceinfo.DeviceInfoModule;
import com.facebook.react.modules.systeminfo.AndroidInfoModule;
import com.facebook.react.testing.FakeWebSocketModule;
import com.facebook.react.testing.ReactIntegrationTestCase;
import com.facebook.react.testing.ReactTestHelper;
import com.facebook.react.uimanager.PixelUtil;
import com.facebook.react.uimanager.UIImplementationProvider;
import com.facebook.react.uimanager.UIManagerModule;
import com.facebook.react.uimanager.ViewManager;
import com.facebook.react.views.view.ReactViewGroup;
import com.facebook.react.views.view.ReactViewManager;
import java.util.Arrays;
import java.util.List;
public class ViewRenderingTestCase extends ReactIntegrationTestCase {
private interface ViewRenderingTestModule extends JavaScriptModule {
void renderViewApplication(int rootTag);
void renderMarginApplication(int rootTag);
void renderBorderApplication(int rootTag);
void updateMargins();
void renderTransformApplication(int rootTag);
}
private CatalystInstance mCatalystInstance;
private ReactRootView mRootView;
private int mRootTag;
@Override
protected void setUp() throws Exception {
super.setUp();
List<ViewManager> viewManagers = Arrays.<ViewManager>asList(new ReactViewManager());
final UIManagerModule uiManager =
new UIManagerModule(getContext(), viewManagers, new UIImplementationProvider(), 0);
UiThreadUtil.runOnUiThread(
new Runnable() {
@Override
public void run() {
uiManager.onHostResume();
}
});
waitForIdleSync();
mCatalystInstance = ReactTestHelper.catalystInstanceBuilder(this)
.addNativeModule(uiManager)
.addNativeModule(new AndroidInfoModule(getContext()))
.addNativeModule(new DeviceInfoModule(getContext()))
.addNativeModule(new AppStateModule(getContext()))
.addNativeModule(new FakeWebSocketModule())
.build();
mRootView = new ReactRootView(getContext());
mRootTag = uiManager.addRootView(mRootView);
}
public void testViewRenderedWithCorrectProperties() {
float expectedOpacity = 0.75f;
int expectedBackgroundColor = Color.rgb(255, 0, 0);
mCatalystInstance.getJSModule(ViewRenderingTestModule.class).renderViewApplication(mRootTag);
waitForBridgeAndUIIdle();
ReactViewGroup view = getViewAtPath(mRootView);
assertEquals("Incorrect (or not applied) opacity", expectedOpacity, view.getAlpha());
assertEquals(
"Incorrect (or not applied) backgroundColor",
expectedBackgroundColor,
view.getBackgroundColor());
}
public void testMarginsApplied() {
mCatalystInstance.getJSModule(ViewRenderingTestModule.class).renderMarginApplication(mRootTag);
waitForBridgeAndUIIdle();
View view = getViewAtPath(mRootView);
int expectedMargin = Math.round(PixelUtil.toPixelFromDIP(10));
int expectedMarginLeft = Math.round(PixelUtil.toPixelFromDIP(20));
assertEquals(expectedMarginLeft, (int) view.getX());
assertEquals(expectedMargin, (int) view.getY());
}
public void testMarginUpdateDoesntForgetPreviousValue() {
mCatalystInstance.getJSModule(ViewRenderingTestModule.class).renderMarginApplication(mRootTag);
waitForBridgeAndUIIdle();
View view = getViewAtPath(mRootView);
// before: margin: 10, marginLeft: 20
mCatalystInstance.getJSModule(ViewRenderingTestModule.class).updateMargins();
waitForBridgeAndUIIdle();
// after: margin: 15; it should not forget marginLeft was set to 20
int expectedMargin = Math.round(PixelUtil.toPixelFromDIP(15));
int expectedMarginLeft = Math.round(PixelUtil.toPixelFromDIP(20));
assertEquals(expectedMarginLeft, (int) view.getX());
assertEquals(expectedMargin, (int) view.getY());
}
public void testBordersApplied() {
mCatalystInstance.getJSModule(ViewRenderingTestModule.class).renderBorderApplication(mRootTag);
waitForBridgeAndUIIdle();
View view = getViewAtPath(mRootView);
View child = ((ViewGroup) view).getChildAt(0);
int expectedBorderX = Math.round(PixelUtil.toPixelFromDIP(20));
int expectedBorderY = Math.round(PixelUtil.toPixelFromDIP(5));
assertEquals(expectedBorderX, (int) child.getX());
assertEquals(expectedBorderY, (int) child.getY());
}
public void testTransformations() {
mCatalystInstance.getJSModule(ViewRenderingTestModule.class)
.renderTransformApplication(mRootTag);
waitForBridgeAndUIIdle();
View view = getViewAtPath(mRootView);
float expectedTranslateX = PixelUtil.toPixelFromDIP(20);
float expectedTranslateY = PixelUtil.toPixelFromDIP(25);
assertEquals(5f, view.getScaleX());
assertEquals(10f, view.getScaleY());
assertEquals(15f, view.getRotation());
assertEquals(expectedTranslateX, view.getTranslationX());
assertEquals(expectedTranslateY, view.getTranslationY());
}
}

View File

@@ -0,0 +1,30 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule Asserts
*/
'use strict';
var Assert = require('NativeModules').Assert;
var Asserts = {
assertEquals: function(expected, actual, msg) {
if (expected !== actual) {
Assert.fail(
msg ||
'Expected: ' + expected + ', received: ' + actual + '\n' +
'at ' + (new Error()).stack);
} else {
Assert.success();
}
},
assertTrue: function(expr, msg) {
Asserts.assertEquals(true, expr, msg);
},
};
module.exports = Asserts;

View File

@@ -0,0 +1,28 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule CatalystRootViewTestModule
*/
'use strict';
var React = require('React');
var Recording = require('NativeModules').Recording;
var View = require('View');
class CatalystRootViewTestApp extends React.Component {
componentWillUnmount() {
Recording.record('RootComponentWillUnmount');
}
render() {
return <View collapsable={false} style={{alignSelf: 'stretch'}} />;
}
}
module.exports = {
CatalystRootViewTestApp: CatalystRootViewTestApp,
};

View File

@@ -0,0 +1,45 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule DatePickerDialogTestModule
*/
'use strict';
var BatchedBridge = require('BatchedBridge');
var DatePickerAndroid = require('DatePickerAndroid');
var React = require('React');
var RecordingModule = require('NativeModules').DatePickerDialogRecordingModule;
var View = require('View');
class DatePickerDialogTestApp extends React.Component {
render() {
return (<View />);
}
}
var DatePickerDialogTestModule = {
DatePickerDialogTestApp: DatePickerDialogTestApp,
showDatePickerDialog: function(options) {
DatePickerAndroid.open(options).then(
({action, year, month, day}) => {
if (action === DatePickerAndroid.dateSetAction) {
RecordingModule.recordDate(year, month, day);
} else if (action === DatePickerAndroid.dismissedAction) {
RecordingModule.recordDismissed();
}
},
({code, message}) => RecordingModule.recordError()
);
},
};
BatchedBridge.registerCallableModule(
'DatePickerDialogTestModule',
DatePickerDialogTestModule
);
module.exports = DatePickerDialogTestModule;

View File

@@ -0,0 +1,26 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule InitialPropsTestApp
*/
'use strict';
var React = require('React');
var RecordingModule = require('NativeModules').InitialPropsRecordingModule;
var Text = require('Text');
class InitialPropsTestApp extends React.Component {
componentDidMount() {
RecordingModule.recordProps(this.props);
}
render() {
return <Text>dummy</Text>;
}
}
module.exports = InitialPropsTestApp;

View File

@@ -0,0 +1,63 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule JSResponderTestApp
*/
'use strict';
var React = require('React');
var StyleSheet = require('StyleSheet');
var View = require('View');
var Text = require('Text');
var PanResponder = require('PanResponder');
var ScrollView = require('ScrollView');
class JSResponderTestApp extends React.Component {
_handleMoveShouldSetPanResponder = (e, gestureState) => {
return Math.abs(gestureState.dx) > 30;
};
UNSAFE_componentWillMount() {
this.panGesture = PanResponder.create({
onMoveShouldSetPanResponder: this._handleMoveShouldSetPanResponder,
});
}
render() {
var views = [];
for (var i = 0; i < 100; i++) {
views[i] = (
<View key={i} style={styles.row} collapsable={false}>
<Text>I am row {i}</Text>
</View>
);
}
return (
<View
style={styles.container}
{...this.panGesture.panHandlers}
collapsable={false}>
<ScrollView style={styles.scrollview} testID="scroll_view">
{views}
</ScrollView>
</View>
);
}
}
var styles = StyleSheet.create({
container: {
flex: 1,
},
scrollview: {
flex: 1,
},
row: {
height: 30,
}
});
module.exports = JSResponderTestApp;

View File

@@ -0,0 +1,73 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule LayoutEventsTestApp
*/
'use strict';
var React = require('React');
var View = require('View');
var RecordingModule = require('NativeModules').Recording;
const LAYOUT_SPECS = [
[10, 10, 100, 100],
[10, 10, 50, 50],
[0, 0, 50, 50],
[0, 0, 50, 50],
];
class LayoutEventsTestApp extends React.Component {
constructor() {
super();
this.state = {
specNumber: 0,
};
this.numParentLayouts = 0;
}
handleOnLayout = (e) => {
var layout = e.nativeEvent.layout;
RecordingModule.record(layout.x + ',' + layout.y + '-' + layout.width + 'x' + layout.height);
if (this.state.specNumber >= LAYOUT_SPECS.length) {
// This will cause the test to fail
RecordingModule.record('Got an extraneous layout call');
} else {
this.setState({
specNumber: this.state.specNumber + 1,
});
}
};
handleParentOnLayout = (e) => {
if (this.numParentLayouts > 0) {
// This will cause the test to fail - the parent's layout doesn't change
// so we should only get the event once.
RecordingModule.record('Got an extraneous layout call on the parent');
}
this.numParentLayouts++;
};
render() {
const layout = LAYOUT_SPECS[this.state.specNumber];
return (
<View
onLayout={this.handleParentOnLayout}
testID="parent"
style={{left: 0, top: 0, width: 500, height: 500}}>
<View
onLayout={this.handleOnLayout}
testID="container"
style={{left: layout[0], top: layout[1], width: layout[2], height: layout[3]}}/>
</View>
);
}
}
module.exports = LayoutEventsTestApp;

View File

@@ -0,0 +1,204 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule MeasureLayoutTestModule
*/
'use strict';
var BatchedBridge = require('BatchedBridge');
var React = require('React');
var ReactNative = require('ReactNative');
var View = require('View');
var StyleSheet = require('StyleSheet');
var UIManager = require('UIManager');
var assertEquals = require('Asserts').assertEquals;
var styles = StyleSheet.create({
A: {
'width': 500,
'height': 500,
},
B: {
backgroundColor: 'rgb(255, 0, 0)',
'left': 50,
'top': 80,
'width': 200,
'height': 300,
},
C: {
backgroundColor: 'rgb(0, 255, 0)',
'left': 100,
'top': 70,
'width': 50,
'height': 150,
},
D: {
backgroundColor: 'rgb(0, 0, 255)',
'left': 400,
'top': 100,
'width': 50,
'height': 200,
},
});
var A, B, C, D;
class MeasureLayoutTestApp extends React.Component {
componentDidMount() {
A = ReactNative.findNodeHandle(this.refs.A);
B = ReactNative.findNodeHandle(this.refs.B);
C = ReactNative.findNodeHandle(this.refs.C);
D = ReactNative.findNodeHandle(this.refs.D);
}
render() {
return (
<View ref="A" style={styles.A} collapsable={false}>
<View ref="B" style={styles.B} collapsable={false}>
<View ref="C" style={styles.C} collapsable={false} />
</View>
<View ref="D" style={styles.D} collapsable={false} />
</View>
);
}
}
function shouldNotCallThisCallback() {
assertEquals(false, true);
}
var MeasureLayoutTestModule = {
MeasureLayoutTestApp: MeasureLayoutTestApp,
verifyMeasureOnViewA: function() {
UIManager.measure(A, function(a, b, width, height, x, y) {
assertEquals(500, width);
assertEquals(500, height);
assertEquals(0, x);
assertEquals(0, y);
});
},
verifyMeasureOnViewC: function() {
UIManager.measure(C, function(a, b, width, height, x, y) {
assertEquals(50, width);
assertEquals(150, height);
assertEquals(150, x);
assertEquals(150, y);
});
},
verifyMeasureLayoutCRelativeToA: function() {
UIManager.measureLayout(
C,
A,
shouldNotCallThisCallback,
function (x, y, width, height) {
assertEquals(50, width);
assertEquals(150, height);
assertEquals(150, x);
assertEquals(150, y);
});
},
verifyMeasureLayoutCRelativeToB: function() {
UIManager.measureLayout(
C,
B,
shouldNotCallThisCallback,
function (x, y, width, height) {
assertEquals(50, width);
assertEquals(150, height);
assertEquals(100, x);
assertEquals(70, y);
});
},
verifyMeasureLayoutCRelativeToSelf: function() {
UIManager.measureLayout(
C,
C,
shouldNotCallThisCallback,
function (x, y, width, height) {
assertEquals(50, width);
assertEquals(150, height);
assertEquals(0, x);
assertEquals(0, y);
});
},
verifyMeasureLayoutRelativeToParentOnViewA: function() {
UIManager.measureLayoutRelativeToParent(
A,
shouldNotCallThisCallback,
function (x, y, width, height) {
assertEquals(500, width);
assertEquals(500, height);
assertEquals(0, x);
assertEquals(0, y);
});
},
verifyMeasureLayoutRelativeToParentOnViewB: function() {
UIManager.measureLayoutRelativeToParent(
B,
shouldNotCallThisCallback,
function (x, y, width, height) {
assertEquals(200, width);
assertEquals(300, height);
assertEquals(50, x);
assertEquals(80, y);
});
},
verifyMeasureLayoutRelativeToParentOnViewC: function() {
UIManager.measureLayoutRelativeToParent(
C,
shouldNotCallThisCallback,
function (x, y, width, height) {
assertEquals(50, width);
assertEquals(150, height);
assertEquals(100, x);
assertEquals(70, y);
});
},
verifyMeasureLayoutDRelativeToB: function() {
UIManager.measureLayout(
D,
B,
function () {
assertEquals(true, true);
},
shouldNotCallThisCallback);
},
verifyMeasureLayoutNonExistentTag: function() {
UIManager.measureLayout(
192,
A,
function () {
assertEquals(true, true);
},
shouldNotCallThisCallback);
},
verifyMeasureLayoutNonExistentAncestor: function() {
UIManager.measureLayout(
B,
192,
function () {
assertEquals(true, true);
},
shouldNotCallThisCallback);
},
verifyMeasureLayoutRelativeToParentNonExistentTag: function() {
UIManager.measureLayoutRelativeToParent(
192,
function () {
assertEquals(true, true);
},
shouldNotCallThisCallback);
},
};
BatchedBridge.registerCallableModule(
'MeasureLayoutTestModule',
MeasureLayoutTestModule
);
module.exports = MeasureLayoutTestModule;

View File

@@ -0,0 +1,66 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule MultitouchHandlingTestAppModule
*/
'use strict';
var React = require('React');
var Recording = require('NativeModules').Recording;
var StyleSheet = require('StyleSheet');
var TouchEventUtils = require('fbjs/lib/TouchEventUtils');
var View = require('View');
class TouchTestApp extends React.Component {
handleStartShouldSetResponder = (e) => {
return true;
};
handleOnResponderMove = (e) => {
e = TouchEventUtils.extractSingleTouch(e.nativeEvent);
Recording.record('move;' + e.touches.length);
};
handleResponderStart = (e) => {
e = TouchEventUtils.extractSingleTouch(e.nativeEvent);
if (e.touches) {
Recording.record('start;' + e.touches.length);
} else {
Recording.record('start;ExtraPointer');
}
};
handleResponderEnd = (e) => {
e = TouchEventUtils.extractSingleTouch(e.nativeEvent);
if (e.touches) {
Recording.record('end;' + e.touches.length);
} else {
Recording.record('end;ExtraPointer');
}
};
render() {
return (
<View
style={styles.container}
onStartShouldSetResponder={this.handleStartShouldSetResponder}
onResponderMove={this.handleOnResponderMove}
onResponderStart={this.handleResponderStart}
onResponderEnd={this.handleResponderEnd}
collapsable={false}
/>
);
}
}
var styles = StyleSheet.create({
container: {
flex: 1,
},
});
module.exports = TouchTestApp;

View File

@@ -0,0 +1,75 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule NativeIdTestModule
* @flow
*/
'use strict';
const Image = require('Image');
const React = require('React');
const StyleSheet = require('StyleSheet');
const Text = require('Text');
const TextInput = require('TextInput');
const TouchableBounce = require('TouchableBounce');
const TouchableHighlight = require('TouchableHighlight');
const TouchableOpacity = require('TouchableOpacity');
const TouchableWithoutFeedback = require('TouchableWithoutFeedback');
const View = require('View');
/**
* All the views implemented on Android, each with the nativeID property set.
* We test that:
* - The app renders fine
* - The nativeID property is passed to the native views
*/
class NativeIdTestApp extends React.Component<{}> {
render() {
const uri = 'data:image/gif;base64,' +
'R0lGODdhMAAwAPAAAAAAAP///ywAAAAAMAAwAAAC8IyPqcvt3wCcDkiLc7C0qwyGHhSWpjQu5yqmCYsapy' +
'uvUUlvONmOZtfzgFzByTB10QgxOR0TqBQejhRNzOfkVJ+5YiUqrXF5Y5lKh/DeuNcP5yLWGsEbtLiOSpa/' +
'TPg7JpJHxyendzWTBfX0cxOnKPjgBzi4diinWGdkF8kjdfnycQZXZeYGejmJlZeGl9i2icVqaNVailT6F5' +
'iJ90m6mvuTS4OK05M0vDk0Q4XUtwvKOzrcd3iq9uisF81M1OIcR7lEewwcLp7tuNNkM3uNna3F2JQFo97V' +
'riy/Xl4/f1cf5VWzXyym7PHhhx4dbgYKAAA7';
return (
<View>
<Image
nativeID="Image"
source={{uri: uri}}
style={styles.base} />
<Text nativeID="Text">text</Text>
<TextInput nativeID="TextInput" value="Text input" />
<TouchableBounce nativeID="TouchableBounce">
<Text>TouchableBounce</Text>
</TouchableBounce>
<TouchableHighlight nativeID="TouchableHighlight">
<Text>TouchableHighlight</Text>
</TouchableHighlight>
<TouchableOpacity nativeID="TouchableOpacity">
<Text>TouchableOpacity</Text>
</TouchableOpacity>
<TouchableWithoutFeedback nativeID="TouchableWithoutFeedback">
<View>
<Text>TouchableWithoutFeedback</Text>
</View>
</TouchableWithoutFeedback>
<View nativeID="View" />
</View>
);
}
}
const styles = StyleSheet.create({
base: {
width: 150,
height: 50,
},
});
module.exports = {
NativeIdTestApp: NativeIdTestApp,
};

View File

@@ -0,0 +1,90 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule PickerAndroidTestModule
*/
'use strict';
var BatchedBridge = require('BatchedBridge');
var React = require('React');
var RecordingModule = require('NativeModules').PickerAndroidRecordingModule;
var Picker = require('Picker');
var View = require('View');
var Item = Picker.Item;
var appInstance;
class PickerAndroidTestApp extends React.Component {
state = {
selected: 1,
mode: 'dropdown',
style: {},
};
UNSAFE_componentWillMount() {
appInstance = this;
}
render() {
return (
<View collapsable={false}>
<Picker
mode="dialog"
prompt="prompt"
style={this.state.style}
selectedValue={this.state.selected}
onValueChange={this.onValueChange}>
<Item label="item1" color="#ff0000" value={0} />
<Item label="item2" color="#00ff00" value={1} />
<Item label="item3" color="#0000ff" value={2} />
</Picker>
<Picker mode={this.state.mode}>
<Item label="item1" />
<Item label="item2" />
</Picker>
<Picker enabled={false}>
<Item label="item1" />
<Item label="item2" />
</Picker>
<Picker
mode="dropdown"
selectedValue={this.state.selected}
onValueChange={this.onValueChange}>
<Item label="item in sync 1" value={0} />
<Item label="item in sync 2" value={1} />
<Item label="item in sync 3" value={2} />
</Picker>
</View>
);
}
onValueChange = (value) => {
this.setState({selected: value});
RecordingModule.recordSelection(value);
};
}
var PickerAndroidTestModule = {
PickerAndroidTestApp: PickerAndroidTestApp,
selectItem: function(value) {
appInstance.setState({selected: value});
},
setMode: function(mode) {
appInstance.setState({mode: mode});
},
setPrimaryColor: function(color) {
appInstance.setState({style: {color}});
},
};
BatchedBridge.registerCallableModule(
'PickerAndroidTestModule',
PickerAndroidTestModule
);
module.exports = PickerAndroidTestModule;

View File

@@ -0,0 +1,51 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule ProgressBarTestModule
*/
'use strict';
var BatchedBridge = require('BatchedBridge');
var React = require('React');
var ProgressBar = require('ProgressBarAndroid');
var View = require('View');
var renderApplication = require('renderApplication');
class ProgressBarSampleApp extends React.Component {
state = {};
render() {
return (
<View>
<ProgressBar styleAttr="Horizontal" testID="Horizontal"/>
<ProgressBar styleAttr="Small" testID="Small"/>
<ProgressBar styleAttr="Large" testID="Large"/>
<ProgressBar styleAttr="Normal" testID="Normal"/>
<ProgressBar styleAttr="Inverse" testID="Inverse"/>
<ProgressBar styleAttr="SmallInverse" testID="SmallInverse"/>
<ProgressBar styleAttr="LargeInverse" testID="LargeInverse"/>
<View style={{width:200}}>
<ProgressBar styleAttr="Horizontal" testID="Horizontal200" />
</View>
</View>
);
}
}
var ProgressBarTestModule = {
renderProgressBarApplication: function(rootTag) {
renderApplication(ProgressBarSampleApp, {}, rootTag);
},
};
BatchedBridge.registerCallableModule(
'ProgressBarTestModule',
ProgressBarTestModule
);
module.exports = ProgressBarTestModule;

View File

@@ -0,0 +1,143 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule ScrollViewTestModule
*/
'use strict';
var BatchedBridge = require('BatchedBridge');
var React = require('React');
var createReactClass = require('create-react-class');
var View = require('View');
var ScrollView = require('ScrollView');
var Text = require('Text');
var StyleSheet = require('StyleSheet');
var TouchableWithoutFeedback = require('TouchableWithoutFeedback');
var ScrollListener = require('NativeModules').ScrollListener;
var NUM_ITEMS = 100;
// Shared by integration tests for ScrollView and HorizontalScrollView
var scrollViewApp;
class Item extends React.Component {
render() {
return (
<TouchableWithoutFeedback onPress={this.props.onPress}>
<View style={styles.item_container}>
<Text style={styles.item_text}>{this.props.text}</Text>
</View>
</TouchableWithoutFeedback>
);
}
}
var getInitialState = function() {
var data = [];
for (var i = 0; i < NUM_ITEMS; i++) {
data[i] = {text: 'Item ' + i + '!'};
}
return {
data: data,
};
};
var onScroll = function(e) {
ScrollListener.onScroll(e.nativeEvent.contentOffset.x, e.nativeEvent.contentOffset.y);
};
var onScrollBeginDrag = function(e) {
ScrollListener.onScrollBeginDrag(e.nativeEvent.contentOffset.x, e.nativeEvent.contentOffset.y);
};
var onScrollEndDrag = function(e) {
ScrollListener.onScrollEndDrag(e.nativeEvent.contentOffset.x, e.nativeEvent.contentOffset.y);
};
var onItemPress = function(itemNumber) {
ScrollListener.onItemPress(itemNumber);
};
var ScrollViewTestApp = createReactClass({
displayName: 'ScrollViewTestApp',
getInitialState: getInitialState,
onScroll: onScroll,
onItemPress: onItemPress,
onScrollBeginDrag: onScrollBeginDrag,
onScrollEndDrag: onScrollEndDrag,
scrollTo: function(destX, destY) {
this.refs.scrollView.scrollTo(destY, destX);
},
render: function() {
scrollViewApp = this;
var children = this.state.data.map((item, index) => (
<Item
key={index} text={item.text}
onPress={this.onItemPress.bind(this, index)} />
));
return (
<ScrollView onScroll={this.onScroll} onScrollBeginDrag={this.onScrollBeginDrag} onScrollEndDrag={this.onScrollEndDrag} ref="scrollView">
{children}
</ScrollView>
);
},
});
var HorizontalScrollViewTestApp = createReactClass({
displayName: 'HorizontalScrollViewTestApp',
getInitialState: getInitialState,
onScroll: onScroll,
onItemPress: onItemPress,
scrollTo: function(destX, destY) {
this.refs.scrollView.scrollTo(destY, destX);
},
render: function() {
scrollViewApp = this;
var children = this.state.data.map((item, index) => (
<Item
key={index} text={item.text}
onPress={this.onItemPress.bind(this, index)} />
));
return (
<ScrollView horizontal={true} onScroll={this.onScroll} ref="scrollView">
{children}
</ScrollView>
);
},
});
var styles = StyleSheet.create({
item_container: {
padding: 30,
backgroundColor: '#ffffff',
},
item_text: {
flex: 1,
fontSize: 18,
alignSelf: 'center',
},
});
var ScrollViewTestModule = {
ScrollViewTestApp: ScrollViewTestApp,
HorizontalScrollViewTestApp: HorizontalScrollViewTestApp,
scrollTo: function(destX, destY) {
scrollViewApp.scrollTo(destX, destY);
},
};
BatchedBridge.registerCallableModule(
'ScrollViewTestModule',
ScrollViewTestModule
);
module.exports = ScrollViewTestModule;

View File

@@ -0,0 +1,39 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule ShareTestModule
*/
'use strict';
var BatchedBridge = require('BatchedBridge');
var React = require('React');
var RecordingModule = require('NativeModules').ShareRecordingModule;
var Share = require('Share');
var View = require('View');
class ShareTestApp extends React.Component {
render() {
return (<View />);
}
}
var ShareTestModule = {
ShareTestApp: ShareTestApp,
showShareDialog: function(content, options) {
Share.share(content, options).then(
() => RecordingModule.recordOpened(),
({code, message}) => RecordingModule.recordError()
);
},
};
BatchedBridge.registerCallableModule(
'ShareTestModule',
ShareTestModule
);
module.exports = ShareTestModule;

View File

@@ -0,0 +1,310 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule SubviewsClippingTestModule
*/
'use strict';
var BatchedBridge = require('BatchedBridge');
var React = require('React');
var ReactNativeViewAttributes = require('ReactNativeViewAttributes');
var ScrollView = require('ScrollView');
var StyleSheet = require('StyleSheet');
var View = require('View');
var requireNativeComponent = require('requireNativeComponent');
var ClippableView = requireNativeComponent('ClippableView', null);
class ClippingSample1 extends React.Component {
render() {
var styles = sample1Styles;
return (
<View>
<ClippableView clippableViewID="outer" style={styles.outer} removeClippedSubviews={true}>
<ClippableView clippableViewID="inner1" style={[styles.inner, styles.inner1]}/>
<ClippableView clippableViewID="inner2" style={[styles.inner, styles.inner2]}/>
<ClippableView clippableViewID="inner3" style={[styles.inner, styles.inner3]}/>
<ClippableView clippableViewID="inner4" style={[styles.inner, styles.inner4]}/>
<ClippableView clippableViewID="inner5" style={[styles.inner, styles.inner5]}/>
</ClippableView>
</View>
);
}
}
var sample1Styles = StyleSheet.create({
outer: {
width: 200,
height: 200,
backgroundColor: 'red',
},
inner: {
position: 'absolute',
width: 100,
height: 100,
backgroundColor: 'green',
},
inner1: {
top: -150,
left: 50,
},
inner2: {
top: 50,
left: 50,
},
inner3: {
top: 250,
left: 50,
},
inner4: {
left: -150,
top: 50,
},
inner5: {
left: 250,
top: 50,
},
});
class ClippingSample2 extends React.Component {
render() {
var styles = sample2Styles;
return (
<View>
<ClippableView clippableViewID="outer" style={styles.outer} removeClippedSubviews={true}>
<ClippableView
clippableViewID="complexInner"
style={styles.complexInner}
removeClippedSubviews={true}>
<ClippableView clippableViewID="inner1" style={[styles.inner, styles.inner1]}/>
<ClippableView clippableViewID="inner2" style={[styles.inner, styles.inner2]}/>
<ClippableView clippableViewID="inner3" style={[styles.inner, styles.inner3]}/>
<ClippableView clippableViewID="inner4" style={[styles.inner, styles.inner4]}/>
</ClippableView>
</ClippableView>
</View>
);
}
}
var sample2Styles = StyleSheet.create({
outer: {
width: 200,
height: 200,
backgroundColor: 'red',
},
complexInner: {
position: 'absolute',
width: 200,
height: 200,
left: 100,
top: 100,
backgroundColor: 'green',
},
inner: {
position: 'absolute',
width: 80,
height: 80,
backgroundColor: 'blue',
},
inner1: {
left: 10,
top: 10,
},
inner2: {
right: 10,
top: 10,
},
inner3: {
left: 10,
bottom: 10,
},
inner4: {
right: 10,
bottom: 10,
},
});
class UpdatingSample1 extends React.Component {
render() {
var styles = updating1Styles;
var inner1Styles = [styles.inner1, {height: this.props.update1 ? 200 : 100}];
var inner2Styles = [styles.inner2, {top: this.props.update2 ? 200 : 50}];
return (
<View>
<ClippableView clippableViewID="outer" style={styles.outer} removeClippedSubviews={true}>
<ClippableView clippableViewID="inner1" style={inner1Styles}/>
<ClippableView clippableViewID="inner2" style={inner2Styles}/>
</ClippableView>
</View>
);
}
}
var updating1Styles = StyleSheet.create({
outer: {
width: 200,
height: 200,
backgroundColor: 'red',
},
inner1: {
position: 'absolute',
width: 100,
height: 100,
left: 50,
top: -100,
backgroundColor: 'green',
},
inner2: {
position: 'absolute',
width: 100,
height: 100,
left: 50,
top: 50,
backgroundColor: 'green',
}
});
class UpdatingSample2 extends React.Component {
render() {
var styles = updating2Styles;
var outerStyles = [styles.outer, {height: this.props.update ? 200 : 100}];
return (
<View>
<ClippableView clippableViewID="outer" style={outerStyles} removeClippedSubviews={true}>
<ClippableView clippableViewID="inner" style={styles.inner}/>
</ClippableView>
</View>
);
}
}
var updating2Styles = StyleSheet.create({
outer: {
width: 100,
height: 100,
backgroundColor: 'red',
},
inner: {
position: 'absolute',
width: 100,
height: 100,
top: 100,
backgroundColor: 'green',
},
});
class ScrollViewTest extends React.Component {
render() {
var styles = scrollTestStyles;
var children = [];
for (var i = 0; i < 4; i++) {
children[i] = (
<ClippableView key={i} style={styles.row} clippableViewID={'' + i}/>
);
}
for (var i = 4; i < 6; i++) {
var viewID = 'C' + (i - 4);
children[i] = (
<ClippableView
key={i}
style={styles.complex}
clippableViewID={viewID}
removeClippedSubviews={true}>
<ClippableView style={styles.inner} clippableViewID={viewID + '.1'}/>
<ClippableView style={styles.inner} clippableViewID={viewID + '.2'}/>
</ClippableView>
);
}
return (
<ScrollView removeClippedSubviews={true} style={styles.scrollView} testID="scroll_view">
{children}
</ScrollView>
);
}
}
var scrollTestStyles = StyleSheet.create({
scrollView: {
width: 200,
height: 300,
},
row: {
flex: 1,
height: 120,
backgroundColor: 'red',
borderColor: 'blue',
borderBottomWidth: 1,
},
complex: {
flex: 1,
height: 240,
backgroundColor: 'yellow',
borderColor: 'blue',
borderBottomWidth: 1,
},
inner: {
flex: 1,
margin: 10,
height: 100,
backgroundColor: 'gray',
borderColor: 'green',
borderWidth: 1,
},
});
var appInstance = null;
class SubviewsClippingTestApp extends React.Component {
state = {};
UNSAFE_componentWillMount() {
appInstance = this;
}
setComponent = (component) => {
this.setState({component: component});
};
render() {
var component = this.state.component;
return (
<View>
{component}
</View>
);
}
}
var SubviewsClippingTestModule = {
App: SubviewsClippingTestApp,
renderClippingSample1: function() {
appInstance.setComponent(<ClippingSample1/>);
},
renderClippingSample2: function() {
appInstance.setComponent(<ClippingSample2/>);
},
renderUpdatingSample1: function(update1, update2) {
appInstance.setComponent(<UpdatingSample1 update1={update1} update2={update2}/>);
},
renderUpdatingSample2: function(update) {
appInstance.setComponent(<UpdatingSample2 update={update}/>);
},
renderScrollViewTest: function() {
appInstance.setComponent(<ScrollViewTest/>);
},
};
BatchedBridge.registerCallableModule(
'SubviewsClippingTestModule',
SubviewsClippingTestModule
);
module.exports = SubviewsClippingTestModule;

View File

@@ -0,0 +1,89 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule SwipeRefreshLayoutTestModule
*/
'use strict';
var BatchedBridge = require('BatchedBridge');
var React = require('React');
var RecordingModule = require('NativeModules').SwipeRefreshLayoutRecordingModule;
var ScrollView = require('ScrollView');
var RefreshControl = require('RefreshControl');
var Text = require('Text');
var TouchableWithoutFeedback = require('TouchableWithoutFeedback');
var View = require('View');
class Row extends React.Component {
state = {
clicks: 0,
};
render() {
return (
<TouchableWithoutFeedback onPress={this._onPress}>
<View>
<Text>
{this.state.clicks + ' clicks'}
</Text>
</View>
</TouchableWithoutFeedback>
);
}
_onPress = () => {
this.setState({clicks: this.state.clicks + 1});
};
}
var app = null;
class SwipeRefreshLayoutTestApp extends React.Component {
state = {
rows: 2,
};
componentDidMount() {
app = this;
}
render() {
var rows = [];
for (var i = 0; i < this.state.rows; i++) {
rows.push(<Row key={i} />);
}
return (
<ScrollView
style={{flex: 1}}
refreshControl={
<RefreshControl
style={{flex: 1}}
refreshing={false}
onRefresh={() => RecordingModule.onRefresh()}
/>
}>
{rows}
</ScrollView>
);
}
}
var SwipeRefreshLayoutTestModule = {
SwipeRefreshLayoutTestApp,
setRows: function(rows) {
if (app != null) {
app.setState({rows});
}
}
};
BatchedBridge.registerCallableModule(
'SwipeRefreshLayoutTestModule',
SwipeRefreshLayoutTestModule
);
module.exports = SwipeRefreshLayoutTestModule;

View File

@@ -0,0 +1,115 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule TestBundle
*/
'use strict';
// Disable YellowBox so we do not have to mock its dependencies
console.disableYellowBox = true;
// Include callable JS modules first, in case one of the other ones below throws
require('ProgressBarTestModule');
require('ViewRenderingTestModule');
require('TestJavaToJSArgumentsModule');
require('TestJSLocaleModule');
require('TestJSToJavaParametersModule');
require('TestJavaToJSReturnValuesModule');
require('UIManagerTestModule');
require('CatalystRootViewTestModule');
require('DatePickerDialogTestModule');
require('MeasureLayoutTestModule');
require('PickerAndroidTestModule');
require('ScrollViewTestModule');
require('ShareTestModule');
require('SwipeRefreshLayoutTestModule');
require('TextInputTestModule');
require('TimePickerDialogTestModule');
// Define catalyst test apps used in integration tests
var AppRegistry = require('AppRegistry');
var apps = [
{
appKey: 'CatalystRootViewTestApp',
component: () => require('CatalystRootViewTestModule').CatalystRootViewTestApp
},
{
appKey: 'DatePickerDialogTestApp',
component: () => require('DatePickerDialogTestModule').DatePickerDialogTestApp
},
{
appKey: 'JSResponderTestApp',
component: () => require('JSResponderTestApp'),
},
{
appKey: 'HorizontalScrollViewTestApp',
component: () => require('ScrollViewTestModule').HorizontalScrollViewTestApp,
},
{
appKey: 'InitialPropsTestApp',
component: () => require('InitialPropsTestApp'),
},
{
appKey: 'LayoutEventsTestApp',
component: () => require('LayoutEventsTestApp'),
},
{
appKey: 'MeasureLayoutTestApp',
component: () => require('MeasureLayoutTestModule').MeasureLayoutTestApp
},
{
appKey: 'MultitouchHandlingTestAppModule',
component: () => require('MultitouchHandlingTestAppModule')
},
{
appKey: 'NativeIdTestApp',
component: () => require('NativeIdTestModule').NativeIdTestApp
},
{
appKey: 'PickerAndroidTestApp',
component: () => require('PickerAndroidTestModule').PickerAndroidTestApp,
},
{
appKey: 'ScrollViewTestApp',
component: () => require('ScrollViewTestModule').ScrollViewTestApp,
},
{
appKey: 'ShareTestApp',
component: () => require('ShareTestModule').ShareTestApp,
},
{
appKey: 'SubviewsClippingTestApp',
component: () => require('SubviewsClippingTestModule').App,
},
{
appKey: 'SwipeRefreshLayoutTestApp',
component: () => require('SwipeRefreshLayoutTestModule').SwipeRefreshLayoutTestApp
},
{
appKey: 'TextInputTestApp',
component: () => require('TextInputTestModule').TextInputTestApp
},
{
appKey: 'TestIdTestApp',
component: () => require('TestIdTestModule').TestIdTestApp
},
{
appKey: 'TimePickerDialogTestApp',
component: () => require('TimePickerDialogTestModule').TimePickerDialogTestApp
},
{
appKey: 'TouchBubblingTestAppModule',
component: () => require('TouchBubblingTestAppModule')
},
];
module.exports = apps;
AppRegistry.registerConfig(apps);

View File

@@ -0,0 +1,83 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule TestIdTestModule
*/
'use strict';
var Image = require('Image');
var React = require('React');
var StyleSheet = require('StyleSheet');
var Switch = require('Switch');
var Text = require('Text');
var TextInput = require('TextInput');
var TouchableBounce = require('TouchableBounce');
var TouchableHighlight = require('TouchableHighlight');
var TouchableOpacity = require('TouchableOpacity');
var TouchableWithoutFeedback = require('TouchableWithoutFeedback');
var View = require('View');
/**
* All the views implemented on Android, each with the testID property set.
* We test that:
* - The app renders fine
* - The testID property is passed to the native views
*/
class TestIdTestApp extends React.Component {
render() {
return (
<View>
<Image
testID="Image"
source={{uri: 'data:image/gif;base64,' +
'R0lGODdhMAAwAPAAAAAAAP///ywAAAAAMAAwAAAC8IyPqcvt3wCcDkiLc7C0qwyGHhSWpjQu5yqmCYsapy' +
'uvUUlvONmOZtfzgFzByTB10QgxOR0TqBQejhRNzOfkVJ+5YiUqrXF5Y5lKh/DeuNcP5yLWGsEbtLiOSpa/' +
'TPg7JpJHxyendzWTBfX0cxOnKPjgBzi4diinWGdkF8kjdfnycQZXZeYGejmJlZeGl9i2icVqaNVailT6F5' +
'iJ90m6mvuTS4OK05M0vDk0Q4XUtwvKOzrcd3iq9uisF81M1OIcR7lEewwcLp7tuNNkM3uNna3F2JQFo97V' +
'riy/Xl4/f1cf5VWzXyym7PHhhx4dbgYKAAA7'}}
style={styles.base} />
<Text testID="Text">text</Text>
<TextInput testID="TextInput" value="Text input" />
<TouchableBounce testID="TouchableBounce">
<Text>TouchableBounce</Text>
</TouchableBounce>
<TouchableHighlight testID="TouchableHighlight">
<Text>TouchableHighlight</Text>
</TouchableHighlight>
<TouchableOpacity testID="TouchableOpacity">
<Text>TouchableOpacity</Text>
</TouchableOpacity>
<TouchableWithoutFeedback testID="TouchableWithoutFeedback">
<View>
<Text>TouchableWithoutFeedback</Text>
</View>
</TouchableWithoutFeedback>
<View testID="View" />
</View>
);
}
}
var styles = StyleSheet.create({
base: {
width: 150,
height: 50,
},
});
module.exports = {
TestIdTestApp: TestIdTestApp,
};

View File

@@ -0,0 +1,29 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule TestJSLocaleModule
*/
'use strict';
var BatchedBridge = require('BatchedBridge');
var Recording = require('NativeModules').Recording;
var TestJSLocaleModule = {
toUpper: function(s) {
Recording.record(s.toUpperCase());
},
toLower: function(s) {
Recording.record(s.toLowerCase());
},
};
BatchedBridge.registerCallableModule(
'TestJSLocaleModule',
TestJSLocaleModule
);
module.exports = TestJSLocaleModule;

View File

@@ -0,0 +1,122 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule TestJSToJavaParametersModule
*/
'use strict';
var BatchedBridge = require('BatchedBridge');
var Recording = require('NativeModules').Recording;
var TestJSToJavaParametersModule = {
returnBasicTypes: function() {
Recording.receiveBasicTypes('foo', 3.14, true, null);
},
returnBoxedTypes: function() {
Recording.receiveBoxedTypes(42, 3.14, true);
},
returnDynamicTypes: function() {
Recording.receiveDynamic('foo');
Recording.receiveDynamic(3.14);
},
returnArrayWithBasicTypes: function() {
Recording.receiveArray(['foo', 3.14, -111, true, null]);
},
returnNestedArray: function() {
Recording.receiveArray(['we', ['have', ['to', ['go', ['deeper']]]]]);
},
returnArrayWithMaps: function() {
Recording.receiveArray([{m1k1: 'm1v1', m1k2: 'm1v2'}, {m2k1: 'm2v1'}]);
},
returnMapWithBasicTypes: function() {
Recording.receiveMap({
stringKey: 'stringValue',
doubleKey: 3.14,
intKey: -11,
booleanKey: true,
nullKey: null,
});
},
returnNestedMap: function() {
Recording.receiveMap({
weHaveToGoDeeper: {
inception: true,
},
});
},
returnMapWithArrays: function() {
Recording.receiveMap({
'empty': [],
'ints': [43, 44],
'mixed': [77, 'string', ['another', 'array']],
});
},
returnArrayWithStringDoubleIntMapArrayBooleanNull: function() {
Recording.receiveArray(['string', 3.14, 555, {}, [], true, null]);
},
returnMapWithStringDoubleIntMapArrayBooleanNull: function() {
Recording.receiveMap({
string: 'string',
double: 3,
map: {},
int: -55,
array: [],
boolean: true,
null: null
});
},
returnArrayWithLargeInts: function() {
Recording.receiveArray([2147483648, -5555555555]);
},
returnMapWithLargeInts: function() {
Recording.receiveMap({first: -2147483649, second: 5551231231});
},
returnMapForMerge1: function() {
Recording.receiveMap({
a: 1,
b: 41,
c: 'string',
d: 'other string',
e: [1,'foo','bar'],
f: null,
});
},
returnMapForMerge2: function() {
Recording.receiveMap({
a: 'overwrite',
d: 77,
e: null,
f: ['array', 'with', 'stuff'],
newkey: 'newvalue',
});
},
returnMapWithMultibyteUTF8CharacterString: function() {
Recording.receiveMap({
'one-byte': 'a',
'two-bytes': '\u00A2',
'three-bytes': '\u20AC',
'four-bytes': '\uD83D\uDE1C',
'mixed': '\u017C\u00F3\u0142\u0107 g\u0119\u015Bl\u0105 \u6211 \uD83D\uDE0E ja\u017A\u0107'
});
},
returnArrayWithMultibyteUTF8CharacterString: function() {
Recording.receiveArray([
'a',
'\u00A2',
'\u20AC',
'\uD83D\uDE1C',
'\u017C\u00F3\u0142\u0107 g\u0119\u015Bl\u0105 \u6211 \uD83D\uDE0E ja\u017A\u0107'
]);
},
};
BatchedBridge.registerCallableModule(
'TestJSToJavaParametersModule',
TestJSToJavaParametersModule
);
module.exports = TestJSToJavaParametersModule;

View File

@@ -0,0 +1,114 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule TestJavaToJSArgumentsModule
*/
'use strict';
var BatchedBridge = require('BatchedBridge');
var {assertEquals, assertTrue} = require('Asserts');
function strictStringCompare(a, b) {
if (typeof a !== 'string' || typeof b !== 'string' || a.length !== b.length) {
return false;
}
for (var i = 0; i < a.length; i++) {
if (a.charCodeAt(i) !== b.charCodeAt(i)) {
return false;
}
}
return true;
}
function assertStrictStringEquals(a, b) {
assertTrue(
strictStringCompare(a,b),
'Expected: ' + a + ', received: ' + b);
}
var TestJavaToJSArgumentsModule = {
receiveBasicTypes: function(str, dbl, bool, null_arg) {
assertEquals('foo', str);
assertEquals(3.14, dbl);
assertEquals(true, bool);
assertEquals(null, null_arg);
},
receiveArrayWithBasicTypes: function(arr) {
assertEquals(4, arr.length);
assertEquals('red panda', arr[0]);
assertEquals(1.19, arr[1]);
assertEquals(true, arr[2]);
assertEquals(null, arr[3]);
},
receiveNestedArray: function(arr) {
assertEquals(2, arr.length);
assertEquals('level1', arr[0]);
var arr2 = arr[1];
assertEquals('level2', arr2[0]);
var arr3 = arr2[1];
assertEquals('level3', arr3[0]);
},
receiveArrayWithMaps: function(arr) {
assertEquals(2, arr.length);
var m1 = arr[0];
var m2 = arr[1];
assertEquals('m1v1', m1.m1k1);
assertEquals('m1v2', m1.m1k2);
assertEquals('m2v1', m2.m2k1);
},
receiveMapWithBasicTypes: function(map) {
assertEquals('stringValue', map.stringKey);
assertEquals(3.14, map.doubleKey);
assertEquals(true, map.booleanKey);
assertEquals(null, map.nullKey);
},
receiveNestedMap: function(map) {
var nestedMap = map.nestedMap;
assertEquals('foxes', nestedMap.animals);
},
receiveMapWithArrays: function(map) {
var a1 = map.array1;
var a2 = map.array2;
assertEquals(3, a1.length);
assertEquals(2, a2.length);
assertEquals(3, a1[0]);
assertEquals(9, a2[1]);
},
receiveMapAndArrayWithNullValues: function(map, array) {
assertEquals(null, map.string);
assertEquals(null, map.array);
assertEquals(null, map.map);
assertEquals(null, array[0]);
assertEquals(null, array[1]);
assertEquals(null, array[2]);
},
receiveMapWithMultibyteUTF8CharacterString: function(map) {
assertStrictStringEquals('\u00A2', map['two-bytes']);
assertStrictStringEquals('\u20AC', map['three-bytes']);
assertStrictStringEquals('\uD83D\uDE1C', map['four-bytes']);
assertStrictStringEquals(
'\u017C\u00F3\u0142\u0107 g\u0119\u015Bl\u0105 \u6211 \uD83D\uDE0E ja\u017A\u0107',
map.mixed);
},
receiveArrayWithMultibyteUTF8CharacterString: function(array) {
assertTrue(true);
assertStrictStringEquals('\u00A2', array[0]);
assertStrictStringEquals('\u20AC', array[1]);
assertStrictStringEquals('\uD83D\uDE1C', array[2]);
assertStrictStringEquals(
'\u017C\u00F3\u0142\u0107 g\u0119\u015Bl\u0105 \u6211 \uD83D\uDE0E ja\u017A\u0107',
array[3]);
},
};
BatchedBridge.registerCallableModule(
'TestJavaToJSArgumentsModule',
TestJavaToJSArgumentsModule
);
module.exports = TestJavaToJSArgumentsModule;

View File

@@ -0,0 +1,38 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule TestJavaToJSReturnValuesModule
*/
'use strict';
const BatchedBridge = require('BatchedBridge');
const {assertEquals, assertTrue} = require('Asserts');
const {TestModule} = require('NativeModules');
var TestJavaToJSReturnValuesModule = {
callMethod: function(methodName, expectedType, expectedJSON) {
const result = TestModule[methodName]();
assertEquals(expectedType, typeof result);
assertEquals(expectedJSON, JSON.stringify(result));
},
triggerException: function() {
try {
TestModule.triggerException();
} catch (ex) {
assertTrue(ex.message.indexOf('Exception triggered') !== -1);
}
}
};
BatchedBridge.registerCallableModule(
'TestJavaToJSReturnValuesModule',
TestJavaToJSReturnValuesModule
);
module.exports = TestJavaToJSReturnValuesModule;

View File

@@ -0,0 +1,180 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule TextInputTestModule
*/
'use strict';
var BatchedBridge = require('BatchedBridge');
var React = require('React');
var StyleSheet = require('StyleSheet');
var Text = require('Text');
var TextInput = require('TextInput');
var View = require('View');
var Recording = require('NativeModules').Recording;
var app;
class TokenizedTextExample extends React.Component {
constructor(props) {
super(props);
this.state = {text: ''};
}
render() {
//define delimiter
let delimiter = /\s+/;
//split string
let _text = this.state.text;
let token, index, parts = [];
while (_text) {
delimiter.lastIndex = 0;
token = delimiter.exec(_text);
if (token === null) {
break;
}
index = token.index;
if (token[0].length === 0) {
index = 1;
}
parts.push(_text.substr(0, index));
parts.push(token[0]);
index = index + token[0].length;
_text = _text.slice(index);
}
parts.push(_text);
//highlight hashtags
parts = parts.map((text) => {
if (/^#/.test(text)) {
return <Text key={text} style={styles.hashtag}>{text}</Text>;
} else {
return text;
}
});
return (
<View>
<TextInput
ref="tokenizedInput"
testID="tokenizedInput"
multiline={true}
style={styles.multiline}
onChangeText={(text) => {
this.setState({text});
}}>
<Text>{parts}</Text>
</TextInput>
</View>
);
}
}
class TextInputTestApp extends React.Component {
componentDidMount() {
app = this;
}
handleOnSubmitEditing = (record) => {
Recording.record(record);
};
render() {
return (
<View style={styles.container}>
<TextInput
style={styles.textInputHeight}
autoCorrect={true}
autoFocus={true}
keyboardType="numeric"
multiline={true}
secureTextEntry={true}
defaultValue="This is text"
testID="textInput1"
/>
<TextInput
style={styles.textInput}
autoCapitalize="sentences"
autoCorrect={false}
autoFocus={false}
keyboardType="default"
multiline={false}
secureTextEntry={false}
placeholder="1234"
testID="textInput2"
/>
<TextInput
ref="textInput3"
style={styles.textInput}
defaultValue="Hello, World"
testID="textInput3"
/>
<TextInput
ref="textInput4"
style={[styles.textInput, {color: '#00ff00'}]}
testID="textInput4"
/>
<TextInput
ref="textInput5"
style={[styles.textInput, {color: '#00ff00'}]}
defaultValue=""
testID="textInput5"
/>
<TextInput
ref="textInput6"
style={[styles.textInput, {color: '#00ff00'}]}
defaultValue="Text"
testID="textInput6"
/>
<TextInput
ref="onSubmitTextInput"
onSubmitEditing={this.handleOnSubmitEditing.bind(this, 'onSubmit')}
defaultValue=""
testID="onSubmitTextInput"
/>
<TokenizedTextExample />
</View>
);
}
}
var styles = StyleSheet.create({
container: {
padding: 5,
margin: 10,
},
textInputHeight: {
fontSize: 21,
height: 30,
},
textInput: {
fontSize: 21,
padding: 0,
},
hashtag: {
color: 'blue',
fontWeight: 'bold',
},
});
var TextInputTestModule = {
TextInputTestApp,
setValueRef: function(ref, value) {
app.refs[ref].setNativeProps({
text: value,
});
},
};
BatchedBridge.registerCallableModule(
'TextInputTestModule',
TextInputTestModule
);
module.exports = TextInputTestModule;

View File

@@ -0,0 +1,45 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule TimePickerDialogTestModule
*/
'use strict';
var BatchedBridge = require('BatchedBridge');
var TimePickerAndroid = require('TimePickerAndroid');
var React = require('React');
var RecordingModule = require('NativeModules').TimePickerDialogRecordingModule;
var View = require('View');
class TimePickerDialogTestApp extends React.Component {
render() {
return <View />;
}
}
var TimePickerDialogTestModule = {
TimePickerDialogTestApp: TimePickerDialogTestApp,
showTimePickerDialog: function(options) {
TimePickerAndroid.open(options).then(
({action, hour, minute}) => {
if (action === TimePickerAndroid.timeSetAction) {
RecordingModule.recordTime(hour, minute);
} else if (action === TimePickerAndroid.dismissedAction) {
RecordingModule.recordDismissed();
}
},
({code, message}) => RecordingModule.recordError()
);
},
};
BatchedBridge.registerCallableModule(
'TimePickerDialogTestModule',
TimePickerDialogTestModule
);
module.exports = TimePickerDialogTestModule;

View File

@@ -0,0 +1,75 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule TouchBubblingTestAppModule
*/
'use strict';
var Recording = require('NativeModules').Recording;
var React = require('React');
var StyleSheet = require('StyleSheet');
var View = require('View');
var TouchableWithoutFeedback = require('TouchableWithoutFeedback');
class TouchBubblingTestApp extends React.Component {
handlePress = (record) => {
Recording.record(record);
};
render() {
return (
<View style={styles.container}>
<TouchableWithoutFeedback onPress={this.handlePress.bind(this, 'outer')} testID="D">
<View style={styles.outer}>
<TouchableWithoutFeedback onPress={this.handlePress.bind(this, 'inner')} testID="B">
<View style={styles.inner}>
<View style={styles.superinner} testID="A" />
</View>
</TouchableWithoutFeedback>
<View style={styles.inner} testID="C" />
</View>
</TouchableWithoutFeedback>
<TouchableWithoutFeedback onPress={this.handlePress.bind(this, 'outsider')} testID="E">
<View style={styles.element} />
</TouchableWithoutFeedback>
</View>
);
}
}
var styles = StyleSheet.create({
container: {
flexDirection: 'column',
backgroundColor: '#ccdd44',
},
element: {
backgroundColor: '#ff0000',
height: 100,
margin: 30,
},
outer: {
backgroundColor: '#00ff00',
height: 100,
margin: 30,
flexDirection: 'row',
justifyContent: 'space-between',
},
inner: {
backgroundColor: '#0000ff',
height: 50,
width: 50,
margin: 10,
},
superinner: {
backgroundColor: '#eeeeee',
height: 20,
width: 20,
}
});
module.exports = TouchBubblingTestApp;

View File

@@ -0,0 +1,206 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule UIManagerTestModule
*/
'use strict';
var BatchedBridge = require('BatchedBridge');
var React = require('React');
var StyleSheet = require('StyleSheet');
var View = require('View');
var Text = require('Text');
var createReactClass = require('create-react-class');
var renderApplication = require('renderApplication');
var FlexTestApp = createReactClass({
displayName: 'FlexTestApp',
_styles: StyleSheet.create({
container: {
width: 200,
height: 200,
flexDirection: 'row',
},
child: {
flex: 1,
},
absolute: {
position: 'absolute',
top: 15,
left: 10,
width: 50,
height: 60,
}
}),
render: function() {
return (
<View style={this._styles.container} testID="container" collapsable={false}>
<View style={[this._styles.child, {backgroundColor: '#ff0000'}]} collapsable={false}/>
<View style={[this._styles.child, {backgroundColor: '#0000ff'}]} collapsable={false}/>
</View>
);
}
});
var FlexWithText = createReactClass({
displayName: 'FlexWithText',
_styles: StyleSheet.create({
container: {
flexDirection: 'column',
margin: 20,
},
row: {
flexDirection: 'row',
alignItems: 'flex-end',
height: 300,
},
inner: {
flex: 1,
margin: 10,
},
}),
render: function() {
return (
<View style={this._styles.container} testID="container" collapsable={false}>
<View style={this._styles.row} collapsable={false}>
<Text style={this._styles.inner}>Hello</Text>
<Text style={this._styles.inner}>World</Text>
</View>
</View>
);
}
});
var AbsolutePositionTestApp = createReactClass({
displayName: 'AbsolutePositionTestApp',
_styles: StyleSheet.create({
absolute: {
position: 'absolute',
top: 15,
left: 10,
width: 50,
height: 60,
}
}),
render: function() {
return <View style={this._styles.absolute} testID="absolute" collapsable={false}/>;
}
});
var AbsolutePositionBottomRightTestApp = createReactClass({
displayName: 'AbsolutePositionBottomRightTestApp',
_styles: StyleSheet.create({
container: {
width: 100,
height: 100,
},
absolute: {
position: 'absolute',
bottom: 15,
right: 10,
width: 50,
height: 60,
}
}),
render: function() {
return (
<View style={this._styles.container} testID="container" collapsable={false}>
<View style={this._styles.absolute} collapsable={false}/>
</View>
);
}
});
var CenteredTextView = createReactClass({
displayName: 'CenteredTextView',
_styles: StyleSheet.create({
parent: {
width: 200,
height: 100,
backgroundColor: '#aa3311',
justifyContent: 'center',
alignItems: 'center',
},
text: {
fontSize: 15,
color: '#672831',
},
}),
render: function() {
return (
<View collapsable={false}>
<View style={this._styles.parent} collapsable={false}>
<Text style={this._styles.text} testID="text">{this.props.text}</Text>
</View>
</View>
);
}
});
var flushUpdatePositionInList = null;
var UpdatePositionInListTestApp = createReactClass({
displayName: 'UpdatePositionInListTestApp',
_styles: StyleSheet.create({
element: {
height: 10,
},
active: {
height: 50,
}
}),
getInitialState: function() {
flushUpdatePositionInList = () => this.setState({ active: true });
return { active: false };
},
render: function() {
return (
<View collapsable={false} testID="container">
<View style={this._styles.element} collapsable={false} />
<View
style={[
this._styles.element,
this.state.active && this._styles.active,
]}
collapsable={false}
/>
<View style={this._styles.element} collapsable={false}/>
</View>
);
}
});
var UIManagerTestModule = {
renderFlexTestApplication: function(rootTag) {
renderApplication(FlexTestApp, {}, rootTag);
},
renderFlexWithTextApplication: function(rootTag) {
renderApplication(FlexWithText, {}, rootTag);
},
renderAbsolutePositionBottomRightTestApplication: function(rootTag) {
renderApplication(AbsolutePositionBottomRightTestApp, {}, rootTag);
},
renderAbsolutePositionTestApplication: function(rootTag) {
renderApplication(AbsolutePositionTestApp, {}, rootTag);
},
renderCenteredTextViewTestApplication: function(rootTag, text) {
renderApplication(CenteredTextView, {text: text}, rootTag);
},
renderUpdatePositionInListTestApplication: function(rootTag) {
renderApplication(UpdatePositionInListTestApp, {}, rootTag);
},
flushUpdatePositionInList: function() {
flushUpdatePositionInList();
}
};
BatchedBridge.registerCallableModule(
'UIManagerTestModule',
UIManagerTestModule
);
module.exports = UIManagerTestModule;

View File

@@ -0,0 +1,99 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule ViewRenderingTestModule
*/
'use strict';
var BatchedBridge = require('BatchedBridge');
var React = require('React');
var View = require('View');
var StyleSheet = require('StyleSheet');
var renderApplication = require('renderApplication');
var styles = StyleSheet.create({
view: {
opacity: 0.75,
backgroundColor: 'rgb(255, 0, 0)',
},
});
class ViewSampleApp extends React.Component {
state = {};
render() {
return (
<View style={styles.view} collapsable={false}/>
);
}
}
var updateMargins;
class MarginSampleApp extends React.Component {
state = {margin: 10};
render() {
updateMargins = this.setState.bind(this, {margin: 15});
return (
<View style={{margin: this.state.margin, marginLeft: 20}} collapsable={false}/>
);
}
}
class BorderSampleApp extends React.Component {
render() {
return (
<View style={{borderLeftWidth: 20, borderWidth: 5, backgroundColor: 'blue'}} collapsable={false}>
<View style={{backgroundColor: 'red', width: 20, height: 20}} collapsable={false}/>
</View>
);
}
}
class TransformSampleApp extends React.Component {
render() {
var style = {
transform: [
{translateX: 20},
{translateY: 25},
{rotate: '15deg'},
{scaleX: 5},
{scaleY: 10},
]
};
return (
<View style={style} collapsable={false}/>
);
}
}
var ViewRenderingTestModule = {
renderViewApplication: function(rootTag) {
renderApplication(ViewSampleApp, {}, rootTag);
},
renderMarginApplication: function(rootTag) {
renderApplication(MarginSampleApp, {}, rootTag);
},
renderBorderApplication: function(rootTag) {
renderApplication(BorderSampleApp, {}, rootTag);
},
renderTransformApplication: function(rootTag) {
renderApplication(TransformSampleApp, {}, rootTag);
},
updateMargins: function() {
updateMargins();
},
};
BatchedBridge.registerCallableModule(
'ViewRenderingTestModule',
ViewRenderingTestModule
);
module.exports = ViewRenderingTestModule;

View File

@@ -0,0 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.facebook.react">
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<application />
</manifest>

View File

@@ -0,0 +1,10 @@
load("//ReactNative:DEFS.bzl", "rn_android_resource", "react_native_dep")
# used by ReactToolbarManager because of Gradle
# TODO t10182713 will be replaced with appcompat-orig when we stop using Gradle
rn_android_resource(
name = "appcompat",
package = "com.facebook.react",
res = react_native_dep("third-party/android/support/v7/appcompat-orig:res-unpacker-cmd"),
visibility = ["//ReactAndroid/..."],
)

View File

@@ -0,0 +1,13 @@
load("//ReactNative:DEFS.bzl", "rn_android_library", "react_native_dep")
rn_android_library(
name = "yoga",
srcs = glob(["yoga/*.java"]),
visibility = ["PUBLIC"],
deps = [
react_native_dep("java/com/facebook/proguard/annotations:annotations"),
react_native_dep("libraries/soloader/java/com/facebook/soloader:soloader"),
react_native_dep("third-party/java/infer-annotations:infer-annotations"),
react_native_dep("third-party/java/jsr-305:jsr-305"),
],
)

View File

@@ -0,0 +1,12 @@
load("//ReactNative:DEFS.bzl", "rn_android_library", "react_native_dep")
rn_android_library(
name = "model",
srcs = glob(["*.java"]),
visibility = [
"PUBLIC",
],
deps = [
react_native_dep("third-party/java/jsr-305:jsr-305"),
],
)

View File

@@ -0,0 +1,25 @@
// Copyright 2004-present Facebook. All Rights Reserved.
package com.facebook.debug.debugoverlay.model;
import javax.annotation.concurrent.Immutable;
/** Tag for a debug overlay log message. Name must be unique. */
@Immutable
public class DebugOverlayTag {
/** Name of tag. */
public final String name;
/** Description to display in settings. */
public final String description;
/** Color for tag display. */
public final int color;
public DebugOverlayTag(String name, String description, int color) {
this.name = name;
this.description = description;
this.color = color;
}
}

View File

@@ -0,0 +1,15 @@
load("//ReactNative:DEFS.bzl", "rn_android_library", "react_native_dep")
rn_android_library(
name = "holder",
srcs = glob(["*.java"]),
exported_deps = [
react_native_dep("java/com/facebook/debug/debugoverlay/model:model"),
],
visibility = [
"PUBLIC",
],
deps = [
react_native_dep("third-party/java/jsr-305:jsr-305"),
],
)

View File

@@ -0,0 +1,24 @@
// Copyright 2004-present Facebook. All Rights Reserved.
package com.facebook.debug.holder;
import com.facebook.debug.debugoverlay.model.DebugOverlayTag;
/** No-op implementation of {@link Printer}. */
public class NoopPrinter implements Printer {
public static final NoopPrinter INSTANCE = new NoopPrinter();
private NoopPrinter() {}
@Override
public void logMessage(DebugOverlayTag tag, String message, Object... args) {}
@Override
public void logMessage(DebugOverlayTag tag, String message) {}
@Override
public boolean shouldDisplayLogMessage(final DebugOverlayTag tag) {
return false;
}
}

View File

@@ -0,0 +1,13 @@
// Copyright 2004-present Facebook. All Rights Reserved.
package com.facebook.debug.holder;
import com.facebook.debug.debugoverlay.model.DebugOverlayTag;
/** Interface to debugging tool. */
public interface Printer {
void logMessage(final DebugOverlayTag tag, final String message, Object... args);
void logMessage(final DebugOverlayTag tag, final String message);
boolean shouldDisplayLogMessage(final DebugOverlayTag tag);
}

View File

@@ -0,0 +1,21 @@
// Copyright 2004-present Facebook. All Rights Reserved.
package com.facebook.debug.holder;
/** Holder for debugging tool instance. */
public class PrinterHolder {
private static Printer sPrinter = NoopPrinter.INSTANCE;
public static void setPrinter(Printer printer) {
if (printer == null) {
sPrinter = NoopPrinter.INSTANCE;
} else {
sPrinter = printer;
}
}
public static Printer getPrinter() {
return sPrinter;
}
}

View File

@@ -0,0 +1,15 @@
load("//ReactNative:DEFS.bzl", "rn_android_library", "react_native_dep")
rn_android_library(
name = "tags",
srcs = glob(["*.java"]),
exported_deps = [
react_native_dep("java/com/facebook/debug/debugoverlay/model:model"),
],
visibility = [
"PUBLIC",
],
deps = [
react_native_dep("third-party/java/jsr-305:jsr-305"),
],
)

Some files were not shown because too many files have changed in this diff Show More