Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/ryanseddon/bunyip into gh…
Browse files Browse the repository at this point in the history
…-pages

* 'master' of https://github.com/ryanseddon/bunyip:
  Bumped version and added documentation
  Added config check for localbrowsers so you don't need one to run them
  Experimenting with launching locally installed browsers
  Added release history info
  Added config generator command, config flag - Added config command e.g. `bunyip config config.js` fixes #1 - Added -c flag to specify path to a config file, defaults to cwd - Re-architectured helpers.js to browserstack.js - Fixed undefined value when killing browserstack workers - Bumped version to 0.1.3
  Bumped required node version, fixes #7, and added .editorconfig file
  Bumped required node version and added .editorconfig file
  Yeti 0.2.6 release has breaking changes force 0.2.5
  Update lib/helpers.js: Remove duplication.
  Remove switch statement and simplify the platform helper keywords
  Fix markdown link in readme
  Added keywords to package.json
  Bunyip initial release version 0.1.0 - Broke lib into manageable chunks - Added grunt.js - Did very simple unit testing using cli-easy - Some detailed instructions in the readme
  • Loading branch information
ryanseddon committed Aug 20, 2012
2 parents 575b0a8 + d7b0ce9 commit cd07f5b
Showing 14 changed files with 837 additions and 0 deletions.
8 changes: 8 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
; editorconfig.org
root = true

; Unix-style newlines
[*]
end_of_line = LF
indent_style = space
indent_size = 4
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/node_modules/
/lib/config.js
.DS_Store
*.log
1 change: 1 addition & 0 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/node_modules/
22 changes: 22 additions & 0 deletions LICENSE-MIT
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
Copyright (c) 2012 Ryan Seddon

Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
114 changes: 114 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
# bunyip

Automate client-side unit testing in real browsers using the CLI

## Getting Started
Install the module with: `npm install -g bunyip`. This is a CLI tool so it needs to be globally installed.

### BrowserStack account
In order for bunyip to flex its real muscle I recommend you get a paid [BrowserStack account](http://www.browserstack.com/pricing) as all paid accounts have access to their API. Without the API you'll need to connect your own slave browsers to bunyip.

### localhost sharing service
If you wish to test on devices that are not part of your local network you'll be required to setup a tunneling service. I recommend [pagekite](https://pagekite.net/support/quickstart/) as it gives you a nice free chunk of data and allows you to specify a reusable subdomain. [Showoff.io](https://showoff.io/) is another good option.

### Setup the config.js file
If you don't wish to use BrowserStack or a localhost sharing service you can skip this step.

To generate a config file you can run this command:

`bunyip config path/to/config.js`

This will take you through a multistep process to setup your username, password, tunnel url and tunnel command.

So I if wanted to save a config file to my home directory I would do the following.

`bunyip config ~/config.js`

### Specify config file to use

If I want to specify a specific config file to use the `-c, --cdir` flag lets you do that.

`bunyip -f test/tests.html -c path/to/config.js`

If you do not specify a config file to use it will look in your current working directory for a file name `config.js` otherwise it will not require one in.

## Test suite adaptors

Behind the scenes bunyip uses a tool called Yeti unfortunately Yeti only works with YUI Test. However I have written some [adaptors](https://github.com/ryanseddon/yeti-adaptors) for QUnit and jasmine, go check out my other repo for examples on using them with your current test suites.

If you use another client-side testsuite please feel free to contribute it to my [yeti-adaptors](https://github.com/ryanseddon/yeti-adaptors) repo.

## Examples

```bash
bunyip -f index.html
```

The above command will launch a simple Yeti hub on port 9000 and use the `index.html` inside your current working directory.

```bash
bunyip -f index.html -p 1337
```

This will change the port that is used. The global config value will be updated for you so don't worry.

### Locally installed browsers

Using the `local` command you can now open your test suite in all locally installed browsers or specify a series of browsers

```bash
bunyip -f index.html local
```

This will open in all locally installed browsers with one assumption that phantomjs is installed in `/usr/bin/`.

```bash
bunyip -f index.html local -l "firefox|chrome|safari|phantomjs"
```

This will open the installed versions of Firefox, Chrome, Safari and Phantomjs.

The `local` command looks for the following browsers:

* Firefox, Firefox Nightly
* Chrome, Chrome Canary
* Opera, Opera Next
* Safari
* Phantomjs

### BrowserStack workers

```bash
bunyip -f index.html -b ios
```

Assuming you have a BrowserStack paid account and have setup a localhost sharing service the `-b ios` will send off a command to launch all iOS devices (3 iPhones and 3 iPads) on BrowserStack and once they're connected you can run your test suite.

```bash
bunyip -s
```

This will query the BrowserStack API for any device or browsers that are currently running on your account.

```bash
bunyip -k <id> or all
```

If you no longer need a specific worker or you wish to destroy all of them you can either specify a single worker id or `all` and it will destroy said worker(s).

```bash
bunyip -h
```

For more info specify the help flag to get more info about each command flag available.

## Contributing
In lieu of a formal styleguide, take care to maintain the existing coding style. Add unit tests for any new or changed functionality. Lint and test your code using [grunt](https://github.com/cowboy/grunt).

## Release History
* 20/08/2012 - v0.2.0 - Added a `local` command to run your test suite in locally installed browsers.
* 15/07/2012 - v0.1.3 - Added ability to generate config file and to specify location of config file to use.

## License
Copyright (c) 2012 Ryan Seddon
Licensed under the MIT license.
3 changes: 3 additions & 0 deletions cli.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/usr/bin/env node
var program = require('./lib/options');
require("./lib/bunyip").route(program);
39 changes: 39 additions & 0 deletions grunt.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
module.exports = function(grunt) {

// Project configuration.
grunt.initConfig({
pkg: '<json:package.json>',
test: {
files: ['test/**/*.js']
},
lint: {
files: ['grunt.js', 'lib/**/*.js']
},
watch: {
files: '<config:lint.files>',
tasks: 'default'
},
jshint: {
options: {
curly: true,
eqeqeq: true,
immed: true,
latedef: true,
newcap: true,
noarg: true,
sub: true,
undef: true,
boss: true,
eqnull: true,
node: true
},
globals: {
exports: true
}
}
});

// Default task.
grunt.registerTask('default', 'lint');

};
164 changes: 164 additions & 0 deletions lib/browserstack.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
var browserstack = require("browserstack"),
fs = require("fs");

/*
Code heavily influenced by jayarjo's fork
https://github.com/jayarjo/bunyip/blob/master/lib/browserstack.js
*/

function BrowserStack(config) {
var workers = [],
bs = config.browserstack,
tunnellink = config.tunnellink,
timeout = bs.timeout || 480, // 8 minutes
client = browserstack.createClient(bs),
platforms = /^(all|ios|win|mac|android|opera)$/,
osMap = {},

loadBrowsers = function(browsers) {
browsers.forEach(function(browser, idx){
client.createWorker(browser, function(err, worker){
if(err) {
console.log(err);
console.log("Whoops! BrowserStack failed to create a worker: %s", err);
} else {
console.log(" BrowserStack "+ (browser.browser || browser.device) + " " + browser.version +" worker launched: %s", worker.id);
workers.push(worker.id);
}
});
});
},
availableBrowsers = function(cb) {
client.getBrowsers(function(err, browsers){
if(!err) {
cb(browsers);
}

console.log(err);
});
},
killBrowser = function(id, cb) {
client.terminateWorker(id, function(err, data) {
if(cb) {
cb(err, data);
}
});
},
killBrowsers = function() {
client.getWorkers(function(err, workers) {
workers.forEach(function(worker, i) {
killBrowser(worker.id, function(err, data) {
console.log("Worker %s successfully killed, it ran for %ss", this, Math.round(data.time));
}.bind(worker.id));
});
});
},
getWorkers = function() {
client.getWorkers(function(err, workers) {
var running = [],
queued = [];

workers.forEach(function(worker, i) {
if(worker.status === "running") {
running.push((worker.browser || worker.device) + " - " + worker.version + " ("+worker.id+") ");
} else if(worker.status === "queue") {
queued.push((worker.browser || worker.device) + " - " + worker.version + " ("+worker.id+") ");
}
});

console.log("Running:\n\t" + running.join("\n\t"));
console.log("Queued:\n\t" + queued.join("\n\t"));
});
},
manageBrowsers = function (program) {
var testURL = "http://"+config.tunnellink,
timeout = bs.timeout,
browsers = [],
file = false;

if(program.browsers && !platforms.test(program.browsers)) {
try {
file = fs.readFileSync(program.browsers,'utf8');
} catch(e) {}

if(file) {
// You can pass in a JSON file specifying the browsers
browsers = JSON.parse(file);

browsers.forEach(function(browser,i) {
browser.url = testURL;
browser.timeout = timeout;
});
} else {
var opt = program.browsers.split('|'),
versions, os, platform, data;

opt.forEach(function(browser,i) {
data = browser.split("/");
platform = data[0].split(":");
browser = platform[0];
os = platform[1];
versions = data[1].split(',');

versions.forEach(function(ver, i) {
browsers.push({
browser: browser,
device: browser,
os: os,
version: ver,
url: testURL,
timeout: timeout
});
});
});
}

this.loadBrowsers(browsers);
} else if(platforms.test(program.browsers)) {

if(program.browsers === "all") {
this.availableBrowsers(function(list){
list.forEach(function(browser, i) {
browser.url = testURL;
browser.timeout = timeout;
});

this.loadBrowsers(list);
});
} else {
this.platformBrowsers(program.browsers);
}
}
},
platformBrowsers = function(os) {
osMap = {};

availableBrowsers(function(browsers) {
browsers.forEach(function(browser, i) {
if(!osMap[browser.os]) {
osMap[browser.os] = [];
}
osMap[browser.os].push({
browser: browser.browser || "",
device: browser.device || "",
version: browser.version,
os: browser.os,
url: "http://" + config.tunnellink,
timeout: timeout
});
});

loadBrowsers(osMap[os]);
});
};

this.loadBrowsers = loadBrowsers;
this.availableBrowsers = availableBrowsers;
this.killBrowser = killBrowser;
this.killBrowsers = killBrowsers;
this.getWorkers = getWorkers;
this.manageBrowsers = manageBrowsers;
this.platformBrowsers = platformBrowsers;
}

module.exports = BrowserStack;
Loading

0 comments on commit cd07f5b

Please sign in to comment.