summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAndrej Mihajlov <and@codeispoetry.ru>2017-03-17 23:01:16 +0000
committerAndrej Mihajlov <and@codeispoetry.ru>2017-03-17 23:01:16 +0000
commit3f1345fe42b44d1d9d0ee7ef9888020889d89b3e (patch)
treec530aa9a560ec3d7cf6cba3b09f3a85553295101
parentaae71ffac0d0656e550f3653a4393a18bb7cd4d1 (diff)
downloadmullvadvpn-3f1345fe42b44d1d9d0ee7ef9888020889d89b3e.tar.xz
mullvadvpn-3f1345fe42b44d1d9d0ee7ef9888020889d89b3e.zip
New keyframe animation
-rw-r--r--app/lib/keyframe-animation.js (renamed from app/lib/tray-animation.js)128
-rw-r--r--app/lib/tray-animator.js113
-rw-r--r--app/lib/tray-icon-manager.js85
-rw-r--r--app/lib/tray-icon-provider.js47
-rw-r--r--app/main.js3
-rw-r--r--test/keyframe-animation.spec.js227
-rw-r--r--test/tray-animator.spec.js125
7 files changed, 338 insertions, 390 deletions
diff --git a/app/lib/tray-animation.js b/app/lib/keyframe-animation.js
index f415ecbe75..b8f4a206d4 100644
--- a/app/lib/tray-animation.js
+++ b/app/lib/keyframe-animation.js
@@ -2,18 +2,18 @@ import assert from 'assert';
import { nativeImage } from 'electron';
/**
- * Tray animation descriptor
+ * Keyframe animation
*
* @export
- * @class TrayAnimation
+ * @class KeyframeAnimation
*/
-export default class TrayAnimation {
+export default class KeyframeAnimation {
/**
* Set callback called on each frame update
*
* @type {function}
- * @memberOf TrayAnimation
+ * @memberOf KeyframeAnimation
*/
set onFrame(v) { this._onFrame = v; }
@@ -22,7 +22,7 @@ export default class TrayAnimation {
*
* @readonly
* @type {function}
- * @memberOf TrayAnimation
+ * @memberOf KeyframeAnimation
*/
get onFrame() { this._onFrame; }
@@ -30,7 +30,7 @@ export default class TrayAnimation {
* Set callback called when animation finished
*
* @type {function}
- * @memberOf TrayAnimation
+ * @memberOf KeyframeAnimation
*/
set onFinish(v) { this._onFinish = v; }
@@ -39,7 +39,7 @@ export default class TrayAnimation {
*
* @readonly
*
- * @memberOf TrayAnimation
+ * @memberOf KeyframeAnimation
*/
get onFinish() { this._onFinish; }
@@ -47,7 +47,7 @@ export default class TrayAnimation {
* Set animation pace per frame in ms
*
* @type {number}
- * @memberOf TrayAnimation
+ * @memberOf KeyframeAnimation
*/
set speed(v) { this._speed = parseInt(v); }
@@ -56,7 +56,7 @@ export default class TrayAnimation {
*
* @readonly
* @type {number}
- * @memberOf TrayAnimation
+ * @memberOf KeyframeAnimation
*/
get speed() { return this._speed; }
@@ -64,7 +64,7 @@ export default class TrayAnimation {
* Set animation repetition
* @type {bool}
*
- * @memberOf TrayAnimation
+ * @memberOf KeyframeAnimation
*/
set repeat(v) { this._repeat = !!v; }
@@ -73,14 +73,14 @@ export default class TrayAnimation {
*
* @readonly
* @type {bool}
- * @memberOf TrayAnimation
+ * @memberOf KeyframeAnimation
*/
get repeat() { return this._repeat; }
/**
* Set animation reversal
* @type {bool}
- * @memberOf TrayAnimation
+ * @memberOf KeyframeAnimation
*/
set reverse(v) { this._reverse = !!v; }
@@ -89,14 +89,14 @@ export default class TrayAnimation {
*
* @readonly
* @type {bool}
- * @memberOf TrayAnimation
+ * @memberOf KeyframeAnimation
*/
get reverse() { return this._repeat; }
/**
* Set animation alternation
* @type {bool}
- * @memberOf TrayAnimation
+ * @memberOf KeyframeAnimation
*/
set alternate(v) { this._alternate = !!v; }
@@ -105,7 +105,7 @@ export default class TrayAnimation {
*
* @readonly
* @type {bool}
- * @memberOf TrayAnimation
+ * @memberOf KeyframeAnimation
*/
get alternate() { return this._alternate; }
@@ -114,7 +114,7 @@ export default class TrayAnimation {
*
* @readonly
* @type {array}
- * @memberOf TrayAnimation
+ * @memberOf KeyframeAnimation
*/
get source() { return this._source.slice(); }
@@ -123,7 +123,7 @@ export default class TrayAnimation {
*
* @readonly
* @type {Electron.NativeImage[]}
- * @memberOf TrayAnimation
+ * @memberOf KeyframeAnimation
*/
get nativeImages() { return this._nativeImages.slice(); }
@@ -132,7 +132,7 @@ export default class TrayAnimation {
*
* @readonly
* @type {bool}
- * @memberOf TrayAnimation
+ * @memberOf KeyframeAnimation
*/
get isFinished() { return this._isFinished; }
@@ -143,8 +143,8 @@ export default class TrayAnimation {
* @param {string} filePattern - file name pattern where {s} is replaced with index
* @param {number[]} range - sequence range [start, end]
*
- * @memberOf TrayAnimation
- * @return {TrayAnimation}
+ * @memberOf KeyframeAnimation
+ * @return {KeyframeAnimation}
*/
static fromFileSequence(filePattern, range) {
assert(range.length === 2 && range[0] < range[1]);
@@ -154,14 +154,14 @@ export default class TrayAnimation {
images.push(filePattern.replace('{s}', i));
}
- return new TrayAnimation(images);
+ return new KeyframeAnimation(images);
}
/**
- * Creates an instance of TrayAnimation.
+ * Creates an instance of KeyframeAnimation.
* @param {string[]} images
*
- * @memberOf TrayAnimation
+ * @memberOf KeyframeAnimation
*/
constructor(images) {
assert(images.length > 0);
@@ -194,7 +194,7 @@ export default class TrayAnimation {
*
* @readonly
* @type {Electron.NativeImage}
- * @memberOf TrayAnimation
+ * @memberOf KeyframeAnimation
*/
get currentImage() {
return this._nativeImages[this._currentFrame];
@@ -202,19 +202,27 @@ export default class TrayAnimation {
/**
* Prepare initial state for animation before running it.
- * @param {object} [options = {}] - animation options
- * @param {number} [options.startFrame] - start frame
- * @param {number} [options.endFrame] - end frame
+ * @param {object} [options = {}] - animation options
+ * @param {number} [options.startFrame] - start frame
+ * @param {number} [options.endFrame] - end frame
* @param {bool} [options.beginFromCurrentState] - continue animation from current state
- * @memberOf TrayAnimation
+ * @param {string} [options.advanceTo] - resets current frame. (possible values: end)
+ * @memberOf KeyframeAnimation
*/
play(options = {}) {
- let {startFrame, endFrame, beginFromCurrentState} = options;
+ let { startFrame, endFrame, beginFromCurrentState, advanceTo } = options;
- if(startFrame === undefined && endFrame === undefined) {
- this._frameRange = [ 0, this._numFrames - 1 ];
+ if(startFrame !== undefined && endFrame !== undefined) {
+ assert(startFrame >= 0 && startFrame < this._numFrames);
+ assert(endFrame >= 0 && endFrame < this._numFrames);
+
+ if(startFrame < endFrame) {
+ this._frameRange = [ startFrame, endFrame ];
+ } else {
+ this._frameRange = [ endFrame, startFrame ];
+ }
} else {
- throw 'not implemented';
+ this._frameRange = [ 0, this._numFrames - 1 ];
}
if(!beginFromCurrentState || this._isFirstRun) {
@@ -225,14 +233,22 @@ export default class TrayAnimation {
this._isFirstRun = false;
}
+ if(advanceTo === 'end') {
+ this._currentFrame = this._frameRange[this._reverse ? 0 : 1];
+ }
+
this._isFinished = false;
- this._render();
-
this._unscheduleUpdate();
+
+ this._render();
this._scheduleUpdate();
}
+ /**
+ * Stop animation
+ * @memberOf KeyframeAnimation
+ */
stop() {
this._unscheduleUpdate();
}
@@ -271,18 +287,14 @@ export default class TrayAnimation {
/**
* Advance animation frame
- * @memberOf TrayAnimation
+ * @memberOf KeyframeAnimation
*/
_advanceFrame() {
// do not advance frame when animation is finished
if(this._isFinished) { return; }
// advance frame
- let nextFrame = this._nextFrame(this._currentFrame, this._reverse);
-
- // let animation pick up from current state
- let didReachEnd = (nextFrame < this._frameRange[0] && this._reverse) || // out of bounds but moving into
- (nextFrame > this._frameRange[1] && !this._reverse); // out of bounds but moving into
+ let didReachEnd = this._currentFrame === this._frameRange[this._reverse ? 0 : 1];
// did reach end?
if(didReachEnd) {
@@ -297,31 +309,41 @@ export default class TrayAnimation {
// change animation direction if marked for alternation
if(this._alternate) {
this._reverse = !this._reverse;
-
- // clamp range
- nextFrame = Math.min(Math.max(this._frameRange[0], nextFrame), this._frameRange[1]);
- // skip corner frame when alternating by advancing frame once again
- nextFrame = this._nextFrame(nextFrame, this._reverse);
+ this._currentFrame = this._nextFrame(this._currentFrame, this._frameRange, this._reverse);
} else {
- nextFrame = this._frameRange[this._reverse ? 1 : 0];
+ this._currentFrame = this._frameRange[this._reverse ? 1 : 0];
}
+ } else {
+ this._currentFrame = this._nextFrame(this._currentFrame, this._frameRange, this._reverse);
}
-
- this._currentFrame = nextFrame;
}
/**
* Calculate next frame
* @private
- * @param {number} cur - current frame
- * @param {bool} isReverse - reverse sequence direction?
+ * @param {number} cur - current frame
+ * @param {number[]} frameRange - frame range
+ * @param {bool} isReverse - reverse sequence direction?
* @returns {number}
*
- * @memberOf TrayAnimation
+ * @memberOf KeyframeAnimation
*/
- _nextFrame(cur, isReverse) {
- return cur + (isReverse ? -1 : 1);
+ _nextFrame(cur, frameRange, isReverse) {
+ if(isReverse) {
+ if(cur < frameRange[0]) {
+ return cur + 1;
+ } else if(cur > frameRange[0]) {
+ return cur - 1;
+ }
+ } else {
+ if(cur > frameRange[1]) {
+ return cur - 1;
+ } else if(cur < frameRange[1]) {
+ return cur + 1;
+ }
+ }
+ return cur;
}
} \ No newline at end of file
diff --git a/app/lib/tray-animator.js b/app/lib/tray-animator.js
deleted file mode 100644
index 9fb03b5588..0000000000
--- a/app/lib/tray-animator.js
+++ /dev/null
@@ -1,113 +0,0 @@
-import assert from 'assert';
-
-/**
- * Tray icon animator
- * @class TrayAnimator
- */
-export default class TrayAnimator {
-
- /**
- * Whether animator has started.
- * @readonly
- * @memberOf TrayAnimator
- */
- get isStarted() { return this._started; }
-
- /**
- * Creates an instance of TrayAnimator.
- * @param {Electron.Tray} tray - an instance of Tray
- * @param {TrayAnimation} animation - an instance of TrayAnimation
- *
- * @memberOf TrayAnimator
- */
- constructor(tray, animation) {
- assert(tray);
- assert(animation);
-
- this._tray = tray;
- this._animation = animation;
- this._started = false;
- this._timer = null;
- }
-
- /**
- * Advance animation to the start *
- * @memberOf TrayAnimator
- */
- advanceToStart() {
- this._animation.advanceToStart();
- this._updateTrayIcon();
- }
-
- /**
- * Advance animation to the end
- * @memberOf TrayAnimator
- */
- advanceToEnd() {
- this._animation.advanceToEnd();
- this._updateTrayIcon();
- }
-
- /**
- * Start animating
- * @memberOf TrayAnimator
- */
- start() {
- if(this._started) { return; }
-
- this._timer = this._nextFrame();
- this._started = true;
-
- // prepare animation
- this._animation.prepare();
-
- // update from initial frame
- this._updateTrayIcon();
- }
-
- /**
- * Stop animating
- * @memberOf TrayAnimator
- */
- stop() {
- if(!this._started) { return; }
-
- this._started = false;
-
- clearTimeout(this._timer);
- this._timer = null;
- }
-
- /**
- * Schedules next animation frame
- * @returns {number} timer ID
- * @memberOf TrayAnimator
- */
- _nextFrame() {
- return setTimeout(::this._updateAnimationFrame, this._animation.speed);
- }
-
- /**
- * Updates animation frame
- * @memberOf TrayAnimator
- */
- _updateAnimationFrame() {
- if(!this._started) { return; }
-
- this._animation.advanceFrame();
- this._updateTrayIcon();
-
- if(!this._animation.isFinished) {
- this._nextFrame();
- }
- }
-
- /**
- * Update tray icon with current frame
- * @memberOf TrayAnimator
- */
- _updateTrayIcon() {
- this._tray.setImage(this._animation.currentImage);
- }
-
-}
diff --git a/app/lib/tray-icon-manager.js b/app/lib/tray-icon-manager.js
index 987698a23d..82c226d3f6 100644
--- a/app/lib/tray-icon-manager.js
+++ b/app/lib/tray-icon-manager.js
@@ -1,7 +1,7 @@
import assert from 'assert';
-import TrayAnimator from './tray-animator';
-import TrayIconProvider from './tray-icon-provider';
+import path from 'path';
import { TrayIconType } from '../enums';
+import KeyframeAnimation from './keyframe-animation';
/**
* Tray icon manager
@@ -14,17 +14,19 @@ export default class TrayIconManager {
/**
* Creates an instance of TrayIconManager.
* @param {Electron.Tray} tray
- * @param {TrayIconProvider} iconProvider
*
* @memberOf TrayIconManager
*/
- constructor(tray, iconProvider) {
+ constructor(tray) {
assert(tray);
- assert(iconProvider);
- this._tray = tray;
- this._iconProvider = iconProvider;
- this._animator = null;
+ const basePath = path.join(path.resolve(__dirname, '..'), 'assets/images/menubar icons');
+ let filePath = path.join(basePath, 'lock-{s}.png');
+ let animation = KeyframeAnimation.fromFileSequence(filePath, [1, 9]);
+ animation.onFrame = (img) => tray.setImage(img);
+ animation.speed = 100;
+
+ this._animation = animation;
this._iconType = null;
}
@@ -33,9 +35,9 @@ export default class TrayIconManager {
* @memberOf TrayIconManager
*/
destroy() {
- if(this._animator) {
- this._animator.stop();
- this._animator = null;
+ if(this._animation) {
+ this._animation.stop();
+ this._animation = null;
}
this._iconType = null;
}
@@ -67,64 +69,47 @@ export default class TrayIconManager {
*
* @memberOf TrayIconManager
*/
- _updateIconType(type) {
+ _updateIconType(type) {
// no-op if same animator requested
if(this._iconType === type) { return; }
- // skip animation if:
- // 1. there was no icon set before (which is usually when app starts)
- // 2. unsecured -> securing
- // 3. securing -> unsecured
- const skip = this._iconType === null ||
- type === TrayIconType.securing || // unsecured -> securing
- (type === TrayIconType.unsecured && this._iconType === TrayIconType.securing); // securing -> unsecured
-
- // do not animate if setting icon for the first time
- this._updateType(type, skip);
- }
-
- /**
- * Get animation for iconType
- *
- * @param {TrayIconType} type
- * @returns TrayIconAnimator
- *
- * @memberOf TrayIconManager
- */
- _animationForType(type) {
- switch(type) {
- case TrayIconType.secured: return this._iconProvider.lockAnimation();
- case TrayIconType.unsecured: return this._iconProvider.unlockAnimation();
- case TrayIconType.securing: return this._iconProvider.unlockAnimation();
- }
+ this._updateType(type);
}
/**
* Update icon animator with new type
*
* @param {TrayIconType} type
- * @param {boolean} [skipAnimation=false] whether animation should be skipped
*
* @memberOf TrayIconManager
*/
- _updateType(type, skipAnimation = false) {
+ _updateType(type) {
assert(TrayIconType.isValid(type));
- let animator = new TrayAnimator(this._tray, this._animationForType(type));
+ let options = { beginFromCurrentState: true };
- // destroy existing animator
- if(this._animator) {
- this._animator.stop();
- this._animator = null;
+ switch(type) {
+ case TrayIconType.secured:
+ this._animation.reverse = false;
+ break;
+ case TrayIconType.securing:
+ case TrayIconType.unsecured:
+ this._animation.reverse = true;
+ break;
}
- if(skipAnimation) {
- animator.advanceToEnd();
- } else {
- animator.start();
+ if(this._iconType === null) {
+ options.advanceTo = 'end';
}
+
+ this._animation.play(options);
+
+ // if(skipAnimation) {
+ // animator.advanceToEnd();
+ // } else {
+ // animator.start();
+ // }
- this._animator = animator;
this._iconType = type;
}
diff --git a/app/lib/tray-icon-provider.js b/app/lib/tray-icon-provider.js
deleted file mode 100644
index aa50a71a71..0000000000
--- a/app/lib/tray-icon-provider.js
+++ /dev/null
@@ -1,47 +0,0 @@
-import path from 'path';
-import { EventEmitter } from 'events';
-import TrayAnimation from './tray-animation';
-import Enum from './enum';
-
-const menubarIcons = {
- base: path.join(path.resolve(__dirname, '..'), 'assets/images/menubar icons'),
- lock: 'lock-{s}.png'
-};
-
-/**
- * Tray icon provider
- *
- * @export
- * @class TrayIconProvider
- */
-export default class TrayIconProvider {
-
- /**
- * Get lock animation
- *
- * @param {boolean} [isReverse=false] whether animation should be reversed
- * @returns TrayIconAnimator
- *
- * @memberOf TrayIconProvider
- */
- lockAnimation(isReverse = false) {
- let filePath = path.join(menubarIcons.base, menubarIcons.lock);
- let animation = TrayAnimation.fromFileSequence(filePath, [1, 9]);
- animation.speed = 100;
- animation.reverse = isReverse;
-
- return animation;
- }
-
- /**
- * Get unlock animation
- *
- * @returns TrayIconAnimator
- *
- * @memberOf TrayIconProvider
- */
- unlockAnimation() {
- return this.lockAnimation(true);
- }
-
-} \ No newline at end of file
diff --git a/app/main.js b/app/main.js
index 374ece212b..753ac685a9 100644
--- a/app/main.js
+++ b/app/main.js
@@ -1,7 +1,6 @@
import path from 'path';
import { app, crashReporter, BrowserWindow, ipcMain, Tray, Menu, nativeImage } from 'electron';
import TrayIconManager from './lib/tray-icon-manager';
-import TrayIconProvider from './lib/tray-icon-provider';
// Override appData path to avoid collisions with old client
// New userData path, i.e on macOS: ~/Library/Application Support/mullvad.vpn
@@ -160,7 +159,7 @@ const createTray = () => {
tray.on('click', toggleWindow);
tray.setHighlightMode('selection');
- trayIconManager = new TrayIconManager(tray, new TrayIconProvider());
+ trayIconManager = new TrayIconManager(tray);
};
crashReporter.start({
diff --git a/test/keyframe-animation.spec.js b/test/keyframe-animation.spec.js
new file mode 100644
index 0000000000..83e74b72c6
--- /dev/null
+++ b/test/keyframe-animation.spec.js
@@ -0,0 +1,227 @@
+import { expect } from 'chai';
+import KeyframeAnimation from '../app/lib/keyframe-animation';
+import { nativeImage } from 'electron';
+
+describe('lib/keyframe-animation', function() {
+ this.timeout(1000);
+
+ let animation, seq;
+
+ beforeEach(() => {
+ const images = [1, 2, 3, 4, 5].map(() => nativeImage.createEmpty());
+ animation = new KeyframeAnimation(images);
+ animation.speed = 1;
+
+ seq = [];
+ });
+
+ afterEach(() => {
+ animation.stop();
+ });
+
+ it('should play sequence', (done) => {
+ animation.onFrame = () => seq.push(animation._currentFrame);
+ animation.onFinish = () => {
+ expect(seq).to.be.deep.equal([0, 1, 2, 3, 4]);
+ expect(animation._currentFrame).to.be.equal(4);
+ done();
+ };
+
+ animation.play();
+ });
+
+ it('should play one frame', (done) => {
+ animation.onFrame = () => seq.push(animation._currentFrame);
+ animation.onFinish = () => {
+ expect(seq).to.be.deep.equal([3]);
+ expect(animation._currentFrame).to.be.equal(3);
+ done();
+ };
+
+ animation.play({ startFrame: 3, endFrame: 3 });
+ });
+
+ it('should play sequence with custom frames', (done) => {
+ animation.onFrame = () => seq.push(animation._currentFrame);
+ animation.onFinish = () => {
+ expect(seq).to.be.deep.equal([2, 3, 4]);
+ expect(animation._currentFrame).to.be.equal(4);
+ done();
+ };
+
+ animation.play({
+ startFrame: 2,
+ endFrame: 4
+ });
+ });
+
+ it('should play sequence with custom frames in reverse', (done) => {
+ animation.onFrame = () => seq.push(animation._currentFrame);
+ animation.onFinish = () => {
+ expect(seq).to.be.deep.equal([4, 3, 2]);
+ expect(animation._currentFrame).to.be.equal(2);
+ done();
+ };
+
+ animation.reverse = true;
+ animation.play({
+ startFrame: 4,
+ endFrame: 2
+ });
+ });
+
+ it('should begin from current state starting below range', (done) => {
+ animation.onFrame = () => seq.push(animation._currentFrame);
+ animation.onFinish = () => {
+ expect(seq).to.be.deep.equal([0, 1, 2, 3, 4]);
+ expect(animation._currentFrame).to.be.equal(4);
+ done();
+ };
+
+ animation._currentFrame = 0;
+ animation._isFirstRun = false;
+
+ animation.play({
+ beginFromCurrentState: true,
+ startFrame: 3,
+ endFrame: 4
+ });
+ });
+
+ it('should begin from current state starting below range reverse', (done) => {
+ animation.onFrame = () => seq.push(animation._currentFrame);
+ animation.onFinish = () => {
+ expect(seq).to.be.deep.equal([0, 1, 2, 3]);
+ expect(animation._currentFrame).to.be.equal(3);
+ done();
+ };
+
+ animation._currentFrame = 0;
+ animation._isFirstRun = false;
+ animation.reverse = true;
+
+ animation.play({
+ beginFromCurrentState: true,
+ startFrame: 3,
+ endFrame: 4
+ });
+ });
+
+ it('should begin from current state starting above range', (done) => {
+ animation.onFrame = () => seq.push(animation._currentFrame);
+ animation.onFinish = () => {
+ expect(seq).to.be.deep.equal([4, 3, 2]);
+ expect(animation._currentFrame).to.be.equal(2);
+ done();
+ };
+
+ animation._currentFrame = 4;
+ animation._isFirstRun = false;
+
+ animation.play({
+ beginFromCurrentState: true,
+ startFrame: 1,
+ endFrame: 2
+ });
+ });
+
+ it('should begin from current state starting above range reverse', (done) => {
+ animation.onFrame = () => seq.push(animation._currentFrame);
+ animation.onFinish = () => {
+ expect(seq).to.be.deep.equal([4, 3, 2, 1]);
+ expect(animation._currentFrame).to.be.equal(1);
+ done();
+ };
+
+ animation._currentFrame = 4;
+ animation._isFirstRun = false;
+ animation.reverse = true;
+
+ animation.play({
+ beginFromCurrentState: true,
+ startFrame: 1,
+ endFrame: 3
+ });
+ });
+
+ it('should play sequence in reverse', (done) => {
+ animation.onFrame = () => seq.push(animation._currentFrame);
+ animation.onFinish = () => {
+ expect(seq).to.be.deep.equal([4, 3, 2, 1, 0]);
+ expect(animation._currentFrame).to.be.equal(0);
+ done();
+ };
+
+ animation.reverse = true;
+ animation.play();
+ });
+
+ it('should play sequence on repeat', (done) => {
+ const expectedFrames = [0, 1, 2, 3, 4, 0, 1, 2, 3, 4];
+
+ animation.onFrame = () => {
+ if(seq.length === expectedFrames.length) {
+ expect(seq).to.be.deep.equal(expectedFrames);
+ done();
+ } else {
+ seq.push(animation._currentFrame);
+ }
+ };
+
+ animation.repeat = true;
+ animation.play();
+ });
+
+ it('should play sequence on repeat in reverse', (done) => {
+ const expectedFrames = [4, 3, 2, 1, 0, 4, 3, 2, 1, 0];
+
+ animation.onFrame = () => {
+ if(seq.length === expectedFrames.length) {
+ expect(seq).to.be.deep.equal(expectedFrames);
+ done();
+ } else {
+ seq.push(animation._currentFrame);
+ }
+ };
+
+ animation.repeat = true;
+ animation.reverse = true;
+ animation.play();
+ });
+
+ it('should alternate sequence', (done) => {
+ const expectedFrames = [0, 1, 2, 3, 4, 3, 2, 1, 0];
+
+ animation.onFrame = () => {
+ if(seq.length === expectedFrames.length) {
+ expect(seq).to.be.deep.equal(expectedFrames);
+ done();
+ } else {
+ seq.push(animation._currentFrame);
+ }
+ };
+
+ animation.repeat = true;
+ animation.alternate = true;
+ animation.play();
+ });
+
+ it('should alternate reverse sequence', (done) => {
+ const expectedFrames = [4, 3, 2, 1, 0, 1, 2, 3, 4];
+
+ animation.onFrame = () => {
+ if(seq.length === expectedFrames.length) {
+ expect(seq).to.be.deep.equal(expectedFrames);
+ done();
+ } else {
+ seq.push(animation._currentFrame);
+ }
+ };
+
+ animation.repeat = true;
+ animation.reverse = true;
+ animation.alternate = true;
+ animation.play();
+ });
+
+}); \ No newline at end of file
diff --git a/test/tray-animator.spec.js b/test/tray-animator.spec.js
deleted file mode 100644
index c07bc6b632..0000000000
--- a/test/tray-animator.spec.js
+++ /dev/null
@@ -1,125 +0,0 @@
-import { expect } from 'chai';
-import TrayAnimation from '../app/lib/tray-animation';
-import { nativeImage } from 'electron';
-
-describe('lib/tray-animation', function() {
- this.timeout(1000);
-
- let animation;
-
- beforeEach(() => {
- const images = [1, 2, 3, 4, 5].map(() => nativeImage.createEmpty());
- animation = new TrayAnimation(images);
- animation.speed = 1;
- });
-
- afterEach(() => {
- animation.stop();
- });
-
- it('should play sequence', (done) => {
- let seq = [];
-
- animation.onFrame = () => {
- seq.push(animation._currentFrame);
- };
-
- animation.onFinish = () => {
- expect(seq).to.be.deep.equal([0, 1, 2, 3, 4]);
- expect(animation._currentFrame).to.be.equal(4);
- done();
- };
-
- animation.play();
- });
-
- it('should play sequence in reverse', (done) => {
- let seq = [];
-
- animation.onFrame = () => {
- seq.push(animation._currentFrame);
- };
-
- animation.onFinish = () => {
- expect(seq).to.be.deep.equal([4, 3, 2, 1, 0]);
- expect(animation._currentFrame).to.be.equal(0);
- done();
- };
-
- animation.reverse = true;
- animation.play();
- });
-
- it('should play sequence on repeat', (done) => {
- const expectedFrames = [0, 1, 2, 3, 4, 0, 1, 2, 3, 4];
- let receivedFrames = [];
-
- animation.onFrame = () => {
- if(receivedFrames.length === expectedFrames.length) {
- expect(receivedFrames).to.be.deep.equal(expectedFrames);
- done();
- } else {
- receivedFrames.push(animation._currentFrame);
- }
- };
-
- animation.repeat = true;
- animation.play();
- });
-
- it('should play sequence on repeat in reverse', (done) => {
- const expectedFrames = [4, 3, 2, 1, 0, 4, 3, 2, 1, 0];
- let receivedFrames = [];
-
- animation.onFrame = () => {
- if(receivedFrames.length === expectedFrames.length) {
- expect(receivedFrames).to.be.deep.equal(expectedFrames);
- done();
- } else {
- receivedFrames.push(animation._currentFrame);
- }
- };
-
- animation.repeat = true;
- animation.reverse = true;
- animation.play();
- });
-
- it('should alternate sequence', (done) => {
- const expectedFrames = [0, 1, 2, 3, 4, 3, 2, 1, 0];
- let receivedFrames = [];
-
- animation.onFrame = () => {
- if(receivedFrames.length === expectedFrames.length) {
- expect(receivedFrames).to.be.deep.equal(expectedFrames);
- done();
- } else {
- receivedFrames.push(animation._currentFrame);
- }
- };
-
- animation.repeat = true;
- animation.alternate = true;
- animation.play();
- });
-
- it('should alternate reverse sequence', (done) => {
- const expectedFrames = [4, 3, 2, 1, 0, 1, 2, 3, 4];
- let receivedFrames = [];
-
- animation.onFrame = () => {
- if(receivedFrames.length === expectedFrames.length) {
- expect(receivedFrames).to.be.deep.equal(expectedFrames);
- done();
- } else {
- receivedFrames.push(animation._currentFrame);
- }
- };
-
- animation.repeat = true;
- animation.reverse = true;
- animation.alternate = true;
- animation.play();
- });
-
-}); \ No newline at end of file