From 22cf386f505b5c741156dcccea2d4b4075d338b0 Mon Sep 17 00:00:00 2001 From: Overtorment Date: Mon, 16 Mar 2020 13:51:08 +0000 Subject: [PATCH] TST: wip detox on circleci --- .flowconfig | 2 +- .travis.yml | 115 +++++ android/app/build.gradle | 14 + .../io/bluewallet/bluewallet/DetoxTest.java | 24 + android/build.gradle | 20 +- android/gradle.properties | 6 + ios/BlueWallet.xcodeproj/project.pbxproj | 52 +-- ios/BlueWallet/Info.plist | 2 +- ios/Podfile | 15 +- package-lock.json | 429 ++++++++++++++++-- package.json | 37 +- screen/wallets/list.js | 2 +- tests/e2e/addWallet.spec.js | 11 + tests/e2e/android-wait-for-emulator.sh | 26 ++ tests/e2e/config.json | 6 + tests/e2e/detox-build-release-apk.sh | 27 ++ tests/e2e/detox-prepare-android-emu.sh | 2 + tests/e2e/init.js | 40 ++ 18 files changed, 743 insertions(+), 87 deletions(-) create mode 100644 .travis.yml create mode 100644 android/app/src/androidTest/java/io/bluewallet/bluewallet/DetoxTest.java create mode 100644 tests/e2e/addWallet.spec.js create mode 100644 tests/e2e/android-wait-for-emulator.sh create mode 100644 tests/e2e/config.json create mode 100755 tests/e2e/detox-build-release-apk.sh create mode 100644 tests/e2e/detox-prepare-android-emu.sh create mode 100644 tests/e2e/init.js diff --git a/.flowconfig b/.flowconfig index ebf6585f..0b40d728 100644 --- a/.flowconfig +++ b/.flowconfig @@ -67,4 +67,4 @@ suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError [version] -^0.97.0 +^0.102.0 diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..f7b1ecd0 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,115 @@ +# stolen from https://github.com/andstatus/todoagenda/blob/master/.travis.yml + +# Based on https://travis-ci.org/ankidroid/Anki-Android/builds/624268367 +# See also https://travis-ci.community/t/is-android-28-emulator-supported/1718/6 +sudo: true +language: bash +# ignored on non-linux platforms, but bionic is required for nested virtualization +dist: bionic + +stages: + - install + - unit_test # custom stage defined in jobs::include section + - test + - finalize_coverage # custom stage defined in jobs::include section + - cache + +env: + global: + - NODE_VERSION=stable + - COMPILE_API=29 + - ANDROID_BUILD_TOOLS=29.0.2 + - ABI=x86_64 + - ADB_INSTALL_TIMEOUT=8 + - ANDROID_HOME=${HOME}/android-sdk + - ANDROID_TOOLS_URL="https://dl.google.com/android/repository/sdk-tools-linux-4333796.zip" + - EMU_FLAVOR=default # use google_apis flavor if no default flavor emulator + - GRAVIS="https://raw.githubusercontent.com/DanySK/Gravis-CI/master/" + - JDK="1.8" + - TOOLS=${ANDROID_HOME}/tools + # PATH order is incredibly important. e.g. the 'emulator' script exists in more than one place! + - PATH=${ANDROID_HOME}:${ANDROID_HOME}/emulator:${TOOLS}:${TOOLS}/bin:${ANDROID_HOME}/platform-tools:${PATH} + - UNIT_TEST=FALSE # by default we don't run the unit tests, they are run only in specific builds + - FINALIZE_COVERAGE=FALSE # by default we don't finalize coverage, it is done in one specific build + matrix: + - API=28 + +before_install: + # This section may run on all platforms, and may run for unit tests or for coverage finalization + # It should not make assumptions about os platform or desired tool installation + + # Set up JDK 8 for Android SDK - Java is universally needed: codacy, unit tests, emulators + - curl "${GRAVIS}.install-jdk-travis.sh" --output ~/.install-jdk-travis.sh + - export TARGET_JDK="${JDK}" + - JDK="1.8" + - source ~/.install-jdk-travis.sh + + # Set up Android SDK - this is needed everywhere but coverage finalization, so toggle on that + - if [ "$FINALIZE_COVERAGE" = "FALSE" ]; then wget -q "${ANDROID_TOOLS_URL}" -O android-sdk-tools.zip; fi + - if [ "$FINALIZE_COVERAGE" = "FALSE" ]; then unzip -q android-sdk-tools.zip -d ${ANDROID_HOME}; fi + - if [ "$FINALIZE_COVERAGE" = "FALSE" ]; then rm android-sdk-tools.zip; fi + - if [ "$FINALIZE_COVERAGE" = "FALSE" ]; then mkdir ~/.android; fi # avoid harmless sdkmanager warning + - if [ "$FINALIZE_COVERAGE" = "FALSE" ]; then echo 'count=0' > ~/.android/repositories.cfg; fi # avoid harmless sdkmanager warning + - if [ "$FINALIZE_COVERAGE" = "FALSE" ]; then yes | sdkmanager --licenses >/dev/null; fi # accept all sdkmanager warnings + - if [ "$FINALIZE_COVERAGE" = "FALSE" ]; then echo y | sdkmanager --no_https "platform-tools" >/dev/null; fi + - if [ "$FINALIZE_COVERAGE" = "FALSE" ]; then echo y | sdkmanager --no_https "tools" >/dev/null; fi # A second time per Travis docs, gets latest versions + - if [ "$FINALIZE_COVERAGE" = "FALSE" ]; then echo y | sdkmanager --no_https "build-tools;${ANDROID_BUILD_TOOLS}" >/dev/null; fi # Implicit gradle dependency - gradle drives changes + - if [ "$FINALIZE_COVERAGE" = "FALSE" ]; then echo y | sdkmanager --no_https "platforms;android-${COMPILE_API}" >/dev/null; fi # We need the API of the current compileSdkVersion from gradle.properties + +install: + # In our setup, install only runs on matrix entries we want full emulator tests on + # That only happens currently on linux, so this section can assume linux + emulator is desired + # Download required emulator tools + - echo y | sdkmanager --no_https "platforms;android-$API" >/dev/null # We need the API of the emulator we will run + - echo y | sdkmanager --no_https "emulator" >/dev/null + - echo y | sdkmanager --no_https "system-images;android-$API;$EMU_FLAVOR;$ABI" >/dev/null # install our emulator + + # Set up KVM on linux for hardware acceleration. Manually here so it only happens for emulator tests, takes ~30s + - sudo -E apt-get -yq --no-install-suggests --no-install-recommends install bridge-utils libpulse0 libvirt-bin qemu-kvm virtinst ubuntu-vm-builder + - sudo adduser $USER libvirt + - sudo adduser $USER kvm + + # Create an Android emulator + - echo no | avdmanager create avd --force -n Pixel_API_28_AOSP -k "system-images;android-$API;$EMU_FLAVOR;$ABI" -c 10M + - | + EMU_PARAMS="-verbose -no-snapshot -no-window -camera-back none -camera-front none -selinux permissive -qemu -m 2048" + EMU_COMMAND="emulator" + # This double "sudo" monstrosity is used to have Travis execute the + # emulator with its new group permissions and help preserve the rule + # of least privilege. + sudo -E sudo -u $USER -E bash -c "${ANDROID_HOME}/emulator/${EMU_COMMAND} -avd Pixel_API_28_AOSP ${AUDIO} ${EMU_PARAMS} &" + + # Wait for emulator to be ready + - chmod +x ./tests/e2e/android-wait-for-emulator.sh + - ./tests/e2e/android-wait-for-emulator.sh + - adb shell input keyevent 82 & + + # Switch back to our target JDK version to build and run tests + - JDK="${TARGET_JDK}" + - source ~/.install-jdk-travis.sh + + - curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.2/install.sh | bash + - export NVM_DIR="$HOME/.nvm" && [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" + - nvm install $NODE_VERSION + - nvm use $NODE_VERSION + - nvm alias default $NODE_VERSION + +script: + - echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p + - printf "\nhw.lcd.height=1920\nhw.lcd.width=1080\nhw.lcd.density=161\nskin.name=240x426" >> /home/travis/.android/avd/Pixel_API_28_AOSP.avd/config.ini + - npm i + - npm i -g detox-cli + - npm run e2e:release + + +before_cache: + - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock + - rm -r -f node_modules/ + - curl "${GRAVIS}.clean_gradle_cache.sh" --output ~/.clean_gradle_cache.sh + - bash ~/.clean_gradle_cache.sh > /dev/null + +cache: + directories: + - $HOME/.gradle/caches/ + - $HOME/.gradle/wrapper/ + - node_modules/ \ No newline at end of file diff --git a/android/app/build.gradle b/android/app/build.gradle index c883e7ab..0bf37117 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -107,6 +107,14 @@ def enableProguardInReleaseBuilds = false def useIntlJsc = false android { + + /* added because of memory: + https://stackoverflow.com/questions/56075455/expiring-daemon-because-jvm-heap-space-is-exhausted + */ + dexOptions { + javaMaxHeapSize "3g" + } + compileSdkVersion rootProject.ext.compileSdkVersion compileOptions { @@ -122,6 +130,8 @@ android { versionName "5.1.0" multiDexEnabled true missingDimensionStrategy 'react-native-camera', 'general' + testBuildType System.getProperty('testBuildType', 'debug') // This will later be used to control the test apk build type + testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' } splits { abi { @@ -155,6 +165,10 @@ android { dependencies { implementation fileTree(dir: "libs", include: ["*.jar"]) implementation "com.facebook.react:react-native:+" // From node_modules + androidTestImplementation('com.wix:detox:+') { + exclude group: 'org.jetbrains.kotlin', module: 'kotlin-stdlib-jdk8' + } + androidTestImplementation 'junit:junit:4.12' // JSC from node_modules if (useIntlJsc) { implementation 'org.webkit:android-jsc-intl:+' diff --git a/android/app/src/androidTest/java/io/bluewallet/bluewallet/DetoxTest.java b/android/app/src/androidTest/java/io/bluewallet/bluewallet/DetoxTest.java new file mode 100644 index 00000000..8de2c34c --- /dev/null +++ b/android/app/src/androidTest/java/io/bluewallet/bluewallet/DetoxTest.java @@ -0,0 +1,24 @@ +package io.bluewallet.bluewallet; + +import com.wix.detox.Detox; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.LargeTest; +import androidx.test.rule.ActivityTestRule; + +@RunWith(AndroidJUnit4.class) +@LargeTest +public class DetoxTest { + + @Rule + public ActivityTestRule mActivityRule = new ActivityTestRule<>(MainActivity.class, false, false); + + @Test + public void runDetoxTests() { + Detox.runTests(mActivityRule); + } +} diff --git a/android/build.gradle b/android/build.gradle index a0881b02..f19d5d46 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -3,7 +3,7 @@ buildscript { ext { buildToolsVersion = "28.0.3" - minSdkVersion = 16 + minSdkVersion = 18 compileSdkVersion = 28 targetSdkVersion = 28 supportLibVersion = "28.0.0" @@ -14,8 +14,10 @@ buildscript { google() jcenter() } + ext.kotlinVersion = '1.3.+' dependencies { classpath("com.android.tools.build:gradle:3.4.1") + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion" // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files @@ -33,8 +35,22 @@ allprojects { // Android JSC is installed from npm url("$rootDir/../node_modules/jsc-android/dist") } - + maven { + // All of Detox' artifacts are provided via the npm module + url "$rootDir/../node_modules/detox/Detox-android" + } google() jcenter() } } + +subprojects { + afterEvaluate {project -> + if (project.hasProperty("android")) { + android { + compileSdkVersion 28 + buildToolsVersion '28.0.0' + } + } + } +} \ No newline at end of file diff --git a/android/gradle.properties b/android/gradle.properties index 027ef9db..e7a26bd6 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -19,3 +19,9 @@ android.useAndroidX=true android.enableJetifier=true + +# added when build failed because of memory: +# https://stackoverflow.com/questions/56075455/expiring-daemon-because-jvm-heap-space-is-exhausted +org.gradle.daemon=true +org.gradle.configureondemand=true +org.gradle.jvmargs=-Xmx4g -XX:MaxPermSize=2048m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 \ No newline at end of file diff --git a/ios/BlueWallet.xcodeproj/project.pbxproj b/ios/BlueWallet.xcodeproj/project.pbxproj index 78e8ee35..e40ae967 100644 --- a/ios/BlueWallet.xcodeproj/project.pbxproj +++ b/ios/BlueWallet.xcodeproj/project.pbxproj @@ -570,7 +570,6 @@ 13B07F8E1A680F5B00A75B9A /* Resources */, 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, B40D4E2D225841C300428FCC /* Embed Watch Content */, - 791C03B6EF06B63A39F55115 /* [CP] Copy Pods Resources */, 2130DE983D1D45AC8FC45F7E /* Upload Debug Symbols to Sentry */, 3271B0B6236E2E0700DA766F /* Embed App Extensions */, ); @@ -691,7 +690,6 @@ TestTargetID = 13B07F861A680F5B00A75B9A; }; 13B07F861A680F5B00A75B9A = { - DevelopmentTeam = A7W54YZ4WU; LastSwiftMigration = 1030; ProvisioningStyle = Automatic; SystemCapabilities = { @@ -926,54 +924,6 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - 791C03B6EF06B63A39F55115 /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-BlueWallet/Pods-BlueWallet-resources.sh", - "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/AntDesign.ttf", - "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Entypo.ttf", - "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/EvilIcons.ttf", - "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Feather.ttf", - "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/FontAwesome.ttf", - "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Brands.ttf", - "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Regular.ttf", - "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Solid.ttf", - "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Fontisto.ttf", - "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Foundation.ttf", - "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Ionicons.ttf", - "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/MaterialCommunityIcons.ttf", - "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/MaterialIcons.ttf", - "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Octicons.ttf", - "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/SimpleLineIcons.ttf", - "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Zocial.ttf", - ); - name = "[CP] Copy Pods Resources"; - outputPaths = ( - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AntDesign.ttf", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Entypo.ttf", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EvilIcons.ttf", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Feather.ttf", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome.ttf", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome5_Brands.ttf", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome5_Regular.ttf", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome5_Solid.ttf", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Fontisto.ttf", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Foundation.ttf", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Ionicons.ttf", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/MaterialCommunityIcons.ttf", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/MaterialIcons.ttf", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Octicons.ttf", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/SimpleLineIcons.ttf", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Zocial.ttf", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-BlueWallet/Pods-BlueWallet-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; 910F5F6DC7F7ADF3C6EE8653 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -1288,7 +1238,7 @@ ); PRODUCT_BUNDLE_IDENTIFIER = io.bluewallet.bluewallet; PRODUCT_NAME = BlueWallet; - PROVISIONING_PROFILE_SPECIFIER = ""; + PROVISIONING_PROFILE_SPECIFIER = "io.bluewallet.bluewallet AppStore"; SWIFT_OBJC_BRIDGING_HEADER = "BlueWallet-Bridging-Header.h"; SWIFT_VERSION = 4.2; TARGETED_DEVICE_FAMILY = "1,2"; diff --git a/ios/BlueWallet/Info.plist b/ios/BlueWallet/Info.plist index fe11e820..f5f3c3e8 100644 --- a/ios/BlueWallet/Info.plist +++ b/ios/BlueWallet/Info.plist @@ -67,7 +67,7 @@ CFBundleVersion - 232 + 239 ITSAppUsesNonExemptEncryption LSRequiresIPhoneOS diff --git a/ios/Podfile b/ios/Podfile index 0786aa18..1b9df7be 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -11,8 +11,21 @@ post_install do |installer| config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '10.0' end end + installer.generated_projects.each do |project| + project.build_configurations.each do |config| + if config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'].to_f < 8.0 + config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '8.0' + end + end + project.targets.each do |target| + target.build_configurations.each do |config| + if config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'].to_f < 8.0 + config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '8.0' + end + end + end + end end - def sharedPods pod 'React', :path => '../node_modules/react-native/' pod 'React-Core', :path => '../node_modules/react-native/React' diff --git a/package-lock.json b/package-lock.json index 4a765ac0..4d2d404e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2329,14 +2329,6 @@ } } }, - "@egjs/hammerjs": { - "version": "2.0.17", - "resolved": "https://registry.npmjs.org/@egjs/hammerjs/-/hammerjs-2.0.17.tgz", - "integrity": "sha512-XQsZgjm2EcVUiZQf11UBJQfmZeEmOW8DpI1gsFeln6w0ae0ii4dMQEQ0kjl6DspdWX1aGY1/loyXnP0JS06e/A==", - "requires": { - "@types/hammerjs": "^2.0.36" - } - }, "@hapi/address": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/@hapi/address/-/address-2.1.4.tgz", @@ -2954,11 +2946,6 @@ "integrity": "sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==", "dev": true }, - "@types/hammerjs": { - "version": "2.0.36", - "resolved": "https://registry.npmjs.org/@types/hammerjs/-/hammerjs-2.0.36.tgz", - "integrity": "sha512-7TUK/k2/QGpEAv/BCwSHlYu3NXZhQ9ZwBYpzr9tjlPIL2C5BeGhH3DmVavRx3ZNyELX5TLC91JTz/cen6AAtIQ==" - }, "@types/istanbul-lib-coverage": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz", @@ -4181,6 +4168,12 @@ } } }, + "bluebird": { + "version": "3.5.5", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.5.tgz", + "integrity": "sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w==", + "dev": true + }, "blueimp-md5": { "version": "2.12.0", "resolved": "https://registry.npmjs.org/blueimp-md5/-/blueimp-md5-2.12.0.tgz", @@ -4462,6 +4455,28 @@ "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=" }, + "bunyan": { + "version": "1.8.12", + "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.8.12.tgz", + "integrity": "sha1-8VDw9nSKvdcq6uhPBEA74u8RN5c=", + "dev": true, + "requires": { + "dtrace-provider": "~0.8", + "moment": "^2.10.6", + "mv": "~2", + "safe-json-stringify": "~1" + } + }, + "bunyan-debug-stream": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/bunyan-debug-stream/-/bunyan-debug-stream-1.1.1.tgz", + "integrity": "sha512-jJbQ1gXUL6vMmZVdbaTFK1v1sGa7axLrSQQwkB6HU9HCPTzsw2HsKcPHm1vgXZlEck/4IvEuRwg/9+083YelCg==", + "dev": true, + "requires": { + "colors": "^1.0.3", + "exception-formatter": "^1.0.4" + } + }, "bytes": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", @@ -4569,6 +4584,29 @@ "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" }, + "child-process-promise": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/child-process-promise/-/child-process-promise-2.2.1.tgz", + "integrity": "sha1-RzChHvYQ+tRQuPIjx50x172tgHQ=", + "dev": true, + "requires": { + "cross-spawn": "^4.0.2", + "node-version": "^1.0.0", + "promise-polyfill": "^6.0.1" + }, + "dependencies": { + "cross-spawn": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz", + "integrity": "sha1-e5JHYhwjrf3ThWAEqCPL45dCTUE=", + "dev": true, + "requires": { + "lru-cache": "^4.0.1", + "which": "^1.2.9" + } + } + } + }, "chokidar": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", @@ -4815,6 +4853,12 @@ "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.1.0.tgz", "integrity": "sha512-6S062WDQUXi6hOfkO/sBPVwE5ASXY4G2+b4atvhJfSsuUUhIaUKlkjLe9692Ipyt5/a+IPF5aVTu3V5gvXq5cg==" }, + "colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "dev": true + }, "combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -5365,6 +5409,150 @@ "integrity": "sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I=", "dev": true }, + "detox": { + "version": "15.4.2", + "resolved": "https://registry.npmjs.org/detox/-/detox-15.4.2.tgz", + "integrity": "sha512-nUNY8kM3T4iPqzg0KvdcpXpVc0rvbRHNAYOldCsRQZo6WlOKFdiQAD8snvp1We0Wwm0cm4UZ4/Usj/UPtFc8Uw==", + "dev": true, + "requires": { + "@babel/core": "^7.4.5", + "bunyan": "^1.8.12", + "bunyan-debug-stream": "^1.1.0", + "chalk": "^2.4.2", + "child-process-promise": "^2.2.0", + "fs-extra": "^4.0.2", + "funpermaproxy": "^1.0.1", + "get-port": "^2.1.0", + "ini": "^1.3.4", + "lodash": "^4.17.5", + "minimist": "^1.2.0", + "proper-lockfile": "^3.0.2", + "sanitize-filename": "^1.6.1", + "shell-utils": "^1.0.9", + "tail": "^2.0.0", + "telnet-client": "0.15.3", + "tempfile": "^2.0.0", + "which": "^1.3.1", + "ws": "^3.3.1", + "yargs": "^13.0.0", + "yargs-parser": "^13.0.0" + }, + "dependencies": { + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "fs-extra": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.3.tgz", + "integrity": "sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "ultron": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", + "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==", + "dev": true + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + } + }, + "ws": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", + "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", + "dev": true, + "requires": { + "async-limiter": "~1.0.0", + "safe-buffer": "~5.1.0", + "ultron": "~1.1.0" + } + }, + "yargs": { + "version": "13.3.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz", + "integrity": "sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==", + "dev": true, + "requires": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.1" + } + }, + "yargs-parser": { + "version": "13.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz", + "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + }, "diff": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", @@ -5465,6 +5653,16 @@ "create-hmac": "^1.1.4" } }, + "dtrace-provider": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.8.8.tgz", + "integrity": "sha512-b7Z7cNtHPhH9EJhNNbbeqTcXB8LGFFZhq1PGgEvpeHlzd36bhbdTWoE/Ba/YguqpBSlAPKnARWhVlhunCMwfxg==", + "dev": true, + "optional": true, + "requires": { + "nan": "^2.14.0" + } + }, "ecc-jsbn": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", @@ -6305,6 +6503,15 @@ "safe-buffer": "^5.1.1" } }, + "exception-formatter": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/exception-formatter/-/exception-formatter-1.0.7.tgz", + "integrity": "sha512-zV45vEsjytJrwfGq6X9qd1Ll56cW4NC2mhCO6lqwMk4ZpA1fZ6C3UiaQM/X7if+7wZFmCgss3ahp9B/uVFuLRw==", + "dev": true, + "requires": { + "colors": "^1.0.3" + } + }, "exec-sh": { "version": "0.3.4", "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.4.tgz", @@ -7362,6 +7569,12 @@ "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", "dev": true }, + "funpermaproxy": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/funpermaproxy/-/funpermaproxy-1.0.1.tgz", + "integrity": "sha512-9pEzs5vnNtR7ZGihly98w/mQ7blsvl68Wj30ZCDAXy7qDN4CWLLjdfjtH/P2m6whsnaJkw15hysCNHMXue+wdA==", + "dev": true + }, "fwd-stream": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/fwd-stream/-/fwd-stream-1.0.4.tgz", @@ -7398,6 +7611,15 @@ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==" }, + "get-port": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-2.1.0.tgz", + "integrity": "sha1-h4P53OvR7qSVozThpqJR54iHqxo=", + "dev": true, + "requires": { + "pinkie-promise": "^2.0.0" + } + }, "get-stdin": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz", @@ -7454,7 +7676,6 @@ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", "dev": true, - "optional": true, "requires": { "is-glob": "^2.0.0" } @@ -7488,13 +7709,6 @@ "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=" }, - "hammerjs": { - "version": "git+https://github.com/naver/hammer.js.git#54bc698b25edd6e1b76ca975ebaced5ce0467d51", - "from": "git+https://github.com/naver/hammer.js.git", - "requires": { - "@types/hammerjs": "^2.0.36" - } - }, "handlebars": { "version": "4.5.3", "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.5.3.tgz", @@ -7793,6 +8007,12 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "dev": true + }, "inquirer": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.5.2.tgz", @@ -7968,8 +8188,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", - "dev": true, - "optional": true + "dev": true }, "is-finite": { "version": "1.0.2", @@ -8001,7 +8220,6 @@ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", "dev": true, - "optional": true, "requires": { "is-extglob": "^1.0.0" } @@ -10454,6 +10672,13 @@ } } }, + "moment": { + "version": "2.24.0", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", + "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==", + "dev": true, + "optional": true + }, "morgan": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.1.tgz", @@ -10491,6 +10716,44 @@ "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=" }, + "mv": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", + "integrity": "sha1-rmzg1vbV4KT32JN5jQPB6pVZtqI=", + "dev": true, + "optional": true, + "requires": { + "mkdirp": "~0.5.1", + "ncp": "~2.0.0", + "rimraf": "~2.4.0" + }, + "dependencies": { + "glob": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", + "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=", + "dev": true, + "optional": true, + "requires": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "rimraf": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", + "integrity": "sha1-7nEM5dk6j9uFb7Xqj/Di11k0sto=", + "dev": true, + "optional": true, + "requires": { + "glob": "^6.0.1" + } + } + } + }, "nan": { "version": "2.14.0", "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", @@ -10520,6 +10783,13 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, + "ncp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", + "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=", + "dev": true, + "optional": true + }, "negotiator": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", @@ -10658,6 +10928,12 @@ } } }, + "node-version": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/node-version/-/node-version-1.2.0.tgz", + "integrity": "sha512-ma6oU4Sk0qOoKEAymVoTvk8EdXEobdS7m/mAGhDJ8Rouugho48crHBORAmy5BoOcv8wraPM6xumapQp5hl4iIQ==", + "dev": true + }, "normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", @@ -11971,6 +12247,12 @@ "asap": "~2.0.3" } }, + "promise-polyfill": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-6.1.0.tgz", + "integrity": "sha1-36lpQ+qcEh/KTem1hoyznTRy4Fc=", + "dev": true + }, "prompts": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.3.0.tgz", @@ -11991,6 +12273,17 @@ "react-is": "^16.8.1" } }, + "proper-lockfile": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-3.2.0.tgz", + "integrity": "sha512-iMghHHXv2bsxl6NchhEaFck8tvX3F9cknEEh1SUpguUOBjN7PAAW9BLzmbc1g/mCD1gY3EE2EABBHPJfFdHFmA==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "retry": "^0.12.0", + "signal-exit": "^3.0.2" + } + }, "proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", @@ -12617,11 +12910,15 @@ "resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-1.5.6.tgz", "integrity": "sha512-z2jLUkRiRc0PBAC9UcXYkqy3VUzBG0cYQAGMsDHsd90JgrzudHAFRJV9fvFm18wNauFTNnJievjZ0C3rI2ydhw==", "requires": { - "@egjs/hammerjs": "^2.0.17", - "hammerjs": "git+https://github.com/naver/hammer.js.git", "hoist-non-react-statics": "^2.3.1", "invariant": "^2.2.4", "prop-types": "^15.7.2" + }, + "dependencies": { + "@egjs/hammerjs": { + "version": "git+https://github.com/naver/hammer.js.git#54bc698b25edd6e1b76ca975ebaced5ce0467d51", + "from": "git+https://github.com/naver/hammer.js.git#54bc698b25edd6e1b76ca975ebaced5ce0467d51" + } } }, "react-native-handoff": { @@ -13122,8 +13419,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true, - "optional": true + "dev": true }, "string_decoder": { "version": "1.1.1", @@ -13393,6 +13689,12 @@ "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==" }, + "retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", + "dev": true + }, "rimraf": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", @@ -13504,6 +13806,13 @@ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==" }, + "safe-json-stringify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz", + "integrity": "sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg==", + "dev": true, + "optional": true + }, "safe-regex": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", @@ -13540,6 +13849,15 @@ } } }, + "sanitize-filename": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.3.tgz", + "integrity": "sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg==", + "dev": true, + "requires": { + "truncate-utf8-bytes": "^1.0.0" + } + }, "sax": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", @@ -13711,6 +14029,15 @@ "jsonify": "~0.0.0" } }, + "shell-utils": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/shell-utils/-/shell-utils-1.0.10.tgz", + "integrity": "sha512-p1xuqhj3jgcXiV8wGoF1eL/NOvapN9tyGDoObqKwvZTUZn7fIzK75swLTEHfGa7sObeN9vxFplHw/zgYUYRTsg==", + "dev": true, + "requires": { + "lodash": "4.x.x" + } + }, "shellwords": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", @@ -14251,6 +14578,21 @@ } } }, + "tail": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tail/-/tail-2.0.3.tgz", + "integrity": "sha512-s9NOGkLqqiDEtBttQZI7acLS8ycYK5sTlDwNjGnpXG9c8AWj0cfAtwEIzo/hVRMMiC5EYz+bXaJWC1u1u0GPpQ==", + "dev": true + }, + "telnet-client": { + "version": "0.15.3", + "resolved": "https://registry.npmjs.org/telnet-client/-/telnet-client-0.15.3.tgz", + "integrity": "sha512-GSfdzQV0BKIYsmeXq7bJFJ2wHeJud6icaIxCUf6QCGQUD6R0BBGbT1+yLDhq67JRdgRpwyPwUbV7JxFeRrZomQ==", + "dev": true, + "requires": { + "bluebird": "3.5.x" + } + }, "temp": { "version": "0.8.3", "resolved": "https://registry.npmjs.org/temp/-/temp-0.8.3.tgz", @@ -14267,6 +14609,22 @@ } } }, + "temp-dir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-1.0.0.tgz", + "integrity": "sha1-CnwOom06Oa+n4OvqnB/AvE2qAR0=", + "dev": true + }, + "tempfile": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tempfile/-/tempfile-2.0.0.tgz", + "integrity": "sha1-awRGhWqbERTRhW/8vlCczLCXcmU=", + "dev": true, + "requires": { + "temp-dir": "^1.0.0", + "uuid": "^3.0.1" + } + }, "test-exclude": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-5.2.3.tgz", @@ -14505,6 +14863,15 @@ "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", "dev": true }, + "truncate-utf8-bytes": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz", + "integrity": "sha1-QFkjkJWS1W94pYGENLC3hInKXys=", + "dev": true, + "requires": { + "utf8-byte-length": "^1.0.1" + } + }, "tslib": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", @@ -14745,6 +15112,12 @@ "resolved": "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz", "integrity": "sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ==" }, + "utf8-byte-length": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.4.tgz", + "integrity": "sha1-9F8VDExm7uloGGUFq5P8u4rWv2E=", + "dev": true + }, "util": { "version": "0.12.1", "resolved": "https://registry.npmjs.org/util/-/util-0.12.1.tgz", diff --git a/package.json b/package.json index 092a926b..cc1ca395 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "babel-eslint": "^10.0.2", "babel-jest": "^24.8.0", "babel-preset-flow": "^6.23.0", + "detox": "15.4.2", "eslint": "^6.0.1", "eslint-plugin-babel": "^5.3.0", "eslint-plugin-import": "^2.18.0", @@ -38,8 +39,10 @@ "postinstall": "./node_modules/.bin/rn-nodeify --install buffer,events,process,stream,util,inherits,fs,path --hack; npm run releasenotes2json; npm run podinstall; npx jetify", "test": "npm run unit && npm run jest && npm run lint", "jest": "node node_modules/jest/bin/jest.js tests/integration/*", - "lint": "./node_modules/.bin/eslint *.js screen/**/*.js screen/ class/ models/ loc/ tests/integration/", - "lint:fix": "./node_modules/.bin/eslint *.js screen/**/*.js screen/ class/ models/ loc/ tests/integration/ --fix", + "e2e:release": "detox build -c android.emu.release; detox test -c android.emu.release --cleanup", + "e2e:debug": "test -f android/app/build/outputs/apk/debug/app-debug.apk || detox build -c android.emu.debug; detox test -c android.emu.debug --cleanup", + "lint": "./node_modules/.bin/eslint *.js screen/**/*.js screen/ class/ models/ loc/ tests/integration/ tests/e2e/", + "lint:fix": "./node_modules/.bin/eslint *.js screen/**/*.js screen/ class/ models/ loc/ tests/integration/ tests/e2e/ --fix", "unit": "./node_modules/.bin/mocha tests/unit/*" }, "jest": { @@ -145,6 +148,36 @@ "_stream_passthrough": "readable-stream/passthrough", "stream": "stream-browserify" }, + "detox": { + "configurations": { + "ios.sim.release": { + "binaryPath": "ios/build/Build/Products/Release-iphonesimulator/BlueWallet.app", + "build": "xcodebuild clean build -workspace ios/BlueWallet.xcworkspace -scheme BlueWallet -configuration Release -derivedDataPath ios/build -sdk iphonesimulator13.2", + "type": "ios.simulator", + "device": { + "type": "iPhone 11" + } + }, + "android.emu.debug": { + "binaryPath": "android/app/build/outputs/apk/debug/app-debug.apk", + "build": "cd android && ./gradlew assembleDebug assembleAndroidTest -DtestBuildType=debug && cd ..", + "type": "android.emulator", + "device": { + "avdName": "Pixel_API_28_AOSP" + } + }, + "android.emu.release": { + "binaryPath": "android/app/build/outputs/apk/release/app-release.apk", + "build": "# deleting old artifacts\nfind | grep '\\.apk' --color=never | grep -v node_modules | xargs -l rm\n\n# creating fresh keystore\nrm detox.keystore\nkeytool -genkeypair -v -keystore detox.keystore -alias detox -keyalg RSA -keysize 2048 -validity 10000 -storepass 123456 -keypass 123456 -dname 'cn=Unknown, ou=Unknown, o=Unknown, c=Unknown'\n\n# building release APK\ncd android && ./gradlew assembleRelease assembleAndroidTest -DtestBuildType=release && cd ..\n\n# backup & sign apk1\ncp ./android/app/build/outputs/apk/release/app-release-unsigned.apk ./android/app/build/outputs/apk/release/app-release-unsigned.apk.bak\njarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore detox.keystore ./android/app/build/outputs/apk/release/app-release-unsigned.apk detox -storepass 123456\n\n# move apk1 to expected filename\nmv ./android/app/build/outputs/apk/release/app-release-unsigned.apk ./android/app/build/outputs/apk/release/app-release.apk\n\n# backup and sign apk2\ncp android/app/build/outputs/apk/androidTest/release/app-release-androidTest.apk android/app/build/outputs/apk/androidTest/release/app-release-androidTest.apk.bak\njarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore detox.keystore android/app/build/outputs/apk/androidTest/release/app-release-androidTest.apk detox -storepass 123456", + "type": "android.emulator", + "device": { + "avdName": "Pixel_API_28_AOSP" + } + } + }, + "test-runner": "jest", + "runner-config": "tests/e2e/config.json" + }, "browser": { "path": "path-browserify", "fs": "react-native-level-fs", diff --git a/screen/wallets/list.js b/screen/wallets/list.js index a28f1f1a..82ff981c 100644 --- a/screen/wallets/list.js +++ b/screen/wallets/list.js @@ -327,7 +327,7 @@ export default class WalletsList extends Component { return ; } return ( - + { this.redrawScreen(); diff --git a/tests/e2e/addWallet.spec.js b/tests/e2e/addWallet.spec.js new file mode 100644 index 00000000..96febed5 --- /dev/null +++ b/tests/e2e/addWallet.spec.js @@ -0,0 +1,11 @@ +/* global it, describe, expect, element, by */ + +describe('BlueWallet UI Tests', () => { + it('Shows Wallets List screen', async () => { + await expect(element(by.id('WalletsList'))).toBeVisible(); + }); + + it('Shows Wallets List screen', async () => { + await expect(element(by.id('WalletsList'))).toBeVisible(); + }); +}); diff --git a/tests/e2e/android-wait-for-emulator.sh b/tests/e2e/android-wait-for-emulator.sh new file mode 100644 index 00000000..6c4ee960 --- /dev/null +++ b/tests/e2e/android-wait-for-emulator.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +# Originally written by Ralf Kistner , but placed in the public domain + +set +e + +bootanim="" +failcounter=0 +timeout_in_sec=600 # 10 minutes + +until [[ "$bootanim" =~ "stopped" ]]; do + bootanim=`adb -e shell getprop init.svc.bootanim 2>&1 &` + if [[ "$bootanim" =~ "device not found" || "$bootanim" =~ "device offline" + || "$bootanim" =~ "running" || "$bootanim" =~ "error: no emulators found" ]]; then + let "failcounter += 1" + echo "Waiting for emulator to start" + if [[ $failcounter -gt timeout_in_sec ]]; then + echo "Timeout ($timeout_in_sec seconds) reached; failed to start emulator" + exit 1 + fi + fi + sleep 1 +done + +echo "Emulator is ready" + diff --git a/tests/e2e/config.json b/tests/e2e/config.json new file mode 100644 index 00000000..3ff37ef8 --- /dev/null +++ b/tests/e2e/config.json @@ -0,0 +1,6 @@ +{ + "setupFilesAfterEnv": ["./init.js"], + "testEnvironment": "node", + "reporters": ["detox/runners/jest/streamlineReporter"], + "verbose": true +} diff --git a/tests/e2e/detox-build-release-apk.sh b/tests/e2e/detox-build-release-apk.sh new file mode 100755 index 00000000..349e1990 --- /dev/null +++ b/tests/e2e/detox-build-release-apk.sh @@ -0,0 +1,27 @@ +# script thats used to build & sign release APK in preparation for Detox e2e testing. +# should be copied in package.json - detox - configurations - android.emu.release - build + +# deleting old artifacts +find | grep '\.apk' --color=never | grep -v node_modules | xargs -l rm + +# creating fresh keystore +rm detox.keystore +keytool -genkeypair -v -keystore detox.keystore -alias detox -keyalg RSA -keysize 2048 -validity 10000 -storepass 123456 -keypass 123456 -dname 'cn=Unknown, ou=Unknown, o=Unknown, c=Unknown' + +# building release APK +cd android && ./gradlew assembleRelease assembleAndroidTest -DtestBuildType=release && cd .. + +# backup & sign apk1 +cp ./android/app/build/outputs/apk/release/app-release-unsigned.apk ./android/app/build/outputs/apk/release/app-release-unsigned.apk.bak +jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore detox.keystore ./android/app/build/outputs/apk/release/app-release-unsigned.apk detox -storepass 123456 + +# move apk1 to expected filename +mv ./android/app/build/outputs/apk/release/app-release-unsigned.apk ./android/app/build/outputs/apk/release/app-release.apk + +# backup and sign apk2 +cp android/app/build/outputs/apk/androidTest/release/app-release-androidTest.apk android/app/build/outputs/apk/androidTest/release/app-release-androidTest.apk.bak +jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore detox.keystore android/app/build/outputs/apk/androidTest/release/app-release-androidTest.apk detox -storepass 123456 + + + +# why the fuck there are 2 apks..? oh well, if it works - dont touch \ No newline at end of file diff --git a/tests/e2e/detox-prepare-android-emu.sh b/tests/e2e/detox-prepare-android-emu.sh new file mode 100644 index 00000000..a7d983dc --- /dev/null +++ b/tests/e2e/detox-prepare-android-emu.sh @@ -0,0 +1,2 @@ +$ANDROID_HOME/tools/bin/sdkmanager "system-images;android-28;default;x86_64" +$ANDROID_HOME/tools/bin/avdmanager create avd -n Pixel_API_28_AOSP -d pixel --package "system-images;android-28;default;x86_64" \ No newline at end of file diff --git a/tests/e2e/init.js b/tests/e2e/init.js new file mode 100644 index 00000000..3da3dc1b --- /dev/null +++ b/tests/e2e/init.js @@ -0,0 +1,40 @@ +/* global jasmine, jest, beforeAll, afterAll, beforeEach, device */ + +const detox = require('detox'); +const config = require('../../package.json').detox; +const adapter = require('detox/runners/jest/adapter'); +const specReporter = require('detox/runners/jest/specReporter'); +const assignReporter = require('detox/runners/jest/assignReporter'); + +jasmine.getEnv().addReporter(adapter); + +// This takes care of generating status logs on a per-spec basis. By default, jest only reports at file-level. +// This is strictly optional. +jasmine.getEnv().addReporter(specReporter); + +// This will post which device has assigned to run a suite, which can be useful in a multiple-worker tests run. +// This is strictly optional. +jasmine.getEnv().addReporter(assignReporter); + +// Set the default timeout +jest.setTimeout(90000); + +beforeAll(async () => { + await detox.init(config, { launchApp: false }); +}, 300000); + +beforeEach(async () => { + await device.launchApp({ newInstance: true }); + console.log('sleeping for logo animation...'); + await sleep(2000); + await adapter.beforeEach(); +}); + +afterAll(async () => { + await adapter.afterAll(); + await detox.cleanup(); +}); + +async function sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); +}