diff --git a/.npmignore b/.npmignore index c187208c..f208fe2f 100644 --- a/.npmignore +++ b/.npmignore @@ -2,3 +2,4 @@ tags .zedstate /scripts /temp +/test diff --git a/.travis.yml b/.travis.yml index 4348c023..50e474fe 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,43 @@ -language: node_js -node_js: -- lts/* +sudo: false + +language: objective-c +os: osx +osx_image: xcode9.1 + +cache: false + +addons: + sauce_connect: + username: silkimen + jwt: TwUj8A5qAR53BRhVwcdLCM64kI+f8beOLbZ2VmLJ557ptGAxvkHrhQ19uLEQakL64SWoeznObJjTG/Sp8zbO6O9xkfTw5mbNPk7FBTMd77x62l/PHZSWuwiFEVPfLnG2jSGWBqrLxr/g8cIiruzHSXS9J4C86SYIKIGnz9atJxB7XswwzAkvmyzgzC6iRCFhoAgmnhtY+Xr+QHcbpESoiUPv/MQ8CFNpk65CX+fuvVunWX70e3Qlelcs7nkbF/soFwD1WExCbTaPlaNvKqBIc1YuO4qdNy4Uff4mZZLsARH0X618EwFAVN4VM7AIqeUp3NoQYDw9vpkR7lmoOUJzI0aovasXa8frDNvm8GSP17jtCTQzfYEMK+B9rIA9oBRd46dyGujZtbKoTpDdTMIe0ayqb38NRuNwDzEV7WVMCcUgux0Q8NIAkWYYb21QN9z7XOOznP7BatK7mlJ0V6J7taBW2EBOwQzHWRG7n9GknGXsPzwxDW6aBCiOem/AcxZkajDPcRu/3diq5ZSZcC7cTVBxCZZCqpPCsZH0l47jW8MNh+CYw+i5TGsp7g0YHVmbWzE8dv73tMD9zQP0pUjOSkY55xbS7kTCXvFqP0OdMccpzdZY5h7JER95tIPz3gZwVFCoBg0GvwTqTxhGKR+jNosUD2n6WhCVFAU+AspgCKo= + +before_install: +- export LANG=en_US.UTF-8 +- brew update + +install: +- npm install +- brew install gradle +- wget http://dl.google.com/android/android-sdk_r24.4-macosx.zip +- tar -xvf android-sdk_r24.4-macosx.zip +- echo y | ./android-sdk-macosx/tools/android update sdk --no-ui --all --filter platform-tools +- echo y | ./android-sdk-macosx/tools/android update sdk --no-ui --all --filter build-tools-25.0.0 +- echo y | ./android-sdk-macosx/tools/android update sdk --no-ui --all --filter android-25 +- echo y | ./android-sdk-macosx/tools/android update sdk --no-ui --all --filter extra-android-support +- echo y | ./android-sdk-macosx/tools/android update sdk --no-ui --all --filter extra-android-m2repository +- echo y | ./android-sdk-macosx/tools/android update sdk --no-ui --all --filter extra-google-m2repository +- export ANDROID_HOME=./android-sdk-macosx +- export PATH=${PATH}:$ANDROID_HOME/tools:$ANDROID_HOME/platform-tools:$ANDROID_HOME/build-tools/23.0.2 + +script: +- scripts/build-test-app.sh +- scripts/upload-artifact.sh +- scripts/test.sh --ios --emulator + +after_success: + +deploy: + notifications: slack: secure: lXE+2AgsxZU5G5dI91LkMAIgo8MAWfdM7DB5UOtn5LpuNln+2FmJo1gOI7tkdmLOqpXTGYnpI2VyQN3H4nOF21YhuouzD1Sh8n2wtQg1iTm353kuQpqiVhSBX8ZJ7Be1e1G8OsnxoYOxbs4Zo9qI40EruwkvqLCBHWM5MRGyd4M7EFWwb9Z29VZN0y1Nt5g/c3bT76kdKmF+JCLur2OeEKxAity7sIKgZekSqeIMwEVLSxXnda6Dbjc/cg0MJ0iDArkD7iu6fz/Fcrrxgm/pUxjcgvqze7Gy5i31mjEfspnrglWV1cshMd48BTDKCJ2AMmxH8O3GPSWE2txjIvGRWUve7iViNylvmQCVz3Eyf99+4EuuVGa+5PSodQ/CqODx/65EwtcN3PE1tNz2puKOK8nrOJcFkcbG8KTHKUlQtHCkjitbykUnj/hvhLK5/oWlQYVOLWWrHwdGUh8FI8aFPVGjRjWbHbhdayjEIqxwr1ns+6mYrP1EFNXbaeZxnLNC59XpJl1ifuezqYAk7YEiU5j4rtC7YKgyQ3ueb7anOHTJoTMyDn8mpZXgwuyhoBaeEYytQVgRyMtL6Y5cP98Jn2kv0+vdne3rkk9/JEBTo32HOjvoij6rsqEvXC0LhUDJSNadOVdHht0jjoN6zBH37HIE5/3zysLlPcAcHAS83ow= diff --git a/package.json b/package.json index 46cc120c..52b4bd91 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "version": "1.6.1", "description": "Cordova / Phonegap plugin for communicating with HTTP servers using SSL pinning", "scripts": { - "test": "./scripts/test-installation.sh" + "test": "./scripts/build-test-app.sh && ./scripts/test.sh --ios --emulator" }, "cordova": { "id": "cordova-plugin-advanced-http", @@ -49,9 +49,14 @@ }, "homepage": "https://github.com/silkimen/cordova-plugin-advanced-http#readme", "devDependencies": { + "chai": "4.1.2", + "chai-as-promised": "7.1.1", + "colors": "1.1.2", "cordova": "7.0.1", + "mocha": "4.0.0", "mz": "2.7.0", "umd-tough-cookie": "2.3.2", + "wd": "1.4.1", "xml2js": "0.4.19" } } diff --git a/release.sh b/release.sh index 75969241..f73fb031 100755 --- a/release.sh +++ b/release.sh @@ -5,5 +5,6 @@ VERSION=$(node -e "console.log(require('./package.json').version)") ./scripts/update-tough-cookie.sh node ./scripts/update-plugin-xml.js $VERSION -npm publish +git commit -a -m "release v$VERSION" git tag "v$VERSION" +npm publish diff --git a/scripts/build-test-app.sh b/scripts/build-test-app.sh new file mode 100755 index 00000000..ef062fac --- /dev/null +++ b/scripts/build-test-app.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +set -e + +PLATFORM=$([[ "${@#--android}" = "$@" ]] && echo "ios" || echo "android") +TARGET=$([[ "${@#--device}" = "$@" ]] && echo "emulator" || echo "device") +ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"/.. +CDV=$ROOT/node_modules/.bin/cordova + +rm -rf $ROOT/temp +mkdir $ROOT/temp +cp -r $ROOT/test/app-template/ $ROOT/temp/ +cp $ROOT/test/test-definitions.js $ROOT/temp/www/js/ +cd $ROOT/temp +$CDV prepare +$CDV plugins add $ROOT +$CDV build $PLATFORM --$TARGET diff --git a/scripts/test-installation.sh b/scripts/test-installation.sh deleted file mode 100755 index 7e57faf4..00000000 --- a/scripts/test-installation.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env bash -set -e - -ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"/.. -CDV=$ROOT/node_modules/.bin/cordova - -rm -rf $ROOT/temp -$CDV create $ROOT/temp -cd $ROOT/temp -$CDV platforms add android -$CDV platforms add ios -$CDV plugins add $ROOT diff --git a/scripts/test.sh b/scripts/test.sh new file mode 100755 index 00000000..15404bae --- /dev/null +++ b/scripts/test.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set -e + +ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"/.. + +pushd $ROOT +./node_modules/.bin/mocha ./test/mocha-specs/test.js "$@" +popd diff --git a/scripts/upload-artifact.sh b/scripts/upload-artifact.sh new file mode 100755 index 00000000..ffab5aac --- /dev/null +++ b/scripts/upload-artifact.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +set -e + +ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"/.. +TEMP=$ROOT/temp + +rm -rf $TEMP/HttpDemo.app.zip +pushd $TEMP/platforms/ios/build/emulator +zip -r $TEMP/HttpDemo.app.zip ./HttpDemo.app +popd + +curl -u $SAUCE_USERNAME:$SAUCE_ACCESS_KEY \ + -X POST \ + -H "Content-Type: application/octet-stream" \ + https://saucelabs.com/rest/v1/storage/$SAUCE_USERNAME/HttpDemo.app.zip?overwrite=true \ + --data-binary @$TEMP/HttpDemo.app.zip diff --git a/test/app-template/config.xml b/test/app-template/config.xml new file mode 100644 index 00000000..6baa9538 --- /dev/null +++ b/test/app-template/config.xml @@ -0,0 +1,27 @@ + + + HttpDemo + + A sample Apache Cordova application that demonstrates advanced HTTP plugin. + + + Sefa Ilkimen + + + + + + + + + + + + + + + + + + + diff --git a/test/app-template/package.json b/test/app-template/package.json new file mode 100644 index 00000000..568e628f --- /dev/null +++ b/test/app-template/package.json @@ -0,0 +1,25 @@ +{ + "name": "com.ilkimen.http.demo", + "displayName": "HttpDemo", + "version": "1.0.0", + "description": "A sample Apache Cordova application that demonstrates advanced HTTP plugin.", + "main": "index.js", + "scripts": { + "build": "scripts/build.sh", + "test": "npm run build && scripts/test.sh" + }, + "author": "Sefa Ilkimen", + "license": "Apache-2.0", + "dependencies": { + "cordova": "7.0.1", + "cordova-android": "6.2.3", + "cordova-ios": "4.4.0" + }, + "cordova": { + "platforms": [ + "android", + "ios" + ] + }, + "devDependencies": {} +} diff --git a/test/app-template/www/css/index.css b/test/app-template/www/css/index.css new file mode 100644 index 00000000..37f86e19 --- /dev/null +++ b/test/app-template/www/css/index.css @@ -0,0 +1,24 @@ +* { + -webkit-tap-highlight-color: rgba(0,0,0,0); /* make transparent link selection, adjust last value opacity 0 to 1.0 */ +} + +body { + -webkit-touch-callout: none; /* prevent callout to copy image, etc when tap to hold */ + -webkit-text-size-adjust: none; /* prevent webkit from resizing text to fit */ + -webkit-user-select: none; /* prevent copy paste, to allow, change 'none' to 'text' */ + height:100%; + margin:0px; + padding:20px 0; + width:100%; + font-family: sans-serif; +} + +button, input, textarea { + display: block; + width: 100%; +} + +h1 { + font-size: 12pt; + text-align: center; +} diff --git a/test/app-template/www/img/logo.png b/test/app-template/www/img/logo.png new file mode 100644 index 00000000..9519e7dd Binary files /dev/null and b/test/app-template/www/img/logo.png differ diff --git a/test/app-template/www/index.html b/test/app-template/www/index.html new file mode 100644 index 00000000..4c33ca9a --- /dev/null +++ b/test/app-template/www/index.html @@ -0,0 +1,19 @@ + + + + + + + + + + +

Advanced HTTP test suite

+ + + + + + + + diff --git a/test/app-template/www/js/index.js b/test/app-template/www/js/index.js new file mode 100644 index 00000000..9c8d0dfe --- /dev/null +++ b/test/app-template/www/js/index.js @@ -0,0 +1,51 @@ +const app = { + testIndex: -1, + + initialize: function() { + document.getElementById('nextBtn').addEventListener('click', app.onNextBtnClick); + }, + + print: function(prefix, content) { + const text = '\n' + prefix + ': ' + JSON.stringify(content); + + document.getElementById('resultTextarea').value += text; + }, + + reject: function(content) { + app.print('result - rejected', content); + }, + + resolve: function(content) { + app.print('result - resolved', content); + }, + + runTest: function(index) { + const testDefinition = tests[index]; + const titleText = app.testIndex + ': ' + testDefinition.description; + const resultText = 'expectation - ' + testDefinition.expected; + + document.getElementById('resultTextarea').value = resultText; + document.getElementById('descriptionLbl').innerText = titleText; + testDefinition.func(index); + }, + + onFinishedAllTests: function() { + const titleText = 'No more tests'; + const resultText = 'You have run all available tests.'; + + document.getElementById('resultTextarea').value = resultText; + document.getElementById('descriptionLbl').innerText = titleText; + }, + + onNextBtnClick: function() { + app.testIndex += 1; + + if (app.testIndex < tests.length) { + app.runTest(app.testIndex); + } else { + app.onFinishedAllTests(); + } + } +}; + +app.initialize(); diff --git a/test/mocha-specs/helpers/apps.js b/test/mocha-specs/helpers/apps.js new file mode 100644 index 00000000..711b7d87 --- /dev/null +++ b/test/mocha-specs/helpers/apps.js @@ -0,0 +1,10 @@ +const path = require('path'); + +if (process.env.SAUCE_USERNAME) { + exports.iosTestApp = 'sauce-storage:HttpDemo.app.zip'; + exports.androidTestApp = 'sauce-storage:HttpDemo.apk'; +} else { + // these paths are relative to working directory + exports.iosTestApp = path.resolve('temp/platforms/ios/build/emulator/HttpDemo.app'); + exports.androidTestApp = path.resolve('temp/platforms/android/build/outputs/apk/android-debug.apk'); +} diff --git a/test/mocha-specs/helpers/caps.js b/test/mocha-specs/helpers/caps.js new file mode 100644 index 00000000..069ddcd1 --- /dev/null +++ b/test/mocha-specs/helpers/caps.js @@ -0,0 +1,65 @@ +const local = { + iosDevice: { + browserName: '', + 'appium-version': '1.7.1', + platformName: 'iOS', + platformVersion: '10.3', + deviceName: 'iPhone 6', + autoWebview: true, + app: undefined // will be set later + }, + iosEmulator: { + browserName: '', + 'appium-version': '1.7.1', + platformName: 'iOS', + platformVersion: '11.0', + deviceName: 'iPhone Simulator', + autoWebview: true, + app: undefined // will be set later + }, + androidEmulator: { + browserName: '', + 'appium-version': '1.7.1', + platformName: 'Android', + platformVersion: '5.1', + deviceName: 'Android Emulator', + autoWebview: true, + app: undefined // will be set later + } +}; + +const sauce = { + iosDevice: { + browserName: '', + 'appium-version': '1.7.1', + platformName: 'iOS', + platformVersion: '10.3', + deviceName: 'iPhone 6', + autoWebview: true, + app: undefined // will be set later + }, + iosEmulator: { + browserName: '', + 'appium-version': '1.7.1', + platformName: 'iOS', + platformVersion: '10.3', + deviceName: 'iPhone Simulator', + autoWebview: true, + app: undefined // will be set later + }, + androidEmulator: { + browserName: '', + 'appium-version': '1.7.1', + platformName: 'Android', + platformVersion: '5.1', + deviceName: 'Android Emulator', + autoWebview: true, + app: undefined // will be set later + } +}; + +if (process.env.SAUCE_USERNAME) { + module.exports = sauce; +} else { + module.exports = local; +} diff --git a/test/mocha-specs/helpers/logging.js b/test/mocha-specs/helpers/logging.js new file mode 100644 index 00000000..11a7be13 --- /dev/null +++ b/test/mocha-specs/helpers/logging.js @@ -0,0 +1,13 @@ +exports.configure = driver => { + driver.on('status', info => { + console.log(info.cyan); + }); + + driver.on('command', (meth, path, data) => { + console.log(' > ' + meth.yellow, path.grey, data || ''); + }); + + driver.on('http', (meth, path, data) => { + console.log(' > ' + meth.magenta, path, (data || '').grey); + }); +}; diff --git a/test/mocha-specs/helpers/server.js b/test/mocha-specs/helpers/server.js new file mode 100644 index 00000000..f96ab11d --- /dev/null +++ b/test/mocha-specs/helpers/server.js @@ -0,0 +1,16 @@ +const local = { + host: 'localhost', + port: 4723 +}; + +const sauce = { + host: 'ondemand.saucelabs.com', + port: 80, + auth: process.env.SAUCE_USERNAME + ":" + process.env.SAUCE_ACCESS_KEY +}; + +if (process.env.SAUCE_USERNAME) { + module.exports = sauce; +} else { + module.exports = local; +} diff --git a/test/mocha-specs/helpers/setup.js b/test/mocha-specs/helpers/setup.js new file mode 100644 index 00000000..3e09b7fa --- /dev/null +++ b/test/mocha-specs/helpers/setup.js @@ -0,0 +1,12 @@ +const wd = require("wd"); + +require('colors'); + +const chai = require('chai'); +const chaiAsPromised = require('chai-as-promised'); +chai.use(chaiAsPromised); + +const should = chai.should(); +chaiAsPromised.transferPromiseness = wd.transferPromiseness; + +exports.should = should; diff --git a/test/mocha-specs/test.js b/test/mocha-specs/test.js new file mode 100644 index 00000000..36dbf31b --- /dev/null +++ b/test/mocha-specs/test.js @@ -0,0 +1,71 @@ +require('./helpers/setup'); + +const wd = require('wd'); +const apps = require('./helpers/apps'); +const caps = Object.assign({}, require('./helpers/caps')); +const serverConfig = require('./helpers/server'); +const testDefinitions = require('../test-definitions'); + +describe('Advanced HTTP', function() { + let driver; + let allPassed = true; + + this.timeout(300000); + + const getCaps = appName => { + const isDevice = process.argv.includes('--device'); + const isAndroid = process.argv.includes('--android'); + const desiredCaps = caps[(isAndroid ? 'android' : 'ios') + (isDevice ? 'Device' : 'Emulator')]; + const desiredApp = apps[(isAndroid ? 'android' : 'ios') + appName]; + + desiredCaps.app = desiredApp; + + return desiredCaps; + }; + + const validateTestIndex = number => driver + .elementById('descriptionLbl') + .text() + .then(text => parseInt(text.match(/(\d):/)[1], 10)) + .should.eventually.become(number); + + const validateTestTitle = testTitle => driver + .elementById('descriptionLbl') + .text() + .then(text => text.match(/\d:\ (.*)/)[1]) + .should.eventually.become(testTitle); + + const validateResult = text => driver + .elementById('resultTextarea') + .getAttribute('value') + .should.eventually.include(text); + + const clickNext = () => driver + .elementById('nextBtn') + .click() + .sleep(1000); + + before(() => { + driver = wd.promiseChainRemote(serverConfig); + require('./helpers/logging').configure(driver); + + return driver.init(getCaps('TestApp')); + }); + + after(() => driver + .quit() + .finally(function () { + if (process.env.SAUCE_USERNAME) { + return driver.sauceJobStatus(allPassed); + } + })); + + testDefinitions.forEach((definition, index) => { + it(definition.description, function() { + return clickNext() + .then(() => validateTestIndex(index)) + .then(() => validateTestTitle(this.test.title)) + .then(() => validateResult(definition.expected)) + }); + }); +}); diff --git a/test/test-definitions.js b/test/test-definitions.js new file mode 100644 index 00000000..7ec72b12 --- /dev/null +++ b/test/test-definitions.js @@ -0,0 +1,27 @@ +const tests = [ + { + description: 'should reject self signed cert (GET)', + expected: 'rejected: {"status":-1,"error":"cancelled"}', + func: function() { cordova.plugin.http.get('https://self-signed.badssl.com/', {}, {}, app.resolve, app.reject); } + },{ + description: 'should reject self signed cert (PUT)', + expected: 'rejected: {"status":-1,"error":"cancelled"}', + func: function() { cordova.plugin.http.put('https://self-signed.badssl.com/', { test: 'testString' }, {}, app.resolve, app.reject); } + },{ + description: 'should reject self signed cert (POST)', + expected: 'rejected: {"status":-1,"error":"cancelled"}', + func: function() { cordova.plugin.http.post('https://self-signed.badssl.com/', { test: 'testString' }, {}, app.resolve, app.reject); } + },{ + description: 'should reject self signed cert (PATCH)', + expected: 'rejected: {"status":-1,"error":"cancelled"}', + func: function() { cordova.plugin.http.patch('https://self-signed.badssl.com/', { test: 'testString' }, {}, app.resolve, app.reject); } + },{ + description: 'should reject self signed cert (DELETE)', + expected: 'rejected: {"status":-1,"error":"cancelled"}', + func: function() { cordova.plugin.http.delete('https://self-signed.badssl.com/', {}, {}, app.resolve, app.reject); } + } +]; + +if (module && module.exports) { + module.exports = tests; +}