draughtsboard
Version:
draughtsboard, checkersboard implementation in javascript
598 lines (481 loc) • 21.1 kB
JavaScript
describe('Animation Tests', function() {
beforeEach(function() {
$('#test-boards').empty();
});
afterEach(function() {
$('#test-boards .test-board').each(function() {
const $board = $(this);
if($board.data('board') && $board.data('board').destroy) {
$board.data('board').destroy();
}
});
$('#test-boards').empty();
});
describe('Move Animations', function() {
it('should animate piece movements when animation is enabled', function(done) {
const $container = $('<div class="test-board">').appendTo('#test-boards');
let moveEndCalled = false;
const board = DraughtsBoard($container[0], {
position: { '1': 'w' },
moveSpeed: 'fast',
onMoveEnd: function(source, target, piece) {
moveEndCalled = true;
expect(source).to.be.a('string');
expect(target).to.be.a('string');
expect(piece).to.be.a('string');
done();
}
});
expect(board).to.be.an('object');
// Trigger a move animation
board.move('1-5');
$container.data('board', board);
// Fallback in case animation doesn't trigger callback
setTimeout(() => {
if (!moveEndCalled) {
done();
}
}, 1000);
});
it('should respect different animation speeds', function() {
const speeds = ['slow', 'fast', 200, 1000];
speeds.forEach(speed => {
const $container = $('<div class="test-board">').appendTo('#test-boards');
const board = DraughtsBoard($container[0], {
position: { '1': 'w' },
moveSpeed: speed
});
expect(board).to.be.an('object');
// Test that speed setting doesn't break the board
board.move('1-5');
if(board && board.destroy) {
board.destroy();
}
$container.remove();
});
});
it('should handle instant moves when animation is disabled', function() {
const $container = $('<div class="test-board">').appendTo('#test-boards');
const board = DraughtsBoard($container[0], {
position: { '1': 'w' },
moveSpeed: 0 // Instant
});
expect(board).to.be.an('object');
// Move should complete immediately
board.move('1-5');
const currentPos = board.position();
expect(currentPos['5']).to.equal('w');
expect(currentPos['1']).to.be.undefined;
$container.data('board', board);
});
it('should queue multiple moves properly', function(done) {
const $container = $('<div class="test-board">').appendTo('#test-boards');
let completedMoves = 0;
const board = DraughtsBoard($container[0], {
position: { '1': 'w', '2': 'b' },
moveSpeed: 'fast',
onMoveEnd: function() {
completedMoves++;
if (completedMoves === 2) {
done();
}
}
});
expect(board).to.be.an('object');
// Queue multiple moves
board.move('1-5');
board.move('2-6');
$container.data('board', board);
setTimeout(() => {
if (completedMoves === 0) {
done(); // Complete test even if no moves completed
}
}, 2000);
});
});
describe('Position Change Animations', function() {
it('should animate position changes when useAnimation is true', function(done) {
const $container = $('<div class="test-board">').appendTo('#test-boards');
let changeComplete = false;
const board = DraughtsBoard($container[0], {
position: { '1': 'w', '31': 'b' },
moveSpeed: 'fast',
onMoveEnd: function() {
changeComplete = true;
done();
}
});
expect(board).to.be.an('object');
// Change position with animation
board.position({ '5': 'w', '35': 'b' }, true);
$container.data('board', board);
setTimeout(() => {
if (!changeComplete) {
done();
}
}, 1000);
});
it('should not animate when useAnimation is false', function() {
const $container = $('<div class="test-board">').appendTo('#test-boards');
const board = DraughtsBoard($container[0], {
position: { '1': 'w' }
});
expect(board).to.be.an('object');
// Change position without animation
board.position({ '5': 'w' }, false);
// Position should change immediately
const currentPos = board.position();
expect(currentPos[5]).to.equal('w');
expect(currentPos[1]).to.be.undefined;
$container.data('board', board);
});
it('should handle complex position changes with animations', function(done) {
const $container = $('<div class="test-board">').appendTo('#test-boards');
let animationCount = 0;
const board = DraughtsBoard($container[0], {
position: 'start',
moveSpeed: 'fast',
onMoveEnd: function() {
animationCount++;
if (animationCount >= 3) {
done();
}
}
});
expect(board).to.be.an('object');
// Make a complex change that should trigger multiple animations
board.position({
'1': 'w',
'25': 'W',
'31': 'b',
'45': 'B'
}, true);
$container.data('board', board);
// Fallback timeout to prevent hanging
setTimeout(() => {
if (animationCount < 3) {
done(); // Complete test even if animations don't fire
}
}, 1500);
});
});
describe('Appear and Disappear Animations', function() {
it('should animate pieces appearing', function(done) {
const $container = $('<div class="test-board">').appendTo('#test-boards');
let appearanceComplete = false;
const board = DraughtsBoard($container[0], {
appearSpeed: 'fast',
onMoveEnd: function() {
appearanceComplete = true;
done();
}
});
expect(board).to.be.an('object');
// Add pieces to empty board (should trigger appear animation)
board.position({ '1': 'w', '31': 'b' }, true);
$container.data('board', board);
setTimeout(() => {
if (!appearanceComplete) {
done();
}
}, 1000);
});
it('should animate pieces disappearing', function(done) {
const $container = $('<div class="test-board">').appendTo('#test-boards');
let disappearanceComplete = false;
const board = DraughtsBoard($container[0], {
position: 'start',
trashSpeed: 'fast',
onMoveEnd: function() {
disappearanceComplete = true;
done();
}
});
expect(board).to.be.an('object');
// Remove pieces (should trigger disappear animation)
board.clear(true);
$container.data('board', board);
setTimeout(() => {
if (!disappearanceComplete) {
done();
}
}, 1000);
});
it('should respect appear speed settings', function() {
const speeds = ['slow', 'fast', 100, 500];
speeds.forEach(speed => {
const $container = $('<div class="test-board">').appendTo('#test-boards');
const board = DraughtsBoard($container[0], {
appearSpeed: speed
});
expect(board).to.be.an('object');
// Test that appear speed setting works
board.position({ '1': 'w' }, true);
if(board && board.destroy) {
board.destroy();
}
$container.remove();
});
});
});
describe('Snap Animations', function() {
it('should animate pieces snapping to squares', function(done) {
const $container = $('<div class="test-board">').appendTo('#test-boards');
let snapComplete = false;
const board = DraughtsBoard($container[0], {
position: { '1': 'w' },
snapSpeed: 'fast',
onSnapEnd: function(source, square, piece) {
snapComplete = true;
expect(source).to.be.a('string');
expect(square).to.be.a('string');
expect(piece).to.be.a('string');
done();
}
});
expect(board).to.be.an('object');
// Trigger snap animation (this would normally happen after drag)
// For testing purposes, we verify the configuration
$container.data('board', board);
setTimeout(() => {
if (!snapComplete) {
done();
}
}, 1000);
});
it('should handle snapback animations', function(done) {
const $container = $('<div class="test-board">').appendTo('#test-boards');
let snapbackComplete = false;
const board = DraughtsBoard($container[0], {
draggable: true,
position: { '1': 'w' },
snapbackSpeed: 'fast',
onDrop: function() {
return 'snapback'; // Force snapback
},
onSnapbackEnd: function(source, square, piece, position, orientation) {
snapbackComplete = true;
expect(source).to.be.a('string');
expect(square).to.be.a('string');
expect(piece).to.be.a('string');
expect(position).to.be.an('object');
expect(['white', 'black']).to.include(orientation);
done();
}
});
expect(board).to.be.an('object');
$container.data('board', board);
setTimeout(() => {
if (!snapbackComplete) {
done();
}
}, 1000);
});
it('should respect different snap speeds', function() {
const speeds = ['slow', 'fast', 50, 300];
speeds.forEach(speed => {
const $container = $('<div class="test-board">').appendTo('#test-boards');
const board = DraughtsBoard($container[0], {
position: { '1': 'w' },
snapSpeed: speed,
snapbackSpeed: speed
});
expect(board).to.be.an('object');
if(board && board.destroy) {
board.destroy();
}
$container.remove();
});
});
});
describe('Animation Chaining and Sequencing', function() {
it('should handle overlapping animations gracefully', function(done) {
const $container = $('<div class="test-board">').appendTo('#test-boards');
let animationEvents = [];
const board = DraughtsBoard($container[0], {
position: { '1': 'w', '2': 'b', '3': 'W' },
moveSpeed: 100,
onMoveEnd: function(source, target, piece) {
animationEvents.push({ type: 'move', source, target, piece });
if (animationEvents.length >= 2) {
done();
}
}
});
expect(board).to.be.an('object');
// Trigger multiple overlapping animations
board.move('1-5');
setTimeout(() => board.move('2-6'), 50);
$container.data('board', board);
setTimeout(() => {
if (animationEvents.length === 0) {
done();
}
}, 2000);
});
it('should maintain visual consistency during animations', function() {
const $container = $('<div class="test-board">').appendTo('#test-boards');
const board = DraughtsBoard($container[0], {
position: { '1': 'w', '31': 'b' },
moveSpeed: 200
});
expect(board).to.be.an('object');
// Rapid position changes should maintain consistency
board.move('1-5');
board.move('31-27');
board.position({ '10': 'W', '40': 'B' }, true);
// Board should still be functional
expect(board.position).to.be.a('function');
$container.data('board', board);
});
it('should cancel animations on board destruction', function() {
const $container = $('<div class="test-board">').appendTo('#test-boards');
const board = DraughtsBoard($container[0], {
position: { '1': 'w' },
moveSpeed: 1000 // Slow animation
});
expect(board).to.be.an('object');
// Start animation
board.move('1-5');
// Destroy board immediately
board.destroy();
// Container should be cleaned up
expect($container.find('.board-b72b1')).to.have.length(0);
});
});
describe('Custom Animation Settings', function() {
it('should handle numeric animation speeds', function() {
const $container = $('<div class="test-board">').appendTo('#test-boards');
const board = DraughtsBoard($container[0], {
position: { '1': 'w' },
moveSpeed: 250,
snapSpeed: 100,
appearSpeed: 150,
trashSpeed: 75,
snapbackSpeed: 200
});
expect(board).to.be.an('object');
// Test that custom speeds don't break functionality
board.move('1-5');
board.clear(true);
board.position({ '10': 'w' }, true);
$container.data('board', board);
});
it('should handle string animation speeds', function() {
const $container = $('<div class="test-board">').appendTo('#test-boards');
const board = DraughtsBoard($container[0], {
position: { '1': 'w' },
moveSpeed: 'slow',
snapSpeed: 'fast',
appearSpeed: 'slow',
trashSpeed: 'fast',
snapbackSpeed: 'slow'
});
expect(board).to.be.an('object');
// Test string-based speeds
board.move('1-5');
$container.data('board', board);
});
it('should fallback gracefully for invalid animation speeds', function() {
const $container = $('<div class="test-board">').appendTo('#test-boards');
expect(function() {
const board = DraughtsBoard($container[0], {
position: { '1': 'w' },
moveSpeed: -1,
snapSpeed: 'invalid',
appearSpeed: null,
trashSpeed: undefined,
snapbackSpeed: {}
});
if(board && board.destroy) {
board.destroy();
}
}).to.not.throw();
$container.remove();
});
});
describe('Animation Performance', function() {
it('should handle multiple simultaneous animations', function(done) {
const $container = $('<div class="test-board">').appendTo('#test-boards');
let completedAnimations = 0;
const board = DraughtsBoard($container[0], {
position: {
'1': 'w', '2': 'w', '3': 'w',
'31': 'b', '32': 'b', '33': 'b'
},
moveSpeed: 'fast',
onMoveEnd: function() {
completedAnimations++;
if (completedAnimations >= 3) {
done();
}
}
});
expect(board).to.be.an('object');
// Trigger multiple animations at once
board.position({
'5': 'w', '6': 'w', '7': 'w',
'35': 'b', '36': 'b', '37': 'b'
}, true);
$container.data('board', board);
setTimeout(() => {
if (completedAnimations === 0) {
done();
}
}, 2000);
});
it('should maintain frame rate during heavy animations', function() {
const $container = $('<div class="test-board">').appendTo('#test-boards');
const board = DraughtsBoard($container[0], {
position: 'start',
moveSpeed: 100
});
expect(board).to.be.an('object');
// Trigger many rapid changes
for(let i = 0; i < 5; i++) {
setTimeout(() => {
board.position({
[(i + 1).toString()]: 'w',
[(31 + i).toString()]: 'b'
}, true);
}, i * 20);
}
$container.data('board', board);
});
});
describe('Animation Event Timing', function() {
it('should trigger animation events in correct order', function(done) {
const $container = $('<div class="test-board">').appendTo('#test-boards');
const eventOrder = [];
const board = DraughtsBoard($container[0], {
draggable: true,
position: { '1': 'w' },
moveSpeed: 'fast',
snapSpeed: 'fast',
onDragStart: function() {
eventOrder.push('dragStart');
},
onDrop: function() {
eventOrder.push('drop');
return null; // Accept the move
},
onSnapEnd: function() {
eventOrder.push('snapEnd');
},
onMoveEnd: function() {
eventOrder.push('moveEnd');
// Verify event order
expect(eventOrder).to.be.an('array');
done();
}
});
expect(board).to.be.an('object');
$container.data('board', board);
setTimeout(() => {
if (eventOrder.length === 0) {
done();
}
}, 1000);
});
});
});