From ebc19125d984a8c5a1b2ffdd1ab5a91bc23d5104 Mon Sep 17 00:00:00 2001 From: Krzysztof Borowy Date: Sat, 24 Oct 2020 22:00:52 +0200 Subject: [PATCH 1/9] chore: e2e setup --- .circleci/config.yml | 186 ++++++++++++++++++++++--------------------- package.json | 1 + 2 files changed, 98 insertions(+), 89 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index dc2df03e..4914d4ba 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -26,6 +26,13 @@ default config for macOS: &macos_defaults macos: xcode: '12.1.0' + +config for macOS (android): &macos_defaults_android + <<: *defaults + resource_class: 'medium' + macos: + xcode: '11.5.0' + default config for android apk builds: &android_defaults <<: *defaults docker: @@ -41,67 +48,76 @@ default config for android apk builds: &android_defaults # CACHE CONFIG # ============================== -# brew -save brew cache: &cache_save_brew - name: Saving Brew cache - paths: +cache keys: + brew ios: &key_brew_ios cache-brew-ios-v3-{{ arch }} + brew android: &key_brew_android cache-brew-android-v3-{{ arch }} + yarn: &key_yarn cache-yarn-{{ checksum "package.json" }}-{{ arch }} + gradle: &key_gradle cache-gradle-{{ checksum "example/android/gradle/wrapper/gradle-wrapper.properties" }}-{{ checksum "example/android/app/build.gradle" }}-{{ arch }} + pods: &key_pods cache-pods-v1-{{ checksum "example/ios/Podfile" }}-{{ checksum "package.json" }}-{{ arch }} + +cache: + # brew + save brew cache for ios: &cache_save_brew_ios + name: Saving Brew cache + paths: + - /usr/local/Homebrew + - ~/Library/Caches/Homebrew + key: *key_brew_ios + + restore brew cache for ios: &cache_restore_brew_ios + name: Restoring Brew cache + keys: + - *key_brew_ios + + save brew cache for android: &cache_save_brew_android + name: Saving Brew cache for android + paths: - /usr/local/Homebrew - ~/Library/Caches/Homebrew - key: legacy-brew-cache-node12-{{ arch }} - -restore brew cache: &cache_restore_brew - name: Restoring Brew cache - keys: - - legacy-brew-cache-node12-{{ arch }} - -save brew cache for android: &cache_save_brew_android - name: Saving Brew cache for android - paths: - - /usr/local/Homebrew - - ~/Library/Caches/Homebrew - key: legacy-brew-cache-node12-{{ arch }}-android - -restore brew cache for android: &cache_restore_brew_android - name: Restoring Brew cache for android - keys: - - legacy-brew-cache-node12-{{ arch }}-android - -# yarn -save yarn cache: &cache_save_yarn - name: Saving Yarn cache - paths: - - ~/.cache/yarn - - ~/Library/Detox - key: legacy-yarn-cache-{{ checksum "package.json" }}-{{ arch }} - -restore yarn cache: &cache_restore_yarn - name: Restoring Yarn cache - keys: - - legacy-yarn-cache-{{ checksum "package.json" }}-{{ arch }} - -# gradle -save gradle wrapper cache: &cache_save_gradle_wrapper - name: Saving Gradle Wrapper cache - paths: - - ~/.gradle/wrapper - key: gradle-wrapper-legacy-{{ checksum "example/android/gradle/wrapper/gradle-wrapper.properties" }} - -save gradle build cache: &cache_save_gradle_build - name: Saving Gradle app/build cache - paths: - - ~/.gradle/caches - key: app-build-gradle-legacy-{{ checksum "example/android/app/build.gradle" }} - -restore gradle wrapper cache: &cache_restore_gradle_wrapper - name: Restoring Gradle Wrapper cache - keys: - - gradle-wrapper-legacy-{{ checksum "example/android/gradle/wrapper/gradle-wrapper.properties" }} - -restore gradle build cache: &cache_restore_gradle_build - name: Restoring Gradle app/build cache - keys: - - app-build-gradle-legacy-{{ checksum "example/android/app/build.gradle" }} - + key: *key_brew_android + + restore brew cache for android: &cache_restore_brew_android + name: Restoring Brew cache for android + keys: + - *key_brew_android + + # yarn + save yarn cache: &cache_save_yarn + name: Saving Yarn cache + paths: + - ~/.cache/yarn + - ~/Library/Detox + key: *key_yarn + + restore yarn cache: &cache_restore_yarn + name: Restoring Yarn cache + keys: + - *key_yarn + + # gradle + save gradle cache: &cache_save_gradle + name: Saving Gradle cache + key: *key_gradle + paths: + - ~/.gradle/wrapper + - ~/.gradle/caches + + restore gradle cache: &cache_restore_gradle + name: Restoring Gradle cache + keys: + - *key_gradle + + # cocoapods + save pods cache: &cache_save_pods + name: Saving Pods + key: *key_pods + paths: + - example/ios/Pods + + restore pods cache: &cache_restore_pods + name: Restoring Pods + keys: + - *key_pods # ============================== # JOBS @@ -141,19 +157,20 @@ jobs: <<: *macos_defaults steps: - *addWorkspace - - restore-cache: *cache_restore_brew + - restore-cache: *cache_restore_brew_ios - run: name: Configure macOS Environment command: | brew bundle --file=.circleci/Brewfile.ios --no-lock touch .watchmanconfig echo Node $(node --version) - - save-cache: *cache_save_brew + - save-cache: *cache_save_brew_ios - restore-cache: *cache_restore_yarn - run: name: Installing Yarn dependencies command: yarn --pure-lockfile --non-interactive --cache-folder ~/.cache/yarn - save-cache: *cache_save_yarn + - restore-cache: *cache_restore_pods - run: name: Bundle JS command: yarn bundle:ios --dev false @@ -161,6 +178,7 @@ jobs: name: Install pod dependencies working_directory: example/ios command: pod install + - save-cache: *cache_save_pods - run: name: Build iOS app command: yarn build:e2e:ios @@ -177,18 +195,12 @@ jobs: name: Installing Yarn dependencies command: yarn --pure-lockfile --non-interactive --cache-folder ~/.cache/yarn - save-cache: *cache_save_yarn - - # Gradle - - restore-cache: *cache_restore_gradle_wrapper - - restore-cache: *cache_restore_gradle_build + - restore-cache: *cache_restore_gradle - run: name: Downloading Gradle dependencies working_directory: example/android command: ./gradlew --max-workers 2 fetchDependencies - - save-cache: *cache_save_gradle_wrapper - - save-cache: *cache_save_gradle_build - - # Build and test + - save-cache: *cache_save_gradle - run: name: Bundle JS command: yarn bundle:android --dev false @@ -203,7 +215,7 @@ jobs: - example/android/app/build/outputs/apk/* "Test: Android e2e": - <<: *macos_defaults + <<: *macos_defaults_android steps: - *addWorkspace - run: @@ -211,27 +223,26 @@ jobs: command: | echo 'export ANDROID_HOME="/usr/local/share/android-sdk"' >> $BASH_ENV echo 'export ANDROID_SDK_ROOT="/usr/local/share/android-sdk"' >> $BASH_ENV - echo 'export PATH="$ANDROID_SDK_ROOT/emulator:$ANDROID_SDK_ROOT/tools:$ANDROID_SDK_ROOT/platform-tools:$ANDROID_SDK_ROOT/tools/bin:$PATH"' >> $BASH_ENV + echo 'export PATH="$ANDROID_SDK_ROOT/emulator:$ANDROID_SDK_ROOT/tools:$ANDROID_SDK_ROOT/platform-tools:$PATH"' >> $BASH_ENV echo 'export QEMU_AUDIO_DRV=none' >> $BASH_ENV - echo 'export JAVA_HOME=/Library/Java/Home' >> $BASH_ENV + echo 'export JAVA_HOME=$(/usr/libexec/java_home)' >> $BASH_ENV source $BASH_ENV - - # Android tools - restore-cache: *cache_restore_brew_android - run: name: Install Android SDK tools command: | + brew update --preinstall brew bundle --file=.circleci/Brewfile.android --no-lock - save-cache: *cache_save_brew_android - run: name: Install Android emulator shell: /bin/bash -e command: | - yes | sdkmanager "platform-tools" "tools" 1> /dev/null - yes | sdkmanager "platforms;android-28" "system-images;android-21;google_apis;x86" 1> /dev/null - yes | sdkmanager "emulator" --channel=3 1> /dev/null - yes | sdkmanager "build-tools;28.0.3" 1> /dev/null - yes | sdkmanager --licenses 1> /dev/null + yes | sdkmanager "platform-tools" "tools" >/dev/null + yes | sdkmanager "platforms;android-28" "system-images;android-28;default;x86_64" >/dev/null + yes | sdkmanager "emulator" --channel=3 >/dev/null + yes | sdkmanager "build-tools;28.0.3" >/dev/null + yes | sdkmanager --licenses >/dev/null yes | sdkmanager --list - run: name: ADB start/stop @@ -243,12 +254,11 @@ jobs: - run: name: Create emulator command: | - avdmanager create avd \ - --force \ + avdmanager create avd --force \ -n "Emu_E2E" \ - -k "system-images;android-21;google_apis;x86" \ - -g "google_apis" \ - -d "Nexus 4" + -k "system-images;android-28;default;x86_64" \ + -g "default" \ + -d "pixel" - run: name: Start emulator in background background: true @@ -311,8 +321,6 @@ workflows: filters: branches: only: master -# - "Test: Android e2e": -# requires: -# - "Test: lint" -# - "Test: flow" -# - "Build: Android release apk" + - "Test: Android e2e": + requires: + - "Build: Android release apk" diff --git a/package.json b/package.json index 1c26d19e..a640d3a2 100644 --- a/package.json +++ b/package.json @@ -112,6 +112,7 @@ }, "android.emu.release": { "binaryPath": "example/android/app/build/outputs/apk/release/app-release.apk", + "testBinaryPath": "example/android/app/build/outputs/apk/androidTest/release/app-release-androidTest.apk", "build": ".circleci/scripts/run_android_e2e.sh", "type": "android.emulator", "device": { From 22d78a39e3f724a8fafdac7ad5dc53f97f1d53b8 Mon Sep 17 00:00:00 2001 From: Krzysztof Borowy Date: Wed, 11 Nov 2020 17:33:16 +0100 Subject: [PATCH 2/9] fix: android key --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 4914d4ba..0f1885f1 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -52,7 +52,7 @@ cache keys: brew ios: &key_brew_ios cache-brew-ios-v3-{{ arch }} brew android: &key_brew_android cache-brew-android-v3-{{ arch }} yarn: &key_yarn cache-yarn-{{ checksum "package.json" }}-{{ arch }} - gradle: &key_gradle cache-gradle-{{ checksum "example/android/gradle/wrapper/gradle-wrapper.properties" }}-{{ checksum "example/android/app/build.gradle" }}-{{ arch }} + gradle: &key_gradle cache-gradle-{{ checksum "example/android/gradle/wrapper/gradle-wrapper.properties" }}-{{ checksum "package.json" }}-{{ arch }} pods: &key_pods cache-pods-v1-{{ checksum "example/ios/Podfile" }}-{{ checksum "package.json" }}-{{ arch }} cache: From 1cc77cba5fd8546467152b27a43970b0e1339243 Mon Sep 17 00:00:00 2001 From: Krzysztof Borowy Date: Mon, 16 Nov 2020 19:47:54 +0100 Subject: [PATCH 3/9] sign test app --- example/.gitignore | 1 + example/android/build.gradle | 21 +++++++++++++++++++++ example/android/debug.keystore | Bin 0 -> 2257 bytes 3 files changed, 22 insertions(+) create mode 100644 example/android/debug.keystore diff --git a/example/.gitignore b/example/.gitignore index 22f263aa..d0792870 100644 --- a/example/.gitignore +++ b/example/.gitignore @@ -40,6 +40,7 @@ yarn-error.log buck-out/ \.buckd/ *.keystore +!android/debug.keystore # fastlane # diff --git a/example/android/build.gradle b/example/android/build.gradle index e03f6ad3..6c143bb5 100644 --- a/example/android/build.gradle +++ b/example/android/build.gradle @@ -24,3 +24,24 @@ task fetchDependencies() { } } } + +allprojects { + afterEvaluate { project -> + def configName = 'release' + def androidExtension = project.extensions.findByName("android") + + if(androidExtension != null && project.name == "app") { + // android extension from test-app pacakge + androidExtension.signingConfigs { + test { + storeFile file('debug.keystore') + storePassword 'android' + keyAlias 'androiddebugkey' + keyPassword 'android' + } + } + + androidExtension.buildTypes.release.signingConfig = androidExtension.signingConfigs.test + } + } +} diff --git a/example/android/debug.keystore b/example/android/debug.keystore new file mode 100644 index 0000000000000000000000000000000000000000..364e105ed39fbfd62001429a68140672b06ec0de GIT binary patch literal 2257 zcmchYXEfYt8;7T1^dLH$VOTZ%2NOdOH5j5LYLtZ0q7x-V8_6gU5)#7dkq{HTmsfNq zB3ZqcAxeY^G10@?efK?Q&)M(qInVv!xjx+IKEL}p*K@LYvIzo#AZG>st5|P)KF1_Z;y){W{<7K{nl!CPuE z_^(!C(Ol0n8 zK13*rzAtW>(wULKPRYLd7G18F8#1P`V*9`(Poj26eOXYyBVZPno~Cvvhx7vPjAuZo zF?VD!zB~QG(!zbw#qsxT8%BSpqMZ4f70ZPn-3y$L8{EVbbN9$H`B&Z1quk9tgp5FM zuxp3pJ0b8u|3+#5bkJ4SRnCF2l7#DyLYXYY8*?OuAwK4E6J{0N=O3QNVzQ$L#FKkR zi-c@&!nDvezOV$i$Lr}iF$XEcwnybQ6WZrMKuw8gCL^U#D;q3t&HpTbqyD%vG=TeDlzCT~MXUPC|Leb-Uk+ z=vnMd(|>ld?Fh>V8poP;q;;nc@en$|rnP0ytzD&fFkCeUE^kG9Kx4wUh!!rpjwKDP zyw_e|a^x_w3E zP}}@$g>*LLJ4i0`Gx)qltL}@;mDv}D*xR^oeWcWdPkW@Uu)B^X&4W1$p6}ze!zudJ zyiLg@uggoMIArBr*27EZV7djDg@W1MaL+rcZ-lrANJQ%%>u8)ZMWU@R2qtnmG(acP z0d_^!t>}5W zpT`*2NR+0+SpTHb+6Js4b;%LJB;B_-ChhnU5py}iJtku*hm5F0!iql8Hrpcy1aYbT z1*dKC5ua6pMX@@iONI?Hpr%h;&YaXp9n!ND7-=a%BD7v&g zOO41M6EbE24mJ#S$Ui0-brR5ML%@|ndz^)YLMMV1atna{Fw<;TF@>d&F|!Z>8eg>>hkFrV)W+uv=`^F9^e zzzM2*oOjT9%gLoub%(R57p-`TXFe#oh1_{&N-YN z<}artH|m=d8TQuKSWE)Z%puU|g|^^NFwC#N=@dPhasyYjoy(fdEVfKR@cXKHZV-`06HsP`|Ftx;8(YD$fFXumLWbGnu$GMqRncXYY9mwz9$ap zQtfZB^_BeNYITh^hA7+(XNFox5WMeG_LtJ%*Q}$8VKDI_p8^pqX)}NMb`0e|wgF7D zuQACY_Ua<1ri{;Jwt@_1sW9zzdgnyh_O#8y+C;LcZq6=4e^cs6KvmK@$vVpKFGbQ= z$)Eux5C|Fx;Gtmv9^#Y-g@7Rt7*eLp5n!gJmn7&B_L$G?NCN`AP>cXQEz}%F%K;vUs{+l4Q{}eWW;ATe2 zqvXzxoIDy(u;F2q1JH7Sf;{jy_j})F+cKlIOmNfjBGHoG^CN zM|Ho&&X|L-36f}Q-obEACz`sI%2f&k>z5c$2TyTSj~vmO)BW~+N^kt`Jt@R|s!){H ze1_eCrlNaPkJQhL$WG&iRvF*YG=gXd1IyYQ9ew|iYn7r~g!wOnw;@n42>enAxBv*A zEmV*N#sxdicyNM=A4|yaOC5MByts}s_Hpfj|y<6G=o=!3S@eIFKDdpR7|FY>L&Wat&oW&cm&X~ z5Bt>Fcq(fgnvlvLSYg&o6>&fY`ODg4`V^lWWD=%oJ#Kbad2u~! zLECFS*??>|vDsNR&pH=Ze0Eo`sC_G`OjoEKVHY|wmwlX&(XBE<@sx3Hd^gtd-fNwUHsylg06p`U2y_={u}Bc Date: Tue, 17 Nov 2020 09:06:46 +0100 Subject: [PATCH 4/9] test release --- example/android/build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/example/android/build.gradle b/example/android/build.gradle index 6c143bb5..2fad23bd 100644 --- a/example/android/build.gradle +++ b/example/android/build.gradle @@ -42,6 +42,7 @@ allprojects { } androidExtension.buildTypes.release.signingConfig = androidExtension.signingConfigs.test + androidExtension.testBuildType = "release" } } } From b7040b9b96e40464efc08ac93ba312fb2e93a424 Mon Sep 17 00:00:00 2001 From: Tommy Nguyen Date: Fri, 20 Nov 2020 15:25:23 +0100 Subject: [PATCH 5/9] also build for x86 and x86_64 --- example/android/build.gradle | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/example/android/build.gradle b/example/android/build.gradle index 2fad23bd..a8e98051 100644 --- a/example/android/build.gradle +++ b/example/android/build.gradle @@ -27,11 +27,16 @@ task fetchDependencies() { allprojects { afterEvaluate { project -> - def configName = 'release' - def androidExtension = project.extensions.findByName("android") + def androidExtension = project.extensions.findByName('android') - if(androidExtension != null && project.name == "app") { + if(androidExtension != null && project.name == 'app') { // android extension from test-app pacakge + androidExtension.defaultConfig { + ndk { + abiFilters 'arm64-v8a', 'x86', 'x86_64' + } + } + androidExtension.signingConfigs { test { storeFile file('debug.keystore') @@ -42,7 +47,7 @@ allprojects { } androidExtension.buildTypes.release.signingConfig = androidExtension.signingConfigs.test - androidExtension.testBuildType = "release" + androidExtension.testBuildType = 'release' } } } From d41e49f844a3f4be067543d79fba56d09b5a221a Mon Sep 17 00:00:00 2001 From: Tommy Nguyen Date: Sat, 21 Nov 2020 00:28:13 +0100 Subject: [PATCH 6/9] restore Detox integration --- .../com/microsoft/reacttestapp/DetoxTest.java | 30 +++++++++++++++++++ example/android/build.gradle | 11 +++++++ example/examples/MergeItem.js | 10 +++++-- 3 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 example/android/app/src/androidTest/java/com/microsoft/reacttestapp/DetoxTest.java diff --git a/example/android/app/src/androidTest/java/com/microsoft/reacttestapp/DetoxTest.java b/example/android/app/src/androidTest/java/com/microsoft/reacttestapp/DetoxTest.java new file mode 100644 index 00000000..efac899a --- /dev/null +++ b/example/android/app/src/androidTest/java/com/microsoft/reacttestapp/DetoxTest.java @@ -0,0 +1,30 @@ +package com.microsoft.reacttestapp; + +import com.wix.detox.Detox; +import com.wix.detox.config.DetoxConfig; + +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() { + DetoxConfig detoxConfig = new DetoxConfig(); + detoxConfig.idlePolicyConfig.masterTimeoutSec = 90; + detoxConfig.idlePolicyConfig.idleResourceTimeoutSec = 60; + detoxConfig.rnContextLoadTimeoutSec = 60; + + Detox.runTests(mActivityRule, detoxConfig); + } +} diff --git a/example/android/build.gradle b/example/android/build.gradle index a8e98051..949174a1 100644 --- a/example/android/build.gradle +++ b/example/android/build.gradle @@ -26,6 +26,11 @@ task fetchDependencies() { } allprojects { + repositories { + maven { + url("$rootDir/../../node_modules/detox/Detox-android") + } + } afterEvaluate { project -> def androidExtension = project.extensions.findByName('android') @@ -48,6 +53,12 @@ allprojects { androidExtension.buildTypes.release.signingConfig = androidExtension.signingConfigs.test androidExtension.testBuildType = 'release' + + androidExtension.sourceSets.androidTest.java.srcDirs += "$rootDir/app/src/androidTest/java" + + project.dependencies { + androidTestImplementation('com.wix:detox:+') + } } } } diff --git a/example/examples/MergeItem.js b/example/examples/MergeItem.js index 81ac71a1..2ce8681b 100644 --- a/example/examples/MergeItem.js +++ b/example/examples/MergeItem.js @@ -217,7 +217,10 @@ export default class Merge extends Component {