| OLD | NEW |
| (Empty) |
| 1 /** | |
| 2 * Copyright 2013 Google Inc. All Rights Reserved. | |
| 3 * | |
| 4 * Licensed under the Apache License, Version 2.0 (the "License"); | |
| 5 * you may not use this file except in compliance with the License. | |
| 6 * You may obtain a copy of the License at | |
| 7 * | |
| 8 * http://www.apache.org/licenses/LICENSE-2.0 | |
| 9 * | |
| 10 * Unless required by applicable law or agreed to in writing, software | |
| 11 * distributed under the License is distributed on an "AS IS" BASIS, | |
| 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 13 * See the License for the specific language governing permissions and | |
| 14 * limitations under the License. | |
| 15 */ | |
| 16 'use strict'; | |
| 17 | |
| 18 (function() { | |
| 19 | |
| 20 var log_element = document.createElement('pre'); | |
| 21 log_element.id = 'debug'; | |
| 22 | |
| 23 var log_element_parent = setInterval(function() { | |
| 24 if (log_element.parentNode == null && document.body != null) { | |
| 25 document.body.appendChild(log_element); | |
| 26 clearInterval(log_element_parent); | |
| 27 } | |
| 28 }, 100); | |
| 29 | |
| 30 function log() { | |
| 31 | |
| 32 var output = []; | |
| 33 for (var i = 0; i < arguments.length; i++) { | |
| 34 if (typeof arguments[i] === "function") { | |
| 35 arguments[i] = '' + arguments[i]; | |
| 36 } | |
| 37 if (typeof arguments[i] === "string") { | |
| 38 var bits = arguments[i].replace(/\s*$/, '').split('\n'); | |
| 39 if (bits.length > 5) { | |
| 40 bits.splice(3, bits.length-5, '...'); | |
| 41 } | |
| 42 output.push(bits.join('\n')); | |
| 43 } else if (typeof arguments[i] === "object" && typeof arguments[i].name ===
"string") { | |
| 44 output.push('"'+arguments[i].name+'"'); | |
| 45 } else { | |
| 46 output.push(JSON.stringify(arguments[i], undefined, 2)); | |
| 47 } | |
| 48 output.push(' '); | |
| 49 } | |
| 50 log_element.appendChild(document.createTextNode(output.join('') + '\n')); | |
| 51 } | |
| 52 | |
| 53 var thisScript = document.querySelector("script[src$='bootstrap.js']"); | |
| 54 var coverageMode = Boolean(parent.window.__coverage__) || /coverage/.test(window
.location.hash); | |
| 55 | |
| 56 // Inherit these properties from the parent test-runner if any. | |
| 57 window.__resources__ = parent.window.__resources__ || {original: {}}; | |
| 58 window.__coverage__ = parent.window.__coverage__; | |
| 59 | |
| 60 function getSync(src) { | |
| 61 var xhr = new XMLHttpRequest(); | |
| 62 xhr.open('GET', src, false); | |
| 63 xhr.send(); | |
| 64 if (xhr.responseCode > 400) { | |
| 65 console.error('Error loading ' + src); | |
| 66 return ''; | |
| 67 } | |
| 68 return xhr.responseText; | |
| 69 } | |
| 70 | |
| 71 function loadScript(src, options) { | |
| 72 // Add changing parameter to prevent script caching. | |
| 73 options = options || {coverage: true}; | |
| 74 if (window.__resources__[src]) { | |
| 75 document.write('<script type="text/javascript">eval(window.__resources__["'+
src+'"]);</script>'); | |
| 76 } else if (coverageMode && options.coverage) { | |
| 77 instrument(src); | |
| 78 loadScript(src); | |
| 79 } else { | |
| 80 if (!inExploreMode()) { | |
| 81 src += '?' + getCacheBuster(); | |
| 82 } | |
| 83 document.write('<script type="text/javascript" src="'+ src + '"></script>'); | |
| 84 } | |
| 85 } | |
| 86 | |
| 87 function loadCSS(src) { | |
| 88 document.write('<link rel="stylesheet" type="text/css" href="' + src + '">'); | |
| 89 } | |
| 90 | |
| 91 function forEach(array, callback, thisObj) { | |
| 92 for (var i=0; i < array.length; i++) { | |
| 93 if (array.hasOwnProperty(i)) { | |
| 94 callback.call(thisObj, array[i], i, array); | |
| 95 } | |
| 96 } | |
| 97 } | |
| 98 | |
| 99 function hasFlag(flag) { | |
| 100 return thisScript && thisScript.getAttribute(flag) !== null; | |
| 101 } | |
| 102 | |
| 103 function testType() { | |
| 104 var p = location.pathname; | |
| 105 p = p.replace(/^disabled-/, ''); | |
| 106 | |
| 107 var match = /(auto|impl|manual|unit)-test[^\\\/]*$/.exec(p); | |
| 108 return match ? match[1]: 'unknown'; | |
| 109 } | |
| 110 | |
| 111 function inExploreMode() { | |
| 112 return '#explore' == window.location.hash || window.location.hash.length == 0; | |
| 113 } | |
| 114 | |
| 115 /** | |
| 116 * Get a value for busting the cache. If we got given a cache buster, pass it | |
| 117 * along, otherwise generate a new one. | |
| 118 */ | |
| 119 var cacheBusterValue = '' + window.Date.now(); | |
| 120 function getCacheBuster() { | |
| 121 if (window.location.search.length > 0) | |
| 122 cacheBusterValue = window.location.search.substr(1, window.location.search.l
ength); | |
| 123 return cacheBusterValue; | |
| 124 } | |
| 125 | |
| 126 var instrumentationDepsLoaded = false; | |
| 127 /** | |
| 128 * Instrument the source at {@code location} and store it in | |
| 129 * {@code window.__resources__[name]}. | |
| 130 */ | |
| 131 function instrument(src) { | |
| 132 if (__resources__[src]) { | |
| 133 return; | |
| 134 } | |
| 135 if (!instrumentationDepsLoaded) { | |
| 136 instrumentationDepsLoaded = true; | |
| 137 (function() { | |
| 138 eval(getSync('../coverage/esprima/esprima.js')); | |
| 139 eval(getSync('../coverage/escodegen/escodegen.browser.js')); | |
| 140 eval(getSync('../coverage/istanbul/lib/instrumenter.js')); | |
| 141 }).call(window); | |
| 142 } | |
| 143 var js = getSync(src); | |
| 144 window.__resources__.original[src] = js; | |
| 145 var inst = window.__resources__[src] = new Instrumenter().instrumentSync(js, s
rc); | |
| 146 } | |
| 147 | |
| 148 var svg_namespace_uri = 'http://www.w3.org/2000/svg'; | |
| 149 | |
| 150 /** | |
| 151 * Figure out a useful name for an element. | |
| 152 * | |
| 153 * @param {Element} element Element to get the name for. | |
| 154 * | |
| 155 * @private | |
| 156 */ | |
| 157 function _element_name(element) { | |
| 158 if (element.id) { | |
| 159 return element.tagName.toLowerCase() + '#' + element.id; | |
| 160 } else { | |
| 161 return 'An anonymous ' + element.tagName.toLowerCase(); | |
| 162 } | |
| 163 } | |
| 164 | |
| 165 /** | |
| 166 * Get the style for a given element. | |
| 167 * | |
| 168 * @param {Array.<Object.<string, string>>|Object.<string, string>} style | |
| 169 * Either; | |
| 170 * * A list of dictionaries, each node returned is checked against the | |
| 171 * associated dictionary, or | |
| 172 * * A single dictionary, each node returned is checked against the | |
| 173 * given dictionary. | |
| 174 * Each dictionary should be of the form {style_name: style_value}. | |
| 175 * | |
| 176 * @private | |
| 177 */ | |
| 178 function _assert_style_get(style, i) { | |
| 179 if (typeof style[i] === 'undefined') { | |
| 180 return style; | |
| 181 } else { | |
| 182 return style[i]; | |
| 183 } | |
| 184 } | |
| 185 | |
| 186 /** | |
| 187 * Extract all the informative parts of a string. Ignores spacing, punctuation | |
| 188 * and other random extra characters. | |
| 189 */ | |
| 190 function _extract_important(input) { | |
| 191 var re = /([-+]?[0-9]+\.?[0-9]*(?:[eE][-+]?[0-9]+)?)|[A-Za-z%]+/g; | |
| 192 | |
| 193 var match; | |
| 194 var result = []; | |
| 195 while (match = re.exec(input)) { | |
| 196 var value = match[0]; | |
| 197 if (typeof match[1] != "undefined") { | |
| 198 value = Number(match[1]); | |
| 199 } | |
| 200 result.push(value); | |
| 201 } | |
| 202 return result; | |
| 203 } | |
| 204 window.assert_styles_extract_important = _extract_important; | |
| 205 | |
| 206 function AssertionError(message) { | |
| 207 this.message = message; | |
| 208 } | |
| 209 window.assert_styles_assertion_error = AssertionError; | |
| 210 | |
| 211 /** | |
| 212 * Asserts that a string is in the array of expected only comparing the | |
| 213 * important parts. Ignores spacing, punctuation and other random extra | |
| 214 * characters. | |
| 215 */ | |
| 216 function _assert_important_in_array(actual, expected, message) { | |
| 217 var actual_array = _extract_important(actual); | |
| 218 | |
| 219 var expected_array_array = []; | |
| 220 for (var i = 0; i < expected.length; i++) { | |
| 221 expected_array_array.push(_extract_important(expected[i])); | |
| 222 } | |
| 223 | |
| 224 var errors = []; | |
| 225 for (var i = 0; i < expected_array_array.length; i++) { | |
| 226 var expected_array = expected_array_array[i]; | |
| 227 | |
| 228 var element_errors = []; | |
| 229 if (actual_array.length != expected_array.length) { | |
| 230 element_errors.push('Number of elements don\'t match'); | |
| 231 } | |
| 232 | |
| 233 for (var j = 0; j < expected_array.length; j++) { | |
| 234 var actual = actual_array[j]; | |
| 235 var expected = expected_array[j]; | |
| 236 | |
| 237 try { | |
| 238 assert_equals(typeof actual, typeof expected); | |
| 239 | |
| 240 if (typeof actual === 'number') { | |
| 241 if (Math.abs(actual) < 1e-10) { | |
| 242 actual = 0; | |
| 243 } | |
| 244 actual = '' + actual.toPrecision(4); | |
| 245 } | |
| 246 if (typeof expected === 'number') { | |
| 247 if (Math.abs(expected) < 1e-10) { | |
| 248 expected = 0; | |
| 249 } | |
| 250 expected = '' + expected.toPrecision(4); | |
| 251 } | |
| 252 | |
| 253 assert_equals(actual, expected); | |
| 254 } catch (e) { | |
| 255 element_errors.push( | |
| 256 'Element ' + j + ' - ' + e.message); | |
| 257 } | |
| 258 } | |
| 259 | |
| 260 if (element_errors.length == 0) { | |
| 261 return; | |
| 262 } else { | |
| 263 errors.push( | |
| 264 ' Expectation ' + JSON.stringify(expected_array) + ' did not match\n'
+ | |
| 265 ' ' + element_errors.join('\n ')); | |
| 266 } | |
| 267 } | |
| 268 if (expected_array_array.length > 1) | |
| 269 errors.unshift(' ' + expected_array_array.length + ' possible expectations'
); | |
| 270 | |
| 271 errors.unshift(' Actual - ' + JSON.stringify(actual_array)); | |
| 272 if (typeof message !== 'undefined') { | |
| 273 errors.unshift(message); | |
| 274 } | |
| 275 throw new AssertionError(errors.join('\n')); | |
| 276 } | |
| 277 window.assert_styles_assert_important_in_array = _assert_important_in_array; | |
| 278 | |
| 279 /** | |
| 280 * asserts that actual has the same styles as the dictionary given by | |
| 281 * expected. | |
| 282 * | |
| 283 * @param {Element} object DOM node to check the styles on | |
| 284 * @param {Object.<string, string>} styles Dictionary of {style_name: style_valu
e} to check | |
| 285 * on the object. | |
| 286 * @param {String} description Human readable description of what you are | |
| 287 * trying to check. | |
| 288 * | |
| 289 * @private | |
| 290 */ | |
| 291 function _assert_style_element(object, style, description) { | |
| 292 if (typeof message == 'undefined') | |
| 293 description = ''; | |
| 294 | |
| 295 // Create an element of the same type as testing so the style can be applied | |
| 296 // from the test. This is so the css property (not the -webkit-does-something | |
| 297 // tag) can be read. | |
| 298 var reference_element = (object.namespaceURI == svg_namespace_uri) ? | |
| 299 document.createElementNS(svg_namespace_uri, object.nodeName) : | |
| 300 document.createElement(object.nodeName); | |
| 301 var computedObjectStyle = getComputedStyle(object, null); | |
| 302 for (var i = 0; i < computedObjectStyle.length; i++) { | |
| 303 var property = computedObjectStyle[i]; | |
| 304 reference_element.style.setProperty(property, | |
| 305 computedObjectStyle.getPropertyValue(property)); | |
| 306 } | |
| 307 reference_element.style.position = 'absolute'; | |
| 308 if (object.parentNode) { | |
| 309 object.parentNode.appendChild(reference_element); | |
| 310 } | |
| 311 | |
| 312 try { | |
| 313 // Apply the style | |
| 314 for (var prop_name in style) { | |
| 315 // If the passed in value is an element then grab its current style for | |
| 316 // that property | |
| 317 if (style[prop_name] instanceof HTMLElement || | |
| 318 style[prop_name] instanceof SVGElement) { | |
| 319 | |
| 320 var prop_value = getComputedStyle(style[prop_name], null)[prop_name]; | |
| 321 } else { | |
| 322 var prop_value = style[prop_name]; | |
| 323 } | |
| 324 | |
| 325 prop_value = '' + prop_value; | |
| 326 | |
| 327 var output_prop_name = _WebAnimationsTestingUtilities._prefixProperty(prop
_name); | |
| 328 | |
| 329 var is_svg = _WebAnimationsTestingUtilities._propertyIsSVGAttrib(prop_name
, object); | |
| 330 if (is_svg) { | |
| 331 reference_element.setAttribute(prop_name, prop_value); | |
| 332 | |
| 333 var current_style = object.attributes; | |
| 334 var target_style = reference_element.attributes; | |
| 335 } else { | |
| 336 reference_element.style[output_prop_name] = prop_value; | |
| 337 | |
| 338 var current_style = computedObjectStyle; | |
| 339 var target_style = getComputedStyle(reference_element, null); | |
| 340 | |
| 341 _assert_important_in_array( | |
| 342 prop_value, [reference_element.style[output_prop_name], target_style
[output_prop_name]], | |
| 343 'Tried to set the reference element\'s '+ output_prop_name + | |
| 344 ' to ' + JSON.stringify(prop_value) + | |
| 345 ' but neither the style' + | |
| 346 ' ' + JSON.stringify(reference_element.style[output_prop_name]) + | |
| 347 ' nor computedStyle ' + JSON.stringify(target) + | |
| 348 ' ended up matching requested value.'); | |
| 349 } | |
| 350 | |
| 351 if (prop_name == 'ctm') { | |
| 352 var ctm = object.getCTM(); | |
| 353 var curr = '{' + ctm.a + ', ' + | |
| 354 ctm.b + ', ' + ctm.c + ', ' + ctm.d + ', ' + | |
| 355 ctm.e + ', ' + ctm.f + '}'; | |
| 356 | |
| 357 var target = prop_value; | |
| 358 | |
| 359 } else if (is_svg) { | |
| 360 var target = target_style[prop_name].value; | |
| 361 var curr = current_style[prop_name].value; | |
| 362 } else { | |
| 363 var target = target_style[output_prop_name]; | |
| 364 var curr = current_style[output_prop_name]; | |
| 365 } | |
| 366 | |
| 367 var description_extra = '\n Property ' + prop_name; | |
| 368 if (prop_name != output_prop_name) | |
| 369 description_extra += '(actually ' + output_prop_name + ')'; | |
| 370 | |
| 371 _assert_important_in_array(curr, [target], description + description_extra
); | |
| 372 } | |
| 373 } finally { | |
| 374 if (reference_element.parentNode) { | |
| 375 reference_element.parentNode.removeChild(reference_element); | |
| 376 } | |
| 377 } | |
| 378 } | |
| 379 | |
| 380 /** | |
| 381 * asserts that elements in the list have given styles. | |
| 382 * | |
| 383 * @param {Array.<Element>} objects List of DOM nodes to check the styles on | |
| 384 * @param {Array.<Object.<string, string>>|Object.<string, string>} style | |
| 385 * See _assert_style_get for information. | |
| 386 * @param {String} description Human readable description of what you are | |
| 387 * trying to check. | |
| 388 * | |
| 389 * @private | |
| 390 */ | |
| 391 function _assert_style_element_list(objects, style, description) { | |
| 392 var error = ''; | |
| 393 forEach(objects, function(object, i) { | |
| 394 try { | |
| 395 _assert_style_element( | |
| 396 object, _assert_style_get(style, i), | |
| 397 description + ' ' + _element_name(object) | |
| 398 ); | |
| 399 } catch (e) { | |
| 400 if (error) { | |
| 401 error += '; '; | |
| 402 } | |
| 403 error += 'Element ' + _element_name(object) + ' at index ' + i + ' failed
' + e.message + '\n'; | |
| 404 } | |
| 405 }); | |
| 406 if (error) { | |
| 407 throw error; | |
| 408 } | |
| 409 } | |
| 410 | |
| 411 /** | |
| 412 * asserts that elements returned from a query selector have a list of styles. | |
| 413 * | |
| 414 * @param {string} qs A query selector to use to get the DOM nodes. | |
| 415 * @param {Array.<Object.<string, string>>|Object.<string, string>} style | |
| 416 * See _assert_style_get for information. | |
| 417 * @param {String} description Human readable description of what you are | |
| 418 * trying to check. | |
| 419 * | |
| 420 * @private | |
| 421 */ | |
| 422 function _assert_style_queryselector(qs, style, description) { | |
| 423 var objects = document.querySelectorAll(qs); | |
| 424 assert_true(objects.length > 0, description + | |
| 425 ' is invalid, no elements match query selector: ' + qs); | |
| 426 _assert_style_element_list(objects, style, description); | |
| 427 } | |
| 428 | |
| 429 /** | |
| 430 * asserts that elements returned from a query selector have a list of styles. | |
| 431 * | |
| 432 * Assert the element with id #hello is 100px wide; | |
| 433 * assert_styles(document.getElementById('hello'), {'width': '100px'}) | |
| 434 * assert_styles('#hello'), {'width': '100px'}) | |
| 435 * | |
| 436 * Assert all divs are 100px wide; | |
| 437 * assert_styles(document.getElementsByTagName('div'), {'width': '100px'}) | |
| 438 * assert_styles('div', {'width': '100px'}) | |
| 439 * | |
| 440 * Assert all objects with class 'red' are 100px wide; | |
| 441 * assert_styles(document.getElementsByClassName('red'), {'width': '100px'}) | |
| 442 * assert_styles('.red', {'width': '100px'}) | |
| 443 * | |
| 444 * Assert first div is 100px wide, second div is 200px wide; | |
| 445 * assert_styles(document.getElementsByTagName('div'), | |
| 446 * [{'width': '100px'}, {'width': '200px'}]) | |
| 447 * assert_styles('div', | |
| 448 * [{'width': '100px'}, {'width': '200px'}]) | |
| 449 * | |
| 450 * @param {string|Element|Array.<Element>} objects Either; | |
| 451 * * A query selector to use to get DOM nodes, | |
| 452 * * A DOM node. | |
| 453 * * A list of DOM nodes. | |
| 454 * @param {Array.<Object.<string, string>>|Object.<string, string>} style | |
| 455 * See _assert_style_get for information. | |
| 456 */ | |
| 457 function assert_styles(objects, style, description) { | |
| 458 switch (typeof objects) { | |
| 459 case 'string': | |
| 460 _assert_style_queryselector(objects, style, description); | |
| 461 break; | |
| 462 | |
| 463 case 'object': | |
| 464 if (objects instanceof Array || objects instanceof NodeList) { | |
| 465 _assert_style_element_list(objects, style, description); | |
| 466 } else if (objects instanceof Element) { | |
| 467 _assert_style_element(objects, style, description); | |
| 468 } else { | |
| 469 throw new Error('Expected Array, NodeList or Element but got ' + objects
); | |
| 470 } | |
| 471 break; | |
| 472 } | |
| 473 } | |
| 474 window.assert_styles = assert_styles; | |
| 475 | |
| 476 /** | |
| 477 * Schedule something to be called at a given time. | |
| 478 * | |
| 479 * @constructor | |
| 480 * @param {number} millis Milliseconds after start at which the callback should | |
| 481 * be called. | |
| 482 * @param {bool} autostart Auto something... | |
| 483 */ | |
| 484 function TestTimelineGroup(millis) { | |
| 485 this.millis = millis; | |
| 486 | |
| 487 /** | |
| 488 * @type {bool} | |
| 489 */ | |
| 490 this.autorun_ = false; | |
| 491 | |
| 492 /** | |
| 493 * @type {!Array.<function(): ?Object>} | |
| 494 */ | |
| 495 this.startCallbacks = null; | |
| 496 | |
| 497 /** | |
| 498 * Callbacks which are added after the timeline has started. We clear them | |
| 499 * when going backwards. | |
| 500 * | |
| 501 * @type {?Array.<function(): ?Object>} | |
| 502 */ | |
| 503 this.lateCallbacks = null; | |
| 504 | |
| 505 /** | |
| 506 * @type {Element} | |
| 507 */ | |
| 508 this.marker = document.createElement('img'); | |
| 509 /** | |
| 510 * @type {Element} | |
| 511 */ | |
| 512 this.info = document.createElement('div'); | |
| 513 | |
| 514 this.setup_(); | |
| 515 } | |
| 516 | |
| 517 TestTimelineGroup.prototype.setup_ = function() { | |
| 518 this.endTime_ = 0; | |
| 519 this.startCallbacks = new Array(); | |
| 520 this.lateCallbacks = null; | |
| 521 this.marker.innerHTML = ''; | |
| 522 this.info.innerHTML = ''; | |
| 523 }; | |
| 524 | |
| 525 /** | |
| 526 * Add a new callback to the event group | |
| 527 * | |
| 528 * @param {function(): ?Object} callback Callback given the currentTime of | |
| 529 * callback. | |
| 530 */ | |
| 531 TestTimelineGroup.prototype.add = function(callback) { | |
| 532 if (this.lateCallbacks === null) { | |
| 533 this.startCallbacks.unshift(callback); | |
| 534 } else { | |
| 535 this.lateCallbacks.unshift(callback); | |
| 536 } | |
| 537 | |
| 538 // Trim out extra 'function() { ... }' | |
| 539 var callbackString = callback.name; | |
| 540 // FIXME: This should probably unindent too.... | |
| 541 this.info.innerHTML += '<div>' + callbackString + '</div>'; | |
| 542 }; | |
| 543 | |
| 544 /** | |
| 545 * Reset this event group to the state before start was called. | |
| 546 */ | |
| 547 TestTimelineGroup.prototype.reset = function() { | |
| 548 this.lateCallbacks = null; | |
| 549 | |
| 550 var callbacks = this.startCallbacks.slice(0); | |
| 551 this.setup_(); | |
| 552 while (callbacks.length > 0) { | |
| 553 var callback = callbacks.shift(); | |
| 554 this.add(callback); | |
| 555 } | |
| 556 }; | |
| 557 | |
| 558 /** | |
| 559 * Tell the event group that the timeline has started and that any callbacks | |
| 560 * added from now are dynamically generated and hence should be cleared when a | |
| 561 * reset is called. | |
| 562 */ | |
| 563 TestTimelineGroup.prototype.start = function() { | |
| 564 this.lateCallbacks = new Array(); | |
| 565 }; | |
| 566 | |
| 567 /** | |
| 568 * Call all the callbacks in the EventGroup. | |
| 569 */ | |
| 570 TestTimelineGroup.prototype.call = function() { | |
| 571 var callbacks = (this.startCallbacks.slice(0)).concat(this.lateCallbacks); | |
| 572 var statuses = this.info.children; | |
| 573 | |
| 574 var overallResult = true; | |
| 575 while (callbacks.length > 0) { | |
| 576 var callback = callbacks.pop(); | |
| 577 | |
| 578 var status_ = statuses[statuses.length - callbacks.length - 1]; | |
| 579 | |
| 580 if (typeof callback == 'function') { | |
| 581 log('TestTimelineGroup', 'calling function', callback); | |
| 582 try { | |
| 583 callback(); | |
| 584 } catch (e) { | |
| 585 // On IE the only way to get the real stack is to do this | |
| 586 window.onerror(e.message, e.fileName, e.lineNumber, e); | |
| 587 // On other browsers we want to throw the error later | |
| 588 setTimeout(function () { throw e; }, 0); | |
| 589 } | |
| 590 } else { | |
| 591 log('TestTimelineGroup', 'calling test', callback); | |
| 592 var result = callback.step(callback.f); | |
| 593 callback.done(); | |
| 594 } | |
| 595 | |
| 596 if (result === undefined || result == null) { | |
| 597 overallResult = overallResult && true; | |
| 598 | |
| 599 status_.style.color = 'green'; | |
| 600 } else { | |
| 601 overallResult = overallResult && false; | |
| 602 status_.style.color = 'red'; | |
| 603 status_.innerHTML += '<div>' + result.toString() + '</div>'; | |
| 604 } | |
| 605 } | |
| 606 if (overallResult) { | |
| 607 this.marker.src = '../img/success.png'; | |
| 608 } else { | |
| 609 this.marker.src = '../img/error.png'; | |
| 610 } | |
| 611 } | |
| 612 | |
| 613 /** | |
| 614 * Draw the EventGroup's marker at the correct position on the timeline. | |
| 615 * | |
| 616 * FIXME(mithro): This mixes display and control :( | |
| 617 * | |
| 618 * @param {number} endTime The endtime of the timeline in millis. Used to | |
| 619 * display the marker at the right place on the timeline. | |
| 620 */ | |
| 621 TestTimelineGroup.prototype.draw = function(container, endTime) { | |
| 622 this.marker.title = this.millis + 'ms'; | |
| 623 this.marker.className = 'marker'; | |
| 624 this.marker.src = '../img/unknown.png'; | |
| 625 | |
| 626 var mleft = 'calc(100% - 10px)'; | |
| 627 if (endTime != 0) { | |
| 628 mleft = 'calc(' + (this.millis / endTime) * 100.0 + '%' + ' - 10px)'; | |
| 629 } | |
| 630 this.marker.style.left = mleft; | |
| 631 | |
| 632 container.appendChild(this.marker); | |
| 633 | |
| 634 this.info.className = 'info'; | |
| 635 container.appendChild(this.info); | |
| 636 | |
| 637 // Display details about the events at this time period when hovering over | |
| 638 // the marker. | |
| 639 this.marker.onmouseover = function() { | |
| 640 this.style.display = 'block'; | |
| 641 }.bind(this.info); | |
| 642 | |
| 643 this.marker.onmouseout = function() { | |
| 644 this.style.display = 'none'; | |
| 645 }.bind(this.info); | |
| 646 | |
| 647 | |
| 648 var offset = Math.ceil(this.info.offsetWidth / 2); | |
| 649 var ileft = 'calc(100% - ' + offset + 'px)'; | |
| 650 if (endTime != 0) { | |
| 651 ileft = 'calc(' + (this.millis / endTime) * 100.0 + '%' + ' - ' + offset + | |
| 652 'px)'; | |
| 653 } | |
| 654 this.info.style.left = ileft; | |
| 655 | |
| 656 this.info.style.display = 'none'; | |
| 657 }; | |
| 658 | |
| 659 | |
| 660 | |
| 661 /** | |
| 662 * Moves the testharness_timeline in "real time". | |
| 663 * (IE 1 test second takes 1 real second). | |
| 664 * | |
| 665 * @constructor | |
| 666 */ | |
| 667 function RealtimeRunner(timeline) { | |
| 668 this.timeline = timeline; | |
| 669 | |
| 670 // Capture the real requestAnimationFrame so we can run in 'real time' mode | |
| 671 // rather than as fast as possible. | |
| 672 var nativeRequestAnimationFrame = window.requestAnimationFrame || window.mozRe
questAnimationFrame; | |
| 673 this.boundRequestAnimationFrame = function(f) { | |
| 674 nativeRequestAnimationFrame(f.bind(this)) | |
| 675 }; | |
| 676 this.now = window.Date.now; | |
| 677 | |
| 678 this.zeroTime = null; // Time the page loaded | |
| 679 this.pauseStartTime = null; // Time at which we paused raf | |
| 680 this.timeDrift = 0; // Amount we have been stopped for | |
| 681 } | |
| 682 | |
| 683 /** | |
| 684 * Callback called from nativeRequestAnimationFrame. | |
| 685 * | |
| 686 * @private | |
| 687 * @param {number} timestamp The current time for the animation frame | |
| 688 * (in millis). | |
| 689 */ | |
| 690 RealtimeRunner.prototype.animationFrame_ = function(timestamp) { | |
| 691 if (this.zeroTime === null) { | |
| 692 this.zeroTime = timestamp; | |
| 693 } | |
| 694 | |
| 695 // Are we paused? Stop calling requestAnimationFrame. | |
| 696 if (this.pauseStartTime != null) { | |
| 697 return; | |
| 698 } | |
| 699 | |
| 700 var virtualAnimationTime = timestamp - this.zeroTime - this.timeDrift; | |
| 701 var endTime = this.timeline.endTime_; | |
| 702 // If we have no events paste t=0, endTime is going to be zero. Instead | |
| 703 // make the test run for 2 minutes. | |
| 704 if (endTime == 0) { | |
| 705 endTime = 120e3; | |
| 706 } | |
| 707 | |
| 708 // Do we still have time to go? | |
| 709 if (virtualAnimationTime < endTime) { | |
| 710 try { | |
| 711 this.timeline.setTime(virtualAnimationTime); | |
| 712 } finally { | |
| 713 this.boundRequestAnimationFrame(this.animationFrame_); | |
| 714 } | |
| 715 | |
| 716 } else { | |
| 717 // Have we gone past endTime_? Force the harness to its endTime_. | |
| 718 | |
| 719 this.timeline.setTime(endTime); | |
| 720 // Don't continue to raf | |
| 721 } | |
| 722 }; | |
| 723 | |
| 724 RealtimeRunner.prototype.start = function() { | |
| 725 if (this.pauseStartTime != null) { | |
| 726 this.timeDrift += (this.now() - this.pauseStartTime); | |
| 727 this.pauseStartTime = null; | |
| 728 } | |
| 729 this.boundRequestAnimationFrame(this.animationFrame_); | |
| 730 }; | |
| 731 | |
| 732 RealtimeRunner.prototype.pause = function() { | |
| 733 if (this.pauseStartTime != null) { | |
| 734 return; | |
| 735 } | |
| 736 this.pauseStartTime = this.now(); | |
| 737 }; | |
| 738 | |
| 739 | |
| 740 /** | |
| 741 * Class for storing events that happen during at given times (such as | |
| 742 * animation checks, or setTimeout). | |
| 743 * | |
| 744 * @constructor | |
| 745 */ | |
| 746 function TestTimeline(everyFrame) { | |
| 747 log('TestTimeline', 'constructor', everyFrame); | |
| 748 /** | |
| 749 * Stores the events which are upcoming. | |
| 750 * | |
| 751 * @type Object.<number, TestTimelineGroup> | |
| 752 * @private | |
| 753 */ | |
| 754 this.timeline_ = new Array(); | |
| 755 | |
| 756 this.everyFrame = everyFrame; | |
| 757 this.frameMillis = 1000.0 / 60; //60fps | |
| 758 | |
| 759 this.currentTime_ = -this.frameMillis; | |
| 760 | |
| 761 // Schedule an event at t=0, needed temporarily. | |
| 762 this.schedule(function() {}, 0); | |
| 763 | |
| 764 this.reset(); | |
| 765 | |
| 766 this.runner_ = new RealtimeRunner(this); | |
| 767 } | |
| 768 | |
| 769 /** | |
| 770 * Create the GUI controller for the timeline. | |
| 771 * @param {Element} body DOM element to add the GUI too, normally the <body> | |
| 772 * element. | |
| 773 */ | |
| 774 TestTimeline.prototype.createGUI = function(body) { | |
| 775 // HTML needed to create the timeline UI | |
| 776 this.div = document.createElement('div'); | |
| 777 this.div.id = 'timeline'; | |
| 778 | |
| 779 this.timelinebar = document.createElement('div'); | |
| 780 this.timelinebar.className = 'bar'; | |
| 781 | |
| 782 this.timelineprogress = document.createElement('div'); | |
| 783 this.timelineprogress.className = 'progress'; | |
| 784 | |
| 785 this.timelinebar.appendChild(this.timelineprogress); | |
| 786 this.div.appendChild(this.timelinebar); | |
| 787 | |
| 788 this.next = document.createElement('button'); | |
| 789 this.next.innerText = '>'; | |
| 790 this.next.id = 'next'; | |
| 791 this.next.onclick = this.toNextEvent.bind(this); | |
| 792 this.div.appendChild(this.next); | |
| 793 | |
| 794 this.prev = document.createElement('button'); | |
| 795 this.prev.innerText = '<'; | |
| 796 this.prev.id = 'prev'; | |
| 797 this.prev.onclick = this.toPrevEvent.bind(this); | |
| 798 this.div.appendChild(this.prev); | |
| 799 | |
| 800 this.control = document.createElement('button'); | |
| 801 this.control.innerText = 'Pause'; | |
| 802 this.control.id = 'control'; | |
| 803 this.control.onclick = function() { | |
| 804 if (this.control.innerText == 'Go!') { | |
| 805 this.runner_.start(); | |
| 806 this.control.innerText = 'Pause'; | |
| 807 } else { | |
| 808 this.runner_.pause(); | |
| 809 this.control.innerText = 'Go!'; | |
| 810 } | |
| 811 }.bind(this); | |
| 812 this.div.appendChild(this.control); | |
| 813 | |
| 814 body.appendChild(this.div); | |
| 815 } | |
| 816 | |
| 817 /** | |
| 818 * Update GUI elements. | |
| 819 * | |
| 820 * @private | |
| 821 */ | |
| 822 TestTimeline.prototype.updateGUI = function () { | |
| 823 // Update the timeline | |
| 824 var width = "100%"; | |
| 825 if (this.endTime_ != 0) { | |
| 826 width = (this.currentTime_ / this.endTime_) * 100.0 +'%' | |
| 827 } | |
| 828 this.timelineprogress.style.width = width; | |
| 829 this.timelinebar.title = (this.currentTime_).toFixed(0) + 'ms'; | |
| 830 }; | |
| 831 | |
| 832 | |
| 833 /** | |
| 834 * Sort the timeline into run order. Should be called after adding something to | |
| 835 * the timeline. | |
| 836 * | |
| 837 * @private | |
| 838 */ | |
| 839 TestTimeline.prototype.sort_ = function() { | |
| 840 this.timeline_.sort(function(a,b) { | |
| 841 return a.millis - b.millis; | |
| 842 }); | |
| 843 }; | |
| 844 | |
| 845 /** | |
| 846 * Schedule something to be called at a given time. | |
| 847 * | |
| 848 * @param {function(number)} callback Callback to call after the number of milli
s | |
| 849 * have elapsed. | |
| 850 * @param {number} millis Milliseconds after start at which the callback should | |
| 851 * be called. | |
| 852 */ | |
| 853 TestTimeline.prototype.schedule = function(callback, millis) { | |
| 854 log('TestTimeline', 'schedule', millis, callback); | |
| 855 if (millis < this.currentTime_) { | |
| 856 // Can't schedule something in the past? | |
| 857 return; | |
| 858 } | |
| 859 | |
| 860 // See if there is something at that time in the timeline already? | |
| 861 var timeline = this.timeline_.slice(0); | |
| 862 var group = null; | |
| 863 while (timeline.length > 0) { | |
| 864 if (timeline[0].millis == millis) { | |
| 865 group = timeline[0]; | |
| 866 break; | |
| 867 } else { | |
| 868 timeline.shift(); | |
| 869 } | |
| 870 } | |
| 871 | |
| 872 // If not, create a node at that time. | |
| 873 if (group === null) { | |
| 874 group = new TestTimelineGroup(millis); | |
| 875 this.timeline_.unshift(group); | |
| 876 this.sort_(); | |
| 877 } | |
| 878 group.add(callback); | |
| 879 | |
| 880 var newEndTime = this.timeline_.slice(-1)[0].millis * 1.1; | |
| 881 if (this.endTime_ != newEndTime) { | |
| 882 this.endTime_ = newEndTime; | |
| 883 } | |
| 884 }; | |
| 885 | |
| 886 /** | |
| 887 * Return the current time in milliseconds. | |
| 888 */ | |
| 889 TestTimeline.prototype.now = function() { | |
| 890 log('TestTimeline', 'now', Math.max(this.currentTime_, 0)); | |
| 891 return Math.max(this.currentTime_, 0); | |
| 892 }; | |
| 893 | |
| 894 /** | |
| 895 * Set the current time to a given value. | |
| 896 * | |
| 897 * @param {number} millis Time in milliseconds to set the current time too. | |
| 898 */ | |
| 899 TestTimeline.prototype.setTime = function(millis) { | |
| 900 log('TestTimeline', 'setTime', millis); | |
| 901 // Time is going backwards, we actually have to reset and go forwards as | |
| 902 // events can cause the creation of more events. | |
| 903 if (this.currentTime_ > millis) { | |
| 904 this.reset(); | |
| 905 this.start(); | |
| 906 } | |
| 907 | |
| 908 var events = this.timeline_.slice(0); | |
| 909 | |
| 910 // Already processed events | |
| 911 while (events.length > 0 && events[0].millis <= this.currentTime_) { | |
| 912 events.shift(); | |
| 913 } | |
| 914 | |
| 915 while (this.currentTime_ < millis) { | |
| 916 var event_ = null; | |
| 917 var moveTo = millis; | |
| 918 | |
| 919 if (events.length > 0 && events[0].millis <= millis) { | |
| 920 event_ = events.shift(); | |
| 921 moveTo = event_.millis; | |
| 922 } | |
| 923 | |
| 924 // Call the callback | |
| 925 if (this.currentTime_ != moveTo) { | |
| 926 log('TestTimeline', 'setting time to', moveTo); | |
| 927 this.currentTime_ = moveTo; | |
| 928 this.animationFrame(this.currentTime_); | |
| 929 } | |
| 930 | |
| 931 if (event_) { | |
| 932 event_.call(); | |
| 933 } | |
| 934 } | |
| 935 | |
| 936 this.updateGUI(); | |
| 937 | |
| 938 if (millis >= this.endTime_) { | |
| 939 this.done(); | |
| 940 } | |
| 941 }; | |
| 942 | |
| 943 /** | |
| 944 * Call all callbacks registered for the next (virtual) animation frame. | |
| 945 * | |
| 946 * @param {number} millis Time in milliseconds. | |
| 947 * @private | |
| 948 */ | |
| 949 TestTimeline.prototype.animationFrame = function(millis) { | |
| 950 /* FIXME(mithro): Code should appear here to allow testing of running | |
| 951 * every animation frame. | |
| 952 | |
| 953 if (this.everyFrame) { | |
| 954 } | |
| 955 | |
| 956 */ | |
| 957 | |
| 958 var callbacks = this.animationFrameCallbacks; | |
| 959 callbacks.reverse(); | |
| 960 this.animationFrameCallbacks = []; | |
| 961 for (var i = 0; i < callbacks.length; i++) { | |
| 962 log('TestTimeline raf callback', callbacks[i], millis); | |
| 963 try { | |
| 964 callbacks[i](millis); | |
| 965 } catch (e) { | |
| 966 // On IE the only way to get the real stack is to do this | |
| 967 window.onerror(e.message, e.fileName, e.lineNumber, e); | |
| 968 // On other browsers we want to throw the error later | |
| 969 setTimeout(function () { throw e; }, 0); | |
| 970 } | |
| 971 } | |
| 972 }; | |
| 973 | |
| 974 /** | |
| 975 * Set a callback to run at the next (virtual) animation frame. | |
| 976 * | |
| 977 * @param {function(millis)} millis Time in milliseconds to set the current | |
| 978 * time too. | |
| 979 */ | |
| 980 TestTimeline.prototype.requestAnimationFrame = function(callback) { | |
| 981 // FIXME: This should return a reference that allows people to cancel the | |
| 982 // animationFrame callback. | |
| 983 this.animationFrameCallbacks.push(callback); | |
| 984 return -1; | |
| 985 }; | |
| 986 | |
| 987 /** | |
| 988 * Go to next scheduled event in timeline. | |
| 989 */ | |
| 990 TestTimeline.prototype.toNextEvent = function() { | |
| 991 var events = this.timeline_.slice(0); | |
| 992 while (events.length > 0 && events[0].millis <= this.currentTime_) { | |
| 993 events.shift(); | |
| 994 } | |
| 995 if (events.length > 0) { | |
| 996 this.setTime(events[0].millis); | |
| 997 | |
| 998 if (this.autorun_) { | |
| 999 setTimeout(this.toNextEvent.bind(this), 0); | |
| 1000 } | |
| 1001 | |
| 1002 return true; | |
| 1003 } else { | |
| 1004 this.setTime(this.endTime_); | |
| 1005 return false; | |
| 1006 } | |
| 1007 | |
| 1008 }; | |
| 1009 | |
| 1010 /** | |
| 1011 * Go to previous scheduled event in timeline. | |
| 1012 * (This actually goes back to time zero and then forward to this event.) | |
| 1013 */ | |
| 1014 TestTimeline.prototype.toPrevEvent = function() { | |
| 1015 var events = this.timeline_.slice(0); | |
| 1016 while (events.length > 0 && | |
| 1017 events[events.length - 1].millis >= this.currentTime_) { | |
| 1018 events.pop(); | |
| 1019 } | |
| 1020 if (events.length > 0) { | |
| 1021 this.setTime(events[events.length - 1].millis); | |
| 1022 return true; | |
| 1023 } else { | |
| 1024 this.setTime(0); | |
| 1025 return false; | |
| 1026 } | |
| 1027 }; | |
| 1028 | |
| 1029 /** | |
| 1030 * Reset the timeline to time zero. | |
| 1031 */ | |
| 1032 TestTimeline.prototype.reset = function () { | |
| 1033 for (var t in this.timeline_) { | |
| 1034 this.timeline_[t].reset(); | |
| 1035 } | |
| 1036 | |
| 1037 this.currentTime_ = -this.frameMillis; | |
| 1038 this.animationFrameCallbacks = []; | |
| 1039 this.started_ = false; | |
| 1040 }; | |
| 1041 | |
| 1042 /** | |
| 1043 * Call to initiate starting??? | |
| 1044 */ | |
| 1045 TestTimeline.prototype.start = function () { | |
| 1046 this.started_ = true; | |
| 1047 | |
| 1048 var parent = this; | |
| 1049 | |
| 1050 for (var t in this.timeline_) { | |
| 1051 this.timeline_[t].start(); | |
| 1052 // FIXME(mithro) this is confusing... | |
| 1053 this.timeline_[t].draw(this.timelinebar, this.endTime_); | |
| 1054 | |
| 1055 this.timeline_[t].marker.onclick = function(event) { | |
| 1056 parent.setTime(this.millis); | |
| 1057 event.stopPropagation(); | |
| 1058 }.bind(this.timeline_[t]); | |
| 1059 } | |
| 1060 | |
| 1061 this.timelinebar.onclick = function(evt) { | |
| 1062 var setPercent = | |
| 1063 ((evt.clientX - this.offsetLeft) / this.offsetWidth); | |
| 1064 parent.setTime(setPercent * parent.endTime_); | |
| 1065 }.bind(this.timelinebar); | |
| 1066 }; | |
| 1067 | |
| 1068 TestTimeline.prototype.done = function () { | |
| 1069 log('TestTime', 'done'); | |
| 1070 done(); | |
| 1071 }; | |
| 1072 | |
| 1073 TestTimeline.prototype.autorun = function() { | |
| 1074 this.autorun_ = true; | |
| 1075 this.toNextEvent(); | |
| 1076 }; | |
| 1077 | |
| 1078 function testharness_timeline_setup() { | |
| 1079 log('testharness_timeline_setup'); | |
| 1080 testharness_timeline.createGUI(document.getElementsByTagName('body')[0]); | |
| 1081 testharness_timeline.start(); | |
| 1082 testharness_timeline.updateGUI(); | |
| 1083 | |
| 1084 // Start running the test on message | |
| 1085 if ('#message' == window.location.hash) { | |
| 1086 window.addEventListener('message', function(evt) { | |
| 1087 switch (evt.data['type']) { | |
| 1088 case 'start': | |
| 1089 if (evt.data['url'] == window.location.href) { | |
| 1090 testharness_timeline.autorun(); | |
| 1091 } | |
| 1092 break; | |
| 1093 } | |
| 1094 }); | |
| 1095 } else if ('#auto' == window.location.hash || '#coverage' == window.location.h
ash) { | |
| 1096 // Run the test as fast as possible, skipping time. | |
| 1097 | |
| 1098 // Need non-zero timeout to allow chrome to run other code. | |
| 1099 setTimeout(testharness_timeline.autorun.bind(testharness_timeline), 1); | |
| 1100 | |
| 1101 } else if (inExploreMode()) { | |
| 1102 setTimeout(testharness_timeline.runner_.start.bind(testharness_timeline.runn
er_), 1); | |
| 1103 } else { | |
| 1104 alert('Unknown start mode.'); | |
| 1105 } | |
| 1106 } | |
| 1107 | |
| 1108 // Capture testharness's test as we are about to screw with it. | |
| 1109 var testharness_test = window.test; | |
| 1110 | |
| 1111 function override_at(replacement_at, f, args) { | |
| 1112 var orig_at = window.at; | |
| 1113 window.at = replacement_at; | |
| 1114 f.apply(null, args); | |
| 1115 window.at = orig_at; | |
| 1116 } | |
| 1117 | |
| 1118 function timing_test(f, desc) { | |
| 1119 /** | |
| 1120 * at function inside a timing_test function allows testing things at a | |
| 1121 * given time rather then onload. | |
| 1122 * @param {number} millis Milliseconds after page load to run the tests. | |
| 1123 * @param {function()} f Closure containing the asserts to be run. | |
| 1124 * @param {string} desc Description | |
| 1125 */ | |
| 1126 var at = function(millis, f, desc_at) { | |
| 1127 assert_true(typeof millis == 'number', "at's first argument shoud be a numbe
r."); | |
| 1128 assert_true(!isNaN(millis), "at's first argument should be a number not NaN!
"); | |
| 1129 assert_true(millis >= 0, "at's first argument should be greater then 0."); | |
| 1130 assert_true(isFinite(millis), "at's first argument should be finite."); | |
| 1131 | |
| 1132 assert_true(typeof f == 'function', "at's second argument should be a functi
on."); | |
| 1133 | |
| 1134 // Deliberately hoist the desc if we where not given one. | |
| 1135 if (typeof desc_at == 'undefined' || desc_at == null || desc_at.length == 0)
{ | |
| 1136 desc_at = desc; | |
| 1137 } | |
| 1138 | |
| 1139 // And then provide 'Unnamed' as a default | |
| 1140 if (typeof desc_at == 'undefined' || desc_at == null || desc_at.length == 0)
{ | |
| 1141 desc_at = 'Unnamed assert'; | |
| 1142 } | |
| 1143 | |
| 1144 var t = async_test(desc_at + ' at t=' + millis + 'ms'); | |
| 1145 t.f = f; | |
| 1146 window.testharness_timeline.schedule(t, millis); | |
| 1147 }; | |
| 1148 override_at(at, f); | |
| 1149 } | |
| 1150 | |
| 1151 function test_without_at(f, desc) { | |
| 1152 // Make sure calling at inside a test() function is a failure. | |
| 1153 override_at(function() { | |
| 1154 throw {'message': 'Can not use at() inside a test, use a timing_test instead
.'}; | |
| 1155 }, function() { testharness_test(f, desc); }); | |
| 1156 } | |
| 1157 | |
| 1158 /** | |
| 1159 * at function schedules a to be called at a given point. | |
| 1160 * @param {number} millis Milliseconds after page load to run the function. | |
| 1161 * @param {function()} f Function to be called. Called with no arguments | |
| 1162 */ | |
| 1163 function at(millis, f) { | |
| 1164 assert_true(typeof millis == 'number', "at's first argument shoud be a number.
"); | |
| 1165 assert_true(typeof f == 'function', "at's second argument should be a function
."); | |
| 1166 | |
| 1167 window.testharness_timeline.schedule(f, millis); | |
| 1168 } | |
| 1169 | |
| 1170 window.testharness_after_loaded = function() { | |
| 1171 log('testharness_after_loaded'); | |
| 1172 /** | |
| 1173 * These steps needs to occur after testharness is loaded. | |
| 1174 */ | |
| 1175 setup(function() {}, { | |
| 1176 explicit_timeout: true, | |
| 1177 explicit_done: ((typeof window.testharness_timeline) !== 'undefined')}); | |
| 1178 | |
| 1179 /** | |
| 1180 * Create an testharness test which makes sure the page contains no | |
| 1181 * javascript errors. This is needed otherwise if the page contains errors | |
| 1182 * then preventing the tests loading it will look like it passed. | |
| 1183 */ | |
| 1184 var pageerror_test = async_test('Page contains no errors'); | |
| 1185 | |
| 1186 window.onerror = function(msg, url, line, e) { | |
| 1187 var msg = '\nError in ' + url + '\n' + | |
| 1188 'Line ' + line + ': ' + msg + '\n'; | |
| 1189 | |
| 1190 if (typeof e != "undefined") { | |
| 1191 msg += e.stack; | |
| 1192 } | |
| 1193 | |
| 1194 pageerror_test.is_done = true; | |
| 1195 pageerror_test.step(function() { | |
| 1196 throw new AssertionError(msg); | |
| 1197 }); | |
| 1198 pageerror_test.is_done = false; | |
| 1199 }; | |
| 1200 | |
| 1201 var pageerror_tests; | |
| 1202 function pageerror_othertests_finished(test, harness) { | |
| 1203 if (harness == null && pageerror_tests == null) { | |
| 1204 return; | |
| 1205 } | |
| 1206 | |
| 1207 if (pageerror_tests == null) { | |
| 1208 pageerror_tests = harness; | |
| 1209 } | |
| 1210 | |
| 1211 if (pageerror_tests.all_loaded && pageerror_tests.num_pending == 1) { | |
| 1212 pageerror_test.done(); | |
| 1213 } | |
| 1214 } | |
| 1215 add_result_callback(pageerror_othertests_finished); | |
| 1216 addEventListener('load', pageerror_othertests_finished); | |
| 1217 | |
| 1218 }; | |
| 1219 | |
| 1220 loadScript('../testharness/testharness.js', {coverage: false}); | |
| 1221 document.write('<script type="text/javascript">window.testharness_after_loaded()
;</script>'); | |
| 1222 loadCSS('../testharness/testharness.css'); | |
| 1223 loadCSS('../testharness_timing.css'); | |
| 1224 | |
| 1225 if (testType() == 'auto') { | |
| 1226 var checksFile = location.pathname; | |
| 1227 checksFile = checksFile.replace(/disabled-/, ''); | |
| 1228 checksFile = checksFile.replace(/.html$/, '-checks.js') | |
| 1229 loadScript(checksFile, {coverage: false}); | |
| 1230 } | |
| 1231 | |
| 1232 document.write('<div id="log"></div>'); | |
| 1233 loadScript('../testharness/testharnessreport.js', {coverage: false}); | |
| 1234 | |
| 1235 if (!hasFlag('nopolyfill')) { | |
| 1236 loadScript('../../web-animations.js'); | |
| 1237 } | |
| 1238 | |
| 1239 addEventListener('load', function() { | |
| 1240 if (window._WebAnimationsTestingUtilities) { | |
| 1241 // Currently enabling asserts breaks auto-test-initial in IE. | |
| 1242 //_WebAnimationsTestingUtilities._enableAsserts(); | |
| 1243 } | |
| 1244 }); | |
| 1245 | |
| 1246 // Don't export the timing functions in unittests. | |
| 1247 if (testType() != 'unit') { | |
| 1248 addEventListener('load', testharness_timeline_setup); | |
| 1249 | |
| 1250 window.at = at; | |
| 1251 window.timing_test = timing_test; | |
| 1252 window.test = test_without_at; | |
| 1253 | |
| 1254 // Expose the extra API | |
| 1255 window.testharness_timeline = new TestTimeline(); | |
| 1256 | |
| 1257 // Override existing timing functions | |
| 1258 window.requestAnimationFrame = | |
| 1259 testharness_timeline.requestAnimationFrame.bind(testharness_timeline); | |
| 1260 window.performance.now = null; | |
| 1261 window.Date.now = testharness_timeline.now.bind(testharness_timeline); | |
| 1262 } | |
| 1263 | |
| 1264 window.inExploreMode = inExploreMode; | |
| 1265 | |
| 1266 })(); | |
| OLD | NEW |