|
| 1 | +import { runCommand } from "citty"; |
| 2 | +import { afterEach, beforeEach, expect, it, vi } from "vitest"; |
| 3 | +import { mkdirSync, rmSync } from "node:fs"; |
| 4 | +import { join } from "node:path"; |
| 5 | +import type { CreateSolidV2Args } from "../src/create-solid-v2"; |
| 6 | + |
| 7 | +const { autocomplete, confirm, logInfo, createSolidV2 } = vi.hoisted(() => ({ |
| 8 | + autocomplete: vi.fn(), |
| 9 | + confirm: vi.fn(), |
| 10 | + logInfo: vi.fn(), |
| 11 | + createSolidV2: vi.fn(), |
| 12 | +})); |
| 13 | + |
| 14 | +vi.mock("@clack/prompts", async (importOriginal) => { |
| 15 | + const original = await importOriginal<typeof import("@clack/prompts")>(); |
| 16 | + return { |
| 17 | + ...original, |
| 18 | + autocomplete, |
| 19 | + confirm, |
| 20 | + log: { ...original.log, info: logInfo }, |
| 21 | + }; |
| 22 | +}); |
| 23 | + |
| 24 | +// The scaffold itself (network download + conversion) is covered by template.test.ts; |
| 25 | +// here only the prompt flow and the transpile flag passed down are under test. |
| 26 | +vi.mock("../src/create-solid-v2", async (importOriginal) => ({ |
| 27 | + ...(await importOriginal<typeof import("../src/create-solid-v2")>()), |
| 28 | + createSolidV2, |
| 29 | +})); |
| 30 | + |
| 31 | +import { createSolid } from "../src"; |
| 32 | + |
| 33 | +const destinations: string[] = []; |
| 34 | +const scratch = (name: string) => { |
| 35 | + const destination = join("test", name); |
| 36 | + rmSync(destination, { recursive: true, force: true }); |
| 37 | + destinations.push(destination); |
| 38 | + return destination; |
| 39 | +}; |
| 40 | + |
| 41 | +beforeEach(() => { |
| 42 | + // Unroutable manifest URL: falls back to the baked-in lists, so these tests |
| 43 | + // also assert the baked with-tsrx entry carries the tsOnly flag |
| 44 | + process.env.SOLID_CLI_TEMPLATES_MANIFEST_URL = "http://127.0.0.1:1/templates.json"; |
| 45 | + // The command writes .gitignore into the destination after scaffolding |
| 46 | + createSolidV2.mockImplementation(async ({ destination }: CreateSolidV2Args) => |
| 47 | + mkdirSync(destination, { recursive: true }), |
| 48 | + ); |
| 49 | +}); |
| 50 | + |
| 51 | +afterEach(() => { |
| 52 | + delete process.env.SOLID_CLI_TEMPLATES_MANIFEST_URL; |
| 53 | + vi.clearAllMocks(); |
| 54 | + for (const destination of destinations.splice(0)) rmSync(destination, { recursive: true, force: true }); |
| 55 | +}); |
| 56 | + |
| 57 | +const transpileArg = () => createSolidV2.mock.calls[0][1]; |
| 58 | +const noticeCalls = () => logInfo.mock.calls.filter(([message]) => /TypeScript-only/.test(String(message))); |
| 59 | + |
| 60 | +it("skips the TypeScript prompt when a tsOnly template is passed as an argument", async () => { |
| 61 | + const destination = scratch("tsonly-arg"); |
| 62 | + |
| 63 | + await runCommand(createSolid("test"), { rawArgs: [destination, "--solid", "-t", "with-tsrx"] }); |
| 64 | + |
| 65 | + expect(confirm).not.toHaveBeenCalled(); |
| 66 | + expect(createSolidV2).toHaveBeenCalledOnce(); |
| 67 | + expect(transpileArg()).toBeFalsy(); |
| 68 | +}); |
| 69 | + |
| 70 | +it("prints a notice and scaffolds TypeScript when --js is passed for a tsOnly template", async () => { |
| 71 | + const destination = scratch("tsonly-js-flag"); |
| 72 | + |
| 73 | + await runCommand(createSolid("test"), { rawArgs: [destination, "--solid", "-t", "with-tsrx", "--js"] }); |
| 74 | + |
| 75 | + expect(confirm).not.toHaveBeenCalled(); |
| 76 | + expect(noticeCalls()).toHaveLength(1); |
| 77 | + expect(noticeCalls()[0][0]).toContain("with-tsrx"); |
| 78 | + expect(transpileArg()).toBeFalsy(); |
| 79 | +}); |
| 80 | + |
| 81 | +it("forces TypeScript when a tsOnly template is picked interactively after choosing JavaScript", async () => { |
| 82 | + const destination = scratch("tsonly-interactive"); |
| 83 | + autocomplete.mockResolvedValueOnce("with-tsrx"); |
| 84 | + |
| 85 | + await runCommand(createSolid("test"), { rawArgs: [destination, "--solid", "--js"] }); |
| 86 | + |
| 87 | + expect(noticeCalls()).toHaveLength(1); |
| 88 | + expect(transpileArg()).toBeFalsy(); |
| 89 | +}); |
| 90 | + |
| 91 | +it("leaves non-tsOnly templates unaffected: --js still converts and no notice is printed", async () => { |
| 92 | + const destination = scratch("tsonly-unaffected-js"); |
| 93 | + |
| 94 | + await runCommand(createSolid("test"), { rawArgs: [destination, "--solid", "-t", "bare", "--js"] }); |
| 95 | + |
| 96 | + expect(noticeCalls()).toHaveLength(0); |
| 97 | + expect(transpileArg()).toBe(true); |
| 98 | +}); |
| 99 | + |
| 100 | +it("leaves non-tsOnly templates unaffected: the TypeScript prompt is still offered", async () => { |
| 101 | + const destination = scratch("tsonly-unaffected-prompt"); |
| 102 | + confirm.mockResolvedValueOnce(true); // "Use Typescript?" -> yes |
| 103 | + |
| 104 | + await runCommand(createSolid("test"), { rawArgs: [destination, "--solid", "-t", "bare"] }); |
| 105 | + |
| 106 | + expect(confirm).toHaveBeenCalledWith({ message: "Use Typescript?" }); |
| 107 | + expect(transpileArg()).toBeFalsy(); |
| 108 | +}); |
0 commit comments