detox-tools icon indicating copy to clipboard operation
detox-tools copied to clipboard

Not working with detox > 12.3.0

Open maitriyogin opened this issue 5 years ago • 37 comments

Hiya, we updated detox to 12.8 and noticed that our e2e tests kind of stopped working ... Had a crazy afternoon of going back minor versions and found out that 12.3 is the last working detox version with this most amazing lib! Not sure what's changed in detox that could have broken this but I'll dig around and see if I can find something. I guess you guys could maybe pin point it directly? Huge thanks, Stephen.

maitriyogin avatar May 15 '19 14:05 maitriyogin

@maitriyogin Just had this problem myself! All interactions stopped working. Has anything progressed from your side or are you still using 12.3? Thanks for the interim fix!

bladey avatar Jun 13 '19 05:06 bladey

@maitriyogin https://github.com/wix/Detox/issues/1422

bladey avatar Jun 13 '19 05:06 bladey

Still on 12.3.. haven’t had time to fiddle for a while.. will post once I’ve investigated

maitriyogin avatar Jun 15 '19 15:06 maitriyogin

Following up on this, I'm not able to get reloadApp to in detox-expo-helpers to complete.

I now think the issue is related to how expo-detox-hook notifies the detox server loading is complete via websockets.

detox-expo-helpers:reloadApp calls device.launchApp and the actual expo app boots), the isReady message doesn't seem to get a response.

detox[94950] WARN:  [Client.js/PENDING_REQUESTS] App has not responded to the network requests below:
  (id = -1000) isReady: {}

brookemitchell avatar Jul 14 '19 20:07 brookemitchell

This is still an issue for me, is there a workaround?

SeanHayes avatar Sep 08 '20 04:09 SeanHayes

As of December 2020, I'm using detox 17.14.3 with Expo 39.0.5 and I've managed to solve this issue. Patch and explanation provided below.

It turns out that detox-expo-helpers is overriding an environment variable (specifically SIMCTL_CHILD_DYLD_INSERT_LIBRARIES) to specify the path to the ExpoDetoxHook framework (provided by the expo-detox-hook package). Well, that update to the environment now happens too late in the cycle. It takes place when running Detox's reloadApp, but by then, Detox has started up Jest which has workers running with their own copy of process.env. Workers get a copy of process.env at the time of creation, and they are not shared with the parent process, so the changes this library makes to environment variables are not reflected inside Jest workers. The hook framework is not available to the running application, and so the Detox is stuck waiting for a ready signal that doesn't come.

Detox's launchApp for iOS simulator uses SIMCTL_CHILD_DYLD_INSERT_LIBRARIES to specify it's own library, but because the environment vars are isolated in the worker, it does not see the change this package makes. To solve this, I modified this library to expose the changes to process.env as a separate exported function, and then I call that much earlier in the test lifecycle to ensure it is set before any workers are started. Here is a patch compatible with patch-package.

# file patches/detox-expo-helpers+0.6.0.patch

diff --git a/node_modules/detox-expo-helpers/index.js b/node_modules/detox-expo-helpers/index.js
index 864493b..3147a55 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,12 +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();
@@ -121,5 +134,6 @@ module.exports = {
   getAppUrl,
   getAppHttpUrl,
   blacklistLiveReloadUrl,
+  init,
   reloadApp,
 };

With that in place, I modified my e2e/environment.js file to look like the following. The addition is the initExpo call. When it runs this early in the test lifecycle, the environment is modified before any workers are started, and as a result, calls to reloadApp no longer wait indefinitely.

/* eslint-disable import/no-extraneous-dependencies */
const { init: initExpo } = require('detox-expo-helpers');
const { DetoxCircusEnvironment, SpecReporter, WorkerAssignReporter } = require('detox/runners/jest-circus');

class CustomDetoxEnvironment extends DetoxCircusEnvironment {
  constructor(config) {
    super(config);

    initExpo();

    // 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;

lookitsatravis avatar Dec 08 '20 15:12 lookitsatravis

@lookitsatravis, were you able to make it work for detox@18? When I run detox test --configuration ios.sim --loglevel verbose, I get this:

detox[25249] INFO:  [actions.js] The system is busy with the following tasks:

Dispatch Queue
⏱ Queue: “Main Queue (<OS_dispatch_queue_main: com.apple.main-thread>)” with 1 work item

Run Loop
⏱ “Main Run Loop”

One-time Events
⏱ “React Native (bundle load)”
detox[25249] WARN:  [Client.js/PENDING_REQUESTS] App has not responded to the network requests below:
  (id = -1000) isReady: {}

That might be the reason why the test "SignIn should show getting started" has timed out.

jeloagnasin avatar Mar 19 '21 23:03 jeloagnasin

Would also like to second this @lookitsatravis , this is a great update though!

caelinsutch avatar Apr 07 '21 21:04 caelinsutch

@jeloagnasin Were you able to fix this? I'm having the same problem here with detox@18.

luluanacarla avatar Apr 28 '21 13:04 luluanacarla

Same problem with detox 18 :(

martinezguillaume avatar Jul 12 '21 14:07 martinezguillaume

@luluanacarla and @martinezguillaume try using detox@^17.0.0

jeloagnasin avatar Jul 17 '21 07:07 jeloagnasin

We have made a working example with Detox@18 and Expo@42: https://github.com/fschoenfeldt/clean-expo-detox-testing

If you have any questions, feel free to ping me. 😃

fschoenfeldt avatar Jul 21 '21 11:07 fschoenfeldt

@fschoenfeldt Thank you, awesome job !! Do you know if it works with Android ? Thanks again!

martinezguillaume avatar Jul 23 '21 15:07 martinezguillaume

@fschoenfeldt How did you figure it out?

Jonatthu avatar Aug 08 '21 20:08 Jonatthu

@fschoenfeldt Thank you, awesome job !! Do you know if it works with Android ? Thanks again!

@martinezguillaume AFAIK, Android only works with ejecting the Expo project

Further readings:

  • Feature Request Expo: https://expo.canny.io/feature-requests/p/native-end-to-end-testing
  • Plugin that could help with ejecting: https://github.com/expo/config-plugins/tree/master/packages/detox

@fschoenfeldt How did you figure it out?

@Jonatthu essentially, we disabled device synchronization because it simply wasn't working. We now wait for the first element of the start screen to appear. For more information, see this readme: https://github.com/fschoenfeldt/clean-expo-detox-testing#readme

fschoenfeldt avatar Aug 09 '21 09:08 fschoenfeldt

We have made a working example with Detox@18 and Expo@42: https://github.com/fschoenfeldt/clean-expo-detox-testing

If you have any questions, feel free to ping me. 😃

Thank you @fschoenfeldt! It just works!

jorgearuv avatar Aug 25 '21 16:08 jorgearuv

Hi Guys! @jorgeruvalcaba @fschoenfeldt

Has anyone been able to get detox working with expo 44? :/

haibert avatar Feb 23 '22 01:02 haibert

Hi Guys! @jorgeruvalcaba @fschoenfeldt

Has anyone been able to get detox working with expo 44? :/

@haibert Haven't try with expo 44, but this plugin is supposed to work with it: https://github.com/expo/config-plugins/tree/master/packages/detox

jorgearuv avatar Feb 23 '22 16:02 jorgearuv

@jorgeruvalcaba Thank you so much! Do you by any chance know if this also works with the custom built clients from expo ?

haibert avatar Feb 23 '22 16:02 haibert

Guys here is a full instruction list on how I made it work, I was over thinking it like crazy you actually don't need to do anything special for expo... Hopefully this helps someone.

1. yarn add --dev detox jest-circus 2. detox init -r jest (This creates the e2e folder along with the .detoxrc.json) 3. yarn add --dev eslint-plugin-detox (optional)

   plugins: [...'detox'...],
  • Add this to your eslint config plugins
  1. Create a simulator release build using the following configuration in your eas.json by running
  • eas build --profile simulator --platform ios
"simulator": {
           "ios": {
               "simulator": true,
               "developmentClient": false
           }
       }
  1. Create a folder called bin inside the e2e folder and drop the unzipped YourApp.app file into it
  2. Inside the .detoxrc.json set the file set the binaryPath to your simulator build
"apps": {
       "ios": {
           "type": "ios.app",
           "binaryPath": "e2e/bin/YourApp.app"
       },

Write some tests and run that bitch. Enjoy!

- detox test -c ios

haibert avatar Feb 26 '22 03:02 haibert

@haibert Hey, that works for me! Is there a way for me to test new updates to the app without having to rebuild the eas app?

corliansa avatar Apr 18 '22 09:04 corliansa

@Corliansa So glad it helped! 😄

I don't think so, I would say you can push an update through expo update but updates dont work on simulators.

haibert avatar Apr 18 '22 15:04 haibert

@haibert Yeah sadly the updates doesn't get loaded in simulators. And unfortunately I haven't found a way to use detox for expo dev client or expo go for Expo 44.

corliansa avatar Apr 19 '22 23:04 corliansa

have you guys checked https://github.com/calitb/expo-e2e-demo?

jorgearuv avatar Apr 19 '22 23:04 jorgearuv

I'm finally able to use expo dev client for detox testing!

beforeAll(async () => {
		await device.launchApp();
		await element(by.text("Got it")).tap();
		await element(by.text("Development Client")).swipe("down");
		await element(by.text("http://localhost:8081")).tap();
	});
  • Add this at the beginning of describe, and don't forget to set the binaryPath in .detoxrc.json to your expo dev client app

I know there's a better way to do it, but this works too so for now i'm satisfied 😁

corliansa avatar Apr 20 '22 00:04 corliansa

@haibert Hey, that works for me! Is there a way for me to test new updates to the app without having to rebuild the eas app?

I'm using expo 46 and detox 19.9.3 and achieved this (iOS - haven't tested android yet, but theres a config plugin here so it should work: https://github.com/expo/config-plugins/tree/main/packages/detox)

What you do is download the expo client from https://expo.dev/tools#client - or make a dev client build if you have custom code - this is the binary you point to in your detox config like so:

 "apps": {
    "ios": {
      "type": "ios.app",
      "binaryPath": "bin/Exponent.app"
    }
  },

Then in your e2e tests, instead of calling the typical device.reloadReactNativeApp() you replace it with

    const { reloadApp } = require('detox-expo-helpers');
    // ...
    await reloadApp();

this loads in the app currently served on metro

Now theres a few steps to get this to work:

    1. make sure the metro server is running, I typically am running expo start --ios
    1. once that's running start your tests detox test --configuration ios --watch
    1. You'll likely notice the tests fail because it didnt get into your app in time, so all you do is set a beforeAll assertion to wait for it to load, so for me on the first page it's like this:
  beforeAll(async () => {
    await reloadApp();
    const welcomeScreen = element(by.id("welcome-screen"));
    await waitFor(welcomeScreen).toBeVisible().withTimeout(20000);
  });

With this I'm able to use the jest controls in the command line to re-run the tests automatically if I change the test files, or via w a to re-run them all if I change the source files, all without re-building the app each time.

kieran-osgood avatar Aug 18 '22 09:08 kieran-osgood

Does someone has a working example with sdk 46 and newest detox version? I can't get it running. Tried many different approaches already.. even @kieran-osgood example is not running for me.

borstessi avatar Sep 30 '22 11:09 borstessi

Does someone has a working example with sdk 46 and newest detox version? I can't get it running. Tried many different approaches already.. even @kieran-osgood example is not running for me.

I'll try to upload the code later and share a link as a reference if that helps, what version of detox is it you're using specifically?

kieran-osgood avatar Sep 30 '22 11:09 kieran-osgood

Currently trying to get a expo sdk 46 typescript example going with the newest detox version 19.12.3 or 19.12.2. But i'll take any detox version as long as it is running. Were you able to run it with Expo go app? Or with the prebuild versions only?

borstessi avatar Sep 30 '22 12:09 borstessi

@kieran-osgood any progress by any chance?

borstessi avatar Oct 04 '22 16:10 borstessi