testing.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459
  1. /**
  2. * @license Angular v8.1.0
  3. * (c) 2010-2019 Google LLC. https://angular.io/
  4. * License: MIT
  5. */
  6. import { HttpHeaders, HttpResponse, HttpErrorResponse, HttpEventType, HttpClientModule, HttpBackend } from '@angular/common/http';
  7. import { Injectable, NgModule } from '@angular/core';
  8. import { Observable } from 'rxjs';
  9. /**
  10. * @fileoverview added by tsickle
  11. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  12. */
  13. /**
  14. * Controller to be injected into tests, that allows for mocking and flushing
  15. * of requests.
  16. *
  17. * \@publicApi
  18. * @abstract
  19. */
  20. class HttpTestingController {
  21. }
  22. /**
  23. * @fileoverview added by tsickle
  24. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  25. */
  26. /**
  27. * A mock requests that was received and is ready to be answered.
  28. *
  29. * This interface allows access to the underlying `HttpRequest`, and allows
  30. * responding with `HttpEvent`s or `HttpErrorResponse`s.
  31. *
  32. * \@publicApi
  33. */
  34. class TestRequest {
  35. /**
  36. * @param {?} request
  37. * @param {?} observer
  38. */
  39. constructor(request, observer) {
  40. this.request = request;
  41. this.observer = observer;
  42. /**
  43. * \@internal set by `HttpClientTestingBackend`
  44. */
  45. this._cancelled = false;
  46. }
  47. /**
  48. * Whether the request was cancelled after it was sent.
  49. * @return {?}
  50. */
  51. get cancelled() { return this._cancelled; }
  52. /**
  53. * Resolve the request by returning a body plus additional HTTP information (such as response
  54. * headers) if provided.
  55. * If the request specifies an expected body type, the body is converted into the requested type.
  56. * Otherwise, the body is converted to `JSON` by default.
  57. *
  58. * Both successful and unsuccessful responses can be delivered via `flush()`.
  59. * @param {?} body
  60. * @param {?=} opts
  61. * @return {?}
  62. */
  63. flush(body, opts = {}) {
  64. if (this.cancelled) {
  65. throw new Error(`Cannot flush a cancelled request.`);
  66. }
  67. /** @type {?} */
  68. const url = this.request.urlWithParams;
  69. /** @type {?} */
  70. const headers = (opts.headers instanceof HttpHeaders) ? opts.headers : new HttpHeaders(opts.headers);
  71. body = _maybeConvertBody(this.request.responseType, body);
  72. /** @type {?} */
  73. let statusText = opts.statusText;
  74. /** @type {?} */
  75. let status = opts.status !== undefined ? opts.status : 200;
  76. if (opts.status === undefined) {
  77. if (body === null) {
  78. status = 204;
  79. statusText = statusText || 'No Content';
  80. }
  81. else {
  82. statusText = statusText || 'OK';
  83. }
  84. }
  85. if (statusText === undefined) {
  86. throw new Error('statusText is required when setting a custom status.');
  87. }
  88. if (status >= 200 && status < 300) {
  89. this.observer.next(new HttpResponse({ body, headers, status, statusText, url }));
  90. this.observer.complete();
  91. }
  92. else {
  93. this.observer.error(new HttpErrorResponse({ error: body, headers, status, statusText, url }));
  94. }
  95. }
  96. /**
  97. * Resolve the request by returning an `ErrorEvent` (e.g. simulating a network failure).
  98. * @param {?} error
  99. * @param {?=} opts
  100. * @return {?}
  101. */
  102. error(error, opts = {}) {
  103. if (this.cancelled) {
  104. throw new Error(`Cannot return an error for a cancelled request.`);
  105. }
  106. if (opts.status && opts.status >= 200 && opts.status < 300) {
  107. throw new Error(`error() called with a successful status.`);
  108. }
  109. /** @type {?} */
  110. const headers = (opts.headers instanceof HttpHeaders) ? opts.headers : new HttpHeaders(opts.headers);
  111. this.observer.error(new HttpErrorResponse({
  112. error,
  113. headers,
  114. status: opts.status || 0,
  115. statusText: opts.statusText || '',
  116. url: this.request.urlWithParams,
  117. }));
  118. }
  119. /**
  120. * Deliver an arbitrary `HttpEvent` (such as a progress event) on the response stream for this
  121. * request.
  122. * @param {?} event
  123. * @return {?}
  124. */
  125. event(event) {
  126. if (this.cancelled) {
  127. throw new Error(`Cannot send events to a cancelled request.`);
  128. }
  129. this.observer.next(event);
  130. }
  131. }
  132. /**
  133. * Helper function to convert a response body to an ArrayBuffer.
  134. * @param {?} body
  135. * @return {?}
  136. */
  137. function _toArrayBufferBody(body) {
  138. if (typeof ArrayBuffer === 'undefined') {
  139. throw new Error('ArrayBuffer responses are not supported on this platform.');
  140. }
  141. if (body instanceof ArrayBuffer) {
  142. return body;
  143. }
  144. throw new Error('Automatic conversion to ArrayBuffer is not supported for response type.');
  145. }
  146. /**
  147. * Helper function to convert a response body to a Blob.
  148. * @param {?} body
  149. * @return {?}
  150. */
  151. function _toBlob(body) {
  152. if (typeof Blob === 'undefined') {
  153. throw new Error('Blob responses are not supported on this platform.');
  154. }
  155. if (body instanceof Blob) {
  156. return body;
  157. }
  158. if (ArrayBuffer && body instanceof ArrayBuffer) {
  159. return new Blob([body]);
  160. }
  161. throw new Error('Automatic conversion to Blob is not supported for response type.');
  162. }
  163. /**
  164. * Helper function to convert a response body to JSON data.
  165. * @param {?} body
  166. * @param {?=} format
  167. * @return {?}
  168. */
  169. function _toJsonBody(body, format = 'JSON') {
  170. if (typeof ArrayBuffer !== 'undefined' && body instanceof ArrayBuffer) {
  171. throw new Error(`Automatic conversion to ${format} is not supported for ArrayBuffers.`);
  172. }
  173. if (typeof Blob !== 'undefined' && body instanceof Blob) {
  174. throw new Error(`Automatic conversion to ${format} is not supported for Blobs.`);
  175. }
  176. if (typeof body === 'string' || typeof body === 'number' || typeof body === 'object' ||
  177. Array.isArray(body)) {
  178. return body;
  179. }
  180. throw new Error(`Automatic conversion to ${format} is not supported for response type.`);
  181. }
  182. /**
  183. * Helper function to convert a response body to a string.
  184. * @param {?} body
  185. * @return {?}
  186. */
  187. function _toTextBody(body) {
  188. if (typeof body === 'string') {
  189. return body;
  190. }
  191. if (typeof ArrayBuffer !== 'undefined' && body instanceof ArrayBuffer) {
  192. throw new Error('Automatic conversion to text is not supported for ArrayBuffers.');
  193. }
  194. if (typeof Blob !== 'undefined' && body instanceof Blob) {
  195. throw new Error('Automatic conversion to text is not supported for Blobs.');
  196. }
  197. return JSON.stringify(_toJsonBody(body, 'text'));
  198. }
  199. /**
  200. * Convert a response body to the requested type.
  201. * @param {?} responseType
  202. * @param {?} body
  203. * @return {?}
  204. */
  205. function _maybeConvertBody(responseType, body) {
  206. if (body === null) {
  207. return null;
  208. }
  209. switch (responseType) {
  210. case 'arraybuffer':
  211. return _toArrayBufferBody(body);
  212. case 'blob':
  213. return _toBlob(body);
  214. case 'json':
  215. return _toJsonBody(body);
  216. case 'text':
  217. return _toTextBody(body);
  218. default:
  219. throw new Error(`Unsupported responseType: ${responseType}`);
  220. }
  221. }
  222. /**
  223. * @fileoverview added by tsickle
  224. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  225. */
  226. /**
  227. * A testing backend for `HttpClient` which both acts as an `HttpBackend`
  228. * and as the `HttpTestingController`.
  229. *
  230. * `HttpClientTestingBackend` works by keeping a list of all open requests.
  231. * As requests come in, they're added to the list. Users can assert that specific
  232. * requests were made and then flush them. In the end, a verify() method asserts
  233. * that no unexpected requests were made.
  234. *
  235. *
  236. */
  237. class HttpClientTestingBackend {
  238. constructor() {
  239. /**
  240. * List of pending requests which have not yet been expected.
  241. */
  242. this.open = [];
  243. }
  244. /**
  245. * Handle an incoming request by queueing it in the list of open requests.
  246. * @param {?} req
  247. * @return {?}
  248. */
  249. handle(req) {
  250. return new Observable((/**
  251. * @param {?} observer
  252. * @return {?}
  253. */
  254. (observer) => {
  255. /** @type {?} */
  256. const testReq = new TestRequest(req, observer);
  257. this.open.push(testReq);
  258. observer.next((/** @type {?} */ ({ type: HttpEventType.Sent })));
  259. return (/**
  260. * @return {?}
  261. */
  262. () => { testReq._cancelled = true; });
  263. }));
  264. }
  265. /**
  266. * Helper function to search for requests in the list of open requests.
  267. * @private
  268. * @param {?} match
  269. * @return {?}
  270. */
  271. _match(match) {
  272. if (typeof match === 'string') {
  273. return this.open.filter((/**
  274. * @param {?} testReq
  275. * @return {?}
  276. */
  277. testReq => testReq.request.urlWithParams === match));
  278. }
  279. else if (typeof match === 'function') {
  280. return this.open.filter((/**
  281. * @param {?} testReq
  282. * @return {?}
  283. */
  284. testReq => match(testReq.request)));
  285. }
  286. else {
  287. return this.open.filter((/**
  288. * @param {?} testReq
  289. * @return {?}
  290. */
  291. testReq => (!match.method || testReq.request.method === match.method.toUpperCase()) &&
  292. (!match.url || testReq.request.urlWithParams === match.url)));
  293. }
  294. }
  295. /**
  296. * Search for requests in the list of open requests, and return all that match
  297. * without asserting anything about the number of matches.
  298. * @param {?} match
  299. * @return {?}
  300. */
  301. match(match) {
  302. /** @type {?} */
  303. const results = this._match(match);
  304. results.forEach((/**
  305. * @param {?} result
  306. * @return {?}
  307. */
  308. result => {
  309. /** @type {?} */
  310. const index = this.open.indexOf(result);
  311. if (index !== -1) {
  312. this.open.splice(index, 1);
  313. }
  314. }));
  315. return results;
  316. }
  317. /**
  318. * Expect that a single outstanding request matches the given matcher, and return
  319. * it.
  320. *
  321. * Requests returned through this API will no longer be in the list of open requests,
  322. * and thus will not match twice.
  323. * @param {?} match
  324. * @param {?=} description
  325. * @return {?}
  326. */
  327. expectOne(match, description) {
  328. description = description || this.descriptionFromMatcher(match);
  329. /** @type {?} */
  330. const matches = this.match(match);
  331. if (matches.length > 1) {
  332. throw new Error(`Expected one matching request for criteria "${description}", found ${matches.length} requests.`);
  333. }
  334. if (matches.length === 0) {
  335. throw new Error(`Expected one matching request for criteria "${description}", found none.`);
  336. }
  337. return matches[0];
  338. }
  339. /**
  340. * Expect that no outstanding requests match the given matcher, and throw an error
  341. * if any do.
  342. * @param {?} match
  343. * @param {?=} description
  344. * @return {?}
  345. */
  346. expectNone(match, description) {
  347. description = description || this.descriptionFromMatcher(match);
  348. /** @type {?} */
  349. const matches = this.match(match);
  350. if (matches.length > 0) {
  351. throw new Error(`Expected zero matching requests for criteria "${description}", found ${matches.length}.`);
  352. }
  353. }
  354. /**
  355. * Validate that there are no outstanding requests.
  356. * @param {?=} opts
  357. * @return {?}
  358. */
  359. verify(opts = {}) {
  360. /** @type {?} */
  361. let open = this.open;
  362. // It's possible that some requests may be cancelled, and this is expected.
  363. // The user can ask to ignore open requests which have been cancelled.
  364. if (opts.ignoreCancelled) {
  365. open = open.filter((/**
  366. * @param {?} testReq
  367. * @return {?}
  368. */
  369. testReq => !testReq.cancelled));
  370. }
  371. if (open.length > 0) {
  372. // Show the methods and URLs of open requests in the error, for convenience.
  373. /** @type {?} */
  374. const requests = open.map((/**
  375. * @param {?} testReq
  376. * @return {?}
  377. */
  378. testReq => {
  379. /** @type {?} */
  380. const url = testReq.request.urlWithParams.split('?')[0];
  381. /** @type {?} */
  382. const method = testReq.request.method;
  383. return `${method} ${url}`;
  384. }))
  385. .join(', ');
  386. throw new Error(`Expected no open requests, found ${open.length}: ${requests}`);
  387. }
  388. }
  389. /**
  390. * @private
  391. * @param {?} matcher
  392. * @return {?}
  393. */
  394. descriptionFromMatcher(matcher) {
  395. if (typeof matcher === 'string') {
  396. return `Match URL: ${matcher}`;
  397. }
  398. else if (typeof matcher === 'object') {
  399. /** @type {?} */
  400. const method = matcher.method || '(any)';
  401. /** @type {?} */
  402. const url = matcher.url || '(any)';
  403. return `Match method: ${method}, URL: ${url}`;
  404. }
  405. else {
  406. return `Match by function: ${matcher.name}`;
  407. }
  408. }
  409. }
  410. HttpClientTestingBackend.decorators = [
  411. { type: Injectable }
  412. ];
  413. /**
  414. * @fileoverview added by tsickle
  415. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  416. */
  417. /**
  418. * Configures `HttpClientTestingBackend` as the `HttpBackend` used by `HttpClient`.
  419. *
  420. * Inject `HttpTestingController` to expect and flush requests in your tests.
  421. *
  422. * \@publicApi
  423. */
  424. class HttpClientTestingModule {
  425. }
  426. HttpClientTestingModule.decorators = [
  427. { type: NgModule, args: [{
  428. imports: [
  429. HttpClientModule,
  430. ],
  431. providers: [
  432. HttpClientTestingBackend,
  433. { provide: HttpBackend, useExisting: HttpClientTestingBackend },
  434. { provide: HttpTestingController, useExisting: HttpClientTestingBackend },
  435. ],
  436. },] }
  437. ];
  438. /**
  439. * @fileoverview added by tsickle
  440. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  441. */
  442. /**
  443. * @fileoverview added by tsickle
  444. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  445. */
  446. /**
  447. * Generated bundle index. Do not edit.
  448. */
  449. export { HttpClientTestingBackend as ɵangular_packages_common_http_testing_testing_a, HttpTestingController, HttpClientTestingModule, TestRequest };
  450. //# sourceMappingURL=testing.js.map