import { getNextRobotStateAndWarningsSingleCommand , getNextRobotStateAndWarnings , } from '../getNextRobotStateAndWarnings' ; import { curryCommandCreator , reduceCommandCreators , commandCreatorsTimeline , } from '../utils' ; import { DEFAULT_CONFIG } from '../fixtures' ; import type { InvariantContext } from '../types' ; jest . mock ( '../getNextRobotStateAndWarnings' ) ; let invariantContext ; /** This is a minimalistic toy version of step-generation command creators and timeline updaters,
 * to show, at a glance, how all this works together.
 */ interface CountState { count : number ; } interface CountStateAndWarnings { robotState : CountState ; warnings : any [ ] ; } interface CountParams { value : number ; } interface CountCommand { command : 'add' | 'multiply' | 'divide' ; params : { value : number ; } ; } const addCreator = ( params , invariantContext , prevState ) => ( { commands : [ { command : 'add' , params } ] , warnings : [ ] , } ) ; const addCreatorWithWarning = ( params , invariantContext , prevState ) => { const { value } = params ; const result = addCreator ( params , invariantContext , prevState ) ; return { ... result , warnings : [ { type : 'ADD_WARNING' , message : `adding ${ value } with warning example` , } , ] , } ; } ; const multiplyCreator = ( params , invariantContext , prevState ) => ( { commands : [ { command : 'multiply' , params } ] , warnings : [ ] , } ) ; const divideCreator = ( params , invariantContext , prevState ) => { const { value } = params ; if ( value === 0 ) { return { errors : [ { message : 'Cannot divide by zero' , type : 'DIVIDE_BY_ZERO' , } , ] , } ; } return { commands : [ { command : 'divide' , params : { value } } ] , warnings : [ ] , } ; } ; function mockNextRobotStateAndWarningsSingleCommand ( command , invariantContext , prevState ) { const prevCount = prevState . count ; let count = null ; switch ( command . command ) { case 'add' : { count = prevCount + command . params . value ; break ; } case 'multiply' : { count = prevCount * command . params . value ; break ; } case 'divide' : { count = prevCount / command . params . value ; break ; } } if ( count === null ) { throw new Error ( `unknown command: ${ command . command } passed to mockNextRobotStateAndWarning` ) ; } const warnings = [ ] ; return { robotState : { count } , warnings } ; } function mockNextRobotStateAndWarnings ( commands , invariantContext , prevState ) { const nextState = commands . reduce < CountStateAndWarnings > ( ( acc , command ) => mockNextRobotStateAndWarningsSingleCommand ( command , invariantContext , acc . robotState ) , { robotState : prevState , warnings : [ ] } ) ; const warnings = [ ] ; return { robotState : nextState . robotState , warnings } ; } getNextRobotStateAndWarningsSingleCommand . mockImplementation ( mockNextRobotStateAndWarningsSingleCommand ) ; getNextRobotStateAndWarnings . mockImplementation ( mockNextRobotStateAndWarnings ) ; beforeEach ( ( ) => { invariantContext = { labwareEntities : { } , moduleEntities : { } , pipetteEntities : { } , config : DEFAULT_CONFIG , } ; } ) ; describe ( 'reduceCommandCreators' , ( ) => { it ( 'basic command creators' , ( ) => { const initialState = { count : 0 } ; const result = reduceCommandCreators ( [ curryCommandCreator ( addCreator , { value : 1 } ) , curryCommandCreator ( multiplyCreator , { value : 2 } ) , ] , invariantContext , initialState ) ; expect ( result ) . toEqual ( { commands : [ { command : 'add' , params : { value : 1 } } , { command : 'multiply' , params : { value : 2 } } , ] , warnings : [ ] , } ) ; } ) ; it ( 'error in a command short-circuits the command creation pipeline' , ( ) => { const initialState = { count : 5 } ; const result = reduceCommandCreators ( [ curryCommandCreator ( addCreator , { value : 4 } ) , curryCommandCreator ( divideCreator , { value : 0 } ) , curryCommandCreator ( multiplyCreator , { value : 3 } ) , ] , invariantContext , initialState ) ; expect ( result ) . toEqual ( { errors : [ { message : 'Cannot divide by zero' , type : 'DIVIDE_BY_ZERO' , } , ] , } ) ; } ) ; it ( 'warnings accumulate in a flat array across the command chain' , ( ) => { const initialState = { count : 5 } ; const result = reduceCommandCreators ( [ curryCommandCreator ( addCreatorWithWarning , { value : 3 } ) , curryCommandCreator ( multiplyCreator , { value : 2 } ) , curryCommandCreator ( addCreatorWithWarning , { value : 1 } ) , ] , invariantContext , initialState ) ; expect ( result ) . toEqual ( { commands : [ { command : 'add' , params : { value : 3 } } , { command : 'multiply' , params : { value : 2 } } , { command : 'add' , params : { value : 1 } } , ] , warnings : [ { type : 'ADD_WARNING' , message : 'adding 3 with warning example' } , { type : 'ADD_WARNING' , message : 'adding 1 with warning example' } , ] , } ) ; } ) ; } ) ; describe ( 'commandCreatorsTimeline' , ( ) => { it ( 'any errors short-circuit the timeline chain' , ( ) => { const initialState = { count : 5 } ; const result = commandCreatorsTimeline ( [ curryCommandCreator ( addCreatorWithWarning , { value : 4 } ) , curryCommandCreator ( divideCreator , { value : 0 } ) , curryCommandCreator ( multiplyCreator , { value : 3 } ) , ] , invariantContext , initialState ) ; expect ( result ) . toEqual ( { errors : [ { message : 'Cannot divide by zero' , type : 'DIVIDE_BY_ZERO' , } , ] , timeline : [ { robotState : { count : 5 + 4 } , commands : [ { command : 'add' , params : { value : 4 } } ] , warnings : [ { message : 'adding 4 with warning example' , type : 'ADD_WARNING' , } , ] , } , ] , } ) ; } ) ; it ( 'warnings are indexed in an indexed command chain' , ( ) => { const initialState = { count : 5 } ; const result = commandCreatorsTimeline ( [ curryCommandCreator ( addCreatorWithWarning , { value : 3 } ) , curryCommandCreator ( multiplyCreator , { value : 2 } ) , curryCommandCreator ( addCreatorWithWarning , { value : 1 } ) , ] , invariantContext , initialState ) ; expect ( result . timeline ) . toEqual ( [ { robotState : { count : 8 } , commands : [ { command : 'add' , params : { value : 3 } } ] , warnings : [ { message : 'adding 3 with warning example' , type : 'ADD_WARNING' , } , ] , } , { robotState : { count : 16 } , commands : [ { command : 'multiply' , params : { value : 2 } } ] , warnings : [ ] , } , { robotState : { count : 17 } , commands : [ { command : 'add' , params : { value : 1 } } ] , warnings : [ { message : 'adding 1 with warning example' , type : 'ADD_WARNING' , } , ] , } , ] ) ; } ) ; } ) ;