Skip to content

Commit 8d303c2

Browse files
committed
[js] Move all logic for interpreting responses from the remote end to the
command executor.
1 parent c291ea9 commit 8d303c2

File tree

11 files changed

+368
-338
lines changed

11 files changed

+368
-338
lines changed

javascript/node/selenium-webdriver/CHANGES.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
### Change Summary
44

5+
* Moved all logic for parsing and interpreting responses from the remote end
6+
into the individual `command.Executor` implementations.
57
* For consistency with the other Selenium language bindings,
68
`WebDriver#isElementPresent()` and `WebElement#isElementPresent()` have
79
been deprecated. These methods will be removed in v3.0. Use the findElements

javascript/node/selenium-webdriver/error.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -577,7 +577,16 @@ function checkLegacyResponse(responseObj) {
577577
if (!value || typeof value !== 'object') {
578578
throw new ctor(value + '');
579579
} else {
580-
throw new ctor(value['message'] + '');
580+
let message = value['message'] + '';
581+
if (ctor !== UnexpectedAlertOpenError) {
582+
throw new ctor(message);
583+
}
584+
585+
let text = '';
586+
if (value['alert'] && typeof value['alert']['text'] === 'string') {
587+
text = value['alert']['text'];
588+
}
589+
throw new UnexpectedAlertOpenError(message, text);
581590
}
582591
}
583592
return responseObj;

javascript/node/selenium-webdriver/http/index.js

Lines changed: 41 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ const error = require('../error');
2929
const cmd = require('../lib/command');
3030
const logging = require('../lib/logging');
3131
const promise = require('../lib/promise');
32+
const Session = require('../lib/session').Session;
3233

3334

3435

@@ -432,40 +433,62 @@ class Executor {
432433
log.finer(() => '>>>\n' + request);
433434
return this.client_.send(request).then(function(response) {
434435
log.finer(() => '<<<\n' + response);
435-
return parseHttpResponse(/** @type {!HttpResponse} */ (response));
436+
437+
let value = parseHttpResponse(/** @type {!HttpResponse} */ (response));
438+
let isResponseObj = (value && typeof value === 'object');
439+
if (command.getName() === cmd.Name.NEW_SESSION) {
440+
if (!isResponseObj) {
441+
throw new error.WebDriverError(
442+
'Unable to parse new session response: ' + response.body);
443+
}
444+
return new Session(value['sessionId'], value['value']);
445+
}
446+
return isResponseObj ? value['value'] : value;
436447
});
437448
}
438449
}
439450

440451

452+
/**
453+
* @param {string} str .
454+
* @return {?} .
455+
*/
456+
function tryParse(str) {
457+
try {
458+
return JSON.parse(str);
459+
} catch (ignored) {
460+
// Do nothing.
461+
}
462+
}
463+
464+
441465
/**
442466
* Callback used to parse {@link HttpResponse} objects from a
443467
* {@link HttpClient}.
444468
* @param {!HttpResponse} httpResponse The HTTP response to parse.
445469
* @return {!Object} The parsed response.
446470
*/
447471
function parseHttpResponse(httpResponse) {
448-
try {
449-
return /** @type {!Object} */ (JSON.parse(httpResponse.body));
450-
} catch (ignored) {
451-
// Whoops, looks like the server sent us a malformed response. We'll need
452-
// to manually build a response object based on the response code.
472+
let parsed = tryParse(httpResponse.body);
473+
if (parsed !== undefined) {
474+
error.checkLegacyResponse(parsed);
475+
return parsed;
476+
}
477+
478+
let value = httpResponse.body.replace(/\r\n/g, '\n');
479+
if (!value) {
480+
return null;
453481
}
454482

455-
let response = {
456-
'status': error.ErrorCode.SUCCESS,
457-
'value': httpResponse.body.replace(/\r\n/g, '\n')
458-
};
459-
460-
if (httpResponse.status >= 400) {
461-
// 404 represents an unknown command; anything else is a generic unknown
462-
// error.
463-
response['status'] = httpResponse.status == 404 ?
464-
error.ErrorCode.UNKNOWN_COMMAND :
465-
error.ErrorCode.UNKNOWN_ERROR;
483+
// 404 represents an unknown command; anything else is a generic unknown
484+
// error.
485+
if (httpResponse.status == 404) {
486+
throw new error.UnsupportedOperationError(value);
487+
} else if (httpResponse.status >= 400) {
488+
throw new error.WebDriverError(value);
466489
}
467490

468-
return response;
491+
return value;
469492
}
470493

471494

javascript/node/selenium-webdriver/http/util.js

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,7 @@
2121

2222
'use strict';
2323

24-
const error = require('../error'),
25-
Executor = require('./index').Executor,
24+
const Executor = require('./index').Executor,
2625
HttpClient = require('./index').HttpClient,
2726
HttpRequest = require('./index').Request,
2827
Command = require('../lib/command').Command,
@@ -41,10 +40,7 @@ function getStatus(url) {
4140
var client = new HttpClient(url);
4241
var executor = new Executor(client);
4342
var command = new Command(CommandName.GET_SERVER_STATUS);
44-
return executor.execute(command).then(function(responseObj) {
45-
error.checkLegacyResponse(responseObj);
46-
return responseObj['value'];
47-
});
43+
return executor.execute(command);
4844
}
4945

5046

javascript/node/selenium-webdriver/lib/command.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ class Executor {
227227
* response object.
228228
*
229229
* @param {!Command} command The command to execute.
230-
* @return {!promise.Promise<!Object>} A promise that will be fulfilled with
230+
* @return {!promise.Promise<?>} A promise that will be fulfilled with
231231
* the command result.
232232
*/
233233
execute(command) {}

javascript/node/selenium-webdriver/lib/webdriver.js

Lines changed: 3 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,7 @@ const until = require('./until');
6060
function acquireSession(executor, command, description, opt_flow) {
6161
let flow = opt_flow || promise.controlFlow();
6262
let session = flow.execute(function() {
63-
return executeCommand(executor, command).then(function(response) {
64-
error.checkLegacyResponse(response);
65-
return new Session(response['sessionId'], response['value']);
66-
});
63+
return executeCommand(executor, command);
6764
}, description);
6865
return new WebDriver(session, executor, flow);
6966
}
@@ -354,24 +351,8 @@ class WebDriver {
354351
return prepCommand.then(function(parameters) {
355352
command.setParameters(parameters);
356353
return executor.execute(command);
357-
});
358-
}, description).then(function(response) {
359-
try {
360-
error.checkLegacyResponse(response);
361-
} catch (ex) {
362-
if (ex instanceof error.UnexpectedAlertOpenError) {
363-
let text = '';
364-
if (response['value']
365-
&& response['value']['alert']
366-
&& typeof response['value']['alert']['text'] === 'string') {
367-
text = response['value']['alert']['text'];
368-
}
369-
throw new error.UnexpectedAlertOpenError(ex.message, text);
370-
}
371-
throw ex;
372-
}
373-
return fromWireValue(self, response['value']);
374-
});
354+
}).then(value => fromWireValue(self, value));
355+
}, description);
375356

376357
function checkHasNotQuit() {
377358
if (!self.session_) {

javascript/node/selenium-webdriver/safari.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ const url = require('url');
3434
const util = require('util');
3535
const ws = require('ws');
3636

37+
const error = require('./error');
3738
const io = require('./io');
3839
const exec = require('./io/exec');
3940
const isDevMode = require('./lib/devmode');
@@ -303,8 +304,7 @@ class CommandExecutor {
303304
break;
304305

305306
case command.Name.QUIT:
306-
self.destroySession_()
307-
.then(() => fulfill({status: 0, value: null}), reject);
307+
self.destroySession_().then(() => fulfill(null), reject);
308308
break;
309309

310310
default:
@@ -344,7 +344,13 @@ class CommandExecutor {
344344
reject(Error('Failed to parse driver message: ' + data));
345345
return;
346346
}
347-
fulfill(data['response']);
347+
348+
try {
349+
error.checkLegacyResponse(data['response']);
350+
fulfill(data['response']['value']);
351+
} catch (ex) {
352+
reject(ex);
353+
}
348354
});
349355
});
350356
}

javascript/node/selenium-webdriver/test/error_test.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,43 @@ describe('error', function() {
109109
test('INVALID_XPATH_SELECTOR_RETURN_TYPE', error.InvalidSelectorError);
110110
test('METHOD_NOT_ALLOWED', error.UnsupportedOperationError);
111111

112+
describe('UnexpectedAlertOpenError', function() {
113+
it('includes alert text from the response object', function() {
114+
let response = {
115+
status: error.ErrorCode.UNEXPECTED_ALERT_OPEN,
116+
value: {
117+
message: 'hi',
118+
alert: {text: 'alert text here'}
119+
}
120+
};
121+
assert.throws(
122+
() => error.checkLegacyResponse(response),
123+
(e) => {
124+
assert.equal(error.UnexpectedAlertOpenError, e.constructor);
125+
assert.equal(e.message, 'hi');
126+
assert.equal(e.getAlertText(), 'alert text here');
127+
return true;
128+
});
129+
});
130+
131+
it('uses an empty string if alert text omitted', function() {
132+
let response = {
133+
status: error.ErrorCode.UNEXPECTED_ALERT_OPEN,
134+
value: {
135+
message: 'hi'
136+
}
137+
};
138+
assert.throws(
139+
() => error.checkLegacyResponse(response),
140+
(e) => {
141+
assert.equal(error.UnexpectedAlertOpenError, e.constructor);
142+
assert.equal(e.message, 'hi');
143+
assert.equal(e.getAlertText(), '');
144+
return true;
145+
});
146+
});
147+
});
148+
112149
function test(codeKey, expectedType) {
113150
it(`${codeKey} => ${expectedType.name}`, function() {
114151
let code = error.ErrorCode[codeKey];

0 commit comments

Comments
 (0)