UNPKG

alepha

Version:

Easy-to-use modern TypeScript framework for building many kind of applications.

128 lines (104 loc) 3.65 kB
import { Alepha, AlephaError, z } from "alepha"; import { LogDestinationProvider, MemoryDestinationProvider, } from "alepha/logger"; import { afterEach, beforeEach, describe, expect, test, vi } from "vitest"; import { Asker } from "../index.ts"; describe("Asker", () => { let alepha: Alepha; let asker: Asker; let mockLogger: MemoryDestinationProvider; const mockPromptInterface = (answers: string[]) => { const question = vi.fn(); for (const answer of answers) { question.mockResolvedValueOnce(answer); } const close = vi.fn(); vi.spyOn( asker as unknown as { createPromptInterface: () => any }, "createPromptInterface", ).mockReturnValue({ question, close, }); return { question, close }; }; beforeEach(() => { alepha = Alepha.create({ env: { LOG_LEVEL: "info", }, }) .with({ provide: LogDestinationProvider, use: MemoryDestinationProvider, }) .with(Asker); asker = alepha.inject(Asker); mockLogger = alepha.inject(MemoryDestinationProvider); mockLogger.clear(); }); afterEach(() => { vi.restoreAllMocks(); }); test("returns trimmed string when no schema is provided", async () => { const { question, close } = mockPromptInterface([" hello world "]); const result = await asker.ask("What is your name?"); expect(result).toBe("hello world"); expect(question).toHaveBeenCalledTimes(1); expect(question).toHaveBeenCalledWith("> "); expect(close).toHaveBeenCalledTimes(1); }); test("retries until schema validation passes", async () => { const { question, close } = mockPromptInterface(["abc", "41"]); const result = await asker.ask("Enter a number", { schema: z.number(), }); expect(result).toBe(41); expect(question).toHaveBeenCalledTimes(2); expect(close).toHaveBeenCalledTimes(1); expect(mockLogger.logs.some((log) => log.level === "ERROR")).toBe(true); }); test("uses schema defaults when the user provides an empty answer", async () => { const { question, close } = mockPromptInterface([""]); const result = await asker.ask("What is your favorite color?", { schema: z.text({ default: "blue" }), }); expect(result).toBe("blue"); expect(question).toHaveBeenCalledTimes(1); expect(close).toHaveBeenCalledTimes(1); }); test("retries when custom validation throws an AlephaError", async () => { const { question, close } = mockPromptInterface(["wrong", "right"]); const result = await asker.ask("Provide the secret", { schema: z.text(), validate: (value) => { if (value !== "right") { throw new AlephaError("Invalid secret"); } }, }); expect(result).toBe("right"); expect(question).toHaveBeenCalledTimes(2); expect(close).toHaveBeenCalledTimes(1); const errorLogs = mockLogger.logs.filter((log) => log.level === "ERROR"); expect(errorLogs).toHaveLength(1); expect(errorLogs[0].message).toContain("Invalid secret"); }); test("propagates unexpected errors without logging", async () => { const { question, close } = mockPromptInterface(["value"]); const unexpectedError = new Error("boom"); await expect( asker.ask("Trigger failure", { schema: z.text(), validate: () => { throw unexpectedError; }, }), ).rejects.toBe(unexpectedError); expect(question).toHaveBeenCalledTimes(1); expect(close).toHaveBeenCalledTimes(1); expect(mockLogger.logs.some((log) => log.level === "ERROR")).toBe(false); }); });