Skip to content

Commit

Permalink
feat: mocking library
Browse files Browse the repository at this point in the history
  • Loading branch information
bhovhannes committed Dec 26, 2016
1 parent 5209de5 commit 1472b42
Show file tree
Hide file tree
Showing 8 changed files with 170 additions and 36 deletions.
67 changes: 67 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,73 @@ Cancels current animation.
Puts animation into the last state.


## Mock

`additween` package provides a separate library which can be used to test animated behavior in a synchronous manner.

That library is included in `additween-mocks.js` which exports a `AdditiveTweeningMock` constructor. It can be used with any testing framework (in the usage example below we use jasmine).

```javascript
import {AdditiveTweening} from 'additween'
import {AdditiveTweeningMock} from 'additween/dist/additween-mocks'

describe('my great app', function() {
let additiveTweeningMock = new AdditiveTweeningMock()
beforeEach(function () {
additiveTweeningMock = new AdditiveTweeningMock()
additiveTweeningMock.install(AdditiveTweening)
})

afterEach(function () {
additiveTweeningMock.uninstall(AdditiveTweening)
})

it('should animate perfectly', function() {
// let's say clicking a button
// will cause an animation with 2000ms duration
// we assume there is a triggerClick method defined somewhere
let btn = document.getElementById('byButton')
triggerClick(btn)

// let time go forward by 1000ms
additiveTweeningMock.tick(1000)

//now we can make assertions about animation state after half of time
//expect(...)

// let time go forward by another 1000ms
additiveTweeningMock.tick(1000)

//now we can make assertions about animation final state
//expect(...)
})
})
```

### Mock API

#### mock = new AdditiveTweeningMock()

Creates a new instance of mocking library.

#### mock.install(AdditiveTweening)

Pass an `AdditiveTweening` constructor in order to let mock patch its animation-related methods.

#### mock.uninstall(AdditiveTweening)

Restores original implementation of passed in `AdditiveTweening`.

#### mock.tick(duration)

Moves animation clock forward by `duration` msecs. Animation `onRender` callback will be called once after that. If duration is greater or equal than total animation duration, `onFinish` callback also will be called.

#### mock.reset()

Reset animation clock to its initial state.



## Browser support

This will work for all browsers with `requestAnimationFrame` support (see [here](http://caniuse.com#search=requestAnimationFrame)).
Expand Down
5 changes: 5 additions & 0 deletions index-mocks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
'use strict'

module.exports = {
AdditiveTweeningMock: require('./src/AdditiveTweeningMock')
}
43 changes: 18 additions & 25 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,39 +2,32 @@
// Project: additween
// Definitions by: Hovhannes Babayan <bhovhannes@gmail.com>

export = additween;
// export as namespace additween;

export as namespace additween;
export type EasingFunction = (t: number) => number

declare namespace additween {

export type EasingFunction = (t: number) => number

export interface IStateReducer<T> {
clone: (state: T) => T,
reduce: (targetState: T, toState: T, fromState: T, pos: number) => T
}

export const ArrayReducer: IStateReducer<number[]>
export interface IStateReducer<T> {
clone: (state: T) => T,
reduce: (targetState: T, toState: T, fromState: T, pos: number) => T
}

export const PlainObjectReducer: IStateReducer<Object>
export const PlainObjectReducer: IStateReducer<Object>

export class AdditiveTweening<T> {
export class AdditiveTweening<T> {

constructor(options: {
onRender: (state: T) => void,
onFinish?: (finalState: T) => void,
onCancel?: () => void,
stateReducer?: IStateReducer<T>
})
constructor(options: {
onRender: (state: T) => void,
onFinish?: (finalState: T) => void,
onCancel?: () => void,
stateReducer?: IStateReducer<T>
})

tween(fromState: T, toState: T, duration?: number, easing?: EasingFunction): number
tween(fromState: T, toState: T, duration?: number, easing?: EasingFunction): number

isTweening(): boolean
isTweening(): boolean

finish(): void
finish(): void

cancel(): void
}
cancel(): void
}

22 changes: 14 additions & 8 deletions src/AdditiveTweening.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,7 @@ function AdditiveTweening(options) {
onRender = options.onRender || noop,
stateReducer = options.stateReducer || PlainObjectReducer,
onFinish = options.onFinish || noop,
onCancel = options.onCancel || noop,
stepFunc

stepFunc = window.requestAnimationFrame
onCancel = options.onCancel || noop


function filterOutdatedTargetsFromStack(time) {
Expand Down Expand Up @@ -68,14 +65,14 @@ function AdditiveTweening(options) {
return
}

var time = now()
var time = this.now()

currentState = getCurrentState(time)

onRender(currentState)

if (hasActiveAnimation(time)) {
frame = stepFunc(animationStep)
frame = this.scheduleAnimationFrame(animationStep)
} else {
this.finish()
}
Expand Down Expand Up @@ -126,7 +123,7 @@ function AdditiveTweening(options) {
* @param {Function} easing An easing function. It should take a number from [0,1] range and return a number from [0,1] range.
*/
this.tween = function(fromState, toState, duration, easing) {
var time = now(),
var time = this.now(),
animation

animation = {
Expand All @@ -143,8 +140,17 @@ function AdditiveTweening(options) {

lastTargetState = toState

frame = stepFunc(animationStep)
frame = this.scheduleAnimationFrame(animationStep)
}
}

AdditiveTweening.prototype.scheduleAnimationFrame = function(cb) {
return window.requestAnimationFrame(cb)
}

AdditiveTweening.prototype.now = function() {
return now()
}


module.exports = AdditiveTweening
55 changes: 55 additions & 0 deletions src/AdditiveTweeningMock.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
'use strict'

function noop() {}

function AdditiveTweeningMock() {

this.originalMethods = {
scheduleAnimationFrame: undefined,
now: undefined
}
this.reset()
}

AdditiveTweeningMock.prototype.tick = function(duration) {
this.time += duration
if (this.animationStepScheduled) {
this.animationStep()
this.animationStepScheduled = false
}
}

AdditiveTweeningMock.prototype.reset = function() {
this.time = 0
this.animationStep = noop
this.animationStepScheduled = false
}

AdditiveTweeningMock.prototype.stubs = {
scheduleAnimationFrame: function(cb) {
this.animationStep = cb
this.animationStepScheduled = true
},

now: function() {
return this.time
}
}

AdditiveTweeningMock.prototype.install = function(additiveTweening) {
for (var key in this.originalMethods) {
this.originalMethods[key] = additiveTweening.prototype[key]
additiveTweening.prototype[key] = this.stubs[key].bind(this)
}
this.reset()
}

AdditiveTweeningMock.prototype.uninstall = function(additiveTweening) {
for (var key in this.originalMethods) {
additiveTweening.prototype[key] = this.originalMethods[key]
}
this.reset()
}


module.exports = AdditiveTweeningMock
2 changes: 1 addition & 1 deletion test/typings-test-fixtures/AdditiveTweening.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { AdditiveTweening } from '../../index'
interface FakeAnimState {
}

let anim: additween.AdditiveTweening<FakeAnimState> = new AdditiveTweening({
let anim: AdditiveTweening<FakeAnimState> = new AdditiveTweening({
onRender: function(s) {
return s
}
Expand Down
4 changes: 3 additions & 1 deletion test/typings-test-fixtures/EasingFunction.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
/// <reference path="../../index.d.ts" />

let fakeEasing: additween.EasingFunction
import {EasingFunction} from '../../index'

let fakeEasing: EasingFunction

fakeEasing = function(a) {
return a
Expand Down
8 changes: 7 additions & 1 deletion webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,13 @@ unminifiedConfig.entry = {
'additween': './index'
}

var mocksConfig = getBaseWebpackConfig()
mocksConfig.entry = {
'additween-mocks': './index-mocks'
}

module.exports = [
minifiedConfig,
unminifiedConfig
unminifiedConfig,
mocksConfig
]

0 comments on commit 1472b42

Please sign in to comment.