diff --git a/android/app/build.gradle b/android/app/build.gradle index a81f3fba720..6958bfc8488 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -186,6 +186,7 @@ repositories { maven { url 'https://maven.google.com' } + maven { url 'https://jitpack.io' } } dependencies { @@ -203,8 +204,11 @@ dependencies { implementation 'com.google.android.material:material:1.12.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' implementation "com.google.firebase:firebase-messaging:$firebaseVersion" - androidTestImplementation('com.wix:detox:+') + androidTestImplementation 'androidx.test:core:1.6.0' + androidTestImplementation 'androidx.test:runner:1.5.2' + androidTestImplementation 'com.wix:detox:20.26.2' + implementation project(':reactnativenotifications') implementation project(':watermelondb-jsi') @@ -217,6 +221,7 @@ dependencies { configurations.all { resolutionStrategy { + force 'androidx.test:core:1.6.0' eachDependency { DependencyResolveDetails details -> if (details.requested.name == 'play-services-base') { details.useTarget group: details.requested.group, name: details.requested.name, version: '18.2.0' diff --git a/android/build.gradle b/android/build.gradle index e1b16d9d0d1..a7c12891fe7 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -34,7 +34,7 @@ buildscript { allprojects { repositories { maven { - url "$rootDir/../node_modules/detox/Detox-android" + url "$rootDir/../detox/node_modules/detox/Detox-android" } } } diff --git a/android/gradle.properties b/android/gradle.properties index 5ef02eb480c..6e14a186d9e 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -10,7 +10,7 @@ # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. # Default value: -Xmx512m -XX:MaxMetaspaceSize=256m -org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m +org.gradle.jvmargs=-Xmx4096m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit diff --git a/detox/README.md b/detox/README.md new file mode 100644 index 00000000000..c30cc25d8a0 --- /dev/null +++ b/detox/README.md @@ -0,0 +1,35 @@ +# How to Run Detox Tests + +## Android + +### Install Dependencies + +From the root directory, run the following command to install the necessary dependencies: + +```sh +npm install +``` + +### Inject Detox Settings + +To inject the Detox settings into your project, navigate to the `detox` directory and run the following command: + +```sh +npm run inject-detox-settings +``` + +### Update `minSdkVersion` for `react-native-image-picker` + +On macOS machines, update the `minSdkVersion` of `react-native-image-picker` to 23 by running the following command from the root directory: + +```sh +sed -i '' 's/minSdkVersion 21/minSdkVersion 23/' ./node_modules/react-native-image-picker/android/build.gradle +``` + +### Build detox android app + +From the `detox` folder run: + +``` +npm run e2e:android-build +``` \ No newline at end of file diff --git a/detox/e2e/support/ui/screen/login.ts b/detox/e2e/support/ui/screen/login.ts index fd663837d29..31cc26a9860 100644 --- a/detox/e2e/support/ui/screen/login.ts +++ b/detox/e2e/support/ui/screen/login.ts @@ -39,7 +39,7 @@ class LoginScreen { signinButtonDisabled = element(by.id(this.testID.signinButtonDisabled)); toBeVisible = async () => { - await wait(timeouts.ONE_SEC); + await wait(timeouts.FOUR_SEC); await waitFor(this.loginScreen).toExist().withTimeout(timeouts.TEN_SEC); await waitFor(this.usernameInput).toBeVisible().withTimeout(timeouts.TEN_SEC); @@ -60,18 +60,25 @@ class LoginScreen { login = async (user: any = {}) => { await this.toBeVisible(); - await this.usernameInput.typeText(`${user.newUser.email}\n`); - await this.passwordInput.typeText(`${user.newUser.password}\n`); + + await this.usernameInput.tap({x: 150, y: 10}); + await this.usernameInput.replaceText(user.newUser.email); + await this.passwordInput.tap(); + await this.passwordInput.replaceText(user.newUser.password); + await this.signinButton.tap(); await wait(timeouts.FOUR_SEC); }; loginAsAdmin = async (user: any = {}) => { await this.toBeVisible(); - await this.usernameInput.typeText(user.username); - await this.passwordInput.typeText(`${user.password}\n`); + await this.usernameInput.tap({x: 150, y: 10}); - await wait(timeouts.ONE_SEC); + await this.usernameInput.replaceText(user.username); + await this.passwordInput.tap(); + await this.passwordInput.replaceText(user.password); + await this.signinButton.tap(); + await wait(timeouts.FOUR_SEC); }; } diff --git a/detox/e2e/support/ui/screen/server.ts b/detox/e2e/support/ui/screen/server.ts index 71e5f9cd74a..6a97ff9c2c3 100644 --- a/detox/e2e/support/ui/screen/server.ts +++ b/detox/e2e/support/ui/screen/server.ts @@ -49,10 +49,9 @@ class ServerScreen { connectToServer = async (serverUrl: string, serverDisplayName: string) => { await this.toBeVisible(); await this.serverUrlInput.replaceText(serverUrl); - await this.serverUrlInput.tapReturnKey(); await this.serverDisplayNameInput.replaceText(serverDisplayName); if (isAndroid()) { - await this.serverDisplayNameInput.tapReturnKey(); + await this.tapConnectButton(); } if (isIos()) { await this.tapConnectButton(); @@ -67,7 +66,7 @@ class ServerScreen { } } } - await waitFor(this.usernameInput).toExist().withTimeout(timeouts.ONE_SEC); + await waitFor(this.usernameInput).toExist().withTimeout(timeouts.FOUR_SEC); }; close = async () => { diff --git a/detox/inject-detox-settings.js b/detox/inject-detox-settings.js new file mode 100644 index 00000000000..8ba2db574dc --- /dev/null +++ b/detox/inject-detox-settings.js @@ -0,0 +1,72 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. +/* eslint-disable no-console */ +const fs = require('fs'); +const path = require('path'); + +// Paths to files +const androidManifestPath = path.resolve( + __dirname, + '../android/app/src/debug/AndroidManifest.xml', +); +const settingsGradlePath = path.resolve(__dirname, '../android/settings.gradle'); + +// Detox code to add to settings.gradle +const detoxSettings = ` +include ':detox' +project(':detox').projectDir = new File(rootProject.projectDir, '../detox/node_modules/detox/android') +`; + +// Updated AndroidManifest.xml content +const updatedManifest = ` + + + + + + + +`; + +// Update AndroidManifest.xml +function updateAndroidManifest() { + try { + fs.writeFileSync(androidManifestPath, updatedManifest, 'utf-8'); + console.log('AndroidManifest.xml updated successfully.'); + } catch (err) { + console.error(`Failed to update AndroidManifest.xml: ${err.message}`); + } +} + +// Update settings.gradle +function updateSettingsGradle() { + try { + const content = fs.readFileSync(settingsGradlePath, 'utf-8'); + if (content.includes("include ':detox'")) { + console.log('Detox settings already present in settings.gradle.'); + return; + } + fs.writeFileSync(settingsGradlePath, content + detoxSettings, 'utf-8'); + console.log('settings.gradle updated successfully.'); + } catch (err) { + console.error(`Failed to update settings.gradle: ${err.message}`); + } +} + +// Run updates +updateAndroidManifest(); +updateSettingsGradle(); diff --git a/detox/package.json b/detox/package.json index cc81cdfba02..b048be07068 100644 --- a/detox/package.json +++ b/detox/package.json @@ -42,6 +42,7 @@ "xml2js": "0.6.2" }, "scripts": { + "e2e:android-inject-settings": "node inject-detox-settings.js", "e2e:android-create-emulator": "./create_android_emulator.sh", "e2e:android-build": "detox build -c android.emu.debug", "e2e:android-test": "detox test -c android.emu.debug",