diff --git a/spec/operators/catch-spec.ts b/spec/operators/catch-spec.ts index 5e2b076405..1a933d0cb0 100644 --- a/spec/operators/catch-spec.ts +++ b/spec/operators/catch-spec.ts @@ -1,5 +1,6 @@ import {expect} from 'chai'; import * as Rx from '../../dist/cjs/Rx'; +import * as sinon from 'sinon'; import {createObservableInputs} from '../helpers/test-helper'; import marbleTestingSignature = require('../helpers/marble-testing'); // tslint:disable-line:no-require-imports @@ -278,4 +279,83 @@ describe('Observable.prototype.catch', () => { done(); }); }); + + context('fromPromise', () => { + type SetTimeout = (callback: (...args: any[]) => void, ms: number, ...args: any[]) => NodeJS.Timer; + + let trueSetTimeout: SetTimeout; + let sandbox: sinon.SinonSandbox; + let timers: sinon.SinonFakeTimers; + + beforeEach(() => { + trueSetTimeout = global.setTimeout; + sandbox = sinon.sandbox.create(); + timers = sandbox.useFakeTimers(); + }); + + afterEach(() => { + sandbox.restore(); + }); + + it('should chain a throw from a promise using throw', (done: MochaDone) => { + const subscribeSpy = sinon.spy(); + const testError = new Error('BROKEN PROMISE'); + Observable.fromPromise(Promise.reject(testError)).catch(err => { + throw new Error('BROKEN THROW'); + }).subscribe(subscribeSpy); + + trueSetTimeout(() => { + try { + timers.tick(1); + } catch (e) { + expect(subscribeSpy).not.to.be.called; + expect(e.message).to.equal('BROKEN THROW'); + return done(); + } + done(new Error('This should have thrown an error')); + }, 0); + }); + + it('should chain a throw from a promise using Observable.throw', (done: MochaDone) => { + const subscribeSpy = sinon.spy(); + const testError = new Error('BROKEN PROMISE'); + Observable.fromPromise(Promise.reject(testError)).catch(err => + Observable.throw(new Error('BROKEN THROW')) + ).subscribe(subscribeSpy); + + trueSetTimeout(() => { + try { + timers.tick(1); + } catch (e) { + expect(subscribeSpy).not.to.be.called; + expect(e.message).to.equal('BROKEN THROW'); + return done(); + } + done(new Error('This should have thrown an error')); + }, 0); + }); + + it('should chain a throw from a promise using Observable.throw', (done: MochaDone) => { + const subscribeSpy = sinon.spy(); + const errorSpy = sinon.spy(); + const thrownError = new Error('BROKEN THROW'); + const testError = new Error('BROKEN PROMISE'); + Observable.fromPromise(Promise.reject(testError)).catch(err => + Observable.throw(thrownError) + ).subscribe(subscribeSpy, errorSpy); + + trueSetTimeout(() => { + try { + timers.tick(1); + } catch (e) { + return done(new Error('This should not have thrown an error')); + } + expect(subscribeSpy).not.to.be.called; + expect(errorSpy).to.have.been.called; + expect(errorSpy).to.have.been.calledWith(thrownError); + done(); + }, 0); + }); + }); + }); diff --git a/src/observable/ErrorObservable.ts b/src/observable/ErrorObservable.ts index 3e461be47f..e759bc813a 100644 --- a/src/observable/ErrorObservable.ts +++ b/src/observable/ErrorObservable.ts @@ -1,6 +1,7 @@ import { IScheduler } from '../Scheduler'; import { Observable } from '../Observable'; import { TeardownLogic } from '../Subscription'; +import { Subscriber } from '../Subscriber'; export interface DispatchArg { error: any; @@ -67,10 +68,12 @@ export class ErrorObservable extends Observable { super(); } - protected _subscribe(subscriber: any): TeardownLogic { + protected _subscribe(subscriber: Subscriber): TeardownLogic { const error = this.error; const scheduler = this.scheduler; + subscriber.syncErrorThrowable = true; + if (scheduler) { return scheduler.schedule(ErrorObservable.dispatch, 0, { error, subscriber