UNPKG

@instructure/quiz-interactions

Version:

A React UI component Library for quiz interaction types.

274 lines (213 loc) • 8.09 kB
import {vi, beforeAll} from 'vitest' import React from 'react' import {render, fireEvent} from '@testing-library/react' import Polygon from '../index' // vitest-canvas-mock doesn't fully patch getContext in vitest 4. // Ensure canvas elements return a mocked 2d context. beforeAll(() => { const originalGetContext = HTMLCanvasElement.prototype.getContext HTMLCanvasElement.prototype.getContext = function (type, ...args) { const ctx = originalGetContext.call(this, type, ...args) if (ctx) return ctx // Fallback: return a mock context if the real one is null return { clearRect: vi.fn(), beginPath: vi.fn(), moveTo: vi.fn(), lineTo: vi.fn(), stroke: vi.fn(), arc: vi.fn(), fill: vi.fn(), closePath: vi.fn(), setLineDash: vi.fn(), save: vi.fn(), restore: vi.fn(), fillRect: vi.fn(), strokeRect: vi.fn(), canvas: this, globalAlpha: 1, strokeStyle: '', fillStyle: '', lineWidth: 1, } } }) const handleSetCoordinatesStub = vi.fn() const props = { coordinates: [ {x: 10, y: 10}, {x: 10, y: 20}, {x: 20, y: 20}, ], height: 100, width: 100, handleSetCoordinates: handleSetCoordinatesStub, } describe('Polygon', () => { let polygon function renderPolygon(customProps = {}) { render( <Polygon ref={node => { polygon = node }} {...props} {...customProps} />, ) } afterEach(() => { handleSetCoordinatesStub.mockReset() vi.restoreAllMocks() }) describe('#__drawLine', () => { it('draws a line on canvas given begin and end coordinates', () => { renderPolygon() polygon.clearCanvas() const begin = props.coordinates[0] const end = props.coordinates[1] const ctx = polygon.canvas.canvasElement.getContext('2d') const moveToSpy = vi.spyOn(ctx, 'moveTo') const lineToSpy = vi.spyOn(ctx, 'lineTo') const strokeSpy = vi.spyOn(ctx, 'stroke') polygon.canvas.setStrokeStyle() polygon.__drawLine(begin, end) expect(moveToSpy).toHaveBeenCalledWith(begin.x, begin.y) expect(lineToSpy).toHaveBeenCalledWith(end.x, end.y) expect(strokeSpy).toHaveBeenCalled() }) }) describe('#clearCanvas', () => { it('clears the canvas', () => { renderPolygon() polygon.drawShape(props.coordinates) const ctx = polygon.canvas.canvasElement.getContext('2d') const clearRectSpy = vi.spyOn(ctx, 'clearRect') polygon.clearCanvas() expect(clearRectSpy).toHaveBeenCalledWith(0, 0, props.width, props.height) }) }) describe('#drawShape', () => { it('draws a polygon on canvas based on the given coordinates', () => { renderPolygon() polygon.clearCanvas() const ctx = polygon.canvas.canvasElement.getContext('2d') const moveToSpy = vi.spyOn(ctx, 'moveTo') const lineToSpy = vi.spyOn(ctx, 'lineTo') const fillSpy = vi.spyOn(ctx, 'fill') polygon.drawShape(props.coordinates) const coordA = props.coordinates[0] const coordB = props.coordinates[1] const coordC = props.coordinates[2] // first line starts from A and goes to B expect(moveToSpy).toHaveBeenCalledWith(coordA.x, coordA.y) expect(lineToSpy).toHaveBeenCalledWith(coordB.x, coordB.y) // second line goes to C expect(lineToSpy).toHaveBeenCalledWith(coordC.x, coordC.y) // shape is filled expect(fillSpy).toHaveBeenCalled() }) describe('when the given list of coordinates is empty', () => { it('does nothing', () => { renderPolygon() const fill = vi.fn() vi.spyOn(polygon.canvas.canvasElement, 'getContext').mockReturnValue({fill}) polygon.drawShape([]) expect(fill).not.toHaveBeenCalled() }) }) }) describe('events', () => { describe('onMouseMove', () => { it('draws a line from the last coordinate to the current coordinate', () => { renderPolygon({coordinates: [{x: 2, y: 2}]}) polygon.canvas.clearCanvas() polygon.canvas.state.isDrawing = true // Mock getCurrentCoordinates since RTL fireEvent doesn't set nativeEvent.offsetX vi.spyOn(polygon.canvas, 'getCurrentCoordinates').mockReturnValue({x: 10, y: 10}) const clearCanvasSpy = vi.spyOn(polygon, 'clearCanvas') const drawShapeSpy = vi.spyOn(polygon, 'drawShape') fireEvent.mouseMove(polygon.canvas.canvasElement) expect(clearCanvasSpy).toHaveBeenCalled() expect(drawShapeSpy).toHaveBeenCalledWith([ {x: 2, y: 2}, {x: 10, y: 10}, ]) }) }) describe('onMouseOut', () => { it('updates the coordinates list including the mouseOut event coordinate and finishes the drawing', () => { renderPolygon({ coordinates: [ {x: 5, y: 5}, {x: 10, y: 10}, {x: 15, y: 15}, ], }) polygon.canvas.clearCanvas() polygon.canvas.state.isDrawing = true const setStateSpy = vi.spyOn(polygon.canvas, 'setState') fireEvent.mouseOut(polygon.canvas.canvasElement) const newCoordinates = handleSetCoordinatesStub.mock.calls[0][0] const newState = setStateSpy.mock.calls[0][0] expect(newCoordinates.length).toBe(4) // and the first coordinate is added at the end of the array to close the polygon expect(newCoordinates[3].x).toBe(5) expect(newCoordinates[3].y).toBe(5) expect(newState.isDrawing).toBe(false) }) }) describe('onDoubleClick', () => { it('update the coordinates list and finishes the drawing', () => { renderPolygon() polygon.canvas.clearCanvas() polygon.canvas.state.isDrawing = true const setStateSpy = vi.spyOn(polygon.canvas, 'setState') fireEvent.dblClick(polygon.canvas.canvasElement) const newCoordinates = handleSetCoordinatesStub.mock.calls[0][0] const newState = setStateSpy.mock.calls[0][0] expect(newCoordinates.length).toBe(4) // the last item has to be first one expect(newCoordinates[3].x).toBe(10) expect(newCoordinates[3].y).toBe(10) expect(newState.isDrawing).toBe(false) }) }) describe('onClick', () => { it('starts drawing', () => { renderPolygon({coordinates: []}) polygon.canvas.clearCanvas() vi.spyOn(polygon.canvas, 'getCurrentCoordinates').mockReturnValue({x: 10, y: 10}) const setStateSpy = vi.spyOn(polygon.canvas, 'setState') fireEvent.click(polygon.canvas.canvasElement) const newCoordinates = handleSetCoordinatesStub.mock.calls[0][0] const newState = setStateSpy.mock.calls[0][0] expect(newCoordinates.length).toBe(1) // the new coordinate expect(newCoordinates[0].x).toBe(10) expect(newCoordinates[0].y).toBe(10) expect(newState.isDrawing).toBe(true) }) it('adds a new coordinate when drawing', () => { renderPolygon() polygon.canvas.clearCanvas() polygon.canvas.state.isDrawing = true vi.spyOn(polygon.canvas, 'getCurrentCoordinates').mockReturnValue({x: 13, y: 14}) fireEvent.click(polygon.canvas.canvasElement) const newCoordinates = handleSetCoordinatesStub.mock.calls[0][0] expect(newCoordinates.length).toBe(4) // the new coordinate expect(newCoordinates[3].x).toBe(13) expect(newCoordinates[3].y).toBe(14) }) it('does nothing when the click happens at the same place of the previous click', () => { renderPolygon({coordinates: [{x: 10, y: 10}]}) polygon.canvas.clearCanvas() polygon.canvas.state.isDrawing = true vi.spyOn(polygon.canvas, 'getCurrentCoordinates').mockReturnValue({x: 10, y: 10}) fireEvent.click(polygon.canvas.canvasElement) expect(handleSetCoordinatesStub).not.toHaveBeenCalled() }) }) }) })