feat(http): refactor library to work in dart

Mostly internal refactoring needed to make ts2dart and DartAnalyzer happy.

Fixes #2415
This commit is contained in:
Jeff Cross
2015-06-19 12:14:12 -07:00
parent fa7da0ca5d
commit 55bf0e554f
26 changed files with 424 additions and 379 deletions
@@ -14,13 +14,15 @@ import {BrowserXHR} from 'angular2/src/http/backends/browser_xhr';
import {XHRConnection, XHRBackend} from 'angular2/src/http/backends/xhr_backend';
import {bind, Injector} from 'angular2/di';
import {Request} from 'angular2/src/http/static_request';
import {StringMapWrapper} from 'angular2/src/facade/collection';
import {RequestOptions} from 'angular2/src/http/base_request_options';
var abortSpy;
var sendSpy;
var openSpy;
var addEventListenerSpy;
class MockBrowserXHR extends SpyObject {
class MockBrowserXHR extends BrowserXHR {
abort: any;
send: any;
open: any;
@@ -29,24 +31,26 @@ class MockBrowserXHR extends SpyObject {
responseText: string;
constructor() {
super();
this.abort = abortSpy = this.spy('abort');
this.send = sendSpy = this.spy('send');
this.open = openSpy = this.spy('open');
this.addEventListener = addEventListenerSpy = this.spy('addEventListener');
var spy = new SpyObject();
this.abort = abortSpy = spy.spy('abort');
this.send = sendSpy = spy.spy('send');
this.open = openSpy = spy.spy('open');
this.addEventListener = addEventListenerSpy = spy.spy('addEventListener');
}
build() { return new MockBrowserXHR(); }
}
export function main() {
describe('XHRBackend', () => {
var backend;
var sampleRequest;
var constructSpy = new SpyObject();
beforeEach(() => {
var injector =
Injector.resolveAndCreate([bind(BrowserXHR).toValue(MockBrowserXHR), XHRBackend]);
Injector.resolveAndCreate([bind(BrowserXHR).toClass(MockBrowserXHR), XHRBackend]);
backend = injector.get(XHRBackend);
sampleRequest = new Request('https://google.com');
sampleRequest = new Request(new RequestOptions({url: 'https://google.com'}));
});
it('should create a connection',
@@ -55,22 +59,21 @@ export function main() {
describe('XHRConnection', () => {
it('should call abort when disposed', () => {
var connection = new XHRConnection(sampleRequest, MockBrowserXHR);
var connection = new XHRConnection(sampleRequest, new MockBrowserXHR());
connection.dispose();
expect(abortSpy).toHaveBeenCalled();
});
it('should automatically call open with method and url', () => {
new XHRConnection(sampleRequest, MockBrowserXHR);
new XHRConnection(sampleRequest, new MockBrowserXHR());
expect(openSpy).toHaveBeenCalledWith('GET', sampleRequest.url);
});
it('should automatically call send on the backend with request body', () => {
var body = 'Some body to love';
var request = new Request('https://google.com', {body: body});
var connection = new XHRConnection(request, MockBrowserXHR);
new XHRConnection(new Request(new RequestOptions({body: body})), new MockBrowserXHR());
expect(sendSpy).toHaveBeenCalledWith(body);
});
});
+20 -20
View File
@@ -1,5 +1,5 @@
import {Headers} from 'angular2/src/http/headers';
import {Map} from 'angular2/src/facade/collection';
import {Map, StringMapWrapper} from 'angular2/src/facade/collection';
import {
AsyncTestCompleter,
beforeEach,
@@ -17,27 +17,24 @@ export function main() {
it('should conform to spec', () => {
// Examples borrowed from https://developer.mozilla.org/en-US/docs/Web/API/Headers/Headers
// Spec at https://fetch.spec.whatwg.org/#dom-headers
var myHeaders = new Headers(); // Currently empty
myHeaders.append('Content-Type', 'image/jpeg');
expect(myHeaders.get('Content-Type')).toBe('image/jpeg');
var httpHeaders = {
'Content-Type': 'image/jpeg',
'Accept-Charset': 'utf-8',
'X-My-Custom-Header': 'Zeke are cool'
};
var myHeaders = new Headers(httpHeaders);
var secondHeadersObj = new Headers(myHeaders);
var firstHeaders = new Headers(); // Currently empty
firstHeaders.append('Content-Type', 'image/jpeg');
expect(firstHeaders.get('Content-Type')).toBe('image/jpeg');
var httpHeaders = StringMapWrapper.create();
StringMapWrapper.set(httpHeaders, 'Content-Type', 'image/jpeg');
StringMapWrapper.set(httpHeaders, 'Accept-Charset', 'utf-8');
StringMapWrapper.set(httpHeaders, 'X-My-Custom-Header', 'Zeke are cool');
var secondHeaders = new Headers(httpHeaders);
var secondHeadersObj = new Headers(secondHeaders);
expect(secondHeadersObj.get('Content-Type')).toBe('image/jpeg');
});
describe('initialization', () => {
it('should create a private headersMap map',
() => { expect(new Headers()._headersMap).toBeAnInstanceOf(Map); });
it('should merge values in provided dictionary', () => {
var headers = new Headers({foo: 'bar'});
var map = StringMapWrapper.create();
StringMapWrapper.set(map, 'foo', 'bar');
var headers = new Headers(map);
expect(headers.get('foo')).toBe('bar');
expect(headers.getAll('foo')).toEqual(['bar']);
});
@@ -46,7 +43,9 @@ export function main() {
describe('.set()', () => {
it('should clear all values and re-set for the provided key', () => {
var headers = new Headers({foo: 'bar'});
var map = StringMapWrapper.create();
StringMapWrapper.set(map, 'foo', 'bar');
var headers = new Headers(map);
expect(headers.get('foo')).toBe('bar');
expect(headers.getAll('foo')).toEqual(['bar']);
headers.set('foo', 'baz');
@@ -57,9 +56,10 @@ export function main() {
it('should convert input array to string', () => {
var headers = new Headers();
headers.set('foo', ['bar', 'baz']);
expect(headers.get('foo')).toBe('bar,baz');
expect(headers.getAll('foo')).toEqual(['bar,baz']);
var inputArr = ['bar', 'baz'];
headers.set('foo', inputArr);
expect(/bar, ?baz/g.test(headers.get('foo'))).toBe(true);
expect(/bar, ?baz/g.test(headers.getAll('foo')[0])).toBe(true);
});
});
});
+89 -118
View File
@@ -1,5 +1,6 @@
import {
AsyncTestCompleter,
afterEach,
beforeEach,
ddescribe,
describe,
@@ -11,13 +12,14 @@ import {
SpyObject
} from 'angular2/test_lib';
import {Http, HttpFactory} from 'angular2/src/http/http';
import {XHRBackend} from 'angular2/src/http/backends/xhr_backend';
import {Injector, bind} from 'angular2/di';
import {MockBackend} from 'angular2/src/http/backends/mock_backend';
import {Response} from 'angular2/src/http/static_response';
import {RequestMethods} from 'angular2/src/http/enums';
import {BaseRequestOptions} from 'angular2/src/http/base_request_options';
import {BaseRequestOptions, RequestOptions} from 'angular2/src/http/base_request_options';
import {Request} from 'angular2/src/http/static_request';
import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
import {ConnectionBackend} from 'angular2/src/http/interfaces';
class SpyObserver extends SpyObject {
onNext: Function;
@@ -34,20 +36,19 @@ class SpyObserver extends SpyObject {
export function main() {
describe('http', () => {
var url = 'http://foo.bar';
var http;
var injector;
var http: Http;
var injector: Injector;
var backend: MockBackend;
var baseResponse;
var sampleObserver;
var httpFactory;
beforeEach(() => {
injector = Injector.resolveAndCreate([
BaseRequestOptions,
MockBackend,
bind(XHRBackend).toClass(MockBackend),
bind(ConnectionBackend).toClass(MockBackend),
bind(HttpFactory).toFactory(HttpFactory, [MockBackend, BaseRequestOptions]),
bind(Http).toFactory(
function(backend: XHRBackend, defaultOptions: BaseRequestOptions) {
function(backend: ConnectionBackend, defaultOptions: BaseRequestOptions) {
return new Http(backend, defaultOptions);
},
[MockBackend, BaseRequestOptions])
@@ -55,224 +56,194 @@ export function main() {
http = injector.get(Http);
httpFactory = injector.get(HttpFactory);
backend = injector.get(MockBackend);
baseResponse = new Response('base response');
sampleObserver = new SpyObserver();
baseResponse = new Response({body: 'base response'});
});
afterEach(() => backend.verifyNoPendingRequests());
describe('HttpFactory', () => {
it('should return an Observable', () => {
expect(typeof httpFactory(url).subscribe).toBe('function');
expect(ObservableWrapper.isObservable(httpFactory(url))).toBe(true);
backend.resolveAllConnections();
});
it('should perform a get request for given url if only passed a string',
inject([AsyncTestCompleter], (async) => {
var connection;
backend.connections.subscribe((c) => connection = c);
var subscription = httpFactory('http://basic.connection')
.subscribe(res => {
expect(res.text()).toBe('base response');
async.done();
});
connection.mockRespond(baseResponse)
ObservableWrapper.subscribe(backend.connections, c => c.mockRespond(baseResponse));
ObservableWrapper.subscribe(httpFactory('http://basic.connection'), res => {
expect(res.text()).toBe('base response');
async.done();
});
}));
it('should accept a fully-qualified request as its only parameter', () => {
var req = new Request('https://google.com');
backend.connections.subscribe(c => {
expect(c.request.url).toBe('https://google.com');
c.mockRespond(new Response('Thank you'));
});
httpFactory(req).subscribe(() => {});
});
it('should perform a get request for given url if passed a ConnectionConfig instance',
inject([AsyncTestCompleter], async => {
var connection;
backend.connections.subscribe((c) => connection = c);
httpFactory('http://basic.connection', {method: RequestMethods.GET})
.subscribe(res => {
expect(res.text()).toBe('base response');
async.done();
});
connection.mockRespond(baseResponse)
it('should accept a fully-qualified request as its only parameter',
inject([AsyncTestCompleter], (async) => {
var req = new Request(new RequestOptions({url: 'https://google.com'}));
ObservableWrapper.subscribe(backend.connections, c => {
expect(c.request.url).toBe('https://google.com');
c.mockRespond(new Response({body: 'Thank you'}));
async.done();
});
ObservableWrapper.subscribe(httpFactory(req), (res) => {});
}));
it('should perform a get request for given url if passed a dictionary',
inject([AsyncTestCompleter], async => {
var connection;
backend.connections.subscribe((c) => connection = c);
httpFactory(url, {method: RequestMethods.GET})
.subscribe(res => {
expect(res.text()).toBe('base response');
async.done();
});
connection.mockRespond(baseResponse)
}));
// TODO: make dart not complain about "argument type 'Map' cannot be assigned to the parameter
// type 'IRequestOptions'"
// xit('should perform a get request for given url if passed a dictionary',
// inject([AsyncTestCompleter], async => {
// ObservableWrapper.subscribe(backend.connections, c => c.mockRespond(baseResponse));
// ObservableWrapper.subscribe(httpFactory(url, {method: RequestMethods.GET}), res => {
// expect(res.text()).toBe('base response');
// async.done();
// });
// }));
});
describe('Http', () => {
describe('.request()', () => {
it('should return an Observable',
() => { expect(typeof http.request(url).subscribe).toBe('function'); });
() => { expect(ObservableWrapper.isObservable(http.request(url))).toBe(true); });
it('should accept a fully-qualified request as its only parameter', () => {
var req = new Request('https://google.com');
backend.connections.subscribe(c => {
expect(c.request.url).toBe('https://google.com');
c.mockRespond(new Response('Thank you'));
});
http.request(req).subscribe(() => {});
});
it('should accept a fully-qualified request as its only parameter',
inject([AsyncTestCompleter], (async) => {
ObservableWrapper.subscribe(backend.connections, c => {
expect(c.request.url).toBe('https://google.com');
c.mockRespond(new Response({body: 'Thank you'}));
async.done();
});
ObservableWrapper.subscribe(
http.request(new Request(new RequestOptions({url: 'https://google.com'}))),
(res) => {});
}));
it('should perform a get request for given url if only passed a string',
inject([AsyncTestCompleter], (async) => {
ObservableWrapper.subscribe(backend.connections, c => c.mockRespond(baseResponse));
ObservableWrapper.subscribe(http.request('http://basic.connection'), res => {
expect(res.text()).toBe('base response');
async.done();
});
}));
// TODO: make dart not complain about "argument type 'Map' cannot be assigned to the
// parameter type 'IRequestOptions'"
// xit('should perform a get request for given url if passed a dictionary',
// inject([AsyncTestCompleter], async => {
// ObservableWrapper.subscribe(backend.connections, c => c.mockRespond(baseResponse));
// ObservableWrapper.subscribe(http.request(url, {method: RequestMethods.GET}), res =>
// {
// expect(res.text()).toBe('base response');
// async.done();
// });
// }));
});
it('should perform a get request for given url if only passed a string',
inject([AsyncTestCompleter], (async) => {
var connection;
backend.connections.subscribe((c) => connection = c);
var subscription = http.request('http://basic.connection')
.subscribe(res => {
expect(res.text()).toBe('base response');
async.done();
});
connection.mockRespond(baseResponse)
}));
it('should perform a get request for given url if passed a ConnectionConfig instance',
inject([AsyncTestCompleter], async => {
var connection;
backend.connections.subscribe((c) => connection = c);
http.request('http://basic.connection', {method: RequestMethods.GET})
.subscribe(res => {
expect(res.text()).toBe('base response');
async.done();
});
connection.mockRespond(baseResponse);
}));
it('should perform a get request for given url if passed a dictionary',
inject([AsyncTestCompleter], async => {
var connection;
backend.connections.subscribe((c) => connection = c);
http.request(url, {method: RequestMethods.GET})
.subscribe(res => {
expect(res.text()).toBe('base response');
async.done();
});
connection.mockRespond(baseResponse);
}));
describe('.get()', () => {
it('should perform a get request for given url', inject([AsyncTestCompleter], async => {
backend.connections.subscribe((c) => {
ObservableWrapper.subscribe(backend.connections, c => {
expect(c.request.method).toBe(RequestMethods.GET);
backend.resolveAllConnections();
async.done();
});
http.get(url).subscribe(res => {});
ObservableWrapper.subscribe(http.get(url), res => {});
}));
});
describe('.post()', () => {
it('should perform a post request for given url', inject([AsyncTestCompleter], async => {
backend.connections.subscribe((c) => {
ObservableWrapper.subscribe(backend.connections, c => {
expect(c.request.method).toBe(RequestMethods.POST);
backend.resolveAllConnections();
async.done();
});
http.post(url).subscribe(res => {});
ObservableWrapper.subscribe(http.post(url, 'post me'), res => {});
}));
it('should attach the provided body to the request', inject([AsyncTestCompleter], async => {
var body = 'this is my put body';
backend.connections.subscribe((c) => {
var body = 'this is my post body';
ObservableWrapper.subscribe(backend.connections, c => {
expect(c.request.text()).toBe(body);
backend.resolveAllConnections();
async.done();
});
http.post(url, body).subscribe(res => {});
ObservableWrapper.subscribe(http.post(url, body), res => {});
}));
});
describe('.put()', () => {
it('should perform a put request for given url', inject([AsyncTestCompleter], async => {
backend.connections.subscribe((c) => {
ObservableWrapper.subscribe(backend.connections, c => {
expect(c.request.method).toBe(RequestMethods.PUT);
backend.resolveAllConnections();
async.done();
});
http.put(url).subscribe(res => {});
ObservableWrapper.subscribe(http.put(url, 'put me'), res => {});
}));
it('should attach the provided body to the request', inject([AsyncTestCompleter], async => {
var body = 'this is my put body';
backend.connections.subscribe((c) => {
ObservableWrapper.subscribe(backend.connections, c => {
expect(c.request.text()).toBe(body);
backend.resolveAllConnections();
async.done();
});
http.put(url, body).subscribe(res => {});
ObservableWrapper.subscribe(http.put(url, body), res => {});
}));
});
describe('.delete()', () => {
it('should perform a delete request for given url', inject([AsyncTestCompleter], async => {
backend.connections.subscribe((c) => {
ObservableWrapper.subscribe(backend.connections, c => {
expect(c.request.method).toBe(RequestMethods.DELETE);
backend.resolveAllConnections();
async.done();
});
http.delete(url).subscribe(res => {});
ObservableWrapper.subscribe(http.delete(url), res => {});
}));
});
describe('.patch()', () => {
it('should perform a patch request for given url', inject([AsyncTestCompleter], async => {
backend.connections.subscribe((c) => {
ObservableWrapper.subscribe(backend.connections, c => {
expect(c.request.method).toBe(RequestMethods.PATCH);
backend.resolveAllConnections();
async.done();
});
http.patch(url).subscribe(res => {});
ObservableWrapper.subscribe(http.patch(url, 'this is my patch body'), res => {});
}));
it('should attach the provided body to the request', inject([AsyncTestCompleter], async => {
var body = 'this is my put body';
backend.connections.subscribe((c) => {
var body = 'this is my patch body';
ObservableWrapper.subscribe(backend.connections, c => {
expect(c.request.text()).toBe(body);
backend.resolveAllConnections();
async.done();
});
http.patch(url, body).subscribe(res => {});
ObservableWrapper.subscribe(http.patch(url, body), res => {});
}));
});
describe('.head()', () => {
it('should perform a head request for given url', inject([AsyncTestCompleter], async => {
backend.connections.subscribe((c) => {
ObservableWrapper.subscribe(backend.connections, c => {
expect(c.request.method).toBe(RequestMethods.HEAD);
backend.resolveAllConnections();
async.done();
});
http.head(url).subscribe(res => {});
ObservableWrapper.subscribe(http.head(url), res => {});
}));
});
});
@@ -22,14 +22,14 @@ export function main() {
// Compliant with spec described at https://url.spec.whatwg.org/#urlsearchparams
expect(searchParams.has("topic")).toBe(true);
expect(searchParams.has("foo")).toBe(false);
expect(searchParams.get("topic")).toBe("api");
expect(searchParams.get("topic")).toEqual("api");
expect(searchParams.getAll("topic")).toEqual(["api"]);
expect(searchParams.get("foo")).toBe(null);
searchParams.append("topic", "webdev");
expect(searchParams.getAll("topic")).toEqual(["api", "webdev"]);
expect(searchParams.toString()).toBe("q=URLUtils.searchParams&topic=api&topic=webdev");
expect(searchParams.toString()).toEqual("q=URLUtils.searchParams&topic=api&topic=webdev");
searchParams.delete("topic");
expect(searchParams.toString()).toBe("q=URLUtils.searchParams");
expect(searchParams.toString()).toEqual("q=URLUtils.searchParams");
});
});
}