Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Please consider adding support for TestingBot.com #36

Open
wants to merge 8 commits into from

2 participants

@testingbot

Hi,

It'd be great if you could add support for TestingBot.com's Selenium grid.
Please consider adding this commit to Soda. If the code is not ok, please let me know what I should change.

Thank you very much,
Jochen

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Dec 26, 2011
  1. update README

    Jochen authored
Commits on Mar 23, 2012
  1. merge a81a2de from learnboost

    Jochen authored
Commits on May 7, 2012
Commits on Oct 27, 2012
  1. http.request now used instead of http.createClient, that it was depre…

    Carlos García authored
    …cated since node 0.6.x
  2. fs.existsSync now is used instead of path.existsSync, that it was dep…

    Carlos García authored
    …recated since node 0.8.x
  3. @carlosypunto

    Update package.json

    carlosypunto authored
Commits on Dec 1, 2012
  1. @testingbot

    Merge pull request #3 from carlosypunto/master

    testingbot authored
    fs.existsSync now is used instead of path.existsSync, that it was deprecated since node 0.8.x
This page is out of date. Refresh to see the latest.
View
43 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:
-
- <script src="http://saucelabs.com/video-embed/<job-id>.js?username=<username>&access_key=<access-key>"/>
-
## 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': '<your username>'
- , 'access-key': '<your api 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).
View
89 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
View
6 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';
View
157 lib/soda/sauce.js
@@ -1,157 +0,0 @@
-/*!
- * Soda - Sauce
- * Copyright(c) 2010 LearnBoost <tj@learnboost.com>
- * 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 '<script src="http://saucelabs.com/video-embed/'
- + jobId + '.js?username='
- + username + '&access_key='
- + accessKey + '"/>';
-};
View
154 lib/soda/testingbot.js
@@ -0,0 +1,154 @@
+/*!
+ * Soda - TestingBot
+ * Copyright(c) 2011 TestingBot <info@testingbot.com>
+ * 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;
+};
View
14 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 <tj@learnboost.com>",
+ "name": "testingbot",
+ "description": "Selenium RC Node Adapter (with TestingBot support)",
+ "keywords": ["selenium", "testingbot", "testing", "test", "tests"],
+ "version": "0.2.6",
+ "author": "Jochen Delabie <info@testingbot.com>",
"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"
}
}
Something went wrong with that request. Please try again.