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