From 32286912db9a1b99156b73c00743ad8d66abbac3 Mon Sep 17 00:00:00 2001 From: Jochen Date: Sun, 6 Mar 2016 14:39:42 +0100 Subject: [PATCH] Add TestingBot DriverProvider which sends the key/secret via the config values testingbotKey and testingbotSecret. The test success state is sent to TestingBot via the TestingBot API. At the end of the test, a link to view the test is provided. --- docs/referenceConf.js | 14 +++-- docs/server-setup.md | 14 ++++- lib/driverProviders/testingbot.js | 90 +++++++++++++++++++++++++++++++ lib/runner.js | 2 + package.json | 3 +- 5 files changed, 117 insertions(+), 6 deletions(-) create mode 100644 lib/driverProviders/testingbot.js diff --git a/docs/referenceConf.js b/docs/referenceConf.js index 2ba5b6282..0c5e81ace 100644 --- a/docs/referenceConf.js +++ b/docs/referenceConf.js @@ -21,7 +21,8 @@ exports.config = { // running. // 3. sauceUser/sauceKey - to use remote Selenium Servers via Sauce Labs. // 4. browserstackUser/browserstackKey - to use remote Selenium Servers via BrowserStack. - // 5. directConnect - to connect directly to the browser Drivers. + // 5. testingbotKey/testingbotSecret - to use remote Selenium Servers via TestingBot. + // 6. directConnect - to connect directly to the browser Drivers. // This option is only available for Firefox and Chrome. // ---- 1. To start a standalone Selenium Server locally --------------------- @@ -84,7 +85,13 @@ exports.config = { browserstackUser: null, browserstackKey: null, - // ---- 5. To connect directly to Drivers ------------------------------------ + // ---- 5. To use remote browsers via TestingBot --------------------------- + // If testingbotKey and testingbotSecret are specified, seleniumServerJar will be ignored. + // The tests will be run remotely using TestingBot. + testingbotKey: null, + testingbotSecret: null, + + // ---- 6. To connect directly to Drivers ------------------------------------ // Boolean. If true, Protractor will connect directly to the browser Drivers // at the locations specified by chromeDriver and firefoxPath. Only Chrome // and Firefox are supported for direct connect. @@ -134,7 +141,7 @@ exports.config = { // Name of the process executing this capability. Not used directly by // protractor or the browser, but instead pass directly to third parties - // like BrowserStack and SauceLabs as the name of the job running this test + // like BrowserStack, SauceLabs and TestingBot as the name of the job running this test name: 'Unnamed Job', // User defined name for the capability that will display in the results log @@ -168,6 +175,7 @@ exports.config = { // specified here. // For a list of BrowserStack specific capabilities, visit // https://www.browserstack.com/automate/capabilities + // or for TestingBot: https://testingbot.com/support/other/test-options }, // If you would like to run more than one instance of WebDriver on the same diff --git a/docs/server-setup.md b/docs/server-setup.md index ffac30823..a42eba3a8 100644 --- a/docs/server-setup.md +++ b/docs/server-setup.md @@ -55,13 +55,13 @@ To connect to a running instance of a standalone Selenium Server, set this optio - `seleniumAddress` - Connect to a running instance of a standalone Selenium Server. The address will be a URL. -Please note that if you set seleniumAddress, the settings for `seleniumServerJar`, `seleniumPort`, `seleniumArgs`, `browserstackUser`, `browserstackKey`, `sauceUser` and `sauceKey` will be ignored. +Please note that if you set seleniumAddress, the settings for `seleniumServerJar`, `seleniumPort`, `seleniumArgs`, `browserstackUser`, `browserstackKey`, `testingbotKey`, `testingbotSecret`, `sauceUser` and `sauceKey` will be ignored. Remote Selenium Server ---------------------- -To run your tests against a remote Selenium Server, you will need an account with a service that hosts the server (and the browser drivers). Protractor has built in support for [BrowserStack](https://www.browserstack.com) and [Sauce Labs](http://www.saucelabs.com). +To run your tests against a remote Selenium Server, you will need an account with a service that hosts the server (and the browser drivers). Protractor has built in support for [BrowserStack](https://www.browserstack.com), [TestingBot](https://testingbot.com) and [Sauce Labs](http://www.saucelabs.com). **Using BrowserStack as remote Selenium Server** @@ -83,6 +83,16 @@ Please note that if you set `sauceUser` and `sauceKey`, the settings for `seleni You can optionally set the [`name` property](referenceConf.js#L121) in a capability in order to give the jobs a name on the server. Otherwise they will just be called `Unnamed Job`. +**Using TestingBot as remote Selenium Server** + +In your config file, set these options: + - `testingbotKey` - The key for your TestingBot account. + - `testingbotSecret` - The secret for your TestingBot account. + +Please note that if you set `testingbotKey` and `testingbotSecret`, the settings for `seleniumServerJar`, `seleniumPort`, `seleniumArgs`, `browserstackUser` and `browserstackKey`, `sauceUser` and `sauceKey` will be ignored. + +You can optionally set the [`name` property](referenceConf.js#L121) in a capability in order to give the jobs a name on the server. Otherwise they will just be allotted a random hash. + Connecting Directly to Browser Drivers -------------------------------------- diff --git a/lib/driverProviders/testingbot.js b/lib/driverProviders/testingbot.js new file mode 100644 index 000000000..5744c57d8 --- /dev/null +++ b/lib/driverProviders/testingbot.js @@ -0,0 +1,90 @@ +/* + * This is an implementation of the TestingBot Driver Provider. + * It is responsible for setting up the account object, tearing + * it down, and setting up the driver correctly. + */ + +var util = require('util'), + log = require('../logger'), + TestingBot = require('testingbot-api'), + crypto = require('crypto'), + q = require('q'), + DriverProvider = require('./driverProvider'); + + +var TestingBotDriverProvider = function(config) { + DriverProvider.call(this, config); + this.tbServer_ = {}; +}; +util.inherits(TestingBotDriverProvider, DriverProvider); + + +/** + * Hook to update the TestingBot job. + * @public + * @param {Object} update + * @return {q.promise} A promise that will resolve when the update is complete. + */ +TestingBotDriverProvider.prototype.updateJob = function(update) { + var self = this; + var deferredArray = this.drivers_.map(function(driver) { + var deferred = q.defer(); + driver.getSession().then(function(session) { + var hash = crypto.createHash('md5'); + var auth = hash.update(self.config_.testingbotKey + ':' + + self.config_.testingbotSecret + ':' + session.getId()).digest('hex'); + log.puts('TestingBot results available at http://testingbot.com/tests/' + + session.getId() + '?auth=' + auth); + var apiSend = { + 'test[success]' : update.passed ? 1 : 0 + }; + self.tbServer_.updateTest(apiSend, session.getId(), function(err) { + if (err) { + throw new Error( + 'Error updating TestingBot pass/fail status: ' + util.inspect(err) + ); + } + deferred.resolve(); + }); + }); + return deferred.promise; + }); + return q.all(deferredArray); +}; + +/** + * Configure and launch (if applicable) the object's environment. + * @public + * @return {q.promise} A promise which will resolve when the environment is + * ready to test. + */ +TestingBotDriverProvider.prototype.setupEnv = function() { + var deferred = q.defer(); + this.tbServer_ = new TestingBot({ + api_key: this.config_.testingbotKey, + api_secret: this.config_.testingbotSecret + }); + this.config_.capabilities.username = this.config_.testingbotKey; + this.config_.capabilities.accessKey = this.config_.testingbotSecret; + var auth = 'http://' + this.config_.testingbotKey + ':' + + this.config_.testingbotSecret + '@'; + this.config_.seleniumAddress = auth + + 'hub.testingbot.com:80/wd/hub'; + + // Append filename to capabilities.name so that it's easier to identify tests. + if (this.config_.capabilities.name && + this.config_.capabilities.shardTestFiles) { + this.config_.capabilities.name += ( + ':' + this.config_.specs.toString().replace(/^.*[\\\/]/, '')); + } + + log.puts('Using TestingBot selenium server at ' + + this.config_.seleniumAddress.replace(/\/\/.+@/, '//')); + deferred.resolve(); + return deferred.promise; +}; + +// new instance w/ each include +module.exports = function(config) { + return new TestingBotDriverProvider(config); +}; diff --git a/lib/runner.js b/lib/runner.js index 4cc8eb89d..e54d0d3df 100644 --- a/lib/runner.js +++ b/lib/runner.js @@ -102,6 +102,8 @@ Runner.prototype.loadDriverProvider_ = function() { runnerPath = './driverProviders/browserstack'; } else if (this.config_.sauceUser && this.config_.sauceKey) { runnerPath = './driverProviders/sauce'; + } else if (this.config_.testingbotKey && this.config_.testingbotSecret) { + runnerPath = './driverProviders/testingbot'; } else if (this.config_.seleniumServerJar) { runnerPath = './driverProviders/local'; } else if (this.config_.mockSelenium) { diff --git a/package.json b/package.json index 4febd7a36..0effec977 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,8 @@ "request": "~2.67.0", "saucelabs": "~1.0.1", "selenium-webdriver": "2.48.2", - "source-map-support": "~0.4.0" + "source-map-support": "~0.4.0", + "testingbot-api": "~1.0.1" }, "devDependencies": { "body-parser": "1.14.2",