blob: 276d12054c920761f463130706ed68ac9ecf9fa1 [file] [log] [blame]
import {EventStream} from './Ref.js';
class AssertFailedError extends Error {
constructor(msg) {
super(msg)
}
}
function stripRef(html) {
return html.replace(/ref="[\w\d\-]+"/g, "")
}
class Expect {
constructor(valueA) {
this.valueA = valueA;
this.valueB = null;
return this;
}
isType(type) {
if (type === null) {
if (this.valueA !== null)
throw new AssertFailedError(`${this.valueA} should be null`);
} else if (Number.isNaN(type) || type === "NaN") {
if (!Number.isNaN(this.valueA))
throw new AssertFailedError(`${this.valueA} should be NaN`);
} else if (type === "Array" || type === "array" || type === Array) {
if (!Array.isArray(this.valueA))
throw new AssertFailedError(`${this.valueA} should be an Array`);
} else if (typeof this.valueA !== type && false === this.valueA instanceof type)
throw new AssertFailedError(`${this.valueA} should be type ${type}`);
return this;
}
equalToValue(valueB) {
if (this.valueA !== valueB)
throw new AssertFailedError(`${this.valueA} should equal to ${valueB}`);
}
equalToHtmlWithoutRef(html) {
return this.equalToValue(stripRef(this.valueA), stripRef(html));
}
equalToArray(array, compare = (x, y) => expect(x).equalToValue(y)) {
expect(this.valueA).isType("Array");
expect(array).isType("Array");
expect(this.valueA.length).equalToValue(array.length);
for (let i = 0; i < this.valueA.length; i++) {
compare(this.valueA[i], array[i]);
}
}
notEqualToValue(valueB) {
if (this.valueA === valueB)
throw new AssertFailedError(`${this.valueA} should not equal to ${valueB}`);
}
greaterThan(valueB) {
if (this.valueA <= valueB)
throw new AssertFailedError(`${this.valueA} should greater than ${valueB}`);
}
greaterThanOrEqualTo(valueB) {
if (this.valueA < valueB)
throw new AssertFailedError(`${this.valueA} should greater than or equal to ${valueB}`);
}
lessThan(valueB) {
if (this.valueA >= valueB)
throw new AssertFailedError(`${this.valueA} should less than ${valueB}`);
}
lessThanOrEqualTo(valueB) {
if (this.valueA > valueB)
throw new AssertFailedError(`${this.valueA} should less than or equal to ${valueB}`);
}
}
function expect(value) {
return new Expect(value);
}
class TestSuite {
expect(value) {
return expect(value);
}
sleep(milliseconds) {
return new Promise((resolve) => {
setTimeout(resolve, milliseconds);
});
}
waitForSignal(singal, name, timeout=1000) {
return new Promise((resolve, reject) => {
const handler = () => {
clearTimeout(timeoutHandler);
resolve();
singal.removeListener(handler);
};
const timeoutHandler = setTimeout(() => {
singal.removeListener(handler);
reject(new AssertFailedError(`Cannot get the ${name} signal after ${timeout} ms`));
}, timeout);
singal.addListener(handler);
});
}
waitForRefMounted(ref, timeout=1000) {
return this.waitForSignal(ref.onElementMount, "mount", timeout);
}
waitForRefUnmounted(ref, timeout=1000) {
return this.waitForSignal(ref.onElementUnmount, "unmount", timeout);
}
waitForStateUpdated(ref, timeout=1000) {
return this.waitForSignal(ref.onStateUpdate, "state update", timeout);
}
async setup() {}
async clearUp() {}
}
function getTestFucntionNames(testObj) {
const fixedMethods = new Set(Object.getOwnPropertyNames(TestSuite.prototype));
const testObjMethods = Object.getOwnPropertyNames(testObj.constructor.prototype);
const testMethods = [];
for (let method of testObjMethods) {
if (!fixedMethods.has(method))
testMethods.push(method);
}
return testMethods;
}
const TEST_RESULT_TYPE = Object.freeze({
Success: Symbol("Success"),
Error: Symbol("Error"),
Failed: Symbol("Failed"),
});
class TestResult {
constructor(className, fnName) {
this.className = className;
this.fnName = fnName;
this.exception = null;
this.type = TEST_RESULT_TYPE.Success;
}
catchException(e) {
this.exception = e;
console.error(e);
if (e instanceof AssertFailedError)
this.type = TEST_RESULT_TYPE.Failed;
else
this.type = TEST_RESULT_TYPE.Error;
}
}
async function getTestResult(obj, fnName, args = []) {
const result = new TestResult(obj.constructor.name, fnName);
try {
await obj[fnName](...args);
} catch (e) {
result.catchException(e);
}
return result;
}
class TestController {
constructor(setupArgs) {
this.allTests = {}
this.resultsEs = new EventStream();
this.setupArgs = Array.isArray(setupArgs) ? setupArgs : [];
}
addResultHandler(handler) {
this.resultsEs.action(handler);
}
addSetupArgs(args) {
this.setupArgs = this.setupArgs.concat(args);
}
collect(testSuiteClass) {
const testInstance = new testSuiteClass();
const testName = testInstance.constructor.name;
if (testName in this.allTests) {
throw new Error(`${testName} has already been collected`);
}
this.allTests[testName] = testInstance;
}
async collectFile(filePath) {
const testModule = await import(filePath);
Object.keys(testModule).forEach(className => this.collect(testModule[className]));
}
async runTest(testName, fnName) {
let haveError = false;
const testInstance = this.allTests[testName];
const testMethods = getTestFucntionNames(testInstance);
let result = await getTestResult(testInstance, "setup", this.setupArgs);
this.resultsEs.add(result);
if (result.type === TEST_RESULT_TYPE.Success) {
for (let testMethodName of testMethods) {
if (fnName && fnName !== testMethodName)
return;
result = await getTestResult(testInstance, testMethodName);
this.resultsEs.add(result);
if (result.type !== TEST_RESULT_TYPE.Success)
haveError = true;
}
} else
haveError = true;
result = await getTestResult(testInstance, "clearUp");
this.resultsEs.add(result);
if (result.type !== TEST_RESULT_TYPE.Success)
haveError = true;
return haveError;
}
async run(testSuiteClassName, testFnName) {
let haveError = false;
if (testSuiteClassName)
haveError = await this.runTest(testSuiteClassName, testFnName);
else {
for(let testSuiteClassName of Object.keys(this.allTests))
haveError |= await this.runTest(testSuiteClassName);
}
const finalResult = new TestResult("", "");
if (!haveError)
finalResult.type = TEST_RESULT_TYPE.Success;
else
finalResult.type = TEST_RESULT_TYPE.Failed;
this.resultsEs.add(finalResult);
}
}
export {TestSuite, TestController, TEST_RESULT_TYPE}