import { decorate , types , addMiddleware , flow , destroy , IMiddlewareHandler } from "mobx-state-tree" ; let error = null ; const omitNextAbort = ( call , next ) => { } ; const nextAndAbort = ( call , next , abort ) => { abort ( "someValue" ) ; next ( call ) ; } ; const abortString = ( call , next , abort ) => { abort ( "someValue" ) ; } ; const abortNumeric = ( call , next , abort ) => { abort ( 5 ) ; } ; const abortPromise = ( call , next , abort ) => { abort ( Promise . resolve ( 5 ) ) ; } ; const nextNoAlter = ( call , next , abort ) => { next ( call ) ; } ; const nextAlter = ( call , next , abort ) => { next ( call , ( value ) => value + 1 ) ; } ; const nextAlter2 = ( call , next , abort ) => { next ( call , ( value ) => value + 2 ) ; } ; const alterArguments = ( call , next , abort ) => { next ( { ... call , args : [ call . args [ 0 ] . toUpperCase ( ) ] } ) ; } ; const alterArguments2 = ( call , next , abort ) => { next ( { ... call , args : [ call . args [ 0 ] . slice ( 0 , - 1 ) ] } ) ; } ; const nextAlterAsync = ( call , next , abort ) => { next ( call , async ( value ) => ( await value ) + 2 ) ; } ; const noHooksMiddleware = ( call , next , abort ) => { if ( call . name === "beforeDestroy" ) error = Error ( "hook in middleware" ) ; next ( call ) ; } ; const shouldNeverBeInvoked = ( call , next , abort ) => { error = Error ( "customMiddleware called even though the queue was aborted" ) ; next ( call ) ; } ; function delay ( time ) { return new Promise ( ( resolve ) => { setTimeout ( resolve , time ) ; } ) ; } const TestModel = types . model ( "Test1" , { z : 1 } ) . postProcessSnapshot ( ( snapshot ) => { return snapshot ; } ) . actions ( ( self ) => { return { asyncInc : flow ( function * ( x ) { yield delay ( 2 ) ; self . z += x ; yield delay ( 2 ) ; return self . z ; } ) , asyncIncSuccess : flow ( function * ( x ) { yield ( self as any ) . asyncAbortedPromise ( ) ; self . z += x ; return self . z ; } ) , asyncIncFailing : flow ( function * ( x ) { yield ( self as any ) . asyncAbortedString ( ) ; self . z += x ; return self . z ; } ) , asyncAbortedString : decorate ( abortString , flow ( function * ( x ) { yield delay ( 2 ) ; return 205 ; } ) ) , asyncAbortedPromise : decorate ( abortPromise , flow ( function * ( x ) { yield delay ( 2 ) ; return 205 ; } ) ) , inc ( x ) { self . z += x ; return self . z ; } , addName ( name ) { return name ; } , beforeDestroy ( ) { } } ; } ) ; test ( "next() middleware queue " , ( ) => { const m = TestModel . create ( ) ; addMiddleware ( m , nextNoAlter ) ; const valueFromMiddleware = m . inc ( 1 ) ; expect ( valueFromMiddleware ) . toBe ( 2 ) ; } ) ; test ( "next() middleware queue and alter the call arguments" , ( ) => { const m = TestModel . create ( ) ; addMiddleware ( m , alterArguments ) ; addMiddleware ( m , alterArguments2 ) ; const valueFromMiddleware = m . addName ( "Freddy" ) ; expect ( valueFromMiddleware ) . toBe ( "FREDD" ) ; } ) ; test ( "next() middleware queue and alter the action value" , ( ) => { const m = TestModel . create ( ) ; addMiddleware ( m , nextAlter ) ; addMiddleware ( m , nextAlter2 ) ; const valueFromMiddleware = m . inc ( 1 ) ; expect ( valueFromMiddleware ) . toBe ( 5 ) ; } ) ; test ( "next() middleware queue and alter the async action value" , async ( ) => { const m = TestModel . create ( ) ; addMiddleware ( m , nextAlterAsync ) ; const valueFromMiddleware = await m . asyncInc ( 1 ) ; expect ( valueFromMiddleware ) . toBe ( 4 ) ; } ) ; test ( "abort() middleware queue" , ( ) => { error = null ; const m = TestModel . create ( ) ; addMiddleware ( m , abortString ) ; addMiddleware ( m , shouldNeverBeInvoked ) ; const valueFromMiddleware = m . inc ( 1 ) ; expect ( valueFromMiddleware ) . toBe ( "someValue" ) ; expect ( error ) . toBe ( null ) ; } ) ; test ( "abort() middleware queue and alter the abort value" , ( ) => { const m = TestModel . create ( ) ; addMiddleware ( m , nextAlter ) ; addMiddleware ( m , abortNumeric ) ; const valueFromMiddleware = m . inc ( 1 ) ; expect ( valueFromMiddleware ) . toBe ( 6 ) ; } ) ; test ( "next() middleware queue and alter the async action value" , async ( ) => { const m = TestModel . create ( ) ; addMiddleware ( m , nextAlterAsync ) ; addMiddleware ( m , abortPromise ) ; const valueFromMiddleware = await m . asyncInc ( 1 ) ; expect ( valueFromMiddleware ) . toBe ( 7 ) ; } ) ; test ( "abort() within nested async actions with string" , async ( ) => { const m = TestModel . create ( ) ; try { const valueFromMiddleware = await m . asyncIncFailing ( 1 ) ; } catch ( e ) { expect ( e ) . toBeTruthy ( ) ; } } ) ; test ( "abort() within nested async actions with promise" , async ( ) => { const m = TestModel . create ( ) ; const valueFromMiddleware = await m . asyncIncSuccess ( 1 ) ; expect ( valueFromMiddleware ) . toBe ( 2 ) ; } ) ; test ( "middleware should be invoked on hooks" , ( ) => { error = null ; const m = TestModel . create ( ) ; addMiddleware ( m , noHooksMiddleware ) ; m . inc ( 1 ) ; destroy ( m ) ; expect ( error ) . toBeTruthy ( ) ; } ) ; test ( "middleware should not be invoked on hooks" , ( ) => { error = null ; const m = TestModel . create ( ) ; addMiddleware ( m , noHooksMiddleware , false ) ; m . inc ( 1 ) ; destroy ( m ) ; expect ( error ) . toBeFalsy ( ) ; } ) ; if ( process . env . NODE_ENV === "development" ) { test ( "next()/ abort() omitted within middleware" , ( ) => { const m = TestModel . create ( ) ; addMiddleware ( m , omitNextAbort ) ; let thrownError = null ; try { m . inc ( 1 ) ; } catch ( e ) { thrownError = e ; } expect ( thrownError ) . toBeTruthy ( ) ; } ) ; } if ( process . env . NODE_ENV === "development" ) { test ( "abort() and next() invoked within middleware" , ( ) => { const m = TestModel . create ( ) ; addMiddleware ( m , nextAndAbort ) ; let thrownError = null ; try { m . inc ( 1 ) ; } catch ( e ) { thrownError = e ; } expect ( thrownError ) . toBeTruthy ( ) ; } ) ; }