aboutsummaryrefslogtreecommitdiffstats
path: root/library/Sortable/tests
diff options
context:
space:
mode:
authorMario <mario@mariovavti.com>2021-08-03 07:12:35 +0000
committerMario <mario@mariovavti.com>2021-08-03 07:12:35 +0000
commitcddc0217724f1a7661014d50e4c940e623a0c2dc (patch)
treef24595d659adbb7d1e5d2e8e6dcd829b093887bb /library/Sortable/tests
parent571bae9d1c07bb08270163a314c91c138b42e62f (diff)
downloadvolse-hubzilla-cddc0217724f1a7661014d50e4c940e623a0c2dc.tar.gz
volse-hubzilla-cddc0217724f1a7661014d50e4c940e623a0c2dc.tar.bz2
volse-hubzilla-cddc0217724f1a7661014d50e4c940e623a0c2dc.zip
Apps drag and drop feature
Diffstat (limited to 'library/Sortable/tests')
-rw-r--r--library/Sortable/tests/Sortable.compat.test.js39
-rw-r--r--library/Sortable/tests/Sortable.test.js386
-rw-r--r--library/Sortable/tests/dual-list.html34
-rw-r--r--library/Sortable/tests/empty-list.html30
-rw-r--r--library/Sortable/tests/filter.html27
-rw-r--r--library/Sortable/tests/handles.html27
-rw-r--r--library/Sortable/tests/nested.html67
-rw-r--r--library/Sortable/tests/single-list.html25
-rw-r--r--library/Sortable/tests/style.css18
9 files changed, 653 insertions, 0 deletions
diff --git a/library/Sortable/tests/Sortable.compat.test.js b/library/Sortable/tests/Sortable.compat.test.js
new file mode 100644
index 000000000..7c00396b9
--- /dev/null
+++ b/library/Sortable/tests/Sortable.compat.test.js
@@ -0,0 +1,39 @@
+import { Selector } from 'testcafe';
+
+
+fixture `Simple Sorting`
+ .page `./single-list.html`;
+
+let list1 = Selector('#list1');
+
+test('Sort down list', async browser => {
+ const dragStartPosition = list1.child(0);
+ const dragEl = await dragStartPosition();
+ const dragEndPosition = list1.child(2);
+ const targetStartPosition = list1.child(2);
+ const target = await targetStartPosition();
+ const targetEndPosition = list1.child(1);
+
+ await browser
+ .expect(dragStartPosition.innerText).eql(dragEl.innerText)
+ .expect(targetStartPosition.innerText).eql(target.innerText)
+ .dragToElement(dragEl, target)
+ .expect(dragEndPosition.innerText).eql(dragEl.innerText)
+ .expect(targetEndPosition.innerText).eql(target.innerText);
+});
+
+test('Sort up list', async browser => {
+ const dragStartPosition = list1.child(2);
+ const dragEl = await dragStartPosition();
+ const dragEndPosition = list1.child(0);
+ const targetStartPosition = list1.child(0);
+ const target = await targetStartPosition();
+ const targetEndPosition = list1.child(1);
+
+ await browser
+ .expect(dragStartPosition.innerText).eql(dragEl.innerText)
+ .expect(targetStartPosition.innerText).eql(target.innerText)
+ .dragToElement(dragEl, target)
+ .expect(dragEndPosition.innerText).eql(dragEl.innerText)
+ .expect(targetEndPosition.innerText).eql(target.innerText);
+});
diff --git a/library/Sortable/tests/Sortable.test.js b/library/Sortable/tests/Sortable.test.js
new file mode 100644
index 000000000..59ce11f46
--- /dev/null
+++ b/library/Sortable/tests/Sortable.test.js
@@ -0,0 +1,386 @@
+import { Selector } from 'testcafe';
+const itemHeight = 54; // px
+const leeway = 1;
+
+
+fixture `Simple Sorting`
+ .page `./single-list.html`;
+
+let list1 = Selector('#list1');
+
+test('Sort down list', async browser => {
+ const dragStartPosition = list1.child(0);
+ const dragEl = await dragStartPosition();
+ const dragEndPosition = list1.child(2);
+ const targetStartPosition = list1.child(2);
+ const target = await targetStartPosition();
+ const targetEndPosition = list1.child(1);
+
+ await browser
+ .expect(dragStartPosition.innerText).eql(dragEl.innerText)
+ .expect(targetStartPosition.innerText).eql(target.innerText)
+ .dragToElement(dragEl, target)
+ .expect(dragEndPosition.innerText).eql(dragEl.innerText)
+ .expect(targetEndPosition.innerText).eql(target.innerText);
+});
+
+test('Sort up list', async browser => {
+ const dragStartPosition = list1.child(2);
+ const dragEl = await dragStartPosition();
+ const dragEndPosition = list1.child(0);
+ const targetStartPosition = list1.child(0);
+ const target = await targetStartPosition();
+ const targetEndPosition = list1.child(1);
+
+ await browser
+ .expect(dragStartPosition.innerText).eql(dragEl.innerText)
+ .expect(targetStartPosition.innerText).eql(target.innerText)
+ .dragToElement(dragEl, target)
+ .expect(dragEndPosition.innerText).eql(dragEl.innerText)
+ .expect(targetEndPosition.innerText).eql(target.innerText);
+});
+
+test('Swap threshold', async browser => {
+ const dragStartPosition = list1.child(0);
+ const dragEl = await dragStartPosition();
+ const dragEndPosition = list1.child(1);
+ const targetStartPosition = list1.child(1);
+ const target = await targetStartPosition();
+ const targetEndPosition = list1.child(0);
+
+ await browser.eval(() => {
+ Sortable.get(document.getElementById('list1')).option('swapThreshold', 0.6);
+ });
+
+
+ await browser
+ .expect(dragStartPosition.innerText).eql(dragEl.innerText)
+ .expect(targetStartPosition.innerText).eql(target.innerText)
+ .dragToElement(dragEl, target, {
+ destinationOffsetY: Math.round(itemHeight / 2 * 0.4 - leeway)
+ })
+ .expect(dragStartPosition.innerText).eql(dragEl.innerText)
+ .expect(targetStartPosition.innerText).eql(target.innerText)
+ .dragToElement(dragEl, target, {
+ destinationOffsetY: Math.round(itemHeight / 2 * 0.4 + leeway)
+ })
+ .expect(dragEndPosition.innerText).eql(dragEl.innerText)
+ .expect(targetEndPosition.innerText).eql(target.innerText);
+});
+
+test('Invert swap', async browser => {
+ const dragStartPosition = list1.child(0);
+ const dragEl = await dragStartPosition();
+ const dragEndPosition = list1.child(1);
+ const targetStartPosition = list1.child(1);
+ const target = await targetStartPosition();
+ const targetEndPosition = list1.child(0);
+
+ await browser.eval(() => {
+ Sortable.get(document.getElementById('list1')).option('invertSwap', true);
+ });
+
+
+ await browser
+ .expect(dragStartPosition.innerText).eql(dragEl.innerText)
+ .expect(targetStartPosition.innerText).eql(target.innerText)
+ .dragToElement(dragEl, target, {
+ destinationOffsetY: Math.round(itemHeight / 2 - leeway)
+ })
+ .expect(dragStartPosition.innerText).eql(dragEl.innerText)
+ .expect(targetStartPosition.innerText).eql(target.innerText)
+ .dragToElement(dragEl, target, {
+ destinationOffsetY: Math.round(itemHeight / 2 + leeway)
+ })
+ .expect(dragEndPosition.innerText).eql(dragEl.innerText)
+ .expect(targetEndPosition.innerText).eql(target.innerText);
+});
+
+
+test('Inverted swap threshold', async browser => {
+ const dragStartPosition = list1.child(0);
+ const dragEl = await dragStartPosition();
+ const dragEndPosition = list1.child(1);
+ const targetStartPosition = list1.child(1);
+ const target = await targetStartPosition();
+ const targetEndPosition = list1.child(0);
+
+ await browser.eval(() => {
+ Sortable.get(document.getElementById('list1')).option('invertSwap', true);
+ Sortable.get(document.getElementById('list1')).option('invertedSwapThreshold', 0.5);
+ });
+
+
+ await browser
+ .expect(dragStartPosition.innerText).eql(dragEl.innerText)
+ .expect(targetStartPosition.innerText).eql(target.innerText)
+ .dragToElement(dragEl, target, {
+ destinationOffsetY: Math.round(itemHeight - (itemHeight / 2 * 0.5) - leeway)
+ })
+ .expect(dragStartPosition.innerText).eql(dragEl.innerText)
+ .expect(targetStartPosition.innerText).eql(target.innerText)
+ .dragToElement(dragEl, target, {
+ destinationOffsetY: Math.round(itemHeight - (itemHeight / 2 * 0.5) + leeway)
+ })
+ .expect(dragEndPosition.innerText).eql(dragEl.innerText)
+ .expect(targetEndPosition.innerText).eql(target.innerText);
+});
+
+
+fixture `Grouping`
+ .page `./dual-list.html`;
+
+let list2 = Selector('#list2');
+
+test('Move to list of the same group', async browser => {
+ const dragStartPosition = list1.child(0);
+ const dragEl = await dragStartPosition();
+ const dragEndPosition = list2.child(0);
+ const targetStartPosition = list2.child(0);
+ const target = await targetStartPosition();
+ const targetEndPosition = list2.child(1);
+
+ await browser.eval(() => {
+ Sortable.get(document.getElementById('list2')).option('group', 'shared');
+ });
+
+ await browser
+ .expect(dragStartPosition.innerText).eql(dragEl.innerText)
+ .expect(targetStartPosition.innerText).eql(target.innerText)
+ .dragToElement(dragEl, target, { offsetY: 0, destinationOffsetY: 0 })
+ .expect(dragEndPosition.innerText).eql(dragEl.innerText)
+ .expect(targetEndPosition.innerText).eql(target.innerText);
+});
+
+
+test('Do not move to list of different group', async browser => {
+ const dragStartPosition = list1.child(0);
+ const dragEl = await dragStartPosition();
+ const targetStartPosition = list2.child(0);
+ const target = await targetStartPosition();
+
+ await browser.eval(() => {
+ Sortable.get(document.getElementById('list2')).option('group', null);
+ });
+
+ await browser
+ .expect(dragStartPosition.innerText).eql(dragEl.innerText)
+ .expect(targetStartPosition.innerText).eql(target.innerText)
+ .dragToElement(dragEl, target, { offsetY: 0, destinationOffsetY: 0 })
+ .expect(dragStartPosition.innerText).eql(dragEl.innerText)
+ .expect(targetStartPosition.innerText).eql(target.innerText);
+});
+
+
+test('Move to list with put:true', async browser => {
+ // Should allow insert, since pull defaults to `true`
+ const dragStartPosition = list1.child(0);
+ const dragEl = await dragStartPosition();
+ const dragEndPosition = list2.child(0);
+ const targetStartPosition = list2.child(0);
+ const target = await targetStartPosition();
+ const targetEndPosition = list2.child(1);
+
+ await browser.eval(() => {
+ Sortable.get(document.getElementById('list2')).option('group', { put: true });
+ });
+
+ await browser
+ .expect(dragStartPosition.innerText).eql(dragEl.innerText)
+ .expect(targetStartPosition.innerText).eql(target.innerText)
+ .dragToElement(dragEl, target, { offsetY: 0, destinationOffsetY: 0 })
+ .expect(dragEndPosition.innerText).eql(dragEl.innerText)
+ .expect(targetEndPosition.innerText).eql(target.innerText);
+});
+
+test('Do not move from list with pull:false', async browser => {
+ // Should not allow insert, since put defaults to `false`
+ const dragStartPosition = list1.child(0);
+ const dragEl = await dragStartPosition();
+ const targetStartPosition = list2.child(0);
+ const target = await targetStartPosition();
+
+ await browser.eval(() => {
+ Sortable.get(document.getElementById('list1')).option('group', { pull: false });
+ });
+
+ await browser
+ .expect(dragStartPosition.innerText).eql(dragEl.innerText)
+ .expect(targetStartPosition.innerText).eql(target.innerText)
+ .dragToElement(dragEl, target, { offsetY: 0, destinationOffsetY: 0 })
+ .expect(dragStartPosition.innerText).eql(dragEl.innerText)
+ .expect(targetStartPosition.innerText).eql(target.innerText);
+});
+
+test('Clone element if pull:"clone"', async browser => {
+ const dragStartPosition = list1.child(0);
+ const dragEl = await dragStartPosition();
+ const dragEndPosition = list2.child(0);
+ const targetStartPosition = list2.child(0);
+ const target = await targetStartPosition();
+ const targetEndPosition = list2.child(1);
+
+ await browser.eval(() => {
+ Sortable.get(document.getElementById('list1')).option('group', { pull: 'clone' });
+ Sortable.get(document.getElementById('list2')).option('group', { put: true });
+ });
+
+ await browser
+ .expect(dragStartPosition.innerText).eql(dragEl.innerText)
+ .expect(targetStartPosition.innerText).eql(target.innerText)
+ .dragToElement(dragEl, target, { offsetY: 0, destinationOffsetY: 0 })
+ .expect(dragStartPosition.innerText).eql(dragEl.innerText) // clone check
+ .expect(dragEndPosition.innerText).eql(dragEl.innerText)
+ .expect(targetEndPosition.innerText).eql(target.innerText);
+});
+
+
+
+fixture `Handles`
+ .page `./handles.html`;
+
+test('Do not allow dragging not using handle', async browser => {
+ const dragStartPosition = list1.child(0);
+ const dragEl = await dragStartPosition();
+ const targetStartPosition = list1.child(1);
+ const target = await targetStartPosition();
+
+ await browser
+ .expect(dragStartPosition.innerText).eql(dragEl.innerText)
+ .expect(targetStartPosition.innerText).eql(target.innerText)
+ .dragToElement(dragEl, target)
+ .expect(dragStartPosition.innerText).eql(dragEl.innerText)
+ .expect(targetStartPosition.innerText).eql(target.innerText);
+});
+
+
+test('Allow dragging using handle', async browser => {
+ const dragStartPosition = list1.child(0);
+ const dragEl = await dragStartPosition();
+ const dragEndPosition = list1.child(1);
+ const targetStartPosition = list1.child(1);
+ const target = await targetStartPosition();
+ const targetEndPosition = list1.child(0);
+
+ await browser
+ .expect(dragStartPosition.innerText).eql(dragEl.innerText)
+ .expect(targetStartPosition.innerText).eql(target.innerText)
+ .dragToElement(await dragStartPosition.child('.handle'), target)
+ .expect(dragEndPosition.innerText).eql(dragEl.innerText)
+ .expect(targetEndPosition.innerText).eql(target.innerText);
+});
+
+fixture `Filter`
+ .page `./filter.html`;
+
+test('Do not allow dragging of filtered element', async browser => {
+ const dragStartPosition = list1.child('.filtered');
+ const dragEl = await dragStartPosition();
+ const targetStartPosition = dragStartPosition.nextSibling(1);
+ const target = await targetStartPosition();
+
+ await browser
+ .expect(dragStartPosition.innerText).eql(dragEl.innerText)
+ .expect(targetStartPosition.innerText).eql(target.innerText)
+ .dragToElement(dragEl, target)
+ .expect(dragStartPosition.innerText).eql(dragEl.innerText)
+ .expect(targetStartPosition.innerText).eql(target.innerText);
+});
+
+
+test('Allow dragging of non-filtered element', async browser => {
+ const dragStartPosition = list1.child(':not(.filtered)');
+ const dragEl = await dragStartPosition();
+ const dragEndPosition = dragStartPosition.nextSibling(1);
+ const targetStartPosition = dragStartPosition.nextSibling(1);
+ const target = await targetStartPosition();
+ const targetEndPosition = dragStartPosition.nextSibling(0);
+
+ await browser
+ .expect(dragStartPosition.innerText).eql(dragEl.innerText)
+ .expect(targetStartPosition.innerText).eql(target.innerText)
+ .dragToElement(dragEl, target)
+ .expect(dragEndPosition.innerText).eql(dragEl.innerText)
+ .expect(targetEndPosition.innerText).eql(target.innerText);
+});
+
+
+
+fixture `Nested`
+ .page `./nested.html`;
+
+let list1n1 = Selector('.n1');
+let list1n2 = Selector('.n2');
+let list2n1 = Selector('.n1:nth-of-type(2)');
+
+test('Dragging from level 1 to level 0', async browser => {
+ const dragStartPosition = list1n1.child(0);
+ const dragEl = await dragStartPosition();
+ const dragEndPosition = list1.child(2);
+ const targetStartPosition = list1.child(2);
+ const target = await targetStartPosition();
+ const targetEndPosition = list1.child(3);
+
+ await browser
+ .expect(dragStartPosition.innerText).eql(dragEl.innerText)
+ .expect(targetStartPosition.innerText).eql(target.innerText)
+ .dragToElement(dragEl, target, { destinationOffsetY: 0 })
+ .expect(dragEndPosition.innerText).eql(dragEl.innerText)
+ .expect(targetEndPosition.innerText).eql(target.innerText);
+});
+
+
+test('Dragging from level 0 to level 2', async browser => {
+ const dragStartPosition = list1.child(1);
+ const dragEl = await dragStartPosition();
+ const dragEndPosition = list1n2.child(2);
+ const targetStartPosition = list1n2.child(2);
+ const target = await targetStartPosition();
+ const targetEndPosition = list1n2.child(3);
+
+ await browser
+ .expect(dragStartPosition.innerText).eql(dragEl.innerText)
+ .expect(targetStartPosition.innerText).eql(target.innerText)
+ .dragToElement(dragEl, target, { destinationOffsetY: 0 })
+ .expect(dragEndPosition.innerText).eql(dragEl.innerText)
+ .expect(targetEndPosition.innerText).eql(target.innerText);
+});
+
+
+fixture `Empty Insert`
+ .page `./empty-list.html`;
+
+test('Insert into empty list if within emptyInsertThreshold', async browser => {
+ const threshold = await browser.eval(() => Sortable.get(document.getElementById('list2')).option('emptyInsertThreshold'));
+ const dragStartPosition = list1.child(0);
+ const dragEl = await dragStartPosition();
+ const dragEndPosition = list2.child(0);
+ // Must use rects since testcafe won't drag to element that is "not visible"
+ const dragRect = dragEl.boundingClientRect;
+ const list2Rect = await list2.boundingClientRect;
+
+
+ await browser
+ .expect(dragStartPosition.innerText).eql(dragEl.innerText)
+ .drag(dragEl, Math.round(list2Rect.left - dragRect.left) - (threshold - 1), -(threshold - 1), {
+ offsetY: 0,
+ offsetX: 0
+ })
+ .expect(dragEndPosition.innerText).eql(dragEl.innerText);
+});
+
+test('Do not insert into empty list if outside emptyInsertThreshold', async browser => {
+ const threshold = await browser.eval(() => Sortable.get(document.getElementById('list2')).option('emptyInsertThreshold'));
+ const dragStartPosition = list1.child(0);
+ const dragEl = await dragStartPosition();
+ const dragRect = dragEl.boundingClientRect;
+ const list2Rect = await list2.boundingClientRect;
+
+ await browser
+ .expect(dragStartPosition.innerText).eql(dragEl.innerText)
+ .drag(dragEl, Math.round(list2Rect.left - dragRect.left) - (threshold + 1), -(threshold + 1), {
+ offsetY: 0,
+ offsetX: 0
+ })
+ .expect(dragStartPosition.innerText).eql(dragEl.innerText);
+});
diff --git a/library/Sortable/tests/dual-list.html b/library/Sortable/tests/dual-list.html
new file mode 100644
index 000000000..6d7e15b9b
--- /dev/null
+++ b/library/Sortable/tests/dual-list.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title></title>
+ <link rel="stylesheet" type="text/css" href="./style.css">
+</head>
+<body>
+
+<div class="list half" id="list1">
+ <div>Item 1.1</div>
+ <div>Item 1.2</div>
+ <div>Item 1.3</div>
+ <div>Item 1.4</div>
+ <div>Item 1.5</div>
+</div>
+
+<div class="list half" id="list2">
+ <div>Item 2.1</div>
+ <div>Item 2.2</div>
+ <div>Item 2.3</div>
+ <div>Item 2.4</div>
+ <div>Item 2.5</div>
+</div>
+
+
+<script src="../Sortable.js"></script>
+
+<script type="text/javascript">
+ new Sortable(document.getElementById('list1'), { group: 'shared' });
+ new Sortable(document.getElementById('list2'), { });
+</script>
+
+</body>
+</html>
diff --git a/library/Sortable/tests/empty-list.html b/library/Sortable/tests/empty-list.html
new file mode 100644
index 000000000..3e2064154
--- /dev/null
+++ b/library/Sortable/tests/empty-list.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title></title>
+ <link rel="stylesheet" type="text/css" href="./style.css">
+</head>
+<body>
+
+<div class="list half" id="list1">
+ <div>Item 1.1</div>
+ <div>Item 1.2</div>
+ <div>Item 1.3</div>
+ <div>Item 1.4</div>
+ <div>Item 1.5</div>
+</div>
+
+<div class="list half" id="list2">
+
+</div>
+
+
+<script src="../Sortable.js"></script>
+
+<script type="text/javascript">
+ new Sortable(document.getElementById('list1'), { group: 'shared' });
+ new Sortable(document.getElementById('list2'), { group: 'shared' });
+</script>
+
+</body>
+</html>
diff --git a/library/Sortable/tests/filter.html b/library/Sortable/tests/filter.html
new file mode 100644
index 000000000..49b40dd1d
--- /dev/null
+++ b/library/Sortable/tests/filter.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title></title>
+ <link rel="stylesheet" type="text/css" href="./style.css">
+</head>
+<body>
+
+<div class="list" id="list1">
+ <div>Item 1.1</div>
+ <div>Item 1.2</div>
+ <div class="filtered">Item 1.3</div>
+ <div>Item 1.4</div>
+ <div>Item 1.5</div>
+</div>
+
+
+
+
+<script src="../Sortable.js"></script>
+
+<script type="text/javascript">
+ new Sortable(document.getElementById('list1'), { filter: '.filtered' });
+</script>
+
+</body>
+</html>
diff --git a/library/Sortable/tests/handles.html b/library/Sortable/tests/handles.html
new file mode 100644
index 000000000..037e272d0
--- /dev/null
+++ b/library/Sortable/tests/handles.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title></title>
+ <link rel="stylesheet" type="text/css" href="./style.css">
+</head>
+<body>
+
+<div class="list" id="list1">
+ <div><span class="handle">::</span>Item 1.1</div>
+ <div><span class="handle">::</span>Item 1.2</div>
+ <div><span class="handle">::</span>Item 1.3</div>
+ <div><span class="handle">::</span>Item 1.4</div>
+ <div><span class="handle">::</span>Item 1.5</div>
+</div>
+
+
+
+
+<script src="../Sortable.js"></script>
+
+<script type="text/javascript">
+ new Sortable(document.getElementById('list1'), { handle: '.handle' });
+</script>
+
+</body>
+</html>
diff --git a/library/Sortable/tests/nested.html b/library/Sortable/tests/nested.html
new file mode 100644
index 000000000..f9dd15778
--- /dev/null
+++ b/library/Sortable/tests/nested.html
@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title></title>
+ <link rel="stylesheet" type="text/css" href="./style.css">
+</head>
+<body>
+
+<div id="list1" class="list">
+ <div>Item 1.1
+ <div class="list n1">
+ <div>Item 2.1</div>
+ <div>Item 2.2
+ <div class="list n2">
+ <div>Item 3.1</div>
+ <div>Item 3.2</div>
+ <div>Item 3.3</div>
+ <div>Item 3.4</div>
+ </div>
+ </div>
+ <div>Item 2.3</div>
+ <div>Item 2.4</div>
+ </div>
+ </div>
+ <div>Item 1.2</div>
+ <div>Item 1.3</div>
+ <div>Item 1.4
+ <div class="list n1">
+ <div>Item 2.1</div>
+ <div>Item 2.2</div>
+ <div>Item 2.3</div>
+ <div>Item 2.4</div>
+ </div>
+ </div>
+ <div>Item 1.5</div>
+</div>
+
+<style>
+ .n1 > div {
+ background-color: lightblue;
+ }
+
+ .n2 > div {
+ background-color: lightgreen
+ }
+
+ .list {
+ padding: 50px;
+ }
+
+</style>
+
+<script src="../Sortable.js"></script>
+
+<script type="text/javascript">
+ var elements = document.getElementsByClassName('list');
+
+ for (var i = 0; i < elements.length; i++) {
+ new Sortable(elements[i], {
+ group: 'shared',
+ invertSwap: true
+ });
+ }
+</script>
+
+</body>
+</html>
diff --git a/library/Sortable/tests/single-list.html b/library/Sortable/tests/single-list.html
new file mode 100644
index 000000000..30b984b0d
--- /dev/null
+++ b/library/Sortable/tests/single-list.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title></title>
+ <link rel="stylesheet" type="text/css" href="./style.css">
+</head>
+<body>
+
+<div class="list" id="list1">
+ <div>Item 1.1</div>
+ <div>Item 1.2</div>
+ <div>Item 1.3</div>
+ <div>Item 1.4</div>
+ <div>Item 1.5</div>
+</div>
+
+
+<script src="../Sortable.js"></script>
+
+<script type="text/javascript">
+ new Sortable(document.getElementById('list1'));
+</script>
+
+</body>
+</html>
diff --git a/library/Sortable/tests/style.css b/library/Sortable/tests/style.css
new file mode 100644
index 000000000..02269cf21
--- /dev/null
+++ b/library/Sortable/tests/style.css
@@ -0,0 +1,18 @@
+.list > div {
+ min-height: 50px;
+ border-style: solid;
+ border-width: 2px;
+ text-align: center;
+ line-height: 50px;
+ font-size: 20px;
+ font-family: Helvetica;
+}
+
+
+.half {
+ display: inline-block;
+ width: 49%;
+ padding: 0;
+ margin: 0;
+ vertical-align: top;
+}