mirror of
https://github.com/silkimen/cordova-plugin-advanced-http.git
synced 2026-07-26 00:00:14 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b6a8acd25b | ||
|
|
0ccf64e289 | ||
|
|
986ddeefd4 | ||
|
|
67c2c733f5 | ||
|
|
d077d02062 | ||
|
|
060794354d | ||
|
|
4ebd259c7e | ||
|
|
bbd4cf01ab | ||
|
|
7eb3395aa4 | ||
|
|
05abdfcd91 | ||
|
|
bda4eedfb9 | ||
|
|
9d6005af29 | ||
|
|
94126b9021 | ||
|
|
007d5c609f | ||
|
|
badf6dcdc2 | ||
|
|
c081060a9e | ||
|
|
2ce130133c | ||
|
|
cfcc572ca0 | ||
|
|
d7688b485d | ||
|
|
81ba667e37 | ||
|
|
5ad1967b46 | ||
|
|
90ed474b29 | ||
|
|
1947906c4c | ||
|
|
b25b07a2a7 | ||
|
|
a01625ecfb | ||
|
|
b03ae7d6d1 | ||
|
|
fdaea418be | ||
|
|
d84df0fb00 | ||
|
|
baedd60329 | ||
|
|
80b22d4202 | ||
|
|
89ac260ef6 | ||
|
|
6a266218fb | ||
|
|
6050830423 | ||
|
|
95c9eb85d0 | ||
|
|
7e3ff25195 | ||
|
|
8a6b9e583e | ||
|
|
b32dfc7143 | ||
|
|
a72bf758c6 | ||
|
|
45f9cbfaa4 | ||
|
|
f8f52e1e97 | ||
|
|
9b995724f4 | ||
|
|
25a0a9a7ae | ||
|
|
e44def06a5 | ||
|
|
3d288951bf | ||
|
|
f39063cec9 | ||
|
|
1bcb8016ff |
@@ -0,0 +1 @@
|
||||
github: silkimen
|
||||
@@ -53,8 +53,8 @@ jobs:
|
||||
java-version: 1.8
|
||||
- name: Update test cert for httpbin.org
|
||||
run: npm run updatecert
|
||||
- name: Add workaround for mipsel reference
|
||||
run: sudo mkdir -p $ANDROID_HOME/ndk-bundle/toolchains/mips64el-linux-android-4.9/prebuilt/linux-x86_64
|
||||
- name: Add workaround for missing DX files in build-tools 31 (https://stackoverflow.com/a/68430992)
|
||||
run: ln -s $ANDROID_HOME/build-tools/31.0.0/d8 $ANDROID_HOME/build-tools/31.0.0/dx && ln -s $ANDROID_HOME/build-tools/31.0.0/lib/d8.jar $ANDROID_HOME/build-tools/31.0.0/lib/dx.jar
|
||||
- name: Build test app
|
||||
run: scripts/build-test-app.sh --android --device
|
||||
- name: Upload artifact to BrowserStack
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
# For most projects, this workflow file will not need changing; you simply need
|
||||
# to commit it to your repository.
|
||||
#
|
||||
# You may wish to alter this file to override the set of languages analyzed,
|
||||
# or to provide custom queries or build logic.
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [master]
|
||||
schedule:
|
||||
- cron: '0 20 * * 3'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
# Override automatic language detection by changing the below list
|
||||
# Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python']
|
||||
language: ['javascript']
|
||||
# Learn more...
|
||||
# https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v1
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
|
||||
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
||||
# and modify them (or add more) to build your code if your project
|
||||
# uses a compiled language
|
||||
|
||||
#- run: |
|
||||
# make bootstrap
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
||||
@@ -7,3 +7,5 @@ npm-debug.log
|
||||
/temp
|
||||
/android-sdk-macosx.zip
|
||||
/android-sdk-macosx/**
|
||||
.idea/
|
||||
*.iml
|
||||
|
||||
+4
-7
@@ -1,7 +1,3 @@
|
||||
notifications:
|
||||
slack:
|
||||
secure: twDT06GAiu0jsKizow7TcghZj70KbuuTrlo02QGmbSxBk2rJfsXSrHAsA3+s/9Q4mudENk6na7fs1aPCxz+u2etUGp+2PaJKVKR5n3jrNNt3SnYeWsBgVo7o7H1aLXatX3a+TdPXh1F5gQ4Ycr93nTYbW/077jsOholwbOHDZE3VcU9dzNPwFaEvhrDbr/ei3tef0ZiM1qxIad74TgwWMKClwai3I7HCVkZOPsyV+ve6cdIJ8Dt47JzFUHSW3SZuoe5Kywxvp0VvMo/QAJw95y3edNafx4EXHwbaN71rpGWSJXIKSZzcSQalZJ9DxGYspIBkWvGsNuQRzG9CzIoNQK10iERlIVC5vKDfKX22gayOQPSDkswJzIduylBUC8zdTPCndXyNEM/Lrj6hg+ksFWN58vYNPgfUeiga7X+LV5HytftsMFW+xx2kbnGeU8doGeX8Q8G7h9OIkHCTTG7R0ldYMIqTm8YJGPkRIv4OReC5ZOhiZD+wSg4KQ0wmMeRi+hyn+I5UPnKEOHAIN8FmLNCZFbgr1wuPFp9xnJIOcumQnQVZ2t6vk6IjIbwhYPWCnf7Sr4BvJxE8eyiLrEaXK0FiPb3My9wK9tLFjj1zdD7e4+SLq+WFMeCxp2eXOGF0Bu+2VK2tGjgWhaudaIpjbRQAAQ5nPa43h16NruEvNWI=
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- node_modules
|
||||
@@ -15,10 +11,11 @@ matrix:
|
||||
language: objective-c
|
||||
sudo: false
|
||||
os: osx
|
||||
osx_image: xcode10.1
|
||||
osx_image: xcode12.5
|
||||
|
||||
before_install:
|
||||
- export LANG=en_US.UTF-8
|
||||
- export LANG=en_US.UTF-8 &&
|
||||
nvm use 14
|
||||
|
||||
install:
|
||||
- npm install
|
||||
@@ -46,7 +43,7 @@ matrix:
|
||||
|
||||
before_install:
|
||||
- export LANG=en_US.UTF-8 &&
|
||||
curl -sL https://deb.nodesource.com/setup_10.x | sudo -E bash - &&
|
||||
curl -sL https://deb.nodesource.com/setup_14.x | sudo -E bash - &&
|
||||
sudo apt-get install -y nodejs
|
||||
- yes | sdkmanager --update
|
||||
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
# Changelog
|
||||
|
||||
## 3.2.0
|
||||
|
||||
- Feature #420: implement blacklist feature to disable SSL/TLS versions on Android (thanks to @MobisysGmbH)
|
||||
|
||||
## 3.1.1
|
||||
|
||||
- Fixed #372: malformed empty multipart request on Android
|
||||
- Fixed #399: memory leakage leads to app crashes on iOS (thanks avargaskun)
|
||||
|
||||
## 3.1.0
|
||||
|
||||
- Feature #272: add support for aborting requests (thanks russaa)
|
||||
|
||||
@@ -4,7 +4,7 @@ Cordova Advanced HTTP
|
||||
[](https://opensource.org/licenses/mit-license.php)
|
||||
[](https://www.npmjs.com/package/cordova-plugin-advanced-http)
|
||||
|
||||
[](https://travis-ci.org/silkimen/cordova-plugin-advanced-http)
|
||||
[](https://travis-ci.com/silkimen/cordova-plugin-advanced-http)
|
||||
[](https://github.com/silkimen/cordova-plugin-advanced-http/actions)
|
||||
|
||||
|
||||
@@ -34,6 +34,15 @@ phonegap plugin add cordova-plugin-advanced-http
|
||||
cordova plugin add cordova-plugin-advanced-http
|
||||
```
|
||||
|
||||
### Plugin Preferences
|
||||
|
||||
`AndroidBlacklistSecureSocketProtocols`: define a blacklist of secure socket protocols for Android. This preference allows you to disable protocols which are considered unsafe. You need to provide a comma-separated list of protocols ([check Android SSLSocket#protocols docu for protocol names](https://developer.android.com/reference/javax/net/ssl/SSLSocket#protocols)).
|
||||
|
||||
e.g. blacklist `SSLv3` and `TLSv1`:
|
||||
```xml
|
||||
<preference name="AndroidBlacklistSecureSocketProtocols" value="SSLv3,TLSv1" />
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Plain Cordova
|
||||
@@ -477,6 +486,28 @@ This plugin uses amazing cloud services to maintain quality. CI Builds and E2E t
|
||||
* [BrowserStack](https://www.browserstack.com/)
|
||||
* [Sauce Labs](https://saucelabs.com/)
|
||||
* [httpbin.org](https://httpbin.org/)
|
||||
* [go-httpbin](https://httpbingo.org/)
|
||||
|
||||
### Local Testing
|
||||
|
||||
First, install current package with `npm install` to fetch dev dependencies.
|
||||
|
||||
Then, to execute Javascript tests:
|
||||
```shell
|
||||
npm run testjs
|
||||
```
|
||||
|
||||
And, to execute E2E tests:
|
||||
- setup local Android sdk and emulators, or Xcode and simulators for iOS
|
||||
- launch emulator or simulator
|
||||
- install [Appium](http://appium.io/) (see [Getting Started](https://github.com/appium/appium/blob/HEAD/docs/en/about-appium/getting-started.md))
|
||||
- start `appium`
|
||||
- run
|
||||
- updating client and server certificates, building test app, and running e2e tests
|
||||
```shell
|
||||
npm run testandroid
|
||||
npm run testios
|
||||
```
|
||||
|
||||
## Contribute & Develop
|
||||
|
||||
|
||||
Generated
+9055
-2074
File diff suppressed because it is too large
Load Diff
+3
-3
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "cordova-plugin-advanced-http",
|
||||
"version": "3.1.0",
|
||||
"version": "3.2.0",
|
||||
"description": "Cordova / Phonegap plugin for communicating with HTTP servers using SSL pinning",
|
||||
"scripts": {
|
||||
"updatecert": "node ./scripts/update-e2e-server-cert.js && node ./scripts/update-e2e-client-cert.js",
|
||||
@@ -60,8 +60,8 @@
|
||||
"devDependencies": {
|
||||
"chai": "4.2.0",
|
||||
"colors": "1.4.0",
|
||||
"cordova": "9.0.0",
|
||||
"mocha": "8.0.1",
|
||||
"cordova": "10.0.0",
|
||||
"mocha": "8.2.0",
|
||||
"umd-tough-cookie": "2.4.3",
|
||||
"wd": "1.12.1",
|
||||
"xml2js": "0.4.23"
|
||||
|
||||
+2
-1
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<plugin xmlns="http://www.phonegap.com/ns/plugins/1.0" xmlns:android="http://schemas.android.com/apk/res/android" id="cordova-plugin-advanced-http" version="3.1.0">
|
||||
<plugin xmlns="http://www.phonegap.com/ns/plugins/1.0" xmlns:android="http://schemas.android.com/apk/res/android" id="cordova-plugin-advanced-http" version="3.2.0">
|
||||
<name>Advanced HTTP plugin</name>
|
||||
<description>
|
||||
Cordova / Phonegap plugin for communicating with HTTP servers using SSL pinning
|
||||
@@ -8,6 +8,7 @@
|
||||
<engine name="cordova" version=">=4.0.0"/>
|
||||
</engines>
|
||||
<dependency id="cordova-plugin-file" version=">=2.0.0"/>
|
||||
<preference name="AndroidBlacklistSecureSocketProtocols" default="SSLv3,TLSv1"/>
|
||||
<js-module src="www/cookie-handler.js" name="cookie-handler"/>
|
||||
<js-module src="www/dependency-validator.js" name="dependency-validator"/>
|
||||
<js-module src="www/error-codes.js" name="error-codes"/>
|
||||
|
||||
+1
-1
@@ -10,5 +10,5 @@ fi
|
||||
|
||||
printf 'Running e2e tests\n'
|
||||
pushd $ROOT
|
||||
./node_modules/.bin/mocha ./test/e2e-tooling/test.js "$@"
|
||||
./node_modules/.bin/mocha --reporter ./test/e2e-tooling/reporter.js ./test/e2e-tooling/test.js "$@"
|
||||
popd
|
||||
|
||||
@@ -72,8 +72,8 @@ abstract class CordovaHttpBase implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
CordovaHttpResponse response = new CordovaHttpResponse();
|
||||
|
||||
HttpRequest request = null;
|
||||
|
||||
try {
|
||||
request = this.createRequest();
|
||||
this.prepareRequest(request);
|
||||
@@ -81,27 +81,27 @@ abstract class CordovaHttpBase implements Runnable {
|
||||
this.processResponse(request, response);
|
||||
request.disconnect();
|
||||
} catch (HttpRequestException e) {
|
||||
if (e.getCause() instanceof SSLException) {
|
||||
Throwable cause = e.getCause();
|
||||
String message = cause.getMessage();
|
||||
|
||||
if (cause instanceof SSLException) {
|
||||
response.setStatus(-2);
|
||||
response.setErrorMessage("TLS connection could not be established: " + e.getMessage());
|
||||
Log.w(TAG, "TLS connection could not be established", e);
|
||||
} else if (e.getCause() instanceof UnknownHostException) {
|
||||
} else if (cause instanceof UnknownHostException) {
|
||||
response.setStatus(-3);
|
||||
response.setErrorMessage("Host could not be resolved: " + e.getMessage());
|
||||
Log.w(TAG, "Host could not be resolved", e);
|
||||
} else if (e.getCause() instanceof SocketTimeoutException) {
|
||||
} else if (cause instanceof SocketTimeoutException) {
|
||||
response.setStatus(-4);
|
||||
response.setErrorMessage("Request timed out: " + e.getMessage());
|
||||
Log.w(TAG, "Request timed out", e);
|
||||
} else if (cause instanceof InterruptedIOException && "thread interrupted".equals(message.toLowerCase())) {
|
||||
this.setAborted(request, response);
|
||||
} else {
|
||||
String cause = e.getCause().getMessage();
|
||||
if(e.getCause() instanceof InterruptedIOException && "thread interrupted".equals(cause.toLowerCase())){
|
||||
this.setAborted(request, response);
|
||||
} else {
|
||||
response.setStatus(-1);
|
||||
response.setErrorMessage("There was an error with the request: " + cause);
|
||||
Log.w(TAG, "Generic request error", e);
|
||||
}
|
||||
response.setStatus(-1);
|
||||
response.setErrorMessage("There was an error with the request: " + message);
|
||||
Log.w(TAG, "Generic request error", e);
|
||||
}
|
||||
} catch (InterruptedException ie) {
|
||||
this.setAborted(request, response);
|
||||
@@ -154,7 +154,7 @@ abstract class CordovaHttpBase implements Runnable {
|
||||
} else if ("urlencoded".equals(this.serializer)) {
|
||||
// intentionally left blank, because content type is set in HttpRequest.form()
|
||||
} else if ("multipart".equals(this.serializer)) {
|
||||
request.contentType("multipart/form-data");
|
||||
// intentionally left blank, because content type is set in HttpRequest.part()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,6 +187,12 @@ abstract class CordovaHttpBase implements Runnable {
|
||||
request.part(name, fileNames.getString(i), types.getString(i), new ByteArrayInputStream(bytes));
|
||||
}
|
||||
}
|
||||
|
||||
// prevent sending malformed empty multipart requests (#372)
|
||||
if (buffers.length() == 0) {
|
||||
request.contentType("multipart/form-data; boundary=00content0boundary00");
|
||||
request.send("\r\n--00content0boundary00--\r\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,13 +219,15 @@ abstract class CordovaHttpBase implements Runnable {
|
||||
protected void setAborted(HttpRequest request, CordovaHttpResponse response) {
|
||||
response.setStatus(-8);
|
||||
response.setErrorMessage("Request was aborted");
|
||||
if(request != null){
|
||||
try{
|
||||
|
||||
if (request != null) {
|
||||
try {
|
||||
request.disconnect();
|
||||
} catch(Exception any){
|
||||
Log.w(TAG, "Failed to close aborted request", any);
|
||||
}
|
||||
}
|
||||
|
||||
Log.i(TAG, "Request was aborted");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,13 @@ public class CordovaHttpPlugin extends CordovaPlugin implements Observer {
|
||||
|
||||
this.tlsConfiguration.setHostnameVerifier(null);
|
||||
this.tlsConfiguration.setTrustManagers(tmf.getTrustManagers());
|
||||
|
||||
if (this.preferences.contains("androidblacklistsecuresocketprotocols")) {
|
||||
this.tlsConfiguration.setBlacklistedProtocols(
|
||||
this.preferences.getString("androidblacklistsecuresocketprotocols", "").split(",")
|
||||
);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "An error occured while loading system's CA certificates", e);
|
||||
}
|
||||
@@ -199,16 +206,17 @@ public class CordovaHttpPlugin extends CordovaPlugin implements Observer {
|
||||
}
|
||||
|
||||
private boolean abort(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
|
||||
int reqId = args.getInt(0);
|
||||
boolean result = false;
|
||||
// NOTE no synchronized (reqMapLock), since even if the req was already removed from reqMap,
|
||||
// the worst that would happen calling task.cancel(true) is a result of false
|
||||
// (i.e. same result as locking & not finding the req in reqMap)
|
||||
Future<?> task = this.reqMap.get(reqId);
|
||||
|
||||
if (task != null && !task.isDone()) {
|
||||
result = task.cancel(true);
|
||||
}
|
||||
|
||||
callbackContext.success(new JSONObject().put("aborted", result));
|
||||
|
||||
return true;
|
||||
@@ -216,7 +224,7 @@ public class CordovaHttpPlugin extends CordovaPlugin implements Observer {
|
||||
|
||||
private void addReq(final Integer reqId, final Future<?> task, final CordovaObservableCallbackContext observableCallbackContext) {
|
||||
synchronized (reqMapLock) {
|
||||
if(!task.isDone()){
|
||||
if (!task.isDone()){
|
||||
this.reqMap.put(reqId, task);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,9 +13,10 @@ import javax.net.ssl.TrustManager;
|
||||
import com.silkimen.http.TLSSocketFactory;
|
||||
|
||||
public class TLSConfiguration {
|
||||
private TrustManager[] trustManagers;
|
||||
private KeyManager[] keyManagers;
|
||||
private HostnameVerifier hostnameVerifier;
|
||||
private TrustManager[] trustManagers = null;
|
||||
private KeyManager[] keyManagers = null;
|
||||
private HostnameVerifier hostnameVerifier = null;
|
||||
private String[] blacklistedProtocols = {};
|
||||
|
||||
private SSLSocketFactory socketFactory;
|
||||
|
||||
@@ -33,6 +34,11 @@ public class TLSConfiguration {
|
||||
this.socketFactory = null;
|
||||
}
|
||||
|
||||
public void setBlacklistedProtocols(String[] protocols) {
|
||||
this.blacklistedProtocols = protocols;
|
||||
this.socketFactory = null;
|
||||
}
|
||||
|
||||
public HostnameVerifier getHostnameVerifier() {
|
||||
return this.hostnameVerifier;
|
||||
}
|
||||
@@ -46,12 +52,7 @@ public class TLSConfiguration {
|
||||
SSLContext context = SSLContext.getInstance("TLS");
|
||||
|
||||
context.init(this.keyManagers, this.trustManagers, new SecureRandom());
|
||||
|
||||
if (android.os.Build.VERSION.SDK_INT < 20) {
|
||||
this.socketFactory = new TLSSocketFactory(context);
|
||||
} else {
|
||||
this.socketFactory = context.getSocketFactory();
|
||||
}
|
||||
this.socketFactory = new TLSSocketFactory(context, this.blacklistedProtocols);
|
||||
|
||||
return this.socketFactory;
|
||||
} catch (GeneralSecurityException e) {
|
||||
|
||||
@@ -5,6 +5,9 @@ import java.net.InetAddress;
|
||||
import java.net.Socket;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLSocket;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
@@ -12,9 +15,11 @@ import javax.net.ssl.SSLSocketFactory;
|
||||
public class TLSSocketFactory extends SSLSocketFactory {
|
||||
|
||||
private SSLSocketFactory delegate;
|
||||
private String[] blacklistedProtocols;
|
||||
|
||||
public TLSSocketFactory(SSLContext context) {
|
||||
delegate = context.getSocketFactory();
|
||||
public TLSSocketFactory(SSLContext context, String[] blacklistedProtocols) {
|
||||
this.delegate = context.getSocketFactory();
|
||||
this.blacklistedProtocols = Arrays.stream(blacklistedProtocols).map(String::trim).toArray(String[]::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -55,9 +60,18 @@ public class TLSSocketFactory extends SSLSocketFactory {
|
||||
}
|
||||
|
||||
private Socket enableTLSOnSocket(Socket socket) {
|
||||
if (socket != null && (socket instanceof SSLSocket)) {
|
||||
((SSLSocket) socket).setEnabledProtocols(new String[] { "TLSv1", "TLSv1.1", "TLSv1.2" });
|
||||
if (socket == null || !(socket instanceof SSLSocket)) {
|
||||
return socket;
|
||||
}
|
||||
|
||||
String[] supported = ((SSLSocket) socket).getSupportedProtocols();
|
||||
|
||||
String[] filtered = Arrays.stream(supported).filter(
|
||||
val -> Arrays.stream(this.blacklistedProtocols).noneMatch(val::equals)
|
||||
).toArray(String[]::new);
|
||||
|
||||
((SSLSocket) socket).setEnabledProtocols(filtered);
|
||||
|
||||
return socket;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,6 +228,7 @@
|
||||
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary];
|
||||
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
|
||||
[manager invalidateSessionCancelingTasks:YES];
|
||||
};
|
||||
|
||||
void (^onFailure)(NSURLSessionTask *, NSError *) = ^(NSURLSessionTask *task, NSError *error) {
|
||||
@@ -239,6 +240,7 @@
|
||||
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
|
||||
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
|
||||
[manager invalidateSessionCancelingTasks:YES];
|
||||
};
|
||||
|
||||
NSURLSessionDataTask *task = [manager downloadTaskWithHTTPMethod:method URLString:url parameters:nil progress:nil success:onSuccess failure:onFailure];
|
||||
@@ -316,6 +318,7 @@
|
||||
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary];
|
||||
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
|
||||
[manager invalidateSessionCancelingTasks:YES];
|
||||
};
|
||||
|
||||
void (^onFailure)(NSURLSessionTask *, NSError *) = ^(NSURLSessionTask *task, NSError *error) {
|
||||
@@ -327,6 +330,7 @@
|
||||
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
|
||||
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
|
||||
[manager invalidateSessionCancelingTasks:YES];
|
||||
};
|
||||
|
||||
NSURLSessionDataTask *task;
|
||||
|
||||
@@ -26,9 +26,6 @@
|
||||
<allow-intent href="itms:*" />
|
||||
<allow-intent href="itms-apps:*" />
|
||||
</platform>
|
||||
<engine name="android" spec="8.1.0" />
|
||||
<engine name="browser" spec="6.0.0" />
|
||||
<engine name="ios" spec="5.1.0" />
|
||||
<plugin name="cordova-plugin-file" spec="6.0.2" />
|
||||
<preference name="AndroidPersistentFileLocation" value="Internal" />
|
||||
<preference name="AndroidBlacklistSecureSocketProtocols" value="SSLv3,TLSv1" />
|
||||
</widget>
|
||||
|
||||
@@ -3,5 +3,7 @@
|
||||
<domain-config cleartextTrafficPermitted="true">
|
||||
<domain includeSubdomains="true">localhost</domain>
|
||||
<domain includeSubdomains="true">httpbin.org</domain>
|
||||
<domain includeSubdomains="true">httpbingo.org</domain>
|
||||
<domain includeSubdomains="true">www.columbia.edu</domain>
|
||||
</domain-config>
|
||||
</network-security-config>
|
||||
|
||||
@@ -11,21 +11,25 @@
|
||||
"author": "Sefa Ilkimen",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"cordova": "9.0.0",
|
||||
"cordova-android": "8.1.0",
|
||||
"cordova": "10.0.0",
|
||||
"cordova-android": "9.0.0",
|
||||
"cordova-browser": "6.0.0",
|
||||
"cordova-ios": "5.1.0"
|
||||
"cordova-ios": "6.2.0",
|
||||
"cordova-plugin-device": "2.0.3",
|
||||
"cordova-plugin-wkwebview-file-xhr": "3.0.0"
|
||||
},
|
||||
"cordova": {
|
||||
"platforms": [
|
||||
"android",
|
||||
"ios"
|
||||
"ios",
|
||||
"browser"
|
||||
],
|
||||
"plugins": {
|
||||
"cordova-plugin-device": {}
|
||||
"cordova-plugin-device": {},
|
||||
"cordova-plugin-wkwebview-file-xhr": {}
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"cordova-plugin-device": "2.0.3"
|
||||
}
|
||||
}
|
||||
}
|
||||
+92
-38
@@ -86,16 +86,31 @@ const helpers = {
|
||||
},
|
||||
isAbortSupported: function () {
|
||||
if (window.cordova && window.cordova.platformId === 'android') {
|
||||
var version = device.version; //NOTE will throw error if cordova is present without cordova-plugin-device
|
||||
var version = device.version; // NOTE will throw error if cordova is present without cordova-plugin-device
|
||||
var major = parseInt(/^(\d+)(\.|$)/.exec(version)[1], 10);
|
||||
return isFinite(major) && major >= 6;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
getAbortDelay: function () { return 10; },
|
||||
getAbortDelay: function () { return 0; },
|
||||
getDemoArrayBuffer: function(size) {
|
||||
var demoText = [73, 39, 109, 32, 97, 32, 100, 117, 109, 109, 121, 32, 102, 105, 108, 101, 33, 32, 73, 39, 109, 32, 117, 115, 101, 100, 32, 102, 111, 114, 32, 116, 101, 115, 116, 105, 110, 103, 32, 112, 117, 114, 112, 111, 115, 101, 115, 46, 32, 82, 97, 110, 100, 111, 109, 32, 100, 97, 116, 97, 32, 105, 115, 32, 102, 111, 108, 108, 111, 119, 105, 110, 103, 58, 32];
|
||||
var buffer = new ArrayBuffer(size);
|
||||
var view = new Uint8Array(buffer);
|
||||
|
||||
for (var i = 0; i < size; ++i) {
|
||||
view[i] = demoText[i];
|
||||
}
|
||||
|
||||
return buffer;
|
||||
},
|
||||
isTlsBlacklistSupported: function () {
|
||||
return window.cordova && window.cordova.platformId === 'android';
|
||||
}
|
||||
};
|
||||
|
||||
const messageFactory = {
|
||||
handshakeFailed: function() { return 'TLS connection could not be established: javax.net.ssl.SSLHandshakeException: Handshake failed' },
|
||||
sslTrustAnchor: function () { return 'TLS connection could not be established: javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.' },
|
||||
invalidCertificate: function (domain) { return 'The certificate for this server is invalid. You might be connecting to a server that is pretending to be “' + domain + '” which could put your confidential information at risk.' }
|
||||
}
|
||||
@@ -297,25 +312,25 @@ const tests = [
|
||||
JSON.parse(result.data.data).form.should.eql({ test: 'testString' });
|
||||
}
|
||||
},
|
||||
// {
|
||||
// description: 'should resolve correct URL after redirect (GET) #33',
|
||||
// expected: 'resolved: {"status": 200, url: "http://httpbin.org/anything", ...',
|
||||
// func: function (resolve, reject) { cordova.plugin.http.get('http://httpbin.org/redirect-to?url=http://httpbin.org/anything', {}, {}, resolve, reject); },
|
||||
// validationFunc: function (driver, result) {
|
||||
// result.type.should.be.equal('resolved');
|
||||
// result.data.url.should.be.equal('http://httpbin.org/anything');
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// description: 'should not follow 302 redirect when following redirects is disabled',
|
||||
// expected: 'rejected: {"status": 302, ...',
|
||||
// before: function (resolve, reject) { cordova.plugin.http.disableRedirect(true, resolve, reject) },
|
||||
// func: function (resolve, reject) { cordova.plugin.http.get('http://httpbin.org/redirect-to?url=http://httpbin.org/anything', {}, {}, resolve, reject); },
|
||||
// validationFunc: function (driver, result) {
|
||||
// result.type.should.be.equal('rejected');
|
||||
// result.data.status.should.be.equal(302);
|
||||
// }
|
||||
// },
|
||||
{
|
||||
description: 'should resolve correct URL after redirect (GET) #33',
|
||||
expected: 'resolved: {"status": 200, url: "http://httpbin.org/anything", ...',
|
||||
func: function (resolve, reject) { cordova.plugin.http.get('http://httpbingo.org/redirect-to?url=http://httpbin.org/anything', {}, {}, resolve, reject); },
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
result.data.url.should.be.equal('http://httpbin.org/anything');
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should not follow 302 redirect when following redirects is disabled',
|
||||
expected: 'rejected: {"status": 302, ...',
|
||||
before: function (resolve, reject) { cordova.plugin.http.setFollowRedirect(false); resolve(); },
|
||||
func: function (resolve, reject) { cordova.plugin.http.get('http://httpbingo.org/redirect-to?url=http://httpbin.org/anything', {}, {}, resolve, reject); },
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('rejected');
|
||||
result.data.status.should.be.equal(302);
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should download a file from given URL to given path in local filesystem',
|
||||
expected: 'resolved: {"content": "<?xml version=\'1.0\' encoding=\'us-ascii\'?>\\n\\n<!-- A SAMPLE set of slides -->" ...',
|
||||
@@ -909,7 +924,7 @@ const tests = [
|
||||
},
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
should.equal(null, result.data.data);
|
||||
should.equal(true, result.data.data === null || result.data.data === undefined);
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -1003,17 +1018,16 @@ const tests = [
|
||||
before: helpers.setRawSerializer,
|
||||
func: function (resolve, reject, skip) {
|
||||
if (!helpers.isAbortSupported()) {
|
||||
skip();
|
||||
return;
|
||||
return skip();
|
||||
}
|
||||
helpers.getWithXhr(function (buffer) {
|
||||
var reqId = cordova.plugin.http.post('http://httpbin.org/anything', buffer, {}, resolve, reject);
|
||||
|
||||
setTimeout(function () {
|
||||
cordova.plugin.http.abort(reqId);
|
||||
}, helpers.getAbortDelay());
|
||||
var targetUrl = 'http://httpbin.org/post';
|
||||
var fileContent = helpers.getDemoArrayBuffer(10000);
|
||||
var reqId = cordova.plugin.http.post(targetUrl, fileContent, {}, resolve, reject);
|
||||
|
||||
}, './res/cordova_logo.png', 'arraybuffer');
|
||||
setTimeout(function () {
|
||||
cordova.plugin.http.abort(reqId);
|
||||
}, helpers.getAbortDelay());
|
||||
},
|
||||
validationFunc: function (driver, result) {
|
||||
helpers.checkResult(result, 'rejected');
|
||||
@@ -1025,10 +1039,9 @@ const tests = [
|
||||
expected: 'rejected: {"status":-8, "error": "Request ...}',
|
||||
func: function (resolve, reject, skip) {
|
||||
if (!helpers.isAbortSupported()) {
|
||||
skip();
|
||||
return;
|
||||
return skip();
|
||||
}
|
||||
var url = 'https://httpbin.org/image/jpeg';
|
||||
var url = 'https://httpbin.org/drip?duration=2&numbytes=10&code=200';
|
||||
var options = { method: 'get', responseType: 'blob' };
|
||||
var success = function (response) {
|
||||
resolve({
|
||||
@@ -1053,8 +1066,7 @@ const tests = [
|
||||
expected: 'rejected: {"status":-8, "error": "Request ...}',
|
||||
func: function (resolve, reject, skip) {
|
||||
if (!helpers.isAbortSupported()) {
|
||||
skip();
|
||||
return;
|
||||
return skip();
|
||||
}
|
||||
var sourceUrl = 'http://httpbin.org/xml';
|
||||
var targetPath = cordova.file.cacheDirectory + 'test.xml';
|
||||
@@ -1086,11 +1098,12 @@ const tests = [
|
||||
expected: 'rejected: {"status":-8, "error": "Request ...}',
|
||||
func: function (resolve, reject, skip) {
|
||||
if (!helpers.isAbortSupported()) {
|
||||
skip();
|
||||
return;
|
||||
return skip();
|
||||
}
|
||||
|
||||
|
||||
var fileName = 'test-file.txt';
|
||||
var fileContent = 'I am a dummy file. I am used for testing purposes!';
|
||||
var fileContent = helpers.getDemoArrayBuffer(10000);
|
||||
var sourcePath = cordova.file.cacheDirectory + fileName;
|
||||
var targetUrl = 'http://httpbin.org/post';
|
||||
|
||||
@@ -1109,6 +1122,47 @@ const tests = [
|
||||
result.data.status.should.be.equal(-8);
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should not send malformed request when FormData is empty #372',
|
||||
expected: 'resolved: {"status":200, ...',
|
||||
before: helpers.setMultipartSerializer,
|
||||
func: function (resolve, reject) {
|
||||
var ponyfills = cordova.plugin.http.ponyfills;
|
||||
var formData = new ponyfills.FormData();
|
||||
|
||||
var url = 'http://httpbin.org/anything';
|
||||
var options = { method: 'post', data: formData };
|
||||
cordova.plugin.http.sendRequest(url, options, resolve, reject);
|
||||
},
|
||||
validationFunc: function (driver, result, targetInfo) {
|
||||
helpers.checkResult(result, 'resolved');
|
||||
|
||||
var parsed = JSON.parse(result.data.data);
|
||||
|
||||
if (targetInfo.isAndroid) {
|
||||
// boundary should be sent correctly on Android
|
||||
parsed.headers['Content-Type'].should.be.equal('multipart/form-data; boundary=00content0boundary00');
|
||||
} else {
|
||||
// falling back to empty url encoded request on iOS
|
||||
parsed.headers['Content-Type'].should.be.equal('application/x-www-form-urlencoded');
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should reject connecting to server with blacklisted SSL version #420',
|
||||
expected: 'rejected: {"status":-2, ...',
|
||||
func: function (resolve, reject, skip) {
|
||||
if (!helpers.isTlsBlacklistSupported()) {
|
||||
return skip();
|
||||
}
|
||||
|
||||
cordova.plugin.http.get('https://tls-v1-0.badssl.com:1010/', {}, {}, resolve, reject);
|
||||
},
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('rejected');
|
||||
result.data.should.be.eql({ status: -2, error: messageFactory.handshakeFailed() });
|
||||
}
|
||||
},
|
||||
];
|
||||
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
|
||||
+15
-13
@@ -14,7 +14,7 @@ const configs = {
|
||||
},
|
||||
localIosEmulator: {
|
||||
platformName: 'iOS',
|
||||
platformVersion: '13.2',
|
||||
platformVersion: '14.3',
|
||||
automationName: 'XCUITest',
|
||||
deviceName: 'iPhone 8',
|
||||
autoWebview: true,
|
||||
@@ -22,7 +22,7 @@ const configs = {
|
||||
},
|
||||
localAndroidEmulator: {
|
||||
platformName: 'Android',
|
||||
platformVersion: '5',
|
||||
platformVersion: '9',
|
||||
deviceName: 'Android Emulator',
|
||||
autoWebview: true,
|
||||
fullReset: true,
|
||||
@@ -32,27 +32,27 @@ const configs = {
|
||||
// testing on SauceLabs
|
||||
saucelabsIosDevice: {
|
||||
browserName: '',
|
||||
'appium-version': '1.9.1',
|
||||
'appium-version': '1.20.1',
|
||||
platformName: 'iOS',
|
||||
platformVersion: '10.3',
|
||||
platformVersion: '14.3',
|
||||
deviceName: 'iPhone 6',
|
||||
autoWebview: true,
|
||||
app: 'sauce-storage:HttpDemo.app.zip'
|
||||
},
|
||||
saucelabsIosEmulator: {
|
||||
browserName: '',
|
||||
'appium-version': '1.9.1',
|
||||
'appium-version': '1.20.1',
|
||||
platformName: 'iOS',
|
||||
platformVersion: '10.3',
|
||||
platformVersion: '14.3',
|
||||
deviceName: 'iPhone Simulator',
|
||||
autoWebview: true,
|
||||
app: 'sauce-storage:HttpDemo.app.zip'
|
||||
},
|
||||
saucelabsAndroidEmulator: {
|
||||
browserName: '',
|
||||
'appium-version': '1.9.1',
|
||||
'appium-version': '1.20.1',
|
||||
platformName: 'Android',
|
||||
platformVersion: '5.1',
|
||||
platformVersion: '8.0',
|
||||
deviceName: 'Android Emulator',
|
||||
autoWebview: true,
|
||||
app: 'sauce-storage:HttpDemo.apk'
|
||||
@@ -60,18 +60,20 @@ const configs = {
|
||||
|
||||
// testing on BrowserStack
|
||||
browserstackIosDevice: {
|
||||
device: 'iPhone 7',
|
||||
os_version: '10',
|
||||
device: 'iPhone 12',
|
||||
os_version: '14',
|
||||
project: 'HTTP Test App',
|
||||
autoWebview: true,
|
||||
app: 'HttpTestAppAndroid'
|
||||
app: 'HttpTestAppAndroid',
|
||||
'browserstack.networkLogs': true
|
||||
},
|
||||
browserstackAndroidDevice: {
|
||||
device: 'Google Nexus 6',
|
||||
os_version: '5.0',
|
||||
os_version: '6.0',
|
||||
project: 'HTTP Test App',
|
||||
autoWebview: true,
|
||||
app: 'HttpTestAppAndroid'
|
||||
app: 'HttpTestAppAndroid',
|
||||
'browserstack.networkLogs': true
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,223 @@
|
||||
'use strict';
|
||||
|
||||
const Mocha = require('mocha');
|
||||
const milliseconds = require('ms');
|
||||
const Base = Mocha.reporters.Base;
|
||||
const color = Base.color;
|
||||
|
||||
const {
|
||||
isString,
|
||||
stringify,
|
||||
inherits
|
||||
} = Mocha.utils;
|
||||
|
||||
const {
|
||||
EVENT_RUN_BEGIN,
|
||||
EVENT_RUN_END,
|
||||
EVENT_TEST_FAIL,
|
||||
EVENT_TEST_PASS,
|
||||
EVENT_TEST_PENDING,
|
||||
EVENT_SUITE_BEGIN,
|
||||
EVENT_SUITE_END
|
||||
} = Mocha.Runner.constants;
|
||||
|
||||
function Spec(runner, options) {
|
||||
Base.call(this, runner, options);
|
||||
|
||||
var self = this;
|
||||
var indents = 0;
|
||||
var n = 0;
|
||||
|
||||
function indent() {
|
||||
return Array(indents).join(' ');
|
||||
}
|
||||
|
||||
runner.on(EVENT_RUN_BEGIN, function() {
|
||||
Base.consoleLog();
|
||||
});
|
||||
|
||||
runner.on(EVENT_SUITE_BEGIN, function(suite) {
|
||||
++indents;
|
||||
Base.consoleLog(color('suite', '%s%s'), indent(), suite.title);
|
||||
});
|
||||
|
||||
runner.on(EVENT_SUITE_END, function() {
|
||||
--indents;
|
||||
if (indents === 1) {
|
||||
Base.consoleLog();
|
||||
}
|
||||
});
|
||||
|
||||
runner.on(EVENT_TEST_PENDING, function(test) {
|
||||
var fmt = indent() + color('pending', ' - %s');
|
||||
Base.consoleLog(fmt, test.title);
|
||||
});
|
||||
|
||||
runner.on(EVENT_TEST_PASS, function(test) {
|
||||
var fmt;
|
||||
if (test.speed === 'fast') {
|
||||
fmt =
|
||||
indent() +
|
||||
color('checkmark', ' ' + Base.symbols.ok) +
|
||||
color('pass', ' %s');
|
||||
Base.consoleLog(fmt, test.title);
|
||||
} else {
|
||||
fmt =
|
||||
indent() +
|
||||
color('checkmark', ' ' + Base.symbols.ok) +
|
||||
color('pass', ' %s') +
|
||||
color(test.speed, ' (%dms)');
|
||||
Base.consoleLog(fmt, test.title, test.duration);
|
||||
}
|
||||
});
|
||||
|
||||
runner.on(EVENT_TEST_FAIL, function(test) {
|
||||
Base.consoleLog(indent() + color('fail', ' %d) %s'), ++n, test.title);
|
||||
});
|
||||
|
||||
runner.once(EVENT_RUN_END, self.epilogue.bind(self));
|
||||
}
|
||||
|
||||
/**
|
||||
* Inherit from `Base.prototype`.
|
||||
*/
|
||||
inherits(Spec, Base);
|
||||
|
||||
Spec.description = 'custom reporter for HTTP plugin testing';
|
||||
|
||||
Spec.prototype.epilogue = function() {
|
||||
var stats = this.stats;
|
||||
var fmt;
|
||||
|
||||
Base.consoleLog();
|
||||
|
||||
// passes
|
||||
fmt =
|
||||
color('bright pass', ' ') +
|
||||
color('green', ' %d passing') +
|
||||
color('light', ' (%s)');
|
||||
|
||||
Base.consoleLog(fmt, stats.passes || 0, milliseconds(stats.duration));
|
||||
|
||||
// pending
|
||||
if (stats.pending) {
|
||||
fmt = color('pending', ' ') + color('pending', ' %d pending');
|
||||
|
||||
Base.consoleLog(fmt, stats.pending);
|
||||
}
|
||||
|
||||
// failures
|
||||
if (stats.failures) {
|
||||
fmt = color('fail', ' %d failing');
|
||||
|
||||
Base.consoleLog(fmt, stats.failures);
|
||||
|
||||
this.showList(this.failures);
|
||||
Base.consoleLog();
|
||||
}
|
||||
|
||||
Base.consoleLog();
|
||||
};
|
||||
|
||||
Spec.prototype.showList = function(failures) {
|
||||
var multipleErr, multipleTest;
|
||||
var self = this;
|
||||
|
||||
Base.consoleLog();
|
||||
failures.forEach(function(test, i) {
|
||||
// format
|
||||
var fmt =
|
||||
color('error title', ' %s) %s:\n') +
|
||||
color('error message', ' %s') +
|
||||
color('error stack', '\n%s\n');
|
||||
|
||||
// msg
|
||||
var msg;
|
||||
var err;
|
||||
if (test.err && test.err.multiple) {
|
||||
if (multipleTest !== test) {
|
||||
multipleTest = test;
|
||||
multipleErr = [test.err].concat(test.err.multiple);
|
||||
}
|
||||
err = multipleErr.shift();
|
||||
} else {
|
||||
err = test.err;
|
||||
}
|
||||
var message;
|
||||
if (err.message && typeof err.message.toString === 'function') {
|
||||
message = err.message + '';
|
||||
} else if (typeof err.inspect === 'function') {
|
||||
message = err.inspect() + '';
|
||||
} else {
|
||||
message = '';
|
||||
}
|
||||
var stack = err.stack || message;
|
||||
var index = message ? stack.indexOf(message) : -1;
|
||||
|
||||
if (index === -1) {
|
||||
msg = message;
|
||||
} else {
|
||||
index += message.length;
|
||||
msg = stack.slice(0, index);
|
||||
// remove msg from stack
|
||||
stack = stack.slice(index + 1);
|
||||
}
|
||||
|
||||
// uncaught
|
||||
if (err.uncaught) {
|
||||
msg = 'Uncaught ' + msg;
|
||||
}
|
||||
// explicitly show diff
|
||||
if (Base.showDiff(err)) {
|
||||
self.stringifyDiffObjs(err);
|
||||
fmt =
|
||||
color('error title', ' %s) %s:\n%s') + color('error stack', '\n%s\n');
|
||||
var match = message.match(/^([^:]+): expected/);
|
||||
msg = '\n ' + color('error message', match ? match[1] : msg);
|
||||
|
||||
msg += Base.generateDiff(err.actual, err.expected);
|
||||
}
|
||||
|
||||
// indent stack trace
|
||||
stack = stack.replace(/^/gm, ' ');
|
||||
|
||||
// indented test title
|
||||
var testTitle = '';
|
||||
test.titlePath().forEach(function(str, index) {
|
||||
if (index !== 0) {
|
||||
testTitle += '\n ';
|
||||
}
|
||||
for (var i = 0; i < index; i++) {
|
||||
testTitle += ' ';
|
||||
}
|
||||
testTitle += str;
|
||||
});
|
||||
|
||||
Base.consoleLog(fmt, i + 1, testTitle, msg, stack);
|
||||
self.showDetails(err);
|
||||
});
|
||||
};
|
||||
|
||||
Spec.prototype.stringifyDiffObjs = function(err) {
|
||||
if (!isString(err.actual) || !isString(err.expected)) {
|
||||
err.actual = stringify(err.actual);
|
||||
err.expected = stringify(err.expected);
|
||||
}
|
||||
}
|
||||
|
||||
Spec.prototype.showDetails = function(err) {
|
||||
if (!err.details) {
|
||||
return;
|
||||
}
|
||||
|
||||
const details = JSON
|
||||
.stringify(err.details, null, 2)
|
||||
.replace(/^/gm, ' ');
|
||||
|
||||
Base.consoleLog(
|
||||
color('error stack', '\n Details:\n%s'),
|
||||
details
|
||||
);
|
||||
}
|
||||
|
||||
exports = module.exports = Spec;
|
||||
@@ -7,6 +7,9 @@ const testDefinitions = require('../e2e-specs');
|
||||
|
||||
global.should = chai.should();
|
||||
|
||||
let driver;
|
||||
let allPassed = true;
|
||||
|
||||
describe('Advanced HTTP e2e test suite', function () {
|
||||
const isSauceLabs = !!process.env.SAUCE_USERNAME;
|
||||
const isBrowserStack = !!process.env.BROWSERSTACK_USERNAME;
|
||||
@@ -17,9 +20,6 @@ describe('Advanced HTTP e2e test suite', function () {
|
||||
const targetInfo = { isSauceLabs, isBrowserStack, isDevice, isAndroid };
|
||||
const environment = isSauceLabs ? 'saucelabs' : isBrowserStack ? 'browserstack' : 'local';
|
||||
|
||||
let driver;
|
||||
let allPassed = true;
|
||||
|
||||
this.timeout(15000);
|
||||
this.slow(4000);
|
||||
|
||||
@@ -121,7 +121,15 @@ async function waitToBeFinished(driver, timeout) {
|
||||
|
||||
async function validateResult(driver, validationFunc, targetInfo) {
|
||||
const result = await driver.safeExecute('app.lastResult');
|
||||
validationFunc(driver, result, targetInfo);
|
||||
|
||||
try {
|
||||
validationFunc(driver, result, targetInfo);
|
||||
} catch (error) {
|
||||
allPassed = false;
|
||||
error.details = result;
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async function checkSkipped(driver) {
|
||||
|
||||
Reference in New Issue
Block a user