@instructure/quiz-interactions
Version:
A React UI component Library for quiz interaction types.
303 lines (267 loc) • 9.19 kB
JavaScript
import React from 'react'
import {vi} from 'vitest'
import runAxeCheck from '@instructure/ui-axe-check'
import {render} from '@testing-library/react'
import TargetContainer from '../index'
import Target from '../../../Take/Target'
const renderUserResponseFeedbackStub = vi.fn()
const renderScoringDataFeedbackStub = vi.fn()
const props = {
coordinates: [{x: 0.3, y: 0.3}],
drawingType: Target,
readOnly: false,
handleSetCoordinates: () => {},
renderUserResponseFeedback: renderUserResponseFeedbackStub,
hotspots: [
{
coordinates: [{x: 0.1, y: 0.2}],
drawingType: Target,
renderScoringDataFeedback: renderScoringDataFeedbackStub,
},
],
results: true,
url: 'https://i.ytimg.com/vi/LgkAebhJ7iE/maxresdefault.jpg',
userResponseCoordinate: [{x: 0.1, y: 0.2, correct: 'correct'}],
}
// TargetContainer is wrapped by withStyleOverrides HOC; use originalType for ref access
const TargetContainerRaw = TargetContainer.originalType
describe('Target Container', () => {
afterEach(() => {
renderUserResponseFeedbackStub.mockClear()
renderScoringDataFeedbackStub.mockClear()
})
it('should render', () => {
const {container} = render(
<TargetContainerRaw {...props} makeStyles={vi.fn()} styles={{image: {}, container: {}}} />,
)
expect(container.firstChild).toBeDefined()
})
describe('rendering', () => {
it('renders image and Target components when readOnly is false', () => {
const {container} = render(
<TargetContainerRaw {...props} makeStyles={vi.fn()} styles={{image: {}, container: {}}} />,
)
const images = container.querySelectorAll('img')
expect(images.length).toBe(1)
expect(images[0].src).toBe(props.url)
})
it('#renderImage', () => {
const ref = React.createRef()
render(
<TargetContainerRaw
{...props}
makeStyles={vi.fn()}
styles={{image: {}, container: {}}}
ref={ref}
/>,
)
const component = ref.current.renderImage()
expect(component.props.src).toBe(props.url)
expect(component.props.alt).toBe('Target Image')
})
it('does not render feedback components if results is false', () => {
render(
<TargetContainerRaw
{...props}
results={false}
makeStyles={vi.fn()}
styles={{image: {}, container: {}}}
/>,
)
expect(renderUserResponseFeedbackStub).not.toHaveBeenCalled()
expect(renderScoringDataFeedbackStub).not.toHaveBeenCalled()
})
it('renders feedback components when results is true', () => {
render(
<TargetContainerRaw {...props} makeStyles={vi.fn()} styles={{image: {}, container: {}}} />,
)
expect(renderUserResponseFeedbackStub).toHaveBeenCalledOnce()
expect(renderScoringDataFeedbackStub).toHaveBeenCalledOnce()
})
})
describe('Helpers', () => {
describe('#componentDidMount', () => {
it('adds event listener', () => {
const addEventListenerSpy = vi.spyOn(window, 'addEventListener')
const ref = React.createRef()
render(
<TargetContainerRaw
{...props}
makeStyles={vi.fn()}
styles={{image: {}, container: {}}}
ref={ref}
/>,
)
ref.current.componentDidMount()
const resizeCall = addEventListenerSpy.mock.calls.find(call => call[0] === 'resize')
expect(resizeCall).toBeDefined()
expect(resizeCall[0]).toBe('resize')
addEventListenerSpy.mockRestore()
})
})
describe('#componentWillUnmount', () => {
it('removes event listener', () => {
const removeEventListenerSpy = vi.spyOn(window, 'removeEventListener')
const ref = React.createRef()
render(
<TargetContainerRaw
{...props}
makeStyles={vi.fn()}
styles={{image: {}, container: {}}}
ref={ref}
/>,
)
ref.current.componentWillUnmount()
const resizeCall = removeEventListenerSpy.mock.calls.find(call => call[0] === 'resize')
expect(resizeCall).toBeDefined()
expect(resizeCall[0]).toBe('resize')
removeEventListenerSpy.mockRestore()
})
})
describe('#convertCoordinate', () => {
it('returns the current coordinates proportional to the image size', () => {
const ref = React.createRef()
render(
<TargetContainerRaw
{...props}
makeStyles={vi.fn()}
styles={{image: {}, container: {}}}
ref={ref}
/>,
)
const newState = {imageWidth: 700, imageHeight: 500}
const oldCoordinates = props.coordinates[0]
ref.current.setState(newState)
const convertedCoordinates = ref.current.convertCoordinate(oldCoordinates)
expect(convertedCoordinates.x).toBe(oldCoordinates.x * newState.imageWidth)
expect(convertedCoordinates.y).toBe(oldCoordinates.y * newState.imageHeight)
})
})
describe('#currentCoordinates', () => {
it('returns null when coordinates array is empty', () => {
const ref = React.createRef()
render(
<TargetContainerRaw
{...props}
coordinates={[]}
makeStyles={vi.fn()}
styles={{image: {}, container: {}}}
ref={ref}
/>,
)
expect(ref.current.currentCoordinates([])).toBeNull()
})
it('returns converted coordinates relative to the image size', () => {
const ref = React.createRef()
render(
<TargetContainerRaw
{...props}
makeStyles={vi.fn()}
styles={{image: {}, container: {}}}
ref={ref}
/>,
)
const newState = {imageWidth: 700, imageHeight: 500}
ref.current.setState(newState)
const currentCoordinates = ref.current.currentCoordinates(props.coordinates)
const expectedX = props.coordinates[0].x * newState.imageWidth
const expectedY = props.coordinates[0].y * newState.imageHeight
expect(currentCoordinates[0].x).toBe(expectedX)
expect(currentCoordinates[0].y).toBe(expectedY)
})
})
describe('#currentImageWidth', () => {
it('returns current image width', () => {
const ref = React.createRef()
render(
<TargetContainerRaw
{...props}
makeStyles={vi.fn()}
styles={{image: {}, container: {}}}
ref={ref}
/>,
)
const newState = {
imageWidth: 700,
imageHeight: 500,
}
ref.current.state = newState
const imageWidth = ref.current.state.imageWidth
expect(imageWidth).toBe(newState.imageWidth)
})
})
describe('#currentImageHeight', () => {
it('returns current image width', () => {
const ref = React.createRef()
render(
<TargetContainerRaw
{...props}
makeStyles={vi.fn()}
styles={{image: {}, container: {}}}
ref={ref}
/>,
)
const newState = {
imageWidth: 700,
imageHeight: 500,
}
ref.current.state = newState
const imageHeight = ref.current.state.imageHeight
expect(imageHeight).toBe(newState.imageHeight)
})
})
})
describe('Actions', () => {
describe('#updateImageDimensions', () => {
it('updates the state with the new image width and height', () => {
const ref = React.createRef()
render(
<TargetContainerRaw
{...props}
makeStyles={vi.fn()}
styles={{image: {}, container: {}}}
ref={ref}
/>,
)
const onSetStateStub = vi.fn()
ref.current.setState = onSetStateStub
ref.current.updateImageDimensions()
const newState = onSetStateStub.mock.calls[0][0]
expect(newState.imageWidth).toBeDefined()
expect(newState.imageHeight).toBeDefined()
})
})
describe('#handleImageLoad', () => {
it('updates the state with the new image width and height', () => {
const ref = React.createRef()
render(
<TargetContainerRaw
{...props}
makeStyles={vi.fn()}
styles={{image: {}, container: {}}}
ref={ref}
/>,
)
const onSetStateStub = vi.fn()
ref.current.setState = onSetStateStub
ref.current.handleImageLoad({
target: {
offsetWidth: 100,
offsetHeight: 200,
},
})
const newState = onSetStateStub.mock.calls[0][0]
expect(newState.imageWidth).toBe(100)
expect(newState.imageHeight).toBe(200)
})
})
})
describe('a11y tests', () => {
it('should meet a11y standards', async () => {
const {container} = render(
<TargetContainerRaw {...props} makeStyles={vi.fn()} styles={{image: {}, container: {}}} />,
)
expect(await runAxeCheck(container)).toBe(true)
})
})
})