From 112073d77d7d29ce4182c4f0c477873384e1e449 Mon Sep 17 00:00:00 2001 From: sebv Date: Sun, 25 May 2014 12:49:55 +0800 Subject: [PATCH] boosting ci --- .travis.yml | 6 +- ci/git-config.sh | 9 - ci/git-push.sh | 17 -- ci/script.sh | 57 ++--- ci/test-config.js | 43 ++++ ci/tools/parallel-mocha.js | 126 ++++++++++ ci/tools/testfiles-tool.js | 61 ----- ci/travis-functional.yml | 32 --- ci/upload_build_to_sauce.sh | 2 +- .../apidemos/touch/multi-actions-specs.js | 142 ++++------- .../touch/multi-actions-with-wait-specs.js | 65 +++++ test/functional/android/toggle-specs.js | 57 ----- test/functional/android/toggle/desired.js | 10 + test/functional/android/toggle/toggle-base.js | 31 +++ .../toggle/toggle-cellular-data-specs.js | 10 + .../toggle/toggle-flight-mode-specs.js | 11 + .../toggle/toggle-location-services-specs.js | 10 + .../android/toggle/toggle-wifi-specs.js | 10 + .../ios/testapp/autoAcceptAlerts-specs.js | 95 ------- .../autoaccept-empty-specs.js | 23 ++ .../autoaccept-false-specs.js | 41 ++++ .../autoaccept-true-specs.js | 41 ++++ test/functional/ios/testapp/basic-specs.js | 231 ------------------ .../ios/testapp/basics/calc-app-1-specs.js | 145 +++++++++++ .../ios/testapp/basics/calc-app-2-specs.js | 84 +++++++ .../ios/uicatalog/background-app-specs.js | 17 ++ test/functional/ios/uicatalog/device-specs.js | 36 --- .../ios/uicatalog/gestures/flick-specs.js | 1 - .../load-app/load-abs-path-app-specs.js | 22 +- .../load-abs-path-zipped-app-specs.js | 22 +- .../load-app/load-rel-path-app-specs.js | 21 +- .../load-rel-path-zipped-app-specs.js | 22 +- .../load-app/load-zipped-url-app-specs.js | 24 +- .../ios/uicatalog/lock-device-specs.js | 21 ++ test/functional/ios/webview/basics-specs.js | 4 +- test/helpers/env.js | 3 +- test/helpers/session.js | 12 +- 37 files changed, 814 insertions(+), 750 deletions(-) delete mode 100755 ci/git-config.sh delete mode 100755 ci/git-push.sh create mode 100644 ci/test-config.js create mode 100644 ci/tools/parallel-mocha.js delete mode 100644 ci/tools/testfiles-tool.js delete mode 100644 ci/travis-functional.yml create mode 100644 test/functional/android/apidemos/touch/multi-actions-with-wait-specs.js delete mode 100644 test/functional/android/toggle-specs.js create mode 100644 test/functional/android/toggle/desired.js create mode 100644 test/functional/android/toggle/toggle-base.js create mode 100644 test/functional/android/toggle/toggle-cellular-data-specs.js create mode 100644 test/functional/android/toggle/toggle-flight-mode-specs.js create mode 100644 test/functional/android/toggle/toggle-location-services-specs.js create mode 100644 test/functional/android/toggle/toggle-wifi-specs.js delete mode 100644 test/functional/ios/testapp/autoAcceptAlerts-specs.js create mode 100644 test/functional/ios/testapp/autoaccept-alerts/autoaccept-empty-specs.js create mode 100644 test/functional/ios/testapp/autoaccept-alerts/autoaccept-false-specs.js create mode 100644 test/functional/ios/testapp/autoaccept-alerts/autoaccept-true-specs.js delete mode 100644 test/functional/ios/testapp/basic-specs.js create mode 100644 test/functional/ios/testapp/basics/calc-app-1-specs.js create mode 100644 test/functional/ios/testapp/basics/calc-app-2-specs.js create mode 100644 test/functional/ios/uicatalog/background-app-specs.js delete mode 100644 test/functional/ios/uicatalog/device-specs.js create mode 100644 test/functional/ios/uicatalog/lock-device-specs.js diff --git a/.travis.yml b/.travis.yml index 9c17f605376..a5f62de706c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,6 +17,11 @@ env: - SAUCE_USERNAME=appium - secure: GXGFbQptLEsKlaWnpOL2EB9hqQ4XBT6tqg7REt5j1h1jYBsfHQHSgSLt1jpzOF5kooXVbw2K7IrZyNVBXEUHR00Hp6O72FBOd+SSj8uPCJBLjAOaJWs0ZtujqGHwHpe6gadFfn0L7q0ffoV5OJVeNHHcuw4f9BaA78fTFNfm1jE= - secure: WLRP9RJIHWPer189swCqMCnexS8XpVQ8FRMSXNxLXp5fUaERVzU7/ncGFeqcM8GWGIU12PP8TJUaMRa58vTGgApW6POMdAPiauepfzoru+n/S4Xrw64hmd4oK0GEZI3wgnkPofUn6TzQeXRmVG496PNzbV8KfqF4mTAesZOLSFA= + - HTTP_RETRIES=5 + - HTTP_RETRY_DELAY=5000 + - DEBUG_CONNECTION=1 + - MOCHA_INIT_TIMEOUT=600000 + - LAUNCH_TIMEOUT='{"global":90000,"afterSimLaunch":30000}' matrix: - CI_CONFIG=unit - CI_CONFIG=build_ios @@ -25,7 +30,6 @@ env: - CI_CONFIG=build_gappium before_script: - gem install --no-rdoc --no-ri appium_doc_lint -- "./ci/git-config.sh" - "./ci/installers/install-node.sh" - "echo Node.js version: `node -v`" - npm install -g jshint grunt-cli mocha diff --git a/ci/git-config.sh b/ci/git-config.sh deleted file mode 100755 index 8929175e813..00000000000 --- a/ci/git-config.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash -set -e -git config --global user.email "appium-ci@appium.io" -git config --global user.name "appium-ci" -PUSH_URL=$(git config --get remote.origin.url | sed s/git:/https:/) -echo "https://${GH_TOKEN}:@github.com" > ${HOME}/.git-credentials -git config credential.helper "store --file=${HOME}/.git-credentials" -git remote set-url --push origin ${PUSH_URL} -git config --global push.default simple diff --git a/ci/git-push.sh b/ci/git-push.sh deleted file mode 100755 index c939146ad6b..00000000000 --- a/ci/git-push.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/sh -set -e - -CI_BRANCH=ci-${BRANCH_CAT}-${TRAVIS_BRANCH} -UPLOAD_INFO_FILE=ci/build-upload-info.json - - -# preparing test branch -git branch -f ${CI_BRANCH} -git checkout ${CI_BRANCH} -cp .travis.yml .travis.yml.master -node ci/tools/travis-yml-tool.js .travis.yml.master ci/travis-functional.yml > .travis.yml -git add . -git commit -a -m "ci ${BRANCH_CAT} branch for build #${TRAVIS_JOB_NUMBER}" - -# pushing -git push -f origin ${CI_BRANCH}:${CI_BRANCH} diff --git a/ci/script.sh b/ci/script.sh index bc1ef54a822..dea0aec8509 100755 --- a/ci/script.sh +++ b/ci/script.sh @@ -1,5 +1,8 @@ #!/bin/bash set -e + +BZ2_FILE=appium-ci-${TRAVIS_BRANCH}-${TRAVIS_JOB_NUMBER}-${TRAVIS_COMMIT:0:10}.tar.bz2 + if [[ $CI_CONFIG == 'unit' ]]; then cd docs appium_doc_lint || exit 1 @@ -13,11 +16,10 @@ elif [[ $CI_CONFIG == 'build_ios' ]]; then ./reset.sh --hardcore --no-npmlink --dev --ios if [[ $TRAVIS_SECURE_ENV_VARS == true ]]; then ./ci/upload_build_to_sauce.sh - GLOB_PATTERNS='test/functional/common/**/*-specs.js' - GLOB_PATTERNS+=',test/functional/ios/**/*-specs.js' - node ci/tools/testfiles-tool.js split "${GLOB_PATTERNS}" > ci/test-split.json - cp ci/mochas/ios71-mocha ci/mocha - BRANCH_CAT=ios ./ci/git-push.sh + TARBALL=sauce-storage:$BZ2_FILE \ + node ./ci/tools/parallel-mocha.js \ + -p 30 \ + -c ios fi elif [[ $CI_CONFIG == 'build_android' ]]; then source ./ci/android_env @@ -27,11 +29,10 @@ elif [[ $CI_CONFIG == 'build_android' ]]; then rm sample-code/apps/ApiDemos mv submodules/ApiDemos sample-code/apps/ ./ci/upload_build_to_sauce.sh - GLOB_PATTERNS='test/functional/common/**/*-specs.js' - GLOB_PATTERNS+=',test/functional/android/**/*-specs.js' - node ci/tools/testfiles-tool.js split "${GLOB_PATTERNS}" > ci/test-split.json - cp ci/mochas/android-mocha ci/mocha - BRANCH_CAT=android ./ci/git-push.sh + TARBALL=sauce-storage:$BZ2_FILE \ + node ./ci/tools/parallel-mocha.js \ + -p 30 \ + -c android fi elif [[ $CI_CONFIG == 'build_selendroid' ]]; then source ./ci/android_env @@ -44,10 +45,10 @@ elif [[ $CI_CONFIG == 'build_selendroid' ]]; then mv submodules/selendroid/selendroid-test-app/target/selendroid-test-app-0.10.0.apk \ sample-code/apps/selendroid-test-app.apk ./ci/upload_build_to_sauce.sh - GLOB_PATTERNS='test/functional/selendroid/**/*-specs.js' - node ci/tools/testfiles-tool.js split "${GLOB_PATTERNS}" > ci/test-split.json - cp ci/mochas/selendroid-mocha ci/mocha - BRANCH_CAT=selendroid ./ci/git-push.sh + TARBALL=sauce-storage:$BZ2_FILE \ + node ./ci/tools/parallel-mocha.js \ + -p 30 \ + -c selendroid fi elif [[ $CI_CONFIG == 'build_gappium' ]]; then source ./ci/android_env @@ -61,29 +62,9 @@ elif [[ $CI_CONFIG == 'build_gappium' ]]; then rm sample-code/apps/io.appium.gappium.sampleapp mv submodules/io.appium.gappium.sampleapp sample-code/apps/ ./ci/upload_build_to_sauce.sh - sed -i.bak s/CI_CONFIG=functional/CI_CONFIG=run_gappium/g ci/travis-functional.yml - rm ci/*.bak - BRANCH_CAT=gappium ./ci/git-push.sh - fi -elif [[ $CI_CONFIG == 'functional' ]]; then - TARBALL=sauce-storage:$(node ./ci/tools/build-upload-tool.js \ - ./ci/build-upload-info.json filename) - echo node ci/tools/testfiles-tool.js list ci/test-split.json "${TEST_GROUP}" - TEST_FILES=$(node ci/tools/testfiles-tool.js list ci/test-split.json "${TEST_GROUP}") - echo "TEST_FILES --> ${TEST_FILES}" - if [[ -n "$TEST_FILES" ]]; then - TARBALL=$TARBALL ci/mocha ${TEST_FILES} - fi -elif [[ $CI_CONFIG == 'run_gappium' ]]; then - TARBALL=sauce-storage:$(node ./ci/tools/build-upload-tool.js \ - ./ci/build-upload-info.json filename) - if [[ $TEST_GROUP == 'group 1' ]]; then - # TODO: investigate WEBVIEW context issue - echo 'skipping android gappium test' - # TARBALL=$TARBALL ci/mochas/android-mocha test/functional/gappium/**/*-specs.js - elif [[ $TEST_GROUP == 'group 2' ]]; then - TARBALL=$TARBALL ci/mochas/ios71-mocha test/functional/gappium/**/*-specs.js - elif [[ $TEST_GROUP == 'group 3' ]]; then - TARBALL=$TARBALL ci/mochas/selendroid-mocha test/functional/gappium/**/*-specs.js + TARBALL=sauce-storage:$BZ2_FILE \ + node ./ci/tools/parallel-mocha.js \ + -p 30 \ + -c gappium fi fi diff --git a/ci/test-config.js b/ci/test-config.js new file mode 100644 index 00000000000..d91aac6b527 --- /dev/null +++ b/ci/test-config.js @@ -0,0 +1,43 @@ +module.exports = { + 'ios':{ + 'mocha-bin': 'ci/mochas/ios71-mocha', + 'glob-patterns': [ + 'test/functional/common/**/*-specs.js', + 'test/functional/ios/**/*-specs.js' + ] + }, + 'android':{ + 'mocha-bin': 'ci/mochas/android-mocha', + 'glob-patterns': [ + 'test/functional/common/**/*-specs.js', + 'test/functional/android/**/*-specs.js' + ] + }, + 'selendroid':{ + 'mocha-bin': 'ci/mochas/selendroid-mocha', + 'glob-patterns': [ + 'test/functional/selendroid/**/*-specs.js' + ] + }, + 'gappium':[ + { + 'mocha-bin': 'ci/mochas/ios71-mocha', + 'glob-patterns': [ + 'test/functional/gappium/**/*-specs.js' + ] + }, + // TODO: gappium/android hangs on sauce. + // { + // 'mocha-bin': 'ci/mochas/android-mocha', + // 'glob-patterns': [ + // 'test/functional/gappium/**/*-specs.js' + // ] + // }, + { + 'mocha-bin': 'ci/mochas/selendroid-mocha', + 'glob-patterns': [ + 'test/functional/gappium/**/*-specs.js' + ] + } + ] +}; diff --git a/ci/tools/parallel-mocha.js b/ci/tools/parallel-mocha.js new file mode 100644 index 00000000000..dd8c3b7c86d --- /dev/null +++ b/ci/tools/parallel-mocha.js @@ -0,0 +1,126 @@ +'use strict'; + +var args = process.argv.slice(2), + _ = require('underscore'), + glob = require('glob'), + async = require('async'), + Q = require("q"), + spawn = require('child_process').spawn, + argparse = require('argparse'); + + +var parser = new argparse.ArgumentParser({ + description: 'Parallel Mocha' +}); +parser.addArgument( + [ '-p' ], + { + help: 'number of processes', + type: 'int', + required: true, + dest: 'numOfWorkers' + } +); +parser.addArgument( + [ '-c' ], + { + help: 'test config', + required: true, + dest: 'config' + } +); +var args = parser.parseArgs(); + +var config = require('../test-config'); + +var fileInfos = config[args.config]; +if (!(fileInfos instanceof Array)) { + fileInfos = [fileInfos]; +} + +var allGood = true; + +function runOne(mochaBin, filepath) { + var deferred = Q.defer(); + var out = ''; + var child = spawn(mochaBin, [filepath]); + child.on('error', function (err) { + deferred.reject(err); + }); + + child.stdout.setEncoding('utf8'); + child.stdout.on('data', function (data) { + out += data; + }); + child.stderr.setEncoding('utf8'); + child.stderr.on('data', function (data) { + out += data; + }); + child.on('close', function (code) { + if (code !== 0) { + var err = new Error( + "Mocha process terminated with code: " + code + '.'); + err.out = out; + return deferred.reject(err); + } + deferred.resolve(out); + }); + return deferred.promise; +} + +var queue = async.queue(function (task, done) { + var startMs = Date.now(); + console.log('running ' + task.filepath); + + var interval = setInterval(function () { + console.log(task.filepath, 'has been running for', + Math.round((Date.now() - startMs)/1000), 'seconds.'); + }, 300000); + + runOne(task.mochaBin, task.filepath) + .then(function (out) { + console.log('finished to run', task.filepath,'\n', out); + }) + .catch(function (err) { + allGood = false; + console.error(err + (err.out ? '\n' + err.out : '')); + }).fin(function () { clearInterval(interval); }) + .nodeify(done); +}, args.numOfWorkers); + +queue.drain = function () { + if (allGood) { + console.log('\n\nALL GOOD\n\n'); + process.exit(0); + } + else { + throw new Error('FAILING TESTS!'); + } +}; + +function fillQueue(fileInfo) { + var deferred = Q.defer(); + async.eachSeries( + fileInfo['glob-patterns'], + function (globPattern, done) { + glob(globPattern, function (err, _files) { + if (err) return done(err); + _(_files).map(function (file) { + queue.push({mochaBin: fileInfo['mocha-bin'], filepath: file}); + }); + done(); + }); + }, + function (err) { + if (err) { + return deferred.reject(err); + } + deferred.resolve(); + } + ); + return deferred.promise; +} + +Q.all(_(fileInfos).map(function (fileInfo) { + return fillQueue(fileInfo); +})).done(); diff --git a/ci/tools/testfiles-tool.js b/ci/tools/testfiles-tool.js deleted file mode 100644 index b9601b10acf..00000000000 --- a/ci/tools/testfiles-tool.js +++ /dev/null @@ -1,61 +0,0 @@ -'use strict'; - -var args = process.argv.slice(2), - _ = require('underscore'), - glob = require('glob'), - fs = require('fs'), - async = require('async'); - -var action = args[0]; - -function split() { - var globPatterns = args[1].split(','); - var blackList = _((args[2] || "").split(',')).map(function (filename) { - return filename.trim(); - }); - - var groups = {}; - _(5).times(function (i) { groups['group ' + (i + 1)] = []; }); - var files = []; - async.eachSeries( - globPatterns, - function (globPattern, done) { - glob(globPattern, function (err, _files) { - if (err) return done(err); - files = _.union(files, _files); - done(); - }); - }, - function (err) { - if (err) { - console.log(err); - process.exit(1); - } - files = _(files).reject(function (filename) { - return blackList.indexOf(filename) >= 0; - }); - _(files).each(function (filename, i) { - groups['group ' + ((i % 5) + 1)].push(filename); - }); - console.log(JSON.stringify(groups, null, 2)); - } - ); -} - -function list() { - var splitData = JSON.parse(fs.readFileSync(args[1], 'utf8')); - var group = args[2]; - console.log(splitData[group].join(' ')); -} - -switch (action) { - case 'split': - split(); - break; - case 'list': - list(); - break; - default: - console.log("Invalid action"); - process.exit(1); -} diff --git a/ci/travis-functional.yml b/ci/travis-functional.yml deleted file mode 100644 index e2858deced9..00000000000 --- a/ci/travis-functional.yml +++ /dev/null @@ -1,32 +0,0 @@ -language: node_js -node_js: -- '0.10' -notifications: - email: false -git: - submodules: false -branches: - only: - - /^ci-.*$/ -env: - global: - - CI_CONFIG=functional - - SAUCE_REST_ROOT=auto - - APPIUM_HOST=auto - - APPIUM_PORT=auto - - SAUCE_USERNAME=auto - - HTTP_TIMEOUT=300000 - - HTTP_RETRIES=5 - - HTTP_RETRY_DELAY=10000 - - DEBUG_CONNECTION=1 - - MOCHA_INIT_TIMEOUT=600000 - - LAUNCH_TIMEOUT='{"global":120000,"afterSimLaunch":30000}' - # - matrix: - - TEST_GROUP="group 1" - - TEST_GROUP="group 2" - - TEST_GROUP="group 3" - - TEST_GROUP="group 4" - - TEST_GROUP="group 5" -script: -- "./ci/script.sh" diff --git a/ci/upload_build_to_sauce.sh b/ci/upload_build_to_sauce.sh index 8b22a832ea6..14f10d7789e 100755 --- a/ci/upload_build_to_sauce.sh +++ b/ci/upload_build_to_sauce.sh @@ -2,7 +2,7 @@ set -e BZ2_FILE=appium-ci-${TRAVIS_BRANCH}-${TRAVIS_JOB_NUMBER}-${TRAVIS_COMMIT:0:10}.tar.bz2 -UPLOAD_INFO_FILE=ci/build-upload-info.json +UPLOAD_INFO_FILE=/tmp/build-upload-info.json # zipping/uploading tar \ diff --git a/test/functional/android/apidemos/touch/multi-actions-specs.js b/test/functional/android/apidemos/touch/multi-actions-specs.js index 42ff3481b10..6d89c691f85 100644 --- a/test/functional/android/apidemos/touch/multi-actions-specs.js +++ b/test/functional/android/apidemos/touch/multi-actions-specs.js @@ -20,105 +20,51 @@ describe("apidemo - touch - multi-actions", function () { }); } - describe('multi actions', function () { - it('should scroll two different lists', function (done) { - var scrollOpts = {}; - driver - .elementByClassName(droidList) - .then(function (el) { - scrollOpts = { - element: el.value - , text: 'Views' - }; - return driver.execute("mobile: scrollTo", [scrollOpts]); - }).elementByXPath("//" + droidText + "[@text='Views']") - .then(function (el) { - return new TouchAction().tap().performOn(el); - }) - .then(function () { - scrollOpts.text = 'Splitting Touches across Views'; - return driver.execute("mobile: scrollTo", [scrollOpts]); - }).elementByXPath("//" + droidText + "[@text='Splitting Touches across Views']") - .then(function (el) { - return new TouchAction().tap().performOn(el); - }) - .elementsByClassName(droidList) - .then(function (els) { - // scroll slowly on the left - var a1 = new TouchAction().applyTo(els[0]); - a1 - .press() - .moveTo({ x: 10, y: 0 }) - .moveTo({ x: 10, y: -75 }) - .moveTo({ x: 10, y: -150 }) - .release(); + it('should scroll two different lists', function (done) { + var scrollOpts = {}; + driver + .elementByClassName(droidList) + .then(function (el) { + scrollOpts = { + element: el.value + , text: 'Views' + }; + return driver.execute("mobile: scrollTo", [scrollOpts]); + }).elementByXPath("//" + droidText + "[@text='Views']") + .then(function (el) { + return new TouchAction().tap().performOn(el); + }) + .then(function () { + scrollOpts.text = 'Splitting Touches across Views'; + return driver.execute("mobile: scrollTo", [scrollOpts]); + }).elementByXPath("//" + droidText + "[@text='Splitting Touches across Views']") + .then(function (el) { + return new TouchAction().tap().performOn(el); + }) + .elementsByClassName(droidList) + .then(function (els) { + // scroll slowly on the left + var a1 = new TouchAction().applyTo(els[0]); + a1 + .press() + .moveTo({ x: 10, y: 0 }) + .moveTo({ x: 10, y: -75 }) + .moveTo({ x: 10, y: -150 }) + .release(); - // scross quickly on the right - var a2 = new TouchAction().applyTo(els[1]); - a2 - .press() - .moveTo({ x: 10, y: 0 }) - .moveTo({ x: 10, y: -300 }) - .moveTo({ x: 10, y: -600 }) - .release(); + // scross quickly on the right + var a2 = new TouchAction().applyTo(els[1]); + a2 + .press() + .moveTo({ x: 10, y: 0 }) + .moveTo({ x: 10, y: -300 }) + .moveTo({ x: 10, y: -600 }) + .release(); - var ma = new MultiAction(); - ma.add(a1, a2); - return ma.performOn(els[0]); - }) - .sleep(15000) - .nodeify(done); - }); - - it('should scroll two different lists with waits', function (done) { - var scrollOpts = {}; - driver - .elementByClassName(droidList) - .then(function (el) { - scrollOpts = { - element: el.value - , text: 'Views' - }; - return driver.execute("mobile: scrollTo", [scrollOpts]); - }).elementByXPath("//" + droidText + "[@text='Views']") - .then(function (el) { - return new TouchAction().tap().performOn(el); - }) - .then(function () { - scrollOpts.text = 'Splitting Touches across Views'; - return driver.execute("mobile: scrollTo", [scrollOpts]); - }).elementByXPath("//" + droidText + "[@text='Splitting Touches across Views']") - .then(function (el) { - return new TouchAction().tap().performOn(el); - }) - .elementsByClassName(droidList) - .then(function (els) { - // scroll slowly on the left - var a1 = new TouchAction().applyTo(els[0]); - a1 - .press() - .moveTo({ x: 10, y: 0 }) - .moveTo({ x: 10, y: -75 }) - .wait(1000) - .moveTo({ x: 10, y: -350 }) - .release(); - - // scross quickly on the right - var a2 = new TouchAction().applyTo(els[1]); - a2 - .press() - .moveTo({ x: 10, y: 100 }) - .moveTo({ x: 10, y: -300 }) - .wait(500) - .moveTo({ x: 10, y: -600 }) - .release(); - - var ma = new MultiAction(); - ma.add(a1, a2); - return ma.performOn(els[0]); - }) - .sleep(15000) - .nodeify(done); - }); + var ma = new MultiAction(); + ma.add(a1, a2); + return ma.performOn(els[0]); + }) + .nodeify(done); }); }); diff --git a/test/functional/android/apidemos/touch/multi-actions-with-wait-specs.js b/test/functional/android/apidemos/touch/multi-actions-with-wait-specs.js new file mode 100644 index 00000000000..60fc917dc98 --- /dev/null +++ b/test/functional/android/apidemos/touch/multi-actions-with-wait-specs.js @@ -0,0 +1,65 @@ +"use strict"; + +var setup = require("../../../common/setup-base") + , desired = require("../desired") + , wd = require("wd") + , droidText = 'android.widget.TextView' + , droidList = 'android.widget.ListView' + , TouchAction = wd.TouchAction + , MultiAction = wd.MultiAction; + + +describe("apidemo - touch - multi-actions with wait", function () { + var driver; + setup(this, desired).then(function (d) { driver = d; }); + + it('should scroll two different lists with waits', function (done) { + var scrollOpts = {}; + driver + .elementByClassName(droidList) + .then(function (el) { + scrollOpts = { + element: el.value + , text: 'Views' + }; + return driver.execute("mobile: scrollTo", [scrollOpts]); + }).elementByXPath("//" + droidText + "[@text='Views']") + .then(function (el) { + return new TouchAction().tap().performOn(el); + }) + .then(function () { + scrollOpts.text = 'Splitting Touches across Views'; + return driver.execute("mobile: scrollTo", [scrollOpts]); + }).elementByXPath("//" + droidText + "[@text='Splitting Touches across Views']") + .then(function (el) { + return new TouchAction().tap().performOn(el); + }) + .elementsByClassName(droidList) + .then(function (els) { + // scroll slowly on the left + var a1 = new TouchAction().applyTo(els[0]); + a1 + .press() + .moveTo({ x: 10, y: 0 }) + .moveTo({ x: 10, y: -75 }) + .wait(1000) + .moveTo({ x: 10, y: -350 }) + .release(); + + // scross quickly on the right + var a2 = new TouchAction().applyTo(els[1]); + a2 + .press() + .moveTo({ x: 10, y: 100 }) + .moveTo({ x: 10, y: -300 }) + .wait(500) + .moveTo({ x: 10, y: -600 }) + .release(); + + var ma = new MultiAction(); + ma.add(a1, a2); + return ma.performOn(els[0]); + }) + .nodeify(done); + }); +}); diff --git a/test/functional/android/toggle-specs.js b/test/functional/android/toggle-specs.js deleted file mode 100644 index 0ab1fa7f1ae..00000000000 --- a/test/functional/android/toggle-specs.js +++ /dev/null @@ -1,57 +0,0 @@ -"use strict"; - -var setup = require("../common/setup-base") - , getAppPath = require('../../helpers/app').getAppPath; - -module.exports = { - app: getAppPath('TestApp') -}; - -var desired = { - app: getAppPath('ToggleTest'), - appPackage: 'com.example.toggletest', - appActivity: '.MainActivity', - newCommandTimeout: 90 -}; - -var toggleTest = function (promisedBrowser, displayName, toggleElementName, toggleMethod) { - return function () { - var driver; - promisedBrowser.then(function (d) { driver = d; }); - - var initialValue; - it('should toggle ' + displayName, function (done) { - driver - .elementByName(toggleElementName).text().then(function (txt) { - initialValue = txt; - return driver[toggleMethod](); - }) - .then(function () { - return driver.elementByName(toggleElementName).text().then(function (txt) { - txt.should.equal(initialValue === "ON" ? "OFF" : "ON"); - }); - }) - .nodeify(done); - }); - - it('should toggle ' + displayName + ' back to initial value', function (done) { - driver[toggleMethod]() - .then(function () { - return driver.elementByName(toggleElementName).text().then(function (txt) { - txt.should.equal(initialValue); - }); - }) - .nodeify(done); - }); - }; -}; - -describe('toggles', function () { - var promisedBrowser = setup(this, desired); - - describe('toggle cellular data', toggleTest(promisedBrowser, "cellular data", "data_toggle", "toggleData")); - describe('toggle Flight Mode', toggleTest(promisedBrowser, "Flight Mode", "flight_toggle", "toggleFlightMode")); - // toggle Wi-Fi doesn't work on emulator, real device only. - describe('toggle Wi-Fi @skip-android-all', toggleTest(promisedBrowser, "Wi-Fi", "wifi_toggle", "toggleWiFi")); - describe('toggle Location Services', toggleTest(promisedBrowser, "Location Services", "gps_toggle", "toggleLocationServices")); -}); diff --git a/test/functional/android/toggle/desired.js b/test/functional/android/toggle/desired.js new file mode 100644 index 00000000000..dbb36c06042 --- /dev/null +++ b/test/functional/android/toggle/desired.js @@ -0,0 +1,10 @@ +"use strict"; + +var getAppPath = require('../../../helpers/app').getAppPath; + +module.exports = { + app: getAppPath('ToggleTest'), + appPackage: 'com.example.toggletest', + appActivity: '.MainActivity', + newCommandTimeout: 90 +}; diff --git a/test/functional/android/toggle/toggle-base.js b/test/functional/android/toggle/toggle-base.js new file mode 100644 index 00000000000..14c1fedc89e --- /dev/null +++ b/test/functional/android/toggle/toggle-base.js @@ -0,0 +1,31 @@ +"use strict"; + +exports.toggleTest = function (promisedBrowser, displayName, toggleElementName, toggleMethod) { + var driver; + promisedBrowser.then(function (d) { driver = d; }); + + var initialValue; + it('should toggle ' + displayName, function (done) { + driver + .elementByName(toggleElementName).text().then(function (txt) { + initialValue = txt; + return driver[toggleMethod](); + }) + .then(function () { + return driver.elementByName(toggleElementName).text().then(function (txt) { + txt.should.equal(initialValue === "ON" ? "OFF" : "ON"); + }); + }) + .nodeify(done); + }); + + it('should toggle ' + displayName + ' back to initial value', function (done) { + driver[toggleMethod]() + .then(function () { + return driver.elementByName(toggleElementName).text().then(function (txt) { + txt.should.equal(initialValue); + }); + }) + .nodeify(done); + }); +}; diff --git a/test/functional/android/toggle/toggle-cellular-data-specs.js b/test/functional/android/toggle/toggle-cellular-data-specs.js new file mode 100644 index 00000000000..2a8969fb22a --- /dev/null +++ b/test/functional/android/toggle/toggle-cellular-data-specs.js @@ -0,0 +1,10 @@ +"use strict"; + +var setup = require("../../common/setup-base"), + desired = require('./desired'), + toggleTest = require('./toggle-base').toggleTest; + +describe('toggle - cellular data', function () { + var promisedBrowser = setup(this, desired); + toggleTest(promisedBrowser, "cellular data", "data_toggle", "toggleData"); +}); diff --git a/test/functional/android/toggle/toggle-flight-mode-specs.js b/test/functional/android/toggle/toggle-flight-mode-specs.js new file mode 100644 index 00000000000..cac08b0ca02 --- /dev/null +++ b/test/functional/android/toggle/toggle-flight-mode-specs.js @@ -0,0 +1,11 @@ +"use strict"; + +var setup = require("../../common/setup-base"), + desired = require('./desired'), + toggleTest = require('./toggle-base').toggleTest; + +// TODO: very flaky on sauce, investigate. +describe('toggle - flight mode @skip-ci', function () { + var promisedBrowser = setup(this, desired); + toggleTest(promisedBrowser, "Flight Mode", "flight_toggle", "toggleFlightMode"); +}); diff --git a/test/functional/android/toggle/toggle-location-services-specs.js b/test/functional/android/toggle/toggle-location-services-specs.js new file mode 100644 index 00000000000..2b8230d7fdd --- /dev/null +++ b/test/functional/android/toggle/toggle-location-services-specs.js @@ -0,0 +1,10 @@ +"use strict"; + +var setup = require("../../common/setup-base"), + desired = require('./desired'), + toggleTest = require('./toggle-base').toggleTest; + +describe('toggle - location services', function () { + var promisedBrowser = setup(this, desired); + toggleTest(promisedBrowser, "Location Services", "gps_toggle", "toggleLocationServices"); +}); diff --git a/test/functional/android/toggle/toggle-wifi-specs.js b/test/functional/android/toggle/toggle-wifi-specs.js new file mode 100644 index 00000000000..ee31c5c7281 --- /dev/null +++ b/test/functional/android/toggle/toggle-wifi-specs.js @@ -0,0 +1,10 @@ +"use strict"; + +var setup = require("../../common/setup-base"), + desired = require('./desired'), + toggleTest = require('./toggle-base').toggleTest; + +describe('toggle - wifi @skip-android-all', function () { + var promisedBrowser = setup(this, desired); + toggleTest(promisedBrowser, "Wi-Fi", "wifi_toggle", "toggleWiFi"); +}); diff --git a/test/functional/ios/testapp/autoAcceptAlerts-specs.js b/test/functional/ios/testapp/autoAcceptAlerts-specs.js deleted file mode 100644 index 4bfee731f66..00000000000 --- a/test/functional/ios/testapp/autoAcceptAlerts-specs.js +++ /dev/null @@ -1,95 +0,0 @@ -"use strict"; - -var setup = require("../../common/setup-base"), - desired = require('./desired'), - _ = require('underscore'); - -describe('testapp - autoAcceptAlerts cap = true', function () { - var self = this; - var driver; - - var caps = _.clone(desired); - caps.autoAcceptAlerts = true; - setup(self, caps).then(function (d) { driver = d; }); - - it('auto-accepts alerts', function (done) { - driver.elementsByClassName('UIAButton') - .then(function (buttons) { return buttons[3].click(); }) - .sleep(2000) - .alertText() - .should.be.rejectedWith(/status: 27/) - .nodeify(done); - }); -}); - -describe('testapp - autoAcceptAlerts cap = false', function () { - var self = this; - var driver; - - var caps = _.clone(desired); - caps.autoAcceptAlerts = false; - setup(self, caps).then(function (d) { driver = d; }); - - it('does not auto-accept alerts', function (done) { - driver.elementsByClassName('UIAButton') - .then(function (buttons) { return buttons[3].click(); }) - .sleep(2000) - .alertText() - .should.eventually.exist - .nodeify(done); - }); -}); - -describe('testapp - autoAcceptAlerts cap = "true"', function () { - var self = this; - var driver; - - var caps = _.clone(desired); - caps.autoAcceptAlerts = 'true'; - setup(self, caps).then(function (d) { driver = d; }); - - it('auto-accepts alerts', function (done) { - driver.elementsByClassName('UIAButton') - .then(function (buttons) { return buttons[3].click(); }) - .sleep(2000) - .alertText() - .should.be.rejectedWith(/status: 27/) - .nodeify(done); - }); -}); - -describe('testapp - autoAcceptAlerts cap = "false"', function () { - var self = this; - var driver; - - var caps = _.clone(desired); - caps.autoAcceptAlerts = 'false'; - setup(self, caps).then(function (d) { driver = d; }); - - it('does not auto-accept alerts', function (done) { - driver.elementsByClassName('UIAButton') - .then(function (buttons) { return buttons[3].click(); }) - .sleep(2000) - .alertText() - .should.eventually.exist - .nodeify(done); - }); -}); - -describe('testapp - autoAcceptAlerts cap = ""', function () { - var self = this; - var driver; - - var caps = _.clone(desired); - caps.autoAcceptAlerts = ""; - setup(self, caps).then(function (d) { driver = d; }); - - it('does not auto-accept alerts', function (done) { - driver.elementsByClassName('UIAButton') - .then(function (buttons) { return buttons[3].click(); }) - .sleep(2000) - .alertText() - .should.eventually.exist - .nodeify(done); - }); -}); diff --git a/test/functional/ios/testapp/autoaccept-alerts/autoaccept-empty-specs.js b/test/functional/ios/testapp/autoaccept-alerts/autoaccept-empty-specs.js new file mode 100644 index 00000000000..86d998d86aa --- /dev/null +++ b/test/functional/ios/testapp/autoaccept-alerts/autoaccept-empty-specs.js @@ -0,0 +1,23 @@ +"use strict"; + +var setup = require("../../../common/setup-base"), + desired = require('../desired'), + _ = require('underscore'); + +describe('testapp - autoAcceptAlerts cap = ""', function () { + var self = this; + var driver; + + var caps = _.clone(desired); + caps.autoAcceptAlerts = ""; + setup(self, caps).then(function (d) { driver = d; }); + + it('does not auto-accept alerts', function (done) { + driver.elementsByClassName('UIAButton') + .then(function (buttons) { return buttons[3].click(); }) + .sleep(2000) + .alertText() + .should.eventually.exist + .nodeify(done); + }); +}); diff --git a/test/functional/ios/testapp/autoaccept-alerts/autoaccept-false-specs.js b/test/functional/ios/testapp/autoaccept-alerts/autoaccept-false-specs.js new file mode 100644 index 00000000000..5cf2a1ce22b --- /dev/null +++ b/test/functional/ios/testapp/autoaccept-alerts/autoaccept-false-specs.js @@ -0,0 +1,41 @@ +"use strict"; + +var setup = require("../../../common/setup-base"), + desired = require('../desired'), + _ = require('underscore'); + +describe('testapp - autoAcceptAlerts cap = false', function () { + var self = this; + var driver; + + var caps = _.clone(desired); + caps.autoAcceptAlerts = false; + setup(self, caps).then(function (d) { driver = d; }); + + it('does not auto-accept alerts', function (done) { + driver.elementsByClassName('UIAButton') + .then(function (buttons) { return buttons[3].click(); }) + .sleep(2000) + .alertText() + .should.eventually.exist + .nodeify(done); + }); +}); + +describe('testapp - autoAcceptAlerts cap = "false"', function () { + var self = this; + var driver; + + var caps = _.clone(desired); + caps.autoAcceptAlerts = 'false'; + setup(self, caps).then(function (d) { driver = d; }); + + it('does not auto-accept alerts', function (done) { + driver.elementsByClassName('UIAButton') + .then(function (buttons) { return buttons[3].click(); }) + .sleep(2000) + .alertText() + .should.eventually.exist + .nodeify(done); + }); +}); diff --git a/test/functional/ios/testapp/autoaccept-alerts/autoaccept-true-specs.js b/test/functional/ios/testapp/autoaccept-alerts/autoaccept-true-specs.js new file mode 100644 index 00000000000..6d7ad0c0d34 --- /dev/null +++ b/test/functional/ios/testapp/autoaccept-alerts/autoaccept-true-specs.js @@ -0,0 +1,41 @@ +"use strict"; + +var setup = require("../../../common/setup-base"), + desired = require('../desired'), + _ = require('underscore'); + +describe('testapp - autoAcceptAlerts cap = true', function () { + var self = this; + var driver; + + var caps = _.clone(desired); + caps.autoAcceptAlerts = true; + setup(self, caps).then(function (d) { driver = d; }); + + it('auto-accepts alerts', function (done) { + driver.elementsByClassName('UIAButton') + .then(function (buttons) { return buttons[3].click(); }) + .sleep(2000) + .alertText() + .should.be.rejectedWith(/status: 27/) + .nodeify(done); + }); +}); + +describe('testapp - autoAcceptAlerts cap = "true"', function () { + var self = this; + var driver; + + var caps = _.clone(desired); + caps.autoAcceptAlerts = 'true'; + setup(self, caps).then(function (d) { driver = d; }); + + it('auto-accepts alerts', function (done) { + driver.elementsByClassName('UIAButton') + .then(function (buttons) { return buttons[3].click(); }) + .sleep(2000) + .alertText() + .should.be.rejectedWith(/status: 27/) + .nodeify(done); + }); +}); diff --git a/test/functional/ios/testapp/basic-specs.js b/test/functional/ios/testapp/basic-specs.js deleted file mode 100644 index e0a5f1ae657..00000000000 --- a/test/functional/ios/testapp/basic-specs.js +++ /dev/null @@ -1,231 +0,0 @@ -// This is basically a port of webdriver-test.py -// https://github.com/hugs/appium/blob/master/sample-code/webdriver-test.py -"use strict"; - -var env = require('../../../helpers/env') - , setup = require("../../common/setup-base") - , desired = require('./desired') - , Q = require("q") - , fs = require('fs') - , path = require('path') - , _ = require("underscore") - , filterVisible = require('../../../helpers/ios-uiautomation').filterVisible; - -describe('testapp - basic', function () { - - describe('using calc app - 1', function () { - var driver; - setup(this, desired).then(function (d) { driver = d; }); - - var values = null; - - var clearFields = function (driver) { - values = []; - return driver - .elementsByClassName('UIATextField').then(function (elems) { - var sequence = _(elems).map(function (elem) { - return function () { return elem.clear(); }; - }); - return sequence.reduce(Q.when, new Q()); // running sequence - }).then(function () { - return driver.elementByClassName('UIAButton').click(); - }); - }; - - var populate = function (type, driver) { - values = []; - return driver - .elementsByIosUIAutomation(filterVisible('.textFields();')) - .then(function (elems) { - var sequence = _(elems).map(function (elem) { - var val = Math.round(Math.random() * 10); - values.push(val); - if (type === "elem") { - return function () { return elem.sendKeys(val); }; - } else if (type === "elem-setvalue") { - return function () { return elem.setImmediateValue(val); }; - } else if (type === "driver") { - return function () { return elem.click().keys(val); }; - } - }); - return sequence.reduce(Q.when, new Q()); // running sequence - }); - }; - - var computeAndCheck = function (driver) { - return driver - .elementByClassName('UIAButton').click() - .elementByClassName('UIAStaticText').text().then(function (text) { - parseInt(text, 10).should.equal(values[0] + values[1]); - }); - }; - - if (env.FAST_TESTS) { - beforeEach(function (done) { - clearFields(driver).nodeify(done); - }); - } - - it('should fill two fields with numbers', function (done) { - populate("elem", driver) - .then(computeAndCheck.bind(null, driver)) - .nodeify(done); - }); - - // using sendKeysToActiveElement - it('should fill two fields with numbers - sendKeys', function (done) { - populate("driver", driver) - .then(computeAndCheck.bind(null, driver)) - .nodeify(done); - }); - - it('should fill two fields with numbers - setValue', function (done) { - populate("elem-setvalue", driver) - .then(computeAndCheck.bind(null, driver)) - .nodeify(done); - }); - - it('should confirm that button is displayed', function (done) { - driver - .elementByClassName('UIATextField').isDisplayed() - .should.eventually.be.ok - .nodeify(done); - }); - - it('should confirm that the disabled button is disabled', function (done) { - driver - .elementByName('DisabledButton').isEnabled() - .should.not.eventually.be.ok - .nodeify(done); - }); - - it('should confirm that the compute sum button is enabled', function (done) { - driver - .elementByName('ComputeSumButton').isEnabled() - .should.eventually.be.ok - .nodeify(done); - }); - - it('should interact with alert', function (done) { - driver.elementsByClassName('UIAButton').then(function (buttons) { - return buttons[1]; - }).then(function (button) { - return button - .click() - .acceptAlert() - .then(function () { return button.click(); }) - .alertText().then(function (text) { - text.should.include("Cool title"); - text.should.include("this alert is so cool."); - }).dismissAlert(); - }) - .nodeify(done); - }); - - - it('should find alert like other elements', function (done) { - driver.elementsByClassName('UIAButton').then(function (buttons) { - return buttons[1]; - }).then(function (button) { - return button.click() - .elementByClassName('UIAAlert') - // maybe we could get alert body text too? - .elementByClassName('>', 'UIAStaticText').text().should.become("Cool title") - .dismissAlert(); - }) - .nodeify(done); - }); - - it('should get tag names of elements', function (done) { - driver - .elementByClassName('UIAButton').getTagName().should.become("UIAButton") - .elementByClassName('UIAStaticText').getTagName().should.become("UIAStaticText") - .nodeify(done); - }); - - it('should be able to get text of a button', function (done) { - driver - .elementByClassName('UIAButton').text().should.become("ComputeSumButton") - .nodeify(done); - }); - - }); // end describe - - describe('using calc app - 2', function () { - var driver; - setup(this, desired).then(function (d) { driver = d; }); - - var sum = 0 - , lookup = function (textFieldNum) { - var num = Math.round(Math.random() * 10000); - sum += num; - return driver - .elementByName('TextField' + textFieldNum) - .sendKeys(num); - }; - - it('should lookup two fields by name and populate them with ' + - 'random numbers to finally sum them up', function (done) { - driver.elementByName('SumLabel').then(function (sumLabel) { - return driver.chain() - .then(lookup.bind(null, 1)) - .then(lookup.bind(null, 2)) - .elementByName('ComputeSumButton').click() - .then(function () { return sumLabel.text(); }) - .then(function (text) { parseInt(text, 10).should.equal(sum); }); - }).nodeify(done); - }); - - it('should receive correct error', function (done) { - driver - .execute("mobile: doesn't exist") - .then(function () {}, function (err) { - err.cause.value.message.should.equal("Not yet implemented. " + - "Please help us: http://appium.io/get-involved.html"); - throw err; - }).should.be.rejectedWith(/status: 13/) - .nodeify(done); - }); - - it('should be able to get syslog log type', function (done) { - driver.logTypes().then(function (logTypes) { - logTypes.should.include('syslog'); - logTypes.should.include('crashlog'); - logTypes.should.not.include('logcat'); - }).nodeify(done); - }); - - // TODO: Fails on sauce, investigate - it('should be able to get syslog logs @skip-ios6 @skip-ci', function (done) { - driver - .setImplicitWaitTimeout(4000) - .elementByName('SumLabelz') - .should.be.rejectedWith(/status: 7/) - .log('syslog').then(function (logs) { - logs.length.should.be.above(0); - logs[0].message.should.not.include("\n"); - logs[0].level.should.equal("ALL"); - logs[0].timestamp.should.exist; - }) - .nodeify(done); - }); - - it('should be able to get crashlog logs @skip-ci', function (done) { - var dir = path.resolve(process.env.HOME, "Library", "Logs", "DiagnosticReports"); - var msg = 'boom'; - var numBeforeLogs; - driver - .log('crashlog').then(function (logsBefore) { - numBeforeLogs = logsBefore.length; - fs.writeFileSync(dir + '/myApp_' + Date.parse(new Date()) + '_rocksauce.crash', msg); - }).log('crashlog').then(function (logsAfter) { - logsAfter.length.should.be.above(0); - logsAfter.length.should.not.equal(numBeforeLogs); - logsAfter[0].message.should.not.include("\n"); - logsAfter[0].message.should.equal(msg); - logsAfter[0].level.should.equal("ALL"); - logsAfter[0].timestamp.should.exist; - }).nodeify(done); - }); - }); -}); diff --git a/test/functional/ios/testapp/basics/calc-app-1-specs.js b/test/functional/ios/testapp/basics/calc-app-1-specs.js new file mode 100644 index 00000000000..421485f8731 --- /dev/null +++ b/test/functional/ios/testapp/basics/calc-app-1-specs.js @@ -0,0 +1,145 @@ +"use strict"; + +var env = require('../../../../helpers/env') + , setup = require("../../../common/setup-base") + , desired = require('../desired') + , Q = require("q") + , _ = require("underscore") + , filterVisible = require('../../../../helpers/ios-uiautomation').filterVisible; + +describe('testapp - basics - calc app 1', function () { + var driver; + setup(this, desired).then(function (d) { driver = d; }); + + var values = null; + + var clearFields = function (driver) { + values = []; + return driver + .elementsByClassName('UIATextField').then(function (elems) { + var sequence = _(elems).map(function (elem) { + return function () { return elem.clear(); }; + }); + return sequence.reduce(Q.when, new Q()); // running sequence + }).then(function () { + return driver.elementByClassName('UIAButton').click(); + }); + }; + + var populate = function (type, driver) { + values = []; + return driver + .elementsByIosUIAutomation(filterVisible('.textFields();')) + .then(function (elems) { + var sequence = _(elems).map(function (elem) { + var val = Math.round(Math.random() * 10); + values.push(val); + if (type === "elem") { + return function () { return elem.sendKeys(val); }; + } else if (type === "elem-setvalue") { + return function () { return elem.setImmediateValue(val); }; + } else if (type === "driver") { + return function () { return elem.click().keys(val); }; + } + }); + return sequence.reduce(Q.when, new Q()); // running sequence + }); + }; + + var computeAndCheck = function (driver) { + return driver + .elementByClassName('UIAButton').click() + .elementByClassName('UIAStaticText').text().then(function (text) { + parseInt(text, 10).should.equal(values[0] + values[1]); + }); + }; + + if (env.FAST_TESTS) { + beforeEach(function (done) { + clearFields(driver).nodeify(done); + }); + } + + it('should fill two fields with numbers', function (done) { + populate("elem", driver) + .then(computeAndCheck.bind(null, driver)) + .nodeify(done); + }); + + // using sendKeysToActiveElement + it('should fill two fields with numbers - sendKeys', function (done) { + populate("driver", driver) + .then(computeAndCheck.bind(null, driver)) + .nodeify(done); + }); + + it('should fill two fields with numbers - setValue', function (done) { + populate("elem-setvalue", driver) + .then(computeAndCheck.bind(null, driver)) + .nodeify(done); + }); + + it('should confirm that button is displayed', function (done) { + driver + .elementByClassName('UIATextField').isDisplayed() + .should.eventually.be.ok + .nodeify(done); + }); + + it('should confirm that the disabled button is disabled', function (done) { + driver + .elementByName('DisabledButton').isEnabled() + .should.not.eventually.be.ok + .nodeify(done); + }); + + it('should confirm that the compute sum button is enabled', function (done) { + driver + .elementByName('ComputeSumButton').isEnabled() + .should.eventually.be.ok + .nodeify(done); + }); + + it('should interact with alert', function (done) { + driver.elementsByClassName('UIAButton').then(function (buttons) { + return buttons[1]; + }).then(function (button) { + return button + .click() + .acceptAlert() + .then(function () { return button.click(); }) + .alertText().then(function (text) { + text.should.include("Cool title"); + text.should.include("this alert is so cool."); + }).dismissAlert(); + }) + .nodeify(done); + }); + + + it('should find alert like other elements', function (done) { + driver.elementsByClassName('UIAButton').then(function (buttons) { + return buttons[1]; + }).then(function (button) { + return button.click() + .elementByClassName('UIAAlert') + // maybe we could get alert body text too? + .elementByClassName('>', 'UIAStaticText').text().should.become("Cool title") + .dismissAlert(); + }) + .nodeify(done); + }); + + it('should get tag names of elements', function (done) { + driver + .elementByClassName('UIAButton').getTagName().should.become("UIAButton") + .elementByClassName('UIAStaticText').getTagName().should.become("UIAStaticText") + .nodeify(done); + }); + + it('should be able to get text of a button', function (done) { + driver + .elementByClassName('UIAButton').text().should.become("ComputeSumButton") + .nodeify(done); + }); +}); diff --git a/test/functional/ios/testapp/basics/calc-app-2-specs.js b/test/functional/ios/testapp/basics/calc-app-2-specs.js new file mode 100644 index 00000000000..40f19f4c308 --- /dev/null +++ b/test/functional/ios/testapp/basics/calc-app-2-specs.js @@ -0,0 +1,84 @@ +"use strict"; + +var setup = require("../../../common/setup-base") + , desired = require('../desired') + , fs = require('fs') + , path = require('path'); + +describe('testapp - basics - calc app 2', function () { + var driver; + setup(this, desired).then(function (d) { driver = d; }); + + var sum = 0 + , lookup = function (textFieldNum) { + var num = Math.round(Math.random() * 10000); + sum += num; + return driver + .elementByName('TextField' + textFieldNum) + .sendKeys(num); + }; + + it('should lookup two fields by name and populate them with ' + + 'random numbers to finally sum them up', function (done) { + driver.elementByName('SumLabel').then(function (sumLabel) { + return driver.chain() + .then(lookup.bind(null, 1)) + .then(lookup.bind(null, 2)) + .elementByName('ComputeSumButton').click() + .then(function () { return sumLabel.text(); }) + .then(function (text) { parseInt(text, 10).should.equal(sum); }); + }).nodeify(done); + }); + + it('should receive correct error', function (done) { + driver + .execute("mobile: doesn't exist") + .then(function () {}, function (err) { + err.cause.value.message.should.equal("Not yet implemented. " + + "Please help us: http://appium.io/get-involved.html"); + throw err; + }).should.be.rejectedWith(/status: 13/) + .nodeify(done); + }); + + it('should be able to get syslog log type', function (done) { + driver.logTypes().then(function (logTypes) { + logTypes.should.include('syslog'); + logTypes.should.include('crashlog'); + logTypes.should.not.include('logcat'); + }).nodeify(done); + }); + + // TODO: Fails on sauce, investigate + it('should be able to get syslog logs @skip-ios6 @skip-ci', function (done) { + driver + .setImplicitWaitTimeout(4000) + .elementByName('SumLabelz') + .should.be.rejectedWith(/status: 7/) + .log('syslog').then(function (logs) { + logs.length.should.be.above(0); + logs[0].message.should.not.include("\n"); + logs[0].level.should.equal("ALL"); + logs[0].timestamp.should.exist; + }) + .nodeify(done); + }); + + it('should be able to get crashlog logs @skip-ci', function (done) { + var dir = path.resolve(process.env.HOME, "Library", "Logs", "DiagnosticReports"); + var msg = 'boom'; + var numBeforeLogs; + driver + .log('crashlog').then(function (logsBefore) { + numBeforeLogs = logsBefore.length; + fs.writeFileSync(dir + '/myApp_' + Date.parse(new Date()) + '_rocksauce.crash', msg); + }).log('crashlog').then(function (logsAfter) { + logsAfter.length.should.be.above(0); + logsAfter.length.should.not.equal(numBeforeLogs); + logsAfter[0].message.should.not.include("\n"); + logsAfter[0].message.should.equal(msg); + logsAfter[0].level.should.equal("ALL"); + logsAfter[0].timestamp.should.exist; + }).nodeify(done); + }); +}); diff --git a/test/functional/ios/uicatalog/background-app-specs.js b/test/functional/ios/uicatalog/background-app-specs.js new file mode 100644 index 00000000000..fcfaab574b8 --- /dev/null +++ b/test/functional/ios/uicatalog/background-app-specs.js @@ -0,0 +1,17 @@ +"use strict"; + +var setup = require("../../common/setup-base"), + desired = require('./desired'); + +describe('uicatalog - background app @skip-ios6', function () { + var driver; + setup(this, desired).then(function (d) { driver = d; }); + it("should background the app for 4 of seconds (+/- 6 secs)", function (done) { + var before = new Date().getTime() / 1000; + driver + .backgroundApp(4) + .then(function () { + ((new Date().getTime() / 1000) - before).should.be.below(11); + }).nodeify(done); + }); +}); diff --git a/test/functional/ios/uicatalog/device-specs.js b/test/functional/ios/uicatalog/device-specs.js deleted file mode 100644 index d6c8c2e110a..00000000000 --- a/test/functional/ios/uicatalog/device-specs.js +++ /dev/null @@ -1,36 +0,0 @@ -"use strict"; - -var env = require('../../../helpers/env'), - setup = require("../../common/setup-base"), - desired = require('./desired'); - -describe('uicatalog - device @skip-ios6', function () { - - describe('lock device', function () { - var driver; - setup(this, desired).then(function (d) { driver = d; }); - var allowance = env.IOS7 ? 5 : 2; - it("should lock the device for 4 of seconds (+/- " + allowance + " secs)", function (done) { - var before = new Date().getTime() / 1000; - driver - .lockDevice(4) - .then(function () { - var now = (new Date().getTime() / 1000); - (now - before).should.be.above(4); - (now - before).should.be.below(4 + allowance + 1); - }).nodeify(done); - }); - }); - describe('background app', function () { - var driver; - setup(this, desired).then(function (d) { driver = d; }); - it("should background the app for 4 of seconds (+/- 6 secs)", function (done) { - var before = new Date().getTime() / 1000; - driver - .backgroundApp(4) - .then(function () { - ((new Date().getTime() / 1000) - before).should.be.below(11); - }).nodeify(done); - }); - }); -}); diff --git a/test/functional/ios/uicatalog/gestures/flick-specs.js b/test/functional/ios/uicatalog/gestures/flick-specs.js index 0389df8791f..7e52aa22422 100644 --- a/test/functional/ios/uicatalog/gestures/flick-specs.js +++ b/test/functional/ios/uicatalog/gestures/flick-specs.js @@ -26,7 +26,6 @@ describe('uicatalog - gestures - flick @skip-ios7 @skip-ios6', function () { .then(function (location1) { return driver .flick(0, -100, false) - .sleep(300000) .elementByClassName('UIATableCell').getLocation() .then(function (location2) { location2.x.should.equal(location1.x); diff --git a/test/functional/ios/uicatalog/load-app/load-abs-path-app-specs.js b/test/functional/ios/uicatalog/load-app/load-abs-path-app-specs.js index c379b793557..0d73f57081c 100644 --- a/test/functional/ios/uicatalog/load-app/load-abs-path-app-specs.js +++ b/test/functional/ios/uicatalog/load-app/load-abs-path-app-specs.js @@ -6,19 +6,15 @@ var env = require('../../../../helpers/env') , path = require('path') , _ = require('underscore'); -describe('uicatalog - basic', function () { +describe('uicatalog - load app with absolute path @skip-ios6', function () { + var driver; + var appPath = path.resolve(env.SAUCE? '/Users/chef/appium' : process.cwd(), desired.app); + setup(this, _.defaults({'app': appPath}, desired)) + .then(function (d) { driver = d; }); - describe('load app with absolute path @skip-ios6', function () { - var driver; - var appPath = path.resolve(env.SAUCE? '/Users/chef/appium' : process.cwd(), desired.app); - setup(this, _.defaults({'app': appPath}, desired)) - .then(function (d) { driver = d; }); - - it('should load with absolute path', function (done) { - driver.elementByClassName('UIATableView') - .should.eventually.exist - .nodeify(done); - }); + it('should load with absolute path', function (done) { + driver.elementByClassName('UIATableView') + .should.eventually.exist + .nodeify(done); }); - }); diff --git a/test/functional/ios/uicatalog/load-app/load-abs-path-zipped-app-specs.js b/test/functional/ios/uicatalog/load-app/load-abs-path-zipped-app-specs.js index 0ef24a09ad4..e719a3b0771 100644 --- a/test/functional/ios/uicatalog/load-app/load-abs-path-zipped-app-specs.js +++ b/test/functional/ios/uicatalog/load-app/load-abs-path-zipped-app-specs.js @@ -4,19 +4,15 @@ var env = require('../../../../helpers/env') , setup = require("../../../common/setup-base") , path = require('path'); -describe('uicatalog - basic', function () { +describe('uicatalog - load zipped app @skip-ios6', function () { + var driver; + var appZip = path.resolve(env.SAUCE? '/Users/chef/appium' : process.cwd(), 'assets/UICatalog6.0.app.zip'); + setup(this, {app: appZip}) + .then(function (d) { driver = d; }); - describe('load zipped app @skip-ios6', function () { - var driver; - var appZip = path.resolve(env.SAUCE? '/Users/chef/appium' : process.cwd(), 'assets/UICatalog6.0.app.zip'); - setup(this, {app: appZip}) - .then(function (d) { driver = d; }); - - it('should load a zipped app via path', function (done) { - driver.elementByClassName('UIATableView') - .should.eventually.exist - .nodeify(done); - }); + it('should load a zipped app via path', function (done) { + driver.elementByClassName('UIATableView') + .should.eventually.exist + .nodeify(done); }); - }); diff --git a/test/functional/ios/uicatalog/load-app/load-rel-path-app-specs.js b/test/functional/ios/uicatalog/load-app/load-rel-path-app-specs.js index 2e9c75c191a..17fc50a25f2 100644 --- a/test/functional/ios/uicatalog/load-app/load-rel-path-app-specs.js +++ b/test/functional/ios/uicatalog/load-app/load-rel-path-app-specs.js @@ -5,18 +5,15 @@ var setup = require("../../../common/setup-base") , path = require('path') , _ = require('underscore'); -describe('uicatalog - basic', function () { +describe('uicatalog - load app with relative path @skip-ios6', function () { + var driver; + var appPath = path.relative(process.cwd(), desired.app); + setup(this, _.defaults({'app': appPath}, desired)) + .then(function (d) { driver = d; }); - describe('load app with relative path @skip-ios6', function () { - var driver; - var appPath = path.relative(process.cwd(), desired.app); - setup(this, _.defaults({'app': appPath}, desired)) - .then(function (d) { driver = d; }); - - it('should load with relative path', function (done) { - driver.elementByClassName('UIATableView') - .should.eventually.exist - .nodeify(done); - }); + it('should load with relative path', function (done) { + driver.elementByClassName('UIATableView') + .should.eventually.exist + .nodeify(done); }); }); diff --git a/test/functional/ios/uicatalog/load-app/load-rel-path-zipped-app-specs.js b/test/functional/ios/uicatalog/load-app/load-rel-path-zipped-app-specs.js index 2f798954ff2..bdd2ce58636 100644 --- a/test/functional/ios/uicatalog/load-app/load-rel-path-zipped-app-specs.js +++ b/test/functional/ios/uicatalog/load-app/load-rel-path-zipped-app-specs.js @@ -2,19 +2,15 @@ var setup = require("../../../common/setup-base"); -describe('uicatalog - basic', function () { +describe('uicatalog - load zipped app with relative path @skip-ios6', function () { + var driver; + var appZip = "assets/UICatalog6.0.app.zip"; + setup(this, {app: appZip}) + .then(function (d) { driver = d; }); - describe('load zipped app with relative path @skip-ios6', function () { - var driver; - var appZip = "assets/UICatalog6.0.app.zip"; - setup(this, {app: appZip}) - .then(function (d) { driver = d; }); - - it('should load a zipped app via path', function (done) { - driver.elementByClassName('UIATableView') - .should.eventually.exist - .nodeify(done); - }); + it('should load a zipped app via path', function (done) { + driver.elementByClassName('UIATableView') + .should.eventually.exist + .nodeify(done); }); - }); diff --git a/test/functional/ios/uicatalog/load-app/load-zipped-url-app-specs.js b/test/functional/ios/uicatalog/load-app/load-zipped-url-app-specs.js index 5f9e73b67e7..31d6fea3c83 100644 --- a/test/functional/ios/uicatalog/load-app/load-zipped-url-app-specs.js +++ b/test/functional/ios/uicatalog/load-app/load-zipped-url-app-specs.js @@ -2,20 +2,16 @@ var setup = require("../../../common/setup-base"); -describe('uicatalog - basic', function () { +describe('uicatalog - load zipped app via url @skip-ios6', function () { + var driver; + var appUrl = 'http://appium.s3.amazonaws.com/UICatalog6.0.app.zip'; + setup(this, {app: appUrl}) + .then(function (d) { driver = d; }); - describe('load zipped app via url @skip-ios6', function () { - var driver; - var appUrl = 'http://appium.s3.amazonaws.com/UICatalog6.0.app.zip'; - setup(this, {app: appUrl}) - .then(function (d) { driver = d; }); - - it('should load a zipped app via url', function (done) { - driver - .elementByClassName('UIATableView') - .should.eventually.exist - .nodeify(done); - }); + it('should load a zipped app via url', function (done) { + driver + .elementByClassName('UIATableView') + .should.eventually.exist + .nodeify(done); }); - }); diff --git a/test/functional/ios/uicatalog/lock-device-specs.js b/test/functional/ios/uicatalog/lock-device-specs.js new file mode 100644 index 00000000000..bebee8cb372 --- /dev/null +++ b/test/functional/ios/uicatalog/lock-device-specs.js @@ -0,0 +1,21 @@ +"use strict"; + +var env = require('../../../helpers/env'), + setup = require("../../common/setup-base"), + desired = require('./desired'); + +describe('uicatalog - lock device @skip-ios6', function () { + var driver; + setup(this, desired).then(function (d) { driver = d; }); + var allowance = env.IOS7 ? 5 : 2; + it("should lock the device for 4 of seconds (+/- " + allowance + " secs)", function (done) { + var before = new Date().getTime() / 1000; + driver + .lockDevice(4) + .then(function () { + var now = (new Date().getTime() / 1000); + (now - before).should.be.above(4); + (now - before).should.be.below(4 + allowance + 1); + }).nodeify(done); + }); +}); diff --git a/test/functional/ios/webview/basics-specs.js b/test/functional/ios/webview/basics-specs.js index aa70d54f026..f7d60a647dd 100644 --- a/test/functional/ios/webview/basics-specs.js +++ b/test/functional/ios/webview/basics-specs.js @@ -103,13 +103,13 @@ describe('webview - basics', function () { .contexts() .context("WEBVIEW_1") .get("http://google.com") - .sleep(500) + .sleep(3000) .title() .should.eventually.equal("Google") .context("NATIVE_APP") .context("WEBVIEW_1") .get("https://saucelabs.com/home") - .sleep(500) + .sleep(3000) .title() .should.eventually.equal("Sauce Labs: Selenium Testing, Mobile Testing, JS Unit Testing and More") .nodeify(done); diff --git a/test/helpers/env.js b/test/helpers/env.js index 821fe29e646..00cf7d7e9cf 100644 --- a/test/helpers/env.js +++ b/test/helpers/env.js @@ -103,16 +103,17 @@ switch (env.DEVICE) { , platformName: 'Android' , deviceName: 'Android Emulator' }; + if (env.SAUCE) env.CAPS.platformVersion = '4.3'; break; case 'selendroid': env.CAPS = { browserName: '' , platformName: 'Android' - , platformVersion: '4.1' , automationName: 'Selendroid' , deviceName: 'Android Emulator' , app: process.env.APP ? path.resolve(__dirname, "../../sample-code/apps/" + process.env.APP + "/bin/" + process.env.APP + "-debug.apk") : '' }; + if (env.SAUCE) env.CAPS.platformVersion = '4.1'; break; case 'firefox': env.CAPS = { diff --git a/test/helpers/session.js b/test/helpers/session.js index 44d6a5782f6..6c5ce755788 100644 --- a/test/helpers/session.js +++ b/test/helpers/session.js @@ -83,9 +83,6 @@ module.exports.initSession = function (desired, opts) { deferred.resolve(browser); var caps = _.defaults(desired, env.CAPS); - if (env.VERBOSE) console.log("caps -->", caps); - if (env.VERBOSE) console.log("opts -->", opts); - if (env.SAUCE) { if (env.TRAVIS_JOB_NUMBER) name = '[' + env.TRAVIS_JOB_NUMBER + '] ' + name; if (name) desired.name = name.replace(/@[^\s]*/,''); @@ -95,6 +92,9 @@ module.exports.initSession = function (desired, opts) { } } + if (env.VERBOSE) console.log("caps -->", caps); + if (env.VERBOSE) console.log("opts -->", opts); + function init(remainingAttempts) { if (env.VERBOSE) console.log("remainingAttempts -->", remainingAttempts); return browser @@ -115,12 +115,14 @@ module.exports.initSession = function (desired, opts) { if (env.MAX_RETRY) attempts = Math.min(env.MAX_RETRY, attempts); return browser.chain() .then(function () { - if (env.IOS && env.RESET_IOS && !opts['no-reset']) { + if (!env.SAUCE && env.IOS && env.RESET_IOS && !opts['no-reset']) { + // TODO this should not be necessary return iosReset(); } }).then(function () { // if android uninstall package first - if (desired.platformName === 'Android' && desired.appPackage) { + if (!env.SAUCE && desired.platformName === 'Android' && desired.appPackage) { + // TODO this should not be necessary return androidUninstall(desired.appPackage); } }).then(function () { return init(attempts); })