1 // Copyright (c) 2023 650 Industries, Inc. All rights reserved. 2 3 // swiftlint:disable closure_body_length 4 5 import ExpoModulesTestCore 6 7 @testable import EXUpdates 8 9 import EXManifests 10 11 class TestStateChangeDelegate: UpdatesStateChangeDelegate { 12 var lastEventType: EXUpdates.UpdatesStateEventType? 13 var lastEventBody: [String: Any?]? sendUpdateStateChangeEventToBridgenull14 func sendUpdateStateChangeEventToBridge(_ eventType: EXUpdates.UpdatesStateEventType, body: [String: Any?]) { 15 lastEventType = eventType 16 lastEventBody = body 17 } 18 } 19 20 class UpdatesStateMachineSpec: ExpoSpec { specnull21 override func spec() { 22 describe("default state") { 23 it("instantiates") { 24 let testStateChangeDelegate = TestStateChangeDelegate() 25 let machine = UpdatesStateMachine(changeEventDelegate: testStateChangeDelegate) 26 expect(machine.state) == .idle 27 } 28 29 it("should handle check and checkCompleteAvailable") { 30 let testStateChangeDelegate = TestStateChangeDelegate() 31 let machine = UpdatesStateMachine(changeEventDelegate: testStateChangeDelegate) 32 33 machine.processEvent(UpdatesStateEventCheck()) 34 expect(machine.state) == .checking 35 expect(testStateChangeDelegate.lastEventType) == .check 36 37 machine.processEvent(UpdatesStateEventCheckCompleteWithUpdate(manifest: [ 38 "updateId": "0000-xxxx" 39 ])) 40 expect(machine.state) == .idle 41 expect(machine.context.isChecking) == false 42 expect(machine.context.checkError).to(beNil()) 43 expect(machine.context.latestManifest?["updateId"] as? String ?? "") == "0000-xxxx" 44 expect(machine.context.isUpdateAvailable) == true 45 expect(machine.context.isUpdatePending) == false 46 expect(testStateChangeDelegate.lastEventType) == .checkCompleteAvailable 47 let values = testStateChangeDelegate.lastEventBody?["context"] as? [String: Any] ?? [:] 48 expect(values["isUpdateAvailable"] as? Bool ?? false) == true 49 } 50 51 it("should handle check and checkCompleteUnavailable") { 52 let testStateChangeDelegate = TestStateChangeDelegate() 53 let machine = UpdatesStateMachine(changeEventDelegate: testStateChangeDelegate) 54 55 machine.processEvent(UpdatesStateEventCheck()) 56 expect(machine.state) == .checking 57 58 machine.processEvent(UpdatesStateEventCheckComplete()) 59 expect(machine.state) == .idle 60 expect(machine.context.isChecking) == false 61 expect(machine.context.checkError).to(beNil()) 62 expect(machine.context.latestManifest).to(beNil()) 63 expect(machine.context.isUpdateAvailable) == false 64 expect(machine.context.isUpdatePending) == false 65 } 66 67 it("should handle download and downloadComplete") { 68 let testStateChangeDelegate = TestStateChangeDelegate() 69 let machine = UpdatesStateMachine(changeEventDelegate: testStateChangeDelegate) 70 71 machine.processEvent(UpdatesStateEventDownload()) 72 expect(machine.state) == .downloading 73 74 machine.processEvent(UpdatesStateEventDownloadCompleteWithUpdate(manifest: [ 75 "updateId": "0000-xxxx" 76 ])) 77 expect(machine.state) == .idle 78 expect(machine.context.isChecking) == false 79 expect(machine.context.downloadError).to(beNil()) 80 expect(machine.context.latestManifest?["updateId"] as? String ?? "") == "0000-xxxx" 81 expect(machine.context.downloadedManifest?["updateId"] as? String ?? "") == "0000-xxxx" 82 expect(machine.context.isUpdateAvailable) == true 83 expect(machine.context.isUpdatePending) == true 84 expect(machine.context.isRollback) == false 85 } 86 87 it("should handle rollback") { 88 let testStateChangeDelegate = TestStateChangeDelegate() 89 let machine = UpdatesStateMachine(changeEventDelegate: testStateChangeDelegate) 90 let commitTime = Date() 91 machine.processEvent(UpdatesStateEventCheck()) 92 expect(machine.state) == .checking 93 94 machine.processEvent(UpdatesStateEventCheckCompleteWithRollback(rollbackCommitTime: commitTime)) 95 expect(machine.state) == .idle 96 expect(machine.context.isChecking) == false 97 expect(machine.context.checkError).to(beNil()) 98 expect(machine.context.latestManifest).to(beNil()) 99 expect(machine.context.isUpdateAvailable) == true 100 expect(machine.context.isUpdatePending) == false 101 expect(machine.context.rollback?.commitTime) == commitTime 102 } 103 104 it("invalid transitions are handled as expected") { 105 let testStateChangeDelegate = TestStateChangeDelegate() 106 let machine = UpdatesStateMachine(changeEventDelegate: testStateChangeDelegate) 107 108 machine.processEvent(UpdatesStateEventCheck()) 109 expect(machine.state) == .checking 110 // Reset the test delegate 111 testStateChangeDelegate.lastEventBody = nil 112 testStateChangeDelegate.lastEventType = nil 113 114 // In .checking state, download events should be ignored, 115 // state should not change, context should not change, 116 // no events should be sent to JS 117 machine.processEvent(UpdatesStateEventDownload()) 118 119 expect(machine.state) == .checking 120 expect(testStateChangeDelegate.lastEventType).to(beNil()) 121 expect(testStateChangeDelegate.lastEventBody).to(beNil()) 122 123 machine.processEvent(UpdatesStateEventDownloadCompleteWithUpdate(manifest: [ 124 "updateId": "0000-xxxx" 125 ])) 126 127 expect(machine.state) == .checking 128 expect(machine.context.downloadedManifest).to(beNil()) 129 130 machine.reset() // go back to .idle 131 132 machine.processEvent(UpdatesStateEventRestart()) 133 expect(machine.state) == .restarting 134 135 // If restarting, all events should be ignored 136 machine.processEvent(UpdatesStateEventCheck()) 137 expect(machine.state) == .restarting 138 139 machine.processEvent(UpdatesStateEventDownload()) 140 expect(machine.state) == .restarting 141 142 machine.processEvent(UpdatesStateEventDownloadComplete()) 143 expect(machine.state) == .restarting 144 } 145 } 146 } 147 } 148 149 // swiftlint:enable closure_body_length 150