From e258d595b02c07572a9163386d63df931799bfd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20S=C3=A1nchez-Mar=C3=ADn?= Date: Thu, 16 Apr 2015 17:10:09 +0200 Subject: [PATCH] tests(gridster): add basic test suite for gridster MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tests are build with mocha + chai, and you can run them from the command line with `grunt karma` or opening `test/index.html` in your browser. This is a first approach, there are hundreds of tests more that should be added. I’ve ported some tests on PRs but more needs to be done. --- Gruntfile.js | 7 + package.json | 27 +- test/amd/index.html | 40 ++ test/amd/index.js | 71 ++++ test/index.html | 29 ++ test/index.js | 709 ++++++++++++++++++++++++++++++++++++ test/jquery.gridder.html | 67 ---- test/jquery.gridder_test.js | 38 -- test/karma.conf.js | 39 ++ test/lib/test.css | 25 ++ test/lib/test_utils.js | 198 ++++++++++ 11 files changed, 1136 insertions(+), 114 deletions(-) create mode 100644 test/amd/index.html create mode 100644 test/amd/index.js create mode 100644 test/index.html create mode 100644 test/index.js delete mode 100644 test/jquery.gridder.html delete mode 100644 test/jquery.gridder_test.js create mode 100644 test/karma.conf.js create mode 100644 test/lib/test.css create mode 100644 test/lib/test_utils.js diff --git a/Gruntfile.js b/Gruntfile.js index 48be913248..a1176b4d42 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -156,6 +156,12 @@ module.exports = function(grunt) { watch: { files: ['libs/*.js', 'src/*.js', 'src/*.css', 'Gruntfile.js'], tasks: ['concat', 'uglify', 'cssmin'] + }, + + karma: { + gridster: { + configFile: 'test/karma.conf.js' + } } }); @@ -168,6 +174,7 @@ module.exports = function(grunt) { grunt.loadNpmTasks('grunt-contrib-yuidoc'); grunt.loadNpmTasks('grunt-bump'); grunt.loadNpmTasks('grunt-conventional-changelog'); + grunt.loadNpmTasks('grunt-karma'); // Default task. grunt.registerTask('default', ['jshint', 'concat', 'uglify', 'cssmin']); diff --git a/package.json b/package.json index 1ec7f199e9..5d1d10af61 100644 --- a/package.json +++ b/package.json @@ -26,17 +26,26 @@ "jquery": "2.1.3" }, "devDependencies": { + "chai": "^2.2.0", "grunt": "~0.4.1", - "grunt-contrib-uglify": "~0.2.0", - "grunt-contrib-jshint": "~0.3.0", - "grunt-contrib-concat": "~0.1.3", - "grunt-contrib-watch": "~0.3.1", - "grunt-contrib-cssmin": "~0.5.0", - "grunt-contrib-yuidoc": "~0.4.0", - "bower": "~0.9.2", - "qunitjs": "~1.11.0", "grunt-bump": "0.0.11", - "grunt-conventional-changelog": "~1.0.0" + "grunt-contrib-concat": "~0.1.3", + "grunt-contrib-cssmin": "~0.5.0", + "grunt-contrib-jshint": "~0.3.0", + "grunt-contrib-uglify": "~0.2.0", + "grunt-contrib-watch": "~0.3.1", + "grunt-contrib-yuidoc": "~0.4.0", + "grunt-conventional-changelog": "~1.0.0", + "grunt-karma": "^0.10.1", + "karma": "^0.12.31", + "karma-chai": "^0.1.0", + "karma-chrome-launcher": "^0.1.7", + "karma-mocha": "^0.1.10", + "karma-mocha-reporter": "^1.0.2", + "karma-requirejs": "^0.2.2", + "mocha": "^2.2.4", + "qunitjs": "~1.11.0", + "requirejs": "^2.1.17" }, "browserify": { "transform": [ diff --git a/test/amd/index.html b/test/amd/index.html new file mode 100644 index 0000000000..6677a9ac1f --- /dev/null +++ b/test/amd/index.html @@ -0,0 +1,40 @@ + + + + + gridster.js tests + + + + + + + + + + + +
+
+ + \ No newline at end of file diff --git a/test/amd/index.js b/test/amd/index.js new file mode 100644 index 0000000000..48fd56be6c --- /dev/null +++ b/test/amd/index.js @@ -0,0 +1,71 @@ +/*global Gridster:false*/ +/*global chai:false, describe:false, beforeEach:false, afterEach:false, it:false*/ + +require.config({ + baseUrl : '../../', + + paths: { + mocha: 'node_modules/mocha/mocha', + chai: 'node_modules/chai/chai', + jquery: 'node_modules/jquery/dist/jquery', + gridster: 'dist/jquery.gridster' + } + +}); + +require(['jquery'], function($) { + $.noConflict( true ); + + require(['test/amd/index'], function(Gridster) { + mocha.setup('bdd'); + + $(function() { + mocha.run(); + }); + }); +}); + + +define(['chai', 'jquery', 'gridster'], function(chai, $, Gridster) { + 'use strict'; + + var expect = chai.expect; + + describe('AMD support', function() { + describe('Gridster', function() { + it('should not define jQuery as global', function() { + expect(window.$).to.be.undefined; + expect(window.jQuery).to.be.undefined; + }); + + it('should not define Gridster as global', function() { + expect(window.Gridster).to.be.undefined; + expect(window.GridsterDraggable).to.be.undefined; + expect(window.GridsterCoords).to.be.undefined; + expect(window.GridsterCollision).to.be.undefined; + }); + + it('should return Gridster class', function() { + expect(Gridster).to.be.a('function'); + expect(Gridster.name).to.equal('Gridster'); + }); + + it('should define the jquery bridge', function() { + expect($.fn.gridster).to.be.a('function'); + }); + }); + + describe('Draggable', function() { + var Draggable = require('gridster-draggable'); + + it('should not be defined in the global scope', function() { + expect(window.GridsterDraggable).to.be.undefined; + }); + + it('should return the Draggable class', function() { + expect(Draggable.name).to.equal('Draggable'); + }); + }); + }); + +}); diff --git a/test/index.html b/test/index.html new file mode 100644 index 0000000000..8496df4ba9 --- /dev/null +++ b/test/index.html @@ -0,0 +1,29 @@ + + + + + gridster.js tests + + + + + + + + + + + + + + + + +
+
+ + \ No newline at end of file diff --git a/test/index.js b/test/index.js new file mode 100644 index 0000000000..72bb503d0f --- /dev/null +++ b/test/index.js @@ -0,0 +1,709 @@ +/*global Gridster:false*/ +/*global chai:false, describe:false, beforeEach:false, afterEach:false, it:false*/ + +'use strict'; + +// add #fixture div when running tests with karma +if (document.body) { + var fixture = document.createElement('div'); fixture.setAttribute('id', 'fixture'); + document.querySelector('#fixture') || document.body.appendChild(fixture); +} + +var expect = chai.expect; +var serialization = { + 'default': function() { + return [ + {name: 'A', col: 1, row: 1, size_x: 1, size_y: 2}, + {name: 'B', col: 2, row: 1, size_x: 3, size_y: 2}, + {name: 'C', col: 5, row: 1, size_x: 3, size_y: 2}, + {name: 'D', col: 8, row: 1, size_x: 2, size_y: 1}, + {name: 'E', col: 1, row: 3, size_x: 4, size_y: 1}, + {name: 'F', col: 10, row: 1, size_x: 1, size_y: 2}, + {name: 'G', col: 8, row: 2, size_x: 2, size_y: 1}, + {name: 'H', col: 5, row: 3, size_x: 3, size_y: 2}, + {name: 'I', col: 8, row: 3, size_x: 1, size_y: 1}, + {name: 'J', col: 9, row: 3, size_x: 2, size_y: 2}, + {name: 'K', col: 1, row: 4, size_x: 1, size_y: 3} + ]; + } +}; + +var SPEED = 100; + +var u = { + pick: function(data, prop) { + return data.map(function(elm) { + return elm[prop]; + }); + }, + + getRandomColor: function() { + var letters = '0123456789ABCDEF'.split(''); + var color = '#'; + for (var i = 0; i < 6; i++) { + color += letters[Math.round(Math.random() * 10)]; + } + return color; + }, + + createGridster: function(config, serialize, fromDom) { + var defaults = { + widget_margins: [5, 5], + widget_base_dimensions: [100, 55], + min_cols: 10, + max_cols: 10 + }; + + serialize || (serialize = serialization.default()); + + var widgets = []; + $.each(serialize, function(i, w) { + widgets.push(['
  • ' + w.name + '
  • ', w.size_x, w.size_y, w.col, w.row]); + }); + + this.$fixture = $('#fixture'); + this.$fixture.html('
    '); + this.$el = $(".gridster > ul"); + + if (fromDom) { + var html = []; + $.each(serialize, function(i, w) { + html.push('
  • ' + w.name + '
  • '); + }); + this.$el.html(html.join('\n')); + } + + this.gridster = this.$el.gridster( + $.extend({}, defaults, config)).data('gridster'); + + if (!fromDom) { + $.each(widgets, function(i, widget) { + this.gridster.add_widget.apply(this.gridster, widget); + }.bind(this)); + } + + this.drag_from_to = u._dragFromTo; + + return this.gridster; + }, + + createEvent: function(type, offset) { + var event = document.createEvent("MouseEvents"); + event.initMouseEvent(type, true, true, window, 0, 0, 0, + offset.left, offset.top, false, false, false, false, 0, null); + + return event; + }, + + dispatchEvent: function(elem, type, event) { + if (elem.dispatchEvent) { + elem.dispatchEvent(event); + } else if (elem.fireEvent) { + elem.fireEvent('on' + type, event); + } + }, + + _dragFromTo: function(fromCoords, toCoords) { + var d = $.Deferred(); + var gridster = this.gridster; + var $el; + + function getMousePos(coords) { + var size = gridster.options.widget_base_dimensions; + var margin = gridster.options.widget_margins; + + var left = ((coords[0] - 1) * size[0]) + (margin[0] * ((coords[0] * 2) - 1)); + var top = ((coords[1] - 1) * size[1]) + (margin[1] * ((coords[1] * 2) - 1)); + + var parentOffset = gridster.$wrapper.offset(); + + return { + left: parentOffset.left + left + 20, + top: parentOffset.top + top + 20 + }; + } + + function addPoint(offset) { + $('').css({ + left: offset.left + 'px', + top: offset.top + 'px', + width: '2px', + height: '2px', + background: 'red', + display: 'inline-block', + position: 'absolute', + zIndex: '9999' + }).appendTo('body'); + } + + var from_offset; + + if (fromCoords instanceof $) { + $el = fromCoords; + var offset = $el.offset(); + from_offset = { + left: offset.left + ($el.width() / 2), + top: offset.top + ($el.height() / 2) + }; + } else { + $el = this.gridster.gridmap[fromCoords[0]][fromCoords[1]]; + from_offset = getMousePos(fromCoords); + } + + if (! $el) { + return; + } + + var to_offset = getMousePos(toCoords); + var el = $el.get(0); + + addPoint(from_offset); + addPoint(to_offset); + + // Simulating drag start + var type = 'mousedown'; + var event = u.createEvent(type, from_offset); + u.dispatchEvent(el, type, event); + + // Simulating drop + type = 'mousemove'; + u.dispatchEvent(el, type, u.createEvent(type, { + top: from_offset.top + 2, + left: from_offset.left + 2 + })); + + this.gridster.$el.on('gridster:dragstop gridster:resizestop', function() { + setTimeout(function() { + d.resolveWith(this); + }.bind(this), SPEED); + }.bind(this)); + + var diff_x = to_offset.left - from_offset.left; + var diff_y = to_offset.top - from_offset.top; + var steps = 10; + var step_x = diff_x / steps; + var step_y = diff_y / steps; + + var tmp_offset = { + left: from_offset.left, + top: from_offset.top + }; + + for (var i = 0; i < steps; i++) { + tmp_offset.left += step_x; + tmp_offset.top += step_y; + addPoint(tmp_offset); + u.dispatchEvent(el, type, u.createEvent(type, tmp_offset)); + } + + u.dispatchEvent(el, type, u.createEvent(type, to_offset)); + addPoint(to_offset); + + // Simulating drag end + type = 'mouseup'; + var dragEndEvent = u.createEvent(type, to_offset); + u.dispatchEvent(el, type, dragEndEvent); + + return d.promise(); + } +}; + + + + +describe('gridster.js', function() { + it('should expose Gridster, Draggable, Coords and Collision classes to window', function() { + expect(window.Gridster).to.be.a.function; + expect(window.GridsterDraggable).to.be.a.function; + expect(window.GridsterCoords).to.be.a.function; + expect(window.GridsterCollision).to.be.a.function; + }); + + it('should define the jquery bridge', function() { + expect($.fn.gridster).to.be.a('function'); + }); + + describe('Initialization', function() { + beforeEach(function() { + u.createGridster.call(this, { + serialize_params: function($w, wgd) { + return { + name: $w.text(), col: wgd.col, row: wgd.row, + size_x: wgd.size_x, size_y: wgd.size_y + }; + } + }); + }); + + afterEach(function() { + this.gridster.destroy(true); + }); + + it('should access the gridster instance', function() { + expect(this.$el.data('gridster')).to.be.an('object'); + expect(this.$el.data('gridster')).to.have.property('$widgets'); + expect(this.$el.data('gridster').$widgets).to.have.length.above(1); + }); + + it('should bind resize event to window', function() { + var events = $._data(window, "events"); + expect(events).to.have.property('resize'); + // both gridster and dragabble resize event handlers + expect(events.resize).to.have.length(2); + }); + + it('should respect the serialized positions', function(done) { + var serialize = this.gridster.serialize(); + expect(serialize).to.deep.equal(serialization.default()); + done(); + }); + }); + + + describe('Instance methods', function() { + describe('Register widgets from DOM', function() { + beforeEach(function() { + u.createGridster.call(this, { + serialize_params: function($w, wgd) { + return { + name: $w.text(), col: wgd.col, row: wgd.row, + size_x: wgd.size_x, size_y: wgd.size_y + }; + } + }, null, true); + }); + + afterEach(function() { + this.gridster.destroy(true); + }); + + it('should sync DOM widgets with gridster widgets', function() { + expect(this.gridster.$widgets).to.have.length(serialization.default().length); + }); + + it('should respect the serialized positions', function() { + var serialize = this.gridster.serialize(); + expect(serialize).to.deep.equal(serialization.default()); + }); + }); + + + describe('Destroy', function() { + + beforeEach(function() { + u.createGridster.call(this); + }); + + afterEach(function() { + this.gridster.destroy(true); + }); + + it('should clean data attached but keep the grid on DOM', function() { + this.gridster.destroy(); + expect(this.$el.data('gridster')).to.be.undefined; + expect(document.contains(this.$el[0])).to.be.true; + }); + + it('should clean widgets data even if gridster is not removed from DOM', function() { + var $el = this.gridster.$widgets.eq(1); + this.gridster.destroy(); + expect($el.data('coords')).to.be.undefined; + }); + + it('should remove the grid from the DOM', function() { + this.gridster.destroy(true); + expect(document.contains(this.$el[0])).to.be.false; + }); + + it('should clean widgets data', function() { + var $el = this.gridster.$widgets.eq(1); + this.gridster.destroy(true); + expect($el.data('coords')).to.be.undefined; + }); + + it('should unbind resize event from window', function() { + this.gridster.destroy(true); + expect($._data(window, "events")).to.be.undefined; + }); + + it('should clean gridmap array', function() { + this.gridster.destroy(); + expect(this.gridster.gridmap).to.be.empty; + expect(this.gridster.faux_grid).to.be.empty; + }); + }); + + + describe('Add a widget dynamically', function() { + beforeEach(function() { + u.createGridster.call(this); + }); + + afterEach(function() { + this.gridster.destroy(true); + }); + + it('should be added to the grid', function() { + this.gridster.add_widget('
  • new
  • ', 2, 2); + var $el = this.gridster.$el.find('.new'); + expect($el).to.have.length(1); + }); + + it('should be positioned in the top/leftmost available space', function() { + this.gridster.remove_widget('[data-col=1][data-row=1]'); + + this.gridster.add_widget('
  • new
  • ', 1, 1); + var $el = this.gridster.$el.find('.new'); + + expect($el.attr('data-col')).to.equal('1'); + expect($el.attr('data-row')).to.equal('1'); + expect(this.gridster.gridmap[1][1][0]).to.equal($el[0]); + }); + + it('should respect the specified dimensions and coords', function() { + this.gridster.add_widget('
  • new
  • ', 2, 2, 2, 1); + var $el = this.gridster.$el.find('.new'); + expect($el.attr('data-col')).to.equal('2'); + expect($el.attr('data-row')).to.equal('1'); + expect($el.attr('data-sizex')).to.equal('2'); + expect($el.attr('data-sizey')).to.equal('2'); + + expect(this.gridster.gridmap[2][1][0]).to.equal($el[0]); + }); + }); + + + describe('Remove a widget', function() { + beforeEach(function() { + u.createGridster.call(this); + }); + + afterEach(function() { + this.gridster.destroy(true); + }); + + it('should be removed from the grid', function(done) { + var $el = this.gridster.$el.find('[data-col=1][data-row=1]'); + this.gridster.remove_widget($el, false, function() { + expect(this.gridmap[1][1]).to.be.false; + expect(document.contains($el[0])).to.be.false; + done(); + }); + }); + + it('should cause elements below moving up', function(done) { + var $el1 = this.gridster.$el.find('[data-col=1][data-row=1]'); + var $el2 = this.gridster.$el.find('[data-col=2][data-row=1]'); + var $el3 = this.gridster.$el.find('[data-col=1][data-row=3]'); + var silent = false; + this.gridster.remove_widget($el1, silent, function() { + this.remove_widget($el2, silent, function() { + expect($el3.attr('data-col')).to.equal('1'); + expect($el3.attr('data-row')).to.equal('1'); + done(); + }); + }); + }); + + it('shouldn\'t cause elements below moving up (silent=true)', function(done) { + var $el1 = this.gridster.$el.find('[data-col=1][data-row=1]'); + var $el2 = this.gridster.$el.find('[data-col=2][data-row=1]'); + var $el3 = this.gridster.$el.find('[data-col=1][data-row=3]'); + var silent = true; + this.gridster.remove_widget($el1, silent, function() { + this.remove_widget($el2, silent, function() { + expect($el3.attr('data-col')).to.equal('1'); + expect($el3.attr('data-row')).to.equal('3'); + done(); + }); + }); + }); + }); + + + describe('Resize a widget', function() { + beforeEach(function() { + u.createGridster.call(this); + }); + + afterEach(function() { + this.gridster.destroy(true); + }); + + it('should be resized to the new dimensions', function(done) { + var $el = this.gridster.$el.find('[data-col=1][data-row=1]'); + this.gridster.resize_widget($el, 3, 3, function() { + expect(this.gridmap[2][2][0]).to.equal($el[0]); + done(); + }); + }); + + it('should cause elements below to be displaced', function(done) { + var $el = this.gridster.$el.find('[data-col=1][data-row=1]'); + var $el2 = this.gridster.$el.find('[data-col=2][data-row=1]'); + this.gridster.resize_widget($el, 3, 3, function() { + expect(this.gridmap[2][4][0]).to.equal($el2[0]); + done(); + }); + }); + + it('should respect the resizing limits if specified', function(done) { + var $el = this.gridster.$el.find('[data-col=1][data-row=1]'); + $el.attr({ + 'data-max-sizex': 2, + 'data-max-sizey': 2 + }); + this.gridster.resize_widget($el, 3, 3, function() { + expect(this.gridmap[3][3][0]).to.equal($el[0]); + done(); + }); + }); + }); + }); + + + describe('Class methods', function() { + describe('Gridster.sort_by_row_asc', function() { + it('should sort coords by row in ascending order', function() { + var sorted = Gridster.sort_by_row_asc(serialization.default()); + var result = u.pick(sorted, 'name').join(','); + var expected = 'A,C,D,B,F,G,E,H,I,J,K'; + + expect(result).to.equal(expected); + }); + }); + + describe('Gridster.sort_by_row_and_col_asc', function() { + it('should sort coords by row and col (top-left) in ascending order', function() { + var sorted = Gridster.sort_by_row_and_col_asc(serialization.default()); + var result = u.pick(sorted, 'name').join(','); + var expected = 'A,B,C,D,F,G,E,H,I,J,K'; + + expect(result).to.equal(expected); + }); + }); + + describe('Gridster.sort_by_col_asc', function() { + it('should sort coords by col in ascending order', function() { + var sorted = Gridster.sort_by_col_asc(serialization.default()); + var result = u.pick(sorted, 'name').join(','); + var expected = 'A,E,K,B,C,H,D,G,I,J,F'; + + expect(result).to.equal(expected); + }); + }); + + describe('Gridster.sort_by_row_desc', function() { + it('should sort coords by row in descending order', function() { + var sorted = Gridster.sort_by_row_desc(serialization.default()); + var result = u.pick(sorted, 'name').join(','); + // note that size_y are taken into account + var expected = 'K,J,H,E,I,C,B,G,A,F,D'; + + expect(result).to.equal(expected); + }); + }); + }); + + describe('Interactions', function() { + describe('Drag and drop', function() { + beforeEach(function() { + u.createGridster.call(this); + }); + + afterEach(function() { + this.gridster.destroy(true); + }); + + describe('move up', function() { + it('should displace the dragged element one cell above', function(done) { + var $el = this.gridster.gridmap[1][3]; + this.drag_from_to([1, 3], [1, 1]).done(function() { + expect(this.gridster.gridmap[1][1][0]).to.equal($el[0]); + done(); + }); + }); + + it('should displace elements above under the dragged element', function(done) { + var $above1 = this.gridster.gridmap[1][1]; + var $above2 = this.gridster.gridmap[2][1]; + + this.drag_from_to([1, 3], [1, 1]).done(function() { + expect(this.gridster.gridmap[1][2][0]).to.equal($above1[0]); + expect(this.gridster.gridmap[2][2][0]).to.equal($above2[0]); + done(); + }); + }); + }); + + describe('move down', function() { + it('should displace the dragged element one cell below', function(done) { + var $el = this.gridster.gridmap[5][1]; + this.drag_from_to([5, 1], [5, 6]).done(function() { + expect(this.gridster.gridmap[5][4]).to.be.ok; + expect(this.gridster.gridmap[5][4][0]).to.equal($el[0]); + done(); + }); + }); + + it('should displace elements below on the top of the dragged element', function(done) { + var $below1 = this.gridster.gridmap[5][3]; + + this.drag_from_to([5, 1], [5, 6]).done(function() { + expect(this.gridster.gridmap[5][1][0]).to.equal($below1[0]); + done(); + }); + }); + }); + + describe('move left', function() { + it('should displace the dragged element one cell left', function(done) { + var $el = this.gridster.gridmap[2][1]; + this.drag_from_to([2, 1], [1, 1]).done(function() { + expect(this.gridster.gridmap[1][1]).to.be.ok; + expect(this.gridster.gridmap[1][1][0]).to.equal($el[0]); + done(); + }); + }); + + it('should displace the item on the left under the dragged element', function(done) { + var $left = this.gridster.gridmap[1][1]; + var $left_bottom = this.gridster.gridmap[1][3]; + this.drag_from_to([2, 1], [1, 1]).done(function() { + expect(this.gridster.gridmap[1][3]).to.be.ok; + expect(this.gridster.gridmap[1][3][0]).to.equal($left[0]); + expect(this.gridster.gridmap[1][5]).to.be.ok; + expect(this.gridster.gridmap[1][5][0]).to.equal($left_bottom[0]); + done(); + }); + }); + }); + + describe('move right', function() { + it('should displace the dragged element one cell right', function(done) { + var $el = this.gridster.gridmap[4][3]; + this.drag_from_to([4, 3], [5, 3]).done(function() { + expect(this.gridster.gridmap[5][3]).to.be.ok; + expect(this.gridster.gridmap[5][3][0]).to.equal($el[0]); + done(); + }); + }); + + it('should displace the item below one cell up', function(done) { + var $below = this.gridster.gridmap[1][4]; + this.drag_from_to([4, 3], [5, 3]).done(function() { + expect(this.gridster.gridmap[1][3]).to.be.ok; + expect(this.gridster.gridmap[1][3][0]).to.equal($below[0]); + done(); + }); + }); + + it('should displace the item on the right one cell down', function(done) { + var $right = this.gridster.gridmap[5][3]; + this.drag_from_to([4, 3], [5, 3]).done(function() { + expect(this.gridster.gridmap[5][4]).to.be.ok; + expect(this.gridster.gridmap[5][4][0]).to.equal($right[0]); + done(); + }); + }); + }); + }); + + + describe('Resize', function() { + describe('axis', function() { + beforeEach(function() { + u.createGridster.call(this, { + resize: { + enabled: true, + axes: ['x', 'y', 'both'] + } + }); + }); + + afterEach(function() { + this.gridster.destroy(true); + }); + + it('should resize in both x/y axis', function(done) { + var $el = this.gridster.gridmap[1][1]; + var $handle = $el.find('.gs-resize-handle-both'); + this.drag_from_to($handle, [7, 4]).done(function() { + expect($el.attr('data-sizex')).to.equal('7'); + expect($el.attr('data-sizey')).to.equal('4'); + this.drag_from_to($handle, [1, 1]).done(function() { + expect($el.attr('data-sizex')).to.equal('1'); + expect($el.attr('data-sizey')).to.equal('1'); + done(); + }); + }); + }); + + it('should resize horizontally', function(done) { + var $el = this.gridster.gridmap[1][1]; + var $handle = $el.find('.gs-resize-handle-x'); + this.drag_from_to($handle, [6, 1]).done(function() { + expect($el.attr('data-sizex')).to.equal('6'); + expect($el.attr('data-sizey')).to.equal('2'); + this.drag_from_to($handle, [1, 1]).done(function() { + expect($el.attr('data-sizex')).to.equal('1'); + expect($el.attr('data-sizey')).to.equal('2'); + done(); + }); + }); + }); + + it('should resize vertically', function(done) { + var $el = this.gridster.gridmap[1][1]; + var $handle = $el.find('.gs-resize-handle-y'); + this.drag_from_to($handle, [1, 6]).done(function() { + expect($el.attr('data-sizex')).to.equal('1'); + expect($el.attr('data-sizey')).to.equal('6'); + this.drag_from_to($handle, [1, 1]).done(function() { + done(); + }); + }); + }); + }); + + describe('max/min size', function() { + beforeEach(function() { + u.createGridster.call(this, { + resize: { + enabled: true, + max_size: [4, 4], + min_size: [2, 2] + } + }); + }); + + afterEach(function() { + this.gridster.destroy(true); + }); + + it('should respect specified resize.max_size option', function(done) { + var $el = this.gridster.gridmap[1][1]; + var $handle = $el.find('.gs-resize-handle-both'); + this.drag_from_to($handle, [8, 8]).done(function() { + expect($el.attr('data-sizex')).to.equal('4'); + expect($el.attr('data-sizey')).to.equal('4'); + done(); + }); + }); + + it('should respect specified resize.min_size option', function(done) { + var $el = this.gridster.gridmap[1][1]; + var $handle = $el.find('.gs-resize-handle-both'); + this.drag_from_to($handle, [8, 8]).done(function() { + this.drag_from_to($handle, [1, 1]).done(function() { + expect($el.attr('data-sizex')).to.equal('2'); + expect($el.attr('data-sizey')).to.equal('2'); + done(); + }); + }); + }); + }); + }); + }); + +}); \ No newline at end of file diff --git a/test/jquery.gridder.html b/test/jquery.gridder.html deleted file mode 100644 index 1750f0a4dc..0000000000 --- a/test/jquery.gridder.html +++ /dev/null @@ -1,67 +0,0 @@ - - - - - gridster.js Test Suite - - - - - - - - - - - - - - - - - - - - - - -

    gridster.js Test Suite

    -

    -
    -

    -
      -
      - -
      -
        -
      • -
      • -
      • -
      • - -
      • -
      • -
      • -
      • -
      • - -
      • - - -
      • -
      • -
      • -
      • - -
      • -
      • - -
      • - -
      -
      -
      - - - - diff --git a/test/jquery.gridder_test.js b/test/jquery.gridder_test.js deleted file mode 100644 index 2df25e775e..0000000000 --- a/test/jquery.gridder_test.js +++ /dev/null @@ -1,38 +0,0 @@ -/*global QUnit:false, module:false, test:false, asyncTest:false, expect:false*/ -/*global start:false, stop:false ok:false, equal:false, notEqual:false, deepEqual:false*/ -/*global notDeepEqual:false, strictEqual:false, notStrictEqual:false, raises:false*/ -(function($) { - - /* - ======== A Handy Little QUnit Reference ======== - http://docs.jquery.com/QUnit - - Test methods: - expect(numAssertions) - stop(increment) - start(decrement) - Test assertions: - ok(value, [message]) - equal(actual, expected, [message]) - notEqual(actual, expected, [message]) - deepEqual(actual, expected, [message]) - notDeepEqual(actual, expected, [message]) - strictEqual(actual, expected, [message]) - notStrictEqual(actual, expected, [message]) - raises(block, [expected], [message]) - */ - - module('jQuery#gridster', { - setup: function() { - - this.el = $('#qunit-fixture').find(".wrapper ul"); - - } - }); - - // test('is chainable', 1, function() { - // // Not a bad test to run on collection methods. - // strictEqual(this.el, this.el.gridster(), 'should be chaninable'); - // }); - -}(jQuery)); diff --git a/test/karma.conf.js b/test/karma.conf.js new file mode 100644 index 0000000000..6b3c25436f --- /dev/null +++ b/test/karma.conf.js @@ -0,0 +1,39 @@ +module.exports = function(config) { + 'use strict'; + + config.set({ + frameworks: ['mocha', 'chai'], + + client: { + captureConsole: true, + mocha: { + reporter: 'html', // change Karma's debug.html to the mocha web reporter + ui: 'bdd' + } + }, + + files: [ + {pattern: 'node_modules/jquery/dist/jquery.js', include: true}, + {pattern: 'dist/jquery.gridster.css', include: true}, + {pattern: 'dist/jquery.gridster.js', include: true}, + {pattern: 'test/lib/test.css', include: true}, + 'test/index.js' + ], + + reporters: ['mocha'], + + // level of logging (config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG) + logLevel: config.LOG_DEBUG, + + // enable / disable watching file and executing tests whenever any file changes + autoWatch: true, + + colors: true, + + // Start these browsers, currently available: + // Chrome, ChromeCanary, Firefox, Opera, Safari (only Mac), PhantomJS, IE (only Windows) + browsers: ['Chrome'], + + singleRun: true + }); +}; \ No newline at end of file diff --git a/test/lib/test.css b/test/lib/test.css new file mode 100644 index 0000000000..7465bd5a6e --- /dev/null +++ b/test/lib/test.css @@ -0,0 +1,25 @@ +#mocha { + box-sizing: border-box; + float: left; + width: 50%; + margin: 0; + padding: 20px; +} + +#mocha-stats { + position: static; +} + +#fixture { + width: 50%; + float: right; +} + + +.gridster ul { + list-style: none; +} + +.gridster li { + background-color: #DDD; +} \ No newline at end of file diff --git a/test/lib/test_utils.js b/test/lib/test_utils.js new file mode 100644 index 0000000000..0c7c6d9de1 --- /dev/null +++ b/test/lib/test_utils.js @@ -0,0 +1,198 @@ +'use strict'; + +window.serialization = { + 'default': function() { + return [ + {name: 'A', col: 1, row: 1, size_x: 1, size_y: 2}, + {name: 'B', col: 2, row: 1, size_x: 3, size_y: 2}, + {name: 'C', col: 5, row: 1, size_x: 3, size_y: 2}, + {name: 'D', col: 8, row: 1, size_x: 2, size_y: 1}, + {name: 'E', col: 1, row: 3, size_x: 4, size_y: 1}, + {name: 'F', col: 10, row: 1, size_x: 1, size_y: 2}, + {name: 'G', col: 8, row: 2, size_x: 2, size_y: 1}, + {name: 'H', col: 5, row: 3, size_x: 3, size_y: 2}, + {name: 'I', col: 8, row: 3, size_x: 1, size_y: 1}, + {name: 'J', col: 9, row: 3, size_x: 2, size_y: 2}, + {name: 'K', col: 1, row: 4, size_x: 1, size_y: 3} + ]; + } +}; + + +window.u = { + pick: function(data, prop) { + return data.map(function(elm) { + return elm[prop]; + }); + }, + + getRandomColor: function() { + var letters = '0123456789ABCDEF'.split(''); + var color = '#'; + for (var i = 0; i < 6; i++) { + color += letters[Math.round(Math.random() * 10)]; + } + return color; + }, + + createGridster: function(config, serialize, fromDom) { + var defaults = { + widget_margins: [5, 5], + widget_base_dimensions: [100, 55], + min_cols: 10, + max_cols: 10 + }; + + serialize || (serialize = window.serialization.default()); + + var widgets = []; + $.each(serialize, function(i, w) { + widgets.push(['
    1. ' + w.name + '
    2. ', w.size_x, w.size_y, w.col, w.row]); + }); + + this.$fixture = $('#fixture'); + this.$fixture.html('
        '); + this.$el = $(".gridster > ul"); + + if (fromDom) { + var html = []; + $.each(serialize, function(i, w) { + html.push('
      • ' + w.name + '
      • '); + }); + this.$el.html(html.join('\n')); + } + + this.gridster = this.$el.gridster( + $.extend({}, defaults, config)).data('gridster'); + + if (!fromDom) { + $.each(widgets, function(i, widget) { + this.gridster.add_widget.apply(this.gridster, widget); + }.bind(this)); + } + + this.drag_from_to = u._dragFromTo; + + return this.gridster; + }, + + createEvent: function(type, offset) { + var event = document.createEvent("MouseEvents"); + event.initMouseEvent(type, true, true, window, 0, 0, 0, + offset.left, offset.top, false, false, false, false, 0, null); + + return event; + }, + + dispatchEvent: function(elem, type, event) { + if (elem.dispatchEvent) { + elem.dispatchEvent(event); + } else if (elem.fireEvent) { + elem.fireEvent('on' + type, event); + } + }, + + _dragFromTo: function(fromCoords, toCoords) { + var d = $.Deferred(); + var gridster = this.gridster; + var $el; + + function getMousePos(coords) { + var size = gridster.options.widget_base_dimensions; + var margin = gridster.options.widget_margins; + + var left = ((coords[0] - 1) * size[0]) + (margin[0] * ((coords[0] * 2) - 1)); + var top = ((coords[1] - 1) * size[1]) + (margin[1] * ((coords[1] * 2) - 1)); + + var parentOffset = gridster.$wrapper.offset(); + + return { + left: parentOffset.left + left + 20, + top: parentOffset.top + top + 20 + }; + } + + function addPoint(offset) { + $('').css({ + left: offset.left + 'px', + top: offset.top + 'px', + width: '2px', + height: '2px', + background: 'red', + display: 'inline-block', + position: 'absolute', + zIndex: '9999' + }).appendTo('body'); + } + + var from_offset; + + if (fromCoords instanceof $) { + $el = fromCoords; + var offset = $el.offset(); + from_offset = { + left: offset.left + ($el.width() / 2), + top: offset.top + ($el.height() / 2) + }; + } else { + $el = this.gridster.gridmap[fromCoords[0]][fromCoords[1]]; + from_offset = getMousePos(fromCoords); + } + + if (! $el) { + return; + } + + var to_offset = getMousePos(toCoords); + var el = $el.get(0); + + addPoint(from_offset); + addPoint(to_offset); + + // Simulating drag start + var type = 'mousedown'; + var event = u.createEvent(type, from_offset); + u.dispatchEvent(el, type, event); + + // Simulating drop + type = 'mousemove'; + u.dispatchEvent(el, type, u.createEvent(type, { + top: from_offset.top + 2, + left: from_offset.left + 2 + })); + + this.gridster.$el.on('gridster:dragstop gridster:resizestop', function() { + setTimeout(function() { + d.resolveWith(this); + }.bind(this), SPEED); + }.bind(this)); + + var diff_x = to_offset.left - from_offset.left; + var diff_y = to_offset.top - from_offset.top; + var steps = 10; + var step_x = diff_x / steps; + var step_y = diff_y / steps; + + var tmp_offset = { + left: from_offset.left, + top: from_offset.top + }; + + for (var i = 0; i < steps; i++) { + tmp_offset.left += step_x; + tmp_offset.top += step_y; + addPoint(tmp_offset); + u.dispatchEvent(el, type, u.createEvent(type, tmp_offset)); + } + + u.dispatchEvent(el, type, u.createEvent(type, to_offset)); + addPoint(to_offset); + + // Simulating drag end + type = 'mouseup'; + var dragEndEvent = u.createEvent(type, to_offset); + u.dispatchEvent(el, type, dragEndEvent); + + return d.promise(); + } +};