http_test.js 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696
  1. // Licensed to the Software Freedom Conservancy (SFC) under one
  2. // or more contributor license agreements. See the NOTICE file
  3. // distributed with this work for additional information
  4. // regarding copyright ownership. The SFC licenses this file
  5. // to you under the Apache License, Version 2.0 (the
  6. // "License"); you may not use this file except in compliance
  7. // with the License. You may obtain a copy of the License at
  8. //
  9. // http://www.apache.org/licenses/LICENSE-2.0
  10. //
  11. // Unless required by applicable law or agreed to in writing,
  12. // software distributed under the License is distributed on an
  13. // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  14. // KIND, either express or implied. See the License for the
  15. // specific language governing permissions and limitations
  16. // under the License.
  17. 'use strict';
  18. var assert = require('assert'),
  19. sinon = require('sinon');
  20. var Capabilities = require('../../lib/capabilities').Capabilities,
  21. Command = require('../../lib/command').Command,
  22. CommandName = require('../../lib/command').Name,
  23. error = require('../../lib/error'),
  24. http = require('../../lib/http'),
  25. Session = require('../../lib/session').Session,
  26. promise = require('../../lib/promise'),
  27. WebElement = require('../../lib/webdriver').WebElement;
  28. describe('http', function() {
  29. describe('buildPath', function() {
  30. it('properly replaces path segments with command parameters', function() {
  31. var parameters = {'sessionId':'foo', 'url':'http://www.google.com'};
  32. var finalPath = http.buildPath('/session/:sessionId/url', parameters);
  33. assert.equal(finalPath, '/session/foo/url');
  34. assert.deepEqual(parameters, {'url':'http://www.google.com'});
  35. });
  36. it('handles web element references', function() {
  37. var parameters = {'sessionId':'foo', 'id': WebElement.buildId('bar')};
  38. var finalPath = http.buildPath(
  39. '/session/:sessionId/element/:id/click', parameters);
  40. assert.equal(finalPath, '/session/foo/element/bar/click');
  41. assert.deepEqual(parameters, {});
  42. });
  43. it('throws if missing a parameter', function() {
  44. assert.throws(
  45. () => http.buildPath('/session/:sessionId', {}),
  46. function(err) {
  47. return err instanceof error.InvalidArgumentError
  48. && 'Missing required parameter: sessionId' === err.message;
  49. });
  50. assert.throws(
  51. () => http.buildPath(
  52. '/session/:sessionId/element/:id', {'sessionId': 'foo'}),
  53. function(err) {
  54. return err instanceof error.InvalidArgumentError
  55. && 'Missing required parameter: id' === err.message;
  56. });
  57. });
  58. it('does not match on segments that do not start with a colon', function() {
  59. assert.equal(
  60. http.buildPath('/session/foo:bar/baz', {}),
  61. '/session/foo:bar/baz');
  62. });
  63. });
  64. describe('Executor', function() {
  65. let executor;
  66. let client;
  67. let send;
  68. beforeEach(function setUp() {
  69. client = new http.Client;
  70. send = sinon.stub(client, 'send');
  71. executor = new http.Executor(client);
  72. });
  73. describe('command routing', function() {
  74. it('rejects unrecognized commands', function() {
  75. return executor.execute(new Command('fake-name'))
  76. .then(assert.fail, err => {
  77. if (err instanceof error.UnknownCommandError
  78. && 'Unrecognized command: fake-name' === err.message) {
  79. return;
  80. }
  81. throw err;
  82. })
  83. });
  84. it('rejects promise if client fails to send request', function() {
  85. let error = new Error('boom');
  86. send.returns(Promise.reject(error));
  87. return assertFailsToSend(new Command(CommandName.NEW_SESSION))
  88. .then(function(e) {
  89. assert.strictEqual(error, e);
  90. assertSent(
  91. 'POST', '/session', {},
  92. [['Accept', 'application/json; charset=utf-8']]);
  93. });
  94. });
  95. it('can execute commands with no URL parameters', function() {
  96. var resp = JSON.stringify({sessionId: 'abc123'});
  97. send.returns(Promise.resolve(new http.Response(200, {}, resp)));
  98. let command = new Command(CommandName.NEW_SESSION);
  99. return assertSendsSuccessfully(command).then(function(response) {
  100. assertSent(
  101. 'POST', '/session', {},
  102. [['Accept', 'application/json; charset=utf-8']]);
  103. });
  104. });
  105. it('rejects commands missing URL parameters', function() {
  106. let command =
  107. new Command(CommandName.FIND_CHILD_ELEMENT).
  108. setParameter('sessionId', 's123').
  109. // Let this be missing: setParameter('id', {'ELEMENT': 'e456'}).
  110. setParameter('using', 'id').
  111. setParameter('value', 'foo');
  112. assert.throws(
  113. () => executor.execute(command),
  114. function(err) {
  115. return err instanceof error.InvalidArgumentError
  116. && 'Missing required parameter: id' === err.message;
  117. });
  118. assert.ok(!send.called);
  119. });
  120. it('replaces URL parameters with command parameters', function() {
  121. var command = new Command(CommandName.GET).
  122. setParameter('sessionId', 's123').
  123. setParameter('url', 'http://www.google.com');
  124. send.returns(Promise.resolve(new http.Response(200, {}, '')));
  125. return assertSendsSuccessfully(command).then(function(response) {
  126. assertSent(
  127. 'POST', '/session/s123/url', {'url': 'http://www.google.com'},
  128. [['Accept', 'application/json; charset=utf-8']]);
  129. });
  130. });
  131. describe('uses correct URL', function() {
  132. beforeEach(() => executor = new http.Executor(client));
  133. describe('in legacy mode', function() {
  134. test(CommandName.GET_WINDOW_SIZE, {sessionId:'s123'}, false,
  135. 'GET', '/session/s123/window/current/size');
  136. test(CommandName.SET_WINDOW_SIZE,
  137. {sessionId:'s123', width: 1, height: 1}, false,
  138. 'POST', '/session/s123/window/current/size',
  139. {width: 1, height: 1});
  140. test(CommandName.MAXIMIZE_WINDOW, {sessionId:'s123'}, false,
  141. 'POST', '/session/s123/window/current/maximize');
  142. // This is consistent b/w legacy and W3C, just making sure.
  143. test(CommandName.GET,
  144. {sessionId:'s123', url: 'http://www.example.com'}, false,
  145. 'POST', '/session/s123/url', {url: 'http://www.example.com'});
  146. });
  147. describe('in W3C mode', function() {
  148. test(CommandName.GET_WINDOW_SIZE,
  149. {sessionId:'s123'}, true,
  150. 'GET', '/session/s123/window/size');
  151. test(CommandName.SET_WINDOW_SIZE,
  152. {sessionId:'s123', width: 1, height: 1}, true,
  153. 'POST', '/session/s123/window/size', {width: 1, height: 1});
  154. test(CommandName.MAXIMIZE_WINDOW, {sessionId:'s123'}, true,
  155. 'POST', '/session/s123/window/maximize');
  156. // This is consistent b/w legacy and W3C, just making sure.
  157. test(CommandName.GET,
  158. {sessionId:'s123', url: 'http://www.example.com'}, true,
  159. 'POST', '/session/s123/url', {url: 'http://www.example.com'});
  160. });
  161. function test(command, parameters, w3c,
  162. expectedMethod, expectedUrl, opt_expectedParams) {
  163. it(`command=${command}`, function() {
  164. var resp = JSON.stringify({sessionId: 'abc123'});
  165. send.returns(Promise.resolve(new http.Response(200, {}, resp)));
  166. let cmd = new Command(command).setParameters(parameters);
  167. executor.w3c = w3c;
  168. return executor.execute(cmd).then(function() {
  169. assertSent(
  170. expectedMethod, expectedUrl, opt_expectedParams || {},
  171. [['Accept', 'application/json; charset=utf-8']]);
  172. });
  173. });
  174. }
  175. });
  176. });
  177. describe('response parsing', function() {
  178. it('extracts value from JSON response', function() {
  179. var responseObj = {
  180. 'status': error.ErrorCode.SUCCESS,
  181. 'value': 'http://www.google.com'
  182. };
  183. var command = new Command(CommandName.GET_CURRENT_URL)
  184. .setParameter('sessionId', 's123');
  185. send.returns(Promise.resolve(
  186. new http.Response(200, {}, JSON.stringify(responseObj))));
  187. return executor.execute(command).then(function(response) {
  188. assertSent('GET', '/session/s123/url', {},
  189. [['Accept', 'application/json; charset=utf-8']]);
  190. assert.strictEqual(response, 'http://www.google.com');
  191. });
  192. });
  193. describe('extracts Session from NEW_SESSION response', function() {
  194. beforeEach(() => executor = new http.Executor(client));
  195. const command = new Command(CommandName.NEW_SESSION);
  196. describe('fails if server returns invalid response', function() {
  197. describe('(empty response)', function() {
  198. test(true);
  199. test(false);
  200. function test(w3c) {
  201. it('w3c === ' + w3c, function() {
  202. send.returns(Promise.resolve(new http.Response(200, {}, '')));
  203. executor.w3c = w3c;
  204. return executor.execute(command).then(
  205. () => assert.fail('expected to fail'),
  206. (e) => {
  207. if (!e.message.startsWith('Unable to parse')) {
  208. throw e;
  209. }
  210. });
  211. });
  212. }
  213. });
  214. describe('(no session ID)', function() {
  215. test(true);
  216. test(false);
  217. function test(w3c) {
  218. it('w3c === ' + w3c, function() {
  219. let resp = {value:{name: 'Bob'}};
  220. send.returns(Promise.resolve(
  221. new http.Response(200, {}, JSON.stringify(resp))));
  222. executor.w3c = w3c;
  223. return executor.execute(command).then(
  224. () => assert.fail('expected to fail'),
  225. (e) => {
  226. if (!e.message.startsWith('Unable to parse')) {
  227. throw e;
  228. }
  229. });
  230. });
  231. }
  232. });
  233. });
  234. it('handles legacy response', function() {
  235. var rawResponse = {sessionId: 's123', status: 0, value: {name: 'Bob'}};
  236. send.returns(Promise.resolve(
  237. new http.Response(200, {}, JSON.stringify(rawResponse))));
  238. assert.ok(!executor.w3c);
  239. return executor.execute(command).then(function(response) {
  240. assert.ok(response instanceof Session);
  241. assert.equal(response.getId(), 's123');
  242. let caps = response.getCapabilities();
  243. assert.ok(caps instanceof Capabilities);
  244. assert.equal(caps.get('name'), 'Bob');
  245. assert.ok(!executor.w3c);
  246. });
  247. });
  248. it('auto-upgrades on W3C response', function() {
  249. let rawResponse = {
  250. value: {
  251. sessionId: 's123',
  252. value: {
  253. name: 'Bob'
  254. }
  255. }
  256. };
  257. send.returns(Promise.resolve(
  258. new http.Response(200, {}, JSON.stringify(rawResponse))));
  259. assert.ok(!executor.w3c);
  260. return executor.execute(command).then(function(response) {
  261. assert.ok(response instanceof Session);
  262. assert.equal(response.getId(), 's123');
  263. let caps = response.getCapabilities();
  264. assert.ok(caps instanceof Capabilities);
  265. assert.equal(caps.get('name'), 'Bob');
  266. assert.ok(executor.w3c);
  267. });
  268. });
  269. it('if w3c, does not downgrade on legacy response', function() {
  270. var rawResponse = {sessionId: 's123', status: 0, value: null};
  271. send.returns(Promise.resolve(
  272. new http.Response(200, {}, JSON.stringify(rawResponse))));
  273. executor.w3c = true;
  274. return executor.execute(command).then(function(response) {
  275. assert.ok(response instanceof Session);
  276. assert.equal(response.getId(), 's123');
  277. assert.equal(response.getCapabilities().size, 0);
  278. assert.ok(executor.w3c, 'should never downgrade');
  279. });
  280. });
  281. it('handles legacy new session failures', function() {
  282. let rawResponse = {
  283. status: error.ErrorCode.NO_SUCH_ELEMENT,
  284. value: {message: 'hi'}
  285. };
  286. send.returns(Promise.resolve(
  287. new http.Response(500, {}, JSON.stringify(rawResponse))));
  288. return executor.execute(command)
  289. .then(() => assert.fail('should have failed'),
  290. e => {
  291. assert.ok(e instanceof error.NoSuchElementError);
  292. assert.equal(e.message, 'hi');
  293. });
  294. });
  295. it('handles w3c new session failures', function() {
  296. let rawResponse =
  297. {value: {error: 'no such element', message: 'oops'}};
  298. send.returns(Promise.resolve(
  299. new http.Response(500, {}, JSON.stringify(rawResponse))));
  300. return executor.execute(command)
  301. .then(() => assert.fail('should have failed'),
  302. e => {
  303. assert.ok(e instanceof error.NoSuchElementError);
  304. assert.equal(e.message, 'oops');
  305. });
  306. });
  307. });
  308. describe('extracts Session from DESCRIBE_SESSION response', function() {
  309. let command;
  310. beforeEach(function() {
  311. executor = new http.Executor(client);
  312. command = new Command(CommandName.DESCRIBE_SESSION)
  313. .setParameter('sessionId', 'foo');
  314. });
  315. describe('fails if server returns invalid response', function() {
  316. describe('(empty response)', function() {
  317. test(true);
  318. test(false);
  319. function test(w3c) {
  320. it('w3c === ' + w3c, function() {
  321. send.returns(Promise.resolve(new http.Response(200, {}, '')));
  322. executor.w3c = w3c;
  323. return executor.execute(command).then(
  324. () => assert.fail('expected to fail'),
  325. (e) => {
  326. if (!e.message.startsWith('Unable to parse')) {
  327. throw e;
  328. }
  329. });
  330. });
  331. }
  332. });
  333. describe('(no session ID)', function() {
  334. test(true);
  335. test(false);
  336. function test(w3c) {
  337. it('w3c === ' + w3c, function() {
  338. let resp = {value:{name: 'Bob'}};
  339. send.returns(Promise.resolve(
  340. new http.Response(200, {}, JSON.stringify(resp))));
  341. executor.w3c = w3c;
  342. return executor.execute(command).then(
  343. () => assert.fail('expected to fail'),
  344. (e) => {
  345. if (!e.message.startsWith('Unable to parse')) {
  346. throw e;
  347. }
  348. });
  349. });
  350. }
  351. });
  352. });
  353. it('handles legacy response', function() {
  354. var rawResponse = {sessionId: 's123', status: 0, value: {name: 'Bob'}};
  355. send.returns(Promise.resolve(
  356. new http.Response(200, {}, JSON.stringify(rawResponse))));
  357. assert.ok(!executor.w3c);
  358. return executor.execute(command).then(function(response) {
  359. assert.ok(response instanceof Session);
  360. assert.equal(response.getId(), 's123');
  361. let caps = response.getCapabilities();
  362. assert.ok(caps instanceof Capabilities);
  363. assert.equal(caps.get('name'), 'Bob');
  364. assert.ok(!executor.w3c);
  365. });
  366. });
  367. it('does not auto-upgrade on W3C response', function() {
  368. var rawResponse = {value: {sessionId: 's123', value: {name: 'Bob'}}};
  369. send.returns(Promise.resolve(
  370. new http.Response(200, {}, JSON.stringify(rawResponse))));
  371. assert.ok(!executor.w3c);
  372. return executor.execute(command).then(function(response) {
  373. assert.ok(response instanceof Session);
  374. assert.equal(response.getId(), 's123');
  375. let caps = response.getCapabilities();
  376. assert.ok(caps instanceof Capabilities);
  377. assert.equal(caps.get('name'), 'Bob');
  378. assert.ok(!executor.w3c);
  379. });
  380. });
  381. it('if w3c, does not downgrade on legacy response', function() {
  382. var rawResponse = {sessionId: 's123', status: 0, value: null};
  383. send.returns(Promise.resolve(
  384. new http.Response(200, {}, JSON.stringify(rawResponse))));
  385. executor.w3c = true;
  386. return executor.execute(command).then(function(response) {
  387. assert.ok(response instanceof Session);
  388. assert.equal(response.getId(), 's123');
  389. assert.equal(response.getCapabilities().size, 0);
  390. assert.ok(executor.w3c, 'should never downgrade');
  391. });
  392. });
  393. });
  394. it('handles JSON null', function() {
  395. var command = new Command(CommandName.GET_CURRENT_URL)
  396. .setParameter('sessionId', 's123');
  397. send.returns(Promise.resolve(new http.Response(200, {}, 'null')));
  398. return executor.execute(command).then(function(response) {
  399. assertSent('GET', '/session/s123/url', {},
  400. [['Accept', 'application/json; charset=utf-8']]);
  401. assert.strictEqual(response, null);
  402. });
  403. });
  404. describe('falsy values', function() {
  405. test(0);
  406. test(false);
  407. test('');
  408. function test(value) {
  409. it(`value=${value}`, function() {
  410. var command = new Command(CommandName.GET_CURRENT_URL)
  411. .setParameter('sessionId', 's123');
  412. send.returns(Promise.resolve(
  413. new http.Response(200, {},
  414. JSON.stringify({status: 0, value: value}))));
  415. return executor.execute(command).then(function(response) {
  416. assertSent('GET', '/session/s123/url', {},
  417. [['Accept', 'application/json; charset=utf-8']]);
  418. assert.strictEqual(response, value);
  419. });
  420. });
  421. }
  422. });
  423. it('handles non-object JSON', function() {
  424. var command = new Command(CommandName.GET_CURRENT_URL)
  425. .setParameter('sessionId', 's123');
  426. send.returns(Promise.resolve(new http.Response(200, {}, '123')));
  427. return executor.execute(command).then(function(response) {
  428. assertSent('GET', '/session/s123/url', {},
  429. [['Accept', 'application/json; charset=utf-8']]);
  430. assert.strictEqual(response, 123);
  431. });
  432. });
  433. it('returns body text when 2xx but not JSON', function() {
  434. var command = new Command(CommandName.GET_CURRENT_URL)
  435. .setParameter('sessionId', 's123');
  436. send.returns(Promise.resolve(
  437. new http.Response(200, {}, 'hello, world\r\ngoodbye, world!')));
  438. return executor.execute(command).then(function(response) {
  439. assertSent('GET', '/session/s123/url', {},
  440. [['Accept', 'application/json; charset=utf-8']]);
  441. assert.strictEqual(response, 'hello, world\ngoodbye, world!');
  442. });
  443. });
  444. it('returns body text when 2xx but invalid JSON', function() {
  445. var command = new Command(CommandName.GET_CURRENT_URL)
  446. .setParameter('sessionId', 's123');
  447. send.returns(Promise.resolve(
  448. new http.Response(200, {}, '[')));
  449. return executor.execute(command).then(function(response) {
  450. assertSent('GET', '/session/s123/url', {},
  451. [['Accept', 'application/json; charset=utf-8']]);
  452. assert.strictEqual(response, '[');
  453. });
  454. });
  455. it('returns null if no body text and 2xx', function() {
  456. var command = new Command(CommandName.GET_CURRENT_URL)
  457. .setParameter('sessionId', 's123');
  458. send.returns(Promise.resolve(new http.Response(200, {}, '')));
  459. return executor.execute(command).then(function(response) {
  460. assertSent('GET', '/session/s123/url', {},
  461. [['Accept', 'application/json; charset=utf-8']]);
  462. assert.strictEqual(response, null);
  463. });
  464. });
  465. it('returns normalized body text when 2xx but not JSON', function() {
  466. var command = new Command(CommandName.GET_CURRENT_URL)
  467. .setParameter('sessionId', 's123');
  468. send.returns(Promise.resolve(new http.Response(200, {}, '\r\n\n\n\r\n')));
  469. return executor.execute(command).then(function(response) {
  470. assertSent('GET', '/session/s123/url', {},
  471. [['Accept', 'application/json; charset=utf-8']]);
  472. assert.strictEqual(response, '\n\n\n\n');
  473. });
  474. });
  475. it('throws UnsupportedOperationError for 404 and body not JSON',
  476. function() {
  477. var command = new Command(CommandName.GET_CURRENT_URL)
  478. .setParameter('sessionId', 's123');
  479. send.returns(Promise.resolve(
  480. new http.Response(404, {}, 'hello, world\r\ngoodbye, world!')));
  481. return executor.execute(command)
  482. .then(
  483. () => assert.fail('should have failed'),
  484. checkError(
  485. error.UnsupportedOperationError,
  486. 'hello, world\ngoodbye, world!'));
  487. });
  488. it('throws WebDriverError for generic 4xx when body not JSON',
  489. function() {
  490. var command = new Command(CommandName.GET_CURRENT_URL)
  491. .setParameter('sessionId', 's123');
  492. send.returns(Promise.resolve(
  493. new http.Response(500, {}, 'hello, world\r\ngoodbye, world!')));
  494. return executor.execute(command)
  495. .then(
  496. () => assert.fail('should have failed'),
  497. checkError(
  498. error.WebDriverError,
  499. 'hello, world\ngoodbye, world!'))
  500. .then(function() {
  501. assertSent('GET', '/session/s123/url', {},
  502. [['Accept', 'application/json; charset=utf-8']]);
  503. });
  504. });
  505. });
  506. it('canDefineNewCommands', function() {
  507. executor.defineCommand('greet', 'GET', '/person/:name');
  508. var command = new Command('greet').
  509. setParameter('name', 'Bob');
  510. send.returns(Promise.resolve(new http.Response(200, {}, '')));
  511. return assertSendsSuccessfully(command).then(function(response) {
  512. assertSent('GET', '/person/Bob', {},
  513. [['Accept', 'application/json; charset=utf-8']]);
  514. });
  515. });
  516. it('canRedefineStandardCommands', function() {
  517. executor.defineCommand(CommandName.GO_BACK, 'POST', '/custom/back');
  518. var command = new Command(CommandName.GO_BACK).
  519. setParameter('times', 3);
  520. send.returns(Promise.resolve(new http.Response(200, {}, '')));
  521. return assertSendsSuccessfully(command).then(function(response) {
  522. assertSent('POST', '/custom/back', {'times': 3},
  523. [['Accept', 'application/json; charset=utf-8']]);
  524. });
  525. });
  526. it('accepts promised http clients', function() {
  527. executor = new http.Executor(Promise.resolve(client));
  528. var resp = JSON.stringify({sessionId: 'abc123'});
  529. send.returns(Promise.resolve(new http.Response(200, {}, resp)));
  530. let command = new Command(CommandName.NEW_SESSION);
  531. return executor.execute(command).then(response => {
  532. assertSent(
  533. 'POST', '/session', {},
  534. [['Accept', 'application/json; charset=utf-8']]);
  535. });
  536. });
  537. function entries(map) {
  538. let entries = [];
  539. for (let e of map.entries()) {
  540. entries.push(e);
  541. }
  542. return entries;
  543. }
  544. function checkError(type, message) {
  545. return function(e) {
  546. if (e instanceof type) {
  547. assert.strictEqual(e.message, message);
  548. } else {
  549. throw e;
  550. }
  551. };
  552. }
  553. function assertSent(method, path, data, headers) {
  554. assert.ok(send.calledWith(sinon.match(function(value) {
  555. assert.equal(value.method, method);
  556. assert.equal(value.path, path);
  557. assert.deepEqual(value.data, data);
  558. assert.deepEqual(entries(value.headers), headers);
  559. return true;
  560. })));
  561. }
  562. function assertSendsSuccessfully(command) {
  563. return executor.execute(command).then(function(response) {
  564. return response;
  565. });
  566. }
  567. function assertFailsToSend(command, opt_onError) {
  568. return executor.execute(command).then(
  569. () => {throw Error('should have failed')},
  570. (e) => {return e});
  571. }
  572. });
  573. });