diff --git a/package.json b/package.json index 6b4d3ff..66398d5 100644 --- a/package.json +++ b/package.json @@ -5,8 +5,10 @@ "scripts": { "updatecert": "node ./scripts/update-e2e-server-cert.js && node ./scripts/update-e2e-client-cert.js", "buildbrowser": "./scripts/build-test-app.sh --browser", - "testandroid": "npm run updatecert && ./scripts/build-test-app.sh --android --emulator && ./scripts/test-app.sh --android --emulator", - "testios": "npm run updatecert && ./scripts/build-test-app.sh --ios --emulator && ./scripts/test-app.sh --ios --emulator", + "buildandroid": "./scripts/build-test-app.sh --android --emulator", + "buildios": "./scripts/build-test-app.sh --ios --emulator", + "testandroid": "npm run updatecert && npm run buildandroid && ./scripts/test-app.sh --android --emulator", + "testios": "npm run updatecert && npm run buildios && ./scripts/test-app.sh --ios --emulator", "testapp": "npm run testandroid && npm run testios", "testjs": "mocha ./test/js-specs.js", "test": "npm run testjs && npm run testapp", diff --git a/test/e2e-tooling/caps.js b/test/e2e-tooling/caps.js new file mode 100644 index 0000000..5349261 --- /dev/null +++ b/test/e2e-tooling/caps.js @@ -0,0 +1,71 @@ +module.exports = { getCaps }; + +const path = require('path'); + +const configs = { + // testing on local machine + localIosDevice: { + platformName: 'iOS', + platformVersion: '10.3', + deviceName: 'iPhone 6', + autoWebview: true, + app: path.resolve('temp/platforms/ios/build/emulator/HttpDemo.app') + }, + localIosEmulator: { + platformName: 'iOS', + platformVersion: '11.0', + deviceName: 'iPhone Simulator', + autoWebview: true, + app: path.resolve('temp/platforms/ios/build/emulator/HttpDemo.app') + }, + localAndroidEmulator: { + platformName: 'Android', + platformVersion: '9', + deviceName: 'Android Emulator', + autoWebview: true, + fullReset: true, + app: path.resolve('temp/platforms/android/app/build/outputs/apk/debug/app-debug.apk') + }, + + // testing on SauceLabs + saucelabsIosDevice: { + browserName: '', + 'appium-version': '1.9.1', + platformName: 'iOS', + platformVersion: '10.3', + deviceName: 'iPhone 6', + autoWebview: true, + app: 'sauce-storage:HttpDemo.app.zip' + }, + saucelabsIosEmulator: { + browserName: '', + 'appium-version': '1.9.1', + platformName: 'iOS', + platformVersion: '10.3', + deviceName: 'iPhone Simulator', + autoWebview: true, + app: 'sauce-storage:HttpDemo.app.zip' + }, + saucelabsAndroidEmulator: { + browserName: '', + 'appium-version': '1.9.1', + platformName: 'Android', + platformVersion: '5.1', + deviceName: 'Android Emulator', + autoWebview: true, + app: 'sauce-storage:HttpDemo.apk' + } +}; + +function getCaps(environment, os, runtime) { + const key = environment.toLowerCase() + capitalize(os) + capitalize(runtime); + const caps = configs[key]; + + caps.name = `cordova-plugin-advanced-http (${os})`; + + return caps; +}; + +function capitalize(text) { + return text.charAt(0).toUpperCase() + text.slice(1).toLowerCase(); +} diff --git a/test/e2e-tooling/helpers/apps.js b/test/e2e-tooling/helpers/apps.js deleted file mode 100644 index 2abab02..0000000 --- a/test/e2e-tooling/helpers/apps.js +++ /dev/null @@ -1,10 +0,0 @@ -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/app/build/outputs/apk/debug/app-debug.apk'); -} diff --git a/test/e2e-tooling/helpers/caps.js b/test/e2e-tooling/helpers/caps.js deleted file mode 100644 index b259567..0000000 --- a/test/e2e-tooling/helpers/caps.js +++ /dev/null @@ -1,60 +0,0 @@ -const local = { - iosDevice: { - platformName: 'iOS', - platformVersion: '10.3', - deviceName: 'iPhone 6', - autoWebview: true, - app: undefined // will be set later - }, - iosEmulator: { - platformName: 'iOS', - platformVersion: '11.0', - deviceName: 'iPhone Simulator', - autoWebview: true, - app: undefined // will be set later - }, - androidEmulator: { - platformName: 'Android', - platformVersion: '5.1', - deviceName: 'Android Emulator', - autoWebview: true, - fullReset: true, - app: undefined // will be set later - } -}; - -const sauce = { - iosDevice: { - browserName: '', - 'appium-version': '1.9.1', - platformName: 'iOS', - platformVersion: '10.3', - deviceName: 'iPhone 6', - autoWebview: true, - app: undefined // will be set later - }, - iosEmulator: { - browserName: '', - 'appium-version': '1.9.1', - platformName: 'iOS', - platformVersion: '10.3', - deviceName: 'iPhone Simulator', - autoWebview: true, - app: undefined // will be set later - }, - androidEmulator: { - browserName: '', - 'appium-version': '1.9.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/e2e-tooling/helpers/server.js b/test/e2e-tooling/helpers/server.js deleted file mode 100644 index f96ab11..0000000 --- a/test/e2e-tooling/helpers/server.js +++ /dev/null @@ -1,16 +0,0 @@ -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/e2e-tooling/helpers/logging.js b/test/e2e-tooling/logging.js similarity index 76% rename from test/e2e-tooling/helpers/logging.js rename to test/e2e-tooling/logging.js index 11a7be1..5564efb 100644 --- a/test/e2e-tooling/helpers/logging.js +++ b/test/e2e-tooling/logging.js @@ -1,4 +1,8 @@ -exports.configure = driver => { +module.exports = { setupLogging }; + +function setupLogging(driver) { + require('colors'); + driver.on('status', info => { console.log(info.cyan); }); @@ -10,4 +14,4 @@ exports.configure = driver => { driver.on('http', (meth, path, data) => { console.log(' > ' + meth.magenta, path, (data || '').grey); }); -}; +} diff --git a/test/e2e-tooling/server.js b/test/e2e-tooling/server.js new file mode 100644 index 0000000..d239ea9 --- /dev/null +++ b/test/e2e-tooling/server.js @@ -0,0 +1,17 @@ +module.exports = { getServer }; + +const configs = { + local: { + host: 'localhost', + port: 4723 + }, + saucelabs: { + host: 'ondemand.saucelabs.com', + port: 80, + auth: process.env.SAUCE_USERNAME + ":" + process.env.SAUCE_ACCESS_KEY + } +} + +function getServer(environment) { + return configs[environment.toLowerCase()]; +} diff --git a/test/e2e-tooling/test.js b/test/e2e-tooling/test.js index 9b5d3a5..39f710c 100644 --- a/test/e2e-tooling/test.js +++ b/test/e2e-tooling/test.js @@ -1,100 +1,101 @@ const wd = require('wd'); const chai = require('chai'); const chaiAsPromised = require('chai-as-promised'); -const apps = require('./helpers/apps'); -const caps = Object.assign({}, require('./helpers/caps')); -const serverConfig = require('./helpers/server'); +const logging = require('./logging'); +const capsConfig = require('./caps'); +const serverConfig = require('./server'); const testDefinitions = require('../e2e-specs'); -const pkgjson = require('../../package.json'); chai.use(chaiAsPromised); chaiAsPromised.transferPromiseness = wd.transferPromiseness; global.should = chai.should(); -require('colors'); - -describe('Advanced HTTP', function() { +describe('Advanced HTTP e2e test suite', function () { + const isSauceLabs = !!process.env.SAUCE_USERNAME; + const isVerbose = process.argv.includes('--verbose'); const isDevice = process.argv.includes('--device'); const isAndroid = process.argv.includes('--android'); - const targetInfo = { isDevice, isAndroid }; - let driver = null; + const targetInfo = { isSauceLabs, isDevice, isAndroid }; + const environment = isSauceLabs ? 'saucelabs' : 'local'; + + let driver; let allPassed = true; this.timeout(900000); - const getCaps = appName => { - const desiredOs = isAndroid ? 'android' : 'ios'; - const desiredCaps = caps[desiredOs + (isDevice ? 'Device' : 'Emulator')]; - const desiredApp = apps[desiredOs + appName]; + before(async function () { + driver = await wd.promiseChainRemote(serverConfig.getServer(environment)); - desiredCaps.name = pkgjson.name + ` (${desiredOs})`; - desiredCaps.app = desiredApp; + if (isVerbose) { + logging.setupLogging(driver); + } - return desiredCaps; - }; - - const validateTestIndex = number => driver - .elementById('descriptionLbl') - .text() - .then(text => parseInt(text.match(/(\d+):/)[1], 10)) - .should.eventually.become(number, 'Test index is not matching!'); - - const validateTestTitle = testTitle => driver - .elementById('descriptionLbl') - .text() - .then(text => text.match(/\d+:\ (.*)/)[1]) - .should.eventually.become(testTitle, 'Test description is not matching!'); - - const waitToBeFinished = timeout => new Promise((resolve, reject) => { - const timeoutTimestamp = Date.now() + timeout; - const checkIfFinished = () => driver - .elementById('statusInput') - .getValue() - .then(value => { - if (value === 'finished') { - resolve(); - } else if (Date.now() > timeoutTimestamp) { - reject(new Error('Test function timed out!')); - } else { - setTimeout(checkIfFinished, 500); - } - }); - - checkIfFinished(); + await driver.init( + capsConfig.getCaps( + environment, + isAndroid ? 'android' : 'ios', + isDevice ? 'device' : 'emulator' + ) + ); }); - const validateResult = testDefinition => driver - .safeExecute('app.lastResult') - .then(result => testDefinition.validationFunc(driver, result, targetInfo)); - - const clickNext = () => driver - .elementById('nextBtn') - .click() - .sleep(1000); - - before(() => { - driver = wd.promiseChainRemote(serverConfig); - require('./helpers/logging').configure(driver); - - return driver.init(getCaps('TestApp')); + after(async function () { + await driver.quit().finally( + () => isSauceLabs && driver.sauceJobStatus(allPassed) + ); }); - after(() => driver - .quit() - .finally(function () { - if (process.env.SAUCE_USERNAME) { - return driver.sauceJobStatus(allPassed); - } - })); - testDefinitions.tests.forEach((definition, index) => { - it(index + ': ' + definition.description, function() { - return clickNext() - .then(() => validateTestIndex(index)) - .then(() => validateTestTitle(definition.description)) - .then(() => waitToBeFinished(definition.timeout || 10000)) - .then(() => validateResult(definition)) - }); + it(index + ': ' + definition.description, function () { + return clickNext(driver) + .then(() => validateTestIndex(driver, index)) + .then(() => validateTestTitle(driver, definition.description)) + .then(() => waitToBeFinished(driver, definition.timeout || 10000)) + .then(() => validateResult(driver, definition.validationFunc, targetInfo)) + }); }); }); + +async function clickNext(driver) { + await driver.elementById('nextBtn').click().sleep(1000); +} + +async function validateTestIndex(driver, testIndex) { + const description = await driver.elementById('descriptionLbl').text(); + const index = parseInt(description.match(/(\d+):/)[1], 10); + + index.should.be.equal(testIndex, 'Test index is not matching!'); +} + +async function validateTestTitle(driver, testTitle) { + const description = await driver.elementById('descriptionLbl').text(); + const title = description.match(/\d+:\ (.*)/)[1]; + + title.should.be.equal(testTitle, 'Test description is not matching!'); +} + +async function waitToBeFinished(driver, timeout) { + const timeoutTimestamp = Date.now() + timeout; + + while (true) { + if (await driver.elementById('statusInput').getValue() === 'finished') { + return true; + } + + if (Date.now() > timeoutTimestamp) { + throw new Error('Test function timed out!'); + } + + await sleep(500); + } +} + +async function validateResult(driver, validationFunc, targetInfo) { + const result = await driver.safeExecute('app.lastResult'); + validationFunc(driver, result, targetInfo); +} + +function sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); +}