Permalink
Browse files

Releasing Sauce Connect Launcher

  • Loading branch information...
0 parents commit 1906d7cc256fd129f556873a877bf96f6a14b107 @bermi committed Nov 16, 2012
Showing with 400 additions and 0 deletions.
  1. +9 −0 .gitignore
  2. +12 −0 .npmignore
  3. +6 −0 .travis.yml
  4. +17 −0 Makefile
  5. +32 −0 README.md
  6. +14 −0 examples/example.js
  7. +49 −0 grunt.js
  8. +169 −0 lib/sauce-connect-launcher.js
  9. +33 −0 package.json
  10. +59 −0 test/sauce-connect-launcher.test.js
@@ -0,0 +1,9 @@
+.DS_Store
+.tags
+lib-cov/
+lib/Sauce-Connect-latest.zip
+lib/Sauce-Connect.jar
+sauce_connect.log
+node_modules/
+npm-debug.log
+test/coverage.html
@@ -0,0 +1,12 @@
+.DS_Store
+.gitignore
+.npmignore
+.tags
+lib/Sauce-Connect.jar
+lib/Sauce-Connect-latest.zip
+sauce_connect.log
+test/coverage.html
+lib-cov/
+node_modules/
+npm-debug.log
+test/
@@ -0,0 +1,6 @@
+language: node_js
+node_js:
+ - 0.8
+before_script:
+ - "export DISPLAY=:99.0"
+ - "sh -e /etc/init.d/xvfb start"
@@ -0,0 +1,17 @@
+test:
+ npm test
+
+test-instrument:
+ jscoverage js js-cov
+
+test-clean-instrument:
+ rm -rf js-cov
+
+test-coverage-report:
+ COVERAGE=1 mocha --reporter html-cov > test/coverage.html
+
+test-coverage: test-clean-instrument test-instrument test-coverage-report
+
+.PHONY: default test test-coverage test-clean-instrument test-instrument test-coverage-report
+
+default: test-coverage
@@ -0,0 +1,32 @@
+# sauce-connect-launcher
+
+[![Build Status](https://secure.travis-ci.org/bermi/sauce-connect-launcher.png)](http://travis-ci.org/bermi/sauce-connect-launcher)
+
+A library to download and launch Sauce Connect.
+
+## Usage
+
+```javascript
+
+var sauceConnectLauncher = require('sauce-connect-launcher'),
+ options = {
+ username: 'bermi',
+ accessKey: '12345678-1234-1234-1234-1234567890ab',
+ verbose: false,
+ logger: console.log
+ };
+
+sauceConnectLauncher(options, function (err, sauceConnectProcess) {
+ console.log("Started Sauce Connect Process");
+ sauceConnectProcess.close(function () {
+ console.log("Closed Sauce Connect process");
+ });
+});
+
+```
+
+## Testing
+
+```sh
+npm test
+```
@@ -0,0 +1,14 @@
+
+var sauceConnectLauncher = require('../lib/sauce-connect-launcher');
+
+sauceConnectLauncher({
+ username: 'bermi',
+ accessKey: '12345678-1234-1234-1234-1234567890ab',
+ verbose: false,
+ logger: console.log
+}, function (err, sauceConnectProcess) {
+ console.log("Started Sauce Connect Process");
+ sauceConnectProcess.close(function () {
+ console.log("Closed Sauce Connect process");
+ });
+});
@@ -0,0 +1,49 @@
+
+module.exports = function (grunt) {
+
+ // Project configuration.
+ grunt.initConfig({
+ lint: {
+ files: ['grunt.js', 'lib/*.js', 'test/*.js']
+ },
+ watch: {
+ files: ['grunt.js', 'lib/*.js', 'test/*.js'],
+ tasks: 'lint'
+ },
+ jshint: {
+ options: {
+ bitwise: true,
+ curly: true,
+ eqeqeq: true,
+ forin: true,
+ immed: true,
+ latedef: true,
+ newcap: true,
+ noarg: true,
+ nonew: true,
+ plusplus: true,
+ regexp: true,
+ noempty: true,
+ sub: true,
+ undef: true,
+ trailing: true,
+ eqnull: true,
+ browser: true,
+ node: true,
+ indent: 2,
+ onevar: true,
+ white: true,
+ strict: false
+ },
+ globals: {
+ describe: true,
+ after: true,
+ before: true,
+ it: true
+ }
+ }
+ });
+
+ grunt.registerTask('default', 'lint');
+
+};
@@ -0,0 +1,169 @@
+var
+ fs = require('fs'),
+ path = require('path'),
+ ProgressBar = require('progress'),
+ http = require('http'),
+ unzip = require('unzip'),
+ spawn = require('child_process').spawn,
+ EventEmitter = require('events').EventEmitter,
+ util = require('util'),
+ jarfile = path.normalize(__dirname + '/Sauce-Connect.jar'),
+ outfile = path.normalize(__dirname + '/Sauce-Connect-latest.zip'),
+ openProcesses = [],
+ logger = console.log;
+
+
+function download(options, callback) {
+ var req = http.request({
+ host: 'saucelabs.com',
+ port: 80,
+ path: '/downloads/Sauce-Connect-latest.zip'
+ });
+
+ logger("Missing Sauce Connect local proxy, downloading dependency");
+ logger("This will only happen once.");
+
+ req.on('response', function (res) {
+
+ logger("Downloading ", res.headers['content-length'], "bytes");
+
+ var bar, len = parseInt(res.headers['content-length'], 10);
+
+ res.pipe(fs.createWriteStream(outfile));
+
+ logger();
+ bar = new ProgressBar(' downloading Sauce-Connect-latest.zip [:bar] :percent :etas', {
+ complete: '=',
+ incomplete: ' ',
+ width: 20,
+ total: len
+ });
+
+ res.on('data', function (chunk) {
+ bar.tick(chunk.length);
+ });
+
+ res.on('end', function () {
+ logger('\n');
+ logger("Unzipping Sauce-Connect-latest.zip");
+ setTimeout(function () {
+ fs.createReadStream(outfile)
+ .pipe(unzip.Parse())
+ .on('entry', function (entry) {
+ if (entry.path.match(/Sauce-Connect\.jar$/)) {
+ logger("Saving Sauce-Connect.jar");
+ var jar = fs.createWriteStream(jarfile);
+ entry.on('end', function (err) {
+ // write queued data before closing the stream
+ logger("Removing Sauce-Connect-latest.zip");
+ fs.unlink(outfile, function (err) {
+ setTimeout(function () {
+ logger("Sauce Connect installed correctly");
+ callback(null);
+ }, 500);
+ });
+ });
+ entry.pipe(jar);
+ }
+ });
+ }, 1000);
+
+ });
+
+ });
+
+ req.end();
+}
+
+
+
+function run(options, callback) {
+ logger("Opening local tunnel using Sauce Connect");
+ var child = spawn("java", ["-jar", jarfile, options.username, options.accessKey]),
+ badExit = function () {
+ return callback(new Error("Could not start Sauce Connect."));
+ };
+
+ child.stdout.on("data", function (data) {
+ var connectingText = 'Please wait for "You may start your tests" to start your tests',
+ readyText = "Connected! You may start your tests",
+ outdatedText = "This version of Sauce Connect is outdated",
+ errorText = "Exception: ",
+ credentialsError = "java.io.IOException: Server returned HTTP response code: 401",
+ shutDown = "Finished shutting down tunnel remote VM";
+
+ data = data.toString();
+ if (options.verbose) {
+ console.log(data);
+ }
+
+ if (data.indexOf(connectingText) !== -1) {
+ logger("Creating tunnel with Sauce Labs");
+ } else if (data.indexOf(readyText) !== -1) {
+ logger("Testing tunnel ready");
+ callback(null, child);
+ } else if (data.indexOf(credentialsError) !== -1) {
+ logger("Invalid Sauce Connect Credentials");
+ callback(new Error("Invalid Sauce Connect Credentials. " + data), child);
+ } else if (data.indexOf(errorText) !== -1) {
+ logger("Sauce Connect Error");
+ callback(new Error(data), child);
+ }
+ });
+
+ openProcesses.push(child);
+
+ child.close = function () {
+ child.kill('SIGTERM');
+ };
+}
+
+function downloadAndStartProcess(options, callback) {
+ if (arguments.length === 1) {
+ callback = options;
+ options = {};
+ }
+ logger = options.logger || function () {};
+ fs.exists(jarfile, function (exists) {
+ if (exists) {
+ run(options, callback);
+ } else {
+ // Being downloaded?
+ fs.exists(outfile, function (exists) {
+ if (!exists) {
+ download(options, function (err) {
+ if (err) {
+ return callback(err);
+ }
+ run(options, callback);
+ });
+ } else {
+ // The file is already being downloaded
+ // by another process, lets poll until
+ // it's available to be used
+ setTimeout(function () {
+ downloadAndStartProcess(options, callback);
+ }, 1000);
+ }
+ });
+ }
+ });
+}
+
+
+// Make sure all processes have been closed
+// when the script goes down
+process.on('exit', function (err) {
+ logger("Shutting down");
+ while (openProcesses.length) {
+ var fakeProcess = openProcesses.pop();
+ try {
+ fakeProcess.emit("exit");
+ fakeProcess.kill('SIGTERM');
+ } catch (e) {}
+ }
+});
+
+module.exports = downloadAndStartProcess;
+
+
@@ -0,0 +1,33 @@
+{
+ "name": "sauce-connect-launcher",
+ "description": "A library to download and launch Sauce Connect.",
+ "version": "0.0.1",
+ "homepage": "https://github.com/bermi/sauce-connect-launcher",
+ "author": "Bermi Ferrer <bermi@bermilabs.com>",
+ "main": "lib/sauce-connect-launcher",
+ "keywords": [
+ "selenium",
+ "sauce connect",
+ "sauce labs",
+ "testing",
+ "local tunnel"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/bermi/sauce-connect-launcher"
+ },
+ "scripts": {
+ "test": "mocha"
+ },
+ "dependencies": {
+ "unzip": "0.0.4",
+ "progress": "~0.1.0"
+ },
+ "devDependencies": {
+ "mocha": "~1.7.0",
+ "expect.js": "~0.2.0"
+ },
+ "engines": {
+ "node": ">= 0.8.x"
+ }
+}
Oops, something went wrong.

0 comments on commit 1906d7c

Please sign in to comment.