From cb6d810a6fd41e9cfd2cf969a0e844a54dcdb8bb Mon Sep 17 00:00:00 2001 From: Randy Tarampi Date: Thu, 18 Apr 2019 11:34:39 -0700 Subject: [PATCH 01/17] Automatically start (and kill) an android emulator on `npm run e2e`. Android specific for now, but we'll cross the iOS bridge when we get there... --- .gitignore | 3 +++ README.md | 29 +++++++++++++++++------------ package.json | 12 +++++++++--- 3 files changed, 29 insertions(+), 15 deletions(-) diff --git a/.gitignore b/.gitignore index 5d64756..3333b1c 100644 --- a/.gitignore +++ b/.gitignore @@ -54,3 +54,6 @@ buck-out/ # Bundle artifact *.jsbundle + +# Installation artifacts +bin/android-wait-for-emulator \ No newline at end of file diff --git a/README.md b/README.md index f3f11b5..ee51c22 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ ### Make it run with ```bash -npm install +yarn install ``` ### Create your Signed APK (For android) @@ -25,18 +25,23 @@ keytool -genkeypair -v -keystore my-release-key.keystore -alias my-key-alias -ke mv my-release-key.keystore android/app/ # Build the APK -npm run bs:android +yarn run bs:android ``` -### Run tests +### Run tests locally ```bash # Run unit & integration tests -npm test +yarn test -# Run E2E tests locally -npm run test:e2e +# Create an AVD to run tests against locally +yarn run emulator:android:create +# Run E2E tests locally +yarn run test:e2e +``` +### Run tests in the cloud +```bash # Setup for uploading to cloud services export APP_PATH=$(pwd)/android/app/build/outputs/apk/release/app-release.apk export APP_EXTENSION=apk @@ -52,13 +57,13 @@ export BROWSERSTACK_USERNAME= export BROWSERSTACK_ACCESS_KEY= # Upload and run tests on Sauce Labs (emulated) -npm run upload:e2e:sauce -npm run test:e2e:sauce +yarn run upload:e2e:sauce +yarn run test:e2e:sauce # Upload and run tests on Sauce Labs (real devices) -npm run upload:e2e:testobject -npm run test:e2e:testobject +yarn run upload:e2e:testobject +yarn run test:e2e:testobject # Run it on BrowserStack -npm run upload:e2e:browserstack -npm run test:e2e:browserstack +yarn run upload:e2e:browserstack +yarn run test:e2e:browserstack ``` \ No newline at end of file diff --git a/package.json b/package.json index df78b85..5538e36 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,8 @@ "version": "0.0.1", "private": true, "scripts": { + "postinstall": "npm run postinstall:download:android-wait-for-emulator", + "postinstall:download:android-wait-for-emulator": "if [ ! -f bin/android-wait-for-emulator ]; then mkdir -p bin; curl https://raw.githubusercontent.com/travis-ci/travis-cookbooks/precise-stable/ci_environment/android-sdk/files/default/android-wait-for-emulator > bin/android-wait-for-emulator; chmod +x bin/android-wait-for-emulator; fi;", "start": "node node_modules/react-native/local-cli/cli.js start", "start:android": "adb uninstall com.apktest && react-native run-android", "start:android:release": "react-native run-android --variant=release", @@ -19,11 +21,15 @@ "clean:nodemodules": "rm -rf node_modules && rm -f package-lock.json && npm install", "build:android": "cd android && ./gradlew assembleRelease", "bs:android": "npm run build:android && adb uninstall com.apktest && npm run start:android:release", + "emulator:android:create": "avdmanager create avd -n $(node -p 'require(\"./package.json\").name') -k \"system-images;android-28;google_apis_playstore;x86\" -d \"Nexus 5X\"", + "emulator:android:delete": "avdmanager delete avd -n $(node -p 'require(\"./package.json\").name')", + "emulator:android:start": "emulator -avd $(node -p 'require(\"./package.json\").name') -no-audio -no-window & echo $! > /tmp/$(node -p 'require(\"./package.json\").name').emulator.android.pid; echo \"Started Android emulator (`cat /tmp/$(node -p 'require(\"./package.json\").name').emulator.android.pid`) from /tmp/$(node -p 'require(\"./package.json\").name').emulator.android.pid\"; ./bin/android-wait-for-emulator", "appium:start": "appium & echo $! > /tmp/$(node -p 'require(\"./package.json\").name').appium.pid; echo \"Started server (`cat /tmp/$(node -p 'require(\"./package.json\").name').appium.pid`) from /tmp/$(node -p 'require(\"./package.json\").name').appium.pid\";", - "pree2e": "npm run appium:kill; npm run appium:start", + "pree2e": "npm run emulator:android:kill; npm run emulator:android:start; npm run appium:kill; npm run appium:start", "e2e": "npm run appium:test", - "poste2e": "npm run appium:kill", - "appium:kill": "if [ -f /tmp/$(node -p 'require(\"./package.json\").name').appium.pid ]; then echo \"Killing appium (`cat /tmp/$(node -p 'require(\"./package.json\").name').appium.pid`) from /tmp/$(node -p 'require(\"./package.json\").name').appium.pid\"; kill -9 `cat /tmp/$(node -p 'require(\"./package.json\").name').appium.pid`; rm /tmp/$(node -p 'require(\"./package.json\").name').appium.pid; fi;" + "poste2e": "npm run appium:kill; npm run emulator:android:kill", + "appium:kill": "if [ -f /tmp/$(node -p 'require(\"./package.json\").name').appium.pid ]; then echo \"Killing appium (`cat /tmp/$(node -p 'require(\"./package.json\").name').appium.pid`) from /tmp/$(node -p 'require(\"./package.json\").name').appium.pid\"; kill -9 `cat /tmp/$(node -p 'require(\"./package.json\").name').appium.pid`; rm /tmp/$(node -p 'require(\"./package.json\").name').appium.pid; fi;", + "emulator:android:kill": "if [ -f /tmp/$(node -p 'require(\"./package.json\").name').emulator.android.pid ]; then echo \"Killing Android emulator (`cat /tmp/$(node -p 'require(\"./package.json\").name').emulator.android.pid`) from /tmp/$(node -p 'require(\"./package.json\").name').emulator.android.pid\"; kill -9 `cat /tmp/$(node -p 'require(\"./package.json\").name').emulator.android.pid`; rm /tmp/$(node -p 'require(\"./package.json\").name').emulator.android.pid; fi;" }, "dependencies": { "react": "16.8.3", From 46ec27a119340a65c7fbcd4a743bfabe8f1b365a Mon Sep 17 00:00:00 2001 From: Randy Tarampi Date: Sat, 20 Apr 2019 17:00:52 -0700 Subject: [PATCH 02/17] Specify a `adbExecTimeout` --- e2e/capabilities.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/e2e/capabilities.js b/e2e/capabilities.js index b539aac..124e778 100644 --- a/e2e/capabilities.js +++ b/e2e/capabilities.js @@ -66,7 +66,8 @@ export const android = { automationName: 'UiAutomator2', deviceName: ANDROID_DEVICE_NAME, platformVersion: ANDROID_PLATFORM_VERSION, - app: ANDROID_APPLICATION_PATH + app: ANDROID_APPLICATION_PATH, + adbExecTimeout: DEVICE_TIMEOUT } }; @@ -94,11 +95,13 @@ if (process.env.SAUCE) { delete android.capabilities.waitforTimeout; delete android.capabilities.commandTimeout; delete android.capabilities.newCommandTimeout; + delete android.capabilities.adbExecTimeout; android.capabilities.automationName = android.capabilities.automationName.toLowerCase(); // NOTE-RT: Sauce Labs wants this to be lowercase for some reason delete ios.capabilities.waitforTimeout; delete ios.capabilities.commandTimeout; delete ios.capabilities.newCommandTimeout; + delete ios.capabilities.adbExecTimeout; } if (process.env.TESTOBJECT) { From 741d133999fa37df96da89d3285946f34dd47ea5 Mon Sep 17 00:00:00 2001 From: Randy Tarampi Date: Sun, 21 Apr 2019 01:25:39 -0700 Subject: [PATCH 03/17] Add a bunch of android specific timeouts. And make them long. Just for slow environments. --- e2e/capabilities.js | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/e2e/capabilities.js b/e2e/capabilities.js index 124e778..4d5eb07 100644 --- a/e2e/capabilities.js +++ b/e2e/capabilities.js @@ -26,7 +26,8 @@ const common = { appiumVersion, waitforTimeout: DEVICE_TIMEOUT, commandTimeout: DEVICE_TIMEOUT, - newCommandTimeout: DEVICE_TIMEOUT + newCommandTimeout: DEVICE_TIMEOUT, + isHeadless: process.env.HEADLESS || process.env.CI || false } }; @@ -67,7 +68,14 @@ export const android = { deviceName: ANDROID_DEVICE_NAME, platformVersion: ANDROID_PLATFORM_VERSION, app: ANDROID_APPLICATION_PATH, - adbExecTimeout: DEVICE_TIMEOUT + adbExecTimeout: DEVICE_TIMEOUT, + androidDeviceReadyTimeout: DEVICE_TIMEOUT, + androidInstallTimeout: DEVICE_TIMEOUT, + appWaitDuration: DEVICE_TIMEOUT, + avdLaunchTimeout: DEVICE_TIMEOUT, + avdReadyTimeout: DEVICE_TIMEOUT, + uiautomator2ServerInstallTimeout: DEVICE_TIMEOUT, + uiautomator2ServerLaunchTimeout: DEVICE_TIMEOUT } }; @@ -96,6 +104,13 @@ if (process.env.SAUCE) { delete android.capabilities.commandTimeout; delete android.capabilities.newCommandTimeout; delete android.capabilities.adbExecTimeout; + delete android.capabilities.androidDeviceReadyTimeout; + delete android.capabilities.androidInstallTimeout; + delete android.capabilities.appWaitDuration; + delete android.capabilities.avdLaunchTimeout; + delete android.capabilities.avdReadyTimeout; + delete android.capabilities.uiautomator2ServerInstallTimeout; + delete android.capabilities.uiautomator2ServerLaunchTimeout; android.capabilities.automationName = android.capabilities.automationName.toLowerCase(); // NOTE-RT: Sauce Labs wants this to be lowercase for some reason delete ios.capabilities.waitforTimeout; From 0ee6df7d41feae0556dadda90ff6ad61261ffd2f Mon Sep 17 00:00:00 2001 From: Randy Tarampi Date: Sat, 20 Apr 2019 17:01:26 -0700 Subject: [PATCH 04/17] `emulator:android:create` script shouldn't specify a device. It's not totally necessary. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5538e36..d8f110f 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "clean:nodemodules": "rm -rf node_modules && rm -f package-lock.json && npm install", "build:android": "cd android && ./gradlew assembleRelease", "bs:android": "npm run build:android && adb uninstall com.apktest && npm run start:android:release", - "emulator:android:create": "avdmanager create avd -n $(node -p 'require(\"./package.json\").name') -k \"system-images;android-28;google_apis_playstore;x86\" -d \"Nexus 5X\"", + "emulator:android:create": "avdmanager create avd -n $(node -p 'require(\"./package.json\").name') -k \"system-images;android-28;google_apis_playstore;x86\"", "emulator:android:delete": "avdmanager delete avd -n $(node -p 'require(\"./package.json\").name')", "emulator:android:start": "emulator -avd $(node -p 'require(\"./package.json\").name') -no-audio -no-window & echo $! > /tmp/$(node -p 'require(\"./package.json\").name').emulator.android.pid; echo \"Started Android emulator (`cat /tmp/$(node -p 'require(\"./package.json\").name').emulator.android.pid`) from /tmp/$(node -p 'require(\"./package.json\").name').emulator.android.pid\"; ./bin/android-wait-for-emulator", "appium:start": "appium & echo $! > /tmp/$(node -p 'require(\"./package.json\").name').appium.pid; echo \"Started server (`cat /tmp/$(node -p 'require(\"./package.json\").name').appium.pid`) from /tmp/$(node -p 'require(\"./package.json\").name').appium.pid\";", From 71fedc9d48037460921a9f30f9d9dd68ca18c427 Mon Sep 17 00:00:00 2001 From: Randy Tarampi Date: Sun, 21 Apr 2019 10:42:09 -0700 Subject: [PATCH 05/17] Add `deviceReadyTimeout` and note how it and `androidDeviceReadyTimeout` are actually in seconds, not milliseconds. --- e2e/capabilities.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/e2e/capabilities.js b/e2e/capabilities.js index 4d5eb07..285961e 100644 --- a/e2e/capabilities.js +++ b/e2e/capabilities.js @@ -69,7 +69,8 @@ export const android = { platformVersion: ANDROID_PLATFORM_VERSION, app: ANDROID_APPLICATION_PATH, adbExecTimeout: DEVICE_TIMEOUT, - androidDeviceReadyTimeout: DEVICE_TIMEOUT, + deviceReadyTimeout: DEVICE_TIMEOUT / 1000, // NOTE-RT: This is actually just in seconds + androidDeviceReadyTimeout: DEVICE_TIMEOUT / 1000, // NOTE-RT: This is actually just in seconds androidInstallTimeout: DEVICE_TIMEOUT, appWaitDuration: DEVICE_TIMEOUT, avdLaunchTimeout: DEVICE_TIMEOUT, @@ -104,6 +105,7 @@ if (process.env.SAUCE) { delete android.capabilities.commandTimeout; delete android.capabilities.newCommandTimeout; delete android.capabilities.adbExecTimeout; + delete android.capabilities.deviceReadyTimeout; delete android.capabilities.androidDeviceReadyTimeout; delete android.capabilities.androidInstallTimeout; delete android.capabilities.appWaitDuration; From 4cc338701fc0f5667cd72b810c83e8a967fcb8aa Mon Sep 17 00:00:00 2001 From: Randy Tarampi Date: Sun, 21 Apr 2019 12:57:15 -0700 Subject: [PATCH 06/17] `disableWindowAnimation` on Android clients in `CI` or `HEADLESS` envs. --- e2e/capabilities.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/e2e/capabilities.js b/e2e/capabilities.js index 285961e..083b394 100644 --- a/e2e/capabilities.js +++ b/e2e/capabilities.js @@ -76,7 +76,8 @@ export const android = { avdLaunchTimeout: DEVICE_TIMEOUT, avdReadyTimeout: DEVICE_TIMEOUT, uiautomator2ServerInstallTimeout: DEVICE_TIMEOUT, - uiautomator2ServerLaunchTimeout: DEVICE_TIMEOUT + uiautomator2ServerLaunchTimeout: DEVICE_TIMEOUT, + disableWindowAnimation: process.env.HEADLESS || process.env.CI || false } }; @@ -113,6 +114,7 @@ if (process.env.SAUCE) { delete android.capabilities.avdReadyTimeout; delete android.capabilities.uiautomator2ServerInstallTimeout; delete android.capabilities.uiautomator2ServerLaunchTimeout; + delete android.capabilities.disableWindowAnimation; android.capabilities.automationName = android.capabilities.automationName.toLowerCase(); // NOTE-RT: Sauce Labs wants this to be lowercase for some reason delete ios.capabilities.waitforTimeout; From 5ff010ea3db322b0e9075ed0b1c14370389b083b Mon Sep 17 00:00:00 2001 From: Randy Tarampi Date: Sun, 21 Apr 2019 15:01:23 -0700 Subject: [PATCH 07/17] Prefer `jest-circus/runner` so we bail out when `beforeAll` explodes. Per https://github.com/facebook/jest/issues/2713#issuecomment-430932326 --- e2e/__tests__/appium-test-wd.js | 3 ++- e2e/jest.config.json | 1 + jest.config.json | 1 + package.json | 1 + yarn.lock | 22 ++++++++++++++++++++++ 5 files changed, 27 insertions(+), 1 deletion(-) diff --git a/e2e/__tests__/appium-test-wd.js b/e2e/__tests__/appium-test-wd.js index fe58dec..d82c7c8 100644 --- a/e2e/__tests__/appium-test-wd.js +++ b/e2e/__tests__/appium-test-wd.js @@ -3,11 +3,12 @@ import * as wd from 'wd'; import * as capabilities from '../capabilities'; import {DEVICE_TIMEOUT, JEST_TIMEOUT, TARGET_PLATFORM} from '../constants'; +jest.setTimeout(JEST_TIMEOUT); + describe('Create Android session (wd)', () => { let driver; beforeAll(async () => { - jest.setTimeout(JEST_TIMEOUT); const {capabilities: deviceConfig, ...serverConfig} = capabilities[TARGET_PLATFORM]; diff --git a/e2e/jest.config.json b/e2e/jest.config.json index 1238c70..e5c211f 100644 --- a/e2e/jest.config.json +++ b/e2e/jest.config.json @@ -1,3 +1,4 @@ { + "testRunner": "jest-circus/runner", "verbose": true } diff --git a/jest.config.json b/jest.config.json index 3821fc1..7febdfe 100644 --- a/jest.config.json +++ b/jest.config.json @@ -5,5 +5,6 @@ "/node_modules/", "/e2e/" ], + "testRunner": "jest-circus/runner", "verbose": true } diff --git a/package.json b/package.json index d8f110f..d8379c3 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "emulator:android:kill": "if [ -f /tmp/$(node -p 'require(\"./package.json\").name').emulator.android.pid ]; then echo \"Killing Android emulator (`cat /tmp/$(node -p 'require(\"./package.json\").name').emulator.android.pid`) from /tmp/$(node -p 'require(\"./package.json\").name').emulator.android.pid\"; kill -9 `cat /tmp/$(node -p 'require(\"./package.json\").name').emulator.android.pid`; rm /tmp/$(node -p 'require(\"./package.json\").name').emulator.android.pid; fi;" }, "dependencies": { + "jest-circus": "^24.7.1", "react": "16.8.3", "react-native": "0.59.4" }, diff --git a/yarn.lock b/yarn.lock index fbec858..c81d29f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4736,6 +4736,28 @@ jest-changed-files@^24.7.0: execa "^1.0.0" throat "^4.0.0" +jest-circus@^24.7.1: + version "24.7.1" + resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-24.7.1.tgz#f8dc4db5217eb010d71ad2f5b6e6f357590ccd01" + integrity sha512-zDNSS+7qQN0nSbR77qcOb+tOUWLcvZGzvXE1PjoV6xeHV5Vz7DPK9JkBIQF/n5Nz9yZbApDxq5NqGqmVCe0nnQ== + dependencies: + "@babel/traverse" "^7.1.0" + "@jest/environment" "^24.7.1" + "@jest/test-result" "^24.7.1" + "@jest/types" "^24.7.0" + chalk "^2.0.1" + co "^4.6.0" + expect "^24.7.1" + is-generator-fn "^2.0.0" + jest-each "^24.7.1" + jest-matcher-utils "^24.7.0" + jest-message-util "^24.7.1" + jest-snapshot "^24.7.1" + jest-util "^24.7.1" + pretty-format "^24.7.0" + stack-utils "^1.0.1" + throat "^4.0.0" + jest-cli@^24.7.1: version "24.7.1" resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-24.7.1.tgz#6093a539073b6f4953145abeeb9709cd621044f1" From 8c21d70564be6823d9af0f1e39bed75512abf9ca Mon Sep 17 00:00:00 2001 From: Randy Tarampi Date: Mon, 22 Apr 2019 00:30:17 -0700 Subject: [PATCH 08/17] `emulator:android:create` shouldn't require any input. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d8379c3..b8fc325 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "clean:nodemodules": "rm -rf node_modules && rm -f package-lock.json && npm install", "build:android": "cd android && ./gradlew assembleRelease", "bs:android": "npm run build:android && adb uninstall com.apktest && npm run start:android:release", - "emulator:android:create": "avdmanager create avd -n $(node -p 'require(\"./package.json\").name') -k \"system-images;android-28;google_apis_playstore;x86\"", + "emulator:android:create": "echo no | avdmanager create avd -n $(node -p 'require(\"./package.json\").name') -k \"system-images;android-28;google_apis_playstore;x86\"", "emulator:android:delete": "avdmanager delete avd -n $(node -p 'require(\"./package.json\").name')", "emulator:android:start": "emulator -avd $(node -p 'require(\"./package.json\").name') -no-audio -no-window & echo $! > /tmp/$(node -p 'require(\"./package.json\").name').emulator.android.pid; echo \"Started Android emulator (`cat /tmp/$(node -p 'require(\"./package.json\").name').emulator.android.pid`) from /tmp/$(node -p 'require(\"./package.json\").name').emulator.android.pid\"; ./bin/android-wait-for-emulator", "appium:start": "appium & echo $! > /tmp/$(node -p 'require(\"./package.json\").name').appium.pid; echo \"Started server (`cat /tmp/$(node -p 'require(\"./package.json\").name').appium.pid`) from /tmp/$(node -p 'require(\"./package.json\").name').appium.pid\";", From b78cf78cb13002a15876929bf05b32f44350906d Mon Sep 17 00:00:00 2001 From: Randy Tarampi Date: Sun, 21 Apr 2019 10:47:33 -0700 Subject: [PATCH 09/17] Give appium some more time to install the application by `sleep`ing for longer before tests. --- e2e/__tests__/appium-test-wd.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/e2e/__tests__/appium-test-wd.js b/e2e/__tests__/appium-test-wd.js index d82c7c8..413c1d9 100644 --- a/e2e/__tests__/appium-test-wd.js +++ b/e2e/__tests__/appium-test-wd.js @@ -24,7 +24,7 @@ describe('Create Android session (wd)', () => { // Start the session await driver.init(deviceConfig) .setImplicitWaitTimeout(DEVICE_TIMEOUT) - .sleep(5000); + .sleep(DEVICE_TIMEOUT); console.info('[beforeAll] driver initialized %j', driver); }); From 796a55757c9409a093b79b1eb522f39e5f847f80 Mon Sep 17 00:00:00 2001 From: Randy Tarampi Date: Mon, 22 Apr 2019 14:23:01 -0700 Subject: [PATCH 10/17] Note where I left off with Travis. (#2) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Try hooking this up all up in Travis. Two jobs, one to run the unit tests and one to run the E2E ones headlessly on Travis infrastructure. Tempted to add jobs to build and run against BrowserStack, Sauce Labs and TestObject, but one thing at a time first... @hadnazzar already committed the dummy credentials in `android/gradle.properties` though, so it's not like having Travis decrypt this committed keystore will be more secure or anything. If anything, it's worse 😂 * Just install Android SDK dependencies ourselves. Travis is still using the deprecated `android` command to install things per https://github.com/travis-ci/travis-ci/issues/8874#issuecomment-350350607, which might be behind this issue (and others to come). * Actually, just prefer letting Travis install these (properly). Copy/pasta problems coming from the `sdkmanager` command notation - `android` should still be able to find these versions no? * Try for `ANDROID_BUILD_TOOLS_VERSION` at 28.0.3. * `npm install -g yarn` `before_install` on `E2E tests (android)`. * Use the given `nvm` to install `$TRAVIS_NODE_VERSION`. Travis still sets this right? I mean, the version is in the build name... * Defer to `TRAVIS_NODE_VERSION` if it exists, otherwise use `10`. * Explicitly say `yes` to all `sdkmanager --licenses`. * Just explicitly install the Android SDK per https://github.com/travis-ci/travis-ci/issues/8874#issuecomment-350350607. Why wasn't this license already accepted? * Explicitly call `$ANDROID_HOME/tools/bin/sdkmanager`. And accept all licenses. * Remove extra slash. * `${ANDROID_HOME}` is what needs to be evaluated, not `$ANDROID_HOMEtools` * This wanted to be a space, not a `"`. * Whitelist all the licenses, even though it shouldn't matter... * Set `supportLibVersion` to be the same as `buildToolsVersion`. * Move `node_js` language/version selection to the `Unit tests` job. * Revert "Set `supportLibVersion` to be the same as `buildToolsVersion`." Yeah. No. This reverts commit 6fd8f255ea8eb3eea76328a3696b07acfe3cfafc. * Just `echo` in the license manually... * Run `build:android`, not `bs:android`. * Actually specify the correct `ANDROID_API_VERSION`. And use `android create avd` and `android delete avd` instead of `avdmanager`, which doesn't seem to get installed properly. * `pree2e` script should `adb wait-for-device get-serialno`. * Note the closest I got to getting Travis to run the app on an emulator. I'm pretty sure I'm just running into a race condition somewhere installing the app, but I can't quite tell exactly where, or what to wait for. * Just bring our own Android environment per https://medium.com/@nocnoc/android-emulators-in-thcloud-f39e11c15bfa. It boots the emulators, but getting Appium to install the emulators is a whole other story... * Fix missing `ANDROID_TOOLS_BUILD_VERSION` and incorrect `ANDROID_EMULATOR_PLATFORM_VERSION`. * Define `ANDROID_HOME` and `ANDROID_SDK_ROOT` in `before_install`. Not `before_script`. * Try testing with `android-23` on `armeabi-v7a`. * Try and let `appium` boot and initialize the emulator on its own. * Only `sleep` for at most 60 seconds `beforeAll` `wd` E2E tests. * Set Travis timeouts to be 10 minutes. Worth a shot I guess? * Revert "Only `sleep` for at most 60 seconds `beforeAll` `wd` E2E tests." Just defer to the `DEVICE_TIMEOUT` here. This reverts commit 4f1416cae7d874fa3d0a70600565952900062f27. * Don't bother caching `gradle` artifacts. * Just boot the emulator ourselves. This commit reverts (conceptually) 4ae60c04c4572d335b20477b77ccf7d6df28c2e2. * `sdkmanager` installs `tools` on its own, to avoid `sdkmanager` errors. I don't know why it can't find `tools` sometimes, but this seems to help. * I think the whitespace here is tripping Travis up. It finds `tools` just fine when I copy/paste this file in... * Travis allows the local E2E Android tests to fail. * Add a separate `E2E` stage. --- .travis.yml | 99 ++++++++++++++++++++++++ android/app/my-release-key.keystore.enc | Bin 0 -> 2256 bytes e2e/capabilities.js | 6 +- e2e/constants.js | 15 ++-- package.json | 2 +- 5 files changed, 114 insertions(+), 8 deletions(-) create mode 100644 .travis.yml create mode 100644 android/app/my-release-key.keystore.enc diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..5686c21 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,99 @@ +notifications: + email: false +os: linux +dist: trusty # NOTE-RT: This needs to be `trusty`, android builds just hang on `xenial` :\ +env: + global: + - ANDROID_PLATFORM_VERSION=9 + - ANDROID_API_VERSION=28 + - ANDROID_TOOLS_BUILD_VERSION=4333796 # NOTE-RT: To match `ANDROID_API_VERSION` + - ANDROID_BUILD_TOOLS_VERSION=28.0.3 + - ANDROID_EMULATOR_PLATFORM_VERSION=6.0 + - ANDROID_EMULATOR_API_VERSION=23 + - ANDROID_EMULATOR_ABI=armeabi-v7a +jobs: + allow_failures: + - script: ANDROID_PLATFORM_VERSION=$ANDROID_EMULATOR_PLATFORM_VERSION JEST_TIMEOUT=600000 DEVICE_TIMEOUT=300000 npm run appium:test + include: + - language: node_js + node_js: 10 + cache: yarn + - stage: E2E + name: Android (local) + sudo: required + language: android + jdk: oraclejdk8 + cache: yarn + android: + components: + - tools + - platform-tools + before_install: + - export ANDROID_HOME=~/android-sdk + - export ANDROID_SDK_ROOT=$ANDROID_HOME + - | + if [ $TRAVIS_OS_NAME == "linux" ]; then + export DISPLAY=:99.0; + sh -e /etc/init.d/xvfb start; + sleep 3; # give xvfb some time to start + fi + - wget -q "https://dl.google.com/android/repository/sdk-tools-linux-$ANDROID_TOOLS_BUILD_VERSION.zip" -O android-sdk-tools.zip + - unzip -q android-sdk-tools.zip -d ${ANDROID_HOME} + - rm android-sdk-tools.zip + - PATH=${PATH}:${ANDROID_HOME}/tools:${ANDROID_HOME}/tools/bin:${ANDROID_HOME}/platform-tools + - mkdir -p ~/.android + - touch ~/.android/repositories.cfg + - yes | $ANDROID_HOME/tools/bin/sdkmanager --licenses + - | + $ANDROID_HOME/tools/bin/sdkmanager \ + "tools" \ + "platform-tools" \ + "emulator" \ + "build-tools;$ANDROID_BUILD_TOOLS_VERSION" \ + "system-images;android-$ANDROID_EMULATOR_API_VERSION;google_apis;$ANDROID_EMULATOR_ABI" \ + "patcher;v4" \ + "platforms;android-$ANDROID_API_VERSION" \ + "platforms;android-$ANDROID_EMULATOR_API_VERSION" \ + "extras;google;google_play_services" \ + "extras;google;m2repository" \ + "extras;android;m2repository" > /dev/null + - openssl aes-256-cbc -K $encrypted_78b6aab41b54_key -iv $encrypted_78b6aab41b54_iv -in android/app/my-release-key.keystore.enc -out android/app/my-release-key.keystore -d + - export NODE_VERSION=${TRAVIS_NODE_VERSION:=10} + - nvm install $NODE_VERSION + - npm install -g yarn + install: + - yarn install + - npm run build:android + before_script: + - echo no | $ANDROID_HOME/tools/bin/avdmanager create avd -n $(node -p 'require("./package.json").name') -k "system-images;android-$ANDROID_EMULATOR_API_VERSION;google_apis;$ANDROID_EMULATOR_ABI" # NOTE-RT: This should really be `yarn run emulator:android:create` + # NOTE-RT: The following here should really be `npm run pree2e` + - EMU_ADDITIONAL_OPTIONS="-no-boot-anim -no-audio -no-window -memory 2048" + - EMU_PARAMS="-gpu off" + - $ANDROID_HOME/emulator/emulator -avd $(node -p 'require("./package.json").name') $EMU_PARAMS $EMU_ADDITIONAL_OPTIONS ${EMU_DEBUG} & echo $! > /tmp/$(node -p 'require("./package.json").name').emulator.android.pid; echo "Started Android emulator (`cat /tmp/$(node -p 'require("./package.json").name').emulator.android.pid`) from /tmp/$(node -p 'require("./package.json").name').emulator.android.pid" + - ./bin/android-wait-for-emulator + - $ANDROID_HOME/platform-tools/adb devices + - $ANDROID_HOME/platform-tools/adb shell input keyevent 82 & + - npm run appium:kill + - npm run appium:start + script: + - ANDROID_PLATFORM_VERSION=$ANDROID_EMULATOR_PLATFORM_VERSION JEST_TIMEOUT=600000 DEVICE_TIMEOUT=300000 npm run appium:test + after_script: + - npm run poste2e # NOTE-RT: Make sure we kill things and clean up + - android delete avd -n $(node -p 'require("./package.json").name') # NOTE-RT: This should really be `npm run emulator:android:delete` +# - name: E2E tests (iOS) +# os: osx +# language: objective-c +# podfile: ios/Podfile +# osx_image: xcode10.2 +# cache: +# - yarn +# - cocoapods +# before_install: +# - export NODE_VERSION=${TRAVIS_NODE_VERSION:=10} +# - nvm install $NODE_VERSION +# - npm install -g yarn +# install: +# - yarn install +# - pushd ios && pod install && popd ios + + diff --git a/android/app/my-release-key.keystore.enc b/android/app/my-release-key.keystore.enc new file mode 100644 index 0000000000000000000000000000000000000000..f17e8e1ae90eefd3835c33d15e13d5022c3e8b17 GIT binary patch literal 2256 zcmV;>2ru_ISR=ZklcbxF-_Rrz7AH2CV1JyeDse*BiET&$c=?l5H!f7-apXPRH$)(L zHxa$kFa8ha{ih_xCA{v*D`!YsC5cYx(yzYn{{DZ%%KJ!6 z`6dZzkc^nvA}^K>UkO~^4r`QWhd;9T*%a9)2fQRvKmy5&`!=p#+yl!9k*T`J`H7~- z^;h@%0^6G-ygA%-=sF8681X6y4=iyR z)T&smm948~X$p7Va6tvF5nYz+Qy)~p0Qj`XaRd;EGN%i#mf&|^!>~)2h`LLrov2Tp zO`Mitju6Mf4h+*#iXYv&q&4XqhMybUxi|;W9)J@sz$G-w08>Tn8mv3JHI7YrFh8XO z`kxl?q>(&vHvTv|J~afp=;eQ=@>W`hDZ<0tQv>(lV;x*w_&yub(`r%~INN~fM_WAf zw$=S7IiYT4(5wFix3Ycj0e^D1{U6(a_uPfwn>og1D&72}eLN}_YzHQHJ~iyvkG!+Y z>Y_R`x+X>n!Ck6`^*<>n$FmVf2F1@F=)M1+`23zeA`XGSm`9Pf>Pt#jcXxWd7a<4)-*lnvx4v(an-5m z7dgCr;i&R_(|_v6(>nyiM}&WK&6dv3wNzn+$R@!a%|^gnHJp|V0?;PLbyxFtyC);H zHfYNc)a@lc>D#i%xoy4!SmYifAXJqSVR80MY|?pb+1tyeI;9f9H(AT>NVs-%vnOII*K)W!_{FY->6z2x z4_V!yG+1{#38d}b`bWLNvk|T(5|1(w{cI9>!}xGB`(IfMXy71Hff>?eD=o+2 zuOHT6A=IUfl6La9?@9+X%jnAF1X zICdHH97JliP=dir(>EDdJ{2I4v|L=mXGzo%k}Y}Zs=X@i5!Sl4~) z>~GXZufUKQm2Y`0OSgQVbK1c~6dGP^PH^3AW&5Fi5cO8I>BRU17CGYh1nr@FzUlIf zEX@z^h_>9+3IkzHF>GZ{R08lrpj&e3J>{H}2fkz@Z4>d%cw)F_l=3*Rn*6_PT1hC^ z@Y+SE;s-H3F>UK4QGMhZj9;hWVg`T5ZWI5qo$ zhi3z7E=qJHAsVT3E2#){dWn~cV(=*=&ioh5o@@32wiIL0OpBd(>uA4u(LonEq(g2L{07c)ZV$XhibdX!wY8|ET7g?j;neqwa#CWpo0w9IRx~`g- z&c4a%7xFf8h2_(J(OgnEf!aF!#Y-E~jp))!s+le>J*P1`j!=Q6CZFNjN_B68y~nZD zy;Ha(%ru?Z_lCn&y2=<7pKB4TQWrS!Z)uSU6U~1rfXlVW48%4Pi5zK*oY+n985x%r ekZCIJ%ge!f{>{Da!F1rKBkV8Q{_Y|4qSsIU;&hw< literal 0 HcmV?d00001 diff --git a/e2e/capabilities.js b/e2e/capabilities.js index 083b394..de3d6c8 100644 --- a/e2e/capabilities.js +++ b/e2e/capabilities.js @@ -1,5 +1,7 @@ import { ANDROID_APPLICATION_PATH, + ANDROID_AVD_ARGS, + ANDROID_AVD_NAME, ANDROID_DEVICE_NAME, ANDROID_PLATFORM_VERSION, APPIUM_HOST, @@ -77,7 +79,9 @@ export const android = { avdReadyTimeout: DEVICE_TIMEOUT, uiautomator2ServerInstallTimeout: DEVICE_TIMEOUT, uiautomator2ServerLaunchTimeout: DEVICE_TIMEOUT, - disableWindowAnimation: process.env.HEADLESS || process.env.CI || false + disableWindowAnimation: process.env.HEADLESS || process.env.CI || false, + avd: ANDROID_AVD_NAME, + avdArgs: ANDROID_AVD_ARGS } }; diff --git a/e2e/constants.js b/e2e/constants.js index e755a0b..c9ee5c4 100644 --- a/e2e/constants.js +++ b/e2e/constants.js @@ -1,10 +1,13 @@ import path from 'path'; +import {name} from '../package.json'; -export const ANDROID_DEVICE_NAME = process.env.ANDROID_DEVICE_NAME || 'Nexus_5X_API_28'; -export const ANDROID_PLATFORM_VERSION = process.env.ANDROID_PLATFORM_VERSION || '9'; +export const ANDROID_AVD_NAME = process.env.ANDROID_AVD_NAME; +export const ANDROID_AVD_ARGS = process.env.ANDROID_AVD_ARGS; +export const ANDROID_DEVICE_NAME = process.env.ANDROID_DEVICE_NAME || ANDROID_AVD_NAME || name; +export const ANDROID_PLATFORM_VERSION = process.env.ANDROID_PLATFORM_VERSION; export const ANDROID_APPLICATION_PATH = process.env.ANDROID_APPLICATION_PATH || path.resolve(__dirname, '../android/app/build/outputs/apk/release/app-release.apk'); -export const DEVICE_TIMEOUT = process.env.DEVICE_TIMEOUT || 90 * 1000; +export const DEVICE_TIMEOUT = process.env.DEVICE_TIMEOUT || 1.5 * 60 * 1000; export const IOS_DEVICE_NAME = process.env.IOS_DEVICE_NAME || 'iPhone SE'; export const IOS_PLATFORM_VERSION = process.env.IOS_PLATFORM_VERSION || '12.1'; @@ -12,10 +15,10 @@ export const IOS_APPLICATION_PATH = process.env.IOS_APPLICATION_PATH; // FIXME-R export const APPIUM_HOST = process.env.APPIUM_HOST || 'localhost'; export const APPIUM_LOG_LEVEL = process.env.APPIUM_LOG_LEVEL || 'debug'; -export const APPIUM_PASSWORD = process.env.APPIUM_PASSWORD || undefined; +export const APPIUM_PASSWORD = process.env.APPIUM_PASSWORD; export const APPIUM_PORT = process.env.APPIUM_PORT && Number(process.env.APPIUM_PORT) || 4723; -export const APPIUM_USER = process.env.APPIUM_USER || undefined; +export const APPIUM_USER = process.env.APPIUM_USER; export const TARGET_PLATFORM = process.env.TARGET_PLATFORM || 'android'; -export const JEST_TIMEOUT = process.env.JEST_TIMEOUT || 180 * 1000; +export const JEST_TIMEOUT = process.env.JEST_TIMEOUT || 3 * 60 * 1000; diff --git a/package.json b/package.json index b8fc325..0a07635 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "emulator:android:delete": "avdmanager delete avd -n $(node -p 'require(\"./package.json\").name')", "emulator:android:start": "emulator -avd $(node -p 'require(\"./package.json\").name') -no-audio -no-window & echo $! > /tmp/$(node -p 'require(\"./package.json\").name').emulator.android.pid; echo \"Started Android emulator (`cat /tmp/$(node -p 'require(\"./package.json\").name').emulator.android.pid`) from /tmp/$(node -p 'require(\"./package.json\").name').emulator.android.pid\"; ./bin/android-wait-for-emulator", "appium:start": "appium & echo $! > /tmp/$(node -p 'require(\"./package.json\").name').appium.pid; echo \"Started server (`cat /tmp/$(node -p 'require(\"./package.json\").name').appium.pid`) from /tmp/$(node -p 'require(\"./package.json\").name').appium.pid\";", - "pree2e": "npm run emulator:android:kill; npm run emulator:android:start; npm run appium:kill; npm run appium:start", + "pree2e": "npm run emulator:android:kill; npm run emulator:android:start; npm run appium:kill; npm run appium:start; adb wait-for-device get-serialno;", "e2e": "npm run appium:test", "poste2e": "npm run appium:kill; npm run emulator:android:kill", "appium:kill": "if [ -f /tmp/$(node -p 'require(\"./package.json\").name').appium.pid ]; then echo \"Killing appium (`cat /tmp/$(node -p 'require(\"./package.json\").name').appium.pid`) from /tmp/$(node -p 'require(\"./package.json\").name').appium.pid\"; kill -9 `cat /tmp/$(node -p 'require(\"./package.json\").name').appium.pid`; rm /tmp/$(node -p 'require(\"./package.json\").name').appium.pid; fi;", From 9a8355979b4b1c05f7136bc52f90d8b4e773db74 Mon Sep 17 00:00:00 2001 From: Randy Tarampi Date: Mon, 22 Apr 2019 14:26:09 -0700 Subject: [PATCH 11/17] Add a separate `DRIVER_INITIALIZATION_TIMEOUT`. --- e2e/__tests__/appium-test-wd.js | 4 ++-- e2e/constants.js | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/e2e/__tests__/appium-test-wd.js b/e2e/__tests__/appium-test-wd.js index 413c1d9..33d205a 100644 --- a/e2e/__tests__/appium-test-wd.js +++ b/e2e/__tests__/appium-test-wd.js @@ -1,7 +1,7 @@ import path from 'path'; import * as wd from 'wd'; import * as capabilities from '../capabilities'; -import {DEVICE_TIMEOUT, JEST_TIMEOUT, TARGET_PLATFORM} from '../constants'; +import {DEVICE_TIMEOUT, DRIVER_INITIALIZATION_TIMEOUT, JEST_TIMEOUT, TARGET_PLATFORM} from '../constants'; jest.setTimeout(JEST_TIMEOUT); @@ -24,7 +24,7 @@ describe('Create Android session (wd)', () => { // Start the session await driver.init(deviceConfig) .setImplicitWaitTimeout(DEVICE_TIMEOUT) - .sleep(DEVICE_TIMEOUT); + .sleep(DRIVER_INITIALIZATION_TIMEOUT); console.info('[beforeAll] driver initialized %j', driver); }); diff --git a/e2e/constants.js b/e2e/constants.js index c9ee5c4..bda5306 100644 --- a/e2e/constants.js +++ b/e2e/constants.js @@ -9,6 +9,8 @@ export const ANDROID_APPLICATION_PATH = process.env.ANDROID_APPLICATION_PATH || export const DEVICE_TIMEOUT = process.env.DEVICE_TIMEOUT || 1.5 * 60 * 1000; +export const DRIVER_INITIALIZATION_TIMEOUT = process.env.DRIVER_INITIALIZATION_TIMEOUT || 5 * 1000; + export const IOS_DEVICE_NAME = process.env.IOS_DEVICE_NAME || 'iPhone SE'; export const IOS_PLATFORM_VERSION = process.env.IOS_PLATFORM_VERSION || '12.1'; export const IOS_APPLICATION_PATH = process.env.IOS_APPLICATION_PATH; // FIXME-RT: Provide a real directory here From 733cf1fa5cf716a8e530366ffb0f339749464890 Mon Sep 17 00:00:00 2001 From: Randy Tarampi Date: Mon, 22 Apr 2019 14:27:47 -0700 Subject: [PATCH 12/17] Set `DRIVER_INITIALIZATION_TIMEOUT` on `Android (local)` to 5 minutes. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 5686c21..83b550a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -76,7 +76,7 @@ jobs: - npm run appium:kill - npm run appium:start script: - - ANDROID_PLATFORM_VERSION=$ANDROID_EMULATOR_PLATFORM_VERSION JEST_TIMEOUT=600000 DEVICE_TIMEOUT=300000 npm run appium:test + - ANDROID_PLATFORM_VERSION=$ANDROID_EMULATOR_PLATFORM_VERSION JEST_TIMEOUT=600000 DEVICE_TIMEOUT=300000 DRIVER_INITIALIZATION_TIMEOUT=30000 npm run appium:test after_script: - npm run poste2e # NOTE-RT: Make sure we kill things and clean up - android delete avd -n $(node -p 'require("./package.json").name') # NOTE-RT: This should really be `npm run emulator:android:delete` From 2341c2b9e50d99910256316244c78aa011d69206 Mon Sep 17 00:00:00 2001 From: Randy Tarampi Date: Mon, 22 Apr 2019 14:47:48 -0700 Subject: [PATCH 13/17] Explicitly allow the `E2E` `Android (local)` job to fail. --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 83b550a..5292ddc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,7 +13,8 @@ env: - ANDROID_EMULATOR_ABI=armeabi-v7a jobs: allow_failures: - - script: ANDROID_PLATFORM_VERSION=$ANDROID_EMULATOR_PLATFORM_VERSION JEST_TIMEOUT=600000 DEVICE_TIMEOUT=300000 npm run appium:test + - stage: E2E + name: Android (local) include: - language: node_js node_js: 10 From d19f7fa68444672822aa9c13a37f22a045814779 Mon Sep 17 00:00:00 2001 From: Randy Tarampi Date: Mon, 22 Apr 2019 17:41:54 -0700 Subject: [PATCH 14/17] Don't provide any default `IOS_DEVICE_NAME` or `IOS_PLATFORM_VERSION`. --- e2e/constants.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/e2e/constants.js b/e2e/constants.js index bda5306..fa86b09 100644 --- a/e2e/constants.js +++ b/e2e/constants.js @@ -11,8 +11,8 @@ export const DEVICE_TIMEOUT = process.env.DEVICE_TIMEOUT || 1.5 * 60 * 1000; export const DRIVER_INITIALIZATION_TIMEOUT = process.env.DRIVER_INITIALIZATION_TIMEOUT || 5 * 1000; -export const IOS_DEVICE_NAME = process.env.IOS_DEVICE_NAME || 'iPhone SE'; -export const IOS_PLATFORM_VERSION = process.env.IOS_PLATFORM_VERSION || '12.1'; +export const IOS_DEVICE_NAME = process.env.IOS_DEVICE_NAME; +export const IOS_PLATFORM_VERSION = process.env.IOS_PLATFORM_VERSION; export const IOS_APPLICATION_PATH = process.env.IOS_APPLICATION_PATH; // FIXME-RT: Provide a real directory here export const APPIUM_HOST = process.env.APPIUM_HOST || 'localhost'; From 5ce8b3209ef64ba7c3ec07f26f3d50ec8a811db5 Mon Sep 17 00:00:00 2001 From: Randy Tarampi Date: Mon, 22 Apr 2019 17:53:37 -0700 Subject: [PATCH 15/17] Note where I left off with perfecto.io. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Let's not use these guys. Their examples are over two years old and their documentation is all kinds of contradictory and confusing – just look at all my notes inline. Looks like the below _should_ work, but it doesn't, and their API doesn't return anything useful. I'll link their support to this issue and maybe they can point us in the right direction I'm already not feeling them. ```json { "user": "randy.tarampi@smunch.co", "password": , "platformName": "Android", "automationName": "UIAutomator2", "platformVersion": "9", "app": "PRIVATE:apktest.apk", "automationInfrastructure": "UIAutomator2", "appPackage": "com.apktest", "appActivity": ".MainActivity" } ``` --- README.md | 8 ++++++++ e2e/capabilities.js | 41 +++++++++++++++++++++++++++++++++++++++++ package.json | 2 ++ 3 files changed, 51 insertions(+) diff --git a/README.md b/README.md index ee51c22..92f48b3 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,10 @@ export TESTOBJECT_ACCESS_KEY= export BROWSERSTACK_USERNAME= export BROWSERSTACK_ACCESS_KEY= +# BrowserStack free trial per https://www.perfecto.io/perfecto-free-trial/ +export PERFECTO_USERNAME= +export PERFECTO_PASSWORD= + # Upload and run tests on Sauce Labs (emulated) yarn run upload:e2e:sauce yarn run test:e2e:sauce @@ -66,4 +70,8 @@ yarn run test:e2e:testobject # Run it on BrowserStack yarn run upload:e2e:browserstack yarn run test:e2e:browserstack + +# Run it on Perfecto +yarn run upload:e2e:perfecto +yarn run test:e2e:perfecto ``` \ No newline at end of file diff --git a/e2e/capabilities.js b/e2e/capabilities.js index de3d6c8..5126ddb 100644 --- a/e2e/capabilities.js +++ b/e2e/capabilities.js @@ -61,6 +61,12 @@ if (process.env.TESTOBJECT) { common.capabilities.privateDevicesOnly = process.env.TESTOBJECT_PRIVATE_DEVICES_ONLY ? JSON.parse(process.env.TESTOBJECT_PRIVATE_DEVICES_ONLY) : false; } +if (process.env.PERFECTO) { + // NOTE-RT: These seem to be a constant requirement, but my testing seems to indicate that it's not required for auth. And still the expected `user` value isn't consistent. + common.capabilities.user = common.user; // NOTE-RT: Per https://developers.perfectomobile.com/pages/viewpage.action?pageId=11174626, but their broken JS WDIO example wants a `@perfectomobile.com` appended to the end... https://github.com/PerfectoCode/Samples/tree/master/Appium/JavaScript#getting-started + common.capabilities.password = common.pwd; +} + export const android = { ...common, capabilities: { @@ -133,4 +139,39 @@ if (process.env.TESTOBJECT) { delete ios.capabilities.deviceName; // FIXME-RT: Per the above delete ios.capabilities.platformVersion; // FIXME-RT: Per the above +} + +if (process.env.PERFECTO) { + delete android.capabilities.appiumVersion; + delete android.capabilities.waitforTimeout; + delete android.capabilities.commandTimeout; + delete android.capabilities.newCommandTimeout; + delete android.capabilities.adbExecTimeout; + delete android.capabilities.deviceReadyTimeout; + delete android.capabilities.androidDeviceReadyTimeout; + delete android.capabilities.androidInstallTimeout; + delete android.capabilities.appWaitDuration; + delete android.capabilities.avdLaunchTimeout; + delete android.capabilities.avdReadyTimeout; + delete android.capabilities.uiautomator2ServerInstallTimeout; + delete android.capabilities.uiautomator2ServerLaunchTimeout; + delete android.capabilities.disableWindowAnimation; + delete android.capabilities.deviceName; + delete android.capabilities.isHeadless; + android.capabilities.automationInfrastructure = 'UIAutomator2'; // NOTE-RT: https://developers.perfectomobile.com/display/TT/Select+a+Device+Based+on+Automation+Infrastructure differs from https://developers.perfectomobile.com/display/PD/Supported+Appium+Capabilities... + android.capabilities.automationName = android.capabilities.automationInfrastructure; // NOTE-RT: Per https://developers.perfectomobile.com/display/PD/Supported+Appium+Capabilities + android.capabilities.appPackage = 'com.apktest'; // NOTE-RT: Per https://developers.perfectomobile.com/display/PD/Supported+Appium+Capabilities#SupportedAppiumCapabilities-Android + android.capabilities.appActivity = '.MainActivity'; // NOTE-RT: Per https://developers.perfectomobile.com/display/PD/Supported+Appium+Capabilities#SupportedAppiumCapabilities-Android + // NOTE-RT: I shouldn't need these according to https://developers.perfectomobile.com/pages/viewpage.action?pageId=11174626 I can't get a matching capability + // android.capabilities.manufacturer = "Samsung"; + // android.capabilities.model = "Galaxy S10e"; + // android.capabilities.resolution = "1080x2280"; + // android.capabilities.deviceName = 'R58M21YXQBL'; + + delete ios.capabilities.appiumVersion; + delete ios.capabilities.waitforTimeout; + delete ios.capabilities.commandTimeout; + delete ios.capabilities.newCommandTimeout; + delete ios.capabilities.adbExecTimeout; + // ios.capabilities.deviceName = '17E62A82172A36293D316B522623430FD9D7EA0C'; } \ No newline at end of file diff --git a/package.json b/package.json index 0a07635..70e1467 100644 --- a/package.json +++ b/package.json @@ -13,9 +13,11 @@ "test:e2e:sauce": "SAUCE=true ANDROID_DEVICE_NAME='Google Pixel GoogleAPI Emulator' ANDROID_PLATFORM_VERSION=8.1 APPIUM_HOST=ondemand.eu-central-1.saucelabs.com APPIUM_PORT=80 APPIUM_USER=$SAUCE_USERNAME APPIUM_PASSWORD=$SAUCE_ACCESS_KEY ANDROID_APPLICATION_PATH=\"sauce-storage:$(node -p 'require(\"./package.json\").name').$APP_EXTENSION\" IOS_APPLICATION_PATH=\"sauce-storage:$(node -p 'require(\"./package.json\").name').$APP_EXTENSION\" jest --config e2e/jest.config.json --runInBand", "test:e2e:testobject": "TESTOBJECT=true ANDROID_DEVICE_NAME='LG Nexus 5X Free' ANDROID_PLATFORM_VERSION=8.1 APPIUM_HOST=eu1.appium.testobject.com APPIUM_PORT=80 APPIUM_USER=$SAUCE_USERNAME APPIUM_PASSWORD=$TESTOBJECT_ACCESS_KEY ANDROID_APPLICATION_PATH=\"sauce-storage:$(node -p 'require(\"./package.json\").name').$APP_EXTENSION\" IOS_APPLICATION_PATH=\"sauce-storage:$(node -p 'require(\"./package.json\").name').$APP_EXTENSION\" jest --config e2e/jest.config.json --runInBand", "test:e2e:browserstack": "BROWSERSTACK=true ANDROID_DEVICE_NAME='Google Pixel' ANDROID_PLATFORM_VERSION=7.1 IOS_DEVICE_NAME='iPhone 8 Plus' IOS_PLATFORM_VERSION=11 APPIUM_HOST=hub-cloud.browserstack.com APPIUM_PORT=80 APPIUM_USER=$BROWSERSTACK_USERNAME APPIUM_PASSWORD=$BROWSERSTACK_ACCESS_KEY ANDROID_APPLICATION_PATH=$BROWSERSTACK_USERNAME/$(node -p 'require(\"./package.json\").name').$APP_EXTENSION IOS_APPLICATION_PATH=$BROWSERSTACK_USERNAME/$(node -p 'require(\"./package.json\").name').$APP_EXTENSION jest --config e2e/jest.config.json --runInBand", + "test:e2e:perfecto": "PERFECTO=true ANDROID_PLATFORM_VERSION=9 IOS_PLATFORM_VERSION=12.1.4 APPIUM_HOST=mobilecloud.perfectomobile.com APPIUM_PORT=80 APPIUM_USER=$PERFECTO_USERNAME APPIUM_PASSWORD=$PERFECTO_PASSWORD ANDROID_APPLICATION_PATH=PRIVATE:$(node -p 'require(\"./package.json\").name').$APP_EXTENSION IOS_APPLICATION_PATH=PRIVATE:$(node -p 'require(\"./package.json\").name').$APP_EXTENSION jest --config e2e/jest.config.json --runInBand", "upload:e2e:sauce": "curl -v -u \"$SAUCE_USERNAME:$SAUCE_ACCESS_KEY\" -X POST https://eu-central-1.saucelabs.com/rest/v1/storage/$SAUCE_USERNAME/$(node -p 'require(\"./package.json\").name').$APP_EXTENSION?overwrite=true -H \"Content-Type: application/octet-stream\" --data-binary @$APP_PATH", "upload:e2e:testobject": "curl -v -u \"$SAUCE_USERNAME:$TESTOBJECT_ACCESS_KEY\" -X POST https://app.testobject.com:443/api/storage/upload -H \"Content-Type: application/octet-stream\" --data-binary @$APP_PATH", "upload:e2e:browserstack": "curl -v -u \"$BROWSERSTACK_USERNAME:$BROWSERSTACK_ACCESS_KEY\" -X POST https://api-cloud.browserstack.com/app-automate/upload -F \"file=@$APP_PATH\" -F \"data={\\\"custom_id\\\": \\\"$(node -p 'require(\"./package.json\").name').$APP_EXTENSION\\\"}\"", + "upload:e2e:perfecto": "curl -v -X POST \"https://mobilecloud.perfectomobile.com/services/repositories/media/PRIVATE:$(node -p 'require(\"./package.json\").name').$APP_EXTENSION?operation=upload&overwrite=true&user=$PERFECTO_USERNAME&password=$PERFECTO_PASSWORD&responseFormat=json\" -H \"Content-Type: application/octet-stream\" --data-binary @$APP_PATH --http1.1", "appium": "npm run appium:start", "appium:test": "npm run test:e2e", "clean:nodemodules": "rm -rf node_modules && rm -f package-lock.json && npm install", From da188e260c1d937cb19e76e9e02e11571d51d05d Mon Sep 17 00:00:00 2001 From: Randy Tarampi Date: Tue, 7 May 2019 23:35:38 -0400 Subject: [PATCH 16/17] chore: Set BrowserStack `appium_version` properly. I don't know why they want this specifically, but let's give it to them. --- e2e/capabilities.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/e2e/capabilities.js b/e2e/capabilities.js index 5126ddb..301bebf 100644 --- a/e2e/capabilities.js +++ b/e2e/capabilities.js @@ -1,3 +1,5 @@ +import {version as appiumVersion} from 'appium/package.json' +import {name} from '../package.json' import { ANDROID_APPLICATION_PATH, ANDROID_AVD_ARGS, @@ -14,9 +16,7 @@ import { IOS_DEVICE_NAME, IOS_PLATFORM_VERSION, TARGET_PLATFORM -} from './constants'; -import {name} from '../package.json'; -import {version as appiumVersion} from 'appium/package.json'; +} from './constants' const common = { hostname: APPIUM_HOST, @@ -41,6 +41,8 @@ if (process.env.BROWSERSTACK) { common.capabilities['browserstack.debug'] = true; common.capabilities['browserstack.video'] = true; common.capabilities['browserstack.networkLogs'] = true; + common.capabilities.appiumVersion = '1.9.1' // FIXME-RT: Ugh. 🤞 this doesn't break 'cause we're actually on 1.12.1 + common.capabilities['browserstack.appium_version'] = common.capabilities.appiumVersion common.capabilities.build = `${name}:${TARGET_PLATFORM}`; } @@ -76,11 +78,11 @@ export const android = { deviceName: ANDROID_DEVICE_NAME, platformVersion: ANDROID_PLATFORM_VERSION, app: ANDROID_APPLICATION_PATH, - adbExecTimeout: DEVICE_TIMEOUT, + adbExecTimeout: 30, deviceReadyTimeout: DEVICE_TIMEOUT / 1000, // NOTE-RT: This is actually just in seconds androidDeviceReadyTimeout: DEVICE_TIMEOUT / 1000, // NOTE-RT: This is actually just in seconds androidInstallTimeout: DEVICE_TIMEOUT, - appWaitDuration: DEVICE_TIMEOUT, + appWaitDuration: 10, avdLaunchTimeout: DEVICE_TIMEOUT, avdReadyTimeout: DEVICE_TIMEOUT, uiautomator2ServerInstallTimeout: DEVICE_TIMEOUT, From 4521a3aa5a50cb8bd60cb3f3422b7adfa1e7ec9c Mon Sep 17 00:00:00 2001 From: Randy Tarampi Date: Tue, 7 May 2019 23:36:22 -0400 Subject: [PATCH 17/17] chore: Reinstate WDIO tests. Since it looks like this is our new pick for a webdriver. --- .../{appium-test-wdio.js.old => appium-test-wdio.js} | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) rename e2e/__tests__/{appium-test-wdio.js.old => appium-test-wdio.js} (94%) diff --git a/e2e/__tests__/appium-test-wdio.js.old b/e2e/__tests__/appium-test-wdio.js similarity index 94% rename from e2e/__tests__/appium-test-wdio.js.old rename to e2e/__tests__/appium-test-wdio.js index 18cc361..abf1e9f 100644 --- a/e2e/__tests__/appium-test-wdio.js.old +++ b/e2e/__tests__/appium-test-wdio.js @@ -1,7 +1,7 @@ -import * as wdio from 'webdriverio'; -import * as capabilities from '../capabilities'; -import {JEST_TIMEOUT, TARGET_PLATFORM} from '../constants'; -import path from 'path'; +import path from 'path' +import * as wdio from 'webdriverio' +import * as capabilities from '../capabilities' +import {JEST_TIMEOUT, TARGET_PLATFORM} from '../constants' xdescribe('Create Android session (wdio)', () => { let client;