1/** 2 * Copyright (c) Meta Platforms, Inc. and affiliates. 3 * 4 * This source code is licensed under the MIT license found in the 5 * LICENSE file in the root directory of this source tree. 6 * 7 * @format 8 */ 9 10// Used in run-ci-e2e-test.js and executed in Circle CI. 11// 12// E2e test that verifies that init app can be installed, compiled, started and 13// Hot Module reloading and Chrome debugging work. 14// 15// For other examples of appium refer to: 16// https://github.com/appium/sample-code/tree/master/sample-code/examples/node and 17// https://www.npmjs.com/package/wd-android 18// 19// To set up: 20// - npm install --save-dev [email protected] [email protected] [email protected] [email protected] [email protected] 21// - cp <this file> <to app installation path> 22// - keytool -genkey -v -keystore android/app/debug.keystore -storepass android -alias androiddebugkey -keypass android -keyalg RSA -keysize 2048 -validity 10000 -dname "CN=Android Debug,O=Android,C=US" 23// 24// To run this test: 25// - npm start 26// - node node_modules/.bin/appium 27// - (cd android && ./gradlew :app:copyDownloadableDepsToLibs) 28// - react-native run-android 29// - node ../node_modules/.bin/_mocha ../android-e2e-test.js 30 31/* eslint-env mocha */ 32 33'use strict'; 34 35const wd = require('wd'); 36const path = require('path'); 37const fs = require('fs'); 38const pd = require('pretty-data2').pd; 39 40// value in ms to print out screen contents, set this value in CI to debug if tests are failing 41const appiumDebugInterval = process.env.APPIUM_DEBUG_INTERVAL; 42 43describe('Android Test App', function () { 44 this.timeout(600000); 45 let driver; 46 let debugIntervalId; 47 48 before(function () { 49 driver = wd.promiseChainRemote({ 50 host: 'localhost', 51 port: 4723, 52 }); 53 driver.on('status', function (info) { 54 console.log(info.cyan); 55 }); 56 driver.on('command', function (method, command, data) { 57 if (command === 'source()' && data) { 58 console.log( 59 ' > ' + method.yellow, 60 'Screen contents'.grey, 61 '\n', 62 pd.xml(data).yellow, 63 ); 64 } else { 65 console.log(' > ' + method.yellow, command.grey, data || ''); 66 } 67 }); 68 driver.on('http', function (method, urlPath, data) { 69 console.log(' > ' + method.magenta, urlPath, (data || '').grey); 70 }); 71 72 // every interval print what is on the screen 73 if (appiumDebugInterval) { 74 debugIntervalId = setInterval(() => { 75 // it driver.on('command') will log the screen contents 76 driver.source(); 77 }, appiumDebugInterval); 78 } 79 80 const desired = { 81 platformName: 'Android', 82 deviceName: 'Android Emulator', 83 app: path.resolve('android/app/build/outputs/apk/debug/app-debug.apk'), 84 }; 85 86 // React Native in dev mode often starts with Red Box "Can't fibd variable __fbBatchedBridge..." 87 // This is fixed by clicking Reload JS which will trigger a request to Metro 88 return driver 89 .init(desired) 90 .setImplicitWaitTimeout(5000) 91 .waitForElementByXPath('//android.widget.Button[@text="Reload JS"]') 92 .then( 93 elem => { 94 elem.click(); 95 driver.sleep(2000); 96 }, 97 // eslint-disable-next-line handle-callback-err 98 err => { 99 // ignoring if Reload JS button can't be located 100 }, 101 ); 102 }); 103 104 after(function () { 105 if (debugIntervalId) { 106 clearInterval(debugIntervalId); 107 } 108 return driver.quit(); 109 }); 110 111 it('should display new content after a refresh', function () { 112 const androidAppCode = fs.readFileSync('App.js', 'utf-8'); 113 let intervalToUpdate; 114 return ( 115 driver 116 .waitForElementByXPath( 117 '//android.widget.TextView[starts-with(@text, "Welcome to React")]', 118 ) 119 .then(() => { 120 fs.writeFileSync( 121 'App.js', 122 androidAppCode.replace('Step One', 'Step 1'), 123 'utf-8', 124 ); 125 }) 126 .sleep(1000) 127 // http://developer.android.com/reference/android/view/KeyEvent.html#KEYCODE_MENU 128 .pressDeviceKey(46) 129 .pressDeviceKey(46) 130 .sleep(2000) 131 .waitForElementByXPath( 132 '//android.widget.TextView[starts-with(@text, "Step 1")]', 133 ) 134 .finally(() => { 135 clearInterval(intervalToUpdate); 136 fs.writeFileSync('App.js', androidAppCode, 'utf-8'); 137 driver.pressDeviceKey(46).pressDeviceKey(46).sleep(2000); 138 }) 139 ); 140 }); 141 142 it('should have the menu available', function () { 143 return ( 144 driver 145 .waitForElementByXPath( 146 '//android.widget.TextView[starts-with(@text, "Welcome to React")]', 147 ) 148 // http://developer.android.com/reference/android/view/KeyEvent.html#KEYCODE_MENU 149 .pressDeviceKey(82) 150 .waitForElementByXPath( 151 '//android.widget.TextView[starts-with(@text, "Toggle Inspector")]', 152 ) 153 ); 154 }); 155}); 156