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

Side by Side Diff: bower_components/web-animations-js/test/bootstrap.js

Issue 786953007: npm_modules: Fork bower_components into Polymer 0.4.0 and 0.5.0 versions (Closed) Base URL: https://chromium.googlesource.com/infra/third_party/npm_modules.git@master
Patch Set: Created 5 years, 11 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
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 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 })();
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698