Skip to content

Commit ac9d344

Browse files
crisbetojosephperrott
authored andcommitted
test(drag-drop): add missing unit tests (#12219)
1 parent 40c3a47 commit ac9d344

File tree

3 files changed

+231
-33
lines changed

3 files changed

+231
-33
lines changed
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import {moveItemInArray, transferArrayItem} from './drag-utils';
2+
3+
describe('dragging utilities', () => {
4+
describe('moveItemInArray', () => {
5+
let array: number[];
6+
7+
beforeEach(() => array = [0, 1, 2, 3]);
8+
9+
it('should be able to move an item inside an array', () => {
10+
moveItemInArray(array, 1, 3);
11+
expect(array).toEqual([0, 2, 3, 1]);
12+
});
13+
14+
it('should not do anything if the index is the same', () => {
15+
moveItemInArray(array, 2, 2);
16+
expect(array).toEqual([0, 1, 2, 3]);
17+
});
18+
19+
it('should handle an index greater than the length', () => {
20+
moveItemInArray(array, 0, 7);
21+
expect(array).toEqual([1, 2, 3, 0]);
22+
});
23+
24+
it('should handle an index less than zero', () => {
25+
moveItemInArray(array, 3, -1);
26+
expect(array).toEqual([3, 0, 1, 2]);
27+
});
28+
});
29+
30+
describe('transferArrayItem', () => {
31+
it('should be able to move an item from one array to another', () => {
32+
const a = [0, 1, 2];
33+
const b = [3, 4, 5];
34+
35+
transferArrayItem(a, b, 1, 2);
36+
expect(a).toEqual([0, 2]);
37+
expect(b).toEqual([3, 4, 1, 5]);
38+
});
39+
40+
it('should handle an index greater than the target array length', () => {
41+
const a = [0, 1, 2];
42+
const b = [3, 4, 5];
43+
44+
transferArrayItem(a, b, 0, 7);
45+
46+
expect(a).toEqual([1, 2]);
47+
expect(b).toEqual([3, 4, 5, 0]);
48+
});
49+
50+
it('should handle an index less than zero', () => {
51+
const a = [0, 1, 2];
52+
const b = [3, 4, 5];
53+
54+
transferArrayItem(a, b, 2, -1);
55+
expect(a).toEqual([0, 1]);
56+
expect(b).toEqual([2, 3, 4, 5]);
57+
});
58+
59+
it('should not do anything if the source array is empty', () => {
60+
const a = [];
61+
const b = [3, 4, 5];
62+
63+
transferArrayItem(a, b, 0, 0);
64+
expect(a).toEqual([]);
65+
expect(b).toEqual([3, 4, 5]);
66+
});
67+
68+
});
69+
});

src/cdk-experimental/drag-drop/drag-utils.ts

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,21 @@
1313
* @param toIndex Index to which the item should be moved.
1414
*/
1515
export function moveItemInArray<T = any>(array: T[], fromIndex: number, toIndex: number): void {
16-
if (fromIndex === toIndex) {
16+
const from = clamp(fromIndex, array.length - 1);
17+
const to = clamp(toIndex, array.length - 1);
18+
19+
if (from === to) {
1720
return;
1821
}
1922

20-
const target = array[fromIndex];
21-
const delta = toIndex < fromIndex ? -1 : 1;
23+
const target = array[from];
24+
const delta = to < from ? -1 : 1;
2225

23-
for (let i = fromIndex; i !== toIndex; i += delta) {
26+
for (let i = from; i !== to; i += delta) {
2427
array[i] = array[i + delta];
2528
}
2629

27-
array[toIndex] = target;
30+
array[to] = target;
2831
}
2932

3033

@@ -39,5 +42,16 @@ export function transferArrayItem<T = any>(currentArray: T[],
3942
targetArray: T[],
4043
currentIndex: number,
4144
targetIndex: number): void {
42-
targetArray.splice(targetIndex, 0, currentArray.splice(currentIndex, 1)[0]);
45+
46+
const from = clamp(currentIndex, currentArray.length - 1);
47+
const to = clamp(targetIndex, targetArray.length);
48+
49+
if (currentArray.length) {
50+
targetArray.splice(to, 0, currentArray.splice(from, 1)[0]);
51+
}
52+
}
53+
54+
/** Clamps a number between zero and a maximum. */
55+
function clamp(value: number, max: number): number {
56+
return Math.max(0, Math.min(max, value));
4357
}

src/cdk-experimental/drag-drop/drag.spec.ts

Lines changed: 142 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import {dispatchMouseEvent, dispatchTouchEvent} from '@angular/cdk/testing';
1515
import {Directionality} from '@angular/cdk/bidi';
1616
import {CdkDrag} from './drag';
1717
import {CdkDragDrop} from './drag-events';
18-
import {moveItemInArray, transferArrayItem} from './drag-utils';
18+
import {moveItemInArray} from './drag-utils';
1919
import {CdkDrop} from './drop';
2020
import {CdkDragHandle} from './drag-handle';
2121

@@ -460,7 +460,9 @@ describe('CdkDrag', () => {
460460
it('should move the placeholder as an item is being sorted down', fakeAsync(() => {
461461
const fixture = createComponent(DraggableInDropZone);
462462
fixture.detectChanges();
463-
assertDownwardSorting(fixture);
463+
assertDownwardSorting(fixture, fixture.componentInstance.dragItems.map(item => {
464+
return item.element.nativeElement;
465+
}));
464466
}));
465467

466468
it('should move the placeholder as an item is being sorted down on a scrolled page',
@@ -470,14 +472,18 @@ describe('CdkDrag', () => {
470472
const cleanup = makePageScrollable();
471473

472474
scrollTo(0, 500);
473-
assertDownwardSorting(fixture);
475+
assertDownwardSorting(fixture, fixture.componentInstance.dragItems.map(item => {
476+
return item.element.nativeElement;
477+
}));
474478
cleanup();
475479
}));
476480

477481
it('should move the placeholder as an item is being sorted up', fakeAsync(() => {
478482
const fixture = createComponent(DraggableInDropZone);
479483
fixture.detectChanges();
480-
assertUpwardSorting(fixture);
484+
assertUpwardSorting(fixture, fixture.componentInstance.dragItems.map(item => {
485+
return item.element.nativeElement;
486+
}));
481487
}));
482488

483489
it('should move the placeholder as an item is being sorted up on a scrolled page',
@@ -487,7 +493,9 @@ describe('CdkDrag', () => {
487493
const cleanup = makePageScrollable();
488494

489495
scrollTo(0, 500);
490-
assertUpwardSorting(fixture);
496+
assertUpwardSorting(fixture, fixture.componentInstance.dragItems.map(item => {
497+
return item.element.nativeElement;
498+
}));
491499
cleanup();
492500
}));
493501

@@ -617,9 +625,127 @@ describe('CdkDrag', () => {
617625
const fixture = createComponent(ConnectedDropZones);
618626
fixture.detectChanges();
619627

620-
// TODO
621-
// console.log(fixture.componentInstance.groupedDragItems.map(d => d.length));
628+
const groups = fixture.componentInstance.groupedDragItems;
629+
const item = groups[0][1];
630+
const targetRect = groups[1][2].element.nativeElement.getBoundingClientRect();
631+
632+
dragElementViaMouse(fixture, item.element.nativeElement,
633+
targetRect.left + 1, targetRect.top + 1);
634+
flush();
635+
fixture.detectChanges();
636+
637+
expect(fixture.componentInstance.droppedSpy).toHaveBeenCalledTimes(1);
638+
639+
const event = fixture.componentInstance.droppedSpy.calls.mostRecent().args[0];
640+
641+
expect(event).toEqual({
642+
previousIndex: 1,
643+
currentIndex: 3,
644+
item,
645+
container: fixture.componentInstance.dropInstances.toArray()[1],
646+
previousContainer: fixture.componentInstance.dropInstances.first
647+
});
622648
}));
649+
650+
it('should be able to move the element over a new container and return it', fakeAsync(() => {
651+
const fixture = createComponent(ConnectedDropZones);
652+
fixture.detectChanges();
653+
654+
const groups = fixture.componentInstance.groupedDragItems;
655+
const dropZones = fixture.componentInstance.dropInstances.map(d => d.element.nativeElement);
656+
const item = groups[0][1];
657+
const initialRect = item.element.nativeElement.getBoundingClientRect();
658+
const targetRect = groups[1][2].element.nativeElement.getBoundingClientRect();
659+
660+
expect(dropZones[0].contains(item.element.nativeElement)).toBe(true);
661+
dispatchMouseEvent(item.element.nativeElement, 'mousedown');
662+
fixture.detectChanges();
663+
664+
const placeholder = dropZones[0].querySelector('.cdk-drag-placeholder')!;
665+
666+
expect(placeholder).toBeTruthy();
667+
668+
dispatchMouseEvent(document, 'mousemove', targetRect.left + 1, targetRect.top + 1);
669+
fixture.detectChanges();
670+
671+
expect(dropZones[1].contains(placeholder))
672+
.toBe(true, 'Expected placeholder to be inside second container.');
673+
674+
dispatchMouseEvent(document, 'mousemove', initialRect.left + 1, initialRect.top + 1);
675+
fixture.detectChanges();
676+
677+
expect(dropZones[0].contains(placeholder))
678+
.toBe(true, 'Expected placeholder to be inside first container.');
679+
680+
dispatchMouseEvent(document, 'mouseup');
681+
fixture.detectChanges();
682+
683+
expect(fixture.componentInstance.droppedSpy).not.toHaveBeenCalled();
684+
}));
685+
686+
it('should transfer the DOM element from one drop zone to another', fakeAsync(() => {
687+
const fixture = createComponent(ConnectedDropZones);
688+
fixture.detectChanges();
689+
690+
const groups = fixture.componentInstance.groupedDragItems;
691+
const element = groups[0][1].element.nativeElement;
692+
const dropZones = fixture.componentInstance.dropInstances.map(d => d.element.nativeElement);
693+
const targetRect = groups[1][2].element.nativeElement.getBoundingClientRect();
694+
695+
expect(dropZones[0].contains(element)).toBe(true);
696+
697+
dragElementViaMouse(fixture, element, targetRect.left + 1, targetRect.top + 1);
698+
flush();
699+
fixture.detectChanges();
700+
701+
expect(dropZones[1].contains(element)).toBe(true);
702+
}));
703+
704+
it('should not be able to transfer an item into a container that is not in `connectedTo`',
705+
fakeAsync(() => {
706+
const fixture = createComponent(ConnectedDropZones);
707+
708+
fixture.detectChanges();
709+
fixture.componentInstance.dropInstances.forEach(d => d.connectedTo = []);
710+
fixture.detectChanges();
711+
712+
const groups = fixture.componentInstance.groupedDragItems;
713+
const element = groups[0][1].element.nativeElement;
714+
const dropZones = fixture.componentInstance.dropInstances.map(d => d.element.nativeElement);
715+
const targetRect = groups[1][2].element.nativeElement.getBoundingClientRect();
716+
717+
expect(dropZones[0].contains(element)).toBe(true);
718+
719+
dragElementViaMouse(fixture, element, targetRect.left + 1, targetRect.top + 1);
720+
flush();
721+
fixture.detectChanges();
722+
723+
expect(dropZones[0].contains(element)).toBe(true);
724+
}));
725+
726+
727+
it('should be able to start dragging after an item has been transferred', fakeAsync(() => {
728+
const fixture = createComponent(ConnectedDropZones);
729+
fixture.detectChanges();
730+
731+
const groups = fixture.componentInstance.groupedDragItems;
732+
const element = groups[0][1].element.nativeElement;
733+
const dropZones = fixture.componentInstance.dropInstances.map(d => d.element.nativeElement);
734+
const targetRect = dropZones[1].getBoundingClientRect();
735+
736+
expect(dropZones[0].contains(element)).toBe(true);
737+
738+
// Drag the element into the drop zone and move it to the top.
739+
[1, -1].forEach(offset => {
740+
dragElementViaMouse(fixture, element, targetRect.left + offset, targetRect.top + offset);
741+
flush();
742+
fixture.detectChanges();
743+
expect(dropZones[1].contains(element)).toBe(true);
744+
});
745+
746+
assertDownwardSorting(fixture, Array.from(dropZones[1].querySelectorAll('.cdk-drag')));
747+
}));
748+
623749
});
624750

625751
});
@@ -821,20 +947,9 @@ export class ConnectedDropZones implements AfterViewInit {
821947
@ViewChildren(CdkDrop) dropInstances: QueryList<CdkDrop>;
822948

823949
groupedDragItems: CdkDrag[][] = [];
824-
825950
todo = ['Zero', 'One', 'Two', 'Three'];
826951
done = ['Four', 'Five', 'Six'];
827-
828-
droppedSpy = jasmine.createSpy('dropped spy').and.callFake((event: CdkDragDrop<string[]>) => {
829-
if (event.previousContainer === event.container) {
830-
moveItemInArray(event.container.data, event.previousIndex, event.currentIndex);
831-
} else {
832-
transferArrayItem(event.previousContainer.data,
833-
event.container.data,
834-
event.previousIndex,
835-
event.currentIndex);
836-
}
837-
});
952+
droppedSpy = jasmine.createSpy('dropped spy');
838953

839954
ngAfterViewInit() {
840955
this.dropInstances.forEach((dropZone, index) => {
@@ -913,10 +1028,10 @@ function makePageScrollable() {
9131028
/**
9141029
* Asserts that sorting an element down works correctly.
9151030
* @param fixture Fixture against which to run the assertions.
1031+
* @param items Array of items against which to test sorting.
9161032
*/
917-
function assertDownwardSorting(fixture: ComponentFixture<DraggableInDropZone>) {
918-
const items = fixture.componentInstance.dragItems.toArray();
919-
const draggedItem = items[0].element.nativeElement;
1033+
function assertDownwardSorting(fixture: ComponentFixture<any>, items: Element[]) {
1034+
const draggedItem = items[0];
9201035
const {top, left} = draggedItem.getBoundingClientRect();
9211036

9221037
dispatchMouseEvent(draggedItem, 'mousedown', left, top);
@@ -926,7 +1041,7 @@ function assertDownwardSorting(fixture: ComponentFixture<DraggableInDropZone>) {
9261041

9271042
// Drag over each item one-by-one going downwards.
9281043
for (let i = 0; i < items.length; i++) {
929-
const elementRect = items[i].element.nativeElement.getBoundingClientRect();
1044+
const elementRect = items[i].getBoundingClientRect();
9301045

9311046
// Add a few pixels to the top offset so we get some overlap.
9321047
dispatchMouseEvent(document, 'mousemove', elementRect.left, elementRect.top + 5);
@@ -942,10 +1057,10 @@ function assertDownwardSorting(fixture: ComponentFixture<DraggableInDropZone>) {
9421057
/**
9431058
* Asserts that sorting an element up works correctly.
9441059
* @param fixture Fixture against which to run the assertions.
1060+
* @param items Array of items against which to test sorting.
9451061
*/
946-
function assertUpwardSorting(fixture: ComponentFixture<DraggableInDropZone>) {
947-
const items = fixture.componentInstance.dragItems.toArray();
948-
const draggedItem = items[items.length - 1].element.nativeElement;
1062+
function assertUpwardSorting(fixture: ComponentFixture<any>, items: Element[]) {
1063+
const draggedItem = items[items.length - 1];
9491064
const {top, left} = draggedItem.getBoundingClientRect();
9501065

9511066
dispatchMouseEvent(draggedItem, 'mousedown', left, top);
@@ -955,7 +1070,7 @@ function assertUpwardSorting(fixture: ComponentFixture<DraggableInDropZone>) {
9551070

9561071
// Drag over each item one-by-one going upwards.
9571072
for (let i = items.length - 1; i > -1; i--) {
958-
const elementRect = items[i].element.nativeElement.getBoundingClientRect();
1073+
const elementRect = items[i].getBoundingClientRect();
9591074

9601075
// Remove a few pixels from the bottom offset so we get some overlap.
9611076
dispatchMouseEvent(document, 'mousemove', elementRect.left, elementRect.bottom - 5);

0 commit comments

Comments
 (0)