diff --git a/Readme.md b/Readme.md
index f3c04cf..202d9c9 100644
--- a/Readme.md
+++ b/Readme.md
@@ -1,7 +1,7 @@
# Soda
-Selenium Node Adapter. A light-weight Selenium RC client for [NodeJS](http://nodejs.org), with additional [Sauce Labs](http://saucelabs.com) integration for acceptance testing in the cloud.
+Selenium Node Adapter. A light-weight Selenium RC client for [NodeJS](http://nodejs.org), forked from [Soda](https://github.com/LearnBoost/soda) with support for [TestingBot](http://testingbot.com)
## Installation
@@ -62,12 +62,8 @@ When chaining successful commands may receive a callback, which is useful for cu
.getTitle(function(title){
assert.equal('Hello World', title);
})
- .end(function(err){
- browser.testComplete(function() {
- console.log('done');
- if(err) throw err;
- });
- })
+ .testComplete()
+ .end(function(err) { if (err) { console.log(err); } })
With the `.and()` method you can add additional commands to the queue. The callback accepts the client instance, which is also the value of "this".
@@ -101,21 +97,6 @@ With this helper function we can now re-use this logic in several places, an exp
});
});
-## Sauce Labs Videos & Logs
-
-When a job is complete, you can request the log or flv video from Sauce Labs. To access the url for these resources you may use `SauceClient#videoUrl` or `SauceClient#logUrl`, for example:
-
- ...
- .end(function(err){
- console.log(this.jobUrl)
- console.log(this.videoUrl)
- console.log(this.logUrl)
- })
-
-Sauce Labs also provides a script that you may embed in your CI server to display the video, accessible via `SauceClient#video`, which will yield something similar to:
-
-
-
## Selenium RC Example
var soda = require('soda')
@@ -145,15 +126,13 @@ Sauce Labs also provides a script that you may embed in your CI server to displa
});
-## Sauce Labs Example
+## TestingBot Example
var soda = require('soda')
, assert = require('assert');
- var browser = soda.createSauceClient({
+ var browser = soda.createTestingBotClient({
'url': 'http://sirrobertborden.ca.app.learnboost.com/'
- , 'username': ''
- , 'access-key': ''
, 'os': 'Linux'
, 'browser': 'firefox'
, 'browser-version': '3.'
@@ -177,14 +156,8 @@ Sauce Labs also provides a script that you may embed in your CI server to displa
.clickAndWait('//input[@value="Save"]')
.assertTextPresent('Account info updated')
.clickAndWait('link=Log out')
- .end(function(err){
- browser.setContext('sauce:job-info={"passed": ' + (err === null) + '}', function(){
- browser.testComplete(function(){
- console.log(browser.jobUrl);
- if (err) throw err;
- });
- });
- });
+ .testComplete()
+ .end(function(err) { if (err) { console.log(err); } })
## Creating Helpers
@@ -206,7 +179,7 @@ Keep in mind you can extend the prototype as needed for your test. An example of
## More Information
- - Sauce Labs [Supported Browsers](http://saucelabs.com/docs/ondemand/browsers/env/js/se1/mac)
+ - TestingBot [Supported Browsers](http://testingbot.com/support/getting-started/browsers.html)
- Introduction to [Selenese](http://seleniumhq.org/docs/02_selenium_basics.html)
- Selenium [Command Reference](http://release.seleniumhq.org/selenium-core/1.0.1/reference.html).
diff --git a/lib/soda/client.js b/lib/soda/client.js
index b892c23..ab46ff0 100755
--- a/lib/soda/client.js
+++ b/lib/soda/client.js
@@ -14,14 +14,14 @@ var http = require('http')
/**
* Initialize a `Client` with the given `options`.
- *
+ *
* Options:
- *
+ *
* - `host` Hostname defaulting to localhost
* - `port` Port number defaulting to 4444
* - `browser` Browser name
* - `url` URL string
- *
+ *
* @params {Object} options
* @api public
*/
@@ -83,51 +83,47 @@ Client.prototype.session = function(fn){
Client.prototype.command = function(cmd, args, fn){
this.emit('command', cmd, args);
- // HTTP client
- var client = http.createClient(this.port, this.host);
-
// Path construction
var path = this.commandPath(cmd, args);
+ var postData = path.replace('/selenium-server/driver/?', "");
+
+ // HTTP client request options
+ var options = {
+ host: this.host,
+ port: this.port,
+ method: "POST",
+ path: path,
+ headers: {
+ Host: this.host + ( this.port ? ':'+this.port : '' ),
+ 'Content-Length': postData.length,
+ 'Content-Type': 'application/x-www-form-urlencoded'
+ }
+ };
- var req;
-
- // Selenium RC can support POST request: http://svn.openqa.org/fisheye/changelog/selenium-rc/?cs=1898,
- // we need to switch to use POST if the URL's is too long (Below I use the Internet Explorer's limit).
- // See also: http://jira.openqa.org/browse/SRC-50
- if (path.length > 2048 && (this.host + path ).length > 2083) {
- postData = this.commandPath(cmd, args).replace('/selenium-server/driver/?', "");
- req = client.request('POST'
- , path
- , { Host: this.host + (this.port ? ':' + this.port : '')
- , 'Content-Length': postData.length
- , 'Content-Type': 'application/x-www-form-urlencoded'
- });
-
- req.write(postData);
- } else {
- req = client.request('GET'
- , path
- , { Host: this.host + (this.port ? ':' + this.port : '') });
- }
-
- req.on('response', function(res){
- res.body = '';
- res.setEncoding('utf8');
- res.on('data', function(chunk){ res.body += chunk; });
- res.on('end', function(){
- if (res.body.indexOf('ERROR') === 0 ||
- res.body.indexOf('Timed out after ') === 0) {
- var err = res.body.replace(/^ERROR: */, '');
- err = cmd + '(' + args.join(', ') + '): ' + err;
- fn(new Error(err), res.body, res);
- } else {
- if (res.body.indexOf('OK') === 0) {
- res.body = res.body.replace(/^OK,?/, '');
+ var req = http.request(options, function(res) {
+ res.body = '';
+ res.setEncoding('utf8');
+ res.on('data', function(chunk){ res.body += chunk; });
+ res.on('end', function(){
+ if (res.body.indexOf('ERROR') === 0 ||
+ res.body.indexOf('Timed out after ') === 0) {
+ var err = res.body.replace(/^ERROR: */, '');
+ err = cmd + '(' + args.join(', ') + '): ' + err;
+ fn(new Error(err), res.body, res);
+ } else {
+ if (res.body.indexOf('OK') === 0) {
+ res.body = res.body.replace(/^OK,?/, '');
+ }
+ fn(null, res.body, res);
}
- fn(null, res.body, res);
- }
- });
+ });
+ });
+
+ req.on('error', function(e) {
+ console.log('problem with request: ' + e.message);
});
+
+ req.write(postData);
req.end();
return this;
};
@@ -262,7 +258,7 @@ exports.createClient = function(options){
/**
* Command names.
- *
+ *
* @type Array
*/
@@ -362,7 +358,7 @@ exports.commands = [
/**
* Accessor names.
- *
+ *
* @type Array
*/
@@ -423,12 +419,13 @@ exports.accessors = [
, 'PromptPresent'
, 'SomethingSelected'
, 'TextPresent'
+ , 'TextNotPresent'
, 'Visible'
];
/**
* Generate commands via accessors.
- *
+ *
* All accessors get prefixed with:
*
* - get
diff --git a/lib/soda/index.js b/lib/soda/index.js
index ef7c789..3cd237c 100644
--- a/lib/soda/index.js
+++ b/lib/soda/index.js
@@ -15,8 +15,8 @@ exports = module.exports = require('./client');
* Export sauce client.
*/
-exports.SauceClient = require('./sauce');
-exports.createSauceClient = require('./sauce').createClient;
+exports.TestingBotClient = require('./testingbot');
+exports.createTestingBotClient = require('./testingbot').createClient;
/**
* Library version.
@@ -24,4 +24,4 @@ exports.createSauceClient = require('./sauce').createClient;
* @type String
*/
-exports.version = '0.2.4';
+exports.version = '0.2.5';
diff --git a/lib/soda/sauce.js b/lib/soda/sauce.js
deleted file mode 100644
index b96bd47..0000000
--- a/lib/soda/sauce.js
+++ /dev/null
@@ -1,157 +0,0 @@
-/*!
- * Soda - Sauce
- * Copyright(c) 2010 LearnBoost
- * MIT Licensed
- */
-
-/**
- * Module dependencies.
- */
-
-var Client = require('./client');
-
-/**
- * Initialize a `SauceClient` with the given `options`. A suite of environment
- * variables are also supported in place of the options described below.
- *
- * Options:
- *
- * - `username` Sauce Labs username
- * - `access-key` Account access key
- * - `os` Operating system ex "Linux"
- * - `browser` Browser name, ex "firefox"
- * - `browser-version` Browser version, ex "3.0.", "7."
- * - `max-duration` Maximum test duration in seconds, ex 300 (5 minutes)
- *
- * Environment Variables:
- *
- * - `SAUCE_HOST` Defaulting to "ondemand.saucelabs.com"
- * - `SAUCE_PORT` Defaulting to 80
- * - `SAUCE_OS`
- * - `SAUCE_BROWSER`
- * - `SAUCE_USERNAME`
- * - `SAUCE_ACCESS_KEY`
- * - `SAUCE_BROWSER_VERSION`
- *
- * @params {Object} options
- * @api public
- */
-
-var SauceClient = exports = module.exports = function SauceClient(options) {
- options = options || {};
- this.host = process.env.SAUCE_HOST || 'ondemand.saucelabs.com';
- this.port = process.env.SAUCE_PORT || 80;
-
- // Check sauce env variables, and provide defaults
- options.os = options.os || process.env.SAUCE_OS || 'Linux';
- options.url = options.url || process.env.SAUCE_BROWSER_URL;
- options.browser = options.browser || process.env.SAUCE_BROWSER || 'firefox';
- options.username = options.username || process.env.SAUCE_USERNAME;
- options['access-key'] = options['access-key'] || process.env.SAUCE_ACCESS_KEY;
-
- // Allow users to specify an empty browser-version
- options['browser-version'] = options['browser-version'] == undefined
- ? (process.env.SAUCE_BROWSER_VERSION || '')
- : (options['browser-version'] || '');
-
- this.url = options.url;
- this.username = options.username;
- this.accessKey = options['access-key'];
- this.options = options;
- this.browser = JSON.stringify(options);
-};
-
-/**
- * Interit from `Client`.
- */
-
-SauceClient.prototype.__proto__ = Client.prototype;
-
-/**
- * Return saucelabs job url.
- *
- * @return {String}
- * @api public
- */
-
-SauceClient.prototype.__defineGetter__('jobUrl', function(){
- return 'https://saucelabs.com/jobs/' + this.sid;
-});
-/**
- * Return saucelabs video flv url.
- *
- * @return {String}
- * @api public
- */
-
-SauceClient.prototype.__defineGetter__('videoUrl', function(){
- return exports.url(this.username, this.sid, 'video.flv');
-});
-
-/**
- * Return saucelabs log file url.
- *
- * @return {String}
- * @api public
- */
-
-SauceClient.prototype.__defineGetter__('logUrl', function(){
- return exports.url(this.username, this.sid, 'selenium-server.log');
-});
-
-/**
- * Return saucelabs video embed script.
- *
- * @return {String}
- * @api public
- */
-
-SauceClient.prototype.__defineGetter__('video', function(){
- return exports.video(this.username, this.accessKey, this.sid);
-});
-
-/**
- * Shortcut for `new soda.SauceClient()`.
- *
- * @param {Object} options
- * @return {Client}
- * @api public
- */
-
-exports.createClient = function(options){
- return new SauceClient(options);
-};
-
-/**
- * Return saucelabs url to `jobId`'s `filename`.
- *
- * @param {String} username
- * @param {String} jobId
- * @param {String} filename
- * @return {String}
- * @api public
- */
-
-exports.url = function(username, jobId, filename){
- return 'https://saucelabs.com/rest/'
- + username + '/jobs/'
- + jobId + '/results/'
- + filename;
-};
-
-/**
- * Return saucelabs video embed script.
- *
- * @param {String} username
- * @param {String} accessKey
- * @param {String} jobId
- * @return {String}
- * @api public
- */
-
-exports.video = function(username, accessKey, jobId){
- return '';
-};
diff --git a/lib/soda/testingbot.js b/lib/soda/testingbot.js
new file mode 100644
index 0000000..d7949ab
--- /dev/null
+++ b/lib/soda/testingbot.js
@@ -0,0 +1,154 @@
+/*!
+ * Soda - TestingBot
+ * Copyright(c) 2011 TestingBot
+ * MIT Licensed
+ */
+
+/**
+ * Module dependencies.
+ */
+var http = require('http')
+ , qs = require('querystring')
+ , EventEmitter = require('events').EventEmitter
+ , fs = require('fs');
+var Client = require('./client');
+
+/**
+ * Initialize a TestingBot client with the given `options`. A suite of environment
+ * variables are also supported in place of the options described below.
+ *
+ * Options:
+ *
+ * - `client_key` TestingBot client key
+ * - `client_secret` TestingBot client secret
+ * - `os` Operating system ex "Linux"
+ * - `browser` Browser name, ex "firefox"
+ * - `browser-version` Browser version, ex "3.0.", "7."
+ * - `max-duration` Maximum test duration in seconds, ex 300 (5 minutes)
+ *
+ * @params {Object} options
+ * @api public
+ */
+
+var TestingBotClient = exports = module.exports = function TestingBotClient(options) {
+ options = options || {};
+ this.host = 'hub.testingbot.com';
+ this.port = 4444;
+
+ options.os = options.os || 'Windows';
+ options['browser-version'] = options['browser-version'] == undefined ? '' : options['browser-version'];
+
+ this.options = options;
+ this.browser = options.browser || 'firefox';
+ this.url = options.url;
+
+ if (fs.existsSync(process.env.HOME + "/.testingbot")) {
+ var data = fs.readFileSync(process.env.HOME + "/.testingbot", "utf-8");
+ var parts = data.split(':');
+ this.client_key = parts[0];
+ this.client_secret = parts[1].replace("\n", "");
+ } else {
+ if (options.client_key && options.client_secret) {
+ this.client_key = options.client_key;
+ this.client_secret = options.client_secret;
+ } else {
+ console.log("*** Please specify your TestingBot client_key and client_secret in the options, or add the ~/.testingbot file");
+ }
+ }
+};
+
+/**
+ * Construct a `cmd` path with the given `args`.
+ *
+ * @param {String} name
+ * @param {Array} args
+ * @return {String}
+ * @api private
+ */
+
+TestingBotClient.prototype.commandPath = function(cmd, args){
+ var obj = { cmd: cmd, client_key: this.client_key, client_secret: this.client_secret };
+
+ // Arguments by nth
+ if (args) {
+ args.forEach(function(arg, i) {
+ obj[i+1] = arg;
+ });
+ }
+
+ if (cmd === 'getNewBrowserSession') {
+ obj[3] = '';
+ obj[4] = 'platform=' + this.options.os + ';version=' + this.options['browser-version'];
+ }
+ // Ignore session id for getNewBrowserSession
+ if (this.sid && cmd !== 'getNewBrowserSession') {
+ obj.sessionId = this.sid;
+ }
+ return '/selenium-server/driver/?' + qs.stringify(obj);
+};
+
+
+/**
+ * Interit from `Client`.
+ */
+
+TestingBotClient.prototype.__proto__ = Client.prototype;
+
+/**
+ * Return testingbot test url.
+ *
+ * @return {String}
+ * @api public
+ */
+
+TestingBotClient.prototype.__defineGetter__('testUrl', function(){
+ return 'https://testingbot.com/members/tests/' + this.sid;
+});
+
+/**
+ * Shortcut for `new soda.TestingBotClient()`.
+ *
+ * @param {Object} options
+ * @return {Client}
+ * @api public
+ */
+
+exports.createClient = function(options){
+ var obj = new TestingBotClient(options);
+ obj.end = function(fn) {
+ TestingBotClient.prototype.end.call(this, function(err) {
+ // original callback
+ fn();
+ // send results to TestingBot
+ var postData = qs.stringify({
+ client_key: obj.client_key,
+ client_secret: obj.client_secret,
+ session_id: obj.sid,
+ success: err === null,
+ kind: 10
+ });
+
+ // An object of options to indicate where to post to
+ var post_options = {
+ host: 'testingbot.com',
+ port: '80',
+ path: '/hq',
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/x-www-form-urlencoded',
+ 'Content-Length': postData.length
+ }
+ };
+
+ // Set up the request
+ var post_req = http.request(post_options, function(res) {
+ res.setEncoding('utf8');
+ });
+
+ // post the data
+ post_req.write(postData);
+ post_req.end();
+ });
+ };
+ return obj;
+};
diff --git a/package.json b/package.json
index 2b38d47..bd57b2b 100644
--- a/package.json
+++ b/package.json
@@ -1,13 +1,13 @@
{
- "name": "soda",
- "description": "Selenium RC Node Adapter (with Sauce Labs support)",
- "keywords": ["selenium", "saucelabs", "testing", "test", "tests"],
- "version": "0.2.5",
- "author": "TJ Holowaychuk ",
+ "name": "testingbot",
+ "description": "Selenium RC Node Adapter (with TestingBot support)",
+ "keywords": ["selenium", "testingbot", "testing", "test", "tests"],
+ "version": "0.2.6",
+ "author": "Jochen Delabie ",
"main": "./lib/soda/index.js",
- "engines": { "node": ">= 0.2.0" },
+ "engines": { "node": ">= 0.8.0" },
"repository": {
"type": "git",
- "url": "git://github.com/LearnBoost/soda.git"
+ "url": "git://github.com/testingbot/soda.git"
}
}