coze
Version:
Coze - A cryptographic JSON messaging specification designed for human readability.
299 lines (271 loc) • 9.87 kB
JavaScript
;
export {
Run,
}
import * as Unit from '../test_unit.js';
// Test will call .trim() on string's golden and returned values.
/**
* -----------------------------------------------------------------------------
*
* Test defines a test to be run.
*
* - name: Name of the test to display to the user.
* - func: Test function to execute.
* - golden: Correct return value for the test.
*
* @typedef {object} Test
* @property {string} name
* @property {Function} func
* @property {string} golden
*
* -----------------------------------------------------------------------------
*
* Tests defines an array of `Test` objects to be run.
* @typedef {Array<Test>} Tests
*
* -----------------------------------------------------------------------------
*
* TestsToRun defines the tests that will be ran on the Browser Test JS page.
* @typedef {Tests} TestsToRun
*
* -----------------------------------------------------------------------------
*
* Stylesheet is the object for displaying CSS/stylesheets on the test page.
*
* - href: The link to the stylesheet. Required.
* - rel: The relationship between the linked source and current
* document. Defaults to "stylesheet"
* - integrity: The digest/checksum for the given source/stylesheet.
* - crossorigin: Sets the crossorigin for requests to the resource.
*
* @typedef {object} Stylesheet
* @property {string} href
* @property {string} [rel]
* @property {string} [integrity]
* @property {string} [crossorigin]
*
* -----------------------------------------------------------------------------
*
* TestGUIOptions defines options for the Browser Test JS Testing page. All
* options are optional, and will use the default if not proivded.
*
* Currently supported options:
*
* For 'header', 'footer', and any other Custom HTML elements:
* These options must be written as HTML, but encapsulated in a string. If set,
* the string will be used as the inner HTML and appended to the custom portion
* of the page.
*
* 'header' and 'stylesheet' are mutually exclusive, with 'header' taking
* precedence. If they are both set, `header` will be used. If just wanting
* to use a custom stylesheet, `stylesheet` must be set, and `header` must not.
* If wanting a custom header and stylesheet, the custom stylesheet must go in
* the `header`, and header must be set.
*
* - header: Custom header.
* - stylesheet: Custom stylesheet.
* - footer: Custom footer.
* - main_image: Custom main image.
* - html_test_area: Custom HTML for testing and page customization.
*
* @typedef {object} TestGUIOptions
* @property {Stylesheet} [header]
* @property {Stylesheet} [stylesheet]
* @property {String=Element} [footer]
* @property {String=Image} [main_image]
* @property {Stylesheet} [html_test_area]
*
* -----------------------------------------------------------------------------
*
* TestBrowserJS defines an object for projects/packages to interact with the
* browsertestjs library.
*
* When Browser Test JS page is loaded, it will attempt to retrieve the
* TestBrowserJS object from the `unit_test.js` file. It will use this object to
* 1.) Run the tests 2.) Make modifications to the GUI.
*
* - TestsToRun: Holds the tests to be ran for the given application/package.
* - TestOptions: Holds the options to be used for the GUI.
*
* @typedef {object} TestBrowserJS
* @property {TestsToRun} TestsToRun
* @property {TestGUIOptions} TestGUIOptions
*
* -----------------------------------------------------------------------------
*/
let totalTestsToRun = 0;
let totalTestsRan = 0;
let testPastCount = 0;
let testFailCount = 0;
// Template for displaying test results in the GUI. Must be cloned.
const jsResultTemplate = document.getElementById('js_test_results');
/**
* Calls tests to be ran, after the DOM has been loaded.
* See README for structuring your directory and `test_unit.js` file.
**/
document.addEventListener('DOMContentLoaded', () => {
// If imported, don't run onload as determined by existence of #NoModuleFound.
let noMod = document.getElementById('NoModuleFound');
if (noMod == null) {
return;
}
document.getElementById('NoModuleFound').hidden = true;
document.getElementById('testsResultsMeta').hidden = false;
let tbjs = Unit.TestBrowserJS;
if (tbjs === null || tbjs == undefined || typeof tbjs !== "object" || Object.keys(tbjs).length <= 0) {
console.error("TestBrowserJS: The TestBrowserJS object is not properly defined, please see README.", tbjs);
} else {
if (tbjs.TestGUIOptions === null || tbjs.TestGUIOptions === undefined || typeof tbjs.TestGUIOptions !== "object") {
tbjs.TestGUIOptions = {};
}
setGUI(tbjs);
if (Array.isArray(tbjs.TestsToRun) && tbjs.TestsToRun.length > 0) {
Run(tbjs.TestsToRun);
return;
}
}
document.getElementById('testsResultsMeta').innerHTML = "";
document.getElementById('noTestsToRun').hidden = false;
});
/**
* Runs all of the tests in the 'TestsToRun' array.
* @param {Tests} tests
* @returns {void}
*/
async function Run(tests) {
totalTestsToRun = Object.entries(tests).length;
let values = Object.values(tests);
for (let i = 0; i < totalTestsToRun; i++) {
var test = {};
test.name = values[i].name;
test.golden = values[i].golden;
try {
test.result = await values[i].func();
} catch (err) {
console.error(err);
test.result = err;
}
appendResult(test);
}
stats();
};
/**
* stats displays statistics about the tests that are being run, out to the
* screen. It will show the tests that ran, and which passed and failed.
*
* @returns {void} Displays the stats on the page.
*/
async function stats() {
document.getElementById("totalTestsToRun").innerText = totalTestsToRun;
document.getElementById("totalTestsRan").innerText = totalTestsRan;
document.getElementById("testPastCount").innerText = testPastCount;
document.getElementById("testFailCount").innerText = testFailCount;
document.getElementById("testsRunning").hidden = true;
if (testFailCount == 0) {
document.getElementById("testsPassed").hidden = false;
} else {
document.getElementById("testsFailed").hidden = false;
}
};
/**
* appendResults appends the results to the div on the page.
*
* @param {object} obj The object that holds the name of the test,
* function, expected result, and actual results.
* @returns {void}
*/
function appendResult(obj) {
let clone = jsResultTemplate.content.cloneNode(true);
let test = "" + obj.name + "\: ";
if (typeof obj.golden == "string") {
// Trim outer strings making tests easier to write.
var failed = (obj.result.trim() != obj.golden.trim())
} else {
failed = obj.result != obj.golden
}
if (failed) {
console.error("❌ Failed. Got: " + obj.result + " Expected: " + obj.golden);
test += "❌ Failed";
clone.querySelector('div').classList.add("text-danger")
clone.querySelector('.result').textContent = "Got: " + obj.result;
clone.querySelector('.expected').innerHTML = "Expected: " + obj.golden;
testFailCount++;
} else {
// Test Passed
clone.querySelector('div').classList.add("text-success")
test += "✅ Passed";
testPastCount++;
}
totalTestsRan++;
clone.querySelector('.test').textContent = test;
document.getElementById("testsResultsList").append(clone);
};
// Uses Bootstrap 5 CDN as default stylesheet/CSS.
const DefaultPageStylesheet = {
href: "https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/css/bootstrap.min.css",
rel: "stylesheet",
integrity: "sha384-gH2yIJqKdNHPEq0n4Mqa/HGKIhSkIHeL5AyhkYV8i59U5AR6csBvApHHNl/vI1Bx",
crossOrigin: "anonymous"
};
/**
* Sets the Browser Test JS GUI. Sets values if provided, otherwise sets defaults.
*
* @param {TestBrowserJS} tbjs Object. Options for TestBrowserJS.
* @returns {void}
**/
function setGUI(tbjs) {
let keys = Object.keys(tbjs.TestGUIOptions);
if (keys.length <= 0) {
setStyleSheet(DefaultPageStylesheet);
return;
}
// Perform checks for what options are provided and what is default.
// Set custom header and stylesheet if provided
if (keys.includes("header")) {
document.getElementById('CustomHeader').innerHTML = tbjs.TestGUIOptions.header;
} else if (keys.includes("stylesheet")) {
// Set stylesheet if custom provided, and custom header is not.
setStyleSheet(tbjs.TestGUIOptions.stylesheet);
} else {
// If no custom header or stylesheet is given, use defaults.
setStyleSheet(DefaultPageStylesheet);
}
// Set custom footer if provided
if (keys.includes("footer")) {
document.getElementById('CustomFooter').innerHTML = tbjs.TestGUIOptions.footer;
}
// Set custom main image if given.
if (keys.includes("main_image")) {
document.getElementById('MainImage').src = tbjs.TestGUIOptions.main_image;
}
// Set custom custom HTML testing area in body if given.
if (keys.includes("html_test_area")) {
document.getElementById('htmlTestArea').innerHTML = tbjs.TestGUIOptions.html_test_area;
}
}
/**
* Sets the stylesheet for the Browser Test JS page, with the given
* Stylesheet object.
*
* @param {Stylesheet} ss Object. Stylesheet object.
* @returns {void}
**/
function setStyleSheet(ss) {
let stylesheet = document.getElementById('bootstrapCSS');
stylesheet.href = ss.href;
let keys = Object.keys(ss);
if (keys.includes("crossOrigin")) {
stylesheet.crossOrigin = ss.crossOrigin;
} else {
stylesheet.crossOrigin = DefaultPageStylesheet.crossOrigin;
}
if (keys.includes("integrity")) {
stylesheet.integrity = ss.integrity;
}
if (keys.includes("rel")) {
stylesheet.rel = ss.rel;
} else {
stylesheet.rel = DefaultPageStylesheet.rel;
}
// console.log(stylesheet); // debugging
}