@consensys/create-web3-app
Version:
CLI tool for generating Web3 starter projects, streamlining the setup of monorepo structures with a frontend (Next.js or React) and blockchain tooling (HardHat or Foundry). It leverages the commander library for command-line interactions and guides users
583 lines (582 loc) • 34.4 kB
JavaScript
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (g && (g = 0, op[0] && (_ = 0)), _) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
import { describe, it, expect, vi, beforeEach } from "vitest";
import inquirer from "inquirer";
import { promises as fsPromises } from "fs";
import path from "path";
import degit from "degit"; // Import the actual degit
// Import functions to test and mocks
import * as utils from "./index.js"; // Import all exports
import { TEMPLATES, BLOCKCHAIN_TOOLING_CHOICES, PACAKGE_MANAGER_CHOICES, } from "../constants/index.js";
// --- Mocks ---
vi.mock("inquirer");
// Mock fs promises
vi.mock("fs", function (importOriginal) { return __awaiter(void 0, void 0, void 0, function () {
var originalFs;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, importOriginal()];
case 1:
originalFs = _a.sent();
return [2 /*return*/, __assign(__assign({}, originalFs), { promises: {
mkdir: vi.fn(),
writeFile: vi.fn(),
readFile: vi.fn(),
rm: vi.fn(),
} })];
}
});
}); });
// Mock execAsync
vi.mock("./index", function (importOriginal) { return __awaiter(void 0, void 0, void 0, function () {
var originalModule;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, importOriginal()];
case 1:
originalModule = _a.sent();
return [2 /*return*/, __assign(__assign({}, originalModule), { execAsync: vi.fn() })];
}
});
}); });
// --- Refined Degit Mock ---
// 1. Define the structure the mock factory will return
var singleDegitInstance = { clone: vi.fn() };
// 2. Mock the factory to *always* return this single instance
vi.mock("degit", function () { return ({
default: vi.fn().mockImplementation(function () { return singleDegitInstance; })
}); });
// --- Access Mocks via Modules ---
var mockedFsMkdir = vi.mocked(fsPromises.mkdir);
var mockedFsWriteFile = vi.mocked(fsPromises.writeFile);
var mockedFsReadFile = vi.mocked(fsPromises.readFile);
var mockedFsRm = vi.mocked(fsPromises.rm);
var mockedExecAsync = vi.mocked(utils.execAsync);
// 3. Get a reference to the mocked factory
var mockedDegitFactory = vi.mocked(degit);
// 4. Get a direct reference to the clone method on our single instance
var mockedDegitClone = vi.mocked(singleDegitInstance.clone);
// Mock console logging
vi.spyOn(console, "log").mockImplementation(function () { });
vi.spyOn(console, "error").mockImplementation(function () { });
vi.spyOn(console, "warn").mockImplementation(function () { });
describe("create-web3-app Utils", function () {
beforeEach(function () {
// Reset mocks using the direct references
vi.clearAllMocks(); // Clears call history, reset spies
mockedFsMkdir.mockReset();
mockedFsWriteFile.mockReset();
mockedFsReadFile.mockReset();
mockedFsRm.mockReset();
mockedExecAsync.mockReset();
mockedDegitFactory.mockClear(); // Clear calls to the factory itself
mockedDegitClone.mockReset(); // Reset the clone method on the single instance
vi.mocked(inquirer.prompt).mockReset();
});
// --- Test promptForOptions ---
describe("promptForOptions", function () {
it("should return correct options when all prompts are answered", function () { return __awaiter(void 0, void 0, void 0, function () {
var mockArgs, mockTemplate, mockTooling, mockPackageManager, mockAnswers, options;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
mockArgs = "my-test-project";
mockTemplate = TEMPLATES[0];
mockTooling = BLOCKCHAIN_TOOLING_CHOICES[0];
mockPackageManager = PACAKGE_MANAGER_CHOICES[0];
mockAnswers = {
frameworkName: mockTemplate.name,
tooling: mockTooling.name,
packageManager: mockPackageManager.name,
};
// Set mock for this test only
vi.mocked(inquirer.prompt).mockResolvedValue(mockAnswers);
return [4 /*yield*/, utils.promptForOptions(mockArgs)];
case 1:
options = _a.sent();
expect(inquirer.prompt).toHaveBeenCalledTimes(3);
expect(options).toEqual({
projectName: mockArgs,
templateId: mockTemplate.id,
blockchain_tooling: mockTooling.value,
packageManager: mockPackageManager.value,
});
return [2 /*return*/];
}
});
}); });
it("should prompt for projectName if args are empty", function () { return __awaiter(void 0, void 0, void 0, function () {
var mockProjectName, mockTemplate, mockTooling, mockPackageManager, mockAnswers, options;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
mockProjectName = "prompted-project";
mockTemplate = TEMPLATES[1];
mockTooling = BLOCKCHAIN_TOOLING_CHOICES[1];
mockPackageManager = PACAKGE_MANAGER_CHOICES[1];
mockAnswers = {
projectName: mockProjectName,
frameworkName: mockTemplate.name,
tooling: mockTooling.name,
packageManager: mockPackageManager.name,
};
// Chain mocks for this test
vi.mocked(inquirer.prompt)
.mockResolvedValueOnce({ projectName: mockProjectName })
.mockResolvedValueOnce({ frameworkName: mockAnswers.frameworkName })
.mockResolvedValueOnce({ tooling: mockAnswers.tooling })
.mockResolvedValueOnce({ packageManager: mockAnswers.packageManager });
return [4 /*yield*/, utils.promptForOptions("")];
case 1:
options = _a.sent();
expect(inquirer.prompt).toHaveBeenCalledTimes(4);
expect(options).toEqual({
projectName: mockProjectName,
templateId: mockTemplate.id,
blockchain_tooling: mockTooling.value,
packageManager: mockPackageManager.value,
});
return [2 /*return*/];
}
});
}); });
// Updated test for invalid template name
it("should throw error if template selection is invalid", function () { return __awaiter(void 0, void 0, void 0, function () {
var mockArgs, mockAnswers;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
mockArgs = "my-test-project";
mockAnswers = {
frameworkName: "NonExistentTemplate", // Invalid name
tooling: "HardHat",
packageManager: "npm",
};
vi.mocked(inquirer.prompt).mockResolvedValue(mockAnswers);
// Expect the error thrown by promptForFramework
return [4 /*yield*/, expect(utils.promptForOptions(mockArgs)).rejects.toThrow('Internal error: Could not find template data for selected name "NonExistentTemplate"')];
case 1:
// Expect the error thrown by promptForFramework
_a.sent();
return [2 /*return*/];
}
});
}); });
});
// --- Test cloneTemplate ---
describe("cloneTemplate", function () {
var degitTemplate = TEMPLATES.find(function (t) { return t.id === 'next-sdk-quickstart'; });
var gitTemplate = TEMPLATES.find(function (t) { return t.id === 'react-web3-starter'; });
var destinationPath = "/path/to/project";
var projectName = "my-project";
var gitPath = path.join(destinationPath, ".git");
var packageJsonPath = path.join(destinationPath, "package.json");
var options = { projectName: projectName, templateId: degitTemplate.id, blockchain_tooling: 'none', packageManager: 'yarn' };
beforeEach(function () {
// Reset mocks used within cloneTemplate tests
mockedExecAsync.mockResolvedValue({ stdout: "", stderr: "" });
mockedFsReadFile.mockResolvedValue(JSON.stringify({ name: "template-name", version: "1.0.0" }));
mockedFsWriteFile.mockResolvedValue(undefined);
mockedFsRm.mockResolvedValue(undefined);
// Reset the clone mock specifically
mockedDegitClone.mockReset().mockResolvedValue(undefined); // Ensure clone defaults to success
});
// --- Degit Path Tests ---
it("should call degit factory and clone method for DegitTemplate", function () { return __awaiter(void 0, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, utils.cloneTemplate(options, destinationPath)];
case 1:
_a.sent();
// Check the factory call
expect(mockedDegitFactory).toHaveBeenCalledWith(degitTemplate.degitSource, expect.anything());
// Check the clone call on the instance
expect(mockedDegitClone).toHaveBeenCalledWith(destinationPath);
expect(mockedExecAsync).not.toHaveBeenCalled();
expect(mockedFsRm).not.toHaveBeenCalledWith(gitPath, expect.anything());
return [2 /*return*/];
}
});
}); });
it("should read, update, and write package.json for DegitTemplate", function () { return __awaiter(void 0, void 0, void 0, function () {
var expectedPackageJson;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
mockedFsReadFile.mockResolvedValue(JSON.stringify({ name: "old-name", version: "1.0.0" })); // Provide specific content
return [4 /*yield*/, utils.cloneTemplate(options, destinationPath)];
case 1:
_a.sent();
// Verify degit clone was called first
expect(mockedDegitClone).toHaveBeenCalled();
// Verify package.json handling
expect(mockedFsReadFile).toHaveBeenCalledWith(packageJsonPath, "utf-8");
expectedPackageJson = { name: projectName, version: "1.0.0" };
expect(mockedFsWriteFile).toHaveBeenCalledWith(packageJsonPath, JSON.stringify(expectedPackageJson, null, 2), "utf-8");
return [2 /*return*/];
}
});
}); });
it("should handle errors during degit clone", function () { return __awaiter(void 0, void 0, void 0, function () {
var cloneError;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
cloneError = new Error("Degit clone failed");
mockedDegitClone.mockRejectedValueOnce(cloneError); // Make degit fail
return [4 /*yield*/, expect(utils.cloneTemplate(options, destinationPath)).rejects.toThrow(cloneError)];
case 1:
_a.sent();
// Ensure subsequent steps didn't run
expect(mockedFsReadFile).not.toHaveBeenCalled();
expect(mockedFsWriteFile).not.toHaveBeenCalled();
return [2 /*return*/];
}
});
}); });
it("should warn if package.json update fails for DegitTemplate", function () { return __awaiter(void 0, void 0, void 0, function () {
var writeError;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
writeError = new Error("Failed to write file");
mockedFsWriteFile.mockRejectedValueOnce(writeError); // Fail writing package.json
return [4 /*yield*/, utils.cloneTemplate(options, destinationPath)];
case 1:
_a.sent();
expect(mockedDegitClone).toHaveBeenCalled(); // Ensure clone happened
expect(mockedFsReadFile).toHaveBeenCalled(); // Read should have happened
expect(console.warn).toHaveBeenCalledWith(expect.stringContaining("Warning: Could not update package.json name"), expect.stringContaining(writeError.message));
return [2 /*return*/];
}
});
}); });
// --- Git Clone Path Tests ---
it("should call git clone with correct arguments for GitTemplate", function () { return __awaiter(void 0, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, utils.cloneTemplate(options, destinationPath)];
case 1:
_a.sent();
expect(mockedExecAsync).toHaveBeenCalledWith("git clone ".concat(gitTemplate.repo_url, " ").concat(destinationPath));
expect(mockedDegitFactory).not.toHaveBeenCalled(); // Ensure degit factory wasn't called
expect(mockedDegitClone).not.toHaveBeenCalled(); // Ensure degit clone wasn't called
return [2 /*return*/];
}
});
}); });
it("should remove the .git directory after cloning for GitTemplate", function () { return __awaiter(void 0, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, utils.cloneTemplate(options, destinationPath)];
case 1:
_a.sent();
expect(mockedExecAsync).toHaveBeenCalled(); // Ensure clone happened
expect(mockedFsRm).toHaveBeenCalledWith(gitPath, {
recursive: true,
force: true,
});
return [2 /*return*/];
}
});
}); });
it("should read, update, and write package.json for GitTemplate", function () { return __awaiter(void 0, void 0, void 0, function () {
var expectedPackageJson;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
mockedFsReadFile.mockResolvedValue(JSON.stringify({ name: "old-name", version: "1.0.0" }));
return [4 /*yield*/, utils.cloneTemplate(options, destinationPath)];
case 1:
_a.sent();
// Verify clone happened first
expect(mockedExecAsync).toHaveBeenCalled();
expect(mockedFsRm).toHaveBeenCalled();
// Verify package.json handling
expect(mockedFsReadFile).toHaveBeenCalledWith(packageJsonPath, "utf-8");
expectedPackageJson = { name: projectName, version: "1.0.0" };
expect(mockedFsWriteFile).toHaveBeenCalledWith(packageJsonPath, JSON.stringify(expectedPackageJson, null, 2), "utf-8");
return [2 /*return*/];
}
});
}); });
it("should handle errors during git clone for GitTemplate", function () { return __awaiter(void 0, void 0, void 0, function () {
var cloneError;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
cloneError = new Error("Git clone failed");
mockedExecAsync.mockRejectedValueOnce(cloneError); // Make git clone fail
return [4 /*yield*/, expect(utils.cloneTemplate(options, destinationPath)).rejects.toThrow(cloneError)];
case 1:
_a.sent();
// Ensure subsequent steps didn't run
expect(mockedFsRm).not.toHaveBeenCalled();
expect(mockedFsReadFile).not.toHaveBeenCalled();
return [2 /*return*/];
}
});
}); });
// --- Common Tests ---
it("should throw error if templateId is not found", function () { return __awaiter(void 0, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, expect(utils.cloneTemplate(options, destinationPath)).rejects.toThrow('Template with id "invalid-id" not found.')];
case 1:
_a.sent();
expect(mockedExecAsync).not.toHaveBeenCalled();
expect(mockedDegitClone).not.toHaveBeenCalled(); // Check clone mock here
return [2 /*return*/];
}
});
}); });
});
// --- Test initializeMonorepo ---
describe("initializeMonorepo", function () {
var projectName = "my-monorepo";
var packagesPath = path.join(projectName, "packages");
var blockchainPath = path.join(projectName, "packages", "blockchain");
var sitePath = path.join(projectName, "packages", "site");
var gitignorePath = path.join(projectName, ".gitignore");
var rootPackageJsonPath = path.join(projectName, "package.json");
var pnpmWorkspacePath = path.join(projectName, "pnpm-workspace.yaml");
it("should create base directories", function () { return __awaiter(void 0, void 0, void 0, function () {
var options;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
options = { projectName: projectName, templateId: "t", blockchain_tooling: 'none', packageManager: 'npm' };
return [4 /*yield*/, utils.initializeMonorepo(options)];
case 1:
_a.sent();
// Check that mkdir was called for all necessary paths
expect(mockedFsMkdir).toHaveBeenCalledWith(packagesPath, { recursive: true });
expect(mockedFsMkdir).toHaveBeenCalledWith(blockchainPath, { recursive: true });
expect(mockedFsMkdir).toHaveBeenCalledWith(sitePath, { recursive: true });
return [2 /*return*/];
}
});
}); });
it("should create .gitignore", function () { return __awaiter(void 0, void 0, void 0, function () {
var options;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
options = { projectName: projectName, templateId: "t", blockchain_tooling: 'none', packageManager: 'npm' };
return [4 /*yield*/, utils.initializeMonorepo(options)];
case 1:
_a.sent();
// Check that writeFile was called for .gitignore
expect(mockedFsWriteFile).toHaveBeenCalledWith(gitignorePath, expect.stringContaining("node_modules"));
return [2 /*return*/];
}
});
}); });
it("should create root package.json", function () { return __awaiter(void 0, void 0, void 0, function () {
var options, expectedPackageJson;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
options = { projectName: projectName, templateId: "t", blockchain_tooling: 'none', packageManager: 'yarn' };
return [4 /*yield*/, utils.initializeMonorepo(options)];
case 1:
_a.sent();
expectedPackageJson = {
name: projectName,
private: true,
workspaces: ["packages/*"],
scripts: {},
};
// Check that writeFile was called for package.json
expect(mockedFsWriteFile).toHaveBeenCalledWith(rootPackageJsonPath, JSON.stringify(expectedPackageJson, null, 2));
return [2 /*return*/];
}
});
}); });
it("should create pnpm-workspace.yaml for pnpm", function () { return __awaiter(void 0, void 0, void 0, function () {
var options;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
options = { projectName: projectName, templateId: "t", blockchain_tooling: 'none', packageManager: 'pnpm' };
return [4 /*yield*/, utils.initializeMonorepo(options)];
case 1:
_a.sent();
// Check that writeFile was called for pnpm-workspace.yaml
expect(mockedFsWriteFile).toHaveBeenCalledWith(pnpmWorkspacePath, expect.stringContaining("packages:"));
return [2 /*return*/];
}
});
}); });
// This test was passing, should still pass
it("should not create pnpm-workspace.yaml for npm/yarn", function () { return __awaiter(void 0, void 0, void 0, function () {
var npmOptions, yarnOptions;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
npmOptions = { projectName: projectName, templateId: "t", blockchain_tooling: 'none', packageManager: 'npm' };
return [4 /*yield*/, utils.initializeMonorepo(npmOptions)];
case 1:
_a.sent();
expect(mockedFsWriteFile).not.toHaveBeenCalledWith(pnpmWorkspacePath, expect.anything());
vi.clearAllMocks(); // Reset mocks for the next part of the test
mockedFsWriteFile.mockReset(); // Specifically reset writeFile
yarnOptions = { projectName: projectName, templateId: "t", blockchain_tooling: 'none', packageManager: 'yarn' };
return [4 /*yield*/, utils.initializeMonorepo(yarnOptions)];
case 2:
_a.sent();
expect(mockedFsWriteFile).not.toHaveBeenCalledWith(pnpmWorkspacePath, expect.anything());
return [2 /*return*/];
}
});
}); });
});
// --- Test createProject (integration-like) ---
describe("createProject", function () {
var projectName = "final-project";
var templateId = TEMPLATES[0].id; // Use the degit template for these tests
var installCommand = "yarn install";
var mockOptions = { projectName: projectName, templateId: templateId, blockchain_tooling: 'none', packageManager: 'yarn' };
var mockHardhatOptions = { projectName: projectName, templateId: templateId, blockchain_tooling: 'hardhat', packageManager: 'yarn' };
var mockFoundryOptions = { projectName: projectName, templateId: templateId, blockchain_tooling: 'foundry', packageManager: 'pnpm' };
// Mock promptForOptions directly for these integration tests
var promptOptionsMock = vi.spyOn(utils, 'promptForOptions');
// Keep stubs for lower-level functions if needed for fine-grained checks,
// but the main goal here is to test the createProject logic flow.
var initializeMonorepoMock = vi.spyOn(utils, 'initializeMonorepo').mockResolvedValue(undefined);
var cloneTemplateMock = vi.spyOn(utils, 'cloneTemplate').mockResolvedValue(undefined);
beforeEach(function () {
// Reset spies and mocks specific to this suite
promptOptionsMock.mockClear();
initializeMonorepoMock.mockClear();
cloneTemplateMock.mockClear();
mockedExecAsync.mockReset(); // Ensure execAsync is clean for the install command check
mockedExecAsync.mockResolvedValue({ stdout: "", stderr: "" }); // Default success for install command
});
it("should call cloneTemplate directly for 'none' tooling", function () { return __awaiter(void 0, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
promptOptionsMock.mockResolvedValue(mockOptions); // Control options returned
return [4 /*yield*/, utils.createProject(projectName)];
case 1:
_a.sent();
expect(promptOptionsMock).toHaveBeenCalled(); // Verify options were prompted/retrieved
expect(initializeMonorepoMock).not.toHaveBeenCalled(); // Should not init monorepo
expect(cloneTemplateMock).toHaveBeenCalledWith(templateId, projectName, projectName); // Called directly
expect(mockedExecAsync).toHaveBeenCalledWith("cd ".concat(projectName, " && ").concat(installCommand)); // Install called
expect(console.log).toHaveBeenCalledWith(expect.stringContaining("Success! Created"));
expect(console.log).toHaveBeenCalledWith(expect.stringContaining("yarn run dev")); // Standalone guidance
expect(console.log).not.toHaveBeenCalledWith(expect.stringContaining("blockchain")); // No monorepo guidance
return [2 /*return*/];
}
});
}); });
it("should call initializeMonorepo and cloneTemplate (via createHardhatProject) for 'hardhat' tooling", function () { return __awaiter(void 0, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
promptOptionsMock.mockResolvedValue(mockHardhatOptions);
return [4 /*yield*/, utils.createProject(projectName)];
case 1:
_a.sent();
expect(promptOptionsMock).toHaveBeenCalled();
expect(initializeMonorepoMock).toHaveBeenCalledWith(mockHardhatOptions);
// Hardhat template clone uses execAsync, frontend uses cloneTemplate
expect(mockedExecAsync).toHaveBeenCalledWith(expect.stringContaining('git clone https://github.com/Consensys/hardhat-template.git'));
expect(cloneTemplateMock).toHaveBeenCalledWith(templateId, path.join(projectName, "packages", "site"), projectName);
expect(mockedExecAsync).toHaveBeenCalledWith("cd ".concat(projectName, " && ").concat(installCommand)); // Install command
expect(console.log).toHaveBeenCalledWith(expect.stringContaining("Success! Created"));
expect(console.log).toHaveBeenCalledWith(expect.stringContaining("yarn run compile")); // Monorepo guidance
return [2 /*return*/];
}
});
}); });
it("should call initializeMonorepo and cloneTemplate (via createFoundryProject) for 'foundry' tooling", function () { return __awaiter(void 0, void 0, void 0, function () {
var pnpmInstallCommand;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
pnpmInstallCommand = "pnpm install";
promptOptionsMock.mockResolvedValue(mockFoundryOptions);
return [4 /*yield*/, utils.createProject(projectName)];
case 1:
_a.sent();
expect(promptOptionsMock).toHaveBeenCalled();
expect(initializeMonorepoMock).toHaveBeenCalledWith(mockFoundryOptions);
// Foundry init uses execAsync, frontend uses cloneTemplate
expect(mockedExecAsync).toHaveBeenCalledWith(expect.stringContaining('forge init . --no-commit'));
expect(cloneTemplateMock).toHaveBeenCalledWith(templateId, path.join(projectName, "packages", "site"), projectName);
expect(mockedExecAsync).toHaveBeenCalledWith("cd ".concat(projectName, " && ").concat(pnpmInstallCommand)); // Install command
expect(console.log).toHaveBeenCalledWith(expect.stringContaining("Success! Created"));
expect(console.log).toHaveBeenCalledWith(expect.stringContaining("pnpm run compile")); // Monorepo guidance
return [2 /*return*/];
}
});
}); });
it("should handle errors during creation and not install", function () { return __awaiter(void 0, void 0, void 0, function () {
var creationError;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
creationError = new Error("Setup failed");
promptOptionsMock.mockResolvedValue(mockOptions);
// Make cloneTemplate fail
cloneTemplateMock.mockRejectedValueOnce(creationError);
return [4 /*yield*/, utils.createProject(projectName)];
case 1:
_a.sent();
expect(console.error).toHaveBeenCalledWith(expect.stringContaining("An error occurred during project creation:"), creationError);
// Ensure install command is NOT run if setup fails
expect(mockedExecAsync).not.toHaveBeenCalledWith(expect.stringContaining("install"));
return [2 /*return*/];
}
});
}); });
});
});