Skip to content

Commit

Permalink
Merge pull request #25 from institutional/release/v0.9.0
Browse files Browse the repository at this point in the history
API Usage & More Unit Tests
  • Loading branch information
scott2449 committed Nov 5, 2014
2 parents b74bde0 + ee17643 commit 60dff38
Show file tree
Hide file tree
Showing 9 changed files with 402 additions and 133 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.*
!.gitignore
node_modules
coverage
18 changes: 17 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[Fiveby](http://en.wikipedia.org/wiki/Five_by_five)
[Fiveby](http://en.wikipedia.org/wiki/Five_by_five) [![Build Status](http://djin-jenkins01.dowjones.net/job/fiveby/badge/icon)](http://djin-jenkins01.dowjones.net/job/fiveby/)
========

makes it easier to write automated tests/suites. Here's the idea: don't worry about selenium (or it's config), don't worry about selenium JS api oddities, don't worry about mocha, just use fiveby:
Expand All @@ -19,3 +19,19 @@ new fiveby(function (browser) { //browser is driver if you are looking at seleni
});
```
See [docs](https://github.dowjones.net/institutional/fiveby/docs) for more details and use [gulp-fiveby](https://github.dowjones.net/institutional/gulp-fiveby) as a scaffold project. [Live Help](https://dowjones.slack.com/messages/fiveby/)

###Configuration - fiveby-config.json

```json
{
"implicitWait": 5000,
"hubUrl": null,
"browsers": {
"chrome": 1
},
"disableBrowsers": false
}
```
disableBrowsers is optional, defaults to false

hubUrl is optional, if not provided (and disableBrowsers = false) it will spin up a selenium server *requires java*
14 changes: 14 additions & 0 deletions gulpfile.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
var gulp = require('gulp');
var istanbul = require('gulp-istanbul');
var mocha = require('gulp-mocha');

gulp.task('test', function (cb) {
gulp.src(['lib/fiveby.js', 'index.js'])
.pipe(istanbul())
.on('finish', function () {
gulp.src(['test/*.js'])
.pipe(mocha())
.pipe(istanbul.writeReports())
.on('end', cb);
});
});
130 changes: 18 additions & 112 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,142 +1,48 @@
var webdriver = require('selenium-webdriver');
var Hook = require('mocha').Hook;
var fiveby = require('./lib/fiveby');
var path = require('path');
var fs = require('fs');
var _ = require('lodash');
var tb = require('traceback');
var Properties = require('./lib/properties');
require('should');

module.exports = fiveby;

//simplify webdriver usage
global.by = webdriver.By;
global.key = webdriver.Key;
global.promise = webdriver.promise;
global.bot = webdriver.error;

//get project configuration if one exists
if (!global.fivebyConfig) {

if (process.env.fivebyopts) {
global.fivebyConfig = JSON.parse(process.env.fivebyopts);
} else {
var configPath = path.resolve('fiveby-config.json');
try {
try {
if (process.env.fivebyopts) {
global.fivebyConfig = JSON.parse(process.env.fivebyopts);
} else {
var configPath = path.resolve('fiveby-config.json');
global.fivebyConfig = JSON.parse(fs.readFileSync(configPath, {encoding: 'utf-8'}));
} catch (e) {
console.error('No global config loaded %s', e);
process.exit(1);
}
} catch (e) {
console.error('No global config loaded %s', e);
return process.exit(1);
}

//prep properties
global.propertyService = new Properties(global.fivebyConfig.environment||'local');
var props = global.propertyService.getProperties('default');
props.setMany(global.fivebyConfig.properties||{});

global.testPromise = webdriver.promise.fulfilled();
console.info('Configuration complete');

}

//spin up local selenium server if none provided
if (!global.fivebyConfig.hubUrl) {
console.info("No server defined, spinning one up ...");
SeleniumServer = require('selenium-webdriver/remote').SeleniumServer;
var server = new SeleniumServer('./node_modules/fiveby/selenium-server-standalone-2.42.2.jar', { port: 4444 });
server.start();
global.fivebyConfig.hubUrl = server.address();
}

//main test driver
function fiveby(params, test) {
module.exports = function (params, test) {

var file = tb()[1].path;
var config = _.cloneDeep(global.fivebyConfig); //seperate config for merge

if (arguments.length === 1) {//switch params for 1 arg
test = params;
} else {
_.merge(global.fivebyConfig, params); //merge test params with global
_.merge(config, params); //merge test params with config
}

//ensure minimal configuration is provided
if (!global.fivebyConfig.browsers) {
return console.warn('No browsers provided, must provide at least one');
}

//for each browser in the configuration
Object.keys(global.fivebyConfig.browsers).forEach(function (elem) {

//check if specific browser is valid in selenium
if (!webdriver.Capabilities[elem]) {
return console.warn('No such browser: %s', elem);
}

var lastPromise = global.testPromise;
var testComplete = webdriver.promise.defer();
global.testPromise = testComplete.promise;

//create a control flow and driver per test file
lastPromise.then(function() {

// set options for current browser
var capabilities = webdriver.Capabilities[elem]();

if (elem === 'chrome') {
capabilities.set('chromeOptions', {
args: ['--disable-extensions']
});
}

//build driver
var driver = new webdriver.Builder()
.usingServer(global.fivebyConfig.hubUrl)
.withCapabilities(capabilities)
.build();
driver.name = elem;
driver.manage().timeouts().implicitlyWait(global.fivebyConfig.implicitWait);

//register tests with mocha
var describe = test(driver);

//register hooks with mocha
registerHook('fiveby error handling', describe, "beforeEach", function () {
this.currentTest.parent.file = this.currentTest.file = file;
webdriver.promise.controlFlow().on('uncaughtException', function (e) {
if(this.currentTest) {
this.currentTest.callback(e);
} else {
console.error("Failed in setup or teardown, test result may not be valid for this file");
throw(e);
}
});
});

registerHook('fiveby cleanup', describe, "afterAll", function () {
testComplete.fulfill();
if (driver.session_) { //in case the tests already killed the session
return driver.quit();
}
});

});
});
}

function registerHook(name, suite, hookarr, func) {
var hook = new Hook(name, func);
hook.parent = suite;
if (suite && suite.ctx) {
hook.ctx = suite.ctx;
} else {
console.error("Please return test suite (describe) in the fiveby constructor callback.");
process.exit(2);
}
hook.timeout(5000);
if(hookarr.indexOf("before") > -1){
suite["_" + hookarr].unshift(hook);
if(global.fivebyConfig.disableBrowsers){
test();
} else {
suite["_" + hookarr].push(hook);
var fb = new fiveby(config);
fb.runSuiteInBrowsers(test);
}
}

};
117 changes: 117 additions & 0 deletions lib/fiveby.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
var webdriver = require('selenium-webdriver');
var Hook = require('mocha').Hook;
var tb = require('traceback');

//simplify webdriver usage
global.by = webdriver.By;
global.key = webdriver.Key;
global.promise = webdriver.promise;
global.bot = webdriver.error;

if(!global.testPromise){
global.testPromise = webdriver.promise.fulfilled();
}

module.exports = fiveby;

function fiveby(config) {
this.config = config;
//spin up local selenium server if none provided
if (!global.fivebyConfig.hubUrl && !config.hubUrl) {
console.info("No server defined, spinning one up ...");
SeleniumServer = require('selenium-webdriver/remote').SeleniumServer;
var server = new SeleniumServer('./node_modules/fiveby/selenium-server-standalone-2.44.0.jar', { port: 4444 });
server.start();
global.fivebyConfig.hubUrl = config.hubUrl = server.address();
}
}

fiveby.prototype.runSuiteInBrowsers = function (test) {

if(test.length === 0){ //bail if they don't want a browser
return test();
}

var self = this;
//ensure minimal configuration is provided
if (!this.config.browsers) {
return console.warn('No browsers provided, must provide at least one');
}

var file = tb()[2].path;

//for each browser in the configuration
Object.keys(this.config.browsers).forEach(function (elem) {

//check if specific browser is valid in selenium
if (!webdriver.Capabilities[elem]) {
return console.warn('No such browser: %s', elem);
}

var lastPromise = global.testPromise;
var testComplete = webdriver.promise.defer();
global.testPromise = testComplete.promise;

//create a control flow and driver per test file
lastPromise.then(function() {

// set options for current browser
var capabilities = webdriver.Capabilities[elem]();

if (elem === 'chrome') {
capabilities.set('chromeOptions', {
args: ['--disable-extensions']
});
}

//build driver
var driver = new webdriver.Builder()
.usingServer(self.config.hubUrl)
.withCapabilities(capabilities)
.build();
driver.name = elem;
driver.manage().timeouts().implicitlyWait(self.config.implicitWait);

//register tests with mocha
var describe = test(driver);

//register hooks with mocha
self.registerHook('fiveby error handling', describe, "beforeEach", function () {
this.currentTest.parent.file = this.currentTest.file = file;
webdriver.promise.controlFlow().on('uncaughtException', function (e) {
if(this.currentTest) {
this.currentTest.callback(e);
} else {
console.error("Failed in setup or teardown, test result may not be valid for this file");
throw(e);
}
});
});

self.registerHook('fiveby cleanup', describe, "afterAll", function () {
testComplete.fulfill();
if (driver.session_) { //in case the tests already killed the session
return driver.quit();
}
});

});
});
};

fiveby.prototype.registerHook = function (name, suite, hookarr, func) {
var hook = new Hook(name, func);
hook.parent = suite;
if (suite && suite.ctx) {
hook.ctx = suite.ctx;
} else {
console.error("Please return test suite (describe) in the fiveby constructor callback.");
return process.exit(2);
}
hook.timeout(5000);
if(hookarr.indexOf("before") > -1){
suite["_" + hookarr].unshift(hook);
} else {
suite["_" + hookarr].push(hook);
}
};
20 changes: 13 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,21 +1,27 @@
{
"name": "fiveby",
"version": "0.8.0",
"version": "0.9.0",
"description": "Package up testing options, levels, apis, and dependencies into one simple lib",
"scripts": {
"test": "mocha"
"test": "gulp test"
},
"author": "Scott Rahner",
"repository": {
"type": "git",
"url": "https://github.dowjones.net/institutional/fiveby.git"
},
"dependencies": {
"selenium-webdriver": "2.42.0",
"should": "^4.0.4",
"mocha": "^1.21.4",
"lodash": "^2.4.1",
"traceback": "^0.3.1",
"tesla.lib.cache": "^0.1.1"
"mocha": "^1.21.4",
"selenium-webdriver": "2.44.0",
"tesla.lib.cache": "^0.1.1",
"traceback": "^0.3.1"
},
"devDependencies": {
"gulp": "^3.8.9",
"gulp-istanbul": "^0.3.1",
"gulp-mocha": "^1.1.1",
"should": "^4.0.4",
"proxyquire": "^1.0.1"
}
}
Loading

0 comments on commit 60dff38

Please sign in to comment.