File diff suppressed because it is too large
Load Diff
@ -24,7 +24,7 @@ export class HttpHeaders {
|
||||
* Internal map of lowercase header names to values.
|
||||
*/
|
||||
// TODO(issue/24571): remove '!'.
|
||||
private headers !: Map<string, string[]>;
|
||||
private headers!: Map<string, string[]>;
|
||||
|
||||
|
||||
/**
|
||||
@ -36,7 +36,7 @@ export class HttpHeaders {
|
||||
/**
|
||||
* Complete the lazy initialization of this object (needed before reading).
|
||||
*/
|
||||
private lazyInit !: HttpHeaders | Function | null;
|
||||
private lazyInit!: HttpHeaders|Function|null;
|
||||
|
||||
/**
|
||||
* Queued updates to be materialized the next initialization.
|
||||
@ -59,7 +59,7 @@ export class HttpHeaders {
|
||||
const value = line.slice(index + 1).trim();
|
||||
this.maybeSetNormalizedName(name, key);
|
||||
if (this.headers.has(key)) {
|
||||
this.headers.get(key) !.push(value);
|
||||
this.headers.get(key)!.push(value);
|
||||
} else {
|
||||
this.headers.set(key, [value]);
|
||||
}
|
||||
@ -169,7 +169,7 @@ export class HttpHeaders {
|
||||
*
|
||||
* @returns A clone of the HTTP headers object with the given value deleted.
|
||||
*/
|
||||
delete (name: string, value?: string|string[]): HttpHeaders {
|
||||
delete(name: string, value?: string|string[]): HttpHeaders {
|
||||
return this.clone({name, value, op: 'd'});
|
||||
}
|
||||
|
||||
@ -197,8 +197,8 @@ export class HttpHeaders {
|
||||
private copyFrom(other: HttpHeaders) {
|
||||
other.init();
|
||||
Array.from(other.headers.keys()).forEach(key => {
|
||||
this.headers.set(key, other.headers.get(key) !);
|
||||
this.normalizedNames.set(key, other.normalizedNames.get(key) !);
|
||||
this.headers.set(key, other.headers.get(key)!);
|
||||
this.normalizedNames.set(key, other.normalizedNames.get(key)!);
|
||||
});
|
||||
}
|
||||
|
||||
@ -215,7 +215,7 @@ export class HttpHeaders {
|
||||
switch (update.op) {
|
||||
case 'a':
|
||||
case 's':
|
||||
let value = update.value !;
|
||||
let value = update.value!;
|
||||
if (typeof value === 'string') {
|
||||
value = [value];
|
||||
}
|
||||
@ -255,6 +255,6 @@ export class HttpHeaders {
|
||||
forEach(fn: (name: string, values: string[]) => void) {
|
||||
this.init();
|
||||
Array.from(this.normalizedNames.keys())
|
||||
.forEach(key => fn(this.normalizedNames.get(key) !, this.headers.get(key) !));
|
||||
.forEach(key => fn(this.normalizedNames.get(key)!, this.headers.get(key)!));
|
||||
}
|
||||
}
|
||||
|
@ -38,8 +38,8 @@ import {HttpEvent} from './response';
|
||||
* To use the same instance of `HttpInterceptors` for the entire app, import the `HttpClientModule`
|
||||
* only in your `AppModule`, and add the interceptors to the root application injector .
|
||||
* If you import `HttpClientModule` multiple times across different modules (for example, in lazy
|
||||
* loading modules), each import creates a new copy of the `HttpClientModule`, which overwrites the interceptors
|
||||
* provided in the root module.
|
||||
* loading modules), each import creates a new copy of the `HttpClientModule`, which overwrites the
|
||||
* interceptors provided in the root module.
|
||||
*
|
||||
*/
|
||||
export interface HttpInterceptor {
|
||||
|
@ -36,7 +36,9 @@ export const JSONP_ERR_WRONG_RESPONSE_TYPE = 'JSONP requests must use Json respo
|
||||
*
|
||||
*
|
||||
*/
|
||||
export abstract class JsonpCallbackContext { [key: string]: (data: any) => void; }
|
||||
export abstract class JsonpCallbackContext {
|
||||
[key: string]: (data: any) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes an `HttpRequest` with the JSONP method,
|
||||
@ -53,7 +55,9 @@ export class JsonpClientBackend implements HttpBackend {
|
||||
/**
|
||||
* Get the name of the next callback method, by incrementing the global `nextRequestId`.
|
||||
*/
|
||||
private nextCallback(): string { return `ng_jsonp_callback_${nextRequestId++}`; }
|
||||
private nextCallback(): string {
|
||||
return `ng_jsonp_callback_${nextRequestId++}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a JSONP request and returns an event stream of the results.
|
||||
@ -157,7 +161,8 @@ export class JsonpClientBackend implements HttpBackend {
|
||||
observer.next(new HttpResponse({
|
||||
body,
|
||||
status: 200,
|
||||
statusText: 'OK', url,
|
||||
statusText: 'OK',
|
||||
url,
|
||||
}));
|
||||
|
||||
// Complete the stream, the response is over.
|
||||
@ -178,7 +183,8 @@ export class JsonpClientBackend implements HttpBackend {
|
||||
observer.error(new HttpErrorResponse({
|
||||
error,
|
||||
status: 0,
|
||||
statusText: 'JSONP Error', url,
|
||||
statusText: 'JSONP Error',
|
||||
url,
|
||||
}));
|
||||
};
|
||||
|
||||
|
@ -52,7 +52,7 @@ export class HttpInterceptingHandler implements HttpHandler {
|
||||
*
|
||||
*/
|
||||
export function interceptingHandler(
|
||||
backend: HttpBackend, interceptors: HttpInterceptor[] | null = []): HttpHandler {
|
||||
backend: HttpBackend, interceptors: HttpInterceptor[]|null = []): HttpHandler {
|
||||
if (!interceptors) {
|
||||
return backend;
|
||||
}
|
||||
|
@ -37,28 +37,36 @@ export class HttpUrlEncodingCodec implements HttpParameterCodec {
|
||||
* @param key The key name.
|
||||
* @returns The encoded key name.
|
||||
*/
|
||||
encodeKey(key: string): string { return standardEncoding(key); }
|
||||
encodeKey(key: string): string {
|
||||
return standardEncoding(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes the value of a URL parameter or query-string.
|
||||
* @param value The value.
|
||||
* @returns The encoded value.
|
||||
*/
|
||||
encodeValue(value: string): string { return standardEncoding(value); }
|
||||
encodeValue(value: string): string {
|
||||
return standardEncoding(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes an encoded URL parameter or query-string key.
|
||||
* @param key The encoded key name.
|
||||
* @returns The decoded key name.
|
||||
*/
|
||||
decodeKey(key: string): string { return decodeURIComponent(key); }
|
||||
decodeKey(key: string): string {
|
||||
return decodeURIComponent(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes an encoded URL parameter or query-string value.
|
||||
* @param value The encoded value.
|
||||
* @returns The decoded value.
|
||||
*/
|
||||
decodeValue(value: string) { return decodeURIComponent(value); }
|
||||
decodeValue(value: string) {
|
||||
return decodeURIComponent(value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -97,7 +105,8 @@ interface Update {
|
||||
op: 'a'|'d'|'s';
|
||||
}
|
||||
|
||||
/** Options used to construct an `HttpParams` instance.
|
||||
/**
|
||||
* Options used to construct an `HttpParams` instance.
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
@ -109,7 +118,7 @@ export interface HttpParamsOptions {
|
||||
fromString?: string;
|
||||
|
||||
/** Object map of the HTTP parameters. Mutually exclusive with `fromString`. */
|
||||
fromObject?: {[param: string]: string | ReadonlyArray<string>};
|
||||
fromObject?: {[param: string]: string|ReadonlyArray<string>};
|
||||
|
||||
/** Encoding codec used to parse and serialize the parameters. */
|
||||
encoder?: HttpParameterCodec;
|
||||
@ -140,7 +149,7 @@ export class HttpParams {
|
||||
this.map = new Map<string, string[]>();
|
||||
Object.keys(options.fromObject).forEach(key => {
|
||||
const value = (options.fromObject as any)[key];
|
||||
this.map !.set(key, Array.isArray(value) ? value : [value]);
|
||||
this.map!.set(key, Array.isArray(value) ? value : [value]);
|
||||
});
|
||||
} else {
|
||||
this.map = null;
|
||||
@ -155,7 +164,7 @@ export class HttpParams {
|
||||
*/
|
||||
has(param: string): boolean {
|
||||
this.init();
|
||||
return this.map !.has(param);
|
||||
return this.map!.has(param);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -166,7 +175,7 @@ export class HttpParams {
|
||||
*/
|
||||
get(param: string): string|null {
|
||||
this.init();
|
||||
const res = this.map !.get(param);
|
||||
const res = this.map!.get(param);
|
||||
return !!res ? res[0] : null;
|
||||
}
|
||||
|
||||
@ -178,7 +187,7 @@ export class HttpParams {
|
||||
*/
|
||||
getAll(param: string): string[]|null {
|
||||
this.init();
|
||||
return this.map !.get(param) || null;
|
||||
return this.map!.get(param) || null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -187,7 +196,7 @@ export class HttpParams {
|
||||
*/
|
||||
keys(): string[] {
|
||||
this.init();
|
||||
return Array.from(this.map !.keys());
|
||||
return Array.from(this.map!.keys());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -196,7 +205,9 @@ export class HttpParams {
|
||||
* @param value The new value to add.
|
||||
* @return A new body with the appended value.
|
||||
*/
|
||||
append(param: string, value: string): HttpParams { return this.clone({param, value, op: 'a'}); }
|
||||
append(param: string, value: string): HttpParams {
|
||||
return this.clone({param, value, op: 'a'});
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the value for a parameter.
|
||||
@ -204,7 +215,9 @@ export class HttpParams {
|
||||
* @param value The new value.
|
||||
* @return A new body with the new value.
|
||||
*/
|
||||
set(param: string, value: string): HttpParams { return this.clone({param, value, op: 's'}); }
|
||||
set(param: string, value: string): HttpParams {
|
||||
return this.clone({param, value, op: 's'});
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a given value or all values from a parameter.
|
||||
@ -213,7 +226,9 @@ export class HttpParams {
|
||||
* @return A new body with the given value removed, or with all values
|
||||
* removed if no value is specified.
|
||||
*/
|
||||
delete (param: string, value?: string): HttpParams { return this.clone({param, value, op: 'd'}); }
|
||||
delete(param: string, value?: string): HttpParams {
|
||||
return this.clone({param, value, op: 'd'});
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes the body to an encoded string, where key-value pairs (separated by `=`) are
|
||||
@ -227,7 +242,7 @@ export class HttpParams {
|
||||
// `a: ['1']` produces `'a=1'`
|
||||
// `b: []` produces `''`
|
||||
// `c: ['1', '2']` produces `'c=1&c=2'`
|
||||
return this.map !.get(key) !.map(value => eKey + '=' + this.encoder.encodeValue(value))
|
||||
return this.map!.get(key)!.map(value => eKey + '=' + this.encoder.encodeValue(value))
|
||||
.join('&');
|
||||
})
|
||||
// filter out empty values because `b: []` produces `''`
|
||||
@ -237,7 +252,7 @@ export class HttpParams {
|
||||
}
|
||||
|
||||
private clone(update: Update): HttpParams {
|
||||
const clone = new HttpParams({ encoder: this.encoder } as HttpParamsOptions);
|
||||
const clone = new HttpParams({encoder: this.encoder} as HttpParamsOptions);
|
||||
clone.cloneFrom = this.cloneFrom || this;
|
||||
clone.updates = (this.updates || []).concat([update]);
|
||||
return clone;
|
||||
@ -249,29 +264,29 @@ export class HttpParams {
|
||||
}
|
||||
if (this.cloneFrom !== null) {
|
||||
this.cloneFrom.init();
|
||||
this.cloneFrom.keys().forEach(key => this.map !.set(key, this.cloneFrom !.map !.get(key) !));
|
||||
this.updates !.forEach(update => {
|
||||
this.cloneFrom.keys().forEach(key => this.map!.set(key, this.cloneFrom!.map!.get(key)!));
|
||||
this.updates!.forEach(update => {
|
||||
switch (update.op) {
|
||||
case 'a':
|
||||
case 's':
|
||||
const base = (update.op === 'a' ? this.map !.get(update.param) : undefined) || [];
|
||||
base.push(update.value !);
|
||||
this.map !.set(update.param, base);
|
||||
const base = (update.op === 'a' ? this.map!.get(update.param) : undefined) || [];
|
||||
base.push(update.value!);
|
||||
this.map!.set(update.param, base);
|
||||
break;
|
||||
case 'd':
|
||||
if (update.value !== undefined) {
|
||||
let base = this.map !.get(update.param) || [];
|
||||
let base = this.map!.get(update.param) || [];
|
||||
const idx = base.indexOf(update.value);
|
||||
if (idx !== -1) {
|
||||
base.splice(idx, 1);
|
||||
}
|
||||
if (base.length > 0) {
|
||||
this.map !.set(update.param, base);
|
||||
this.map!.set(update.param, base);
|
||||
} else {
|
||||
this.map !.delete(update.param);
|
||||
this.map!.delete(update.param);
|
||||
}
|
||||
} else {
|
||||
this.map !.delete(update.param);
|
||||
this.map!.delete(update.param);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -89,7 +89,7 @@ export class HttpRequest<T> {
|
||||
* Outgoing headers for this request.
|
||||
*/
|
||||
// TODO(issue/24571): remove '!'.
|
||||
readonly headers !: HttpHeaders;
|
||||
readonly headers!: HttpHeaders;
|
||||
|
||||
/**
|
||||
* Whether this request should be made in a way that exposes progress events.
|
||||
@ -121,7 +121,7 @@ export class HttpRequest<T> {
|
||||
* Outgoing URL parameters.
|
||||
*/
|
||||
// TODO(issue/24571): remove '!'.
|
||||
readonly params !: HttpParams;
|
||||
readonly params!: HttpParams;
|
||||
|
||||
/**
|
||||
* The outgoing URL with all URL parameters set.
|
||||
@ -312,7 +312,7 @@ export class HttpRequest<T> {
|
||||
body?: T|null,
|
||||
method?: string,
|
||||
url?: string,
|
||||
setHeaders?: {[name: string]: string | string[]},
|
||||
setHeaders?: {[name: string]: string|string[]},
|
||||
setParams?: {[param: string]: string},
|
||||
}): HttpRequest<T>;
|
||||
clone<V>(update: {
|
||||
@ -324,7 +324,7 @@ export class HttpRequest<T> {
|
||||
body?: V|null,
|
||||
method?: string,
|
||||
url?: string,
|
||||
setHeaders?: {[name: string]: string | string[]},
|
||||
setHeaders?: {[name: string]: string|string[]},
|
||||
setParams?: {[param: string]: string},
|
||||
}): HttpRequest<V>;
|
||||
clone(update: {
|
||||
@ -336,7 +336,7 @@ export class HttpRequest<T> {
|
||||
body?: any|null,
|
||||
method?: string,
|
||||
url?: string,
|
||||
setHeaders?: {[name: string]: string | string[]},
|
||||
setHeaders?: {[name: string]: string|string[]},
|
||||
setParams?: {[param: string]: string};
|
||||
} = {}): HttpRequest<any> {
|
||||
// For method, url, and responseType, take the current value unless
|
||||
@ -368,20 +368,23 @@ export class HttpRequest<T> {
|
||||
// Set every requested header.
|
||||
headers =
|
||||
Object.keys(update.setHeaders)
|
||||
.reduce((headers, name) => headers.set(name, update.setHeaders ![name]), headers);
|
||||
.reduce((headers, name) => headers.set(name, update.setHeaders![name]), headers);
|
||||
}
|
||||
|
||||
// Check whether the caller has asked to set params.
|
||||
if (update.setParams) {
|
||||
// Set every requested param.
|
||||
params = Object.keys(update.setParams)
|
||||
.reduce((params, param) => params.set(param, update.setParams ![param]), params);
|
||||
.reduce((params, param) => params.set(param, update.setParams![param]), params);
|
||||
}
|
||||
|
||||
// Finally, construct the new HttpRequest using the pieces from above.
|
||||
return new HttpRequest(
|
||||
method, url, body, {
|
||||
params, headers, reportProgress, responseType, withCredentials,
|
||||
});
|
||||
return new HttpRequest(method, url, body, {
|
||||
params,
|
||||
headers,
|
||||
reportProgress,
|
||||
responseType,
|
||||
withCredentials,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -100,7 +100,9 @@ export interface HttpUploadProgressEvent extends HttpProgressEvent {
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
export interface HttpSentEvent { type: HttpEventType.Sent; }
|
||||
export interface HttpSentEvent {
|
||||
type: HttpEventType.Sent;
|
||||
}
|
||||
|
||||
/**
|
||||
* A user-defined event.
|
||||
@ -110,7 +112,9 @@ export interface HttpSentEvent { type: HttpEventType.Sent; }
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
export interface HttpUserEvent<T> { type: HttpEventType.User; }
|
||||
export interface HttpUserEvent<T> {
|
||||
type: HttpEventType.User;
|
||||
}
|
||||
|
||||
/**
|
||||
* An error that represents a failed attempt to JSON.parse text coming back
|
||||
@ -133,7 +137,7 @@ export interface HttpJsonParseError {
|
||||
* @publicApi
|
||||
*/
|
||||
export type HttpEvent<T> =
|
||||
HttpSentEvent | HttpHeaderResponse | HttpResponse<T>| HttpProgressEvent | HttpUserEvent<T>;
|
||||
HttpSentEvent|HttpHeaderResponse|HttpResponse<T>|HttpProgressEvent|HttpUserEvent<T>;
|
||||
|
||||
/**
|
||||
* Base class for both `HttpResponse` and `HttpHeaderResponse`.
|
||||
@ -172,7 +176,7 @@ export abstract class HttpResponseBase {
|
||||
* Type of the response, narrowed to either the full response or the header.
|
||||
*/
|
||||
// TODO(issue/24571): remove '!'.
|
||||
readonly type !: HttpEventType.Response | HttpEventType.ResponseHeader;
|
||||
readonly type!: HttpEventType.Response|HttpEventType.ResponseHeader;
|
||||
|
||||
/**
|
||||
* Super-constructor for all responses.
|
||||
@ -260,7 +264,11 @@ export class HttpResponse<T> extends HttpResponseBase {
|
||||
* Construct a new `HttpResponse`.
|
||||
*/
|
||||
constructor(init: {
|
||||
body?: T | null, headers?: HttpHeaders; status?: number; statusText?: string; url?: string;
|
||||
body?: T|null,
|
||||
headers?: HttpHeaders;
|
||||
status?: number;
|
||||
statusText?: string;
|
||||
url?: string;
|
||||
} = {}) {
|
||||
super(init);
|
||||
this.body = init.body !== undefined ? init.body : null;
|
||||
@ -272,10 +280,18 @@ export class HttpResponse<T> extends HttpResponseBase {
|
||||
clone(update: {headers?: HttpHeaders; status?: number; statusText?: string; url?: string;}):
|
||||
HttpResponse<T>;
|
||||
clone<V>(update: {
|
||||
body?: V | null, headers?: HttpHeaders; status?: number; statusText?: string; url?: string;
|
||||
body?: V|null,
|
||||
headers?: HttpHeaders;
|
||||
status?: number;
|
||||
statusText?: string;
|
||||
url?: string;
|
||||
}): HttpResponse<V>;
|
||||
clone(update: {
|
||||
body?: any | null; headers?: HttpHeaders; status?: number; statusText?: string; url?: string;
|
||||
body?: any|null;
|
||||
headers?: HttpHeaders;
|
||||
status?: number;
|
||||
statusText?: string;
|
||||
url?: string;
|
||||
} = {}): HttpResponse<any> {
|
||||
return new HttpResponse<any>({
|
||||
body: (update.body !== undefined) ? update.body : this.body,
|
||||
@ -311,7 +327,11 @@ export class HttpErrorResponse extends HttpResponseBase implements Error {
|
||||
readonly ok = false;
|
||||
|
||||
constructor(init: {
|
||||
error?: any; headers?: HttpHeaders; status?: number; statusText?: string; url?: string;
|
||||
error?: any;
|
||||
headers?: HttpHeaders;
|
||||
status?: number;
|
||||
statusText?: string;
|
||||
url?: string;
|
||||
}) {
|
||||
// Initialize with a default status of 0 / Unknown Error.
|
||||
super(init, 0, 'Unknown Error');
|
||||
@ -322,8 +342,8 @@ export class HttpErrorResponse extends HttpResponseBase implements Error {
|
||||
if (this.status >= 200 && this.status < 300) {
|
||||
this.message = `Http failure during parsing for ${init.url || '(unknown url)'}`;
|
||||
} else {
|
||||
this.message =
|
||||
`Http failure response for ${init.url || '(unknown url)'}: ${init.status} ${init.statusText}`;
|
||||
this.message = `Http failure response for ${init.url || '(unknown url)'}: ${init.status} ${
|
||||
init.statusText}`;
|
||||
}
|
||||
this.error = init.error || null;
|
||||
}
|
||||
|
@ -35,7 +35,9 @@ function getResponseUrl(xhr: any): string|null {
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
export abstract class XhrFactory { abstract build(): XMLHttpRequest; }
|
||||
export abstract class XhrFactory {
|
||||
abstract build(): XMLHttpRequest;
|
||||
}
|
||||
|
||||
/**
|
||||
* A factory for `HttpXhrBackend` that uses the `XMLHttpRequest` browser API.
|
||||
@ -44,7 +46,9 @@ export abstract class XhrFactory { abstract build(): XMLHttpRequest; }
|
||||
@Injectable()
|
||||
export class BrowserXhr implements XhrFactory {
|
||||
constructor() {}
|
||||
build(): any { return <any>(new XMLHttpRequest()); }
|
||||
build(): any {
|
||||
return <any>(new XMLHttpRequest());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -200,7 +204,7 @@ export class HttpXhrBackend implements HttpBackend {
|
||||
// Even though the response status was 2xx, this is still an error.
|
||||
ok = false;
|
||||
// The parse error contains the text of the body that failed to parse.
|
||||
body = { error, text: body } as HttpJsonParseError;
|
||||
body = {error, text: body} as HttpJsonParseError;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -318,7 +322,7 @@ export class HttpXhrBackend implements HttpBackend {
|
||||
}
|
||||
|
||||
// Fire the request, and notify the event stream that it was fired.
|
||||
xhr.send(reqBody !);
|
||||
xhr.send(reqBody!);
|
||||
observer.next({type: HttpEventType.Sent});
|
||||
|
||||
// This is the return from the Observable function, which is the
|
||||
|
@ -14,13 +14,15 @@ import {toArray} from 'rxjs/operators';
|
||||
|
||||
{
|
||||
describe('HttpClient', () => {
|
||||
let client: HttpClient = null !;
|
||||
let backend: HttpClientTestingBackend = null !;
|
||||
let client: HttpClient = null!;
|
||||
let backend: HttpClientTestingBackend = null!;
|
||||
beforeEach(() => {
|
||||
backend = new HttpClientTestingBackend();
|
||||
client = new HttpClient(backend);
|
||||
});
|
||||
afterEach(() => { backend.verify(); });
|
||||
afterEach(() => {
|
||||
backend.verify();
|
||||
});
|
||||
describe('makes a basic request', () => {
|
||||
it('for JSON data', done => {
|
||||
client.get('/test').subscribe(res => {
|
||||
|
@ -10,7 +10,6 @@ import {HttpHeaders} from '@angular/common/http/src/headers';
|
||||
|
||||
{
|
||||
describe('HttpHeaders', () => {
|
||||
|
||||
describe('initialization', () => {
|
||||
it('should conform to spec', () => {
|
||||
const httpHeaders = {
|
||||
|
@ -18,19 +18,23 @@ export class MockScriptElement {
|
||||
this.listeners[event] = handler as any;
|
||||
}
|
||||
|
||||
removeEventListener(event: 'load'|'error'): void { delete this.listeners[event]; }
|
||||
removeEventListener(event: 'load'|'error'): void {
|
||||
delete this.listeners[event];
|
||||
}
|
||||
}
|
||||
|
||||
export class MockDocument {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
mock !: MockScriptElement | null;
|
||||
mock!: MockScriptElement|null;
|
||||
readonly body: any = this;
|
||||
|
||||
createElement(tag: 'script'): HTMLScriptElement {
|
||||
return new MockScriptElement() as any as HTMLScriptElement;
|
||||
}
|
||||
|
||||
appendChild(node: any): void { this.mock = node; }
|
||||
appendChild(node: any): void {
|
||||
this.mock = node;
|
||||
}
|
||||
|
||||
removeNode(node: any): void {
|
||||
if (this.mock === node) {
|
||||
@ -38,7 +42,11 @@ export class MockDocument {
|
||||
}
|
||||
}
|
||||
|
||||
mockLoad(): void { this.mock !.listeners.load !(null as any); }
|
||||
mockLoad(): void {
|
||||
this.mock!.listeners.load!(null as any);
|
||||
}
|
||||
|
||||
mockError(err: Error) { this.mock !.listeners.error !(err); }
|
||||
mockError(err: Error) {
|
||||
this.mock!.listeners.error!(err);
|
||||
}
|
||||
}
|
||||
|
@ -38,11 +38,15 @@ class TestInterceptor implements HttpInterceptor {
|
||||
}
|
||||
|
||||
class InterceptorA extends TestInterceptor {
|
||||
constructor() { super('A'); }
|
||||
constructor() {
|
||||
super('A');
|
||||
}
|
||||
}
|
||||
|
||||
class InterceptorB extends TestInterceptor {
|
||||
constructor() { super('B'); }
|
||||
constructor() {
|
||||
super('B');
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
@ -98,7 +102,9 @@ class ReentrantInterceptor implements HttpInterceptor {
|
||||
{provide: HTTP_INTERCEPTORS, useClass: ReentrantInterceptor, multi: true},
|
||||
],
|
||||
});
|
||||
injector.get(HttpClient).get('/test').subscribe(() => { done(); });
|
||||
injector.get(HttpClient).get('/test').subscribe(() => {
|
||||
done();
|
||||
});
|
||||
injector.get(HttpTestingController).expectOne('/test').flush('ok!');
|
||||
});
|
||||
});
|
||||
|
@ -80,16 +80,21 @@ const TEST_STRING = `I'm a body!`;
|
||||
expect(clone.headers).toBe(headers);
|
||||
expect(clone.headers.get('Test')).toBe('Test header');
|
||||
});
|
||||
it('and updates the url',
|
||||
() => { expect(req.clone({url: '/changed'}).url).toBe('/changed'); });
|
||||
it('and updates the method',
|
||||
() => { expect(req.clone({method: 'PUT'}).method).toBe('PUT'); });
|
||||
it('and updates the body',
|
||||
() => { expect(req.clone({body: 'changed body'}).body).toBe('changed body'); });
|
||||
it('and updates the url', () => {
|
||||
expect(req.clone({url: '/changed'}).url).toBe('/changed');
|
||||
});
|
||||
it('and updates the method', () => {
|
||||
expect(req.clone({method: 'PUT'}).method).toBe('PUT');
|
||||
});
|
||||
it('and updates the body', () => {
|
||||
expect(req.clone({body: 'changed body'}).body).toBe('changed body');
|
||||
});
|
||||
});
|
||||
describe('content type detection', () => {
|
||||
const baseReq = new HttpRequest('POST', '/test', null);
|
||||
it('handles a null body', () => { expect(baseReq.detectContentTypeHeader()).toBeNull(); });
|
||||
it('handles a null body', () => {
|
||||
expect(baseReq.detectContentTypeHeader()).toBeNull();
|
||||
});
|
||||
it('doesn\'t associate a content type with ArrayBuffers', () => {
|
||||
const req = baseReq.clone({body: new ArrayBuffer(4)});
|
||||
expect(req.detectContentTypeHeader()).toBeNull();
|
||||
@ -113,7 +118,9 @@ const TEST_STRING = `I'm a body!`;
|
||||
});
|
||||
describe('body serialization', () => {
|
||||
const baseReq = new HttpRequest('POST', '/test', null);
|
||||
it('handles a null body', () => { expect(baseReq.serializeBody()).toBeNull(); });
|
||||
it('handles a null body', () => {
|
||||
expect(baseReq.serializeBody()).toBeNull();
|
||||
});
|
||||
it('passes ArrayBuffers through', () => {
|
||||
const body = new ArrayBuffer(4);
|
||||
expect(baseReq.clone({body}).serializeBody()).toBe(body);
|
||||
@ -125,8 +132,9 @@ const TEST_STRING = `I'm a body!`;
|
||||
it('serializes arrays as json', () => {
|
||||
expect(baseReq.clone({body: ['a', 'b']}).serializeBody()).toBe('["a","b"]');
|
||||
});
|
||||
it('handles numbers as json',
|
||||
() => { expect(baseReq.clone({body: 314159}).serializeBody()).toBe('314159'); });
|
||||
it('handles numbers as json', () => {
|
||||
expect(baseReq.clone({body: 314159}).serializeBody()).toBe('314159');
|
||||
});
|
||||
it('handles objects as json', () => {
|
||||
const req = baseReq.clone({body: {data: 'test data'}});
|
||||
expect(req.serializeBody()).toBe('{"data":"test data"}');
|
||||
|
@ -11,9 +11,11 @@ import {XhrFactory} from '@angular/common/http/src/xhr';
|
||||
|
||||
export class MockXhrFactory implements XhrFactory {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
mock !: MockXMLHttpRequest;
|
||||
mock!: MockXMLHttpRequest;
|
||||
|
||||
build(): XMLHttpRequest { return (this.mock = new MockXMLHttpRequest()) as any; }
|
||||
build(): XMLHttpRequest {
|
||||
return (this.mock = new MockXMLHttpRequest()) as any;
|
||||
}
|
||||
}
|
||||
|
||||
export class MockXMLHttpRequestUpload {
|
||||
@ -32,9 +34,9 @@ export class MockXMLHttpRequest {
|
||||
// Set by method calls.
|
||||
body: any;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
method !: string;
|
||||
method!: string;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
url !: string;
|
||||
url!: string;
|
||||
mockHeaders: {[key: string]: string} = {};
|
||||
mockAborted: boolean = false;
|
||||
|
||||
@ -64,7 +66,9 @@ export class MockXMLHttpRequest {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
send(body: any): void { this.body = body; }
|
||||
send(body: any): void {
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
addEventListener(event: 'error'|'load'|'progress'|'uploadProgress', handler: Function): void {
|
||||
this.listeners[event] = handler as any;
|
||||
@ -74,9 +78,13 @@ export class MockXMLHttpRequest {
|
||||
delete this.listeners[event];
|
||||
}
|
||||
|
||||
setRequestHeader(name: string, value: string): void { this.mockHeaders[name] = value; }
|
||||
setRequestHeader(name: string, value: string): void {
|
||||
this.mockHeaders[name] = value;
|
||||
}
|
||||
|
||||
getAllResponseHeaders(): string { return this.mockResponseHeaders; }
|
||||
getAllResponseHeaders(): string {
|
||||
return this.mockResponseHeaders;
|
||||
}
|
||||
|
||||
getResponseHeader(header: string): string|null {
|
||||
return new HttpHeaders(this.mockResponseHeaders).get(header);
|
||||
@ -95,14 +103,17 @@ export class MockXMLHttpRequest {
|
||||
|
||||
mockDownloadProgressEvent(loaded: number, total?: number): void {
|
||||
if (this.listeners.progress) {
|
||||
this.listeners.progress({ lengthComputable: total !== undefined, loaded, total } as any);
|
||||
this.listeners.progress({lengthComputable: total !== undefined, loaded, total} as any);
|
||||
}
|
||||
}
|
||||
|
||||
mockUploadProgressEvent(loaded: number, total?: number) {
|
||||
if (this.listeners.uploadProgress) {
|
||||
this.listeners.uploadProgress(
|
||||
{ lengthComputable: total !== undefined, loaded, total, } as any);
|
||||
this.listeners.uploadProgress({
|
||||
lengthComputable: total !== undefined,
|
||||
loaded,
|
||||
total,
|
||||
} as any);
|
||||
}
|
||||
}
|
||||
|
||||
@ -118,5 +129,7 @@ export class MockXMLHttpRequest {
|
||||
}
|
||||
}
|
||||
|
||||
abort() { this.mockAborted = true; }
|
||||
abort() {
|
||||
this.mockAborted = true;
|
||||
}
|
||||
}
|
||||
|
@ -13,16 +13,22 @@ import {HttpXsrfCookieExtractor, HttpXsrfInterceptor, HttpXsrfTokenExtractor} fr
|
||||
import {HttpClientTestingBackend} from '@angular/common/http/testing/src/backend';
|
||||
|
||||
class SampleTokenExtractor extends HttpXsrfTokenExtractor {
|
||||
constructor(private token: string|null) { super(); }
|
||||
constructor(private token: string|null) {
|
||||
super();
|
||||
}
|
||||
|
||||
getToken(): string|null { return this.token; }
|
||||
getToken(): string|null {
|
||||
return this.token;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
describe('HttpXsrfInterceptor', () => {
|
||||
let backend: HttpClientTestingBackend;
|
||||
const interceptor = new HttpXsrfInterceptor(new SampleTokenExtractor('test'), 'X-XSRF-TOKEN');
|
||||
beforeEach(() => { backend = new HttpClientTestingBackend(); });
|
||||
beforeEach(() => {
|
||||
backend = new HttpClientTestingBackend();
|
||||
});
|
||||
it('applies XSRF protection to outgoing requests', () => {
|
||||
interceptor.intercept(new HttpRequest('POST', '/test', {}), backend).subscribe();
|
||||
const req = backend.expectOne('/test');
|
||||
@ -59,7 +65,9 @@ class SampleTokenExtractor extends HttpXsrfTokenExtractor {
|
||||
expect(req.request.headers.has('X-XSRF-TOKEN')).toEqual(false);
|
||||
req.flush({});
|
||||
});
|
||||
afterEach(() => { backend.verify(); });
|
||||
afterEach(() => {
|
||||
backend.verify();
|
||||
});
|
||||
});
|
||||
describe('HttpXsrfCookieExtractor', () => {
|
||||
let document: {[key: string]: string};
|
||||
@ -70,8 +78,9 @@ class SampleTokenExtractor extends HttpXsrfTokenExtractor {
|
||||
};
|
||||
extractor = new HttpXsrfCookieExtractor(document, 'browser', 'XSRF-TOKEN');
|
||||
});
|
||||
it('parses the cookie from document.cookie',
|
||||
() => { expect(extractor.getToken()).toEqual('test'); });
|
||||
it('parses the cookie from document.cookie', () => {
|
||||
expect(extractor.getToken()).toEqual('test');
|
||||
});
|
||||
it('does not re-parse if document.cookie has not changed', () => {
|
||||
expect(extractor.getToken()).toEqual('test');
|
||||
expect(extractor.getToken()).toEqual('test');
|
||||
|
@ -39,8 +39,10 @@ export class HttpClientTestingBackend implements HttpBackend, HttpTestingControl
|
||||
return new Observable((observer: Observer<any>) => {
|
||||
const testReq = new TestRequest(req, observer);
|
||||
this.open.push(testReq);
|
||||
observer.next({ type: HttpEventType.Sent } as HttpEvent<any>);
|
||||
return () => { testReq._cancelled = true; };
|
||||
observer.next({type: HttpEventType.Sent} as HttpEvent<any>);
|
||||
return () => {
|
||||
testReq._cancelled = true;
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
@ -86,8 +88,8 @@ export class HttpClientTestingBackend implements HttpBackend, HttpTestingControl
|
||||
description = description || this.descriptionFromMatcher(match);
|
||||
const matches = this.match(match);
|
||||
if (matches.length > 1) {
|
||||
throw new Error(
|
||||
`Expected one matching request for criteria "${description}", found ${matches.length} requests.`);
|
||||
throw new Error(`Expected one matching request for criteria "${description}", found ${
|
||||
matches.length} requests.`);
|
||||
}
|
||||
if (matches.length === 0) {
|
||||
let message = `Expected one matching request for criteria "${description}", found none.`;
|
||||
@ -116,8 +118,8 @@ export class HttpClientTestingBackend implements HttpBackend, HttpTestingControl
|
||||
description = description || this.descriptionFromMatcher(match);
|
||||
const matches = this.match(match);
|
||||
if (matches.length > 0) {
|
||||
throw new Error(
|
||||
`Expected zero matching requests for criteria "${description}", found ${matches.length}.`);
|
||||
throw new Error(`Expected zero matching requests for criteria "${description}", found ${
|
||||
matches.length}.`);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,9 @@ export class TestRequest {
|
||||
/**
|
||||
* Whether the request was cancelled after it was sent.
|
||||
*/
|
||||
get cancelled(): boolean { return this._cancelled; }
|
||||
get cancelled(): boolean {
|
||||
return this._cancelled;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal set by `HttpClientTestingBackend`
|
||||
@ -39,7 +41,7 @@ export class TestRequest {
|
||||
* Both successful and unsuccessful responses can be delivered via `flush()`.
|
||||
*/
|
||||
flush(body: ArrayBuffer|Blob|string|number|Object|(string|number|Object|null)[]|null, opts: {
|
||||
headers?: HttpHeaders | {[name: string]: string | string[]},
|
||||
headers?: HttpHeaders|{[name: string]: string | string[]},
|
||||
status?: number,
|
||||
statusText?: string,
|
||||
} = {}): void {
|
||||
@ -75,7 +77,7 @@ export class TestRequest {
|
||||
* Resolve the request by returning an `ErrorEvent` (e.g. simulating a network failure).
|
||||
*/
|
||||
error(error: ErrorEvent, opts: {
|
||||
headers?: HttpHeaders | {[name: string]: string | string[]},
|
||||
headers?: HttpHeaders|{[name: string]: string | string[]},
|
||||
status?: number,
|
||||
statusText?: string,
|
||||
} = {}): void {
|
||||
@ -112,9 +114,8 @@ export class TestRequest {
|
||||
/**
|
||||
* Helper function to convert a response body to an ArrayBuffer.
|
||||
*/
|
||||
function _toArrayBufferBody(
|
||||
body: ArrayBuffer | Blob | string | number | Object |
|
||||
(string | number | Object | null)[]): ArrayBuffer {
|
||||
function _toArrayBufferBody(body: ArrayBuffer|Blob|string|number|Object|
|
||||
(string | number | Object | null)[]): ArrayBuffer {
|
||||
if (typeof ArrayBuffer === 'undefined') {
|
||||
throw new Error('ArrayBuffer responses are not supported on this platform.');
|
||||
}
|
||||
@ -127,9 +128,8 @@ function _toArrayBufferBody(
|
||||
/**
|
||||
* Helper function to convert a response body to a Blob.
|
||||
*/
|
||||
function _toBlob(
|
||||
body: ArrayBuffer | Blob | string | number | Object |
|
||||
(string | number | Object | null)[]): Blob {
|
||||
function _toBlob(body: ArrayBuffer|Blob|string|number|Object|
|
||||
(string | number | Object | null)[]): Blob {
|
||||
if (typeof Blob === 'undefined') {
|
||||
throw new Error('Blob responses are not supported on this platform.');
|
||||
}
|
||||
@ -146,7 +146,7 @@ function _toBlob(
|
||||
* Helper function to convert a response body to JSON data.
|
||||
*/
|
||||
function _toJsonBody(
|
||||
body: ArrayBuffer | Blob | string | number | Object | (string | number | Object | null)[],
|
||||
body: ArrayBuffer|Blob|string|number|Object|(string | number | Object | null)[],
|
||||
format: string = 'JSON'): Object|string|number|(Object | string | number)[] {
|
||||
if (typeof ArrayBuffer !== 'undefined' && body instanceof ArrayBuffer) {
|
||||
throw new Error(`Automatic conversion to ${format} is not supported for ArrayBuffers.`);
|
||||
@ -164,9 +164,8 @@ function _toJsonBody(
|
||||
/**
|
||||
* Helper function to convert a response body to a string.
|
||||
*/
|
||||
function _toTextBody(
|
||||
body: ArrayBuffer | Blob | string | number | Object |
|
||||
(string | number | Object | null)[]): string {
|
||||
function _toTextBody(body: ArrayBuffer|Blob|string|number|Object|
|
||||
(string | number | Object | null)[]): string {
|
||||
if (typeof body === 'string') {
|
||||
return body;
|
||||
}
|
||||
@ -183,9 +182,9 @@ function _toTextBody(
|
||||
* Convert a response body to the requested type.
|
||||
*/
|
||||
function _maybeConvertBody(
|
||||
responseType: string, body: ArrayBuffer | Blob | string | number | Object |
|
||||
(string | number | Object | null)[] | null): ArrayBuffer|Blob|string|number|Object|
|
||||
(string | number | Object | null)[]|null {
|
||||
responseType: string,
|
||||
body: ArrayBuffer|Blob|string|number|Object|(string | number | Object | null)[]|
|
||||
null): ArrayBuffer|Blob|string|number|Object|(string | number | Object | null)[]|null {
|
||||
if (body === null) {
|
||||
return null;
|
||||
}
|
||||
|
@ -15,7 +15,9 @@ describe('HttpClient TestRequest', () => {
|
||||
const client = new HttpClient(mock);
|
||||
|
||||
let resp: any;
|
||||
client.post('/some-url', {test: 'test'}).subscribe(body => { resp = body; });
|
||||
client.post('/some-url', {test: 'test'}).subscribe(body => {
|
||||
resp = body;
|
||||
});
|
||||
|
||||
const req = mock.expectOne('/some-url');
|
||||
req.flush(null);
|
||||
@ -28,7 +30,9 @@ describe('HttpClient TestRequest', () => {
|
||||
const client = new HttpClient(mock);
|
||||
|
||||
let resp: any;
|
||||
client.get('/some-other-url').subscribe(body => { resp = body; });
|
||||
client.get('/some-other-url').subscribe(body => {
|
||||
resp = body;
|
||||
});
|
||||
|
||||
try {
|
||||
// expect different URL
|
||||
@ -48,7 +52,9 @@ describe('HttpClient TestRequest', () => {
|
||||
|
||||
let resp: any;
|
||||
const params = {query: 'hello'};
|
||||
client.get('/some-url', {params}).subscribe(body => { resp = body; });
|
||||
client.get('/some-url', {params}).subscribe(body => {
|
||||
resp = body;
|
||||
});
|
||||
|
||||
try {
|
||||
// expect different query parameters
|
||||
@ -67,8 +73,12 @@ describe('HttpClient TestRequest', () => {
|
||||
const client = new HttpClient(mock);
|
||||
|
||||
let resp: any;
|
||||
client.get('/some-other-url?query=world').subscribe(body => { resp = body; });
|
||||
client.post('/and-another-url', {}).subscribe(body => { resp = body; });
|
||||
client.get('/some-other-url?query=world').subscribe(body => {
|
||||
resp = body;
|
||||
});
|
||||
client.post('/and-another-url', {}).subscribe(body => {
|
||||
resp = body;
|
||||
});
|
||||
|
||||
try {
|
||||
// expect different URL
|
||||
|
Reference in New Issue
Block a user