xhrio.js 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907
  1. // Copyright 2007 The Closure Library Authors. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS-IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. /**
  15. * @fileoverview Mock of XhrIo for unit testing.
  16. */
  17. goog.setTestOnly('goog.testing.net.XhrIo');
  18. goog.provide('goog.testing.net.XhrIo');
  19. goog.require('goog.array');
  20. goog.require('goog.dom.xml');
  21. goog.require('goog.events');
  22. goog.require('goog.events.EventTarget');
  23. goog.require('goog.json');
  24. goog.require('goog.net.ErrorCode');
  25. goog.require('goog.net.EventType');
  26. goog.require('goog.net.HttpStatus');
  27. goog.require('goog.net.XhrIo');
  28. goog.require('goog.net.XmlHttp');
  29. goog.require('goog.object');
  30. goog.require('goog.structs');
  31. goog.require('goog.structs.Map');
  32. goog.require('goog.testing.TestQueue');
  33. goog.require('goog.uri.utils');
  34. /**
  35. * Mock implementation of goog.net.XhrIo. This doesn't provide a mock
  36. * implementation for all cases, but it's not too hard to add them as needed.
  37. * @param {goog.testing.TestQueue=} opt_testQueue Test queue for inserting test
  38. * events.
  39. * @constructor
  40. * @extends {goog.events.EventTarget}
  41. */
  42. goog.testing.net.XhrIo = function(opt_testQueue) {
  43. goog.events.EventTarget.call(this);
  44. /**
  45. * Map of default headers to add to every request, use:
  46. * XhrIo.headers.set(name, value)
  47. * @type {!goog.structs.Map}
  48. */
  49. this.headers = new goog.structs.Map();
  50. /**
  51. * Queue of events write to.
  52. * @type {goog.testing.TestQueue?}
  53. * @private
  54. */
  55. this.testQueue_ = opt_testQueue || null;
  56. };
  57. goog.inherits(goog.testing.net.XhrIo, goog.events.EventTarget);
  58. /**
  59. * To emulate the behavior of the actual XhrIo, we do not allow access to the
  60. * XhrIo's properties outside the event callbacks. For backwards compatibility,
  61. * we allow tests to allow access by setting this value to true.
  62. * @type {boolean}
  63. */
  64. goog.testing.net.XhrIo.allowUnsafeAccessToXhrIoOutsideCallbacks = false;
  65. /**
  66. * Alias this enum here to make mocking of goog.net.XhrIo easier.
  67. * @enum {string}
  68. */
  69. goog.testing.net.XhrIo.ResponseType = goog.net.XhrIo.ResponseType;
  70. /**
  71. * The pattern matching the 'http' and 'https' URI schemes.
  72. * @private {!RegExp}
  73. */
  74. goog.testing.net.XhrIo.HTTP_SCHEME_PATTERN_ = /^https?$/i;
  75. /**
  76. * All non-disposed instances of goog.testing.net.XhrIo created
  77. * by {@link goog.testing.net.XhrIo.send} are in this Array.
  78. * @see goog.testing.net.XhrIo.cleanup
  79. * @type {!Array<!goog.testing.net.XhrIo>}
  80. * @private
  81. */
  82. goog.testing.net.XhrIo.sendInstances_ = [];
  83. /**
  84. * Returns an Array containing all non-disposed instances of
  85. * goog.testing.net.XhrIo created by {@link goog.testing.net.XhrIo.send}.
  86. * @return {!Array<!goog.testing.net.XhrIo>} Array of goog.testing.net.XhrIo
  87. * instances.
  88. */
  89. goog.testing.net.XhrIo.getSendInstances = function() {
  90. return goog.testing.net.XhrIo.sendInstances_;
  91. };
  92. /**
  93. * Disposes all non-disposed instances of goog.testing.net.XhrIo created by
  94. * {@link goog.testing.net.XhrIo.send}.
  95. * @see goog.net.XhrIo.cleanup
  96. */
  97. goog.testing.net.XhrIo.cleanup = function() {
  98. var instances = goog.testing.net.XhrIo.sendInstances_;
  99. while (instances.length) {
  100. instances.pop().dispose();
  101. }
  102. };
  103. /**
  104. * Simulates the static XhrIo send method.
  105. * @param {string} url Uri to make request to.
  106. * @param {Function=} opt_callback Callback function for when request is
  107. * complete.
  108. * @param {string=} opt_method Send method, default: GET.
  109. * @param {string=} opt_content Post data.
  110. * @param {Object|goog.structs.Map=} opt_headers Map of headers to add to the
  111. * request.
  112. * @param {number=} opt_timeoutInterval Number of milliseconds after which an
  113. * incomplete request will be aborted; 0 means no timeout is set.
  114. * @param {boolean=} opt_withCredentials Whether to send credentials with the
  115. * request. Default to false. See {@link goog.net.XhrIo#setWithCredentials}.
  116. * @return {!goog.testing.net.XhrIo} The mocked sent XhrIo.
  117. */
  118. goog.testing.net.XhrIo.send = function(
  119. url, opt_callback, opt_method, opt_content, opt_headers,
  120. opt_timeoutInterval, opt_withCredentials) {
  121. var x = new goog.testing.net.XhrIo();
  122. goog.testing.net.XhrIo.sendInstances_.push(x);
  123. if (opt_callback) {
  124. goog.events.listen(x, goog.net.EventType.COMPLETE, opt_callback);
  125. }
  126. goog.events.listen(
  127. x, goog.net.EventType.READY,
  128. goog.partial(goog.testing.net.XhrIo.cleanupSend_, x));
  129. if (opt_timeoutInterval) {
  130. x.setTimeoutInterval(opt_timeoutInterval);
  131. }
  132. x.setWithCredentials(Boolean(opt_withCredentials));
  133. x.send(url, opt_method, opt_content, opt_headers);
  134. return x;
  135. };
  136. /**
  137. * Disposes of the specified goog.testing.net.XhrIo created by
  138. * {@link goog.testing.net.XhrIo.send} and removes it from
  139. * {@link goog.testing.net.XhrIo.pendingStaticSendInstances_}.
  140. * @param {!goog.testing.net.XhrIo} XhrIo An XhrIo created by
  141. * {@link goog.testing.net.XhrIo.send}.
  142. * @private
  143. */
  144. goog.testing.net.XhrIo.cleanupSend_ = function(XhrIo) {
  145. XhrIo.dispose();
  146. goog.array.remove(goog.testing.net.XhrIo.sendInstances_, XhrIo);
  147. };
  148. /**
  149. * Stores the simulated response headers for the requests which are sent through
  150. * this XhrIo.
  151. * @type {Object}
  152. * @private
  153. */
  154. goog.testing.net.XhrIo.prototype.responseHeaders_;
  155. /**
  156. * Whether MockXhrIo is active.
  157. * @type {boolean}
  158. * @private
  159. */
  160. goog.testing.net.XhrIo.prototype.active_ = false;
  161. /**
  162. * Last URI that was requested.
  163. * @type {string}
  164. * @private
  165. */
  166. goog.testing.net.XhrIo.prototype.lastUri_ = '';
  167. /**
  168. * Last HTTP method that was requested.
  169. * @type {string|undefined}
  170. * @private
  171. */
  172. goog.testing.net.XhrIo.prototype.lastMethod_;
  173. /**
  174. * Last POST content that was requested.
  175. * @type {string|undefined}
  176. * @private
  177. */
  178. goog.testing.net.XhrIo.prototype.lastContent_;
  179. /**
  180. * Additional headers that were requested in the last query.
  181. * @type {Object|goog.structs.Map|undefined}
  182. * @private
  183. */
  184. goog.testing.net.XhrIo.prototype.lastHeaders_;
  185. /**
  186. * Last error code.
  187. * @type {goog.net.ErrorCode}
  188. * @private
  189. */
  190. goog.testing.net.XhrIo.prototype.lastErrorCode_ = goog.net.ErrorCode.NO_ERROR;
  191. /**
  192. * Last error message.
  193. * @type {string}
  194. * @private
  195. */
  196. goog.testing.net.XhrIo.prototype.lastError_ = '';
  197. /**
  198. * The response object.
  199. * @type {string|Document|ArrayBuffer}
  200. * @private
  201. */
  202. goog.testing.net.XhrIo.prototype.response_ = '';
  203. /**
  204. * The status code.
  205. * @type {number}
  206. * @private
  207. */
  208. goog.testing.net.XhrIo.prototype.statusCode_ = 0;
  209. /**
  210. * Mock ready state.
  211. * @type {number}
  212. * @private
  213. */
  214. goog.testing.net.XhrIo.prototype.readyState_ =
  215. goog.net.XmlHttp.ReadyState.UNINITIALIZED;
  216. /**
  217. * Number of milliseconds after which an incomplete request will be aborted and
  218. * a {@link goog.net.EventType.TIMEOUT} event raised; 0 means no timeout is set.
  219. * @type {number}
  220. * @private
  221. */
  222. goog.testing.net.XhrIo.prototype.timeoutInterval_ = 0;
  223. /**
  224. * The requested type for the response. The empty string means use the default
  225. * XHR behavior.
  226. * @type {goog.net.XhrIo.ResponseType}
  227. * @private
  228. */
  229. goog.testing.net.XhrIo.prototype.responseType_ =
  230. goog.net.XhrIo.ResponseType.DEFAULT;
  231. /**
  232. * Whether a "credentialed" request is to be sent (one that is aware of cookies
  233. * and authentication) . This is applicable only for cross-domain requests and
  234. * more recent browsers that support this part of the HTTP Access Control
  235. * standard.
  236. *
  237. * @see http://dev.w3.org/2006/webapi/XMLHttpRequest-2/#withcredentials
  238. *
  239. * @type {boolean}
  240. * @private
  241. */
  242. goog.testing.net.XhrIo.prototype.withCredentials_ = false;
  243. /**
  244. * Whether progress events shall be sent for this request.
  245. *
  246. * @type {boolean}
  247. * @private
  248. */
  249. goog.testing.net.XhrIo.prototype.progressEventsEnabled_ = false;
  250. /**
  251. * Whether there's currently an underlying XHR object.
  252. * @type {boolean}
  253. * @private
  254. */
  255. goog.testing.net.XhrIo.prototype.xhr_ = false;
  256. /**
  257. * Returns the number of milliseconds after which an incomplete request will be
  258. * aborted, or 0 if no timeout is set.
  259. * @return {number} Timeout interval in milliseconds.
  260. */
  261. goog.testing.net.XhrIo.prototype.getTimeoutInterval = function() {
  262. return this.timeoutInterval_;
  263. };
  264. /**
  265. * Sets the number of milliseconds after which an incomplete request will be
  266. * aborted and a {@link goog.net.EventType.TIMEOUT} event raised; 0 means no
  267. * timeout is set.
  268. * @param {number} ms Timeout interval in milliseconds; 0 means none.
  269. */
  270. goog.testing.net.XhrIo.prototype.setTimeoutInterval = function(ms) {
  271. this.timeoutInterval_ = Math.max(0, ms);
  272. };
  273. /**
  274. * Causes timeout events to be fired.
  275. */
  276. goog.testing.net.XhrIo.prototype.simulateTimeout = function() {
  277. this.lastErrorCode_ = goog.net.ErrorCode.TIMEOUT;
  278. this.dispatchEvent(goog.net.EventType.TIMEOUT);
  279. this.abort(goog.net.ErrorCode.TIMEOUT);
  280. };
  281. /**
  282. * Sets the desired type for the response. At time of writing, this is only
  283. * supported in very recent versions of WebKit (10.0.612.1 dev and later).
  284. *
  285. * If this is used, the response may only be accessed via {@link #getResponse}.
  286. *
  287. * @param {goog.net.XhrIo.ResponseType} type The desired type for the response.
  288. */
  289. goog.testing.net.XhrIo.prototype.setResponseType = function(type) {
  290. this.responseType_ = type;
  291. };
  292. /**
  293. * Gets the desired type for the response.
  294. * @return {goog.net.XhrIo.ResponseType} The desired type for the response.
  295. */
  296. goog.testing.net.XhrIo.prototype.getResponseType = function() {
  297. return this.responseType_;
  298. };
  299. /**
  300. * Sets whether a "credentialed" request that is aware of cookie and
  301. * authentication information should be made. This option is only supported by
  302. * browsers that support HTTP Access Control. As of this writing, this option
  303. * is not supported in IE.
  304. *
  305. * @param {boolean} withCredentials Whether this should be a "credentialed"
  306. * request.
  307. */
  308. goog.testing.net.XhrIo.prototype.setWithCredentials = function(
  309. withCredentials) {
  310. this.withCredentials_ = withCredentials;
  311. };
  312. /**
  313. * Gets whether a "credentialed" request is to be sent.
  314. * @return {boolean} The desired type for the response.
  315. */
  316. goog.testing.net.XhrIo.prototype.getWithCredentials = function() {
  317. return this.withCredentials_;
  318. };
  319. /**
  320. * Sets whether progress events are enabled for this request. Note
  321. * that progress events require pre-flight OPTIONS request handling
  322. * for CORS requests, and may cause trouble with older browsers. See
  323. * goog.net.XhrIo.progressEventsEnabled_ for details.
  324. * @param {boolean} enabled Whether progress events should be enabled.
  325. */
  326. goog.testing.net.XhrIo.prototype.setProgressEventsEnabled = function(enabled) {
  327. this.progressEventsEnabled_ = enabled;
  328. };
  329. /**
  330. * Gets whether progress events are enabled.
  331. * @return {boolean} Whether progress events are enabled for this request.
  332. */
  333. goog.testing.net.XhrIo.prototype.getProgressEventsEnabled = function() {
  334. return this.progressEventsEnabled_;
  335. };
  336. /**
  337. * Abort the current XMLHttpRequest
  338. * @param {goog.net.ErrorCode=} opt_failureCode Optional error code to use -
  339. * defaults to ABORT.
  340. */
  341. goog.testing.net.XhrIo.prototype.abort = function(opt_failureCode) {
  342. if (this.active_) {
  343. try {
  344. this.active_ = false;
  345. this.readyState_ = goog.net.XmlHttp.ReadyState.UNINITIALIZED;
  346. this.statusCode_ = -1;
  347. this.lastErrorCode_ = opt_failureCode || goog.net.ErrorCode.ABORT;
  348. this.dispatchEvent(goog.net.EventType.COMPLETE);
  349. this.dispatchEvent(goog.net.EventType.ABORT);
  350. } finally {
  351. this.simulateReady();
  352. }
  353. }
  354. };
  355. /**
  356. * Simulates the XhrIo send.
  357. * @param {string} url Uri to make request too.
  358. * @param {string=} opt_method Send method, default: GET.
  359. * @param {string=} opt_content Post data.
  360. * @param {Object|goog.structs.Map=} opt_headers Map of headers to add to the
  361. * request.
  362. */
  363. goog.testing.net.XhrIo.prototype.send = function(
  364. url, opt_method, opt_content, opt_headers) {
  365. if (this.xhr_) {
  366. throw Error('[goog.net.XhrIo] Object is active with another request');
  367. }
  368. this.lastUri_ = url;
  369. this.lastMethod_ = opt_method || 'GET';
  370. this.lastContent_ = opt_content;
  371. if (!this.headers.isEmpty()) {
  372. this.lastHeaders_ = this.headers.toObject();
  373. // Add headers specific to this request
  374. if (opt_headers) {
  375. goog.structs.forEach(opt_headers, goog.bind(function(value, key) {
  376. this.lastHeaders_[key] = value;
  377. }, this));
  378. }
  379. } else {
  380. this.lastHeaders_ = opt_headers;
  381. }
  382. if (this.testQueue_) {
  383. this.testQueue_.enqueue(['s', url, opt_method, opt_content, opt_headers]);
  384. }
  385. this.xhr_ = true;
  386. this.active_ = true;
  387. this.readyState_ = goog.net.XmlHttp.ReadyState.UNINITIALIZED;
  388. this.simulateReadyStateChange(goog.net.XmlHttp.ReadyState.LOADING);
  389. };
  390. /**
  391. * Creates a new XHR object.
  392. * @return {goog.net.XhrLike.OrNative} The newly created XHR
  393. * object.
  394. * @protected
  395. */
  396. goog.testing.net.XhrIo.prototype.createXhr = function() {
  397. return goog.net.XmlHttp();
  398. };
  399. /**
  400. * Simulates changing to the new ready state.
  401. * @param {number} readyState Ready state to change to.
  402. */
  403. goog.testing.net.XhrIo.prototype.simulateReadyStateChange = function(
  404. readyState) {
  405. if (readyState < this.readyState_) {
  406. throw Error('Readystate cannot go backwards');
  407. }
  408. // INTERACTIVE can be dispatched repeatedly as more data is reported.
  409. if (readyState == goog.net.XmlHttp.ReadyState.INTERACTIVE &&
  410. readyState == this.readyState_) {
  411. this.dispatchEvent(goog.net.EventType.READY_STATE_CHANGE);
  412. return;
  413. }
  414. while (this.readyState_ < readyState) {
  415. this.readyState_++;
  416. this.dispatchEvent(goog.net.EventType.READY_STATE_CHANGE);
  417. if (this.readyState_ == goog.net.XmlHttp.ReadyState.COMPLETE) {
  418. this.active_ = false;
  419. this.dispatchEvent(goog.net.EventType.COMPLETE);
  420. }
  421. }
  422. };
  423. /**
  424. * Simulate receiving some bytes but the request not fully completing, and
  425. * the XHR entering the 'INTERACTIVE' state.
  426. * @param {string} partialResponse A string to append to the response text.
  427. * @param {Object=} opt_headers Simulated response headers.
  428. */
  429. goog.testing.net.XhrIo.prototype.simulatePartialResponse = function(
  430. partialResponse, opt_headers) {
  431. this.response_ += partialResponse;
  432. this.responseHeaders_ = opt_headers || {};
  433. this.statusCode_ = 200;
  434. this.simulateReadyStateChange(goog.net.XmlHttp.ReadyState.INTERACTIVE);
  435. };
  436. /**
  437. * Simulates receiving a response.
  438. * @param {number} statusCode Simulated status code.
  439. * @param {string|Document|ArrayBuffer|null} response Simulated response.
  440. * @param {Object=} opt_headers Simulated response headers.
  441. */
  442. goog.testing.net.XhrIo.prototype.simulateResponse = function(
  443. statusCode, response, opt_headers) {
  444. // This library allows a response to be simulated without send ever being
  445. // called. If there are no send instances, then just pretend that xhr_ and
  446. // active_ have been set to true.
  447. if (!goog.testing.net.XhrIo.allowUnsafeAccessToXhrIoOutsideCallbacks &&
  448. !goog.testing.net.XhrIo.sendInstances_.length) {
  449. this.xhr_ = true;
  450. this.active_ = true;
  451. }
  452. this.statusCode_ = statusCode;
  453. this.response_ = response || '';
  454. this.responseHeaders_ = opt_headers || {};
  455. try {
  456. if (this.isSuccess()) {
  457. this.simulateReadyStateChange(goog.net.XmlHttp.ReadyState.COMPLETE);
  458. this.dispatchEvent(goog.net.EventType.SUCCESS);
  459. } else {
  460. this.lastErrorCode_ = goog.net.ErrorCode.HTTP_ERROR;
  461. this.lastError_ = this.getStatusText() + ' [' + this.getStatus() + ']';
  462. this.simulateReadyStateChange(goog.net.XmlHttp.ReadyState.COMPLETE);
  463. this.dispatchEvent(goog.net.EventType.ERROR);
  464. }
  465. } finally {
  466. this.simulateReady();
  467. }
  468. };
  469. /**
  470. * Simulates the Xhr is ready for the next request.
  471. */
  472. goog.testing.net.XhrIo.prototype.simulateReady = function() {
  473. this.active_ = false;
  474. this.xhr_ = false;
  475. this.dispatchEvent(goog.net.EventType.READY);
  476. };
  477. /**
  478. * Simulates the Xhr progress event.
  479. * @param {boolean} lengthComputable Whether progress is measurable.
  480. * @param {number} loaded Amount of work already performed.
  481. * @param {number} total Total amount of work to perform.
  482. * @param {boolean=} opt_isDownload Whether the progress is from a download or
  483. * upload.
  484. */
  485. goog.testing.net.XhrIo.prototype.simulateProgress = function(
  486. lengthComputable, loaded, total, opt_isDownload) {
  487. var progressEvent = {
  488. type: goog.net.EventType.PROGRESS,
  489. lengthComputable: lengthComputable,
  490. loaded: loaded,
  491. total: total
  492. };
  493. this.dispatchEvent(progressEvent);
  494. var specificProgress = goog.object.clone(progressEvent);
  495. specificProgress.type = opt_isDownload ?
  496. goog.net.EventType.DOWNLOAD_PROGRESS :
  497. goog.net.EventType.UPLOAD_PROGRESS;
  498. this.dispatchEvent(specificProgress);
  499. };
  500. /**
  501. * @return {boolean} Whether there is an active request.
  502. */
  503. goog.testing.net.XhrIo.prototype.isActive = function() {
  504. return !!this.xhr_;
  505. };
  506. /**
  507. * Has the request completed.
  508. * @return {boolean} Whether the request has completed.
  509. */
  510. goog.testing.net.XhrIo.prototype.isComplete = function() {
  511. return this.readyState_ == goog.net.XmlHttp.ReadyState.COMPLETE;
  512. };
  513. /**
  514. * Has the request compeleted with a success.
  515. * @return {boolean} Whether the request compeleted successfully.
  516. */
  517. goog.testing.net.XhrIo.prototype.isSuccess = function() {
  518. var status = this.getStatus();
  519. // A zero status code is considered successful for local files.
  520. return goog.net.HttpStatus.isSuccess(status) ||
  521. status === 0 && !this.isLastUriEffectiveSchemeHttp_();
  522. };
  523. /**
  524. * @return {boolean} whether the effective scheme of the last URI that was
  525. * fetched was 'http' or 'https'.
  526. * @private
  527. */
  528. goog.testing.net.XhrIo.prototype.isLastUriEffectiveSchemeHttp_ = function() {
  529. var scheme = goog.uri.utils.getEffectiveScheme(String(this.lastUri_));
  530. return goog.testing.net.XhrIo.HTTP_SCHEME_PATTERN_.test(scheme);
  531. };
  532. /**
  533. * Returns the readystate.
  534. * @return {number} goog.net.XmlHttp.ReadyState.*.
  535. */
  536. goog.testing.net.XhrIo.prototype.getReadyState = function() {
  537. return this.readyState_;
  538. };
  539. /**
  540. * Get the status from the Xhr object. Will only return correct result when
  541. * called from the context of a callback.
  542. * @return {number} Http status.
  543. */
  544. goog.testing.net.XhrIo.prototype.getStatus = function() {
  545. return this.statusCode_;
  546. };
  547. /**
  548. * Get the status text from the Xhr object. Will only return correct result
  549. * when called from the context of a callback.
  550. * @return {string} Status text.
  551. */
  552. goog.testing.net.XhrIo.prototype.getStatusText = function() {
  553. return '';
  554. };
  555. /**
  556. * Gets the last error message.
  557. * @return {goog.net.ErrorCode} Last error code.
  558. */
  559. goog.testing.net.XhrIo.prototype.getLastErrorCode = function() {
  560. return this.lastErrorCode_;
  561. };
  562. /**
  563. * Gets the last error message.
  564. * @return {string} Last error message.
  565. */
  566. goog.testing.net.XhrIo.prototype.getLastError = function() {
  567. return this.lastError_;
  568. };
  569. /**
  570. * Gets the last URI that was requested.
  571. * @return {string} Last URI.
  572. */
  573. goog.testing.net.XhrIo.prototype.getLastUri = function() {
  574. return this.lastUri_;
  575. };
  576. /**
  577. * Gets the last HTTP method that was requested.
  578. * @return {string|undefined} Last HTTP method used by send.
  579. */
  580. goog.testing.net.XhrIo.prototype.getLastMethod = function() {
  581. return this.lastMethod_;
  582. };
  583. /**
  584. * Gets the last POST content that was requested.
  585. * @return {string|undefined} Last POST content or undefined if last request was
  586. * a GET.
  587. */
  588. goog.testing.net.XhrIo.prototype.getLastContent = function() {
  589. return this.lastContent_;
  590. };
  591. /**
  592. * Gets the headers of the last request.
  593. * @return {Object|goog.structs.Map|undefined} Last headers manually set in send
  594. * call or undefined if no additional headers were specified.
  595. */
  596. goog.testing.net.XhrIo.prototype.getLastRequestHeaders = function() {
  597. return this.lastHeaders_;
  598. };
  599. /**
  600. * Returns true if there is a valid xhr, or if
  601. * allowUnsafeAccessToXhrIoOutsideCallbacks is false.
  602. * @return {boolean}
  603. * @private
  604. */
  605. goog.testing.net.XhrIo.prototype.checkXhr_ = function() {
  606. return (
  607. goog.testing.net.XhrIo.allowUnsafeAccessToXhrIoOutsideCallbacks ||
  608. !!this.xhr_);
  609. };
  610. /**
  611. * Gets the response text from the Xhr object. Will only return correct result
  612. * when called from the context of a callback.
  613. * @return {string} Result from the server.
  614. */
  615. goog.testing.net.XhrIo.prototype.getResponseText = function() {
  616. if (!this.checkXhr_()) {
  617. return '';
  618. } else if (goog.isString(this.response_)) {
  619. return this.response_;
  620. } else if (
  621. goog.global['ArrayBuffer'] && this.response_ instanceof ArrayBuffer) {
  622. return '';
  623. } else {
  624. return goog.dom.xml.serialize(/** @type {Document} */ (this.response_));
  625. }
  626. };
  627. /**
  628. * Gets the response body from the Xhr object. Will only return correct result
  629. * when called from the context of a callback.
  630. * @return {Object} Binary result from the server or null.
  631. */
  632. goog.testing.net.XhrIo.prototype.getResponseBody = function() {
  633. return null;
  634. };
  635. /**
  636. * Gets the response and evaluates it as JSON from the Xhr object. Will only
  637. * return correct result when called from the context of a callback.
  638. * @param {string=} opt_xssiPrefix Optional XSSI prefix string to use for
  639. * stripping of the response before parsing. This needs to be set only if
  640. * your backend server prepends the same prefix string to the JSON response.
  641. * @return {Object|undefined} JavaScript object.
  642. * @throws Error if s is invalid JSON.
  643. */
  644. goog.testing.net.XhrIo.prototype.getResponseJson = function(opt_xssiPrefix) {
  645. if (!this.checkXhr_()) {
  646. return undefined;
  647. }
  648. var responseText = this.getResponseText();
  649. if (opt_xssiPrefix && responseText.indexOf(opt_xssiPrefix) == 0) {
  650. responseText = responseText.substring(opt_xssiPrefix.length);
  651. }
  652. return goog.json.parse(responseText);
  653. };
  654. /**
  655. * Gets the response XML from the Xhr object. Will only return correct result
  656. * when called from the context of a callback.
  657. * @return {Document} Result from the server if it was XML.
  658. */
  659. goog.testing.net.XhrIo.prototype.getResponseXml = function() {
  660. if (!this.checkXhr_()) {
  661. return null;
  662. }
  663. // NOTE(user): I haven't found out how to check in Internet Explorer
  664. // whether the response is XML document, so I do it the other way around.
  665. return goog.isString(this.response_) ||
  666. (goog.global['ArrayBuffer'] &&
  667. this.response_ instanceof ArrayBuffer) ?
  668. null :
  669. /** @type {Document} */ (this.response_);
  670. };
  671. /**
  672. * Get the response as the type specificed by {@link #setResponseType}. At time
  673. * of writing, this is only supported in very recent versions of WebKit
  674. * (10.0.612.1 dev and later).
  675. *
  676. * @return {*} The response.
  677. */
  678. goog.testing.net.XhrIo.prototype.getResponse = function() {
  679. return this.checkXhr_() ? this.response_ : null;
  680. };
  681. /**
  682. * Get the value of the response-header with the given name from the Xhr object
  683. * Will only return correct result when called from the context of a callback
  684. * and the request has completed
  685. * @param {string} key The name of the response-header to retrieve.
  686. * @return {string|undefined} The value of the response-header named key.
  687. */
  688. goog.testing.net.XhrIo.prototype.getResponseHeader = function(key) {
  689. if (!this.checkXhr_() || !this.isComplete()) {
  690. return undefined;
  691. }
  692. return this.responseHeaders_[key];
  693. };
  694. /**
  695. * Gets the text of all the headers in the response.
  696. * Will only return correct result when called from the context of a callback
  697. * and the request has completed
  698. * @return {string} The string containing all the response headers.
  699. */
  700. goog.testing.net.XhrIo.prototype.getAllResponseHeaders = function() {
  701. if (!this.checkXhr_() || !this.isComplete()) {
  702. return '';
  703. }
  704. return this.getAllStreamingResponseHeaders();
  705. };
  706. /**
  707. * Returns all response headers as a key-value map.
  708. * Multiple values for the same header key can be combined into one,
  709. * separated by a comma and a space.
  710. * Note that the native getResponseHeader method for retrieving a single header
  711. * does a case insensitive match on the header name. This method does not
  712. * include any case normalization logic, it will just return a key-value
  713. * representation of the headers.
  714. * See: http://www.w3.org/TR/XMLHttpRequest/#the-getresponseheader()-method
  715. * @return {!Object<string, string>} An object with the header keys as keys
  716. * and header values as values.
  717. */
  718. goog.testing.net.XhrIo.prototype.getResponseHeaders = function() {
  719. if (!this.checkXhr_() || !this.isComplete()) {
  720. return {};
  721. }
  722. var headersObject = {};
  723. goog.object.forEach(this.responseHeaders_, function(value, key) {
  724. if (headersObject[key]) {
  725. headersObject[key] += ', ' + value;
  726. } else {
  727. headersObject[key] = value;
  728. }
  729. });
  730. return headersObject;
  731. };
  732. /**
  733. * Get the value of the response-header with the given name from the Xhr object.
  734. * As opposed to {@link #getResponseHeader}, this method does not require that
  735. * the request has completed.
  736. * @param {string} key The name of the response-header to retrieve.
  737. * @return {?string} The value of the response-header, or null if it is
  738. * unavailable.
  739. */
  740. goog.testing.net.XhrIo.prototype.getStreamingResponseHeader = function(key) {
  741. if (!this.checkXhr_()) {
  742. return null;
  743. }
  744. return key in this.responseHeaders_ ? this.responseHeaders_[key] : null;
  745. };
  746. /**
  747. * Gets the text of all the headers in the response. As opposed to
  748. * {@link #getAllResponseHeaders}, this method does not require that the request
  749. * has completed.
  750. * @return {string} The value of the response headers or empty string.
  751. */
  752. goog.testing.net.XhrIo.prototype.getAllStreamingResponseHeaders = function() {
  753. if (!this.checkXhr_()) {
  754. return '';
  755. }
  756. var headers = [];
  757. goog.object.forEach(this.responseHeaders_, function(value, name) {
  758. headers.push(name + ': ' + value);
  759. });
  760. return headers.join('\r\n');
  761. };