import * as assert from 'assert' ; import { readable , writable , derived , get } from '../../store' ; describe ( 'store' , ( ) => { describe ( 'writable' , ( ) => { it ( 'creates a writable store' , ( ) => { const count = writable ( 0 ) ; const values = [ ] ; const unsubscribe = count . subscribe ( value => { values . push ( value ) ; } ) ; count . set ( 1 ) ; count . update ( n => n + 1 ) ; unsubscribe ( ) ; count . set ( 3 ) ; count . update ( n => n + 1 ) ; assert . deepEqual ( values , [ 0 , 1 , 2 ] ) ; } ) ; it ( 'creates an undefined writable store' , ( ) => { const store = writable ( ) ; const values = [ ] ; const unsubscribe = store . subscribe ( value => { values . push ( value ) ; } ) ; unsubscribe ( ) ; assert . deepEqual ( values , [ undefined ] ) ; } ) ; it ( 'calls provided subscribe handler' , ( ) => { let called = 0 ; const store = writable ( 0 , ( ) => { called += 1 ; return ( ) => called -= 1 ; } ) ; const unsubscribe1 = store . subscribe ( ( ) => { } ) ; assert . equal ( called , 1 ) ; const unsubscribe2 = store . subscribe ( ( ) => { } ) ; assert . equal ( called , 1 ) ; unsubscribe1 ( ) ; assert . equal ( called , 1 ) ; unsubscribe2 ( ) ; assert . equal ( called , 0 ) ; } ) ; it ( 'does not assume immutable data' , ( ) => { const obj = { } ; let called = 0 ; const store = writable ( obj ) ; store . subscribe ( ( ) => { called += 1 ; } ) ; store . set ( obj ) ; assert . equal ( called , 2 ) ; store . update ( obj => obj ) ; assert . equal ( called , 3 ) ; } ) ; it ( 'only calls subscriber once initially, including on resubscriptions' , ( ) => { let num = 0 ; const store = writable ( num , set => set ( num += 1 ) ) ; let count1 = 0 ; let count2 = 0 ; store . subscribe ( ( ) => count1 += 1 ) ( ) ; assert . equal ( count1 , 1 ) ; const unsubscribe = store . subscribe ( ( ) => count2 += 1 ) ; assert . equal ( count2 , 1 ) ; unsubscribe ( ) ; } ) ; } ) ; describe ( 'readable' , ( ) => { it ( 'creates a readable store' , ( ) => { let running ; let tick ; const store = readable ( undefined , set => { tick = set ; running = true ; set ( 0 ) ; return ( ) => { tick = ( ) => { } ; running = false ; } ; } ) ; assert . ok ( ! running ) ; const values = [ ] ; const unsubscribe = store . subscribe ( value => { values . push ( value ) ; } ) ; assert . ok ( running ) ; tick ( 1 ) ; tick ( 2 ) ; unsubscribe ( ) ; assert . ok ( ! running ) ; tick ( 3 ) ; tick ( 4 ) ; assert . deepEqual ( values , [ 0 , 1 , 2 ] ) ; } ) ; it ( 'creates an undefined readable store' , ( ) => { const store = readable ( ) ; const values = [ ] ; const unsubscribe = store . subscribe ( value => { values . push ( value ) ; } ) ; unsubscribe ( ) ; assert . deepEqual ( values , [ undefined ] ) ; } ) ; it ( 'creates a readable store without updater' , ( ) => { const store = readable ( 100 ) ; const values = [ ] ; const unsubscribe = store . subscribe ( value => { values . push ( value ) ; } ) ; unsubscribe ( ) ; assert . deepEqual ( values , [ 100 ] ) ; } ) ; } ) ; const fake_observable = { subscribe ( fn ) { fn ( 42 ) ; return { unsubscribe : ( ) => { } } ; } } ; describe ( 'derived' , ( ) => { it ( 'maps a single store' , ( ) => { const a = writable ( 1 ) ; const b = derived ( a , n => n * 2 ) ; const values = [ ] ; const unsubscribe = b . subscribe ( value => { values . push ( value ) ; } ) ; a . set ( 2 ) ; assert . deepEqual ( values , [ 2 , 4 ] ) ; unsubscribe ( ) ; a . set ( 3 ) ; assert . deepEqual ( values , [ 2 , 4 ] ) ; } ) ; it ( 'maps multiple stores' , ( ) => { const a = writable ( 2 ) ; const b = writable ( 3 ) ; const c = derived ( ( [ a , b ] ) , ( [ a , b ] ) => a * b ) ; const values = [ ] ; const unsubscribe = c . subscribe ( value => { values . push ( value ) ; } ) ; a . set ( 4 ) ; b . set ( 5 ) ; assert . deepEqual ( values , [ 6 , 12 , 20 ] ) ; unsubscribe ( ) ; a . set ( 6 ) ; assert . deepEqual ( values , [ 6 , 12 , 20 ] ) ; } ) ; it ( 'passes optional set function' , ( ) => { const number = writable ( 1 ) ; const evens = derived ( number , ( n , set ) => { if ( n % 2 === 0 ) set ( n ) ; } , 0 ) ; const values = [ ] ; const unsubscribe = evens . subscribe ( value => { values . push ( value ) ; } ) ; number . set ( 2 ) ; number . set ( 3 ) ; number . set ( 4 ) ; number . set ( 5 ) ; assert . deepEqual ( values , [ 0 , 2 , 4 ] ) ; unsubscribe ( ) ; number . set ( 6 ) ; number . set ( 7 ) ; number . set ( 8 ) ; assert . deepEqual ( values , [ 0 , 2 , 4 ] ) ; } ) ; it ( 'prevents glitches' , ( ) => { const lastname = writable ( 'Jekyll' ) ; const firstname = derived ( lastname , n => n === 'Jekyll' ? 'Henry' : 'Edward' ) ; const fullname = derived ( [ firstname , lastname ] , names => names . join ( ' ' ) ) ; const values = [ ] ; const unsubscribe = fullname . subscribe ( value => { values . push ( value ) ; } ) ; lastname . set ( 'Hyde' ) ; assert . deepEqual ( values , [ 'Henry Jekyll' , 'Edward Hyde' ] ) ; unsubscribe ( ) ; } ) ; it ( 'prevents diamond dependency problem' , ( ) => { const count = writable ( 0 ) ; const values = [ ] ; const a = derived ( count , $count => { return 'a' + $count ; } ) ; const b = derived ( count , $count => { return 'b' + $count ; } ) ; const combined = derived ( [ a , b ] , ( [ a , b ] ) => { return a + b ; } ) ; const unsubscribe = combined . subscribe ( v => { values . push ( v ) ; } ) ; assert . deepEqual ( values , [ 'a0b0' ] ) ; count . set ( 1 ) ; assert . deepEqual ( values , [ 'a0b0' , 'a1b1' ] ) ; unsubscribe ( ) ; } ) ; it ( 'derived dependency does not update and shared ancestor updates' , ( ) => { const root = writable ( { a : 0 , b : 0 } ) ; const values = [ ] ; const a = derived ( root , $root => { return 'a' + $root . a ; } ) ; const b = derived ( [ a , root ] , ( [ $a , $root ] ) => { return 'b' + $root . b + $a ; } ) ; const unsubscribe = b . subscribe ( v => { values . push ( v ) ; } ) ; assert . deepEqual ( values , [ 'b0a0' ] ) ; root . set ( { a : 0 , b : 1 } ) ; assert . deepEqual ( values , [ 'b0a0' , 'b1a0' ] ) ; unsubscribe ( ) ; } ) ; it ( 'is updated with safe_not_equal logic' , ( ) => { const arr = [ 0 ] ; const number = writable ( 1 ) ; const numbers = derived ( number , $number => { arr [ 0 ] = $number ; return arr ; } ) ; const concatenated = [ ] ; const unsubscribe = numbers . subscribe ( value => { concatenated . push ( ... value ) ; } ) ; number . set ( 2 ) ; number . set ( 3 ) ; assert . deepEqual ( concatenated , [ 1 , 2 , 3 ] ) ; unsubscribe ( ) ; } ) ; it ( 'calls a cleanup function' , ( ) => { const num = writable ( 1 ) ; const values = [ ] ; const cleaned_up = [ ] ; const d = derived ( num , ( $num , set ) => { set ( $num * 2 ) ; return function cleanup ( ) { cleaned_up . push ( $num ) ; } ; } ) ; num . set ( 2 ) ; const unsubscribe = d . subscribe ( value => { values . push ( value ) ; } ) ; num . set ( 3 ) ; num . set ( 4 ) ; assert . deepEqual ( values , [ 4 , 6 , 8 ] ) ; assert . deepEqual ( cleaned_up , [ 2 , 3 ] ) ; unsubscribe ( ) ; assert . deepEqual ( cleaned_up , [ 2 , 3 , 4 ] ) ; } ) ; it ( 'discards non-function return values' , ( ) => { const num = writable ( 1 ) ; const values = [ ] ; const d = derived ( num , ( $num , set ) => { set ( $num * 2 ) ; return { } ; } ) ; num . set ( 2 ) ; const unsubscribe = d . subscribe ( value => { values . push ( value ) ; } ) ; num . set ( 3 ) ; num . set ( 4 ) ; assert . deepEqual ( values , [ 4 , 6 , 8 ] ) ; unsubscribe ( ) ; } ) ; it ( 'allows derived with different types' , ( ) => { const a = writable ( 'one' ) ; const b = writable ( 1 ) ; const c = derived ( [ a , b ] , ( [ a , b ] ) => `${ a } ${ b }` ) ; assert . deepEqual ( get ( c ) , 'one 1' ) ; a . set ( 'two' ) ; b . set ( 2 ) ; assert . deepEqual ( get ( c ) , 'two 2' ) ; } ) ; it ( 'works with RxJS-style observables' , ( ) => { const d = derived ( fake_observable , _ => _ ) ; assert . equal ( get ( d ) , 42 ) ; } ) ; } ) ; describe ( 'get' , ( ) => { it ( 'gets the current value of a store' , ( ) => { const store = readable ( 42 , ( ) => { } ) ; assert . equal ( get ( store ) , 42 ) ; } ) ; it ( 'works with RxJS-style observables' , ( ) => { assert . equal ( get ( fake_observable ) , 42 ) ; } ) ; } ) ; } ) ;