1--- 2title: Running E2E tests on EAS Build 3sidebar_title: Running E2E tests 4--- 5 6import { Collapsible } from '~/ui/components/Collapsible'; 7import { Terminal, DiffBlock } from '~/ui/components/Snippet'; 8import ImageSpotlight from '~/components/plugins/ImageSpotlight'; 9 10> **Warning** EAS Build support for E2E testing is in a _very early_ state. The intention of this guide is to explain how you can run E2E tests on the service today, 11> without all of the affordances that we plan to build in the future. This guide will evolve over time as support for testing workflows in EAS Build improves. 12 13With EAS Build, you can build a workflow for running E2E tests for your application. In this guide, you will learn how to use one of the most popular libraries ([Detox](https://wix.github.io/Detox)) to do that. 14 15This guide explains how to run E2E tests with Detox in a bare workflow project. You can use [`@config-plugins/detox`](https://github.com/expo/config-plugins/tree/main/packages/detox) for a managed project, but you may need to adjust some of the instructions in this guide in order to do so. 16 17## Running iOS tests 18 19### 1. Initialize a new Bare Workflow project 20 21Let's start by initializing a new Expo project, installing and configuring `@config-plugins/detox`, and running `npx expo prebuild` to generate the native projects. 22 23Start with the following commands: 24 25<Terminal 26 cmd={[ 27 '# Initialize a new project', 28 '$ npx create-expo-app eas-tests-example', 29 '# cd into the project directory', 30 '$ cd eas-tests-example', 31 '# Install @config-plugins/detox', 32 '$ npm install --save-dev @config-plugins/detox', 33 ]} 34/> 35 36Now, open **app.json** and add the `@config-plugins/detox` plugin to your `plugins` list (this must be done before prebuilding). This will automatically configure the Android native code to support Detox. 37 38```json app.json 39{ 40 "expo": { 41 // ... 42 "plugins": ["@config-plugins/detox"] 43 } 44} 45``` 46 47Run prebuild to generate the native projects: 48 49<Terminal cmd={['$ npx expo prebuild']} /> 50 51### 2. Make home screen interactive 52 53The first step to writing E2E tests is to have something to test - we have an empty app, so let's make our app interactive. We can add a button and display some new text when it's pressed. 54Later, we're going to write a test that's going to tap the button and check whether the text has been displayed. 55 56<div style={{ display: 'flex', justifyContent: 'center' }}> 57 <img src="/static/images/eas-build/tests/01-click-me.png" style={{ maxWidth: '45%' }} /> 58 <img src="/static/images/eas-build/tests/02-hi.png" style={{ maxWidth: '45%' }} /> 59</div> 60 61<Collapsible summary=" See the source code"> 62 63```js App.js 64import { StatusBar } from 'expo-status-bar'; 65import { useState } from 'react'; 66import { Pressable, StyleSheet, Text, View } from 'react-native'; 67 68export default function App() { 69 const [clicked, setClicked] = useState(false); 70 71 return ( 72 <View style={styles.container}> 73 {!clicked && ( 74 <Pressable testID="click-me-button" style={styles.button} onPress={() => setClicked(true)}> 75 <Text style={styles.text}>Click me</Text> 76 </Pressable> 77 )} 78 {clicked && <Text style={styles.hi}>Hi!</Text>} 79 <StatusBar style="auto" /> 80 </View> 81 ); 82} 83 84const styles = StyleSheet.create({ 85 container: { 86 flex: 1, 87 backgroundColor: '#fff', 88 alignItems: 'center', 89 justifyContent: 'center', 90 }, 91 hi: { 92 fontSize: 30, 93 color: '#4630EB', 94 }, 95 button: { 96 alignItems: 'center', 97 justifyContent: 'center', 98 paddingVertical: 12, 99 paddingHorizontal: 32, 100 borderRadius: 4, 101 elevation: 3, 102 backgroundColor: '#4630EB', 103 }, 104 text: { 105 fontSize: 16, 106 lineHeight: 21, 107 fontWeight: 'bold', 108 letterSpacing: 0.25, 109 color: 'white', 110 }, 111}); 112``` 113 114</Collapsible> 115 116### 3. Set up Detox 117 118#### Install dependencies 119 120Let's add two development dependencies to the project - `jest` and `detox`. `jest` (or `mocha`) is required because `detox` does not have its own test-runner. 121 122<Terminal 123 cmd={[ 124 '# Install jest & detox', 125 '$ npm install --save-dev jest detox', 126 '# Create Detox configuration files', 127 '$ npx detox init -r jest', 128 ]} 129/> 130 131> See the official Detox docs at https://wix.github.io/Detox/docs/introduction/getting-started/ and https://wix.github.io/Detox/docs/guide/jest to learn about any potential updates to this process. 132 133#### Configure Detox 134 135Detox requires you to specify both the build command and path to the binary produced by it. Technically, the build command is not necessary when running tests on EAS Build, but allows you to run tests locally (for example, using `npx detox build --configuration ios.release`). 136 137Edit **.detoxrc.json** and replace the configuration with: 138 139```json .detoxrc.json 140{ 141 "testRunner": "jest", 142 "runnerConfig": "e2e/config.json", 143 "skipLegacyWorkersInjection": true, 144 "apps": { 145 "ios.release": { 146 "type": "ios.app", 147 /* @info This is project-specific, replace eastestsexample with correct value */ 148 "build": "xcodebuild -workspace ios/eastestsexample.xcworkspace -scheme eastestsexample -configuration Release -sdk iphonesimulator -arch x86_64 -derivedDataPath ios/build", 149 "binaryPath": "ios/build/Build/Products/Release-iphonesimulator/eastestsexample.app" 150 /* @end */ 151 }, 152 "android.release": { 153 "type": "android.apk", 154 /* @info This is project-specific, replace eastestsexample with correct value */ 155 "build": "cd android && ./gradlew :app:assembleRelease :app:assembleAndroidTest -DtestBuildType=release && cd ..", 156 "binaryPath": "android/app/build/outputs/apk/release/app-release.apk" 157 /* @end */ 158 } 159 }, 160 "devices": { 161 "simulator": { 162 "type": "ios.simulator", 163 "device": { 164 "type": "iPhone 14" 165 } 166 }, 167 "emulator": { 168 "type": "android.emulator", 169 "device": { 170 "avdName": "pixel_4" 171 } 172 } 173 }, 174 "configurations": { 175 "ios.release": { 176 "device": "simulator", 177 "app": "ios.release" 178 }, 179 "android.release": { 180 "device": "emulator", 181 "app": "android.release" 182 } 183 } 184} 185``` 186 187### 4. Write E2E tests 188 189Next, we'll add our first E2E tests. Delete the auto-generated **e2e/firstTest.e2e.js** and create our own **e2e/homeScreen.e2e.js** with the following contents: 190 191```js e2e/homeScreen.e2e.js 192describe('Home screen', () => { 193 beforeAll(async () => { 194 await device.launchApp(); 195 }); 196 197 beforeEach(async () => { 198 await device.reloadReactNative(); 199 }); 200 201 it('"Click me" button should be visible', async () => { 202 await expect(element(by.id('click-me-button'))).toBeVisible(); 203 }); 204 205 it('shows "Hi!" after tapping "Click me"', async () => { 206 await element(by.id('click-me-button')).tap(); 207 await expect(element(by.text('Hi!'))).toBeVisible(); 208 }); 209}); 210``` 211 212There are two tests in the suite: 213 214- One that checks whether the "Click me" button is visible on the home screen. 215- Another that verifies that tapping the button triggers displaying "Hi!". 216 217Both tests assume the button has the `testID` set to `click-me-button`. See [the source code](#2-make-home-screen-interactive) for details. 218 219### 5. Configure EAS Build 220 221Now that we have configured Detox and written our first E2E test, let's configure EAS Build and run the tests in the cloud. 222 223#### Create eas.json 224 225The following command creates [eas.json](/build/eas-json.mdx) in the project's root directory: 226 227<Terminal cmd={['$ eas build:configure']} /> 228 229#### Configure EAS Build 230 231There are a few more steps to configure EAS Build for running E2E tests as part of the build: 232 233- Android tests: 234 - Tests are run in the Android Emulator. You will define a build profile that builds your app for the emulator (produces an `apk` file). 235 - Install the emulator and all its system dependencies. 236- iOS test: 237 - Tests are run in the iOS Simulator. You will define a build profile that builds your app for the simulator. 238 - Install the [`applesimutils`](https://github.com/wix/AppleSimulatorUtils) command line util. 239- Configure EAS Build to run Detox tests after successfully building the app. 240 241Edit **eas.json** and add the `test` build profile: 242 243```json eas.json 244{ 245 "build": { 246 "test": { 247 "android": { 248 "gradleCommand": ":app:assembleRelease :app:assembleAndroidTest -DtestBuildType=release", 249 "withoutCredentials": true 250 }, 251 "ios": { 252 "simulator": true 253 } 254 } 255 } 256} 257``` 258 259Create **eas-hooks/eas-build-pre-install.sh** that installs the necessary tools and dependencies for the given platform: 260 261```sh eas-hooks/eas-build-pre-install.sh 262#!/usr/bin/env bash 263 264set -eox pipefail 265 266if [[ "$EAS_BUILD_RUNNER" == "eas-build" && "$EAS_BUILD_PROFILE" == "test"* ]]; then 267 if [[ "$EAS_BUILD_PLATFORM" == "android" ]]; then 268 sudo apt-get --quiet update --yes 269 270 # Install emulator & video bridge dependencies 271 # Source: https://github.com/react-native-community/docker-android/blob/master/Dockerfile 272 sudo apt-get --quiet install --yes \ 273 libc6 \ 274 libdbus-1-3 \ 275 libfontconfig1 \ 276 libgcc1 \ 277 libpulse0 \ 278 libtinfo5 \ 279 libx11-6 \ 280 libxcb1 \ 281 libxdamage1 \ 282 libnss3 \ 283 libxcomposite1 \ 284 libxcursor1 \ 285 libxi6 \ 286 libxext6 \ 287 libxfixes3 \ 288 zlib1g \ 289 libgl1 \ 290 pulseaudio \ 291 socat 292 293 sdkmanager --install "system-images;android-32;google_apis;x86_64" 294 avdmanager --verbose create avd --force --name "pixel_4" --device "pixel_4" --package "system-images;android-32;google_apis;x86_64" 295 else 296 brew tap wix/brew 297 brew install applesimutils 298 fi 299fi 300 301``` 302 303Next, create **eas-hooks/eas-build-on-success.sh** with the following contents. The script runs different commands for Android and iOS. For iOS, the only command is `detox test`. For Android, it's a bit more complicated. You'll have to start the emulator prior to running the tests as `detox` sometimes seems to be having problems with starting the emulator on its own and it can get stuck on running the first test from your test suite. After the `detox test` run, there is a command that kills the previously started emulator. 304 305```sh eas-hooks/eas-build-on-success.sh 306#!/usr/bin/env bash 307 308set -eox pipefail 309 310ANDROID_EMULATOR=pixel_4 311 312if [[ "$EAS_BUILD_PLATFORM" == "android" ]]; then 313 # Start emulator 314 $ANDROID_SDK_ROOT/emulator/emulator @$ANDROID_EMULATOR -no-audio -no-boot-anim -no-window -use-system-libs 2>&1 >/dev/null & 315 316 # Wait for emulator 317 max_retry=10 318 counter=0 319 until adb shell getprop sys.boot_completed; do 320 sleep 10 321 [[ counter -eq $max_retry ]] && echo "Failed to start the emulator!" && exit 1 322 counter=$((counter + 1)) 323 done 324 325 if [[ "$EAS_BUILD_PROFILE" == "test" ]]; then 326 detox test --configuration android.release --headless 327 fi 328 if [[ "$EAS_BUILD_PROFILE" == "test_debug" ]]; then 329 detox test --configuration android.debug --headless 330 fi 331 332 # Kill emulator 333 adb emu kill 334else 335 if [[ "$EAS_BUILD_PROFILE" == "test" ]]; then 336 detox test --configuration ios.release --headless 337 fi 338 if [[ "$EAS_BUILD_PROFILE" == "test_debug" ]]; then 339 detox test --configuration ios.debug --headless 340 fi 341fi 342``` 343 344Edit **package.json** to use [EAS Build hooks](/build-reference/npm-hooks.mdx) to run the above scripts on EAS Build: 345 346```json package.json 347{ 348 "scripts": { 349 "eas-build-pre-install": "./eas-hooks/eas-build-pre-install.sh", 350 "eas-build-on-success": "./eas-hooks/eas-build-on-success.sh" 351 } 352} 353``` 354 355> Don't forget to add executable permissions to **eas-build-pre-install.sh** and **eas-build-on-success.sh**. Run `chmod +x eas-hooks/*.sh`. 356 357### 5.1. Patch **build.gradle** 358 359> This step will be redundant in the future. 360 361The Android build command that you use to produce the test build is `./gradlew :app:assembleRelease :app:assembleAndroidTest -DtestBuildType=release`. Notice that it consists of two Gradle tasks. Unfortunately, when building the `*AndroidTest` task, some versions of the `expo-modules-core` module change what native libraries are included in the app binary. Those settings don't work with settings for `assembleRelease`. 362 363To fix the problem, add the `pickFirsts` list under `android.packagingOptions` in your **android/app/build.gradle**. The `pickFirsts` property overrides the setting for your project. 364 365<DiffBlock source="/static/diffs/e2e-tests-pickfirsts.diff" /> 366 367### 6. Run tests on EAS Build 368 369Running the tests on EAS Build is like running a regular build: 370 371<Terminal cmd={['$ eas build -p all -e test']} /> 372 373If you have set up everything correctly you should see the successful test result in the build logs: 374 375<ImageSpotlight src="/static/images/eas-build/tests/03-logs.png" style={{ maxWidth: '90%' }} /> 376 377### 7. Upload screenshots of failed test cases 378 379> This step is optional but highly recommended. 380 381When an E2E test case fails, it can be helpful to see the screenshot of the application state. EAS Build makes it easy to upload any arbitrary build artifacts using the [`buildArtifactPaths`](/build-reference/eas-json.mdx#buildartifactpaths) field in **eas.json**. 382 383#### Take screenshots for failed tests 384 385[Detox supports taking in-test screenshots of the device](https://wix.github.io/Detox/docs/api/screenshots). It exposes the `device.takeScreenshot()` function that can be called from any test case. 386 387Neither `jest` nor `detox` offers a simple way to detect when a particular test case has failed and take a screenshot for only failed test cases. You will have to implement your own mechanism for that. 388 389Edit **e2e/environment.js** and add the `handleTestEvent` method to the `CustomDetoxEnvironment` class. The function handles test events and sets a global `testFailed` variable for test case failure. See the snippet below: 390 391```ts e2e/environment.js 392class CustomDetoxEnvironment extends DetoxCircusEnvironment { 393 // ... 394 395 async handleTestEvent(event, state) { 396 const { name } = event; 397 398 if (['test_start', 'test_fn_start'].includes(name)) { 399 this.global.testFailed = false; 400 } 401 402 if (name === 'test_fn_failure') { 403 this.global.testFailed = true; 404 } 405 406 await super.handleTestEvent(event, state); 407 } 408} 409``` 410 411After modifying the environment class, define an `afterEach` hook that calls `device.takeScreenshot()` for failed tests. Create the **e2e/setup.js** file with the following snippet: 412 413```ts e2e/setup.js 414afterEach(async () => { 415 if (testFailed) { 416 await device.takeScreenshot('screenshot'); 417 } 418}); 419``` 420 421The last step is to configure `jest` to load `e2e/setup.js` before running tests. This way, you don't need to include the `afterEach` hook in every test suite: 422 423```json e2e/config.json 424{ 425 /// ... 426 "setupFilesAfterEnv": ["./setup.js"] 427} 428``` 429 430After making those changes, screenshots for failed tests will be saved in the `artifacts` directory. 431 432#### Configure EAS Build for screenshots upload 433 434Edit **eas.json** and add `buildArtifactPaths` to the `test` build profile: 435 436```json eas.json 437{ 438 "build": { 439 "test": { 440 "android": { 441 "gradleCommand": ":app:assembleRelease :app:assembleAndroidTest -DtestBuildType=release", 442 "withoutCredentials": true 443 }, 444 "ios": { 445 "simulator": true 446 }, 447 /* @info */ 448 "buildArtifactPaths": ["artifacts/**/*.png"] 449 /* @end */ 450 } 451 } 452} 453``` 454 455In contrast to `applicationArchivePath`, the build artifacts defined at `buildArtifactPaths` will be uploaded even if the build fails. All `.png` files from the `artifacts` directory will be packed into a tarball and uploaded to AWS S3. You can download them later from the build details page. 456 457If you run E2E tests locally, remember to add `artifacts` to `.gitignore`: 458 459```stylus .gitignore 460artifacts/ 461``` 462 463#### Break a test and run a build 464 465To test the new configuration, let's break a test and see that EAS Build uploads the screenshots. 466 467Edit **e2e/homeScreen.e2e.js** and make the following change: 468 469<DiffBlock source="/static/diffs/e2e-tests-homescreen.diff" /> 470 471Run an iOS build with the following command and wait for it to finish: 472 473<Terminal cmd={['$ eas build -p ios -e test']} /> 474 475After going to the build details page you should see that the build failed. Use the **"Download artifacts"** button to download and examine the screenshot: 476 477<ImageSpotlight src="/static/images/eas-build/tests/04-artifacts.png" style={{ maxWidth: '90%' }} /> 478 479## Repository 480 481The full example from this guide is available at https://github.com/expo/eas-tests-example. 482 483## Alternative Approaches 484 485### Using development builds to speed up test run time 486 487The above guide explains how to run E2E tests against a release build of your project, which requires executing a full native build before each test run. Re-building the native app each time you run E2E tests may not be desirable if only the project JavaScript or assets have changed. However, this is necessary for release builds because the app JavaScript bundle is embedded into the binary. 488 489Instead, we can use [development builds](/development/introduction/) to load from a local development server or from [published updates](/eas-update/introduction/) to save time and CI resources. This can be done by having your E2E test runner invoke the app with a URL that points to a specific update bundle URL, as described in the [development builds deep linking URLs guide](/development/development-workflows/#deep-linking-urls). 490 491Development builds typically display an onboarding welcome screen when an app is launched for the first time, which intends to provide context about the `expo-dev-client` UI for developers. However, it can interfere with your E2E tests (which expect to interact with your app and not an onboarding screen). To skip the onboarding screen in a test environment, the query parameter `disableOnboarding=1` can be appended to the project URL (an EAS Update URL or a local development server URL). 492 493An example of such a Detox test is shown below. Full example code is available on the [eas-tests-example](https://github.com/expo/eas-tests-example) repository. 494 495<Collapsible summary="e2e/homeScreen.e2e.js"> 496```js 497/* @info New line */ 498const { openApp } = require('./utils/openApp'); 499/* @end */ 500 501describe('Home screen', () => { 502 beforeEach(async () => { 503 /* @info New line */ await openApp(); /* @end */ 504 }); 505 506 it('"Click me" button should be visible', async () => { 507 await expect(element(by.id('click-me-button'))).toBeVisible(); 508 }); 509 510 it('shows "Hi!" after tapping "Click me"', async () => { 511 await element(by.id('click-me-button')).tap(); 512 await expect(element(by.text('Hi!'))).toBeVisible(); 513 }); 514}); 515``` 516 517</Collapsible> 518 519<Collapsible summary="e2e/utils/openApp.js (new file)"> 520 521```js 522const appConfig = require('../../../app.json'); 523 524module.exports.openApp = async function openApp() { 525 const [platform, target] = process.env.DETOX_CONFIGURATION.split('.'); 526 if (target === 'debug') { 527 return await openAppForDebugBuild(platform); 528 } else { 529 return await device.launchApp({ 530 newInstance: true, 531 }); 532 } 533} 534 535async function openAppForDebugBuild(platform) { 536 const deepLinkUrl = process.env.EXPO_USE_UPDATES 537 ? // Testing latest published EAS update for the test_debug channel 538 getDeepLinkUrl(getLatestUpdateUrl()) 539 : // Local testing with packager 540 getDeepLinkUrl(getDevLauncherPackagerUrl(platform)); 541 542 if (platform === 'ios') { 543 // For iOS, the app must be launched, then the deep link URL invoked 544 await device.launchApp({ 545 newInstance: true, 546 }); 547 sleep(3000); 548 await device.openURL({ 549 url: deepLinkUrl, 550 }); 551 } else { 552 // For Android, the app must be launched directly with the deep link URL 553 await device.launchApp({ 554 newInstance: true, 555 url: deepLinkUrl, 556 }); 557 } 558 559 await sleep(3000); 560}; 561 562const getDeepLinkUrl = url => 563 /* @info This is project-specific, replace eastestsexample with correct value */ 564 `eastestsexample://expo-development-client/?url=${encodeURIComponent(url)}`; 565/* @end */ 566 567const getDevLauncherPackagerUrl = platform => 568 `http://localhost:8081/index.bundle?platform=${platform}&dev=true&minify=false&disableOnboarding=1`; 569 570const getLatestUpdateUrl = () => 571 `https://u.expo.dev/${getAppId()}?channel-name=test_debug&disableOnboarding=1`; 572 573const getAppId = () => appConfig?.expo?.extra?.eas?.projectId ?? ''; 574 575const sleep = t => new Promise(res => setTimeout(res, t)); 576``` 577 578</Collapsible> 579 580<Collapsible summary=".detoxrc.json"> 581 582```json 583{ 584 "testRunner": "jest", 585 "runnerConfig": "e2e/config.json", 586 "skipLegacyWorkersInjection": true, 587 "apps": { 588 /* @info New section */ "ios.debug": { 589 "type": "ios.app", 590 "build": "xcodebuild -workspace ios/eastestsexample.xcworkspace -scheme eastestsexample -configuration Debug -sdk iphonesimulator -arch x86_64 -derivedDataPath ios/build", 591 "binaryPath": "ios/build/Build/Products/Debug-iphonesimulator/eastestsexample.app" 592 }, /* @end */ 593 "ios.release": { 594 "type": "ios.app", 595 "build": "xcodebuild -workspace ios/eastestsexample.xcworkspace -scheme eastestsexample -configuration Release -sdk iphonesimulator -arch x86_64 -derivedDataPath ios/build", 596 "binaryPath": "ios/build/Build/Products/Release-iphonesimulator/eastestsexample.app" 597 }, 598 /* @info New section */ "android.debug": { 599 "type": "android.apk", 600 "build": "cd android && ./gradlew :app:assembleDebug :app:assembleAndroidTest -DtestBuildType=debug && cd ..", 601 "binaryPath": "android/app/build/outputs/apk/debug/app-debug.apk" 602 }, /* @end */ 603 "android.release": { 604 "type": "android.apk", 605 "build": "cd android && ./gradlew :app:assembleRelease :app:assembleAndroidTest -DtestBuildType=release && cd ..", 606 "binaryPath": "android/app/build/outputs/apk/release/app-release.apk" 607 } 608 }, 609 "devices": { 610 "simulator": { 611 "type": "ios.simulator", 612 "device": { 613 "type": "iPhone 14" 614 } 615 }, 616 "emulator": { 617 "type": "android.emulator", 618 "device": { 619 "avdName": "pixel_4" 620 } 621 } 622 }, 623 "configurations": { 624 /* @info New section */ "ios.debug": { 625 "device": "simulator", 626 "app": "ios.debug" 627 }, /* @end */ 628 "ios.release": { 629 "device": "simulator", 630 "app": "ios.release" 631 }, 632 /* @info New section */ "android.debug": { 633 "device": "emulator", 634 "app": "android.debug" 635 }, /* @end */ 636 "android.release": { 637 "device": "emulator", 638 "app": "android.release" 639 } 640 } 641} 642``` 643</Collapsible> 644 645<Collapsible summary="eas-hooks/eas-build-on-success.sh"> 646 647```sh 648#!/usr/bin/env bash 649 650set -eox pipefail 651 652ANDROID_EMULATOR=pixel_4 653 654if [[ "$EAS_BUILD_PLATFORM" == "android" ]]; then 655 # Start emulator 656 $ANDROID_SDK_ROOT/emulator/emulator @$ANDROID_EMULATOR -no-audio -no-boot-anim -no-window -use-system-libs 2>&1 >/dev/null & 657 658 # Wait for emulator 659 max_retry=10 660 counter=0 661 until adb shell getprop sys.boot_completed; do 662 sleep 10 663 [[ counter -eq $max_retry ]] && echo "Failed to start the emulator!" && exit 1 664 counter=$((counter + 1)) 665 done 666 667 if [[ "$EAS_BUILD_PROFILE" == "test" ]]; then 668 detox test --configuration android.release --headless 669 fi 670 # @info New section # 671 if [[ "$EAS_BUILD_PROFILE" == "test_debug" ]]; then 672 detox test --configuration android.debug --headless 673 fi 674 # @end # 675 # Kill emulator 676 adb emu kill 677else 678 if [[ "$EAS_BUILD_PROFILE" == "test" ]]; then 679 detox test --configuration ios.release --headless 680 fi 681 # @info New section # 682 if [[ "$EAS_BUILD_PROFILE" == "test_debug" ]]; then 683 detox test --configuration ios.debug --headless 684 fi 685 # @end # 686fi 687``` 688 689</Collapsible> 690 691<Collapsible summary="eas.json"> 692 693```json 694{ 695 "build": { 696 "test": { 697 "android": { 698 "gradleCommand": ":app:assembleRelease :app:assembleAndroidTest -DtestBuildType=release", 699 "withoutCredentials": true 700 }, 701 "ios": { 702 "simulator": true 703 } 704 }, 705 /* @info New section */ "test_debug": { 706 "android": { 707 "gradleCommand": ":app:assembleDebug :app:assembleAndroidTest -DtestBuildType=debug", 708 "withoutCredentials": true 709 }, 710 "ios": { 711 "buildConfiguration": "Debug", 712 "simulator": true 713 }, 714 "env": { 715 "EXPO_USE_UPDATES": "1" 716 }, 717 "channel": "test_debug" 718 } /* @end */ 719 } 720} 721``` 722 723</Collapsible>