#Confusion about how microtasks being queued when we transition from one state into another

1 messages · Page 1 of 1 (latest)

oblique raft
#

I have a machine that does the following:

  1. It submits a form
  2. Then it processes some images
  3. Then it uploads the images
  4. Then it submits the form data with the uploaded image information

Each of these steps are in a separate state, each of them uses an promise actor to perform the necessary operation.

I am trying to cover this process with some tests but I have a little bit of problem when I flush the promises.
I expect the following to happen in this order:

// Submit the form
machine.send({ type: 'submitForm' })

// Check the state
const imageProcessingSnapshot = machine.getSnapshot()
expect(imageProcessingSnapshot.value).toEqual({ processingImages: 'processing' })

// Wait for the images to be processed
await flushPromises()

// Check the state
const postImageProcessingSnapshot = machine.getSnapshot()
expect(postImageProcessingSnapshot.value).toEqual({ uploadingImages: 'loading' })

// Wait for the images to be uploaded
await flushPromises()

// Check the state
const postImageUploadSnapshot = machine.getSnapshot()
expect(postImageUploadSnapshot.value).toEqual({ submittingDraft: 'loading' })

// Wait for the draft to be submitted
await flushPromises()

// Check the state
const finalSnapshot = machine.getSnapshot()
expect(finalSnapshot.value).toBe('readyToClaim')

The following line in my test suit fails:

expect(postImageProcessingSnapshot.value).toEqual({ uploadingImages: 'loading' })

The value itself is readyToClaim.

I am a bit puzzled why all of these states got resolved. These all should be separate promises added to the microtask queue only after the previous action got resolved not rejected. Could anybody explain this behaviour to me? 🤔

Some additional notes: I had to mock some services but the return values stayed in the same shape etc.. (Promise<T>)

#

The picture/snipper shows the structure of my states from a bird's eye view

#

flushPromises is implemented the usual way:

function flushPromises(): Promise<void> {
  return new Promise(resolve => setImmediate(resolve))
}
oblique raft
#

I've used always in the success case to transition to the next state.
I solved my "problem" using after with a very tiny timeout value but it doesn't feel natural.
Is there any other way I could achieve this kind of granular testing or I should do it differently? I am open to any approach/idea 🙂

oblique raft
#

I've ended up doing the following:

// Once we submit the form the machine should go through the following states in this order:
let idx = 0
const expectedStates = [
  { processingImages: 'processing' },
  { uploadingImages: 'loading' },
  { submittingDraft: 'loading' },
  'readyToClaim',
]

// We subscribe to the state changes for testing
const subscription = machine.subscribe(snapshot => {
  const expectedState = expectedStates[idx++]
  expect(snapshot.value).toEqual(expectedState)
})

// Submit the form
machine.send({ type: 'submitForm' })

// End the subscription
subscription.unsubscribe()