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