Skip to content
This repository has been archived by the owner on Jul 29, 2024. It is now read-only.

Commit

Permalink
feat(attachSession): attach protractor to existing webdriver session
Browse files Browse the repository at this point in the history
Attaching an existing selenium browser session to protractor rather than always creating new one. The session can be passed into the config file as a string via the sessionId.
  • Loading branch information
cnishina committed Jan 28, 2016
1 parent 261b49e commit 3f3805f
Show file tree
Hide file tree
Showing 9 changed files with 208 additions and 1 deletion.
4 changes: 4 additions & 0 deletions docs/referenceConf.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ exports.config = {
// connect to an already running instance of Selenium. This usually looks like
// seleniumAddress: 'http://localhost:4444/wd/hub'
seleniumAddress: null,
// The selenium session id allows Protractor to attach to an existing selenium
// browser session. The selenium session is maintained after the test has
// completed. Ignored if seleniumAddress is null.
seleniumSessionId: null,

// ---- 3. To use remote browsers via Sauce Labs -----------------------------
// If sauceUser and sauceKey are specified, seleniumServerJar will be ignored.
Expand Down
1 change: 1 addition & 0 deletions lib/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ var optimist = require('optimist').
describe('version', 'Print Protractor version').
describe('browser', 'Browsername, e.g. chrome or firefox').
describe('seleniumAddress', 'A running selenium address to use').
describe('seleniumSessionId', 'Attaching an existing session id').
describe('seleniumServerJar', 'Location of the standalone selenium jar file').
describe('seleniumPort', 'Optional port for the selenium standalone server').
describe('baseUrl', 'URL to prepend to all relative paths').
Expand Down
2 changes: 2 additions & 0 deletions lib/configParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ var ConfigParser = function() {
stackFilter: helper.filterStackTrace,
defaultTimeoutInterval: (30 * 1000)
},
seleniumArgs: [],
seleniumSessionId: null,
mochaOpts: {
ui: 'bdd',
reporter: 'list'
Expand Down
58 changes: 58 additions & 0 deletions lib/driverProviders/attachSession.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* This is an implementation of the Attach Session Driver Provider.
* It is responsible for setting up the account object, tearing
* it down, and setting up the driver correctly.
*/

var util = require('util'),
q = require('q'),
DriverProvider = require('./driverProvider'),
log = require('../logger'),
webdriver = require('selenium-webdriver'),
executors = require('selenium-webdriver/executors');

var AttachedSessionDriverProvider = function(config) {
DriverProvider.call(this, config);
};
util.inherits(AttachedSessionDriverProvider, DriverProvider);

/**
* Configure and launch (if applicable) the object's environment.
* @public
* @return {q.promise} A promise which will resolve when the environment is
* ready to test.
*/
AttachedSessionDriverProvider.prototype.setupEnv = function() {
log.puts('Using the selenium server at ' + this.config_.seleniumAddress);
log.puts('Using session id - ' + this.config_.seleniumSessionId);
return q(undefined);
};


/**
* Getting a new driver by attaching an existing session.
*
* @public
* @return {webdriver.WebDriver} webdriver instance
*/
AttachedSessionDriverProvider.prototype.getNewDriver = function() {
var executor = executors.createExecutor(this.config_.seleniumAddress);
var newDriver;
newDriver = new webdriver.WebDriver
.attachToSession(executor, this.config_.seleniumSessionId);
this.drivers_.push(newDriver);
return newDriver;
};

/**
* Maintains the existing session and does not quit the driver.
*
* @public
*/
AttachedSessionDriverProvider.prototype.quitDriver = function() {
};

// new instance w/ each include
module.exports = function(config) {
return new AttachedSessionDriverProvider(config);
};
6 changes: 5 additions & 1 deletion lib/runner.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,11 @@ Runner.prototype.loadDriverProvider_ = function() {
if (this.config_.directConnect) {
runnerPath = './driverProviders/direct';
} else if (this.config_.seleniumAddress) {
runnerPath = './driverProviders/hosted';
if (this.config_.seleniumSessionId) {
runnerPath = './driverProviders/attachSession';
} else {
runnerPath = './driverProviders/hosted';
}
} else if (this.config_.browserstackUser && this.config_.browserstackKey) {
runnerPath = './driverProviders/browserstack';
} else if (this.config_.sauceUser && this.config_.sauceKey) {
Expand Down
105 changes: 105 additions & 0 deletions scripts/attachSession.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
#!/usr/bin/env node

'use strict';

var http = require('http'),
spawn = require('child_process').spawnSync;

var sessionId = '';

// 1. Create a new selenium session.
var postData = JSON.stringify(
{'desiredCapabilities': {'browserName': 'firefox'}});
var createOptions = {
hostname: 'localhost',
port: 4444,
path: '/wd/hub/session',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': Buffer.byteLength(postData)
}
};
var req = http.request(createOptions, function(res) {
res.on('data', setBody);
res.on('end', checkSession);
});
req.write(postData);
req.end();

// 2. After making the request to create a selenium session, read the selenium
// session id.
var setBody = function(chunk) {
var body = chunk.toString();
sessionId = JSON.parse(body).sessionId;
};

// 3. After getting the session id, verify that the selenium session exists.
// If the session exists, run the protractor test.
var checkSession = function() {
var checkOptions = {
hostname: 'localhost',
port: 4444,
path: '/wd/hub/session/' + sessionId,
method: 'GET'
};
var state = '';
var req = http.request(checkOptions, function(res) {
res.on('data', function(chunk) {
state = JSON.parse(chunk.toString()).state;
});
res.on('end', function() {
if (state === 'success') {
var runProtractor = spawn('bin/protractor',
['spec/attachSession.js', '--seleniumSessionId=' + sessionId]);
console.log(runProtractor.stdout.toString());
if (runProtractor.status !== 0) {
throw new Error('Protractor did not run properly.');
}
}
else {
throw new Error('The selenium session was not created.');
}
checkStoppedSession();
});
});
req.end();
};

// 4. After the protractor test completes, check to see that the session still
// exists. If we can find the session, delete it.
var checkStoppedSession = function() {
var checkOptions = {
hostname: 'localhost',
port: 4444,
path: '/wd/hub/session/' + sessionId,
method: 'GET'
};
var state = '';
var req = http.request(checkOptions, function(res) {
res.on('data', function(chunk) {
state = JSON.parse(chunk.toString()).state;
});
res.on('end', function() {
if (state === 'success') {
deleteSession();
}
else {
throw new Error('The selenium session should still exist.');
}
});
});
req.end();
};

// 5. Delete the selenium session.
var deleteSession = function() {
var deleteOptions = {
hostname: 'localhost',
port: 4444,
path: '/wd/hub/session/' + sessionId,
method: 'DELETE'
};
var req = http.request(deleteOptions);
req.end();
};
1 change: 1 addition & 0 deletions scripts/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ var passingTests = [
'node lib/cli.js spec/controlLockConf.js',
'node lib/cli.js spec/customFramework.js',
'node lib/cli.js spec/angular2Conf.js',
'node scripts/attachSession.js',
'node scripts/interactive_tests/interactive_test.js',
'node scripts/interactive_tests/with_base_url.js',
// Unit tests
Expand Down
21 changes: 21 additions & 0 deletions spec/attachSession.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
var env = require('./environment.js');

exports.config = {
seleniumAddress: env.seleniumAddress,

framework: 'jasmine',

specs: [
'attachSessionProvider/attachSession_spec.js'
],

capabilities: env.capabilities,

baseUrl: 'http://localhost:8081',

// Special option for Angular2, to test against all Angular2 applications
// on the page. This means that Protractor will wait for every app to be
// stable before each action, and search within all apps when finding
// elements.
useAllAngular2AppRoots: true
};
11 changes: 11 additions & 0 deletions spec/attachSessionProvider/attachSession_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
describe('selenium session id', function() {
var URL = '/ng2/#/async';

beforeEach(function() {
browser.get(URL);
});
it('should be able to use an existing session', function() {
var increment = $('#increment');
expect(increment).toBeDefined();
});
});

1 comment on commit 3f3805f

@prashanth-sams
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.