1import { interpret } from 'xstate'; 2 3import { UpdatesStateMachineEventTypes, UpdatesStateMachine } from '../UpdatesStateMachine'; 4import type { UpdatesStateMachineEvent } from '../UpdatesStateMachine'; 5 6describe('Updates state machine tests', () => { 7 it('should transition to the checking state', (done) => { 8 const machine = interpret(UpdatesStateMachine) 9 .onTransition((state) => { 10 if (state.value === 'checking') { 11 expect(state.context.isChecking).toBe(true); 12 done(); 13 } 14 }) 15 .start(); 16 const event: UpdatesStateMachineEvent = { type: UpdatesStateMachineEventTypes.CHECK, body: {} }; 17 machine.send(event); 18 }); 19 20 it('should set updateAvailable when new update found', (done) => { 21 const testUpdateId = '00000-xxxx'; 22 let wasChecking = false; 23 const machine = interpret(UpdatesStateMachine) 24 .onTransition((state) => { 25 if (state.value === 'checking') { 26 wasChecking = true; 27 } else if (state.value === 'idle' && wasChecking) { 28 expect(state.context.isChecking).toBe(false); 29 expect(state.context.checkError).toBeUndefined(); 30 expect(state.context.latestManifest).toEqual({ updateId: testUpdateId }); 31 expect(state.context.isUpdateAvailable).toBe(true); 32 expect(state.context.isUpdatePending).toBe(false); 33 done(); 34 } 35 }) 36 .start(); 37 const checkEvent: UpdatesStateMachineEvent = { 38 type: UpdatesStateMachineEventTypes.CHECK, 39 body: {}, 40 }; 41 const checkCompleteEvent: UpdatesStateMachineEvent = { 42 type: UpdatesStateMachineEventTypes.CHECK_COMPLETE_AVAILABLE, 43 body: { manifest: { updateId: testUpdateId } }, 44 }; 45 machine.send(checkEvent); 46 machine.send(checkCompleteEvent); 47 }); 48 49 it('should transition to downloading state', (done) => { 50 const machine = interpret(UpdatesStateMachine) 51 .onTransition((state) => { 52 if (state.value === 'downloading') { 53 expect(state.context.isDownloading).toBe(true); 54 done(); 55 } 56 }) 57 .start(); 58 const downloadEvent: UpdatesStateMachineEvent = { 59 type: UpdatesStateMachineEventTypes.DOWNLOAD, 60 body: {}, 61 }; 62 machine.send(downloadEvent); 63 }); 64 65 it('should see updatePending when new update downloaded', (done) => { 66 const testUpdateId = '00000-xxxx'; 67 let wasDownloading = false; 68 const machine = interpret(UpdatesStateMachine) 69 .onTransition((state) => { 70 if (state.value === 'downloading') { 71 wasDownloading = true; 72 } else if (state.value === 'idle' && wasDownloading) { 73 expect(state.context.isChecking).toBe(false); 74 expect(state.context.checkError).toBeUndefined(); 75 expect(state.context.latestManifest).toEqual({ updateId: testUpdateId }); 76 expect(state.context.downloadedManifest).toEqual({ updateId: testUpdateId }); 77 expect(state.context.isUpdateAvailable).toBe(true); 78 expect(state.context.isUpdatePending).toBe(true); 79 done(); 80 } 81 }) 82 .start(); 83 const downloadEvent: UpdatesStateMachineEvent = { 84 type: UpdatesStateMachineEventTypes.DOWNLOAD, 85 body: {}, 86 }; 87 const downloadCompleteEvent: UpdatesStateMachineEvent = { 88 type: UpdatesStateMachineEventTypes.DOWNLOAD_COMPLETE, 89 body: { manifest: { updateId: testUpdateId } }, 90 }; 91 machine.send(downloadEvent); 92 machine.send(downloadCompleteEvent); 93 }); 94 95 it('should handle rollback', (done) => { 96 let wasChecking = false; 97 const machine = interpret(UpdatesStateMachine) 98 .onTransition((state) => { 99 if (state.value === 'checking') { 100 wasChecking = true; 101 } else if (state.value === 'idle' && wasChecking) { 102 expect(state.context.isChecking).toBe(false); 103 expect(state.context.checkError).toBeUndefined(); 104 expect(state.context.latestManifest).toBeUndefined(); 105 expect(state.context.isUpdateAvailable).toBe(true); 106 expect(state.context.isRollback).toBe(true); 107 expect(state.context.isUpdatePending).toBe(false); 108 done(); 109 } 110 }) 111 .start(); 112 const checkEvent: UpdatesStateMachineEvent = { 113 type: UpdatesStateMachineEventTypes.CHECK, 114 body: {}, 115 }; 116 const checkCompleteAvailableEvent: UpdatesStateMachineEvent = { 117 type: UpdatesStateMachineEventTypes.CHECK_COMPLETE_AVAILABLE, 118 body: { isRollBackToEmbedded: true }, 119 }; 120 machine.send(checkEvent); 121 machine.send(checkCompleteAvailableEvent); 122 }); 123 124 it('should see updatePending but no update available', (done) => { 125 const testUpdateId = '00000-xxxx'; 126 let wasDownloading = false; 127 let wasChecking = false; 128 const machine = interpret(UpdatesStateMachine) 129 .onTransition((state) => { 130 if (state.value === 'checking') { 131 wasChecking = true; 132 } 133 if (state.value === 'downloading') { 134 wasDownloading = true; 135 } else if (state.value === 'idle' && wasChecking && wasDownloading) { 136 expect(state.context.isChecking).toBe(false); 137 expect(state.context.checkError).toBeUndefined(); 138 expect(state.context.latestManifest).toBeUndefined(); 139 expect(state.context.downloadedManifest).toEqual({ updateId: testUpdateId }); 140 expect(state.context.isUpdateAvailable).toBe(false); 141 expect(state.context.isUpdatePending).toBe(true); 142 done(); 143 } 144 }) 145 .start(); 146 const downloadEvent: UpdatesStateMachineEvent = { 147 type: UpdatesStateMachineEventTypes.DOWNLOAD, 148 body: {}, 149 }; 150 const downloadCompleteEvent: UpdatesStateMachineEvent = { 151 type: UpdatesStateMachineEventTypes.DOWNLOAD_COMPLETE, 152 body: { manifest: { updateId: testUpdateId } }, 153 }; 154 const checkEvent: UpdatesStateMachineEvent = { 155 type: UpdatesStateMachineEventTypes.CHECK, 156 body: {}, 157 }; 158 const checkCompleteEvent: UpdatesStateMachineEvent = { 159 type: UpdatesStateMachineEventTypes.CHECK_COMPLETE_UNAVAILABLE, 160 body: { manifest: { updateId: testUpdateId } }, 161 }; 162 163 machine.send(downloadEvent); 164 machine.send(downloadCompleteEvent); 165 machine.send(checkEvent); 166 machine.send(checkCompleteEvent); 167 }); 168}); 169