123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696 |
- // Licensed to the Software Freedom Conservancy (SFC) under one
- // or more contributor license agreements. See the NOTICE file
- // distributed with this work for additional information
- // regarding copyright ownership. The SFC licenses this file
- // to you under the Apache License, Version 2.0 (the
- // "License"); you may not use this file except in compliance
- // with the License. You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing,
- // software distributed under the License is distributed on an
- // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- // KIND, either express or implied. See the License for the
- // specific language governing permissions and limitations
- // under the License.
- 'use strict';
- var assert = require('assert'),
- sinon = require('sinon');
- var Capabilities = require('../../lib/capabilities').Capabilities,
- Command = require('../../lib/command').Command,
- CommandName = require('../../lib/command').Name,
- error = require('../../lib/error'),
- http = require('../../lib/http'),
- Session = require('../../lib/session').Session,
- promise = require('../../lib/promise'),
- WebElement = require('../../lib/webdriver').WebElement;
- describe('http', function() {
- describe('buildPath', function() {
- it('properly replaces path segments with command parameters', function() {
- var parameters = {'sessionId':'foo', 'url':'http://www.google.com'};
- var finalPath = http.buildPath('/session/:sessionId/url', parameters);
- assert.equal(finalPath, '/session/foo/url');
- assert.deepEqual(parameters, {'url':'http://www.google.com'});
- });
- it('handles web element references', function() {
- var parameters = {'sessionId':'foo', 'id': WebElement.buildId('bar')};
- var finalPath = http.buildPath(
- '/session/:sessionId/element/:id/click', parameters);
- assert.equal(finalPath, '/session/foo/element/bar/click');
- assert.deepEqual(parameters, {});
- });
- it('throws if missing a parameter', function() {
- assert.throws(
- () => http.buildPath('/session/:sessionId', {}),
- function(err) {
- return err instanceof error.InvalidArgumentError
- && 'Missing required parameter: sessionId' === err.message;
- });
- assert.throws(
- () => http.buildPath(
- '/session/:sessionId/element/:id', {'sessionId': 'foo'}),
- function(err) {
- return err instanceof error.InvalidArgumentError
- && 'Missing required parameter: id' === err.message;
- });
- });
- it('does not match on segments that do not start with a colon', function() {
- assert.equal(
- http.buildPath('/session/foo:bar/baz', {}),
- '/session/foo:bar/baz');
- });
- });
- describe('Executor', function() {
- let executor;
- let client;
- let send;
- beforeEach(function setUp() {
- client = new http.Client;
- send = sinon.stub(client, 'send');
- executor = new http.Executor(client);
- });
- describe('command routing', function() {
- it('rejects unrecognized commands', function() {
- return executor.execute(new Command('fake-name'))
- .then(assert.fail, err => {
- if (err instanceof error.UnknownCommandError
- && 'Unrecognized command: fake-name' === err.message) {
- return;
- }
- throw err;
- })
- });
- it('rejects promise if client fails to send request', function() {
- let error = new Error('boom');
- send.returns(Promise.reject(error));
- return assertFailsToSend(new Command(CommandName.NEW_SESSION))
- .then(function(e) {
- assert.strictEqual(error, e);
- assertSent(
- 'POST', '/session', {},
- [['Accept', 'application/json; charset=utf-8']]);
- });
- });
- it('can execute commands with no URL parameters', function() {
- var resp = JSON.stringify({sessionId: 'abc123'});
- send.returns(Promise.resolve(new http.Response(200, {}, resp)));
- let command = new Command(CommandName.NEW_SESSION);
- return assertSendsSuccessfully(command).then(function(response) {
- assertSent(
- 'POST', '/session', {},
- [['Accept', 'application/json; charset=utf-8']]);
- });
- });
- it('rejects commands missing URL parameters', function() {
- let command =
- new Command(CommandName.FIND_CHILD_ELEMENT).
- setParameter('sessionId', 's123').
- // Let this be missing: setParameter('id', {'ELEMENT': 'e456'}).
- setParameter('using', 'id').
- setParameter('value', 'foo');
- assert.throws(
- () => executor.execute(command),
- function(err) {
- return err instanceof error.InvalidArgumentError
- && 'Missing required parameter: id' === err.message;
- });
- assert.ok(!send.called);
- });
- it('replaces URL parameters with command parameters', function() {
- var command = new Command(CommandName.GET).
- setParameter('sessionId', 's123').
- setParameter('url', 'http://www.google.com');
- send.returns(Promise.resolve(new http.Response(200, {}, '')));
- return assertSendsSuccessfully(command).then(function(response) {
- assertSent(
- 'POST', '/session/s123/url', {'url': 'http://www.google.com'},
- [['Accept', 'application/json; charset=utf-8']]);
- });
- });
- describe('uses correct URL', function() {
- beforeEach(() => executor = new http.Executor(client));
- describe('in legacy mode', function() {
- test(CommandName.GET_WINDOW_SIZE, {sessionId:'s123'}, false,
- 'GET', '/session/s123/window/current/size');
- test(CommandName.SET_WINDOW_SIZE,
- {sessionId:'s123', width: 1, height: 1}, false,
- 'POST', '/session/s123/window/current/size',
- {width: 1, height: 1});
- test(CommandName.MAXIMIZE_WINDOW, {sessionId:'s123'}, false,
- 'POST', '/session/s123/window/current/maximize');
- // This is consistent b/w legacy and W3C, just making sure.
- test(CommandName.GET,
- {sessionId:'s123', url: 'http://www.example.com'}, false,
- 'POST', '/session/s123/url', {url: 'http://www.example.com'});
- });
- describe('in W3C mode', function() {
- test(CommandName.GET_WINDOW_SIZE,
- {sessionId:'s123'}, true,
- 'GET', '/session/s123/window/size');
- test(CommandName.SET_WINDOW_SIZE,
- {sessionId:'s123', width: 1, height: 1}, true,
- 'POST', '/session/s123/window/size', {width: 1, height: 1});
- test(CommandName.MAXIMIZE_WINDOW, {sessionId:'s123'}, true,
- 'POST', '/session/s123/window/maximize');
- // This is consistent b/w legacy and W3C, just making sure.
- test(CommandName.GET,
- {sessionId:'s123', url: 'http://www.example.com'}, true,
- 'POST', '/session/s123/url', {url: 'http://www.example.com'});
- });
- function test(command, parameters, w3c,
- expectedMethod, expectedUrl, opt_expectedParams) {
- it(`command=${command}`, function() {
- var resp = JSON.stringify({sessionId: 'abc123'});
- send.returns(Promise.resolve(new http.Response(200, {}, resp)));
- let cmd = new Command(command).setParameters(parameters);
- executor.w3c = w3c;
- return executor.execute(cmd).then(function() {
- assertSent(
- expectedMethod, expectedUrl, opt_expectedParams || {},
- [['Accept', 'application/json; charset=utf-8']]);
- });
- });
- }
- });
- });
- describe('response parsing', function() {
- it('extracts value from JSON response', function() {
- var responseObj = {
- 'status': error.ErrorCode.SUCCESS,
- 'value': 'http://www.google.com'
- };
- var command = new Command(CommandName.GET_CURRENT_URL)
- .setParameter('sessionId', 's123');
- send.returns(Promise.resolve(
- new http.Response(200, {}, JSON.stringify(responseObj))));
- return executor.execute(command).then(function(response) {
- assertSent('GET', '/session/s123/url', {},
- [['Accept', 'application/json; charset=utf-8']]);
- assert.strictEqual(response, 'http://www.google.com');
- });
- });
- describe('extracts Session from NEW_SESSION response', function() {
- beforeEach(() => executor = new http.Executor(client));
- const command = new Command(CommandName.NEW_SESSION);
- describe('fails if server returns invalid response', function() {
- describe('(empty response)', function() {
- test(true);
- test(false);
- function test(w3c) {
- it('w3c === ' + w3c, function() {
- send.returns(Promise.resolve(new http.Response(200, {}, '')));
- executor.w3c = w3c;
- return executor.execute(command).then(
- () => assert.fail('expected to fail'),
- (e) => {
- if (!e.message.startsWith('Unable to parse')) {
- throw e;
- }
- });
- });
- }
- });
- describe('(no session ID)', function() {
- test(true);
- test(false);
- function test(w3c) {
- it('w3c === ' + w3c, function() {
- let resp = {value:{name: 'Bob'}};
- send.returns(Promise.resolve(
- new http.Response(200, {}, JSON.stringify(resp))));
- executor.w3c = w3c;
- return executor.execute(command).then(
- () => assert.fail('expected to fail'),
- (e) => {
- if (!e.message.startsWith('Unable to parse')) {
- throw e;
- }
- });
- });
- }
- });
- });
- it('handles legacy response', function() {
- var rawResponse = {sessionId: 's123', status: 0, value: {name: 'Bob'}};
- send.returns(Promise.resolve(
- new http.Response(200, {}, JSON.stringify(rawResponse))));
- assert.ok(!executor.w3c);
- return executor.execute(command).then(function(response) {
- assert.ok(response instanceof Session);
- assert.equal(response.getId(), 's123');
- let caps = response.getCapabilities();
- assert.ok(caps instanceof Capabilities);
- assert.equal(caps.get('name'), 'Bob');
- assert.ok(!executor.w3c);
- });
- });
- it('auto-upgrades on W3C response', function() {
- let rawResponse = {
- value: {
- sessionId: 's123',
- value: {
- name: 'Bob'
- }
- }
- };
- send.returns(Promise.resolve(
- new http.Response(200, {}, JSON.stringify(rawResponse))));
- assert.ok(!executor.w3c);
- return executor.execute(command).then(function(response) {
- assert.ok(response instanceof Session);
- assert.equal(response.getId(), 's123');
- let caps = response.getCapabilities();
- assert.ok(caps instanceof Capabilities);
- assert.equal(caps.get('name'), 'Bob');
- assert.ok(executor.w3c);
- });
- });
- it('if w3c, does not downgrade on legacy response', function() {
- var rawResponse = {sessionId: 's123', status: 0, value: null};
- send.returns(Promise.resolve(
- new http.Response(200, {}, JSON.stringify(rawResponse))));
- executor.w3c = true;
- return executor.execute(command).then(function(response) {
- assert.ok(response instanceof Session);
- assert.equal(response.getId(), 's123');
- assert.equal(response.getCapabilities().size, 0);
- assert.ok(executor.w3c, 'should never downgrade');
- });
- });
- it('handles legacy new session failures', function() {
- let rawResponse = {
- status: error.ErrorCode.NO_SUCH_ELEMENT,
- value: {message: 'hi'}
- };
- send.returns(Promise.resolve(
- new http.Response(500, {}, JSON.stringify(rawResponse))));
- return executor.execute(command)
- .then(() => assert.fail('should have failed'),
- e => {
- assert.ok(e instanceof error.NoSuchElementError);
- assert.equal(e.message, 'hi');
- });
- });
- it('handles w3c new session failures', function() {
- let rawResponse =
- {value: {error: 'no such element', message: 'oops'}};
- send.returns(Promise.resolve(
- new http.Response(500, {}, JSON.stringify(rawResponse))));
- return executor.execute(command)
- .then(() => assert.fail('should have failed'),
- e => {
- assert.ok(e instanceof error.NoSuchElementError);
- assert.equal(e.message, 'oops');
- });
- });
- });
- describe('extracts Session from DESCRIBE_SESSION response', function() {
- let command;
- beforeEach(function() {
- executor = new http.Executor(client);
- command = new Command(CommandName.DESCRIBE_SESSION)
- .setParameter('sessionId', 'foo');
- });
- describe('fails if server returns invalid response', function() {
- describe('(empty response)', function() {
- test(true);
- test(false);
- function test(w3c) {
- it('w3c === ' + w3c, function() {
- send.returns(Promise.resolve(new http.Response(200, {}, '')));
- executor.w3c = w3c;
- return executor.execute(command).then(
- () => assert.fail('expected to fail'),
- (e) => {
- if (!e.message.startsWith('Unable to parse')) {
- throw e;
- }
- });
- });
- }
- });
- describe('(no session ID)', function() {
- test(true);
- test(false);
- function test(w3c) {
- it('w3c === ' + w3c, function() {
- let resp = {value:{name: 'Bob'}};
- send.returns(Promise.resolve(
- new http.Response(200, {}, JSON.stringify(resp))));
- executor.w3c = w3c;
- return executor.execute(command).then(
- () => assert.fail('expected to fail'),
- (e) => {
- if (!e.message.startsWith('Unable to parse')) {
- throw e;
- }
- });
- });
- }
- });
- });
- it('handles legacy response', function() {
- var rawResponse = {sessionId: 's123', status: 0, value: {name: 'Bob'}};
- send.returns(Promise.resolve(
- new http.Response(200, {}, JSON.stringify(rawResponse))));
- assert.ok(!executor.w3c);
- return executor.execute(command).then(function(response) {
- assert.ok(response instanceof Session);
- assert.equal(response.getId(), 's123');
- let caps = response.getCapabilities();
- assert.ok(caps instanceof Capabilities);
- assert.equal(caps.get('name'), 'Bob');
- assert.ok(!executor.w3c);
- });
- });
- it('does not auto-upgrade on W3C response', function() {
- var rawResponse = {value: {sessionId: 's123', value: {name: 'Bob'}}};
- send.returns(Promise.resolve(
- new http.Response(200, {}, JSON.stringify(rawResponse))));
- assert.ok(!executor.w3c);
- return executor.execute(command).then(function(response) {
- assert.ok(response instanceof Session);
- assert.equal(response.getId(), 's123');
- let caps = response.getCapabilities();
- assert.ok(caps instanceof Capabilities);
- assert.equal(caps.get('name'), 'Bob');
- assert.ok(!executor.w3c);
- });
- });
- it('if w3c, does not downgrade on legacy response', function() {
- var rawResponse = {sessionId: 's123', status: 0, value: null};
- send.returns(Promise.resolve(
- new http.Response(200, {}, JSON.stringify(rawResponse))));
- executor.w3c = true;
- return executor.execute(command).then(function(response) {
- assert.ok(response instanceof Session);
- assert.equal(response.getId(), 's123');
- assert.equal(response.getCapabilities().size, 0);
- assert.ok(executor.w3c, 'should never downgrade');
- });
- });
- });
- it('handles JSON null', function() {
- var command = new Command(CommandName.GET_CURRENT_URL)
- .setParameter('sessionId', 's123');
- send.returns(Promise.resolve(new http.Response(200, {}, 'null')));
- return executor.execute(command).then(function(response) {
- assertSent('GET', '/session/s123/url', {},
- [['Accept', 'application/json; charset=utf-8']]);
- assert.strictEqual(response, null);
- });
- });
- describe('falsy values', function() {
- test(0);
- test(false);
- test('');
- function test(value) {
- it(`value=${value}`, function() {
- var command = new Command(CommandName.GET_CURRENT_URL)
- .setParameter('sessionId', 's123');
- send.returns(Promise.resolve(
- new http.Response(200, {},
- JSON.stringify({status: 0, value: value}))));
- return executor.execute(command).then(function(response) {
- assertSent('GET', '/session/s123/url', {},
- [['Accept', 'application/json; charset=utf-8']]);
- assert.strictEqual(response, value);
- });
- });
- }
- });
- it('handles non-object JSON', function() {
- var command = new Command(CommandName.GET_CURRENT_URL)
- .setParameter('sessionId', 's123');
- send.returns(Promise.resolve(new http.Response(200, {}, '123')));
- return executor.execute(command).then(function(response) {
- assertSent('GET', '/session/s123/url', {},
- [['Accept', 'application/json; charset=utf-8']]);
- assert.strictEqual(response, 123);
- });
- });
- it('returns body text when 2xx but not JSON', function() {
- var command = new Command(CommandName.GET_CURRENT_URL)
- .setParameter('sessionId', 's123');
- send.returns(Promise.resolve(
- new http.Response(200, {}, 'hello, world\r\ngoodbye, world!')));
- return executor.execute(command).then(function(response) {
- assertSent('GET', '/session/s123/url', {},
- [['Accept', 'application/json; charset=utf-8']]);
- assert.strictEqual(response, 'hello, world\ngoodbye, world!');
- });
- });
- it('returns body text when 2xx but invalid JSON', function() {
- var command = new Command(CommandName.GET_CURRENT_URL)
- .setParameter('sessionId', 's123');
- send.returns(Promise.resolve(
- new http.Response(200, {}, '[')));
- return executor.execute(command).then(function(response) {
- assertSent('GET', '/session/s123/url', {},
- [['Accept', 'application/json; charset=utf-8']]);
- assert.strictEqual(response, '[');
- });
- });
- it('returns null if no body text and 2xx', function() {
- var command = new Command(CommandName.GET_CURRENT_URL)
- .setParameter('sessionId', 's123');
- send.returns(Promise.resolve(new http.Response(200, {}, '')));
- return executor.execute(command).then(function(response) {
- assertSent('GET', '/session/s123/url', {},
- [['Accept', 'application/json; charset=utf-8']]);
- assert.strictEqual(response, null);
- });
- });
- it('returns normalized body text when 2xx but not JSON', function() {
- var command = new Command(CommandName.GET_CURRENT_URL)
- .setParameter('sessionId', 's123');
- send.returns(Promise.resolve(new http.Response(200, {}, '\r\n\n\n\r\n')));
- return executor.execute(command).then(function(response) {
- assertSent('GET', '/session/s123/url', {},
- [['Accept', 'application/json; charset=utf-8']]);
- assert.strictEqual(response, '\n\n\n\n');
- });
- });
- it('throws UnsupportedOperationError for 404 and body not JSON',
- function() {
- var command = new Command(CommandName.GET_CURRENT_URL)
- .setParameter('sessionId', 's123');
- send.returns(Promise.resolve(
- new http.Response(404, {}, 'hello, world\r\ngoodbye, world!')));
- return executor.execute(command)
- .then(
- () => assert.fail('should have failed'),
- checkError(
- error.UnsupportedOperationError,
- 'hello, world\ngoodbye, world!'));
- });
- it('throws WebDriverError for generic 4xx when body not JSON',
- function() {
- var command = new Command(CommandName.GET_CURRENT_URL)
- .setParameter('sessionId', 's123');
- send.returns(Promise.resolve(
- new http.Response(500, {}, 'hello, world\r\ngoodbye, world!')));
- return executor.execute(command)
- .then(
- () => assert.fail('should have failed'),
- checkError(
- error.WebDriverError,
- 'hello, world\ngoodbye, world!'))
- .then(function() {
- assertSent('GET', '/session/s123/url', {},
- [['Accept', 'application/json; charset=utf-8']]);
- });
- });
- });
- it('canDefineNewCommands', function() {
- executor.defineCommand('greet', 'GET', '/person/:name');
- var command = new Command('greet').
- setParameter('name', 'Bob');
- send.returns(Promise.resolve(new http.Response(200, {}, '')));
- return assertSendsSuccessfully(command).then(function(response) {
- assertSent('GET', '/person/Bob', {},
- [['Accept', 'application/json; charset=utf-8']]);
- });
- });
- it('canRedefineStandardCommands', function() {
- executor.defineCommand(CommandName.GO_BACK, 'POST', '/custom/back');
- var command = new Command(CommandName.GO_BACK).
- setParameter('times', 3);
- send.returns(Promise.resolve(new http.Response(200, {}, '')));
- return assertSendsSuccessfully(command).then(function(response) {
- assertSent('POST', '/custom/back', {'times': 3},
- [['Accept', 'application/json; charset=utf-8']]);
- });
- });
- it('accepts promised http clients', function() {
- executor = new http.Executor(Promise.resolve(client));
- var resp = JSON.stringify({sessionId: 'abc123'});
- send.returns(Promise.resolve(new http.Response(200, {}, resp)));
- let command = new Command(CommandName.NEW_SESSION);
- return executor.execute(command).then(response => {
- assertSent(
- 'POST', '/session', {},
- [['Accept', 'application/json; charset=utf-8']]);
- });
- });
- function entries(map) {
- let entries = [];
- for (let e of map.entries()) {
- entries.push(e);
- }
- return entries;
- }
- function checkError(type, message) {
- return function(e) {
- if (e instanceof type) {
- assert.strictEqual(e.message, message);
- } else {
- throw e;
- }
- };
- }
- function assertSent(method, path, data, headers) {
- assert.ok(send.calledWith(sinon.match(function(value) {
- assert.equal(value.method, method);
- assert.equal(value.path, path);
- assert.deepEqual(value.data, data);
- assert.deepEqual(entries(value.headers), headers);
- return true;
- })));
- }
- function assertSendsSuccessfully(command) {
- return executor.execute(command).then(function(response) {
- return response;
- });
- }
- function assertFailsToSend(command, opt_onError) {
- return executor.execute(command).then(
- () => {throw Error('should have failed')},
- (e) => {return e});
- }
- });
- });
|