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 |