Adds spinners and error handling for process control in main menu

This commit is contained in:
Ben 2025-04-16 10:18:38 +02:00
parent c811b67807
commit c2f1f7cc7b
No known key found for this signature in database
GPG Key ID: 0F16E812E736C24B
2 changed files with 132 additions and 4 deletions

View File

@ -60,7 +60,7 @@ export class MainMenu {
},
{
label: "Stop Codex",
action: this.processControl.stopCodexProcess,
action: this.stopCodex,
},
{
label: "Exit",
@ -73,7 +73,7 @@ export class MainMenu {
await this.ui.askMultipleChoice("Codex is installed but not running", [
{
label: "Start Codex",
action: this.processControl.startCodexProcess,
action: this.startCodex,
},
{
label: "Edit Codex config",
@ -89,4 +89,26 @@ export class MainMenu {
},
]);
};
startCodex = async () => {
const spinner = this.ui.createAndStartSpinner("Starting...");
try {
await this.processControl.startCodexProcess();
this.ui.stopSpinnerSuccess(spinner);
} catch (exception) {
this.ui.stopSpinnerError(spinner);
this.ui.showErrorMessage(`Failed to start Codex. "${exception}"`);
}
};
stopCodex = async () => {
const spinner = this.ui.createAndStartSpinner("Stopping...");
try {
await this.processControl.stopCodexProcess();
this.ui.stopSpinnerSuccess(spinner);
} catch (exception) {
this.ui.stopSpinnerError(spinner);
this.ui.showErrorMessage(`Failed to stop Codex. "${exception}"`);
}
};
}

View File

@ -115,7 +115,7 @@ describe("mainmenu", () => {
"Codex is running",
[
{ label: "Open Codex app", action: mockCodexApp.openCodexApp },
{ label: "Stop Codex", action: mockProcessControl.stopCodexProcess },
{ label: "Stop Codex", action: mainmenu.stopCodex },
{ label: "Exit", action: mockMenuLoop.stopLoop },
],
);
@ -131,7 +131,7 @@ describe("mainmenu", () => {
[
{
label: "Start Codex",
action: mockProcessControl.startCodexProcess,
action: mainmenu.startCodex,
},
{ label: "Edit Codex config", action: mockConfigMenu.show },
{ label: "Uninstall Codex", action: mockInstallMenu.show },
@ -140,4 +140,110 @@ describe("mainmenu", () => {
);
});
});
describe("process control", () => {
const mockSpinner = {
isMock: "yes",
};
beforeEach(() => {
mockUiService.createAndStartSpinner.mockReturnValue(mockSpinner);
});
describe("startCodex", () => {
it("starts codex", async () => {
await mainmenu.startCodex();
expect(mockProcessControl.startCodexProcess).toHaveBeenCalled();
});
it("shows error message when process control throws", async () => {
mockProcessControl.startCodexProcess.mockRejectedValueOnce(
new Error("A!"),
);
await mainmenu.startCodex();
expect(mockUiService.showErrorMessage).toHaveBeenCalledWith(
'Failed to start Codex. "Error: A!"',
);
});
it("starts spinner", async () => {
await mainmenu.startCodex();
expect(mockUiService.createAndStartSpinner).toHaveBeenCalledWith(
"Starting...",
);
});
it("stops spinner on success", async () => {
await mainmenu.startCodex();
expect(mockUiService.stopSpinnerSuccess).toHaveBeenCalledWith(
mockSpinner,
);
});
it("stops spinner on failure", async () => {
mockProcessControl.startCodexProcess.mockRejectedValueOnce(
new Error("A!"),
);
await mainmenu.startCodex();
expect(mockUiService.stopSpinnerError).toHaveBeenCalledWith(
mockSpinner,
);
});
});
describe("stopCodex", () => {
it("stops codex", async () => {
await mainmenu.stopCodex();
expect(mockProcessControl.stopCodexProcess).toHaveBeenCalled();
});
it("shows error message when process control throws", async () => {
mockProcessControl.stopCodexProcess.mockRejectedValueOnce(
new Error("A!"),
);
await mainmenu.stopCodex();
expect(mockUiService.showErrorMessage).toHaveBeenCalledWith(
'Failed to stop Codex. "Error: A!"',
);
});
it("starts spinner", async () => {
await mainmenu.stopCodex();
expect(mockUiService.createAndStartSpinner).toHaveBeenCalledWith(
"Stopping...",
);
});
it("stops spinner on success", async () => {
await mainmenu.stopCodex();
expect(mockUiService.stopSpinnerSuccess).toHaveBeenCalledWith(
mockSpinner,
);
});
it("stops spinner on failure", async () => {
mockProcessControl.stopCodexProcess.mockRejectedValueOnce(
new Error("A!"),
);
await mainmenu.stopCodex();
expect(mockUiService.stopSpinnerError).toHaveBeenCalledWith(
mockSpinner,
);
});
});
});
});