fix(testing): let DOM adapter dictate XHR implementation for tests

The test injector now uses an XHR implementation based on DOM.getXHR,
which allows the current DOM adapter to dictate which XHR impl should
be used.

To prevent the changes to DOM adapter from introducing undesired new
dependencies into the benchmarks, separate the async facade into
a promise facade which is reexported by facade/async.

See #4539
This commit is contained in:
Julie Ralph 2015-10-14 09:41:15 -07:00
parent 65c737fc95
commit d7ab5d44a5
12 changed files with 136 additions and 116 deletions

View File

@ -1,9 +1,7 @@
import {Injectable} from 'angular2/src/core/di';
import {Promise, PromiseWrapper, PromiseCompleter} from 'angular2/src/core/facade/async';
import {Promise, PromiseWrapper, PromiseCompleter} from 'angular2/src/core/facade/promise';
import {isPresent} from 'angular2/src/core/facade/lang';
import {XHR} from './xhr';
@Injectable()
export class XHRImpl extends XHR {
get(url: string): Promise<string> {
var completer: PromiseCompleter < string >= PromiseWrapper.completer();

View File

@ -5,6 +5,7 @@ import 'package:html/dom.dart';
import 'dom_adapter.dart';
import 'emulated_css.dart';
import '../compiler/xhr.dart';
const _attrToPropMap = const {
'innerHtml': 'innerHTML',
@ -66,6 +67,9 @@ abstract class AbstractHtml5LibAdapter implements DomAdapter {
throw 'not implemented';
}
@override
Type getXHR() => XHR;
Element parse(String templateHtml) => parser.parse(templateHtml).firstChild;
query(selector) {
throw 'not implemented';

View File

@ -1,4 +1,4 @@
import {isBlank} from 'angular2/src/core/facade/lang';
import {isBlank, Type} from 'angular2/src/core/facade/lang';
export var DOM: DomAdapter;
@ -23,6 +23,8 @@ export abstract class DomAdapter {
abstract logGroup(error);
abstract logGroupEnd();
abstract getXHR(): Type;
/**
* Maps attribute names to their corresponding property names for cases
* where attribute name doesn't match property name.

View File

@ -1,6 +1,8 @@
import {ListWrapper, StringMapWrapper} from 'angular2/src/core/facade/collection';
import {isPresent, isFunction, StringWrapper} from 'angular2/src/core/facade/lang';
import {isPresent, isFunction, StringWrapper, Type} from 'angular2/src/core/facade/lang';
import {DomAdapter} from './dom_adapter';
import {XHRImpl} from 'angular2/src/core/compiler/xhr_impl';
/**
* Provides DOM operations in any browser environment.
@ -39,6 +41,8 @@ export abstract class GenericBrowserDomAdapter extends DomAdapter {
this._transitionEnd = null;
}
}
getXHR(): Type { return XHRImpl; }
getDistributedNodes(el: HTMLElement): Node[] { return (<any>el).getDistributedNodes(); }
resolveAndSetHref(el: HTMLAnchorElement, baseUrl: string, href: string) {
el.href = href == null ? baseUrl : baseUrl + '/../' + href;

View File

@ -11,11 +11,13 @@ import {
isPresent,
isBlank,
global,
Type,
setValueOnPath,
DateWrapper
} from 'angular2/src/core/facade/lang';
import {BaseException, WrappedException} from 'angular2/src/core/facade/exceptions';
import {SelectorMatcher, CssSelector} from 'angular2/src/core/compiler/selector';
import {XHR} from 'angular2/src/core/compiler/xhr';
var _attrToPropMap: {[key: string]: string} = {
'class': 'className',
@ -61,6 +63,8 @@ export class Parse5DomAdapter extends DomAdapter {
logGroupEnd() {}
getXHR(): Type { return XHR; }
get attrToPropMap() { return _attrToPropMap; }
query(selector) { throw _notImplemented('query'); }

View File

@ -1,37 +1,9 @@
library angular2.core.facade.async;
import 'dart:async';
export 'dart:async' show Future, Stream, StreamController, StreamSubscription;
export 'dart:async' show Stream, StreamController, StreamSubscription;
class PromiseWrapper {
static Future resolve(obj) => new Future.value(obj);
static Future reject(obj, stackTrace) => new Future.error(obj,
stackTrace != null ? stackTrace : obj is Error ? obj.stackTrace : null);
static Future<List> all(List<dynamic> promises) {
return Future
.wait(promises.map((p) => p is Future ? p : new Future.value(p)));
}
static Future then(Future promise, success(value), [Function onError]) {
if (success == null) return promise.catchError(onError);
return promise.then(success, onError: onError);
}
static Future wrap(Function fn) {
return new Future(fn);
}
// Note: We can't rename this method to `catch`, as this is not a valid
// method name in Dart.
static Future catchError(Future promise, Function onError) {
return promise.catchError(onError);
}
static PromiseCompleter<dynamic> completer() =>
new PromiseCompleter(new Completer());
}
export 'promise.dart';
class TimerWrapper {
static Timer setTimeout(fn(), int millis) =>
@ -105,22 +77,3 @@ class EventEmitter extends Stream {
_controller.close();
}
}
class PromiseCompleter<T> {
final Completer<T> c;
PromiseCompleter(this.c);
Future get promise => c.future;
void resolve(v) {
c.complete(v);
}
void reject(error, stack) {
if (stack == null && error is Error) {
stack = error.stackTrace;
}
c.completeError(error, stack);
}
}

View File

@ -1,60 +1,11 @@
import {global, isPresent} from 'angular2/src/core/facade/lang';
// We make sure promises are in a separate file so that we can use promises
// without depending on rxjs.
import {PromiseWrapper, Promise, PromiseCompleter} from 'angular2/src/core/facade/promise';
export {PromiseWrapper, Promise, PromiseCompleter} from 'angular2/src/core/facade/promise';
// TODO(jeffbcross): use ES6 import once typings are available
var Subject = require('@reactivex/rxjs/dist/cjs/Subject');
export {Promise};
export interface PromiseCompleter<R> {
promise: Promise<R>;
resolve: (value?: R | PromiseLike<R>) => void;
reject: (error?: any, stackTrace?: string) => void;
}
export class PromiseWrapper {
static resolve<T>(obj: T): Promise<T> { return Promise.resolve(obj); }
static reject(obj: any, _): Promise<any> { return Promise.reject(obj); }
// Note: We can't rename this method into `catch`, as this is not a valid
// method name in Dart.
static catchError<T>(promise: Promise<T>,
onError: (error: any) => T | PromiseLike<T>): Promise<T> {
return promise.catch(onError);
}
static all(promises: any[]): Promise<any> {
if (promises.length == 0) return Promise.resolve([]);
return Promise.all(promises);
}
static then<T, U>(promise: Promise<T>, success: (value: T) => U | PromiseLike<U>,
rejection?: (error: any, stack?: any) => U | PromiseLike<U>): Promise<U> {
return promise.then(success, rejection);
}
static wrap<T>(computation: () => T): Promise<T> {
return new Promise((res, rej) => {
try {
res(computation());
} catch (e) {
rej(e);
}
});
}
static completer(): PromiseCompleter<any> {
var resolve;
var reject;
var p = new Promise(function(res, rej) {
resolve = res;
reject = rej;
});
return {promise: p, resolve: resolve, reject: reject};
}
}
export namespace NodeJS {
export interface Timer {}
}

View File

@ -0,0 +1,53 @@
library angular2.core.facade.promise;
import 'dart:async';
export 'dart:async' show Future;
class PromiseWrapper {
static Future resolve(obj) => new Future.value(obj);
static Future reject(obj, stackTrace) => new Future.error(obj,
stackTrace != null ? stackTrace : obj is Error ? obj.stackTrace : null);
static Future<List> all(List<dynamic> promises) {
return Future
.wait(promises.map((p) => p is Future ? p : new Future.value(p)));
}
static Future then(Future promise, success(value), [Function onError]) {
if (success == null) return promise.catchError(onError);
return promise.then(success, onError: onError);
}
static Future wrap(Function fn) {
return new Future(fn);
}
// Note: We can't rename this method to `catch`, as this is not a valid
// method name in Dart.
static Future catchError(Future promise, Function onError) {
return promise.catchError(onError);
}
static PromiseCompleter<dynamic> completer() =>
new PromiseCompleter(new Completer());
}
class PromiseCompleter<T> {
final Completer<T> c;
PromiseCompleter(this.c);
Future get promise => c.future;
void resolve(v) {
c.complete(v);
}
void reject(error, stack) {
if (stack == null && error is Error) {
stack = error.stackTrace;
}
c.completeError(error, stack);
}
}

View File

@ -0,0 +1,54 @@
// Promises are put into their own facade file so that they can be used without
// introducing a dependency on rxjs. They are re-exported through facade/async.
export {Promise};
export interface PromiseCompleter<R> {
promise: Promise<R>;
resolve: (value?: R | PromiseLike<R>) => void;
reject: (error?: any, stackTrace?: string) => void;
}
export class PromiseWrapper {
static resolve<T>(obj: T): Promise<T> { return Promise.resolve(obj); }
static reject(obj: any, _): Promise<any> { return Promise.reject(obj); }
// Note: We can't rename this method into `catch`, as this is not a valid
// method name in Dart.
static catchError<T>(promise: Promise<T>,
onError: (error: any) => T | PromiseLike<T>): Promise<T> {
return promise.catch(onError);
}
static all(promises: any[]): Promise<any> {
if (promises.length == 0) return Promise.resolve([]);
return Promise.all(promises);
}
static then<T, U>(promise: Promise<T>, success: (value: T) => U | PromiseLike<U>,
rejection?: (error: any, stack?: any) => U | PromiseLike<U>): Promise<U> {
return promise.then(success, rejection);
}
static wrap<T>(computation: () => T): Promise<T> {
return new Promise((res, rej) => {
try {
res(computation());
} catch (e) {
rej(e);
}
});
}
static completer(): PromiseCompleter<any> {
var resolve;
var reject;
var p = new Promise(function(res, rej) {
resolve = res;
reject = rej;
});
return {promise: p, resolve: resolve, reject: reject};
}
}

View File

@ -115,7 +115,7 @@ function _getAppBindings() {
PipeResolver,
provide(ExceptionHandler, {useValue: new ExceptionHandler(DOM)}),
provide(LocationStrategy, {useClass: MockLocationStrategy}),
XHR,
provide(XHR, {useClass: DOM.getXHR()}),
TestComponentBuilder,
provide(NgZone, {useClass: MockNgZone}),
provide(AnimationBuilder, {useClass: MockAnimationBuilder}),

View File

@ -16,6 +16,8 @@ import {
import {Injectable, NgIf, bind} from 'angular2/core';
import {Directive, Component, View, ViewMetadata} from 'angular2/angular2';
import {XHR} from 'angular2/src/core/compiler/xhr';
import {XHRImpl} from 'angular2/src/core/compiler/xhr_impl';
// Services, and components for the tests.
@ -118,6 +120,9 @@ export function main() {
}, 0);
});
it('provides a real XHR instance',
inject([XHR], (xhr) => { expect(xhr).toBeAnInstanceOf(XHRImpl); }));
describe('setting up Providers', () => {
beforeEachProviders(() => [bind(FancyService).toValue(new FancyService())]);

View File

@ -20,9 +20,6 @@ import {MdButton, MdAnchor} from 'angular2_material/src/components/button/button
import {TestUrlResolver} from './test_url_resolver';
import {XHR} from 'angular2/src/core/compiler/xhr';
import {XHRImpl} from 'angular2/src/core/compiler/xhr_impl';
export function main() {
describe('MdButton', () => {
@ -33,11 +30,6 @@ export function main() {
// with both JS and Dart output.
bind(UrlResolver)
.toValue(new TestUrlResolver()),
// Need to use the real XHR implementation (instead of the mock) so we can actually request
// the template files, since Angular 2 doesn't have anything like $templateCache. This should
// eventually be replaced with a preprocessor that inlines templates.
provide(XHR, {useClass: XHRImpl})
]);
beforeEach(inject([TestComponentBuilder], (tcb) => { builder = tcb; }));