From d1d339a90087d30cd9d623611a18a17d180378a0 Mon Sep 17 00:00:00 2001 From: kwonoj Date: Mon, 5 Oct 2015 10:19:23 -0700 Subject: [PATCH] fix(operator): startWith operator accepts scheduler, multiple values - update startWith operator to accept multiple values as well - update signature of startWith to accept scheduler as last parameter - fix current micro perf test to accept scheduler properly, expand test for multiple values - expand test coverage --- .../operators/startwith-fromarray.js | 20 ++++ .../{start-with.js => startwith-scalar.js} | 8 +- .../operators/startwith-fromarray.js | 20 ++++ .../{start-with.js => startwith-scalar.js} | 8 +- spec/operators/startWith-spec.js | 100 +++++++++++++++--- src/operators/startWith.ts | 21 +++- 6 files changed, 153 insertions(+), 24 deletions(-) create mode 100644 perf/micro/current-thread-scheduler/operators/startwith-fromarray.js rename perf/micro/current-thread-scheduler/operators/{start-with.js => startwith-scalar.js} (74%) create mode 100644 perf/micro/immediate-scheduler/operators/startwith-fromarray.js rename perf/micro/immediate-scheduler/operators/{start-with.js => startwith-scalar.js} (73%) diff --git a/perf/micro/current-thread-scheduler/operators/startwith-fromarray.js b/perf/micro/current-thread-scheduler/operators/startwith-fromarray.js new file mode 100644 index 0000000000..83d58cefff --- /dev/null +++ b/perf/micro/current-thread-scheduler/operators/startwith-fromarray.js @@ -0,0 +1,20 @@ +var RxOld = require('rx'); +var RxNew = require('../../../../index'); + +module.exports = function (suite) { + var oldStartWithWithCurrentThreadScheduler = RxOld.Observable.of(25, RxOld.Scheduler.currentThread) + .startWith(RxOld.Scheduler.currentThread, 5, 5, 5); + var newStartWithWithCurrentThreadScheduler = RxNew.Observable.of(25, RxNew.Scheduler.immediate) + .startWith(5, 5, 5, RxNew.Scheduler.immediate); + + function _next(x) { } + function _error(e) { } + function _complete() { } + return suite + .add('old startWith(fromarray) with current thread scheduler', function () { + oldStartWithWithCurrentThreadScheduler.subscribe(_next, _error, _complete); + }) + .add('new startWith(fromarray) with current thread scheduler', function () { + newStartWithWithCurrentThreadScheduler.subscribe(_next, _error, _complete); + }); +}; \ No newline at end of file diff --git a/perf/micro/current-thread-scheduler/operators/start-with.js b/perf/micro/current-thread-scheduler/operators/startwith-scalar.js similarity index 74% rename from perf/micro/current-thread-scheduler/operators/start-with.js rename to perf/micro/current-thread-scheduler/operators/startwith-scalar.js index be01bae60c..94ab9fd55c 100644 --- a/perf/micro/current-thread-scheduler/operators/start-with.js +++ b/perf/micro/current-thread-scheduler/operators/startwith-scalar.js @@ -3,7 +3,7 @@ var RxNew = require('../../../../index'); module.exports = function (suite) { var oldStartWithWithCurrentThreadScheduler = RxOld.Observable.of(25, RxOld.Scheduler.currentThread) - .startWith(5, RxOld.Scheduler.currentThread); + .startWith(RxOld.Scheduler.currentThread, 5); var newStartWithWithCurrentThreadScheduler = RxNew.Observable.of(25, RxNew.Scheduler.immediate) .startWith(5, RxNew.Scheduler.immediate); @@ -11,10 +11,10 @@ module.exports = function (suite) { function _error(e) { } function _complete() { } return suite - .add('old startWith with current thread scheduler', function () { + .add('old startWith(scalar) with current thread scheduler', function () { oldStartWithWithCurrentThreadScheduler.subscribe(_next, _error, _complete); }) - .add('new startWith with current thread scheduler', function () { + .add('new startWith(scalar) with current thread scheduler', function () { newStartWithWithCurrentThreadScheduler.subscribe(_next, _error, _complete); }); -}; \ No newline at end of file +}; diff --git a/perf/micro/immediate-scheduler/operators/startwith-fromarray.js b/perf/micro/immediate-scheduler/operators/startwith-fromarray.js new file mode 100644 index 0000000000..2e68fd2ff2 --- /dev/null +++ b/perf/micro/immediate-scheduler/operators/startwith-fromarray.js @@ -0,0 +1,20 @@ +var RxOld = require('rx'); +var RxNew = require('../../../../index'); + +module.exports = function (suite) { + var oldStartWithWithImmediateScheduler = RxOld.Observable.of(25, RxOld.Scheduler.immediate) + .startWith(RxOld.Scheduler.immediate, 5, 5, 5); + var newStartWithWithImmediateScheduler = RxNew.Observable.of(25) + .startWith(5, 5, 5); + + function _next(x) { } + function _error(e) { } + function _complete() { } + return suite + .add('old startWith(fromArray) with immediate scheduler', function () { + oldStartWithWithImmediateScheduler.subscribe(_next, _error, _complete); + }) + .add('new startWith(fromArray) with immediate scheduler', function () { + newStartWithWithImmediateScheduler.subscribe(_next, _error, _complete); + }); +}; \ No newline at end of file diff --git a/perf/micro/immediate-scheduler/operators/start-with.js b/perf/micro/immediate-scheduler/operators/startwith-scalar.js similarity index 73% rename from perf/micro/immediate-scheduler/operators/start-with.js rename to perf/micro/immediate-scheduler/operators/startwith-scalar.js index f64a40566c..9aad2007e4 100644 --- a/perf/micro/immediate-scheduler/operators/start-with.js +++ b/perf/micro/immediate-scheduler/operators/startwith-scalar.js @@ -3,7 +3,7 @@ var RxNew = require('../../../../index'); module.exports = function (suite) { var oldStartWithWithImmediateScheduler = RxOld.Observable.of(25, RxOld.Scheduler.immediate) - .startWith(5, RxOld.Scheduler.immediate); + .startWith(RxOld.Scheduler.immediate, 5); var newStartWithWithImmediateScheduler = RxNew.Observable.of(25) .startWith(5); @@ -11,10 +11,10 @@ module.exports = function (suite) { function _error(e) { } function _complete() { } return suite - .add('old startWith with immediate scheduler', function () { + .add('old startWith(scalar) with immediate scheduler', function () { oldStartWithWithImmediateScheduler.subscribe(_next, _error, _complete); }) - .add('new startWith with immediate scheduler', function () { + .add('new startWith(scalar) with immediate scheduler', function () { newStartWithWithImmediateScheduler.subscribe(_next, _error, _complete); }); -}; \ No newline at end of file +}; diff --git a/spec/operators/startWith-spec.js b/spec/operators/startWith-spec.js index 4d2dfb7c68..1804c95a28 100644 --- a/spec/operators/startWith-spec.js +++ b/spec/operators/startWith-spec.js @@ -1,20 +1,92 @@ -/* globals describe, it, expect */ +/* globals describe, it, expect, expectObservable, hot, rxTestScheduler */ var Rx = require('../../dist/cjs/Rx'); var Observable = Rx.Observable; describe('Observable.prototype.startWith()', function () { - it('should start an observable with given value', function (done) { - var source = 'source'; - var init = 'init'; - var expected = [init, source]; - - var i = 0; - Observable.of(source) - .startWith(init) - .subscribe(function (x) { - expect(x).toBe(expected[i++]); - }, null, function () { - done(); - }); + var defaultStartValue = 'x'; + + it('should start an observable with given value', function () { + var e1 = hot('--a--|'); + var expected = 'x-a--|'; + + expectObservable(e1.startWith(defaultStartValue)).toBe(expected); + }); + + it('should start with given value and does not completes if source does not completes', function () { + var e1 = hot('----a-'); + var expected = 'x---a-'; + + expectObservable(e1.startWith(defaultStartValue)).toBe(expected); + }); + + it('should start with given value and does not completes if source never emits', function () { + var e1 = Observable.never(); + var expected = 'x-'; + + expectObservable(e1.startWith(defaultStartValue)).toBe(expected); + }); + + it('should start with given value and completes if source does not emits', function () { + var e1 = hot('---|'); + var expected = 'x--|'; + + expectObservable(e1.startWith(defaultStartValue)).toBe(expected); + }); + + it('should start with given value and complete immediately if source is empty', function () { + var e1 = Observable.empty(); + var expected = '(x|)'; + + expectObservable(e1.startWith(defaultStartValue)).toBe(expected); + }); + + it('should start with given value and source both if source emits single value', function () { + var e1 = Observable.of('a'); + var expected = '(xa|)'; + + expectObservable(e1.startWith(defaultStartValue)).toBe(expected); + }); + + it('should start with given values when given value is more than one', function () { + var e1 = hot('-----a--|'); + var expected = '(yz)-a--|'; + + expectObservable(e1.startWith('y','z')).toBe(expected); + }); + + it('should start with given value and raises error if source raises error', function () { + var e1 = hot('--#'); + var expected = 'x-#'; + + expectObservable(e1.startWith(defaultStartValue)).toBe(expected, defaultStartValue); + }); + + it('should start with given value and raises error immediately if source throws error', function () { + var error = 'error'; + var e1 = Observable.throw(error); + var expected = '(x#)'; + + expectObservable(e1.startWith(defaultStartValue)).toBe(expected, defaultStartValue, error); + }); + + it('should start with empty if given value is not specified', function () { + var e1 = hot('-a-|'); + var expected = '-a-|'; + + expectObservable(e1.startWith(rxTestScheduler)).toBe(expected); + }); + + it('should accept scheduler as last argument with single value', function () { + var e1 = hot('--a--|'); + var expected = 'x-a--|'; + + expectObservable(e1.startWith(defaultStartValue, rxTestScheduler)).toBe(expected); + }); + + it('should accept scheduler as last argument with multiple value', function () { + var e1 = hot('-----a--|'); + var expected = '(yz)-a--|'; + + expectObservable(e1.startWith('y','z', rxTestScheduler)).toBe(expected); }); }); diff --git a/src/operators/startWith.ts b/src/operators/startWith.ts index d39fe0b90b..25927347cf 100644 --- a/src/operators/startWith.ts +++ b/src/operators/startWith.ts @@ -1,7 +1,24 @@ +import Scheduler from '../Scheduler'; import Observable from '../Observable'; +import ArrayObservable from '../observables/ArrayObservable'; import ScalarObservable from '../observables/ScalarObservable'; +import EmptyObservable from '../observables/EmptyObservable'; import concat from './concat-static'; -export default function startWith(x: T): Observable { - return concat(new ScalarObservable(x), this); +export default function startWith(...array: (T | Scheduler)[]): Observable { + let scheduler = array[array.length - 1]; + if (scheduler && typeof scheduler.schedule === 'function') { + array.pop(); + } else { + scheduler = void 0; + } + + const len = array.length; + if (len === 1) { + return concat(new ScalarObservable(array[0], scheduler), this); + } else if (len > 1) { + return concat(new ArrayObservable(array, scheduler), this); + } else { + return concat(new EmptyObservable(scheduler), this); + } } \ No newline at end of file