feat(testing): Use NgZone in TestComponentBuilder.
Instantiating the test component within an NgZone will let us track async tasks in event handlers and change detection. We can also do auto change detection when triggering events through dispatchEvent and not have to do fixture.detectChange() manually in the test. New API: ComponentFixture.autoDetectChanges() - This puts the fixture in auto detect mode that automatically calls detectChanges when the microtask queue is empty (Similar to how change detection is triggered in an actual application). ComponentFixture.isStable() - This returns a boolean whether the fixture is currently stable or has some async tasks that need to be completed. ComponentFixture.whenStable() - This returns a promise that is resolved when the fixture is stable after all async tasks are complete. Closes #8301
This commit is contained in:
parent
ac55e1e27b
commit
769835e53e
|
@ -20,13 +20,17 @@ import {MockNgZone} from 'angular2/src/mock/ng_zone_mock';
|
|||
import {XHRImpl} from "angular2/src/platform/browser/xhr_impl";
|
||||
import {XHR} from 'angular2/compiler';
|
||||
|
||||
import {TestComponentBuilder} from 'angular2/src/testing/test_component_builder';
|
||||
import {
|
||||
TestComponentBuilder,
|
||||
ComponentFixtureAutoDetect,
|
||||
ComponentFixtureNoNgZone
|
||||
} from 'angular2/src/testing/test_component_builder';
|
||||
|
||||
import {BrowserDetection} from 'angular2/src/testing/utils';
|
||||
|
||||
import {ELEMENT_PROBE_PROVIDERS} from 'angular2/platform/common_dom';
|
||||
|
||||
import {CONST_EXPR} from 'angular2/src/facade/lang';
|
||||
import {CONST_EXPR, IS_DART} from 'angular2/src/facade/lang';
|
||||
|
||||
import {Log} from 'angular2/src/testing/utils';
|
||||
|
||||
|
@ -35,6 +39,10 @@ function initBrowserTests() {
|
|||
BrowserDetection.setup();
|
||||
}
|
||||
|
||||
function createNgZone(): NgZone {
|
||||
return IS_DART ? new MockNgZone() : new NgZone({enableLongStackTrace: true});
|
||||
}
|
||||
|
||||
/**
|
||||
* Default platform providers for testing without a compiler.
|
||||
*/
|
||||
|
@ -52,7 +60,7 @@ export const ADDITIONAL_TEST_BROWSER_PROVIDERS: Array<any /*Type | Provider | an
|
|||
new Provider(ViewResolver, {useClass: MockViewResolver}),
|
||||
Log,
|
||||
TestComponentBuilder,
|
||||
new Provider(NgZone, {useClass: MockNgZone}),
|
||||
new Provider(NgZone, {useFactory: createNgZone}),
|
||||
new Provider(LocationStrategy, {useClass: MockLocationStrategy}),
|
||||
new Provider(AnimationBuilder, {useClass: MockAnimationBuilder}),
|
||||
]);
|
||||
|
|
|
@ -18,6 +18,7 @@ import {MockViewResolver} from 'angular2/src/mock/view_resolver_mock';
|
|||
import {MockLocationStrategy} from 'angular2/src/mock/mock_location_strategy';
|
||||
import {MockNgZone} from 'angular2/src/mock/ng_zone_mock';
|
||||
|
||||
import {createNgZone} from 'angular2/src/core/application_ref';
|
||||
import {TestComponentBuilder} from 'angular2/src/testing/test_component_builder';
|
||||
import {XHR} from 'angular2/src/compiler/xhr';
|
||||
import {BrowserDetection} from 'angular2/src/testing/utils';
|
||||
|
@ -85,7 +86,7 @@ export const TEST_SERVER_APPLICATION_PROVIDERS: Array<any /*Type | Provider | an
|
|||
new Provider(ViewResolver, {useClass: MockViewResolver}),
|
||||
Log,
|
||||
TestComponentBuilder,
|
||||
new Provider(NgZone, {useClass: MockNgZone}),
|
||||
new Provider(NgZone, {useFactory: createNgZone}),
|
||||
new Provider(LocationStrategy, {useClass: MockLocationStrategy}),
|
||||
new Provider(AnimationBuilder, {useClass: MockAnimationBuilder}),
|
||||
]);
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import {
|
||||
OpaqueToken,
|
||||
ComponentRef,
|
||||
DynamicComponentLoader,
|
||||
Injector,
|
||||
|
@ -7,12 +8,15 @@ import {
|
|||
ElementRef,
|
||||
EmbeddedViewRef,
|
||||
ChangeDetectorRef,
|
||||
provide
|
||||
provide,
|
||||
NgZone,
|
||||
NgZoneError
|
||||
} from 'angular2/core';
|
||||
import {DirectiveResolver, ViewResolver} from 'angular2/compiler';
|
||||
|
||||
import {Type, isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||
import {PromiseWrapper} from 'angular2/src/facade/async';
|
||||
import {BaseException} from 'angular2/src/facade/exceptions';
|
||||
import {Type, isPresent, isBlank, IS_DART} from 'angular2/src/facade/lang';
|
||||
import {PromiseWrapper, ObservableWrapper, PromiseCompleter} from 'angular2/src/facade/async';
|
||||
import {ListWrapper, MapWrapper} from 'angular2/src/facade/collection';
|
||||
|
||||
import {el} from './utils';
|
||||
|
@ -24,6 +28,9 @@ import {DebugNode, DebugElement, getDebugNode} from 'angular2/src/core/debug/deb
|
|||
|
||||
import {tick} from './fake_async';
|
||||
|
||||
export var ComponentFixtureAutoDetect = new OpaqueToken("ComponentFixtureAutoDetect");
|
||||
export var ComponentFixtureNoNgZone = new OpaqueToken("ComponentFixtureNoNgZone");
|
||||
|
||||
/**
|
||||
* Fixture for debugging and testing a component.
|
||||
*/
|
||||
|
@ -58,31 +65,136 @@ export class ComponentFixture {
|
|||
*/
|
||||
changeDetectorRef: ChangeDetectorRef;
|
||||
|
||||
constructor(componentRef: ComponentRef) {
|
||||
/**
|
||||
* The NgZone in which this component was instantiated.
|
||||
*/
|
||||
ngZone: NgZone;
|
||||
|
||||
private _autoDetect: boolean;
|
||||
|
||||
private _isStable: boolean = true;
|
||||
private _completer: PromiseCompleter<any> = null;
|
||||
private _onUnstableSubscription = null;
|
||||
private _onStableSubscription = null;
|
||||
private _onMicrotaskEmptySubscription = null;
|
||||
private _onErrorSubscription = null;
|
||||
|
||||
constructor(componentRef: ComponentRef, ngZone: NgZone, autoDetect: boolean) {
|
||||
this.changeDetectorRef = componentRef.changeDetectorRef;
|
||||
this.elementRef = componentRef.location;
|
||||
this.debugElement = <DebugElement>getDebugNode(this.elementRef.nativeElement);
|
||||
this.componentInstance = componentRef.instance;
|
||||
this.nativeElement = this.elementRef.nativeElement;
|
||||
this.componentRef = componentRef;
|
||||
this.ngZone = ngZone;
|
||||
this._autoDetect = autoDetect;
|
||||
|
||||
if (ngZone != null) {
|
||||
this._onUnstableSubscription =
|
||||
ObservableWrapper.subscribe(ngZone.onUnstable, (_) => { this._isStable = false; });
|
||||
this._onMicrotaskEmptySubscription =
|
||||
ObservableWrapper.subscribe(ngZone.onMicrotaskEmpty, (_) => {
|
||||
if (this._autoDetect) {
|
||||
// Do a change detection run with checkNoChanges set to true to check
|
||||
// there are no changes on the second run.
|
||||
this.detectChanges(true);
|
||||
}
|
||||
});
|
||||
this._onStableSubscription = ObservableWrapper.subscribe(ngZone.onStable, (_) => {
|
||||
this._isStable = true;
|
||||
if (this._completer != null) {
|
||||
this._completer.resolve(true);
|
||||
this._completer = null;
|
||||
}
|
||||
});
|
||||
|
||||
this._onErrorSubscription = ObservableWrapper.subscribe(
|
||||
ngZone.onError, (error: NgZoneError) => { throw error.error; });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger a change detection cycle for the component.
|
||||
*/
|
||||
detectChanges(checkNoChanges: boolean = true): void {
|
||||
private _tick(checkNoChanges: boolean) {
|
||||
this.changeDetectorRef.detectChanges();
|
||||
if (checkNoChanges) {
|
||||
this.checkNoChanges();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger a change detection cycle for the component.
|
||||
*/
|
||||
detectChanges(checkNoChanges: boolean = true): void {
|
||||
if (this.ngZone != null) {
|
||||
// Run the change detection inside the NgZone so that any async tasks as part of the change
|
||||
// detection are captured by the zone and can be waited for in isStable.
|
||||
this.ngZone.run(() => { this._tick(checkNoChanges); });
|
||||
} else {
|
||||
// Running without zone. Just do the change detection.
|
||||
this._tick(checkNoChanges);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Do a change detection run to make sure there were no changes.
|
||||
*/
|
||||
checkNoChanges(): void { this.changeDetectorRef.checkNoChanges(); }
|
||||
|
||||
/**
|
||||
* Set whether the fixture should autodetect changes.
|
||||
*
|
||||
* Also runs detectChanges once so that any existing change is detected.
|
||||
*/
|
||||
autoDetectChanges(autoDetect: boolean = true) {
|
||||
if (this.ngZone == null) {
|
||||
throw new BaseException('Cannot call autoDetectChanges when ComponentFixtureNoNgZone is set');
|
||||
}
|
||||
this._autoDetect = autoDetect;
|
||||
this.detectChanges();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the fixture is currently stable or has async tasks that have not been completed
|
||||
* yet.
|
||||
*/
|
||||
isStable(): boolean { return this._isStable; }
|
||||
|
||||
/**
|
||||
* Get a promise that resolves when the fixture is stable.
|
||||
*
|
||||
* This can be used to resume testing after events have triggered asynchronous activity or
|
||||
* asynchronous change detection.
|
||||
*/
|
||||
whenStable(): Promise<any> {
|
||||
if (this._isStable) {
|
||||
return PromiseWrapper.resolve(false);
|
||||
} else {
|
||||
this._completer = new PromiseCompleter<any>();
|
||||
return this._completer.promise;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger component destruction.
|
||||
*/
|
||||
destroy(): void { this.componentRef.destroy(); }
|
||||
destroy(): void {
|
||||
this.componentRef.destroy();
|
||||
if (this._onUnstableSubscription != null) {
|
||||
ObservableWrapper.dispose(this._onUnstableSubscription);
|
||||
this._onUnstableSubscription = null;
|
||||
}
|
||||
if (this._onStableSubscription != null) {
|
||||
ObservableWrapper.dispose(this._onStableSubscription);
|
||||
this._onStableSubscription = null;
|
||||
}
|
||||
if (this._onMicrotaskEmptySubscription != null) {
|
||||
ObservableWrapper.dispose(this._onMicrotaskEmptySubscription);
|
||||
this._onMicrotaskEmptySubscription = null;
|
||||
}
|
||||
if (this._onErrorSubscription != null) {
|
||||
ObservableWrapper.dispose(this._onErrorSubscription);
|
||||
this._onErrorSubscription = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var _nextRootElementId = 0;
|
||||
|
@ -108,7 +220,7 @@ export class TestComponentBuilder {
|
|||
|
||||
/** @internal */
|
||||
_clone(): TestComponentBuilder {
|
||||
var clone = new TestComponentBuilder(this._injector);
|
||||
let clone = new TestComponentBuilder(this._injector);
|
||||
clone._viewOverrides = MapWrapper.clone(this._viewOverrides);
|
||||
clone._directiveOverrides = MapWrapper.clone(this._directiveOverrides);
|
||||
clone._templateOverrides = MapWrapper.clone(this._templateOverrides);
|
||||
|
@ -127,7 +239,7 @@ export class TestComponentBuilder {
|
|||
* @return {TestComponentBuilder}
|
||||
*/
|
||||
overrideTemplate(componentType: Type, template: string): TestComponentBuilder {
|
||||
var clone = this._clone();
|
||||
let clone = this._clone();
|
||||
clone._templateOverrides.set(componentType, template);
|
||||
return clone;
|
||||
}
|
||||
|
@ -141,7 +253,7 @@ export class TestComponentBuilder {
|
|||
* @return {TestComponentBuilder}
|
||||
*/
|
||||
overrideView(componentType: Type, view: ViewMetadata): TestComponentBuilder {
|
||||
var clone = this._clone();
|
||||
let clone = this._clone();
|
||||
clone._viewOverrides.set(componentType, view);
|
||||
return clone;
|
||||
}
|
||||
|
@ -156,8 +268,8 @@ export class TestComponentBuilder {
|
|||
* @return {TestComponentBuilder}
|
||||
*/
|
||||
overrideDirective(componentType: Type, from: Type, to: Type): TestComponentBuilder {
|
||||
var clone = this._clone();
|
||||
var overridesForComponent = clone._directiveOverrides.get(componentType);
|
||||
let clone = this._clone();
|
||||
let overridesForComponent = clone._directiveOverrides.get(componentType);
|
||||
if (!isPresent(overridesForComponent)) {
|
||||
clone._directiveOverrides.set(componentType, new Map<Type, Type>());
|
||||
overridesForComponent = clone._directiveOverrides.get(componentType);
|
||||
|
@ -182,7 +294,7 @@ export class TestComponentBuilder {
|
|||
* @return {TestComponentBuilder}
|
||||
*/
|
||||
overrideProviders(type: Type, providers: any[]): TestComponentBuilder {
|
||||
var clone = this._clone();
|
||||
let clone = this._clone();
|
||||
clone._bindingsOverrides.set(type, providers);
|
||||
return clone;
|
||||
}
|
||||
|
@ -210,7 +322,7 @@ export class TestComponentBuilder {
|
|||
* @return {TestComponentBuilder}
|
||||
*/
|
||||
overrideViewProviders(type: Type, providers: any[]): TestComponentBuilder {
|
||||
var clone = this._clone();
|
||||
let clone = this._clone();
|
||||
clone._viewBindingsOverrides.set(type, providers);
|
||||
return clone;
|
||||
}
|
||||
|
@ -228,40 +340,49 @@ export class TestComponentBuilder {
|
|||
* @return {Promise<ComponentFixture>}
|
||||
*/
|
||||
createAsync(rootComponentType: Type): Promise<ComponentFixture> {
|
||||
var mockDirectiveResolver = this._injector.get(DirectiveResolver);
|
||||
var mockViewResolver = this._injector.get(ViewResolver);
|
||||
this._viewOverrides.forEach((view, type) => mockViewResolver.setView(type, view));
|
||||
this._templateOverrides.forEach((template, type) =>
|
||||
mockViewResolver.setInlineTemplate(type, template));
|
||||
this._directiveOverrides.forEach((overrides, component) => {
|
||||
overrides.forEach(
|
||||
(to, from) => { mockViewResolver.overrideViewDirective(component, from, to); });
|
||||
});
|
||||
this._bindingsOverrides.forEach((bindings, type) =>
|
||||
mockDirectiveResolver.setBindingsOverride(type, bindings));
|
||||
this._viewBindingsOverrides.forEach(
|
||||
(bindings, type) => mockDirectiveResolver.setViewBindingsOverride(type, bindings));
|
||||
let noNgZone = IS_DART || this._injector.get(ComponentFixtureNoNgZone, false);
|
||||
let ngZone: NgZone = noNgZone ? null : this._injector.get(NgZone, null);
|
||||
let autoDetect: boolean = this._injector.get(ComponentFixtureAutoDetect, false);
|
||||
|
||||
var rootElId = `root${_nextRootElementId++}`;
|
||||
var rootEl = el(`<div id="${rootElId}"></div>`);
|
||||
var doc = this._injector.get(DOCUMENT);
|
||||
let initComponent = () => {
|
||||
let mockDirectiveResolver = this._injector.get(DirectiveResolver);
|
||||
let mockViewResolver = this._injector.get(ViewResolver);
|
||||
this._viewOverrides.forEach((view, type) => mockViewResolver.setView(type, view));
|
||||
this._templateOverrides.forEach((template, type) =>
|
||||
mockViewResolver.setInlineTemplate(type, template));
|
||||
this._directiveOverrides.forEach((overrides, component) => {
|
||||
overrides.forEach(
|
||||
(to, from) => { mockViewResolver.overrideViewDirective(component, from, to); });
|
||||
});
|
||||
this._bindingsOverrides.forEach(
|
||||
(bindings, type) => mockDirectiveResolver.setBindingsOverride(type, bindings));
|
||||
this._viewBindingsOverrides.forEach(
|
||||
(bindings, type) => mockDirectiveResolver.setViewBindingsOverride(type, bindings));
|
||||
|
||||
// TODO(juliemr): can/should this be optional?
|
||||
var oldRoots = DOM.querySelectorAll(doc, '[id^=root]');
|
||||
for (var i = 0; i < oldRoots.length; i++) {
|
||||
DOM.remove(oldRoots[i]);
|
||||
}
|
||||
DOM.appendChild(doc.body, rootEl);
|
||||
let rootElId = `root${_nextRootElementId++}`;
|
||||
let rootEl = el(`<div id="${rootElId}"></div>`);
|
||||
let doc = this._injector.get(DOCUMENT);
|
||||
|
||||
var promise: Promise<ComponentRef> =
|
||||
this._injector.get(DynamicComponentLoader)
|
||||
.loadAsRoot(rootComponentType, `#${rootElId}`, this._injector);
|
||||
return promise.then((componentRef) => { return new ComponentFixture(componentRef); });
|
||||
// TODO(juliemr): can/should this be optional?
|
||||
let oldRoots = DOM.querySelectorAll(doc, '[id^=root]');
|
||||
for (let i = 0; i < oldRoots.length; i++) {
|
||||
DOM.remove(oldRoots[i]);
|
||||
}
|
||||
DOM.appendChild(doc.body, rootEl);
|
||||
|
||||
let promise: Promise<ComponentRef> =
|
||||
this._injector.get(DynamicComponentLoader)
|
||||
.loadAsRoot(rootComponentType, `#${rootElId}`, this._injector);
|
||||
return promise.then(
|
||||
(componentRef) => { return new ComponentFixture(componentRef, ngZone, autoDetect); });
|
||||
};
|
||||
|
||||
return ngZone == null ? initComponent() : ngZone.run(initComponent);
|
||||
}
|
||||
|
||||
createFakeAsync(rootComponentType: Type): ComponentFixture {
|
||||
var result;
|
||||
var error;
|
||||
let result;
|
||||
let error;
|
||||
PromiseWrapper.then(this.createAsync(rootComponentType), (_result) => { result = _result; },
|
||||
(_error) => { error = _error; });
|
||||
tick();
|
||||
|
|
|
@ -157,7 +157,7 @@ export class InjectSetupWrapper {
|
|||
inject(tokens: any[], fn: Function): Function {
|
||||
return () => {
|
||||
this._addProviders();
|
||||
return inject(tokens, fn)();
|
||||
return inject_impl(tokens, fn)();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -165,7 +165,7 @@ export class InjectSetupWrapper {
|
|||
injectAsync(tokens: any[], fn: Function): Function {
|
||||
return () => {
|
||||
this._addProviders();
|
||||
return injectAsync(tokens, fn)();
|
||||
return injectAsync_impl(tokens, fn)();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -197,3 +197,8 @@ export function withProviders(providers: () => any) {
|
|||
export function injectAsync(tokens: any[], fn: Function): Function {
|
||||
return async(inject(tokens, fn));
|
||||
}
|
||||
|
||||
// This is to ensure inject(Async) within InjectSetupWrapper doesn't call itself
|
||||
// when transpiled to Dart.
|
||||
var inject_impl = inject;
|
||||
var injectAsync_impl = injectAsync;
|
||||
|
|
|
@ -146,7 +146,7 @@ export function main() {
|
|||
DOM.appendChild(doc.body, rootEl);
|
||||
loader.loadAsRoot(ChildComp, null, injector)
|
||||
.then((componentRef) => {
|
||||
var el = new ComponentFixture(componentRef);
|
||||
var el = new ComponentFixture(componentRef, null, false);
|
||||
|
||||
expect(rootEl.parentNode).toBe(doc.body);
|
||||
|
||||
|
|
|
@ -9,15 +9,20 @@ import {
|
|||
iit,
|
||||
inject,
|
||||
beforeEachProviders,
|
||||
withProviders,
|
||||
it,
|
||||
xit,
|
||||
TestComponentBuilder
|
||||
TestComponentBuilder,
|
||||
ComponentFixtureAutoDetect,
|
||||
ComponentFixtureNoNgZone
|
||||
} from 'angular2/testing_internal';
|
||||
|
||||
import {CONST_EXPR} from 'angular2/src/facade/lang';
|
||||
import {Injectable, provide} from 'angular2/core';
|
||||
import {NgIf} from 'angular2/common';
|
||||
import {Directive, Component, ViewMetadata} from 'angular2/src/core/metadata';
|
||||
import {Directive, Component, ViewMetadata, Input} from 'angular2/src/core/metadata';
|
||||
import {IS_DART} from 'angular2/src/facade/lang';
|
||||
import {PromiseWrapper} from 'angular2/src/facade/promise';
|
||||
|
||||
@Component(
|
||||
{selector: 'child-comp', template: `<span>Original {{childBinding}}</span>`, directives: []})
|
||||
|
@ -72,7 +77,42 @@ class ChildWithChildComp {
|
|||
class MockChildChildComp {
|
||||
}
|
||||
|
||||
@Component({selector: 'autodetect-comp', template: `<span (click)='click()'>{{text}}</span>`})
|
||||
class AutoDetectComp {
|
||||
text: string = '1';
|
||||
|
||||
click() { this.text += '1'; }
|
||||
}
|
||||
|
||||
@Component({selector: 'async-comp', template: `<span (click)='click()'>{{text}}</span>`})
|
||||
class AsyncComp {
|
||||
text: string = '1';
|
||||
|
||||
click() {
|
||||
PromiseWrapper.resolve(null).then((_) => { this.text += '1'; });
|
||||
}
|
||||
}
|
||||
|
||||
@Component({selector: 'async-child-comp', template: '<span>{{localText}}</span>'})
|
||||
class AsyncChildComp {
|
||||
localText: string = '';
|
||||
|
||||
@Input()
|
||||
set text(value: string) {
|
||||
PromiseWrapper.resolve(null).then((_) => { this.localText = value; });
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'async-change-comp',
|
||||
template: `<async-child-comp (click)='click()' [text]="text"></async-child-comp>`,
|
||||
directives: [AsyncChildComp]
|
||||
})
|
||||
class AsyncChangeComp {
|
||||
text: string = '1';
|
||||
|
||||
click() { this.text += '1'; }
|
||||
}
|
||||
|
||||
class FancyService {
|
||||
value: string = 'real value';
|
||||
|
@ -244,5 +284,184 @@ export function main() {
|
|||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
if (!IS_DART) {
|
||||
describe('ComponentFixture', () => {
|
||||
it('should auto detect changes if autoDetectChanges is called',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async) => {
|
||||
|
||||
tcb.createAsync(AutoDetectComp)
|
||||
.then((componentFixture) => {
|
||||
expect(componentFixture.ngZone).not.toBeNull();
|
||||
componentFixture.autoDetectChanges();
|
||||
expect(componentFixture.nativeElement).toHaveText('1');
|
||||
|
||||
let element = componentFixture.debugElement.children[0];
|
||||
dispatchEvent(element.nativeElement, 'click');
|
||||
|
||||
expect(componentFixture.isStable()).toBe(true);
|
||||
expect(componentFixture.nativeElement).toHaveText('11');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should auto detect changes if ComponentFixtureAutoDetect is provided as true',
|
||||
withProviders(() => [provide(ComponentFixtureAutoDetect, {useValue: true})])
|
||||
.inject([TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async) => {
|
||||
|
||||
tcb.createAsync(AutoDetectComp)
|
||||
.then((componentFixture) => {
|
||||
expect(componentFixture.nativeElement).toHaveText('1');
|
||||
|
||||
let element = componentFixture.debugElement.children[0];
|
||||
dispatchEvent(element.nativeElement, 'click');
|
||||
|
||||
expect(componentFixture.nativeElement).toHaveText('11');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should signal through whenStable when the fixture is stable (autoDetectChanges)',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async) => {
|
||||
|
||||
tcb.createAsync(AsyncComp).then((componentFixture) => {
|
||||
componentFixture.autoDetectChanges();
|
||||
expect(componentFixture.nativeElement).toHaveText('1');
|
||||
|
||||
let element = componentFixture.debugElement.children[0];
|
||||
dispatchEvent(element.nativeElement, 'click');
|
||||
expect(componentFixture.nativeElement).toHaveText('1');
|
||||
|
||||
// Component is updated asynchronously. Wait for the fixture to become stable
|
||||
// before checking for new value.
|
||||
expect(componentFixture.isStable()).toBe(false);
|
||||
componentFixture.whenStable().then((waited) => {
|
||||
expect(waited).toBe(true);
|
||||
expect(componentFixture.nativeElement).toHaveText('11');
|
||||
async.done();
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
it('should signal through isStable when the fixture is stable (no autoDetectChanges)',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async) => {
|
||||
|
||||
tcb.createAsync(AsyncComp).then((componentFixture) => {
|
||||
componentFixture.detectChanges();
|
||||
expect(componentFixture.nativeElement).toHaveText('1');
|
||||
|
||||
let element = componentFixture.debugElement.children[0];
|
||||
dispatchEvent(element.nativeElement, 'click');
|
||||
expect(componentFixture.nativeElement).toHaveText('1');
|
||||
|
||||
// Component is updated asynchronously. Wait for the fixture to become stable
|
||||
// before checking.
|
||||
componentFixture.whenStable().then((waited) => {
|
||||
expect(waited).toBe(true);
|
||||
componentFixture.detectChanges();
|
||||
expect(componentFixture.nativeElement).toHaveText('11');
|
||||
async.done();
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
it('should stabilize after async task in change detection (autoDetectChanges)',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async) => {
|
||||
|
||||
tcb.createAsync(AsyncChangeComp)
|
||||
.then((componentFixture) => {
|
||||
componentFixture.autoDetectChanges();
|
||||
componentFixture.whenStable().then((_) => {
|
||||
expect(componentFixture.nativeElement).toHaveText('1');
|
||||
|
||||
let element = componentFixture.debugElement.children[0];
|
||||
dispatchEvent(element.nativeElement, 'click');
|
||||
|
||||
componentFixture.whenStable().then((_) => {
|
||||
expect(componentFixture.nativeElement).toHaveText('11');
|
||||
async.done();
|
||||
});
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
it('should stabilize after async task in change detection(no autoDetectChanges)',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async) => {
|
||||
|
||||
tcb.createAsync(AsyncChangeComp)
|
||||
.then((componentFixture) => {
|
||||
componentFixture.detectChanges();
|
||||
componentFixture.whenStable().then((_) => {
|
||||
// Run detectChanges again so that stabilized value is reflected in the
|
||||
// DOM.
|
||||
componentFixture.detectChanges();
|
||||
expect(componentFixture.nativeElement).toHaveText('1');
|
||||
|
||||
let element = componentFixture.debugElement.children[0];
|
||||
dispatchEvent(element.nativeElement, 'click');
|
||||
componentFixture.detectChanges();
|
||||
|
||||
componentFixture.whenStable().then((_) => {
|
||||
// Run detectChanges again so that stabilized value is reflected in
|
||||
// the DOM.
|
||||
componentFixture.detectChanges();
|
||||
expect(componentFixture.nativeElement).toHaveText('11');
|
||||
async.done();
|
||||
});
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
describe('No NgZone', () => {
|
||||
beforeEachProviders(() => [provide(ComponentFixtureNoNgZone, {useValue: true})]);
|
||||
|
||||
it('calling autoDetectChanges raises an error', () => {
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder,
|
||||
async) => {
|
||||
tcb.createAsync(ChildComp).then((componentFixture) => {
|
||||
expect(() => {
|
||||
componentFixture.autoDetectChanges();
|
||||
}).toThrow('Cannot call autoDetectChanges when ComponentFixtureNoNgZone is set!!');
|
||||
async.done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should instantiate a component with valid DOM',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async) => {
|
||||
|
||||
tcb.createAsync(ChildComp).then((componentFixture) => {
|
||||
expect(componentFixture.ngZone).toBeNull();
|
||||
componentFixture.detectChanges();
|
||||
expect(componentFixture.nativeElement).toHaveText('Original Child');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should allow changing members of the component',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async) => {
|
||||
|
||||
tcb.createAsync(MyIfComp).then((componentFixture) => {
|
||||
componentFixture.detectChanges();
|
||||
expect(componentFixture.nativeElement).toHaveText('MyIf()');
|
||||
|
||||
componentFixture.componentInstance.showMore = true;
|
||||
componentFixture.detectChanges();
|
||||
expect(componentFixture.nativeElement).toHaveText('MyIf(More)');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import {
|
||||
AsyncTestCompleter,
|
||||
withProviders,
|
||||
inject,
|
||||
describe,
|
||||
it,
|
||||
|
@ -8,6 +9,7 @@ import {
|
|||
beforeEachProviders,
|
||||
SpyObject
|
||||
} from 'angular2/testing_internal';
|
||||
import {provide} from 'angular2/src/core/di';
|
||||
import {ObservableWrapper, TimerWrapper} from 'angular2/src/facade/async';
|
||||
import {MessageBus} from 'angular2/src/web_workers/shared/message_bus';
|
||||
import {createConnectedMessageBus} from './message_bus_util';
|
||||
|
@ -111,30 +113,34 @@ export function main() {
|
|||
// TODO(mlaval): timeout is fragile, test to be rewritten
|
||||
function flushMessages(fn: () => void) { TimerWrapper.setTimeout(fn, 50); }
|
||||
|
||||
beforeEach(() => { bus = createConnectedMessageBus(); });
|
||||
|
||||
it("should buffer messages and wait for the zone to exit before sending",
|
||||
inject([AsyncTestCompleter, NgZone], (async, zone: MockNgZone) => {
|
||||
setup(true, zone);
|
||||
withProviders(() => [provide(NgZone, {useClass: MockNgZone})])
|
||||
.inject([AsyncTestCompleter, NgZone],
|
||||
(async, zone: MockNgZone) => {
|
||||
bus = createConnectedMessageBus();
|
||||
setup(true, zone);
|
||||
|
||||
var wasCalled = false;
|
||||
ObservableWrapper.subscribe(bus.from(CHANNEL), (message) => { wasCalled = true; });
|
||||
ObservableWrapper.callEmit(bus.to(CHANNEL), "hi");
|
||||
var wasCalled = false;
|
||||
ObservableWrapper.subscribe(bus.from(CHANNEL),
|
||||
(message) => { wasCalled = true; });
|
||||
ObservableWrapper.callEmit(bus.to(CHANNEL), "hi");
|
||||
|
||||
|
||||
flushMessages(() => {
|
||||
expect(wasCalled).toBeFalsy();
|
||||
flushMessages(() => {
|
||||
expect(wasCalled).toBeFalsy();
|
||||
|
||||
zone.simulateZoneExit();
|
||||
flushMessages(() => {
|
||||
expect(wasCalled).toBeTruthy();
|
||||
async.done();
|
||||
});
|
||||
});
|
||||
}), 500);
|
||||
zone.simulateZoneExit();
|
||||
flushMessages(() => {
|
||||
expect(wasCalled).toBeTruthy();
|
||||
async.done();
|
||||
});
|
||||
});
|
||||
}),
|
||||
500);
|
||||
|
||||
it("should send messages immediatly when run outside the zone",
|
||||
inject([AsyncTestCompleter, NgZone], (async, zone: MockNgZone) => {
|
||||
bus = createConnectedMessageBus();
|
||||
setup(false, zone);
|
||||
|
||||
var wasCalled = false;
|
||||
|
|
|
@ -7,7 +7,12 @@
|
|||
*
|
||||
*/
|
||||
export * from './src/testing/testing';
|
||||
export {ComponentFixture, TestComponentBuilder} from './src/testing/test_component_builder';
|
||||
export {
|
||||
ComponentFixture,
|
||||
TestComponentBuilder,
|
||||
ComponentFixtureAutoDetect,
|
||||
ComponentFixtureNoNgZone
|
||||
} from './src/testing/test_component_builder';
|
||||
export * from './src/testing/test_injector';
|
||||
export * from './src/testing/fake_async';
|
||||
|
||||
|
|
Loading…
Reference in New Issue