| OLD | NEW |
| 1 /*global self*/ | 1 /*global self*/ |
| 2 /*jshint latedef: nofunc*/ | 2 /*jshint latedef: nofunc*/ |
| 3 /* | 3 /* |
| 4 Distributed under both the W3C Test Suite License [1] and the W3C | 4 Distributed under both the W3C Test Suite License [1] and the W3C |
| 5 3-clause BSD License [2]. To contribute to a W3C Test Suite, see the | 5 3-clause BSD License [2]. To contribute to a W3C Test Suite, see the |
| 6 policies and contribution forms [3]. | 6 policies and contribution forms [3]. |
| 7 | 7 |
| 8 [1] http://www.w3.org/Consortium/Legal/2008/04-testsuite-license | 8 [1] http://www.w3.org/Consortium/Legal/2008/04-testsuite-license |
| 9 [2] http://www.w3.org/Consortium/Legal/2008/03-bsd-license | 9 [2] http://www.w3.org/Consortium/Legal/2008/03-bsd-license |
| 10 [3] http://www.w3.org/2004/10/27-testcases | 10 [3] http://www.w3.org/2004/10/27-testcases |
| 11 */ | 11 */ |
| 12 | 12 |
| 13 /* Documentation is in docs/api.md */ | 13 /* Documentation is in docs/api.md */ |
| 14 | 14 |
| 15 (function () | 15 (function () |
| 16 { | 16 { |
| 17 var debug = false; | 17 var debug = false; |
| 18 // default timeout is 10 seconds, test can override if needed | 18 // default timeout is 10 seconds, test can override if needed |
| 19 var settings = { | 19 var settings = { |
| 20 output:true, | 20 output:true, |
| 21 harness_timeout:{ | 21 harness_timeout:{ |
| 22 "normal":10000, | 22 "normal":10000, |
| 23 "long":60000 | 23 "long":60000 |
| 24 }, | 24 }, |
| 25 test_timeout:null | 25 test_timeout:null, |
| 26 message_events: ["start", "test_state", "result", "completion"] |
| 26 }; | 27 }; |
| 27 | 28 |
| 28 var xhtml_ns = "http://www.w3.org/1999/xhtml"; | 29 var xhtml_ns = "http://www.w3.org/1999/xhtml"; |
| 29 | 30 |
| 30 /* | 31 /* |
| 31 * TestEnvironment is an abstraction for the environment in which the test | 32 * TestEnvironment is an abstraction for the environment in which the test |
| 32 * harness is used. Each implementation of a test environment has to provide | 33 * harness is used. Each implementation of a test environment has to provide |
| 33 * the following interface: | 34 * the following interface: |
| 34 * | 35 * |
| 35 * interface TestEnvironment { | 36 * interface TestEnvironment { |
| (...skipping 21 matching lines...) Expand all Loading... |
| 57 * test results are displayed in a table. Any parent windows receive | 58 * test results are displayed in a table. Any parent windows receive |
| 58 * callbacks or messages via postMessage() when test events occur. See | 59 * callbacks or messages via postMessage() when test events occur. See |
| 59 * apisample11.html and apisample12.html. | 60 * apisample11.html and apisample12.html. |
| 60 */ | 61 */ |
| 61 function WindowTestEnvironment() { | 62 function WindowTestEnvironment() { |
| 62 this.name_counter = 0; | 63 this.name_counter = 0; |
| 63 this.window_cache = null; | 64 this.window_cache = null; |
| 64 this.output_handler = null; | 65 this.output_handler = null; |
| 65 this.all_loaded = false; | 66 this.all_loaded = false; |
| 66 var this_obj = this; | 67 var this_obj = this; |
| 68 this.message_events = []; |
| 69 |
| 70 this.message_functions = { |
| 71 start: [add_start_callback, remove_start_callback, |
| 72 function (properties) { |
| 73 this_obj._dispatch("start_callback", [properties], |
| 74 {type: "start", properties: propertie
s}); |
| 75 }], |
| 76 |
| 77 test_state: [add_test_state_callback, remove_test_state_callback, |
| 78 function(test) { |
| 79 this_obj._dispatch("test_state_callback", [test], |
| 80 {type: "test_state", |
| 81 test: test.structured_clone()})
; |
| 82 }], |
| 83 result: [add_result_callback, remove_result_callback, |
| 84 function (test) { |
| 85 this_obj.output_handler.show_status(); |
| 86 this_obj._dispatch("result_callback", [test], |
| 87 {type: "result", |
| 88 test: test.structured_clone()}); |
| 89 }], |
| 90 completion: [add_completion_callback, remove_completion_callback, |
| 91 function (tests, harness_status) { |
| 92 var cloned_tests = map(tests, function(test) { |
| 93 return test.structured_clone(); |
| 94 }); |
| 95 this_obj._dispatch("completion_callback", [tests, h
arness_status], |
| 96 {type: "complete", |
| 97 tests: cloned_tests, |
| 98 status: harness_status.structur
ed_clone()}); |
| 99 }] |
| 100 } |
| 101 |
| 67 on_event(window, 'load', function() { | 102 on_event(window, 'load', function() { |
| 68 this_obj.all_loaded = true; | 103 this_obj.all_loaded = true; |
| 69 }); | 104 }); |
| 70 } | 105 } |
| 71 | 106 |
| 72 WindowTestEnvironment.prototype._dispatch = function(selector, callback_args
, message_arg) { | 107 WindowTestEnvironment.prototype._dispatch = function(selector, callback_args
, message_arg) { |
| 73 this._forEach_windows( | 108 this._forEach_windows( |
| 74 function(w, same_origin) { | 109 function(w, same_origin) { |
| 75 if (same_origin) { | 110 if (same_origin) { |
| 76 try { | 111 try { |
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 143 function(a) { | 178 function(a) { |
| 144 callback.apply(null, a); | 179 callback.apply(null, a); |
| 145 }); | 180 }); |
| 146 }; | 181 }; |
| 147 | 182 |
| 148 WindowTestEnvironment.prototype.on_tests_ready = function() { | 183 WindowTestEnvironment.prototype.on_tests_ready = function() { |
| 149 var output = new Output(); | 184 var output = new Output(); |
| 150 this.output_handler = output; | 185 this.output_handler = output; |
| 151 | 186 |
| 152 var this_obj = this; | 187 var this_obj = this; |
| 188 |
| 153 add_start_callback(function (properties) { | 189 add_start_callback(function (properties) { |
| 154 this_obj.output_handler.init(properties); | 190 this_obj.output_handler.init(properties); |
| 155 this_obj._dispatch("start_callback", [properties], | |
| 156 { type: "start", properties: properties }); | |
| 157 }); | 191 }); |
| 192 |
| 158 add_test_state_callback(function(test) { | 193 add_test_state_callback(function(test) { |
| 159 this_obj.output_handler.show_status(); | 194 this_obj.output_handler.show_status(); |
| 160 this_obj._dispatch("test_state_callback", [test], | |
| 161 { type: "test_state", test: test.structured_clone
() }); | |
| 162 }); | 195 }); |
| 196 |
| 163 add_result_callback(function (test) { | 197 add_result_callback(function (test) { |
| 164 this_obj.output_handler.show_status(); | 198 this_obj.output_handler.show_status(); |
| 165 this_obj._dispatch("result_callback", [test], | |
| 166 { type: "result", test: test.structured_clone() }
); | |
| 167 }); | 199 }); |
| 200 |
| 168 add_completion_callback(function (tests, harness_status) { | 201 add_completion_callback(function (tests, harness_status) { |
| 169 this_obj.output_handler.show_results(tests, harness_status); | 202 this_obj.output_handler.show_results(tests, harness_status); |
| 170 var cloned_tests = map(tests, function(test) { return test.structure
d_clone(); }); | |
| 171 this_obj._dispatch("completion_callback", [tests, harness_status], | |
| 172 { type: "complete", tests: cloned_tests, | |
| 173 status: harness_status.structured_clone() }); | |
| 174 }); | 203 }); |
| 204 this.setup_messages(settings.message_events); |
| 175 }; | 205 }; |
| 176 | 206 |
| 207 WindowTestEnvironment.prototype.setup_messages = function(new_events) { |
| 208 var this_obj = this; |
| 209 forEach(settings.message_events, function(x) { |
| 210 var current_dispatch = this_obj.message_events.indexOf(x) !== -1; |
| 211 var new_dispatch = new_events.indexOf(x) !== -1; |
| 212 if (!current_dispatch && new_dispatch) { |
| 213 this_obj.message_functions[x][0](this_obj.message_functions[x][2
]); |
| 214 } else if (current_dispatch && !new_dispatch) { |
| 215 this_obj.message_functions[x][1](this_obj.message_functions[x][2
]); |
| 216 } |
| 217 }); |
| 218 this.message_events = new_events; |
| 219 } |
| 220 |
| 177 WindowTestEnvironment.prototype.next_default_test_name = function() { | 221 WindowTestEnvironment.prototype.next_default_test_name = function() { |
| 178 //Don't use document.title to work around an Opera bug in XHTML document
s | 222 //Don't use document.title to work around an Opera bug in XHTML document
s |
| 179 var title = document.getElementsByTagName("title")[0]; | 223 var title = document.getElementsByTagName("title")[0]; |
| 180 var prefix = (title && title.firstChild && title.firstChild.data) || "Un
titled"; | 224 var prefix = (title && title.firstChild && title.firstChild.data) || "Un
titled"; |
| 181 var suffix = this.name_counter > 0 ? " " + this.name_counter : ""; | 225 var suffix = this.name_counter > 0 ? " " + this.name_counter : ""; |
| 182 this.name_counter++; | 226 this.name_counter++; |
| 183 return prefix + suffix; | 227 return prefix + suffix; |
| 184 }; | 228 }; |
| 185 | 229 |
| 186 WindowTestEnvironment.prototype.on_new_harness_properties = function(propert
ies) { | 230 WindowTestEnvironment.prototype.on_new_harness_properties = function(propert
ies) { |
| 187 this.output_handler.setup(properties); | 231 this.output_handler.setup(properties); |
| 232 if (properties.hasOwnProperty("message_events")) { |
| 233 this.setup_messages(properties.message_events); |
| 234 } |
| 188 }; | 235 }; |
| 189 | 236 |
| 190 WindowTestEnvironment.prototype.add_on_loaded_callback = function(callback)
{ | 237 WindowTestEnvironment.prototype.add_on_loaded_callback = function(callback)
{ |
| 191 on_event(window, 'load', callback); | 238 on_event(window, 'load', callback); |
| 192 }; | 239 }; |
| 193 | 240 |
| 194 WindowTestEnvironment.prototype.test_timeout = function() { | 241 WindowTestEnvironment.prototype.test_timeout = function() { |
| 195 var metas = document.getElementsByTagName("meta"); | 242 var metas = document.getElementsByTagName("meta"); |
| 196 for (var i = 0; i < metas.length; i++) { | 243 for (var i = 0; i < metas.length; i++) { |
| 197 if (metas[i].name == "timeout") { | 244 if (metas[i].name == "timeout") { |
| (...skipping 163 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 361 * inside a service worker. | 408 * inside a service worker. |
| 362 */ | 409 */ |
| 363 function ServiceWorkerTestEnvironment() { | 410 function ServiceWorkerTestEnvironment() { |
| 364 WorkerTestEnvironment.call(this); | 411 WorkerTestEnvironment.call(this); |
| 365 this.all_loaded = false; | 412 this.all_loaded = false; |
| 366 this.on_loaded_callback = null; | 413 this.on_loaded_callback = null; |
| 367 var this_obj = this; | 414 var this_obj = this; |
| 368 self.addEventListener("message", | 415 self.addEventListener("message", |
| 369 function(event) { | 416 function(event) { |
| 370 if (event.data.type && event.data.type === "connect") { | 417 if (event.data.type && event.data.type === "connect") { |
| 371 this_obj._add_message_port(event.ports[0]); | 418 if (event.ports && event.ports[0]) { |
| 372 event.ports[0].start(); | 419 // If a MessageChannel was passed, then use it to |
| 420 // send results back to the main window. This |
| 421 // allows the tests to work even if the browser |
| 422 // does not fully support MessageEvent.source in |
| 423 // ServiceWorkers yet. |
| 424 this_obj._add_message_port(event.ports[0]); |
| 425 event.ports[0].start(); |
| 426 } else { |
| 427 // If there is no MessageChannel, then attempt to |
| 428 // use the MessageEvent.source to send results |
| 429 // back to the main window. |
| 430 this_obj._add_message_port(event.source); |
| 431 } |
| 373 } | 432 } |
| 374 }); | 433 }); |
| 375 | 434 |
| 376 // The oninstall event is received after the service worker script and | 435 // The oninstall event is received after the service worker script and |
| 377 // all imported scripts have been fetched and executed. It's the | 436 // all imported scripts have been fetched and executed. It's the |
| 378 // equivalent of an onload event for a document. All tests should have | 437 // equivalent of an onload event for a document. All tests should have |
| 379 // been added by the time this event is received, thus it's not | 438 // been added by the time this event is received, thus it's not |
| 380 // necessary to wait until the onactivate event. | 439 // necessary to wait until the onactivate event. |
| 381 on_event(self, "install", | 440 on_event(self, "install", |
| 382 function(event) { | 441 function(event) { |
| (...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 451 properties = properties ? properties : {}; | 510 properties = properties ? properties : {}; |
| 452 var test_obj = new Test(test_name, properties); | 511 var test_obj = new Test(test_name, properties); |
| 453 if (func) { | 512 if (func) { |
| 454 test_obj.step(func, test_obj, test_obj); | 513 test_obj.step(func, test_obj, test_obj); |
| 455 } | 514 } |
| 456 return test_obj; | 515 return test_obj; |
| 457 } | 516 } |
| 458 | 517 |
| 459 function promise_test(func, name, properties) { | 518 function promise_test(func, name, properties) { |
| 460 var test = async_test(name, properties); | 519 var test = async_test(name, properties); |
| 461 Promise.resolve(test.step(func, test, test)) | 520 // If there is no promise tests queue make one. |
| 462 .then( | 521 test.step(function() { |
| 463 function() { | 522 if (!tests.promise_tests) { |
| 464 test.done(); | 523 tests.promise_tests = Promise.resolve(); |
| 465 }) | 524 } |
| 466 .catch(test.step_func( | 525 }); |
| 467 function(value) { | 526 tests.promise_tests = tests.promise_tests.then(function() { |
| 468 if (value instanceof AssertionError) { | 527 return Promise.resolve(test.step(func, test, test)) |
| 469 throw value; | 528 .then( |
| 470 } | 529 function() { |
| 471 assert(false, "promise_test", null, | 530 test.done(); |
| 472 "Unhandled rejection with value: ${value}", {value:va
lue}); | 531 }) |
| 473 })); | 532 .catch(test.step_func( |
| 533 function(value) { |
| 534 if (value instanceof AssertionError) { |
| 535 throw value; |
| 536 } |
| 537 assert(false, "promise_test", null, |
| 538 "Unhandled rejection with value: ${value}", {valu
e:value}); |
| 539 })); |
| 540 }); |
| 474 } | 541 } |
| 475 | 542 |
| 476 function promise_rejects(test, expected, promise) { | 543 function promise_rejects(test, expected, promise) { |
| 477 return promise.then(test.unreached_func("Should have rejected.")).catch(
function(e) { | 544 return promise.then(test.unreached_func("Should have rejected.")).catch(
function(e) { |
| 478 assert_throws(expected, function() { throw e }); | 545 assert_throws(expected, function() { throw e }); |
| 479 }); | 546 }); |
| 480 } | 547 } |
| 481 | 548 |
| 482 /** | 549 /** |
| 483 * This constructor helper allows DOM events to be handled using Promises, | 550 * This constructor helper allows DOM events to be handled using Promises, |
| (...skipping 945 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1429 }); | 1496 }); |
| 1430 this.index = null; | 1497 this.index = null; |
| 1431 this.phase = this.phases.INITIAL; | 1498 this.phase = this.phases.INITIAL; |
| 1432 this.update_state_from(clone); | 1499 this.update_state_from(clone); |
| 1433 tests.push(this); | 1500 tests.push(this); |
| 1434 } | 1501 } |
| 1435 | 1502 |
| 1436 RemoteTest.prototype.structured_clone = function() { | 1503 RemoteTest.prototype.structured_clone = function() { |
| 1437 var clone = {}; | 1504 var clone = {}; |
| 1438 Object.keys(this).forEach( | 1505 Object.keys(this).forEach( |
| 1439 function(key) { | 1506 (function(key) { |
| 1440 if (typeof(this[key]) === "object") { | 1507 if (typeof(this[key]) === "object") { |
| 1441 clone[key] = merge({}, this[key]); | 1508 clone[key] = merge({}, this[key]); |
| 1442 } else { | 1509 } else { |
| 1443 clone[key] = this[key]; | 1510 clone[key] = this[key]; |
| 1444 } | 1511 } |
| 1445 }); | 1512 }).bind(this)); |
| 1446 clone.phases = merge({}, this.phases); | 1513 clone.phases = merge({}, this.phases); |
| 1447 return clone; | 1514 return clone; |
| 1448 }; | 1515 }; |
| 1449 | 1516 |
| 1450 RemoteTest.prototype.cleanup = function() {}; | 1517 RemoteTest.prototype.cleanup = function() {}; |
| 1451 RemoteTest.prototype.phases = Test.prototype.phases; | 1518 RemoteTest.prototype.phases = Test.prototype.phases; |
| 1452 RemoteTest.prototype.update_state_from = function(clone) { | 1519 RemoteTest.prototype.update_state_from = function(clone) { |
| 1453 this.status = clone.status; | 1520 this.status = clone.status; |
| 1454 this.message = clone.message; | 1521 this.message = clone.message; |
| 1455 this.stack = clone.stack; | 1522 this.stack = clone.stack; |
| (...skipping 13 matching lines...) Expand all Loading... |
| 1469 function RemoteWorker(worker) { | 1536 function RemoteWorker(worker) { |
| 1470 this.running = true; | 1537 this.running = true; |
| 1471 this.tests = new Array(); | 1538 this.tests = new Array(); |
| 1472 | 1539 |
| 1473 var this_obj = this; | 1540 var this_obj = this; |
| 1474 worker.onerror = function(error) { this_obj.worker_error(error); }; | 1541 worker.onerror = function(error) { this_obj.worker_error(error); }; |
| 1475 | 1542 |
| 1476 var message_port; | 1543 var message_port; |
| 1477 | 1544 |
| 1478 if (is_service_worker(worker)) { | 1545 if (is_service_worker(worker)) { |
| 1479 // The ServiceWorker's implicit MessagePort is currently not | 1546 if (window.MessageChannel) { |
| 1480 // reliably accessible from the ServiceWorkerGlobalScope due to | 1547 // The ServiceWorker's implicit MessagePort is currently not |
| 1481 // Blink setting MessageEvent.source to null for messages sent via | 1548 // reliably accessible from the ServiceWorkerGlobalScope due to |
| 1482 // ServiceWorker.postMessage(). Until that's resolved, create an | 1549 // Blink setting MessageEvent.source to null for messages sent |
| 1483 // explicit MessageChannel and pass one end to the worker. | 1550 // via ServiceWorker.postMessage(). Until that's resolved, |
| 1484 var message_channel = new MessageChannel(); | 1551 // create an explicit MessageChannel and pass one end to the |
| 1485 message_port = message_channel.port1; | 1552 // worker. |
| 1486 message_port.start(); | 1553 var message_channel = new MessageChannel(); |
| 1487 worker.postMessage({type: "connect"}, [message_channel.port2]); | 1554 message_port = message_channel.port1; |
| 1555 message_port.start(); |
| 1556 worker.postMessage({type: "connect"}, [message_channel.port2]); |
| 1557 } else { |
| 1558 // If MessageChannel is not available, then try the |
| 1559 // ServiceWorker.postMessage() approach using MessageEvent.sourc
e |
| 1560 // on the other end. |
| 1561 message_port = navigator.serviceWorker; |
| 1562 worker.postMessage({type: "connect"}); |
| 1563 } |
| 1488 } else if (is_shared_worker(worker)) { | 1564 } else if (is_shared_worker(worker)) { |
| 1489 message_port = worker.port; | 1565 message_port = worker.port; |
| 1490 } else { | 1566 } else { |
| 1491 message_port = worker; | 1567 message_port = worker; |
| 1492 } | 1568 } |
| 1493 | 1569 |
| 1494 // Keeping a reference to the worker until worker_done() is seen | 1570 // Keeping a reference to the worker until worker_done() is seen |
| 1495 // prevents the Worker object and its MessageChannel from going away | 1571 // prevents the Worker object and its MessageChannel from going away |
| 1496 // before all the messages are dispatched. | 1572 // before all the messages are dispatched. |
| 1497 this.worker = worker; | 1573 this.worker = worker; |
| (...skipping 332 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1830 expose(timeout, 'timeout'); | 1906 expose(timeout, 'timeout'); |
| 1831 | 1907 |
| 1832 function add_start_callback(callback) { | 1908 function add_start_callback(callback) { |
| 1833 tests.start_callbacks.push(callback); | 1909 tests.start_callbacks.push(callback); |
| 1834 } | 1910 } |
| 1835 | 1911 |
| 1836 function add_test_state_callback(callback) { | 1912 function add_test_state_callback(callback) { |
| 1837 tests.test_state_callbacks.push(callback); | 1913 tests.test_state_callbacks.push(callback); |
| 1838 } | 1914 } |
| 1839 | 1915 |
| 1840 function add_result_callback(callback) | 1916 function add_result_callback(callback) { |
| 1841 { | |
| 1842 tests.test_done_callbacks.push(callback); | 1917 tests.test_done_callbacks.push(callback); |
| 1843 } | 1918 } |
| 1844 | 1919 |
| 1845 function add_completion_callback(callback) | 1920 function add_completion_callback(callback) { |
| 1846 { | 1921 tests.all_done_callbacks.push(callback); |
| 1847 tests.all_done_callbacks.push(callback); | |
| 1848 } | 1922 } |
| 1849 | 1923 |
| 1850 expose(add_start_callback, 'add_start_callback'); | 1924 expose(add_start_callback, 'add_start_callback'); |
| 1851 expose(add_test_state_callback, 'add_test_state_callback'); | 1925 expose(add_test_state_callback, 'add_test_state_callback'); |
| 1852 expose(add_result_callback, 'add_result_callback'); | 1926 expose(add_result_callback, 'add_result_callback'); |
| 1853 expose(add_completion_callback, 'add_completion_callback'); | 1927 expose(add_completion_callback, 'add_completion_callback'); |
| 1854 | 1928 |
| 1929 function remove(array, item) { |
| 1930 var index = array.indexOf(item); |
| 1931 if (index > -1) { |
| 1932 array.splice(index, 1); |
| 1933 } |
| 1934 } |
| 1935 |
| 1936 function remove_start_callback(callback) { |
| 1937 remove(tests.start_callbacks, callback); |
| 1938 } |
| 1939 |
| 1940 function remove_test_state_callback(callback) { |
| 1941 remove(tests.test_state_callbacks, callback); |
| 1942 } |
| 1943 |
| 1944 function remove_result_callback(callback) { |
| 1945 remove(tests.test_done_callbacks, callback); |
| 1946 } |
| 1947 |
| 1948 function remove_completion_callback(callback) { |
| 1949 remove(tests.all_done_callbacks, callback); |
| 1950 } |
| 1951 |
| 1855 /* | 1952 /* |
| 1856 * Output listener | 1953 * Output listener |
| 1857 */ | 1954 */ |
| 1858 | 1955 |
| 1859 function Output() { | 1956 function Output() { |
| 1860 this.output_document = document; | 1957 this.output_document = document; |
| 1861 this.output_node = null; | 1958 this.output_node = null; |
| 1862 this.enabled = settings.output; | 1959 this.enabled = settings.output; |
| 1863 this.phase = this.INITIAL; | 1960 this.phase = this.INITIAL; |
| 1864 } | 1961 } |
| (...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1952 var log = this.output_node; | 2049 var log = this.output_node; |
| 1953 if (!log) { | 2050 if (!log) { |
| 1954 return; | 2051 return; |
| 1955 } | 2052 } |
| 1956 var output_document = this.output_document; | 2053 var output_document = this.output_document; |
| 1957 | 2054 |
| 1958 while (log.lastChild) { | 2055 while (log.lastChild) { |
| 1959 log.removeChild(log.lastChild); | 2056 log.removeChild(log.lastChild); |
| 1960 } | 2057 } |
| 1961 | 2058 |
| 1962 var script_prefix = null; | 2059 var harness_url = get_harness_url(); |
| 1963 var scripts = document.getElementsByTagName("script"); | 2060 if (harness_url !== null) { |
| 1964 for (var i = 0; i < scripts.length; i++) { | |
| 1965 var src; | |
| 1966 if (scripts[i].src) { | |
| 1967 src = scripts[i].src; | |
| 1968 } else if (scripts[i].href) { | |
| 1969 //SVG case | |
| 1970 src = scripts[i].href.baseVal; | |
| 1971 } | |
| 1972 | |
| 1973 var matches = src && src.match(/^(.*\/|)testharness\.js$/); | |
| 1974 if (matches) { | |
| 1975 script_prefix = matches[1]; | |
| 1976 break; | |
| 1977 } | |
| 1978 } | |
| 1979 | |
| 1980 if (script_prefix !== null) { | |
| 1981 var stylesheet = output_document.createElementNS(xhtml_ns, "link"); | 2061 var stylesheet = output_document.createElementNS(xhtml_ns, "link"); |
| 1982 stylesheet.setAttribute("rel", "stylesheet"); | 2062 stylesheet.setAttribute("rel", "stylesheet"); |
| 1983 stylesheet.setAttribute("href", script_prefix + "testharness.css"); | 2063 stylesheet.setAttribute("href", harness_url + "testharness.css"); |
| 1984 var heads = output_document.getElementsByTagName("head"); | 2064 var heads = output_document.getElementsByTagName("head"); |
| 1985 if (heads.length) { | 2065 if (heads.length) { |
| 1986 heads[0].appendChild(stylesheet); | 2066 heads[0].appendChild(stylesheet); |
| 1987 } | 2067 } |
| 1988 } | 2068 } |
| 1989 | 2069 |
| 1990 var status_text_harness = {}; | 2070 var status_text_harness = {}; |
| 1991 status_text_harness[harness_status.OK] = "OK"; | 2071 status_text_harness[harness_status.OK] = "OK"; |
| 1992 status_text_harness[harness_status.ERROR] = "Error"; | 2072 status_text_harness[harness_status.ERROR] = "Error"; |
| 1993 status_text_harness[harness_status.TIMEOUT] = "Timeout"; | 2073 status_text_harness[harness_status.TIMEOUT] = "Timeout"; |
| (...skipping 334 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2328 | 2408 |
| 2329 function AssertionError(message) | 2409 function AssertionError(message) |
| 2330 { | 2410 { |
| 2331 this.message = message; | 2411 this.message = message; |
| 2332 this.stack = this.get_stack(); | 2412 this.stack = this.get_stack(); |
| 2333 } | 2413 } |
| 2334 | 2414 |
| 2335 AssertionError.prototype = Object.create(Error.prototype); | 2415 AssertionError.prototype = Object.create(Error.prototype); |
| 2336 | 2416 |
| 2337 AssertionError.prototype.get_stack = function() { | 2417 AssertionError.prototype.get_stack = function() { |
| 2338 var lines = new Error().stack.split("\n"); | 2418 var stack = new Error().stack; |
| 2339 var rv = []; | 2419 // IE11 does not initialize 'Error.stack' until the object is thrown. |
| 2340 var re = /\/resources\/testharness\.js/; | 2420 if (!stack) { |
| 2421 try { |
| 2422 throw new Error(); |
| 2423 } catch (e) { |
| 2424 stack = e.stack; |
| 2425 } |
| 2426 } |
| 2427 |
| 2428 var lines = stack.split("\n"); |
| 2429 |
| 2430 // Create a pattern to match stack frames originating within testharness
.js. These include the |
| 2431 // script URL, followed by the line/col (e.g., '/resources/testharness.j
s:120:21'). |
| 2432 var re = new RegExp((get_script_url() || "\\btestharness.js") + ":\\d+:\
\d+"); |
| 2433 |
| 2434 // Some browsers include a preamble that specifies the type of the error
object. Skip this by |
| 2435 // advancing until we find the first stack frame originating from testha
rness.js. |
| 2341 var i = 0; | 2436 var i = 0; |
| 2342 // Fire remove any preamble that doesn't match the regexp | 2437 while (!re.test(lines[i]) && i < lines.length) { |
| 2343 while (!re.test(lines[i])) { | 2438 i++; |
| 2344 i++ | |
| 2345 } | 2439 } |
| 2346 // Then remove top frames in testharness.js itself | 2440 |
| 2347 while (re.test(lines[i])) { | 2441 // Then skip the top frames originating from testharness.js to begin the
stack at the test code. |
| 2348 i++ | 2442 while (re.test(lines[i]) && i < lines.length) { |
| 2443 i++; |
| 2349 } | 2444 } |
| 2445 |
| 2446 // Paranoid check that we didn't skip all frames. If so, return the ori
ginal stack unmodified. |
| 2447 if (i >= lines.length) { |
| 2448 return stack; |
| 2449 } |
| 2450 |
| 2350 return lines.slice(i).join("\n"); | 2451 return lines.slice(i).join("\n"); |
| 2351 } | 2452 } |
| 2352 | 2453 |
| 2353 function make_message(function_name, description, error, substitutions) | 2454 function make_message(function_name, description, error, substitutions) |
| 2354 { | 2455 { |
| 2355 for (var p in substitutions) { | 2456 for (var p in substitutions) { |
| 2356 if (substitutions.hasOwnProperty(p)) { | 2457 if (substitutions.hasOwnProperty(p)) { |
| 2357 substitutions[p] = format_value(substitutions[p]); | 2458 substitutions[p] = format_value(substitutions[p]); |
| 2358 } | 2459 } |
| 2359 } | 2460 } |
| (...skipping 27 matching lines...) Expand all Loading... |
| 2387 } | 2488 } |
| 2388 } | 2489 } |
| 2389 return rv; | 2490 return rv; |
| 2390 } | 2491 } |
| 2391 | 2492 |
| 2392 function extend(array, items) | 2493 function extend(array, items) |
| 2393 { | 2494 { |
| 2394 Array.prototype.push.apply(array, items); | 2495 Array.prototype.push.apply(array, items); |
| 2395 } | 2496 } |
| 2396 | 2497 |
| 2397 function forEach (array, callback, thisObj) | 2498 function forEach(array, callback, thisObj) |
| 2398 { | 2499 { |
| 2399 for (var i = 0; i < array.length; i++) { | 2500 for (var i = 0; i < array.length; i++) { |
| 2400 if (array.hasOwnProperty(i)) { | 2501 if (array.hasOwnProperty(i)) { |
| 2401 callback.call(thisObj, array[i], i, array); | 2502 callback.call(thisObj, array[i], i, array); |
| 2402 } | 2503 } |
| 2403 } | 2504 } |
| 2404 } | 2505 } |
| 2405 | 2506 |
| 2406 function merge(a,b) | 2507 function merge(a,b) |
| 2407 { | 2508 { |
| (...skipping 23 matching lines...) Expand all Loading... |
| 2431 | 2532 |
| 2432 function is_same_origin(w) { | 2533 function is_same_origin(w) { |
| 2433 try { | 2534 try { |
| 2434 'random_prop' in w; | 2535 'random_prop' in w; |
| 2435 return true; | 2536 return true; |
| 2436 } catch (e) { | 2537 } catch (e) { |
| 2437 return false; | 2538 return false; |
| 2438 } | 2539 } |
| 2439 } | 2540 } |
| 2440 | 2541 |
| 2542 /** Returns the 'src' URL of the first <script> tag in the page to include t
he file 'testharness.js'. */ |
| 2543 function get_script_url() |
| 2544 { |
| 2545 if (!('document' in self)) { |
| 2546 return undefined; |
| 2547 } |
| 2548 |
| 2549 var scripts = document.getElementsByTagName("script"); |
| 2550 for (var i = 0; i < scripts.length; i++) { |
| 2551 var src; |
| 2552 if (scripts[i].src) { |
| 2553 src = scripts[i].src; |
| 2554 } else if (scripts[i].href) { |
| 2555 //SVG case |
| 2556 src = scripts[i].href.baseVal; |
| 2557 } |
| 2558 |
| 2559 var matches = src && src.match(/^(.*\/|)testharness\.js$/); |
| 2560 if (matches) { |
| 2561 return src; |
| 2562 } |
| 2563 } |
| 2564 return undefined; |
| 2565 } |
| 2566 |
| 2567 /** Returns the URL path at which the files for testharness.js are assumed t
o reside (e.g., '/resources/'). |
| 2568 The path is derived from inspecting the 'src' of the <script> tag that i
ncluded 'testharness.js'. */ |
| 2569 function get_harness_url() |
| 2570 { |
| 2571 var script_url = get_script_url(); |
| 2572 |
| 2573 // Exclude the 'testharness.js' file from the returned path, but '+ 1' t
o include the trailing slash. |
| 2574 return script_url ? script_url.slice(0, script_url.lastIndexOf('/') + 1)
: undefined; |
| 2575 } |
| 2576 |
| 2441 function supports_post_message(w) | 2577 function supports_post_message(w) |
| 2442 { | 2578 { |
| 2443 var supports; | 2579 var supports; |
| 2444 var type; | 2580 var type; |
| 2445 // Given IE implements postMessage across nested iframes but not across | 2581 // Given IE implements postMessage across nested iframes but not across |
| 2446 // windows or tabs, you can't infer cross-origin communication from the
presence | 2582 // windows or tabs, you can't infer cross-origin communication from the
presence |
| 2447 // of postMessage on the current window object only. | 2583 // of postMessage on the current window object only. |
| 2448 // | 2584 // |
| 2449 // Touching the postMessage prop on a window can throw if the window is | 2585 // Touching the postMessage prop on a window can throw if the window is |
| 2450 // not from the same origin AND post message is not supported in that | 2586 // not from the same origin AND post message is not supported in that |
| 2451 // browser. So just doing an existence test here won't do, you also need | 2587 // browser. So just doing an existence test here won't do, you also need |
| 2452 // to wrap it in a try..cacth block. | 2588 // to wrap it in a try..cacth block. |
| 2453 try { | 2589 try { |
| 2454 type = typeof w.postMessage; | 2590 type = typeof w.postMessage; |
| 2455 if (type === "function") { | 2591 if (type === "function") { |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2495 tests.status.status = tests.status.ERROR; | 2631 tests.status.status = tests.status.ERROR; |
| 2496 tests.status.message = e.message; | 2632 tests.status.message = e.message; |
| 2497 tests.status.stack = e.stack; | 2633 tests.status.stack = e.stack; |
| 2498 } | 2634 } |
| 2499 }); | 2635 }); |
| 2500 | 2636 |
| 2501 test_environment.on_tests_ready(); | 2637 test_environment.on_tests_ready(); |
| 2502 | 2638 |
| 2503 })(); | 2639 })(); |
| 2504 // vim: set expandtab shiftwidth=4 tabstop=4: | 2640 // vim: set expandtab shiftwidth=4 tabstop=4: |
| OLD | NEW |