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

Unified 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, 10 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 side-by-side diff with in-line comments
Download patch
Index: pkg/polymer/lib/elements/web-animations-js/test/bootstrap.js
diff --git a/pkg/polymer/lib/elements/web-animations-js/test/bootstrap.js b/pkg/polymer/lib/elements/web-animations-js/test/bootstrap.js
new file mode 100644
index 0000000000000000000000000000000000000000..fa953864fd41c0eb39f2beaa77819714080df5da
--- /dev/null
+++ b/pkg/polymer/lib/elements/web-animations-js/test/bootstrap.js
@@ -0,0 +1,1304 @@
+/**
+ * Copyright 2013 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+'use strict';
+
+(function() {
+
+var log_element = document.createElement('pre');
+log_element.id = 'debug';
+
+var log_element_parent = setInterval(function() {
+ if (log_element.parentNode == null && document.body != null) {
+ document.body.appendChild(log_element);
+ clearInterval(log_element_parent);
+ }
+}, 100);
+
+function log() {
+
+ var output = [];
+ for (var i = 0; i < arguments.length; i++) {
+ if (typeof arguments[i] === "function") {
+ arguments[i] = '' + arguments[i];
+ }
+ if (typeof arguments[i] === "string") {
+ var bits = arguments[i].replace(/\s*$/, '').split('\n');
+ if (bits.length > 5) {
+ bits.splice(3, bits.length-5, '...');
+ }
+ output.push(bits.join('\n'));
+ } else if (typeof arguments[i] === "object" && typeof arguments[i].name === "string") {
+ output.push('"'+arguments[i].name+'"');
+ } else {
+ output.push(JSON.stringify(arguments[i], undefined, 2));
+ }
+ output.push(' ');
+ }
+ log_element.appendChild(document.createTextNode(output.join('') + '\n'));
+}
+
+var thisScript = document.querySelector("script[src$='bootstrap.js']");
+var coverageMode = Boolean(parent.window.__coverage__) || /coverage/.test(window.location.hash);
+
+// Inherit these properties from the parent test-runner if any.
+window.__resources__ = parent.window.__resources__ || {original: {}};
+window.__coverage__ = parent.window.__coverage__;
+
+function getSync(src) {
+ var xhr = new XMLHttpRequest();
+ xhr.open('GET', src, false);
+ xhr.send();
+ if (xhr.responseCode > 400) {
+ console.error('Error loading ' + src);
+ return '';
+ }
+ return xhr.responseText;
+}
+
+function loadScript(src, options) {
+ // Add changing parameter to prevent script caching.
+ options = options || {coverage: true};
+ if (window.__resources__[src]) {
+ document.write('<script type="text/javascript">eval(window.__resources__["'+src+'"]);</script>');
+ } else if (coverageMode && options.coverage) {
+ instrument(src);
+ loadScript(src);
+ } else {
+ if (!inExploreMode()) {
+ src += '?' + getCacheBuster();
+ }
+ document.write('<script type="text/javascript" src="'+ src + '"></script>');
+ }
+}
+
+function loadCSS(src) {
+ document.write('<link rel="stylesheet" type="text/css" href="' + src + '">');
+}
+
+function forEach(array, callback, thisObj) {
+ for (var i=0; i < array.length; i++) {
+ if (array.hasOwnProperty(i)) {
+ callback.call(thisObj, array[i], i, array);
+ }
+ }
+}
+
+function hasFlag(flag) {
+ return thisScript && thisScript.getAttribute(flag) !== null;
+}
+
+function testType() {
+ var p = location.pathname;
+ p = p.replace(/^disabled-/, '');
+
+ var match = /(auto|impl|manual|unit)-test[^\\\/]*$/.exec(p);
+ return match ? match[1]: 'unknown';
+}
+
+function inExploreMode() {
+ return '#explore' == window.location.hash || window.location.hash.length == 0;
+}
+
+/**
+ * Get a value for busting the cache. If we got given a cache buster, pass it
+ * along, otherwise generate a new one.
+ */
+var cacheBusterValue = '' + window.Date.now();
+function getCacheBuster() {
+ if (window.location.search.length > 0)
+ cacheBusterValue = window.location.search.substr(1, window.location.search.length);
+ return cacheBusterValue;
+}
+
+var instrumentationDepsLoaded = false;
+/**
+ * Instrument the source at {@code location} and store it in
+ * {@code window.__resources__[name]}.
+ */
+function instrument(src) {
+ if (__resources__[src]) {
+ return;
+ }
+ if (!instrumentationDepsLoaded) {
+ instrumentationDepsLoaded = true;
+ (function() {
+ eval(getSync('../coverage/esprima/esprima.js'));
+ eval(getSync('../coverage/escodegen/escodegen.browser.js'));
+ eval(getSync('../coverage/istanbul/lib/instrumenter.js'));
+ }).call(window);
+ }
+ var js = getSync(src);
+ window.__resources__.original[src] = js;
+ var inst = window.__resources__[src] = new Instrumenter().instrumentSync(js, src);
+}
+
+
+var svg_properties = {
+ cx: 1,
+ width: 1,
+ x: 1,
+ y: 1
+};
+
+var is_svg_attrib = function(property, target) {
+ return target.namespaceURI == 'http://www.w3.org/2000/svg' &&
+ property in svg_properties;
+};
+
+var svg_namespace_uri = 'http://www.w3.org/2000/svg';
+
+window.test_features = (function() {
+ var style = document.createElement('style');
+ style.textContent = '' +
+ 'dummyRuleForTesting {' +
+ 'width: calc(0px);' +
+ 'width: -webkit-calc(0px); }';
+ document.head.appendChild(style);
+ var transformCandidates = [
+ 'transform',
+ 'webkitTransform',
+ 'msTransform'
+ ];
+ var transformProperty = transformCandidates.filter(function(property) {
+ return property in style.sheet.cssRules[0].style;
+ })[0];
+ var calcFunction = style.sheet.cssRules[0].style.width.split('(')[0];
+ document.head.removeChild(style);
+ return {
+ transformProperty: transformProperty,
+ calcFunction: calcFunction
+ };
+})();
+
+/**
+ * Figure out a useful name for an element.
+ *
+ * @param {Element} element Element to get the name for.
+ *
+ * @private
+ */
+function _element_name(element) {
+ if (element.id) {
+ return element.tagName.toLowerCase() + '#' + element.id;
+ } else {
+ return 'An anonymous ' + element.tagName.toLowerCase();
+ }
+}
+
+/**
+ * Get the style for a given element.
+ *
+ * @param {Array.<Object.<string, string>>|Object.<string, string>} style
+ * Either;
+ * * A list of dictionaries, each node returned is checked against the
+ * associated dictionary, or
+ * * A single dictionary, each node returned is checked against the
+ * given dictionary.
+ * Each dictionary should be of the form {style_name: style_value}.
+ *
+ * @private
+ */
+function _assert_style_get(style, i) {
+ if (typeof style[i] === 'undefined') {
+ return style;
+ } else {
+ return style[i];
+ }
+}
+
+/**
+ * Extract all the informative parts of a string. Ignores spacing, punctuation
+ * and other random extra characters.
+ */
+function _extract_important(input) {
+ var re = /([-+]?[0-9]+\.?[0-9]*(?:[eE][-+]?[0-9]+)?)|[A-Za-z%]+/g;
+
+ var match;
+ var result = [];
+ while (match = re.exec(input)) {
+ var value = match[0];
+ if (typeof match[1] != "undefined") {
+ value = Number(match[1]);
+ }
+ result.push(value);
+ }
+ return result;
+}
+window.assert_styles_extract_important = _extract_important;
+
+function AssertionError(message) {
+ this.message = message;
+}
+window.assert_styles_assertion_error = AssertionError;
+
+/**
+ * Asserts that a string is in the array of expected only comparing the
+ * important parts. Ignores spacing, punctuation and other random extra
+ * characters.
+ */
+function _assert_important_in_array(actual, expected, message) {
+ var actual_array = _extract_important(actual);
+
+ var expected_array_array = [];
+ for (var i = 0; i < expected.length; i++) {
+ expected_array_array.push(_extract_important(expected[i]));
+ }
+
+ var errors = [];
+ for (var i = 0; i < expected_array_array.length; i++) {
+ var expected_array = expected_array_array[i];
+
+ var element_errors = [];
+ if (actual_array.length != expected_array.length) {
+ element_errors.push('Number of elements don\'t match');
+ }
+
+ for (var j = 0; j < expected_array.length; j++) {
+ var actual = actual_array[j];
+ var expected = expected_array[j];
+
+ try {
+ assert_equals(typeof actual, typeof expected);
+
+ if (typeof actual === 'number') {
+ if (Math.abs(actual) < 1e-10) {
+ actual = 0;
+ }
+ actual = '' + actual.toPrecision(4);
+ }
+ if (typeof expected === 'number') {
+ if (Math.abs(expected) < 1e-10) {
+ expected = 0;
+ }
+ expected = '' + expected.toPrecision(4);
+ }
+
+ assert_equals(actual, expected);
+ } catch (e) {
+ element_errors.push(
+ 'Element ' + j + ' - ' + e.message);
+ }
+ }
+
+ if (element_errors.length == 0) {
+ return;
+ } else {
+ errors.push(
+ ' Expectation ' + JSON.stringify(expected_array) + ' did not match\n' +
+ ' ' + element_errors.join('\n '));
+ }
+ }
+ if (expected_array_array.length > 1)
+ errors.unshift(' ' + expected_array_array.length + ' possible expectations');
+
+ errors.unshift(' Actual - ' + JSON.stringify(actual_array));
+ if (typeof message !== 'undefined') {
+ errors.unshift(message);
+ }
+ throw new AssertionError(errors.join('\n'));
+}
+window.assert_styles_assert_important_in_array = _assert_important_in_array;
+
+/**
+ * asserts that actual has the same styles as the dictionary given by
+ * expected.
+ *
+ * @param {Element} object DOM node to check the styles on
+ * @param {Object.<string, string>} styles Dictionary of {style_name: style_value} to check
+ * on the object.
+ * @param {String} description Human readable description of what you are
+ * trying to check.
+ *
+ * @private
+ */
+function _assert_style_element(object, style, description) {
+ if (typeof message == 'undefined')
+ description = '';
+
+ // Create an element of the same type as testing so the style can be applied
+ // from the test. This is so the css property (not the -webkit-does-something
+ // tag) can be read.
+ var reference_element = (object.namespaceURI == svg_namespace_uri) ?
+ document.createElementNS(svg_namespace_uri, object.nodeName) :
+ document.createElement(object.nodeName);
+ var computedObjectStyle = getComputedStyle(object, null);
+ for (var i = 0; i < computedObjectStyle.length; i++) {
+ var property = computedObjectStyle[i];
+ reference_element.style.setProperty(property,
+ computedObjectStyle.getPropertyValue(property));
+ }
+ reference_element.style.position = 'absolute';
+ if (object.parentNode) {
+ object.parentNode.appendChild(reference_element);
+ }
+
+ try {
+ // Apply the style
+ for (var prop_name in style) {
+ // If the passed in value is an element then grab its current style for
+ // that property
+ if (style[prop_name] instanceof HTMLElement ||
+ style[prop_name] instanceof SVGElement) {
+
+ var prop_value = getComputedStyle(style[prop_name], null)[prop_name];
+ } else {
+ var prop_value = style[prop_name];
+ }
+
+ prop_value = '' + prop_value;
+
+ if (prop_name == 'transform') {
+ var output_prop_name = test_features.transformProperty;
+ } else {
+ var output_prop_name = prop_name;
+ }
+
+ var is_svg = is_svg_attrib(prop_name, object);
+ if (is_svg) {
+ reference_element.setAttribute(prop_name, prop_value);
+
+ var current_style = object.attributes;
+ var target_style = reference_element.attributes;
+ } else {
+ reference_element.style[output_prop_name] = prop_value;
+
+ var current_style = computedObjectStyle;
+ var target_style = getComputedStyle(reference_element, null);
+
+ _assert_important_in_array(
+ prop_value, [reference_element.style[output_prop_name], target_style[output_prop_name]],
+ 'Tried to set the reference element\'s '+ output_prop_name +
+ ' to ' + JSON.stringify(prop_value) +
+ ' but neither the style' +
+ ' ' + JSON.stringify(reference_element.style[output_prop_name]) +
+ ' nor computedStyle ' + JSON.stringify(target) +
+ ' ended up matching requested value.');
+ }
+
+ if (prop_name == 'ctm') {
+ var ctm = object.getCTM();
+ var curr = '{' + ctm.a + ', ' +
+ ctm.b + ', ' + ctm.c + ', ' + ctm.d + ', ' +
+ ctm.e + ', ' + ctm.f + '}';
+
+ var target = prop_value;
+
+ } else if (is_svg) {
+ var target = target_style[prop_name].value;
+ var curr = current_style[prop_name].value;
+ } else {
+ var target = target_style[output_prop_name];
+ var curr = current_style[output_prop_name];
+ }
+
+ var description_extra = '\n Property ' + prop_name;
+ if (prop_name != output_prop_name)
+ description_extra += '(actually ' + output_prop_name + ')';
+
+ _assert_important_in_array(curr, [target], description + description_extra);
+ }
+ } finally {
+ if (reference_element.parentNode) {
+ reference_element.parentNode.removeChild(reference_element);
+ }
+ }
+}
+
+/**
+ * asserts that elements in the list have given styles.
+ *
+ * @param {Array.<Element>} objects List of DOM nodes to check the styles on
+ * @param {Array.<Object.<string, string>>|Object.<string, string>} style
+ * See _assert_style_get for information.
+ * @param {String} description Human readable description of what you are
+ * trying to check.
+ *
+ * @private
+ */
+function _assert_style_element_list(objects, style, description) {
+ var error = '';
+ forEach(objects, function(object, i) {
+ try {
+ _assert_style_element(
+ object, _assert_style_get(style, i),
+ description + ' ' + _element_name(object)
+ );
+ } catch (e) {
+ if (error) {
+ error += '; ';
+ }
+ error += 'Element ' + _element_name(object) + ' at index ' + i + ' failed ' + e.message + '\n';
+ }
+ });
+ if (error) {
+ throw error;
+ }
+}
+
+/**
+ * asserts that elements returned from a query selector have a list of styles.
+ *
+ * @param {string} qs A query selector to use to get the DOM nodes.
+ * @param {Array.<Object.<string, string>>|Object.<string, string>} style
+ * See _assert_style_get for information.
+ * @param {String} description Human readable description of what you are
+ * trying to check.
+ *
+ * @private
+ */
+function _assert_style_queryselector(qs, style, description) {
+ var objects = document.querySelectorAll(qs);
+ assert_true(objects.length > 0, description +
+ ' is invalid, no elements match query selector: ' + qs);
+ _assert_style_element_list(objects, style, description);
+}
+
+/**
+ * asserts that elements returned from a query selector have a list of styles.
+ *
+ * Assert the element with id #hello is 100px wide;
+ * assert_styles(document.getElementById('hello'), {'width': '100px'})
+ * assert_styles('#hello'), {'width': '100px'})
+ *
+ * Assert all divs are 100px wide;
+ * assert_styles(document.getElementsByTagName('div'), {'width': '100px'})
+ * assert_styles('div', {'width': '100px'})
+ *
+ * Assert all objects with class 'red' are 100px wide;
+ * assert_styles(document.getElementsByClassName('red'), {'width': '100px'})
+ * assert_styles('.red', {'width': '100px'})
+ *
+ * Assert first div is 100px wide, second div is 200px wide;
+ * assert_styles(document.getElementsByTagName('div'),
+ * [{'width': '100px'}, {'width': '200px'}])
+ * assert_styles('div',
+ * [{'width': '100px'}, {'width': '200px'}])
+ *
+ * @param {string|Element|Array.<Element>} objects Either;
+ * * A query selector to use to get DOM nodes,
+ * * A DOM node.
+ * * A list of DOM nodes.
+ * @param {Array.<Object.<string, string>>|Object.<string, string>} style
+ * See _assert_style_get for information.
+ */
+function assert_styles(objects, style, description) {
+ switch (typeof objects) {
+ case 'string':
+ _assert_style_queryselector(objects, style, description);
+ break;
+
+ case 'object':
+ if (objects instanceof Array || objects instanceof NodeList) {
+ _assert_style_element_list(objects, style, description);
+ } else if (objects instanceof Element) {
+ _assert_style_element(objects, style, description);
+ } else {
+ throw new Error('Expected Array, NodeList or Element but got ' + objects);
+ }
+ break;
+ }
+}
+window.assert_styles = assert_styles;
+
+/**
+ * Schedule something to be called at a given time.
+ *
+ * @constructor
+ * @param {number} millis Microseconds after start at which the callback should
+ * be called.
+ * @param {bool} autostart Auto something...
+ */
+function TestTimelineGroup(millis) {
+ this.millis = millis;
+
+ /**
+ * @type {bool}
+ */
+ this.autorun_ = false;
+
+ /**
+ * @type {!Array.<function(): ?Object>}
+ */
+ this.startCallbacks = null;
+
+ /**
+ * Callbacks which are added after the timeline has started. We clear them
+ * when going backwards.
+ *
+ * @type {?Array.<function(): ?Object>}
+ */
+ this.lateCallbacks = null;
+
+ /**
+ * @type {Element}
+ */
+ this.marker = document.createElement('img');
+ /**
+ * @type {Element}
+ */
+ this.info = document.createElement('div');
+
+ this.setup_();
+}
+
+TestTimelineGroup.prototype.setup_ = function() {
+ this.endTime_ = 0;
+ this.startCallbacks = new Array();
+ this.lateCallbacks = null;
+ this.marker.innerHTML = '';
+ this.info.innerHTML = '';
+};
+
+/**
+ * Add a new callback to the event group
+ *
+ * @param {function(): ?Object} callback Callback given the currentTime of
+ * callback.
+ */
+TestTimelineGroup.prototype.add = function(callback) {
+ if (this.lateCallbacks === null) {
+ this.startCallbacks.unshift(callback);
+ } else {
+ this.lateCallbacks.unshift(callback);
+ }
+
+ // Trim out extra 'function() { ... }'
+ var callbackString = callback.name;
+ // FIXME: This should probably unindent too....
+ this.info.innerHTML += '<div>' + callbackString + '</div>';
+};
+
+/**
+ * Reset this event group to the state before start was called.
+ */
+TestTimelineGroup.prototype.reset = function() {
+ this.lateCallbacks = null;
+
+ var callbacks = this.startCallbacks.slice(0);
+ this.setup_();
+ while (callbacks.length > 0) {
+ var callback = callbacks.shift();
+ this.add(callback);
+ }
+};
+
+/**
+ * Tell the event group that the timeline has started and that any callbacks
+ * added from now are dynamically generated and hence should be cleared when a
+ * reset is called.
+ */
+TestTimelineGroup.prototype.start = function() {
+ this.lateCallbacks = new Array();
+};
+
+/**
+ * Call all the callbacks in the EventGroup.
+ */
+TestTimelineGroup.prototype.call = function() {
+ var callbacks = (this.startCallbacks.slice(0)).concat(this.lateCallbacks);
+ var statuses = this.info.children;
+
+ var overallResult = true;
+ while (callbacks.length > 0) {
+ var callback = callbacks.pop();
+
+ var status_ = statuses[statuses.length - callbacks.length - 1];
+
+ if (typeof callback == 'function') {
+ log('TestTimelineGroup', 'calling function', callback);
+ try {
+ callback();
+ } catch (e) {
+ // On IE the only way to get the real stack is to do this
+ window.onerror(e.message, e.fileName, e.lineNumber, e);
+ // On other browsers we want to throw the error later
+ setTimeout(function () { throw e; }, 0);
+ }
+ } else {
+ log('TestTimelineGroup', 'calling test', callback);
+ var result = callback.step(callback.f);
+ callback.done();
+ }
+
+ if (result === undefined || result == null) {
+ overallResult = overallResult && true;
+
+ status_.style.color = 'green';
+ } else {
+ overallResult = overallResult && false;
+ status_.style.color = 'red';
+ status_.innerHTML += '<div>' + result.toString() + '</div>';
+ }
+ }
+ if (overallResult) {
+ this.marker.src = '../img/success.png';
+ } else {
+ this.marker.src = '../img/error.png';
+ }
+}
+
+/**
+ * Draw the EventGroup's marker at the correct position on the timeline.
+ *
+ * FIXME(mithro): This mixes display and control :(
+ *
+ * @param {number} endTime The endtime of the timeline in millis. Used to
+ * display the marker at the right place on the timeline.
+ */
+TestTimelineGroup.prototype.draw = function(container, endTime) {
+ this.marker.title = this.millis + 'ms';
+ this.marker.className = 'marker';
+ this.marker.src = '../img/unknown.png';
+
+ var mleft = 'calc(100% - 10px)';
+ if (endTime != 0) {
+ mleft = 'calc(' + (this.millis / endTime) * 100.0 + '%' + ' - 10px)';
+ }
+ this.marker.style.left = mleft;
+
+ container.appendChild(this.marker);
+
+ this.info.className = 'info';
+ container.appendChild(this.info);
+
+ // Display details about the events at this time period when hovering over
+ // the marker.
+ this.marker.onmouseover = function() {
+ this.style.display = 'block';
+ }.bind(this.info);
+
+ this.marker.onmouseout = function() {
+ this.style.display = 'none';
+ }.bind(this.info);
+
+
+ var offset = Math.ceil(this.info.offsetWidth / 2);
+ var ileft = 'calc(100% - ' + offset + 'px)';
+ if (endTime != 0) {
+ ileft = 'calc(' + (this.millis / endTime) * 100.0 + '%' + ' - ' + offset +
+ 'px)';
+ }
+ this.info.style.left = ileft;
+
+ this.info.style.display = 'none';
+};
+
+
+
+/**
+ * Moves the testharness_timeline in "real time".
+ * (IE 1 test second takes 1 real second).
+ *
+ * @constructor
+ */
+function RealtimeRunner(timeline) {
+ this.timeline = timeline;
+
+ // Capture the real requestAnimationFrame so we can run in 'real time' mode
+ // rather than as fast as possible.
+ var nativeRequestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame;
+ this.boundRequestAnimationFrame = function(f) {
+ nativeRequestAnimationFrame(f.bind(this))
+ };
+ this.now = window.Date.now;
+
+ this.zeroTime = null; // Time the page loaded
+ this.pauseStartTime = null; // Time at which we paused raf
+ this.timeDrift = 0; // Amount we have been stopped for
+}
+
+/**
+ * Callback called from nativeRequestAnimationFrame.
+ *
+ * @private
+ * @param {number} timestamp The current time for the animation frame
+ * (in millis).
+ */
+RealtimeRunner.prototype.animationFrame_ = function(timestamp) {
+ if (this.zeroTime === null) {
+ this.zeroTime = timestamp;
+ }
+
+ // Are we paused? Stop calling requestAnimationFrame.
+ if (this.pauseStartTime != null) {
+ return;
+ }
+
+ var virtualAnimationTime = timestamp - this.zeroTime - this.timeDrift;
+ var endTime = this.timeline.endTime_;
+ // If we have no events paste t=0, endTime is going to be zero. Instead
+ // make the test run for 2 minutes.
+ if (endTime == 0) {
+ endTime = 120e3;
+ }
+
+ // Do we still have time to go?
+ if (virtualAnimationTime < endTime) {
+ try {
+ this.timeline.setTime(virtualAnimationTime);
+ } finally {
+ this.boundRequestAnimationFrame(this.animationFrame_);
+ }
+
+ } else {
+ // Have we gone past endTime_? Force the harness to its endTime_.
+
+ this.timeline.setTime(endTime);
+ // Don't continue to raf
+ }
+};
+
+RealtimeRunner.prototype.start = function() {
+ if (this.pauseStartTime != null) {
+ this.timeDrift += (this.now() - this.pauseStartTime);
+ this.pauseStartTime = null;
+ }
+ this.boundRequestAnimationFrame(this.animationFrame_);
+};
+
+RealtimeRunner.prototype.pause = function() {
+ if (this.pauseStartTime != null) {
+ return;
+ }
+ this.pauseStartTime = this.now();
+};
+
+
+/**
+ * Class for storing events that happen during at given times (such as
+ * animation checks, or setTimeout).
+ *
+ * @constructor
+ */
+function TestTimeline(everyFrame) {
+ log('TestTimeline', 'constructor', everyFrame);
+ /**
+ * Stores the events which are upcoming.
+ *
+ * @type Object.<number, TestTimelineGroup>
+ * @private
+ */
+ this.timeline_ = new Array();
+
+ this.everyFrame = everyFrame;
+ this.frameMillis = 1000.0 / 60; //60fps
+
+ this.currentTime_ = -this.frameMillis;
+
+ // Schedule an event at t=0, needed temporarily.
+ this.schedule(function() {}, 0);
+
+ this.reset();
+
+ this.runner_ = new RealtimeRunner(this);
+}
+
+/**
+ * Create the GUI controller for the timeline.
+ * @param {Element} body DOM element to add the GUI too, normally the <body>
+ * element.
+ */
+TestTimeline.prototype.createGUI = function(body) {
+ // HTML needed to create the timeline UI
+ this.div = document.createElement('div');
+ this.div.id = 'timeline';
+
+ this.timelinebar = document.createElement('div');
+ this.timelinebar.className = 'bar';
+
+ this.timelineprogress = document.createElement('div');
+ this.timelineprogress.className = 'progress';
+
+ this.timelinebar.appendChild(this.timelineprogress);
+ this.div.appendChild(this.timelinebar);
+
+ this.next = document.createElement('button');
+ this.next.innerText = '>';
+ this.next.id = 'next';
+ this.next.onclick = this.toNextEvent.bind(this);
+ this.div.appendChild(this.next);
+
+ this.prev = document.createElement('button');
+ this.prev.innerText = '<';
+ this.prev.id = 'prev';
+ this.prev.onclick = this.toPrevEvent.bind(this);
+ this.div.appendChild(this.prev);
+
+ this.control = document.createElement('button');
+ this.control.innerText = 'Pause';
+ this.control.id = 'control';
+ this.control.onclick = function() {
+ if (this.control.innerText == 'Go!') {
+ this.runner_.start();
+ this.control.innerText = 'Pause';
+ } else {
+ this.runner_.pause();
+ this.control.innerText = 'Go!';
+ }
+ }.bind(this);
+ this.div.appendChild(this.control);
+
+ body.appendChild(this.div);
+}
+
+/**
+ * Update GUI elements.
+ *
+ * @private
+ */
+TestTimeline.prototype.updateGUI = function () {
+ // Update the timeline
+ var width = "100%";
+ if (this.endTime_ != 0) {
+ width = (this.currentTime_ / this.endTime_) * 100.0 +'%'
+ }
+ this.timelineprogress.style.width = width;
+ this.timelinebar.title = (this.currentTime_).toFixed(0) + 'ms';
+};
+
+
+/**
+ * Sort the timeline into run order. Should be called after adding something to
+ * the timeline.
+ *
+ * @private
+ */
+TestTimeline.prototype.sort_ = function() {
+ this.timeline_.sort(function(a,b) {
+ return a.millis - b.millis;
+ });
+};
+
+/**
+ * Schedule something to be called at a given time.
+ *
+ * @param {function(number)} callback Callback to call after the number of millis
+ * have elapsed.
+ * @param {number} millis Milliseconds after start at which the callback should
+ * be called.
+ */
+TestTimeline.prototype.schedule = function(callback, millis) {
+ log('TestTimeline', 'schedule', millis, callback);
+ if (millis < this.currentTime_) {
+ // Can't schedule something in the past?
+ return;
+ }
+
+ // See if there is something at that time in the timeline already?
+ var timeline = this.timeline_.slice(0);
+ var group = null;
+ while (timeline.length > 0) {
+ if (timeline[0].millis == millis) {
+ group = timeline[0];
+ break;
+ } else {
+ timeline.shift();
+ }
+ }
+
+ // If not, create a node at that time.
+ if (group === null) {
+ group = new TestTimelineGroup(millis);
+ this.timeline_.unshift(group);
+ this.sort_();
+ }
+ group.add(callback);
+
+ var newEndTime = this.timeline_.slice(-1)[0].millis * 1.1;
+ if (this.endTime_ != newEndTime) {
+ this.endTime_ = newEndTime;
+ }
+};
+
+/**
+ * Return the current time in milliseconds.
+ */
+TestTimeline.prototype.now = function() {
+ log('TestTimeline', 'now', Math.max(this.currentTime_, 0));
+ return Math.max(this.currentTime_, 0);
+};
+
+/**
+ * Set the current time to a given value.
+ *
+ * @param {number} millis Time in milliseconds to set the current time too.
+ */
+TestTimeline.prototype.setTime = function(millis) {
+ log('TestTimeline', 'setTime', millis);
+ // Time is going backwards, we actually have to reset and go forwards as
+ // events can cause the creation of more events.
+ if (this.currentTime_ > millis) {
+ this.reset();
+ this.start();
+ }
+
+ var events = this.timeline_.slice(0);
+
+ // Already processed events
+ while (events.length > 0 && events[0].millis <= this.currentTime_) {
+ events.shift();
+ }
+
+ while (this.currentTime_ < millis) {
+ var event_ = null;
+ var moveTo = millis;
+
+ if (events.length > 0 && events[0].millis <= millis) {
+ event_ = events.shift();
+ moveTo = event_.millis;
+ }
+
+ // Call the callback
+ if (this.currentTime_ != moveTo) {
+ log('TestTimeline', 'setting time to', moveTo);
+ this.currentTime_ = moveTo;
+ this.animationFrame(this.currentTime_);
+ }
+
+ if (event_) {
+ event_.call();
+ }
+ }
+
+ this.updateGUI();
+
+ if (millis >= this.endTime_) {
+ this.done();
+ }
+};
+
+/**
+ * Call all callbacks registered for the next (virtual) animation frame.
+ *
+ * @param {number} millis Time in milliseconds.
+ * @private
+ */
+TestTimeline.prototype.animationFrame = function(millis) {
+ /* FIXME(mithro): Code should appear here to allow testing of running
+ * every animation frame.
+
+ if (this.everyFrame) {
+ }
+
+ */
+
+ var callbacks = this.animationFrameCallbacks;
+ callbacks.reverse();
+ this.animationFrameCallbacks = [];
+ for (var i = 0; i < callbacks.length; i++) {
+ log('TestTimeline raf callback', callbacks[i], millis);
+ try {
+ callbacks[i](millis);
+ } catch (e) {
+ // On IE the only way to get the real stack is to do this
+ window.onerror(e.message, e.fileName, e.lineNumber, e);
+ // On other browsers we want to throw the error later
+ setTimeout(function () { throw e; }, 0);
+ }
+ }
+};
+
+/**
+ * Set a callback to run at the next (virtual) animation frame.
+ *
+ * @param {function(millis)} millis Time in milliseconds to set the current
+ * time too.
+ */
+TestTimeline.prototype.requestAnimationFrame = function(callback) {
+ // FIXME: This should return a reference that allows people to cancel the
+ // animationFrame callback.
+ this.animationFrameCallbacks.push(callback);
+ return -1;
+};
+
+/**
+ * Go to next scheduled event in timeline.
+ */
+TestTimeline.prototype.toNextEvent = function() {
+ var events = this.timeline_.slice(0);
+ while (events.length > 0 && events[0].millis <= this.currentTime_) {
+ events.shift();
+ }
+ if (events.length > 0) {
+ this.setTime(events[0].millis);
+
+ if (this.autorun_) {
+ setTimeout(this.toNextEvent.bind(this), 0);
+ }
+
+ return true;
+ } else {
+ this.setTime(this.endTime_);
+ return false;
+ }
+
+};
+
+/**
+ * Go to previous scheduled event in timeline.
+ * (This actually goes back to time zero and then forward to this event.)
+ */
+TestTimeline.prototype.toPrevEvent = function() {
+ var events = this.timeline_.slice(0);
+ while (events.length > 0 &&
+ events[events.length - 1].millis >= this.currentTime_) {
+ events.pop();
+ }
+ if (events.length > 0) {
+ this.setTime(events[events.length - 1].millis);
+ return true;
+ } else {
+ this.setTime(0);
+ return false;
+ }
+};
+
+/**
+ * Reset the timeline to time zero.
+ */
+TestTimeline.prototype.reset = function () {
+ for (var t in this.timeline_) {
+ this.timeline_[t].reset();
+ }
+
+ this.currentTime_ = -this.frameMillis;
+ this.animationFrameCallbacks = [];
+ this.started_ = false;
+};
+
+/**
+ * Call to initiate starting???
+ */
+TestTimeline.prototype.start = function () {
+ this.started_ = true;
+
+ var parent = this;
+
+ for (var t in this.timeline_) {
+ this.timeline_[t].start();
+ // FIXME(mithro) this is confusing...
+ this.timeline_[t].draw(this.timelinebar, this.endTime_);
+
+ this.timeline_[t].marker.onclick = function(event) {
+ parent.setTime(this.millis);
+ event.stopPropagation();
+ }.bind(this.timeline_[t]);
+ }
+
+ this.timelinebar.onclick = function(evt) {
+ var setPercent =
+ ((evt.clientX - this.offsetLeft) / this.offsetWidth);
+ parent.setTime(setPercent * parent.endTime_);
+ }.bind(this.timelinebar);
+};
+
+TestTimeline.prototype.done = function () {
+ log('TestTime', 'done');
+ done();
+};
+
+TestTimeline.prototype.autorun = function() {
+ this.autorun_ = true;
+ this.toNextEvent();
+};
+
+function testharness_timeline_setup() {
+ log('testharness_timeline_setup');
+ testharness_timeline.createGUI(document.getElementsByTagName('body')[0]);
+ testharness_timeline.start();
+ testharness_timeline.updateGUI();
+
+ // Start running the test on message
+ if ('#message' == window.location.hash) {
+ window.addEventListener('message', function(evt) {
+ switch (evt.data['type']) {
+ case 'start':
+ if (evt.data['url'] == window.location.href) {
+ testharness_timeline.autorun();
+ }
+ break;
+ }
+ });
+ } else if ('#auto' == window.location.hash || '#coverage' == window.location.hash) {
+ // Run the test as fast as possible, skipping time.
+
+ // Need non-zero timeout to allow chrome to run other code.
+ setTimeout(testharness_timeline.autorun.bind(testharness_timeline), 1);
+
+ } else if (inExploreMode()) {
+ setTimeout(testharness_timeline.runner_.start.bind(testharness_timeline.runner_), 1);
+ } else {
+ alert('Unknown start mode.');
+ }
+}
+
+// Capture testharness's test as we are about to screw with it.
+var testharness_test = window.test;
+
+function override_at(replacement_at, f, args) {
+ var orig_at = window.at;
+ window.at = replacement_at;
+ f.apply(null, args);
+ window.at = orig_at;
+}
+
+function timing_test(f, desc) {
+ /**
+ * at function inside a timing_test function allows testing things at a
+ * given time rather then onload.
+ * @param {number} seconds Seconds after page load to run the tests.
+ * @param {function()} f Closure containing the asserts to be run.
+ * @param {string} desc Description
+ */
+ var at = function(seconds, f, desc_at) {
+ assert_true(typeof seconds == 'number', "at's first argument shoud be a number.");
+ assert_true(!isNaN(seconds), "at's first argument should be a number not NaN!");
+ assert_true(seconds >= 0, "at's first argument should be greater then 0.");
+ assert_true(isFinite(seconds), "at's first argument should be finite.");
+
+ assert_true(typeof f == 'function', "at's second argument should be a function.");
+
+ // Deliberately hoist the desc if we where not given one.
+ if (typeof desc_at == 'undefined' || desc_at == null || desc_at.length == 0) {
+ desc_at = desc;
+ }
+
+ // And then provide 'Unnamed' as a default
+ if (typeof desc_at == 'undefined' || desc_at == null || desc_at.length == 0) {
+ desc_at = 'Unnamed assert';
+ }
+
+ var t = async_test(desc_at + ' at t=' + seconds + 's');
+ t.f = f;
+ window.testharness_timeline.schedule(t, seconds * 1000.0);
+ };
+ override_at(at, f);
+}
+
+function test_without_at(f, desc) {
+ // Make sure calling at inside a test() function is a failure.
+ override_at(function() {
+ throw {'message': 'Can not use at() inside a test, use a timing_test instead.'};
+ }, function() { testharness_test(f, desc); });
+}
+
+/**
+ * at function schedules a to be called at a given point.
+ * @param {number} seconds Seconds after page load to run the function.
+ * @param {function()} f Function to be called. Called with no arguments
+ */
+function at(seconds, f) {
+ assert_true(typeof seconds == 'number', "at's first argument shoud be a number.");
+ assert_true(typeof f == 'function', "at's second argument should be a function.");
+
+ window.testharness_timeline.schedule(f, seconds * 1000.0);
+}
+
+window.testharness_after_loaded = function() {
+ log('testharness_after_loaded');
+ /**
+ * These steps needs to occur after testharness is loaded.
+ */
+ setup(function() {}, {
+ explicit_timeout: true,
+ explicit_done: ((typeof window.testharness_timeline) !== 'undefined')});
+
+ /**
+ * Create an testharness test which makes sure the page contains no
+ * javascript errors. This is needed otherwise if the page contains errors
+ * then preventing the tests loading it will look like it passed.
+ */
+ var pageerror_test = async_test('Page contains no errors');
+
+ window.onerror = function(msg, url, line, e) {
+ var msg = '\nError in ' + url + '\n' +
+ 'Line ' + line + ': ' + msg + '\n';
+
+ if (typeof e != "undefined") {
+ msg += e.stack;
+ }
+
+ pageerror_test.is_done = true;
+ pageerror_test.step(function() {
+ throw new AssertionError(msg);
+ });
+ pageerror_test.is_done = false;
+ };
+
+ var pageerror_tests;
+ function pageerror_othertests_finished(test, harness) {
+ if (harness == null && pageerror_tests == null) {
+ return;
+ }
+
+ if (pageerror_tests == null) {
+ pageerror_tests = harness;
+ }
+
+ if (pageerror_tests.all_loaded && pageerror_tests.num_pending == 1) {
+ pageerror_test.done();
+ }
+ }
+ add_result_callback(pageerror_othertests_finished);
+ addEventListener('load', pageerror_othertests_finished);
+
+};
+
+loadScript('../testharness/testharness.js', {coverage: false});
+document.write('<script type="text/javascript">window.testharness_after_loaded();</script>');
+loadCSS('../testharness/testharness.css');
+loadCSS('../testharness_timing.css');
+
+if (testType() == 'auto') {
+ var checksFile = location.pathname;
+ checksFile = checksFile.replace(/disabled-/, '');
+ checksFile = checksFile.replace(/.html$/, '-checks.js')
+ loadScript(checksFile, {coverage: false});
+}
+
+document.write('<div id="log"></div>');
+loadScript('../testharness/testharnessreport.js', {coverage: false});
+
+if (!hasFlag('nopolyfill')) {
+ loadScript('../../web-animations.js');
+}
+
+addEventListener('load', function() {
+ if (window._WebAnimationsTestingUtilities) {
+ // Currently enabling asserts breaks auto-test-initial in IE.
+ //_WebAnimationsTestingUtilities._enableAsserts();
+ }
+});
+
+// Don't export the timing functions in unittests.
+if (testType() != 'unit') {
+ addEventListener('load', testharness_timeline_setup);
+
+ window.at = at;
+ window.timing_test = timing_test;
+ window.test = test_without_at;
+
+ // Expose the extra API
+ window.testharness_timeline = new TestTimeline();
+
+ // Override existing timing functions
+ window.requestAnimationFrame =
+ testharness_timeline.requestAnimationFrame.bind(testharness_timeline);
+ window.performance.now = null;
+ window.Date.now = testharness_timeline.now.bind(testharness_timeline);
+}
+
+})();

Powered by Google App Engine
This is Rietveld 408576698