Tools/ChangeLog

 12019-09-13 Zhifei Fang <zhifei_fang@apple.com>
 2
 3 Add a test library and some fundamental test for Ref.js.
 4 https://bugs.webkit.org/show_bug.cgi?id=201778.
 5
 6 Reviewed by NOBODY (OOPS!).
 7
 8 * resultsdbpy/resultsdbpy/view/static/library/js/Ref.js:
 9 (applyStateDiff): For singal value, null, 0, false those are still valid state
 10 (Ref.prototype.setState): We should do nothing for undefined stateDiff
 11 * resultsdbpy/resultsdbpy/view/static/library/js/Test.js: Added.
 12 (AssertFailedError):
 13 (Expect): Expect class help user perform a expectation assertion
 14 (Expect.prototype.isType):
 15 (Expect.prototype.equalToValue):
 16 (Expect.prototype.equalToHtmlWithoutRef):
 17 (Expect.prototype.notEqualToValue):
 18 (Expect.prototype.greaterThan):
 19 (Expect.prototype.greaterThanOrEqualTo):
 20 (Expect.prototype.lessThan):
 21 (Expect.prototype.lessThanOrEqualTo):
 22 (Test): Common Test class for user to extend
 23 (Test.prototype.expect): This expect method will help the test perform expectation assertions
 24 (Test.prototype.sleep): Test will sleep for certain ms
 25 (Test.prototype.waitForSignal): Wait until we receive a certain signal with timeout
 26 (Test.prototype.waitForRefMounted): Wait until we revice ref's onElementMount signal with timeout
 27 (Test.prototype.waitForRefUnmounted): Wait until we revice ref's onElementUnmount signal with timeout
 28 (Test.prototype.waitForStateUpdated):Wait until we revice ref's onStateUpdate signal with timeout
 29 (Test.prototype.async.setup): Common interface for setup a test class
 30 (Test.prototype.async.clearUp): Common interface for clearup a test class
 31 (getTestFucntionNames): Collect all the test method of a test instance.
 32 (TestResult):
 33 (TestResult.prototype.catchException):
 34 (async.getTestResult): This will run the test and generate a TestResult object
 35 (TestController): Control how the test running
 36 (TestController.prototype.addResultHandler): Result handler should be hook with a component or something else to render the test result
 37 (TestController.prototype.addSetupArgs): This gives some additional args for the common setup method for each test class,
 38 it is the best place to setup something like a root element, a fake data source, etc
 39 (TestController.prototype.collect): This method used for collect the test classes.
 40 (TestController.prototype.async.collectFile): It will import the file as a module dynamicly and collect all the test classes that module export
 41 (TestController.prototype.async.runTest): It will run a test method of a test class
 42 (TestController.prototype.async.run): It will run all test or a test class or a test method
 43 * resultsdbpy/resultsdbpy/view/static/library/js/components/TestComponents.js: components for test app.
 44 * resultsdbpy/resultsdbpy/view/static/library/js/test/RefTest.js: Ref.js test cases.
 45 * resultsdbpy/resultsdbpy/view/static/library/js/test/index.html: Test app entry.
 46
1472019-09-06 Alex Christensen <achristensen@webkit.org>
248
349 Deprecate all WKCookieManagerRef functions

Tools/resultsdbpy/resultsdbpy/view/static/library/js/Ref.js

@@function isMergeableState(state) {
217217}
218218
219219function applyStateDiff(state, diff) {
220  if (!diff)
 220 if (diff === undefined)
221221 return state;
222222 if (!isMergeableState(diff))
223223 return diff;

@@class Ref {
275275 return this.key;
276276 }
277277 setState(stateDiff) {
 278 if (stateDiff === undefined)
 279 return;
278280 if (TRACE_STATE)
279281 this.stateTracer.push(stateDiff);
280282 else

Tools/resultsdbpy/resultsdbpy/view/static/library/js/Test.js

 1import {EventStream} from './Ref.js';
 2
 3class AssertFailedError extends Error {
 4 constructor(msg) {
 5 super(msg)
 6 }
 7}
 8
 9function stripRef(html) {
 10 return html.replace(/ref="[\w\d\-]+"/g, "")
 11}
 12
 13class Expect {
 14 constructor(valueA) {
 15 this.valueA = valueA;
 16 this.valueB = null;
 17 return this;
 18 }
 19
 20 isType(type) {
 21 if (type === null) {
 22 if (this.valueA !== null)
 23 throw new AssertFailedError(`${this.valueA} should be null`);
 24 } else if (Number.isNaN(type) || type === "NaN") {
 25 if (!Number.isNaN(this.valueA))
 26 throw new AssertFailedError(`${this.valueA} should be NaN`);
 27 } else if (type === "Array" || type === "array" || type === Array) {
 28 if (!Array.isArray(this.valueA))
 29 throw new AssertFailedError(`${this.valueA} should be an Array`);
 30 } else if (typeof this.valueA !== type && false === this.valueA instanceof type)
 31 throw new AssertFailedError(`${this.valueA} should be type ${type}`);
 32 return this;
 33 }
 34
 35 equalToValue(valueB) {
 36 if (this.valueA !== valueB)
 37 throw new AssertFailedError(`${this.valueA} should equal to ${valueB}`);
 38 }
 39
 40 equalToHtmlWithoutRef(html) {
 41 return this.equalToValue(stripRef(this.valueA), stripRef(html));
 42 }
 43
 44 equalToArray(array, compare = (x, y) => expect(x).equalToValue(y)) {
 45 expect(this.valueA).isType("Array");
 46 expect(array).isType("Array");
 47 expect(this.valueA.length).equalToValue(array.length);
 48 for (let i = 0; i < this.valueA.length; i++) {
 49 compare(this.valueA[i], array[i]);
 50 }
 51 }
 52
 53 notEqualToValue(valueB) {
 54 if (this.valueA === valueB)
 55 throw new AssertFailedError(`${this.valueA} should not equal to ${valueB}`);
 56 }
 57
 58 greaterThan(valueB) {
 59 if (this.valueA <= valueB)
 60 throw new AssertFailedError(`${this.valueA} should greater than ${valueB}`);
 61 }
 62
 63 greaterThanOrEqualTo(valueB) {
 64 if (this.valueA < valueB)
 65 throw new AssertFailedError(`${this.valueA} should greater than or equal to ${valueB}`);
 66 }
 67
 68 lessThan(valueB) {
 69 if (this.valueA >= valueB)
 70 throw new AssertFailedError(`${this.valueA} should less than ${valueB}`);
 71 }
 72
 73 lessThanOrEqualTo(valueB) {
 74 if (this.valueA > valueB)
 75 throw new AssertFailedError(`${this.valueA} should less than or equal to ${valueB}`);
 76 }
 77}
 78
 79function expect(value) {
 80 return new Expect(value);
 81}
 82
 83class Test {
 84 expect(value) {
 85 return expect(value);
 86 }
 87
 88 sleep(ms) {
 89 return new Promise((resolve) => {
 90 setTimeout(resolve, ms);
 91 });
 92 }
 93
 94 waitForSignal(singal, name, timeout=1000) {
 95 return new Promise((resolve, reject) => {
 96 const handler = () => {
 97 clearTimeout(timeoutHandler);
 98 resolve();
 99 singal.removeListener(handler);
 100 };
 101 const timeoutHandler = setTimeout(() => {
 102 singal.removeListener(handler);
 103 reject(new AssertFailedError(`Cannot get the ${name} signal after ${timeout} ms`));
 104 }, timeout);
 105 singal.addListener(handler);
 106 });
 107 }
 108
 109 waitForRefMounted(ref, timeout=1000) {
 110 return this.waitForSignal(ref.onElementMount, "mount", timeout);
 111 }
 112
 113 waitForRefUnmounted(ref, timeout=1000) {
 114 return this.waitForSignal(ref.onElementUnmount, "unmount", timeout);
 115 }
 116
 117 waitForStateUpdated(ref, timeout=1000) {
 118 return this.waitForSignal(ref.onStateUpdate, "state update", timeout);
 119 }
 120
 121 /*Common interface*/
 122 async setup() {}
 123 async clearUp() {}
 124}
 125
 126
 127function getTestFucntionNames(testObj) {
 128 const fixedMethods = new Set(Object.getOwnPropertyNames(Test.prototype));
 129 const testObjMethods = Object.getOwnPropertyNames(testObj.constructor.prototype);
 130 const testMethods = [];
 131 for (let method of testObjMethods) {
 132 if (!fixedMethods.has(method))
 133 testMethods.push(method);
 134 }
 135 return testMethods;
 136}
 137
 138const TEST_RESULT_TYPE = Object.freeze({
 139 Success: Symbol("Success"),
 140 Error: Symbol("Error"),
 141 Failed: Symbol("Failed"),
 142});
 143
 144class TestResult {
 145 constructor(className, fnName) {
 146 this.className = className;
 147 this.fnName = fnName;
 148 this.exception = null;
 149 this.type = TEST_RESULT_TYPE.Success;
 150 }
 151
 152 catchException(e) {
 153 this.exception = e;
 154 console.error(e);
 155 if (e instanceof AssertFailedError)
 156 this.type = TEST_RESULT_TYPE.Failed;
 157 else
 158 this.type = TEST_RESULT_TYPE.Error;
 159 }
 160}
 161
 162async function getTestResult(obj, fnName, args = []) {
 163 const result = new TestResult(obj.constructor.name, fnName);
 164 try {
 165 await obj[fnName](...args);
 166 } catch (e) {
 167 result.catchException(e);
 168 }
 169 return result;
 170}
 171
 172class TestController {
 173 constructor(setupArgs) {
 174 this.allTests = {}
 175 this.resultsEs = new EventStream();
 176 this.setupArgs = Array.isArray(setupArgs) ? setupArgs : [];
 177 }
 178
 179 addResultHandler(handler) {
 180 this.resultsEs.action(handler);
 181 }
 182
 183 addSetupArgs(args) {
 184 this.setupArgs = this.setupArgs.concat(args);
 185 }
 186
 187 collect(testClass) {
 188 const testInstance = new testClass();
 189 const testName = testInstance.constructor.name;
 190 if ( testName in this.allTests) {
 191 throw new Error(`${testName} has already been collected`);
 192 }
 193 this.allTests[testName] = testInstance;
 194 }
 195
 196 async collectFile(filePath) {
 197 const testModule = await import(filePath);
 198 Object.keys(testModule).forEach(className => this.collect(testModule[className]));
 199 }
 200
 201 async runTest(testName, fnName) {
 202 let haveError = false;
 203 const testInstance = this.allTests[testName];
 204 const testMethods = getTestFucntionNames(testInstance);
 205 let result = await getTestResult(testInstance, "setup", this.setupArgs);
 206 this.resultsEs.add(result);
 207 if (result.type === TEST_RESULT_TYPE.Success) {
 208 for (let testMethodName of testMethods) {
 209 if (fnName && fnName !== testMethodName)
 210 return;
 211 result = await getTestResult(testInstance, testMethodName);
 212 this.resultsEs.add(result);
 213 if (result.type !== TEST_RESULT_TYPE.Success)
 214 haveError = true;
 215 }
 216 } else
 217 haveError = true;
 218 result = await getTestResult(testInstance, "clearUp");
 219 this.resultsEs.add(result);
 220 if (result.type !== TEST_RESULT_TYPE.Success)
 221 haveError = true;
 222 return haveError;
 223 }
 224
 225 async run(testClassName, testFnName) {
 226 let haveError = false;
 227 if (testClassName)
 228 haveErrsor = await this.runTest(testClassName, testFnName);
 229 else {
 230 for(let testClassName of Object.keys(this.allTests))
 231 haveError |= await this.runTest(testClassName);
 232 }
 233 const finalResult = new TestResult("", "");
 234 if (!haveError)
 235 finalResult.type = TEST_RESULT_TYPE.Success;
 236 else
 237 finalResult.type = TEST_RESULT_TYPE.Failed;
 238 this.resultsEs.add(finalResult);
 239 }
 240}
 241
 242export {Test, TestController, TEST_RESULT_TYPE}

Tools/resultsdbpy/resultsdbpy/view/static/library/js/components/TestComponents.js

 1import {DOM, REF} from "../Ref.js"
 2import {TestController, TEST_RESULT_TYPE} from "../Test.js"
 3
 4
 5function TestApp(testController) {
 6 return (
 7 `<div class="row content">
 8 <div class="col-6">
 9 ${TestResultConsole(testController)}
 10 </div>
 11 <div class="col-6">
 12 ${TestRenderArea(testController)}
 13 </div>
 14 </div>`
 15 );
 16}
 17
 18function TestResult(testResult) {
 19 let statusClass = "success";
 20 let description = testResult.className ? "PASS" : "ALL PASS";
 21 switch (testResult.type) {
 22 case TEST_RESULT_TYPE.Failed:
 23 statusClass = "failed";
 24 description = "failed"
 25 break;
 26 case TEST_RESULT_TYPE.Error:
 27 statusClass = "error";
 28 description = "raise an error";
 29 break;
 30 }
 31 return (
 32 `
 33 <div class="text">
 34 ${testResult.className ? "" : "<hr>"}
 35 <div class="text block">
 36 <a href="?className=${testResult.className}">${testResult.className}</a>
 37 </div>
 38 <div class="text block">
 39 <a href="?className=${testResult.className}&fnName=${testResult.fnName}">${testResult.fnName}</a>
 40 </div>
 41 <div class="text block ${statusClass}">
 42 ${description}
 43 </div>
 44 ${testResult.exception ? `
 45 <div class="text ${statusClass}">
 46 <pre>${testResult.exception.message}\n${testResult.exception.stack}</pre>
 47 </div>` : ""}
 48 </div>`
 49 );
 50}
 51
 52function TestResultConsole(testController) {
 53 const ref = REF.createRef({
 54 onStateUpdate: (element, stateDiff) => {
 55 if (stateDiff.addTestResult) {
 56 DOM.append(element, TestResult(stateDiff.addTestResult));
 57 if (!stateDiff.addTestResult.className) {
 58 let final_res = "ALL PASS";
 59 if (stateDiff.addTestResult.type !== TEST_RESULT_TYPE.Success)
 60 final_res = "FAILED";
 61 document.title = `${final_res}`;
 62 }
 63 }
 64 }
 65 });
 66 testController.addResultHandler(testResult => ref.setState({addTestResult: testResult}));
 67 return `<div ref="${ref}"></div>`;
 68}
 69
 70
 71function TestRenderArea(testController) {
 72 const ref = REF.createRef({
 73 onElementMount: (element) => {
 74 testController.addSetupArgs([element]);
 75 const url = new URL(window.location);
 76 const searchParam = new URLSearchParams(url.search);
 77 const className = searchParam.get("className");
 78 if (className)
 79 document.title = className;
 80 const fnName = searchParam.get("fnName");
 81 testController.run(className, fnName)
 82 }
 83 });
 84 return `<div ref="${ref}"></div>`;
 85}
 86
 87export {TestApp};

Tools/resultsdbpy/resultsdbpy/view/static/library/js/test/RefTest.js

 1import {Test} from '../Test.js';
 2import {REF, DOM, diff, FP, EventStream} from '../Ref.js';
 3
 4class DiffTest extends Test {
 5 testArrayDiff() {
 6 let removed = [];
 7 let newArray = [];
 8 diff([1, 2, 3], [4, 5, 6], item => removed.push(item), item => newArray.push(item));
 9 this.expect(removed).equalToArray([1, 2, 3]);
 10 this.expect(newArray).equalToArray([4, 5, 6]);
 11
 12 removed = [];
 13 newArray = [];
 14 diff([1, 2, 3], [2, 3, 4], item => removed.push(item), item => newArray.push(item));
 15 this.expect(removed).equalToArray([1]);
 16 this.expect(newArray).equalToArray([2, 3, 4]);
 17
 18 removed = [];
 19 newArray = [];
 20 diff([2], [2, 3, 4], item => removed.push(item), item => newArray.push(item));
 21 this.expect(removed).equalToArray([]);
 22 this.expect(newArray).equalToArray([2, 3, 4]);
 23
 24 removed = [];
 25 newArray = [];
 26 diff([], [2, 3, 4], item => removed.push(item), item => newArray.push(item));
 27 this.expect(removed).equalToArray([]);
 28 this.expect(newArray).equalToArray([2, 3, 4]);
 29
 30 removed = [];
 31 newArray = [];
 32 diff([4, 3, 2], [2, 3, 4], item => removed.push(item), item => newArray.push(item));
 33 this.expect(removed).equalToArray([]);
 34 this.expect(newArray).equalToArray([2, 3, 4]);
 35
 36 removed = [];
 37 newArray = [];
 38 diff([4, 3, 2, 5, 6], [2, 3, 4], item => removed.push(item), item => newArray.push(item));
 39 this.expect(removed).equalToArray([5, 6]);
 40 this.expect(newArray).equalToArray([2, 3, 4]);
 41
 42 removed = [];
 43 newArray = [];
 44 diff([], [], item => removed.push(item), item => newArray.push(item));
 45 this.expect(removed).equalToArray([]);
 46 this.expect(newArray).equalToArray([]);
 47 }
 48}
 49
 50class DomTest extends Test {
 51 setup(rootElement) {
 52 this.rootElement = rootElement;
 53 }
 54
 55 testInject() {
 56 this.rootElement.innerHTML = "";
 57 const injector = `<div id="${Math.random()}"></div>`;
 58 DOM.inject(this.rootElement, injector);
 59 this.expect(this.rootElement.innerHTML).equalToValue(injector);
 60 this.rootElement.innerHTML = "";
 61 }
 62
 63 testBefore() {
 64 let initial = '<div id="1"></div><div id="2"></div><div id="3"></div>';
 65 this.rootElement.innerHTML = initial;
 66 let injector = `<div id="${Math.random()}"></div>`;
 67 DOM.before(this.rootElement.children[0], injector);
 68 this.expect(this.rootElement.innerHTML).equalToValue(`${injector}${initial}`);
 69 this.rootElement.innerHTML = initial;
 70 DOM.before(this.rootElement.children[1], injector);
 71 this.expect(this.rootElement.innerHTML).equalToValue(`<div id="1"></div>${injector}<div id="2"></div><div id="3"></div>`);
 72 this.rootElement.innerHTML = initial;
 73 DOM.before(this.rootElement.children[2], injector);
 74 this.expect(this.rootElement.innerHTML).equalToValue(`<div id="1"></div><div id="2"></div>${injector}<div id="3"></div>`);
 75 }
 76
 77 testAfter() {
 78 let initial = '<div id="1"></div><div id="2"></div><div id="3"></div>';
 79 this.rootElement.innerHTML = initial;
 80 let injector = `<div id="${Math.random()}"></div>`;
 81 DOM.after(this.rootElement.children[0], injector);
 82 this.expect(this.rootElement.innerHTML).equalToValue(`<div id="1"></div>${injector}<div id="2"></div><div id="3"></div>`);
 83 this.rootElement.innerHTML = initial;
 84 DOM.after(this.rootElement.children[1], injector);
 85 this.expect(this.rootElement.innerHTML).equalToValue(`<div id="1"></div><div id="2"></div>${injector}<div id="3"></div>`);
 86 this.rootElement.innerHTML = initial;
 87 DOM.after(this.rootElement.children[2], injector);
 88 this.expect(this.rootElement.innerHTML).equalToValue(`${initial}${injector}`);
 89 }
 90
 91 testPrepend() {
 92 let initial = '<div id="1"></div><div id="2"></div><div id="3"></div>';
 93 this.rootElement.innerHTML = initial;
 94 let injector = `<div id="${Math.random()}"></div>`;
 95 DOM.prepend(this.rootElement, injector);
 96 this.expect(this.rootElement.innerHTML).equalToValue(`${injector}${initial}`);
 97 }
 98
 99 testAppend() {
 100 let initial = '<div id="1"></div><div id="2"></div><div id="3"></div>';
 101 this.rootElement.innerHTML = initial;
 102 let injector = `<div id="${Math.random()}"></div>`;
 103 DOM.append(this.rootElement, injector);
 104 this.expect(this.rootElement.innerHTML).equalToValue(`${initial}${injector}`);
 105 }
 106
 107 testReplace() {
 108 let initial = '<div id="1"></div><div id="2"></div><div id="3"></div>';
 109 this.rootElement.innerHTML = initial;
 110 let injector = `<div id="${Math.random()}"></div>`;
 111 DOM.replace(this.rootElement.children[0], injector);
 112 this.expect(this.rootElement.innerHTML).equalToValue(`${injector}<div id="2"></div><div id="3"></div>`);
 113 }
 114
 115 testRemove() {
 116 let initial = '<div id="1"></div><div id="2"></div><div id="3"></div>';
 117 this.rootElement.innerHTML = initial;
 118 DOM.remove(this.rootElement.children[0]);
 119 this.expect(this.rootElement.innerHTML).equalToValue(`<div id="2"></div><div id="3"></div>`);
 120 }
 121}
 122
 123class RefTest extends Test {
 124 setup(rootElement) {
 125 this.rootElement = rootElement;
 126 }
 127
 128 async testOnElementMount() {
 129 let triggered = false;
 130 let currentRef = null;
 131 const creatAComponent = () => {
 132 let ref = REF.createRef({
 133 onElementMount: (element) => {
 134 triggered = true;
 135 }
 136 });
 137 currentRef = ref;
 138 return `<div ref="${ref}"></div>`;
 139 };
 140
 141 const firstComp = creatAComponent();
 142 DOM.inject(this.rootElement, firstComp);
 143 await this.waitForRefMounted(currentRef);
 144 this.expect(triggered).equalToValue(true);
 145 this.expect(currentRef.element.outerHTML).equalToValue(firstComp);
 146
 147 triggered = false;
 148 const secondComp = creatAComponent();
 149 DOM.replace(this.rootElement.children[0], secondComp);
 150 await this.waitForRefMounted(currentRef);
 151 this.expect(triggered).equalToValue(true);
 152 this.expect(currentRef.element.outerHTML).equalToValue(secondComp);
 153 }
 154
 155 async testOnElementUnmount() {
 156 let triggered = false;
 157 let currentRef = null;
 158 const creatAComponent = () => {
 159 let ref = REF.createRef({
 160 onElementUnmount: (element) => {
 161 triggered = true;
 162 }
 163 });
 164 currentRef = ref;
 165 return `<div ref="${ref}"></div>`;
 166 };
 167
 168 const firstComp = creatAComponent();
 169 DOM.inject(this.rootElement, firstComp);
 170 DOM.replace(this.rootElement.children[0], "<div></div>");
 171 await this.waitForRefUnmounted(currentRef);
 172 this.expect(triggered).equalToValue(true);
 173 this.expect(currentRef.element.parentElement).equalToValue(null);
 174
 175 triggered = false;
 176 DOM.inject(this.rootElement, firstComp);
 177 DOM.remove(this.rootElement.children[0]);
 178 let expectedE = null;
 179 try {
 180 await this.waitForRefUnmounted(currentRef);
 181 } catch (e) {
 182 // destoried ref won't be triggered
 183 this.expect(triggered).equalToValue(false);
 184 expectedE = e;
 185 }
 186 this.expect(expectedE).notEqualToValue(null);
 187
 188 triggered = false;
 189 const secondComp = creatAComponent();
 190 DOM.inject(this.rootElement, secondComp);
 191 DOM.remove(this.rootElement.children[0]);
 192 await this.waitForRefUnmounted(currentRef);
 193 this.expect(triggered).equalToValue(true);
 194 this.expect(currentRef.element.parentElement).equalToValue(null);
 195 }
 196
 197 async testOnComplexStateUpdate() {
 198 let verifier = null;
 199 let triggered = false;
 200 let expectedE = null;
 201 let initialState = {
 202 initialState: Math.random()
 203 };
 204 let ref = REF.createRef({
 205 onStateUpdate:(element, stateDiff, state) => {
 206 if (verifier) verifier(element, stateDiff, state);
 207 }
 208 });
 209 ref.setState(initialState);
 210 try {
 211 await this.waitForStateUpdated(ref);
 212 } catch (e) {
 213 expectedE = e;
 214 }
 215 // before element mount, we don't trigger state update callback, we just save the state
 216 this.expect(triggered).equalToValue(false);
 217 this.expect(expectedE).notEqualToValue(null);
 218 this.expect(ref.state.initialState).equalToValue(initialState.initialState);
 219 // Each time it will create a new state object
 220 this.expect(ref.state).notEqualToValue(initialState);
 221
 222 DOM.inject(this.rootElement, `<div ref="${ref}"></div>`);
 223 verifier = (element, stateDiff, state) => {
 224 triggered = true;
 225 this.expect(stateDiff.initialState).equalToValue(initialState.initialState);
 226 this.expect(state.fakeState).equalToValue(undefined);
 227 };
 228 await this.waitForStateUpdated(ref);
 229 this.expect(triggered).equalToValue(true);
 230
 231 triggered = false;
 232 let fakeState = {
 233 fakeState: Math.random()
 234 };
 235 verifier = (element, stateDiff, state) => {
 236 triggered = true;
 237 this.expect(stateDiff.fakeState).equalToValue(fakeState.fakeState);
 238 this.expect(state.fakeState).equalToValue(undefined);
 239
 240 this.expect(state.initialState).equalToValue(initialState.initialState);
 241 this.expect(stateDiff.initialState).equalToValue(undefined);
 242 };
 243 ref.setState(fakeState);
 244 await this.waitForStateUpdated(ref);
 245 this.expect(ref.state.fakeState).equalToValue(fakeState.fakeState);
 246 this.expect(ref.state.initialState).equalToValue(initialState.initialState);
 247 this.expect(triggered).equalToValue(true);
 248 }
 249
 250 async testOnValueStateUpdate() {
 251 let triggered = false;
 252 let verifier = null;
 253 const initialState = Math.random();
 254 const ref = REF.createRef({
 255 state: initialState,
 256 onStateUpdate:(element, stateDiff, state) => {
 257 if (verifier) verifier(element, stateDiff, state);
 258 }
 259 });
 260 verifier = (element, stateDiff, state) => {
 261 triggered = true;
 262 this.expect(element).notEqualToValue(null);
 263 this.expect(stateDiff).equalToValue(initialState);
 264 }
 265 DOM.inject(this.rootElement, `<div ref="${ref}"></div>`);
 266 await this.waitForStateUpdated(ref);
 267 this.expect(triggered).equalToValue(true);
 268
 269 const newState = null;
 270 triggered = false;
 271 verifier = (element, stateDiff, state) => {
 272 triggered = true;
 273 this.expect(stateDiff).equalToValue(newState);
 274 this.expect(state).equalToValue(initialState);
 275 }
 276 ref.setState(newState);
 277 await this.waitForStateUpdated(ref);
 278 this.expect(triggered).equalToValue(true);
 279
 280 const newState1 = 0;
 281 triggered = false;
 282 verifier = (element, stateDiff, state) => {
 283 triggered = true;
 284 this.expect(stateDiff).equalToValue(newState1);
 285 this.expect(state).equalToValue(newState);
 286 }
 287 ref.setState(newState1);
 288 await this.waitForStateUpdated(ref);
 289 this.expect(triggered).equalToValue(true);
 290
 291 const newState2 = undefined;
 292 let expectedE = null;
 293 ref.setState(newState1);
 294 try {
 295 await this.waitForStateUpdated(ref);
 296 } catch(e) {
 297 expectedE = e;
 298 }
 299 this.expect(expectedE).notEqualToValue(null);
 300 }
 301}
 302export {DiffTest, DomTest, RefTest};

Tools/resultsdbpy/resultsdbpy/view/static/library/js/test/index.html

 1<!DOCTYPE html>
 2<html>
 3 <head>
 4 <title>Test</title>
 5 <link rel="stylesheet" href="https://results.safari.apple.com/library/css/webkit.css"></link>
 6 <link rel="shortcut icon" sizes="32x32" type="image/x-icon" href="https://webkit.org/favicon.ico">
 7 <meta content="width=device-width, initial-scale=1, viewport-fit=cover" name="viewport">
 8 <meta charset="UTF-8">
 9 </head>
 10 <body>
 11 <div id="app"></div>
 12 <script type="module">
 13 import {DOM} from "../Ref.js";
 14 import {TestController} from "../Test.js";
 15 import {TestApp} from "../components/TestComponents.js";
 16 const testController = new TestController();
 17 async function main() {
 18 // Test file list: Root Path will be Test.js's folder
 19 await testController.collectFile("./test/RefTest.js");
 20 DOM.inject(document.getElementById("app"), TestApp(testController));
 21 };
 22 main();
 23 </script>
 24 </body>
 25</html>