diff --git a/modules/angular2/pipes.ts b/modules/angular2/pipes.ts index 256ce582fd..fbc2758ef7 100644 --- a/modules/angular2/pipes.ts +++ b/modules/angular2/pipes.ts @@ -4,10 +4,9 @@ * This module provides advanced support for extending change detection. */ -export {PromisePipe} from './src/change_detection/pipes/promise_pipe'; export {UpperCasePipe} from './src/change_detection/pipes/uppercase_pipe'; export {LowerCasePipe} from './src/change_detection/pipes/lowercase_pipe'; -export {ObservablePipe} from './src/change_detection/pipes/observable_pipe'; +export {AsyncPipe} from './src/change_detection/pipes/async_pipe'; export {JsonPipe} from './src/change_detection/pipes/json_pipe'; export {DatePipe} from './src/change_detection/pipes/date_pipe'; export {DecimalPipe, PercentPipe, CurrencyPipe} from './src/change_detection/pipes/number_pipe'; diff --git a/modules/angular2/src/change_detection/change_detection.ts b/modules/angular2/src/change_detection/change_detection.ts index caa512cdf7..bbf553a736 100644 --- a/modules/angular2/src/change_detection/change_detection.ts +++ b/modules/angular2/src/change_detection/change_detection.ts @@ -7,8 +7,7 @@ import {IterableDiffers, IterableDifferFactory} from './differs/iterable_differs import {DefaultIterableDifferFactory} from './differs/default_iterable_differ'; import {KeyValueDiffers, KeyValueDifferFactory} from './differs/keyvalue_differs'; import {DefaultKeyValueDifferFactory} from './differs/default_keyvalue_differ'; -import {ObservablePipeFactory} from './pipes/observable_pipe'; -import {PromisePipeFactory} from './pipes/promise_pipe'; +import {AsyncPipeFactory} from './pipes/async_pipe'; import {UpperCasePipe} from './pipes/uppercase_pipe'; import {LowerCasePipe} from './pipes/lowercase_pipe'; import {JsonPipe} from './pipes/json_pipe'; @@ -75,11 +74,8 @@ export const iterableDiff: IterableDifferFactory[] = /** * Async binding to such types as Observable. */ -export const async: List = CONST_EXPR([ - CONST_EXPR(new ObservablePipeFactory()), - CONST_EXPR(new PromisePipeFactory()), - CONST_EXPR(new NullPipeFactory()) -]); +export const async: List = + CONST_EXPR([CONST_EXPR(new AsyncPipeFactory()), CONST_EXPR(new NullPipeFactory())]); /** * Uppercase text transform. diff --git a/modules/angular2/src/change_detection/pipes/async_pipe.ts b/modules/angular2/src/change_detection/pipes/async_pipe.ts new file mode 100644 index 0000000000..878af7e832 --- /dev/null +++ b/modules/angular2/src/change_detection/pipes/async_pipe.ts @@ -0,0 +1,134 @@ +import {isBlank, isPresent, isPromise, CONST, BaseException} from 'angular2/src/facade/lang'; +import {Observable, Promise, ObservableWrapper} from 'angular2/src/facade/async'; +import {Pipe, WrappedValue, PipeFactory} from './pipe'; +import {ChangeDetectorRef} from '../change_detector_ref'; + + +class ObservableStrategy { + createSubscription(async: any, updateLatestValue: any): any { + return ObservableWrapper.subscribe(async, updateLatestValue, e => { throw e; }); + } + + dispose(subscription: any): void { ObservableWrapper.dispose(subscription); } + + onDestroy(subscription: any): void { ObservableWrapper.dispose(subscription); } +} + +class PromiseStrategy { + createSubscription(async: any, updateLatestValue: any): any { + return async.then(updateLatestValue); + } + + dispose(subscription: any): void {} + + onDestroy(subscription: any): void {} +} + +var _promiseStrategy = new PromiseStrategy(); +var _observableStrategy = new ObservableStrategy(); + + +/** + * Implements async bindings to Observable and Promise. + * + * # Example + * + * In this example we bind the description observable to the DOM. The async pipe will convert an + *observable to the + * latest value it emitted. It will also request a change detection check when a new value is + *emitted. + * + * ``` + * @Component({ + * selector: "task-cmp", + * changeDetection: ON_PUSH + * }) + * @View({ + * template: "Task Description {{ description | async }}" + * }) + * class Task { + * description:Observable; + * } + * + * ``` + */ +export class AsyncPipe implements Pipe { + _latestValue: Object = null; + _latestReturnedValue: Object = null; + + _subscription: Object = null; + _obj: Observable | Promise = null; + private _strategy: any = null; + + constructor(public _ref: ChangeDetectorRef) {} + + supports(obj: any): boolean { return true; } + + onDestroy(): void { + if (isPresent(this._subscription)) { + this._dispose(); + } + } + + transform(obj: Observable | Promise, args?: any[]): any { + if (isBlank(this._obj)) { + if (isPresent(obj)) { + this._subscribe(obj); + } + return null; + } + + if (obj !== this._obj) { + this._dispose(); + return this.transform(obj); + } + + if (this._latestValue === this._latestReturnedValue) { + return this._latestReturnedValue; + } else { + this._latestReturnedValue = this._latestValue; + return WrappedValue.wrap(this._latestValue); + } + } + + _subscribe(obj: Observable | Promise): void { + this._obj = obj; + this._strategy = this._selectStrategy(obj); + this._subscription = + this._strategy.createSubscription(obj, value => this._updateLatestValue(obj, value)); + } + + _selectStrategy(obj: Observable | Promise) { + if (isPromise(obj)) { + return _promiseStrategy; + } else if (ObservableWrapper.isObservable(obj)) { + return _observableStrategy; + } else { + throw new BaseException(`Async pipe does not support object '${obj}'`); + } + } + + _dispose(): void { + this._strategy.dispose(this._subscription); + this._latestValue = null; + this._latestReturnedValue = null; + this._subscription = null; + this._obj = null; + } + + _updateLatestValue(async: any, value: Object) { + if (async === this._obj) { + this._latestValue = value; + this._ref.requestCheck(); + } + } +} + +/** + * Provides a factory for [AsyncPipe]. + */ +@CONST() +export class AsyncPipeFactory implements PipeFactory { + supports(obj: any): boolean { return true; } + create(cdRef: ChangeDetectorRef): Pipe { return new AsyncPipe(cdRef); } +} diff --git a/modules/angular2/src/change_detection/pipes/observable_pipe.ts b/modules/angular2/src/change_detection/pipes/observable_pipe.ts deleted file mode 100644 index 7abfc5deee..0000000000 --- a/modules/angular2/src/change_detection/pipes/observable_pipe.ts +++ /dev/null @@ -1,94 +0,0 @@ -import {Observable, ObservableWrapper} from 'angular2/src/facade/async'; -import {isBlank, isPresent, CONST} from 'angular2/src/facade/lang'; -import {Pipe, WrappedValue, PipeFactory} from './pipe'; -import {ChangeDetectorRef} from '../change_detector_ref'; - -/** - * Implements async bindings to Observable. - * - * # Example - * - * In this example we bind the description observable to the DOM. The async pipe will convert an - *observable to the - * latest value it emitted. It will also request a change detection check when a new value is - *emitted. - * - * ``` - * @Component({ - * selector: "task-cmp", - * changeDetection: ON_PUSH - * }) - * @View({ - * template: "Task Description {{ description | async }}" - * }) - * class Task { - * description:Observable; - * } - * - * ``` - */ -export class ObservablePipe implements Pipe { - _latestValue: Object = null; - _latestReturnedValue: Object = null; - - _subscription: Object = null; - _observable: Observable = null; - - constructor(public _ref: ChangeDetectorRef) {} - - supports(obs: any): boolean { return ObservableWrapper.isObservable(obs); } - - onDestroy(): void { - if (isPresent(this._subscription)) { - this._dispose(); - } - } - - transform(obs: Observable, args: List = null): any { - if (isBlank(this._subscription)) { - this._subscribe(obs); - return null; - } - - if (obs !== this._observable) { - this._dispose(); - return this.transform(obs); - } - - if (this._latestValue === this._latestReturnedValue) { - return this._latestReturnedValue; - } else { - this._latestReturnedValue = this._latestValue; - return WrappedValue.wrap(this._latestValue); - } - } - - _subscribe(obs: Observable): void { - this._observable = obs; - this._subscription = ObservableWrapper.subscribe(obs, value => this._updateLatestValue(value), - e => { throw e; }); - } - - _dispose(): void { - ObservableWrapper.dispose(this._subscription); - this._latestValue = null; - this._latestReturnedValue = null; - this._subscription = null; - this._observable = null; - } - - _updateLatestValue(value: Object) { - this._latestValue = value; - this._ref.requestCheck(); - } -} - -/** - * Provides a factory for [ObervablePipe]. - */ -@CONST() -export class ObservablePipeFactory implements PipeFactory { - supports(obs: any): boolean { return ObservableWrapper.isObservable(obs); } - - create(cdRef: ChangeDetectorRef): Pipe { return new ObservablePipe(cdRef); } -} diff --git a/modules/angular2/src/change_detection/pipes/promise_pipe.ts b/modules/angular2/src/change_detection/pipes/promise_pipe.ts deleted file mode 100644 index 4b8b00a8bc..0000000000 --- a/modules/angular2/src/change_detection/pipes/promise_pipe.ts +++ /dev/null @@ -1,84 +0,0 @@ -import {Promise, PromiseWrapper} from 'angular2/src/facade/async'; -import {isBlank, isPresent, isPromise, CONST} from 'angular2/src/facade/lang'; -import {Pipe, PipeFactory, WrappedValue} from './pipe'; -import {ChangeDetectorRef} from '../change_detector_ref'; - -/** - * Implements async bindings to Promise. - * - * # Example - * - * In this example we bind the description promise to the DOM. - * The async pipe will convert a promise to the value with which it is resolved. It will also - * request a change detection check when the promise is resolved. - * - * ``` - * @Component({ - * selector: "task-cmp", - * changeDetection: ON_PUSH - * }) - * @View({ - * template: "Task Description {{ description | async }}" - * }) - * class Task { - * description:Promise; - * } - * - * ``` - */ -export class PromisePipe implements Pipe { - _latestValue: Object = null; - _latestReturnedValue: Object = null; - _sourcePromise: Promise; - - constructor(public _ref: ChangeDetectorRef) {} - - supports(promise: any): boolean { return isPromise(promise); } - - onDestroy(): void { - if (isPresent(this._sourcePromise)) { - this._latestValue = null; - this._latestReturnedValue = null; - this._sourcePromise = null; - } - } - - transform(promise: Promise, args: List = null): any { - if (isBlank(this._sourcePromise)) { - this._sourcePromise = promise; - promise.then((val) => { - if (this._sourcePromise === promise) { - this._updateLatestValue(val); - } - }); - return null; - } - - if (promise !== this._sourcePromise) { - this._sourcePromise = null; - return this.transform(promise); - } - - if (this._latestValue === this._latestReturnedValue) { - return this._latestReturnedValue; - } else { - this._latestReturnedValue = this._latestValue; - return WrappedValue.wrap(this._latestValue); - } - } - - _updateLatestValue(value: Object) { - this._latestValue = value; - this._ref.requestCheck(); - } -} - -/** - * Provides a factory for [PromisePipe]. - */ -@CONST() -export class PromisePipeFactory implements PipeFactory { - supports(promise: any): boolean { return isPromise(promise); } - - create(cdRef: ChangeDetectorRef): Pipe { return new PromisePipe(cdRef); } -} diff --git a/modules/angular2/src/test_lib/spies.ts b/modules/angular2/src/test_lib/spies.ts index 391bd9e1d2..5526edd7c8 100644 --- a/modules/angular2/src/test_lib/spies.ts +++ b/modules/angular2/src/test_lib/spies.ts @@ -1,5 +1,6 @@ import { ChangeDetector, + ChangeDetectorRef, ProtoChangeDetector, DynamicChangeDetector } from 'angular2/src/change_detection/change_detection'; @@ -26,3 +27,7 @@ export class SpyPipeFactory extends SpyObject {} export class SpyDependencyProvider extends SpyObject {} export class SpyIterableDifferFactory extends SpyObject {} + +export class SpyChangeDetectorRef extends SpyObject { + constructor() { super(ChangeDetectorRef); } +} diff --git a/modules/angular2/test/change_detection/pipes/async_pipe_spec.ts b/modules/angular2/test/change_detection/pipes/async_pipe_spec.ts new file mode 100644 index 0000000000..04e4dc6b1a --- /dev/null +++ b/modules/angular2/test/change_detection/pipes/async_pipe_spec.ts @@ -0,0 +1,220 @@ +import { + ddescribe, + describe, + it, + iit, + xit, + expect, + beforeEach, + afterEach, + AsyncTestCompleter, + SpyChangeDetectorRef, + inject, + SpyObject +} from 'angular2/test_lib'; + +import {IMPLEMENTS, isBlank} from 'angular2/src/facade/lang'; +import {WrappedValue} from 'angular2/src/change_detection/pipes/pipe'; +import {AsyncPipe} from 'angular2/src/change_detection/pipes/async_pipe'; +import { + EventEmitter, + ObservableWrapper, + PromiseWrapper, + TimerWrapper +} from 'angular2/src/facade/async'; +import {DOM} from 'angular2/src/dom/dom_adapter'; + +export function main() { + describe("AsyncPipe", () => { + + describe('Observable', () => { + var emitter; + var pipe; + var ref; + var message = new Object(); + + beforeEach(() => { + emitter = new EventEmitter(); + ref = new SpyChangeDetectorRef(); + pipe = new AsyncPipe(ref); + }); + + describe("transform", () => { + it("should return null when subscribing to an observable", + () => { expect(pipe.transform(emitter)).toBe(null); }); + + it("should return the latest available value wrapped", + inject([AsyncTestCompleter], (async) => { + pipe.transform(emitter); + + ObservableWrapper.callNext(emitter, message); + + TimerWrapper.setTimeout(() => { + expect(pipe.transform(emitter)).toEqual(new WrappedValue(message)); + async.done(); + }, 0) + })); + + + it("should return same value when nothing has changed since the last call", + inject([AsyncTestCompleter], (async) => { + pipe.transform(emitter); + ObservableWrapper.callNext(emitter, message); + + TimerWrapper.setTimeout(() => { + pipe.transform(emitter); + expect(pipe.transform(emitter)).toBe(message); + async.done(); + }, 0) + })); + + it("should dispose of the existing subscription when subscribing to a new observable", + inject([AsyncTestCompleter], (async) => { + pipe.transform(emitter); + + var newEmitter = new EventEmitter(); + expect(pipe.transform(newEmitter)).toBe(null); + + // this should not affect the pipe + ObservableWrapper.callNext(emitter, message); + + TimerWrapper.setTimeout(() => { + expect(pipe.transform(newEmitter)).toBe(null); + async.done(); + }, 0) + })); + + it("should request a change detection check upon receiving a new value", + inject([AsyncTestCompleter], (async) => { + pipe.transform(emitter); + ObservableWrapper.callNext(emitter, message); + + TimerWrapper.setTimeout(() => { + expect(ref.spy('requestCheck')).toHaveBeenCalled(); + async.done(); + }, 0) + })); + }); + + describe("onDestroy", () => { + it("should do nothing when no subscription", + () => { expect(() => pipe.onDestroy()).not.toThrow(); }); + + it("should dispose of the existing subscription", inject([AsyncTestCompleter], (async) => { + pipe.transform(emitter); + pipe.onDestroy(); + + ObservableWrapper.callNext(emitter, message); + + TimerWrapper.setTimeout(() => { + expect(pipe.transform(emitter)).toBe(null); + async.done(); + }, 0) + })); + }); + }); + + describe("Promise", () => { + var message = new Object(); + var pipe; + var completer; + var ref; + // adds longer timers for passing tests in IE + var timer = (!isBlank(DOM) && DOM.getUserAgent().indexOf("Trident") > -1) ? 50 : 0; + + beforeEach(() => { + completer = PromiseWrapper.completer(); + ref = new SpyChangeDetectorRef(); + pipe = new AsyncPipe(ref); + }); + + describe("transform", () => { + it("should return null when subscribing to a promise", + () => { expect(pipe.transform(completer.promise)).toBe(null); }); + + it("should return the latest available value", inject([AsyncTestCompleter], (async) => { + pipe.transform(completer.promise); + + completer.resolve(message); + + TimerWrapper.setTimeout(() => { + expect(pipe.transform(completer.promise)).toEqual(new WrappedValue(message)); + async.done(); + }, timer) + })); + + it("should return unwrapped value when nothing has changed since the last call", + inject([AsyncTestCompleter], (async) => { + pipe.transform(completer.promise); + completer.resolve(message); + + TimerWrapper.setTimeout(() => { + pipe.transform(completer.promise); + expect(pipe.transform(completer.promise)).toBe(message); + async.done(); + }, timer) + })); + + it("should dispose of the existing subscription when subscribing to a new promise", + inject([AsyncTestCompleter], (async) => { + pipe.transform(completer.promise); + + var newCompleter = PromiseWrapper.completer(); + expect(pipe.transform(newCompleter.promise)).toBe(null); + + // this should not affect the pipe, so it should return WrappedValue + completer.resolve(message); + + TimerWrapper.setTimeout(() => { + expect(pipe.transform(newCompleter.promise)).toBe(null); + async.done(); + }, timer) + })); + + it("should request a change detection check upon receiving a new value", + inject([AsyncTestCompleter], (async) => { + pipe.transform(completer.promise); + completer.resolve(message); + + TimerWrapper.setTimeout(() => { + expect(ref.spy('requestCheck')).toHaveBeenCalled(); + async.done(); + }, timer) + })); + + describe("onDestroy", () => { + it("should do nothing when no source", + () => { expect(() => pipe.onDestroy()).not.toThrow(); }); + + it("should dispose of the existing source", inject([AsyncTestCompleter], (async) => { + pipe.transform(completer.promise); + expect(pipe.transform(completer.promise)).toBe(null); + completer.resolve(message) + + + TimerWrapper.setTimeout(() => { + expect(pipe.transform(completer.promise)).toEqual(new WrappedValue(message)); + pipe.onDestroy(); + expect(pipe.transform(completer.promise)).toBe(null); + async.done(); + }, timer); + })); + }); + }); + }); + + describe('null', () => { + it('should return null when given null', () => { + var pipe = new AsyncPipe(null); + expect(pipe.transform(null, [])).toEqual(null); + }); + }); + + describe('other types', () => { + it('should throw when given an invalid object', () => { + var pipe = new AsyncPipe(null); + expect(() => pipe.transform("some bogus object", [])).toThrowError(); + }); + }); + }); +} diff --git a/modules/angular2/test/change_detection/pipes/observable_pipe_spec.ts b/modules/angular2/test/change_detection/pipes/observable_pipe_spec.ts deleted file mode 100644 index cc32b55930..0000000000 --- a/modules/angular2/test/change_detection/pipes/observable_pipe_spec.ts +++ /dev/null @@ -1,125 +0,0 @@ -import { - ddescribe, - describe, - it, - iit, - xit, - expect, - beforeEach, - afterEach, - AsyncTestCompleter, - inject, - proxy, - SpyObject -} from 'angular2/test_lib'; - -import {IMPLEMENTS} from 'angular2/src/facade/lang'; -import {WrappedValue} from 'angular2/src/change_detection/pipes/pipe'; -import {ObservablePipe} from 'angular2/src/change_detection/pipes/observable_pipe'; -import {ChangeDetectorRef} from 'angular2/src/change_detection/change_detector_ref'; -import {EventEmitter, ObservableWrapper, TimerWrapper} from 'angular2/src/facade/async'; - -export function main() { - describe("ObservablePipe", () => { - var emitter; - var pipe; - var ref; - var message = new Object(); - - beforeEach(() => { - emitter = new EventEmitter(); - ref = new SpyChangeDetectorRef(); - pipe = new ObservablePipe(ref); - }); - - describe("supports", () => { - it("should support observables", () => { expect(pipe.supports(emitter)).toBe(true); }); - - it("should not support other objects", () => { - expect(pipe.supports("string")).toBe(false); - expect(pipe.supports(null)).toBe(false); - }); - }); - - describe("transform", () => { - it("should return null when subscribing to an observable", - () => { expect(pipe.transform(emitter)).toBe(null); }); - - it("should return the latest available value wrapped", - inject([AsyncTestCompleter], (async) => { - pipe.transform(emitter); - - ObservableWrapper.callNext(emitter, message); - - TimerWrapper.setTimeout(() => { - expect(pipe.transform(emitter)).toEqual(new WrappedValue(message)); - async.done(); - }, 0) - })); - - - it("should return same value when nothing has changed since the last call", - inject([AsyncTestCompleter], (async) => { - pipe.transform(emitter); - ObservableWrapper.callNext(emitter, message); - - TimerWrapper.setTimeout(() => { - pipe.transform(emitter); - expect(pipe.transform(emitter)).toBe(message); - async.done(); - }, 0) - })); - - it("should dispose of the existing subscription when subscribing to a new observable", - inject([AsyncTestCompleter], (async) => { - pipe.transform(emitter); - - var newEmitter = new EventEmitter(); - expect(pipe.transform(newEmitter)).toBe(null); - - // this should not affect the pipe - ObservableWrapper.callNext(emitter, message); - - TimerWrapper.setTimeout(() => { - expect(pipe.transform(newEmitter)).toBe(null); - async.done(); - }, 0) - })); - - it("should request a change detection check upon receiving a new value", - inject([AsyncTestCompleter], (async) => { - pipe.transform(emitter); - ObservableWrapper.callNext(emitter, message); - - TimerWrapper.setTimeout(() => { - expect(ref.spy('requestCheck')).toHaveBeenCalled(); - async.done(); - }, 0) - })); - }); - - describe("onDestroy", () => { - it("should do nothing when no subscription", - () => { expect(() => pipe.onDestroy()).not.toThrow(); }); - - it("should dispose of the existing subscription", inject([AsyncTestCompleter], (async) => { - pipe.transform(emitter); - pipe.onDestroy(); - - ObservableWrapper.callNext(emitter, message); - - TimerWrapper.setTimeout(() => { - expect(pipe.transform(emitter)).toBe(null); - async.done(); - }, 0) - })); - }); - }); -} - -@proxy -@IMPLEMENTS(ChangeDetectorRef) -class SpyChangeDetectorRef extends SpyObject { - constructor() { super(ChangeDetectorRef); } - noSuchMethod(m) { return super.noSuchMethod(m) } -} diff --git a/modules/angular2/test/change_detection/pipes/promise_pipe_spec.ts b/modules/angular2/test/change_detection/pipes/promise_pipe_spec.ts deleted file mode 100644 index db9b00c8a2..0000000000 --- a/modules/angular2/test/change_detection/pipes/promise_pipe_spec.ts +++ /dev/null @@ -1,127 +0,0 @@ -import { - ddescribe, - describe, - it, - iit, - xit, - expect, - beforeEach, - afterEach, - AsyncTestCompleter, - inject, - proxy, - SpyObject -} from 'angular2/test_lib'; -import {IMPLEMENTS, isBlank} from 'angular2/src/facade/lang'; -import {PromisePipe} from 'angular2/src/change_detection/pipes/promise_pipe'; -import {WrappedValue} from 'angular2/src/change_detection/pipes/pipe'; -import {ChangeDetectorRef} from 'angular2/src/change_detection/change_detector_ref'; -import {PromiseWrapper, TimerWrapper} from 'angular2/src/facade/async'; -import {DOM} from 'angular2/src/dom/dom_adapter'; - -export function main() { - describe("PromisePipe", () => { - var message = new Object(); - var pipe; - var completer; - var ref; - // adds longer timers for passing tests in IE - var timer = (!isBlank(DOM) && DOM.getUserAgent().indexOf("Trident") > -1) ? 50 : 0; - - beforeEach(() => { - completer = PromiseWrapper.completer(); - ref = new SpyChangeDetectorRef(); - pipe = new PromisePipe(ref); - }); - - describe("supports", () => { - it("should support promises", () => { expect(pipe.supports(completer.promise)).toBe(true); }); - - it("should not support other objects", () => { - expect(pipe.supports("string")).toBe(false); - expect(pipe.supports(null)).toBe(false); - }); - }); - - describe("transform", () => { - it("should return null when subscribing to a promise", - () => { expect(pipe.transform(completer.promise)).toBe(null); }); - - it("should return the latest available value", inject([AsyncTestCompleter], (async) => { - pipe.transform(completer.promise); - - completer.resolve(message); - - TimerWrapper.setTimeout(() => { - expect(pipe.transform(completer.promise)).toEqual(new WrappedValue(message)); - async.done(); - }, timer) - })); - - it("should return unwrapped value when nothing has changed since the last call", - inject([AsyncTestCompleter], (async) => { - pipe.transform(completer.promise); - completer.resolve(message); - - TimerWrapper.setTimeout(() => { - pipe.transform(completer.promise); - expect(pipe.transform(completer.promise)).toBe(message); - async.done(); - }, timer) - })); - - it("should dispose of the existing subscription when subscribing to a new promise", - inject([AsyncTestCompleter], (async) => { - pipe.transform(completer.promise); - - var newCompleter = PromiseWrapper.completer(); - expect(pipe.transform(newCompleter.promise)).toBe(null); - - // this should not affect the pipe, so it should return WrappedValue - completer.resolve(message); - - TimerWrapper.setTimeout(() => { - expect(pipe.transform(newCompleter.promise)).toBe(null); - async.done(); - }, timer) - })); - - it("should request a change detection check upon receiving a new value", - inject([AsyncTestCompleter], (async) => { - pipe.transform(completer.promise); - completer.resolve(message); - - TimerWrapper.setTimeout(() => { - expect(ref.spy('requestCheck')).toHaveBeenCalled(); - async.done(); - }, timer) - })); - - describe("onDestroy", () => { - it("should do nothing when no source", - () => { expect(() => pipe.onDestroy()).not.toThrow(); }); - - it("should dispose of the existing source", inject([AsyncTestCompleter], (async) => { - pipe.transform(completer.promise); - expect(pipe.transform(completer.promise)).toBe(null); - completer.resolve(message) - - - TimerWrapper.setTimeout(() => { - expect(pipe.transform(completer.promise)).toEqual(new WrappedValue(message)); - pipe.onDestroy(); - expect(pipe.transform(completer.promise)).toBe(null); - async.done(); - }, timer); - })); - }); - }); - }); -} - -@proxy -@IMPLEMENTS(ChangeDetectorRef) -class SpyChangeDetectorRef extends SpyObject { - constructor() { super(ChangeDetectorRef); } - noSuchMethod(m) { return super.noSuchMethod(m) } -}