fix(): Fixes ALL dartanalyzer problems (so far).

This commit is contained in:
Hans Larsen
2016-02-10 10:29:43 -08:00
parent 2cc9ae5aab
commit 15fb40e2a5
12 changed files with 530 additions and 50 deletions
+8 -5
View File
@@ -1,15 +1,18 @@
import {
inject,
injectAsync,
ComponentFixture,
TestComponentBuilder,
beforeEachProviders,
} from 'angular2/testing';
import {
it,
iit,
describe,
ddescribe,
expect,
inject,
injectAsync,
TestComponentBuilder,
beforeEachProviders,
beforeEach,
} from 'angular2/testing';
} from '../../core/facade/testing';
import {provide, Component, DebugElement} from 'angular2/core';
import {By} from 'angular2/platform/browser';
+10 -2
View File
@@ -1,4 +1,12 @@
import {Component, ViewEncapsulation, Input, HostBinding, HostListener, ChangeDetectionStrategy} from 'angular2/core';
import {
Component,
ViewEncapsulation,
Input,
HostBinding,
HostListener,
ChangeDetectionStrategy,
} from 'angular2/core';
import {TimerWrapper} from 'angular2/src/facade/async';
// TODO(jelbourn): Ink ripples.
// TODO(jelbourn): Make the `isMouseDown` stuff done with one global listener.
@@ -37,7 +45,7 @@ export class MdButton {
// button continues to look :active after clicking.
// @see http://marcysutton.com/button-focus-hell/
this.isMouseDown = true;
setTimeout(() => { this.isMouseDown = false; }, 100);
TimerWrapper.setTimeout(() => { this.isMouseDown = false; }, 100);
}
setKeyboardFocus($event: any) {
+30 -30
View File
@@ -1,30 +1,34 @@
import {
inject,
injectAsync,
ComponentFixture,
TestComponentBuilder,
beforeEachProviders,
} from 'angular2/testing';
import {
it,
iit,
describe,
ddescribe,
expect,
inject,
injectAsync,
TestComponentBuilder,
beforeEachProviders,
beforeEach,
} from 'angular2/testing';
} from '../../core/facade/testing';
import {
getComputedStyle
} from '../../core/facade/html';
import {provide, Component, DebugElement} from 'angular2/core';
import {By} from 'angular2/platform/browser';
import {MdSidenav, MdSidenavLayout, MD_SIDENAV_DIRECTIVES} from './sidenav';
import {AsyncTestFn, FunctionWithParamTokens} from 'angular2/testing';
import {ComponentFixture} from 'angular2/testing';
import {EventEmitter} from 'angular2/core';
import {Predicate} from 'angular2/src/facade/collection';
import {PromiseWrapper} from 'angular2/src/facade/promise';
import {TimerWrapper} from 'angular2/src/facade/async';
import {MdSidenav, MdSidenavLayout, MD_SIDENAV_DIRECTIVES} from './sidenav';
function wait(msec: number) {
let completer = PromiseWrapper.completer();
TimerWrapper.setTimeout(completer.resolve, msec);
TimerWrapper.setTimeout(() => {
completer.resolve(null);
}, msec);
return completer.promise;
}
@@ -38,7 +42,7 @@ function waitOnEvent(fixture: ComponentFixture,
let completer = PromiseWrapper.completer();
let component: any = fixture.debugElement.query(by).componentInstance;
component[propertyName].subscribe(() => {
completer.resolve();
completer.resolve(null);
});
return completer.promise;
}
@@ -63,7 +67,7 @@ export function main() {
testComponent = fixture.debugElement.componentInstance;
fixture.detectChanges();
return wait(1);
}).then((f) => {
}).then((_: any) => {
let openButtonElement = fixture.debugElement.query(By.css('.open'));
openButtonElement.nativeElement.click();
fixture.detectChanges();
@@ -81,8 +85,8 @@ export function main() {
let sidenavElement = fixture.debugElement.query(By.css('md-sidenav'));
let sidenavBackdropElement = fixture.debugElement.query(By.css('.md-sidenav-backdrop'));
expect(window.getComputedStyle(sidenavElement.nativeElement).visibility).toEqual('visible');
expect(window.getComputedStyle(sidenavBackdropElement.nativeElement).visibility).toEqual('visible');
expect(getComputedStyle(sidenavElement.nativeElement).visibility).toEqual('visible');
expect(getComputedStyle(sidenavBackdropElement.nativeElement).visibility).toEqual('visible');
// Close it.
let closeButtonElement = fixture.debugElement.query(By.css('.close'));
@@ -107,10 +111,10 @@ export function main() {
let sidenavElement = fixture.debugElement.query(By.css('md-sidenav'));
let sidenavBackdropElement = fixture.debugElement.query(By.css('.md-sidenav-backdrop'));
expect(window.getComputedStyle(sidenavElement.nativeElement).visibility).toEqual('hidden');
expect(window.getComputedStyle(sidenavBackdropElement.nativeElement).visibility).toEqual('hidden');
expect(getComputedStyle(sidenavElement.nativeElement).visibility).toEqual('hidden');
expect(getComputedStyle(sidenavBackdropElement.nativeElement).visibility).toEqual('hidden');
})
.then(done, done.fail);
.then((_: any) => { done(); });
}, 8000);
it('open() and close() return a promise that resolves after the animation ended',
@@ -149,7 +153,7 @@ export function main() {
})
.then((_: any) => promise)
.then((_: any) => expect(called).toBe(true))
.then(done, done.fail);
.then((_: any) => { done(); });
}, 8000);
it('open() twice returns the same promise', (done: any) => {
@@ -175,7 +179,7 @@ export function main() {
promise = sidenav.close();
expect(sidenav.close()).toBe(promise);
})
.then(done, done.fail);
.then((_: any) => { done(); });
});
it('open() then close() cancel animations when called too fast',
@@ -183,7 +187,6 @@ export function main() {
let fixture: ComponentFixture;
let sidenav: MdSidenav;
let openPromise: Promise<void>;
let closePromise: Promise<void>;
let openCalled: boolean = false;
let openCancelled: boolean = false;
@@ -194,7 +197,7 @@ export function main() {
fixture = f;
sidenav = fixture.debugElement.query(By.directive(MdSidenav)).componentInstance;
openPromise = sidenav.open().then((_: any) => {
sidenav.open().then((_: any) => {
openCalled = true;
},
() => {
@@ -208,7 +211,7 @@ export function main() {
.then((_: any) => {
closePromise = sidenav.close().then((_: any) => {
closeCalled = true;
}, done.fail);
});
return wait(1);
})
.then((_: any) => {
@@ -220,17 +223,15 @@ export function main() {
expect(openCancelled).toBe(true);
expect(closeCalled).toBe(true);
})
.then(done, done.fail);
.then((_: any) => { done(); });
}, 8000);
it('close() then open() cancel animations when called too fast',
(done: any) => {
let testComponent: BasicTestApp;
let fixture: ComponentFixture;
let sidenav: MdSidenav;
let openPromise: Promise<void>;
let closePromise: Promise<void>;
let closeCalled: boolean = false;
let closeCancelled: boolean = false;
let openCalled: boolean = false;
@@ -250,10 +251,9 @@ export function main() {
})
.then((_: any) => {
// Then close and check behavior.
closePromise = sidenav.close().then((_: any) => {
sidenav.close().then((_: any) => {
closeCalled = true;
},
() => {
}, () => {
closeCancelled = true;
});
})
@@ -276,7 +276,7 @@ export function main() {
expect(closeCancelled).toBe(true);
expect(openCalled).toBe(true);
})
.then(done, done.fail);
.then((_: any) => { done(); });
}, 8000);
});
});
+8 -11
View File
@@ -3,7 +3,6 @@ import {
Component,
ContentChildren,
ElementRef,
EventEmitter,
Host,
HostBinding,
HostListener,
@@ -14,7 +13,8 @@ import {
Type,
ChangeDetectionStrategy
} from 'angular2/core';
import {PromiseWrapper} from 'angular2/src/facade/promise';
import {PromiseWrapper, ObservableWrapper, EventEmitter} from 'angular2/src/facade/async';
import {iterateListLike} from 'angular2/src/facade/collection';
import {BaseException} from 'angular2/src/facade/exceptions';
import {CONST_EXPR, isPresent} from 'angular2/src/facade/lang';
import {Dir} from '../../directives/dir/dir';
@@ -128,9 +128,6 @@ export class MdSidenav {
this.onCloseStart.emit(null);
}
let emitter = isOpen ? this.onOpen : this.onClose;
let other = isOpen ? this.onClose : this.onOpen;
if (isOpen) {
if (this.openPromise_ == null) {
let completer = PromiseWrapper.completer();
@@ -259,13 +256,13 @@ export class MdSidenavLayout implements AfterContentInit {
// If a `Dir` directive exists up the tree, listen direction changes and update the left/right
// properties to point to the proper start/end.
if (dir_ != null) {
dir_.dirChange.subscribe(() => this.validateDrawers_());
dir_.dirChange.add(() => this.validateDrawers_());
}
}
ngAfterContentInit() {
// On changes, assert on consistency.
this.sidenavs_.changes.subscribe(() => this.validateDrawers_());
ObservableWrapper.subscribe(this.sidenavs_.changes, () => this.validateDrawers_());
this.validateDrawers_();
}
@@ -294,17 +291,17 @@ export class MdSidenavLayout implements AfterContentInit {
}
// Ensure that we have at most one start and one end sidenav.
this.sidenavs_.toArray().forEach(drawer => {
if (drawer.align == 'end') {
iterateListLike(this.sidenavs_, (sidenav: any) => {
if (sidenav.align == 'end') {
if (this.end_ != null) {
throw new MdDuplicatedSidenavException('end');
}
this.end_ = drawer;
this.end_ = sidenav;
} else {
if (this.start_ != null) {
throw new MdDuplicatedSidenavException('start');
}
this.start_ = drawer;
this.start_ = sidenav;
}
});
+1 -1
View File
@@ -3,5 +3,5 @@
* TODO(hansl): Implement this properly in Dart.
*/
class OneOf {
const OneOf(List<string> values);
const OneOf(List<String> values);
}
+5
View File
@@ -0,0 +1,5 @@
import "dart:html";
CssStyleDeclaration getComputedStyle(element, [String pseudoElement]) {
return element.getComputedStyle(pseudoElement);
}
+4
View File
@@ -0,0 +1,4 @@
export function getComputedStyle(element: HTMLElement, pseudoElt?: string) {
return window.getComputedStyle(element, pseudoElt);
}
+132
View File
@@ -0,0 +1,132 @@
library testing.matchers;
import 'dart:async';
import 'package:guinness/guinness.dart' as gns;
import 'package:angular2/src/platform/dom/dom_adapter.dart' show DOM;
import 'package:angular2/src/facade/lang.dart' show isString;
Expect expect(actual, [matcher]) {
final expect = new Expect(actual);
if (matcher != null) expect.to(matcher);
return expect;
}
const _u = const Object();
bool elementContainsStyle(element, styles) {
var allPassed = true;
if (isString(styles)) {
allPassed = DOM.hasStyle(element, styles);
} else {
styles.forEach((prop, style) {
allPassed = allPassed && DOM.hasStyle(element, prop, style);
});
}
return allPassed;
}
expectErrorMessage(actual, expectedMessage) {
expect(actual.toString()).toContain(expectedMessage);
}
expectException(Function actual, expectedMessage) {
try {
actual();
} catch (e, s) {
expectErrorMessage(e, expectedMessage);
}
}
class Expect extends gns.Expect {
Expect(actual) : super(actual);
NotExpect get not => new NotExpect(actual);
void toEqual(expected) => toHaveSameProps(expected);
void toContainError(message) => expectErrorMessage(this.actual, message);
void toThrowError([message = ""]) => toThrowWith(message: message);
void toThrowErrorWith(message) => expectException(this.actual, message);
void toBePromise() => gns.guinness.matchers.toBeTrue(actual is Future);
void toHaveCssClass(className) =>
gns.guinness.matchers.toBeTrue(DOM.hasClass(actual, className));
void toHaveCssStyle(styles) {
gns.guinness.matchers.toBeTrue(elementContainsStyle(actual, styles));
}
void toImplement(expected) => toBeA(expected);
void toBeNaN() =>
gns.guinness.matchers.toBeTrue(double.NAN.compareTo(actual) == 0);
void toHaveText(expected) => _expect(elementText(actual), expected);
void toHaveBeenCalledWith([a = _u, b = _u, c = _u, d = _u, e = _u, f = _u]) =>
_expect(_argsMatch(actual, a, b, c, d, e, f), true,
reason: 'method invoked with correct arguments');
Function get _expect => gns.guinness.matchers.expect;
// TODO(tbosch): move this hack into Guinness
_argsMatch(spyFn, [a0 = _u, a1 = _u, a2 = _u, a3 = _u, a4 = _u, a5 = _u]) {
var calls = spyFn.calls;
final toMatch = _takeDefined([a0, a1, a2, a3, a4, a5]);
if (calls.isEmpty) {
return false;
} else {
gns.SamePropsMatcher matcher = new gns.SamePropsMatcher(toMatch);
for (var i = 0; i < calls.length; i++) {
var call = calls[i];
// TODO: create a better error message, not just 'Expected: <true> Actual: <false>'.
// For hacking this is good:
// print(call.positionalArguments);
if (matcher.matches(call.positionalArguments, null)) {
return true;
}
}
return false;
}
}
List _takeDefined(List iter) => iter.takeWhile((_) => _ != _u).toList();
}
class NotExpect extends gns.NotExpect {
NotExpect(actual) : super(actual);
void toEqual(expected) => toHaveSameProps(expected);
void toBePromise() => gns.guinness.matchers.toBeFalse(actual is Future);
void toHaveCssClass(className) =>
gns.guinness.matchers.toBeFalse(DOM.hasClass(actual, className));
void toHaveCssStyle(styles) {
gns.guinness.matchers.toBeFalse(elementContainsStyle(actual, styles));
}
void toBeNull() => gns.guinness.matchers.toBeFalse(actual == null);
Function get _expect => gns.guinness.matchers.expect;
}
String elementText(n) {
hasNodes(n) {
var children = DOM.childNodes(n);
return children != null && children.length > 0;
}
if (n is Iterable) {
return n.map(elementText).join("");
}
if (DOM.isCommentNode(n)) {
return '';
}
if (DOM.isElementNode(n) && DOM.tagName(n) == 'CONTENT') {
return elementText(DOM.getDistributedNodes(n));
}
if (DOM.hasShadowRoot(n)) {
return elementText(DOM.childNodesAsList(DOM.getShadowRoot(n)));
}
if (hasNodes(n)) {
return elementText(DOM.childNodesAsList(n));
}
return DOM.getText(n);
}
+156
View File
@@ -0,0 +1,156 @@
import {Injector, Provider, PLATFORM_INITIALIZER} from 'angular2/core';
import {BaseException, ExceptionHandler} from 'angular2/src/facade/exceptions';
import {ListWrapper} from 'angular2/src/facade/collection';
import {FunctionWrapper, isPresent, Type} from 'angular2/src/facade/lang';
export class TestInjector {
private _instantiated: boolean = false;
private _injector: Injector = null;
private _providers: Array<Type | Provider | any[]> = [];
reset() {
this._injector = null;
this._providers = [];
this._instantiated = false;
}
platformProviders: Array<Type | Provider | any[]> = [];
applicationProviders: Array<Type | Provider | any[]> = [];
addProviders(providers: Array<Type | Provider | any[]>) {
if (this._instantiated) {
throw new BaseException('Cannot add providers after test injector is instantiated');
}
this._providers = ListWrapper.concat(this._providers, providers);
}
createInjector() {
var rootInjector = Injector.resolveAndCreate(this.platformProviders);
this._injector = rootInjector.resolveAndCreateChild(
ListWrapper.concat(this.applicationProviders, this._providers));
this._instantiated = true;
return this._injector;
}
execute(fn: FunctionWithParamTokens): any {
if (!this._instantiated) {
this.createInjector();
}
return fn.execute(this._injector);
}
}
var _testInjector: TestInjector = null;
export function getTestInjector() {
if (_testInjector == null) {
_testInjector = new TestInjector();
}
return _testInjector;
}
/**
* Set the providers that the test injector should use. These should be providers
* common to every test in the suite.
*
* This may only be called once, to set up the common providers for the current test
* suite on teh current platform. If you absolutely need to change the providers,
* first use `resetBaseTestProviders`.
*
* Test Providers for individual platforms are available from
* 'angular2/platform/testing/<platform_name>'.
*/
export function setBaseTestProviders(platformProviders: Array<Type | Provider | any[]>,
applicationProviders: Array<Type | Provider | any[]>) {
var testInjector = getTestInjector();
if (testInjector.platformProviders.length > 0 || testInjector.applicationProviders.length > 0) {
throw new BaseException('Cannot set base providers because it has already been called');
}
testInjector.platformProviders = platformProviders;
testInjector.applicationProviders = applicationProviders;
var injector = testInjector.createInjector();
let inits: Function[] = <Function[]>injector.getOptional(PLATFORM_INITIALIZER);
if (isPresent(inits)) {
inits.forEach(init => init());
}
testInjector.reset();
}
/**
* Reset the providers for the test injector.
*/
export function resetBaseTestProviders() {
var testInjector = getTestInjector();
testInjector.platformProviders = [];
testInjector.applicationProviders = [];
testInjector.reset();
}
/**
* Allows injecting dependencies in `beforeEach()` and `it()`.
*
* Example:
*
* ```
* beforeEach(inject([Dependency, AClass], (dep, object) => {
* // some code that uses `dep` and `object`
* // ...
* }));
*
* it('...', inject([AClass], (object) => {
* object.doSomething();
* expect(...);
* })
* ```
*
* Notes:
* - inject is currently a function because of some Traceur limitation the syntax should
* eventually
* becomes `it('...', @Inject (object: AClass, async: AsyncTestCompleter) => { ... });`
*
* @param {Array} tokens
* @param {Function} fn
* @return {FunctionWithParamTokens}
*/
export function inject(tokens: any[], fn: Function): FunctionWithParamTokens {
return new FunctionWithParamTokens(tokens, fn, false);
}
/**
* Allows injecting dependencies in `beforeEach()` and `it()`. The test must return
* a promise which will resolve when all asynchronous activity is complete.
*
* Example:
*
* ```
* it('...', injectAsync([AClass], (object) => {
* return object.doSomething().then(() => {
* expect(...);
* });
* })
* ```
*
* @param {Array} tokens
* @param {Function} fn
* @return {FunctionWithParamTokens}
*/
export function injectAsync(tokens: any[], fn: Function): FunctionWithParamTokens {
return new FunctionWithParamTokens(tokens, fn, true);
}
export class FunctionWithParamTokens {
constructor(private _tokens: any[], private _fn: Function, public isAsync: boolean) {}
/**
* Returns the value of the executed function.
*/
execute(injector: Injector): any {
var params = this._tokens.map(t => injector.get(t));
return FunctionWrapper.apply(this._fn, params);
}
hasToken(token: any): boolean { return this._tokens.indexOf(token) > -1; }
}
+166
View File
@@ -0,0 +1,166 @@
library angular2.src.testing.testing_internal;
import 'dart:async';
import 'package:guinness/guinness.dart' as gns;
export 'package:guinness/guinness.dart'
hide
Expect,
expect,
NotExpect,
beforeEach,
it,
iit,
xit,
SpyObject,
SpyFunction;
export 'matchers.dart' show expect, Expect, NotExpect;
import 'package:angular2/src/core/reflection/reflection.dart';
import 'package:angular2/src/core/reflection/reflection_capabilities.dart';
import 'package:angular2/src/core/di/provider.dart' show bind;
import 'package:angular2/src/facade/collection.dart' show StringMapWrapper;
import 'test_injector.dart';
export 'test_injector.dart' show inject;
TestInjector _testInjector = getTestInjector();
bool _isCurrentTestAsync;
Future _currentTestFuture;
bool _inIt = false;
class AsyncTestCompleter {
final _completer = new Completer();
AsyncTestCompleter() {
_currentTestFuture = this.future;
}
void done() {
_completer.complete();
}
Future get future => _completer.future;
}
void testSetup() {
reflector.reflectionCapabilities = new ReflectionCapabilities();
// beforeEach configuration:
// - Priority 3: clear the bindings before each test,
// - Priority 2: collect the bindings before each test, see beforeEachProviders(),
// - Priority 1: create the test injector to be used in beforeEach() and it()
gns.beforeEach(() {
_testInjector.reset();
_currentTestFuture = null;
}, priority: 3);
var completerProvider = bind(AsyncTestCompleter).toFactory(() {
// Mark the test as async when an AsyncTestCompleter is injected in an it(),
if (!_inIt) throw 'AsyncTestCompleter can only be injected in an "it()"';
_isCurrentTestAsync = true;
return new AsyncTestCompleter();
});
gns.beforeEach(() {
_isCurrentTestAsync = false;
_testInjector.addProviders([completerProvider]);
}, priority: 1);
}
/**
* Allows overriding default providers defined in test_injector.js.
*
* The given function must return a list of DI providers.
*
* Example:
*
* beforeEachProviders(() => [
* bind(Compiler).toClass(MockCompiler),
* bind(SomeToken).toValue(myValue),
* ]);
*/
void beforeEachProviders(Function fn) {
gns.beforeEach(() {
var providers = fn();
if (providers != null) _testInjector.addProviders(providers);
}, priority: 2);
}
@Deprecated('using beforeEachProviders instead')
void beforeEachBindings(Function fn) {
beforeEachProviders(fn);
}
void beforeEach(fn) {
if (fn is! FunctionWithParamTokens) fn =
new FunctionWithParamTokens([], fn, false);
gns.beforeEach(() {
_testInjector.execute(fn);
});
}
void _it(gnsFn, name, fn) {
if (fn is! FunctionWithParamTokens) fn =
new FunctionWithParamTokens([], fn, false);
gnsFn(name, () {
_inIt = true;
_testInjector.execute(fn);
_inIt = false;
if (_isCurrentTestAsync) return _currentTestFuture;
});
}
void it(name, fn, [timeOut = null]) {
_it(gns.it, name, fn);
}
void iit(name, fn, [timeOut = null]) {
_it(gns.iit, name, fn);
}
void xit(name, fn, [timeOut = null]) {
_it(gns.xit, name, fn);
}
class SpyFunction extends gns.SpyFunction {
SpyFunction(String name) : super(name);
// TODO: vsavkin move to guinness
andReturn(value) {
return andCallFake(([a0, a1, a2, a3, a4, a5]) => value);
}
}
class SpyObject extends gns.SpyObject {
final Map<String, SpyFunction> _spyFuncs = {};
SpyObject([arg]) {}
SpyFunction spy(String funcName) =>
_spyFuncs.putIfAbsent(funcName, () => new SpyFunction(funcName));
void prop(String funcName, value) {
_spyFuncs
.putIfAbsent("get:${funcName}", () => new SpyFunction(funcName))
.andReturn(value);
}
static stub([object = null, config = null, overrides = null]) {
if (object is! SpyObject) {
overrides = config;
config = object;
object = new SpyObject();
}
var m = StringMapWrapper.merge(config, overrides);
StringMapWrapper.forEach(m, (value, key) {
object.spy(key).andReturn(value);
});
return object;
}
}
bool isInInnerZone() => Zone.current['_innerZone'] == true;
+8
View File
@@ -0,0 +1,8 @@
export {
it,
iit,
describe,
ddescribe,
expect,
beforeEach,
} from 'angular2/testing';
+2 -1
View File
@@ -1,4 +1,5 @@
import {Directive, HostBinding, EventEmitter, Output, Input} from 'angular2/core';
import {EventEmitter} from 'angular2/src/facade/async';
import {Directive, HostBinding, Output, Input} from 'angular2/core';
import {OneOf} from "../../core/annotations/one-of";