/ src / ui / installMenu.test.js
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  });