[Test] Add support for custom boilerplates to create-test.js (#6158)

* Added support for custom boilerplates to test:create script

* Added support for custom boilerplates to create-test.js

* Fixed syntax error

* Update create-test.js

Co-authored-by: Amani H. <109637146+xsn34kzx@users.noreply.github.com>

* Fix pluralization error in `create-test.js`

---------

Co-authored-by: Amani H. <109637146+xsn34kzx@users.noreply.github.com>
This commit is contained in:
Bertie690 2025-07-28 14:51:43 -04:00 committed by GitHub
parent c92b895946
commit 48db9491c6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 63 additions and 44 deletions

View File

@ -17,15 +17,20 @@ const version = "2.0.1";
const __filename = fileURLToPath(import.meta.url); const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename); const __dirname = path.dirname(__filename);
const projectRoot = path.join(__dirname, "..", ".."); const projectRoot = path.join(__dirname, "..", "..");
const boilerplateFilePath = path.join(__dirname, "test-boilerplate.ts");
const choices = [ const choices = /** @type {const} */ (["Move", "Ability", "Item", "Reward", "Mystery Encounter", "Utils", "UI"]);
{ label: "Move", dir: "moves" }, /** @typedef {choices[number]} choiceType */
{ label: "Ability", dir: "abilities" },
{ label: "Item", dir: "items" }, /** @satisfies {{[k in choiceType]: string}} */
{ label: "Mystery Encounter", dir: "mystery-encounter/encounters" }, const choicesToDirs = /** @type {const} */ ({
{ label: "Utils", dir: "utils" }, Move: "moves",
{ label: "UI", dir: "ui" }, Ability: "abilities",
]; Item: "items",
Reward: "rewards",
"Mystery Encounter": "mystery-encounter/encounters",
Utils: "utils",
UI: "ui",
});
//#endregion //#endregion
//#region Functions //#region Functions
@ -41,48 +46,47 @@ function getTestFolderPath(...folders) {
/** /**
* Prompts the user to select a type via list. * Prompts the user to select a type via list.
* @returns {Promise<{selectedOption: {label: string, dir: string}}>} the selected type * @returns {Promise<choiceType>} the selected type
*/ */
async function promptTestType() { async function promptTestType() {
const typeAnswer = await inquirer /** @type {choiceType | "EXIT"} */
const choice = await inquirer
.prompt([ .prompt([
{ {
type: "list", type: "list",
name: "selectedOption", name: "selectedOption",
message: "What type of test would you like to create?", message: "What type of test would you like to create?",
choices: [...choices.map(choice => ({ name: choice.label, value: choice })), { name: "EXIT", value: "N/A" }], choices: [...choices, "EXIT"],
}, },
]) ])
.then(ans => ans.selectedOption); .then(ta => ta.selectedOption);
if (typeAnswer.name === "EXIT") { if (choice === "EXIT") {
console.log("Exiting..."); console.log("Exiting...");
return process.exit(0); return process.exit(0);
} }
if (!choices.some(choice => choice.dir === typeAnswer.dir)) {
console.error(`Please provide a valid type: (${choices.map(choice => choice.label).join(", ")})!`);
return await promptTestType();
}
return typeAnswer; return choice;
} }
/** /**
* Prompts the user to provide a file name. * Prompts the user to provide a file name.
* @param {string} selectedType * @param {choiceType} selectedType The chosen string (used to display console logs)
* @returns {Promise<{userInput: string}>} the selected file name * @returns {Promise<string>} the selected file name
*/ */
async function promptFileName(selectedType) { async function promptFileName(selectedType) {
/** @type {{userInput: string}} */ /** @type {string} */
const fileNameAnswer = await inquirer.prompt([ const fileNameAnswer = await inquirer
{ .prompt([
type: "input", {
name: "userInput", type: "input",
message: `Please provide the name of the ${selectedType}:`, name: "userInput",
}, message: `Please provide the name of the ${selectedType}.`,
]); },
])
.then(fa => fa.userInput);
if (!fileNameAnswer.userInput || fileNameAnswer.userInput.trim().length === 0) { if (fileNameAnswer.trim().length === 0) {
console.error("Please provide a valid file name!"); console.error("Please provide a valid file name!");
return await promptFileName(selectedType); return await promptFileName(selectedType);
} }
@ -90,51 +94,66 @@ async function promptFileName(selectedType) {
return fileNameAnswer; return fileNameAnswer;
} }
/**
* Obtain the path to the boilerplate file based on the current option.
* @param {choiceType} choiceType The choice selected
* @returns {string} The path to the boilerplate file
*/
function getBoilerplatePath(choiceType) {
switch (choiceType) {
// case "Reward":
// return path.join(__dirname, "boilerplates/reward.ts");
default:
return path.join(__dirname, "boilerplates/default.ts");
}
}
/** /**
* Runs the interactive test:create "CLI" * Runs the interactive test:create "CLI"
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
async function runInteractive() { async function runInteractive() {
console.group(chalk.grey(`Create Test - v${version}\n`)); console.group(chalk.grey(`🧪 Create Test - v${version}\n`));
try { try {
const typeAnswer = await promptTestType(); const choice = await promptTestType();
const fileNameAnswer = await promptFileName(typeAnswer.selectedOption.label); const fileNameAnswer = await promptFileName(choice);
const type = typeAnswer.selectedOption;
// Convert fileName from snake_case or camelCase to kebab-case // Convert fileName from snake_case or camelCase to kebab-case
const fileName = fileNameAnswer.userInput const fileName = fileNameAnswer
.replace(/_+/g, "-") // Convert snake_case (underscore) to kebab-case (dashes) .replace(/_+/g, "-") // Convert snake_case (underscore) to kebab-case (dashes)
.replace(/([a-z])([A-Z])/g, "$1-$2") // Convert camelCase to kebab-case .replace(/([a-z])([A-Z])/g, "$1-$2") // Convert camelCase to kebab-case
.replace(/\s+/g, "-") // Replace spaces with dashes .replace(/\s+/g, "-") // Replace spaces with dashes
.toLowerCase(); // Ensure all lowercase .toLowerCase(); // Ensure all lowercase
// Format the description for the test case
// Format the description for the test case in Title Case
const formattedName = fileName.replace(/-/g, " ").replace(/\b\w/g, char => char.toUpperCase()); const formattedName = fileName.replace(/-/g, " ").replace(/\b\w/g, char => char.toUpperCase());
const description = `${choice} - ${formattedName}`;
// Determine the directory based on the type // Determine the directory based on the type
const dir = getTestFolderPath(type.dir); const localDir = choicesToDirs[choice];
const description = `${type.label} - ${formattedName}`; const absoluteDir = getTestFolderPath(localDir);
// Define the content template // Define the content template
const content = fs.readFileSync(boilerplateFilePath, "utf8").replace("{{description}}", description); const content = fs.readFileSync(getBoilerplatePath(choice), "utf8").replace("{{description}}", description);
// Ensure the directory exists // Ensure the directory exists
if (!fs.existsSync(dir)) { if (!fs.existsSync(absoluteDir)) {
fs.mkdirSync(dir, { recursive: true }); fs.mkdirSync(absoluteDir, { recursive: true });
} }
// Create the file with the given name // Create the file with the given name
const filePath = path.join(dir, `${fileName}.test.ts`); const filePath = path.join(absoluteDir, `${fileName}.test.ts`);
if (fs.existsSync(filePath)) { if (fs.existsSync(filePath)) {
console.error(chalk.red.bold(`\n✗ File "${fileName}.test.ts" already exists!\n`)); console.error(chalk.red.bold(`✗ File "${fileName}.test.ts" already exists!\n`));
process.exit(1); process.exit(1);
} }
// Write the template content to the file // Write the template content to the file
fs.writeFileSync(filePath, content, "utf8"); fs.writeFileSync(filePath, content, "utf8");
console.log(chalk.green.bold(`\n✔ File created at: test/${type.dir}/${fileName}.test.ts\n`)); console.log(chalk.green.bold(`✔ File created at: test/${localDir}/${fileName}.test.ts\n`));
console.groupEnd(); console.groupEnd();
} catch (err) { } catch (err) {
console.error(chalk.red("✗ Error: ", err.message)); console.error(chalk.red("✗ Error: ", err.message));