Skip to content

Instantly share code, notes, and snippets.

@solkaz
Last active August 14, 2024 18:07
Show Gist options
  • Save solkaz/ead11515e2aa91d0dc04e609b3108841 to your computer and use it in GitHub Desktop.
Save solkaz/ead11515e2aa91d0dc04e609b3108841 to your computer and use it in GitHub Desktop.
Writing Detox Tests with TypeScript

Usage

This guide assumes you've got a project using Detox with Jest, and you want to write your Detox tests in TypeScript.

  • Refer to this guide if you need to set up such a project.

1. Add TypeScript + ts-jest to package.json

We'll be using ts-jest to run Jest tests with TypeScript.

npm install --save-dev typescript ts-jest

2. Configure Jest to use ts-jest

Modify your Jest configuration (e2e/config.json by default) to include the following properties

{
  "preset": "ts-jest",
  "testEnvironment": "node",
  "setupTestFrameworkScriptFile": "./init.ts"
}

NB: this is mostly the same output of running ts-jest config:init, with setupTestFrameworkScriptFile being the only added property.

3. .js -> .ts

Convert all files in the e2e directory ending in .js to .ts

4. Add typings for Detox, Jest, and Jasmine

Add typings for Detox, Jest, and Jasmine (the latter two are used in init.ts), as well as for other modules that you use in your Detox tests.

npm install --save-dev @types/detox @types/jest @types/jasmine

5. Writing tests

It's recommended that you import the Detox methods instead of their globally defined counterparts, to avoid a typing issue between Detox and Jest. This can be enforced by calling detox.init with { initGlobals: false }

Your init.ts would look simliar to this:

import { cleanup, init } from 'detox';
import * as adapter from 'detox/runners/jest/adapter';

const config = require('../package.json').detox;

jest.setTimeout(120000);
jasmine.getEnv().addReporter(adapter);

beforeAll(async () => {
    await init(config, { initGlobals: false });
});

beforeEach(async () => {
    await adapter.beforeEach();
});

afterAll(async () => {
    await adapter.afterAll();
    await cleanup();
});

Note: the global constants are still defined in the typings, so using the globals will not result in a tsc error.

Your tests would then import the Detox methods from the detox module like so:

import { by, device, expect, element, waitFor } from 'detox';

Note: @types/detox is maintained by the community and not by Wix.

You should now be able to run your Detox tests, written in TypeScript!

@pietgk
Copy link

pietgk commented Jul 22, 2020

i have the same
ReferenceError: jasmine is not defined
error.

I have not been able to solve the issue or find any other reference to hint for a solution yet.

@Townsheriff
Copy link

Having same issue

@BrianCodeItUp
Copy link

BrianCodeItUp commented Aug 27, 2020

ReferenceError: jasmine is not defined is possibly caused by the testRunner settingjest-circus/runner.

Issue: zaqqaz/jest-allure#8
StackOverflow: https://stackoverflow.com/questions/62258500/jasmine-is-not-define-error-after-using-jest-circus

@preetb123
Copy link

I got this working.

|--e2e
   |--config.json
   |--*.spec.ts
   |--init.ts

config.json

{
  "preset": "ts-jest",
  "testEnvironment": "node",
  "setupFilesAfterEnv": ["./init.ts"]
}

firstTest.spec.ts

import { by, element, expect } from "detox"

const { reloadApp } = require("detox-expo-helpers")

describe("Shows welcome screen", () => {
  beforeEach(async () => {
    await reloadApp()
  })

  it("should have welcome screen", async () => {
    await expect(element(by.id("welcome"))).toBeVisible()
  })
}

init.ts

import { cleanup, init } from "detox"
import * as adapter from "detox/runners/jest/adapter"

const config = require("../package.json").detox

jest.setTimeout(120000)
jasmine.getEnv().addReporter(adapter)

beforeAll(async () => {
  await init(config)
})

beforeEach(async () => {
  await adapter.beforeEach()
})

afterAll(async () => {
  await adapter.afterAll()
  await cleanup()
})

have detox defined in package.json

{
  "detox": {
    "configurations": {
      "ios.sim": {
        "binaryPath": "bin/Exponent.app",
        "type": "ios.simulator",
        "name": "iPhone 11"
      }
    },
    "test-runner": "jest",
    "specs": ""
  }
}

@preetb123
Copy link

Typescript now works out of the box with detox. Just make sure to "testRegex": "\\.e2e\\.ts$", in config.json and test files extension to .ts

@designervoid
Copy link

@preetb123 me too.

@gruckionvit
Copy link

@preetb123

I hope you are well. I am attempting to setup Detox e2e tests on an Expo project but I am having some trouble with jasmine is not defined
Here's the full stack trace

> [email protected] test:e2e /Users/stephen.rayner/repos/MyApp> detox test -c ios.sim
detox[65664] INFO:  [test.js] DETOX_CONFIGURATION="ios.sim" DETOX_REPORT_SPECS=true DETOX_START_TIMESTAMP=1612546720391 DETOX_USE_CUSTOM_LOGGER=true jest --config e2e/config.json --testNamePattern '^((?!:android:).)*$' --maxWorkers 1 e2edetox[65665] INFO:  [DetoxServer.js] server listening on localhost:60533... FAIL  e2e/firstTest.e2e.ts  ● Test suite failed to run
    ReferenceError: jasmine is not defined
       5 |        6 | jest.setTimeout(120000)    >  7 | jasmine.getEnv().addReporter(adapter)         | ^       8 |        9 | beforeAll(async () => {      10 |   await init(config)
      at Object.<anonymous> (init.ts:7:1)
Test Suites: 1 failed, 1 totalTests:       0 totalSnapshots:   0 totalTime:        12.644 sRan all test suites matching /e2e/i with tests matching "^((?!:android:).)*$".detox[65665] WARN:  at node_modules/jest/node_modules/jest-cli/build/cli/index.js:261:15  Jest did not exit one second after the test run has completed.
This usually means that there are asynchronous operations that weren't stopped in your tests. Consider running Jest with `--detectOpenHandles` to troubleshoot this issue.```
As you can see it can't fine the jasmine reference.

You said you managed to get it to work.

Here is my configuration
```json
{
  "preset": "ts-jest",
  "testRunner": "jest-circus/runner",
  "testEnvironment": "./environment",
  "testTimeout": 120000,
  "testRegex": "\\.e2e\\.ts$",
  "setupFilesAfterEnv": [
    "./init.ts"
  ],
  "verbose": true
}

environment.js

const {
  DetoxCircusEnvironment,
  SpecReporter,
  WorkerAssignReporter,
} = require("detox/runners/jest-circus")
const { init } = require('detox-expo-helpers')

class CustomDetoxEnvironment extends DetoxCircusEnvironment {
  constructor(config, context) {
    super(config, context)

    init()

    // Can be safely removed, if you are content with the default value (=300000ms)
    this.initTimeout = 300000

    // This takes care of generating status logs on a per-spec basis. By default, Jest only reports at file-level.
    // This is strictly optional.
    this.registerListeners({
      SpecReporter,
      WorkerAssignReporter,
    })
  }
}

module.exports = CustomDetoxEnvironment

init.ts

import { cleanup, init } from "detox"
import * as adapter from "detox/runners/jest/adapter"

const config = require('../package.json').detox

jest.setTimeout(120000)
jasmine.getEnv().addReporter(adapter)

beforeAll(async () => {
  await init(config)
})

beforeEach(async () => {
  await adapter.beforeEach()
})

afterAll(async () => {
  await adapter.afterAll()
  await cleanup()
})

.detoxrc.json

{
  "configurations": {
    "ios.sim": {
      "type": "ios.simulator",
      "binaryPath": "bin/Exponent.app",
      "device": {
        "type": "iPhone 11"
      }
    }
  },
  "test-runner": "jest",
  "specs": ""
}

My firstTest.e2e.ts

import { by, element, expect } from 'detox'
import { reloadApp } from "detox-expo-helpers"

// For more info on how to write Detox tests, see the official docs:
// https://github.com/wix/Detox/blob/master/docs/README.md

describe("Example", () => {
  beforeEach(async () => {
    await reloadApp()
  })

  it("should have welcome screen", async () => {
    await expect(element(by.id("MainText"))).toExist()
    await expect(element(by.id("WelcomeScreen"))).toBeVisible()
  })
})

in package.json I have a "jest" block which looks like;

  "jest": {
    "projects": [
      {
        "preset": "jest-expo/ios",
        "setupFiles": [
          "<rootDir>/node_modules/react-native/jest/setup.js",
          "<rootDir>/test/setupTests.ts"
        ],
        "testPathIgnorePatterns": [
          "/node_modules/",
          "/e2e"
        ],
        "transformIgnorePatterns": [
          "node_modules/(?!(jest-)?react-native|react-clone-referenced-element|@react-native-community|expo(nent)?|@expo(nent)?/.*|react-navigation|@react-navigation/.*|@unimodules/.*|native-base|@storybook|graphql)"
        ],
        "setupFilesAfterEnv": [
          "@testing-library/jest-native/extend-expect"
        ]
      },
      {
        "preset": "jest-expo/android",
        "setupFiles": [
          "<rootDir>/node_modules/react-native/jest/setup.js",
          "<rootDir>/test/setup.ts"
        ],
        "testPathIgnorePatterns": [
          "/node_modules/",
          "/e2e"
        ],
        "transformIgnorePatterns": [
          "node_modules/(?!(jest-)?react-native|react-clone-referenced-element|@react-native-community|expo(nent)?|@expo(nent)?/.*|react-navigation|@react-navigation/.*|@unimodules/.*|native-base|@storybook|graphql)"
        ],
        "setupFilesAfterEnv": [
          "@testing-library/jest-native/extend-expect"
        ]
      }
    ]
  },

Here are my devDependencies

"@types/jasmine": "^3.6.3",
"@types/jest": "^26.0.20",
"detox": "^18.2.2",
"detox-expo-helpers": "^0.6.0",
"expo-detox-hook": "^1.0.10",
"jasmine": "^3.6.4",
"jest": "^26.6.3",
"jest-circus": "^26.6.3",
"jest-expo": "^37.0.0",
"ts-jest": "^26.5.0",
"typescript": "3.9.7"

I am using a script to get the Exponent.app files for the iOS Expo app."dl_expo_bins": "./scripts/dl_expo_bins"

/scripts/dl_expo_bins

#!/bin/bash
set -eo pipefail

# query expo.io to find most recent ipaUrl
IPA_URL=$(curl -sS https://expo.io/--/api/v2/versions | python -c 'import sys, json; print json.load(sys.stdin)["iosUrl"]')
# Skipping android apk dl for now
# APK_URL=$(curl -sS https://expo.io/--/api/v2/versions | python -c 'import sys, json; print json.load(sys.stdin)["androidUrl"]')

# download tar.gz
TMP_PATH_IPA=/tmp/exponent-app.tar.gz
curl -o $TMP_PATH_IPA "$IPA_URL"

# recursively make app dir
APP_PATH=bin/Exponent.app
mkdir -p $APP_PATH

# create apk (isn't stored tar'd)
# APK_PATH=bin/Exponent.apk
# curl -o $APK_PATH "$APK_URL"

# unzip tar.gz into APP_PATH
tar -C $APP_PATH -xzf $TMP_PATH_IPA

There is also an issue in detox-expo-helpers which needs a patch. I am using npm patch-package to make the following changes. This is to stop the emulator just hanging after it opens. More information can be found here;
https://stackoverflow.com/questions/63137314/error-running-tests-with-detox-in-expo-react-native-project

diff --git a/node_modules/detox-expo-helpers/index.js b/node_modules/detox-expo-helpers/index.js
index 864493b..e900ddf 100644
--- a/node_modules/detox-expo-helpers/index.js
+++ b/node_modules/detox-expo-helpers/index.js
@@ -45,7 +45,16 @@ function resetEnvDyldVar(oldEnvVar) {
   }
 }
 
-const reloadApp = async (params) => {
+let initialized = false;
+let detoxVersion;
+let oldEnvVar;
+
+const init = () => {
+  if (initialized) {
+    return;
+  }
+
+  initialized = true;
   if (!fs.existsSync(expoDetoxHookPackageJsonPath)) {
     throw new Error("expo-detox-hook is not installed in this directory. You should declare it in package.json and run `npm install`");
   }
@@ -56,13 +65,16 @@ const reloadApp = async (params) => {
     throw new Error ("expo-detox-hook is not installed in your osx Library. Run `npm install -g expo-detox-cli && expotox clean-framework-cache && expotox build-framework-cache` to fix this.");
   }
 
-  const detoxVersion = getDetoxVersion();
-  const oldEnvVar = process.env.SIMCTL_CHILD_DYLD_INSERT_LIBRARIES;
+  detoxVersion = getDetoxVersion();
+  oldEnvVar = process.env.SIMCTL_CHILD_DYLD_INSERT_LIBRARIES;
 
   if (semver.gte(detoxVersion, '9.0.6')) {
     process.env.SIMCTL_CHILD_DYLD_INSERT_LIBRARIES = expoDetoxHookFrameworkPath;
   }
+};
 
+const reloadApp = async (params) => {
+  init();
   const formattedBlacklistArg = await blacklistCmdlineFormat(params && params.urlBlacklist);
   const url = await getAppUrl();
   await device.launchApp({
@@ -121,5 +133,6 @@ module.exports = {
   getAppUrl,
   getAppHttpUrl,
   blacklistLiveReloadUrl,
+  init,
   reloadApp,
 };
\ No newline at end of file

When I run; "test:e2e": "detox test -c ios.sim",

I get the error about jasmine not being defined. Any ideas what I am missing? Could really do with some help and happy to help you out in the future too. I've been working on this for two days and can't figure it out.

@designervoid
Copy link

@gruckionvit
try this

// tslint:disable-next-line:no-import-side-effect
import "jasmine";

jasmine.getEnv().addReporter(adapter);

@martsie
Copy link

martsie commented Jul 20, 2021

@gruckionvit
try this

// tslint:disable-next-line:no-import-side-effect
import "jasmine";

jasmine.getEnv().addReporter(adapter);

Also make sure you install @types/jasmine ... jest has its own jasmine typings which can make it seem like your jasmine types are installed when they're not.

@lelukas
Copy link

lelukas commented Mar 3, 2022

I'm getting ReferenceError: jasmine is not defined

@dklymenk
Copy link

dklymenk commented May 8, 2022

For some reason I got an error that is not even googlable:

Cannot read properties of undefined (reading 'jasmineStarted')

Removing this line resolved the issue:

  "preset": "ts-jest",

So far everything is good.

@timotgl
Copy link

timotgl commented May 15, 2023

If you end up here from search engines, be advised that Detox 20.9.0 seems to support TypeScript out-of-the-box. I set it up in 2023-05 for a react native project following the official instructions, and the only additional changes I had to do were:

  1. Scan for TypeScript files in your jest config: testMatch: ['<rootDir>/e2e/**/*.test.ts'], (instead of .js)
  2. Import the Detox version of expect, so your IDE/editor doesn't think it's the one from jest or jasmine: import { expect } from 'detox';

I didn't have to install any other dependencies and didn't need a init.ts. No further changes to the jest config besides scanning for .ts files.

@Rossella-Mascia-Neosyn
Copy link

@timotgl i tried your solution but it doesn't recognize me it etc... do i need to add a tsconfig?
image

@timotgl
Copy link

timotgl commented Aug 24, 2023

@Rossella-Mascia-Neosyn I used WebStorm, not familiar with VSCode, sorry. Recognizing it, describe etc. as globals properly with TS is a different issue, there should be stackoverflow answers for that.

@Rossella-Mascia-Neosyn
Copy link

@timotgl i solved it like this:

  1. yarn add -D @types/jest
  2. add in tsconfig jest

image

@Sakshi900
Copy link

adapter is depreceted

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment