Skip to content

Commit

Permalink
Unit Test Case for useDraggableTable
Browse files Browse the repository at this point in the history
  • Loading branch information
uidoyen committed Apr 16, 2024
1 parent 98bedee commit dde3236
Show file tree
Hide file tree
Showing 2 changed files with 423 additions and 81 deletions.
322 changes: 322 additions & 0 deletions frontend/src/utilities/__tests__/useDraggableTable.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,322 @@
import { act } from '@testing-library/react';
import useDraggableTable from '~/utilities/useDraggableTable';
import { testHook } from '~/__tests__/unit/testUtils/hooks';

let setItemOrder = jest.fn();
beforeEach(() => {
setItemOrder = jest.fn();
});

afterEach(() => {
setItemOrder.mockReset();
});

const generateRowsWithItems = (itemOrder: string[]) => {
const tbody = document.createElement('tbody');

itemOrder.forEach((itemId) => {
const row = document.createElement('tr');
row.id = itemId;
tbody.appendChild(row);
});

return tbody;
};
describe('initializeState', () => {
it('should initialize state and return tableProps and rowProps', async () => {
const renderResult = testHook(useDraggableTable)(['item1', 'item2', 'item3'], setItemOrder);
expect(renderResult).hookToHaveUpdateCount(1);
renderResult.rerender(['item1', 'item2', 'item3'], setItemOrder);

expect(renderResult).hookToHaveUpdateCount(2);
expect(renderResult.result.current.tableProps.tbodyProps.ref.current).toBe(null);
expect(renderResult.result.current.tableProps.className).toBe(undefined);

expect(renderResult).hookToBeStable({
rowProps: { onDragEnd: false, onDragStart: false, onDrop: false },
tableProps: {
className: false,
tbodyProps: {
onDragLeave: false,
onDragOver: false,
ref: false,
},
},
});
});
});
describe('dragStart', () => {
it('should handle drag start behaviour correctly and setDraggedItemId accurately', () => {
const itemOrder = ['item1', 'item2', 'item3'];
const tableElement = generateRowsWithItems(itemOrder);
document.body.appendChild(tableElement);

const renderResult = testHook(useDraggableTable)(itemOrder, setItemOrder);
const { rowProps, tableProps } = renderResult.result.current;
const { onDragStart } = rowProps;
const { ref } = tableProps.tbodyProps;

ref.current = document.body.querySelector('tbody');

const dragStartEvent = {
dataTransfer: { effectAllowed: 'none', setData: jest.fn() },
currentTarget: {
id: 'item3',
classList: { add: jest.fn() },
setAttribute: jest.fn(),
},
} as unknown as React.DragEvent<HTMLTableRowElement>;

expect(renderResult.result.current.tableProps.tbodyProps.ref.current).toBe(ref.current);
expect(renderResult).hookToHaveUpdateCount(1);

act(() => {
onDragStart(dragStartEvent);
});

expect(renderResult).hookToHaveUpdateCount(2);
expect(dragStartEvent.dataTransfer.effectAllowed).toBe('move');
expect(dragStartEvent.dataTransfer.setData).toHaveBeenCalledWith('text/plain', 'item3');
expect(dragStartEvent.currentTarget.classList.add).toHaveBeenCalledWith('pf-m-ghost-row');
expect(dragStartEvent.currentTarget.setAttribute).toHaveBeenCalledWith('aria-pressed', 'true');
expect(renderResult.result.current.tableProps.className).toBe('pf-m-drag-over');

tableProps.tbodyProps.ref.current = null;
});
});
describe('onDrop', () => {
it('should handle onDrop behaviour with valid ref and event', () => {
const itemOrder = ['item3', 'item1', 'item2'];
const tableElement = generateRowsWithItems(itemOrder);
document.body.appendChild(tableElement);

const renderResult = testHook(useDraggableTable)(itemOrder, setItemOrder);

const { rowProps, tableProps } = renderResult.result.current;
const { onDrop } = rowProps;
const { ref } = tableProps.tbodyProps;
ref.current = document.body.querySelector('tbody');

if (ref.current) {
ref.current.getBoundingClientRect = jest.fn().mockReturnValue({
x: 40,
y: 40,
width: 20,
height: 20,
} as DOMRect);
}

act(() => {
onDrop({
clientX: 45,
clientY: 45,
} as unknown as React.DragEvent<HTMLTableRowElement>);
});

expect(renderResult).hookToHaveUpdateCount(1);
expect(setItemOrder).toHaveBeenCalledWith(itemOrder);
expect(renderResult.result.current.tableProps.tbodyProps.ref.current).toBe(ref.current);
expect(renderResult.result.current.tableProps.className).toBe(undefined);
});

it('should call onDragCancel when event is invalid and ref is null', () => {
const renderResult = testHook(useDraggableTable)(['item1', 'item2', 'item3'], setItemOrder);

const { rowProps, tableProps } = renderResult.result.current;
const { onDrop } = rowProps;
const { ref } = tableProps.tbodyProps;
ref.current = null;

expect(renderResult.result.current.tableProps.tbodyProps.ref.current).toBe(null);

act(() => {
onDrop({} as unknown as React.DragEvent<HTMLTableRowElement>);
});

expect(renderResult).hookToHaveUpdateCount(1);
expect(setItemOrder).not.toHaveBeenCalled();

tableProps.tbodyProps.ref.current = null;
});

it('should call onDragCancel when event is invalid but ref is not null', () => {
const itemOrder = ['item1', 'item2', 'item3'];
const tableElement = generateRowsWithItems(itemOrder);
document.body.appendChild(tableElement);

const renderResult = testHook(useDraggableTable)(itemOrder, setItemOrder);

const { rowProps, tableProps } = renderResult.result.current;
const { onDrop } = rowProps;
const { ref } = tableProps.tbodyProps;
ref.current = document.body.querySelector('tbody');

act(() => {
onDrop({} as unknown as React.DragEvent<HTMLTableRowElement>);
});

expect(renderResult).hookToHaveUpdateCount(1);
expect(setItemOrder).not.toHaveBeenCalled();
expect(renderResult.result.current.tableProps.tbodyProps.ref.current).toBe(ref.current);

tableProps.tbodyProps.ref.current = null;
});
});
describe('onDragOver', () => {
it('should handle dragover behaviour correctly with different item id', () => {
const itemOrder = ['item1', 'item2', 'item3'];
const tableElement = generateRowsWithItems(itemOrder);
document.body.appendChild(tableElement);

const renderResult = testHook(useDraggableTable)(itemOrder, setItemOrder);

const { tableProps } = renderResult.result.current;
const { onDragOver, ref } = tableProps.tbodyProps;
ref.current = document.body.querySelector('tbody');

act(() => {
onDragOver({
preventDefault: jest.fn(),
clientX: 50,
clientY: 50,
target: document.body.querySelector('tbody tr:first-child'),
} as unknown as React.DragEvent<HTMLTableSectionElement>);
});

expect(tableProps.tbodyProps.ref).toBe(ref);
});
it('should handle dragover behaviour correctly with same item id', () => {
const itemOrder = ['item1', 'item2', 'item3'];
const tableElement = generateRowsWithItems(itemOrder);
document.body.appendChild(tableElement);

const renderResult = testHook(useDraggableTable)(itemOrder, setItemOrder);

const { tableProps } = renderResult.result.current;
const { onDragOver, ref } = tableProps.tbodyProps;
ref.current = document.body.querySelector('tbody');

act(() => {
onDragOver({
preventDefault: jest.fn(),
clientX: 50,
clientY: 50,
target: document.body.querySelector('tbody tr:first-child'),
} as unknown as React.DragEvent<HTMLTableSectionElement>);
});
expect(tableProps.tbodyProps.ref).toBe(ref);
});
it('should handle dragover behaviour correctly when bodyRef is null', async () => {
const itemOrder = ['item1', 'item2', 'item3'];
const tableElement = generateRowsWithItems(itemOrder);
document.body.appendChild(tableElement);

const renderResult = testHook(useDraggableTable)(['item1', 'item2', 'item3'], setItemOrder);

const { tableProps } = renderResult.result.current;
const { onDragOver, ref } = tableProps.tbodyProps;
ref.current = null;

act(() => {
onDragOver({
preventDefault: jest.fn(),
clientX: 50,
clientY: 50,
target: document.body.querySelector('tbody tr:first-child'),
} as unknown as React.DragEvent<HTMLTableSectionElement>);
});
});
it('should handle drag and drop behavior correctly with invalid element', async () => {
const itemOrder = ['item1', 'item2', 'item3'];
const inValidElement = document.createElement('td');
inValidElement.id = 'item3';
document.body.appendChild(inValidElement);

const renderResult = testHook(useDraggableTable)(itemOrder, setItemOrder);

const { tableProps } = renderResult.result.current;
const { onDragOver, ref } = tableProps.tbodyProps;

ref.current = document.body.querySelector('tbody');

act(() => {
onDragOver({
preventDefault: jest.fn(),
clientX: 50,
clientY: 50,
target: inValidElement,
} as unknown as React.DragEvent<HTMLTableSectionElement>);
});

expect(tableProps.tbodyProps.ref).toBe(ref);
});
});
describe('onDragEnd', () => {
it('should handle drag end behaviour correctly', () => {
const itemOrder = ['item3', 'item1', 'item2'];
const tableElement = generateRowsWithItems(itemOrder);
document.body.appendChild(tableElement);

const renderResult = testHook(useDraggableTable)(itemOrder, setItemOrder);

const { rowProps, tableProps } = renderResult.result.current;
const { onDragEnd } = rowProps;
const { ref } = tableProps.tbodyProps;
ref.current = document.body.querySelector('tbody');

expect(renderResult.result.current.tableProps.tbodyProps.ref.current).toBe(ref.current);

const dragEndEvent = {
target: {
id: 'item3',
classList: { remove: jest.fn() },
setAttribute: jest.fn(),
},
} as unknown as React.DragEvent<HTMLTableRowElement>;

act(() => {
onDragEnd(dragEndEvent);
});
expect(renderResult).hookToHaveUpdateCount(1);
expect((dragEndEvent.target as HTMLTableRowElement).classList.remove).toHaveBeenCalled();
expect((dragEndEvent.target as HTMLTableRowElement).setAttribute).toHaveBeenCalledWith(
'aria-pressed',
'false',
);
});
});
describe('onDragLeave', () => {
const dragLeaveEvent = {} as unknown as React.DragEvent<HTMLTableSectionElement>;

it('should handle drag leave behaviour correctly with invalid event', () => {
const itemOrder = ['item1', 'item2', 'item3'];
const renderResult = testHook(useDraggableTable)(itemOrder, setItemOrder);

const { tableProps } = renderResult.result.current;
const { onDragLeave, ref } = tableProps.tbodyProps;
ref.current = null;

act(() => {
onDragLeave(dragLeaveEvent);
});
expect(renderResult.result.current.tableProps.tbodyProps.ref.current).toBe(null);
expect(renderResult.result.current.tableProps.className).toBe(undefined);
expect(renderResult).hookToHaveUpdateCount(1);
});

it('should remove the last child if ulNode has children', () => {
const itemOrder = ['item4'];
const tableElement = generateRowsWithItems(itemOrder);
document.body.appendChild(tableElement);

const renderResult = testHook(useDraggableTable)(itemOrder, setItemOrder);
const { tableProps } = renderResult.result.current;
const { onDragLeave, ref } = tableProps.tbodyProps;

ref.current = document.body.querySelector('tbody');
expect(renderResult.result.current.tableProps.tbodyProps.ref.current).toBe(ref.current);
act(() => {
onDragLeave(dragLeaveEvent);
});
});
});
Loading

0 comments on commit dde3236

Please sign in to comment.