prettier import { expect } from 'chai' ; import { scan , mergeMap , finalize , take } from 'rxjs/operators' ; import { of , Observable } from 'rxjs' ; import { TestScheduler } from 'rxjs/testing' ; import { observableMatcher } from '../helpers/observableMatcher' ; test describe ( 'scan' , ( ) => { let testScheduler ; beforeEach ( ( ) => { testScheduler = new TestScheduler ( observableMatcher ) ; } ) ; it ( 'should scan' , ( ) => { testScheduler . run ( ( { hot , expectObservable , expectSubscriptions } ) => { const values = { a : 1 , b : 3 , c : 5 , x : 1 , y : 4 , z : 9 , } ; const e1 = hot ( '  --a--b--c--|' , values ) ; const e1subs = '  ^----------!' ; const expected = '--x--y--z--|' ; const scanFunction = function ( o , x ) { return o + x ; } ; expectObservable ( e1 . pipe ( scan ( scanFunction , 0 ) ) ) . toBe ( expected , values ) ; expectSubscriptions ( e1 . subscriptions ) . toBe ( e1subs ) ; } ) ; } ) ; it ( 'should scan things' , ( ) => { testScheduler . run ( ( { hot , expectObservable , expectSubscriptions } ) => { const e1 = hot ( '--a--^--b--c--d--e--f--g--|' ) ; const e1subs = '     ^--------------------!' ; const expected = '   ---u--v--w--x--y--z--|' ; const values = { u : [ 'b' ] , v : [ 'b' , 'c' ] , w : [ 'b' , 'c' , 'd' ] , x : [ 'b' , 'c' , 'd' , 'e' ] , y : [ 'b' , 'c' , 'd' , 'e' , 'f' ] , z : [ 'b' , 'c' , 'd' , 'e' , 'f' , 'g' ] , } ; const source = e1 . pipe ( scan ( ( acc , x ) => acc . concat ( x ) , [ ] as string [ ] ) ) ; expectObservable ( source ) . toBe ( expected , values ) ; expectSubscriptions ( e1 . subscriptions ) . toBe ( e1subs ) ; } ) ; } ) ; it ( 'should provide the proper index if seed is skipped' , ( ) => { const expected = [ 1 , 2 ] ; of ( 3 , 3 , 3 ) . pipe ( scan ( ( _ , __ , i ) => { expect ( i ) . to . equal ( expected . shift ( ) ) ; return null ; } ) ) . subscribe ( ) ; } ) ; it ( 'should scan with a seed of undefined' , ( ) => { testScheduler . run ( ( { hot , expectObservable , expectSubscriptions } ) => { const e1 = hot ( '--a--^--b--c--d--e--f--g--|' ) ; const e1subs = '     ^--------------------!' ; const expected = '   ---u--v--w--x--y--z--|' ; const values = { u : 'undefined b' , v : 'undefined b c' , w : 'undefined b c d' , x : 'undefined b c d e' , y : 'undefined b c d e f' , z : 'undefined b c d e f g' , } ; const source = e1 . pipe ( scan ( ( acc , x ) => acc + ' ' + x , undefined ) ) ; expectObservable ( source ) . toBe ( expected , values ) ; expectSubscriptions ( e1 . subscriptions ) . toBe ( e1subs ) ; } ) ; } ) ; it ( 'should scan without seed' , ( ) => { testScheduler . run ( ( { hot , expectObservable , expectSubscriptions } ) => { const e1 = hot ( '--a--^--b--c--d--|' ) ; const e1subs = '     ^-----------!' ; const expected = '   ---x--y--z--|' ; const values = { x : 'b' , y : 'bc' , z : 'bcd' , } ; const source = e1 . pipe ( scan ( ( acc , x ) => acc + x ) ) ; expectObservable ( source ) . toBe ( expected , values ) ; expectSubscriptions ( e1 . subscriptions ) . toBe ( e1subs ) ; } ) ; } ) ; it ( 'should handle errors' , ( ) => { testScheduler . run ( ( { hot , expectObservable , expectSubscriptions } ) => { const e1 = hot ( '--a--^--b--c--d--#' ) ; const e1subs = '     ^-----------!' ; const expected = '   ---u--v--w--#' ; const values = { u : [ 'b' ] , v : [ 'b' , 'c' ] , w : [ 'b' , 'c' , 'd' ] , } ; const source = e1 . pipe ( scan ( ( acc , x ) => acc . concat ( x ) , [ ] as string [ ] ) ) ; expectObservable ( source ) . toBe ( expected , values ) ; expectSubscriptions ( e1 . subscriptions ) . toBe ( e1subs ) ; } ) ; } ) ; it ( 'should handle errors in the projection function' , ( ) => { testScheduler . run ( ( { hot , expectObservable , expectSubscriptions } ) => { const e1 = hot ( '--a--^--b--c--d--e--f--g--|' ) ; const e1subs = '     ^--------!            ' ; const expected = '   ---u--v--#            ' ; const values = { u : [ 'b' ] , v : [ 'b' , 'c' ] , w : [ 'b' , 'c' , 'd' ] , x : [ 'b' , 'c' , 'd' , 'e' ] , y : [ 'b' , 'c' , 'd' , 'e' , 'f' ] , z : [ 'b' , 'c' , 'd' , 'e' , 'f' , 'g' ] , } ; const source = e1 . pipe ( scan ( ( acc , x ) => { if ( x === 'd' ) { throw 'bad!' ; } return acc . concat ( x ) ; } , [ ] as string [ ] ) ) ; expectObservable ( source ) . toBe ( expected , values , 'bad!' ) ; expectSubscriptions ( e1 . subscriptions ) . toBe ( e1subs ) ; } ) ; } ) ; it ( 'handle empty' , ( ) => { testScheduler . run ( ( { cold , expectObservable , expectSubscriptions } ) => { const e1 = cold ( ' |   ' ) ; const e1subs = '  (^!)' ; const expected = '|   ' ; const source = e1 . pipe ( scan ( ( acc , x ) => acc . concat ( x ) , [ ] as string [ ] ) ) ; expectObservable ( source ) . toBe ( expected ) ; expectSubscriptions ( e1 . subscriptions ) . toBe ( e1subs ) ; } ) ; } ) ; it ( 'handle never' , ( ) => { testScheduler . run ( ( { cold , expectObservable , expectSubscriptions } ) => { const e1 = cold ( ' -' ) ; const e1subs = '  ^' ; const expected = '-' ; const source = e1 . pipe ( scan ( ( acc , x ) => acc . concat ( x ) , [ ] as string [ ] ) ) ; expectObservable ( source ) . toBe ( expected ) ; expectSubscriptions ( e1 . subscriptions ) . toBe ( e1subs ) ; } ) ; } ) ; it ( 'handle throw' , ( ) => { testScheduler . run ( ( { cold , expectObservable , expectSubscriptions } ) => { const e1 = cold ( ' #   ' ) ; const e1subs = '  (^!)' ; const expected = '#   ' ; const source = e1 . pipe ( scan ( ( acc , x ) => acc . concat ( x ) , [ ] as string [ ] ) ) ; expectObservable ( source ) . toBe ( expected ) ; expectSubscriptions ( e1 . subscriptions ) . toBe ( e1subs ) ; } ) ; } ) ; it ( 'should allow unsubscribing explicitly and early' , ( ) => { testScheduler . run ( ( { hot , expectObservable , expectSubscriptions } ) => { const e1 = hot ( '--a--^--b--c--d--e--f--g--|' ) ; const unsub = '      --------------!       ' ; const e1subs = '     ^-------------!       ' ; const expected = '   ---u--v--w--x--       ' ; const values = { u : [ 'b' ] , v : [ 'b' , 'c' ] , w : [ 'b' , 'c' , 'd' ] , x : [ 'b' , 'c' , 'd' , 'e' ] , y : [ 'b' , 'c' , 'd' , 'e' , 'f' ] , z : [ 'b' , 'c' , 'd' , 'e' , 'f' , 'g' ] , } ; const source = e1 . pipe ( scan ( ( acc , x ) => acc . concat ( x ) , [ ] as string [ ] ) ) ; expectObservable ( source , unsub ) . toBe ( expected , values ) ; expectSubscriptions ( e1 . subscriptions ) . toBe ( e1subs ) ; } ) ; } ) ; it ( 'should not break unsubscription chains when result is unsubscribed explicitly' , ( ) => { testScheduler . run ( ( { hot , expectObservable , expectSubscriptions } ) => { const e1 = hot ( '--a--^--b--c--d--e--f--g--|' ) ; const e1subs = '     ^-------------!       ' ; const expected = '   ---u--v--w--x--       ' ; const unsub = '      --------------!       ' ; const values = { u : [ 'b' ] , v : [ 'b' , 'c' ] , w : [ 'b' , 'c' , 'd' ] , x : [ 'b' , 'c' , 'd' , 'e' ] , y : [ 'b' , 'c' , 'd' , 'e' , 'f' ] , z : [ 'b' , 'c' , 'd' , 'e' , 'f' , 'g' ] , } ; const source = e1 . pipe ( mergeMap ( ( x ) => of ( x ) ) , scan ( ( acc , x ) => acc . concat ( x ) , [ ] as string [ ] ) , mergeMap ( ( x ) => of ( x ) ) ) ; expectObservable ( source , unsub ) . toBe ( expected , values ) ; expectSubscriptions ( e1 . subscriptions ) . toBe ( e1subs ) ; } ) ; } ) ; it ( 'should pass current index to accumulator' , ( ) => { testScheduler . run ( ( { hot , expectObservable , expectSubscriptions } ) => { const values = { a : 1 , b : 3 , c : 5 , x : 1 , y : 4 , z : 9 , } ; let idx = [ 0 , 1 , 2 ] ; const e1 = hot ( '  --a--b--c--|' , values ) ; const e1subs = '  ^----------!' ; const expected = '--x--y--z--|' ; const scanFunction = ( o , value , index ) => { expect ( index ) . to . equal ( idx . shift ( ) ) ; return o + value ; } ; const scanObs = e1 . pipe ( scan ( scanFunction , 0 ) , finalize ( ( ) => { expect ( idx ) . to . be . empty ; } ) ) ; expectObservable ( scanObs ) . toBe ( expected , values ) ; expectSubscriptions ( e1 . subscriptions ) . toBe ( e1subs ) ; } ) ; } ) ; it ( 'should stop listening to a synchronous observable when unsubscribed' , ( ) => { const sideEffects = [ ] ; const synchronousObservable = new Observable < number > ( ( subscriber ) => { for ( let i = 0 ; ! subscriber . closed && i < 10 ; i ++ ) { sideEffects . push ( i ) ; subscriber . next ( i ) ; } } ) ; synchronousObservable . pipe ( scan ( ( _acc , value ) => value , 0 ) , take ( 3 ) ) . subscribe ( ( ) => { } ) ; expect ( sideEffects ) . to . deep . equal ( [ 0 , 1 , 2 ] ) ; } ) ; } ) ;