import atomic from "../src/atomic" ; import { types , addMiddleware , flow , decorate } from "mobx-state-tree" ; const TestModel = types . model ( { a : 1 , b : 1 } ) . actions ( ( self ) => { addMiddleware ( self , atomic ) ; return { incA ( x ) { self . a += x ; this . incB ( x ) ; return self . a ; } , incB ( x ) { self . b += x ; return self . b ; } , throwingFn ( x ) { this . incA ( x ) ; throw "Oops" ; } , incProcessA : flow ( function * ( x ) { yield Promise . resolve ( ) ; self . a += x ; yield Promise . resolve ( ) ; self . a += x ; yield ( self as any ) . incProcessB ( x ) ; return self . a ; } ) , incProcessB : flow ( function * ( x ) { yield Promise . resolve ( ) ; self . b += x ; yield Promise . resolve ( ) ; self . b += x ; return self . a ; } ) , throwingProcess : flow ( function * ( x ) { yield ( self as any ) . incProcessA ( x ) ; throw "Oops" ; } ) } ; } ) ; test ( "should run action normally" , ( ) => { const m = TestModel . create ( ) ; expect ( m . incA ( 3 ) ) . toBe ( 4 ) ; expect ( m . a ) . toBe ( 4 ) ; expect ( m . b ) . toBe ( 4 ) ; } ) ; test ( "should rollback on action failure" , ( ) => { const m = TestModel . create ( ) ; expect ( ( ) => { m . throwingFn ( 3 ) ; } ) . toThrow ( /Oops/ ) ; expect ( m . a ) . toBe ( 1 ) ; expect ( m . b ) . toBe ( 1 ) ; } ) ; test ( "should run async action normally on action failure" , async ( ) => { const m = TestModel . create ( ) ; const value = await m . incProcessA ( 3 ) ; expect ( value ) . toBe ( 7 ) ; expect ( m . a ) . toBe ( 7 ) ; expect ( m . b ) . toBe ( 7 ) ; } ) ; test ( "should rollback on async action failure" , async ( ) => { const m = TestModel . create ( ) ; try { await m . throwingProcess ( 3 ) ; } catch ( e ) { expect ( e ) . toBe ( "Oops" ) ; expect ( m . a ) . toBe ( 1 ) ; expect ( m . b ) . toBe ( 1 ) ; } } ) ; test ( "decorate atomic should work" , ( ) => { const T = types . model ( { a : 0 } ) . actions ( ( self ) => ( { inc ( x ) { self . a += x ; } , atomicInc : decorate ( atomic , ( x , _throw ) => { self . a += x ; ( self as any ) . inc ( x ) ; if ( _throw ) { throw "Oops" ; } return self . a ; } ) , nonAtomicInc ( x , _throw ) { self . a += x ; ( self as any ) . inc ( x ) ; if ( _throw ) { throw "Oops" ; } return self . a ; } } ) ) ; const t = T . create ( ) ; expect ( t . atomicInc ( 1 , false ) ) . toBe ( 2 ) ; expect ( t . nonAtomicInc ( - 1 , false ) ) . toBe ( 0 ) ; expect ( t . a ) . toBe ( 0 ) ; expect ( ( ) => t . atomicInc ( 1 , true ) ) . toThrow ( "Oops" ) ; expect ( t . a ) . toBe ( 0 ) ; expect ( ( ) => t . nonAtomicInc ( 1 , true ) ) . toThrow ( "Oops" ) ; expect ( t . a ) . toBe ( 2 ) ; } ) ; test ( "#1250" , async ( ) => { const M = types . model ( { x : 0 , y : 0 } ) . actions ( ( self ) => ( { setX : decorate ( atomic , flow ( function * ( ) { self . x = 10 ; yield new Promise ( ( resolve ) => setTimeout ( resolve , 1000 ) ) ; throw new Error ( ) ; } ) ) , setY ( ) { self . y = 10 ; } } ) ) ; const model = M . create ( { } ) ; expect ( model . x ) . toBe ( 0 ) ; expect ( model . y ) . toBe ( 0 ) ; const p = model . setX ( ) ; expect ( model . x ) . toBe ( 10 ) ; expect ( model . y ) . toBe ( 0 ) ; await new Promise ( ( r ) => setTimeout ( ( ) => { model . setY ( ) ; r ( ) ; } , 500 ) ) ; expect ( model . x ) . toBe ( 10 ) ; expect ( model . y ) . toBe ( 10 ) ; try { await p ; fail ( "should have failed" ) ; } catch ( e ) { expect ( model . x ) . toBe ( 0 ) ; expect ( model . y ) . toBe ( 10 ) ; } } ) ;