Effects: Add tests for jQuery.Tween

This commit is contained in:
Corey Frang 2015-05-18 17:11:21 -04:00
parent 3a0d582cf6
commit cdaed15c7e
10 changed files with 6246 additions and 446 deletions

View File

@ -104,7 +104,7 @@ module.exports = function( grunt ) {
gruntfile: "Gruntfile.js", gruntfile: "Gruntfile.js",
// Right now, check only test helpers // Right now, check only test helpers
test: [ "test/data/testrunner.js" ], test: [ "test/data/testrunner.js", "test/unit/tween.js" ],
release: [ "build/*.js", "!build/release-notes.js" ], release: [ "build/*.js", "!build/release-notes.js" ],
tasks: "build/tasks/*.js" tasks: "build/tasks/*.js"
}, },
@ -126,7 +126,8 @@ module.exports = function( grunt ) {
"selector", "selector",
"serialize", "serialize",
"support", "support",
"traversing" "traversing",
"tween"
] ]
}, },
watch: { watch: {

View File

@ -1,27 +0,0 @@
(The BSD License)
Copyright (c) 2010-2014, Christian Johansen, christian@cjohansen.no
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of Christian Johansen nor the names of his contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -1,409 +0,0 @@
/*jslint eqeqeq: false, plusplus: false, evil: true, onevar: false, browser: true, forin: false*/
/*global module, require, window*/
/**
* Fake timer API
* setTimeout
* setInterval
* clearTimeout
* clearInterval
* tick
* reset
* Date
*
* Inspired by jsUnitMockTimeOut from JsUnit
*
* @author Christian Johansen (christian@cjohansen.no)
* @license BSD
*
* Copyright (c) 2010-2013 Christian Johansen
*/
"use strict";
if (typeof sinon == "undefined") {
var sinon = {};
}
(function (global) {
// node expects setTimeout/setInterval to return a fn object w/ .ref()/.unref()
// browsers, a number.
// see https://github.com/cjohansen/Sinon.JS/pull/436
var timeoutResult = setTimeout(function() {}, 0);
var addTimerReturnsObject = typeof timeoutResult === 'object';
clearTimeout(timeoutResult);
var id = 1;
function addTimer(args, recurring) {
if (args.length === 0) {
throw new Error("Function requires at least 1 parameter");
}
if (typeof args[0] === "undefined") {
throw new Error("Callback must be provided to timer calls");
}
var toId = id++;
var delay = args[1] || 0;
if (!this.timeouts) {
this.timeouts = {};
}
this.timeouts[toId] = {
id: toId,
func: args[0],
callAt: this.now + delay,
invokeArgs: Array.prototype.slice.call(args, 2)
};
if (recurring === true) {
this.timeouts[toId].interval = delay;
}
if (addTimerReturnsObject) {
return {
id: toId,
ref: function() {},
unref: function() {}
};
}
else {
return toId;
}
}
function parseTime(str) {
if (!str) {
return 0;
}
var strings = str.split(":");
var l = strings.length, i = l;
var ms = 0, parsed;
if (l > 3 || !/^(\d\d:){0,2}\d\d?$/.test(str)) {
throw new Error("tick only understands numbers and 'h:m:s'");
}
while (i--) {
parsed = parseInt(strings[i], 10);
if (parsed >= 60) {
throw new Error("Invalid time " + str);
}
ms += parsed * Math.pow(60, (l - i - 1));
}
return ms * 1000;
}
function createObject(object) {
var newObject;
if (Object.create) {
newObject = Object.create(object);
} else {
var F = function () {};
F.prototype = object;
newObject = new F();
}
newObject.Date.clock = newObject;
return newObject;
}
sinon.clock = {
now: 0,
create: function create(now) {
var clock = createObject(this);
if (typeof now == "number") {
clock.now = now;
}
if (!!now && typeof now == "object") {
throw new TypeError("now should be milliseconds since UNIX epoch");
}
return clock;
},
setTimeout: function setTimeout(callback, timeout) {
return addTimer.call(this, arguments, false);
},
clearTimeout: function clearTimeout(timerId) {
if (!timerId) {
// null appears to be allowed in most browsers, and appears to be relied upon by some libraries, like Bootstrap carousel
return;
}
if (!this.timeouts) {
this.timeouts = [];
}
// in Node, timerId is an object with .ref()/.unref(), and
// its .id field is the actual timer id.
if (typeof timerId === 'object') {
timerId = timerId.id
}
if (timerId in this.timeouts) {
delete this.timeouts[timerId];
}
},
setInterval: function setInterval(callback, timeout) {
return addTimer.call(this, arguments, true);
},
clearInterval: function clearInterval(timerId) {
this.clearTimeout(timerId);
},
setImmediate: function setImmediate(callback) {
var passThruArgs = Array.prototype.slice.call(arguments, 1);
return addTimer.call(this, [callback, 0].concat(passThruArgs), false);
},
clearImmediate: function clearImmediate(timerId) {
this.clearTimeout(timerId);
},
tick: function tick(ms) {
ms = typeof ms == "number" ? ms : parseTime(ms);
var tickFrom = this.now, tickTo = this.now + ms, previous = this.now;
var timer = this.firstTimerInRange(tickFrom, tickTo);
var firstException;
while (timer && tickFrom <= tickTo) {
if (this.timeouts[timer.id]) {
tickFrom = this.now = timer.callAt;
try {
this.callTimer(timer);
} catch (e) {
firstException = firstException || e;
}
}
timer = this.firstTimerInRange(previous, tickTo);
previous = tickFrom;
}
this.now = tickTo;
if (firstException) {
throw firstException;
}
return this.now;
},
firstTimerInRange: function (from, to) {
var timer, smallest = null, originalTimer;
for (var id in this.timeouts) {
if (this.timeouts.hasOwnProperty(id)) {
if (this.timeouts[id].callAt < from || this.timeouts[id].callAt > to) {
continue;
}
if (smallest === null || this.timeouts[id].callAt < smallest) {
originalTimer = this.timeouts[id];
smallest = this.timeouts[id].callAt;
timer = {
func: this.timeouts[id].func,
callAt: this.timeouts[id].callAt,
interval: this.timeouts[id].interval,
id: this.timeouts[id].id,
invokeArgs: this.timeouts[id].invokeArgs
};
}
}
}
return timer || null;
},
callTimer: function (timer) {
if (typeof timer.interval == "number") {
this.timeouts[timer.id].callAt += timer.interval;
} else {
delete this.timeouts[timer.id];
}
try {
if (typeof timer.func == "function") {
timer.func.apply(null, timer.invokeArgs);
} else {
eval(timer.func);
}
} catch (e) {
var exception = e;
}
if (!this.timeouts[timer.id]) {
if (exception) {
throw exception;
}
return;
}
if (exception) {
throw exception;
}
},
reset: function reset() {
this.timeouts = {};
},
Date: (function () {
var NativeDate = Date;
function ClockDate(year, month, date, hour, minute, second, ms) {
// Defensive and verbose to avoid potential harm in passing
// explicit undefined when user does not pass argument
switch (arguments.length) {
case 0:
return new NativeDate(ClockDate.clock.now);
case 1:
return new NativeDate(year);
case 2:
return new NativeDate(year, month);
case 3:
return new NativeDate(year, month, date);
case 4:
return new NativeDate(year, month, date, hour);
case 5:
return new NativeDate(year, month, date, hour, minute);
case 6:
return new NativeDate(year, month, date, hour, minute, second);
default:
return new NativeDate(year, month, date, hour, minute, second, ms);
}
}
return mirrorDateProperties(ClockDate, NativeDate);
}())
};
function mirrorDateProperties(target, source) {
if (source.now) {
target.now = function now() {
return target.clock.now;
};
} else {
delete target.now;
}
if (source.toSource) {
target.toSource = function toSource() {
return source.toSource();
};
} else {
delete target.toSource;
}
target.toString = function toString() {
return source.toString();
};
target.prototype = source.prototype;
target.parse = source.parse;
target.UTC = source.UTC;
target.prototype.toUTCString = source.prototype.toUTCString;
for (var prop in source) {
if (source.hasOwnProperty(prop)) {
target[prop] = source[prop];
}
}
return target;
}
var methods = ["Date", "setTimeout", "setInterval",
"clearTimeout", "clearInterval"];
if (typeof global.setImmediate !== "undefined") {
methods.push("setImmediate");
}
if (typeof global.clearImmediate !== "undefined") {
methods.push("clearImmediate");
}
function restore() {
var method;
for (var i = 0, l = this.methods.length; i < l; i++) {
method = this.methods[i];
if (global[method].hadOwnProperty) {
global[method] = this["_" + method];
} else {
try {
delete global[method];
} catch (e) {}
}
}
// Prevent multiple executions which will completely remove these props
this.methods = [];
}
function stubGlobal(method, clock) {
clock[method].hadOwnProperty = Object.prototype.hasOwnProperty.call(global, method);
clock["_" + method] = global[method];
if (method == "Date") {
var date = mirrorDateProperties(clock[method], global[method]);
global[method] = date;
} else {
global[method] = function () {
return clock[method].apply(clock, arguments);
};
for (var prop in clock[method]) {
if (clock[method].hasOwnProperty(prop)) {
global[method][prop] = clock[method][prop];
}
}
}
global[method].clock = clock;
}
sinon.useFakeTimers = function useFakeTimers(now) {
var clock = sinon.clock.create(now);
clock.restore = restore;
clock.methods = Array.prototype.slice.call(arguments,
typeof now == "number" ? 1 : 0);
if (clock.methods.length === 0) {
clock.methods = methods;
}
for (var i = 0, l = clock.methods.length; i < l; i++) {
stubGlobal(clock.methods[i], clock);
}
return clock;
};
}(typeof global != "undefined" && typeof global !== "function" ? global : this));
sinon.timers = {
setTimeout: setTimeout,
clearTimeout: clearTimeout,
setImmediate: (typeof setImmediate !== "undefined" ? setImmediate : undefined),
clearImmediate: (typeof clearImmediate !== "undefined" ? clearImmediate: undefined),
setInterval: setInterval,
clearInterval: clearInterval,
Date: Date
};
if (typeof module !== 'undefined' && module.exports) {
module.exports = sinon;
}

5931
external/sinon/sinon-1.14.1.js vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -59,8 +59,10 @@ Tween.propHooks = {
get: function( tween ) { get: function( tween ) {
var result; var result;
if ( tween.elem[ tween.prop ] != null && // Use a property on the element directly when it is not a DOM element,
(!tween.elem.style || tween.elem.style[ tween.prop ] == null) ) { // or when there is no matching style property that exists.
if ( tween.elem.nodeType !== 1 ||
tween.elem[ tween.prop ] != null && tween.elem.style[ tween.prop ] == null ) {
return tween.elem[ tween.prop ]; return tween.elem[ tween.prop ];
} }
@ -78,7 +80,7 @@ Tween.propHooks = {
// Use .style if available and use plain properties where available. // Use .style if available and use plain properties where available.
if ( jQuery.fx.step[ tween.prop ] ) { if ( jQuery.fx.step[ tween.prop ] ) {
jQuery.fx.step[ tween.prop ]( tween ); jQuery.fx.step[ tween.prop ]( tween );
} else if ( tween.elem.style && } else if ( tween.elem.nodeType === 1 &&
( tween.elem.style[ jQuery.cssProps[ tween.prop ] ] != null || ( tween.elem.style[ jQuery.cssProps[ tween.prop ] ] != null ||
jQuery.cssHooks[ tween.prop ] ) ) { jQuery.cssHooks[ tween.prop ] ) ) {
jQuery.style( tween.elem, tween.prop, tween.now + tween.unit ); jQuery.style( tween.elem, tween.prop, tween.now + tween.unit );

View File

@ -154,7 +154,8 @@ jQuery.extend({
expr: { expr: {
attrHandle: {}, attrHandle: {},
match: { match: {
bool: /^(?:checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped)$/i, bool: new RegExp( "^(?:checked|selected|async|autofocus|autoplay|controls|defer" +
"|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped)$", "i" ),
needsContext: /^[\x20\t\r\n\f]*[>+~]/ needsContext: /^[\x20\t\r\n\f]*[>+~]/
} }
} }

View File

@ -285,7 +285,8 @@ this.loadTests = function() {
"unit/ajax.js", "unit/ajax.js",
"unit/effects.js", "unit/effects.js",
"unit/offset.js", "unit/offset.js",
"unit/dimensions.js" "unit/dimensions.js",
"unit/tween.js"
]; ];
// Ensure load order (to preserve test numbers) // Ensure load order (to preserve test numbers)

View File

@ -13,9 +13,9 @@
<script src="data/jquery-1.9.1.js"></script> <script src="data/jquery-1.9.1.js"></script>
<script src="../external/qunit/qunit.js"></script> <script src="../external/qunit/qunit.js"></script>
<script src="../external/sinon/sinon-1.14.1.js"></script>
<script src="../external/npo/npo.js"></script> <script src="../external/npo/npo.js"></script>
<script src="../external/requirejs/require.js"></script> <script src="../external/requirejs/require.js"></script>
<script src="../external/sinon/fake_timers.js"></script>
<!-- See testinit for the list of tests --> <!-- See testinit for the list of tests -->
<script src="data/testinit.js"></script> <script src="data/testinit.js"></script>

View File

@ -10,13 +10,15 @@ var oldRaf = window.requestAnimationFrame;
module("effects", { module("effects", {
setup: function() { setup: function() {
window.requestAnimationFrame = null; window.requestAnimationFrame = null;
this.clock = sinon.useFakeTimers( 505877050 ); this.sandbox = sinon.sandbox.create();
this.clock = this.sandbox.useFakeTimers( 505877050 );
this._oldInterval = jQuery.fx.interval; this._oldInterval = jQuery.fx.interval;
jQuery.fx.step = {};
jQuery.fx.interval = 10; jQuery.fx.interval = 10;
jQuery.now = Date.now; jQuery.now = Date.now;
}, },
teardown: function() { teardown: function() {
this.clock.restore(); this.sandbox.restore();
jQuery.now = Date.now; jQuery.now = Date.now;
jQuery.fx.stop(); jQuery.fx.stop();
jQuery.fx.interval = this._oldInterval; jQuery.fx.interval = this._oldInterval;

298
test/unit/tween.js Normal file
View File

@ -0,0 +1,298 @@
( function() {
// Can't test what ain't there
if ( !jQuery.fx ) {
return;
}
var oldRaf = window.requestAnimationFrame;
module( "tween", {
setup: function() {
window.requestAnimationFrame = null;
this.sandbox = sinon.sandbox.create();
this.clock = this.sandbox.useFakeTimers( 505877050 );
this._oldInterval = jQuery.fx.interval;
jQuery.fx.step = {};
jQuery.fx.interval = 10;
jQuery.now = Date.now;
},
teardown: function() {
this.sandbox.restore();
jQuery.now = Date.now;
jQuery.fx.stop();
jQuery.fx.interval = this._oldInterval;
window.requestAnimationFrame = oldRaf;
return moduleTeardown.apply( this, arguments );
}
} );
test( "jQuery.Tween - Default propHooks on plain objects", function() {
expect( 8 );
var propHooks, defaultHook, testObject, fakeTween, stepSpy;
propHooks = jQuery.Tween.propHooks;
equal( typeof propHooks, "object", "jQuery.Tween.propHooks exists" );
defaultHook = propHooks._default;
ok( defaultHook, "_default propHook exists" );
testObject = { test: 0 };
fakeTween = { elem: testObject, prop: "test", now: 10, unit: "px" };
equal( defaultHook.get( fakeTween ), 0, "Can get property of object" );
fakeTween.prop = "testMissing";
equal( defaultHook.get( fakeTween ), undefined, "Can get missing property on object" );
defaultHook.set( fakeTween );
equal( testObject.testMissing, 10, "Sets missing value properly on plain object" );
fakeTween.prop = "opacity";
defaultHook.set( fakeTween );
equal( testObject.opacity, 10, "Correctly set opacity on plain object" );
fakeTween.prop = "test";
stepSpy = jQuery.fx.step.test = this.sandbox.spy();
defaultHook.set( fakeTween );
ok( stepSpy.calledWith( fakeTween ), "Step function called with Tween" );
equal( testObject.test, 0, "Because step didn't set, value is unchanged" );
} );
test( "jQuery.Tween - Default propHooks on elements", function() {
expect( 19 );
var propHooks, defaultHook, testElement, fakeTween, cssStub, styleStub, stepSpy;
propHooks = jQuery.Tween.propHooks;
equal( typeof propHooks, "object", "jQuery.Tween.propHooks exists" );
defaultHook = propHooks._default;
ok( defaultHook, "_default propHook exists" );
testElement = jQuery( "<div>" )[ 0 ];
fakeTween = { elem: testElement, prop: "height", now: 10, unit: "px" };
cssStub = this.sandbox.stub( jQuery, "css" ).returns( 10 );
equal( defaultHook.get( fakeTween ), 10, "Gets expected style value" );
ok( cssStub.calledWith( testElement, "height", "" ), "Calls jQuery.css correctly" );
fakeTween.prop = "testOpti";
testElement.testOpti = 15;
cssStub.reset();
equal( defaultHook.get( fakeTween ), 15, "Gets expected value not defined on style" );
equal( cssStub.callCount, 0, "Did not call jQuery.css" );
fakeTween.prop = "testMissing";
equal( defaultHook.get( fakeTween ), 10, "Can get missing property on element" );
ok( cssStub.calledWith( testElement, "testMissing", "" ), "...using jQuery.css" );
cssStub.returns( "" );
equal( defaultHook.get( fakeTween ), 0, "Uses 0 for empty string" );
cssStub.returns( "auto" );
equal( defaultHook.get( fakeTween ), 0, "Uses 0 for 'auto'" );
cssStub.returns( null );
equal( defaultHook.get( fakeTween ), 0, "Uses 0 for null" );
cssStub.returns( undefined );
equal( defaultHook.get( fakeTween ), 0, "Uses 0 for undefined" );
cssStub.reset();
// Setters
styleStub = this.sandbox.stub( jQuery, "style" );
fakeTween.prop = "height";
defaultHook.set( fakeTween );
ok( styleStub.calledWith( testElement, "height", "10px" ),
"Calls jQuery.style with elem, prop, now+unit" );
styleStub.reset();
fakeTween.prop = "testMissing";
defaultHook.set( fakeTween );
equal( styleStub.callCount, 0, "Did not call jQuery.style for non css property" );
equal( testElement.testMissing, 10, "Instead, set value on element directly" );
jQuery.cssHooks.testMissing = jQuery.noop;
fakeTween.now = 11;
defaultHook.set( fakeTween );
delete jQuery.cssHooks.testMissing;
ok( styleStub.calledWith( testElement, "testMissing", "11px" ),
"Presence of cssHooks causes jQuery.style with elem, prop, now+unit" );
equal( testElement.testMissing, 10, "And value was unchanged" );
stepSpy = jQuery.fx.step.test = this.sandbox.spy();
styleStub.reset();
fakeTween.prop = "test";
defaultHook.set( fakeTween );
ok( stepSpy.calledWith( fakeTween ), "Step function called with Tween" );
equal( styleStub.callCount, 0, "Did not call jQuery.style" );
} );
test( "jQuery.Tween - Plain Object", function() {
expect( 13 );
var testObject = { test: 100 },
testOptions = { duration: 100 },
tween, easingSpy;
tween = jQuery.Tween( testObject, testOptions, "test", 0, "linear" );
equal( tween.elem, testObject, "Sets .element" );
equal( tween.options, testOptions, "sets .options" );
equal( tween.prop, "test", "sets .prop" );
equal( tween.end, 0, "sets .end" );
equal( tween.easing, "linear", "sets .easing when provided" );
equal( tween.start, 100, "Reads .start value during construction" );
equal( tween.now, 100, "Reads .now value during construction" );
easingSpy = this.sandbox.spy( jQuery.easing, "linear" );
equal( tween.run( 0.1 ), tween, ".run() returns this" );
equal( tween.now, 90, "Calculated tween" );
ok( easingSpy.calledWith( 0.1, 0.1 * testOptions.duration, 0, 1, testOptions.duration ),
"...using jQuery.easing.linear with back-compat arguments" );
equal( testObject.test, 90, "Set value" );
tween.run( 1 );
equal( testObject.test, 0, "Checking another value" );
tween.run( 0 );
equal( testObject.test, 100, "Can even go back in time" );
} );
test( "jQuery.Tween - Element", function() {
expect( 15 );
var testElement = jQuery( "<div>" ).css( "height", 100 )[ 0 ],
testOptions = { duration: 100 },
tween, easingSpy, eased;
tween = jQuery.Tween( testElement, testOptions, "height", 0 );
equal( tween.elem, testElement, "Sets .element" );
equal( tween.options, testOptions, "sets .options" );
equal( tween.prop, "height", "sets .prop" );
equal( tween.end, 0, "sets .end" );
equal( tween.easing, jQuery.easing._default, "sets .easing to default when not provided" );
equal( tween.unit, "px", "sets .unit to px when not provided" );
equal( tween.start, 100, "Reads .start value during construction" );
equal( tween.now, 100, "Reads .now value during construction" );
easingSpy = this.sandbox.spy( jQuery.easing, "swing" );
equal( tween.run( 0.1 ), tween, ".run() returns this" );
equal( tween.pos, jQuery.easing.swing( 0.1 ), "set .pos" );
eased = 100 - ( jQuery.easing.swing( 0.1 ) * 100 );
equal( tween.now, eased, "Calculated tween" );
ok( easingSpy.calledWith( 0.1, 0.1 * testOptions.duration, 0, 1, testOptions.duration ),
"...using jQuery.easing.linear with back-compat arguments" );
equal( parseFloat( testElement.style.height ).toFixed( 5 ), eased.toFixed( 5 ), "Set value" );
tween.run( 1 );
equal( testElement.style.height, "0px", "Checking another value" );
tween.run( 0 );
equal( testElement.style.height, "100px", "Can even go back in time" );
} );
test( "jQuery.Tween - No duration", function() {
expect( 3 );
var testObject = { test: 100 },
testOptions = { duration: 0 },
tween, easingSpy;
tween = jQuery.Tween( testObject, testOptions, "test", 0 );
easingSpy = this.sandbox.spy( jQuery.easing, "swing" );
tween.run( 0.5 );
equal( tween.pos, 0.5, "set .pos correctly" );
equal( testObject.test, 50, "set value on object correctly" );
equal( easingSpy.callCount, 0, "didn't ease the value" );
} );
test( "jQuery.Tween - step function option", function() {
expect( 4 );
var testObject = { test: 100 },
testOptions = { duration: 100, step: this.sandbox.spy() },
tween, propHookSpy;
propHookSpy = this.sandbox.spy( jQuery.Tween.propHooks._default, "set" );
tween = jQuery.Tween( testObject, testOptions, "test", 0, "linear" );
equal( testOptions.step.callCount, 0, "didn't call step on create" );
tween.run( 0.5 );
ok( testOptions.step.calledOn( testObject ),
"Called step function in context of animated object" );
ok( testOptions.step.calledWith( 50, tween ), "Called step function with correct parameters" );
ok( testOptions.step.calledBefore( propHookSpy ),
"Called step function before calling propHook.set" );
} );
test( "jQuery.Tween - custom propHooks", function() {
expect( 3 );
var testObject = {},
testOptions = { duration: 100, step: this.sandbox.spy() },
propHook = {
get: sinon.stub().returns( 100 ),
set: sinon.stub()
},
tween;
jQuery.Tween.propHooks.testHooked = propHook;
tween = jQuery.Tween( testObject, testOptions, "testHooked", 0, "linear" );
ok( propHook.get.calledWith( tween ), "called propHook.get on create" );
equal( tween.now, 100, "Used return value from propHook.get" );
tween.run( 0.5 );
ok( propHook.set.calledWith( tween ), "Called propHook.set function with correct parameters" );
delete jQuery.Tween.propHooks.testHooked;
} );
test( "jQuery.Tween - custom propHooks - advanced values", function() {
expect( 5 );
var testObject = {},
testOptions = { duration: 100, step: this.sandbox.spy() },
propHook = {
get: sinon.stub().returns( [ 0, 0 ] ),
set: sinon.spy()
},
tween;
jQuery.Tween.propHooks.testHooked = propHook;
tween = jQuery.Tween( testObject, testOptions, "testHooked", [ 1, 1 ], "linear" );
ok( propHook.get.calledWith( tween ), "called propHook.get on create" );
deepEqual( tween.start, [ 0, 0 ], "Used return value from get" );
tween.run( 0.5 );
// Some day this NaN assumption might change - perhaps add a "calc" helper to the hooks?
ok( isNaN( tween.now ), "Used return value from propHook.get" );
equal( tween.pos, 0.5, "But the eased percent is still avaliable" );
ok( propHook.set.calledWith( tween ), "Called propHook.set function with correct parameters" );
delete jQuery.Tween.propHooks.testHooked;
} );
} )();