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 () => {
......
This diff is collapsed.
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment