Add smoke tests for Hermes debugger (#1120)
* Add Hermes RN sample project to smoke tests * Add Hermes smoke test scenario * Add TestButton click and fix comments * Add checking for the existence of Hermes mark * Bump RN version for the tests * Change TestButton name to AppTestButton
This commit is contained in:
Родитель
2f5c780b48
Коммит
49798d4dfd
|
@ -183,9 +183,8 @@ gulp.task("clean", () => {
|
|||
"!test/resources/sampleReactNative022Project/**/*.js",
|
||||
".vscode-test/",
|
||||
"nls.*.json",
|
||||
"!test/smoke/resources/ReactNativeSample/App.js",
|
||||
"!test/smoke/resources/ExpoSample/App.js",
|
||||
"!test/smoke/resources/PureRNExpoSample/App.js",
|
||||
"!test/smoke/**/*.js",
|
||||
"!test/smoke/**/*.js.map",
|
||||
]
|
||||
return del(pathsToDelete, { force: true });
|
||||
});
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
],
|
||||
"activationEvents": [
|
||||
"onDebugResolve:reactnative",
|
||||
"onDebugResolve:reactnativedirect",
|
||||
"onDebugInitialConfigurations",
|
||||
"onCommand:reactNative.runAndroidSimulator",
|
||||
"onCommand:reactNative.runAndroidDevice",
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
/**
|
||||
* Sample React Native App
|
||||
* https://github.com/facebook/react-native
|
||||
*
|
||||
* @format
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { SafeAreaView, StyleSheet, ScrollView, View, Text, StatusBar } from 'react-native';
|
||||
import { Header, LearnMoreLinks, Colors, DebugInstructions, ReloadInstructions } from 'react-native/Libraries/NewAppScreen';
|
||||
import AppTestButton from './AppTestButton';
|
||||
|
||||
const App: () => React$Node = () => {
|
||||
console.log('Test output from debuggee');
|
||||
return (
|
||||
<>
|
||||
<StatusBar barStyle="dark-content" />
|
||||
<SafeAreaView>
|
||||
<ScrollView
|
||||
contentInsetAdjustmentBehavior="automatic"
|
||||
style={styles.scrollView}>
|
||||
<Header />
|
||||
{global.HermesInternal == null ? null : (
|
||||
<View style={styles.engine}>
|
||||
<Text style={styles.footer}>Engine: Hermes</Text>
|
||||
</View>
|
||||
)}
|
||||
<AppTestButton />
|
||||
</ScrollView>
|
||||
</SafeAreaView>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
scrollView: {
|
||||
backgroundColor: Colors.lighter,
|
||||
},
|
||||
engine: {
|
||||
position: 'absolute',
|
||||
right: 0,
|
||||
},
|
||||
body: {
|
||||
backgroundColor: Colors.white,
|
||||
},
|
||||
sectionContainer: {
|
||||
marginTop: 32,
|
||||
paddingHorizontal: 24,
|
||||
},
|
||||
sectionTitle: {
|
||||
fontSize: 24,
|
||||
fontWeight: '600',
|
||||
color: Colors.black,
|
||||
},
|
||||
sectionDescription: {
|
||||
marginTop: 8,
|
||||
fontSize: 18,
|
||||
fontWeight: '400',
|
||||
color: Colors.dark,
|
||||
},
|
||||
highlight: {
|
||||
fontWeight: '700',
|
||||
},
|
||||
footer: {
|
||||
color: Colors.dark,
|
||||
fontSize: 12,
|
||||
fontWeight: '600',
|
||||
padding: 4,
|
||||
paddingRight: 12,
|
||||
textAlign: 'right',
|
||||
},
|
||||
});
|
||||
|
||||
export default App;
|
|
@ -0,0 +1,25 @@
|
|||
import React, { Component } from 'react';
|
||||
import { View, Button } from 'react-native';
|
||||
|
||||
export default class AppTestButton extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.handleClick = this.handleClick.bind(this);
|
||||
}
|
||||
handleClick() {
|
||||
let testBooleanValue = true;
|
||||
console.log('Test output from Hermes debuggee');
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<View style={{marginTop: 10}}>
|
||||
<Button
|
||||
onPress={this.handleClick}
|
||||
title="Test Button"
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,201 @@
|
|||
apply plugin: "com.android.application"
|
||||
|
||||
import com.android.build.OutputFile
|
||||
|
||||
/**
|
||||
* The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets
|
||||
* and bundleReleaseJsAndAssets).
|
||||
* These basically call `react-native bundle` with the correct arguments during the Android build
|
||||
* cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the
|
||||
* bundle directly from the development server. Below you can see all the possible configurations
|
||||
* and their defaults. If you decide to add a configuration block, make sure to add it before the
|
||||
* `apply from: "../../node_modules/react-native/react.gradle"` line.
|
||||
*
|
||||
* project.ext.react = [
|
||||
* // the name of the generated asset file containing your JS bundle
|
||||
* bundleAssetName: "index.android.bundle",
|
||||
*
|
||||
* // the entry file for bundle generation
|
||||
* entryFile: "index.android.js",
|
||||
*
|
||||
* // https://facebook.github.io/react-native/docs/performance#enable-the-ram-format
|
||||
* bundleCommand: "ram-bundle",
|
||||
*
|
||||
* // whether to bundle JS and assets in debug mode
|
||||
* bundleInDebug: false,
|
||||
*
|
||||
* // whether to bundle JS and assets in release mode
|
||||
* bundleInRelease: true,
|
||||
*
|
||||
* // whether to bundle JS and assets in another build variant (if configured).
|
||||
* // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants
|
||||
* // The configuration property can be in the following formats
|
||||
* // 'bundleIn${productFlavor}${buildType}'
|
||||
* // 'bundleIn${buildType}'
|
||||
* // bundleInFreeDebug: true,
|
||||
* // bundleInPaidRelease: true,
|
||||
* // bundleInBeta: true,
|
||||
*
|
||||
* // whether to disable dev mode in custom build variants (by default only disabled in release)
|
||||
* // for example: to disable dev mode in the staging build type (if configured)
|
||||
* devDisabledInStaging: true,
|
||||
* // The configuration property can be in the following formats
|
||||
* // 'devDisabledIn${productFlavor}${buildType}'
|
||||
* // 'devDisabledIn${buildType}'
|
||||
*
|
||||
* // the root of your project, i.e. where "package.json" lives
|
||||
* root: "../../",
|
||||
*
|
||||
* // where to put the JS bundle asset in debug mode
|
||||
* jsBundleDirDebug: "$buildDir/intermediates/assets/debug",
|
||||
*
|
||||
* // where to put the JS bundle asset in release mode
|
||||
* jsBundleDirRelease: "$buildDir/intermediates/assets/release",
|
||||
*
|
||||
* // where to put drawable resources / React Native assets, e.g. the ones you use via
|
||||
* // require('./image.png')), in debug mode
|
||||
* resourcesDirDebug: "$buildDir/intermediates/res/merged/debug",
|
||||
*
|
||||
* // where to put drawable resources / React Native assets, e.g. the ones you use via
|
||||
* // require('./image.png')), in release mode
|
||||
* resourcesDirRelease: "$buildDir/intermediates/res/merged/release",
|
||||
*
|
||||
* // by default the gradle tasks are skipped if none of the JS files or assets change; this means
|
||||
* // that we don't look at files in android/ or ios/ to determine whether the tasks are up to
|
||||
* // date; if you have any other folders that you want to ignore for performance reasons (gradle
|
||||
* // indexes the entire tree), add them here. Alternatively, if you have JS files in android/
|
||||
* // for example, you might want to remove it from here.
|
||||
* inputExcludes: ["android/**", "ios/**"],
|
||||
*
|
||||
* // override which node gets called and with what additional arguments
|
||||
* nodeExecutableAndArgs: ["node"],
|
||||
*
|
||||
* // supply additional arguments to the packager
|
||||
* extraPackagerArgs: []
|
||||
* ]
|
||||
*/
|
||||
|
||||
project.ext.react = [
|
||||
entryFile: "index.js",
|
||||
enableHermes: true, // clean and rebuild if changing
|
||||
]
|
||||
|
||||
apply from: "../../node_modules/react-native/react.gradle"
|
||||
|
||||
/**
|
||||
* Set this to true to create two separate APKs instead of one:
|
||||
* - An APK that only works on ARM devices
|
||||
* - An APK that only works on x86 devices
|
||||
* The advantage is the size of the APK is reduced by about 4MB.
|
||||
* Upload all the APKs to the Play Store and people will download
|
||||
* the correct one based on the CPU architecture of their device.
|
||||
*/
|
||||
def enableSeparateBuildPerCPUArchitecture = false
|
||||
|
||||
/**
|
||||
* Run Proguard to shrink the Java bytecode in release builds.
|
||||
*/
|
||||
def enableProguardInReleaseBuilds = false
|
||||
|
||||
/**
|
||||
* The preferred build flavor of JavaScriptCore.
|
||||
*
|
||||
* For example, to use the international variant, you can use:
|
||||
* `def jscFlavor = 'org.webkit:android-jsc-intl:+'`
|
||||
*
|
||||
* The international variant includes ICU i18n library and necessary data
|
||||
* allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that
|
||||
* give correct results when using with locales other than en-US. Note that
|
||||
* this variant is about 6MiB larger per architecture than default.
|
||||
*/
|
||||
def jscFlavor = 'org.webkit:android-jsc:+'
|
||||
|
||||
/**
|
||||
* Whether to enable the Hermes VM.
|
||||
*
|
||||
* This should be set on project.ext.react and mirrored here. If it is not set
|
||||
* on project.ext.react, JavaScript will not be compiled to Hermes Bytecode
|
||||
* and the benefits of using Hermes will therefore be sharply reduced.
|
||||
*/
|
||||
def enableHermes = project.ext.react.get("enableHermes", false);
|
||||
|
||||
android {
|
||||
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.latestrnapp"
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
}
|
||||
splits {
|
||||
abi {
|
||||
reset()
|
||||
enable enableSeparateBuildPerCPUArchitecture
|
||||
universalApk false // If true, also generate a universal APK
|
||||
include "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
|
||||
}
|
||||
}
|
||||
signingConfigs {
|
||||
debug {
|
||||
storeFile file('debug.keystore')
|
||||
storePassword 'android'
|
||||
keyAlias 'androiddebugkey'
|
||||
keyPassword 'android'
|
||||
}
|
||||
}
|
||||
buildTypes {
|
||||
debug {
|
||||
signingConfig signingConfigs.debug
|
||||
}
|
||||
release {
|
||||
// Caution! In production, you need to generate your own keystore file.
|
||||
// see https://facebook.github.io/react-native/docs/signed-apk-android.
|
||||
signingConfig signingConfigs.debug
|
||||
minifyEnabled enableProguardInReleaseBuilds
|
||||
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
|
||||
}
|
||||
}
|
||||
// applicationVariants are e.g. debug, release
|
||||
applicationVariants.all { variant ->
|
||||
variant.outputs.each { output ->
|
||||
// For each separate APK per architecture, set a unique version code as described here:
|
||||
// https://developer.android.com/studio/build/configure-apk-splits.html
|
||||
def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4]
|
||||
def abi = output.getFilter(OutputFile.ABI)
|
||||
if (abi != null) { // null for the universal-debug, universal-release variants
|
||||
output.versionCodeOverride =
|
||||
versionCodes.get(abi) * 1048576 + defaultConfig.versionCode
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: "libs", include: ["*.jar"])
|
||||
implementation "com.facebook.react:react-native:+" // From node_modules
|
||||
|
||||
if (enableHermes) {
|
||||
def hermesPath = "../../node_modules/hermes-engine/android/";
|
||||
debugImplementation files(hermesPath + "hermes-debug.aar")
|
||||
releaseImplementation files(hermesPath + "hermes-release.aar")
|
||||
} else {
|
||||
implementation jscFlavor
|
||||
}
|
||||
}
|
||||
|
||||
// Run this once to be able to run the application with BUCK
|
||||
// puts all compile dependencies into folder libs for BUCK to use
|
||||
task copyDownloadableDepsToLibs(type: Copy) {
|
||||
from configurations.compile
|
||||
into 'libs'
|
||||
}
|
||||
|
||||
apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
|
|
@ -0,0 +1,314 @@
|
|||
// Copyright (c) Facebook, Inc. and its affiliates.
|
||||
|
||||
// This source code is licensed under the MIT license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
|
||||
import org.apache.tools.ant.taskdefs.condition.Os
|
||||
|
||||
def config = project.hasProperty("react") ? project.react : [];
|
||||
|
||||
def cliPath = config.cliPath ?: "node_modules/react-native/cli.js"
|
||||
def composeSourceMapsPath = config.composeSourceMapsPath ?: "node_modules/react-native/scripts/compose-source-maps.js"
|
||||
def bundleAssetName = config.bundleAssetName ?: "index.android.bundle"
|
||||
def entryFile = config.entryFile ?: "index.android.js"
|
||||
def bundleCommand = config.bundleCommand ?: "bundle"
|
||||
def reactRoot = file(config.root ?: "../../")
|
||||
def inputExcludes = config.inputExcludes ?: ["android/**", "ios/**"]
|
||||
def bundleConfig = config.bundleConfig ? "${reactRoot}/${config.bundleConfig}" : null ;
|
||||
def enableVmCleanup = config.enableVmCleanup == null ? true : config.enableVmCleanup
|
||||
def hermesCommand = config.hermesCommand ?: "../../node_modules/hermes-engine/%OS-BIN%/hermes"
|
||||
|
||||
def reactNativeDevServerPort() {
|
||||
def value = project.getProperties().get("reactNativeDevServerPort")
|
||||
return value != null ? value : "8081"
|
||||
}
|
||||
|
||||
def reactNativeInspectorProxyPort() {
|
||||
def value = project.getProperties().get("reactNativeInspectorProxyPort")
|
||||
return value != null ? value : reactNativeDevServerPort()
|
||||
}
|
||||
|
||||
def getHermesOSBin() {
|
||||
if (Os.isFamily(Os.FAMILY_WINDOWS)) return "win64-bin";
|
||||
if (Os.isFamily(Os.FAMILY_MAC)) return "osx-bin";
|
||||
if (Os.isOs(null, "linux", "amd64", null)) return "linux64-bin";
|
||||
throw new Exception("OS not recognized. Please set project.ext.react.hermesCommand " +
|
||||
"to the path of a working Hermes compiler.");
|
||||
}
|
||||
|
||||
// Make sure not to inspect the Hermes config unless we need it,
|
||||
// to avoid breaking any JSC-only setups.
|
||||
def getHermesCommand = {
|
||||
// If the project specifies a Hermes command, don't second guess it.
|
||||
if (!hermesCommand.contains("%OS-BIN%")) {
|
||||
return hermesCommand
|
||||
}
|
||||
|
||||
// Execution on Windows fails with / as separator
|
||||
return hermesCommand
|
||||
.replaceAll("%OS-BIN%", getHermesOSBin())
|
||||
.replace('/' as char, File.separatorChar);
|
||||
}
|
||||
|
||||
// Set enableHermesForVariant to a function to configure per variant,
|
||||
// or set `enableHermes` to True/False to set all of them
|
||||
def enableHermesForVariant = config.enableHermesForVariant ?: {
|
||||
def variant -> config.enableHermes ?: false
|
||||
}
|
||||
|
||||
android {
|
||||
buildTypes.all {
|
||||
resValue "integer", "react_native_dev_server_port", reactNativeDevServerPort()
|
||||
resValue "integer", "react_native_inspector_proxy_port", reactNativeInspectorProxyPort()
|
||||
}
|
||||
}
|
||||
|
||||
afterEvaluate {
|
||||
def isAndroidLibrary = plugins.hasPlugin("com.android.library")
|
||||
def variants = isAndroidLibrary ? android.libraryVariants : android.applicationVariants
|
||||
variants.all { def variant ->
|
||||
// Create variant and target names
|
||||
def targetName = variant.name.capitalize()
|
||||
def targetPath = variant.dirName
|
||||
|
||||
// React js bundle directories
|
||||
def jsBundleDir = file("$buildDir/generated/assets/react/${targetPath}")
|
||||
def resourcesDir = file("$buildDir/generated/res/react/${targetPath}")
|
||||
|
||||
def jsBundleFile = file("$jsBundleDir/$bundleAssetName")
|
||||
def jsSourceMapsDir = file("$buildDir/generated/sourcemaps/react/${targetPath}")
|
||||
def jsIntermediateSourceMapsDir = file("$buildDir/intermediates/sourcemaps/react/${targetPath}")
|
||||
def jsPackagerSourceMapFile = file("$jsIntermediateSourceMapsDir/${bundleAssetName}.packager.map")
|
||||
def jsCompilerSourceMapFile = file("$jsIntermediateSourceMapsDir/${bundleAssetName}.compiler.map")
|
||||
def jsOutputSourceMapFile = file("$jsSourceMapsDir/${bundleAssetName}.map")
|
||||
|
||||
// Additional node and packager commandline arguments
|
||||
def nodeExecutableAndArgs = config.nodeExecutableAndArgs ?: ["node"]
|
||||
def extraPackagerArgs = config.extraPackagerArgs ?: []
|
||||
|
||||
def enableHermes = enableHermesForVariant(variant)
|
||||
|
||||
def currentBundleTask = tasks.create(
|
||||
name: "bundle${targetName}JsAndAssets",
|
||||
type: Exec) {
|
||||
group = "react"
|
||||
description = "bundle JS and assets for ${targetName}."
|
||||
|
||||
// Create dirs if they are not there (e.g. the "clean" task just ran)
|
||||
doFirst {
|
||||
jsBundleDir.deleteDir()
|
||||
jsBundleDir.mkdirs()
|
||||
resourcesDir.deleteDir()
|
||||
resourcesDir.mkdirs()
|
||||
jsIntermediateSourceMapsDir.deleteDir()
|
||||
jsIntermediateSourceMapsDir.mkdirs()
|
||||
jsSourceMapsDir.deleteDir()
|
||||
jsSourceMapsDir.mkdirs()
|
||||
}
|
||||
|
||||
// Set up inputs and outputs so gradle can cache the result
|
||||
inputs.files fileTree(dir: reactRoot, excludes: inputExcludes)
|
||||
outputs.dir(jsBundleDir)
|
||||
outputs.dir(resourcesDir)
|
||||
|
||||
// Set up the call to the react-native cli
|
||||
workingDir(reactRoot)
|
||||
|
||||
// Set up dev mode
|
||||
def devEnabled = !(config."devDisabledIn${targetName}"
|
||||
|| targetName.toLowerCase().contains("release"))
|
||||
|
||||
def extraArgs = extraPackagerArgs;
|
||||
|
||||
if (bundleConfig) {
|
||||
extraArgs = extraArgs.clone()
|
||||
extraArgs.add("--config");
|
||||
extraArgs.add(bundleConfig);
|
||||
}
|
||||
|
||||
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
|
||||
commandLine("cmd", "/c", *nodeExecutableAndArgs, cliPath, bundleCommand, "--platform", "android", "--dev", "${devEnabled}",
|
||||
"--reset-cache", "--entry-file", entryFile, "--bundle-output", jsBundleFile, "--assets-dest", resourcesDir,
|
||||
"--sourcemap-output", enableHermes ? jsPackagerSourceMapFile : jsOutputSourceMapFile, *extraArgs)
|
||||
} else {
|
||||
commandLine(*nodeExecutableAndArgs, cliPath, bundleCommand, "--platform", "android", "--dev", "${devEnabled}",
|
||||
"--reset-cache", "--entry-file", entryFile, "--bundle-output", jsBundleFile, "--assets-dest", resourcesDir,
|
||||
"--sourcemap-output", enableHermes ? jsPackagerSourceMapFile : jsOutputSourceMapFile, *extraArgs)
|
||||
}
|
||||
|
||||
if (enableHermes) {
|
||||
doLast {
|
||||
def hermesFlags;
|
||||
def hbcTempFile = file("${jsBundleFile}.hbc")
|
||||
exec {
|
||||
if (targetName.toLowerCase().contains("release")) {
|
||||
// Can't use ?: since that will also substitute valid empty lists
|
||||
hermesFlags = config.hermesFlagsRelease
|
||||
if (hermesFlags == null) hermesFlags = ["-O", "-output-source-map"]
|
||||
} else {
|
||||
hermesFlags = config.hermesFlagsDebug
|
||||
if (hermesFlags == null) hermesFlags = []
|
||||
}
|
||||
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
|
||||
commandLine("cmd", "/c", getHermesCommand(), "-emit-binary", "-out", hbcTempFile, jsBundleFile, *hermesFlags)
|
||||
} else {
|
||||
commandLine(getHermesCommand(), "-emit-binary", "-out", hbcTempFile, jsBundleFile, *hermesFlags)
|
||||
}
|
||||
}
|
||||
ant.move(
|
||||
file: hbcTempFile,
|
||||
toFile: jsBundleFile
|
||||
);
|
||||
if (hermesFlags.contains("-output-source-map")) {
|
||||
ant.move(
|
||||
// Hermes will generate a source map with this exact name
|
||||
file: "${jsBundleFile}.hbc.map",
|
||||
tofile: jsCompilerSourceMapFile
|
||||
);
|
||||
exec {
|
||||
// TODO: set task dependencies for caching
|
||||
|
||||
// Set up the call to the compose-source-maps script
|
||||
workingDir(reactRoot)
|
||||
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
|
||||
commandLine("cmd", "/c", *nodeExecutableAndArgs, composeSourceMapsPath, jsPackagerSourceMapFile, jsCompilerSourceMapFile, "-o", jsOutputSourceMapFile)
|
||||
} else {
|
||||
commandLine(*nodeExecutableAndArgs, composeSourceMapsPath, jsPackagerSourceMapFile, jsCompilerSourceMapFile, "-o", jsOutputSourceMapFile)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enabled config."bundleIn${targetName}" != null
|
||||
? config."bundleIn${targetName}"
|
||||
: config."bundleIn${variant.buildType.name.capitalize()}" != null
|
||||
? config."bundleIn${variant.buildType.name.capitalize()}"
|
||||
: targetName.toLowerCase().contains("release")
|
||||
}
|
||||
|
||||
// Expose a minimal interface on the application variant and the task itself:
|
||||
variant.ext.bundleJsAndAssets = currentBundleTask
|
||||
currentBundleTask.ext.generatedResFolders = files(resourcesDir).builtBy(currentBundleTask)
|
||||
currentBundleTask.ext.generatedAssetsFolders = files(jsBundleDir).builtBy(currentBundleTask)
|
||||
|
||||
// registerGeneratedResFolders for Android plugin 3.x
|
||||
if (variant.respondsTo("registerGeneratedResFolders")) {
|
||||
variant.registerGeneratedResFolders(currentBundleTask.generatedResFolders)
|
||||
} else {
|
||||
variant.registerResGeneratingTask(currentBundleTask)
|
||||
}
|
||||
variant.mergeResourcesProvider.get().dependsOn(currentBundleTask)
|
||||
|
||||
// packageApplication for Android plugin 3.x
|
||||
def packageTask = variant.hasProperty("packageApplication")
|
||||
? variant.packageApplicationProvider.get()
|
||||
: tasks.findByName("package${targetName}")
|
||||
if (variant.hasProperty("packageLibrary")) {
|
||||
packageTask = variant.packageLibrary
|
||||
}
|
||||
|
||||
// pre bundle build task for Android plugin 3.2+
|
||||
def buildPreBundleTask = tasks.findByName("build${targetName}PreBundle")
|
||||
|
||||
def resourcesDirConfigValue = config."resourcesDir${targetName}"
|
||||
if (resourcesDirConfigValue) {
|
||||
def currentCopyResTask = tasks.create(
|
||||
name: "copy${targetName}BundledResources",
|
||||
type: Copy) {
|
||||
group = "react"
|
||||
description = "copy bundled resources into custom location for ${targetName}."
|
||||
|
||||
from(resourcesDir)
|
||||
into(file(resourcesDirConfigValue))
|
||||
|
||||
dependsOn(currentBundleTask)
|
||||
|
||||
enabled(currentBundleTask.enabled)
|
||||
}
|
||||
|
||||
packageTask.dependsOn(currentCopyResTask)
|
||||
if (buildPreBundleTask != null) {
|
||||
buildPreBundleTask.dependsOn(currentCopyResTask)
|
||||
}
|
||||
}
|
||||
|
||||
def currentAssetsCopyTask = tasks.create(
|
||||
name: "copy${targetName}BundledJs",
|
||||
type: Copy) {
|
||||
group = "react"
|
||||
description = "copy bundled JS into ${targetName}."
|
||||
|
||||
if (config."jsBundleDir${targetName}") {
|
||||
from(jsBundleDir)
|
||||
into(file(config."jsBundleDir${targetName}"))
|
||||
} else {
|
||||
into ("$buildDir/intermediates")
|
||||
into ("assets/${targetPath}") {
|
||||
from(jsBundleDir)
|
||||
}
|
||||
|
||||
// Workaround for Android Gradle Plugin 3.2+ new asset directory
|
||||
into ("merged_assets/${variant.name}/merge${targetName}Assets/out") {
|
||||
from(jsBundleDir)
|
||||
}
|
||||
|
||||
// Workaround for Android Gradle Plugin 3.4+ new asset directory
|
||||
into ("merged_assets/${variant.name}/out") {
|
||||
from(jsBundleDir)
|
||||
}
|
||||
}
|
||||
|
||||
// mergeAssets must run first, as it clears the intermediates directory
|
||||
dependsOn(variant.mergeAssetsProvider.get())
|
||||
|
||||
enabled(currentBundleTask.enabled)
|
||||
}
|
||||
|
||||
packageTask.dependsOn(currentAssetsCopyTask)
|
||||
if (buildPreBundleTask != null) {
|
||||
buildPreBundleTask.dependsOn(currentAssetsCopyTask)
|
||||
}
|
||||
|
||||
// Delete the VM related libraries that this build doesn't need.
|
||||
// The application can manage this manually by setting 'enableVmCleanup: false'
|
||||
//
|
||||
// This should really be done by packaging all Hermes releated libs into
|
||||
// two separate HermesDebug and HermesRelease AARs, but until then we'll
|
||||
// kludge it by deleting the .so files out of the /transforms/ directory.
|
||||
def isRelease = targetName.toLowerCase().contains("release")
|
||||
def libDir = "$buildDir/intermediates/transforms/"
|
||||
def vmSelectionAction = {
|
||||
fileTree(libDir).matching {
|
||||
if (enableHermes) {
|
||||
// For Hermes, delete all the libjsc* files
|
||||
include "**/libjsc*.so"
|
||||
|
||||
if (isRelease) {
|
||||
// Reduce size by deleting the debugger/inspector
|
||||
include '**/libhermes-inspector.so'
|
||||
include '**/libhermes-executor-debug.so'
|
||||
} else {
|
||||
// Release libs take precedence and must be removed
|
||||
// to allow debugging
|
||||
include '**/libhermes-executor-release.so'
|
||||
}
|
||||
} else {
|
||||
// For JSC, delete all the libhermes* files
|
||||
include "**/libhermes*.so"
|
||||
}
|
||||
}.visit { details ->
|
||||
def targetVariant = ".*/transforms/[^/]*/${targetPath}/.*"
|
||||
def path = details.file.getAbsolutePath().replace(File.separatorChar, '/' as char)
|
||||
if (path.matches(targetVariant) && details.file.isFile()) {
|
||||
details.file.delete()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (enableVmCleanup) {
|
||||
def task = tasks.findByName("package${targetName}")
|
||||
task.doFirst(vmSelectionAction)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,6 +8,19 @@
|
|||
"request": "launch",
|
||||
"platform": "android"
|
||||
},
|
||||
{
|
||||
"name": "Debug Android (Hermes) - Experimental",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"type": "reactnativedirect",
|
||||
"request": "launch",
|
||||
"platform": "android"
|
||||
},
|
||||
{
|
||||
"name": "Attach to packager (Hermes) - Experimental",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"type": "reactnativedirect",
|
||||
"request": "attach"
|
||||
},
|
||||
{
|
||||
"name": "Debug iOS",
|
||||
"cwd": "${workspaceFolder}",
|
||||
|
|
|
@ -12,6 +12,7 @@ const DEBUG_OPTIONS_COMBOBOX_OPENED = `${DEBUG_OPTIONS_COMBOBOX}.monaco-select-b
|
|||
const CONFIGURE = `div[id="workbench.parts.sidebar"] .actions-container .configure`;
|
||||
const START = `.icon[title="Start Debugging"]`;
|
||||
const STOP = `.debug-toolbar .action-label[title*=\"Stop\"]`;
|
||||
const DISCONNECT = `.debug-toolbar .action-label[title*=\"Disconnect\"]`;
|
||||
const STEP_OVER = `.debug-toolbar .action-label[title*=\"Step Over\"]`;
|
||||
const STEP_IN = `.debug-toolbar .action-label[title*=\"Step Into\"]`;
|
||||
const STEP_OUT = `.debug-toolbar .action-label[title*=\"Step Out\"]`;
|
||||
|
@ -101,6 +102,11 @@ export class Debug extends Viewlet {
|
|||
await this.spectron.client.waitForElement(NOT_DEBUG_STATUS_BAR);
|
||||
}
|
||||
|
||||
public async disconnectFromDebugger(): Promise<any> {
|
||||
await this.spectron.client.waitAndClick(DISCONNECT);
|
||||
await this.spectron.client.waitForElement(NOT_DEBUG_STATUS_BAR);
|
||||
}
|
||||
|
||||
public async waitForStackFrame(func: (stackFrame: IStackFrame) => boolean, message: string): Promise<IStackFrame> {
|
||||
return await this.spectron.client.waitFor(async () => {
|
||||
const stackFrames = await this.getStackFrames();
|
||||
|
|
|
@ -7,7 +7,7 @@ import { AppiumHelper, Platform, AppiumClient } from "./helpers/appiumHelper";
|
|||
import { AndroidEmulatorHelper } from "./helpers/androidEmulatorHelper";
|
||||
import { sleep } from "./helpers/utilities";
|
||||
import { SmokeTestsConstants } from "./helpers/smokeTestsConstants";
|
||||
import { ExpoWorkspacePath, pureRNWorkspacePath, RNworkspacePath, runVSCode } from "./main";
|
||||
import { ExpoWorkspacePath, pureRNWorkspacePath, RNworkspacePath, prepareReactNativeProjectForHermesTesting, runVSCode } from "./main";
|
||||
import { SetupEnvironmentHelper } from "./helpers/setupEnvironmentHelper";
|
||||
import { TestRunArguments } from "./helpers/configHelper";
|
||||
|
||||
|
@ -16,8 +16,10 @@ const RN_APP_ACTIVITY_NAME = "com.latestrnapp.MainActivity";
|
|||
const EXPO_APP_PACKAGE_NAME = SetupEnvironmentHelper.expoPackageName;
|
||||
const EXPO_APP_ACTIVITY_NAME = `${EXPO_APP_PACKAGE_NAME}.experience.HomeActivity`;
|
||||
const RNDebugConfigName = "Debug Android";
|
||||
const RNHermesDebugConfigName = "Debug Android (Hermes) - Experimental";
|
||||
const ExpoDebugConfigName = "Debug in Exponent";
|
||||
const RNSetBreakpointOnLine = 14;
|
||||
const RNHermesSetBreakpointOnLine = 11;
|
||||
const ExpoSetBreakpointOnLine = 12;
|
||||
const PureRNExpoSetBreakpointOnLine = 23;
|
||||
// Time for Android Debug Test before it reaches timeout
|
||||
|
@ -72,6 +74,53 @@ export function setup(testParameters?: TestRunArguments) {
|
|||
console.log("Android Debug test: Debugging is stopped");
|
||||
});
|
||||
|
||||
it("Hermes RN app Debug test", async function () {
|
||||
this.timeout(debugAndroidTestTime);
|
||||
prepareReactNativeProjectForHermesTesting();
|
||||
AndroidEmulatorHelper.uninstallTestAppFromEmulator(RN_APP_PACKAGE_NAME);
|
||||
app = await runVSCode(RNworkspacePath);
|
||||
await app.workbench.explorer.openExplorerView();
|
||||
await app.workbench.explorer.openFile("AppTestButton.js");
|
||||
await app.runCommand("cursorTop");
|
||||
console.log("Android Debug Hermes test: AppTestButton.js file is opened");
|
||||
await app.workbench.debug.setBreakpointOnLine(RNHermesSetBreakpointOnLine);
|
||||
console.log(`Android Debug Hermes test: Breakpoint is set on line ${RNHermesSetBreakpointOnLine}`);
|
||||
await app.workbench.debug.openDebugViewlet();
|
||||
console.log(`Android Debug Hermes test: Debug Viewlet opened`);
|
||||
await app.workbench.debug.chooseDebugConfiguration(RNHermesDebugConfigName);
|
||||
console.log(`Android Debug Hermes test: Chosen debug configuration: ${RNHermesDebugConfigName}`);
|
||||
console.log("Android Debug Hermes test: Starting debugging");
|
||||
await app.workbench.debug.startDebugging();
|
||||
const opts = AppiumHelper.prepareAttachOptsForAndroidActivity(RN_APP_PACKAGE_NAME, RN_APP_ACTIVITY_NAME, AndroidEmulatorHelper.androidEmulatorName);
|
||||
await AndroidEmulatorHelper.checkIfAppIsInstalled(RN_APP_PACKAGE_NAME, SmokeTestsConstants.androidAppBuildAndInstallTimeout);
|
||||
let client = AppiumHelper.webdriverAttach(opts);
|
||||
clientInited = client.init();
|
||||
await app.workbench.debug.waitForDebuggingToStart();
|
||||
console.log("Android Debug Hermes test: Debugging started");
|
||||
console.log("Android Debug Hermes test: Checking for Hermes mark");
|
||||
let isHermesWorking = await AppiumHelper.isHermesWorking(clientInited);
|
||||
assert.equal(isHermesWorking, true);
|
||||
console.log("Android Debug Hermes test: Reattaching to Hermes app");
|
||||
await app.workbench.debug.stopDebugging();
|
||||
await app.workbench.debug.chooseDebugConfiguration("Attach to packager (Hermes) - Experimental");
|
||||
await app.workbench.debug.startDebugging();
|
||||
console.log("Android Debug Hermes test: Reattached successfully");
|
||||
await sleep(7000);
|
||||
console.log("Android Debug Hermes test: Click Test Button");
|
||||
await AppiumHelper.clickTestButtonHermes(clientInited);
|
||||
await app.workbench.debug.waitForStackFrame(sf => sf.name === "AppTestButton.js" && sf.lineNumber === RNHermesSetBreakpointOnLine, `looking for AppTestButton.js and line ${RNHermesSetBreakpointOnLine}`);
|
||||
console.log("Android Debug Hermes test: Stack frame found");
|
||||
await app.workbench.debug.continue();
|
||||
// await for our debug string renders in debug console
|
||||
await sleep(SmokeTestsConstants.debugConsoleSearchTimeout);
|
||||
console.log("Android Debug Hermes test: Searching for \"Test output from Hermes debuggee\" string in console");
|
||||
let found = await app.workbench.debug.findStringInConsole("Test output from Hermes debuggee", 10000);
|
||||
assert.notStrictEqual(found, false, "\"Test output from Hermes debuggee\" string is missing in debug console");
|
||||
console.log("Android Debug test: \"Test output from Hermes debuggee\" string is found");
|
||||
await app.workbench.debug.disconnectFromDebugger();
|
||||
console.log("Android Debug Hermes test: Debugging is stopped");
|
||||
});
|
||||
|
||||
it("Expo app Debug test", async function () {
|
||||
if (testParameters && testParameters.RunBasicTests) {
|
||||
this.skip();
|
||||
|
|
|
@ -15,7 +15,7 @@ import { TestRunArguments } from "./helpers/configHelper";
|
|||
const RnAppBundleId = "org.reactjs.native.example.latestRNApp";
|
||||
const RNDebugConfigName = "Debug iOS";
|
||||
const ExpoDebugConfigName = "Debug in Exponent";
|
||||
const RNSetBreakpointOnLine = 14;
|
||||
const RNSetBreakpointOnLine = 15;
|
||||
const ExpoSetBreakpointOnLine = 12;
|
||||
const PureRNExpoSetBreakpointOnLine = 23;
|
||||
// Time for OS Debug Test before it reaches timeout
|
||||
|
|
|
@ -144,6 +144,15 @@ export class AndroidEmulatorHelper {
|
|||
});
|
||||
}
|
||||
|
||||
public static uninstallTestAppFromEmulator(appPackage: string) {
|
||||
console.log(`*** Uninstalling test app ${appPackage}' from Emulator`);
|
||||
try {
|
||||
cp.spawnSync("adb", ["shell", "pm", "uninstall", appPackage], {stdio: "inherit"});
|
||||
} catch (e) {
|
||||
console.error(`Error occured while uninstalling test app:\n ${e}`);
|
||||
}
|
||||
}
|
||||
|
||||
public static async enableDrawPermitForApp(packageName: string) {
|
||||
const drawPermitCommand = `adb -s ${AndroidEmulatorHelper.androidEmulatorName} shell appops set ${packageName} SYSTEM_ALERT_WINDOW allow`;
|
||||
console.log(`*** Enabling permission for drawing over apps via: ${drawPermitCommand}`);
|
||||
|
|
|
@ -243,6 +243,19 @@ export class AppiumHelper {
|
|||
}
|
||||
}
|
||||
|
||||
public static async clickTestButtonHermes(client: AppiumClient) {
|
||||
console.log(`*** Pressing button with text "Test Button"...`);
|
||||
const TEST_BUTTON = "//*[@text='TEST BUTTON']";
|
||||
await client.click(TEST_BUTTON);
|
||||
}
|
||||
|
||||
public static async isHermesWorking(client: AppiumClient): Promise<boolean> {
|
||||
const HERMES_MARK = "//*[@text='Engine: Hermes']";
|
||||
return await client
|
||||
.waitForExist(HERMES_MARK, 30 * 1000)
|
||||
.isExisting(HERMES_MARK);
|
||||
}
|
||||
|
||||
private static async openExpoAppViaClipboardAndroid(client: AppiumClient, clipboard: Electron.Clipboard, expoURL: string) {
|
||||
// Expo application automatically detects Expo URLs in the clipboard
|
||||
// So we are copying expoURL to system clipboard and click on the special "Open from Clipboard" UI element
|
||||
|
|
|
@ -27,9 +27,9 @@ export class SetupEnvironmentHelper {
|
|||
console.log(`*** Creating RN app via '${command}' in ${workspacePath}...`);
|
||||
cp.execSync(command, { cwd: resourcesPath, stdio: "inherit" });
|
||||
|
||||
let customEntryPointFile = path.join(resourcesPath, customEntryPointFolder, "App.js");
|
||||
let launchConfigFile = path.join(resourcesPath, "launch.json");
|
||||
let vsCodeConfigPath = path.join(workspacePath, ".vscode");
|
||||
const customEntryPointFile = path.join(resourcesPath, customEntryPointFolder, "App.js");
|
||||
const launchConfigFile = path.join(resourcesPath, "launch.json");
|
||||
const vsCodeConfigPath = path.join(workspacePath, ".vscode");
|
||||
|
||||
console.log(`*** Copying ${customEntryPointFile} into ${workspaceFilePath}...`);
|
||||
fs.writeFileSync(workspaceFilePath, fs.readFileSync(customEntryPointFile));
|
||||
|
@ -45,6 +45,24 @@ export class SetupEnvironmentHelper {
|
|||
SetupEnvironmentHelper.patchMetroConfig(workspacePath);
|
||||
}
|
||||
|
||||
public static prepareHermesReactNativeApplication(workspaceFilePath: string, resourcesPath: string, workspacePath: string, appName: string, customEntryPointFolder: string, version?: string) {
|
||||
const commandClean = path.join(workspacePath, "android", "gradlew") + " clean";
|
||||
|
||||
console.log(`*** Executing ${commandClean} ...`);
|
||||
cp.execSync(commandClean, { cwd: path.join(workspacePath, "android"), stdio: "inherit" });
|
||||
|
||||
const customEntryPointFile = path.join(resourcesPath, customEntryPointFolder, "App.js");
|
||||
const testButtonPath = path.join(resourcesPath, customEntryPointFolder, "AppTestButton.js");
|
||||
|
||||
console.log(`*** Copying ${customEntryPointFile} into ${workspaceFilePath}...`);
|
||||
fs.writeFileSync(workspaceFilePath, fs.readFileSync(customEntryPointFile));
|
||||
|
||||
SetupEnvironmentHelper.copyGradleFilesToHermesApp(workspacePath, resourcesPath, customEntryPointFolder);
|
||||
|
||||
console.log(`*** Copying ${testButtonPath} into ${workspacePath}`);
|
||||
fs.copyFileSync(testButtonPath, path.join(workspacePath, "AppTestButton.js"));
|
||||
}
|
||||
|
||||
public static prepareExpoApplication(workspaceFilePath: string, resourcesPath: string, workspacePath: string, appName: string) {
|
||||
const command = `echo -ne '\\n' | expo init -t tabs --name ${appName} ${appName}`;
|
||||
console.log(`*** Creating Expo app via '${command}' in ${workspacePath}...`);
|
||||
|
@ -278,4 +296,17 @@ module.exports.hasteMapCacheDirectory = ".cache";`;
|
|||
const contentAfterPatching = fs.readFileSync(metroConfigPath);
|
||||
console.log(`*** Content of a metro.config.js after patching: ${contentAfterPatching}`);
|
||||
}
|
||||
|
||||
private static copyGradleFilesToHermesApp(workspacePath: string, resourcesPath: string, customEntryPointFolder: string) {
|
||||
const appGradleBuildFilePath = path.join(workspacePath, "android", "app", "build.gradle");
|
||||
const resGradleBuildFilePath = path.join(resourcesPath, customEntryPointFolder, "build.gradle");
|
||||
const resReactGradleFilePath = path.join(resourcesPath, customEntryPointFolder, "react.gradle"); // TODO: remove after react-native Gradle configuration fix (https://github.com/facebook/react-native/issues/25599)
|
||||
const projReactGradleFilePath = path.join(workspacePath, "node_modules", "react-native", "react.gradle"); // TODO: remove after react-native Gradle configuration fix (https://github.com/facebook/react-native/issues/25599)
|
||||
|
||||
console.log(`*** Copying ${resGradleBuildFilePath} into ${appGradleBuildFilePath}...`);
|
||||
fs.writeFileSync(appGradleBuildFilePath, fs.readFileSync(resGradleBuildFilePath));
|
||||
|
||||
console.log(`*** Copying ${resReactGradleFilePath} into ${projReactGradleFilePath}...`); // TODO: remove after react-native Gradle configuration fix (https://github.com/facebook/react-native/issues/25599)
|
||||
fs.writeFileSync(projReactGradleFilePath, fs.readFileSync(resReactGradleFilePath));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -167,6 +167,10 @@ function createApp(quality: Quality, workspaceOrFolder: string): SpectronApplica
|
|||
});
|
||||
}
|
||||
|
||||
export function prepareReactNativeProjectForHermesTesting() {
|
||||
SetupEnvironmentHelper.prepareHermesReactNativeApplication(RNworkspaceFilePath, resourcesPath, RNworkspacePath, SmokeTestsConstants.RNAppName, "HermesReactNativeSample", process.env.RN_VERSION);
|
||||
}
|
||||
|
||||
const testParams = TestConfigurator.parseTestArguments();
|
||||
async function setup(): Promise<void> {
|
||||
console.log("*** Test VS Code directory:", testVSCodeDirectory);
|
||||
|
|
Загрузка…
Ссылка в новой задаче