Skip to content

Commit 12e952d

Browse files
committed
[Android] implemented setURLBlacklist
1 parent 51c4f33 commit 12e952d

File tree

8 files changed

+173
-18
lines changed

8 files changed

+173
-18
lines changed

detox/android/detox/src/main/java/com/wix/detox/ReactNativeSupport.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import com.wix.detox.espresso.AnimatedModuleIdlingResource;
1010
import com.wix.detox.espresso.LooperIdlingResource;
1111
import com.wix.detox.espresso.ReactBridgeIdlingResource;
12-
import com.wix.detox.espresso.ReactNativeNetworkIdlingResource;
12+
import com.wix.detox.espresso.RNExperimentalNetworkIR;
1313
import com.wix.detox.espresso.ReactNativeTimersIdlingResource;
1414
import com.wix.detox.espresso.ReactNativeUIModuleIdlingResource;
1515

@@ -28,6 +28,8 @@
2828
* Created by simonracz on 15/05/2017.
2929
*/
3030

31+
//TODO Dear reader, if you get this far and find this class messy you are not alone. :) It needs a refactor.
32+
3133
public class ReactNativeSupport {
3234
private static final String LOG_TAG = "Detox";
3335
private static final String METHOD_GET_RN_HOST = "getReactNativeHost";
@@ -402,7 +404,7 @@ public static void enableNetworkSynchronization(boolean enable) {
402404
networkSyncEnabled = enable;
403405
}
404406

405-
private static ReactNativeNetworkIdlingResource networkIR = null;
407+
private static RNExperimentalNetworkIR networkIR = null;
406408
private final static String CLASS_NETWORK_MODULE = "com.facebook.react.modules.network.NetworkingModule";
407409
private final static String METHOD_GET_NATIVE_MODULE = "getNativeModule";
408410
private final static String METHOD_HAS_NATIVE_MODULE = "hasNativeModule";
@@ -431,7 +433,7 @@ private static void setupNetworkIdlingResource() {
431433
.call(METHOD_GET_NATIVE_MODULE, networkModuleClass)
432434
.field(FIELD_OKHTTP_CLIENT)
433435
.get();
434-
networkIR = new ReactNativeNetworkIdlingResource(client.dispatcher());
436+
networkIR = new RNExperimentalNetworkIR(client.dispatcher());
435437
Espresso.registerIdlingResources(networkIR);
436438
} catch (ReflectException e) {
437439
Log.e(LOG_TAG, "Can't set up Networking Module listener", e.getCause());

detox/android/detox/src/main/java/com/wix/detox/espresso/EspressoDetox.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import android.content.Context;
55
import android.content.ContextWrapper;
66
import android.content.pm.ActivityInfo;
7+
import android.support.test.InstrumentationRegistry;
78
import android.support.test.espresso.UiController;
89
import android.support.test.espresso.ViewAction;
910
import android.support.test.espresso.ViewInteraction;
@@ -16,6 +17,8 @@
1617
import org.hamcrest.Matcher;
1718
import org.joor.Reflect;
1819

20+
import java.util.ArrayList;
21+
1922
import static android.support.test.espresso.Espresso.onView;
2023
import static android.support.test.espresso.matcher.ViewMatchers.isRoot;
2124

@@ -99,5 +102,14 @@ public void perform(UiController uiController, View view) {
99102
public static void setSynchronization(boolean enabled) {
100103
ReactNativeSupport.enableNetworkSynchronization(enabled);
101104
}
105+
106+
public static void setURLBlacklist(final ArrayList<String> urls) {
107+
InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
108+
@Override
109+
public void run() {
110+
RNExperimentalNetworkIR.setURLBlacklist(urls);
111+
}
112+
});
113+
}
102114
}
103115

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
package com.wix.detox.espresso;
2+
3+
import android.support.annotation.NonNull;
4+
import android.support.test.espresso.IdlingResource;
5+
import android.util.Log;
6+
import android.view.Choreographer;
7+
8+
import java.util.ArrayList;
9+
import java.util.concurrent.atomic.AtomicBoolean;
10+
import java.util.regex.Pattern;
11+
import java.util.regex.PatternSyntaxException;
12+
13+
import okhttp3.Dispatcher;
14+
import okhttp3.Call;
15+
import java.util.List;
16+
17+
/**
18+
* Created by simonracz on 09/10/2017.
19+
*/
20+
21+
22+
/**
23+
* Idling Resource which monitors React Native's OkHttpClient.
24+
* <p>
25+
* Must call stop() on it, before removing it from Espresso.
26+
*/
27+
public class RNExperimentalNetworkIR implements IdlingResource, Choreographer.FrameCallback {
28+
29+
private static final String LOG_TAG = "Detox";
30+
31+
// private static final String FIELD_RUNNING_ASYNC_CALLS = "runningAsyncCalls";
32+
// private static final String FIELD_RUNNING_SYNC_CALLS = "runningSyncCalls";
33+
// private static final String FIELD_READY_ASYNC_CALLS = "readyAsyncCalls";
34+
35+
private AtomicBoolean stopped = new AtomicBoolean(false);
36+
37+
private ResourceCallback callback;
38+
private Dispatcher dispatcher;
39+
40+
private static final ArrayList<Pattern> blacklist = new ArrayList<>();
41+
42+
/**
43+
* Must be called on the UI thread.
44+
*
45+
* @param urls list of regexes of blacklisted urls
46+
*/
47+
public static void setURLBlacklist(ArrayList<String> urls) {
48+
blacklist.clear();
49+
for (String url : urls) {
50+
try {
51+
blacklist.add(Pattern.compile(url));
52+
} catch (PatternSyntaxException e) {
53+
Log.e(LOG_TAG, "Couldn't parse regular expression for Black list url: " + url, e);
54+
}
55+
}
56+
}
57+
58+
59+
public RNExperimentalNetworkIR(@NonNull Dispatcher dispatcher) {
60+
this.dispatcher = dispatcher;
61+
}
62+
63+
@Override
64+
public String getName() {
65+
return RNExperimentalNetworkIR.class.getName();
66+
}
67+
68+
@Override
69+
public boolean isIdleNow() {
70+
if (stopped.get()) {
71+
if (callback != null) {
72+
callback.onTransitionToIdle();
73+
}
74+
return true;
75+
}
76+
boolean idle = true;
77+
List<Call> calls = dispatcher.runningCalls();
78+
for (Call call : calls) {
79+
idle = false;
80+
String url = call.request().url().toString();
81+
for (Pattern pattern : blacklist) {
82+
if (pattern.matcher(url).matches()) {
83+
idle = true;
84+
break;
85+
}
86+
}
87+
if (!idle) {
88+
break;
89+
}
90+
}
91+
if (!idle) {
92+
Choreographer.getInstance().postFrameCallback(this);
93+
Log.i(LOG_TAG, "Network is busy");
94+
} else {
95+
if (callback != null) {
96+
callback.onTransitionToIdle();
97+
}
98+
}
99+
return idle;
100+
}
101+
102+
@Override
103+
public void registerIdleTransitionCallback(ResourceCallback callback) {
104+
this.callback = callback;
105+
Choreographer.getInstance().postFrameCallback(this);
106+
}
107+
108+
@Override
109+
public void doFrame(long frameTimeNanos) {
110+
isIdleNow();
111+
}
112+
113+
public void stop() {
114+
stopped.set(true);
115+
}
116+
117+
}
118+
119+
120+
121+

detox/android/detox/src/main/java/com/wix/detox/espresso/ReactNativeNetworkIdlingResource.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77

88
import org.joor.Reflect;
99

10+
import java.util.ArrayList;
11+
import java.util.regex.Pattern;
12+
import java.util.regex.PatternSyntaxException;
13+
1014
import okhttp3.Dispatcher;
1115

1216
/**
@@ -18,6 +22,7 @@
1822
* Idling Resource which monitors React Native's OkHttpClient.
1923
*
2024
* Must call stop() on it, before removing it from Espresso.
25+
* @deprecated Please use RNExperimentalNetworkIR
2126
*/
2227
public class ReactNativeNetworkIdlingResource implements IdlingResource {
2328

detox/android/detox/src/main/java/com/wix/detox/espresso/ReactNativeTimersIdlingResource.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import org.joor.ReflectException;
1010

1111
import java.util.PriorityQueue;
12+
import java.util.concurrent.atomic.AtomicBoolean;
1213

1314
/**
1415
* Created by simonracz on 23/05/2017.
@@ -39,6 +40,8 @@ public class ReactNativeTimersIdlingResource implements IdlingResource, Choreogr
3940
private final static String FIELD_TARGET_TIME = "mTargetTime";
4041
private final static String LOCK_TIMER = "mTimerGuard";
4142

43+
private AtomicBoolean stopped = new AtomicBoolean(false);
44+
4245
private static final long LOOK_AHEAD_MS = 15;
4346

4447
private ResourceCallback callback = null;
@@ -55,6 +58,12 @@ public String getName() {
5558

5659
@Override
5760
public boolean isIdleNow() {
61+
if (stopped.get()) {
62+
if (callback != null) {
63+
callback.onTransitionToIdle();
64+
}
65+
return true;
66+
}
5867
Class<?> timingClass = null;
5968
Class<?> timerClass = null;
6069
try {
@@ -137,4 +146,8 @@ public void registerIdleTransitionCallback(ResourceCallback callback) {
137146
public void doFrame(long frameTimeNanos) {
138147
isIdleNow();
139148
}
149+
150+
public void stop() {
151+
stopped.set(true);
152+
}
140153
}

detox/src/devices/AndroidDriver.js

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ const DeviceDriverBase = require('./DeviceDriverBase');
99
const EspressoDetox = 'com.wix.detox.espresso.EspressoDetox';
1010

1111
class AndroidDriver extends DeviceDriverBase {
12-
1312
constructor(client) {
1413
super(client);
1514
const expect = require('../android/expect');
@@ -20,7 +19,6 @@ class AndroidDriver extends DeviceDriverBase {
2019
this.adb = new ADB();
2120
}
2221

23-
2422
async getBundleIdFromBinary(apkPath) {
2523
return await this.aapt.getPackageName(apkPath);
2624
}
@@ -52,7 +50,7 @@ class AndroidDriver extends DeviceDriverBase {
5250
});
5351

5452
if (this.instrumentationProcess) {
55-
let call = invoke.call(invoke.Android.Class("com.wix.detox.Detox"), 'launchMainActivity');
53+
const call = invoke.call(invoke.Android.Class("com.wix.detox.Detox"), 'launchMainActivity');
5654
await this.invocationManager.execute(call);
5755
return this.instrumentationProcess.pid;
5856
}
@@ -76,13 +74,13 @@ class AndroidDriver extends DeviceDriverBase {
7674
}
7775

7876
async openURL(deviceId, params) {
79-
let call = invoke.call(invoke.Android.Class("com.wix.detox.Detox"), 'startActivityFromUrl', invoke.Android.String(params.url));
77+
const call = invoke.call(invoke.Android.Class("com.wix.detox.Detox"), 'startActivityFromUrl', invoke.Android.String(params.url));
8078
await this.invocationManager.execute(call);
8179
}
8280

8381
async sendToHome(deviceId, params) {
84-
let uiDevice = invoke.call(invoke.Android.Class("com.wix.detox.uiautomator.UiAutomator"), 'uiDevice');
85-
let call = invoke.call(uiDevice, 'pressHome');
82+
const uiDevice = invoke.call(invoke.Android.Class("com.wix.detox.uiautomator.UiAutomator"), 'uiDevice');
83+
const call = invoke.call(uiDevice, 'pressHome');
8684
await this.invocationManager.execute(call);
8785
}
8886

@@ -110,24 +108,28 @@ class AndroidDriver extends DeviceDriverBase {
110108
return 'android';
111109
}
112110

111+
async setURLBlacklist(urlList) {
112+
const call = invoke.call(invoke.Android.Class(EspressoDetox), 'setURLBlacklist', urlList);
113+
await this.invocationManager.execute(call);
114+
}
115+
113116
async enableSynchronization() {
114-
let call = invoke.call(invoke.Android.Class(EspressoDetox), 'setSynchronization', invoke.Android.Boolean(true));
117+
const call = invoke.call(invoke.Android.Class(EspressoDetox), 'setSynchronization', invoke.Android.Boolean(true));
115118
await this.invocationManager.execute(call);
116119
}
117120

118121
async disableSynchronization() {
119-
let call = invoke.call(invoke.Android.Class(EspressoDetox), 'setSynchronization', invoke.Android.Boolean(false));
122+
const call = invoke.call(invoke.Android.Class(EspressoDetox), 'setSynchronization', invoke.Android.Boolean(false));
120123
await this.invocationManager.execute(call);
121124
}
122125

123126
async setOrientation(deviceId, orientation) {
124127
const orientationMapping = {
125128
landscape: 1, // top at left side landscape
126-
portrait: 0 // non-reversed portrait.
129+
portrait: 0 // non-reversed portrait.
127130
};
128-
const EspressoDetox = 'com.wix.detox.espresso.EspressoDetox';
129-
const invoke = require('../invoke');
130-
let call = invoke.call(invoke.Android.Class(EspressoDetox), 'changeOrientation', invoke.Android.Integer(orientationMapping[orientation]));
131+
132+
const call = invoke.call(invoke.Android.Class(EspressoDetox), 'changeOrientation', invoke.Android.Integer(orientationMapping[orientation]));
131133
await this.invocationManager.execute(call);
132134
}
133135
}

detox/src/invoke/Invoke.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,5 +44,5 @@ const genericInvokeObject = new Proxy({},
4444
module.exports = {
4545
call,
4646
callDirectly,
47-
genericInvokeObject,
47+
genericInvokeObject
4848
};

detox/test/e2e/m-network.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ describe('Network Synchronization', () => {
3838
});
3939

4040

41-
it(':ios: setURLBlacklist() should disable synchronization for given endpoint', async () => {
42-
const url = device.getPlatform() === 'ios' ? '.*localhost.*' : '*10.0.2.2*';
41+
it('setURLBlacklist() should disable synchronization for given endpoint', async () => {
42+
const url = device.getPlatform() === 'ios' ? '.*localhost.*' : '.*10.0.2.2.*';
4343
await device.setURLBlacklist([url]);
4444

4545
await element(by.id('LongNetworkRequest')).tap();

0 commit comments

Comments
 (0)