feat(common): two missing features in HttpClient (#17996)

- Add params to HttpRequest API
- Add optional description to testing APIs
This commit is contained in:
Alex Rickabaugh
2017-07-07 14:56:36 -07:00
committed by Jason Aden
parent 37797e2b4e
commit c81ad9d19d
14 changed files with 487 additions and 198 deletions

View File

@ -33,16 +33,16 @@ export abstract class HttpTestingController {
abstract match(match: string|RequestMatch|((req: HttpRequest<any>) => boolean)): TestRequest[];
// Expect that exactly one request matches the given parameter.
abstract expectOne(url: string): TestRequest;
abstract expectOne(params: RequestMatch): TestRequest;
abstract expectOne(matchFn: ((req: HttpRequest<any>) => boolean)): TestRequest;
abstract expectOne(match: string|RequestMatch|((req: HttpRequest<any>) => boolean)): TestRequest;
abstract expectOne(url: string, description?: string): TestRequest;
abstract expectOne(params: RequestMatch, description?: string): TestRequest;
abstract expectOne(matchFn: ((req: HttpRequest<any>) => boolean), description?: string): TestRequest;
abstract expectOne(match: string|RequestMatch|((req: HttpRequest<any>) => boolean), description?: string): TestRequest;
// Assert that no requests match the given parameter.
abstract expectNone(url: string): void;
abstract expectNone(params: RequestMatch): void;
abstract expectNone(matchFn: ((req: HttpRequest<any>) => boolean)): void;
abstract expectNone(match: string|RequestMatch|((req: HttpRequest<any>) => boolean)): void;
abstract expectNone(url: string, description?: string): void;
abstract expectNone(params: RequestMatch, description?: string): void;
abstract expectNone(matchFn: ((req: HttpRequest<any>) => boolean), description?: string): void;
abstract expectNone(match: string|RequestMatch|((req: HttpRequest<any>) => boolean), description?: string): void;
// Validate that all requests which were issued were flushed.
abstract verify(opts?: {ignoreCancelled?: boolean}): void;

View File

@ -51,13 +51,13 @@ export class HttpClientTestingBackend implements HttpBackend, HttpTestingControl
*/
private _match(match: string|RequestMatch|((req: HttpRequest<any>) => boolean)): TestRequest[] {
if (typeof match === 'string') {
return this.open.filter(testReq => testReq.request.url === match);
return this.open.filter(testReq => testReq.request.urlWithParams === match);
} else if (typeof match === 'function') {
return this.open.filter(testReq => match(testReq.request));
} else {
return this.open.filter(
testReq => (!match.method || testReq.request.method === match.method.toUpperCase()) &&
(!match.url || testReq.request.url === match.url));
(!match.url || testReq.request.urlWithParams === match.url));
}
}
@ -83,13 +83,14 @@ export class HttpClientTestingBackend implements HttpBackend, HttpTestingControl
* Requests returned through this API will no longer be in the list of open requests,
* and thus will not match twice.
*/
expectOne(match: string|RequestMatch|((req: HttpRequest<any>) => boolean)): TestRequest {
expectOne(match: string|RequestMatch|((req: HttpRequest<any>) => boolean), description?: string): TestRequest {
description = description || this.descriptionFromMatcher(match);
const matches = this.match(match);
if (matches.length > 1) {
throw new Error(`Expected one matching request, found ${matches.length} requests.`);
throw new Error(`Expected one matching request for criteria "${description}", found ${matches.length} requests.`);
}
if (matches.length === 0) {
throw new Error(`Expected one matching request, found none.`);
throw new Error(`Expected one matching request for criteria "${description}", found none.`);
}
return matches[0];
}
@ -98,10 +99,11 @@ export class HttpClientTestingBackend implements HttpBackend, HttpTestingControl
* Expect that no outstanding requests match the given matcher, and throw an error
* if any do.
*/
expectNone(match: string|RequestMatch|((req: HttpRequest<any>) => boolean)): void {
expectNone(match: string|RequestMatch|((req: HttpRequest<any>) => boolean), description?: string): void {
description = description || this.descriptionFromMatcher(match);
const matches = this.match(match);
if (matches.length > 0) {
throw new Error(`Expected zero matching requests, found ${matches.length}.`);
throw new Error(`Expected zero matching requests for criteria "${description}", found ${matches.length}.`);
}
}
@ -116,9 +118,25 @@ export class HttpClientTestingBackend implements HttpBackend, HttpTestingControl
open = open.filter(testReq => !testReq.cancelled);
}
if (open.length > 0) {
// Show the URLs of open requests in the error, for convenience.
const urls = open.map(testReq => testReq.request.url.split('?')[0]).join(', ');
throw new Error(`Expected no open requests, found ${open.length}: ${urls}`);
// Show the methods and URLs of open requests in the error, for convenience.
const requests = open.map(testReq => {
const url = testReq.request.urlWithParams.split('?')[0];
const method = testReq.request.method;
return `${method} ${url}`
}).join(', ');
throw new Error(`Expected no open requests, found ${open.length}: ${requests}`);
}
}
private descriptionFromMatcher(matcher: string|RequestMatch|((req: HttpRequest<any>) => boolean)): string {
if (typeof matcher === 'string') {
return `Match URL: ${matcher}`;
} else if (typeof matcher === 'object') {
const method = matcher.method || '(any)';
const url = matcher.url || '(any)';
return `Match method: ${method}, URL: ${url}`;
} else {
return `Match by function: ${matcher.name}`;
}
}
}

View File

@ -39,7 +39,7 @@ export class TestRequest {
if (this.cancelled) {
throw new Error(`Cannot flush a cancelled request.`);
}
const url = this.request.url;
const url = this.request.urlWithParams;
const headers =
(opts.headers instanceof HttpHeaders) ? opts.headers : new HttpHeaders(opts.headers);
body = _maybeConvertBody(this.request.responseType, body);
@ -83,7 +83,7 @@ export class TestRequest {
headers,
status: opts.status || 0,
statusText: opts.statusText || '',
url: this.request.url,
url: this.request.urlWithParams,
}));
}