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>