Unverified Commit 0ba9966f authored by Hanwen Cheng's avatar Hanwen Cheng Committed by GitHub
Browse files

test: Add integration(e2e) test (#424)



* init detox e2e test

* Add first e2e test

* add android configurations

* fix detox rn-camera problem

* add CI integration

* update ci settings

* update xcode version

* use legacy build in CI

* prettier xcode output

* update with android

* revert back to origin CI test

* add E2E test in readme

* Update .travis.yml

* update detox guide

* renaming

* update readme

* Update README.md
Co-Authored-By: default avatarThibaut Sardan <33178835+Tbaut@users.noreply.github.com>

* Update .travis.yml

* Update README.md

* fix binary path problem

* remove before each reloading

* rephrase
parent 66428c97
Pipeline #55236 failed with stage
in 12 minutes and 13 seconds
---
language: node_js
node_js:
- 8
node_js: 8
cache:
directories:
......@@ -13,3 +12,85 @@ install:
script:
- yarn run lint
- yarn run test
# TODO complete following part to Integrate E2E test
#matrix:
# include:
# - language: objective-c
# osx_image: xcode10.3
#
# branches:
# only:
# - master
#
# cache:
# directories:
# - node_modules
#
# install:
# - brew tap wix/brew
# - brew install applesimutils
# - curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.2/install.sh | bash
# - export NVM_DIR="$HOME/.nvm" && [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
# - nvm install 8
# - nvm use 8
# - nvm alias default 8
# - npm install -g yarn
# - npm install -g react-native-cli
# - npm install -g detox-cli
# - gem install xcpretty
# - yarn
#
# script:
# - detox build -c ios.sim.release
# - detox test -c ios.sim.release
#
# - language: android
## env:
## - REACT_NATIVE_VERSION=0.60.5
# android:
# components:
# - tools
# - platform-tools
# - build-tools-28.0.3
# - android-28
# - extra-google-google_play_services
# - extra-google-m2repository
# - extra-android-m2repository
## - sys-img-x86-android-26
# - sys-img-x86-android-28
#
# cache:
# directories:
# - node_modules
#
# install:
# - curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.2/install.sh | bash
# - export NVM_DIR="$HOME/.nvm" && [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
# - nvm install 8
# - nvm use 8
# - nvm alias default 8
# - npm install -g yarn
# - npm install -g react-native-cli
# - npm install -g detox-cli
# - yes | sdkmanager "build-tools;28.0.3"
# - yarn
#
# script:
# - detox build -c android.emu.release
# - detox test -c android.emu.release
#
# - language: node_js
# node_js: 8
#
# cache:
# directories:
# - node_modules
#
# install:
# - yarn
#
# script:
# - yarn run lint
# - yarn run test
......@@ -117,6 +117,44 @@ Corresponding data:
}
```
#### Integration Test
Parity Signer is integrated with [Detox](https://github.com/wix/Detox) E2E testing. Detox has very detailed [documentation](https://github.com/wix/Detox/blob/master/docs/README.md).
First make sure `detox-cli` is installed as global dependency with
```
yarn global add detox-cli
```
##### Complete Test
1. run react native server with `yarn start`
2. run `yarn e2e:ios` or `yarn e2e:android`.
##### Develop and Test
Details please refer to Detox official guide [here](https://github.com/wix/Detox/blob/master/docs/Guide.DevelopingWhileWritingTests.md)
Once you have run `yarn ios` you do not need to build it, just run:
```shell
yarn test-e2e:ios
```
This command will open another simulator with the pre-defined configurations.
Re-run tests without re-installing the app
```
yarn test-e2e:ios --reuse
```
If you want to use another specific emulator/simulator than those defined in the configuration, add `--device-name` flag (on Android API version is needed), for example:
```
yarn test-e2e:ios --device-name iPhone X
yarn test-e2e:android --device-name Pixel_2_API_28
```
On Android please replace `ios` with `android`, currently Detox's Android 0.60.x support is in progress, if there is an error, try to build it again with `yarn build-e2e:android`
### Troubleshooting
#### `No dimension set for key window` on Android < 5.0
......
......@@ -98,7 +98,8 @@ def enableSeparateBuildPerCPUArchitecture = false
def enableProguardInReleaseBuilds = false
android {
compileSdkVersion 28
compileSdkVersion rootProject.ext.compileSdkVersion
buildToolsVersion rootProject.ext.buildToolsVersion
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
......@@ -107,7 +108,7 @@ android {
defaultConfig {
applicationId "io.parity.signer"
minSdkVersion 16
minSdkVersion 18
missingDimensionStrategy 'react-native-camera', 'general'
targetSdkVersion 28
versionCode 309
......@@ -119,6 +120,8 @@ android {
pickFirst 'lib/x86_64/libjsc.so'
pickFirst 'lib/arm64-v8a/libjsc.so'
}
testBuildType System.getProperty('testBuildType', 'debug') // This will later be used to control the test apk build type
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
}
signingConfigs {
release {
......@@ -146,6 +149,8 @@ android {
release {
minifyEnabled enableProguardInReleaseBuilds
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
// Detox-specific additions to pro-guard
// proguardFile "${rootProject.projectDir}/../node_modules/detox/android/detox/proguard-rules-app.pro"
}
}
// applicationVariants are e.g. debug, release
......@@ -179,6 +184,16 @@ dependencies {
} else {
implementation 'org.webkit:android-jsc:+'
}
androidTestImplementation('com.wix:detox:+') { transitive = true }
androidTestImplementation 'junit:junit:4.12'
}
configurations.all {
resolutionStrategy {
// the line below is required for Detox since version 14.5.0;
// it should removed as soon as the project compiles without it
force 'androidx.annotation:annotation:1.0.0'
}
}
// Run this once to be able to run the application with BUCK
......
package io.parity.signer;
import com.wix.detox.Detox;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.LargeTest;
import androidx.test.rule.ActivityTestRule;
@RunWith(AndroidJUnit4.class)
@LargeTest
public class DetoxTest {
@Rule
public ActivityTestRule<MainActivity> mActivityRule = new ActivityTestRule<>(MainActivity.class, false, false);
@Test
public void runDetoxTests() {
Detox.runTests(mActivityRule);
}
}
......@@ -3,19 +3,19 @@
buildscript {
ext {
buildToolsVersion = "28.0.3"
minSdkVersion = 16
minSdkVersion = 18
compileSdkVersion = 28
targetSdkVersion = 28
supportLibVersion = "28.0.0"
kotlinVersion = '1.3.41'
}
repositories {
google()
jcenter()
}
dependencies {
classpath('com.android.tools.build:gradle:3.4.2')
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
......@@ -35,6 +35,10 @@ allprojects {
// Android JSC is installed from npm
url("$rootDir/../node_modules/jsc-android/dist")
}
maven {
// All of Detox' artifacts are provided via the npm module
url "$rootDir/../node_modules/detox/Detox-android"
}
}
}
......@@ -47,4 +51,12 @@ subprojects {
}
}
}
}
\ No newline at end of file
afterEvaluate {project ->
if (project.hasProperty("android")) {
android {
compileSdkVersion 28
buildToolsVersion "28.0.3"
}
}
}
}
{
"setupFilesAfterEnv": ["./init.js"],
"testEnvironment": "node",
"reporters": ["detox/runners/jest/streamlineReporter"],
"verbose": true
}
import testIDs from "./testIDs";
describe('Load test', () => {
it('should have account list screen', async () => {
await expect(element(by.id(testIDs.TacScreen.tacView))).toBeVisible();
await element(by.id(testIDs.TacScreen.agreePrivacyButton)).tap();
await element(by.id(testIDs.TacScreen.agreeTacButton)).tap();
await element(by.id(testIDs.TacScreen.nextButton)).tap();
await expect(element(by.id(testIDs.AccountListScreen.accountList))).toBeVisible();
});
});
const detox = require('detox');
const config = require('../package.json').detox;
const adapter = require('detox/runners/jest/adapter');
const specReporter = require('detox/runners/jest/specReporter');
// Set the default timeout
jest.setTimeout(120000);
jasmine.getEnv().addReporter(adapter);
// This takes care of generating status logs on a per-spec basis. By default, jest only reports at file-level.
// This is strictly optional.
jasmine.getEnv().addReporter(specReporter);
beforeAll(async () => {
await detox.init(config);
});
beforeEach(async () => {
await adapter.beforeEach();
});
afterAll(async () => {
await adapter.afterAll();
await detox.cleanup();
});
const testIDs = {
TacScreen: {
tacView: 'tac_view',
agreeTacButton: 'tac_agree',
agreePrivacyButton: 'tac_privacy',
nextButton: 'tac_next'
},
AccountListScreen: {
accountList: 'accountList',
}
};
export default testIDs;
......@@ -17,9 +17,15 @@
"ios": "yarn run build-rust-ios && react-native run-ios",
"lint": "npx eslint ./src/ --ext .js,.jsx",
"lint:fix": "npx eslint ./src/ --ext .js,.jsx --fix",
"postinstall": "npx jetify",
"postinstall": "npx jetify && chmod +x ./scripts/fix-rn-camera-path.sh && ./scripts/fix-rn-camera-path.sh ./node_modules/react-native-camera/ios/RNCamera.xcodeproj/project.pbxproj",
"start": "NODE_OPTIONS=--max_old_space_size=8192 react-native start",
"test": "jest"
"test": "jest",
"build-e2e:android": "detox build -c android.emu.debug",
"test-e2e:android": "detox test -c android.emu.debug",
"e2e:android": "yarn run build-e2e:android && yarn run test-e2e:android",
"build-e2e:ios": "detox build -c ios.sim.debug",
"test-e2e:ios": "detox test -c ios.sim.debug",
"e2e:ios": "yarn run build-e2e:ios && yarn run test-e2e:ios"
},
"husky": {
"hooks": {
......@@ -67,6 +73,7 @@
"babel-eslint": "10.0.3",
"babel-jest": "^24.9.0",
"babel-plugin-rewrite-require": "^1.14.5",
"detox": "^14.5.0",
"eslint": "^6.3.0",
"eslint-config-prettier": "^6.2.0",
"eslint-plugin-prettier": "^3.1.0",
......@@ -80,5 +87,44 @@
},
"resolutions": {
"@react-native-community/eslint-config/babel-eslint": "10.0.3"
},
"detox": {
"configurations": {
"ios.sim.debug": {
"binaryPath": "ios/build/NativeSigner/Build/Products/Debug-iphonesimulator/NativeSigner.app",
"build": "xcodebuild -project ios/NativeSigner.xcodeproj -scheme NativeSigner -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build/NativeSigner",
"type": "ios.simulator",
"device": {
"type": "iPhone SE"
}
},
"ios.sim.release": {
"binaryPath": "ios/build/NativeSigner/Build/Products/Release-iphonesimulator/NativeSigner.app",
"build": "xcodebuild -project ios/NativeSigner.xcodeproj -scheme NativeSigner -configuration Release -sdk iphonesimulator -derivedDataPath ios/build/NativeSigner -UseModernBuildSystem=NO | xcpretty -t && exit ${PIPESTATUS[0]}",
"type": "ios.simulator",
"device": {
"type": "iPhone SE"
}
},
"android.emu.debug": {
"binaryPath": "android/app/build/outputs/apk/debug/app-debug.apk",
"build":
"cd android && ./gradlew assembleDebug assembleAndroidTest -DtestBuildType=debug && cd ..",
"type": "android.emulator",
"device": {
"avdName": "Nexus_5_API_28"
}
},
"android.emu.release": {
"binaryPath": "android/app/build/outputs/apk/release/app-release.apk",
"build": "cd android && ./gradlew assembleRelease assembleAndroidTest -DtestBuildType=release && cd ..",
"type": "android.emulator",
"device": {
"avdName": "Nexus_5_API_28"
}
}
},
"runner-config": "e2e/config.json",
"test-runner": "jest"
}
}
#!/bin/bash
#
# fix-rncamera-search-paths.sh
#
# Fix Frameworks/Headers Search Path build settings in react-native-camera/ios Xcode project
# First argument is path to RNCamera xcode project file, e.g.:
# ./node_modules/react-native-camera/ios/RNCamera.xcodeproj/project.pbxproj
#
SRC_FILE="$1"
if [ ! -f ${SRC_FILE} ]; then
echo "Error: RNCamera xcodeproj not found at path: ${SRC_FILE}"
echo "Skipping fix"
exit 0
fi
perl -i -p0e 's/FRAMEWORK_SEARCH_PATHS = (.*?);/FRAMEWORK_SEARCH_PATHS = "\$(SRCROOT)\/..\/..\/..\/ios\/Pods\/Headers\/**\/**";/gs' ${SRC_FILE}
perl -i -p0e 's/HEADER_SEARCH_PATHS = (.*?);/HEADER_SEARCH_PATHS = "\$(SRCROOT)\/..\/..\/react-native\/React\/**";/gs' ${SRC_FILE}
perl -i -p0e 's/LIBRARY_SEARCH_PATHS = (.*?);/LIBRARY_SEARCH_PATHS = "\$(SRCROOT)\/..\/..\/..\/ios\/Pods\/Headers\/**\/**";/gs' ${SRC_FILE}
echo "Fixed RNCamera xcodeproj at path: ${SRC_FILE}"
......@@ -46,7 +46,14 @@ export default class Button extends React.PureComponent<{
};
render() {
const { onPress, title, disabled, textStyles, buttonStyles } = this.props;
const {
onPress,
title,
disabled,
textStyles,
buttonStyles,
testID
} = this.props;
const finalTextStyles = [styles.text, textStyles];
const finalButtonStyles = [styles.button, buttonStyles];
......@@ -63,6 +70,7 @@ export default class Button extends React.PureComponent<{
accessibilityComponentType="button"
disabled={disabled}
onPress={onPress}
testID={testID}
>
<View style={finalButtonStyles}>
<Text style={finalTextStyles} disabled={disabled}>
......
......@@ -28,6 +28,7 @@ import Button from '../components/Button';
import PopupMenu from '../components/PopupMenu';
import fonts from '../fonts';
import AccountsStore from '../stores/AccountsStore';
import testIDs from '../../e2e/testIDs';
export default class AccountList extends React.PureComponent {
static navigationOptions = {
......@@ -89,7 +90,7 @@ class AccountListView extends React.PureComponent {
const { navigate } = navigation;
return (
<View style={styles.body}>
<View style={styles.body} testID={testIDs.AccountListScreen.accountList}>
<Background />
<View style={styles.header}>
<Text style={styles.title}>ACCOUNTS</Text>
......
......@@ -26,6 +26,7 @@ import Button from '../components/Button';
import Markdown from '../components/Markdown';
import TouchableItem from '../components/TouchableItem';
import { saveToCAndPPConfirmation } from '../util/db';
import testIDs from '../../e2e/testIDs';
export default class TermsAndConditions extends React.PureComponent {
static navigationOptions = {
......@@ -42,12 +43,13 @@ export default class TermsAndConditions extends React.PureComponent {
const { navigation } = this.props;
const { tocAgreement, ppAgreement } = this.state;
return (
<View style={styles.body}>
<View style={styles.body} testID={testIDs.TacScreen.tacView}>
<ScrollView contentContainerStyle={{}}>
<Markdown>{toc}</Markdown>
</ScrollView>
<TouchableItem
testID={testIDs.TacScreen.agreeTacButton}
style={{
alignItems: 'center',
flexDirection: 'row'
......@@ -75,6 +77,7 @@ export default class TermsAndConditions extends React.PureComponent {
}}
>
<Icon
testID={testIDs.TacScreen.agreePrivacyButton}
name={ppAgreement ? 'checkbox-marked' : 'checkbox-blank-outline'}
style={[styles.text, { fontSize: 30 }]}
/>
......@@ -94,6 +97,7 @@ export default class TermsAndConditions extends React.PureComponent {
<Button
buttonStyles={{ height: 60, marginTop: 10 }}
testID={testIDs.TacScreen.nextButton}
title="Next"
disabled={!ppAgreement || !tocAgreement}
onPress={async () => {
......
......@@ -46,6 +46,26 @@
semver "^5.4.1"
source-map "^0.5.0"
"@babel/core@^7.4.5":
version "7.6.4"
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.6.4.tgz#6ebd9fe00925f6c3e177bb726a188b5f578088ff"
integrity sha512-Rm0HGw101GY8FTzpWSyRbki/jzq+/PkNQJ+nSulrdY6gFGOsNseCqD6KHRYe2E+EdzuBdr2pxCp6s4Uk6eJ+XQ==
dependencies:
"@babel/code-frame" "^7.5.5"
"@babel/generator" "^7.6.4"
"@babel/helpers" "^7.6.2"
"@babel/parser" "^7.6.4"
"@babel/template" "^7.6.0"
"@babel/traverse" "^7.6.3"
"@babel/types" "^7.6.3"
convert-source-map "^1.1.0"
debug "^4.1.0"
json5 "^2.1.0"
lodash "^4.17.13"
resolve "^1.3.2"
semver "^5.4.1"
source-map "^0.5.0"
"@babel/generator@^7.0.0", "@babel/generator@^7.4.0", "@babel/generator@^7.6.0":
version "7.6.0"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.6.0.tgz#e2c21efbfd3293ad819a2359b448f002bfdfda56"
......@@ -57,6 +77,16 @@
source-map "^0.5.0"
trim-right "^1.0.1"
"@babel/generator@^7.6.3", "@babel/generator@^7.6.4":
version "7.6.4"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.6.4.tgz#a4f8437287bf9671b07f483b76e3bb731bc97671"
integrity sha512-jsBuXkFoZxk0yWLyGI9llT9oiQ2FeTASmRFE32U+aaDTfoE92t78eroO7PTpU/OrYq38hlcDM6vbfLDaOLy+7w==
dependencies:
"@babel/types" "^7.6.3"
jsesc "^2.5.1"
lodash "^4.17.13"
source-map "^0.5.0"
"@babel/helper-annotate-as-pure@^7.0.0":
version "7.0.0"
resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.0.0.tgz#323d39dd0b50e10c7c06ca7d7638e6864d8c5c32"
......@@ -241,6 +271,15 @@
"@babel/traverse" "^7.6.0"
"@babel/types" "^7.6.0"
"@babel/helpers@^7.6.2":
version "7.6.2"
resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.6.2.tgz#681ffe489ea4dcc55f23ce469e58e59c1c045153"
integrity sha512-3/bAUL8zZxYs1cdX2ilEE0WobqbCmKWr/889lf2SS0PpDcpEIY8pb1CCyz0pEcX3pEb+MCbks1jIokz2xLtGTA==
dependencies:
"@babel/template" "^7.6.0"
"@babel/traverse" "^7.6.2"
"@babel/types" "^7.6.0"
"@babel/highlight@^7.0.0":
version "7.5.0"
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.5.0.tgz#56d11312bd9248fa619591d02472be6e8cb32540"
......@@ -255,6 +294,11 @@
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.6.0.tgz#3e05d0647432a8326cb28d0de03895ae5a57f39b"
integrity sha512-+o2q111WEx4srBs7L9eJmcwi655eD8sXniLqMB93TBK9GrNzGrxDWSjiqz2hLU0Ha8MTXFIP0yd9fNdP+m43ZQ==
"@babel/parser@^7.6.3", "@babel/parser@^7.6.4":
version "7.6.4"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.6.4.tgz#cb9b36a7482110282d5cb6dd424ec9262b473d81"
integrity sha512-D8RHPW5qd0Vbyo3qb+YjO5nvUVRTXFLQ/FsDxJU2Nqz4uB5EnUN0ZQSEYpvTIbRuttig1XbHWU5oMeQwQSAA+A==
"@babel/plugin-external-helpers@^7.0.0":
version "7.2.0"
resolved "https://registry.yarnpkg.com/@babel/plugin-external-helpers/-/plugin-external-helpers-7.2.0.tgz#7f4cb7dee651cd380d2034847d914288467a6be4"
......@@ -663,6 +707,21 @@
globals "^11.1.0"
lodash "^4.17.13"
"@babel/traverse@^7.6.2", "@babel/traverse@^7.6.3":
version "7.6.3"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.6.3.tgz#66d7dba146b086703c0fb10dd588b7364cec47f9"
integrity sha512-unn7P4LGsijIxaAJo/wpoU11zN+2IaClkQAxcJWBNCMS6cmVh802IyLHNkAjQ0iYnRS3nnxk5O3fuXW28IMxTw==
dependencies:
"@babel/code-frame" "^7.5.5"
"@babel/generator" "^7.6.3"
"@babel/helper-function-name" "^7.1.0"
"@babel/helper-split-export-declaration" "^7.4.4"
"@babel/parser" "^7.6.3"
"@babel/types" "^7.6.3"
debug "^4.1.0"
globals "^11.1.0"
lodash "^4.17.13"
"@babel/types@^7.0.0", "@babel/types@^7.2.0", "@babel/types@^7.3.0", "@babel/types@^7.4.0", "@babel/types@^7.4.4", "@babel/types@^7.5.5", "@babel/types@^7.6.0":
version "7.6.1"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.6.1.tgz#53abf3308add3ac2a2884d539151c57c4b3ac648"
......@@ -672,6 +731,15 @@
lodash "^4.17.13"
to-fast-properties "^2.0.0"
"@babel/types@^7.6.3":
version "7.6.3"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.6.3.tgz#3f07d96f854f98e2fbd45c64b0cb942d11e8ba09"
integrity sha512-CqbcpTxMcpuQTMhjI37ZHVgjBkysg5icREQIEZ0eG1yCNwg3oy+5AaLiOKmjsCj6nqOsa6Hf0ObjRVwokb7srA==
dependencies:
esutils "^2.0.2"
lodash "^4.17.13"
to-fast-properties "^2.0.0"
"@cnakazawa/watch@^1.0.3":
version "1.0.3"
resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.3.tgz#099139eaec7ebf07a27c1786a3ff64f39464d2ef"
......@@ -1904,6 +1972,11 @@ blakejs@^1.1.0:
resolved "https://registry.yarnpkg.com/blakejs/-/blakejs-1.1.0.tgz#69df92ef953aa88ca51a32df6ab1c54a155fc7a5"
integrity sha1-ad+S75U6qIylGjLfarHFShVfx6U=
bluebird@3.5.x:
version "3.5.5"
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.5.tgz#a8d0afd73251effbbd5fe384a77d73003c17a71f"
integrity sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w==
bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.11.8, bn.js@^4.4.0:
version "4.11.8"
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f"
......@@ -2079,6 +2152,24 @@ builtin-status-codes@^3.0.0:
resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8"
integrity sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=
bunyan-debug-stream@^1.1.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/bunyan-debug-stream/-/bunyan-debug-stream-1.1.1.tgz#4740a00b7d5c2d9d1b714925ab0802516040813e"
integrity sha512-jJbQ1gXUL6vMmZVdbaTFK1v1sGa7axLrSQQwkB6HU9HCPTzsw2HsKcPHm1vgXZlEck/4IvEuRwg/9+083YelCg==
dependencies:
colors "^1.0.3"
e