Skip to content

Commit

Permalink
Merge branch 'develop' into shared/otf-sprint-6
Browse files Browse the repository at this point in the history
* develop:
  fix: fix merging of settings state in settings context (#711)
  chore(tests): Skip waiting for map to completely load in e2e tests (#694)
  chore: Run Github actions on every push to develop branch (#710)
  chore: speedup CI e2e tests (#708)
  chore: enable TestButler for e2e tests (#695)
  • Loading branch information
gmaclennan committed Aug 26, 2021
2 parents ba1cd76 + 6314d6c commit 365b26f
Show file tree
Hide file tree
Showing 8 changed files with 176 additions and 50 deletions.
129 changes: 85 additions & 44 deletions .github/workflows/android.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
name: Android
on:
push:
branches:
- develop
pull_request:
# By default, a workflow only runs when a pull_request 's activity type is
# opened , synchronize , or reopened. Adding ready_for_review here ensures
Expand Down Expand Up @@ -27,87 +30,125 @@ jobs:
run: |
cd src/backend
npm test
e2e-tests:
e2e-build:
if: github.event.pull_request.draft == false
name: e2e tests
runs-on: macos-latest
name: e2e build
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
# - name: Cache npm files
# uses: actions/cache@v1
# with:
# path: ~/.npm
# key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
# restore-keys: |
# ${{ runner.os }}-node-
# - name: Cache Gradle files
# uses: actions/cache@v1
# with:
# path: ~/.gradle/caches
# key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }}-${{ hashFiles('**/gradle/wrapper/gradle-wrapper.properties') }}
# restore-keys: ${{ runner.os }}-gradle-
- name: Cache npm files
uses: actions/cache@v1
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
- name: Cache Gradle files
uses: actions/cache@v1
with:
path: ~/.gradle/caches
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }}-${{ hashFiles('**/gradle/wrapper/gradle-wrapper.properties') }}
restore-keys: ${{ runner.os }}-gradle-
- name: nodejs-mobile build cache
id: cache
uses: actions/cache@v2
with:
path: android/build/nodejs-native-assets
key: ${{ runner.os }}-nodejs-mobile-${{ hashFiles('src/backend/package-lock.json') }}
- name: Node
uses: actions/setup-node@v1
- name: Use specific Java version for sdkmanager to work
uses: joschi/setup-jdk@v2
with:
java-version: "openjdk8"
architecture: "x64"
# - name: Install NDK
# run: |
# export ANDROID_NDK_VERSION='20b'
# export ANDROID_NDK_HOME=$ANDROID_HOME/ndk-bundle
# curl -fsSo android-ndk-r${ANDROID_NDK_VERSION}.zip https://dl.google.com/android/repository/android-ndk-r${ANDROID_NDK_VERSION}-linux-x86_64.zip
# unzip -q -o android-ndk-r${ANDROID_NDK_VERSION}.zip
# rm -rf $ANDROID_NDK_HOME
# mv android-ndk-r${ANDROID_NDK_VERSION} $ANDROID_NDK_HOME
# ls -al $ANDROID_NDK_HOME
# cat $ANDROID_NDK_HOME/source.properties
# Our build will otherwise use NDK 22, which does not work.
- name: Delete newer NDK version
run: rm -rf $ANDROID_HOME/ndk/22*
- name: Install node_modules
run: |
mkdir -p nodejs-assets
npm ci
- name: Build backend
run: |
rm -rf $ANDROID_HOME/ndk/22*
npm run build:backend
- name: Build translations
run: |
npm run build:translations
- name: Build for detox
run: npm run build-detox-android
env:
# If we have cached the nodejs-native-assets, then we do not need
# to build native modules, so we set this to "0"
NODEJS_MOBILE_BUILD_NATIVE_MODULES: ${{ steps.cache.outputs.cache-hit == 'true' && '0' || '1' }}
- name: Upload artifacts
uses: actions/upload-artifact@v2
with:
name: outputs-apk
path: android/app/build/outputs/apk
e2e-tests:
needs: e2e-build
name: e2e tests
runs-on: macos-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- uses: actions/download-artifact@v2
with:
name: outputs-apk
path: android/app/build/outputs/apk
- name: Cache npm files
uses: actions/cache@v1
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
- name: Node
uses: actions/setup-node@v1
- name: Use specific Java version for sdkmanager to work
uses: joschi/setup-jdk@v2
with:
java-version: "openjdk8"
architecture: "x64"
- name: Download Android Emulator Image
run: |
sdkmanager --update | grep -v = || true
echo "SDK versions:"
$ANDROID_HOME/tools/bin/sdkmanager --list
echo "Install latest emulator"
$ANDROID_HOME/tools/bin/sdkmanager --install emulator > /dev/null
echo "Downloading emulator image"
echo "y" | $ANDROID_HOME/tools/bin/sdkmanager --install "system-images;android-28;default;x86" > /dev/null
echo "y" | $ANDROID_HOME/tools/bin/sdkmanager --install "system-images;android-28;default;x86_64" > /dev/null
echo "Creating emulator avd"
echo "no" | $ANDROID_HOME/tools/bin/avdmanager create avd --force --name emu --device "Nexus 6" -k 'system-images;android-28;default;x86'
echo "no" | $ANDROID_HOME/tools/bin/avdmanager create avd --force --name emu --device "Nexus 6" -k 'system-images;android-28;default;x86_64'
echo "Emulator version:"
$ANDROID_HOME/emulator/emulator @emu -version
echo "Hardware acceleration:"
$ANDROID_HOME/emulator/emulator -accel-check
kextstat | grep intel
echo "\nEmulator config"
cat $HOME/.android/avd/emu.avd/config.ini
- name: Start Android Emulator
run: |
echo "Starting emulator"
mkdir -p artifacts
nohup $ANDROID_HOME/emulator/emulator -avd emu -no-audio -no-snapshot -no-window -gpu swiftshader_indirect -camera-back emulated -no-boot-anim &
- name: Install node_modules
run: |
mkdir -p nodejs-assets
npm ci
- name: Build backend
run: |
npm run build:backend
- name: Build translations
run: |
npm run build:translations
- name: Build for detox
run: |
npm run build-detox-android
- name: Android Emulator
# We are only running detox here, so we don't need the postinstall scripts to run
npm ci --ignore-scripts
- name: Wait for Android Emulator
timeout-minutes: 10
continue-on-error: true
run: |
echo "Starting emulator"
mkdir -p artifacts
nohup $ANDROID_HOME/emulator/emulator -avd emu -no-audio -no-snapshot -no-window -gpu swiftshader_indirect -camera-back emulated -no-boot-anim &
$ANDROID_HOME/platform-tools/adb wait-for-device shell 'while [[ -z $(getprop sys.boot_completed | tr -d '\r') ]]; do sleep 1; done; input keyevent 82'
$ANDROID_HOME/platform-tools/adb devices
echo "Emulator started"
- name: Android Detox
run: npm start & npm run test-detox-android -- -l verbose --headless --record-videos all --record-logs all
run: npm start & npm run test-detox-android -- --headless --record-videos all --record-logs all
- name: Upload artifacts
uses: actions/upload-artifact@v1
if: always()
Expand Down
17 changes: 15 additions & 2 deletions android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,18 @@ bugsnag {
sharedObjectPath "app/build/jni/libs"
}

String shouldRebuildNativeModules = System.getenv('NODEJS_MOBILE_BUILD_NATIVE_MODULES');

// This is from the nodejs-mobile build.gradle, but it is only set if
// NODEJS_MOBILE_BUILD_NATIVE_MODULES is "1". When we cache the native modules
// build files we set NODEJS_MOBILE_BUILD_NATIVE_MODULES to "0", but we still
// want to include this folder in the build
if ("0".equals(shouldRebuildNativeModules)) {
// We are not rebuilding native modules, but we still want to include the
// cached native module build files in the build
project.android.sourceSets.main.assets.srcDirs+="${rootProject.buildDir}/nodejs-native-assets/"
}

android {
compileSdkVersion rootProject.ext.compileSdkVersion
buildToolsVersion rootProject.ext.buildToolsVersion
Expand All @@ -204,7 +216,7 @@ android {

// Detox integration
testBuildType System.getProperty('testBuildType', 'debug') // This will later be used to control the test apk build type
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
testInstrumentationRunner 'com.mapeo.DetoxTestAppJUnitRunner'
}
splits {
abi {
Expand Down Expand Up @@ -360,7 +372,8 @@ dependencies {
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0-alpha02'

// Detox
androidTestImplementation('com.wix:detox:+')
androidTestImplementation 'com.wix:detox:+'
androidTestImplementation 'com.linkedin.testbutler:test-butler-library:2.2.1'
}

// Run this once to be able to run the application with BUCK
Expand Down
5 changes: 3 additions & 2 deletions android/app/src/androidTest/java/com/mapeo/DetoxTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@ public class DetoxTest {
public ActivityTestRule<MainActivity> mActivityRule = new ActivityTestRule<>(MainActivity.class, false, false);

@Test
public void runDetoxTests() {// This is optional - in case you've decided to integrate TestButler
public void runDetoxTests() {
// This is optional - in case you've decided to integrate TestButler
// See https://github.com/wix/Detox/blob/master/docs/Introduction.Android.md#8-test-butler-support-optional
// TestButlerProbe.assertReadyIfInstalled();
TestButlerProbe.assertReadyIfInstalled();

DetoxConfig detoxConfig = new DetoxConfig();
detoxConfig.idlePolicyConfig.masterTimeoutSec = 90;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.mapeo;

import android.os.Bundle;

import com.linkedin.android.testbutler.TestButler;

import androidx.test.runner.AndroidJUnitRunner;

public class DetoxTestAppJUnitRunner extends AndroidJUnitRunner {
@Override
public void onStart() {
TestButler.setup(getTargetContext());
super.onStart();
}

@Override
public void finish(int resultCode, Bundle results) {
TestButler.teardown(getTargetContext());
super.finish(resultCode, results);
}
}
49 changes: 49 additions & 0 deletions android/app/src/androidTest/java/com/mapeo/TestButlerProbe.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.mapeo;

import android.content.pm.PackageManager;
import android.util.Log;
import android.view.Surface;

import com.linkedin.android.testbutler.TestButler;

import androidx.test.platform.app.InstrumentationRegistry;

class TestButlerProbe {

private static final String LOG_TAG = TestButlerProbe.class.getSimpleName();
private static final String TEST_BUTLER_PACKAGE_NAME = "com.linkedin.android.testbutler";

private TestButlerProbe() {
}

static void assertReadyIfInstalled() {
Log.i(LOG_TAG, "Test butler service verification started...");

if (!isTestButlerServiceInstalled()) {
Log.w(LOG_TAG, "Test butler not installed on device - skipping verification");
return;
}

assertTestButlerServiceReady();
Log.i(LOG_TAG, "Test butler service is up and running!");
}

static private boolean isTestButlerServiceInstalled() {
try {
PackageManager pm = InstrumentationRegistry.getInstrumentation().getTargetContext().getPackageManager();
pm.getPackageInfo(TEST_BUTLER_PACKAGE_NAME, 0);
return true;
} catch (PackageManager.NameNotFoundException e) {
return false;
}
}

static private void assertTestButlerServiceReady() {
try {
// This has no effect if test-butler is running. However, if it is not, then unlike TestButler.setup(), it would hard-fail.
TestButler.setRotation(Surface.ROTATION_0);
} catch (Exception e) {
throw new RuntimeException("Test butler service is NOT ready!", e);
}
}
}
2 changes: 1 addition & 1 deletion detox.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ module.exports = {
binaryPath:
"android/app/build/outputs/apk/app/universal/mapeo-app-universal.apk",
build:
"cd android && ./gradlew app:assembleUniversal app:assembleAndroidTest -DtestBuildType=universal && cd ..",
"cd android && ./gradlew --parallel app:assembleAppUniversal app:assembleAndroidTest -DtestBuildType=universal && cd ..",
},
"android.debug": {
type: "android.apk",
Expand Down
1 change: 1 addition & 0 deletions e2e/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@

beforeAll(async () => {
await device.launchApp();
await device.setURLBlacklist([".*api.mapbox.com.*"]);
});
2 changes: 1 addition & 1 deletion src/frontend/context/SettingsContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export const SettingsProvider = ({ children }: React.PropsWithChildren<{}>) => {
const contextValue: SettingsContextType = React.useMemo(() => {
// If we add any new properties to the settings state, they will be
// undefined in a users' persisted state, so we merge in the defaults
const mergedState = merge({}, state, DEFAULT_SETTINGS);
const mergedState = merge({}, DEFAULT_SETTINGS, state);
return [mergedState, setSettings];
}, [state, setSettings]);

Expand Down

0 comments on commit 365b26f

Please sign in to comment.