runner.js (30615B)
1 /*jshint nonew: false */ 2 (function() { 3 "use strict"; 4 var runner; 5 var testharness_properties = {output:false, 6 timeout_multiplier:1}; 7 8 function Manifest(path) { 9 this.data = null; 10 this.path = path; 11 this.num_tests = null; 12 } 13 14 Manifest.prototype = { 15 load: function(loaded_callback) { 16 this.generate(loaded_callback); 17 }, 18 19 generate: function(loaded_callback) { 20 var xhr = new XMLHttpRequest(); 21 xhr.onreadystatechange = function() { 22 if (xhr.readyState !== 4) { 23 return; 24 } 25 if (!(xhr.status === 200 || xhr.status === 0)) { 26 throw new Error("Manifest generation failed"); 27 } 28 this.data = JSON.parse(xhr.responseText); 29 loaded_callback(); 30 }.bind(this); 31 xhr.open("POST", "update_manifest.py"); 32 xhr.send(null); 33 }, 34 35 by_type:function(type) { 36 var ret = [] ; 37 if (this.data.items.hasOwnProperty(type)) { 38 for (var propertyName in this.data.items[type]) { 39 var arr = this.data.items[type][propertyName][0]; 40 var item = arr[arr.length - 1]; 41 item.path = propertyName; 42 if ('string' === typeof arr[0]) { 43 item.url = arr[0]; 44 } 45 if (Array.isArray(arr[1])) { 46 item.references = arr[1]; 47 } 48 ret.push(item); 49 } 50 } 51 return ret ; 52 } 53 }; 54 55 function ManifestIterator(manifest, path, test_types, use_regex) { 56 this.manifest = manifest; 57 this.paths = null; 58 this.regex_pattern = null; 59 this.test_types = test_types; 60 this.test_types_index = -1; 61 this.test_list = null; 62 this.test_index = null; 63 64 if (use_regex) { 65 this.regex_pattern = path; 66 } else { 67 // Split paths by either a comma or whitespace, and ignore empty sub-strings. 68 this.paths = path.split(/[,\s]+/).filter(function(s) { return s.length > 0; }); 69 } 70 } 71 72 ManifestIterator.prototype = { 73 next: function() { 74 var manifest_item = null; 75 76 if (this.test_types.length === 0) { 77 return null; 78 } 79 80 while (!manifest_item) { 81 while (this.test_list === null || this.test_index >= this.test_list.length) { 82 this.test_types_index++; 83 if (this.test_types_index >= this.test_types.length) { 84 return null; 85 } 86 this.test_index = 0; 87 this.test_list = this.manifest.by_type(this.test_types[this.test_types_index]); 88 } 89 90 manifest_item = this.test_list[this.test_index++]; 91 while (manifest_item && !this.matches(manifest_item)) { 92 manifest_item = this.test_list[this.test_index++]; 93 } 94 if (manifest_item) { 95 return this.to_test(manifest_item); 96 } 97 } 98 }, 99 100 // Calculate the location of a match within a provided URL. 101 // 102 // @param {string} url - Valid URL 103 // 104 // @returns {null|object} - null if the URL does not satisfy the iterator's 105 // filtering criteria. Otherwise, an object with 106 // the following properties: 107 // 108 // - index - the zero-indexed offset of the start 109 // of the match 110 // - width - the total number of matching 111 // characters 112 match_location: function(url) { 113 var match; 114 115 if (this.regex_pattern) { 116 match = url.match(this.regex_pattern); 117 118 if (!match) { 119 return null; 120 } 121 122 return { index: match.index, width: match[0].length }; 123 } 124 125 this.paths.some(function(path) { 126 if (url.indexOf(path) === 0) { 127 match = path; 128 return true; 129 } 130 return false; 131 }); 132 133 if (!match) { 134 return null; 135 } 136 137 return { index: 0, width: match.length }; 138 }, 139 140 matches: function(manifest_item) { 141 var url_base = this.manifest.data.url_base; 142 if (url_base.charAt(url_base.length - 1) !== "/") { 143 url_base = url_base + "/"; 144 } 145 var url = url_base + manifest_item.url; 146 return this.match_location(url) !== null; 147 }, 148 149 to_test: function(manifest_item) { 150 var url_base = this.manifest.data.url_base; 151 if (url_base.charAt(url_base.length - 1) !== "/") { 152 url_base = url_base + "/"; 153 } 154 var test = { 155 type: this.test_types[this.test_types_index], 156 url: url_base + manifest_item.url 157 }; 158 if (manifest_item.hasOwnProperty("references")) { 159 test.ref_length = manifest_item.references.length; 160 test.ref_type = manifest_item.references[0][1]; 161 test.ref_url = manifest_item.references[0][0]; 162 } 163 return test; 164 }, 165 166 count: function() { 167 return this.test_types.reduce(function(prev, current) { 168 var matches = this.manifest.by_type(current).filter(function(x) { 169 return this.matches(x); 170 }.bind(this)); 171 return prev + matches.length; 172 }.bind(this), 0); 173 } 174 }; 175 176 function VisualOutput(elem, runner) { 177 this.elem = elem; 178 this.runner = runner; 179 this.results_table = null; 180 this.section_wrapper = null; 181 this.results_table = this.elem.querySelector(".results > table"); 182 this.section = null; 183 this.progress = this.elem.querySelector(".summary .progress"); 184 this.meter = this.progress.querySelector(".progress-bar"); 185 this.result_count = null; 186 this.json_results_area = this.elem.querySelector("textarea"); 187 this.instructions = document.querySelector(".instructions"); 188 189 this.elem.style.display = "none"; 190 this.runner.manifest_wait_callbacks.push(this.on_manifest_wait.bind(this)); 191 this.runner.start_callbacks.push(this.on_start.bind(this)); 192 this.runner.result_callbacks.push(this.on_result.bind(this)); 193 this.runner.done_callbacks.push(this.on_done.bind(this)); 194 195 this.display_filter_state = {}; 196 197 var visual_output = this; 198 var display_filter_inputs = this.elem.querySelectorAll(".result-display-filter"); 199 for (var i = 0; i < display_filter_inputs.length; ++i) { 200 var display_filter_input = display_filter_inputs[i]; 201 this.display_filter_state[display_filter_input.value] = display_filter_input.checked; 202 display_filter_input.addEventListener("change", function(e) { 203 visual_output.apply_display_filter(e.target.value, e.target.checked); 204 }) 205 } 206 } 207 208 VisualOutput.prototype = { 209 clear: function() { 210 this.result_count = {"PASS":0, 211 "FAIL":0, 212 "ERROR":0, 213 "TIMEOUT":0, 214 "NOTRUN":0}; 215 for (var p in this.result_count) { 216 if (this.result_count.hasOwnProperty(p)) { 217 this.elem.querySelector("td." + p).textContent = 0; 218 } 219 } 220 if (this.json_results_area) { 221 this.json_results_area.parentNode.removeChild(this.json_results_area); 222 } 223 this.meter.style.width = '0px'; 224 this.meter.textContent = '0%'; 225 this.meter.classList.remove("stopped", "loading-manifest"); 226 this.elem.querySelector(".jsonResults").style.display = "none"; 227 this.results_table.removeChild(this.results_table.tBodies[0]); 228 this.results_table.appendChild(document.createElement("tbody")); 229 }, 230 231 on_manifest_wait: function() { 232 this.clear(); 233 this.instructions.style.display = "none"; 234 this.elem.style.display = "block"; 235 this.steady_status("loading-manifest"); 236 }, 237 238 on_start: function() { 239 this.clear(); 240 this.instructions.style.display = "none"; 241 this.elem.style.display = "block"; 242 this.meter.classList.add("progress-striped", "active"); 243 }, 244 245 on_result: function(test, status, message, subtests) { 246 var row = document.createElement("tr"); 247 248 var subtest_pass_count = subtests.reduce(function(prev, current) { 249 return (current.status === "PASS") ? prev + 1 : prev; 250 }, 0); 251 252 var subtest_notrun_count = subtests.reduce(function(prev, current) { 253 return (current.status === "NOTRUN") ? prev +1 : prev; 254 }, 0); 255 256 var subtests_count = subtests.length; 257 258 var test_status; 259 if (subtest_pass_count === subtests_count && 260 (status == "OK" || status == "PASS")) { 261 test_status = "PASS"; 262 } else if ((!subtests_count && status === "NOTRUN") || 263 (subtests_count && (subtest_notrun_count == subtests_count) ) ) { 264 test_status = "NOTRUN"; 265 } else if (subtests_count > 0 && status === "OK") { 266 test_status = "FAIL"; 267 } else { 268 test_status = status; 269 } 270 271 subtests.forEach(function(subtest) { 272 if (this.result_count.hasOwnProperty(subtest.status)) { 273 this.result_count[subtest.status] += 1; 274 } 275 }.bind(this)); 276 if (this.result_count.hasOwnProperty(status)) { 277 this.result_count[status] += 1; 278 } 279 280 var name_node = row.appendChild(document.createElement("td")); 281 name_node.appendChild(this.test_name_node(test)); 282 283 var status_node = row.appendChild(document.createElement("td")); 284 status_node.textContent = test_status; 285 status_node.className = test_status; 286 287 var message_node = row.appendChild(document.createElement("td")); 288 message_node.textContent = message || ""; 289 290 var subtests_node = row.appendChild(document.createElement("td")); 291 if (subtests_count) { 292 subtests_node.textContent = subtest_pass_count + "/" + subtests_count; 293 } else { 294 if (status == "PASS") { 295 subtests_node.textContent = "1/1"; 296 } else { 297 subtests_node.textContent = "0/1"; 298 } 299 } 300 301 var status_arr = ["PASS", "FAIL", "ERROR", "TIMEOUT", "NOTRUN"]; 302 for (var i = 0; i < status_arr.length; i++) { 303 this.elem.querySelector("td." + status_arr[i]).textContent = this.result_count[status_arr[i]]; 304 } 305 306 this.apply_display_filter_to_result_row(row, this.display_filter_state[test_status]); 307 this.results_table.tBodies[0].appendChild(row); 308 this.update_meter(this.runner.progress(), this.runner.results.count(), this.runner.test_count()); 309 }, 310 311 steady_status: function(statusName) { 312 var statusTexts = { 313 done: "Done!", 314 stopped: "Stopped", 315 "loading-manifest": "Updating and loading test manifest; this may take several minutes." 316 }; 317 var textContent = statusTexts[statusName]; 318 319 this.meter.setAttribute("aria-valuenow", this.meter.getAttribute("aria-valuemax")); 320 this.meter.style.width = "100%"; 321 this.meter.textContent = textContent; 322 this.meter.classList.remove("progress-striped", "active", "stopped", "loading-manifest"); 323 this.meter.classList.add(statusName); 324 this.runner.display_current_test(null); 325 }, 326 327 on_done: function() { 328 this.steady_status(this.runner.stop_flag ? "stopped" : "done"); 329 //add the json serialization of the results 330 var a = this.elem.querySelector(".jsonResults"); 331 var json = this.runner.results.to_json(); 332 333 if (document.getElementById("dumpit").checked) { 334 this.json_results_area = Array.prototype.slice.call(this.elem.querySelectorAll("textarea")); 335 for(var i = 0,t = this.json_results_area.length; i < t; i++){ 336 this.elem.removeChild(this.json_results_area[i]); 337 } 338 this.json_results_area = document.createElement("textarea"); 339 this.json_results_area.style.width = "100%"; 340 this.json_results_area.setAttribute("rows", "50"); 341 this.elem.appendChild(this.json_results_area); 342 this.json_results_area.textContent = json; 343 } 344 var blob = new Blob([json], { type: "application/json" }); 345 a.href = window.URL.createObjectURL(blob); 346 a.download = "runner-results.json"; 347 a.textContent = "Download JSON results"; 348 if (!a.getAttribute("download")) a.textContent += " (right-click and save as to download)"; 349 a.style.display = "inline"; 350 }, 351 352 test_name_node: function(test) { 353 if (!test.hasOwnProperty("ref_url")) { 354 return this.link(test.url); 355 } else { 356 var wrapper = document.createElement("span"); 357 wrapper.appendChild(this.link(test.url)); 358 wrapper.appendChild(document.createTextNode(" " + test.ref_type + " ")); 359 wrapper.appendChild(this.link(test.ref_url)); 360 return wrapper; 361 } 362 }, 363 364 link: function(href) { 365 var link = document.createElement("a"); 366 link.href = this.runner.server + href; 367 link.textContent = href; 368 return link; 369 }, 370 371 update_meter: function(progress, count, total) { 372 this.meter.setAttribute("aria-valuenow", count); 373 this.meter.setAttribute("aria-valuemax", total); 374 this.meter.textContent = this.meter.style.width = (progress * 100).toFixed(1) + "%"; 375 }, 376 377 apply_display_filter: function(test_status, display_state) { 378 this.display_filter_state[test_status] = display_state; 379 var result_cells = this.elem.querySelectorAll(".results > table tr td." + test_status); 380 for (var i = 0; i < result_cells.length; ++i) { 381 this.apply_display_filter_to_result_row(result_cells[i].parentNode, display_state) 382 } 383 }, 384 385 apply_display_filter_to_result_row: function(result_row, display_state) { 386 result_row.style.display = display_state ? "" : "none"; 387 } 388 }; 389 390 function ManualUI(elem, runner) { 391 this.elem = elem; 392 this.runner = runner; 393 this.pass_button = this.elem.querySelector("button.pass"); 394 this.fail_button = this.elem.querySelector("button.fail"); 395 this.skip_button = this.elem.querySelector("button.skip"); 396 this.ref_buttons = this.elem.querySelector(".reftestUI"); 397 this.ref_type = this.ref_buttons.querySelector(".refType"); 398 this.ref_warning = this.elem.querySelector(".reftestWarn"); 399 this.test_button = this.ref_buttons.querySelector("button.test"); 400 this.ref_button = this.ref_buttons.querySelector("button.ref"); 401 402 this.hide(); 403 404 this.runner.test_start_callbacks.push(this.on_test_start.bind(this)); 405 this.runner.test_pause_callbacks.push(this.hide.bind(this)); 406 this.runner.done_callbacks.push(this.on_done.bind(this)); 407 408 this.pass_button.onclick = function() { 409 this.disable_buttons(); 410 this.runner.on_result("PASS", "", []); 411 }.bind(this); 412 413 this.skip_button.onclick = function() { 414 this.disable_buttons(); 415 this.runner.on_result("NOTRUN", "", []); 416 }.bind(this); 417 418 this.fail_button.onclick = function() { 419 this.disable_buttons(); 420 this.runner.on_result("FAIL", "", []); 421 }.bind(this); 422 } 423 424 ManualUI.prototype = { 425 show: function() { 426 this.elem.style.display = "block"; 427 setTimeout(this.enable_buttons.bind(this), 200); 428 }, 429 430 hide: function() { 431 this.elem.style.display = "none"; 432 }, 433 434 show_ref: function() { 435 this.ref_buttons.style.display = "block"; 436 this.test_button.onclick = function() { 437 this.runner.load(this.runner.current_test.url); 438 }.bind(this); 439 this.ref_button.onclick = function() { 440 this.runner.load(this.runner.current_test.ref_url); 441 }.bind(this); 442 }, 443 444 hide_ref: function() { 445 this.ref_buttons.style.display = "none"; 446 }, 447 448 disable_buttons: function() { 449 this.pass_button.disabled = true; 450 this.fail_button.disabled = true; 451 }, 452 453 enable_buttons: function() { 454 this.pass_button.disabled = false; 455 this.fail_button.disabled = false; 456 }, 457 458 on_test_start: function(test) { 459 if (test.type == "manual" || test.type == "reftest") { 460 this.show(); 461 } else { 462 this.hide(); 463 } 464 if (test.type == "reftest") { 465 this.show_ref(); 466 this.ref_type.textContent = test.ref_type === "==" ? "equal" : "unequal"; 467 if (test.ref_length > 1) { 468 this.ref_warning.textContent = "WARNING: only presenting first of " + test.ref_length + " references"; 469 this.ref_warning.style.display = "inline"; 470 } else { 471 this.ref_warning.textContent = ""; 472 this.ref_warning.style.display = "none"; 473 } 474 } else { 475 this.hide_ref(); 476 } 477 }, 478 479 on_done: function() { 480 this.hide(); 481 } 482 }; 483 484 function TestControl(elem, runner) { 485 this.elem = elem; 486 this.path_input = this.elem.querySelector(".path"); 487 this.path_input.addEventListener("change", function() { 488 this.set_counts(); 489 }.bind(this), false); 490 this.use_regex_input = this.elem.querySelector("#use_regex"); 491 this.use_regex_input.addEventListener("change", function() { 492 this.set_counts(); 493 }.bind(this), false); 494 this.pause_button = this.elem.querySelector("button.togglePause"); 495 this.start_button = this.elem.querySelector("button.toggleStart"); 496 this.type_checkboxes = Array.prototype.slice.call( 497 this.elem.querySelectorAll("input[type=checkbox].test-type")); 498 this.type_checkboxes.forEach(function(elem) { 499 elem.addEventListener("change", function() { 500 this.set_counts(); 501 }.bind(this), 502 false); 503 elem.addEventListener("click", function() { 504 this.start_button.disabled = this.get_test_types().length < 1; 505 }.bind(this), 506 false); 507 }.bind(this)); 508 509 this.timeout_input = this.elem.querySelector(".timeout_multiplier"); 510 this.render_checkbox = this.elem.querySelector(".render"); 511 this.testcount_area = this.elem.querySelector("#testcount"); 512 this.runner = runner; 513 this.runner.done_callbacks.push(this.on_done.bind(this)); 514 this.set_start(); 515 this.set_counts(); 516 } 517 518 TestControl.prototype = { 519 set_start: function() { 520 this.start_button.disabled = this.get_test_types().length < 1; 521 this.pause_button.disabled = true; 522 this.start_button.textContent = "Start"; 523 this.path_input.disabled = false; 524 this.type_checkboxes.forEach(function(elem) { 525 elem.disabled = false; 526 }); 527 this.start_button.onclick = function() { 528 var path = this.get_path(); 529 var test_types = this.get_test_types(); 530 var settings = this.get_testharness_settings(); 531 var use_regex = this.get_use_regex(); 532 this.runner.start(path, test_types, settings, use_regex); 533 this.set_stop(); 534 this.set_pause(); 535 }.bind(this); 536 }, 537 538 set_stop: function() { 539 clearTimeout(this.runner.timeout); 540 this.pause_button.disabled = false; 541 this.start_button.textContent = "Stop"; 542 this.path_input.disabled = true; 543 this.type_checkboxes.forEach(function(elem) { 544 elem.disabled = true; 545 }); 546 this.start_button.onclick = function() { 547 this.runner.stop_flag = true; 548 this.runner.done(); 549 }.bind(this); 550 }, 551 552 set_pause: function() { 553 this.pause_button.textContent = "Pause"; 554 this.pause_button.onclick = function() { 555 this.runner.pause(); 556 this.set_resume(); 557 }.bind(this); 558 }, 559 560 set_resume: function() { 561 this.pause_button.textContent = "Resume"; 562 this.pause_button.onclick = function() { 563 this.runner.unpause(); 564 this.set_pause(); 565 }.bind(this); 566 567 }, 568 569 set_counts: function() { 570 if (this.runner.manifest_loading) { 571 setTimeout(function() { 572 this.set_counts(); 573 }.bind(this), 1000); 574 return; 575 } 576 var path = this.get_path(); 577 var test_types = this.get_test_types(); 578 var use_regex = this.get_use_regex(); 579 var iterator = new ManifestIterator(this.runner.manifest, path, test_types, use_regex); 580 var count = iterator.count(); 581 this.testcount_area.textContent = count; 582 }, 583 584 get_path: function() { 585 return this.path_input.value; 586 }, 587 588 get_test_types: function() { 589 return this.type_checkboxes.filter(function(elem) { 590 return elem.checked; 591 }).map(function(elem) { 592 return elem.value; 593 }); 594 }, 595 596 get_testharness_settings: function() { 597 return {timeout_multiplier: parseFloat(this.timeout_input.value), 598 output: this.render_checkbox.checked}; 599 }, 600 601 get_use_regex: function() { 602 return this.use_regex_input.checked; 603 }, 604 605 on_done: function() { 606 this.set_pause(); 607 this.set_start(); 608 } 609 }; 610 611 function Results(runner) { 612 this.test_results = null; 613 this.runner = runner; 614 615 this.runner.start_callbacks.push(this.on_start.bind(this)); 616 } 617 618 Results.prototype = { 619 on_start: function() { 620 this.test_results = []; 621 }, 622 623 set: function(test, status, message, subtests) { 624 this.test_results.push({"test":test, 625 "subtests":subtests, 626 "status":status, 627 "message":message}); 628 }, 629 630 count: function() { 631 return this.test_results.length; 632 }, 633 634 to_json: function() { 635 var test_results = this.test_results || []; 636 var data = { 637 "results": test_results.map(function(result) { 638 var rv = {"test":(result.test.hasOwnProperty("ref_url") ? 639 [result.test.url, result.test.ref_type, result.test.ref_url] : 640 result.test.url), 641 "subtests":result.subtests, 642 "status":result.status, 643 "message":result.message}; 644 return rv; 645 }) 646 }; 647 return JSON.stringify(data, null, 2); 648 } 649 }; 650 651 function Runner(manifest_path) { 652 this.server = get_host_info().HTTP_ORIGIN; 653 this.https_server = get_host_info().HTTPS_ORIGIN; 654 this.manifest = new Manifest(manifest_path); 655 this.path = null; 656 this.test_types = null; 657 this.manifest_iterator = null; 658 659 this.test_window = null; 660 this.test_div = document.getElementById('current_test'); 661 this.test_url = this.test_div.getElementsByTagName('a')[0]; 662 this.current_test = null; 663 this.timeout = null; 664 this.num_tests = null; 665 this.pause_flag = false; 666 this.stop_flag = false; 667 this.done_flag = false; 668 669 this.manifest_wait_callbacks = []; 670 this.start_callbacks = []; 671 this.test_start_callbacks = []; 672 this.test_pause_callbacks = []; 673 this.result_callbacks = []; 674 this.done_callbacks = []; 675 676 this.results = new Results(this); 677 678 this.start_after_manifest_load = false; 679 this.manifest_loading = true; 680 this.manifest.load(this.manifest_loaded.bind(this)); 681 } 682 683 Runner.prototype = { 684 test_timeout: 20000, //ms 685 686 currentTest: function() { 687 return this.manifest[this.mTestCount]; 688 }, 689 690 ensure_test_window: function() { 691 if (!this.test_window || this.test_window.location === null) { 692 this.test_window = window.open("about:blank", 800, 600); 693 } 694 }, 695 696 manifest_loaded: function() { 697 this.manifest_loading = false; 698 if (this.start_after_manifest_load) { 699 this.do_start(); 700 } 701 }, 702 703 start: function(path, test_types, testharness_settings, use_regex) { 704 this.pause_flag = false; 705 this.stop_flag = false; 706 this.done_flag = false; 707 this.path = path; 708 this.use_regex = use_regex; 709 this.test_types = test_types; 710 window.testharness_properties = testharness_settings; 711 this.manifest_iterator = new ManifestIterator(this.manifest, this.path, this.test_types, this.use_regex); 712 this.num_tests = null; 713 714 this.ensure_test_window(); 715 if (this.manifest.data === null) { 716 this.wait_for_manifest(); 717 } else { 718 this.do_start(); 719 } 720 }, 721 722 wait_for_manifest: function() { 723 this.start_after_manifest_load = true; 724 this.manifest_wait_callbacks.forEach(function(callback) { 725 callback(); 726 }); 727 }, 728 729 do_start: function() { 730 if (this.manifest_iterator.count() > 0) { 731 this.start_callbacks.forEach(function(callback) { 732 callback(); 733 }); 734 this.run_next_test(); 735 } else { 736 var tests = "tests"; 737 if (this.test_types.length < 3) { 738 tests = this.test_types.join(" tests or ") + " tests"; 739 } 740 var message = "No " + tests + " found in this path." 741 document.querySelector(".path").setCustomValidity(message); 742 this.done(); 743 } 744 }, 745 746 pause: function() { 747 this.pause_flag = true; 748 this.test_pause_callbacks.forEach(function(callback) { 749 callback(this.current_test); 750 }.bind(this)); 751 }, 752 753 unpause: function() { 754 this.pause_flag = false; 755 this.run_next_test(); 756 }, 757 758 on_result: function(status, message, subtests) { 759 clearTimeout(this.timeout); 760 this.results.set(this.current_test, status, message, subtests); 761 this.result_callbacks.forEach(function(callback) { 762 callback(this.current_test, status, message, subtests); 763 }.bind(this)); 764 this.run_next_test(); 765 }, 766 767 on_timeout: function() { 768 this.on_result("TIMEOUT", "", []); 769 }, 770 771 done: function() { 772 this.done_flag = true; 773 if (this.test_window) { 774 this.test_window.close(); 775 this.test_window = undefined; 776 } 777 this.done_callbacks.forEach(function(callback) { 778 callback(); 779 }); 780 }, 781 782 run_next_test: function() { 783 if (this.pause_flag) { 784 return; 785 } 786 var next_test = this.manifest_iterator.next(); 787 if (next_test === null||this.done_flag) { 788 this.done(); 789 return; 790 } 791 792 this.current_test = next_test; 793 794 if (next_test.type === "testharness") { 795 this.timeout = setTimeout(this.on_timeout.bind(this), 796 this.test_timeout * window.testharness_properties.timeout_multiplier); 797 } 798 this.display_current_test(this.current_test.url); 799 this.load(this.current_test.url); 800 801 this.test_start_callbacks.forEach(function(callback) { 802 callback(this.current_test); 803 }.bind(this)); 804 }, 805 806 display_current_test: function(url) { 807 var match_location, index, width; 808 809 if (url === null) { 810 this.test_div.style.visibility = "hidden"; 811 this.test_url.removeAttribute("href"); 812 this.test_url.textContent = ""; 813 return; 814 } 815 816 match_location = this.manifest_iterator.match_location(url); 817 index = match_location.index; 818 width = match_location.width; 819 820 this.test_url.setAttribute("href", url); 821 this.test_url.innerHTML = url.substring(0, index) + 822 "<span class='match'>" + 823 url.substring(index, index + width) + 824 "</span>" + 825 url.substring(index + width); 826 this.test_div.style.visibility = "visible"; 827 }, 828 829 load: function(path) { 830 this.ensure_test_window(); 831 if (path.match(/\.https\./)) 832 this.test_window.location.href = this.https_server + path; 833 else 834 this.test_window.location.href = this.server + path; 835 }, 836 837 progress: function() { 838 return this.results.count() / this.test_count(); 839 }, 840 841 test_count: function() { 842 if (this.num_tests === null) { 843 this.num_tests = this.manifest_iterator.count(); 844 } 845 return this.num_tests; 846 }, 847 848 on_complete: function(tests, status) { 849 var harness_status_map = {0:"OK", 1:"ERROR", 2:"TIMEOUT", 3:"NOTRUN"}; 850 var subtest_status_map = {0:"PASS", 1:"FAIL", 2:"TIMEOUT", 3:"NOTRUN"}; 851 852 // this ugly hack is because IE really insists on holding on to the objects it creates in 853 // other windows, and on losing track of them when the window gets closed 854 var subtest_results = JSON.parse(JSON.stringify( 855 tests.map(function (test) { 856 return {name: test.name, 857 status: subtest_status_map[test.status], 858 message: test.message}; 859 }) 860 )); 861 862 runner.on_result(harness_status_map[status.status], 863 status.message, 864 subtest_results); 865 } 866 }; 867 868 869 function parseOptions() { 870 var options = { 871 test_types: ["testharness", "reftest", "manual"] 872 }; 873 874 var optionstrings = location.search.substring(1).split("&"); 875 for (var i = 0, il = optionstrings.length; i < il; ++i) { 876 var opt = optionstrings[i]; 877 //TODO: fix this for complex-valued options 878 options[opt.substring(0, opt.indexOf("="))] = 879 opt.substring(opt.indexOf("=") + 1); 880 } 881 return options; 882 } 883 884 function setup() { 885 var options = parseOptions(); 886 887 if (options.path) { 888 document.getElementById('path').value = options.path; 889 } 890 891 runner = new Runner("/MANIFEST.json", options); 892 var test_control = new TestControl(document.getElementById("testControl"), runner); 893 new ManualUI(document.getElementById("manualUI"), runner); 894 new VisualOutput(document.getElementById("output"), runner); 895 896 window.addEventListener("message", function(e) { 897 if (e.data.type === "complete") 898 runner.on_complete(e.data.tests, e.data.status); 899 }); 900 901 if (options.autorun === "1") { 902 runner.start(test_control.get_path(), 903 test_control.get_test_types(), 904 test_control.get_testharness_settings(), 905 test_control.get_use_regex()); 906 } 907 } 908 909 window.addEventListener("DOMContentLoaded", setup, false); 910 })();