This post show how to testing React DnD Chessboard App with React Testing Library.
full code example is here.
https://github.com/laststance/react-testing-library-react-dnd-chessboard-example
Example Code
- Knight.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
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
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 but React Testing Library using Standard Web API to testing browser event(see Firing Event)
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 and Chrome Devtools.
1. Open Source
tab on Chrome Devtools
And then find out Event Listener Breakpoints
pan from furthermost to the right.
2. Check the Event that you should debug
In the following image dragEnd
is selected.
This meant setup ready breakpoint whole screen dragEnd
event.
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.
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! 🤗
Top comments (0)