From f611a435a3b3bcd865e26bcd96b660e1f8069a11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20V=C3=A4limaa?= Date: Tue, 17 Dec 2024 15:16:18 +0800 Subject: [PATCH] test: add integration tests (#368) Co-authored-by: Joonas Kerttula --- .github/workflows/ci.yml | 15 +- CONTRIBUTING.md | 36 + android/build.gradle | 4 +- .../react/navsdk/AndroidAutoBaseScreen.java | 2 + .../google/android/react/navsdk/Package.java | 2 + example/.detoxrc.js | 103 +++ example/android/app/build.gradle | 19 +- .../java/com/sampleapp/DetoxTest.java | 44 + .../android/app/src/main/AndroidManifest.xml | 3 +- .../main/res/xml/network_security_config.xml | 23 + example/android/build.gradle | 13 +- example/e2e/event_listener.test.js | 50 ++ example/e2e/jest.config.js | 33 + example/e2e/map.test.js | 59 ++ example/e2e/navigation.test.js | 73 ++ example/e2e/shared.js | 62 ++ example/package.json | 19 +- example/src/App.tsx | 21 +- .../src/screens/IntegrationTestsScreen.tsx | 340 ++++++++ .../integration_tests/integration_test.ts | 655 +++++++++++++++ .../src/screens/integration_tests/utils.ts | 26 + lefthook.yml | 2 - package.json | 13 - yarn.lock | 789 +++++++++++++++++- 24 files changed, 2337 insertions(+), 69 deletions(-) create mode 100644 example/.detoxrc.js create mode 100644 example/android/app/src/androidTest/java/com/sampleapp/DetoxTest.java create mode 100644 example/android/app/src/main/res/xml/network_security_config.xml create mode 100644 example/e2e/event_listener.test.js create mode 100644 example/e2e/jest.config.js create mode 100644 example/e2e/map.test.js create mode 100644 example/e2e/navigation.test.js create mode 100644 example/e2e/shared.js create mode 100644 example/src/screens/IntegrationTestsScreen.tsx create mode 100644 example/src/screens/integration_tests/integration_test.ts create mode 100644 example/src/screens/integration_tests/utils.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e677320..71493de 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -88,14 +88,17 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 30 steps: - - name: Checkout - uses: actions/checkout@v4 + - name: Placeholder Test Step + run: echo "No tests to run yet" && true + # TODO: build test orchestration for Android and iOS and run detox tests on both platforms. + # - name: Checkout + # uses: actions/checkout@v4 - - name: Setup - uses: ./.github/actions/setup + # - name: Setup + # uses: ./.github/actions/setup - - name: Run unit tests - run: yarn test --maxWorkers=2 --coverage + # - name: Run unit tests + # run: yarn test --maxWorkers=2 --coverage build-library: runs-on: ubuntu-latest diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6c3d2b9..d8e1128 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -109,3 +109,39 @@ Please peruse the [Typescript style guide](https://google.github.io/styleguide/tsguide.html), [Java style guide](https://google.github.io/styleguide/javaguide.html), and [Objective-C style guide](https://google.github.io/styleguide/objcguide.html) before working on anything non-trivial. These guidelines are intended to keep the code consistent and avoid common pitfalls. + +## 6. Running tests + +Google Maps React Native Navigation package has integration tests. + +### Integration tests + +Integration tests are responsible for ensuring that the plugin works against the native Navigation SDK for both Android and iOS platforms. Detox along with example application is used for the integration tests. "Integration tests must accompany the implementation of all new features. +To run the test you must first install and setup detox. Please follow the guide here: +https://wix.github.io/Detox/docs/introduction/environment-setup + +Build the tests using detox-cli in the example folder: + +iOS: +```bash +detox build --configuration ios.sim.release +``` + +Android: +```bash +detox build --configuration android.emu.release +``` + +Google Maps React Native Navigation SDK integration tests can be run with the following command: + +iOS +```bash +yarn run example test:ios-release +``` + +Android: +```bash +yarn run example detox:test:android-release +``` + +When adding new tests, you need to first add the detox part in the [e2e folder](./example/e2e) and then the actual logical part of the test in the [integration tests page](./example/src/screens/IntegrationTestsScreen.tsx) of the example app. diff --git a/android/build.gradle b/android/build.gradle index 15b2024..608615b 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -47,7 +47,7 @@ android { } defaultConfig { - minSdkVersion 23 + minSdkVersion 24 targetSdkVersion 34 versionCode 1 // get version name from package.json version @@ -56,7 +56,7 @@ android { buildTypes { release { - minifyEnabled false + minifyEnabled true } } diff --git a/android/src/main/java/com/google/android/react/navsdk/AndroidAutoBaseScreen.java b/android/src/main/java/com/google/android/react/navsdk/AndroidAutoBaseScreen.java index c7ac8aa..f541c2a 100644 --- a/android/src/main/java/com/google/android/react/navsdk/AndroidAutoBaseScreen.java +++ b/android/src/main/java/com/google/android/react/navsdk/AndroidAutoBaseScreen.java @@ -34,6 +34,7 @@ import androidx.lifecycle.Lifecycle; import androidx.lifecycle.LifecycleObserver; import androidx.lifecycle.LifecycleOwner; +import com.facebook.proguard.annotations.DoNotStrip; import com.facebook.react.bridge.ReadableMap; import com.google.android.gms.maps.CameraUpdate; import com.google.android.gms.maps.CameraUpdateFactory; @@ -48,6 +49,7 @@ // For more information on using Android Auto with the Google Navigation SDK, refer to the official // documentation: // https://developers.google.com/maps/documentation/navigation/android-sdk/android-auto +@DoNotStrip public abstract class AndroidAutoBaseScreen extends Screen implements SurfaceCallback, INavigationViewController { private static final String VIRTUAL_DISPLAY_NAME = "AndroidAutoNavScreen"; diff --git a/android/src/main/java/com/google/android/react/navsdk/Package.java b/android/src/main/java/com/google/android/react/navsdk/Package.java index a345940..d8478a5 100644 --- a/android/src/main/java/com/google/android/react/navsdk/Package.java +++ b/android/src/main/java/com/google/android/react/navsdk/Package.java @@ -13,6 +13,7 @@ */ package com.google.android.react.navsdk; +import com.facebook.proguard.annotations.DoNotStrip; import com.facebook.react.ReactPackage; import com.facebook.react.bridge.NativeModule; import com.facebook.react.bridge.ReactApplicationContext; @@ -21,6 +22,7 @@ import java.util.Arrays; import java.util.List; +@DoNotStrip public class Package implements ReactPackage { private NavViewManager mNavViewManager; diff --git a/example/.detoxrc.js b/example/.detoxrc.js new file mode 100644 index 0000000..34cb0d9 --- /dev/null +++ b/example/.detoxrc.js @@ -0,0 +1,103 @@ +/** + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** @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/SampleApp.app', + build: + 'xcodebuild -workspace ios/SampleApp.xcworkspace -scheme SampleApp -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build', + }, + 'ios.release': { + type: 'ios.app', + binaryPath: + 'ios/build/Build/Products/Release-iphonesimulator/SampleApp.app', + build: + 'xcodebuild -workspace ios/SampleApp.xcworkspace -scheme SampleApp -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 16 Pro', + }, + }, + attached: { + type: 'android.attached', + device: { + adbName: '.*', + }, + }, + emulator: { + type: 'android.emulator', + device: { + avdName: 'Pixel_8_API_35', + }, + }, + }, + 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/android/app/build.gradle b/example/android/app/build.gradle index 2c48cd8..fba02cb 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -74,7 +74,14 @@ react { /** * Set this to true to Run Proguard on Release builds to minify the Java bytecode. */ -def enableProguardInReleaseBuilds = false +def enableProguardInReleaseBuilds = true + + +/** + * Set this to true to enable desugaring if minSdkVersion is set to 34 or below. + * Note that this currently affects Detox testing with release builds and therefore is not enabled by default. + */ +def enableDesugaring = false /** * The preferred build flavor of JavaScriptCore (JSC) @@ -101,6 +108,8 @@ android { targetSdkVersion rootProject.ext.targetSdkVersion versionCode 1 versionName "1.0" + testBuildType System.getProperty('testBuildType', 'debug') + testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' } signingConfigs { debug { @@ -119,17 +128,19 @@ android { // see https://reactnative.dev/docs/signed-apk-android. signingConfig signingConfigs.debug minifyEnabled enableProguardInReleaseBuilds - proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" + proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro", "${rootProject.projectDir}/../node_modules/detox/android/detox/proguard-rules-app.pro" } } compileOptions { - coreLibraryDesugaringEnabled true + coreLibraryDesugaringEnabled enableDesugaring sourceCompatibility JavaVersion.VERSION_17 targetCompatibility JavaVersion.VERSION_17 } } 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") @@ -147,7 +158,7 @@ dependencies { implementation 'com.google.android.libraries.navigation:navigation:6.0.0' // Desugar Java 8+ APIs - coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs_nio:2.0.4' + coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.3' } secrets { diff --git a/example/android/app/src/androidTest/java/com/sampleapp/DetoxTest.java b/example/android/app/src/androidTest/java/com/sampleapp/DetoxTest.java new file mode 100644 index 0000000..145b641 --- /dev/null +++ b/example/android/app/src/androidTest/java/com/sampleapp/DetoxTest.java @@ -0,0 +1,44 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sampleapp; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.LargeTest; +import androidx.test.rule.ActivityTestRule; +import com.wix.detox.Detox; +import com.wix.detox.config.DetoxConfig; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +@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/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml index 5d80e89..55a8cfe 100644 --- a/example/android/app/src/main/AndroidManifest.xml +++ b/example/android/app/src/main/AndroidManifest.xml @@ -33,7 +33,8 @@ android:roundIcon="@mipmap/ic_launcher_round" android:allowBackup="false" android:theme="@style/AppTheme" - android:supportsRtl="true"> + android:supportsRtl="true" + android:networkSecurityConfig="@xml/network_security_config"> + + + + + 10.0.2.2 + localhost + + \ No newline at end of file diff --git a/example/android/build.gradle b/example/android/build.gradle index c7fde43..ce88785 100644 --- a/example/android/build.gradle +++ b/example/android/build.gradle @@ -15,7 +15,10 @@ buildscript { ext { buildToolsVersion = "35.0.0" - minSdkVersion = 24 + // If minSDKVersion is set below 34, make sure to enable desugaring + // to use certain Java 8+ APIs on lower API levels + // by setting enableDesugaring true in app/build.gradle file. + minSdkVersion = 34 compileSdkVersion = 35 targetSdkVersion = 34 ndkVersion = "26.1.10909125" @@ -31,4 +34,12 @@ buildscript { } } +allprojects { + repositories { + maven { + url("$rootDir/../node_modules/detox/Detox-android") + } + } + } + apply plugin: "com.facebook.react.rootproject" diff --git a/example/e2e/event_listener.test.js b/example/e2e/event_listener.test.js new file mode 100644 index 0000000..63f6c49 --- /dev/null +++ b/example/e2e/event_listener.test.js @@ -0,0 +1,50 @@ +/** + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + initializeIntegrationTestsPage, + agreeToTermsAndConditions, + selectTestByName, + waitForTestToFinish, + expectSuccess, +} from './shared.js'; + +describe('Even listener tests', () => { + beforeEach(async () => { + await initializeIntegrationTestsPage(); + }); + + it('T01 - test onRemainingTimeOrDistanceChanged event listener', async () => { + await selectTestByName('testOnRemainingTimeOrDistanceChanged'); + await agreeToTermsAndConditions(); + await waitForTestToFinish(); + await expectSuccess(); + }); + + it('T02 - test onArrival event listener', async () => { + await selectTestByName('testOnArrival'); + await agreeToTermsAndConditions(); + await waitForTestToFinish(); + await expectSuccess(); + }); + + it('T03 - test onRouteChanged event listener', async () => { + await selectTestByName('testOnRouteChanged'); + await agreeToTermsAndConditions(); + await waitForTestToFinish(); + await expectSuccess(); + }); +}); diff --git a/example/e2e/jest.config.js b/example/e2e/jest.config.js new file mode 100644 index 0000000..845d237 --- /dev/null +++ b/example/e2e/jest.config.js @@ -0,0 +1,33 @@ +/** + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** @type {import('@jest/types').Config.InitialOptions} */ +module.exports = { + rootDir: '..', + testMatch: ['/e2e/**/*.test.js'], + 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, + globals: { + detox: { + configuration: process.env.DETOX_CONFIGURATION || 'ios.sim.debug', + }, + }, +}; diff --git a/example/e2e/map.test.js b/example/e2e/map.test.js new file mode 100644 index 0000000..036219f --- /dev/null +++ b/example/e2e/map.test.js @@ -0,0 +1,59 @@ +/** + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + initializeIntegrationTestsPage, + selectTestByName, + waitForTestToFinish, + expectSuccess, +} from './shared.js'; +import { element, by, log } from 'detox'; + +describe('Map view tests', () => { + beforeEach(async () => { + await initializeIntegrationTestsPage(); + }); + + it('T01 - initialize map and test default values', async () => { + await selectTestByName('testMapInitialization'); + await waitForTestToFinish(); + const failureMessageLabel = element(by.id('failure_message_label')); + const attributes = await failureMessageLabel.getAttributes(); + log.error(attributes.text); + await expect(element(by.id('failure_message_label'))).toHaveText(''); + await expectSuccess(); + }); + + it('T02 - initialize map and test move camera', async () => { + await selectTestByName('testMoveCamera'); + await waitForTestToFinish(); + const failureMessageLabel = element(by.id('failure_message_label')); + const attributes = await failureMessageLabel.getAttributes(); + log.error(attributes.text); + await expect(element(by.id('failure_message_label'))).toHaveText(''); + await expectSuccess(); + }); + + it('T03 - initialize map and test camera tilt bearing zoom', async () => { + await selectTestByName('testTiltZoomBearingCamera'); + await waitForTestToFinish(); + const failureMessageLabel = element(by.id('failure_message_label')); + const attributes = await failureMessageLabel.getAttributes(); + log.error(attributes.text); + await expect(element(by.id('failure_message_label'))).toHaveText(''); + await expectSuccess(); + }); +}); diff --git a/example/e2e/navigation.test.js b/example/e2e/navigation.test.js new file mode 100644 index 0000000..f63011e --- /dev/null +++ b/example/e2e/navigation.test.js @@ -0,0 +1,73 @@ +/** + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + initializeIntegrationTestsPage, + agreeToTermsAndConditions, + selectTestByName, + waitForTestToFinish, + expectSuccess, +} from './shared.js'; +import { element, by, log } from 'detox'; + +describe('Navigation tests', () => { + beforeEach(async () => { + await initializeIntegrationTestsPage(); + }); + + it('T01 - initialize navigation controller and test terms and conditions (TOS) dialog acceptance', async () => { + await selectTestByName('testNavigationSessionInitialization'); + await agreeToTermsAndConditions(); + await waitForTestToFinish(); + await expectSuccess(); + }); + + it('T02 - initialize navigation controller and navigate to single destination', async () => { + await selectTestByName('testNavigationToSingleDestination'); + await agreeToTermsAndConditions(); + await waitForTestToFinish(); + await expectSuccess(); + }); + + it('T03 - initialize navigation controller and navigate to multiple destinations', async () => { + await selectTestByName('testNavigationToMultipleDestination'); + await agreeToTermsAndConditions(); + await waitForTestToFinish(); + await expectSuccess(); + }); + + it('T04 - initialize navigation controller and test route segments', async () => { + await selectTestByName('testRouteSegments'); + await agreeToTermsAndConditions(); + const failureMessageLabel = element(by.id('failure_message_label')); + const attributes = await failureMessageLabel.getAttributes(); + log.error(attributes.text); + await expect(element(by.id('failure_message_label'))).toHaveText(''); + await waitForTestToFinish(); + await expectSuccess(); + }); + + it('T05 - initialize navigation controller and test remaining time and distance', async () => { + await selectTestByName('testGetCurrentTimeAndDistance'); + await agreeToTermsAndConditions(); + const failureMessageLabel = element(by.id('failure_message_label')); + const attributes = await failureMessageLabel.getAttributes(); + log.error(attributes.text); + await expect(element(by.id('failure_message_label'))).toHaveText(''); + await waitForTestToFinish(); + await expectSuccess(); + }); +}); diff --git a/example/e2e/shared.js b/example/e2e/shared.js new file mode 100644 index 0000000..702365c --- /dev/null +++ b/example/e2e/shared.js @@ -0,0 +1,62 @@ +/** + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { device, element, waitFor, by, expect } from 'detox'; + +export const agreeToTermsAndConditions = async () => { + if (device.getPlatform() === 'ios') { + await waitFor(element(by.text('OK'))) + .toBeVisible() + .withTimeout(10000); + await element(by.text('OK')).tap(); + } else if (device.getPlatform() === 'android') { + await waitFor(element(by.text('GOT IT'))) + .toBeVisible() + .withTimeout(10000); + await element(by.text('GOT IT')).tap(); + } +}; + +export const waitForStepNumber = async number => { + await waitFor(element(by.id('test_status_label'))) + .toHaveText(`Test status: Step #${number}`) + .withTimeout(10000); +}; + +export const waitForTestToFinish = async (timeInMs = 60000) => { + await waitFor(element(by.id('test_status_label'))) + .toHaveText(`Test status: Finished`) + .withTimeout(timeInMs); +}; + +export const expectSuccess = async () => { + await expect(element(by.id('test_result_label'))).toHaveText( + 'Test result: Success' + ); +}; + +export const initializeIntegrationTestsPage = async () => { + await device.launchApp({ + delete: true, + permissions: { location: 'always' }, + }); + await element(by.id('integration_tests_button')).tap(); +}; + +export const selectTestByName = async name => { + await element(by.id('tests_menu_button')).tap(); + await element(by.id(name)).tap(); +}; diff --git a/example/package.json b/example/package.json index 73e676f..c6fec05 100644 --- a/example/package.json +++ b/example/package.json @@ -5,11 +5,21 @@ "scripts": { "start": "react-native start", "android": "react-native run-android", + "android-release": "react-native run-android --mode release", "ios": "react-native run-ios", + "ios-release": "react-native run-ios --mode release", "lint": "eslint .", "test": "jest", "build:android": "cd android && ./gradlew assembleDebug --no-daemon --console=plain -PreactNativeArchitectures=arm64-v8a", - "build:ios": "cd ios && xcodebuild -workspace SampleApp.xcworkspace -scheme SampleApp -configuration Debug -sdk iphonesimulator CC=clang CPLUSPLUS=clang++ LD=clang LDPLUSPLUS=clang++ GCC_OPTIMIZATION_LEVEL=0 GCC_PRECOMPILE_PREFIX_HEADER=YES ASSETCATALOG_COMPILER_OPTIMIZATION=time DEBUG_INFORMATION_FORMAT=dwarf COMPILER_INDEX_STORE_ENABLE=NO" + "build:ios": "cd ios && xcodebuild -workspace SampleApp.xcworkspace -scheme SampleApp -configuration Debug -sdk iphonesimulator CC=clang CPLUSPLUS=clang++ LD=clang LDPLUSPLUS=clang++ GCC_OPTIMIZATION_LEVEL=0 GCC_PRECOMPILE_PREFIX_HEADER=YES ASSETCATALOG_COMPILER_OPTIMIZATION=time DEBUG_INFORMATION_FORMAT=dwarf COMPILER_INDEX_STORE_ENABLE=NO", + "detox:build:ios-debug": "detox build --configuration ios.sim.debug", + "detox:build:ios-release": "detox build --configuration ios.sim.release", + "detox:build:android-debug": "detox build --configuration android.emu.debug", + "detox:build:android-release": "detox build --configuration android.emu.release", + "detox:test:ios-debug": "detox test --configuration ios.sim.debug", + "detox:test:ios-release": "detox test --configuration ios.sim.release", + "detox:test:android-debug": "detox test --configuration android.emu.debug", + "detox:test:android-release": "detox test --configuration android.emu.release" }, "dependencies": { "@react-navigation/native": "^6.1.18", @@ -33,7 +43,12 @@ "@react-native/babel-preset": "0.76.2", "@react-native/metro-config": "0.76.2", "@react-native/typescript-config": "0.76.2", - "babel-plugin-module-resolver": "^5.0.0" + "@types/jest": "^29.5.14", + "@types/node": "^22.9.0", + "babel-plugin-module-resolver": "^5.0.0", + "detox": "^20.27.6", + "jest": "^29.7.0", + "ts-jest": "^29.2.5" }, "engines": { "node": ">=18" diff --git a/example/src/App.tsx b/example/src/App.tsx index aace977..f6e9e99 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -30,8 +30,14 @@ import { TaskRemovedBehavior, type TermsAndConditionsDialogOptions, } from '@googlemaps/react-native-navigation-sdk'; +import IntegrationTestsScreen from './screens/IntegrationTestsScreen'; -export type ScreenNames = ['Home', 'Navigation', 'Multiple maps']; +export type ScreenNames = [ + 'Home', + 'Navigation', + 'Multiple maps', + 'Integration tests', +]; export type RootStackParamList = Record; export type StackNavigation = NavigationProp; @@ -41,7 +47,7 @@ const HomeScreen = () => { const isFocused = useIsFocused(); return ( - +