mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-12-14 22:05:34 +01:00
[Dev] Break up test:create script; add help message, file name CLI argument support (#6793)
* [Dev] Broke up `test:create` script, added CLI args file name suppoert * Moved `HELP_FLAGS` constant; fixed help msg indentation * ran biome * Fix floting promise err * Added REUSE info * Typo fix * comment out reward boilerplate * Removed redundant comments --------- Co-authored-by: fabske0 <192151969+fabske0@users.noreply.github.com>
This commit is contained in:
parent
4522e9e593
commit
3478e09923
10
biome.jsonc
10
biome.jsonc
@ -251,6 +251,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"overrides": [
|
"overrides": [
|
||||||
|
{
|
||||||
|
"includes": ["**/scripts/**/*.js"],
|
||||||
|
"linter": {
|
||||||
|
"rules": {
|
||||||
|
"nursery": {
|
||||||
|
"noFloatingPromises": "error"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"includes": ["**/test/**/*.test.ts"],
|
"includes": ["**/test/**/*.test.ts"],
|
||||||
"linter": {
|
"linter": {
|
||||||
|
|||||||
80
scripts/create-test/cli.js
Normal file
80
scripts/create-test/cli.js
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024-2025 Pagefault Games
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
import chalk from "chalk";
|
||||||
|
import { cliAliases, validTestTypes } from "./constants.js";
|
||||||
|
import { promptFileName, promptTestType } from "./interactive.js";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @import {testType} from "./constants.js"
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse `process.argv` to retrieve the test type if it exists, otherwise prompting input from the user.
|
||||||
|
* @param {string | undefined} arg - The argument from `process.argv`
|
||||||
|
* @returns {Promise<testType | undefined>}
|
||||||
|
* A Promise that resolves with the type of test to be created, or `undefined` if the user interactively selects "Exit".
|
||||||
|
* Will set `process.exitCode` to a non-zero integer if args are invalid.
|
||||||
|
*/
|
||||||
|
export async function getTestType(arg) {
|
||||||
|
if (arg == null) {
|
||||||
|
return await promptTestType();
|
||||||
|
}
|
||||||
|
|
||||||
|
const testType = getCliTestType(arg);
|
||||||
|
if (testType) {
|
||||||
|
console.log(chalk.blue(`Using ${testType} as test type from CLI...`));
|
||||||
|
return testType;
|
||||||
|
}
|
||||||
|
console.error(
|
||||||
|
chalk.red.bold(
|
||||||
|
`✗ Invalid type of test file specified: ${arg}!\nValid types: ${chalk.blue(validTestTypes.join(", "))}`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
process.exitCode = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a test type from command-line args.
|
||||||
|
* @param {string} arg
|
||||||
|
* @returns {testType | undefined} The resulting test type.
|
||||||
|
* Will return `undefined` if no valid match was found.
|
||||||
|
*/
|
||||||
|
function getCliTestType(arg) {
|
||||||
|
// Check for a direct match, falling back to alias checking if none work
|
||||||
|
const testTypeName = validTestTypes.find(c => c.toLowerCase() === arg.toLowerCase());
|
||||||
|
if (testTypeName) {
|
||||||
|
return testTypeName;
|
||||||
|
}
|
||||||
|
|
||||||
|
const alias = /** @type {(keyof typeof cliAliases)[]} */ (Object.keys(cliAliases)).find(aliasKey =>
|
||||||
|
cliAliases[aliasKey].some(alias => alias.toLowerCase() === arg.toLowerCase()),
|
||||||
|
);
|
||||||
|
return alias;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtain the file name for a given file
|
||||||
|
* @param {testType} testType - The chosen test type
|
||||||
|
* @param {string | undefined} arg - The contents of `process.argv[3]`, if it exists
|
||||||
|
* @returns {Promise<string | undefined>} A promise that resolves with the name of the file to create.
|
||||||
|
*/
|
||||||
|
export async function getFileName(testType, arg) {
|
||||||
|
if (arg == null) {
|
||||||
|
return await promptFileName(testType);
|
||||||
|
}
|
||||||
|
|
||||||
|
const nameTrimmed = arg.trim().replace(".test.ts", "");
|
||||||
|
if (nameTrimmed.length === 0) {
|
||||||
|
console.error(chalk.red.bold("✗ Cannot use an empty string as a file name!"));
|
||||||
|
process.exitCode = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(chalk.blue(`Using ${nameTrimmed} as file name from CLI...`));
|
||||||
|
return nameTrimmed;
|
||||||
|
}
|
||||||
47
scripts/create-test/constants.js
Normal file
47
scripts/create-test/constants.js
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024-2025 Pagefault Games
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Array containing all valid options for the type of test file to create.
|
||||||
|
* @package
|
||||||
|
*/
|
||||||
|
export const validTestTypes = /** @type {const} */ ([
|
||||||
|
"Move",
|
||||||
|
"Ability",
|
||||||
|
"Item",
|
||||||
|
"Reward",
|
||||||
|
"Mystery Encounter",
|
||||||
|
"Utils",
|
||||||
|
"UI",
|
||||||
|
]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {typeof validTestTypes[number]}
|
||||||
|
* testType
|
||||||
|
* Union type representing a single valid choice of test type.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Const object mapping each test type to any additional names they can be used with from CLI.
|
||||||
|
* @satisfies {Partial<Record<testType, readonly string[]>>}
|
||||||
|
*/
|
||||||
|
export const cliAliases = /** @type {const} */ ({
|
||||||
|
"Mystery Encounter": ["ME"],
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Const object matching all test types to the directories in which their tests reside.
|
||||||
|
* @satisfies {Record<testType, string>}
|
||||||
|
*/
|
||||||
|
export const testTypesToDirs = /** @type {const} */ ({
|
||||||
|
Move: "moves",
|
||||||
|
Ability: "abilities",
|
||||||
|
Item: "items",
|
||||||
|
Reward: "rewards",
|
||||||
|
"Mystery Encounter": "mystery-encounter/encounters",
|
||||||
|
Utils: "utils",
|
||||||
|
UI: "ui",
|
||||||
|
});
|
||||||
@ -11,146 +11,25 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import fs from "node:fs";
|
import fs from "node:fs";
|
||||||
import path, { join } from "node:path";
|
import { join } from "node:path";
|
||||||
import chalk from "chalk";
|
import chalk from "chalk";
|
||||||
import inquirer from "inquirer";
|
import { writeFileSafe } from "../helpers/file.js";
|
||||||
|
import { toKebabCase, toTitleCase } from "../helpers/strings.js";
|
||||||
|
import { getFileName, getTestType } from "./cli.js";
|
||||||
|
import { getBoilerplatePath, getTestFileFullPath } from "./dirs.js";
|
||||||
|
import { HELP_FLAGS, showHelpText } from "./help-message.js";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @import {testType} from "./constants.js"
|
||||||
|
*/
|
||||||
|
|
||||||
//#region Constants
|
//#region Constants
|
||||||
|
const version = "2.1.0";
|
||||||
const version = "2.0.2";
|
|
||||||
// Get the directory name of the current module file
|
|
||||||
const __dirname = import.meta.dirname;
|
const __dirname = import.meta.dirname;
|
||||||
const projectRoot = path.join(__dirname, "..", "..");
|
const projectRoot = join(__dirname, "..", "..");
|
||||||
|
|
||||||
const choices = /** @type {const} */ (["Move", "Ability", "Item", "Reward", "Mystery Encounter", "Utils", "UI"]);
|
|
||||||
/** @typedef {typeof choices[number]} choiceType */
|
|
||||||
/**
|
|
||||||
* Object mapping choice types to extra names they can be used with from CLI.
|
|
||||||
* @satisfies {Partial<Record<choiceType, readonly string[]>>}
|
|
||||||
*/
|
|
||||||
const cliAliases = {
|
|
||||||
"Mystery Encounter": ["ME"],
|
|
||||||
};
|
|
||||||
|
|
||||||
/** @satisfies {{[k in choiceType]: string}} */
|
|
||||||
const choicesToDirs = /** @type {const} */ ({
|
|
||||||
Move: "moves",
|
|
||||||
Ability: "abilities",
|
|
||||||
Item: "items",
|
|
||||||
Reward: "rewards",
|
|
||||||
"Mystery Encounter": "mystery-encounter/encounters",
|
|
||||||
Utils: "utils",
|
|
||||||
UI: "ui",
|
|
||||||
});
|
|
||||||
|
|
||||||
//#endregion
|
//#endregion
|
||||||
//#region Functions
|
|
||||||
|
|
||||||
/**
|
//#region Main
|
||||||
* 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<choiceType>} the selected type
|
|
||||||
*/
|
|
||||||
async function promptTestType() {
|
|
||||||
/** @type {choiceType | "EXIT"} */
|
|
||||||
const choice = (
|
|
||||||
await inquirer.prompt([
|
|
||||||
{
|
|
||||||
type: "list",
|
|
||||||
name: "selectedOption",
|
|
||||||
message: "What type of test would you like to create?",
|
|
||||||
choices: [...choices, "EXIT"],
|
|
||||||
},
|
|
||||||
])
|
|
||||||
).selectedOption;
|
|
||||||
|
|
||||||
if (choice === "EXIT") {
|
|
||||||
console.log("Exiting...");
|
|
||||||
return process.exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
return choice;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prompts the user to provide a file name.
|
|
||||||
* @param {choiceType} selectedType The chosen string (used to display console logs)
|
|
||||||
* @returns {Promise<string>} the selected file name
|
|
||||||
*/
|
|
||||||
async function promptFileName(selectedType) {
|
|
||||||
/** @type {string} */
|
|
||||||
const fileNameAnswer = (
|
|
||||||
await inquirer.prompt([
|
|
||||||
{
|
|
||||||
type: "input",
|
|
||||||
name: "userInput",
|
|
||||||
message: `Please provide the name of the ${selectedType}.`,
|
|
||||||
},
|
|
||||||
])
|
|
||||||
).userInput;
|
|
||||||
|
|
||||||
if (fileNameAnswer.trim().length === 0) {
|
|
||||||
console.error("Please provide a valid file name!");
|
|
||||||
return await promptFileName(selectedType);
|
|
||||||
}
|
|
||||||
|
|
||||||
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.boilerplate.ts");
|
|
||||||
default:
|
|
||||||
return path.join(__dirname, "boilerplates/default.boilerplate.ts");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse `process.argv` and get the test type if it exists.
|
|
||||||
* @returns {choiceType | undefined}
|
|
||||||
* The type of choice the CLI args corresponds to, or `undefined` if none were specified.
|
|
||||||
* Will set `process.exitCode` to a non-zero integer if args are invalid.
|
|
||||||
*/
|
|
||||||
function convertArgsToTestType() {
|
|
||||||
// If the first argument is a test name, use that as the test name
|
|
||||||
const args = process.argv.slice(2);
|
|
||||||
if (args[0] == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for a direct match, falling back to alias checking.
|
|
||||||
const choiceName = choices.find(c => c.toLowerCase() === args[0].toLowerCase());
|
|
||||||
if (choiceName) {
|
|
||||||
return choiceName;
|
|
||||||
}
|
|
||||||
|
|
||||||
const alias = /** @type {(keyof cliAliases)[]} */ (Object.keys(cliAliases)).find(k =>
|
|
||||||
cliAliases[k].some(a => a.toLowerCase() === args[0].toLowerCase()),
|
|
||||||
);
|
|
||||||
if (alias) {
|
|
||||||
return alias;
|
|
||||||
}
|
|
||||||
console.error(
|
|
||||||
chalk.red.bold(`✗ Invalid type of test file specified: ${args[0]}!
|
|
||||||
Valid types: ${chalk.blue(choices.join(", "))}`),
|
|
||||||
);
|
|
||||||
process.exitCode = 1;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Run the interactive `test:create` CLI.
|
* Run the interactive `test:create` CLI.
|
||||||
@ -159,65 +38,49 @@ Valid types: ${chalk.blue(choices.join(", "))}`),
|
|||||||
async function runInteractive() {
|
async function runInteractive() {
|
||||||
console.group(chalk.grey(`🧪 Create Test - v${version}\n`));
|
console.group(chalk.grey(`🧪 Create Test - v${version}\n`));
|
||||||
|
|
||||||
const cliTestType = convertArgsToTestType();
|
const args = process.argv.slice(2);
|
||||||
if (process.exitCode) {
|
|
||||||
|
if (HELP_FLAGS.some(h => args.includes(h))) {
|
||||||
|
return showHelpText();
|
||||||
|
}
|
||||||
|
|
||||||
|
const testType = await getTestType(args[0]);
|
||||||
|
if (process.exitCode || !testType) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// TODO: Add a help command
|
|
||||||
|
const fileNameAnswer = await getFileName(testType, args[1]);
|
||||||
|
if (process.exitCode || !fileNameAnswer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let choice;
|
doCreateFile(testType, fileNameAnswer);
|
||||||
if (cliTestType) {
|
|
||||||
console.log(chalk.blue(`Using ${cliTestType} as test type from CLI...`));
|
|
||||||
choice = cliTestType;
|
|
||||||
} else {
|
|
||||||
choice = await promptTestType();
|
|
||||||
}
|
|
||||||
const fileNameAnswer = await promptFileName(choice);
|
|
||||||
|
|
||||||
// Convert fileName from snake_case or camelCase to kebab-case
|
|
||||||
const fileName = fileNameAnswer
|
|
||||||
.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 in Title Case
|
|
||||||
const formattedName = fileName.replace(/-/g, " ").replace(/\b\w/g, char => char.toUpperCase());
|
|
||||||
const description = `${choice} - ${formattedName}`;
|
|
||||||
|
|
||||||
// Determine the directory based on the type
|
|
||||||
const localDir = choicesToDirs[choice];
|
|
||||||
const absoluteDir = getTestFolderPath(localDir);
|
|
||||||
|
|
||||||
// Define the content template
|
|
||||||
const content = fs.readFileSync(getBoilerplatePath(choice), "utf8").replace("{{description}}", description);
|
|
||||||
|
|
||||||
// Ensure the directory exists
|
|
||||||
if (!fs.existsSync(absoluteDir)) {
|
|
||||||
fs.mkdirSync(absoluteDir, { recursive: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the file with the given name
|
|
||||||
const filePath = path.join(absoluteDir, `${fileName}.test.ts`);
|
|
||||||
|
|
||||||
if (fs.existsSync(filePath)) {
|
|
||||||
console.error(chalk.red.bold(`✗ 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(`✔ File created at: ${join("test", localDir, fileName)}.test.ts\n`));
|
|
||||||
console.groupEnd();
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(chalk.red("✗ Error: ", err));
|
console.error(chalk.red("✗ Error: ", err));
|
||||||
}
|
}
|
||||||
|
console.groupEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to create the test file.
|
||||||
|
* @param {testType} testType - The type of test to create
|
||||||
|
* @param {string} fileNameAnswer - The name of the file to create
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
function doCreateFile(testType, fileNameAnswer) {
|
||||||
|
// Convert file name to kebab-case, formatting the description in Title Case
|
||||||
|
const fileName = toKebabCase(fileNameAnswer);
|
||||||
|
const formattedName = toTitleCase(fileNameAnswer);
|
||||||
|
const description = `${testType} - ${formattedName}`;
|
||||||
|
|
||||||
|
const content = fs.readFileSync(getBoilerplatePath(testType), "utf8").replace("{{description}}", description);
|
||||||
|
const filePath = getTestFileFullPath(testType, fileName);
|
||||||
|
writeFileSafe(filePath, content, "utf8");
|
||||||
|
|
||||||
|
console.log(chalk.green.bold(`✔ File created at: ${filePath.replace(`${projectRoot}/`, "")}\n`));
|
||||||
}
|
}
|
||||||
|
|
||||||
//#endregion
|
//#endregion
|
||||||
//#region Run
|
|
||||||
|
|
||||||
runInteractive();
|
await runInteractive();
|
||||||
|
|
||||||
//#endregion
|
|
||||||
|
|||||||
56
scripts/create-test/dirs.js
Normal file
56
scripts/create-test/dirs.js
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024-2025 Pagefault Games
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { join } from "path";
|
||||||
|
import { testTypesToDirs } from "./constants.js";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @import { testType } from "./constants.js";
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Get the directory name of the current module file
|
||||||
|
const __dirname = import.meta.dirname;
|
||||||
|
const projectRoot = join(__dirname, "..", "..");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Const object matching all {@linkcode testType}s to any custom boilerplate files
|
||||||
|
* they may be associated with.
|
||||||
|
* @type {Readonly<Partial<Record<testType, string>>>}
|
||||||
|
*/
|
||||||
|
const customBoilerplates = {
|
||||||
|
// Reward: "boilerplates/reward.boilerplate.ts", // Todo: Boilerplate is added in the modifier rework
|
||||||
|
};
|
||||||
|
|
||||||
|
const DEFAULT_BOILERPLATE_PATH = "boilerplates/default.boilerplate.ts";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the path to the boilerplate file used for the given test type.
|
||||||
|
* @param {testType} testType - The type of test file to create
|
||||||
|
* @returns {string} The path to the boilerplate file.
|
||||||
|
*/
|
||||||
|
export function getBoilerplatePath(testType) {
|
||||||
|
return join(import.meta.dirname, customBoilerplates[testType] ?? DEFAULT_BOILERPLATE_PATH);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 join(projectRoot, "test", ...folders);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to convert the test file name into an absolute path.
|
||||||
|
* @param {testType} testType - The type of test being created (used to look up folder)
|
||||||
|
* @param {string} fileName - The name of the test file (without suffix)
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
export function getTestFileFullPath(testType, fileName) {
|
||||||
|
const absoluteDir = getTestFolderPath(testTypesToDirs[testType]);
|
||||||
|
return join(absoluteDir, `${fileName}.test.ts`);
|
||||||
|
}
|
||||||
32
scripts/create-test/help-message.js
Normal file
32
scripts/create-test/help-message.js
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2025 Pagefault Games
|
||||||
|
* SPDX-FileContributor: Bertie690
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
import chalk from "chalk";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Array containing all valid ways of showing the help message.
|
||||||
|
*/
|
||||||
|
export const HELP_FLAGS = /** @type {const} */ (["-h", "-help", "--help"]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show help/usage text for the `test:create` CLI.
|
||||||
|
* @package
|
||||||
|
*/
|
||||||
|
export function showHelpText() {
|
||||||
|
console.log(`
|
||||||
|
Usage: ${chalk.cyan("pnpm test:create [options] [testType] [fileName]")}
|
||||||
|
If either ${chalk.hex("#7fff00")("testType")} or ${chalk.hex("#7fff00")("fileName")} are omitted,
|
||||||
|
they will be selected interactively.
|
||||||
|
|
||||||
|
${chalk.hex("#8a2be2")("Arguments:")}
|
||||||
|
${chalk.hex("#7fff00")("testType")} The type/category of test file to create.
|
||||||
|
${chalk.hex("#7fff00")("fileName")} The name of the test file to create.
|
||||||
|
|
||||||
|
${chalk.hex("#ffa500")("Options:")}
|
||||||
|
${chalk.blue("-h, -help, --help")} Show this help message.
|
||||||
|
`);
|
||||||
|
}
|
||||||
66
scripts/create-test/interactive.js
Normal file
66
scripts/create-test/interactive.js
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024-2025 Pagefault Games
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
import chalk from "chalk";
|
||||||
|
import inquirer from "inquirer";
|
||||||
|
import { validTestTypes } from "./constants.js";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @import {testType} from "./constants.js"
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Prompt the user to select a test type via list.
|
||||||
|
* @returns {Promise<testType | undefined>} The selected type, or `undefined` if "Exit" was pressed.
|
||||||
|
*/
|
||||||
|
export async function promptTestType() {
|
||||||
|
/** @type {testType | "EXIT"} */
|
||||||
|
const choice = (
|
||||||
|
await inquirer.prompt([
|
||||||
|
{
|
||||||
|
type: "list",
|
||||||
|
name: "selectedOption",
|
||||||
|
message: "What type of test would you like to create?",
|
||||||
|
choices: [...validTestTypes, "EXIT"],
|
||||||
|
},
|
||||||
|
])
|
||||||
|
).selectedOption;
|
||||||
|
|
||||||
|
if (choice === "EXIT") {
|
||||||
|
console.log("Exiting...");
|
||||||
|
process.exitCode = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return choice;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prompt the user to provide a file name.
|
||||||
|
* @param {testType} selectedType - The chosen string (used for logging & validation)
|
||||||
|
* @returns {Promise<string>} The selected file name
|
||||||
|
*/
|
||||||
|
export async function promptFileName(selectedType) {
|
||||||
|
/** @type {string} */
|
||||||
|
const fileNameAnswer = (
|
||||||
|
await inquirer.prompt([
|
||||||
|
{
|
||||||
|
type: "input",
|
||||||
|
name: "userInput",
|
||||||
|
message: `Please provide the name of the ${selectedType}.`,
|
||||||
|
validate: name => {
|
||||||
|
const nameProcessed = name.trim().replace(".test.ts", "");
|
||||||
|
if (nameProcessed.length === 0) {
|
||||||
|
return chalk.red.bold("✗ Cannot use an empty string as a file name!");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
])
|
||||||
|
).userInput;
|
||||||
|
|
||||||
|
// Trim whitespace and any extension suffixes
|
||||||
|
return fileNameAnswer.trim().replace(".test.ts", "");
|
||||||
|
}
|
||||||
36
scripts/helpers/file.js
Normal file
36
scripts/helpers/file.js
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2025 Pagefault Games
|
||||||
|
* SPDX-FileContributor: Bertie690
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
||||||
|
import { dirname } from "node:path";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @import {PathOrFileDescriptor, WriteFileOptions} from "node:fs"
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* "Safely" write to a file, creating any parent directories as required.
|
||||||
|
* @param {PathOrFileDescriptor} file - The filename or file descriptor to open
|
||||||
|
* @param {string | NodeJS.ArrayBufferView<ArrayBufferLike>} content - The content which will be written
|
||||||
|
* @param {WriteFileOptions} [options]
|
||||||
|
* @returns {void}
|
||||||
|
* @remarks
|
||||||
|
* If `file` is a file descriptor, this method will simply return the result of
|
||||||
|
* {@linkcode writeFileSync} verbatim.
|
||||||
|
*/
|
||||||
|
export function writeFileSafe(file, content, options) {
|
||||||
|
if (typeof file === "number") {
|
||||||
|
return writeFileSync(file, content, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
const parentDir = dirname(file.toString("utf-8"));
|
||||||
|
if (!existsSync(parentDir)) {
|
||||||
|
mkdirSync(parentDir, { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
writeFileSync(file, content, options);
|
||||||
|
}
|
||||||
@ -170,4 +170,4 @@ function badArgs() {
|
|||||||
process.exitCode = 1;
|
process.exitCode = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
start();
|
await start();
|
||||||
|
|||||||
@ -297,4 +297,4 @@ async function promptExisting(outFile) {
|
|||||||
).continue;
|
).continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
main();
|
await main();
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user