Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(147)

Side by Side Diff: pkg/polymer/lib/elements/web-animations-js/test/bootstrap.js

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

Powered by Google App Engine
This is Rietveld 408576698