Files
start-sdk/lib/test_runner.js
2023-02-27 10:38:56 -07:00

202 lines
7.1 KiB
JavaScript

const chalk = require("chalk");
const process = require("process");
const { pathToFileURL } = require("url");
const { testDefinitions } = require("@deno/shim-deno/test-internals");
const filePaths = [
"config/index.test.js",
"emver-lite/test.js",
"test.js",
"utils/propertiesMatcher.test.js",
];
async function main() {
const testContext = {
process,
chalk,
};
for (const [i, filePath] of filePaths.entries()) {
if (i > 0) {
console.log("");
}
const scriptPath = "./script/" + filePath;
console.log("Running tests in " + chalk.underline(scriptPath) + "...\n");
process.chdir(__dirname + "/script");
const scriptTestContext = {
origin: pathToFileURL(filePath).toString(),
...testContext,
};
try {
require(scriptPath);
}
catch (err) {
console.error(err);
process.exit(1);
}
await runTestDefinitions(testDefinitions.splice(0, testDefinitions.length), scriptTestContext);
const esmPath = "./esm/" + filePath;
console.log("\nRunning tests in " + chalk.underline(esmPath) + "...\n");
process.chdir(__dirname + "/esm");
const esmTestContext = {
origin: pathToFileURL(filePath).toString(),
...testContext,
};
await import(esmPath);
await runTestDefinitions(testDefinitions.splice(0, testDefinitions.length), esmTestContext);
}
}
async function runTestDefinitions(testDefinitions, options) {
const testFailures = [];
for (const definition of testDefinitions) {
options.process.stdout.write("test " + definition.name + " ...");
if (definition.ignore) {
options.process.stdout.write(` ${options.chalk.gray("ignored")}\n`);
continue;
}
const context = getTestContext(definition, undefined);
let pass = false;
try {
await definition.fn(context);
if (context.hasFailingChild) {
testFailures.push({
name: definition.name,
err: new Error("Had failing test step.")
});
}
else {
pass = true;
}
}
catch (err) {
testFailures.push({
name: definition.name,
err
});
}
const testStepOutput = context.getOutput();
if (testStepOutput.length > 0) {
options.process.stdout.write(testStepOutput);
}
else {
options.process.stdout.write(" ");
}
options.process.stdout.write(getStatusText(pass ? "ok" : "fail"));
options.process.stdout.write("\n");
}
if (testFailures.length > 0) {
options.process.stdout.write("\nFAILURES");
for (const failure of testFailures) {
options.process.stdout.write("\n\n");
options.process.stdout.write(failure.name + "\n");
options.process.stdout.write(indentText((failure.err?.stack ?? failure.err).toString(), 1));
}
options.process.exit(1);
}
function getTestContext(definition, parent) {
return {
name: definition.name,
parent,
origin: options.origin,
/** @type {any} */ err: undefined,
status: "ok",
children: [],
get hasFailingChild() {
return this.children.some((c) => c.status === "fail" || c.status === "pending");
},
getOutput() {
let output = "";
if (this.parent) {
output += "test " + this.name + " ...";
}
if (this.children.length > 0) {
output += "\n" + this.children.map((c) => indentText(c.getOutput(), 1)).join("\n") + "\n";
}
else if (!this.err) {
output += " ";
}
if (this.parent && this.err) {
output += "\n";
}
if (this.err) {
output += indentText((this.err.stack ?? this.err).toString(), 1);
if (this.parent) {
output += "\n";
}
}
if (this.parent) {
output += getStatusText(this.status);
}
return output;
},
async step(nameOrTestDefinition, fn) {
const definition = getDefinition();
const context = getTestContext(definition, this);
context.status = "pending";
this.children.push(context);
if (definition.ignore) {
context.status = "ignored";
return false;
}
try {
await definition.fn(context);
context.status = "ok";
if (context.hasFailingChild) {
context.status = "fail";
return false;
}
return true;
}
catch (err) {
context.status = "fail";
context.err = err;
return false;
}
/** @returns {TestDefinition} */ function getDefinition() {
if (typeof nameOrTestDefinition === "string") {
if (!(fn instanceof Function)) {
throw new TypeError("Expected function for second argument.");
}
return {
name: nameOrTestDefinition,
fn
};
}
else if (typeof nameOrTestDefinition === "object") {
return nameOrTestDefinition;
}
else {
throw new TypeError("Expected a test definition or name and function.");
}
}
}
};
}
function getStatusText(status) {
switch (status) {
case "ok":
return options.chalk.green(status);
case "fail":
case "pending":
return options.chalk.red(status);
case "ignored":
return options.chalk.gray(status);
default:
{
const _assertNever = status;
return status;
}
}
}
function indentText(text, indentLevel) {
if (text === undefined) {
text = "[undefined]";
}
else if (text === null) {
text = "[null]";
}
else {
text = text.toString();
}
return text.split(/\r?\n/).map((line) => " ".repeat(indentLevel) + line).join("\n");
}
}
main();