import test from "blue-tape" ; import { testStrictness , waitForLength , Obs } from "./util" ; import { delay } from "./delay" ; import { observable , runInAction , autorun } from "mobx" ; import { asyncComputed } from "../src/index" ; testStrictness ( "asyncComputed - can't be used outside of reactive contexts" , async ( assert ) => { const o = observable ( { x : 1 , y : 2 } ) ; const r = asyncComputed ( undefined , 10 , async ( ) => { const result = o . x + o . y ; await delay ( 100 ) ; return result ; } ) ; assert . throws ( ( ) => r . get ( ) , /inside reactions/ ) ; } ) ; testStrictness ( "asyncComputed - transitions to new values" , async ( assert ) => { const o = observable ( { x : 1 , y : 2 } ) ; const r = asyncComputed ( 99 , 10 , async ( ) => { const result = o . x + o . y ; await delay ( 10 ) ; return result ; } ) ; const trace = [ ] ; const stop = autorun ( ( ) => trace . push ( r . get ( ) ) ) ; assert . deepEqual ( trace , [ 99 ] , "Init value until promise resolves" ) ; await waitForLength ( trace , 2 ) ; assert . deepEqual ( trace , [ 99 , 3 ] , "First real value appears" ) ; runInAction ( ( ) => o . x = 5 ) ; assert . deepEqual ( trace , [ 99 , 3 ] , "No second value until promise resolves [2]" ) ; await waitForLength ( trace , 3 ) ; assert . deepEqual ( trace , [ 99 , 3 , 7 ] , "Second value appears" ) ; stop ( ) ; } ) ; testStrictness ( "asyncComputed - can be refreshed" , async ( assert ) => { let counter = 0 ; const r = asyncComputed ( 0 , 10 , async ( ) => { await delay ( 10 ) ; return ++ counter ; } ) ; const trace = [ ] ; const stop = autorun ( ( ) => trace . push ( r . get ( ) ) ) ; assert . deepEqual ( trace , [ 0 ] , "No new value until promise resolves" ) ; await waitForLength ( trace , 2 ) ; assert . deepEqual ( trace , [ 0 , 1 ] , "First proper value appears" ) ; r . refresh ( ) ; assert . deepEqual ( trace , [ 0 , 1 ] , "No value until promise resolves [2]" ) ; await waitForLength ( trace , 3 ) ; assert . deepEqual ( trace , [ 0 , 1 , 2 ] , "Second value appears" ) ; stop ( ) ; } ) ; testStrictness ( "asyncComputed - busy property works by itself" , async ( assert ) => { const o = observable ( { x : 1 , y : 2 } ) ; const r = asyncComputed ( undefined , 10 , async ( ) => { const result = o . x + o . y ; await delay ( 10 ) ; return result ; } ) ; const trace = [ ] ; const stop = autorun ( ( ) => trace . push ( r . busy ) ) ; assert . deepEqual ( trace , [ true ] , "Is initially busy" ) ; await waitForLength ( trace , 2 ) ; assert . deepEqual ( trace , [ true , false ] , "Busy transitions to false" ) ; runInAction ( ( ) => o . x = 5 ) ; assert . deepEqual ( trace , [ true , false ] , "Doesn't synchronously transition to true, due to throttling" ) ; await waitForLength ( trace , 3 ) ; assert . deepEqual ( trace , [ true , false , true ] , "Eventually transitions to true" ) ; await waitForLength ( trace , 4 ) ; assert . deepEqual ( trace , [ true , false , true , false ] , "Second transition to false" ) ; stop ( ) ; } ) ; testStrictness ( "asyncComputed - busy property interleaves with value changes" , async ( assert ) => { const o = observable ( { x : 1 , y : 2 } ) ; const r = asyncComputed ( 99 , 10 , async ( ) => { const result = o . x + o . y ; await delay ( 10 ) ; return result ; } ) ; const trace = [ ] ; const stop = autorun ( ( ) => trace . push ( { value : r . get ( ) , busy : r . busy } ) ) ; assert . deepEqual ( trace , [ { value : 99 , busy : true } ] , "No value until promise resolves" ) ; await waitForLength ( trace , 2 ) ; assert . deepEqual ( trace , [ { value : 99 , busy : true } , { value : 3 , busy : false } ] , "Initial value appears" ) ; runInAction ( ( ) => o . x = 5 ) ; assert . deepEqual ( trace , [ { value : 99 , busy : true } , { value : 3 , busy : false } ] , "No synchronous change in busy" ) ; await waitForLength ( trace , 3 ) ; assert . deepEqual ( trace , [ { value : 99 , busy : true } , { value : 3 , busy : false } , { value : 3 , busy : true } ] , "Eventually turns busy" ) ; await waitForLength ( trace , 4 ) ; assert . deepEqual ( trace , [ { value : 99 , busy : true } , { value : 3 , busy : false } , { value : 3 , busy : true } , { value : 7 , busy : false } ] , "Second value appears" ) ; stop ( ) ; } ) ; testStrictness ( "asyncComputed - propagates exceptions" , async ( assert ) => { const o = new Obs ( false ) ; const r = asyncComputed ( "Init" , 10 , async ( ) => { const shouldThrow = o . get ( ) ; await delay ( 10 ) ; if ( shouldThrow ) { throw new Error ( "Badness" ) ; } return "Goodness" ; } ) ; const trace = [ ] ; const stop = autorun ( ( ) => { try { trace . push ( r . get ( ) ) ; } catch ( x ) { trace . push ( x . message ) ; } } ) ; assert . deepEqual ( trace , [ "Init" ] ) ; await waitForLength ( trace , 2 ) ; assert . deepEqual ( trace , [ "Init" , "Goodness" ] ) ; runInAction ( ( ) => o . set ( true ) ) ; assert . deepEqual ( trace , [ "Init" , "Goodness" ] , "Reactive contexts don't seem immediate changes" ) ; await waitForLength ( trace , 3 ) ; assert . deepEqual ( trace , [ "Init" , "Goodness" , "Badness" ] , "But do see delayed changes" ) ; runInAction ( ( ) => o . set ( false ) ) ; runInAction ( ( ) => o . set ( true ) ) ; runInAction ( ( ) => o . set ( false ) ) ; await waitForLength ( trace , 4 ) ; assert . deepEqual ( trace , [ "Init" , "Goodness" , "Badness" , "Badness" ] , "Change to busy makes us see another exception" ) ; await waitForLength ( trace , 5 ) ; assert . deepEqual ( trace , [ "Init" , "Goodness" , "Badness" , "Badness" , "Goodness" ] , "Changes are batched by throttling" ) ; runInAction ( ( ) => o . set ( true ) ) ; await delay ( 1 ) ; runInAction ( ( ) => o . set ( false ) ) ; runInAction ( ( ) => o . set ( true ) ) ; await waitForLength ( trace , 6 ) ; assert . deepEqual ( trace , [ "Init" , "Goodness" , "Badness" , "Badness" , "Goodness" , "Badness" ] , "Changes are batched again" ) ; stop ( ) ; } ) ;