installMenu.test.js
1 import { describe, beforeEach, it, expect, vi } from "vitest"; 2 import { InstallMenu } from "./installMenu.js"; 3 import { mockUiService } from "../__mocks__/service.mocks.js"; 4 import { mockConfigService } from "../__mocks__/service.mocks.js"; 5 import { mockMenuLoop, mockPathSelector } from "../__mocks__/utils.mocks.js"; 6 import { mockInstaller } from "../__mocks__/handler.mocks.js"; 7 8 describe("InstallMenu", () => { 9 const config = { 10 codexRoot: "/codex", 11 }; 12 let installMenu; 13 14 beforeEach(() => { 15 vi.resetAllMocks(); 16 mockConfigService.get.mockReturnValue(config); 17 18 installMenu = new InstallMenu( 19 mockUiService, 20 mockMenuLoop, 21 mockConfigService, 22 mockPathSelector, 23 mockInstaller, 24 ); 25 }); 26 27 describe("constructor", () => { 28 it("initializes the menu loop with the showMenu function", () => { 29 expect(mockMenuLoop.initialize).toHaveBeenCalledWith( 30 installMenu.showMenu, 31 ); 32 }); 33 }); 34 35 describe("show", () => { 36 it("starts the menu loop", async () => { 37 await installMenu.show(); 38 39 expect(mockMenuLoop.showLoop).toHaveBeenCalled(); 40 }); 41 }); 42 43 describe("showMenu", () => { 44 beforeEach(() => { 45 installMenu.showInstallMenu = vi.fn(); 46 installMenu.showUninstallMenu = vi.fn(); 47 }); 48 49 it("shows uninstall menu when codex is installed", async () => { 50 mockInstaller.isCodexInstalled.mockResolvedValue(true); 51 52 await installMenu.showMenu(); 53 54 expect(installMenu.showUninstallMenu).toHaveBeenCalled(); 55 }); 56 57 it("shows install menu when codex is not installed", async () => { 58 mockInstaller.uninstallCodex.mockResolvedValue(false); 59 60 await installMenu.showMenu(); 61 62 expect(installMenu.showInstallMenu).toHaveBeenCalled(); 63 }); 64 }); 65 66 it("displays the install menu", async () => { 67 await installMenu.showInstallMenu(); 68 expect(mockUiService.askMultipleChoice).toHaveBeenCalledWith( 69 "Configure your Codex installation", 70 [ 71 { 72 label: "Install path: " + config.codexRoot, 73 action: installMenu.selectInstallPath, 74 }, 75 { 76 label: "Storage provider module: Disabled (todo)", 77 action: installMenu.storageProviderOption, 78 }, 79 { 80 label: "Install!", 81 action: installMenu.performInstall, 82 }, 83 { 84 label: "Cancel", 85 action: installMenu.doNothing, 86 }, 87 ], 88 ); 89 }); 90 91 it("displays the uninstall menu", async () => { 92 await installMenu.showUninstallMenu(); 93 expect(mockUiService.askMultipleChoice).toHaveBeenCalledWith( 94 "Codex is installed", 95 [ 96 { 97 label: "Uninstall", 98 action: installMenu.showConfirmUninstall, 99 }, 100 { 101 label: "Cancel", 102 action: installMenu.doNothing, 103 }, 104 ], 105 ); 106 }); 107 108 it("confirms uninstall", async () => { 109 await installMenu.showConfirmUninstall(); 110 111 expect(mockUiService.showInfoMessage).toHaveBeenCalledWith( 112 "You are about to:\n" + 113 " - Uninstall the Codex application\n" + 114 " - Delete your Codex ethereum keys\n" + 115 " - Delete the data stored in your Codex node\n" + 116 " - Delete the log files of your Codex node", 117 ); 118 119 expect(mockUiService.askMultipleChoice).toHaveBeenCalledWith( 120 "Are you sure you want to uninstall Codex?", 121 [ 122 { 123 label: "No", 124 action: installMenu.doNothing, 125 }, 126 { 127 label: "Yes", 128 action: installMenu.performUninstall, 129 }, 130 ], 131 ); 132 }); 133 134 it("allows selecting the install path", async () => { 135 const originalPath = config.codexRoot; 136 const newPath = "/new/path"; 137 mockPathSelector.show.mockResolvedValue(newPath); 138 139 await installMenu.selectInstallPath(); 140 141 expect(mockPathSelector.show).toHaveBeenCalledWith(originalPath, false); 142 expect(config.codexRoot).toBe(newPath); 143 expect(mockConfigService.saveConfig).toHaveBeenCalled(); 144 }); 145 146 it("shows storage provider option is unavailable", async () => { 147 const showMock = vi.fn(); 148 installMenu.show = showMock; 149 150 await installMenu.storageProviderOption(); 151 152 expect(mockUiService.showInfoMessage).toHaveBeenCalledWith( 153 "This option is not currently available.", 154 ); 155 }); 156 157 it("calls installed for installation", async () => { 158 await installMenu.performInstall(); 159 160 expect(mockInstaller.installCodex).toHaveBeenCalledWith(installMenu); 161 expect(mockMenuLoop.stopLoop).toHaveBeenCalled(); 162 }); 163 164 it("calls installer for deinstallation", async () => { 165 await installMenu.performUninstall(); 166 167 expect(mockInstaller.uninstallCodex).toHaveBeenCalled(); 168 expect(mockMenuLoop.stopLoop).toHaveBeenCalled(); 169 }); 170 171 it("stops the menu loop when nothing is selected", async () => { 172 await installMenu.doNothing(); 173 174 expect(mockMenuLoop.stopLoop).toHaveBeenCalled(); 175 }); 176 177 describe("process callback handling", () => { 178 const mockSpinner = { 179 isRealSpinner: "no srry", 180 }; 181 182 beforeEach(() => { 183 mockUiService.createAndStartSpinner.mockReturnValue(mockSpinner); 184 }); 185 186 it("creates spinner on installStarts", () => { 187 installMenu.installStarts(); 188 189 expect(installMenu.installSpinner).toBe(mockSpinner); 190 expect(mockUiService.createAndStartSpinner).toHaveBeenCalledWith( 191 "Installing...", 192 ); 193 }); 194 195 it("shows download success message", () => { 196 installMenu.downloadSuccessful(); 197 198 expect(mockUiService.showInfoMessage).toHaveBeenCalledWith( 199 "Download successful...", 200 ); 201 }); 202 203 it("shows install success message", () => { 204 installMenu.installSpinner = mockSpinner; 205 206 installMenu.installSuccessful(); 207 208 expect(mockUiService.showInfoMessage).toHaveBeenCalledWith( 209 "Installation successful!", 210 ); 211 expect(mockUiService.stopSpinnerSuccess).toHaveBeenCalledWith( 212 mockSpinner, 213 ); 214 }); 215 216 it("shows warnings", () => { 217 const message = "warning!"; 218 installMenu.installSpinner = mockSpinner; 219 220 installMenu.warn(message); 221 222 expect(mockUiService.showErrorMessage).toHaveBeenCalledWith(message); 223 expect(mockUiService.stopSpinnerError).toHaveBeenCalledWith(mockSpinner); 224 }); 225 }); 226 });