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

React Native Test App as a Package #204

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Changes from 1 commit
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
285 changes: 285 additions & 0 deletions proposals/0000-React-Native-Test-App-as-a-Package.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,285 @@
---
title: React Native Test App as a Package
author:
- Tommy Nguyen
date: February 2020
---

# RFC0000: React Native Test App as a Package

## Summary

Provide a React Native test app for all platforms as a package.

A working prototype of this can be found here:
https://github.com/microsoft/react-native-test-app

## Motivation

Maintaining and keeping React Native up to date with the latest version can be
time consuming. Things can break if you're not careful, even with
[React Native upgrade helper](https://react-native-community.github.io/upgrade-helper/).
Not all developers have much experience in native development on a single
platform, let alone _all_ platforms they want to support. Adding support for
another platform, such as Windows or macOS, can be overwhelming.

Consider the following scenarios:

- As maintainers of a community package in our spare time, we don't have the
capacity to create and maintain test apps.

- Our inner development loop consists of using `npx react-native init`, and
testing things manually.
- Alternatively, we have set up CI for iOS and Android, and would love to add
support for Windows and macOS but lack the resources.

- We have a monorepo with multiple separate experiences that all need test apps.
It is a pain having to upgrade all of the test apps individually with every
release. Our upgrade process can sometimes take weeks to perform.

- As a consumer of React Native, we should try out release candidates to find
issues early and contribute back, but due to one of the scenarios above, there
is simply too much friction.

The proposed package will, at minimum, provide a customizable test app for all
the major platforms that React Native currently runs on, i.e. iOS, Android,
Windows, and macOS. The ultimate goal of this package is to make adding support
for a platform as simple as `npm add --save-dev`, and upgrading to latest React
Native is just a version bump.

We expect the package will:

- Help developers set up a consistent native environment for both local
development and CI agents
- Reduce or eliminate the friction to upgrade React Native, and try out release
candidates
- Reduce or eliminate the overhead of adding support for new platforms

With common test apps, we will also be able to provide additional value for
everyone simultanously. For instance:

- We will handle upgrading of the test apps to support the latest mobile SDK
- We can provide a common infrastructure for native testing
- We can provide optional modules for commonly used services, such as auth or
App Center integration

## Detailed design

Given an existing React Native app/library, we imagine the ideal flow for
**initial integration** with the native test apps should be:

1. Create folder/package for the test app to live in.
- This package should have a dependency on the app/library to be tested, and
on the test app package.
2. Create a manifest to declare entry points. Optionally, it should also be
possible to declare initial props, native source files and resources.
- This is a **one-time cost**, and should be stable across versions.
- If there is a single entry point, the app may boot directly into this app.
- Otherwise, if there are multiple entry points, we will show them in a list.
- It should still be possible to have the app boot into one of these via
deep linking.
- Native dependencies should be taken care of by auto-linking behind the
scenes.
3. Run `npm install` inside this folder as one would usually do.
4. Depending on the desired target platforms, the next steps are:
- Android:
- Make minimal changes to `build.gradle` (one-time cost)
- Open the project in Android Studio
- iOS:
- Make minimal changes to `Podfile` (one-time cost)
- Run `pod install`
- Open the newly generated Xcode workspace
- Windows:
- <@afoxman or @acoates-ms to fill in>
- macOS:
- See steps for iOS

A hypothetical package should end up looking something like below:

```
my-awesome-component
├── example
│ ├── App.js
│ ├── build.gradle (new/modified)
│ ├── package.json
│ ├── Podfile (new/modified)
│ └── yarn.lock
├── my-awesome-component.podspec
├── my-awesome-component-dev.podspec (new, optional)
├── package.json
├── src
└── yarn.lock
```

For a hypothetical monorepo:

```
my-monorepo
├── package.json
├── packages
│ ├── my-awesome-component-android
│ │ ├── build.gradle (new/modified)
│ │ ├── package.json
│ │ └── src
│ ├── my-awesome-component-core
│ │ ├── package.json
│ │ └── ...
│ └── my-awesome-component-ios
│ ├── my-awesome-component.podspec
│ ├── my-awesome-component-dev.podspec (new, optional)
│ ├── package.json
│ ├── Podfile (new/modified)
│ └── src
└── yarn.lock
```

Platform specific project files should no longer be checked in. They are either
tido64 marked this conversation as resolved.
Show resolved Hide resolved
provided by React Native Test App package or are generated at build time.

Once the test app package is configured, **adding an additional platform**
should only require doing one of the platform specific substeps of step 4.

**Upgrading React Native** to the latest version, or any desired version, should
only require bumping the version of this package.

This package should not require additional tools other than the ones required by
React Native itself, and should not fundamentally change how a package is
structured.

### The Manifest

The manifest will be the file where we declare all our entry points, and
resources that should be included.

We haven't decided what this manifest looks like. Our initial thought is to use
React Native CLI's
[configuration format](https://github.com/react-native-community/cli/blob/master/docs/configuration.md)
as a starting point. This is what
[auto-linking](https://github.com/react-native-community/cli/blob/master/docs/autolinking.md#how-does-it-work)
uses today, and we can extend it to support our scenarios.

For iOS (and macOS), we will need to extend the format to include additional
native source files and assets. An Xcode project generated today, will include a
main app target, and two test targets: One for "normal" tests, and one for UI
tests. We will need to be able to generate `.podspec` files that can support
all.

Entry points will also be declared in the manifest. Entry points tell the test
app how to launch your app. For instance, given the declaration below:
tido64 marked this conversation as resolved.
Show resolved Hide resolved

```js
module.exports = {
...
"entryPoints": [
{
"name": "MyFeature",
"entryPoint": "MyFeatureTestScreen",
"initialProps": {
"showStatus": true
}
},
{
"name": "MySecondFeature",
"entryPoint": "MySecondFeatureTestScreen",
"presentationStyle": "popover"
}
]
}
```

The mobile test app could look like this:

```
┌──────────────────────┐
│ │
│RN Test App │
├──────────────────────┤
│ MyFeature >│
│ ─────────────────────┤
│ MySecondFeature >│
├──────────────────────┤
│ │
│ │
│ │
│ │
│╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲│
```

Tapping on one of these cells will launch the corresponding app. If defined,
`initialProps` will be passed to the appropriate method call, and
`presentationStyle` will tell the test app how to present the new view.

One could also forgo the name, in which case the `entryPoint` string will be
used instead. If only one entry point is declared, the test app should launch
directly into that app.

All of this should be transparent to the consumer; they should only have to deal
with the manifest.

## Drawbacks

- Package maintainers no longer have full control of the test app
- Bugs in the test app may take longer to get fixed
- Limitations in existing tools can cause confusions, e.g. files are not
added/removed until `pod install`/`update` is run

## Alternatives

### Electrode

[Electrode Native](https://www.electrode.io/) is a platform by Walmart Labs
that provides an opinionated, batteries-included solution for integrating React
Native components into existing apps. It includes everything you'll ever need
such as React Native initialization code, code generators, code push, facilities
to test individual components etc.

From a cursory glance, it seems that Electrode decides which React Native
version will be embedded, and which bundler to use. Whereas one of the main
goals of React Native Test App is making it simpler to switch between React
Native versions, and it doesn't care whether Metro or Haul is used. Because
Electrode is all-inclusive, it seems to be overkill for single component
packages. You just want a test app for development and run tests on CI.

### Expo

[Expo](https://github.com/facebook/react-native/tree/master/RNTester) is a great
solution to get started, and you don't mind that it's including a lot of things
that your app won't use. Once your app matures to a point where it needs better
control over what's included, or it needs to consume native modules that aren't
bundled in the SDK. Ejecting brings you back to the world of having to maintain
React Native yourself.

Expo also isn't aimed at core contributors/maintainers. Its main goal is to
simplify getting started with your React Native app. If you're looking to
implement a native module of your own, you'll need to eject and stay ejected.
This defeats the purpose of using Expo in the first place.
tido64 marked this conversation as resolved.
Show resolved Hide resolved

### React Native CLI

[This tool](https://github.com/react-native-community/cli) creates a new project
with a specifiable React Native version. It can be further customized with
templates. We can see this test app package being part of the default template,
with a single entry point defined.

### RNTester

[RNTester](https://github.com/facebook/react-native/tree/master/RNTester) is an
app that showcases React Native views and modules. It is currently tightly
coupled to React Native and requires building the framework with the app.

## Adoption strategy

- Get buy-in from the community; we want to make sure that this package aligns
with what package maintainers expect of a test app
- Submit the first few PRs to volunteering react-native-community packages to
show how it can be integrated

## How we teach this

- Point to real PRs showing how it can be integrated
- Aim to become part of the default create-react-native-app template
tido64 marked this conversation as resolved.
Show resolved Hide resolved

## Unresolved questions

- Manifest format
- Versioning scheme