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