From 5a70312f36355f20e7a643e26e1f6bdb597df7be Mon Sep 17 00:00:00 2001 From: Ulrich-Matthias Schäfer Date: Tue, 28 Apr 2020 14:19:38 +1000 Subject: added tests for transform.js, migrated tests for v3 of types to esm tests --- spec/RAFPlugin.js | 45 +- spec/setupSVGDom.js | 1 + spec/spec/animation/Animator.js | 55 +++ spec/spec/animation/Morphable.js | 167 +++++++ spec/spec/animation/Queue.js | 87 ++++ spec/spec/animation/Runner.js | 842 ++++++++++++++++++++++++++++++++ spec/spec/animation/Timeline.js | 117 +++++ spec/spec/animation/easing.js | 26 + spec/spec/animator.js | 50 -- spec/spec/color.js | 270 ---------- spec/spec/easing.js | 22 - spec/spec/modules/optional/transform.js | 141 ++++++ spec/spec/morphing.js | 161 ------ spec/spec/number.js | 178 ------- spec/spec/point.js | 74 --- spec/spec/pointarray.js | 28 -- spec/spec/queue.js | 84 ---- spec/spec/runner.js | 836 ------------------------------- spec/spec/timeline.js | 112 ----- spec/spec/types/Color.js | 273 +++++++++++ spec/spec/types/EventTarget.js | 4 +- spec/spec/types/Matrix.js | 381 +++++++++++++++ spec/spec/types/Number.js | 184 +++++++ spec/spec/types/Point.js | 78 +++ spec/spec/types/PointArray.js | 32 ++ 25 files changed, 2411 insertions(+), 1837 deletions(-) create mode 100644 spec/spec/animation/Animator.js create mode 100644 spec/spec/animation/Morphable.js create mode 100644 spec/spec/animation/Queue.js create mode 100644 spec/spec/animation/Runner.js create mode 100644 spec/spec/animation/Timeline.js create mode 100644 spec/spec/animation/easing.js delete mode 100644 spec/spec/animator.js delete mode 100644 spec/spec/color.js delete mode 100644 spec/spec/easing.js create mode 100644 spec/spec/modules/optional/transform.js delete mode 100644 spec/spec/morphing.js delete mode 100644 spec/spec/number.js delete mode 100644 spec/spec/point.js delete mode 100644 spec/spec/pointarray.js delete mode 100644 spec/spec/queue.js delete mode 100644 spec/spec/runner.js delete mode 100644 spec/spec/timeline.js create mode 100644 spec/spec/types/Color.js create mode 100644 spec/spec/types/Matrix.js create mode 100644 spec/spec/types/Number.js create mode 100644 spec/spec/types/Point.js create mode 100644 spec/spec/types/PointArray.js diff --git a/spec/RAFPlugin.js b/spec/RAFPlugin.js index 87ce48a..3e82c70 100644 --- a/spec/RAFPlugin.js +++ b/spec/RAFPlugin.js @@ -2,22 +2,19 @@ * Jasmine RequestAnimationFrame: a set of helpers for testing funcionality * that uses requestAnimationFrame under the Jasmine BDD framework for JavaScript. */ -;(function () { +function RAFPlugin (jasmine) { var index = 0 var callbacks = [] - function MockRAF (global) { - this.realRAF = global.requestAnimationFrame - this.realCAF = global.cancelAnimationFrame - this.realPerf = global.performance + function MockRAF () { this.nextTime = 0 var _this = this /** - * Mock for window.requestAnimationFrame - */ + * Mock for window.requestAnimationFrame + */ this.mockRAF = function (fn) { if (typeof fn !== 'function') { throw new Error('You should pass a function to requestAnimationFrame') @@ -29,8 +26,8 @@ } /** - * Mock for window.cancelAnimationFrame - */ + * Mock for window.cancelAnimationFrame + */ this.mockCAF = function (requestID) { callbacks.splice(requestID, 1) } @@ -42,18 +39,21 @@ } /** - * Install request animation frame mocks. - */ - this.install = function () { + * Install request animation frame mocks. + */ + this.install = function (global) { + _this.realRAF = global.requestAnimationFrame + _this.realCAF = global.cancelAnimationFrame + _this.realPerf = global.performance global.requestAnimationFrame = _this.mockRAF global.cancelAnimationFrame = _this.mockCAF global.performance = _this.mockPerf } /** - * Uninstall request animation frame mocks. - */ - this.uninstall = function () { + * Uninstall request animation frame mocks. + */ + this.uninstall = function (global) { global.requestAnimationFrame = _this.realRAF global.cancelAnimationFrame = _this.realCAF global.performance = _this.realPerf @@ -62,8 +62,8 @@ } /** - * Simulate animation frame readiness. - */ + * Simulate animation frame readiness. + */ this.tick = function (dt) { _this.nextTime += (dt || 1) @@ -79,5 +79,12 @@ } } - jasmine.RequestAnimationFrame = new MockRAF(window) -}()) + jasmine.RequestAnimationFrame = new MockRAF() +} + +// if (!module) { +RAFPlugin(jasmine) +// } else { +// module.exports.RAFPlugin = RAFPlugin + +// } diff --git a/spec/setupSVGDom.js b/spec/setupSVGDom.js index 72e5383..22bf565 100644 --- a/spec/setupSVGDom.js +++ b/spec/setupSVGDom.js @@ -1,3 +1,4 @@ +import './RAFPlugin.js' import { createHTMLWindow } from 'svgdom' /* globals beforeEach, afterEach, jasmine */ diff --git a/spec/spec/animation/Animator.js b/spec/spec/animation/Animator.js new file mode 100644 index 0000000..80f2eab --- /dev/null +++ b/spec/spec/animation/Animator.js @@ -0,0 +1,55 @@ +/* globals describe, expect, it, beforeEach, afterEach, jasmine */ + +import { Animator, Queue } from '../../../src/main.js' +import { getWindow } from '../../../src/utils/window.js' + +describe('Animator.js', function () { + + beforeEach(function () { + jasmine.RequestAnimationFrame.install(getWindow()) + Animator.timeouts = new Queue() + Animator.frames = new Queue() + Animator.nextDraw = null + }) + + afterEach(function () { + jasmine.RequestAnimationFrame.uninstall(getWindow()) + }) + + describe('timeout()', function () { + it('calls a function after a specific time', function () { + + var spy = jasmine.createSpy('tester') + Animator.timeout(spy, 100) + + jasmine.RequestAnimationFrame.tick(99) + expect(spy).not.toHaveBeenCalled() + jasmine.RequestAnimationFrame.tick() + expect(spy).toHaveBeenCalled() + }) + }) + + describe('cancelTimeout()', function () { + it('cancels a timeout which was created with timeout()', function () { + var spy = jasmine.createSpy('tester') + var id = Animator.timeout(spy, 100) + Animator.clearTimeout(id) + + expect(spy).not.toHaveBeenCalled() + jasmine.RequestAnimationFrame.tick(100) + expect(spy).not.toHaveBeenCalled() + }) + }) + + describe('frame()', function () { + it('calls a function at the next animationFrame', function () { + var spy = jasmine.createSpy('tester') + + Animator.frame(spy) + expect(spy).not.toHaveBeenCalled() + jasmine.RequestAnimationFrame.tick() + expect(spy).toHaveBeenCalled() + }) + }) + +}) diff --git a/spec/spec/animation/Morphable.js b/spec/spec/animation/Morphable.js new file mode 100644 index 0000000..4b0e2f1 --- /dev/null +++ b/spec/spec/animation/Morphable.js @@ -0,0 +1,167 @@ +/* globals describe, expect, it, jasmine */ + +import { Morphable, NonMorphable, ObjectBag, Color, Box, Matrix, PointArray, PathArray, TransformBag, Number as SVGNumber, Array as SVGArray } from '../../../src/main.js' + +const { objectContaining, arrayContaining, any } = jasmine + +describe('Morphable.js', function () { + describe('constructors', function () { + + it('Morphable with SVGNumber', function () { + var morpher = new Morphable().from(10).to(5) + + expect(morpher).toEqual(any(Morphable)) + expect(morpher.type()).toBe(SVGNumber) + expect(morpher.at(0.5)).toEqual(any(SVGNumber)) + expect(morpher.at(0.5).valueOf()).toBe(7.5) + }) + + it('Morphable with String', function () { + var morpher = new Morphable().from('foo').to('bar') + + expect(morpher).toEqual(any(Morphable)) + expect(morpher.type()).toBe(NonMorphable) + expect(morpher.at(0.5)).toEqual(any(NonMorphable)) + expect(morpher.at(0.5).valueOf()).toBe('foo') + expect(morpher.at(1).valueOf()).toBe('bar') + }) + + it('Morphable with Object', function () { + var morpher = new Morphable().from({ a: 5, b: 10 }).to({ a: 10, b: 20 }) + + expect(morpher).toEqual(any(Morphable)) + expect(morpher.type()).toBe(ObjectBag) + expect(morpher.at(0.5)).toEqual(any(Object)) + expect(morpher.at(0.5).valueOf()).toEqual(objectContaining({ a: 7.5, b: 15 })) + }) + + it('Creates a morphable out of an SVGNumber', function () { + var morpher = new SVGNumber(5).to(10) + + expect(morpher).toEqual(any(Morphable)) + expect(morpher.type()).toBe(SVGNumber) + expect(morpher.at(0.5)).toEqual(any(SVGNumber)) + expect(morpher.at(0.5).valueOf()).toBe(7.5) + }) + + it('Creates a morphable out of an Color', function () { + var morpher = new Color('#fff').to('#000') + + expect(morpher).toEqual(any(Morphable)) + expect(morpher.type()).toBe(Color) + expect(morpher.at(0.5)).toEqual(any(Color)) + expect(morpher.at(0.5).toHex()).toBe('#808080') + }) + + it('Creates a morphable out of an Box', function () { + var morpher = new Box(1, 2, 3, 4).to([ 5, 6, 7, 8 ]) + + expect(morpher).toEqual(any(Morphable)) + expect(morpher.type()).toBe(Box) + expect(morpher.at(0.5)).toEqual(any(Box)) + expect(morpher.at(0.5)).toEqual(objectContaining({ x: 3, y: 4, width: 5, height: 6 })) + }) + + it('Creates a morphable out of an Matrix', function () { + var morpher = new Matrix(1, 2, 3, 4, 5, 6).to([ 3, 4, 5, 6, 7, 8 ]) + + expect(morpher).toEqual(any(Morphable)) + expect(morpher.type()).toBe(Matrix) + expect(morpher.at(0.5)).toEqual(any(Matrix)) + expect(morpher.at(0.5)).toEqual(objectContaining(new Matrix(2, 3, 4, 5, 6, 7))) + }) + + it('Creates a morphable out of an Array', function () { + var morpher = new SVGArray([ 1, 2, 3, 4, 5, 6 ]).to([ 3, 4, 5, 6, 7, 8 ]) + + expect(morpher).toEqual(any(Morphable)) + expect(morpher.type()).toBe(SVGArray) + expect(morpher.at(0.5)).toEqual(any(SVGArray)) + expect(morpher.at(0.5).toArray()).toEqual(arrayContaining([ 2, 3, 4, 5, 6, 7 ])) + }) + + it('Creates a morphable out of an PointArray', function () { + var morpher = new PointArray([ 1, 2, 3, 4, 5, 6 ]).to([ 3, 4, 5, 6, 7, 8 ]) + + expect(morpher).toEqual(any(Morphable)) + expect(morpher.type()).toBe(PointArray) + expect(morpher.at(0.5)).toEqual(any(PointArray)) + expect(morpher.at(0.5).toArray()).toEqual(arrayContaining([ 2, 3, 4, 5, 6, 7 ])) + }) + + it('Creates a morphable out of an PathArray', function () { + var morpher = new PathArray([ 'M', 1, 2, 'L', 3, 4, 'L', 5, 6 ]).to([ 'M', 3, 4, 'L', 5, 6, 'L', 7, 8 ]) + + expect(morpher).toEqual(any(Morphable)) + expect(morpher.type()).toBe(PathArray) + expect(morpher.at(0.5)).toEqual(any(PathArray)) + expect(morpher.at(0.5).toArray()).toEqual(arrayContaining([ 'M', 2, 3, 'L', 4, 5, 'L', 6, 7 ])) + }) + + it('Creates a morphable out of an NonMorphable', function () { + var morpher = new NonMorphable('foo').to('bar') + + expect(morpher).toEqual(any(Morphable)) + expect(morpher.type()).toBe(NonMorphable) + expect(morpher.at(0.5)).toEqual(any(NonMorphable)) + expect(morpher.at(0.5).valueOf()).toBe('foo') + expect(morpher.at(1).valueOf()).toBe('bar') + }) + + it('Creates a morphable out of an TransformBag', function () { + var morpher = new TransformBag({ rotate: 0, translateX: 0 }) + .to({ rotate: 50, translateX: 20 }) + + expect(morpher).toEqual(any(Morphable)) + expect(morpher.type()).toBe(TransformBag) + expect(morpher.at(0.5)).toEqual(any(TransformBag)) + + expect(morpher.at(0.5)).toEqual(objectContaining({ rotate: 25, translateX: 10 })) + }) + + it('Creates a morphable out of an ObjectBag', function () { + var morpher = new ObjectBag({ a: 5, b: 10 }).to({ a: 10, b: 20 }) + + expect(morpher).toEqual(any(Morphable)) + expect(morpher.type()).toBe(ObjectBag) + expect(morpher.at(0.5)).toEqual(any(Object)) + expect(morpher.at(0.5).valueOf()).toEqual(objectContaining({ a: 7.5, b: 15 })) + }) + }) + + describe('from()', function () { + it('sets the type of the runner', function () { + var morpher = new Morphable().from(5) + expect(morpher.type()).toBe(SVGNumber) + }) + + it('sets the from attribute to an array representation of the morphable type', function () { + var morpher = new Morphable().from(5) + expect(morpher.from()).toEqual(arrayContaining([ 5 ])) + }) + }) + + describe('type()', function () { + it('sets the type of the runner', function () { + var morpher = new Morphable().type(SVGNumber) + expect(morpher._type).toBe(SVGNumber) + }) + + it('gets the type of the runner', function () { + var morpher = new Morphable().type(SVGNumber) + expect(morpher.type()).toBe(SVGNumber) + }) + }) + + describe('to()', function () { + it('sets the type of the runner', function () { + var morpher = new Morphable().to(5) + expect(morpher.type()).toBe(SVGNumber) + }) + + it('sets the from attribute to an array representation of the morphable type', function () { + var morpher = new Morphable().to(5) + expect(morpher.to()).toEqual(arrayContaining([ 5 ])) + }) + }) +}) diff --git a/spec/spec/animation/Queue.js b/spec/spec/animation/Queue.js new file mode 100644 index 0000000..4282bf9 --- /dev/null +++ b/spec/spec/animation/Queue.js @@ -0,0 +1,87 @@ +/* globals describe, expect, it */ + +import { Queue } from '../../../src/main.js' + +describe('Queue.js', function () { + + describe('first ()', function () { + + it('returns null if no item in the queue', function () { + var queue = new Queue() + expect(queue.first()).toEqual(null) + }) + + it('returns the first value in the queue', function () { + var queue = new Queue() + queue.push(1) + expect(queue.first()).toBe(1) + queue.push(2) + expect(queue.first()).toBe(1) + }) + }) + + describe('last ()', function () { + + it('returns null if no item in the queue', function () { + var queue = new Queue() + expect(queue.last()).toEqual(null) + }) + + it('returns the last value added', function () { + var queue = new Queue() + queue.push(1) + expect(queue.last()).toBe(1) + queue.push(2) + expect(queue.last()).toBe(2) + }) + }) + + describe('push ()', function () { + + it('adds an element to the end of the queue', function () { + var queue = new Queue() + queue.push(1) + queue.push(2) + queue.push(3) + + expect(queue.first()).toBe(1) + expect(queue.last()).toBe(3) + }) + }) + + describe('remove ()', function () { + it('removes the given item from the queue', function () { + var queue = new Queue() + queue.push(1) + queue.push(2) + var item = queue.push(3) + + queue.remove(item) + + expect(queue.last()).toBe(2) + expect(queue.first()).toBe(1) + }) + }) + + describe('shift ()', function () { + it('returns nothing if queue is empty', function () { + var queue = new Queue() + var val = queue.shift() + expect(val).toBeFalsy() + }) + + it('returns the first item of the queue and removes it', function () { + var queue = new Queue() + queue.push(1) + queue.push(2) + queue.push(3) + + var val = queue.shift() + + expect(queue.last()).toBe(3) + expect(queue.first()).toBe(2) + + expect(val).toBe(1) + }) + }) +}) diff --git a/spec/spec/animation/Runner.js b/spec/spec/animation/Runner.js new file mode 100644 index 0000000..63d1bb8 --- /dev/null +++ b/spec/spec/animation/Runner.js @@ -0,0 +1,842 @@ +/* globals describe, expect, it, beforeEach, afterEach, spyOn, jasmine */ + +import { Runner, defaults, Ease, Controller, SVG, Timeline } from '../../../src/main.js' +import { getWindow } from '../../../src/utils/window.js' + +const { createSpy, objectContaining, arrayContaining } = jasmine + +describe('Runner.js', () => { + + var initFn = createSpy('initFn') + var runFn = createSpy('runFn') + + beforeEach(() => { + jasmine.RequestAnimationFrame.install(getWindow()) + initFn.calls.reset() + runFn.calls.reset() + }) + + afterEach(() => { + jasmine.RequestAnimationFrame.uninstall(getWindow()) + }) + + describe('sanitise()', () => { + it('can handle all form of input', () => { + var fn = Runner.sanitise + + expect(fn(200, 200, 'now')).toEqual(objectContaining({ + duration: 200, + delay: 200, + when: 'now', + times: 1, + wait: 0, + swing: false + })) + + expect(fn(200, 200)).toEqual(objectContaining({ + duration: 200, + delay: 200, + when: 'last', + times: 1, + wait: 0, + swing: false + })) + + expect(fn(200)).toEqual(objectContaining({ + duration: 200, + delay: defaults.timeline.delay, + when: 'last', + times: 1, + wait: 0, + swing: false + })) + + expect(fn(runFn)).toEqual(objectContaining({ + duration: runFn, + delay: defaults.timeline.delay, + when: 'last', + times: 1, + wait: 0, + swing: false + })) + + expect(fn({ delay: 200 })).toEqual(objectContaining({ + duration: defaults.timeline.duration, + delay: 200, + when: 'last', + times: 1, + wait: 0, + swing: false + })) + + expect(fn({ times: 3, delay: 200, when: 'now', swing: true, wait: 200 })).toEqual(objectContaining({ + duration: defaults.timeline.duration, + delay: 200, + when: 'now', + times: 3, + wait: 200, + swing: true + })) + }) + }) + + describe('())', () => { + it('creates a runner with defaults', () => { + var runner = new Runner() + expect(runner instanceof Runner).toBe(true) + expect(runner._duration).toBe(defaults.timeline.duration) + expect(runner._stepper instanceof Ease).toBe(true) + }) + + it('creates a runner with duration set', () => { + var runner = new Runner(1000) + expect(runner instanceof Runner).toBe(true) + expect(runner._duration).toBe(1000) + expect(runner._stepper instanceof Ease).toBe(true) + }) + + it('creates a runner with controller set', () => { + var runner = new Runner(runFn) + expect(runner instanceof Runner).toBe(true) + expect(runner._duration).toBeFalsy() + expect(runner._stepper instanceof Controller).toBe(true) + }) + }) + + describe('constructors', () => { + // FIXME: Not possible to spy like this in es6 + // describe('animate()', () => { + // it('creates a runner with the element set and schedules it on the timeline', () => { + // var orginalRunner = Runner + // spyOn(SVG, 'Runner').and.callFake(() =>{ + // return new orginalRunner() + // }) + // + // var element = SVG('') + // var runner = element.animate() + // expect(Runner).toHaveBeenCalled(); + // expect(runner instanceof Runner) + // expect(runner.element()).toBe(element) + // expect(runner.timeline()).toBe(element.timeline()) + // }) + // }) + + describe('delay()', () => { + it('calls animate with correct parameters', () => { + var element = SVG('') + + spyOn(element, 'animate') + element.delay(100, 'now') + expect(element.animate).toHaveBeenCalledWith(0, 100, 'now') + }) + }) + }) + + describe('queue()', () => { + it('adds another closure to the runner', () => { + var runner = new Runner() + runner.queue(initFn, runFn, true) + + expect(runner._queue[0]).toEqual(objectContaining({ + initialiser: initFn, + initialised: false, + runner: runFn, + finished: false + })) + }) + }) + + describe('step()', () => { + + it('returns itself', () => { + var runner = new Runner() + expect(runner.step()).toBe(runner) + }) + + it('calls initFn once and runFn at every step', () => { + var runner = new Runner() + runner.queue(initFn, runFn, false) + + runner.step() + expect(initFn).toHaveBeenCalled() + expect(runFn).toHaveBeenCalled() + + runner.step() + expect(initFn.calls.count()).toBe(1) + expect(runFn.calls.count()).toBe(2) + }) + + it('calls initFn on every step if its declaritive', () => { + var runner = new Runner(new Controller()) + runner.queue(initFn, runFn, true) + + runner.step() + expect(initFn).toHaveBeenCalled() + expect(runFn).toHaveBeenCalled() + + runner.step() + expect(initFn.calls.count()).toBe(2) + expect(runFn.calls.count()).toBe(2) + }) + + function getLoop (r) { + var loopDuration = r._duration + r._wait + var loopsDone = Math.floor(r._time / loopDuration) + return loopsDone + } + + // step in time + it('steps forward a certain time', () => { + var spy = createSpy('stepper') + var r = new Runner(1000).loop(10, false, 100) + r.queue(null, spy) + + r.step(300) // should be 0.3s + expect(spy).toHaveBeenCalledWith(0.3) + expect(getLoop(r)).toBe(0) + + r.step(300) // should be 0.6s + expect(spy).toHaveBeenCalledWith(0.6) + expect(getLoop(r)).toBe(0) + + r.step(600) // should be 0.1s + expect(spy).toHaveBeenCalledWith(0.1) + expect(getLoop(r)).toBe(1) + + r.step(-300) // should be 0.9s + expect(spy).toHaveBeenCalledWith(0.9) + expect(getLoop(r)).toBe(0) + + r.step(2000) // should be 0.7s + expect(spy).toHaveBeenCalledWith(0.7) + expect(getLoop(r)).toBe(2) + + r.step(-2000) // should be 0.9s + expect(spy).toHaveBeenCalledWith(0.9) + expect(getLoop(r)).toBe(0) + }) + + it('handles dts which are bigger than the animation time', () => { + var runner = new Runner(1000) + runner.queue(initFn, runFn, true) + + runner.step(1100) + expect(initFn).toHaveBeenCalled() + expect(runFn).toHaveBeenCalledWith(1) + }) + + describe('looping', () => { + describe('without wait', () => { + describe('unreversed', () => { + describe('nonswinging', () => { + it('does behave correctly at the end of an even loop', () => { + var spy = createSpy('stepper') + var runner = new Runner(1000).loop(6, false) + runner.queue(null, spy) + + runner.step(5750) + expect(spy).toHaveBeenCalledWith(0.75) + runner.step(250) + expect(spy).toHaveBeenCalledWith(1) + }) + + it('does behave correctly at the end of an uneven loop', () => { + var spy = createSpy('stepper') + var runner = new Runner(1000).loop(5, false) + runner.queue(null, spy) + + runner.step(4750) + expect(spy).toHaveBeenCalledWith(0.75) + runner.step(250) + expect(spy).toHaveBeenCalledWith(1) + }) + }) + + describe('swinging', () => { + it('does behave correctly at the end of an even loop', () => { + var spy = createSpy('stepper') + var runner = new Runner(1000).loop(6, true) + runner.queue(null, spy) + + runner.step(5750) + expect(spy).toHaveBeenCalledWith(0.25) + runner.step(250) + expect(spy).toHaveBeenCalledWith(0) + }) + + it('does behave correctly at the end of an uneven loop', () => { + var spy = createSpy('stepper') + var runner = new Runner(1000).loop(5, true) + runner.queue(null, spy) + + runner.step(4750) + expect(spy).toHaveBeenCalledWith(0.75) + runner.step(250) + expect(spy).toHaveBeenCalledWith(1) + }) + }) + }) + + describe('reversed', () => { + describe('nonswinging', () => { + it('does behave correctly at the end of an even loop', () => { + var spy = createSpy('stepper') + var runner = new Runner(1000).loop(6, false).reverse() + runner.queue(null, spy) + + runner.step(5750) + expect(spy).toHaveBeenCalledWith(0.25) + runner.step(250) + expect(spy).toHaveBeenCalledWith(0) + }) + + it('does behave correctly at the end of an uneven loop', () => { + var spy = createSpy('stepper') + var runner = new Runner(1000).loop(5, false).reverse() + runner.queue(null, spy) + + runner.step(4750) + expect(spy).toHaveBeenCalledWith(0.25) + runner.step(250) + expect(spy).toHaveBeenCalledWith(0) + }) + }) + + describe('swinging', () => { + it('does behave correctly at the end of an even loop', () => { + var spy = createSpy('stepper') + var runner = new Runner(1000).loop(6, true).reverse() + runner.queue(null, spy) + + runner.step(5750) + expect(spy).toHaveBeenCalledWith(0.75) + runner.step(250) + expect(spy).toHaveBeenCalledWith(1) + }) + + it('does behave correctly at the end of an uneven loop', () => { + var spy = createSpy('stepper') + var runner = new Runner(1000).loop(5, true).reverse() + runner.queue(null, spy) + + runner.step(4750) + expect(spy).toHaveBeenCalledWith(0.25) + runner.step(250) + expect(spy).toHaveBeenCalledWith(0) + }) + }) + }) + }) + + describe('with wait', () => { + describe('unreversed', () => { + describe('nonswinging', () => { + it('does behave correctly at the end of an even loop', () => { + var spy = createSpy('stepper') + var runner = new Runner(1000).loop(6, false, 100) + runner.queue(null, spy) + + runner.step(5450) + expect(spy).toHaveBeenCalledWith(1) + spy.calls.reset() + + runner.step(800) + expect(spy).toHaveBeenCalledWith(0.75) + runner.step(250) + expect(spy).toHaveBeenCalledWith(1) + }) + + it('does behave correctly at the end of an uneven loop', () => { + var spy = createSpy('stepper') + var runner = new Runner(1000).loop(5, false, 100) + runner.queue(null, spy) + + runner.step(4350) + expect(spy).toHaveBeenCalledWith(1) + spy.calls.reset() + + runner.step(800) + expect(spy).toHaveBeenCalledWith(0.75) + runner.step(250) + expect(spy).toHaveBeenCalledWith(1) + }) + }) + + describe('swinging', () => { + it('does behave correctly at the end of an even loop', () => { + var spy = createSpy('stepper') + var runner = new Runner(1000).loop(6, true, 100) + runner.queue(null, spy) + + runner.step(5450) + expect(spy).toHaveBeenCalledWith(1) + spy.calls.reset() + + runner.step(800) + expect(spy).toHaveBeenCalledWith(0.25) + runner.step(250) + expect(spy).toHaveBeenCalledWith(0) + }) + + it('does behave correctly at the end of an uneven loop', () => { + var spy = createSpy('stepper') + var runner = new Runner(1000).loop(5, true, 100) + runner.queue(null, spy) + + runner.step(4350) + expect(spy).toHaveBeenCalledWith(0) + spy.calls.reset() + + runner.step(800) + expect(spy).toHaveBeenCalledWith(0.75) + + runner.step(250) + expect(spy).toHaveBeenCalledWith(1) + }) + }) + }) + + describe('reversed', () => { + describe('nonswinging', () => { + it('does behave correctly at the end of an even loop', () => { + var spy = createSpy('stepper') + var runner = new Runner(1000).loop(6, false, 100).reverse() + runner.queue(null, spy) + + runner.step(5450) + expect(spy).toHaveBeenCalledWith(0) + spy.calls.reset() + + runner.step(800) + expect(spy).toHaveBeenCalledWith(0.25) + runner.step(250) + expect(spy).toHaveBeenCalledWith(0) + }) + + it('does behave correctly at the end of an uneven loop', () => { + var spy = createSpy('stepper') + var runner = new Runner(1000).loop(5, false, 100).reverse() + runner.queue(null, spy) + + runner.step(4350) + expect(spy).toHaveBeenCalledWith(0) + spy.calls.reset() + + runner.step(800) + expect(spy).toHaveBeenCalledWith(0.25) + runner.step(250) + expect(spy).toHaveBeenCalledWith(0) + }) + }) + + describe('swinging', () => { + it('does behave correctly at the end of an even loop', () => { + var spy = createSpy('stepper') + var runner = new Runner(1000).loop(6, true, 100).reverse() + runner.queue(null, spy) + + runner.step(5450) + expect(spy).toHaveBeenCalledWith(0) + spy.calls.reset() + + runner.step(800) + expect(spy).toHaveBeenCalledWith(0.75) + runner.step(250) + expect(spy).toHaveBeenCalledWith(1) + }) + + it('does behave correctly at the end of an uneven loop', () => { + var spy = createSpy('stepper') + var runner = new Runner(1000).loop(5, true, 100).reverse() + runner.queue(null, spy) + + runner.step(4350) + expect(spy).toHaveBeenCalledWith(1) + spy.calls.reset() + + runner.step(800) + expect(spy).toHaveBeenCalledWith(0.25) + runner.step(250) + expect(spy).toHaveBeenCalledWith(0) + }) + }) + }) + }) + }) + + }) + + describe('active()', () => { + it('acts as a getter without parameters', () => { + var runner = new Runner() + expect(runner.active()).toBe(true) + }) + + it('disables the runner when false is passed', () => { + var runner = new Runner() + expect(runner.active(false)).toBe(runner) + expect(runner.active()).toBe(false) + }) + + it('enables the runner when true is passed', () => { + var runner = new Runner() + expect(runner.active(false)).toBe(runner) + expect(runner.active(true)).toBe(runner) + expect(runner.active()).toBe(true) + }) + }) + + describe('duration()', () => { + it('return the full duration of the runner including all loops and waits', () => { + var runner = new Runner(800).loop(10, true, 200) + expect(runner.duration()).toBe(9800) + }) + }) + + describe('loop()', () => { + it('makes this runner looping', () => { + var runner = new Runner(1000).loop(5) + expect(runner.duration()).toBe(5000) + }) + }) + + describe('time()', () => { + it('returns itself', () => { + var runner = new Runner() + expect(runner.time(0)).toBe(runner) + }) + + it('acts as a getter with no parameter passed', () => { + var runner = new Runner() + expect(runner.time()).toBe(0) + }) + + it('reschedules the runner to a new time', () => { + var runner = new Runner() + runner.time(10) + + expect(runner.time()).toBe(10) + }) + + it('calls step to reschedule', () => { + var runner = new Runner() + spyOn(runner, 'step') + runner.time(10) + + expect(runner.step).toHaveBeenCalledWith(10) + }) + }) + + describe('loops()', () => { + it('get the loops of a runner', () => { + var spy = createSpy('stepper') + var runner = new Runner(1000).queue(null, spy) + + runner.step(300) + expect(spy).toHaveBeenCalledWith(0.3) + + expect(runner.loops()).toBe(0.3) + }) + it('sets the loops of the runner', () => { + var spy = createSpy('stepper') + var runner = new Runner(1000).queue(null, spy) + + expect(runner.loops(0.5).loops()).toBe(0.5) + expect(spy).toHaveBeenCalledWith(0.5) + + expect(runner.loops(0.1).loops()).toBe(0.1) + expect(spy).toHaveBeenCalledWith(0.1) + + expect(runner.loops(1.5).loops()).toBe(1) + expect(spy).toHaveBeenCalledWith(1) + }) + it('sets the loops of the runner in a loop', () => { + var spy = createSpy('stepper') + var runner = new Runner(1000).loop(5, true, 500).queue(null, spy) + + expect(runner.loops(1.3).loops()).toBe(1.3) + expect(spy).toHaveBeenCalledWith(0.7) + + expect(runner.loops(0.3).loops()).toBe(0.3) + }) + }) + + describe('progress()', () => { + it('gets the progress of a runner', () => { + var spy = createSpy('stepper') + var runner = new Runner(1000).queue(null, spy) + + runner.step(300) + expect(spy).toHaveBeenCalledWith(0.3) + + expect(runner.progress()).toBe(0.3) + }) + + it('gets the progress of a runner when looping', () => { + var spy = createSpy('stepper') + var runner = new Runner(800).queue(null, spy).loop(10, false, 200) // duration should be 9800 + + // middle of animation, in the middle of wait time + runner.step(4900) + expect(runner.progress()).toBe(0.5) + expect(spy).toHaveBeenCalledWith(1) + + // start of next loop + runner.step(100) + expect(spy).toHaveBeenCalledWith(0) + + // move 400 into current loop which is 0.5 progress + // the progress value is 5400 / 9800 + runner.step(400) + expect(spy).toHaveBeenCalledWith(0.5) + expect(runner.progress()).toBe(5400 / 9800) + }) + + it('sets the progress of a runner', () => { + var spy = createSpy('stepper') + var runner = new Runner(1000).queue(null, spy) + + expect(runner.progress(0.5).progress()).toBe(0.5) + expect(spy).toHaveBeenCalledWith(0.5) + }) + + it('sets the progress of a runner when looping', () => { + var spy = createSpy('stepper') + var runner = new Runner(800).queue(null, spy).loop(10, false, 200) + + // progress 0.5 somewhere in the middle of wait time + expect(runner.progress(0.5).progress()).toBe(0.5) + expect(spy).toHaveBeenCalledWith(1) + + // start of next loop + runner.step(100) + expect(spy).toHaveBeenCalledWith(0) + + // should move 0.5 into the next loop + expect(runner.progress(5400 / 9800).progress()).toBe(5400 / 9800) + expect(spy.calls.mostRecent().args[0]).toBeCloseTo(0.5) + }) + }) + + describe('position()', () => { + + it('gets the position of a runner', () => { + var spy = createSpy('stepper') + var runner = new Runner(1000).queue(null, spy) + + runner.step(300) + expect(spy).toHaveBeenCalledWith(0.3) + + expect(runner.position()).toBe(0.3) + }) + + it('gets the position of a runner when looping', () => { + var spy = createSpy('stepper') + var runner = new Runner(1000).loop(5, true, 100).queue(null, spy) + + runner.step(1200) + expect(spy).toHaveBeenCalledWith(0.9) + + expect(runner.position()).toBe(0.9) + }) + + it('sets the position of a runner', () => { + var spy = createSpy('stepper') + var runner = new Runner(1000).queue(null, spy) + + expect(runner.position(0.5).position()).toBe(0.5) + expect(spy).toHaveBeenCalledWith(0.5) + }) + + it('sets the position of a runner in a loop', () => { + var spy = createSpy('stepper') + var runner = new Runner(1000).loop(5, true, 100).queue(null, spy) + + runner.step(1200) + expect(runner.position(0.4).position()).toBe(0.4) + expect(spy).toHaveBeenCalledWith(0.4) + + expect(runner.position(0).position()).toBe(0) + expect(spy).toHaveBeenCalledWith(0) + + expect(runner.position(1).position()).toBe(1) + expect(spy).toHaveBeenCalledWith(1) + }) + }) + + describe('element()', () => { + it('returns the element bound to this runner if any', () => { + var runner1 = new Runner() + expect(runner1.element()).toBe(null) + + var element = SVG('') + var runner2 = element.animate() + expect(runner2.element()).toBe(element) + }) + + it('sets an element to be bound to the runner', () => { + var runner = new Runner() + var element = SVG('') + expect(runner.element(element)).toBe(runner) + expect(runner.element()).toBe(element) + }) + }) + + describe('timeline()', () => { + it('returns the timeline bound to this runner if any', () => { + var runner1 = new Runner() + expect(runner1.element()).toBe(null) + + var element = SVG('') + var runner2 = element.animate() + expect(runner2.timeline()).toBe(element.timeline()) + }) + + it('sets a timeline to be bound to the runner', () => { + var runner = new Runner() + var timeline = new Timeline() + expect(runner.timeline(timeline)).toBe(runner) + expect(runner.timeline()).toBe(timeline) + }) + }) + + describe('schedule()', () => { + it('schedules the runner on a timeline', () => { + var runner = new Runner() + var timeline = new Timeline() + var spy = spyOn(timeline, 'schedule').and.callThrough() + + expect(runner.schedule(timeline, 200, 'now')).toBe(runner) + expect(runner.timeline()).toBe(timeline) + expect(spy).toHaveBeenCalledWith(runner, 200, 'now') + }) + + it('schedules the runner on its own timeline', () => { + var runner = new Runner() + var timeline = new Timeline() + var spy = spyOn(timeline, 'schedule') + runner.timeline(timeline) + + expect(runner.schedule(200, 'now')).toBe(runner) + expect(runner.timeline()).toBe(timeline) + expect(spy).toHaveBeenCalledWith(runner, 200, 'now') + }) + }) + + describe('unschedule()', () => { + it('unschedules this runner from its timeline', () => { + var runner = new Runner() + var timeline = new Timeline() + var spy = spyOn(timeline, 'unschedule').and.callThrough() + + expect(runner.schedule(timeline, 200, 'now')).toBe(runner) + expect(runner.unschedule()).toBe(runner) + expect(spy).toHaveBeenCalledWith(runner) + expect(runner.timeline()).toBe(null) + }) + }) + + describe('animate()', () => { + it('creates a new runner scheduled after the first', () => { + var runner = new Runner(1000) + var timeline = new Timeline() + + runner.schedule(timeline) + + var runner2 = runner.animate(500, 1000) + + var t = timeline.time() + + expect(runner2.timeline()).toBe(timeline) + expect(runner2.time()).toBe(0) + + expect(timeline.schedule()).toEqual(arrayContaining([ + objectContaining({ start: t, duration: 1000, end: t + 1000, runner: runner }), + objectContaining({ start: t + 2000, duration: 500, end: t + 2500, runner: runner2 }) + ])) + }) + }) + + describe('delay()', () => { + it('calls animate with delay parameters', () => { + var runner = new Runner(1000) + spyOn(runner, 'animate') + + runner.delay(500) + expect(runner.animate).toHaveBeenCalledWith(0, 500) + }) + }) + + describe('during()', () => { + it('returns itself', () => { + var runner = new Runner() + expect(runner.during(runFn)).toBe(runner) + }) + + it('calls queue passing only a function to call on every step', () => { + var runner = new Runner() + spyOn(runner, 'queue') + runner.during(runFn) + + expect(runner.queue).toHaveBeenCalledWith(null, runFn) + }) + }) + + // describe('after()', () => { + // it('returns itself', () => { + // var runner = new Runner() + // expect(runner.after(runFn)).toBe(runner) + // }) + // + // it('binds a function to the after event', () => { + // var runner = new Runner() + // spyOn(runner, 'on') + // runner.after(runFn) + // + // expect(runner.on).toHaveBeenCalledWith('finish', runFn) + // }) + // }) + // + // describe('finish()', () => { + // it('returns itself', () => { + // var runner = new Runner() + // expect(runner.finish()).toBe(runner) + // }) + // + // it('calls step with Infinity as argument', () => { + // var runner = new Runner() + // spyOn(runner, 'step') + // runner.finish() + // + // expect(runner.step).toHaveBeenCalledWith(Infinity) + // }) + // }) + + describe('reverse()', () => { + it('returns itself', () => { + var runner = new Runner() + expect(runner.reverse()).toBe(runner) + }) + + it('reverses the runner', () => { + var spy = createSpy('stepper') + var runner = new Runner(1000).reverse().queue(null, spy) + runner.step(750) + expect(spy).toHaveBeenCalledWith(0.25) + }) + }) + + describe('ease()', () => { + it('returns itself', () => { + var runner = new Runner() + expect(runner.ease(() => {})).toBe(runner) + }) + + it('creates an easing Controller from the easing function', () => { + var runner = new Runner() + runner.ease(() => {}) + + expect(runner._stepper instanceof Ease).toBe(true) + }) + }) +}) diff --git a/spec/spec/animation/Timeline.js b/spec/spec/animation/Timeline.js new file mode 100644 index 0000000..1acc663 --- /dev/null +++ b/spec/spec/animation/Timeline.js @@ -0,0 +1,117 @@ +/* globals describe, expect, it, beforeEach, container */ + +import { Timeline, SVG } from '../../../src/main.js' + +describe('Timeline.js', () => { + describe('getEndTimeOfTimeline', () => { + it('returns 0 if no runners are scheduled', () => { + const timeline = new Timeline() + const endTime = timeline.getEndTimeOfTimeline() + expect(endTime).toEqual(0) + }) + }) + + describe('finish - issue #964', () => { + let canvas + + beforeEach(() => { + canvas = SVG().addTo(container) + }) + + it('places all elements at the right position - single runner', () => { + const timeline = new Timeline() + + const rect = canvas.rect(20, 20) + rect.timeline(timeline) + rect.animate().move(100, 200) + + timeline.finish() + expect(rect.x()).toEqual(100) + expect(rect.y()).toEqual(200) + }) + + it('places all elements at the right position - runner that finishes latest is in first position', () => { + const timeline = new Timeline() + + const rect1 = canvas.rect(10, 10) + rect1.timeline(timeline) + + const rect2 = canvas.rect(10, 10) + rect2.timeline(timeline) + + const rect3 = canvas.rect(10, 10) + rect3.timeline(timeline) + + rect1.animate(2000, 0, 'now').move(100, 200) + rect2.animate(1000, 0, 'now').move(100, 200) + rect3.animate(1000, 500, 'now').move(100, 200) + + timeline.finish() + + expect(rect1.x()).toEqual(100) + expect(rect1.y()).toEqual(200) + + expect(rect2.x()).toEqual(100) + expect(rect2.y()).toEqual(200) + + expect(rect3.x()).toEqual(100) + expect(rect3.y()).toEqual(200) + }) + + it('places all elements at the right position - runner that finishes latest is in middle position', () => { + const timeline = new Timeline() + + const rect1 = canvas.rect(10, 10) + rect1.timeline(timeline) + + const rect2 = canvas.rect(10, 10) + rect2.timeline(timeline) + + const rect3 = canvas.rect(10, 10) + rect3.timeline(timeline) + + rect2.animate(1000, 0, 'now').move(100, 200) + rect1.animate(2000, 0, 'now').move(100, 200) + rect3.animate(1000, 500, 'now').move(100, 200) + + timeline.finish() + + expect(rect1.x()).toEqual(100) + expect(rect1.y()).toEqual(200) + + expect(rect2.x()).toEqual(100) + expect(rect2.y()).toEqual(200) + + expect(rect3.x()).toEqual(100) + expect(rect3.y()).toEqual(200) + }) + + it('places all elements at the right position - runner that finishes latest is in last position', () => { + const timeline = new Timeline() + + const rect1 = canvas.rect(10, 10) + rect1.timeline(timeline) + + const rect2 = canvas.rect(10, 10) + rect2.timeline(timeline) + + const rect3 = canvas.rect(10, 10) + rect3.timeline(timeline) + + rect2.animate(1000, 0, 'now').move(100, 200) + rect3.animate(1000, 500, 'now').move(100, 200) + rect1.animate(2000, 0, 'now').move(100, 200) + + timeline.finish() + + expect(rect1.x()).toEqual(100) + expect(rect1.y()).toEqual(200) + + expect(rect2.x()).toEqual(100) + expect(rect2.y()).toEqual(200) + + expect(rect3.x()).toEqual(100) + expect(rect3.y()).toEqual(200) + }) + }) +}) diff --git a/spec/spec/animation/easing.js b/spec/spec/animation/easing.js new file mode 100644 index 0000000..ab9f51c --- /dev/null +++ b/spec/spec/animation/easing.js @@ -0,0 +1,26 @@ +/* globals describe, expect, it */ + +import { easing } from '../../../src/main.js' + +describe('easing', () => { + var easedValues = { + '-': 0.5, + '<>': 0.5, + '>': 0.7071, + '<': 0.2929 + } + + ;[ '-', '<>', '<', '>' ].forEach((el) => { + describe(el, () => { + it('is 0 at 0', () => { + expect(easing[el](0)).toBe(0) + }) + it('is 1 at 1', () => { + expect(Math.round(easing[el](1) * 1000) / 1000).toBe(1) // we need to round cause for some reason at some point 1==0.999999999 + }) + it('is eased at 0.5', () => { + expect(easing[el](0.5)).toBeCloseTo(easedValues[el]) + }) + }) + }) +}) diff --git a/spec/spec/animator.js b/spec/spec/animator.js deleted file mode 100644 index 5ccdcca..0000000 --- a/spec/spec/animator.js +++ /dev/null @@ -1,50 +0,0 @@ -describe('SVG.Animator', function () { - - beforeEach(function () { - jasmine.RequestAnimationFrame.install() - SVG.Animator.timeouts = new SVG.Queue() - SVG.Animator.frames = new SVG.Queue() - SVG.Animator.nextDraw = null - }) - - afterEach(function () { - jasmine.RequestAnimationFrame.uninstall() - }) - - describe('timeout()', function () { - it('calls a function after a specific time', function () { - - var spy = jasmine.createSpy('tester') - var id = SVG.Animator.timeout(spy, 100) - - jasmine.RequestAnimationFrame.tick(99) - expect(spy).not.toHaveBeenCalled() - jasmine.RequestAnimationFrame.tick() - expect(spy).toHaveBeenCalled() - }) - }) - - describe('cancelTimeout()', function () { - it('cancels a timeout which was created with timeout()', function () { - var spy = jasmine.createSpy('tester') - var id = SVG.Animator.timeout(spy, 100) - SVG.Animator.clearTimeout(id) - - expect(spy).not.toHaveBeenCalled() - jasmine.RequestAnimationFrame.tick(100) - expect(spy).not.toHaveBeenCalled() - }) - }) - - describe('frame()', function () { - it('calls a function at the next animationFrame', function () { - var spy = jasmine.createSpy('tester') - - SVG.Animator.frame(spy) - expect(spy).not.toHaveBeenCalled() - jasmine.RequestAnimationFrame.tick() - expect(spy).toHaveBeenCalled() - }) - }) - -}) diff --git a/spec/spec/color.js b/spec/spec/color.js deleted file mode 100644 index 1f0dfbd..0000000 --- a/spec/spec/color.js +++ /dev/null @@ -1,270 +0,0 @@ - -describe('Color', function() { - var color - - beforeEach(function() { - color = new SVG.Color({ r: 0, g: 102, b: 255 }) - }) - - describe ('construct: constructs a color in different formats', () => { - - it ('constructs a color from an object in the correct color space', () => { - - // Try in rgb - let color = new SVG.Color({ r: 255, g: 0, b: 128 }) - expect(color.r).toBe(255) - expect(color.g).toBe(0) - expect(color.b).toBe(128) - expect(color.space).toBe('rgb') - - // Try in cmyk - let color2 = new SVG.Color({ c: 20, y: 15, m: 10, k: 5 }) - expect(color2.c).toBe(20) - expect(color2.m).toBe(10) - expect(color2.y).toBe(15) - expect(color2.k).toBe(5) - expect(color2.space).toBe('cmyk') - }) - - it ('constructs a color from an array', () => { - let color = new SVG.Color([ 30, 24, 50 ]) - expect( color.r ).toBe( 30 ) - expect( color.g ).toBe( 24 ) - expect( color.b ).toBe( 50 ) - expect( color.space ).toBe('rgb') - }) - - it ('constructs a color from an array with space in array', () => { - let color = new SVG.Color([ 50, 50, 5, 'lab' ]) - expect( color.l ).toBe( 50 ) - expect( color.a ).toBe( 50 ) - expect( color.b ).toBe( 5 ) - expect( color.space ).toBe('lab') - }) - - it ('constructs a color from an array with space given', () => { - let color = new SVG.Color([ 50, 50, 5], 'lab' ) - expect( color.l ).toBe( 50 ) - expect( color.a ).toBe( 50 ) - expect( color.b ).toBe( 5 ) - expect( color.space ).toBe('lab') - }) - - it('correclty parses an rgb string', () => { - let color = new SVG.Color('rgb(255,0,128)') - expect(color.r).toBe(255) - expect(color.g).toBe(0) - expect(color.b).toBe(128) - }) - - it('correclty parses a 3 digit hex string', () => { - color = new SVG.Color('#f06') - expect(color.r).toBe(255) - expect(color.g).toBe(0) - expect(color.b).toBe(102) - }) - - it('correclty parses a 6 digit hex string', () => { - color = new SVG.Color('#0066ff') - expect(color.r).toBe(0) - expect(color.g).toBe(102) - expect(color.b).toBe(255) - }) - - }) - - describe ('input and output: Importing and exporting colors', () => { - describe('toHex()', function() { - it('returns a hex color', function() { - expect(color.toHex()).toBe('#0066ff') - }) - }) - - describe('toRgb()', function() { - it('returns a rgb string color', function() { - expect(color.toRgb()).toBe('rgb(0,102,255)') - }) - }) - }) - - describe('color spaces: The color spaces supported by our library', () => { - - describe('lab()', () => { - it ('can convert rgb to lab', () => { - let color = new SVG.Color( 255, 0, 128 ) - let lab = color.lab() - expect( lab.l ).toBeCloseTo( 54.88, 1 ) - expect( lab.a ).toBeCloseTo( 84.55, 1 ) - expect( lab.b ).toBeCloseTo( 4.065, 1 ) - expect( lab.space ).toBe('lab') - }) - - it ('can convert from lab to rgb', () => { - let lab = new SVG.Color( 54.88, 84.55, 4.065, 'lab' ) - let rgb = lab.rgb() - expect( rgb.r ).toBeCloseTo( 255, 0 ) - expect( rgb.g ).toBeCloseTo( 0, 0 ) - expect( rgb.b ).toBeCloseTo( 128, 0 ) - expect( rgb.space ).toBe('rgb') - }) - - it ('is invertable', () => { - let { r, g, b } = new SVG.Color( 255, 0, 128 ).lab().rgb() - expect ( r ).toBeCloseTo( 255, 0 ) - expect ( g ).toBeCloseTo( 0, 0 ) - expect ( b ).toBeCloseTo( 128, 0 ) - }) - - it('handles black', () => { - let color = new SVG.Color(0, 0, 0).lab().rgb() - expect( color.r ).toBeCloseTo(0, 0) - expect( color.g ).toBeCloseTo(0, 0) - expect( color.b ).toBeCloseTo(0, 0) - expect( color.toHex() ).toBe('#000000') - }) - - it('handles white', () => { - let color = new SVG.Color(255, 255, 255).lab().rgb() - expect( color.r ).toBeCloseTo(255, 0) - expect( color.g ).toBeCloseTo(255, 0) - expect( color.b ).toBeCloseTo(255, 0) - expect( color.toHex() ).toBe('#ffffff') - }) - }) - - describe('lch()', () => { - it ('can convert rgb to lch', () => { - let color = new SVG.Color( 255, 0, 128 ) - let lch = color.lch() - expect( lch.l ).toBeCloseTo( 54.88, 1 ) - expect( lch.c ).toBeCloseTo( 84.65, 1 ) - expect( lch.h ).toBeCloseTo( 2.75, 1 ) - expect( lch.space ).toBe('lch') - }) - - it ('can convert from lch to rgb', () => { - let lch = new SVG.Color( 54.88, 84.65, 2.75, 'lch' ) - let rgb = lch.rgb() - expect( rgb.r ).toBeCloseTo( 255, 0 ) - expect( rgb.g ).toBeCloseTo( 0, 0 ) - expect( rgb.b ).toBeCloseTo( 128, 0 ) - expect( rgb.space ).toBe('rgb') - }) - - it ('is invertable', () => { - let { r, g, b } = new SVG.Color( 255, 0, 128 ).lch().rgb() - expect ( r ).toBeCloseTo( 255, 0 ) - expect ( g ).toBeCloseTo( 0, 0 ) - expect ( b ).toBeCloseTo( 128, 0 ) - }) - - it('handles black', () => { - let color = new SVG.Color(0, 0, 0).lch().rgb() - expect( color.r ).toBeCloseTo(0, 0) - expect( color.g ).toBeCloseTo(0, 0) - expect( color.b ).toBeCloseTo(0, 0) - expect( color.toHex() ).toBe('#000000') - }) - - it('handles white', () => { - let color = new SVG.Color(255, 255, 255).lch().rgb() - expect( color.r ).toBeCloseTo(255, 0) - expect( color.g ).toBeCloseTo(255, 0) - expect( color.b ).toBeCloseTo(255, 0) - expect( color.toHex() ).toBe('#ffffff') - }) - }) - - describe('hsl()', () => { - - it ('can convert from rgb to hsl', () => { - let color = new SVG.Color( 255, 0, 128 ) - let hsl = color.hsl() - expect( hsl.h ).toBeCloseTo( 329.88, 1 ) - expect( hsl.s ).toBeCloseTo( 100, 1 ) - expect( hsl.l ).toBeCloseTo( 50, 1 ) - expect( hsl.space ).toBe('hsl') - }) - - it ('can convert from hsl to rgb', () => { - let hsl = new SVG.Color( 329.88, 100, 50, 'hsl' ) - let rgb = hsl.rgb() - expect( rgb.r ).toBeCloseTo( 255, 0 ) - expect( rgb.g ).toBeCloseTo( 0, 0 ) - expect( rgb.b ).toBeCloseTo( 128, 0 ) - expect( rgb.space ).toBe('rgb') - }) - - it ('is invertable', () => { - let { r, g, b } = new SVG.Color( 255, 0, 128 ).hsl().rgb() - expect ( r ).toBeCloseTo( 255, 0 ) - expect ( g ).toBeCloseTo( 0, 0 ) - expect ( b ).toBeCloseTo( 128, 0 ) - }) - - it('handles black', () => { - let color = new SVG.Color(0, 0, 0).hsl().rgb() - expect( color.r ).toBeCloseTo(0, 0) - expect( color.g ).toBeCloseTo(0, 0) - expect( color.b ).toBeCloseTo(0, 0) - expect( color.toHex() ).toBe('#000000') - }) - - it('handles white', () => { - let color = new SVG.Color(255, 255, 255).hsl().rgb() - expect( color.r ).toBeCloseTo(255, 0) - expect( color.g ).toBeCloseTo(255, 0) - expect( color.b ).toBeCloseTo(255, 0) - expect( color.toHex() ).toBe('#ffffff') - }) - }) - - describe('cmyk()', () => { - - it ('can convert from rgb to cmyk', () => { - let color = new SVG.Color( 255, 0, 128 ) - let cmyk = color.cmyk() - expect( cmyk.c ).toBeCloseTo( 0, 1 ) - expect( cmyk.m ).toBeCloseTo( 1, 1 ) - expect( cmyk.y ).toBeCloseTo( 0.49, 1 ) - expect( cmyk.k ).toBeCloseTo( 0, 1 ) - expect( cmyk.space ).toBe('cmyk') - }) - - it ('can convert from cmyk to rgb', () => { - let color = new SVG.Color( 0, 1, 0.49, 0, 'cmyk' ) - let rgb = color.rgb() - expect( rgb.r ).toBeCloseTo( 255, -1 ) - expect( rgb.g ).toBeCloseTo( 0, -1 ) - expect( rgb.b ).toBeCloseTo( 128, -1 ) - expect( rgb.space ).toBe('rgb') - }) - - it ('is invertable', () => { - let { r, g, b } = new SVG.Color( 255, 0, 128 ).cmyk().rgb() - expect ( r ).toBeCloseTo( 255, 0 ) - expect ( g ).toBeCloseTo( 0, 0 ) - expect ( b ).toBeCloseTo( 128, 0 ) - }) - - it('handles black', () => { - let color = new SVG.Color(0, 0, 0).cmyk().rgb() - expect( color.r ).toBeCloseTo(0, 0) - expect( color.g ).toBeCloseTo(0, 0) - expect( color.b ).toBeCloseTo(0, 0) - expect( color.toHex() ).toBe('#000000') - }) - - it('handles white', () => { - let color = new SVG.Color(255, 255, 255).cmyk().rgb() - expect( color.r ).toBeCloseTo(255, 0) - expect( color.g ).toBeCloseTo(255, 0) - expect( color.b ).toBeCloseTo(255, 0) - expect( color.toHex() ).toBe('#ffffff') - }) - - }) - - }) - -}) diff --git a/spec/spec/easing.js b/spec/spec/easing.js deleted file mode 100644 index 04690ac..0000000 --- a/spec/spec/easing.js +++ /dev/null @@ -1,22 +0,0 @@ -describe('SVG.easing', function() { - var easedValues = { - '-':0.5, - '<>':0.5, - '>':0.7071, - '<':0.2929, - } - - ;['-', '<>', '<', '>'].forEach(function(el) { - describe(el, function() { - it('is 0 at 0', function() { - expect(SVG.easing[el](0)).toBe(0) - }) - it('is 1 at 1', function() { - expect(Math.round(SVG.easing[el](1)*1000)/1000).toBe(1) // we need to round cause for some reason at some point 1==0.999999999 - }) - it('is eased at 0.5', function() { - expect(SVG.easing[el](0.5)).toBeCloseTo(easedValues[el]) - }) - }) - }) -}) diff --git a/spec/spec/modules/optional/transform.js b/spec/spec/modules/optional/transform.js new file mode 100644 index 0000000..db59784 --- /dev/null +++ b/spec/spec/modules/optional/transform.js @@ -0,0 +1,141 @@ +/* globals describe, expect, it, spyOn, container */ + +import { Rect, Matrix, SVG } from '../../../../src/main.js' + +describe('transform.js', () => { + describe('untransform()', () => { + it('returns itself', () => { + const rect = new Rect() + expect(rect.untransform()).toBe(rect) + }) + + it('deletes the transform attribute', () => { + const rect = new Rect() + expect(rect.untransform().attr('transform')).toBe(undefined) + }) + }) + + describe('matrixify()', () => { + it('reduces all transformations of the transform list into one matrix - 1', () => { + const rect = new Rect().attr('transform', 'matrix(1, 0, 1, 1, 0, 1)') + expect(rect.matrixify()).toEqual(new Matrix(1, 0, 1, 1, 0, 1)) + }) + + it('reduces all transformations of the transform list into one matrix - 2', () => { + const rect = new Rect().attr('transform', 'translate(10, 20) rotate(45)') + expect(rect.matrixify()).toEqual(new Matrix().rotate(45).translate(10, 20)) + }) + + it('reduces all transformations of the transform list into one matrix - 3', () => { + const rect = new Rect().attr('transform', 'translate(10, 20) rotate(45) skew(1,2) skewX(10) skewY(20)') + expect(rect.matrixify()).toEqual(new Matrix().skewY(20).skewX(10).skew(1, 2).rotate(45).translate(10, 20)) + }) + }) + + describe('toParent()', () => { + it('returns itself', () => { + const canvas = SVG().addTo(container) + const g = canvas.group() + const rect = g.rect(100, 100) + expect(rect.toParent(canvas)).toBe(rect) + }) + + it('does nothing if the parent is the the current element', () => { + const canvas = SVG().addTo(container) + const g = canvas.group() + const parent = g.parent() + const position = g.position() + g.toParent(g) + expect(g.parent()).toBe(parent) + expect(g.position()).toBe(position) + }) + + it('moves an element to a different container without changing its visual representation - 1', () => { + const canvas = SVG().addTo(container) + const g = canvas.group().matrix(1, 0, 1, 1, 0, 1) + const rect = g.rect(100, 100) + rect.toParent(canvas) + expect(rect.matrix()).toEqual(new Matrix(1, 0, 1, 1, 0, 1)) + expect(rect.parent()).toBe(canvas) + }) + + it('moves an element to a different container without changing its visual representation - 2', () => { + const canvas = SVG().addTo(container) + const g = canvas.group().translate(10, 20) + const rect = g.rect(100, 100) + const g2 = canvas.group().rotate(10) + rect.toParent(g2) + const actual = rect.matrix() + const expected = new Matrix().translate(10, 20).rotate(-10) + + // funny enough the dom seems to shorten the floats and precision gets lost + ;[ ...'abcdef' ].forEach(prop => expect(actual[prop]).toBeCloseTo(expected[prop], 5)) + }) + + it('inserts the element at the specified position', () => { + const canvas = SVG().addTo(container) + const g = canvas.group() + const rect = g.rect(100, 100) + canvas.rect(100, 100) + canvas.rect(100, 100) + expect(rect.toParent(canvas, 2).position()).toBe(2) + }) + }) + + describe('toRoot()', () => { + it('calls toParent() with root node', () => { + const canvas = SVG().addTo(container) + const g = canvas.group().matrix(1, 0, 1, 1, 0, 1) + const rect = g.rect(100, 100) + const spy = spyOn(rect, 'toParent') + rect.toRoot(3) + expect(spy).toHaveBeenCalledWith(canvas, 3) + }) + }) + + describe('transform()', () => { + it('acts as full getter with no argument', () => { + const rect = new Rect().attr('transform', 'translate(10, 20) rotate(45)') + const actual = rect.transform() + const expected = new Matrix().rotate(45).translate(10, 20).decompose() + + expect(actual).toEqual(expected) + }) + + it('returns a single transformation value when string was passed', () => { + const rect = new Rect().attr('transform', 'translate(10, 20) rotate(45)') + expect(rect.transform('rotate')).toBe(45) + expect(rect.transform('translateX')).toBe(10) + expect(rect.transform('translateY')).toBe(20) + }) + + it('sets the transformation with an object', () => { + const rect = new Rect().transform({ rotate: 45, translate: [ 10, 20 ] }) + expect(rect.transform('rotate')).toBe(45) + expect(rect.transform('translateX')).toBe(10) + expect(rect.transform('translateY')).toBe(20) + }) + + it('performs a relative transformation with flag=true', () => { + const rect = new Rect().transform({ rotate: 45, translate: [ 10, 20 ] }).transform({ rotate: 10 }, true) + expect(rect.transform('rotate')).toBeCloseTo(55, 5) // rounding errors + expect(rect.transform('translateX')).toBe(10) + expect(rect.transform('translateY')).toBe(20) + }) + + it('performs a relative transformation with flag=other matrix', () => { + const rect = new Rect().transform({ rotate: 45, translate: [ 10, 20 ] }).transform({ rotate: 10 }, new Matrix().rotate(30)) + expect(rect.transform('rotate')).toBeCloseTo(40, 5) // rounding errors + expect(rect.transform('translateX')).toBe(0) + expect(rect.transform('translateY')).toBe(0) + }) + + it('performs a relative transformation with flag=other element', () => { + const referenceElement = new Rect().transform({ rotate: 30 }) + const rect = new Rect().transform({ rotate: 45, translate: [ 10, 20 ] }).transform({ rotate: 10 }, referenceElement) + expect(rect.transform('rotate')).toBeCloseTo(40, 5) // rounding errors + expect(rect.transform('translateX')).toBe(0) + expect(rect.transform('translateY')).toBe(0) + }) + }) +}) diff --git a/spec/spec/morphing.js b/spec/spec/morphing.js deleted file mode 100644 index 505440f..0000000 --- a/spec/spec/morphing.js +++ /dev/null @@ -1,161 +0,0 @@ -describe('Morphing', function () { - describe('constructors', function () { - - it('SVG.Morphable with Number', function () { - var morpher = new SVG.Morphable().from(10).to(5) - - expect(morpher instanceof SVG.Morphable).toBe(true) - expect(morpher.type()).toBe(SVG.Number) - expect(morpher.at(0.5) instanceof SVG.Number).toBe(true) - expect(morpher.at(0.5).valueOf()).toBe(7.5) - }) - - it('SVG.Morphable with String', function () { - var morpher = new SVG.Morphable().from('foo').to('bar') - - expect(morpher instanceof SVG.Morphable).toBe(true) - expect(morpher.type()).toBe(SVG.NonMorphable) - expect(morpher.at(0.5) instanceof SVG.NonMorphable).toBe(true) - expect(morpher.at(0.5).valueOf()).toBe('foo') - expect(morpher.at(1).valueOf()).toBe('bar') - }) - - it('SVG.Morphable with Object', function () { - var morpher = new SVG.Morphable().from({a:5, b: 10}).to({a: 10, b: 20}) - - expect(morpher instanceof SVG.Morphable).toBe(true) - expect(morpher.type()).toBe(SVG.ObjectBag) - expect(morpher.at(0.5) instanceof Object).toBe(true) - expect(morpher.at(0.5).valueOf()).toEqual(jasmine.objectContaining({a: 7.5, b: 15})) - }) - - it('Creates a morphable out of an SVG.Number', function () { - var morpher = new SVG.Number(5).to(10) - - expect(morpher instanceof SVG.Morphable).toBe(true) - expect(morpher.type()).toBe(SVG.Number) - expect(morpher.at(0.5) instanceof SVG.Number).toBe(true) - expect(morpher.at(0.5).valueOf()).toBe(7.5) - }) - - it('Creates a morphable out of an SVG.Color', function () { - var morpher = new SVG.Color('#fff').to('#000') - - expect(morpher instanceof SVG.Morphable).toBe(true) - expect(morpher.type()).toBe(SVG.Color) - expect(morpher.at(0.5) instanceof SVG.Color).toBe(true) - expect(morpher.at(0.5).toHex()).toBe('#808080') - }) - - it('Creates a morphable out of an SVG.Box', function () { - var morpher = new SVG.Box(1, 2, 3, 4).to([5, 6, 7, 8]) - - expect(morpher instanceof SVG.Morphable).toBe(true) - expect(morpher.type()).toBe(SVG.Box) - expect(morpher.at(0.5) instanceof SVG.Box).toBe(true) - expect(morpher.at(0.5)).toEqual(jasmine.objectContaining({x: 3, y: 4, width: 5, height: 6})) - }) - - it('Creates a morphable out of an SVG.Matrix', function () { - var morpher = new SVG.Matrix(1, 2, 3, 4, 5, 6).to([3, 4, 5, 6, 7, 8]) - - expect(morpher instanceof SVG.Morphable).toBe(true) - expect(morpher.type()).toBe(SVG.Matrix) - expect(morpher.at(0.5) instanceof SVG.Matrix).toBe(true) - expect(morpher.at(0.5)).toEqual(jasmine.objectContaining(new SVG.Matrix(2, 3, 4, 5, 6, 7))) - }) - - it('Creates a morphable out of an SVG.Array', function () { - var morpher = new SVG.Array([1,2,3,4,5,6]).to([3,4,5,6,7,8]) - - expect(morpher instanceof SVG.Morphable).toBe(true) - expect(morpher.type()).toBe(SVG.Array) - expect(morpher.at(0.5) instanceof SVG.Array).toBe(true) - expect(morpher.at(0.5).toArray()).toEqual(jasmine.arrayContaining([2, 3, 4, 5, 6, 7])) - }) - - it('Creates a morphable out of an SVG.PointArray', function () { - var morpher = new SVG.PointArray([1, 2, 3, 4, 5, 6]).to([3, 4, 5, 6, 7, 8]) - - expect(morpher instanceof SVG.Morphable).toBe(true) - expect(morpher.type()).toBe(SVG.PointArray) - expect(morpher.at(0.5) instanceof SVG.PointArray).toBe(true) - expect(morpher.at(0.5).toArray()).toEqual(jasmine.arrayContaining([2, 3, 4, 5, 6, 7])) - }) - - it('Creates a morphable out of an SVG.PathArray', function () { - var morpher = new SVG.PathArray(['M', 1, 2, 'L', 3, 4, 'L', 5, 6]).to(['M', 3, 4, 'L', 5, 6, 'L', 7, 8]) - - expect(morpher instanceof SVG.Morphable).toBe(true) - expect(morpher.type()).toBe(SVG.PathArray) - expect(morpher.at(0.5) instanceof SVG.PathArray).toBe(true) - expect(morpher.at(0.5).toArray()).toEqual(jasmine.arrayContaining(['M', 2, 3, 'L', 4, 5, 'L', 6, 7])) - }) - - it('Creates a morphable out of an SVG.NonMorphable', function () { - var morpher = new SVG.NonMorphable('foo').to('bar') - - expect(morpher instanceof SVG.Morphable).toBe(true) - expect(morpher.type()).toBe(SVG.NonMorphable) - expect(morpher.at(0.5) instanceof SVG.NonMorphable).toBe(true) - expect(morpher.at(0.5).valueOf()).toBe('foo') - expect(morpher.at(1).valueOf()).toBe('bar') - }) - - it('Creates a morphable out of an SVG.TransformBag', function () { - var morpher = new SVG.TransformBag({rotate: 0, translateX: 0}) - .to({rotate: 50, translateX: 20}) - - expect(morpher instanceof SVG.Morphable).toBe(true) - expect(morpher.type()).toBe(SVG.TransformBag) - expect(morpher.at(0.5) instanceof SVG.TransformBag).toBe(true) - - expect(morpher.at(0.5)).toEqual(jasmine.objectContaining({rotate: 25, translateX: 10})) - }) - - it('Creates a morphable out of an SVG.ObjectBag', function () { - var morpher = new SVG.ObjectBag({a:5, b: 10}).to({a: 10, b: 20}) - - expect(morpher instanceof SVG.Morphable).toBe(true) - expect(morpher.type()).toBe(SVG.ObjectBag) - expect(morpher.at(0.5) instanceof Object).toBe(true) - expect(morpher.at(0.5).valueOf()).toEqual(jasmine.objectContaining({a: 7.5, b: 15})) - }) - }) - - describe('from()', function () { - it('sets the type of the runner', function () { - var morpher = new SVG.Morphable().from(5) - expect(morpher.type()).toBe(SVG.Number) - }) - - it('sets the from attribute to an array representation of the morphable type', function () { - var morpher = new SVG.Morphable().from(5) - expect(morpher.from()).toEqual(jasmine.arrayContaining([5])) - }) - }) - - describe('type()', function () { - it('sets the type of the runner', function () { - var morpher = new SVG.Morphable().type(SVG.Number) - expect(morpher._type).toBe(SVG.Number) - }) - - it('gets the type of the runner', function () { - var morpher = new SVG.Morphable().type(SVG.Number) - expect(morpher.type()).toBe(SVG.Number) - }) - }) - - describe('to()', function () { - it('sets the type of the runner', function () { - var morpher = new SVG.Morphable().to(5) - expect(morpher.type()).toBe(SVG.Number) - }) - - it('sets the from attribute to an array representation of the morphable type', function () { - var morpher = new SVG.Morphable().to(5) - expect(morpher.to()).toEqual(jasmine.arrayContaining([5])) - }) - }) -}) diff --git a/spec/spec/number.js b/spec/spec/number.js deleted file mode 100644 index eb98fd5..0000000 --- a/spec/spec/number.js +++ /dev/null @@ -1,178 +0,0 @@ -describe('Number', function() { - var number - - beforeEach(function() { - number = new SVG.Number - }) - - describe('new', function() { - it('is zero', function() { - expect(number.value).toBe(0) - }) - it('has a blank unit', function() { - expect(number.unit).toBe('') - }) - it('accepts the unit as a second argument', function() { - number = new SVG.Number(30, '%') - expect(number.value).toBe(30) - expect(number.unit).toBe('%') - }) - it('parses a pixel value', function() { - number = new SVG.Number('20px') - expect(number.value).toBe(20) - expect(number.unit).toBe('px') - }) - it('parses a percent value', function() { - number = new SVG.Number('99%') - expect(number.value).toBe(0.99) - expect(number.unit).toBe('%') - }) - it('parses a seconds value', function() { - number = new SVG.Number('2s') - expect(number.value).toBe(2000) - expect(number.unit).toBe('s') - }) - it('parses a negative percent value', function() { - number = new SVG.Number('-89%') - expect(number.value).toBe(-0.89) - expect(number.unit).toBe('%') - }) - it('falls back to 0 if given value is NaN', function() { - number = new SVG.Number(NaN) - expect(number.value).toBe(0) - }) - it('falls back to maximum value if given number is positive infinite', function() { - number = new SVG.Number(1.7976931348623157E+10308) - expect(number.value).toBe(3.4e+38) - }) - it('falls back to minimum value if given number is negative infinite', function() { - number = new SVG.Number(-1.7976931348623157E+10308) - expect(number.value).toBe(-3.4e+38) - }) - }) - - describe('toString()', function() { - it('converts the number to a string', function() { - expect(number.toString()).toBe('0') - }) - it('appends the unit', function() { - number.value = 1.21 - number.unit = 'px' - expect(number.toString()).toBe('1.21px') - }) - it('converts percent values properly', function() { - number.value = 1.36 - number.unit = '%' - expect(number.toString()).toBe('136%') - }) - it('converts second values properly', function() { - number.value = 2500 - number.unit = 's' - expect(number.toString()).toBe('2.5s') - }) - }) - - describe('valueOf()', function() { - it('returns a numeric value for default units', function() { - expect(typeof number.valueOf()).toBe('number') - number = new SVG.Number('12') - expect(typeof number.valueOf()).toBe('number') - number = new SVG.Number(13) - expect(typeof number.valueOf()).toBe('number') - }) - it('returns a numeric value for pixel units', function() { - number = new SVG.Number('10px') - expect(typeof number.valueOf()).toBe('number') - }) - it('returns a numeric value for percent units', function() { - number = new SVG.Number('20%') - expect(typeof number.valueOf()).toBe('number') - }) - it('converts to a primitive when multiplying', function() { - number.value = 80 - expect(number * 4).toBe(320) - }) - }) - - describe('plus()', function() { - it('returns a new instance', function() { - expect(number.plus(4.5)).not.toBe(number) - expect(number.plus(4.5) instanceof SVG.Number).toBeTruthy() - }) - it('adds a given number', function() { - expect(number.plus(3.5).valueOf()).toBe(3.5) - }) - it('adds a given percentage value', function() { - expect(number.plus('225%').valueOf()).toBe(2.25) - }) - it('adds a given pixel value', function() { - expect(number.plus('83px').valueOf()).toBe(83) - }) - it('use the unit of this number as the unit of the returned number by default', function (){ - expect(new SVG.Number('12s').plus('3%').unit).toBe('s') - }) - it('use the unit of the passed number as the unit of the returned number when this number as no unit', function() { - expect(number.plus('15%').unit).toBe('%') - }) - }) - - describe('minus()', function() { - it('subtracts a given number', function() { - expect(number.minus(3.7).valueOf()).toBe(-3.7) - }) - it('subtracts a given percentage value', function() { - expect(number.minus('223%').valueOf()).toBe(-2.23) - }) - it('subtracts a given pixel value', function() { - expect(number.minus('85px').valueOf()).toBe(-85) - }) - it('use the unit of this number as the unit of the returned number by default', function (){ - expect(new SVG.Number('12s').minus('3%').unit).toBe('s') - }) - it('use the unit of the passed number as the unit of the returned number when this number as no unit', function() { - expect(number.minus('15%').unit).toBe('%') - }) - }) - - describe('times()', function() { - beforeEach(function() { - number = number.plus(4) - }) - it('multiplies with a given number', function() { - expect(number.times(3).valueOf()).toBe(12) - }) - it('multiplies with a given percentage value', function() { - expect(number.times('110%').valueOf()).toBe(4.4) - }) - it('multiplies with a given pixel value', function() { - expect(number.times('85px').valueOf()).toBe(340) - }) - it('use the unit of this number as the unit of the returned number by default', function (){ - expect(new SVG.Number('12s').times('3%').unit).toBe('s') - }) - it('use the unit of the passed number as the unit of the returned number when this number as no unit', function() { - expect(number.times('15%').unit).toBe('%') - }) - }) - - describe('divide()', function() { - beforeEach(function() { - number = number.plus(90) - }) - it('divides by a given number', function() { - expect(number.divide(3).valueOf()).toBe(30) - }) - it('divides by a given percentage value', function() { - expect(number.divide('3000%').valueOf()).toBe(3) - }) - it('divides by a given pixel value', function() { - expect(number.divide('45px').valueOf()).toBe(2) - }) - it('use the unit of this number as the unit of the returned number by default', function (){ - expect(new SVG.Number('12s').divide('3%').unit).toBe('s') - }) - it('use the unit of the passed number as the unit of the returned number when this number as no unit', function() { - expect(number.divide('15%').unit).toBe('%') - }) - }) -}) diff --git a/spec/spec/point.js b/spec/spec/point.js deleted file mode 100644 index 8be944a..0000000 --- a/spec/spec/point.js +++ /dev/null @@ -1,74 +0,0 @@ -describe('Point', function() { - var point - - describe('initialization', function() { - - describe('without a source', function() { - - beforeEach(function() { - point = new SVG.Point - }) - - it('creates a new point with default values', function() { - expect(point.x).toBe(0) - expect(point.y).toBe(0) - }) - - }) - - describe('with x and y given', function() { - it('creates a point with given values', function() { - var point = new SVG.Point(2,4) - - expect(point.x).toBe(2) - expect(point.y).toBe(4) - }) - }) - - describe('with only x given', function() { - it('sets the y value to 0', function() { - var point = new SVG.Point(7) - - expect(point.x).toBe(7) - expect(point.y).toBe(0) - }) - }) - - describe('with array given', function() { - it('creates a point from array', function() { - var point = new SVG.Point([2,4]) - - expect(point.x).toBe(2) - expect(point.y).toBe(4) - }) - }) - - describe('with object given', function() { - it('creates a point from object', function() { - var point = new SVG.Point({x:2,y:4}) - - expect(point.x).toBe(2) - expect(point.y).toBe(4) - }) - }) - - describe('with SVG.Point given', function() { - it('creates a point from SVG.Point', function() { - var point = new SVG.Point(new SVG.Point(2,4)) - - expect(point.x).toBe(2) - expect(point.y).toBe(4) - }) - }) - }) - - describe('clone()', function() { - it('returns cloned point', function() { - var point1 = new SVG.Point(1,1) - , point2 = point1.clone() - - expect(point1).toEqual(point2) - expect(point1).not.toBe(point2) - }) - }) -}) diff --git a/spec/spec/pointarray.js b/spec/spec/pointarray.js deleted file mode 100644 index f50f981..0000000 --- a/spec/spec/pointarray.js +++ /dev/null @@ -1,28 +0,0 @@ -describe('PointArray', function() { - const squareString = '0,0 1,0 1,1 0,1'; - const square = new SVG.PointArray(squareString) - - describe('toString()', function() { - it('round trips with string', () => { - expect(square.toString()).toEqual(squareString) - }) - }) - - describe('transform()', function() { - it('translates correctly', () => { - const translation = new SVG.Matrix().translate(2,1) - const newSquare = square.transform(translation) - expect(newSquare.toString()).toEqual('2,1 3,1 3,2 2,2') - }) - - it('transforms like Point', () => { - const matrix = new SVG.Matrix(1, 2, 3, 4, 5, 6) - const newSquare = square.transform(matrix) - for (let i = 0; i < square.length; i++) { - const squarePoint = new SVG.Point(square[i]) - const newSquarePoint = new SVG.Point(newSquare[i]) - expect(squarePoint.transform(matrix)).toEqual(newSquarePoint) - } - }) - }) -}) diff --git a/spec/spec/queue.js b/spec/spec/queue.js deleted file mode 100644 index 531b900..0000000 --- a/spec/spec/queue.js +++ /dev/null @@ -1,84 +0,0 @@ - -describe ('SVG.Queue()', function () { - - describe('first ()', function () { - - it('returns null if no item in the queue', function () { - var queue = new SVG.Queue() - expect(queue.first()).toEqual(null) - }) - - it ('returns the first value in the queue', function () { - var queue = new SVG.Queue() - queue.push(1) - expect(queue.first()).toBe(1) - queue.push(2) - expect(queue.first()).toBe(1) - }) - }) - - describe('last ()', function () { - - it ('returns null if no item in the queue', function () { - var queue = new SVG.Queue() - expect(queue.last()).toEqual(null) - }) - - it ('returns the last value added', function () { - var queue = new SVG.Queue() - queue.push(1) - expect(queue.last()).toBe(1) - queue.push(2) - expect(queue.last()).toBe(2) - }) - }) - - describe('push ()', function () { - - it ('adds an element to the end of the queue', function () { - var queue = new SVG.Queue() - queue.push(1) - queue.push(2) - queue.push(3) - - expect(queue.first()).toBe(1) - expect(queue.last()).toBe(3) - }) - }) - - describe('remove ()', function () { - it('removes the given item from the queue', function () { - var queue = new SVG.Queue() - queue.push(1) - queue.push(2) - var item = queue.push(3) - - queue.remove(item) - - expect(queue.last()).toBe(2) - expect(queue.first()).toBe(1) - }) - }) - - describe('shift ()', function () { - it('returns nothing if queue is empty', function () { - var queue = new SVG.Queue() - var val = queue.shift() - expect(val).toBeFalsy() - }) - - it('returns the first item of the queue and removes it', function () { - var queue = new SVG.Queue() - queue.push(1) - queue.push(2) - queue.push(3) - - var val = queue.shift() - - expect(queue.last()).toBe(3) - expect(queue.first()).toBe(2) - - expect(val).toBe(1) - }) - }) -}) diff --git a/spec/spec/runner.js b/spec/spec/runner.js deleted file mode 100644 index 2cda176..0000000 --- a/spec/spec/runner.js +++ /dev/null @@ -1,836 +0,0 @@ -describe('SVG.Runner', function () { - - var initFn = jasmine.createSpy('initFn') - var runFn = jasmine.createSpy('runFn') - - beforeEach(function () { - initFn.calls.reset() - runFn.calls.reset() - }) - - describe('sanitise()', function () { - it('can handle all form of input', function () { - var fn = SVG.Runner.sanitise - - expect(fn(200, 200, 'now')).toEqual(jasmine.objectContaining({ - duration: 200, - delay: 200, - when: 'now', - times: 1, - wait: 0, - swing: false - })) - - expect(fn(200, 200)).toEqual(jasmine.objectContaining({ - duration: 200, - delay: 200, - when: 'last', - times: 1, - wait: 0, - swing: false - })) - - expect(fn(200)).toEqual(jasmine.objectContaining({ - duration: 200, - delay: SVG.defaults.timeline.delay, - when: 'last', - times: 1, - wait: 0, - swing: false - })) - - expect(fn(runFn)).toEqual(jasmine.objectContaining({ - duration: runFn, - delay: SVG.defaults.timeline.delay, - when: 'last', - times: 1, - wait: 0, - swing: false - })) - - expect(fn({delay: 200})).toEqual(jasmine.objectContaining({ - duration: SVG.defaults.timeline.duration, - delay: 200, - when: 'last', - times: 1, - wait: 0, - swing: false - })) - - expect(fn({times: 3, delay: 200, when: 'now', swing: true, wait: 200})).toEqual(jasmine.objectContaining({ - duration: SVG.defaults.timeline.duration, - delay: 200, - when: 'now', - times: 3, - wait: 200, - swing: true - })) - }) - }) - - describe('())', function () { - it('creates a runner with defaults', function () { - var runner = new SVG.Runner() - expect(runner instanceof SVG.Runner).toBe(true) - expect(runner._duration).toBe(SVG.defaults.timeline.duration) - expect(runner._stepper instanceof SVG.Ease).toBe(true) - }) - - it('creates a runner with duration set', function () { - var runner = new SVG.Runner(1000) - expect(runner instanceof SVG.Runner).toBe(true) - expect(runner._duration).toBe(1000) - expect(runner._stepper instanceof SVG.Ease).toBe(true) - }) - - it('creates a runner with controller set', function () { - var runner = new SVG.Runner(runFn) - expect(runner instanceof SVG.Runner).toBe(true) - expect(runner._duration).toBeFalsy() - expect(runner._stepper instanceof SVG.Controller).toBe(true) - }) - }) - - describe('constructors', function () { - // FIXME: Not possible to spy like this in es6 - // describe('animate()', function () { - // it('creates a runner with the element set and schedules it on the timeline', function () { - // var orginalRunner = SVG.Runner - // spyOn(SVG, 'Runner').and.callFake(function() { - // return new orginalRunner() - // }) - // - // var element = SVG('') - // var runner = element.animate() - // expect(SVG.Runner).toHaveBeenCalled(); - // expect(runner instanceof SVG.Runner) - // expect(runner.element()).toBe(element) - // expect(runner.timeline()).toBe(element.timeline()) - // }) - // }) - - describe('delay()', function () { - it('calls animate with correct parameters', function () { - var element = SVG('') - - spyOn(element, 'animate') - element.delay(100, 'now') - expect(element.animate).toHaveBeenCalledWith(0, 100, 'now') - }) - }) - }) - - describe('queue()', function () { - it('adds another closure to the runner', function () { - var runner = new SVG.Runner() - runner.queue(initFn, runFn, true) - - expect(runner._queue[0]).toEqual(jasmine.objectContaining({ - initialiser: initFn, - initialised: false, - runner: runFn, - finished: false - })) - }) - }) - - - describe('step()', function () { - - it('returns itself', function () { - var runner = new SVG.Runner() - expect(runner.step()).toBe(runner) - }) - - it('calls initFn once and runFn at every step', function() { - var runner = new SVG.Runner() - runner.queue(initFn, runFn, false) - - runner.step() - expect(initFn).toHaveBeenCalled() - expect(runFn).toHaveBeenCalled() - - runner.step() - expect(initFn.calls.count()).toBe(1) - expect(runFn.calls.count()).toBe(2) - }) - - it('calls initFn on every step if its declaritive', function() { - var runner = new SVG.Runner(new SVG.Controller()) - runner.queue(initFn, runFn, true) - - runner.step() - expect(initFn).toHaveBeenCalled() - expect(runFn).toHaveBeenCalled() - - runner.step() - expect(initFn.calls.count()).toBe(2) - expect(runFn.calls.count()).toBe(2) - }) - - function getLoop(r) { - var loopDuration = r._duration + r._wait - var loopsDone = Math.floor(r._time / loopDuration) - return loopsDone - } - - // step in time - it('steps forward a certain time', function () { - var spy = jasmine.createSpy('stepper') - var r = new SVG.Runner(1000).loop(10, false, 100) - r.queue(null, spy) - - r.step(300) // should be 0.3s - expect(spy).toHaveBeenCalledWith(0.3) - expect(getLoop(r)).toBe(0) - - r.step(300) // should be 0.6s - expect(spy).toHaveBeenCalledWith(0.6) - expect(getLoop(r)).toBe(0) - - r.step(600) // should be 0.1s - expect(spy).toHaveBeenCalledWith(0.1) - expect(getLoop(r)).toBe(1) - - r.step(-300) // should be 0.9s - expect(spy).toHaveBeenCalledWith(0.9) - expect(getLoop(r)).toBe(0) - - r.step(2000) // should be 0.7s - expect(spy).toHaveBeenCalledWith(0.7) - expect(getLoop(r)).toBe(2) - - r.step(-2000) // should be 0.9s - expect(spy).toHaveBeenCalledWith(0.9) - expect(getLoop(r)).toBe(0) - }) - - it('handles dts which are bigger than the animation time', function () { - var runner = new SVG.Runner(1000) - runner.queue(initFn, runFn, true) - - runner.step(1100) - expect(initFn).toHaveBeenCalled() - expect(runFn).toHaveBeenCalledWith(1) - }) - - - describe('looping', function () { - describe('without wait', function () { - describe('unreversed', function () { - describe('nonswinging', function () { - it('does behave correctly at the end of an even loop', function () { - var spy = jasmine.createSpy('stepper') - var runner = new SVG.Runner(1000).loop(6, false) - runner.queue(null, spy) - - runner.step(5750) - expect(spy).toHaveBeenCalledWith(0.75) - runner.step(250) - expect(spy).toHaveBeenCalledWith(1) - }) - - it('does behave correctly at the end of an uneven loop', function () { - var spy = jasmine.createSpy('stepper') - var runner = new SVG.Runner(1000).loop(5, false) - runner.queue(null, spy) - - runner.step(4750) - expect(spy).toHaveBeenCalledWith(0.75) - runner.step(250) - expect(spy).toHaveBeenCalledWith(1) - }) - }) - - describe('swinging', function () { - it('does behave correctly at the end of an even loop', function () { - var spy = jasmine.createSpy('stepper') - var runner = new SVG.Runner(1000).loop(6, true) - runner.queue(null, spy) - - runner.step(5750) - expect(spy).toHaveBeenCalledWith(0.25) - runner.step(250) - expect(spy).toHaveBeenCalledWith(0) - }) - - it('does behave correctly at the end of an uneven loop', function () { - var spy = jasmine.createSpy('stepper') - var runner = new SVG.Runner(1000).loop(5, true) - runner.queue(null, spy) - - runner.step(4750) - expect(spy).toHaveBeenCalledWith(0.75) - runner.step(250) - expect(spy).toHaveBeenCalledWith(1) - }) - }) - }) - - describe('reversed', function () { - describe('nonswinging', function () { - it('does behave correctly at the end of an even loop', function () { - var spy = jasmine.createSpy('stepper') - var runner = new SVG.Runner(1000).loop(6, false).reverse() - runner.queue(null, spy) - - runner.step(5750) - expect(spy).toHaveBeenCalledWith(0.25) - runner.step(250) - expect(spy).toHaveBeenCalledWith(0) - }) - - it('does behave correctly at the end of an uneven loop', function () { - var spy = jasmine.createSpy('stepper') - var runner = new SVG.Runner(1000).loop(5, false).reverse() - runner.queue(null, spy) - - runner.step(4750) - expect(spy).toHaveBeenCalledWith(0.25) - runner.step(250) - expect(spy).toHaveBeenCalledWith(0) - }) - }) - - describe('swinging', function () { - it('does behave correctly at the end of an even loop', function () { - var spy = jasmine.createSpy('stepper') - var runner = new SVG.Runner(1000).loop(6, true).reverse() - runner.queue(null, spy) - - runner.step(5750) - expect(spy).toHaveBeenCalledWith(0.75) - runner.step(250) - expect(spy).toHaveBeenCalledWith(1) - }) - - it('does behave correctly at the end of an uneven loop', function () { - var spy = jasmine.createSpy('stepper') - var runner = new SVG.Runner(1000).loop(5, true).reverse() - runner.queue(null, spy) - - runner.step(4750) - expect(spy).toHaveBeenCalledWith(0.25) - runner.step(250) - expect(spy).toHaveBeenCalledWith(0) - }) - }) - }) - }) - - - describe('with wait', function () { - describe('unreversed', function () { - describe('nonswinging', function () { - it('does behave correctly at the end of an even loop', function () { - var spy = jasmine.createSpy('stepper') - var runner = new SVG.Runner(1000).loop(6, false, 100) - runner.queue(null, spy) - - runner.step(5450) - expect(spy).toHaveBeenCalledWith(1) - spy.calls.reset() - - runner.step(800) - expect(spy).toHaveBeenCalledWith(0.75) - runner.step(250) - expect(spy).toHaveBeenCalledWith(1) - }) - - it('does behave correctly at the end of an uneven loop', function () { - var spy = jasmine.createSpy('stepper') - var runner = new SVG.Runner(1000).loop(5, false, 100) - runner.queue(null, spy) - - runner.step(4350) - expect(spy).toHaveBeenCalledWith(1) - spy.calls.reset() - - runner.step(800) - expect(spy).toHaveBeenCalledWith(0.75) - runner.step(250) - expect(spy).toHaveBeenCalledWith(1) - }) - }) - - describe('swinging', function () { - it('does behave correctly at the end of an even loop', function () { - var spy = jasmine.createSpy('stepper') - var runner = new SVG.Runner(1000).loop(6, true, 100) - runner.queue(null, spy) - - runner.step(5450) - expect(spy).toHaveBeenCalledWith(1) - spy.calls.reset() - - runner.step(800) - expect(spy).toHaveBeenCalledWith(0.25) - runner.step(250) - expect(spy).toHaveBeenCalledWith(0) - }) - - it('does behave correctly at the end of an uneven loop', function () { - var spy = jasmine.createSpy('stepper') - var runner = new SVG.Runner(1000).loop(5, true, 100) - runner.queue(null, spy) - - runner.step(4350) - expect(spy).toHaveBeenCalledWith(0) - spy.calls.reset() - - runner.step(800) - expect(spy).toHaveBeenCalledWith(0.75) - - runner.step(250) - expect(spy).toHaveBeenCalledWith(1) - }) - }) - }) - - describe('reversed', function () { - describe('nonswinging', function () { - it('does behave correctly at the end of an even loop', function () { - var spy = jasmine.createSpy('stepper') - var runner = new SVG.Runner(1000).loop(6, false, 100).reverse() - runner.queue(null, spy) - - runner.step(5450) - expect(spy).toHaveBeenCalledWith(0) - spy.calls.reset() - - runner.step(800) - expect(spy).toHaveBeenCalledWith(0.25) - runner.step(250) - expect(spy).toHaveBeenCalledWith(0) - }) - - it('does behave correctly at the end of an uneven loop', function () { - var spy = jasmine.createSpy('stepper') - var runner = new SVG.Runner(1000).loop(5, false, 100).reverse() - runner.queue(null, spy) - - runner.step(4350) - expect(spy).toHaveBeenCalledWith(0) - spy.calls.reset() - - runner.step(800) - expect(spy).toHaveBeenCalledWith(0.25) - runner.step(250) - expect(spy).toHaveBeenCalledWith(0) - }) - }) - - describe('swinging', function () { - it('does behave correctly at the end of an even loop', function () { - var spy = jasmine.createSpy('stepper') - var runner = new SVG.Runner(1000).loop(6, true, 100).reverse() - runner.queue(null, spy) - - runner.step(5450) - expect(spy).toHaveBeenCalledWith(0) - spy.calls.reset() - - runner.step(800) - expect(spy).toHaveBeenCalledWith(0.75) - runner.step(250) - expect(spy).toHaveBeenCalledWith(1) - }) - - it('does behave correctly at the end of an uneven loop', function () { - var spy = jasmine.createSpy('stepper') - var runner = new SVG.Runner(1000).loop(5, true, 100).reverse() - runner.queue(null, spy) - - runner.step(4350) - expect(spy).toHaveBeenCalledWith(1) - spy.calls.reset() - - runner.step(800) - expect(spy).toHaveBeenCalledWith(0.25) - runner.step(250) - expect(spy).toHaveBeenCalledWith(0) - }) - }) - }) - }) - }) - - - }) - - - describe('active()', function () { - it('acts as a getter without parameters', function () { - var runner = new SVG.Runner() - expect(runner.active()).toBe(true) - }) - - it('disables the runner when false is passed', function () { - var runner = new SVG.Runner() - expect(runner.active(false)).toBe(runner) - expect(runner.active()).toBe(false) - }) - - it('enables the runner when true is passed', function () { - var runner = new SVG.Runner() - expect(runner.active(false)).toBe(runner) - expect(runner.active(true)).toBe(runner) - expect(runner.active()).toBe(true) - }) - }) - - describe('duration()', function () { - it('return the full duration of the runner including all loops and waits', function () { - var runner = new SVG.Runner(800).loop(10, true, 200) - expect(runner.duration()).toBe(9800) - }) - }) - - describe('loop()', function () { - it('makes this runner looping', function () { - var runner = new SVG.Runner(1000).loop(5) - expect(runner.duration()).toBe(5000) - }) - }) - - describe('time()', function () { - it('returns itself', function () { - var runner = new SVG.Runner() - expect(runner.time(0)).toBe(runner) - }) - - it('acts as a getter with no parameter passed', function () { - var runner = new SVG.Runner() - expect(runner.time()).toBe(0) - }) - - it('reschedules the runner to a new time', function () { - var runner = new SVG.Runner() - runner.time(10) - - expect(runner.time()).toBe(10) - }) - - it('calls step to reschedule', function () { - var runner = new SVG.Runner() - spyOn(runner, 'step') - runner.time(10) - - expect(runner.step).toHaveBeenCalledWith(10) - }) - }) - - describe('loops()', function () { - it('get the loops of a runner', function () { - var spy = jasmine.createSpy('stepper') - var runner = new SVG.Runner(1000).queue(null, spy) - - runner.step(300) - expect(spy).toHaveBeenCalledWith(0.3) - - expect(runner.loops()).toBe(0.3) - }) - it('sets the loops of the runner', function () { - var spy = jasmine.createSpy('stepper') - var runner = new SVG.Runner(1000).queue(null, spy) - - expect(runner.loops(0.5).loops()).toBe(0.5) - expect(spy).toHaveBeenCalledWith(0.5) - - expect(runner.loops(0.1).loops()).toBe(0.1) - expect(spy).toHaveBeenCalledWith(0.1) - - expect(runner.loops(1.5).loops()).toBe(1) - expect(spy).toHaveBeenCalledWith(1) - }) - it('sets the loops of the runner in a loop', function () { - var spy = jasmine.createSpy('stepper') - var runner = new SVG.Runner(1000).loop(5, true, 500).queue(null, spy) - - expect(runner.loops(1.3).loops()).toBe(1.3) - expect(spy).toHaveBeenCalledWith(0.7) - - expect(runner.loops(0.3).loops()).toBe(0.3) - }) - }) - - describe('progress()', function () { - it('gets the progress of a runner', function () { - var spy = jasmine.createSpy('stepper') - var runner = new SVG.Runner(1000).queue(null, spy) - - runner.step(300) - expect(spy).toHaveBeenCalledWith(0.3) - - expect(runner.progress()).toBe(0.3) - }) - - it('gets the progress of a runner when looping', function () { - var spy = jasmine.createSpy('stepper') - var runner = new SVG.Runner(800).queue(null, spy).loop(10, false, 200) // duration should be 9800 - - // middle of animation, in the middle of wait time - runner.step(4900) - expect(runner.progress()).toBe(0.5) - expect(spy).toHaveBeenCalledWith(1) - - // start of next loop - runner.step(100) - expect(spy).toHaveBeenCalledWith(0) - - // move 400 into current loop which is 0.5 progress - // the progress value is 5400 / 9800 - runner.step(400) - expect(spy).toHaveBeenCalledWith(0.5) - expect(runner.progress()).toBe(5400 / 9800) - }) - - it('sets the progress of a runner', function () { - var spy = jasmine.createSpy('stepper') - var runner = new SVG.Runner(1000).queue(null, spy) - - expect(runner.progress(0.5).progress()).toBe(0.5) - expect(spy).toHaveBeenCalledWith(0.5) - }) - - it('sets the progress of a runner when looping', function () { - var spy = jasmine.createSpy('stepper') - var runner = new SVG.Runner(800).queue(null, spy).loop(10, false, 200) - - // progress 0.5 somewhere in the middle of wait time - expect(runner.progress(0.5).progress()).toBe(0.5) - expect(spy).toHaveBeenCalledWith(1) - - // start of next loop - runner.step(100) - expect(spy).toHaveBeenCalledWith(0) - - // should move 0.5 into the next loop - expect(runner.progress(5400 / 9800).progress()).toBe(5400 / 9800) - expect(spy.calls.mostRecent().args[0]).toBeCloseTo(0.5) - }) - }) - - describe('position()', function () { - - it('gets the position of a runner', function () { - var spy = jasmine.createSpy('stepper') - var runner = new SVG.Runner(1000).queue(null, spy) - - runner.step(300) - expect(spy).toHaveBeenCalledWith(0.3) - - expect(runner.position()).toBe(0.3) - }) - - it('gets the position of a runner when looping', function () { - var spy = jasmine.createSpy('stepper') - var runner = new SVG.Runner(1000).loop(5, true, 100).queue(null, spy) - - runner.step(1200) - expect(spy).toHaveBeenCalledWith(0.9) - - expect(runner.position()).toBe(0.9) - }) - - it('sets the position of a runner', function () { - var spy = jasmine.createSpy('stepper') - var runner = new SVG.Runner(1000).queue(null, spy) - - expect(runner.position(0.5).position()).toBe(0.5) - expect(spy).toHaveBeenCalledWith(0.5) - }) - - it('sets the position of a runner in a loop', function () { - var spy = jasmine.createSpy('stepper') - var runner = new SVG.Runner(1000).loop(5, true, 100).queue(null, spy) - - runner.step(1200) - expect(runner.position(0.4).position()).toBe(0.4) - expect(spy).toHaveBeenCalledWith(0.4) - - expect(runner.position(0).position()).toBe(0) - expect(spy).toHaveBeenCalledWith(0) - - expect(runner.position(1).position()).toBe(1) - expect(spy).toHaveBeenCalledWith(1) - }) - }) - - describe('element()', function () { - it('returns the element bound to this runner if any', function () { - var runner1 = new SVG.Runner() - expect(runner1.element()).toBe(null) - - var element = SVG('') - var runner2 = element.animate() - expect(runner2.element()).toBe(element) - }) - - it('sets an element to be bound to the runner', function () { - var runner = new SVG.Runner() - var element = SVG('') - expect(runner.element(element)).toBe(runner) - expect(runner.element()).toBe(element) - }) - }) - - describe('timeline()', function () { - it('returns the timeline bound to this runner if any', function () { - var runner1 = new SVG.Runner() - expect(runner1.element()).toBe(null) - - var element = SVG('') - var runner2 = element.animate() - expect(runner2.timeline()).toBe(element.timeline()) - }) - - it('sets a timeline to be bound to the runner', function () { - var runner = new SVG.Runner() - var timeline = new SVG.Timeline() - expect(runner.timeline(timeline)).toBe(runner) - expect(runner.timeline()).toBe(timeline) - }) - }) - - describe('schedule()', function () { - it('schedules the runner on a timeline', function () { - var runner = new SVG.Runner() - var timeline = new SVG.Timeline() - var spy = spyOn(timeline, 'schedule').and.callThrough() - - expect(runner.schedule(timeline, 200, 'now')).toBe(runner) - expect(runner.timeline()).toBe(timeline) - expect(spy).toHaveBeenCalledWith(runner, 200, 'now') - }) - - it('schedules the runner on its own timeline', function () { - var runner = new SVG.Runner() - var timeline = new SVG.Timeline() - var spy = spyOn(timeline, 'schedule') - runner.timeline(timeline) - - expect(runner.schedule(200, 'now')).toBe(runner) - expect(runner.timeline()).toBe(timeline) - expect(spy).toHaveBeenCalledWith(runner, 200, 'now') - }) - }) - - describe('unschedule()', function () { - it('unschedules this runner from its timeline', function () { - var runner = new SVG.Runner() - var timeline = new SVG.Timeline() - var spy = spyOn(timeline, 'unschedule').and.callThrough() - - expect(runner.schedule(timeline, 200, 'now')).toBe(runner) - expect(runner.unschedule()).toBe(runner) - expect(spy).toHaveBeenCalledWith(runner) - expect(runner.timeline()).toBe(null) - }) - }) - - - describe('animate()', function () { - it('creates a new runner scheduled after the first', function () { - var runner = new SVG.Runner(1000) - var timeline = new SVG.Timeline() - - runner.schedule(timeline) - - var runner2 = runner.animate(500, 1000) - - var t = timeline.time() - - expect(runner2.timeline()).toBe(timeline) - expect(runner2.time()).toBe(0) - - expect(timeline.schedule()).toEqual(jasmine.arrayContaining([ - jasmine.objectContaining({start: t, duration: 1000, end: t+1000, runner: runner}), - jasmine.objectContaining({start: t+2000, duration: 500, end: t+2500, runner: runner2}) - ])) - }) - }) - - describe('delay()', function () { - it('calls animate with delay parameters', function () { - var runner = new SVG.Runner(1000) - spyOn(runner, 'animate') - - runner.delay(500) - expect(runner.animate).toHaveBeenCalledWith(0, 500) - }) - }) - - describe('during()', function () { - it('returns itself', function () { - var runner = new SVG.Runner() - expect(runner.during(runFn)).toBe(runner) - }) - - it('calls queue passing only a function to call on every step', function () { - var runner = new SVG.Runner() - spyOn(runner, 'queue') - runner.during(runFn) - - expect(runner.queue).toHaveBeenCalledWith(null, runFn) - }) - }) - - // describe('after()', function () { - // it('returns itself', function () { - // var runner = new SVG.Runner() - // expect(runner.after(runFn)).toBe(runner) - // }) - // - // it('binds a function to the after event', function () { - // var runner = new SVG.Runner() - // spyOn(runner, 'on') - // runner.after(runFn) - // - // expect(runner.on).toHaveBeenCalledWith('finish', runFn) - // }) - // }) - // - // describe('finish()', function () { - // it('returns itself', function () { - // var runner = new SVG.Runner() - // expect(runner.finish()).toBe(runner) - // }) - // - // it('calls step with Infinity as argument', function () { - // var runner = new SVG.Runner() - // spyOn(runner, 'step') - // runner.finish() - // - // expect(runner.step).toHaveBeenCalledWith(Infinity) - // }) - // }) - - describe('reverse()', function () { - it('returns itself', function () { - var runner = new SVG.Runner() - expect(runner.reverse()).toBe(runner) - }) - - it('reverses the runner', function () { - var spy = jasmine.createSpy('stepper') - var runner = new SVG.Runner(1000).reverse().queue(null, spy) - runner.step(750) - expect(spy).toHaveBeenCalledWith(0.25) - }) - }) - - describe('ease()', function () { - it('returns itself', function () { - var runner = new SVG.Runner() - expect(runner.ease(function () {})).toBe(runner) - }) - - it('creates an easing Controller from the easing function', function () { - var runner = new SVG.Runner() - runner.ease(function () {}) - - expect(runner._stepper instanceof SVG.Ease).toBe(true) - }) - }) -}) diff --git a/spec/spec/timeline.js b/spec/spec/timeline.js deleted file mode 100644 index f28a85f..0000000 --- a/spec/spec/timeline.js +++ /dev/null @@ -1,112 +0,0 @@ -describe('timeline', function() { - describe('getEndTimeOfTimeline', function() { - it('returns 0 if no runners are scheduled', function() { - const timeline = new SVG.Timeline(); - endTime = timeline.getEndTimeOfTimeline(); - expect(endTime).toEqual(0); - }) - }) - - describe('finish - issue #964', function() { - beforeEach(function() { - draw.clear() - draw.attr('viewBox', null) - }) - - it('places all elements at the right position - single runner', function() { - const timeline = new SVG.Timeline() - - const rect = draw.rect(20,20) - rect.timeline(timeline) - rect.animate().move(100, 200) - - timeline.finish() - expect(rect.x()).toEqual(100); - expect(rect.y()).toEqual(200); - }) - - it('places all elements at the right position - runner that finishes latest is in first position', function() { - const timeline = new SVG.Timeline() - - const rect1 = draw.rect(10, 10) - rect1.timeline(timeline) - - const rect2 = draw.rect(10, 10) - rect2.timeline(timeline); - - const rect3 = draw.rect(10, 10) - rect3.timeline(timeline); - - rect1.animate(2000, 0, 'now').move(100, 200) - rect2.animate(1000, 0, 'now').move(100, 200) - rect3.animate(1000, 500, 'now').move(100, 200) - - timeline.finish() - - expect(rect1.x()).toEqual(100); - expect(rect1.y()).toEqual(200); - - expect(rect2.x()).toEqual(100); - expect(rect2.y()).toEqual(200); - - expect(rect3.x()).toEqual(100); - expect(rect3.y()).toEqual(200); - }) - - it('places all elements at the right position - runner that finishes latest is in middle position', function() { - const timeline = new SVG.Timeline() - - const rect1 = draw.rect(10, 10) - rect1.timeline(timeline) - - const rect2 = draw.rect(10, 10) - rect2.timeline(timeline); - - const rect3 = draw.rect(10, 10) - rect3.timeline(timeline); - - rect2.animate(1000, 0, 'now').move(100, 200) - rect1.animate(2000, 0, 'now').move(100, 200) - rect3.animate(1000, 500, 'now').move(100, 200) - - timeline.finish() - - expect(rect1.x()).toEqual(100); - expect(rect1.y()).toEqual(200); - - expect(rect2.x()).toEqual(100); - expect(rect2.y()).toEqual(200); - - expect(rect3.x()).toEqual(100); - expect(rect3.y()).toEqual(200); - }) - - it('places all elements at the right position - runner that finishes latest is in last position', function() { - const timeline = new SVG.Timeline() - - const rect1 = draw.rect(10, 10) - rect1.timeline(timeline) - - const rect2 = draw.rect(10, 10) - rect2.timeline(timeline); - - const rect3 = draw.rect(10, 10) - rect3.timeline(timeline); - - rect2.animate(1000, 0, 'now').move(100, 200) - rect3.animate(1000, 500, 'now').move(100, 200) - rect1.animate(2000, 0, 'now').move(100, 200) - - timeline.finish() - - expect(rect1.x()).toEqual(100); - expect(rect1.y()).toEqual(200); - - expect(rect2.x()).toEqual(100); - expect(rect2.y()).toEqual(200); - - expect(rect3.x()).toEqual(100); - expect(rect3.y()).toEqual(200); - }) - }) -}) \ No newline at end of file diff --git a/spec/spec/types/Color.js b/spec/spec/types/Color.js new file mode 100644 index 0000000..ede8863 --- /dev/null +++ b/spec/spec/types/Color.js @@ -0,0 +1,273 @@ +/* globals describe, expect, it, beforeEach */ + +import { Color } from '../../../src/main.js' + +describe('Color.js', () => { + var color + + beforeEach(() => { + color = new Color({ r: 0, g: 102, b: 255 }) + }) + + describe('construct: constructs a color in different formats', () => { + + it('constructs a color from an object in the correct color space', () => { + + // Try in rgb + const color = new Color({ r: 255, g: 0, b: 128 }) + expect(color.r).toBe(255) + expect(color.g).toBe(0) + expect(color.b).toBe(128) + expect(color.space).toBe('rgb') + + // Try in cmyk + const color2 = new Color({ c: 20, y: 15, m: 10, k: 5 }) + expect(color2.c).toBe(20) + expect(color2.m).toBe(10) + expect(color2.y).toBe(15) + expect(color2.k).toBe(5) + expect(color2.space).toBe('cmyk') + }) + + it('constructs a color from an array', () => { + const color = new Color([ 30, 24, 50 ]) + expect(color.r).toBe(30) + expect(color.g).toBe(24) + expect(color.b).toBe(50) + expect(color.space).toBe('rgb') + }) + + it('constructs a color from an array with space in array', () => { + const color = new Color([ 50, 50, 5, 'lab' ]) + expect(color.l).toBe(50) + expect(color.a).toBe(50) + expect(color.b).toBe(5) + expect(color.space).toBe('lab') + }) + + it('constructs a color from an array with space given', () => { + const color = new Color([ 50, 50, 5 ], 'lab') + expect(color.l).toBe(50) + expect(color.a).toBe(50) + expect(color.b).toBe(5) + expect(color.space).toBe('lab') + }) + + it('correclty parses an rgb string', () => { + const color = new Color('rgb(255,0,128)') + expect(color.r).toBe(255) + expect(color.g).toBe(0) + expect(color.b).toBe(128) + }) + + it('correclty parses a 3 digit hex string', () => { + color = new Color('#f06') + expect(color.r).toBe(255) + expect(color.g).toBe(0) + expect(color.b).toBe(102) + }) + + it('correclty parses a 6 digit hex string', () => { + color = new Color('#0066ff') + expect(color.r).toBe(0) + expect(color.g).toBe(102) + expect(color.b).toBe(255) + }) + + }) + + describe('input and output: Importing and exporting colors', () => { + describe('toHex()', () => { + it('returns a hex color', () => { + expect(color.toHex()).toBe('#0066ff') + }) + }) + + describe('toRgb()', () => { + it('returns a rgb string color', () => { + expect(color.toRgb()).toBe('rgb(0,102,255)') + }) + }) + }) + + describe('color spaces: The color spaces supported by our library', () => { + + describe('lab()', () => { + it('can convert rgb to lab', () => { + const color = new Color(255, 0, 128) + const lab = color.lab() + expect(lab.l).toBeCloseTo(54.88, 1) + expect(lab.a).toBeCloseTo(84.55, 1) + expect(lab.b).toBeCloseTo(4.065, 1) + expect(lab.space).toBe('lab') + }) + + it('can convert from lab to rgb', () => { + const lab = new Color(54.88, 84.55, 4.065, 'lab') + const rgb = lab.rgb() + expect(rgb.r).toBeCloseTo(255, 0) + expect(rgb.g).toBeCloseTo(0, 0) + expect(rgb.b).toBeCloseTo(128, 0) + expect(rgb.space).toBe('rgb') + }) + + it('is invertable', () => { + const { r, g, b } = new Color(255, 0, 128).lab().rgb() + expect(r).toBeCloseTo(255, 0) + expect(g).toBeCloseTo(0, 0) + expect(b).toBeCloseTo(128, 0) + }) + + it('handles black', () => { + const color = new Color(0, 0, 0).lab().rgb() + expect(color.r).toBeCloseTo(0, 0) + expect(color.g).toBeCloseTo(0, 0) + expect(color.b).toBeCloseTo(0, 0) + expect(color.toHex()).toBe('#000000') + }) + + it('handles white', () => { + const color = new Color(255, 255, 255).lab().rgb() + expect(color.r).toBeCloseTo(255, 0) + expect(color.g).toBeCloseTo(255, 0) + expect(color.b).toBeCloseTo(255, 0) + expect(color.toHex()).toBe('#ffffff') + }) + }) + + describe('lch()', () => { + it('can convert rgb to lch', () => { + const color = new Color(255, 0, 128) + const lch = color.lch() + expect(lch.l).toBeCloseTo(54.88, 1) + expect(lch.c).toBeCloseTo(84.65, 1) + expect(lch.h).toBeCloseTo(2.75, 1) + expect(lch.space).toBe('lch') + }) + + it('can convert from lch to rgb', () => { + const lch = new Color(54.88, 84.65, 2.75, 'lch') + const rgb = lch.rgb() + expect(rgb.r).toBeCloseTo(255, 0) + expect(rgb.g).toBeCloseTo(0, 0) + expect(rgb.b).toBeCloseTo(128, 0) + expect(rgb.space).toBe('rgb') + }) + + it('is invertable', () => { + const { r, g, b } = new Color(255, 0, 128).lch().rgb() + expect(r).toBeCloseTo(255, 0) + expect(g).toBeCloseTo(0, 0) + expect(b).toBeCloseTo(128, 0) + }) + + it('handles black', () => { + const color = new Color(0, 0, 0).lch().rgb() + expect(color.r).toBeCloseTo(0, 0) + expect(color.g).toBeCloseTo(0, 0) + expect(color.b).toBeCloseTo(0, 0) + expect(color.toHex()).toBe('#000000') + }) + + it('handles white', () => { + const color = new Color(255, 255, 255).lch().rgb() + expect(color.r).toBeCloseTo(255, 0) + expect(color.g).toBeCloseTo(255, 0) + expect(color.b).toBeCloseTo(255, 0) + expect(color.toHex()).toBe('#ffffff') + }) + }) + + describe('hsl()', () => { + + it('can convert from rgb to hsl', () => { + const color = new Color(255, 0, 128) + const hsl = color.hsl() + expect(hsl.h).toBeCloseTo(329.88, 1) + expect(hsl.s).toBeCloseTo(100, 1) + expect(hsl.l).toBeCloseTo(50, 1) + expect(hsl.space).toBe('hsl') + }) + + it('can convert from hsl to rgb', () => { + const hsl = new Color(329.88, 100, 50, 'hsl') + const rgb = hsl.rgb() + expect(rgb.r).toBeCloseTo(255, 0) + expect(rgb.g).toBeCloseTo(0, 0) + expect(rgb.b).toBeCloseTo(128, 0) + expect(rgb.space).toBe('rgb') + }) + + it('is invertable', () => { + const { r, g, b } = new Color(255, 0, 128).hsl().rgb() + expect(r).toBeCloseTo(255, 0) + expect(g).toBeCloseTo(0, 0) + expect(b).toBeCloseTo(128, 0) + }) + + it('handles black', () => { + const color = new Color(0, 0, 0).hsl().rgb() + expect(color.r).toBeCloseTo(0, 0) + expect(color.g).toBeCloseTo(0, 0) + expect(color.b).toBeCloseTo(0, 0) + expect(color.toHex()).toBe('#000000') + }) + + it('handles white', () => { + const color = new Color(255, 255, 255).hsl().rgb() + expect(color.r).toBeCloseTo(255, 0) + expect(color.g).toBeCloseTo(255, 0) + expect(color.b).toBeCloseTo(255, 0) + expect(color.toHex()).toBe('#ffffff') + }) + }) + + describe('cmyk()', () => { + + it('can convert from rgb to cmyk', () => { + const color = new Color(255, 0, 128) + const cmyk = color.cmyk() + expect(cmyk.c).toBeCloseTo(0, 1) + expect(cmyk.m).toBeCloseTo(1, 1) + expect(cmyk.y).toBeCloseTo(0.49, 1) + expect(cmyk.k).toBeCloseTo(0, 1) + expect(cmyk.space).toBe('cmyk') + }) + + it('can convert from cmyk to rgb', () => { + const color = new Color(0, 1, 0.49, 0, 'cmyk') + const rgb = color.rgb() + expect(rgb.r).toBeCloseTo(255, -1) + expect(rgb.g).toBeCloseTo(0, -1) + expect(rgb.b).toBeCloseTo(128, -1) + expect(rgb.space).toBe('rgb') + }) + + it('is invertable', () => { + const { r, g, b } = new Color(255, 0, 128).cmyk().rgb() + expect(r).toBeCloseTo(255, 0) + expect(g).toBeCloseTo(0, 0) + expect(b).toBeCloseTo(128, 0) + }) + + it('handles black', () => { + const color = new Color(0, 0, 0).cmyk().rgb() + expect(color.r).toBeCloseTo(0, 0) + expect(color.g).toBeCloseTo(0, 0) + expect(color.b).toBeCloseTo(0, 0) + expect(color.toHex()).toBe('#000000') + }) + + it('handles white', () => { + const color = new Color(255, 255, 255).cmyk().rgb() + expect(color.r).toBeCloseTo(255, 0) + expect(color.g).toBeCloseTo(255, 0) + expect(color.b).toBeCloseTo(255, 0) + expect(color.toHex()).toBe('#ffffff') + }) + + }) + + }) + +}) diff --git a/spec/spec/types/EventTarget.js b/spec/spec/types/EventTarget.js index cfc7f02..912924a 100644 --- a/spec/spec/types/EventTarget.js +++ b/spec/spec/types/EventTarget.js @@ -1,8 +1,6 @@ /* globals describe, expect, it, spyOn, jasmine */ -import { - EventTarget -} from '../../../src/main.js' +import { EventTarget } from '../../../src/main.js' import { getWindow } from '../../../src/utils/window.js' const { any, objectContaining, createSpy } = jasmine diff --git a/spec/spec/types/Matrix.js b/spec/spec/types/Matrix.js new file mode 100644 index 0000000..33500fa --- /dev/null +++ b/spec/spec/types/Matrix.js @@ -0,0 +1,381 @@ +/* globals describe, expect, it, jasmine */ + +import { Matrix, Rect } from '../../../src/main.js' + +const { objectContaining } = jasmine + +describe('Matrix.js', () => { + const comp = { a: 2, b: 0, c: 0, d: 2, e: 100, f: 50 } + + describe('initialization', () => { + + it('creates a new matrix with default values', () => { + const matrix = new Matrix() + expect(matrix).toEqual(objectContaining( + { a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 } + )) + }) + + it('parses the current transform matrix from an element', () => { + const rect = new Rect().transform(comp) + const matrix = new Matrix(rect) + expect(matrix).toEqual(objectContaining(comp)) + }) + + it('parses a string value correctly', () => { + const matrix = new Matrix('2, 0, 0, 2, 100, 50') + expect(matrix).toEqual(objectContaining(comp)) + }) + + it('parses an array correctly', () => { + const matrix = new Matrix([ 2, 0, 0, 2, 100, 50 ]) + expect(matrix).toEqual(objectContaining(comp)) + }) + + it('parses an object correctly', () => { + const matrix = new Matrix(comp) + expect(matrix).toEqual(objectContaining(comp)) + }) + + it('parses a transform object correctly', () => { + const matrix = new Matrix({ scale: 2, translate: [ 100, 50 ] }) + expect(matrix).toEqual(objectContaining(comp)) + }) + + it('parses 6 arguments correctly', () => { + const matrix = new Matrix(2, 0, 0, 2, 100, 50) + expect(matrix).toEqual(objectContaining(comp)) + }) + }) + + describe('toString()', () => { + it('exports correctly to a string', () => { + expect(new Matrix().toString()).toBe('matrix(1,0,0,1,0,0)') + }) + }) + + describe('compose()', () => { + it('composes a matrix to form the correct result', () => { + const composed = new Matrix().compose({ + scaleX: 3, scaleY: 20, shear: 4, rotate: 50, translateX: 23, translateY: 52 + }) + + const expected = new Matrix().scale(3, 20).shear(4).rotate(50).translate(23, 52) + expect(composed).toEqual(expected) + }) + }) + + describe('decompose()', () => { + it('decomposes a matrix properly', () => { + var matrix = new Matrix().scale(3, 2.5).shear(4).rotate(30).translate(20, 30) + var decomposed = matrix.decompose() + expect(decomposed.scaleX).toBeCloseTo(3) + expect(decomposed.scaleY).toBeCloseTo(2.5) + expect(decomposed.shear).toBeCloseTo(4) + expect(decomposed.rotate).toBeCloseTo(30) + expect(decomposed.translateX).toBeCloseTo(20) + expect(decomposed.translateY).toBeCloseTo(30) + }) + + it('can be recomposed to the same matrix', () => { + var matrix = new Matrix().scale(3, 2.5).shear(4).rotate(30).translate(20, 30) + var decomposed = matrix.decompose() + var composed = new Matrix().compose(decomposed) + expect(matrix.a).toBeCloseTo(composed.a) + expect(matrix.b).toBeCloseTo(composed.b) + expect(matrix.c).toBeCloseTo(composed.c) + expect(matrix.d).toBeCloseTo(composed.d) + expect(matrix.e).toBeCloseTo(composed.e) + expect(matrix.f).toBeCloseTo(composed.f) + }) + }) + + describe('clone()', () => { + it('returns a clone of the matrix', () => { + var matrix = new Matrix(2, 0, 0, 5, 0, 0) + var clone = matrix.clone() + expect(matrix).not.toBe(clone) + for (var i in 'abcdef') { + expect(matrix[i]).toEqual(clone[i]) + } + }) + }) + + describe('multiply()', () => { + it('multiplies two matrices', () => { + var matrix1 = new Matrix(1, 4, 2, 5, 3, 6) + var matrix2 = new Matrix(7, 8, 8, 7, 9, 6) + var matrix3 = matrix1.multiply(matrix2) + + expect(matrix1.toString()).toBe('matrix(1,4,2,5,3,6)') + expect(matrix2.toString()).toBe('matrix(7,8,8,7,9,6)') + expect(matrix3.toString()).toBe('matrix(23,68,22,67,24,72)') + }) + + it('accepts matrices in any form', () => { + var matrix1 = new Matrix(1, 4, 2, 5, 3, 6) + var matrix2 = matrix1.multiply('7,8,8,7,9,6') + + expect(matrix1.toString()).toBe('matrix(1,4,2,5,3,6)') + expect(matrix2.toString()).toBe('matrix(23,68,22,67,24,72)') + }) + }) + + describe('inverse()', () => { + it('inverses matrix', () => { + + var matrix1 = new Matrix(2, 0, 0, 5, 4, 3) + var matrix2 = matrix1.inverse() + var abcdef = [ 0.5, 0, 0, 0.2, -2, -0.6 ] + + for (var i in 'abcdef') { + expect(matrix2['abcdef'[i]]).toBeCloseTo(abcdef[i]) + } + }) + }) + + describe('translate()', () => { + it('translates matrix by given x and y values', () => { + var matrix = new Matrix(1, 0, 0, 1, 4, 3).translate(10, 12.5) + expect(matrix.e).toBe(14) + expect(matrix.f).toBe(15.5) + }) + + it('does nothing if you give it no x or y value', () => { + var matrix = new Matrix(1, 2, 3, 4, 5, 6).translate() + expect(matrix.e).toBe(5) + expect(matrix.f).toBe(6) + }) + }) + + describe('scale()', () => { + it('performs a uniformal scale with one value', () => { + var matrix = new Matrix(1, 0, 0, 1, 4, 3).scale(3) + + expect(matrix.a).toBe(3) + expect(matrix.d).toBe(3) + expect(matrix.e).toBe(4 * 3) + expect(matrix.f).toBe(3 * 3) + }) + it('performs a non-uniformal scale with two values', () => { + var matrix = new Matrix(1, 0, 0, 1, 4, 3).scale(2.5, 3.5) + + expect(matrix.a).toBe(2.5) + expect(matrix.d).toBe(3.5) + expect(matrix.e).toBe(4 * 2.5) + expect(matrix.f).toBe(3 * 3.5) + }) + it('performs a uniformal scale at a given center point with three values', () => { + var matrix = new Matrix(1, 3, 2, 3, 4, 3).scale(3, 2, 3) + + expect(matrix.a).toBe(3) + expect(matrix.b).toBe(9) + expect(matrix.c).toBe(6) + expect(matrix.d).toBe(9) + expect(matrix.e).toBe(8) + expect(matrix.f).toBe(3) + }) + it('performs a non-uniformal scale at a given center point with four values', () => { + var matrix = new Matrix(1, 3, 2, 3, 4, 3).scale(3, 2, 2, 3) + + expect(matrix.a).toBe(3) + expect(matrix.b).toBe(6) + expect(matrix.c).toBe(6) + expect(matrix.d).toBe(6) + expect(matrix.e).toBe(8) + expect(matrix.f).toBe(3) + }) + }) + + describe('rotate()', () => { + it('performs a rotation with one argument', () => { + var matrix = new Matrix(1, 3, 2, 3, 4, 3).rotate(30) + + expect(matrix.a).toBeCloseTo(-0.6339746) + expect(matrix.b).toBeCloseTo(3.09807621) + expect(matrix.c).toBeCloseTo(0.23205081) + expect(matrix.d).toBeCloseTo(3.59807621) + expect(matrix.e).toBeCloseTo(1.96410162) + expect(matrix.f).toBeCloseTo(4.59807621) + }) + it('performs a rotation around a given point with three arguments', () => { + var matrix = new Matrix(1, 3, 2, 3, 4, 3).rotate(30, 2, 3) + + expect(matrix.a).toBeCloseTo(-0.633974596216) + expect(matrix.b).toBeCloseTo(3.09807621135) + expect(matrix.c).toBeCloseTo(0.232050807569) + expect(matrix.d).toBeCloseTo(3.59807621135) + expect(matrix.e).toBeCloseTo(3.73205080757) + expect(matrix.f).toBeCloseTo(4.0) + }) + }) + + describe('flip()', () => { + describe('with x given', () => { + it('performs a flip over the horizontal axis with one argument', () => { + var matrix = new Matrix(1, 0, 0, 1, 4, 3).flip('x') + + expect(matrix.a).toBe(-1) + expect(matrix.d).toBe(1) + expect(matrix.e).toBe(-4) + expect(matrix.f).toBe(3) + }) + it('performs a flip over the horizontal axis over a given point with two arguments', () => { + var matrix = new Matrix(1, 0, 0, 1, 4, 3).flip('x', 150) + + expect(matrix.a).toBe(-1) + expect(matrix.d).toBe(1) + expect(matrix.e).toBe(296) + expect(matrix.f).toBe(3) + }) + }) + describe('with y given', () => { + it('performs a flip over the vertical axis with one argument', () => { + var matrix = new Matrix(1, 0, 0, 1, 4, 3).flip('y') + + expect(matrix.a).toBe(1) + expect(matrix.d).toBe(-1) + expect(matrix.e).toBe(4) + expect(matrix.f).toBe(-3) + }) + it('performs a flip over the vertical axis over a given point with two arguments', () => { + var matrix = new Matrix(1, 0, 0, 1, 4, 3).flip('y', 100) + + expect(matrix.a).toBe(1) + expect(matrix.d).toBe(-1) + expect(matrix.e).toBe(4) + expect(matrix.f).toBe(197) + }) + }) + describe('with no axis given', () => { + it('performs a flip over the horizontal and vertical axis with no argument', () => { + var matrix = new Matrix(1, 0, 0, 1, 4, 3).flip() + + expect(matrix.a).toBe(-1) + expect(matrix.d).toBe(-1) + expect(matrix.e).toBe(-4) + expect(matrix.f).toBe(-3) + }) + it('performs a flip over the horizontal and vertical axis over a given point with one argument that represent both coordinates', () => { + var matrix = new Matrix(1, 0, 0, 1, 4, 3).flip(100) + + expect(matrix.a).toBe(-1) + expect(matrix.d).toBe(-1) + expect(matrix.e).toBe(196) + expect(matrix.f).toBe(197) + }) + it('performs a flip over the horizontal and vertical axis over a given point with two arguments', () => { + var matrix = new Matrix(1, 0, 0, 1, 4, 3).flip(50, 100) + + expect(matrix.a).toBe(-1) + expect(matrix.d).toBe(-1) + expect(matrix.e).toBe(96) + expect(matrix.f).toBe(197) + }) + }) + }) + + describe('skew()', () => { + it('performs a uniformal skew with one value', () => { + var matrix = new Matrix(1, 0, 0, 1, 4, 3).skew(30) + + expect(matrix.a).toBe(1) + expect(matrix.b).toBeCloseTo(0.57735026919) + expect(matrix.c).toBeCloseTo(0.57735026919) + expect(matrix.d).toBe(1) + expect(matrix.e).toBeCloseTo(5.73205080757) + expect(matrix.f).toBeCloseTo(5.30940107676) + }) + + it('performs a non-uniformal skew with two values', () => { + var matrix = new Matrix(1, 0, 0, 1, 4, 3).skew(30, 20) + + expect(matrix.a).toBe(1) + expect(matrix.b).toBeCloseTo(0.363970234266) + expect(matrix.c).toBeCloseTo(0.57735026919) + expect(matrix.d).toBe(1) + expect(matrix.e).toBeCloseTo(5.73205080757) + expect(matrix.f).toBeCloseTo(4.45588093706) + }) + + it('performs a uniformal skew at a given center point with three values', () => { + var matrix = new Matrix(1, 0, 0, 1, 4, 3).skew(30, 150, 100) + + expect(matrix.a).toBe(1) + expect(matrix.b).toBeCloseTo(0.57735026919) + expect(matrix.c).toBeCloseTo(0.57735026919) + expect(matrix.d).toBe(1) + expect(matrix.e).toBeCloseTo(-52.0029761114) + expect(matrix.f).toBeCloseTo(-81.2931393017) + }) + + it('performs a non-uniformal skew at a given center point with four values', () => { + var matrix = new Matrix(1, 0, 0, 1, 4, 3).skew(30, 20, 150, 100) + + expect(matrix.a).toBe(1.0) + expect(matrix.b).toBeCloseTo(0.363970234266) + expect(matrix.c).toBeCloseTo(0.57735026919) + expect(matrix.d).toBe(1.0) + expect(matrix.e).toBeCloseTo(-52.0029761114) + expect(matrix.f).toBeCloseTo(-50.1396542029) + }) + + it('can be chained', () => { + var matrix = new Matrix(1, 0, 0, 1, 4, 3).skew(20, 30).skew(30, 20) + expect(matrix.a).toBeCloseTo(1.33333333333) + expect(matrix.b).toBeCloseTo(0.941320503456) + expect(matrix.c).toBeCloseTo(0.941320503456) + expect(matrix.d).toBeCloseTo(1.13247433143) + expect(matrix.e).toBeCloseTo(8.1572948437) + expect(matrix.f).toBeCloseTo(7.16270500812) + }) + }) + + describe('skewX', () => { + it('performs a skew along the x axis with one value', () => { + var matrix = new Matrix(1, 0, 0, 1, 4, 3).skewX(30) + + expect(matrix.a).toBe(1) + expect(matrix.b).toBe(0) + expect(matrix.c).toBeCloseTo(0.57735026919) + expect(matrix.d).toBe(1) + expect(matrix.e).toBeCloseTo(5.73205080757) + expect(matrix.f).toBe(3) + }) + + it('performs a skew along the x axis at a given center point with three values', () => { + var matrix = new Matrix(1, 0, 0, 1, 4, 3).skewX(30, 150, 100) + + expect(matrix.a).toBe(1) + expect(matrix.b).toBe(0) + expect(matrix.c).toBeCloseTo(0.57735026919) + expect(matrix.d).toBe(1) + expect(matrix.e).toBeCloseTo(-52.0029761114) + expect(matrix.f).toBe(3) + }) + }) + + describe('skewY', () => { + it('performs a skew along the y axis with one value', () => { + var matrix = new Matrix(1, 0, 0, 1, 4, 3).skewY(30) + + expect(matrix.a).toBe(1) + expect(matrix.b).toBeCloseTo(0.57735026919) + expect(matrix.c).toBe(0) + expect(matrix.d).toBe(1) + expect(matrix.e).toBe(4) + expect(matrix.f).toBeCloseTo(5.30940107676) + }) + + it('performs a skew along the y axis at a given center point with three values', () => { + var matrix = new Matrix(1, 0, 0, 1, 4, 3).skewY(30, 150, 100) + + expect(matrix.a).toBe(1) + expect(matrix.b).toBeCloseTo(0.57735026919) + expect(matrix.c).toBe(0) + expect(matrix.d).toBe(1) + expect(matrix.e).toBe(4) + expect(matrix.f).toBeCloseTo(-81.2931393017) + }) + }) +}) diff --git a/spec/spec/types/Number.js b/spec/spec/types/Number.js new file mode 100644 index 0000000..d7ef2fd --- /dev/null +++ b/spec/spec/types/Number.js @@ -0,0 +1,184 @@ +/* globals describe, expect, it, beforeEach, jasmine */ + +import { Number as SVGNumber } from '../../../src/main.js' + +const { any } = jasmine + +describe('Number.js', () => { + var number + + beforeEach(() => { + number = new SVGNumber() + }) + + describe('new', () => { + it('is zero', () => { + expect(number.value).toBe(0) + }) + it('has a blank unit', () => { + expect(number.unit).toBe('') + }) + it('accepts the unit as a second argument', () => { + number = new SVGNumber(30, '%') + expect(number.value).toBe(30) + expect(number.unit).toBe('%') + }) + it('parses a pixel value', () => { + number = new SVGNumber('20px') + expect(number.value).toBe(20) + expect(number.unit).toBe('px') + }) + it('parses a percent value', () => { + number = new SVGNumber('99%') + expect(number.value).toBe(0.99) + expect(number.unit).toBe('%') + }) + it('parses a seconds value', () => { + number = new SVGNumber('2s') + expect(number.value).toBe(2000) + expect(number.unit).toBe('s') + }) + it('parses a negative percent value', () => { + number = new SVGNumber('-89%') + expect(number.value).toBe(-0.89) + expect(number.unit).toBe('%') + }) + it('falls back to 0 if given value is NaN', () => { + number = new SVGNumber(NaN) + expect(number.value).toBe(0) + }) + it('falls back to maximum value if given number is positive infinite', () => { + number = new SVGNumber(1.7976931348623157E+10308) + expect(number.value).toBe(3.4e+38) + }) + it('falls back to minimum value if given number is negative infinite', () => { + number = new SVGNumber(-1.7976931348623157E+10308) + expect(number.value).toBe(-3.4e+38) + }) + }) + + describe('toString()', () => { + it('converts the number to a string', () => { + expect(number.toString()).toBe('0') + }) + it('appends the unit', () => { + number.value = 1.21 + number.unit = 'px' + expect(number.toString()).toBe('1.21px') + }) + it('converts percent values properly', () => { + number.value = 1.36 + number.unit = '%' + expect(number.toString()).toBe('136%') + }) + it('converts second values properly', () => { + number.value = 2500 + number.unit = 's' + expect(number.toString()).toBe('2.5s') + }) + }) + + describe('valueOf()', () => { + it('returns a numeric value for default units', () => { + expect(typeof number.valueOf()).toBe('number') + number = new SVGNumber('12') + expect(typeof number.valueOf()).toBe('number') + number = new SVGNumber(13) + expect(typeof number.valueOf()).toBe('number') + }) + it('returns a numeric value for pixel units', () => { + number = new SVGNumber('10px') + expect(typeof number.valueOf()).toBe('number') + }) + it('returns a numeric value for percent units', () => { + number = new SVGNumber('20%') + expect(typeof number.valueOf()).toBe('number') + }) + it('converts to a primitive when multiplying', () => { + number.value = 80 + expect(number * 4).toBe(320) + }) + }) + + describe('plus()', () => { + it('returns a new instance', () => { + expect(number.plus(4.5)).not.toBe(number) + expect(number.plus(4.5)).toEqual(any(SVGNumber)) + }) + it('adds a given number', () => { + expect(number.plus(3.5).valueOf()).toBe(3.5) + }) + it('adds a given percentage value', () => { + expect(number.plus('225%').valueOf()).toBe(2.25) + }) + it('adds a given pixel value', () => { + expect(number.plus('83px').valueOf()).toBe(83) + }) + it('use the unit of this number as the unit of the returned number by default', () => { + expect(new SVGNumber('12s').plus('3%').unit).toBe('s') + }) + it('use the unit of the passed number as the unit of the returned number when this number as no unit', () => { + expect(number.plus('15%').unit).toBe('%') + }) + }) + + describe('minus()', () => { + it('subtracts a given number', () => { + expect(number.minus(3.7).valueOf()).toBe(-3.7) + }) + it('subtracts a given percentage value', () => { + expect(number.minus('223%').valueOf()).toBe(-2.23) + }) + it('subtracts a given pixel value', () => { + expect(number.minus('85px').valueOf()).toBe(-85) + }) + it('use the unit of this number as the unit of the returned number by default', () => { + expect(new SVGNumber('12s').minus('3%').unit).toBe('s') + }) + it('use the unit of the passed number as the unit of the returned number when this number as no unit', () => { + expect(number.minus('15%').unit).toBe('%') + }) + }) + + describe('times()', () => { + beforeEach(() => { + number = number.plus(4) + }) + it('multiplies with a given number', () => { + expect(number.times(3).valueOf()).toBe(12) + }) + it('multiplies with a given percentage value', () => { + expect(number.times('110%').valueOf()).toBe(4.4) + }) + it('multiplies with a given pixel value', () => { + expect(number.times('85px').valueOf()).toBe(340) + }) + it('use the unit of this number as the unit of the returned number by default', () => { + expect(new SVGNumber('12s').times('3%').unit).toBe('s') + }) + it('use the unit of the passed number as the unit of the returned number when this number as no unit', () => { + expect(number.times('15%').unit).toBe('%') + }) + }) + + describe('divide()', () => { + beforeEach(() => { + number = number.plus(90) + }) + it('divides by a given number', () => { + expect(number.divide(3).valueOf()).toBe(30) + }) + it('divides by a given percentage value', () => { + expect(number.divide('3000%').valueOf()).toBe(3) + }) + it('divides by a given pixel value', () => { + expect(number.divide('45px').valueOf()).toBe(2) + }) + it('use the unit of this number as the unit of the returned number by default', () => { + expect(new SVGNumber('12s').divide('3%').unit).toBe('s') + }) + it('use the unit of the passed number as the unit of the returned number when this number as no unit', () => { + expect(number.divide('15%').unit).toBe('%') + }) + }) +}) diff --git a/spec/spec/types/Point.js b/spec/spec/types/Point.js new file mode 100644 index 0000000..ee0bbd4 --- /dev/null +++ b/spec/spec/types/Point.js @@ -0,0 +1,78 @@ +/* globals describe, expect, it, beforeEach */ + +import { Point } from '../../../src/main.js' + +describe('Point.js', () => { + var point + + describe('initialization', () => { + + describe('without a source', () => { + + beforeEach(() => { + point = new Point() + }) + + it('creates a new point with default values', () => { + expect(point.x).toBe(0) + expect(point.y).toBe(0) + }) + + }) + + describe('with x and y given', () => { + it('creates a point with given values', () => { + var point = new Point(2, 4) + + expect(point.x).toBe(2) + expect(point.y).toBe(4) + }) + }) + + describe('with only x given', () => { + it('sets the y value to 0', () => { + var point = new Point(7) + + expect(point.x).toBe(7) + expect(point.y).toBe(0) + }) + }) + + describe('with array given', () => { + it('creates a point from array', () => { + var point = new Point([ 2, 4 ]) + + expect(point.x).toBe(2) + expect(point.y).toBe(4) + }) + }) + + describe('with object given', () => { + it('creates a point from object', () => { + var point = new Point({ x: 2, y: 4 }) + + expect(point.x).toBe(2) + expect(point.y).toBe(4) + }) + }) + + describe('with Point given', () => { + it('creates a point from Point', () => { + var point = new Point(new Point(2, 4)) + + expect(point.x).toBe(2) + expect(point.y).toBe(4) + }) + }) + }) + + describe('clone()', () => { + it('returns cloned point', () => { + var point1 = new Point(1, 1) + var point2 = point1.clone() + + expect(point1).toEqual(point2) + expect(point1).not.toBe(point2) + }) + }) +}) diff --git a/spec/spec/types/PointArray.js b/spec/spec/types/PointArray.js new file mode 100644 index 0000000..da8675e --- /dev/null +++ b/spec/spec/types/PointArray.js @@ -0,0 +1,32 @@ +/* globals describe, expect, it */ + +import { PointArray, Matrix, Point } from '../../../src/main.js' + +describe('PointArray', () => { + const squareString = '0,0 1,0 1,1 0,1' + const square = new PointArray(squareString) + + describe('toString()', () => { + it('round trips with string', () => { + expect(square.toString()).toEqual(squareString) + }) + }) + + describe('transform()', () => { + it('translates correctly', () => { + const translation = new Matrix().translate(2, 1) + const newSquare = square.transform(translation) + expect(newSquare.toString()).toEqual('2,1 3,1 3,2 2,2') + }) + + it('transforms like Point', () => { + const matrix = new Matrix(1, 2, 3, 4, 5, 6) + const newSquare = square.transform(matrix) + for (let i = 0; i < square.length; i++) { + const squarePoint = new Point(square[i]) + const newSquarePoint = new Point(newSquare[i]) + expect(squarePoint.transform(matrix)).toEqual(newSquarePoint) + } + }) + }) +}) -- cgit v1.2.3