prettier import { expect } from 'chai' ; import { observableMatcher } from '../helpers/observableMatcher' ; import { first , mergeMap , delay } from 'rxjs/operators' ; import { TestScheduler } from 'rxjs/testing' ; import { of , from , Observable , Subject , EmptyError } from 'rxjs' ; test describe ( 'first' , ( ) => { let testScheduler ; beforeEach ( ( ) => { testScheduler = new TestScheduler ( observableMatcher ) ; } ) ; it ( 'should take the first value of an observable with many values' , ( ) => { testScheduler . run ( ( { hot , expectObservable , expectSubscriptions } ) => { const e1 = hot ( '  -----a--b--c---d---|' ) ; const e1subs = '  ^----!              ' ; const expected = '-----(a|)           ' ; expectObservable ( e1 . pipe ( first ( ) ) ) . toBe ( expected ) ; expectSubscriptions ( e1 . subscriptions ) . toBe ( e1subs ) ; } ) ; } ) ; it ( 'should take the first value of an observable with one value' , ( ) => { testScheduler . run ( ( { hot , expectObservable , expectSubscriptions } ) => { const e1 = hot ( '  ---(a|)' ) ; const e1subs = '  ^--!   ' ; const expected = '---(a|)' ; expectObservable ( e1 . pipe ( first ( ) ) ) . toBe ( expected ) ; expectSubscriptions ( e1 . subscriptions ) . toBe ( e1subs ) ; } ) ; } ) ; it ( 'should allow undefined as a default value' , ( ) => { testScheduler . run ( ( { hot , expectObservable , expectSubscriptions } ) => { const e1 = hot ( '  -----a--a---a-|   ' ) ; const e1subs = '  ^-------------!   ' ; const expected = '--------------(U|)' ; expectObservable ( e1 . pipe ( first ( ( value ) => value === 'b' , undefined ) ) ) . toBe ( expected , { U : undefined } ) ; expectSubscriptions ( e1 . subscriptions ) . toBe ( e1subs ) ; } ) ; } ) ; it ( 'should error on empty' , ( ) => { testScheduler . run ( ( { hot , expectObservable , expectSubscriptions } ) => { const e1 = hot ( '--a--^----|' ) ; const e1subs = '     ^----!' ; const expected = '   -----#' ; expectObservable ( e1 . pipe ( first ( ) ) ) . toBe ( expected , null , new EmptyError ( ) ) ; expectSubscriptions ( e1 . subscriptions ) . toBe ( e1subs ) ; } ) ; } ) ; it ( 'should return the default value if source observable was empty' , ( ) => { testScheduler . run ( ( { hot , expectObservable , expectSubscriptions } ) => { const e1 = hot ( '-----^----|   ' ) ; const e1subs = '     ^----!   ' ; const expected = '   -----(a|)' ; expectObservable ( e1 . pipe ( first ( null , 'a' ) ) ) . toBe ( expected ) ; expectSubscriptions ( e1 . subscriptions ) . toBe ( e1subs ) ; } ) ; } ) ; it ( 'should only emit one value in recursive cases' , ( ) => { const subject = new Subject < number > ( ) ; const results = [ ] ; subject . pipe ( first ( ) ) . subscribe ( ( x ) => { results . push ( x ) ; subject . next ( x + 1 ) ; } ) ; subject . next ( 0 ) ; expect ( results ) . to . deep . equal ( [ 0 ] ) ; } ) ; it ( 'should propagate error from the source observable' , ( ) => { testScheduler . run ( ( { hot , expectObservable , expectSubscriptions } ) => { const e1 = hot ( '---^---#' ) ; const e1subs = '   ^---!' ; const expected = ' ----#' ; expectObservable ( e1 . pipe ( first ( ) ) ) . toBe ( expected ) ; expectSubscriptions ( e1 . subscriptions ) . toBe ( e1subs ) ; } ) ; } ) ; it ( 'should go on forever on never' , ( ) => { testScheduler . run ( ( { hot , expectObservable , expectSubscriptions } ) => { const e1 = hot ( '--^-------' ) ; const e1subs = '  ^-------' ; const expected = '--------' ; expectObservable ( e1 . pipe ( first ( ) ) ) . toBe ( expected ) ; expectSubscriptions ( e1 . subscriptions ) . toBe ( e1subs ) ; } ) ; } ) ; it ( 'should allow unsubscribing early and explicitly' , ( ) => { testScheduler . run ( ( { hot , expectObservable , expectSubscriptions } ) => { const e1 = hot ( '--a--^-----b----c---d--|' ) ; const e1subs = '     ^--!               ' ; const expected = '   ----               ' ; const unsub = '      ---!               ' ; expectObservable ( e1 . pipe ( first ( ) ) , unsub ) . toBe ( expected ) ; 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--|' ) ; const e1subs = '     ^--!               ' ; const expected = '   ----               ' ; const unsub = '      ---!               ' ; const result = e1 . pipe ( mergeMap ( ( x ) => of ( x ) ) , first ( ) , mergeMap ( ( x ) => of ( x ) ) ) ; expectObservable ( result , unsub ) . toBe ( expected ) ; expectSubscriptions ( e1 . subscriptions ) . toBe ( e1subs ) ; } ) ; } ) ; it ( 'should unsubscribe when the first value is received' , ( ) => { testScheduler . run ( ( { hot , time , expectObservable , expectSubscriptions } ) => { const e1 = hot ( '  --a--b---c-|' ) ; const e1subs = '  ^-!         ' ; const t = time ( '    --|       ' ) ; const expected = '----(a|)    ' ; const result = e1 . pipe ( first ( ) , delay ( t ) ) ; expectObservable ( result ) . toBe ( expected ) ; expectSubscriptions ( e1 . subscriptions ) . toBe ( e1subs ) ; } ) ; } ) ; it ( 'should return first value that matches a predicate' , ( ) => { testScheduler . run ( ( { hot , expectObservable , expectSubscriptions } ) => { const e1 = hot ( '--a-^--b--c--a--c--|' ) ; const e1subs = '    ^-----!         ' ; const expected = '  ------(c|)      ' ; expectObservable ( e1 . pipe ( first ( ( value ) => value === 'c' ) ) ) . toBe ( expected ) ; expectSubscriptions ( e1 . subscriptions ) . toBe ( e1subs ) ; } ) ; } ) ; it ( 'should return first value that matches a predicate for odd numbers' , ( ) => { testScheduler . run ( ( { hot , expectObservable , expectSubscriptions } ) => { const values = { a : 1 , b : 2 , c : 3 , d : 4 , e : 5 } ; const e1 = hot ( '--a-^--b--c--d--e--|' , values ) ; const e1subs = '    ^-----!         ' ; const expected = '  ------(c|)      ' ; expectObservable ( e1 . pipe ( first ( ( x ) => x % 2 === 1 ) ) ) . toBe ( expected , { c : 3 } ) ; expectSubscriptions ( e1 . subscriptions ) . toBe ( e1subs ) ; } ) ; } ) ; it ( 'should error when no value matches the predicate' , ( ) => { testScheduler . run ( ( { hot , expectObservable , expectSubscriptions } ) => { const e1 = hot ( '--a-^--b--c--a--c--|' ) ; const e1subs = '    ^--------------!' ; const expected = '  ---------------#' ; expectObservable ( e1 . pipe ( first ( ( x ) => x === 's' ) ) ) . toBe ( expected , null , new EmptyError ( ) ) ; expectSubscriptions ( e1 . subscriptions ) . toBe ( e1subs ) ; } ) ; } ) ; it ( 'should return the default value when no value matches the predicate' , ( ) => { testScheduler . run ( ( { hot , expectObservable , expectSubscriptions } ) => { const e1 = hot ( '--a-^--b--c--a--c--|   ' ) ; const e1subs = '    ^--------------!   ' ; const expected = '  ---------------(d|)' ; expectObservable ( e1 . pipe ( first ( ( x ) => x === 's' , 'd' ) ) ) . toBe ( expected ) ; expectSubscriptions ( e1 . subscriptions ) . toBe ( e1subs ) ; } ) ; } ) ; it ( 'should propagate error when no value matches the predicate' , ( ) => { testScheduler . run ( ( { hot , expectObservable , expectSubscriptions } ) => { const e1 = hot ( '--a-^--b--c--a--#' ) ; const e1subs = '    ^-----------!' ; const expected = '  ------------#' ; expectObservable ( e1 . pipe ( first ( ( x ) => x === 's' ) ) ) . toBe ( expected ) ; expectSubscriptions ( e1 . subscriptions ) . toBe ( e1subs ) ; } ) ; } ) ; it ( 'should return first value that matches the index in the predicate' , ( ) => { testScheduler . run ( ( { hot , expectObservable , expectSubscriptions } ) => { const e1 = hot ( '--a-^--b--c--a--c--|' ) ; const e1subs = '    ^--------!      ' ; const expected = '  ---------(a|)   ' ; expectObservable ( e1 . pipe ( first ( ( _ , i ) => i === 2 ) ) ) . toBe ( expected ) ; expectSubscriptions ( e1 . subscriptions ) . toBe ( e1subs ) ; } ) ; } ) ; it ( 'should propagate error from predicate' , ( ) => { testScheduler . run ( ( { hot , expectObservable , expectSubscriptions } ) => { const values = { a : 1 , b : 2 , c : 3 , d : 4 , e : 5 } ; const e1 = hot ( '--a-^--b--c--d--e--|' , values ) ; const e1subs = '    ^--------!      ' ; const expected = '  ---------#      ' ; const predicate = function ( value ) { if ( value < 4 ) { return false ; } else { throw 'error' ; } } ; expectObservable ( e1 . pipe ( first ( predicate ) ) ) . toBe ( expected , null , 'error' ) ; expectSubscriptions ( e1 . subscriptions ) . toBe ( e1subs ) ; } ) ; } ) ; it ( 'should support type guards without breaking previous behavior' , ( ) => { { interface Bar { bar ? : string ; } interface Baz { baz ? : number ; } class Foo implements Bar , Baz { constructor ( public bar = 'name' , public baz = 42 ) { } } const isBar = ( x ) : x is Bar => x && ( x as Bar ) . bar !== undefined ; const isBaz = ( x ) : x is Baz => x && ( x as Baz ) . baz !== undefined ; const foo = new Foo ( ) ; of ( foo ) . pipe ( first ( ) ) . subscribe ( ( x ) => x . baz ) ; of ( foo ) . pipe ( first ( ( foo ) => foo . bar === 'name' ) ) . subscribe ( ( x ) => x . baz ) ; of ( foo ) . pipe ( first ( isBar ) ) . subscribe ( ( x ) => x . bar ) ; const foobar = new Foo ( ) ; of ( foobar ) . pipe ( first ( ) ) . subscribe ( ( x ) => x . bar ) ; of ( foobar ) . pipe ( first ( ( foobar ) => foobar . bar === 'name' ) ) . subscribe ( ( x ) => x . bar ) ; of ( foobar ) . pipe ( first ( isBaz ) ) . subscribe ( ( x ) => x . baz ) ; const barish = { bar : 'quack' , baz : 42 } ; of ( barish ) . pipe ( first ( ) ) . subscribe ( ( x ) => x . baz ) ; of ( barish ) . pipe ( first ( ( x ) => x . bar === 'quack' ) ) . subscribe ( ( x ) => x . bar ) ; of ( barish ) . pipe ( first ( isBar ) ) . subscribe ( ( x ) => x . bar ) ; } { const xs = from ( [ 1 , 'aaa' , 3 , 'bb' ] ) ; const isString = ( x ) : x is string => typeof x === 'string' ; xs . pipe ( first ( ) ) . subscribe ( ( x ) => x ) ; xs . pipe ( first ( null ) ) . subscribe ( ( x ) => x ) ; xs . pipe ( first ( undefined ) ) . subscribe ( ( x ) => x ) ; xs . pipe ( first ( isString ) ) . subscribe ( ( s ) => s . length ) ; xs . pipe ( first ( ( x ) => typeof x === 'string' ) ) . subscribe ( ( x ) => x ) ; } } ) ; 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 ( first ( ( value ) => value === 2 ) ) . subscribe ( ( ) => { } ) ; expect ( sideEffects ) . to . deep . equal ( [ 0 , 1 , 2 ] ) ; } ) ; } ) ;