Skip to content

Commit

Permalink
Enhance Android E2E workflow with Test Butler integration and configu…
Browse files Browse the repository at this point in the history
…ration updates
  • Loading branch information
lposen committed Oct 18, 2024
1 parent e853ec6 commit e7b2b23
Show file tree
Hide file tree
Showing 10 changed files with 135 additions and 9 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/e2e-android.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ jobs:
run: echo "[]" > ~/.local/share/Detox/device.registry.state.lock

- name: Get device name
if: ${{ success() || failure() }}
if: success() || failure()
id: device
run: node -e "console.log('AVD_NAME=' + require('./example/.detoxrc').devices.emulator.device.avdName)" >> $GITHUB_OUTPUT

Expand All @@ -95,6 +95,7 @@ jobs:
--take-screenshots all \
--capture-view-hierarchy enabled \
--maxWorkers=1 \
--detectOpenHandles \
--debug-synchronization 500 \
-l trace \
--cleanup
Expand Down
8 changes: 5 additions & 3 deletions example/.detoxrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@ module.exports = {
},
skipLegacyWorkersInjection: true,
testRunner: {
detached: true,
forwardEnv: true,
args: {
$0: 'jest',
config: 'e2e/jest.config.js',
maxWorkers: process.env.CI ? 2 : undefined,
maxWorkers: 1,
},
jest: {
setupTimeout: 120000,
setupTimeout: 1200000,
},
},
artifacts: {
Expand All @@ -31,7 +33,7 @@ module.exports = {
video: process.env.CI ? 'failing' : undefined,
timeline: process.env.CI ? 'all' : undefined,
instruments: process.env.CI ? 'all' : undefined,
uiHierarchy: process.env.DETOX_ARGV_OVERRIDE ? 'enabled' : undefined,
uiHierarchy: 'enabled',
},
},
apps: {
Expand Down
8 changes: 6 additions & 2 deletions example/android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,8 @@ android {
versionCode 1
versionName "1.0"
testBuildType System.getProperty('testBuildType', 'debug')
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
// testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
testInstrumentationRunner 'com.iterable.reactnativesdk.example.DetoxTestAppJUnitRunner'
}
signingConfigs {
debug {
Expand Down Expand Up @@ -116,7 +117,6 @@ android {
}

dependencies {
androidTestImplementation('com.wix:detox:+')
implementation 'androidx.appcompat:appcompat:1.1.0'
// The version of react-native is set by the React Native Gradle Plugin
implementation("com.facebook.react:react-android")
Expand All @@ -126,6 +126,10 @@ dependencies {
} else {
implementation jscFlavor
}

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

apply from: file("../../node_modules/react-native-vector-icons/fonts.gradle")
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package iterable.reactnativesdk.example;

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);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package iterable.reactnativesdk.example;

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);
}
}
}
5 changes: 3 additions & 2 deletions example/android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,11 @@ buildscript {
allprojects {
repositories {
google()
maven { // (4)
mavenCentral()
maven {
url("$rootDir/../node_modules/detox/Detox-android")
}
maven { url 'https://www.jitpack.io' }
// maven { url 'https://www.jitpack.io' }
}
}

Expand Down
Binary file added example/cache/test-butler-app.apk
Binary file not shown.
29 changes: 29 additions & 0 deletions example/e2e/globalSetup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { execSync } from 'child_process';

import { pathExists, ensureDir } from 'fs-extra';

import { resolveConfig } from 'detox/internals';
import { globalSetup } from 'detox/runners/jest';

export default async function customGlobalSetup() {
const config = await resolveConfig();
if (config.device.type === 'android.emulator') {
await downloadTestButlerAPK();
}

await globalSetup();
}

async function downloadTestButlerAPK() {
const version = '2.2.1';
const artifactUrl = `https://repo1.maven.org/maven2/com/linkedin/testbutler/test-butler-app/${version}/test-butler-app-${version}.apk`;
const filePath = `cache/test-butler-app.apk`;

await ensureDir('cache');
if (!(await pathExists(filePath))) {
console.log(`\nDownloading Test-Butler APK v${version}...`);
execSync(`curl -f -o ${filePath} ${artifactUrl}`);
}
}

module.exports = customGlobalSetup;
4 changes: 3 additions & 1 deletion example/e2e/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ module.exports = {
testMatch: ['<rootDir>/e2e/**/*.test.js'],
testTimeout: 1200000,
maxWorkers: 1,
globalSetup: 'detox/runners/jest/globalSetup',
// globalSetup: 'detox/runners/jest/globalSetup',
globalSetup: './e2e/globalSetup.js',
globalTeardown: 'detox/runners/jest/globalTeardown',
reporters: ['detox/runners/jest/reporter'],
testEnvironment: 'detox/runners/jest/testEnvironment',
setupFilesAfterEnv: ['./e2e/setup.js'],
verbose: true,
};
17 changes: 17 additions & 0 deletions example/e2e/setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { device } from 'detox';

// eslint-disable-next-line no-undef
beforeAll(async () => {
await device.launchApp({
/**
* Uncomment the following lines to enable verbose logging of
* synchronization issues.
* See: https://wix.github.io/Detox/docs/next/troubleshooting/synchronization
*/
// launchArgs: {
// DTXEnableVerboseSyncSystem: 'YES',
// DTXEnableVerboseSyncResources: 'YES',
// },
newInstance: true,
});
});

0 comments on commit e7b2b23

Please sign in to comment.