detox-tools
detox-tools copied to clipboard
Not working with detox > 12.3.0
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 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!
@maitriyogin https://github.com/wix/Detox/issues/1422
Still on 12.3.. haven’t had time to fiddle for a while.. will post once I’ve investigated
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: {}
This is still an issue for me, is there a workaround?
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, 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.
Would also like to second this @lookitsatravis , this is a great update though!
@jeloagnasin Were you able to fix this? I'm having the same problem here with detox@18.
Same problem with detox 18 :(
@luluanacarla and @martinezguillaume try using detox@^17.0.0
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 Thank you, awesome job !! Do you know if it works with Android ? Thanks again!
@fschoenfeldt How did you figure it out?
@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
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!
Hi Guys! @jorgeruvalcaba @fschoenfeldt
Has anyone been able to get detox working with expo 44? :/
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
@jorgeruvalcaba Thank you so much! Do you by any chance know if this also works with the custom built clients from expo ?
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
- 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
}
}
- Create a folder called bin inside the e2e folder and drop the unzipped YourApp.app file into it
- 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 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 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 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.
have you guys checked https://github.com/calitb/expo-e2e-demo?
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 😁
@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:
-
- make sure the metro server is running, I typically am running
expo start --ios
- make sure the metro server is running, I typically am running
-
- once that's running start your tests
detox test --configuration ios --watch
- once that's running start your tests
-
- 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.
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.
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?
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?
@kieran-osgood any progress by any chance?