grunt-saucelabs
Advanced tools
Comparing version 4.0.3 to 4.0.4
@@ -50,2 +50,30 @@ module.exports = function(grunt) { | ||
'saucelabs-yui': { | ||
all: { | ||
//username: '', | ||
//key: '', | ||
options: { | ||
urls: ['http://127.0.0.1:9999/yui/index.html'], | ||
tunnelTimeout: 5, | ||
build: process.env.TRAVIS_JOB_ID, | ||
concurrency: 3, | ||
browsers: browsers, | ||
testname: "yui tests" | ||
} | ||
} | ||
}, | ||
'saucelabs-mocha': { | ||
all: { | ||
//username: '', | ||
//key: '', | ||
options: { | ||
urls: ['http://127.0.0.1:9999/mocha/test/browser/opts.html'], | ||
tunnelTimeout: 5, | ||
build: process.env.TRAVIS_JOB_ID, | ||
concurrency: 3, | ||
browsers: browsers, | ||
testname: "mocha tests" | ||
} | ||
} | ||
}, | ||
'saucelabs-qunit': { | ||
@@ -115,4 +143,4 @@ all: { | ||
grunt.registerTask('test', ['connect', 'saucelabs-qunit', 'saucelabs-jasmine']); | ||
grunt.registerTask('test', ['connect', 'saucelabs-qunit', 'saucelabs-jasmine', 'saucelabs-yui', 'saucelabs-mocha']); | ||
grunt.registerTask('default', ['jshint', 'test', 'publish']); | ||
}; |
{ | ||
"name": "grunt-saucelabs", | ||
"description": "Grunt task running tests using Sauce Labs. Supports Qunit and Jasmine tests", | ||
"version": "4.0.3", | ||
"version": "4.0.4", | ||
"homepage": "https://github.com/axemclion/grunt-saucelabs", | ||
@@ -6,0 +6,0 @@ "author": { |
@@ -7,6 +7,10 @@ grunt-saucelabs | ||
A Grunt task for running qunit and jasmine tests using Sauce Labs' Cloudified Browsers. | ||
[![Selenium Test Status](https://saucelabs.com/browser-matrix/grunt-sauce.svg)](https://saucelabs.com/u/grunt-sauce) | ||
A Grunt task for running QUnit, Jasmine, Mocha and YUI tests using Sauce Labs' Cloudified Browsers. | ||
[Grunt](http://gruntjs.com/) is a task-based command line build tool for JavaScript projects, based on nodejs. | ||
[QUnit](http://qunitjs.com/) is a powerful, easy-to-use JavaScript unit test suite used by the jQuery, jQuery UI and jQuery Mobile projects and is capable of testing any generic JavaScript code, including itself! | ||
[Mocha](http://visionmedia.github.io/mocha/) is a JavaScript test framework for running serial asynchronous tests. | ||
[YUI Test](http://developer.yahoo.com/yui/yuitest/) is a browser-based testing framework from Yahoo!. | ||
[Sauce Labs](https://saucelabs.com/) offers browser environments on the cloud for testing code. | ||
@@ -19,3 +23,3 @@ | ||
The task also uses [Sauce Connect](https://saucelabs.com/docs/connect) to establish a tunnel between Sauce Labs browsers and the machine running Grunt to load local pages. This is typically useful for testing pages on localhost that are not publically accessible on the internet. | ||
The `saucelabs-jasmine` runs jasmine tests in the Sauce Labs browser. The `saucelabs-jasmine` task requires `jasmine-1.3.0`. | ||
The `saucelabs-jasmine` runs [Jasmine](http://pivotal.github.io/jasmine/) tests in the Sauce Labs browser. The `saucelabs-jasmine` task requires `jasmine-1.3.0`. There are also `saucelabs-mocha` and `saucelabs-yui` tasks that let you run your Mocha and YUI tests on Sauce Labs cloudified browser environment. | ||
@@ -73,3 +77,3 @@ Usage | ||
The configuration of `saucelabs-jasmine` are exactly the same. | ||
The configuration of `saucelabs-jasmine`, `saucelabs-mocha`, `saucelabs-yui` are exactly the same. | ||
Note the options object inside a grunt target. This was introduced in grunt-saucelabs-* version 4.0.0 to be compatiable with grunt@0.4.0 | ||
@@ -95,2 +99,19 @@ | ||
Test results details with Jasmine | ||
--------------------------------- | ||
You can make Job Details pages more infromative on Sauce by providing more data with each test. You will get info about each test run inside your suite directly on Sauce pages. | ||
[![Jasmine detailed results](https://saucelabs.com/images/front-tests/jasmine.png)](https://saucelabs.com/docs/javascript-unit-tests-integration) | ||
You can do that by using [Jasmine JS Reporter](https://github.com/detro/jasmine-jsreporter) that will let `saucelabs-jasmine` task provide in-depth data about each test as a JSON object. | ||
All you need to do is to include the new jasmine-jsreporter reporter to the page running Jasmine tests by adding new script in header: | ||
```html | ||
<script src="path/to/jasmine-jsreporter.js" type="text/javascript"></script> | ||
``` | ||
and telling Jasmine to use it: | ||
```javascript | ||
jasmineEnv.addReporter(new jasmine.JSReporter()); | ||
```` | ||
Examples | ||
@@ -97,0 +118,0 @@ -------- |
@@ -417,2 +417,34 @@ module.exports = function(grunt) { | ||
TestRunner.prototype.yuiSaucify = function(results) { | ||
var out = {'custom-data': {}}; | ||
_.each(results, function (result, i) { | ||
if ( result !== null) { | ||
var keyName = i === 0 ? 'yui' : 'yui' + i; | ||
out['custom-data'][keyName] = { | ||
failed: result.failed, | ||
passed: result.passed, | ||
total: result.total, | ||
runtime: result.duration | ||
}; | ||
} | ||
}); | ||
return out; | ||
}; | ||
TestRunner.prototype.mochaSaucify = function(results) { | ||
var out = {'custom-data': {}}; | ||
_.each(results, function (result, i) { | ||
if ( result !== null) { | ||
var keyName = i === 0 ? 'mocha' : 'mocha' + i; | ||
out['custom-data'][keyName] = { | ||
failed: result[2], | ||
passed: result[1], | ||
total: result[4], | ||
runtime: +result[3] * 1000 | ||
}; | ||
} | ||
}); | ||
return out; | ||
}; | ||
TestRunner.prototype.qunitRunner = function(driver, cfg, testTimeout, testInterval, testReadyTimeout, detailedError, callback) { | ||
@@ -493,2 +525,141 @@ var testResult = "qunit-testresult"; | ||
TestRunner.prototype.yuiRunner = function(driver, cfg, testTimeout, testInterval, testReadyTimeout, detailedError, callback) { | ||
grunt.verbose.writeln("[%s] Starting YUI tests for page", cfg.prefix); | ||
driver.waitForConditionInBrowser("YUI.YUITest.Runner.getResults() !=== null", testReadyTimeout, function() { | ||
grunt.verbose.writeln("[%s] Test results ready, fetching", cfg.prefix); | ||
driver.safeEval("YUI.YUITest.Runner.getResults()", function(err, json) { | ||
if (err) { | ||
grunt.log.error("[%s] Could not read test result for %s", cfg.prefix, err, driver.page); | ||
grunt.log.error("[%s] More details at http://saucelabs.com/tests/%s", cfg.prefix, driver.page); | ||
callback(false); | ||
return; | ||
} | ||
grunt.verbose.writeln("[%s] Fetched test results", cfg.prefix); | ||
var showDetailedError = function(cb) { | ||
var outputFailures = function(obj) { | ||
_.forOwn(obj, function (val, key, iobj) { | ||
if (_.isObject(val)) { | ||
return outputFailures(val); | ||
} | ||
if (val === 'fail' && key === 'result') { | ||
grunt.log.error("\n%s", iobj.message.replace(/\n/g, ' ')); | ||
} | ||
}); | ||
}; | ||
outputFailures(json); | ||
cb(); | ||
}; | ||
if (typeof json !== 'object'){ | ||
grunt.log.error('Error - Could not read test run results %s', typeof text); | ||
callback(false); | ||
return; | ||
} | ||
// Test is now completed, so parse the results | ||
grunt.log.subhead('\nTested %s', driver.page); | ||
grunt.log.writeln('Environment: %s', cfg.prefix); | ||
if (err) { | ||
grunt.log.error("Could not see test results: %s", err.replace(/\n/g, ' ')); | ||
callback(false); | ||
return; | ||
} | ||
if (json.failed !== 0) { | ||
return showDetailedError(function () { callback(false, json); }); | ||
} | ||
grunt.log.ok("Result: total: %s passed: %s failed: %s", json.total, json.passed, json.failed); | ||
grunt.log.writeln("Test Video: http://saucelabs.com/tests/%s", driver.sessionID); | ||
callback(true, json); | ||
}); | ||
}); | ||
}; | ||
TestRunner.prototype.mochaRunner = function(driver, cfg, testTimeout, testInterval, testReadyTimeout, detailedError, callback) { | ||
var testResult = "mocha-stats", | ||
resultRegexp = /passes: (\d*)failures: (\d*)duration: ([\d,.]*)s/, | ||
currentState = null, | ||
retryCount = 0; | ||
grunt.verbose.writeln("[%s] Starting mocha tests for page", cfg.prefix); | ||
driver.waitForElementById(testResult, testReadyTimeout, function() { | ||
grunt.verbose.writeln("[%s] Test div found, fetching the test results elements", cfg.prefix); | ||
driver.elementById(testResult, function(err, el) { | ||
if (err) { | ||
grunt.log.error("[%s] Could not read test result for %s", cfg.prefix, err, driver.page); | ||
grunt.log.error("[%s] More details at http://saucelabs.com/tests/%s", cfg.prefix, driver.page); | ||
callback(false); | ||
return; | ||
} | ||
grunt.verbose.writeln("[%s] Fetched test result element, waiting for text inside it show complete status", cfg.prefix); | ||
var showDetailedError = function(cb) { | ||
driver.elementById(testResult, function(err, detailEl) { | ||
driver.text(detailEl, function(err, detailText) { | ||
grunt.log.error("\n%s", detailText); | ||
cb(); | ||
}); | ||
}); | ||
}; | ||
var fetchResults = function(cb, status) { | ||
cb(status, err || currentState); | ||
}; | ||
driver.safeEval("mocha.suite.total()", function(err, totalResults) { | ||
(function isCompleted() { | ||
driver.text(el, function(err, text) { | ||
if (typeof text !== 'string'){ | ||
grunt.log.error('Error - Could not read text to check if this was completed %s', typeof text); | ||
callback(false); | ||
return; | ||
} | ||
currentState = text.match(resultRegexp); | ||
currentState[1] = parseInt(currentState[1], 10); | ||
currentState[2] = parseInt(currentState[2], 10); | ||
currentState.push(totalResults); | ||
if ((!currentState || currentState[1] + currentState[2] < totalResults) && ++retryCount * testInterval <= testTimeout) { | ||
grunt.verbose.writeln("[%s] %s. Still running, Time passed - %s of %s milliseconds", cfg.prefix, retryCount, testInterval * retryCount, testTimeout); | ||
setTimeout(isCompleted, testInterval); | ||
return; | ||
} | ||
// Test is now completed, so parse the results | ||
grunt.log.subhead('\nTested %s', driver.page); | ||
grunt.log.writeln('Environment: %s', cfg.prefix); | ||
if (err) { | ||
grunt.log.error("Could not see test results: %s", err.replace(/\n/g, ' ')); | ||
fetchResults(callback, false); | ||
return; | ||
} | ||
if (retryCount * testInterval > testTimeout) { | ||
grunt.log.error("Timeout, waited for more than %s milliseconds", testTimeout); | ||
fetchResults(callback, false); | ||
return; | ||
} | ||
if (+currentState[2] !== 0) { | ||
if (detailedError) { | ||
return showDetailedError(function() { | ||
fetchResults(callback, false); | ||
}); | ||
} | ||
fetchResults(callback, false); | ||
} else { | ||
grunt.log.ok("Result: %s", text.replace(/\n/g, ' ')); | ||
fetchResults(callback, true); | ||
} | ||
grunt.log.writeln("Test Video: http://saucelabs.com/tests/%s", driver.sessionID); | ||
}); | ||
}()); | ||
}); | ||
}); | ||
}); | ||
}; | ||
var defaultsObj = { | ||
@@ -578,2 +749,50 @@ username: process.env.SAUCE_USERNAME, | ||
}); | ||
grunt.registerMultiTask('saucelabs-yui', 'Run YUI test cases using Sauce Labs browsers', function() { | ||
var done = this.async(), | ||
arg = defaults(this.options(defaultsObj)); | ||
var tunnel = new SauceTunnel(arg.username, arg.key, arg.identifier, arg.tunneled, arg.tunnelTimeout); | ||
grunt.log.writeln("=> Connecting to Saucelabs ..."); | ||
if (this.tunneled) { | ||
grunt.verbose.writeln("=> Starting Tunnel to Sauce Labs".inverse.bold); | ||
} | ||
tunnel.start(function(isCreated) { | ||
if (!isCreated) { | ||
done(false); | ||
return; | ||
} | ||
grunt.log.ok("Connected to Saucelabs"); | ||
var test = new TestRunner(arg.username, arg.key); | ||
test.forEachBrowser(arg.browsers, test.yuiRunner, test.yuiSaucify, arg.concurrency, arg.onTestComplete).testPages(arg.pages, arg.testTimeout, arg.testInterval, arg.testReadyTimeout, arg.detailedError, function(status) { | ||
grunt.log[status ? 'ok' : 'error']("All tests completed with status %s", status); | ||
tunnel.stop(function() { | ||
done(status); | ||
}); | ||
}); | ||
}); | ||
}); | ||
grunt.registerMultiTask('saucelabs-mocha', 'Run Mocha test cases using Sauce Labs browsers', function() { | ||
var done = this.async(), | ||
arg = defaults(this.options(defaultsObj)); | ||
var tunnel = new SauceTunnel(arg.username, arg.key, arg.identifier, arg.tunneled, arg.tunnelTimeout); | ||
grunt.log.writeln("=> Connecting to Saucelabs ..."); | ||
if (this.tunneled) { | ||
grunt.verbose.writeln("=> Starting Tunnel to Sauce Labs".inverse.bold); | ||
} | ||
tunnel.start(function(isCreated) { | ||
if (!isCreated) { | ||
done(false); | ||
return; | ||
} | ||
grunt.log.ok("Connected to Saucelabs"); | ||
var test = new TestRunner(arg.username, arg.key); | ||
test.forEachBrowser(arg.browsers, test.mochaRunner, test.mochaSaucify, arg.concurrency, arg.onTestComplete).testPages(arg.pages, arg.testTimeout, arg.testInterval, arg.testReadyTimeout, arg.detailedError, function(status) { | ||
grunt.log[status ? 'ok' : 'error']("All tests completed with status %s", status); | ||
tunnel.stop(function() { | ||
done(status); | ||
}); | ||
}); | ||
}); | ||
}); | ||
}; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
34445908
103
18840
128
22