UNPKG

@instructure/quiz-interactions

Version:

A React UI component Library for quiz interaction types.

292 lines (225 loc) • 9.21 kB
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) }) }) }) })