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