@instructure/quiz-interactions
Version:
A React UI component Library for quiz interaction types.
292 lines (225 loc) • 9.21 kB
JavaScript
import React from 'react'
import {vi} from 'vitest'
import {render} from '@testing-library/react'
import {assertImageData} from '../../Canvas/__tests__/CanvasTestHelpers'
import Oval from '../index'
const handleSetCoordinatesStub = vi.fn()
const onSetStateStub = vi.fn()
const props = {
coordinates: [
{x: 10, y: 10},
{x: 20, y: 20},
],
height: 500,
width: 700,
handleSetCoordinates: handleSetCoordinatesStub,
}
describe('Oval', () => {
afterEach(() => {
handleSetCoordinatesStub.mockClear()
onSetStateStub.mockClear()
})
describe('#__getOvalCoordinates', () => {
it('returns oval coordinates given a click event', () => {
const ref = React.createRef()
render(<Oval {...props} coordinates={[{x: 5, y: 6}]} ref={ref} />)
const instance = ref.current
const e = {
nativeEvent: {
offsetX: 1,
offsetY: 2,
},
}
const ovalCoordinates = instance.__getOvalCoordinates(e)
expect(ovalCoordinates.length).toBe(2)
expect(ovalCoordinates[0].x).toBe(5)
expect(ovalCoordinates[0].y).toBe(6)
expect(ovalCoordinates[1].x).toBe(1)
expect(ovalCoordinates[1].y).toBe(2)
})
})
// vitest-canvas-mock does not render actual pixels, so getImageData returns zeros.
// Tests that assert positive pixel rendering are skipped.
// TODO: QUIZ-17438 - Investigate alternative canvas testing approach
describe('#drawShape', () => {
it.skip('draws a oval on canvas based on the given coordinates', () => {
const ref = React.createRef()
render(<Oval {...props} ref={ref} />)
const instance = ref.current
instance.canvas.clearCanvas()
const ovalCoordinates = props.coordinates
instance.drawShape(ovalCoordinates)
const ctx = instance.canvas.canvasElement.getContext('2d')
const pointA = ovalCoordinates[0]
const pointB = ovalCoordinates[1]
const radiusX = (pointB.x - pointA.x) * 0.5
const radiusY = (pointB.y - pointA.y) * 0.5
const centerX = pointA.x + radiusX
const centerY = pointA.y + radiusY
// all points form pointA.x to pointB.x
const imageDataX = ctx.getImageData(pointA.x, centerY, pointB.x - pointA.x, 1)
assertImageData(imageDataX, true)
// all points form pointA.y to pointB.y
const imageDataY = ctx.getImageData(pointA.y, centerX, pointB.y - pointA.y, 1)
assertImageData(imageDataY, true)
})
it("does not draw an ellipsis if there's not the expected number of coordinates", () => {
const ref = React.createRef()
render(<Oval {...props} ref={ref} />)
const instance = ref.current
instance.canvas.clearCanvas()
const ovalCoordinates = props.coordinates
instance.drawShape([ovalCoordinates[1]])
const ctx = instance.canvas.canvasElement.getContext('2d')
// return the pixels from the range
const imageData = ctx.getImageData(
ovalCoordinates[0].x,
ovalCoordinates[0].y,
ovalCoordinates[1].x,
ovalCoordinates[1].y,
)
assertImageData(imageData, false)
})
})
describe('events', () => {
describe('onMouseDown', () => {
it('sets a new initial coordinate and updates the drawing state', () => {
const ref = React.createRef()
const {container} = render(<Oval {...props} ref={ref} />)
const instance = ref.current
instance.canvas.setState = onSetStateStub
const canvas = container.querySelector('canvas')
canvas.dispatchEvent(
new MouseEvent('mousedown', {
bubbles: true,
clientX: 1,
clientY: 2,
}),
)
// The mouseDown handler uses nativeEvent.offsetX/Y which may not be available in jsdom
// Instead test through the instance method directly
instance.canvas.setState = onSetStateStub
handleSetCoordinatesStub.mockClear()
onSetStateStub.mockClear()
instance.onMouseDown({
nativeEvent: {offsetX: 1, offsetY: 2},
})
const newCoordinates = handleSetCoordinatesStub.mock.calls[0][0]
const newState = onSetStateStub.mock.calls[0][0]
expect(newCoordinates.length).toBe(1)
expect(newCoordinates[0].x).toBe(1)
expect(newCoordinates[0].y).toBe(2)
expect(newState.isDrawing).toBe(true)
})
})
describe('onMouseUp', () => {
it(`updates the coordinates and the state if the event is triggered
at different position of the first coordinate`, () => {
const ref = React.createRef()
render(<Oval {...props} coordinates={[{x: 1, y: 2}]} ref={ref} />)
const instance = ref.current
instance.canvas.state.isDrawing = true
instance.canvas.setState = onSetStateStub
instance.onMouseUp({
nativeEvent: {offsetX: 5, offsetY: 6},
})
const newCoordinates = handleSetCoordinatesStub.mock.calls[0][0]
const newState = onSetStateStub.mock.calls[0][0]
expect(newCoordinates.length).toBe(2)
expect(newCoordinates[0].x).toBe(1)
expect(newCoordinates[0].y).toBe(2)
expect(newCoordinates[1].x).toBe(5)
expect(newCoordinates[1].y).toBe(6)
expect(newState.isDrawing).toBe(false)
})
it(`does not update the coordinates and the state if the event is triggered
at the same position of the first coordinate`, () => {
const ref = React.createRef()
render(<Oval {...props} coordinates={[{x: 1, y: 1}]} ref={ref} />)
const instance = ref.current
instance.canvas.state.isDrawing = true
instance.canvas.setState = onSetStateStub
instance.onMouseUp({
nativeEvent: {offsetX: 1, offsetY: 1},
})
const newCoordinates = handleSetCoordinatesStub.mock.calls[0]
const newState = onSetStateStub.mock.calls[0]
expect(newCoordinates).toBeUndefined()
expect(newState).toBeUndefined()
})
})
describe('onMouseMove', () => {
it.skip('draws an ellipsis from the initial coordinate to the current coordinate', () => {
const ref = React.createRef()
render(<Oval {...props} coordinates={[{x: 2, y: 2}]} ref={ref} />)
const instance = ref.current
instance.canvas.clearCanvas()
instance.canvas.state.isDrawing = true
instance.onMouseMove({
nativeEvent: {offsetX: 10, offsetY: 10},
})
const ctx = instance.canvas.canvasElement.getContext('2d')
// return the pixels from center point
const imageData = ctx.getImageData(6, 6, 1, 1)
assertImageData(imageData, true)
})
})
describe('onTouchStart', () => {
it('sets a new initial coordinate and updates the drawing state', () => {
const ref = React.createRef()
render(<Oval {...props} ref={ref} />)
const instance = ref.current
instance.canvas.setState = onSetStateStub
instance.onTouchStart({
preventDefault: () => {},
changedTouches: [{pageX: 10, pageY: 20}],
})
const newCoordinates = handleSetCoordinatesStub.mock.calls[0][0]
const newState = onSetStateStub.mock.calls[0][0]
const rect = instance.canvas.canvasElement.getBoundingClientRect()
const newX = 10 - rect.left
const newY = 20 - rect.top
expect(newCoordinates.length).toBe(1)
expect(newCoordinates[0].x).toBe(newX)
expect(newCoordinates[0].y).toBe(newY)
expect(newState.isDrawing).toBe(true)
})
})
describe('onTouchMove', () => {
/*
disabling this test to do research later on what change in inst-ui's ui-test-utils
caused the removal of the `touchMove` function
Ticket for tracking: https://instructure.atlassian.net/browse/QUIZ-14306
*/
it.skip('draws an ellipsis from the initial coordinate to the current coordinate', () => {})
})
describe('onTouchEnd', () => {
it('updates the coordinates and the state', () => {
const initialX = 1
const initialY = 2
const endX = 20
const endY = 40
const ref = React.createRef()
render(<Oval {...props} coordinates={[{x: initialX, y: initialY}]} ref={ref} />)
const instance = ref.current
instance.canvas.state.isDrawing = true
instance.canvas.setState = onSetStateStub
instance.onTouchEnd({
preventDefault: () => {},
changedTouches: [{pageX: endX, pageY: endY}],
})
const rect = instance.canvas.canvasElement.getBoundingClientRect()
const newX = endX - rect.left
const newY = endY - rect.top
const newCoordinates = handleSetCoordinatesStub.mock.calls[0][0]
const newState = onSetStateStub.mock.calls[0][0]
expect(newCoordinates.length).toBe(2)
expect(newCoordinates[0].x).toBe(1)
expect(newCoordinates[0].y).toBe(2)
expect(newCoordinates[1].x).toBe(newX)
expect(newCoordinates[1].y).toBe(newY)
expect(newState.isDrawing).toBe(false)
})
})
})
})