als-document
Version:
A powerful HTML parser & DOM manipulation library for both backend and frontend.
169 lines (157 loc) • 6.72 kB
JavaScript
class SimpleTest {
static tests = [];
static beforeEachCallback;
static afterEachCallback;
static nestingLevel = 0;
static currentParent = [];
static space = ''
static results = {}
static testTitle = ''
static showFullError = false
static colors = {
bold:'\x1b[1m',
cblue:'\x1b[34m',
cred:'\x1b[31m',
cgreen:'\x1b[32m',
cgray:'\x1b[37m',
reset:'\x1b[0m'
}
static delay = (ms,toResolve) => new Promise((resolve) => {
setTimeout(() => {resolve(toResolve)}, ms);
});
static describe(title, callback,pause=false) {
if(pause) return
let { nestingLevel, currentParent } = SimpleTest
nestingLevel++;
currentParent.push(title);
callback();
currentParent.pop();
nestingLevel--;
if (nestingLevel === 0) {
SimpleTest.beforeEachCallback = null; // Clear beforeEachCallback after finishing the outermost describe block
SimpleTest.afterEachCallback = null; // Clear beforeEachCallback after finishing the outermost describe block
}
}
static beforeEach(callback) {SimpleTest.beforeEachCallback = callback;}
static afterEach(callback) {SimpleTest.afterEachCallback = callback;}
static beforeAll(callback) {SimpleTest.beforAllCallback = callback;}
static afterAll(callback) {SimpleTest.afterAllCallback = callback;}
static it(title, callback) {
SimpleTest.tests.push({
titles: [...SimpleTest.currentParent, title],
before: SimpleTest.beforeEachCallback,
after:SimpleTest.afterEachCallback,
test: callback,
});
}
static async runTests(consoleLog=true) {
const {bold,cblue,cred,reset} = SimpleTest.colors
SimpleTest.consoleLog = consoleLog
let { tests,results,showFullError,beforAllCallback,afterAllCallback } = SimpleTest;
let previousTitles = [];
if(typeof beforAllCallback == 'function') await beforAllCallback()
for(const test of tests) {
let curentResult = results
SimpleTest.space = test.titles.map(t => ' ').join('');
test.titles.forEach((title, index) => {
if(curentResult[title] == undefined) {
if(index == test.titles.length-1) curentResult[title] = []
else curentResult[title] = {}
}
curentResult = curentResult[title]
if(previousTitles[index] !== title) {
const indentation = ' '.repeat(index);
console.log(bold+cblue+'%s'+reset,`${indentation} ${title}`);
previousTitles[index] = title;
}
});
SimpleTest.curentResult = curentResult
try {
if (test.before) await test.before();
await test.test();
if (test.after) await test.after();
} catch (error) {
if(showFullError) console.log(error)
else console.log(`${SimpleTest.space}${cred}Error:${reset} ${error.message}`);
curentResult.push({error})
}
console.log('\n');
}
if(typeof afterAllCallback == 'function') await afterAllCallback()
}
static assert(value,testTitle) {
if(testTitle) SimpleTest.testTitle = testTitle
SimpleTest.logResult(value)
}
static expect(value1) {
let { logResult, isEqual } = SimpleTest
const methods = {
is: (text='') => {
SimpleTest.testTitle = text;
SimpleTest.not = false
return methods;
},
isNot: (text='') => {
SimpleTest.testTitle = text;
SimpleTest.not = true
return methods;
},
equalTo: (value2) => {logResult(value1 === value2);},
between(value2,value3) {logResult(value1 >= value2 && value1 <= value3);},
below(value2) {logResult(value1 < value2);},
above(value2) {logResult(value1 > value2);},
atLeast(value2) {logResult(value1 >= value2);},
atMost(value2) {logResult(value1 <= value2);},
sameAs: (value2) => {logResult(isEqual(value1, value2));},
defined: () => {logResult(value1 !== undefined);},
matchTo(pattern) {logResult(pattern.test(new RegExp(value1)));},
includes(value2) {logResult(value1.includes(value2));},
error: () => {
if(typeof value1 !== 'function') return console.log('Expected value has to be function')
try {
let result = value1()
if(result instanceof Error) logResult(true)
else logResult(false);
} catch (error) {logResult(true)}
},
hasProperty: (property) => {logResult(Object.prototype.hasOwnProperty.call(value1, property));},
instanceof: (classType) => {logResult(value1.constructor.name == classType.name)},
closeTo(value2, decimalPlaces = 2) {
const multiplier = 10 ** decimalPlaces;
const roundedActual = Math.round(value1 * multiplier);
const roundedExpected = Math.round(value2 * multiplier);
const diff = Math.abs(roundedActual - roundedExpected) / multiplier;
logResult(diff < 1 / multiplier);
}
};
return methods;
}
static logResult(result) {
let {curentResult,testTitle,not,space,colors} = SimpleTest
let {cgreen,cred,cgray,reset} = colors
if(not) result = !result
curentResult.push({result:result ? true : false,testTitle,error:null})
const status = result ? 'Success' : 'Failed';
const color = result ? cgreen : cred;
if (testTitle == '') console.log(`${color}${space}${status}${reset}`);
else console.log(`${space}${color}${status}: ${testTitle ? `${cgray}${testTitle}` : ''}`+reset,);
SimpleTest.not = false
SimpleTest.testTitle = ''
}
static isEqual(obj1, obj2, visited = new WeakMap()) {
if (obj1 === obj2) return true;
if (visited.has(obj1) && visited.get(obj1) === obj2) return true;
const type1 = typeof obj1;
const type2 = typeof obj2;
if (type1 !== type2 || type1 !== 'object' || obj1 === null || obj2 === null) return false;
visited.set(obj1, obj2);
const keys1 = Object.keys(obj1);
const keys2 = Object.keys(obj2);
if(keys1.length !== keys2.length) return false;
for (let key of keys1) {
if (!keys2.includes(key) || !SimpleTest.isEqual(obj1[key], obj2[key], visited)) return false;
}
return true;
};
}
try {module.exports = SimpleTest} catch (error) {}