Testing React DnD by React Testing Library
Tags: react
Published: 2020-09-01 15:09:47

---
title: Testing React DnD by React Testing Library
published: true
description: This post show how to testing React DnD Chessboard App with React Testing Library.
tags: react
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/hdevtyumd7lrw9l4x72f.png
---

This post show how to testing [React DnD Chessboard App](https://react-testing-library-react-dnd-chessboard-example.netlify.app/) with [React Testing Library](https://github.com/testing-library/react-testing-library).

<a href="https://react-testing-library-react-dnd-chessboard-example.netlify.app/">![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/hdevtyumd7lrw9l4x72f.png)</a>

> full code example is here.
https://github.com/laststance/react-testing-library-react-dnd-chessboard-example

# Example Code

- Knight.tsx

```tsx
import React from 'react'
import { ItemTypes knightImage} from './Game'
import { useDrag, DragPreviewImage } from 'react-dnd'

const Knight: React.FC = () => {
  const [{ isDragging }, drag, preview] = useDrag({
    item: { type: ItemTypes.KNIGHT },
    collect: (monitor) => ({
      isDragging: !!monitor.isDragging(),
    }),
  })

  return (
    <>
      <DragPreviewImage connect={preview} src={knightImage} />
      <div
        ref={drag}
        style={{
          display: 'block',
          opacity: isDragging ? 0.5 : 1,
          fontSize: '64px',
          fontWeight: 'bold',
          cursor: 'move',
        }}
      >
        ♘
      </div>
    </>
  )
}

export default Knight
```

- BoardSquare.tsx

```tsx
Drop area side
import React from 'react'
import Square from './Square'
import Overlay from './Overlay'
import { canMoveKnight, moveKnight, X, Y } from './Game'
import { ItemTypes } from './Game'
import { useDrop } from 'react-dnd'

interface Props {
  x: X
  y: Y
  index: number
}

const BoardSquare: React.FC<Props> = ({ x, y, index, children }) => {
  const black = (x + y) % 2 === 1
  const [{ isOver, canDrop }, drop] = useDrop({
    accept: ItemTypes.KNIGHT,
    drop: () => moveKnight(x, y),
    canDrop: () => canMoveKnight(x, y),
    collect: (monitor) => ({
      isOver: !!monitor.isOver(),
      canDrop: !!monitor.canDrop(),
    }),
  })

  return (
    <div
      role="gridcell"
      ref={drop}
      data-testid={children ? 'KnightPosition: ' + index : index}
      style={{
        position: 'relative',
        width: '100%',
        height: '100%',
      }}
    >
      <Square black={black}>{children}</Square>
      {isOver && !canDrop && <Overlay color="red" data-testid="RedOverlay" />}
      {!isOver && canDrop && (
        <Overlay color="yellow" data-testid="YellowOverlay" />
      )}
      {isOver && canDrop && (
        <Overlay color="green" data-testid="GreenOverlay" />
      )}
    </div>
  )
}

export default BoardSquare
```

- integration.test.tsx

```tsx
import React from 'react'
import '../index.css'
import { render, screen, fireEvent } from '@testing-library/react'
import Board from '../Board'
import { observe, KnightPosition, releaseObserver } from '../Game'

function dragAndDrop(knight: HTMLElement, cell: HTMLElement) {
  fireEvent.dragStart(knight)
  fireEvent.dragEnter(cell)
  fireEvent.dragOver(cell)
  fireEvent.drop(cell)
}

function dragHold(knight: HTMLElement, cell: HTMLElement) {
  fireEvent.dragStart(knight)
  fireEvent.dragEnter(cell)
  fireEvent.dragOver(cell)
}

beforeEach(() => {
  /*
   * Every time Knight initial position: "57"
   * and Knight droppable positions are "40", "42", "51"
   * when you got all cells with screen.getAllByRole('gridcell')
   */
  observe((knightPosition: KnightPosition) =>
    render(<Board knightPosition={knightPosition} />)
  )
})

afterEach(() => {
  releaseObserver()
})

test('should exist Knight with certain visual on board', () => {
  const Knight = screen.getByText('♘')

  const display = window.getComputedStyle(Knight).getPropertyValue('display')
  const opacity = window.getComputedStyle(Knight).getPropertyValue('opacity')
  const fontSize = window.getComputedStyle(Knight).getPropertyValue('font-size')
  const fontWeight = window
    .getComputedStyle(Knight)
    .getPropertyValue('font-weight')
  const cursor = window.getComputedStyle(Knight).getPropertyValue('cursor')

  expect({
    display: display,
    opacity: opacity,
    fontSize: fontSize,
    fontWeight: fontWeight,
    cursor: cursor,
  }).toStrictEqual({
    display: 'block',
    opacity: '1',
    fontSize: '64px',
    fontWeight: 'bold',
    cursor: 'move',
  })
})

test('should board have 64 cells', () => {
  const boardSquares = screen.getAllByRole('gridcell')
  expect(boardSquares.length).toBe(64) // chessboard ragnge is 8 * 8
})

test("Knight initial position is 'index 57' of all cell array", () => {
  expect(screen.getByTestId('KnightPosition: 57')).toHaveTextContent('♘')
})

test('testing the moment of dragging hold', () => {
  const knight = screen.getByText('♘')
  const boardSquares = screen.getAllByRole('gridcell')
  const knightPosition = boardSquares[57]

  dragHold(knight, knightPosition)

  // Yellow cell is knight moving range
  const KnightDropableSquares = screen.getAllByTestId('YellowOverlay')

  // Initially knight can move to 3 position
  expect(KnightDropableSquares.length).toBe(3)

  // Yellow color css check
  KnightDropableSquares.forEach((square) => {
    expect(square).toHaveStyle('backgroundColor: yellow')
  })

  // Red cell is current knight position when hold dragging
  expect(screen.getByTestId('RedOverlay')).toHaveStyle('backgroundColor: red')
})

describe('Knight can drag and drop initial moving range', () => {
  // Knight initially has moving position 'index: 40 42 51' of 64 cell array
  test('gridcell[40]', () => {
    const knight = screen.getByText('♘')
    const yellowCell40 = screen.getAllByRole('gridcell')[40]
    dragAndDrop(knight, yellowCell40)
    expect(screen.getByTestId('KnightPosition: 40')).toHaveTextContent('♘')
  })

  test('gridcell[42]', () => {
    const knight = screen.getByText('♘')
    const yellowCell42 = screen.getAllByRole('gridcell')[42]
    dragAndDrop(knight, yellowCell42)
    expect(screen.getByTestId('KnightPosition: 42')).toHaveTextContent('♘')
  })

  test('gridcell[51]', () => {
    const knight = screen.getByText('♘')
    const yellowCell51 = screen.getAllByRole('gridcell')[51]
    dragAndDrop(knight, yellowCell51)
    expect(screen.getByTestId('KnightPosition: 51')).toHaveTextContent('♘')
  })
})

test('Knight can not drop not yellow cell', () => {
  const knight = screen.getByText('♘')
  const whiteCell = screen.getByTestId('0')
  const blackCell = screen.getByTestId('1')
  expect(whiteCell.firstChild).toHaveStyle('background-color: white;')
  expect(blackCell.firstChild).toHaveStyle('background-color: black;')

  dragAndDrop(knight, whiteCell)

  expect(screen.getByTestId('KnightPosition: 57')).toHaveTextContent('♘')

  dragAndDrop(knight, blackCell)

  expect(screen.getByTestId('KnightPosition: 57')).toHaveTextContent('♘')
})
```

# Problem

React DnD abstract [Standard Drag Web API](https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API) but React Testing Library using Standard Web API to testing browser event(see [Firing Event](https://testing-library.com/docs/dom-testing-library/api-events))

I don't know which React DnD API tied together Web API(onDragstart etc), 
so might be we need infer those mapping or check by Chrome Devtools debug.

# Let's Debug which Browser Event firing
Describe with [Chessboard Example App](https://react-testing-library-react-dnd-chessboard-example.netlify.app/) and Chrome Devtools.

### 1. Open `Source` tab on Chrome Devtools
And then find out `Event Listener Breakpoints` pan from furthermost to the right.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/ioqhd9nm7hnw5x24va3y.png)

### 2. Check the Event that you should debug
In the following image `dragEnd` is selected.  
This meant setup ready breakpoint whole screen `dragEnd` event.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/dxqhyg3gxaf6z95i86t3.png)

### 3. Doing your debug target action on the Browser
In the following image Browser stopped by breakpoint when you start and stop drag item, and showing triggered Event Listener.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/o59sv1v9uqm10jxh6v3l.png)

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/9jdkylp9f1vntg7cxp14.png)

Therefore when you have any behavior that implement by React DnD and you want to write testing with React Testing Library,
you have to investigate which Event Listener tied together with that.

# Conclusion

I think this post is a little bit niche topic though, I'm glad to could be helpful for someone who has relevant issue.  

And I've thought through that important knowledge is fundamentally and basics things of web technology rather than specific library.

Thank you for reading the article!
See you next time! 🤗