From a2c7589688114d0d1f81e1ae583ce08b64f4ea91 Mon Sep 17 00:00:00 2001 From: Alex Risch Date: Fri, 1 Mar 2024 18:46:27 -0700 Subject: [PATCH 1/3] Android e2e Tests setup Merged main changes Updated Android CI Tests --- example/.detoxrc.js | 83 + example/App.tsx | 1 + example/README.md | 26 +- example/android/app/build.gradle | 40 +- .../xmtpreactnativesdk/example/DetoxTest.java | 29 + .../example/EspressoViewFinder.kt | 95 -- .../example/MainActivityTest.kt | 48 - .../android/app/src/main/AndroidManifest.xml | 2 +- .../main/res/xml/network_security_config.xml | 8 + example/android/build.gradle | 9 +- .../e2e/conversation-create-screen.test.ts | 15 + example/e2e/conversation-screen.test.ts | 17 + example/e2e/jest.config.js | 16 + example/e2e/launch-screen.test.ts | 39 + example/e2e/test_screen.test.ts | 19 + example/e2e/utils/createConversation.ts | 15 + example/e2e/utils/generateDevWallet.ts | 9 + example/e2e/utils/sendMessage.ts | 13 + example/ios/Podfile.lock | 2 +- example/package.json | 11 +- ...react-native-encrypted-storage+4.0.3.patch | 13 + example/src/ConversationCreateScreen.tsx | 4 +- example/src/ConversationScreen.tsx | 9 +- example/src/HomeScreen.tsx | 2 + example/src/LaunchScreen.tsx | 6 + example/src/TestScreen.tsx | 8 +- example/yarn.lock | 1451 ++++++++++++++++- 27 files changed, 1771 insertions(+), 219 deletions(-) create mode 100644 example/.detoxrc.js create mode 100644 example/android/app/src/androidTest/java/expo/modules/xmtpreactnativesdk/example/DetoxTest.java delete mode 100644 example/android/app/src/androidTest/java/expo/modules/xmtpreactnativesdk/example/EspressoViewFinder.kt delete mode 100644 example/android/app/src/androidTest/java/expo/modules/xmtpreactnativesdk/example/MainActivityTest.kt create mode 100644 example/android/app/src/main/res/xml/network_security_config.xml create mode 100644 example/e2e/conversation-create-screen.test.ts create mode 100644 example/e2e/conversation-screen.test.ts create mode 100644 example/e2e/jest.config.js create mode 100644 example/e2e/launch-screen.test.ts create mode 100644 example/e2e/test_screen.test.ts create mode 100644 example/e2e/utils/createConversation.ts create mode 100644 example/e2e/utils/generateDevWallet.ts create mode 100644 example/e2e/utils/sendMessage.ts create mode 100644 example/patches/react-native-encrypted-storage+4.0.3.patch diff --git a/example/.detoxrc.js b/example/.detoxrc.js new file mode 100644 index 000000000..fa11dd518 --- /dev/null +++ b/example/.detoxrc.js @@ -0,0 +1,83 @@ +/** @type {Detox.DetoxConfig} */ +module.exports = { + testRunner: { + args: { + '$0': 'jest', + config: 'e2e/jest.config.js' + }, + jest: { + setupTimeout: 120000 + } + }, + apps: { + 'ios.debug': { + type: 'ios.app', + binaryPath: 'ios/build/Build/Products/Debug-iphonesimulator/xmtpreactnativesdkexample.app', + build: 'xcodebuild -workspace ios/xmtpreactnativesdkexample.xcworkspace -scheme xmtpreactnativesdkexample -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build', + }, + 'ios.release': { + type: 'ios.app', + binaryPath: 'ios/build/Build/Products/Release-iphonesimulator/YOUR_APP.app', + build: 'xcodebuild -workspace ios/YOUR_APP.xcworkspace -scheme YOUR_APP -configuration Release -sdk iphonesimulator -derivedDataPath ios/build' + }, + 'android.debug': { + type: 'android.apk', + binaryPath: 'android/app/build/outputs/apk/debug/app-debug.apk', + build: 'cd android && ./gradlew assembleDebug assembleAndroidTest -DtestBuildType=debug', + reversePorts: [ + 8081 + ] + }, + 'android.release': { + type: 'android.apk', + binaryPath: 'android/app/build/outputs/apk/release/app-release.apk', + build: 'cd android && ./gradlew assembleRelease assembleAndroidTest -DtestBuildType=release' + } + }, + devices: { + simulator: { + type: 'ios.simulator', + device: { + type: 'iPhone 15' + } + }, + attached: { + type: 'android.attached', + device: { + adbName: '.*' + } + }, + emulator: { + type: 'android.emulator', + device: { + avdName: 'Pixel7' + } + } + }, + configurations: { + 'ios.sim.debug': { + device: 'simulator', + app: 'ios.debug' + }, + 'ios.sim.release': { + device: 'simulator', + app: 'ios.release' + }, + 'android.att.debug': { + device: 'attached', + app: 'android.debug' + }, + 'android.att.release': { + device: 'attached', + app: 'android.release' + }, + 'android.emu.debug': { + device: 'emulator', + app: 'android.debug' + }, + 'android.emu.release': { + device: 'emulator', + app: 'android.release' + } + } +}; diff --git a/example/App.tsx b/example/App.tsx index 2a7f8b531..5bc3b0d19 100644 --- a/example/App.tsx +++ b/example/App.tsx @@ -80,6 +80,7 @@ export default function App() { onPress={() => navigation.navigate('conversationCreate')} title="New" color={Platform.OS === 'ios' ? '#fff' : 'rgb(49 0 110)'} + testID="new-conversation-button" /> ), })} diff --git a/example/README.md b/example/README.md index f9acb6075..2cc80359a 100644 --- a/example/README.md +++ b/example/README.md @@ -53,4 +53,28 @@ Running tests locally is useful when updating GitHub actions, or locally testing 5. You can stop the XMTP server with the following command: ```bash docker-compose -p xmtp -f dev/local/docker-compose.yml down - ``` \ No newline at end of file + ``` + +## Run e2e tests for example app +Running e2e tests when updating example app with Detox + +1. Setup [Detox Prerequisites](https://wix.github.io/Detox/docs/introduction/environment-setup#detox-prerequisites) + +2. Update example/.detoxrc.js to match device configs - [More information on device config and debugging issues](https://wix.github.io/Detox/docs/introduction/project-setup#step-3-device-configs) + +3. Build the app for testing: +```bash + detox build --configuration ios.sim.debug +``` +or +```bash + detox build --configuration android.emu.debug +``` +4. Run the tests: +```bash + detox test --configuration ios.sim.debug +``` +or +```bash + detox test --configuration android.emu.debug +``` diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index 4fc734075..33e64fb04 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -123,7 +123,8 @@ android { targetSdkVersion rootProject.ext.targetSdkVersion versionCode 1 versionName "1.0.0" - // For Espresso + // For Detox + testBuildType System.getProperty('testBuildType', 'debug') testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' testInstrumentationRunnerArgument('useTestStorageService', 'true') // The following argument makes the Android Test Orchestrator run its @@ -132,23 +133,23 @@ android { testInstrumentationRunnerArgument('clearPackageData', 'true') } - testOptions { - execution 'ANDROIDX_TEST_ORCHESTRATOR' - managedDevices { - devices { - // run with ../gradlew nexusOneApi30DebugAndroidTest - nexusOneApi30(com.android.build.api.dsl.ManagedVirtualDevice) { - // A lower resolution device is used here for better emulator performance - device = "Nexus One" - apiLevel = 30 - // Also use the AOSP ATD image for better emulator performance - // The androidx.test screenshot APIs will automatically enable hardware rendering - // to take a screenshot - systemImageSource = "aosp-atd" - } - } - } - } + // testOptions { + // execution 'ANDROIDX_TEST_ORCHESTRATOR' + // managedDevices { + // devices { + // // run with ../gradlew nexusOneApi30DebugAndroidTest + // nexusOneApi30(com.android.build.api.dsl.ManagedVirtualDevice) { + // // A lower resolution device is used here for better emulator performance + // device = "Nexus One" + // apiLevel = 30 + // // Also use the AOSP ATD image for better emulator performance + // // The androidx.test screenshot APIs will automatically enable hardware rendering + // // to take a screenshot + // systemImageSource = "aosp-atd" + // } + // } + // } + // } splits { abi { @@ -178,6 +179,7 @@ android { signingConfig signingConfigs.debug shrinkResources (findProperty('android.enableShrinkResourcesInReleaseBuilds')?.toBoolean() ?: false) minifyEnabled enableProguardInReleaseBuilds + proguardFile "${rootProject.projectDir}/../node_modules/detox/android/detox/proguard-rules-app.pro" proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" } } @@ -273,6 +275,8 @@ dependencies { // For loading .env implementation project(':react-native-config') + // For Detox + androidTestImplementation 'com.wix:detox:+' } apply from: new File(["node", "--print", "require.resolve('@react-native-community/cli-platform-android/package.json')"].execute(null, rootDir).text.trim(), "../native_modules.gradle"); diff --git a/example/android/app/src/androidTest/java/expo/modules/xmtpreactnativesdk/example/DetoxTest.java b/example/android/app/src/androidTest/java/expo/modules/xmtpreactnativesdk/example/DetoxTest.java new file mode 100644 index 000000000..999d4ef01 --- /dev/null +++ b/example/android/app/src/androidTest/java/expo/modules/xmtpreactnativesdk/example/DetoxTest.java @@ -0,0 +1,29 @@ +package expo.modules.xmtpreactnativesdk.example; + +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 // (2) + public ActivityTestRule 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); + } +} diff --git a/example/android/app/src/androidTest/java/expo/modules/xmtpreactnativesdk/example/EspressoViewFinder.kt b/example/android/app/src/androidTest/java/expo/modules/xmtpreactnativesdk/example/EspressoViewFinder.kt deleted file mode 100644 index 8ec87799d..000000000 --- a/example/android/app/src/androidTest/java/expo/modules/xmtpreactnativesdk/example/EspressoViewFinder.kt +++ /dev/null @@ -1,95 +0,0 @@ -package expo.modules.xmtpreactnativesdk.example - -import android.graphics.Rect -import android.view.View -import androidx.test.espresso.Espresso.onView -import androidx.test.espresso.PerformException -import androidx.test.espresso.UiController -import androidx.test.espresso.ViewAction -import androidx.test.espresso.matcher.ViewMatchers -import androidx.test.espresso.matcher.ViewMatchers.isRoot -import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility -import androidx.test.espresso.util.HumanReadables -import androidx.test.espresso.util.TreeIterables -import org.hamcrest.Matcher -import java.util.concurrent.TimeoutException - -object EspressoViewFinder { - private const val CHECK_INTERVAL = 150L - private const val TIMEOUT_MS = 30 * 1000L - - /** - * Waits for the view referenced in [viewMatcher] to become visible, with a timeout of [timeOut]. If it - * becomes visible, [onDisplayedHandler] will be invoked. - * - * This method is needed because Espresso idling resources are not sufficient in combination with RN; - * it does not wait on the javascript thread and the bridge. - * - * Throws a TimeoutException wrapped in a PerformException when the view is not displayed within [timeOut]. - */ - fun waitForDisplayed( - viewMatcher: Matcher, - timeOut: Long = TIMEOUT_MS, - onDisplayedHandler: ((Matcher) -> Unit)? = null, - ) { - - // wait for view - onView(isRoot()).perform(createWaitForDisplayedViewAction(viewMatcher, timeOut)) - - // call handler - onDisplayedHandler?.invoke(viewMatcher) - } - - private fun createWaitForDisplayedViewAction( - viewMatcher: Matcher, - timeOut: Long = TIMEOUT_MS, - ) = object : ViewAction { - - override fun getConstraints(): Matcher { - return isRoot() - } - - override fun getDescription(): String { - return "waitForDisplayed on viewMatcher <$viewMatcher> without timeOut $timeOut ms." - } - - override fun perform(uiController: UiController, view: View) { - - // wait for idle, so that we don't timeout while waiting on Espresso idling resources: - uiController.loopMainThreadUntilIdle() - - val found = waitForView(uiController, view) - - if (!found) { - throw createPerformException(view) - } - } - - private fun waitForView(uiController: UiController, view: View): Boolean { - - val timeOutTimeStamp = System.currentTimeMillis() + timeOut - do { - // find view with required matcher: - for (child in TreeIterables.breadthFirstViewTraversal(view)) { - if (viewMatcher.matches(child) && isDisplayed(child)) { - return true - } - } - uiController.loopMainThreadForAtLeast(CHECK_INTERVAL) - } while (System.currentTimeMillis() < timeOutTimeStamp) - - return false - } - - private fun createPerformException(view: View) = PerformException.Builder() - .withActionDescription(this.description) - .withViewDescription(HumanReadables.describe(view)) - .withCause(TimeoutException()) - .build() - } - - private fun isDisplayed(view: View) = - view.getGlobalVisibleRect(Rect()) && withEffectiveVisibility( - ViewMatchers.Visibility.VISIBLE - ).matches(view) -} \ No newline at end of file diff --git a/example/android/app/src/androidTest/java/expo/modules/xmtpreactnativesdk/example/MainActivityTest.kt b/example/android/app/src/androidTest/java/expo/modules/xmtpreactnativesdk/example/MainActivityTest.kt deleted file mode 100644 index 8eaf3bf9b..000000000 --- a/example/android/app/src/androidTest/java/expo/modules/xmtpreactnativesdk/example/MainActivityTest.kt +++ /dev/null @@ -1,48 +0,0 @@ -package expo.modules.xmtpreactnativesdk.example - -import android.util.Log -import androidx.test.core.app.takeScreenshot -import androidx.test.core.graphics.writeToTestStorage -import androidx.test.espresso.Espresso.onView -import androidx.test.espresso.action.ViewActions.click -import androidx.test.espresso.matcher.ViewMatchers.withContentDescription -import androidx.test.rule.ActivityTestRule -import androidx.test.runner.AndroidJUnit4 -import expo.modules.xmtpreactnativesdk.example.EspressoViewFinder.waitForDisplayed -import junit.framework.TestCase -import org.junit.Rule -import org.junit.Test -import org.junit.rules.TestName -import org.junit.runner.RunWith - - -@RunWith(AndroidJUnit4::class) -class MainActivityTest { - - @get:Rule - val activityRule = ActivityTestRule(MainActivity::class.java) - - @get:Rule - val nameRule = TestName() - - @Test - fun testRunTests() { - waitForDisplayed(withContentDescription("Unit-tests")) { button -> - // Go to unit tests page - onView(button).perform(click()) - waitForDisplayed(withContentDescription("Test View")) { view -> - waitForDisplayed(withContentDescription("tests-complete")) { complete -> - try { - waitForDisplayed(withContentDescription("FAIL")) { failure -> - takeScreenshot().writeToTestStorage("${javaClass.simpleName}_${nameRule.methodName}") - Log.d("MainActivityTest", "Test Fail") - TestCase.fail("Test failed") - } - } catch (e: Exception) { - Log.d("MainActivityTest", "Test Succeeded $e") - } - } - } - } - } -} \ No newline at end of file diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml index 9e202d864..44b6c8508 100644 --- a/example/android/app/src/main/AndroidManifest.xml +++ b/example/android/app/src/main/AndroidManifest.xml @@ -18,7 +18,7 @@ - + diff --git a/example/android/app/src/main/res/xml/network_security_config.xml b/example/android/app/src/main/res/xml/network_security_config.xml new file mode 100644 index 000000000..995a3fb6a --- /dev/null +++ b/example/android/app/src/main/res/xml/network_security_config.xml @@ -0,0 +1,8 @@ + + + + 10.0.2.2 + localhost + + + diff --git a/example/android/build.gradle b/example/android/build.gradle index 38d541623..5e8038131 100644 --- a/example/android/build.gradle +++ b/example/android/build.gradle @@ -6,9 +6,7 @@ buildscript { minSdkVersion = Integer.parseInt(findProperty('android.minSdkVersion') ?: '23') compileSdkVersion = Integer.parseInt(findProperty('android.compileSdkVersion') ?: '33') targetSdkVersion = Integer.parseInt(findProperty('android.targetSdkVersion') ?: '33') - if (findProperty('android.kotlinVersion')) { - kotlinVersion = findProperty('android.kotlinVersion') - } + kotlinVersion = findProperty('android.kotlinVersion') ?: '1.8.10' frescoVersion = findProperty('expo.frescoVersion') ?: '2.5.0' // We use NDK 23 which has both M1 support and is the side-by-side NDK version from AGP. @@ -21,7 +19,7 @@ buildscript { dependencies { classpath('com.android.tools.build:gradle:7.4.1') classpath('com.facebook.react:react-native-gradle-plugin') - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.10" + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion" } } @@ -38,6 +36,9 @@ allprojects { google() mavenCentral() + maven { // (4) + url("$rootDir/../node_modules/detox/Detox-android") + } maven { url 'https://www.jitpack.io' } } } diff --git a/example/e2e/conversation-create-screen.test.ts b/example/e2e/conversation-create-screen.test.ts new file mode 100644 index 000000000..ef12e6c97 --- /dev/null +++ b/example/e2e/conversation-create-screen.test.ts @@ -0,0 +1,15 @@ +import { device } from 'detox' + +import { createConversation } from './utils/createConversation' +import { generateDevWallet } from './utils/generateDevWallet' + +describe('Conversation Create Screen Tests', () => { + beforeAll(async () => { + await device.launchApp({ newInstance: true }) + await generateDevWallet() + }) + + it('Create Conversation', async () => { + await createConversation() + }) +}) diff --git a/example/e2e/conversation-screen.test.ts b/example/e2e/conversation-screen.test.ts new file mode 100644 index 000000000..f26db6d7d --- /dev/null +++ b/example/e2e/conversation-screen.test.ts @@ -0,0 +1,17 @@ +import { device } from 'detox' + +import { createConversation } from './utils/createConversation' +import { generateDevWallet } from './utils/generateDevWallet' +import { sendMessage } from './utils/sendMessage' + +describe('Conversation Screen Tests', () => { + beforeAll(async () => { + await device.launchApp({ newInstance: true }) + await generateDevWallet() + await createConversation() + }) + + it('Send Message in Conversation', async () => { + await sendMessage('Hello World!') + }) +}) diff --git a/example/e2e/jest.config.js b/example/e2e/jest.config.js new file mode 100644 index 000000000..0684ae9ad --- /dev/null +++ b/example/e2e/jest.config.js @@ -0,0 +1,16 @@ +/** @type {import('@jest/types').Config.InitialOptions} */ +const { defaults: tsjPreset } = require('ts-jest/presets') + +module.exports = { + ...tsjPreset, + preset: 'react-native', + rootDir: '..', + testMatch: ['/e2e/**/*.test.ts'], + testTimeout: 120000, + maxWorkers: 1, + globalSetup: 'detox/runners/jest/globalSetup', + globalTeardown: 'detox/runners/jest/globalTeardown', + reporters: ['detox/runners/jest/reporter'], + testEnvironment: 'detox/runners/jest/testEnvironment', + verbose: true, +}; diff --git a/example/e2e/launch-screen.test.ts b/example/e2e/launch-screen.test.ts new file mode 100644 index 000000000..281854302 --- /dev/null +++ b/example/e2e/launch-screen.test.ts @@ -0,0 +1,39 @@ +import { device, expect } from 'detox' + +import { generateDevWallet } from './utils/generateDevWallet' + +describe('Launch Screen Tests', () => { + beforeAll(async () => { + await device.launchApp({ newInstance: true }) + }) + + beforeEach(async () => { + await device.reloadReactNative() + }) + + it('Tests Button', async () => { + await expect(element(by.id('run-tests-button'))).toBeVisible() + await element(by.id('run-tests-button')).tap() + await waitFor(element(by.id('test-screen'))) + .toBeVisible() + .withTimeout(15000) + }) + + it('Generate Dev Button', async () => { + await generateDevWallet() + }) + + it('Use Saved Wallet Dev Button', async () => { + await expect(element(by.id('saved-dev-button'))).toBeVisible() + await element(by.id('saved-dev-button')).tap() + await waitFor(element(by.id('home-screen'))) + .toBeVisible() + .withTimeout(15000) + }) + + it('Clear Saved Wallet Dev Button', async () => { + await expect(element(by.id('saved-clear-button'))).toBeVisible() + await element(by.id('saved-clear-button')).tap() + await expect(element(by.id('saved-dev-button'))).not.toBeVisible() + }) +}) diff --git a/example/e2e/test_screen.test.ts b/example/e2e/test_screen.test.ts new file mode 100644 index 000000000..688b8ca32 --- /dev/null +++ b/example/e2e/test_screen.test.ts @@ -0,0 +1,19 @@ +import { device, expect } from 'detox' + +describe('Test Screen Tests', () => { + beforeAll(async () => { + await device.launchApp({ newInstance: true }) + await expect(element(by.id('run-tests'))).toBeVisible() + await element(by.id('run-tests')).tap() + await waitFor(element(by.id('test-screen'))) + .toBeVisible() + .withTimeout(1500) + }) + + it('All Tests should pass', async () => { + // Wait until no elements with testID "running" are visible + await waitFor(element(by.id(/^.*running/))) + .not.toBeVisible() + .withTimeout(50000) + }) +}) diff --git a/example/e2e/utils/createConversation.ts b/example/e2e/utils/createConversation.ts new file mode 100644 index 000000000..99acfadda --- /dev/null +++ b/example/e2e/utils/createConversation.ts @@ -0,0 +1,15 @@ +import { element, waitFor } from 'detox' + +export const createConversation = async () => { + await waitFor(element(by.id('home-screen'))) + .toBeVisible() + .withTimeout(15000) + await element(by.id('new-conversation-button')).tap() + await element(by.id('to-address-input')).typeText( + '0xc93C111dcb2Df6Bb25a3F9035D5cd47bDc0381d0' + ) + await element(by.id('start-conversation-button')).tap() + await waitFor(element(by.id('conversation-screen'))) + .toBeVisible() + .withTimeout(15000) +} diff --git a/example/e2e/utils/generateDevWallet.ts b/example/e2e/utils/generateDevWallet.ts new file mode 100644 index 000000000..c8187b7ab --- /dev/null +++ b/example/e2e/utils/generateDevWallet.ts @@ -0,0 +1,9 @@ +import { expect } from 'detox' + +export const generateDevWallet = async () => { + await expect(element(by.id('generated-dev-button'))).toBeVisible() + await element(by.id('generated-dev-button')).tap() + await waitFor(element(by.id('home-screen'))) + .toBeVisible() + .withTimeout(1500) +} diff --git a/example/e2e/utils/sendMessage.ts b/example/e2e/utils/sendMessage.ts new file mode 100644 index 000000000..9d026954e --- /dev/null +++ b/example/e2e/utils/sendMessage.ts @@ -0,0 +1,13 @@ +import { element } from 'detox' + +export const sendMessage = async (message: string) => { + await waitFor(element(by.id('conversation-screen'))) + .toBeVisible() + .withTimeout(15000) + await element(by.id('message-input')).typeText(message) + await element(by.id('send-message-button')).tap() + + await waitFor(element(by.id(/^conversation-message-.*/))) + .toBeVisible() + .withTimeout(15000) +} diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index 8027c2a06..6df148c5a 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -757,4 +757,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 95d6ace79946933ecf80684613842ee553dd76a2 -COCOAPODS: 1.14.2 +COCOAPODS: 1.14.3 diff --git a/example/package.json b/example/package.json index ed42a73b2..bb0716eb8 100644 --- a/example/package.json +++ b/example/package.json @@ -5,7 +5,8 @@ "start": "expo start --dev-client", "android": "expo run:android", "ios": "expo run:ios", - "web": "expo start --web" + "web": "expo start --web", + "postinstall": "patch-package" }, "dependencies": { "@react-native-async-storage/async-storage": "^1.21.0", @@ -24,6 +25,8 @@ "expo-status-bar": "~1.4.4", "moment": "^2.29.4", "node-libs-browser": "^2.2.1", + "patch-package": "^8.0.0", + "postinstall-postinstall": "^2.1.0", "react": "18.2.0", "react-native": "0.71.14", "react-native-blob-util": "^0.19.0", @@ -48,16 +51,20 @@ }, "devDependencies": { "@babel/core": "^7.20.0", - "@types/node": "^18.16.3", + "@types/jest": "^29.5.11", + "@types/node": "^18.19.3", "@types/react": "18.2.0", "@types/react-native": "0.71.8", "@types/text-encoding": "^0.0.39", "@typescript-eslint/eslint-plugin": "^6.13.1", "@typescript-eslint/parser": "^6.13.1", + "detox": "^20.14.3", "eslint": "^8.54.0", "eslint-plugin-prettier": "^5.0.1", "eslint-plugin-react": "^7.33.2", + "jest": "^29.7.0", "prettier": "^3.1.0", + "ts-jest": "^29.1.1", "typescript": "^4.9.4" }, "overrides": { diff --git a/example/patches/react-native-encrypted-storage+4.0.3.patch b/example/patches/react-native-encrypted-storage+4.0.3.patch new file mode 100644 index 000000000..ae6ba7823 --- /dev/null +++ b/example/patches/react-native-encrypted-storage+4.0.3.patch @@ -0,0 +1,13 @@ +diff --git a/node_modules/react-native-encrypted-storage/android/build.gradle b/node_modules/react-native-encrypted-storage/android/build.gradle +index b343dc8..96cede0 100644 +--- a/node_modules/react-native-encrypted-storage/android/build.gradle ++++ b/node_modules/react-native-encrypted-storage/android/build.gradle +@@ -124,7 +124,7 @@ dependencies { + + testImplementation 'junit:junit:4.13.1' + +- androidTestImplementation 'androidx.test.ext:junit:1.1.2' ++ androidTestImplementation 'androidx.test.ext:junit:1.1.5' + //noinspection GradleDependency + androidTestImplementation 'org.mockito:mockito-android:3.4.6' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' diff --git a/example/src/ConversationCreateScreen.tsx b/example/src/ConversationCreateScreen.tsx index 9f0676dc9..ddc016964 100644 --- a/example/src/ConversationCreateScreen.tsx +++ b/example/src/ConversationCreateScreen.tsx @@ -28,7 +28,7 @@ export default function ConversationCreateScreen({ } return ( <> - + setAlert(err.message)) .finally(() => setCreating(false)) }} + testID="start-conversation-button" disabled={isCreating || !toAddress} /> {alert && ( diff --git a/example/src/ConversationScreen.tsx b/example/src/ConversationScreen.tsx index 93ce3f442..4921eb89f 100644 --- a/example/src/ConversationScreen.tsx +++ b/example/src/ConversationScreen.tsx @@ -123,7 +123,11 @@ export default function ConversationScreen({ ) return ( - +