diff --git a/.gitignore b/.gitignore
index 0cfc89f..8409212 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
bower_components/
-node_modules/
\ No newline at end of file
+node_modules/
+coverage/
diff --git a/bower.json b/bower.json
index b9814ab..136f9da 100644
--- a/bower.json
+++ b/bower.json
@@ -17,10 +17,10 @@
],
"dependencies": {
"angular": "~1.0.x",
- "jquery-ui": ">= 1.9",
- "jquery-simulate": "latest"
+ "jquery-ui": ">= 1.9"
},
"devDependencies": {
- "angular-mocks": "~1.0.x"
+ "angular-mocks": "~1.0.x",
+ "jquery-simulate": "latest"
}
}
diff --git a/gruntFile.js b/gruntFile.js
index 58b7d1a..798cbf5 100644
--- a/gruntFile.js
+++ b/gruntFile.js
@@ -1,4 +1,4 @@
-module.exports = function (grunt) {
+module.exports = function(grunt) {
grunt.loadNpmTasks('grunt-testacular');
grunt.loadNpmTasks('grunt-contrib-jshint');
@@ -7,8 +7,14 @@ module.exports = function (grunt) {
grunt.registerTask('default', ['jshint', 'testacular']);
var testacularConfig = function(configFile, customOptions) {
- var options = { configFile: configFile, keepalive: true };
- var travisOptions = process.env.TRAVIS && { browsers: ['Firefox'], reporters: 'dots' };
+ var options = {
+ configFile: configFile,
+ keepalive: true
+ };
+ var travisOptions = process.env.TRAVIS && {
+ browsers: ['Firefox'],
+ reporters: 'dots'
+ };
return grunt.util._.extend(options, customOptions, travisOptions);
};
@@ -19,19 +25,20 @@ module.exports = function (grunt) {
options: testacularConfig('test/test.conf.js')
}
},
- jshint:{
- files:['src/**/*.js', 'test/**/*.js', 'demo/**/*.js'],
- options:{
- curly:true,
- eqeqeq:true,
- immed:true,
- latedef:true,
- newcap:true,
- noarg:true,
- sub:true,
- boss:true,
- eqnull:true,
- globals:{}
+ jshint: {
+ files: ['src/**/*.js', 'test/**/*.js', 'demo/**/*.js', '!test/libs/*.js'],
+ options: {
+ curly: true,
+ eqeqeq: true,
+ immed: true,
+ //indent: 2,
+ latedef: true,
+ newcap: true,
+ noarg: true,
+ sub: true,
+ boss: true,
+ eqnull: true,
+ globals: {}
}
}
});
diff --git a/test/libs/jquery.simulate.dragandrevert.js b/test/libs/jquery.simulate.dragandrevert.js
new file mode 100644
index 0000000..03cda66
--- /dev/null
+++ b/test/libs/jquery.simulate.dragandrevert.js
@@ -0,0 +1,60 @@
+;(function($, undefined) {
+ function findCenter(elem) {
+ var offset,
+ document = $(elem.ownerDocument);
+ elem = $(elem);
+ offset = elem.offset();
+
+ return {
+ x: offset.left + elem.outerWidth() / 2 - document.scrollLeft(),
+ y: offset.top + elem.outerHeight() / 2 - document.scrollTop()
+ };
+ }
+
+ $.extend($.simulate.prototype, {
+ simulateDragAndRevert: function() {
+ var i = 0,
+ target = this.target,
+ options = this.options,
+ center = findCenter(target),
+ x = Math.floor(center.x),
+ y = Math.floor(center.y),
+ dx = options.dx || 0,
+ dy = options.dy || 0,
+ moves = options.moves || 3,
+ coord = {
+ clientX: x,
+ clientY: y
+ };
+
+ this.simulateEvent(target, "mousedown", coord);
+
+ for (; i < moves; i++) {
+ x += dx / moves;
+ y += dy / moves;
+
+ coord = {
+ clientX: Math.round(x),
+ clientY: Math.round(y)
+ };
+
+ this.simulateEvent(document, "mousemove", coord);
+ }
+
+ for (i = 0; i < moves; i++) {
+ x -= dx / moves;
+ y -= dy / moves;
+
+ coord = {
+ clientX: Math.round(x),
+ clientY: Math.round(y)
+ };
+
+ this.simulateEvent(document, "mousemove", coord);
+ }
+
+ this.simulateEvent(target, "mouseup", coord);
+ this.simulateEvent(target, "click", coord);
+ }
+ });
+})(jQuery);
diff --git a/test/sortable.spec.js b/test/sortable.spec.js
index 5a8d1cf..da60de6 100644
--- a/test/sortable.spec.js
+++ b/test/sortable.spec.js
@@ -3,6 +3,17 @@ describe('uiSortable', function() {
// Ensure the sortable angular module is loaded
beforeEach(module('ui.sortable'));
+ beforeEach(function() {
+ this.addMatchers({
+ toEqualListContent: function (list) {
+ if (list && list.length) {
+ return list.children().map(function(){ return this.innerHTML; }).toArray();
+ }
+ return [];
+ }
+ });
+ });
+
var EXTRA_DY_PERCENTAGE = 0.25;
describe('Simple use', function() {
@@ -30,7 +41,7 @@ describe('uiSortable', function() {
});
});
-
+
describe('Drag & Drop simulation', function() {
@@ -60,11 +71,13 @@ describe('uiSortable', function() {
var dy = (1 + EXTRA_DY_PERCENTAGE) * li.outerHeight();
li.simulate('drag', { dy: dy });
expect($rootScope.items).toEqual(["One", "Three", "Two"]);
+ expect($rootScope.items).toEqualListContent(element);
li = element.find(':eq(1)');
dy = -(1 + EXTRA_DY_PERCENTAGE) * li.outerHeight();
li.simulate('drag', { dy: dy });
expect($rootScope.items).toEqual(["Three", "One", "Two"]);
+ expect($rootScope.items).toEqualListContent(element);
$(element).remove();
});
@@ -92,23 +105,25 @@ describe('uiSortable', function() {
var dy = (1 + EXTRA_DY_PERCENTAGE) * li.outerHeight();
li.simulate('drag', { dy: dy });
expect($rootScope.items.map(function(x){ return x.text; })).toEqual(["One", "Two", "Three", "Four"]);
+ expect($rootScope.items.map(function(x){ return x.text; })).toEqualListContent(element);
li = element.find(':eq(1)');
dy = (2 + EXTRA_DY_PERCENTAGE) * li.outerHeight();
li.simulate('drag', { dy: dy });
expect($rootScope.items.map(function(x){ return x.text; })).toEqual(["One", "Three", "Four", "Two"]);
+ expect($rootScope.items.map(function(x){ return x.text; })).toEqualListContent(element);
- // fails on angular 1.2
li = element.find(':eq(2)');
dy = -(2 + EXTRA_DY_PERCENTAGE) * li.outerHeight();
li.simulate('drag', { dy: dy });
expect($rootScope.items.map(function(x){ return x.text; })).toEqual(["Four", "One", "Three", "Two"]);
+ expect($rootScope.items.map(function(x){ return x.text; })).toEqualListContent(element);
- // fails on angular 1.2
li = element.find(':eq(3)');
dy = -(2 + EXTRA_DY_PERCENTAGE) * li.outerHeight();
li.simulate('drag', { dy: dy });
expect($rootScope.items.map(function(x){ return x.text; })).toEqual(["Four", "Two", "One", "Three"]);
+ expect($rootScope.items.map(function(x){ return x.text; })).toEqualListContent(element);
// also placing right above the locked node seems a bit harder !?!?
@@ -116,6 +131,121 @@ describe('uiSortable', function() {
});
});
+ it('should work when "placeholder" option is used', function() {
+ inject(function($compile, $rootScope) {
+ var element;
+ element = $compile('
')($rootScope);
+ $rootScope.$apply(function() {
+ $rootScope.opts = {
+ placeholder: "sortable-item-placeholder"
+ };
+ $rootScope.items = ["One", "Two", "Three"];
+ });
+
+ host.append(element);
+
+ var li = element.find(':eq(1)');
+ var dy = (1 + EXTRA_DY_PERCENTAGE) * li.outerHeight();
+ li.simulate('drag', { dy: dy });
+ expect($rootScope.items).toEqual(["One", "Three", "Two"]);
+ expect($rootScope.items).toEqualListContent(element);
+
+ li = element.find(':eq(1)');
+ dy = -(1 + EXTRA_DY_PERCENTAGE) * li.outerHeight();
+ li.simulate('drag', { dy: dy });
+ expect($rootScope.items).toEqual(["Three", "One", "Two"]);
+ expect($rootScope.items).toEqualListContent(element);
+
+ $(element).remove();
+ });
+ });
+
+ it('should work when "placeholder" option equals the class of items', function() {
+ inject(function($compile, $rootScope) {
+ var element;
+ element = $compile('')($rootScope);
+ $rootScope.$apply(function() {
+ $rootScope.opts = {
+ placeholder: "sortable-item"
+ };
+ $rootScope.items = ["One", "Two", "Three"];
+ });
+
+ host.append(element);
+
+ var li = element.find(':eq(1)');
+ var dy = (1 + EXTRA_DY_PERCENTAGE) * li.outerHeight();
+ li.simulate('drag', { dy: dy });
+ expect($rootScope.items).toEqual(["One", "Three", "Two"]);
+ expect($rootScope.items).toEqualListContent(element);
+
+ li = element.find(':eq(1)');
+ dy = -(1 + EXTRA_DY_PERCENTAGE) * li.outerHeight();
+ li.simulate('drag', { dy: dy });
+ expect($rootScope.items).toEqual(["Three", "One", "Two"]);
+ expect($rootScope.items).toEqualListContent(element);
+
+ $(element).remove();
+ });
+ });
+
+ it('should continue to work after a drag is reverted', function() {
+ inject(function($compile, $rootScope) {
+ var element;
+ element = $compile('')($rootScope);
+ $rootScope.$apply(function() {
+ $rootScope.opts = {
+ placeholder: "sortable-item"
+ };
+ $rootScope.items = ["One", "Two", "Three"];
+ });
+
+ host.append(element);
+
+ var li = element.find(':eq(0)');
+ var dy = (2 + EXTRA_DY_PERCENTAGE) * li.outerHeight();
+ li.simulate('dragAndRevert', { dy: dy });
+ expect($rootScope.items).toEqual(["One", "Two", "Three"]);
+ expect($rootScope.items).toEqualListContent(element);
+
+ li = element.find(':eq(0)');
+ dy = (1 + EXTRA_DY_PERCENTAGE) * li.outerHeight();
+ li.simulate('drag', { dy: dy });
+ expect($rootScope.items).toEqual(["Two", "One", "Three"]);
+ expect($rootScope.items).toEqualListContent(element);
+
+ li = element.find(':eq(1)');
+ dy = (1 + EXTRA_DY_PERCENTAGE) * li.outerHeight();
+ li.simulate('drag', { dy: dy });
+ expect($rootScope.items).toEqual(["Two", "Three", "One"]);
+ expect($rootScope.items).toEqualListContent(element);
+
+ li = element.find(':eq(1)');
+ dy = (1 + EXTRA_DY_PERCENTAGE) * li.outerHeight();
+ li.simulate('drag', { dy: dy });
+ expect($rootScope.items).toEqual(["Two", "One", "Three"]);
+ expect($rootScope.items).toEqualListContent(element);
+
+ $(element).remove();
+ });
+ });
+
+ });
+
+ describe('Multiple sortables related', function() {
+
+ var host;
+
+ beforeEach(inject(function() {
+ host = $('');
+ $('body').append(host);
+ }));
+
+ afterEach(function() {
+ host.remove();
+ host = null;
+ });
+
it('should update model when sorting between sortables', function() {
inject(function($compile, $rootScope) {
var elementTop, elementBottom;
@@ -129,21 +259,101 @@ describe('uiSortable', function() {
host.append(elementTop).append(elementBottom);
- // fails on angular 1.2
var li1 = elementTop.find(':eq(0)');
var li2 = elementBottom.find(':eq(0)');
var dy = EXTRA_DY_PERCENTAGE * li1.outerHeight() + (li2.position().top - li1.position().top);
li1.simulate('drag', { dy: dy });
expect($rootScope.itemsTop).toEqual(["Top Two", "Top Three"]);
expect($rootScope.itemsBottom).toEqual(["Bottom One", "Top One", "Bottom Two", "Bottom Three"]);
+ expect($rootScope.itemsTop).toEqualListContent(elementTop);
+ expect($rootScope.itemsBottom).toEqualListContent(elementBottom);
- // fails on angular 1.2
li1 = elementBottom.find(':eq(1)');
li2 = elementTop.find(':eq(1)');
dy = -EXTRA_DY_PERCENTAGE * li1.outerHeight() - (li1.position().top - li2.position().top);
li1.simulate('drag', { dy: dy });
expect($rootScope.itemsTop).toEqual(["Top Two", "Top One", "Top Three"]);
expect($rootScope.itemsBottom).toEqual(["Bottom One", "Bottom Two", "Bottom Three"]);
+ expect($rootScope.itemsTop).toEqualListContent(elementTop);
+ expect($rootScope.itemsBottom).toEqualListContent(elementBottom);
+
+ $(elementTop).remove();
+ $(elementBottom).remove();
+ });
+ });
+
+ it('should work when "placeholder" option is used', function() {
+ inject(function($compile, $rootScope) {
+ var elementTop, elementBottom;
+ elementTop = $compile('')($rootScope);
+ elementBottom = $compile('')($rootScope);
+ $rootScope.$apply(function() {
+ $rootScope.itemsTop = ["Top One", "Top Two", "Top Three"];
+ $rootScope.itemsBottom = ["Bottom One", "Bottom Two", "Bottom Three"];
+ $rootScope.opts = {
+ placeholder: "sortable-item-placeholder",
+ connectWith: ".cross-sortable"
+ };
+ });
+
+ host.append(elementTop).append(elementBottom);
+
+ var li1 = elementTop.find(':eq(0)');
+ var li2 = elementBottom.find(':eq(0)');
+ var dy = EXTRA_DY_PERCENTAGE * li1.outerHeight() + (li2.position().top - li1.position().top);
+ li1.simulate('drag', { dy: dy });
+ expect($rootScope.itemsTop).toEqual(["Top Two", "Top Three"]);
+ expect($rootScope.itemsBottom).toEqual(["Bottom One", "Top One", "Bottom Two", "Bottom Three"]);
+ expect($rootScope.itemsTop).toEqualListContent(elementTop);
+ expect($rootScope.itemsBottom).toEqualListContent(elementBottom);
+
+ li1 = elementBottom.find(':eq(1)');
+ li2 = elementTop.find(':eq(1)');
+ dy = -EXTRA_DY_PERCENTAGE * li1.outerHeight() - (li1.position().top - li2.position().top);
+ li1.simulate('drag', { dy: dy });
+ expect($rootScope.itemsTop).toEqual(["Top Two", "Top One", "Top Three"]);
+ expect($rootScope.itemsBottom).toEqual(["Bottom One", "Bottom Two", "Bottom Three"]);
+ expect($rootScope.itemsTop).toEqualListContent(elementTop);
+ expect($rootScope.itemsBottom).toEqualListContent(elementBottom);
+
+ $(elementTop).remove();
+ $(elementBottom).remove();
+ });
+ });
+
+ it('should work when "placeholder" option equals the class of items', function() {
+ inject(function($compile, $rootScope) {
+ var elementTop, elementBottom;
+ elementTop = $compile('')($rootScope);
+ elementBottom = $compile('')($rootScope);
+ $rootScope.$apply(function() {
+ $rootScope.itemsTop = ["Top One", "Top Two", "Top Three"];
+ $rootScope.itemsBottom = ["Bottom One", "Bottom Two", "Bottom Three"];
+ $rootScope.opts = {
+ placeholder: "sortable-item",
+ connectWith: ".cross-sortable"
+ };
+ });
+
+ host.append(elementTop).append(elementBottom);
+
+ var li1 = elementTop.find(':eq(0)');
+ var li2 = elementBottom.find(':eq(0)');
+ var dy = EXTRA_DY_PERCENTAGE * li1.outerHeight() + (li2.position().top - li1.position().top);
+ li1.simulate('drag', { dy: dy });
+ expect($rootScope.itemsTop).toEqual(["Top Two", "Top Three"]);
+ expect($rootScope.itemsBottom).toEqual(["Bottom One", "Top One", "Bottom Two", "Bottom Three"]);
+ expect($rootScope.itemsTop).toEqualListContent(elementTop);
+ expect($rootScope.itemsBottom).toEqualListContent(elementBottom);
+
+ li1 = elementBottom.find(':eq(1)');
+ li2 = elementTop.find(':eq(1)');
+ dy = -EXTRA_DY_PERCENTAGE * li1.outerHeight() - (li1.position().top - li2.position().top);
+ li1.simulate('drag', { dy: dy });
+ expect($rootScope.itemsTop).toEqual(["Top Two", "Top One", "Top Three"]);
+ expect($rootScope.itemsBottom).toEqual(["Bottom One", "Bottom Two", "Bottom Three"]);
+ expect($rootScope.itemsTop).toEqualListContent(elementTop);
+ expect($rootScope.itemsBottom).toEqualListContent(elementBottom);
$(elementTop).remove();
$(elementBottom).remove();
@@ -188,16 +398,87 @@ describe('uiSortable', function() {
var dy = (1 + EXTRA_DY_PERCENTAGE) * li.outerHeight();
li.simulate('drag', { dy: dy });
expect($rootScope.items).toEqual(["One", "Two", "Three"]);
+ expect($rootScope.items).toEqualListContent(element);
li = element.find(':eq(0)');
dy = (2 + EXTRA_DY_PERCENTAGE) * li.outerHeight();
li.simulate('drag', { dy: dy });
expect($rootScope.items).toEqual(["Two", "Three", "One"]);
+ expect($rootScope.items).toEqualListContent(element);
+
+ li = element.find(':eq(2)');
+ dy = -(2 + EXTRA_DY_PERCENTAGE) * li.outerHeight();
+ li.simulate('drag', { dy: dy });
+ expect($rootScope.items).toEqual(["One", "Two", "Three"]);
+ expect($rootScope.items).toEqualListContent(element);
$(element).remove();
});
});
+ // it('should cancel sorting of nodes that contain "Two"', function() {
+ // inject(function($compile, $rootScope) {
+ // var elementTop, elementBottom;
+ // elementTop = $compile('')($rootScope);
+ // elementBottom = $compile('')($rootScope);
+ // $rootScope.$apply(function() {
+ // $rootScope.itemsTop = ["Top One", "Top Two", "Top Three"];
+ // $rootScope.itemsBottom = ["Bottom One", "Bottom Two", "Bottom Three"];
+ // $rootScope.opts = {
+ // connectWith: ".cross-sortable",
+ // update: function(e, ui) {
+ // if (ui.item.scope() &&
+ // (typeof ui.item.scope().item === 'string') &&
+ // ui.item.scope().item.indexOf("Two") >= 0) {
+ // ui.item.parent().sortable('cancel');
+ // }
+ // }
+ // };
+ // });
+
+ // host.append(elementTop).append(elementBottom);
+
+ // var li1 = elementTop.find(':eq(1)');
+ // var li2 = elementBottom.find(':eq(0)');
+ // var dy = EXTRA_DY_PERCENTAGE * li1.outerHeight() + (li2.position().top - li1.position().top);
+ // li1.simulate('drag', { dy: dy });
+ // expect($rootScope.itemsTop).toEqual(["Top One", "Top Two", "Top Three"]);
+ // expect($rootScope.itemsBottom).toEqual(["Bottom One", "Bottom Two", "Bottom Three"]);
+ // expect($rootScope.itemsTop).toEqualListContent(elementTop);
+ // expect($rootScope.itemsBottom).toEqualListContent(elementBottom);
+
+ // li1 = elementBottom.find(':eq(1)');
+ // li2 = elementTop.find(':eq(1)');
+ // dy = -EXTRA_DY_PERCENTAGE * li1.outerHeight() - (li1.position().top - li2.position().top);
+ // li1.simulate('drag', { dy: dy });
+ // expect($rootScope.itemsTop).toEqual(["Top One", "Top Two", "Top Three"]);
+ // expect($rootScope.itemsBottom).toEqual(["Bottom One", "Bottom Two", "Bottom Three"]);
+ // expect($rootScope.itemsTop).toEqualListContent(elementTop);
+ // expect($rootScope.itemsBottom).toEqualListContent(elementBottom);
+
+ // li1 = elementTop.find(':eq(0)');
+ // li2 = elementBottom.find(':eq(0)');
+ // dy = EXTRA_DY_PERCENTAGE * li1.outerHeight() + (li2.position().top - li1.position().top);
+ // li1.simulate('drag', { dy: dy });
+ // expect($rootScope.itemsTop).toEqual(["Top Two", "Top Three"]);
+ // expect($rootScope.itemsBottom).toEqual(["Bottom One", "Top One", "Bottom Two", "Bottom Three"]);
+ // expect($rootScope.itemsTop).toEqualListContent(elementTop);
+ // expect($rootScope.itemsBottom).toEqualListContent(elementBottom);
+
+ // li1 = elementBottom.find(':eq(1)');
+ // li2 = elementTop.find(':eq(1)');
+ // dy = -EXTRA_DY_PERCENTAGE * li1.outerHeight() - (li1.position().top - li2.position().top);
+ // li1.simulate('drag', { dy: dy });
+ // expect($rootScope.itemsTop).toEqual(["Top Two", "Top One", "Top Three"]);
+ // expect($rootScope.itemsBottom).toEqual(["Bottom One", "Bottom Two", "Bottom Three"]);
+ // expect($rootScope.itemsTop).toEqualListContent(elementTop);
+ // expect($rootScope.itemsBottom).toEqualListContent(elementBottom);
+
+ // $(elementTop).remove();
+ // $(elementBottom).remove();
+ // });
+ // });
+
it('should update model from update() callback', function() {
inject(function($compile, $rootScope) {
var element, logsElement;
@@ -220,7 +501,8 @@ describe('uiSortable', function() {
li.simulate('drag', { dy: dy });
expect($rootScope.items).toEqual(["One", "Three", "Two"]);
expect($rootScope.logs).toEqual(["Moved element Two"]);
- expect(logsElement.find('li').html()).toEqual("Moved element Two");
+ expect($rootScope.items).toEqualListContent(element);
+ expect($rootScope.logs).toEqualListContent(logsElement);
$(element).remove();
$(logsElement).remove();
@@ -250,7 +532,8 @@ describe('uiSortable', function() {
li.simulate('drag', { dy: dy });
expect($rootScope.items).toEqual(["One", "Three", "Two"]);
expect($rootScope.logs).toEqual(["Moved element Two"]);
- expect(logsElement.find('li').html()).toEqual("Moved element Two");
+ expect($rootScope.items).toEqualListContent(element);
+ expect($rootScope.logs).toEqualListContent(logsElement);
$(element).remove();
$(logsElement).remove();
diff --git a/test/test.conf.js b/test/test.conf.js
index 492223a..8eb9a9e 100644
--- a/test/test.conf.js
+++ b/test/test.conf.js
@@ -4,6 +4,7 @@ files = [
JASMINE_ADAPTER,
'bower_components/jquery/jquery.js',
'bower_components/jquery-simulate/jquery.simulate.js',
+ 'test/libs/jquery.simulate.dragandrevert.js',
'bower_components/jquery-ui/ui/jquery-ui.js',
'bower_components/angular/angular.js',
'bower_components/angular-mocks/angular-mocks.js',
@@ -11,4 +12,15 @@ files = [
'test/*.spec.js'
];
singleRun = true;
-browsers = [ 'Chrome' ];
\ No newline at end of file
+autoWatch = false;
+browsers = [ 'Chrome' ];
+reporters = [ 'dots' ];
+
+if (singleRun) {
+ reporters.push('coverage');
+ preprocessors = { '**/src/*.js': 'coverage' };
+ coverageReporter = {
+ type : 'html',
+ dir : 'coverage/'
+ };
+}