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

Commit 3f3805f

Browse files
committed
feat(attachSession): attach protractor to existing webdriver session
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.
1 parent 261b49e commit 3f3805f

File tree

9 files changed

+208
-1
lines changed

9 files changed

+208
-1
lines changed

docs/referenceConf.js

+4
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ exports.config = {
5555
// connect to an already running instance of Selenium. This usually looks like
5656
// seleniumAddress: 'http://localhost:4444/wd/hub'
5757
seleniumAddress: null,
58+
// The selenium session id allows Protractor to attach to an existing selenium
59+
// browser session. The selenium session is maintained after the test has
60+
// completed. Ignored if seleniumAddress is null.
61+
seleniumSessionId: null,
5862

5963
// ---- 3. To use remote browsers via Sauce Labs -----------------------------
6064
// If sauceUser and sauceKey are specified, seleniumServerJar will be ignored.

lib/cli.js

+1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ var optimist = require('optimist').
3939
describe('version', 'Print Protractor version').
4040
describe('browser', 'Browsername, e.g. chrome or firefox').
4141
describe('seleniumAddress', 'A running selenium address to use').
42+
describe('seleniumSessionId', 'Attaching an existing session id').
4243
describe('seleniumServerJar', 'Location of the standalone selenium jar file').
4344
describe('seleniumPort', 'Optional port for the selenium standalone server').
4445
describe('baseUrl', 'URL to prepend to all relative paths').

lib/configParser.js

+2
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ var ConfigParser = function() {
3232
stackFilter: helper.filterStackTrace,
3333
defaultTimeoutInterval: (30 * 1000)
3434
},
35+
seleniumArgs: [],
36+
seleniumSessionId: null,
3537
mochaOpts: {
3638
ui: 'bdd',
3739
reporter: 'list'

lib/driverProviders/attachSession.js

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* This is an implementation of the Attach Session Driver Provider.
3+
* It is responsible for setting up the account object, tearing
4+
* it down, and setting up the driver correctly.
5+
*/
6+
7+
var util = require('util'),
8+
q = require('q'),
9+
DriverProvider = require('./driverProvider'),
10+
log = require('../logger'),
11+
webdriver = require('selenium-webdriver'),
12+
executors = require('selenium-webdriver/executors');
13+
14+
var AttachedSessionDriverProvider = function(config) {
15+
DriverProvider.call(this, config);
16+
};
17+
util.inherits(AttachedSessionDriverProvider, DriverProvider);
18+
19+
/**
20+
* Configure and launch (if applicable) the object's environment.
21+
* @public
22+
* @return {q.promise} A promise which will resolve when the environment is
23+
* ready to test.
24+
*/
25+
AttachedSessionDriverProvider.prototype.setupEnv = function() {
26+
log.puts('Using the selenium server at ' + this.config_.seleniumAddress);
27+
log.puts('Using session id - ' + this.config_.seleniumSessionId);
28+
return q(undefined);
29+
};
30+
31+
32+
/**
33+
* Getting a new driver by attaching an existing session.
34+
*
35+
* @public
36+
* @return {webdriver.WebDriver} webdriver instance
37+
*/
38+
AttachedSessionDriverProvider.prototype.getNewDriver = function() {
39+
var executor = executors.createExecutor(this.config_.seleniumAddress);
40+
var newDriver;
41+
newDriver = new webdriver.WebDriver
42+
.attachToSession(executor, this.config_.seleniumSessionId);
43+
this.drivers_.push(newDriver);
44+
return newDriver;
45+
};
46+
47+
/**
48+
* Maintains the existing session and does not quit the driver.
49+
*
50+
* @public
51+
*/
52+
AttachedSessionDriverProvider.prototype.quitDriver = function() {
53+
};
54+
55+
// new instance w/ each include
56+
module.exports = function(config) {
57+
return new AttachedSessionDriverProvider(config);
58+
};

lib/runner.js

+5-1
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,11 @@ Runner.prototype.loadDriverProvider_ = function() {
9393
if (this.config_.directConnect) {
9494
runnerPath = './driverProviders/direct';
9595
} else if (this.config_.seleniumAddress) {
96-
runnerPath = './driverProviders/hosted';
96+
if (this.config_.seleniumSessionId) {
97+
runnerPath = './driverProviders/attachSession';
98+
} else {
99+
runnerPath = './driverProviders/hosted';
100+
}
97101
} else if (this.config_.browserstackUser && this.config_.browserstackKey) {
98102
runnerPath = './driverProviders/browserstack';
99103
} else if (this.config_.sauceUser && this.config_.sauceKey) {

scripts/attachSession.js

+105
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
#!/usr/bin/env node
2+
3+
'use strict';
4+
5+
var http = require('http'),
6+
spawn = require('child_process').spawnSync;
7+
8+
var sessionId = '';
9+
10+
// 1. Create a new selenium session.
11+
var postData = JSON.stringify(
12+
{'desiredCapabilities': {'browserName': 'firefox'}});
13+
var createOptions = {
14+
hostname: 'localhost',
15+
port: 4444,
16+
path: '/wd/hub/session',
17+
method: 'POST',
18+
headers: {
19+
'Content-Type': 'application/x-www-form-urlencoded',
20+
'Content-Length': Buffer.byteLength(postData)
21+
}
22+
};
23+
var req = http.request(createOptions, function(res) {
24+
res.on('data', setBody);
25+
res.on('end', checkSession);
26+
});
27+
req.write(postData);
28+
req.end();
29+
30+
// 2. After making the request to create a selenium session, read the selenium
31+
// session id.
32+
var setBody = function(chunk) {
33+
var body = chunk.toString();
34+
sessionId = JSON.parse(body).sessionId;
35+
};
36+
37+
// 3. After getting the session id, verify that the selenium session exists.
38+
// If the session exists, run the protractor test.
39+
var checkSession = function() {
40+
var checkOptions = {
41+
hostname: 'localhost',
42+
port: 4444,
43+
path: '/wd/hub/session/' + sessionId,
44+
method: 'GET'
45+
};
46+
var state = '';
47+
var req = http.request(checkOptions, function(res) {
48+
res.on('data', function(chunk) {
49+
state = JSON.parse(chunk.toString()).state;
50+
});
51+
res.on('end', function() {
52+
if (state === 'success') {
53+
var runProtractor = spawn('bin/protractor',
54+
['spec/attachSession.js', '--seleniumSessionId=' + sessionId]);
55+
console.log(runProtractor.stdout.toString());
56+
if (runProtractor.status !== 0) {
57+
throw new Error('Protractor did not run properly.');
58+
}
59+
}
60+
else {
61+
throw new Error('The selenium session was not created.');
62+
}
63+
checkStoppedSession();
64+
});
65+
});
66+
req.end();
67+
};
68+
69+
// 4. After the protractor test completes, check to see that the session still
70+
// exists. If we can find the session, delete it.
71+
var checkStoppedSession = function() {
72+
var checkOptions = {
73+
hostname: 'localhost',
74+
port: 4444,
75+
path: '/wd/hub/session/' + sessionId,
76+
method: 'GET'
77+
};
78+
var state = '';
79+
var req = http.request(checkOptions, function(res) {
80+
res.on('data', function(chunk) {
81+
state = JSON.parse(chunk.toString()).state;
82+
});
83+
res.on('end', function() {
84+
if (state === 'success') {
85+
deleteSession();
86+
}
87+
else {
88+
throw new Error('The selenium session should still exist.');
89+
}
90+
});
91+
});
92+
req.end();
93+
};
94+
95+
// 5. Delete the selenium session.
96+
var deleteSession = function() {
97+
var deleteOptions = {
98+
hostname: 'localhost',
99+
port: 4444,
100+
path: '/wd/hub/session/' + sessionId,
101+
method: 'DELETE'
102+
};
103+
var req = http.request(deleteOptions);
104+
req.end();
105+
};

scripts/test.js

+1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ var passingTests = [
3232
'node lib/cli.js spec/controlLockConf.js',
3333
'node lib/cli.js spec/customFramework.js',
3434
'node lib/cli.js spec/angular2Conf.js',
35+
'node scripts/attachSession.js',
3536
'node scripts/interactive_tests/interactive_test.js',
3637
'node scripts/interactive_tests/with_base_url.js',
3738
// Unit tests

spec/attachSession.js

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
var env = require('./environment.js');
2+
3+
exports.config = {
4+
seleniumAddress: env.seleniumAddress,
5+
6+
framework: 'jasmine',
7+
8+
specs: [
9+
'attachSessionProvider/attachSession_spec.js'
10+
],
11+
12+
capabilities: env.capabilities,
13+
14+
baseUrl: 'http://localhost:8081',
15+
16+
// Special option for Angular2, to test against all Angular2 applications
17+
// on the page. This means that Protractor will wait for every app to be
18+
// stable before each action, and search within all apps when finding
19+
// elements.
20+
useAllAngular2AppRoots: true
21+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
describe('selenium session id', function() {
2+
var URL = '/ng2/#/async';
3+
4+
beforeEach(function() {
5+
browser.get(URL);
6+
});
7+
it('should be able to use an existing session', function() {
8+
var increment = $('#increment');
9+
expect(increment).toBeDefined();
10+
});
11+
});

0 commit comments

Comments
 (0)