UNPKG

create-nttb

Version:

An opinionated Next.js, TypeScript, and Tailwind boilerplate using Atomic Design Methodology for presentation components.

209 lines 10.1 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const fs_1 = __importDefault(require("fs")); const path_1 = __importDefault(require("path")); const os_1 = __importDefault(require("os")); const install_1 = require("./install"); jest.mock("child_process", () => ({ execSync: jest.fn(), spawnSync: jest.fn(), })); const child = require("child_process"); beforeAll(() => { jest.spyOn(console, "log").mockImplementation(() => { }); }); afterAll(() => { console.log.mockRestore(); }); function makeTempDir() { return fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), "create-nttb-")); } function readPkg(dir) { return JSON.parse(fs_1.default.readFileSync(path_1.default.join(dir, "package.json"), "utf8")); } describe("create-nttb helpers", () => { let tempDir; let projectDir; const name = "jest-app"; beforeEach(() => { tempDir = makeTempDir(); projectDir = path_1.default.join(tempDir, name); }); afterEach(() => { fs_1.default.rmSync(tempDir, { recursive: true, force: true }); }); describe("validateProjectName", () => { test("passes for valid names", () => { expect(() => (0, install_1.validateProjectName)("my-app")).not.toThrow(); expect(() => (0, install_1.validateProjectName)("myapp")).not.toThrow(); expect(() => (0, install_1.validateProjectName)("my_app")).not.toThrow(); expect(() => (0, install_1.validateProjectName)("App123")).not.toThrow(); }); test("throws for name with spaces", () => { expect(() => (0, install_1.validateProjectName)("my app")).toThrow(/Invalid project name/); }); test("throws for name with special characters", () => { expect(() => (0, install_1.validateProjectName)("my@app")).toThrow(/Invalid project name/); expect(() => (0, install_1.validateProjectName)("../evil")).toThrow(/Invalid project name/); expect(() => (0, install_1.validateProjectName)("./local")).toThrow(/Invalid project name/); }); test("throws for name starting with hyphen", () => { expect(() => (0, install_1.validateProjectName)("-myapp")).toThrow(/Invalid project name/); }); test("throws for name ending with hyphen", () => { expect(() => (0, install_1.validateProjectName)("myapp-")).toThrow(/Invalid project name/); }); test("throws for name exceeding 214 characters", () => { expect(() => (0, install_1.validateProjectName)("a".repeat(215))).toThrow(/214 characters/); }); test("passes for name exactly 214 characters", () => { expect(() => (0, install_1.validateProjectName)("a".repeat(214))).not.toThrow(); }); }); describe("checkNodeVersion", () => { test("throws for low version", () => { const original = process.version; Object.defineProperty(process, "version", { value: "v10.0.0" }); expect(() => (0, install_1.checkNodeVersion)(20)).toThrow(/Node\.js v20\+ required/); Object.defineProperty(process, "version", { value: original }); }); test("passes for high version", () => { expect(() => (0, install_1.checkNodeVersion)(10)).not.toThrow(); }); }); describe("checkNpmVersion", () => { afterEach(() => child.execSync.mockReset()); test("passes for sufficient npm version", () => { child.execSync.mockReturnValue("10.0.0"); expect(() => (0, install_1.checkNpmVersion)(10)).not.toThrow(); }); test("passes for higher npm version", () => { child.execSync.mockReturnValue("11.3.0"); expect(() => (0, install_1.checkNpmVersion)(10)).not.toThrow(); }); test("throws for npm version too low", () => { child.execSync.mockReturnValue("9.8.0"); expect(() => (0, install_1.checkNpmVersion)(10)).toThrow(/npm v10\+ required/); }); test("throws when npm not accessible", () => { child.execSync.mockImplementation(() => { throw new Error("not found"); }); expect(() => (0, install_1.checkNpmVersion)(10)).toThrow(/npm is not installed/); }); }); describe("createProjectDirectory", () => { test("creates directory", () => { expect(fs_1.default.existsSync(projectDir)).toBe(false); (0, install_1.createProjectDirectory)(projectDir); expect(fs_1.default.existsSync(projectDir)).toBe(true); }); test("allows empty dir rerun", () => { (0, install_1.createProjectDirectory)(projectDir); expect(() => (0, install_1.createProjectDirectory)(projectDir)).not.toThrow(); }); test("throws if not empty", () => { (0, install_1.createProjectDirectory)(projectDir); fs_1.default.writeFileSync(path_1.default.join(projectDir, "x"), "x"); expect(() => (0, install_1.createProjectDirectory)(projectDir)).toThrow(/not empty/); }); }); describe("runCommand", () => { afterEach(() => child.spawnSync.mockReset()); test("throws on spawn error", () => { child.spawnSync.mockReturnValue({ error: new Error("bad") }); expect(() => (0, install_1.runCommand)("bad")).toThrow(/bad/); }); test("throws on nonzero exit", () => { child.spawnSync.mockReturnValue({ status: 2 }); expect(() => (0, install_1.runCommand)("fail")).toThrow(/failed/); }); test("passes on success", () => { child.spawnSync.mockReturnValue({ status: 0 }); expect(() => (0, install_1.runCommand)("ok")).not.toThrow(); }); test("passes cwd option through", () => { child.spawnSync.mockReturnValue({ status: 0 }); (0, install_1.runCommand)("ok", [], { cwd: "/tmp" }); expect(child.spawnSync).toHaveBeenCalledWith("ok", [], expect.objectContaining({ cwd: "/tmp" })); }); test("passes timeout option through", () => { child.spawnSync.mockReturnValue({ status: 0 }); (0, install_1.runCommand)("ok", [], { timeout: 60000 }); expect(child.spawnSync).toHaveBeenCalledWith("ok", [], expect.objectContaining({ timeout: 60000 })); }); }); describe("copyTemplate", () => { test("copies files from template to project directory", () => { const srcDir = path_1.default.join(tempDir, "template"); fs_1.default.mkdirSync(srcDir); fs_1.default.writeFileSync(path_1.default.join(srcDir, "package.json"), JSON.stringify({ name: "template" })); fs_1.default.mkdirSync(projectDir); (0, install_1.copyTemplate)(srcDir, projectDir); expect(fs_1.default.existsSync(path_1.default.join(projectDir, "package.json"))).toBe(true); }); test("copies nested directories", () => { const srcDir = path_1.default.join(tempDir, "template"); fs_1.default.mkdirSync(srcDir); fs_1.default.mkdirSync(path_1.default.join(srcDir, "src")); fs_1.default.writeFileSync(path_1.default.join(srcDir, "src", "index.ts"), "export default {};"); fs_1.default.mkdirSync(projectDir); (0, install_1.copyTemplate)(srcDir, projectDir); expect(fs_1.default.existsSync(path_1.default.join(projectDir, "src", "index.ts"))).toBe(true); }); }); describe("updatePackageJson", () => { test("updates name, version, description and keywords", () => { fs_1.default.mkdirSync(projectDir); const pkg = { name: "template", version: "1.0.0", private: true, scripts: { dev: "next dev", build: "next build", start: "next start", format: "prettier --write .", test: "jest", "type-check": "tsc --noEmit", }, dependencies: { next: "16.0.0" }, }; fs_1.default.writeFileSync(path_1.default.join(projectDir, "package.json"), JSON.stringify(pkg)); (0, install_1.updatePackageJson)(projectDir, name); const updated = readPkg(projectDir); expect(updated.name).toBe(name); expect(updated.version).toBe("1.0.0"); expect(updated.description).toBe(`${name} app`); expect(updated.keywords).toHaveLength(7); expect(updated.keywords).toContain(name); expect(updated.createNttbVersion).toBe((0, install_1.getCreateNttbVersion)()); }); test("preserves scripts and dependencies", () => { fs_1.default.mkdirSync(projectDir); const pkg = { name: "template", version: "1.0.0", scripts: { dev: "next dev", build: "next build", start: "next start", format: "prettier --write .", test: "jest", "type-check": "tsc --noEmit", }, dependencies: { next: "16.0.0" }, }; fs_1.default.writeFileSync(path_1.default.join(projectDir, "package.json"), JSON.stringify(pkg)); (0, install_1.updatePackageJson)(projectDir, name); const updated = readPkg(projectDir); expect(updated.scripts).toEqual(pkg.scripts); expect(updated.dependencies).toEqual({ next: "16.0.0" }); expect(updated.createNttbVersion).toBe((0, install_1.getCreateNttbVersion)()); }); }); }); //# sourceMappingURL=install.test.js.map