a reactive (signals based) hypermedia web framework (wip) stormlightlabs.github.io/volt/
hypermedia frontend signals
at main 6.0 kB view raw
1import { downloadCommand } from "$commands/download.js"; 2import { initCommand } from "$commands/init.js"; 3import * as downloadUtils from "$utils/download.js"; 4import * as filesUtils from "$utils/files.js"; 5import { mkdtemp, rm } from "node:fs/promises"; 6import { tmpdir } from "node:os"; 7import path from "node:path"; 8import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; 9 10// Mock inquirer prompts 11vi.mock("@inquirer/prompts", () => ({ input: vi.fn(), select: vi.fn() })); 12 13describe("CLI commands", () => { 14 let tempDir: string; 15 16 beforeEach(async () => { 17 tempDir = await mkdtemp(path.join(tmpdir(), "voltx-cmd-test-")); 18 vi.spyOn(downloadUtils, "downloadFile").mockResolvedValue(); 19 vi.spyOn(filesUtils, "createFile").mockResolvedValue(); 20 }); 21 22 afterEach(async () => { 23 await rm(tempDir, { recursive: true, force: true }).catch(() => {}); 24 vi.clearAllMocks(); 25 vi.restoreAllMocks(); 26 }); 27 28 describe("downloadCommand", () => { 29 it("should download JS and CSS by default", async () => { 30 await downloadCommand({ output: tempDir }); 31 32 const downloadFileSpy = vi.mocked(downloadUtils.downloadFile); 33 expect(downloadFileSpy).toHaveBeenCalledTimes(2); 34 expect(downloadFileSpy).toHaveBeenCalledWith( 35 "https://cdn.jsdelivr.net/npm/voltx.js@latest/dist/voltx.min.js", 36 expect.stringContaining("voltx.min.js"), 37 ); 38 expect(downloadFileSpy).toHaveBeenCalledWith( 39 "https://cdn.jsdelivr.net/npm/voltx.js@latest/dist/voltx.min.css", 40 expect.stringContaining("voltx.min.css"), 41 ); 42 }); 43 44 it("should download only JS when css is disabled", async () => { 45 await downloadCommand({ output: tempDir, css: false }); 46 47 const downloadFileSpy = vi.mocked(downloadUtils.downloadFile); 48 expect(downloadFileSpy).toHaveBeenCalledTimes(1); 49 expect(downloadFileSpy).toHaveBeenCalledWith( 50 "https://cdn.jsdelivr.net/npm/voltx.js@latest/dist/voltx.min.js", 51 expect.stringContaining("voltx.min.js"), 52 ); 53 }); 54 55 it("should download only CSS when js is disabled", async () => { 56 await downloadCommand({ output: tempDir, js: false }); 57 58 const downloadFileSpy = vi.mocked(downloadUtils.downloadFile); 59 expect(downloadFileSpy).toHaveBeenCalledTimes(1); 60 expect(downloadFileSpy).toHaveBeenCalledWith( 61 "https://cdn.jsdelivr.net/npm/voltx.js@latest/dist/voltx.min.css", 62 expect.stringContaining("voltx.min.css"), 63 ); 64 }); 65 66 it("should download specific version when specified", async () => { 67 await downloadCommand({ output: tempDir, version: "0.5.0" }); 68 69 const downloadFileSpy = vi.mocked(downloadUtils.downloadFile); 70 expect(downloadFileSpy).toHaveBeenCalledWith( 71 "https://cdn.jsdelivr.net/npm/voltx.js@0.5.0/dist/voltx.min.js", 72 expect.stringContaining("voltx.min.js"), 73 ); 74 expect(downloadFileSpy).toHaveBeenCalledWith( 75 "https://cdn.jsdelivr.net/npm/voltx.js@0.5.0/dist/voltx.min.css", 76 expect.stringContaining("voltx.min.css"), 77 ); 78 }); 79 80 it("should handle download errors and exit with code 1", async () => { 81 const downloadFileSpy = vi.spyOn(downloadUtils, "downloadFile").mockRejectedValue(new Error("Network error")); 82 const exitSpy = vi.spyOn(process, "exit").mockImplementation((() => {}) as any); 83 84 await downloadCommand({ output: tempDir }); 85 86 expect(downloadFileSpy).toHaveBeenCalled(); 87 expect(exitSpy).toHaveBeenCalledWith(1); 88 89 exitSpy.mockRestore(); 90 }); 91 }); 92 93 describe("initCommand", () => { 94 it("should check for existing non-empty directory", async () => { 95 const { input, select } = await import("@inquirer/prompts"); 96 vi.mocked(input).mockResolvedValue("test-project"); 97 vi.mocked(select).mockResolvedValue("minimal" as any); 98 99 const isEmptyOrMissingSpy = vi.spyOn(filesUtils, "isEmptyOrMissing").mockResolvedValue(false); 100 const exitSpy = vi.spyOn(process, "exit").mockImplementation((() => {}) as any); 101 102 await initCommand(); 103 104 expect(isEmptyOrMissingSpy).toHaveBeenCalled(); 105 expect(exitSpy).toHaveBeenCalledWith(1); 106 107 exitSpy.mockRestore(); 108 }); 109 110 it("should create minimal template", async () => { 111 const { select } = await import("@inquirer/prompts"); 112 vi.mocked(select).mockResolvedValue("minimal" as any); 113 114 vi.spyOn(filesUtils, "isEmptyOrMissing").mockResolvedValue(true); 115 116 await initCommand("minimal-app"); 117 118 expect(vi.mocked(filesUtils.createFile)).toHaveBeenCalled(); 119 expect(vi.mocked(downloadUtils.downloadFile)).toHaveBeenCalled(); 120 }); 121 122 it("should create styles template without JS", async () => { 123 const { select } = await import("@inquirer/prompts"); 124 vi.mocked(select).mockResolvedValue("styles" as any); 125 126 vi.spyOn(filesUtils, "isEmptyOrMissing").mockResolvedValue(true); 127 128 await initCommand("styles-app"); 129 130 const downloadSpy = vi.mocked(downloadUtils.downloadFile); 131 const calls = downloadSpy.mock.calls; 132 expect(calls.some((call) => call[0].includes("voltx.min.css"))).toBe(true); 133 expect(calls.some((call) => call[0].includes("voltx.min.js"))).toBe(false); 134 }); 135 136 it("should create with-router template", async () => { 137 const { select } = await import("@inquirer/prompts"); 138 vi.mocked(select).mockResolvedValue("with-router" as any); 139 140 vi.spyOn(filesUtils, "isEmptyOrMissing").mockResolvedValue(true); 141 142 await initCommand("router-app"); 143 144 expect(vi.mocked(filesUtils.createFile)).toHaveBeenCalled(); 145 }); 146 147 it("should create with-plugins template", async () => { 148 const { select } = await import("@inquirer/prompts"); 149 vi.mocked(select).mockResolvedValue("with-plugins" as any); 150 151 vi.spyOn(filesUtils, "isEmptyOrMissing").mockResolvedValue(true); 152 153 await initCommand("plugins-app"); 154 155 expect(vi.mocked(filesUtils.createFile)).toHaveBeenCalled(); 156 }); 157 }); 158});