/** * This script creates a test boilerplate file in the appropriate * directory based on the type selected. * @example npm run test:create */ import chalk from "chalk"; import inquirer from "inquirer"; import fs from "node:fs"; import path from "node:path"; import { fileURLToPath } from "node:url"; //#region Constants const version = "2.0.1"; // Get the directory name of the current module file const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const projectRoot = path.join(__dirname, "..", ".."); const boilerplateFilePath = path.join(__dirname, "test-boilerplate.ts"); const choices = [ { label: "Move", dir: "moves" }, { label: "Ability", dir: "abilities" }, { label: "Item", dir: "items" }, { label: "Mystery Encounter", dir: "mystery-encounter/encounters" }, { label: "Utils", dir: "utils" }, { label: "UI", dir: "ui" }, ]; //#endregion //#region Functions /** * Get the path to a given folder in the test directory * @param {...string} folders the subfolders to append to the base path * @returns {string} the path to the requested folder */ function getTestFolderPath(...folders) { return path.join(projectRoot, "test", ...folders); } /** * Prompts the user to select a type via list. * @returns {Promise<{selectedOption: {label: string, dir: string}}>} the selected type */ async function promptTestType() { const typeAnswer = await inquirer.prompt([ { type: "list", name: "selectedOption", message: "What type of test would you like to create?", choices: [...choices.map(choice => ({ name: choice.label, value: choice })), "EXIT"], }, ]); if (typeAnswer.selectedOption === "EXIT") { console.log("Exiting..."); return process.exit(); } if (!choices.some(choice => choice.dir === typeAnswer.selectedOption.dir)) { console.error(`Please provide a valid type: (${choices.map(choice => choice.label).join(", ")})!`); return await promptTestType(); } return typeAnswer; } /** * Prompts the user to provide a file name. * @param {string} selectedType * @returns {Promise<{userInput: string}>} the selected file name */ async function promptFileName(selectedType) { /** @type {{userInput: string}} */ const fileNameAnswer = await inquirer.prompt([ { type: "input", name: "userInput", message: `Please provide the name of the ${selectedType}:`, }, ]); if (!fileNameAnswer.userInput || fileNameAnswer.userInput.trim().length === 0) { console.error("Please provide a valid file name!"); return await promptFileName(selectedType); } return fileNameAnswer; } /** * Runs the interactive test:create "CLI" * @returns {Promise} */ async function runInteractive() { console.group(chalk.grey(`Create Test - v${version}\n`)); try { const typeAnswer = await promptTestType(); const fileNameAnswer = await promptFileName(typeAnswer.selectedOption.label); const type = typeAnswer.selectedOption; // Convert fileName from snake_case or camelCase to kebab-case const fileName = fileNameAnswer.userInput .replace(/_+/g, "-") // Convert snake_case (underscore) to kebab-case (dashes) .replace(/([a-z])([A-Z])/g, "$1-$2") // Convert camelCase to kebab-case .replace(/\s+/g, "-") // Replace spaces with dashes .toLowerCase(); // Ensure all lowercase // Format the description for the test case const formattedName = fileName.replace(/-/g, " ").replace(/\b\w/g, char => char.toUpperCase()); // Determine the directory based on the type const dir = getTestFolderPath(type.dir); const description = `${type.label} - ${formattedName}`; // Define the content template const content = fs.readFileSync(boilerplateFilePath, "utf8").replace("{{description}}", description); // Ensure the directory exists if (!fs.existsSync(dir)) { fs.mkdirSync(dir, { recursive: true }); } // Create the file with the given name const filePath = path.join(dir, `${fileName}.test.ts`); if (fs.existsSync(filePath)) { console.error(chalk.red.bold(`\nāœ— File "${fileName}.test.ts" already exists!\n`)); process.exit(1); } // Write the template content to the file fs.writeFileSync(filePath, content, "utf8"); console.log(chalk.green.bold(`\nāœ” File created at: test/${type.dir}/${fileName}.test.ts\n`)); console.groupEnd(); } catch (err) { console.error(chalk.red("āœ— Error: ", err.message)); } } //#endregion //#region Run runInteractive(); //#endregion