Skip to content

test(drag-drop): add missing unit tests #12219

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 17, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions src/cdk-experimental/drag-drop/drag-utils.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import {moveItemInArray, transferArrayItem} from './drag-utils';

describe('dragging utilities', () => {
describe('moveItemInArray', () => {
let array: number[];

beforeEach(() => array = [0, 1, 2, 3]);

it('should be able to move an item inside an array', () => {
moveItemInArray(array, 1, 3);
expect(array).toEqual([0, 2, 3, 1]);
});

it('should not do anything if the index is the same', () => {
moveItemInArray(array, 2, 2);
expect(array).toEqual([0, 1, 2, 3]);
});

it('should handle an index greater than the length', () => {
moveItemInArray(array, 0, 7);
expect(array).toEqual([1, 2, 3, 0]);
});

it('should handle an index less than zero', () => {
moveItemInArray(array, 3, -1);
expect(array).toEqual([3, 0, 1, 2]);
});
});

describe('transferArrayItem', () => {
it('should be able to move an item from one array to another', () => {
const a = [0, 1, 2];
const b = [3, 4, 5];

transferArrayItem(a, b, 1, 2);
expect(a).toEqual([0, 2]);
expect(b).toEqual([3, 4, 1, 5]);
});

it('should handle an index greater than the target array length', () => {
const a = [0, 1, 2];
const b = [3, 4, 5];

transferArrayItem(a, b, 0, 7);

expect(a).toEqual([1, 2]);
expect(b).toEqual([3, 4, 5, 0]);
});

it('should handle an index less than zero', () => {
const a = [0, 1, 2];
const b = [3, 4, 5];

transferArrayItem(a, b, 2, -1);
expect(a).toEqual([0, 1]);
expect(b).toEqual([2, 3, 4, 5]);
});

it('should not do anything if the source array is empty', () => {
const a = [];
const b = [3, 4, 5];

transferArrayItem(a, b, 0, 0);
expect(a).toEqual([]);
expect(b).toEqual([3, 4, 5]);
});

});
});
26 changes: 20 additions & 6 deletions src/cdk-experimental/drag-drop/drag-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,21 @@
* @param toIndex Index to which the item should be moved.
*/
export function moveItemInArray<T = any>(array: T[], fromIndex: number, toIndex: number): void {
if (fromIndex === toIndex) {
const from = clamp(fromIndex, array.length - 1);
const to = clamp(toIndex, array.length - 1);

if (from === to) {
return;
}

const target = array[fromIndex];
const delta = toIndex < fromIndex ? -1 : 1;
const target = array[from];
const delta = to < from ? -1 : 1;

for (let i = fromIndex; i !== toIndex; i += delta) {
for (let i = from; i !== to; i += delta) {
array[i] = array[i + delta];
}

array[toIndex] = target;
array[to] = target;
}


Expand All @@ -39,5 +42,16 @@ export function transferArrayItem<T = any>(currentArray: T[],
targetArray: T[],
currentIndex: number,
targetIndex: number): void {
targetArray.splice(targetIndex, 0, currentArray.splice(currentIndex, 1)[0]);

const from = clamp(currentIndex, currentArray.length - 1);
const to = clamp(targetIndex, targetArray.length);

if (currentArray.length) {
targetArray.splice(to, 0, currentArray.splice(from, 1)[0]);
}
}

/** Clamps a number between zero and a maximum. */
function clamp(value: number, max: number): number {
return Math.max(0, Math.min(max, value));
}
169 changes: 142 additions & 27 deletions src/cdk-experimental/drag-drop/drag.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {dispatchMouseEvent, dispatchTouchEvent} from '@angular/cdk/testing';
import {Directionality} from '@angular/cdk/bidi';
import {CdkDrag} from './drag';
import {CdkDragDrop} from './drag-events';
import {moveItemInArray, transferArrayItem} from './drag-utils';
import {moveItemInArray} from './drag-utils';
import {CdkDrop} from './drop';
import {CdkDragHandle} from './drag-handle';

Expand Down Expand Up @@ -460,7 +460,9 @@ describe('CdkDrag', () => {
it('should move the placeholder as an item is being sorted down', fakeAsync(() => {
const fixture = createComponent(DraggableInDropZone);
fixture.detectChanges();
assertDownwardSorting(fixture);
assertDownwardSorting(fixture, fixture.componentInstance.dragItems.map(item => {
return item.element.nativeElement;
}));
}));

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

scrollTo(0, 500);
assertDownwardSorting(fixture);
assertDownwardSorting(fixture, fixture.componentInstance.dragItems.map(item => {
return item.element.nativeElement;
}));
cleanup();
}));

it('should move the placeholder as an item is being sorted up', fakeAsync(() => {
const fixture = createComponent(DraggableInDropZone);
fixture.detectChanges();
assertUpwardSorting(fixture);
assertUpwardSorting(fixture, fixture.componentInstance.dragItems.map(item => {
return item.element.nativeElement;
}));
}));

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

scrollTo(0, 500);
assertUpwardSorting(fixture);
assertUpwardSorting(fixture, fixture.componentInstance.dragItems.map(item => {
return item.element.nativeElement;
}));
cleanup();
}));

Expand Down Expand Up @@ -617,9 +625,127 @@ describe('CdkDrag', () => {
const fixture = createComponent(ConnectedDropZones);
fixture.detectChanges();

// TODO
// console.log(fixture.componentInstance.groupedDragItems.map(d => d.length));
const groups = fixture.componentInstance.groupedDragItems;
const item = groups[0][1];
const targetRect = groups[1][2].element.nativeElement.getBoundingClientRect();

dragElementViaMouse(fixture, item.element.nativeElement,
targetRect.left + 1, targetRect.top + 1);
flush();
fixture.detectChanges();

expect(fixture.componentInstance.droppedSpy).toHaveBeenCalledTimes(1);

const event = fixture.componentInstance.droppedSpy.calls.mostRecent().args[0];

expect(event).toEqual({
previousIndex: 1,
currentIndex: 3,
item,
container: fixture.componentInstance.dropInstances.toArray()[1],
previousContainer: fixture.componentInstance.dropInstances.first
});
}));

it('should be able to move the element over a new container and return it', fakeAsync(() => {
const fixture = createComponent(ConnectedDropZones);
fixture.detectChanges();

const groups = fixture.componentInstance.groupedDragItems;
const dropZones = fixture.componentInstance.dropInstances.map(d => d.element.nativeElement);
const item = groups[0][1];
const initialRect = item.element.nativeElement.getBoundingClientRect();
const targetRect = groups[1][2].element.nativeElement.getBoundingClientRect();

expect(dropZones[0].contains(item.element.nativeElement)).toBe(true);
dispatchMouseEvent(item.element.nativeElement, 'mousedown');
fixture.detectChanges();

const placeholder = dropZones[0].querySelector('.cdk-drag-placeholder')!;

expect(placeholder).toBeTruthy();

dispatchMouseEvent(document, 'mousemove', targetRect.left + 1, targetRect.top + 1);
fixture.detectChanges();

expect(dropZones[1].contains(placeholder))
.toBe(true, 'Expected placeholder to be inside second container.');

dispatchMouseEvent(document, 'mousemove', initialRect.left + 1, initialRect.top + 1);
fixture.detectChanges();

expect(dropZones[0].contains(placeholder))
.toBe(true, 'Expected placeholder to be inside first container.');

dispatchMouseEvent(document, 'mouseup');
fixture.detectChanges();

expect(fixture.componentInstance.droppedSpy).not.toHaveBeenCalled();
}));

it('should transfer the DOM element from one drop zone to another', fakeAsync(() => {
const fixture = createComponent(ConnectedDropZones);
fixture.detectChanges();

const groups = fixture.componentInstance.groupedDragItems;
const element = groups[0][1].element.nativeElement;
const dropZones = fixture.componentInstance.dropInstances.map(d => d.element.nativeElement);
const targetRect = groups[1][2].element.nativeElement.getBoundingClientRect();

expect(dropZones[0].contains(element)).toBe(true);

dragElementViaMouse(fixture, element, targetRect.left + 1, targetRect.top + 1);
flush();
fixture.detectChanges();

expect(dropZones[1].contains(element)).toBe(true);
}));

it('should not be able to transfer an item into a container that is not in `connectedTo`',
fakeAsync(() => {
const fixture = createComponent(ConnectedDropZones);

fixture.detectChanges();
fixture.componentInstance.dropInstances.forEach(d => d.connectedTo = []);
fixture.detectChanges();

const groups = fixture.componentInstance.groupedDragItems;
const element = groups[0][1].element.nativeElement;
const dropZones = fixture.componentInstance.dropInstances.map(d => d.element.nativeElement);
const targetRect = groups[1][2].element.nativeElement.getBoundingClientRect();

expect(dropZones[0].contains(element)).toBe(true);

dragElementViaMouse(fixture, element, targetRect.left + 1, targetRect.top + 1);
flush();
fixture.detectChanges();

expect(dropZones[0].contains(element)).toBe(true);
}));


it('should be able to start dragging after an item has been transferred', fakeAsync(() => {
const fixture = createComponent(ConnectedDropZones);
fixture.detectChanges();

const groups = fixture.componentInstance.groupedDragItems;
const element = groups[0][1].element.nativeElement;
const dropZones = fixture.componentInstance.dropInstances.map(d => d.element.nativeElement);
const targetRect = dropZones[1].getBoundingClientRect();

expect(dropZones[0].contains(element)).toBe(true);

// Drag the element into the drop zone and move it to the top.
[1, -1].forEach(offset => {
dragElementViaMouse(fixture, element, targetRect.left + offset, targetRect.top + offset);
flush();
fixture.detectChanges();
expect(dropZones[1].contains(element)).toBe(true);
});

assertDownwardSorting(fixture, Array.from(dropZones[1].querySelectorAll('.cdk-drag')));
}));

});

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

groupedDragItems: CdkDrag[][] = [];

todo = ['Zero', 'One', 'Two', 'Three'];
done = ['Four', 'Five', 'Six'];

droppedSpy = jasmine.createSpy('dropped spy').and.callFake((event: CdkDragDrop<string[]>) => {
if (event.previousContainer === event.container) {
moveItemInArray(event.container.data, event.previousIndex, event.currentIndex);
} else {
transferArrayItem(event.previousContainer.data,
event.container.data,
event.previousIndex,
event.currentIndex);
}
});
droppedSpy = jasmine.createSpy('dropped spy');

ngAfterViewInit() {
this.dropInstances.forEach((dropZone, index) => {
Expand Down Expand Up @@ -913,10 +1028,10 @@ function makePageScrollable() {
/**
* Asserts that sorting an element down works correctly.
* @param fixture Fixture against which to run the assertions.
* @param items Array of items against which to test sorting.
*/
function assertDownwardSorting(fixture: ComponentFixture<DraggableInDropZone>) {
const items = fixture.componentInstance.dragItems.toArray();
const draggedItem = items[0].element.nativeElement;
function assertDownwardSorting(fixture: ComponentFixture<any>, items: Element[]) {
const draggedItem = items[0];
const {top, left} = draggedItem.getBoundingClientRect();

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

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

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

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

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

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