Added: trunk/Tools/resultsdbpy/resultsdbpy/view/static/library/js/Test.js (0 => 250307)
--- trunk/Tools/resultsdbpy/resultsdbpy/view/static/library/js/Test.js (rev 0)
+++ trunk/Tools/resultsdbpy/resultsdbpy/view/static/library/js/Test.js 2019-09-24 17:48:20 UTC (rev 250307)
@@ -0,0 +1,241 @@
+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}
Added: trunk/Tools/resultsdbpy/resultsdbpy/view/static/library/js/test/RefTest.js (0 => 250307)
--- trunk/Tools/resultsdbpy/resultsdbpy/view/static/library/js/test/RefTest.js (rev 0)
+++ trunk/Tools/resultsdbpy/resultsdbpy/view/static/library/js/test/RefTest.js 2019-09-24 17:48:20 UTC (rev 250307)
@@ -0,0 +1,302 @@
+import {TestSuite} from '../Test.js';
+import {REF, DOM, diff, FP, EventStream} from '../Ref.js';
+
+class DiffTest extends TestSuite {
+ testArrayDiff() {
+ let removed = [];
+ let newArray = [];
+ diff([1, 2, 3], [4, 5, 6], item => removed.push(item), item => newArray.push(item));
+ this.expect(removed).equalToArray([1, 2, 3]);
+ this.expect(newArray).equalToArray([4, 5, 6]);
+
+ removed = [];
+ newArray = [];
+ diff([1, 2, 3], [2, 3, 4], item => removed.push(item), item => newArray.push(item));
+ this.expect(removed).equalToArray([1]);
+ this.expect(newArray).equalToArray([2, 3, 4]);
+
+ removed = [];
+ newArray = [];
+ diff([2], [2, 3, 4], item => removed.push(item), item => newArray.push(item));
+ this.expect(removed).equalToArray([]);
+ this.expect(newArray).equalToArray([2, 3, 4]);
+
+ removed = [];
+ newArray = [];
+ diff([], [2, 3, 4], item => removed.push(item), item => newArray.push(item));
+ this.expect(removed).equalToArray([]);
+ this.expect(newArray).equalToArray([2, 3, 4]);
+
+ removed = [];
+ newArray = [];
+ diff([4, 3, 2], [2, 3, 4], item => removed.push(item), item => newArray.push(item));
+ this.expect(removed).equalToArray([]);
+ this.expect(newArray).equalToArray([2, 3, 4]);
+
+ removed = [];
+ newArray = [];
+ diff([4, 3, 2, 5, 6], [2, 3, 4], item => removed.push(item), item => newArray.push(item));
+ this.expect(removed).equalToArray([5, 6]);
+ this.expect(newArray).equalToArray([2, 3, 4]);
+
+ removed = [];
+ newArray = [];
+ diff([], [], item => removed.push(item), item => newArray.push(item));
+ this.expect(removed).equalToArray([]);
+ this.expect(newArray).equalToArray([]);
+ }
+}
+
+class DomTest extends TestSuite {
+ setup(rootElement) {
+ this.rootElement = rootElement;
+ }
+
+ testInject() {
+ this.rootElement.innerHTML = "";
+ const injector = `<div id="${Math.random()}"></div>`;
+ DOM.inject(this.rootElement, injector);
+ this.expect(this.rootElement.innerHTML).equalToValue(injector);
+ this.rootElement.innerHTML = "";
+ }
+
+ testBefore() {
+ let initial = '<div id="1"></div><div id="2"></div><div id="3"></div>';
+ this.rootElement.innerHTML = initial;
+ let injector = `<div id="${Math.random()}"></div>`;
+ DOM.before(this.rootElement.children[0], injector);
+ this.expect(this.rootElement.innerHTML).equalToValue(`${injector}${initial}`);
+ this.rootElement.innerHTML = initial;
+ DOM.before(this.rootElement.children[1], injector);
+ this.expect(this.rootElement.innerHTML).equalToValue(`<div id="1"></div>${injector}<div id="2"></div><div id="3"></div>`);
+ this.rootElement.innerHTML = initial;
+ DOM.before(this.rootElement.children[2], injector);
+ this.expect(this.rootElement.innerHTML).equalToValue(`<div id="1"></div><div id="2"></div>${injector}<div id="3"></div>`);
+ }
+
+ testAfter() {
+ let initial = '<div id="1"></div><div id="2"></div><div id="3"></div>';
+ this.rootElement.innerHTML = initial;
+ let injector = `<div id="${Math.random()}"></div>`;
+ DOM.after(this.rootElement.children[0], injector);
+ this.expect(this.rootElement.innerHTML).equalToValue(`<div id="1"></div>${injector}<div id="2"></div><div id="3"></div>`);
+ this.rootElement.innerHTML = initial;
+ DOM.after(this.rootElement.children[1], injector);
+ this.expect(this.rootElement.innerHTML).equalToValue(`<div id="1"></div><div id="2"></div>${injector}<div id="3"></div>`);
+ this.rootElement.innerHTML = initial;
+ DOM.after(this.rootElement.children[2], injector);
+ this.expect(this.rootElement.innerHTML).equalToValue(`${initial}${injector}`);
+ }
+
+ testPrepend() {
+ let initial = '<div id="1"></div><div id="2"></div><div id="3"></div>';
+ this.rootElement.innerHTML = initial;
+ let injector = `<div id="${Math.random()}"></div>`;
+ DOM.prepend(this.rootElement, injector);
+ this.expect(this.rootElement.innerHTML).equalToValue(`${injector}${initial}`);
+ }
+
+ testAppend() {
+ let initial = '<div id="1"></div><div id="2"></div><div id="3"></div>';
+ this.rootElement.innerHTML = initial;
+ let injector = `<div id="${Math.random()}"></div>`;
+ DOM.append(this.rootElement, injector);
+ this.expect(this.rootElement.innerHTML).equalToValue(`${initial}${injector}`);
+ }
+
+ testReplace() {
+ let initial = '<div id="1"></div><div id="2"></div><div id="3"></div>';
+ this.rootElement.innerHTML = initial;
+ let injector = `<div id="${Math.random()}"></div>`;
+ DOM.replace(this.rootElement.children[0], injector);
+ this.expect(this.rootElement.innerHTML).equalToValue(`${injector}<div id="2"></div><div id="3"></div>`);
+ }
+
+ testRemove() {
+ let initial = '<div id="1"></div><div id="2"></div><div id="3"></div>';
+ this.rootElement.innerHTML = initial;
+ DOM.remove(this.rootElement.children[0]);
+ this.expect(this.rootElement.innerHTML).equalToValue(`<div id="2"></div><div id="3"></div>`);
+ }
+}
+
+class RefTest extends TestSuite {
+ setup(rootElement) {
+ this.rootElement = rootElement;
+ }
+
+ async testOnElementMount() {
+ let triggered = false;
+ let currentRef = null;
+ const creatAComponent = () => {
+ let ref = REF.createRef({
+ onElementMount: (element) => {
+ triggered = true;
+ }
+ });
+ currentRef = ref;
+ return `<div ref="${ref}"></div>`;
+ };
+
+ const firstComp = creatAComponent();
+ DOM.inject(this.rootElement, firstComp);
+ await this.waitForRefMounted(currentRef);
+ this.expect(triggered).equalToValue(true);
+ this.expect(currentRef.element.outerHTML).equalToValue(firstComp);
+
+ triggered = false;
+ const secondComp = creatAComponent();
+ DOM.replace(this.rootElement.children[0], secondComp);
+ await this.waitForRefMounted(currentRef);
+ this.expect(triggered).equalToValue(true);
+ this.expect(currentRef.element.outerHTML).equalToValue(secondComp);
+ }
+
+ async testOnElementUnmount() {
+ let triggered = false;
+ let currentRef = null;
+ const creatAComponent = () => {
+ let ref = REF.createRef({
+ onElementUnmount: (element) => {
+ triggered = true;
+ }
+ });
+ currentRef = ref;
+ return `<div ref="${ref}"></div>`;
+ };
+
+ const firstComp = creatAComponent();
+ DOM.inject(this.rootElement, firstComp);
+ DOM.replace(this.rootElement.children[0], "<div></div>");
+ await this.waitForRefUnmounted(currentRef);
+ this.expect(triggered).equalToValue(true);
+ this.expect(currentRef.element.parentElement).equalToValue(null);
+
+ triggered = false;
+ DOM.inject(this.rootElement, firstComp);
+ DOM.remove(this.rootElement.children[0]);
+ let expectedE = null;
+ try {
+ await this.waitForRefUnmounted(currentRef);
+ } catch (e) {
+ // destoried ref won't be triggered
+ this.expect(triggered).equalToValue(false);
+ expectedE = e;
+ }
+ this.expect(expectedE).notEqualToValue(null);
+
+ triggered = false;
+ const secondComp = creatAComponent();
+ DOM.inject(this.rootElement, secondComp);
+ DOM.remove(this.rootElement.children[0]);
+ await this.waitForRefUnmounted(currentRef);
+ this.expect(triggered).equalToValue(true);
+ this.expect(currentRef.element.parentElement).equalToValue(null);
+ }
+
+ async testOnComplexStateUpdate() {
+ let verifier = null;
+ let triggered = false;
+ let expectedE = null;
+ let initialState = {
+ initialState: Math.random()
+ };
+ let ref = REF.createRef({
+ onStateUpdate:(element, stateDiff, state) => {
+ if (verifier) verifier(element, stateDiff, state);
+ }
+ });
+ ref.setState(initialState);
+ try {
+ await this.waitForStateUpdated(ref);
+ } catch (e) {
+ expectedE = e;
+ }
+ // before element mount, we don't trigger state update callback, we just save the state
+ this.expect(triggered).equalToValue(false);
+ this.expect(expectedE).notEqualToValue(null);
+ this.expect(ref.state.initialState).equalToValue(initialState.initialState);
+ // Each time it will create a new state object
+ this.expect(ref.state).notEqualToValue(initialState);
+
+ DOM.inject(this.rootElement, `<div ref="${ref}"></div>`);
+ verifier = (element, stateDiff, state) => {
+ triggered = true;
+ this.expect(stateDiff.initialState).equalToValue(initialState.initialState);
+ this.expect(state.fakeState).equalToValue(undefined);
+ };
+ await this.waitForStateUpdated(ref);
+ this.expect(triggered).equalToValue(true);
+
+ triggered = false;
+ let fakeState = {
+ fakeState: Math.random()
+ };
+ verifier = (element, stateDiff, state) => {
+ triggered = true;
+ this.expect(stateDiff.fakeState).equalToValue(fakeState.fakeState);
+ this.expect(state.fakeState).equalToValue(undefined);
+
+ this.expect(state.initialState).equalToValue(initialState.initialState);
+ this.expect(stateDiff.initialState).equalToValue(undefined);
+ };
+ ref.setState(fakeState);
+ await this.waitForStateUpdated(ref);
+ this.expect(ref.state.fakeState).equalToValue(fakeState.fakeState);
+ this.expect(ref.state.initialState).equalToValue(initialState.initialState);
+ this.expect(triggered).equalToValue(true);
+ }
+
+ async testOnValueStateUpdate() {
+ let triggered = false;
+ let verifier = null;
+ const initialState = Math.random();
+ const ref = REF.createRef({
+ state: initialState,
+ onStateUpdate:(element, stateDiff, state) => {
+ if (verifier) verifier(element, stateDiff, state);
+ }
+ });
+ verifier = (element, stateDiff, state) => {
+ triggered = true;
+ this.expect(element).notEqualToValue(null);
+ this.expect(stateDiff).equalToValue(initialState);
+ }
+ DOM.inject(this.rootElement, `<div ref="${ref}"></div>`);
+ await this.waitForStateUpdated(ref);
+ this.expect(triggered).equalToValue(true);
+
+ const newState = null;
+ triggered = false;
+ verifier = (element, stateDiff, state) => {
+ triggered = true;
+ this.expect(stateDiff).equalToValue(newState);
+ this.expect(state).equalToValue(initialState);
+ }
+ ref.setState(newState);
+ await this.waitForStateUpdated(ref);
+ this.expect(triggered).equalToValue(true);
+
+ const newState1 = 0;
+ triggered = false;
+ verifier = (element, stateDiff, state) => {
+ triggered = true;
+ this.expect(stateDiff).equalToValue(newState1);
+ this.expect(state).equalToValue(newState);
+ }
+ ref.setState(newState1);
+ await this.waitForStateUpdated(ref);
+ this.expect(triggered).equalToValue(true);
+
+ const newState2 = undefined;
+ let expectedE = null;
+ ref.setState(newState1);
+ try {
+ await this.waitForStateUpdated(ref);
+ } catch(e) {
+ expectedE = e;
+ }
+ this.expect(expectedE).notEqualToValue(null);
+ }
+}
+export {DiffTest, DomTest, RefTest};