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