Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add RNTester-E2E: tests for iOS and Android via Appium, WDIO and Jest #36267

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 134 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ version: 2.1

orbs:
win: circleci/windows@2.4.0
android: circleci/android@2.3.0

# -------------------------
# REFERENCES
Expand Down Expand Up @@ -724,6 +725,136 @@ jobs:
path: ./reports/junit

# -------------------------
# JOBS: iOS E2E Tests
# -------------------------
test_e2e_ios:
executor: reactnativeios
parameters:
ruby_version:
default: "2.7.7"
description: The version of ruby that must be used
type: string
steps:
- checkout_code_with_cache
- run_yarn
- attach_workspace:
at: .
- run:
name: Install appium
command: npm install appium@2.0.0 -g
- run:
name: Install appium drivers
command: |
appium driver install uiautomator2
appium driver install xcuitest
- run:
name: Start Appium server
command: appium --base-path /wd/hub
background: true
- run:
name: Start Metro
command: |
cd packages/rn-tester
yarn start
background: true
- brew_install:
package: cmake
- setup_ruby:
ruby_version: << parameters.ruby_version >>
- run:
name: Install Bundler
command: |
cd packages/rn-tester
bundle check || bundle install
bundle exec pod setup
bundle exec pod install --verbose
- run:
name: Boot iOS Simulator
command: source scripts/.tests.env && xcrun simctl boot "$IOS_DEVICE" || true
- run:
name: Build app
command: |
xcodebuild build \
-workspace packages/rn-tester/RNTesterPods.xcworkspace \
-configuration Debug \
-scheme RNTester \
-sdk iphonesimulator \
-derivedDataPath /tmp/e2e/
- run:
name: Move app to correct directory
command: mv /tmp/e2e/Build/Products/Debug-iphonesimulator/RNTester.app packages/rn-tester-e2e/apps/rn-tester.app
- run:
name: Check Appium server status
command: |
if ! nc -z 127.0.0.1 4723; then
echo Could not find Appium server!
exit 1
fi
- run:
name: Run E2E tests
command: |
cd packages/rn-tester-e2e
yarn test-ios-e2e
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if the test-e2e produces some artifacts, we can think about saving them in CircleCI using the store_artifacts instruction


# -------------------------
# JOBS: Android E2E Tests
# -------------------------
test_e2e_android:
executor:
name: android/android-machine
tag: 2023.07.1
steps:
- checkout_code_with_cache
- run_yarn
- android/create-avd:
avd-name: e2e_emulator
system-image: system-images;android-33;google_apis;x86_64
kelset marked this conversation as resolved.
Show resolved Hide resolved
install: true
- android/start-emulator:
avd-name: e2e_emulator
no-window: true
restore-gradle-cache-prefix: v1a
post-emulator-launch-assemble-command: ""
- run:
name: Install appium
command: npm install appium@2.0.0 -g
- run:
name: Install appium drivers
command: |
appium driver install uiautomator2
appium driver install xcuitest
- run:
name: Start Appium server
command: appium --base-path /wd/hub
background: true
- run:
name: Start Metro
command: |
cd packages/rn-tester
yarn start
background: true
- attach_workspace:
at: .
- run:
name: Build app
command: |
./gradlew :packages:rn-tester:android:app:assembleHermesDebug -PreactNativeArchitectures=x86_64
- run:
name: Move app to correct directory
command: mv packages/rn-tester/android/app/build/outputs/apk/hermes/debug/app-hermes-x86_64-debug.apk packages/rn-tester-e2e/apps/rn-tester.apk
kelset marked this conversation as resolved.
Show resolved Hide resolved
- run:
name: Check Appium server status
command: |
if ! nc -z 127.0.0.1 4723; then
echo Could not find Appium server
exit 1
fi
- run:
name: Run E2E tests
command: |
cd packages/rn-tester-e2e
yarn test-android-e2e
# -------------------------
# JOBS: Test Android
# -------------------------
test_android:
Expand Down Expand Up @@ -1653,6 +1784,9 @@ workflows:
run_disabled_tests: false
- test_android
- test_android_docker_image
- test_e2e_ios:
ruby_version: "2.7.7"
- test_e2e_android
- test_android_template:
requires:
- build_npm_package
Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -147,3 +147,7 @@ package-lock.json

# Temporary files created by Metro to check the health of the file watcher
.metro-health-check*

# E2E files
/packages/rn-tester-e2e/apps/*.apk
/packages/rn-tester-e2e/apps/*.app
124 changes: 124 additions & 0 deletions packages/rn-tester-e2e/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
# RNTester E2E folder

In this folder we have a the setup for running E2E testing in RNTester via the usage of [Appium](https://appium.io/) and [WebDriverIO](https://webdriver.io/) and [Jest](https://jestjs.io/).

- [Setting up locally](#setting-up-locally)
- [(one-off) Setting up Appium](#one-off-setting-up-appium)
- [Building RNTester app](#building-rntester-app)
- [Building for iOS](#building-for-ios)
- [Building for Android](#building-for-android)
- [Setting up the RNTester E2E folder](#setting-up-the-rntester-e2e-folder)
- [Testing the RNTester app E2E](#testing-the-rntester-app-e2e)
- [Adding new tests (and project structure)](#adding-new-tests-and-project-structure)

## Setting up locally

### (one-off) Setting up Appium

The first step you need to do is to ensure to install the tooling:

```bash
npm install appium@2.0.0 -g
appium driver install uiautomator2
appium driver install xcuitest
```

> More details about drivers in Appium [here](https://appium.github.io/appium/docs/en/2.0/guides/managing-exts/) and [here](https://appium.github.io/appium/docs/en/2.0/quickstart/uiauto2-driver/)

You should not need to run install commands for drivers separately more than once, even if you bump the dep in package.json.

### Building RNTester app

Building manually *.app* and *.apk* is required to run automation tests on local environment.

0. *(optional)* If you previously built RNTester, you may need to clean up build files and Pods:

```bash
yarn test-e2e-local-clean && yarn install
```

1. Step 1: install packages for the repository, then navigate in the rn-tester folder

```bash
cd react-native
yarn install
cd packages/rn-tester
```

Now, depending on the platform, there are some specific steps
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we have a js script that we can invoke with like:

yarn run-e2e-test <platform> # <platform> can be "ios" or "android"

That automate the steps required to build and run the tests?
This will make it easier also to ask people to contribute as they can just write their tests and run the script.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we have this being a feature to implement as a separate follow up PR? It doesn't sound like a deal breaker and I'd rather get this in and then have smaller PRs after to build on top of it

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can handle creating additional PR adding this script, after merging this PR 👍


#### Building for iOS

0. Make sure you have Bundler `gem install bundler` - we use it ensure installing the right version of CocoaPods locally.
1. Install Bundler and CocoaPods dependencies: `bundle install` then `bundle exec pod install` or `yarn setup-ios-hermes` for RNTester with Hermes. In order to use JSC instead of Hermes engine, run: `USE_HERMES=0 bundle exec pod install` or `setup-ios-jsc` instead.
2. You can build app with React Native CLI or manually with Xcode:
1. To build with React Native CLI:
1. Run `npx react-native build-ios --mode Debug --scheme RNTester --buildFolder /path/to/build-folder`, replace `/path/to/build-folder` with the real path.
2. Copy the built app using `mv` - `mv /path/to/build-folder/Build/Products/Debug-iphonesimulator/RNTester.app ~/react-native/packages/rn-tester-e2e/apps` or manually.
2. To build with Xcode, open the generated `RNTester.xcworkspace` and build.
1. Find the **RNTester.app** in `~/Library/Developer/Xcode/DerivedData/RNTesterPods-{id}/Build/Products/Debug-iphonesimulator`
2. Copy the app to the following directory `~/react-native/packages/rn-tester-e2e/apps`.
3. Change its name to: `rn-tester.app`

#### Building for Android

0. You'll need to have all the [prerequisites](https://reactnative.dev/contributing/how-to-build-from-source#prerequisites) (SDK, NDK) for Building React Native installed.
1. Start an Android emulator.
2. Build the app via

```bash
# In order to not use Hermes engine, run `yarn install-android-jsc` instead.
yarn install-android-hermes
yarn start
```

*Note: Building for the first time can take a while.*

3. Find the **app-*-debug.apk** in `~/react-native/packages/rn-tester/android/app/build/outputs/apk/hermes/debug`
4. Copy the app `app-*-debug.apk` to the following directory `~/react-native/packages/rn-tester-e2e/apps`
5. Change its name to: `rn-tester.apk`

### Setting up the RNTester E2E folder

In `react-native/packages/rn-tester-e2e` open the following file

```bash
/react-native/packages/rn-tester-e2e/e2e-config.js
```

And modify lines L24->L39 to reflect your local setup configuration (ex. `platformVersion`, `deviceName`). Make sure to **not** commit this change if you send a PR to add tests.

## Testing the RNTester app E2E

After you have done all the above correctly, and you have the Android/iOS apps in the `rn-tester-e2e/apps` folder, in a dedicated terminal window, run:

```bash
appium --base-path /wd/hub
```

This will start the Appium server - you will need this to keep running.

Then open a second terminal window and start the Metro terminal from the `packages/rn-tester` folder, via `yarn start --reset-cache`. This terminal window also needs to keep running.

Now, make sure that the iOS simulator/the Android emulator is up and running.

Finally, you can open a third terminal window and run:

```bash
yarn test-android-e2e # for android
yarn test-ios-e2e # for ios
```

Now you should see the RNTester app being open, and the defined test being run.

## Adding new tests (and project structure)

This project has 2 main folders:

- `apps`, where, as you have seen above, the iOS/Android RNTester apps need to be put so that appium will pick them and install in the emulator/simulator consistently.

- `tests`, where the tests and referencing files all live. The substructure is as follows:
- `screens` -> in this folder, you will find `*.screen.js` files, where each file represents a navigation screen for RNTester. So there are 3 root ones (`apis`, `bookmarks`, `components`) and then for subscreens, there's a folder with the same name - currently, that's only `components` that contains `buttonComponent.screen.js`. The content of these files is what was earlier mentioned as "references": they provide an easy way to define all elements present in said screen, so that they can be used for tests.
- `specs` -> this folder follows a similar 1:1 mapping to the RNTester screens, but for the tests: for each screen (or subscreen) there's a dedicated `*.test.js` file (such as `buttonComponentScreen.test.js`). Ideally, in this file the Jest tests are standard, leveraging the `*.screen.js` counterpart for the details of defining how Appium/WDIO can reach those elements on screen.

When adding a new test, please ensure that you follow this pattern and add the relevant test in the right screen file / screen test file. Use the files mentioned above as examples.
1 change: 1 addition & 0 deletions packages/rn-tester-e2e/apps/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Put the *.app and *.apk files here.
14 changes: 14 additions & 0 deletions packages/rn-tester-e2e/babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
*/

module.exports = {
presets: [['@babel/preset-env', {targets: {node: 'current'}}]],
plugins: ['@babel/plugin-transform-flow-strip-types'],
};
61 changes: 61 additions & 0 deletions packages/rn-tester-e2e/e2e-config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
*/

const path = require('path');

type Capabilities = {
platformName: 'Android' | 'iOS',
'appium:platformVersion': string,
'appium:deviceName': string,
'appium:app': string,
'appium:automationName': 'UiAutomator2' | 'XCUITest',
'appium:newCommandTimeout'?: number,
};

let capabilities: Capabilities;

const android = {
platformName: 'Android',
'appium:platformVersion': '13.0',
cortinico marked this conversation as resolved.
Show resolved Hide resolved
'appium:deviceName': 'Android Emulator',
'appium:app': path.join(process.cwd(), '/apps/rn-tester.apk'),
'appium:automationName': 'UiAutomator2',
'appium:newCommandTimeout': 240,
};

const ios = {
platformName: 'iOS',
'appium:platformVersion': '16.4',
'appium:deviceName': 'iPhone 14',
'appium:automationName': 'XCUITest',
'appium:app': path.join(process.cwd(), '/apps/rn-tester.app'),
};

// check that E2E_DEVICE exists, is a string and its either "ios" or "android"
if (!process.env.E2E_DEVICE) {
throw new Error('E2E_DEVICE environment variable is not defined');
} else if (typeof process.env.E2E_DEVICE !== 'string') {
throw new Error('E2E_DEVICE environment variable is not a string');
} else if (
process.env.E2E_DEVICE !== 'ios' &&
process.env.E2E_DEVICE !== 'android'
) {
throw new Error('E2E_DEVICE environment variable is not "ios" or "android"');
}

if (process.env.E2E_DEVICE === 'android') {
capabilities = android;
}

if (process.env.E2E_DEVICE === 'ios') {
capabilities = ios;
}

export default capabilities;
17 changes: 17 additions & 0 deletions packages/rn-tester-e2e/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
*/

module.exports = {
kelset marked this conversation as resolved.
Show resolved Hide resolved
testTimeout: 120000,
bail: 0,
setupFilesAfterEnv: ['./jest.setup.js'],
kelset marked this conversation as resolved.
Show resolved Hide resolved
testMatch: ['**/specs/**/*.js'],
maxWorkers: 1,
};
Loading