From 7730176ecc3a3820c3588dd788dd60a74e79c6ac Mon Sep 17 00:00:00 2001 From: thatben Date: Wed, 9 Apr 2025 09:31:22 +0200 Subject: [PATCH] aaa --- src/handlers/installer.js | 2 +- src/handlers/installer.test.js | 70 +++++++++++++++++++++------------- src/main.js | 8 +++- src/services/uiService.js | 15 ++++++++ src/ui/installMenu.js | 49 +++++++++++++++++++++++- src/ui/mainMenu.js | 2 +- src/ui/mainMenu.test.js | 2 +- 7 files changed, 115 insertions(+), 33 deletions(-) diff --git a/src/handlers/installer.js b/src/handlers/installer.js index 9fb80a9..740e8b7 100644 --- a/src/handlers/installer.js +++ b/src/handlers/installer.js @@ -74,7 +74,7 @@ export class Installer { }; installCodexUnix = async (processCallbacks) => { - await this.ensureUnixDependencies(); + if (!await this.ensureUnixDependencies(processCallbacks)) return; await this.shell.run( "curl -# --connect-timeout 10 --max-time 60 -L https://get.codex.storage/install.sh -o install.sh && chmod +x install.sh", ); diff --git a/src/handlers/installer.test.js b/src/handlers/installer.test.js index 4b011d9..72cd207 100644 --- a/src/handlers/installer.test.js +++ b/src/handlers/installer.test.js @@ -239,43 +239,59 @@ describe("Installer", () => { installer.runInstallerLinux = vi.fn(); }); - it("ensures Unix dependencies", async () => { + it("ensures unix dependencies", async () => { await installer.installCodexUnix(processCallbacks); - expect(installer.ensureUnixDependencies).toHaveBeenCalled(); + expect(installer.ensureUnixDependencies).toHaveBeenCalled(processCallbacks); }); - it("runs the curl command to download the installer", async () => { + it("returns early if unix dependencies are not met", async () => { + installer.ensureUnixDependencies.mockResolvedValue(false); + await installer.installCodexUnix(processCallbacks); - expect(mockShellService.run).toHaveBeenCalledWith( - "curl -# --connect-timeout 10 --max-time 60 -L https://get.codex.storage/install.sh -o install.sh && chmod +x install.sh", - ); + + expect(processCallbacks.downloadSuccessful).not.toHaveBeenCalled(); + expect(installer.runInstallerDarwin).not.toHaveBeenCalled(); + expect(installer.runInstallerLinux).not.toHaveBeenCalled(); }); - it("calls downloadSuccessful", async () => { - await installer.installCodexUnix(processCallbacks); - expect(processCallbacks.downloadSuccessful).toHaveBeenCalled(); - }); + describe("when dependencies are met", () => { + beforeEach(() =>{ + installer.ensureUnixDependencies.mockResolvedValue(true); + }) - it("runs installer for darwin ", async () => { - mockOsService.isDarwin.mockReturnValue(true); - await installer.installCodexUnix(processCallbacks); - expect(installer.runInstallerDarwin).toHaveBeenCalled(); - }); + it("runs the curl command to download the installer", async () => { + await installer.installCodexUnix(processCallbacks); + expect(mockShellService.run).toHaveBeenCalledWith( + "curl -# --connect-timeout 10 --max-time 60 -L https://get.codex.storage/install.sh -o install.sh && chmod +x install.sh", + ); + }); - it("runs installer for linux", async () => { - mockOsService.isDarwin.mockReturnValue(false); - await installer.installCodexUnix(processCallbacks); - expect(installer.runInstallerLinux).toHaveBeenCalled(); - }); + it("calls downloadSuccessful", async () => { + await installer.installCodexUnix(processCallbacks); + expect(processCallbacks.downloadSuccessful).toHaveBeenCalled(); + }); - it("saves the codex install path", async () => { - await installer.installCodexUnix(processCallbacks); - expect(installer.saveCodexInstallPath).toHaveBeenCalledWith("codex"); - }); + it("runs installer for darwin ", async () => { + mockOsService.isDarwin.mockReturnValue(true); + await installer.installCodexUnix(processCallbacks); + expect(installer.runInstallerDarwin).toHaveBeenCalled(); + }); - it("deletes the installer script", async () => { - await installer.installCodexUnix(processCallbacks); - expect(mockShellService.run).toHaveBeenCalledWith("rm -f install.sh"); + it("runs installer for linux", async () => { + mockOsService.isDarwin.mockReturnValue(false); + await installer.installCodexUnix(processCallbacks); + expect(installer.runInstallerLinux).toHaveBeenCalled(); + }); + + it("saves the codex install path", async () => { + await installer.installCodexUnix(processCallbacks); + expect(installer.saveCodexInstallPath).toHaveBeenCalledWith("codex"); + }); + + it("deletes the installer script", async () => { + await installer.installCodexUnix(processCallbacks); + expect(mockShellService.run).toHaveBeenCalledWith("rm -f install.sh"); + }); }); }); diff --git a/src/main.js b/src/main.js index 4525c20..c3240ea 100644 --- a/src/main.js +++ b/src/main.js @@ -31,6 +31,9 @@ import { PathSelector } from "./utils/pathSelector.js"; import { NumberSelector } from "./utils/numberSelector.js"; import { MenuLoop } from "./utils/menuLoop.js"; import { DataDirMover } from "./utils/dataDirMover.js"; +import { Installer } from "./handlers/installer.js"; +import { ShellService } from "./services/shellService.js"; +import { OsService } from "./services/osService.js"; async function showNavigationMenu() { console.log("\n"); @@ -100,7 +103,10 @@ export async function main() { const fsService = new FsService(); const pathSelector = new PathSelector(uiService, new MenuLoop(), fsService); const numberSelector = new NumberSelector(uiService); - const installMenu = new InstallMenu(uiService, configService, pathSelector); + const shellService = new ShellService(); + const osService = new OsService(); + const installer = new Installer(configService, shellService, osService, fsService); + const installMenu = new InstallMenu(uiService, configService, pathSelector, installer); const configMenu = new ConfigMenu( uiService, new MenuLoop(), diff --git a/src/services/uiService.js b/src/services/uiService.js index 0ff9233..6c4d2af 100644 --- a/src/services/uiService.js +++ b/src/services/uiService.js @@ -1,6 +1,7 @@ import boxen from "boxen"; import chalk from "chalk"; import inquirer from "inquirer"; +import { createSpinner } from "nanospinner"; import { ASCII_ART } from "../constants/ascii.js"; @@ -87,4 +88,18 @@ export class UiService { ]); return response.valueStr; }; + + createAndStartSpinner = (message) => { + return createSpinner(message).start(); + } + + stopSpinnerSuccess = (spinner) => { + if (spinner == undefined) return; + spinner.stop(); + }; + + stopSpinnerError = (spinner) => { + if (spinner == undefined) return; + spinner.error(); + }; } diff --git a/src/ui/installMenu.js b/src/ui/installMenu.js index 4c4b50d..2008fb8 100644 --- a/src/ui/installMenu.js +++ b/src/ui/installMenu.js @@ -1,12 +1,34 @@ export class InstallMenu { - constructor(uiService, configService, pathSelector) { + constructor(uiService, configService, pathSelector, installer) { this.ui = uiService; this.configService = configService; this.config = configService.get(); this.pathSelector = pathSelector; + this.installer = installer; } show = async () => { + if (await this.installer.isCodexInstalled()) { + await this.showUninstallMenu(); + } else { + await this.showInstallMenu(); + } + } + + showUninstallMenu = async () => { + await this.ui.askMultipleChoice("Codex is installed", [ + { + label: "Uninstall", + action: this.performUninstall, + }, + { + label: "Cancel", + action: this.doNothing, + }, + ]); + } + + showInstallMenu = async () => { await this.ui.askMultipleChoice("Configure your Codex installation", [ { label: "Install path: " + this.config.codexInstallPath, @@ -40,7 +62,30 @@ export class InstallMenu { await this.show(); }; - performInstall = async () => {}; + performInstall = async () => { + await this.installer.installCodex(this); + }; + + performUninstall = async () => {}; doNothing = async () => {}; + + // Progress callbacks from installer module: + installStarts = () => { + this.installSpinner = this.ui.createAndStartSpinner("Installing..."); + }; + + downloadSuccessful = () => { + this.ui.showInfoMessage("Download successful..."); + } + + installSuccessful = () => { + this.ui.showInfoMessage("Installation successful!"); + this.ui.stopSpinnerSuccess(this.installSpinner); + } + + warn = (message) => { + this.ui.showErrorMessage(message); + this.ui.stopSpinnerError(this.installSpinner); + }; } diff --git a/src/ui/mainMenu.js b/src/ui/mainMenu.js index 7c5c928..50c1e11 100644 --- a/src/ui/mainMenu.js +++ b/src/ui/mainMenu.js @@ -19,7 +19,7 @@ export class MainMenu { promptMainMenu = async () => { await this.ui.askMultipleChoice("Select an option", [ { - label: "Install Codex", + label: "Install/uninstall Codex", action: this.installMenu.show, }, { diff --git a/src/ui/mainMenu.test.js b/src/ui/mainMenu.test.js index d5b0a0f..73dfed7 100644 --- a/src/ui/mainMenu.test.js +++ b/src/ui/mainMenu.test.js @@ -47,7 +47,7 @@ describe("mainmenu", () => { expect(mockUiService.askMultipleChoice).toHaveBeenCalledWith( "Select an option", [ - { label: "Install Codex", action: mockInstallMenu.show }, + { label: "Install/uninstall Codex", action: mockInstallMenu.show }, { label: "Configure Codex", action: mockConfigMenu.show }, { label: "Exit", action: mockMenuLoop.stopLoop }, ],