Skip to content

Commit

Permalink
feat: android e2e tests
Browse files Browse the repository at this point in the history
  • Loading branch information
limpbrains committed Jun 15, 2024
1 parent dcbdc2c commit 4840035
Show file tree
Hide file tree
Showing 25 changed files with 372 additions and 76 deletions.
7 changes: 6 additions & 1 deletion .detoxrc.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
// run iPhone 14 on local machine, iPhone 15 Pro on mac mini
const iOSDevice = process.env.MACMINI ? 'iPhone 15 Pro' : 'iPhone 14';

const reversePorts = [3003, 8080, 8081, 9735, 10009, 28334, 28335, 28336, 39388, 43782, 60001];

/** @type {Detox.DetoxConfig} */
module.exports = {
testRunner: {
$0: 'jest',
Expand Down Expand Up @@ -28,12 +31,14 @@ module.exports = {
binaryPath: 'android/app/build/outputs/apk/debug/app-debug.apk',
build:
'cd android && ./gradlew assembleDebug assembleAndroidTest -DtestBuildType=debug && cd .. ',
reversePorts,
},
'android.release': {
type: 'android.apk',
binaryPath: 'android/app/build/outputs/apk/release/app-release.apk',
build:
'cd android && ./gradlew assembleRelease assembleAndroidTest -DtestBuildType=release && cd ..',
reversePorts,
},
},
devices: {
Expand All @@ -46,7 +51,7 @@ module.exports = {
emulator: {
type: 'android.emulator',
device: {
avdName: 'Pixel_API_29_AOSP',
avdName: 'Pixel_API_31_AOSP',
},
},
},
Expand Down
2 changes: 1 addition & 1 deletion .env.test.template
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ BACKUPS_SERVER_PUBKEY=0319c4ff23820afec0c79ce3a42031d7fef1dff78b7bdd69b5560684f3
WEB_RELAY=https://webrelay.slashtags.to

# Blocktank
BLOCKTANK_HOST=https://api1.blocktank.to/api
BLOCKTANK_HOST=https://api.stag.blocktank.to

# Network
ELECTRUM_BITCOIN_HOST=35.187.18.233
Expand Down
141 changes: 141 additions & 0 deletions .github/workflows/e2e-android.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
name: e2e-android

on:
workflow_dispatch:
pull_request:

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

env:
E2E_TESTS: 1 # build without transform-remove-console babel plugin
DEBUG: 'lnurl* lnurl server'

jobs:
e2e:
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 1

- name: Enable KVM group perms
run: |
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
sudo udevadm control --reload-rules
sudo udevadm trigger --name-match=kvm
- name: Free Disk Space
uses: jlumbroso/free-disk-space@main
with:
# this might remove tools that are actually needed,
# if set to "true" but frees about 6 GB
tool-cache: false
android: false
dotnet: true
haskell: true
large-packages: true
docker-images: true
swap-storage: true

- name: yarn and gradle caches in /mnt
run: |
rm -rf ~/.yarn
rm -rf ~/.gradle
sudo mkdir -p /mnt/.yarn
sudo mkdir -p /mnt/.gradle
sudo chown -R runner /mnt/.yarn
sudo chown -R runner /mnt/.gradle
ln -s /mnt/.yarn /home/runner/
ln -s /mnt/.gradle /home/runner/
- name: Create artifacts directory on /mnt
run: |
sudo mkdir -p /mnt/artifacts
sudo chown -R runner /mnt/artifacts
- name: Specify node version
uses: actions/setup-node@v4
with:
node-version: 20

- name: Use gradle caches
uses: actions/cache@v4
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-gradle-
- name: Use yarn caches
uses: actions/cache@v4
with:
path: ~/.yarn
key: ${{ runner.os }}-yarn-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: Activate enviroment variables
run: cp .env.test.template .env

- name: Yarn Install
run: yarn || yarn
env:
HUSKY: 0

- name: Activate Gradle variables
run: |
cp .github/workflows/gradle.properties ~/.gradle/gradle.properties
patch -p1 -i ./.github/workflows/react-native-quick-crypto.patch
- name: Use specific Java version for sdkmanager to work
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '17'

- name: Build
run: yarn e2e:build:android-release || yarn e2e:build:android-release

- name: Kill java processes
run: pkill -9 -f java || true

- name: Run regtest setup
run: |
cd docker
mkdir lnd && chmod 777 lnd
docker-compose pull --quiet
docker compose up -d
- name: Wait for electrum server and LND
timeout-minutes: 10
run: |
while ! nc -z '127.0.0.1' 60001; do sleep 1; done
while ! nc -z '127.0.0.1' 10009; do sleep 1; done
sudo chmod -R 777 docker/lnd
- name: Run tests
uses: reactivecircus/android-emulator-runner@v2
with:
profile: 4.7" WXGA # devices list: avdmanager list device
api-level: 31
avd-name: Pixel_API_31_AOSP
force-avd-creation: false
emulator-options: -no-window -gpu swiftshader_indirect -no-snapshot -noaudio -no-boot-anim -camera-back none -camera-front none -partition-size 2047
arch: x86_64
script: yarn e2e:test:android-release --record-videos all --record-logs all --take-screenshots all --headless -d 200000 -R 2 --artifacts-location /mnt/artifacts

- uses: actions/upload-artifact@v4
if: failure()
with:
name: e2e-test-videos
path: /mnt/artifacts/

- name: Dump docker logs on failure
if: failure()
uses: jwalton/gh-docker-logs@v2
4 changes: 4 additions & 0 deletions .github/workflows/gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
BITKIT_UPLOAD_STORE_FILE=debug.keystore
BITKIT_UPLOAD_STORE_PASSWORD=android
BITKIT_UPLOAD_KEY_ALIAS=androiddebugkey
BITKIT_UPLOAD_KEY_PASSWORD=android
13 changes: 13 additions & 0 deletions .github/workflows/react-native-quick-crypto.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
diff --git a/node_modules/react-native-quick-crypto/android/build.gradle b/node_modules/react-native-quick-crypto/android/build.gradle
index 2ac6c0db..57afa566 100644
--- a/node_modules/react-native-quick-crypto/android/build.gradle
+++ b/node_modules/react-native-quick-crypto/android/build.gradle
@@ -94,6 +94,8 @@ android {
""
]
doNotStrip '**/*.so'
+ pickFirst 'META-INF/com.android.tools/proguard/coroutines.pro'
+ pickFirst 'META-INF/proguard/coroutines.pro'
}

buildTypes {
4 changes: 4 additions & 0 deletions android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ android {
versionName "1.0.1"
multiDexEnabled true
missingDimensionStrategy 'react-native-camera', 'general'
testBuildType System.getProperty('testBuildType', 'debug')
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
}

signingConfigs {
Expand Down Expand Up @@ -112,6 +114,7 @@ android {
signingConfig signingConfigs.release
minifyEnabled enableProguardInReleaseBuilds
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
proguardFile "${rootProject.projectDir}/../node_modules/detox/android/detox/proguard-rules-app.pro"
}
}
packagingOptions {
Expand All @@ -127,6 +130,7 @@ android {
}

dependencies {
androidTestImplementation('com.wix:detox:+')
// The version of react-native is set by the React Native Gradle Plugin
implementation("com.facebook.react:react-android")
implementation files("../../node_modules/@synonymdev/react-native-ldk/android/libs/LDK-release.aar")
Expand Down
29 changes: 29 additions & 0 deletions android/app/src/androidTest/java/com/bitkit/DetoxTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.bitkit;

import com.wix.detox.Detox;
import com.wix.detox.config.DetoxConfig;

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() {
DetoxConfig detoxConfig = new DetoxConfig();
detoxConfig.idlePolicyConfig.masterTimeoutSec = 90;
detoxConfig.idlePolicyConfig.idleResourceTimeoutSec = 60;
detoxConfig.rnContextLoadTimeoutSec = (BuildConfig.DEBUG ? 180 : 60);

Detox.runTests(mActivityRule, detoxConfig);
}
}
3 changes: 2 additions & 1 deletion android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@
android:roundIcon="@mipmap/ic_launcher_orange_round"
android:allowBackup="false"
android:usesCleartextTraffic="true"
android:theme="@style/AppTheme">
android:theme="@style/AppTheme"
android:networkSecurityConfig="@xml/network_security_config">
<activity
android:name=".MainActivity"
android:label="@string/app_name"
Expand Down
8 changes: 8 additions & 0 deletions android/app/src/main/res/xml/network_security_config.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<domain-config cleartextTrafficPermitted="true">
<domain includeSubdomains="true">10.0.2.2</domain>
<domain includeSubdomains="true">localhost</domain>
<domain includeSubdomains="true">127.0.0.1</domain>
</domain-config>
</network-security-config>
7 changes: 7 additions & 0 deletions android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ buildscript {
compileSdkVersion = 34
targetSdkVersion = 34
kotlin_version = "1.8.0"
kotlinVersion = "1.8.0"
ndkVersion = "25.2.9519653"
}
repositories {
Expand All @@ -21,3 +22,9 @@ buildscript {
}

apply plugin: "com.facebook.react.rootproject"

allprojects {
repositories {
maven { url("$rootDir/../node_modules/detox/Detox-android") }
}
}
14 changes: 7 additions & 7 deletions e2e/backup.e2e.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ d('Backup', () => {

await waitFor(element(by.id('NewTxPrompt')))
.toBeVisible()
.withTimeout(10000);
.withTimeout(60000);
await element(by.id('NewTxPrompt')).swipe('down'); // close Receive screen
await sleep(200); // animation

Expand All @@ -79,21 +79,21 @@ d('Backup', () => {
await element(by.id('ActivitySavings')).tap();
await element(by.id('Activity-1')).tap();
await element(by.id('ActivityTag')).tap();
await element(by.id('TagInput')).replaceText(tag);
await element(by.id('TagInput')).typeText(tag);
await element(by.id('TagInput')).tapReturnKey();
await sleep(200); // animation
await element(by.id('NavigationClose')).tap();
await element(by.id('NavigationClose')).atIndex(0).tap();

// change currency to GBP
await element(by.id('TotalBalance')).tap(); // switch to local currency
await element(by.id('Settings')).tap();
await element(by.id('GeneralSettings')).tap();
await element(by.id('CurrenciesSettings')).tap();
await element(by.text('GBP (£)')).tap();
await element(by.id('NavigationClose')).tap();
await element(by.id('NavigationClose')).atIndex(0).tap();

// remove 2 default widgets, leave PriceWidget
await element(by.id('WalletsScrollView')).scroll(100, 'down', NaN, 0.85);
await element(by.id('WalletsScrollView')).scroll(200, 'down', NaN, 0.85);
await element(by.id('WidgetsEdit')).tap();
for (const w of ['HeadlinesWidget', 'BlocksWidget']) {
await element(by.id('WidgetActionDelete').withAncestor(by.id(w))).tap();
Expand All @@ -119,7 +119,7 @@ d('Backup', () => {

await element(by.id('SeedContaider')).swipe('down');
await sleep(200); // animation
await element(by.id('NavigationClose')).tap();
await element(by.id('NavigationClose')).atIndex(0).tap();

await sleep(5000); // make sure everything is saved to cloud storage TODO: improve this

Expand Down Expand Up @@ -165,7 +165,7 @@ d('Backup', () => {
await expect(
element(by.id(`Tag-${tag}`).withAncestor(by.id('ActivityTags'))),
).toBeVisible();
await element(by.id('NavigationClose')).tap();
await element(by.id('NavigationClose')).atIndex(0).tap();

// check widgets
await element(by.id('WalletsScrollView')).scroll(300, 'down', NaN, 0.85);
Expand Down
Loading

0 comments on commit 4840035

Please sign in to comment.