UNPKG

canvasimo

Version:

An HTML5 canvas drawing library, with 150+ useful methods, jQuery-like fluent interface, and cross-browser compatibility enhancements.

979 lines (746 loc) 31.2 kB
import Canvasimo from '../src/'; import getBoundingClientRectStub from './helpers/get-bounding-client-rect-stub'; import getContextStub, { mockClearAll } from './helpers/get-context-stub'; import ImageData from './helpers/image-data-stub'; import { each } from './helpers/utils'; describe('canvasimo', () => { let canvas: Canvasimo; const element = document.createElement('canvas'); const specialFontTypes = [ 'caption', 'icon', 'menu', 'message-box', 'small-caption', 'status-bar', ]; const getters = { getOpacity: {value: 1, type: 'number'}, getCompositeOperation: {value: 'source-over', type: 'string'}, getFill: {value: '#000000', type: 'string'}, getStroke: {value: '#000000', type: 'string'}, getStrokeWidth: {value: 1, type: 'number'}, getStrokeCap: {value: 'butt', type: 'string'}, getStrokeJoin: {value: 'miter', type: 'string'}, getStrokeDashOffset: {value: 0, type: 'number'}, getMiterLimit: {value: 10, type: 'number'}, getShadowColor: {value: 'rgba(0, 0, 0, 0)', type: 'string'}, getShadowBlur: {value: 0, type: 'number'}, getShadowOffsetX: {value: 0, type: 'number'}, getShadowOffsetY: {value: 0, type: 'number'}, getTextAlign: {value: 'start', type: 'string'}, getTextBaseline: {value: 'alphabetic', type: 'string'}, }; const argumentMap = { setWidth: [0], setHeight: [0], setSize: [0, 0], scale: [1, 1], rotate: [0], translate: [0, 0], setTransform: [1, 0, 0, 1, 0, 0], transform: [1, 0, 0, 1, 0, 0], drawFocusIfNeeded: [element], clearRect: [0, 0, 0, 0], moveTo: [0, 0], lineTo: [0, 0], quadraticCurveTo: [0, 0, 0, 0], bezierCurveTo: [0, 0, 0, 0, 0, 0], arcTo: [0, 0, 0, 0, 0], fillText: ['', 0, 0], strokeText: ['', 0, 0], drawImage: [new Image(), 0, 0], putImageData: [new ImageData(1, 1), 0, 0], plotRect: [0, 0, 0, 0], plotArc: [0, 0, 0, 0, 0], plotEllipse: [0, 0, 0, 0, 0, 0, 0], setStrokeDash: [[]], isPointInPath: [0, 0], tap: [() => undefined], repeat: [0, 0, 0, () => undefined], forEach: [[], () => undefined], setLineDash: [[]], setFontWeight: ['normal'], setDensity: [1], plotPath: [[]], fillPath: [[]], strokePath: [[]], plotClosedPath: [[]], fillClosedPath: [[]], strokeClosedPath: [[]], strokeTextMultiline: ['', 0, 0], fillTextMultiline: ['', 0, 0], drawTextWithLineBreaks: [jest.fn(), '', 0, 0], wrapBreakAll: ['', 100], wrapBreakWord: ['', 100], wrapNormal: ['', 100], textMultiline: [jest.fn(), '', 0, 0], }; // tslint:disable-next-line:max-line-length const isGetter = /^(get|create|is|measure|constrain|map|version|wrapBreakAll|wrapBreakWord|wrapNormal|saveContextValues)/i; beforeEach(() => { mockClearAll(); }); afterEach(() => { canvas .setDensity(1) .setFontSize('invalid'); }); it('should return an interface', () => { jest.spyOn(element, 'getContext').mockImplementation(getContextStub); jest.spyOn(element, 'getBoundingClientRect').mockImplementation(getBoundingClientRectStub); canvas = new Canvasimo(element); expect(Boolean(canvas)).toBe(true); }); describe('property getters', () => { it('should return the correct property values from the context', () => { each(getters, (expected, key) => { const result = (canvas as any)[key](); expect(result).toBe(expected.value); expect(typeof result).toBe(expected.type); }); }); }); describe('element getters', () => { it('should return the canvas element', () => { expect(canvas.getCanvas()).toEqual(element); expect(canvas.getElement()).toEqual(element); }); it('should return the element\'s bounding client rect', () => { expect(canvas.getBoundingClientRect()).toEqual({ top: 0, left: 0, right: 50, bottom: 50, width: 100, height: 100, }); }); }); describe('context getters', () => { it('should return the actual canvas context', () => { expect(canvas.getContext('2d')).toBe(element.getContext('2d')); expect(() => canvas.getContext('SomeOtherContext')).toThrow('Cannot get a context that is not "2d"'); }); it('should return the current context & context type', () => { expect(canvas.getCurrentContext()).toBe(element.getContext('2d')); expect(canvas.getCurrentContextType()).toBe('2d'); }); }); describe('get data url', () => { it('should return a data url of the canvas', () => { jest.spyOn(element, 'toDataURL').mockImplementation(() => { return 'url'; }); expect(canvas.getDataURL()).toBe('url'); }); }); describe('canvas sizes', () => { it('should return the canvas width and height as an integer', () => { element.setAttribute('width', '123'); element.setAttribute('height', '456'); expect(canvas.getWidth()).toBe(123); expect(canvas.getHeight()).toBe(456); expect(typeof canvas.getWidth()).toBe('number'); expect(typeof canvas.getHeight()).toBe('number'); element.setAttribute('width', '12.3'); element.setAttribute('height', '45.6'); expect(canvas.getWidth()).toBe(12); expect(canvas.getHeight()).toBe(45); expect(typeof canvas.getWidth()).toBe('number'); expect(typeof canvas.getHeight()).toBe('number'); expect(canvas.getSize()).toEqual({width: 12, height: 45}); }); it('should set the canvas width and height as integers', () => { canvas.setWidth(456); canvas.setHeight(123); expect(element.getAttribute('width')).toBe('456'); expect(element.getAttribute('height')).toBe('123'); canvas.setWidth(45.6); canvas.setHeight(12.3); expect(element.getAttribute('width')).toBe('45'); expect(element.getAttribute('height')).toBe('12'); canvas.setSize(32.1, 65.4); expect(element.getAttribute('width')).toBe('32'); expect(element.getAttribute('height')).toBe('65'); canvas.setSize({width: 353.4, height: 735.3}); expect(element.getAttribute('width')).toBe('353'); expect(element.getAttribute('height')).toBe('735'); // Invalid: should not change the size canvas.setSize('' as any, null as any); expect(element.getAttribute('width')).toBe('353'); expect(element.getAttribute('height')).toBe('735'); }); }); describe('image smoothing', () => { it('should return the first image smoothing value', () => { expect(canvas.getImageSmoothingEnabled()).toBe(true); }); it('should set the first image smoothing value', () => { canvas.setImageSmoothingEnabled(false); expect(canvas.getImageSmoothingEnabled()).toBe(false); }); it('should return false if no image smoothing keys present', () => { const context = element.getContext('2d') as CanvasRenderingContext2D; delete context.imageSmoothingEnabled; delete (context as any).webkitImageSmoothingEnabled; expect(canvas.getImageSmoothingEnabled()).toBe(false); }); it('should not set a value if no image smoothing keys present', () => { canvas.setImageSmoothingEnabled(true); expect(canvas.getImageSmoothingEnabled()).toBe(false); }); }); describe('setDensity', () => { it('should adjust canvas context properties when changing the density', () => { const ctx = canvas.getCurrentContext(); canvas .setDensity(1) .setWidth(1) .setHeight(1) .setFontSize(1) .setLineDashOffset(1) .setLineWidth(1) .setMiterLimit(1) .setShadowBlur(1) .setShadowOffsetX(1) .setShadowOffsetY(1); expect(canvas.getDensity()).toBe(1); expect(canvas.getWidth()).toBe(1); expect(canvas.getHeight()).toBe(1); expect(canvas.getFontSize()).toBe(1); expect(canvas.getLineDashOffset()).toBe(1); expect(canvas.getLineWidth()).toBe(1); expect(canvas.getMiterLimit()).toBe(1); expect(canvas.getShadowBlur()).toBe(1); expect(canvas.getShadowOffsetX()).toBe(1); expect(canvas.getShadowOffsetY()).toBe(1); expect(element.width).toBe(1); expect(element.height).toBe(1); expect(ctx.font).toBe('normal normal normal 1px sans-serif'); expect(ctx.lineDashOffset).toBe(1); expect(ctx.lineWidth).toBe(1); expect(ctx.miterLimit).toBe(1); expect(ctx.shadowBlur).toBe(1); expect(ctx.shadowOffsetX).toBe(1); expect(ctx.shadowOffsetY).toBe(1); canvas.setDensity(2); expect(canvas.getDensity()).toBe(2); expect(canvas.getWidth()).toBe(1); expect(canvas.getHeight()).toBe(1); expect(canvas.getFontSize()).toBe(1); expect(canvas.getLineDashOffset()).toBe(1); expect(canvas.getLineWidth()).toBe(1); expect(canvas.getMiterLimit()).toBe(1); expect(canvas.getShadowBlur()).toBe(1); expect(canvas.getShadowOffsetX()).toBe(1); expect(canvas.getShadowOffsetY()).toBe(1); expect(element.width).toBe(2); expect(element.height).toBe(2); expect(ctx.font).toBe('normal normal normal 2px sans-serif'); expect(ctx.lineDashOffset).toBe(2); expect(ctx.lineWidth).toBe(2); expect(ctx.miterLimit).toBe(2); expect(ctx.shadowBlur).toBe(2); expect(ctx.shadowOffsetX).toBe(2); expect(ctx.shadowOffsetY).toBe(2); canvas.setDensity(1); expect(canvas.getDensity()).toBe(1); expect(canvas.getWidth()).toBe(1); expect(canvas.getHeight()).toBe(1); expect(canvas.getFontSize()).toBe(1); expect(canvas.getLineDashOffset()).toBe(1); expect(canvas.getLineWidth()).toBe(1); expect(canvas.getMiterLimit()).toBe(1); expect(canvas.getShadowBlur()).toBe(1); expect(canvas.getShadowOffsetX()).toBe(1); expect(canvas.getShadowOffsetY()).toBe(1); expect(element.width).toBe(1); expect(element.height).toBe(1); expect(ctx.font).toBe('normal normal normal 1px sans-serif'); expect(ctx.lineDashOffset).toBe(1); expect(ctx.lineWidth).toBe(1); expect(ctx.miterLimit).toBe(1); expect(ctx.shadowBlur).toBe(1); expect(ctx.shadowOffsetX).toBe(1); expect(ctx.shadowOffsetY).toBe(1); }); }); describe('font methods', () => { it('should return a formatted font value', () => { expect(canvas.getFont()).toBe('normal normal normal 10px sans-serif'); }); it('should set the font', () => { canvas.setFont('20.2px times'); expect(canvas.getFont()).toBe('normal normal normal 20.2px times'); canvas.setFont('.2% arial'); expect(canvas.getFont()).toBe('normal normal normal .2% arial'); canvas.setFont('300 italic normal 20px times'); expect(canvas.getFont()).toBe('italic normal 300 20px times'); canvas.setFont('small-caps normal bold 20px arial'); expect(canvas.getFont()).toBe('normal small-caps bold 20px arial'); }); it('should return individual font properties', () => { canvas.setFont('normal small-caps bold 20px arial'); expect(canvas.getFontStyle()).toBe('normal'); expect(canvas.getFontVariant()).toBe('small-caps'); expect(canvas.getFontWeight()).toBe('bold'); expect(canvas.getFontSize()).toBe(20); expect(canvas.getFontFamily()).toBe('arial'); }); it('should set individual font properties', () => { canvas.setFont('normal small-caps bold 20px arial'); expect(canvas.getFont()).toBe('normal small-caps bold 20px arial'); canvas.setFontStyle('italic'); expect(canvas.getFont()).toBe('italic small-caps bold 20px arial'); canvas.setFontVariant('normal'); expect(canvas.getFont()).toBe('italic normal bold 20px arial'); canvas.setFontWeight('normal'); expect(canvas.getFont()).toBe('italic normal normal 20px arial'); canvas.setFontSize(15); expect(canvas.getFont()).toBe('italic normal normal 15px arial'); canvas.setFontFamily('times'); expect(canvas.getFont()).toBe('italic normal normal 15px times'); }); it('should allow setting special font types', () => { each(specialFontTypes, (specialFontType) => { canvas.setFont(specialFontType); expect(canvas.getFont()).toBe(specialFontType); }); }); it('should return null for individual properties when a special font is set', () => { canvas.setFont('menu'); expect(canvas.getFontStyle()).toBe(null); expect(canvas.getFontVariant()).toBe(null); expect(canvas.getFontWeight()).toBe(null); expect(canvas.getFontSize()).toBe(null); expect(canvas.getFontFamily()).toBe(null); }); it('should set the default font if properties are set when a special font is set', () => { // Set to special font type canvas.setFont(specialFontTypes[0]); expect(canvas.getFont()).toBe(specialFontTypes[0]); canvas.setFontStyle(''); expect(canvas.getFont()).toBe('normal normal normal 10px sans-serif'); // Set to special font type canvas.setFont(specialFontTypes[0]); expect(canvas.getFont()).toBe(specialFontTypes[0]); canvas.setFontVariant(''); expect(canvas.getFont()).toBe('normal normal normal 10px sans-serif'); // Set to special font type canvas.setFont(specialFontTypes[0]); expect(canvas.getFont()).toBe(specialFontTypes[0]); canvas.setFontWeight(''); expect(canvas.getFont()).toBe('normal normal normal 10px sans-serif'); // Set to special font type canvas.setFont(specialFontTypes[0]); expect(canvas.getFont()).toBe(specialFontTypes[0]); canvas.setFontSize(''); expect(canvas.getFont()).toBe('normal normal normal 10px sans-serif'); // Set to special font type canvas.setFont(specialFontTypes[0]); expect(canvas.getFont()).toBe(specialFontTypes[0]); canvas.setFontFamily(''); expect(canvas.getFont()).toBe('normal normal normal 10px sans-serif'); }); it('should set the default font if a font value is incorrect', () => { // Set to something correct canvas.setFont('20px times'); expect(canvas.getFont()).toBe('normal normal normal 20px times'); canvas.setFont('oops'); expect(canvas.getFont()).toBe('normal normal normal 10px sans-serif'); // Set to something correct canvas.setFont('20px times'); expect(canvas.getFont()).toBe('normal normal normal 20px times'); canvas.setFont('not real values 15px arial'); expect(canvas.getFont()).toBe('normal normal normal 10px sans-serif'); // Set to something correct canvas.setFont('20px times'); expect(canvas.getFont()).toBe('normal normal normal 20px times'); canvas.setFont('italic bold'); expect(canvas.getFont()).toBe('normal normal normal 10px sans-serif'); }); it('should handle canvas density when setting and getting font sizes', () => { canvas.setDensity(1); canvas.setFont('10px arial'); expect(canvas.getFont()).toBe('normal normal normal 10px arial'); expect(canvas.getFontFamily()).toBe('arial'); expect(canvas.getFontSize()).toBe(10); canvas.setFontSize(5); expect(canvas.getFont()).toBe('normal normal normal 5px arial'); expect(canvas.getFontFamily()).toBe('arial'); expect(canvas.getFontSize()).toBe(5); canvas.setDensity(2); canvas.setFont('10px arial'); expect(canvas.getFont()).toBe('normal normal normal 10px arial'); expect(canvas.getFontFamily()).toBe('arial'); expect(canvas.getFontSize()).toBe(10); canvas.setFontSize(5); expect(canvas.getFont()).toBe('normal normal normal 5px arial'); expect(canvas.getFontFamily()).toBe('arial'); expect(canvas.getFontSize()).toBe(5); canvas.setDensity(1); expect(canvas.getFont()).toBe('normal normal normal 5px arial'); expect(canvas.getFontFamily()).toBe('arial'); expect(canvas.getFontSize()).toBe(5); canvas.setFontWeight(300); expect(canvas.getFont()).toBe('normal normal 300 5px arial'); expect(canvas.getFontFamily()).toBe('arial'); expect(canvas.getFontSize()).toBe(5); canvas.setDensity(2); expect(canvas.getFont()).toBe('normal normal 300 5px arial'); expect(canvas.getFontFamily()).toBe('arial'); expect(canvas.getFontSize()).toBe(5); canvas.setFontWeight(600); expect(canvas.getFont()).toBe('normal normal 600 5px arial'); expect(canvas.getFontFamily()).toBe('arial'); expect(canvas.getFontSize()).toBe(5); canvas.setDensity(1); canvas.setFontSize(50); expect(canvas.getFont()).toBe('normal normal 600 50px arial'); expect(canvas.getFontFamily()).toBe('arial'); expect(canvas.getFontSize()).toBe(50); canvas.setDensity(2); canvas.setFontSize('5em'); expect(canvas.getFont()).toBe('normal normal 600 5em arial'); expect(canvas.getFontFamily()).toBe('arial'); expect(canvas.getFontSize()).toBe(5); canvas.setDensity(2); canvas.setFontSize('70%'); expect(canvas.getFont()).toBe('normal normal 600 70% arial'); expect(canvas.getFontFamily()).toBe('arial'); expect(canvas.getFontSize()).toBe(70); canvas.setDensity(1); canvas.setFontSize('70%'); expect(canvas.getFont()).toBe('normal normal 600 70% arial'); expect(canvas.getFontFamily()).toBe('arial'); expect(canvas.getFontSize()).toBe(70); }); }); describe('plot path', () => { it('should accept and plot valid point arrays', () => { const context = canvas.getCurrentContext(); const moveToSpy = jest.spyOn(context, 'moveTo'); const lineToSpy = jest.spyOn(context, 'lineTo'); canvas.plotPath([0, 1, 2, 3]); expect(moveToSpy).toHaveBeenCalledWith(0, 1); expect(lineToSpy).toHaveBeenCalledWith(2, 3); canvas.plotPath([[4, 5], [6, 7]]); expect(moveToSpy).toHaveBeenCalledWith(4, 5); expect(lineToSpy).toHaveBeenCalledWith(6, 7); canvas.plotPath([{x: 8, y: 9}, {x: 10, y: 11}]); expect(moveToSpy).toHaveBeenCalledWith(8, 9); expect(lineToSpy).toHaveBeenCalledWith(10, 11); }); }); describe('actions and setters', () => { it('should return the canvasimo instance', () => { each(canvas, (method, key) => { if (typeof method === 'function' && !isGetter.test(key)) { let result; try { result = method.apply(null, argumentMap[key as keyof typeof argumentMap]); } catch (error) { throw new Error(`Method ${key} threw an error: ${error}`); } expect(result).toBe(canvas); } }); }); }); describe('fill and stroke', () => { it('should set the fill if it is not a special fill', () => { const fillSpy = jest.spyOn(canvas, 'setFill'); canvas.fill('nonzero'); expect(fillSpy).not.toHaveBeenCalled(); canvas.fill('red'); expect(fillSpy).toHaveBeenCalledTimes(1); }); it('should set the stroke if it is a string', () => { const strokeSpy = jest.spyOn(canvas, 'setStroke'); canvas.stroke('red'); expect(strokeSpy).toHaveBeenCalledTimes(1); }); it('should not call stroke with undefined or null', () => { const ctx = canvas.getCurrentContext(); canvas.stroke(); canvas.stroke('red'); canvas.stroke(undefined); canvas.stroke(null as any); canvas.stroke('red', undefined as any); canvas.stroke('red', null as any); const strokeCalls = (ctx.stroke as jest.Mock<any>).mock.calls; expect(strokeCalls.length).toBe(6); expect(ctx.stroke).not.toHaveBeenCalledWith(null); expect(ctx.stroke).not.toHaveBeenCalledWith(undefined); strokeCalls.forEach((call) => { expect(call.length).toBe(0); }); }); }); describe('resetTransform', () => { it('should use setTransform if resetTransform is unavailable', () => { const ctx = canvas.getCurrentContext(); // tslint:disable-next-line:variable-name const _resetTransform = (ctx as any).resetTransform; delete (ctx as any).resetTransform; const setTransformSpy = jest.spyOn(canvas, 'setTransform').mockImplementation(jest.fn()); canvas.resetTransform(); expect(setTransformSpy).toHaveBeenCalledTimes(1); (ctx as any).resetTransform = _resetTransform; (setTransformSpy as jest.Mock<any>).mockClear(); canvas.resetTransform(); expect(setTransformSpy).not.toHaveBeenCalled(); }); }); describe('helper methods', () => { it('should create color values', () => { expect(canvas.createHSL(123, 40, 50)).toBe('hsl(123,40%,50%)'); expect(canvas.createHSLA(123, 40, 50, 0.5)).toBe('hsla(123,40%,50%,0.5)'); expect(canvas.createRGB(111, 222, 333)).toBe('rgb(111,222,333)'); expect(canvas.createRGBA(111, 222, 333, 0.5)).toBe('rgba(111,222,333,0.5)'); }); it('should remove alpha values from colors', () => { expect(canvas.getHSLFromHSLA(canvas.createHSLA(123, 40, 50, 0.5))).toBe('hsl(123,40%,50%)'); expect(canvas.getRGBFromRGBA(canvas.createRGBA(111, 222, 333, 0.5))).toBe('rgb(111,222,333)'); }); it('should convert angles', () => { expect(canvas.getRadiansFromDegrees(0)).toBe(0); expect(canvas.getRadiansFromDegrees(180)).toBe(Math.PI); expect(canvas.getRadiansFromDegrees(360)).toBe(Math.PI * 2); expect(canvas.getDegreesFromRadians(0)).toBe(0); expect(canvas.getDegreesFromRadians(Math.PI)).toBe(180); expect(canvas.getDegreesFromRadians(Math.PI * 2)).toBe(360); }); it('should convert fractions & percentages', () => { expect(canvas.getPercentFromFraction(0.75)).toBe(75); expect(canvas.getFractionFromPercent(25)).toBe(0.25); }); it('should get fractions & percentages of the canvas size', () => { canvas.setSize(400, 200); expect(canvas.getPercentOfWidth(75)).toBe(300); expect(canvas.getFractionOfWidth(0.25)).toBe(100); expect(canvas.getPercentOfHeight(75)).toBe(150); expect(canvas.getFractionOfHeight(0.25)).toBe(50); }); it('should return information about a pixel', () => { canvas.setSize(5, 5); expect(canvas.getPixelData(2, 2)).toEqual([0, 0, 0, 0]); expect(canvas.getPixelColor(2, 2)).toBe('rgba(0,0,0,0)'); }); it('should calculate the distance between 2 points', () => { expect(canvas.getDistance(0, 0, 0, 10)).toBe(10); expect(canvas.getDistance(0, 0, 0, -10)).toBe(10); expect(canvas.getDistance(0, 0, 10, 0)).toBe(10); expect(canvas.getDistance(0, 3, 4, 0)).toBe(5); expect(canvas.getDistance(4, 0, 0, 3)).toBe(5); expect(canvas.getDistance(-4, 0, 0, -3)).toBe(5); }); it('should error when calculating angles with wrong arguments', () => { const anError = /Incorrect number of arguments/; expect(canvas.getAngle.bind(null, 0, 0, 0, 0)).not.toThrow(anError); expect(canvas.getAngle.bind(null, 0, 0, 0, 0, 0, 0)).not.toThrow(anError); expect(canvas.getAngle.bind(null)).toThrow(anError); expect(canvas.getAngle.bind(null, 0)).toThrow(anError); expect(canvas.getAngle.bind(null, 0, 0, 0)).toThrow(anError); expect(canvas.getAngle.bind(null, 0, 0, 0, 0, 0)).toThrow(anError); expect(canvas.getAngle.bind(null, 0, 0, 0, 0, 0, 0, 0, 0, 0)).toThrow(anError); }); it('should calculate the angle between 2 points', () => { expect(canvas.getAngle(0, 0, 10, 0)).toBe(0); expect(canvas.getAngle(0, 0, 0, 10)).toBe(Math.PI * 0.5); expect(canvas.getAngle(0, 0, 0, -10)).toBe(-Math.PI * 0.5); expect(canvas.getAngle(0, 0, 10, 10)).toBe(Math.PI * 0.25); expect(canvas.getAngle(0, 0, 10, -10)).toBe(-Math.PI * 0.25); expect(canvas.getAngle(0, 0, -10, 10)).toBe(Math.PI * 0.75); expect(canvas.getAngle(0, 0, -10, -10)).toBe(-Math.PI * 0.75); }); it('should calculate the angle between 3 points', () => { expect(canvas.getAngle(0, 0, 10, 0, 20, 0)).toBe(Math.PI); expect(canvas.getAngle(20, 0, 10, 0, 0, 0)).toBe(Math.PI); expect(canvas.getAngle(0, 0, 10, 0, 10, 10)).toBe(Math.PI * 0.5); expect(canvas.getAngle(0, 0, 10, 0, 10, -10)).toBe(-Math.PI * 0.5); expect(canvas.getAngle(0, 0, 10, 0, 20, 10)).toBe(Math.PI * 0.75); expect(canvas.getAngle(0, 0, 10, 0, 20, -10)).toBe(-Math.PI * 0.75); expect(canvas.getAngle(0, 0, 10, 0, 0, 0)).toBe(0); expect(canvas.getAngle(0, 0, -10, 10, -10, 0)).toBe(Math.PI * 0.25); expect(canvas.getAngle(0, 0, -10, 10, 0, 10)).toBe(-Math.PI * 0.25); }); }); describe('tap', () => { it('should allow a function to be run during a chain', () => { let result = false; expect( canvas .setFill('black') .tap(() => { result = true; canvas.setFill('red'); }) .getFill() ).toBe('red'); expect(result).toBe(true); }); }); describe('repeat', () => { it('should loop over the provided range', () => { let expected; const callback = jest.fn(); canvas.repeat(0, callback); expect(callback).not.toHaveBeenCalled(); callback.mockClear(); canvas.repeat(0, 0, callback); expect(callback).not.toHaveBeenCalled(); callback.mockClear(); canvas.repeat(0, 1, 0, callback); expect(callback).not.toHaveBeenCalled(); callback.mockClear(); canvas.repeat(0, 0, 1, callback); expect(callback).not.toHaveBeenCalled(); callback.mockClear(); expected = [0, 1, 2]; canvas.repeat(3, callback); expect(callback).toHaveBeenCalledTimes(3); each(expected, (value, index) => { expect(callback.mock.calls[index]).toEqual([value]); }); callback.mockClear(); expected = [1, 2, 3]; canvas.repeat(1, 4, callback); expect(callback).toHaveBeenCalledTimes(3); each(expected, (value, index) => { expect(callback.mock.calls[index]).toEqual([value]); }); callback.mockClear(); expected = [-2, -3, -4]; canvas.repeat(-2, -5, callback); expect(callback).toHaveBeenCalledTimes(3); each(expected, (value, index) => { expect(callback.mock.calls[index]).toEqual([value]); }); callback.mockClear(); expected = [2, 4, 6]; canvas.repeat(2, 8, 2, callback); expect(callback).toHaveBeenCalledTimes(3); each(expected, (value, index) => { expect(callback.mock.calls[index]).toEqual([value]); }); callback.mockClear(); expected = [10, 5, 0]; canvas.repeat(10, -5, 5, callback); expect(callback).toHaveBeenCalledTimes(3); each(expected, (value, index) => { expect(callback.mock.calls[index]).toEqual([value]); }); callback.mockClear(); expected = [1, 3, 5]; canvas.repeat(1, 6, 2, callback); expect(callback).toHaveBeenCalledTimes(3); each(expected, (value, index) => { expect(callback.mock.calls[index]).toEqual([value]); }); }); it('should stop iteration if false is returned', () => { const callback = jest.fn().mockImplementation((index) => { if (index === 1) { return false; } }); canvas.repeat(3, callback); expect(callback).toHaveBeenCalledTimes(2); }); it('should error if wrong arguments provided', () => { const anError = /arguments/i; expect(canvas.repeat).toThrow(anError); expect(() => (canvas.repeat as any)(0)).toThrow(anError); expect(() => (canvas.repeat as any)(0, 0, 0, 0, 0)).toThrow(anError); }); }); describe('forEach', () => { it('should loop over the array, object, or string provided', () => { let expected; const callback = jest.fn(); expected = [0, 1, 2]; canvas.forEach(expected, callback); expect(callback).toHaveBeenCalledTimes(3); each(expected, (value, index) => { expect(callback.mock.calls[index]).toEqual([value, index]); }); callback.mockClear(); expected = 'str'; canvas.forEach(expected, callback); expect(callback).toHaveBeenCalledTimes(3); each(expected, (value, index) => { expect(callback.mock.calls[index]).toEqual([value, index]); }); callback.mockClear(); let i = 0; expected = {foo: 'bar', bar: 'foo', hello: 'world'}; canvas.forEach(expected, callback); expect(callback).toHaveBeenCalledTimes(3); each(expected, (value, key) => { expect(callback.mock.calls[i]).toEqual([value, key]); i += 1; }); }); it('should stop iteration if false is returned', () => { let expected; let i: number = 0; const callback = jest.fn().mockImplementation(() => { if (i === 1) { return false; } i += 1; }); i = 0; expected = [0, 1, 2]; canvas.forEach(expected, callback); expect(callback).toHaveBeenCalledTimes(2); callback.mockClear(); i = 0; expected = {foo: 'bar', bar: 'foo', hello: 'world'}; canvas.forEach(expected, callback); expect(callback).toHaveBeenCalledTimes(2); }); }); describe('constrain', () => { it('should constrain a number between 2 other numbers', () => { expect(canvas.constrain(0.5, 0, 1)).toBe(0.5); expect(canvas.constrain(2, 0, 1)).toBe(1); expect(canvas.constrain(2, 1, 0)).toBe(1); expect(canvas.constrain(-2, 0, 1)).toBe(0); expect(canvas.constrain(10, 1, 1)).toBe(1); }); }); describe('map', () => { it('should map a number from a given range to another range', () => { expect(canvas.map(0.5, 0, 1, 0, 10)).toBe(5); expect(canvas.map(0.5, 0, 1, 1, 0)).toBe(0.5); expect(canvas.map(0.5, 0, 1, 0, -1)).toBe(-0.5); expect(canvas.map(6, 2, 4, 4, 10)).toBe(16); expect(canvas.map(3, 2, 4, 4, 10)).toBe(7); }); }); describe('version', () => { beforeEach(() => { jest.spyOn(console, 'info').mockImplementation(jest.fn()); }); afterEach(() => { (console.info as jest.Mock<any>).mockReset(); }); const { version } = require('../package.json'); it('should return the current version of Canvasimo', () => { expect(typeof version).toBe('string'); expect(version.length).toBeGreaterThanOrEqual(5); expect(canvas.getVersion()).toBe(version); expect(canvas.version()).toBe(version); }); it('should log to console if logInfo parameter is true', () => { canvas.version(); expect(console.info).not.toHaveBeenCalled(); canvas.version(true); expect(console.info).toHaveBeenCalledTimes(1); expect(console.info).toHaveBeenCalledWith(`Canvasimo: Using version ${version}`); }); }); });