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

feat: react-native android support #777

Merged
merged 6 commits into from
May 21, 2021
Merged
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
3 changes: 2 additions & 1 deletion md/github-actions.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ jobs:
- uses: actions/checkout@v2
- uses: microsoft/playwright-github-action@v1
- run: npm install
- run: npx aegir test -t browser -t webworker --bail --cov
- run: npx aegir test -t browser --bail --cov
- run: npx aegir test -t webworker --bail
- uses: codecov/codecov-action@v1
test-firefox:
needs: check
Expand Down
94 changes: 94 additions & 0 deletions md/react-native.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# React Native Setup

## Boring and heavy way for all OSes

Go to https://reactnative.dev/docs/environment-setup click in "React Native CLI Quickstart" and follow instructions.

If you don't want to fill your system with the full android studio or XCode follow the instructions below but be aware that there are dragons ahead!

## Android

```bash
brew install --cask adoptopenjdk/openjdk/adoptopenjdk8
export ANDROID_SDK_ROOT="~/android-sdk"
touch ~/.android/repositories.cfg
```

Download `cmdline-tool` from [https://developer.android.com/studio#cmdline-tools](https://developer.android.com/studio#cmdline-tools) and extract to `~/android-sdk/cmdline-tools/latest`

```bash

~/android-sdk/cmdline-tools/latest/bin/sdkmanager --update
~/android-sdk/cmdline-tools/latest/bin/sdkmanager "platform-tools" "platforms;android-29" "build-tools;29.0.2" "system-images;android-29;default;x86_64"

// in your .zshrc or similar add sdk to PATH
PATH=$PATH:$ANDROID_SDK_ROOT/emulator
PATH=$PATH:$ANDROID_SDK_ROOT/tools
PATH=$PATH:$ANDROID_SDK_ROOT/tools/bin
PATH=$PATH:$ANDROID_SDK_ROOT/platform-tools
export PATH
```
Now all the tools are in the `$PATH` , no need for absolute paths anymore.

### Some examples

You normally dont need to run any of these

```bash
# install new platforms, build tools and system images
sdkmanager --update
sdkmanager "platforms;android-29" "build-tools;29.0.2" "system-images;android-29;default;x86_64"

# create an avd
avdmanager create avd -n aegir-android -d pixel --package "system-images;android-29;default;x86_64"

# delete avd
avdmanager delete avd -n aegir-android

# manually run the emulator
emulator @aegir-android

# list avds
emulator -list-avds

# redirect port trafic
adb -s emulator-5554 reverse tcp:3000 tcp:3000
adb reverse --list
adb reverse --remove-all
```
> If the internal aegir AVD changes SDK versions you might need to run the `sdkmanager` above to update and install the new SDK versions in your system.


### emulator acceleration (optional)

[https://developer.android.com/studio/run/emulator-acceleration#vm-mac](https://developer.android.com/studio/run/emulator-acceleration#vm-mac)

### Aegir config
Android needs special attention for networks settings ([docs](https://developer.android.com/studio/run/emulator-networking)). The most common change is to use the special address `10.0.2.2` to redirect trafic to your local loopback interface `127.0.0.1`.

```js
module.exports = {
test: {
async before (options) {
let echoServer = new EchoServer()
await echoServer.start()
const { address, port } = echoServer.server.address()
let hostname = address // address will normally be 127.0.0.1

if(options.runner === 'react-native-android') {
hostname = '10.0.2.2'
}

return {
echoServer,
env: { ECHO_SERVER : format({ protocol: 'http:', hostname, port })}
}
},
async after (options, before) {
await before.echoServer.stop()
}
}
}


```
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@
"polka": "^0.5.2",
"premove": "^3.0.1",
"proper-lockfile": "^4.1.2",
"react-native-test-runner": "^5.0.0",
"read-pkg-up": "^7.0.1",
"semver": "^7.3.5",
"simple-git": "^2.37.0",
Expand Down
2 changes: 1 addition & 1 deletion src/cmds/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ module.exports = {
alias: 't',
describe: 'In which target environment to execute the tests',
type: 'array',
choices: ['node', 'browser', 'webworker', 'electron-main', 'electron-renderer'],
choices: ['node', 'browser', 'webworker', 'electron-main', 'electron-renderer', 'react-native-android', 'react-native-ios'],
default: userConfig.test.target
},
watch: {
Expand Down
14 changes: 13 additions & 1 deletion src/test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const pmap = require('p-map')
const node = require('./node')
const browser = require('./browser')
const electron = require('./electron')
const rn = require('./react-native')

/**
* @typedef {import("execa").Options} ExecaOptions
Expand Down Expand Up @@ -71,8 +72,19 @@ const TASKS = [
* @param {TestOptions & GlobalOptions} ctx
*/
enabled: (ctx) => ctx.target.includes('electron-renderer')
},
{
title: 'Test React Native Android',
/**
* @param {TestOptions & GlobalOptions} opts
* @param {ExecaOptions} execaOptions
*/
task: (opts, execaOptions) => rn({ ...opts, runner: 'react-native-android' }, execaOptions),
/**
* @param {TestOptions & GlobalOptions} ctx
*/
enabled: (ctx) => ctx.target.includes('react-native-android')
}

]

module.exports = {
Expand Down
101 changes: 101 additions & 0 deletions src/test/react-native.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
'use strict'
const path = require('path')
const execa = require('execa')
const merge = require('merge-options')

/**
* @typedef {import("execa").Options} ExecaOptions
* @typedef {import('../types').TestOptions} TestOptions
* @typedef {import('../types').GlobalOptions} GlobalOptions
*/

/**
*
* @param {TestOptions & GlobalOptions} argv
* @param {ExecaOptions} execaOptions
*/
module.exports = async (argv, execaOptions) => {
const AVDName = 'aegir-android-29'
const extra = argv['--'] ? argv['--'] : []
const emulator = process.env.CI ? [] : ['--emulator', AVDName]
const forwardOptions = /** @type {string[]} */([
...extra,
argv.timeout && `--timeout=${argv.timeout}`,
argv.grep && `--grep=${argv.grep}`,
argv.bail && '--bail'
].filter(Boolean))
const files = argv.files.length > 0
? argv.files
: [
'**/*.spec.{js,ts}',
'test/browser.{js,ts}'
]

// before hook
const before = await argv.fileConfig.test.before(argv)
const beforeEnv = before && before.env ? before.env : {}

await checkAndroidEnv()

if (!await checkAvd(AVDName)) {
await execa('avdmanager', [
'create',
'avd',
'-n', AVDName,
'-d', 'pixel',
'--package', 'system-images;android-29;default;x86_64'
])
}

// run pw-test
await execa('rn-test',
[
...files,
'--platform', argv.runner === 'react-native-android' ? 'android' : 'ios',
...emulator,
'--reset-cache',
...forwardOptions
],
merge(
{
env: {
AEGIR_RUNNER: argv.runner,
NODE_ENV: process.env.NODE_ENV || 'test',
...beforeEnv
},
preferLocal: true,
localDir: path.join(__dirname, '../..'),
stdio: 'inherit'
},
execaOptions
)
)

// after hook
await argv.fileConfig.test.after(argv, before)
}

/**
* Check for avd
*
* @param {string} name
*/
async function checkAvd (name) {
const avd = await execa('emulator', ['-list-avds'])

return avd.stdout.split('\n').includes(name)
}

async function checkAndroidEnv () {
if (!process.env.ANDROID_SDK_ROOT) {
throw new Error('ANDROID_SDK_ROOT is not set')
}

try {
await execa('emulator', ['-help'])
// await execa('sdkmanager')
await execa('avdmanager', ['list'])
} catch (err) {
throw new Error(`"Command ${err.path}" is not available, you need to properly setup your android environment.`)
}
}
4 changes: 2 additions & 2 deletions src/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ interface TestOptions {
/**
* In which target environment to execute the tests
*/
target: Array<'node' | 'browser' | 'webworker' | 'electron-main' | 'electron-renderer'>
target: Array<'node' | 'browser' | 'webworker' | 'electron-main' | 'electron-renderer' | 'react-native-android' | 'react-native-ios'>
/**
* Watch files for changes and rerun tests
*/
Expand Down Expand Up @@ -203,7 +203,7 @@ interface TestOptions {
/**
* Runner enviroment
*/
runner: 'node' | 'browser' | 'webworker' | 'electron-main' | 'electron-renderer'
runner: 'node' | 'browser' | 'webworker' | 'electron-main' | 'electron-renderer' | 'react-native-android' | 'react-native-ios'
/**
* Browser options
*/
Expand Down
2 changes: 1 addition & 1 deletion test/utils/echo-server.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ describe('echo server spec', () => {

before(async () => {
await echo.start()
const { port, address } = /** @type {import('node:net').AddressInfo} */(echo.server.address())
const { port, address } = /** @type {import('net').AddressInfo} */(echo.server.address())
url = format({ protocol: 'http:', hostname: address, port })
})

Expand Down