import 'reflect-metadata'; import { ElementRef, QueryList, SimpleChange, SimpleChanges, } from '@angular/core'; import { of } from 'rxjs'; import { requiredMocks, } from './../../../test-mocks'; requiredMocks(jest); import * as mock from '../../mocks/index'; const initCanvasRenderingContext2d = mock.canvasRenderingContext2d(jest); const initCanvasUtilitiesService = mock.canvasUtilitiesService(jest); import { BrowserDetectorService, CanvasUtilitiesService, ImagePreloaderService, WindowResizeEventsService, } from './../../helpers/services/index'; import { CanvasImageOnMaskComponent, } from './canvas-image-on-mask.component'; import { EnclosingQuadrilateral, ImageEditorTextInterface, MaskImageMapInterface, } from '../../models'; const initCanvasImageOnMaskComponent = ( canvasUtilitiesService = initCanvasUtilitiesService(), imagePreloaderService?: ImagePreloaderService, browserDetectorService?: BrowserDetectorService, windowResizeEventsService?: WindowResizeEventsService, document?: Document, ) => { return new CanvasImageOnMaskComponent( canvasUtilitiesService, imagePreloaderService, browserDetectorService, windowResizeEventsService, document, ); }; describe('defaults', () => { test('Sets backgroundScale a default value', () => { const canvasImageOnMaskComponent = initCanvasImageOnMaskComponent(); expect(canvasImageOnMaskComponent.backgroundScale).toBe(1); }); }); describe('ngOnChanges', () => { const runNgOnChangesTest = ({ simpleChanges = {}, }: { simpleChanges?: SimpleChanges, } = {}) => { const canvasImageOnMaskComponent = initCanvasImageOnMaskComponent(); canvasImageOnMaskComponent.loadAndRender = jest.fn(); canvasImageOnMaskComponent.ngOnChanges(simpleChanges); return { canvasImageOnMaskComponent, }; }; test('Calls loadAndRender if the changes do not include the maskImagesMap', () => { const { canvasImageOnMaskComponent, } = runNgOnChangesTest(); expect(canvasImageOnMaskComponent.loadAndRender).toHaveBeenCalled(); }); test('Calls loadAndRender if the changes do include the maskImagesMap and it is not the first change', () => { const { canvasImageOnMaskComponent, } = runNgOnChangesTest({ simpleChanges: { maskImagesMap: { firstChange: false, } as SimpleChange, }, }); expect(canvasImageOnMaskComponent.loadAndRender).toHaveBeenCalled(); }); test('Does not call loadAndRender if the changes do include the maskImagesMap but it is the first change', () => { const { canvasImageOnMaskComponent, } = runNgOnChangesTest({ simpleChanges: { maskImagesMap: { firstChange: true, } as SimpleChange, }, }); expect(canvasImageOnMaskComponent.loadAndRender).not.toHaveBeenCalled(); }); }); describe('ngAfterViewInit', () => { const initNgAfterViewInitData = () => { const windowResizeEventsService = {} as WindowResizeEventsService; const browserDetectorService = {} as BrowserDetectorService; browserDetectorService.isSafari = jest.fn().mockReturnValue(false); const mockAddEvent = jest.fn(); windowResizeEventsService.addEvent = mockAddEvent; const canvasImageOnMaskComponent = initCanvasImageOnMaskComponent( undefined, undefined, browserDetectorService, windowResizeEventsService, ); canvasImageOnMaskComponent.initCanvasSize = jest.fn(); canvasImageOnMaskComponent.loadAndRender = jest.fn(); canvasImageOnMaskComponent.ngAfterViewInit(); return { canvasImageOnMaskComponent, mockAddEvent, windowResizeEventsService, }; }; test('Calls initCanvasSize', () => { const { canvasImageOnMaskComponent, } = initNgAfterViewInitData(); expect(canvasImageOnMaskComponent.initCanvasSize) .toHaveBeenCalledTimes(1); }); test('Calls loadAndRender', () => { const { canvasImageOnMaskComponent, } = initNgAfterViewInitData(); expect(canvasImageOnMaskComponent.loadAndRender) .toHaveBeenCalledTimes(1); }); test('Calls windowResizeEventsService.removeEvent', () => { const { mockAddEvent, } = initNgAfterViewInitData(); expect(mockAddEvent) .toHaveBeenCalledTimes(1); }); test('Calls initCanvasSize again after the window resizes', () => { const { canvasImageOnMaskComponent, mockAddEvent, } = initNgAfterViewInitData(); mockAddEvent.mock.calls[0][0](); expect(canvasImageOnMaskComponent.initCanvasSize) .toHaveBeenCalledTimes(2); }); test('Calls loadAndRender again after the window resizes', () => { const { canvasImageOnMaskComponent, mockAddEvent, } = initNgAfterViewInitData(); mockAddEvent.mock.calls[0][0](); expect(canvasImageOnMaskComponent.loadAndRender) .toHaveBeenCalledTimes(2); }); }); describe('ngOnDestroy', () => { test('Removes the window on resize event', () => { const windowResizeEventsService = {} as WindowResizeEventsService; windowResizeEventsService.removeEvent = jest.fn(); const canvasImageOnMaskComponent = initCanvasImageOnMaskComponent( undefined, undefined, undefined, windowResizeEventsService, ); canvasImageOnMaskComponent.ngOnDestroy(); expect(windowResizeEventsService.removeEvent).toHaveBeenCalled(); }); }); describe('loadAndRender', () => { const runLoadAndRenderTest = ({ loadedImages = [{}] as HTMLImageElement[], }: { loadedImages?: HTMLImageElement | HTMLImageElement[], } = {}) => { const canvasImageOnMaskComponent = initCanvasImageOnMaskComponent(); canvasImageOnMaskComponent.initCanvasSize = jest.fn(); canvasImageOnMaskComponent.loadAllImages = jest.fn() .mockReturnValue(of(loadedImages)); canvasImageOnMaskComponent.renderAllUserImages = jest.fn(); canvasImageOnMaskComponent.loadAndRender(); return { canvasImageOnMaskComponent, loadedImages, }; }; test('Calls loadAllImages', () => { const { canvasImageOnMaskComponent, } = runLoadAndRenderTest(); canvasImageOnMaskComponent.loadAndRender(); expect(canvasImageOnMaskComponent.loadAllImages) .toHaveBeenCalled(); }); // tslint:disable-next-line test('Calls renderUserImage with the loaded images after loadAllImages is complete', () => { const { canvasImageOnMaskComponent, loadedImages, } = runLoadAndRenderTest(); canvasImageOnMaskComponent.loadAndRender(); expect(canvasImageOnMaskComponent.renderAllUserImages) .toHaveBeenCalledWith(loadedImages); }); // tslint:disable-next-line test('Calls renderUserImage with an array of the single image if only one is returned from loadAllImages', () => { const loadedImage = {} as HTMLImageElement; const { canvasImageOnMaskComponent, } = runLoadAndRenderTest({ loadedImages: loadedImage, }); expect(canvasImageOnMaskComponent.renderAllUserImages) .toHaveBeenCalledWith([loadedImage]); }); }); describe('initCanvasSize', () => { test('Sets the height of the canvas to the canvas offsetHeight', () => { const canvasImageOnMaskComponent = initCanvasImageOnMaskComponent(); canvasImageOnMaskComponent.imageCanvas = { nativeElement: { offsetHeight: 15, offsetWidth: 10, }, } as ElementRef; canvasImageOnMaskComponent.initCanvasSize(); expect(canvasImageOnMaskComponent.imageCanvas.nativeElement.height) .toBe(15); }); test('Sets the width of the canvas to the canvas offsetHeight', () => { const canvasImageOnMaskComponent = initCanvasImageOnMaskComponent(); canvasImageOnMaskComponent.imageCanvas = { nativeElement: { offsetHeight: 15, offsetWidth: 10, }, } as ElementRef; canvasImageOnMaskComponent.initCanvasSize(); expect(canvasImageOnMaskComponent.imageCanvas.nativeElement.width) .toBe(10); }); }); describe('loadAllImages', () => { const initLoadAllImagesData = () => { const imagePreloaderService = {} as ImagePreloaderService; imagePreloaderService.load = jest.fn(); const canvasImageOnMaskComponent = initCanvasImageOnMaskComponent( undefined, imagePreloaderService, ); canvasImageOnMaskComponent.maskImagesMap = [{ imageEditorImage: { urlToRender: '1', }, mask: { mask: '3', }, }, { imageEditorImage: { urlToRender: '2', }, mask: { mask: '4', }, }] as MaskImageMapInterface[]; canvasImageOnMaskComponent.backgroundUrl = 'http://background.com'; return { canvasImageOnMaskComponent, imagePreloaderService, }; }; test('Calls imagePreloaderService with all the images to load', () => { const { canvasImageOnMaskComponent, imagePreloaderService, } = initLoadAllImagesData(); canvasImageOnMaskComponent.loadAllImages(); expect(imagePreloaderService.load).toHaveBeenCalledWith( [ canvasImageOnMaskComponent.backgroundUrl, '1', '2', '3', '4', undefined, ], ); }); // tslint:disable-next-line test('Calls imagePreloaderService with the foregroundUrl if it exists', () => { const { canvasImageOnMaskComponent, imagePreloaderService, } = initLoadAllImagesData(); canvasImageOnMaskComponent.foregroundUrl = 'overlayImage'; canvasImageOnMaskComponent.loadAllImages(); expect(imagePreloaderService.load).toHaveBeenCalledWith( [ canvasImageOnMaskComponent.backgroundUrl, '1', '2', '3', '4', 'overlayImage', ], ); }); // tslint:disable-next-line test('Does not error if some of the maskImages have no imageEditorImage', () => { const { canvasImageOnMaskComponent, } = initLoadAllImagesData(); canvasImageOnMaskComponent.maskImagesMap = [{ imageEditorImage: { urlToRender: 'url', }, mask: { mask: 'mask', }, }, { mask: { mask: 'otherMask', }, }] as MaskImageMapInterface[]; expect(() => canvasImageOnMaskComponent.loadAllImages()) .not.toThrow(); }); }); describe('clearCanvas', () => { test('Get the 2d context of the canvas', () => { const canvasImageOnMaskComponent = initCanvasImageOnMaskComponent(); const canvas = {} as HTMLCanvasElement; const canvasContext = {} as CanvasRenderingContext2D; canvasContext.clearRect = jest.fn(); canvas.getContext = jest.fn().mockReturnValue(canvasContext); canvasImageOnMaskComponent.clearCanvas( canvas, ); expect(canvas.getContext).toHaveBeenCalledWith('2d'); }); test('Clears the canvas', () => { const canvasImageOnMaskComponent = initCanvasImageOnMaskComponent(); const canvas = { height: 15, width: 10, } as HTMLCanvasElement; const canvasContext = {} as CanvasRenderingContext2D; canvasContext.clearRect = jest.fn(); canvas.getContext = jest.fn().mockReturnValue(canvasContext); canvasImageOnMaskComponent.clearCanvas( canvas, ); expect(canvasContext.clearRect).toHaveBeenCalledWith( 0, 0, 10, 15, ); }); }); describe('renderBackgroundImageOnMask', () => { const initRenderMaskOverlay = () => { const browserDetectorService = {} as BrowserDetectorService; browserDetectorService.isIE = jest.fn().mockReturnValue(false); const canvasImageOnMaskComponent = initCanvasImageOnMaskComponent( undefined, undefined, browserDetectorService, ); const loadedImages = [{ src: 'http://background.com', }, { src: 'loadedImage2', }] as HTMLImageElement[]; canvasImageOnMaskComponent.drawOnCanvas = jest.fn(); const canvasCtx = {} as CanvasRenderingContext2D; canvasCtx.drawImage = jest.fn(); canvasCtx.fillRect = jest.fn(); const canvas = { height: 10, width: 15, } as HTMLCanvasElement; canvas.getContext = jest.fn().mockReturnValue(canvasCtx); return { browserDetectorService, canvas, canvasCtx, canvasImageOnMaskComponent, loadedImages, }; }; test('Does not error if backgroundImageUrl is not defined', () => { const canvasImageOnMaskComponent = initCanvasImageOnMaskComponent(); expect(() => { canvasImageOnMaskComponent.renderBackgroundImageOnMask( undefined, undefined, undefined, undefined, '#000000', ); }).not.toThrow(); }); test('Does not draw an overlay if the browser is IE', () => { const { browserDetectorService, canvas, canvasImageOnMaskComponent, } = initRenderMaskOverlay(); browserDetectorService.isIE = jest.fn().mockReturnValue(true); canvasImageOnMaskComponent.renderBackgroundImageOnMask( canvas, 'http://background.com', 1, [], '#000000', ); expect(canvasImageOnMaskComponent.drawOnCanvas).not.toHaveBeenCalled(); }); test('Calls browserDetectorService.isIE', () => { const { browserDetectorService, canvas, canvasImageOnMaskComponent, } = initRenderMaskOverlay(); canvasImageOnMaskComponent.renderBackgroundImageOnMask( canvas, 'http://background.com', 1, [], '#000000', ); expect(browserDetectorService.isIE).toHaveBeenCalled(); }); test('Gets the 2d context of the canvas', () => { const { canvasImageOnMaskComponent, canvas, loadedImages, } = initRenderMaskOverlay(); canvasImageOnMaskComponent.renderBackgroundImageOnMask( canvas, 'http://background.com', 1, loadedImages, '#000000', ); expect(canvas.getContext).toHaveBeenCalledWith('2d'); }); test('Calls drawOnCanvas with the expected arguments', () => { const { canvasImageOnMaskComponent, canvas, canvasCtx, loadedImages, } = initRenderMaskOverlay(); canvasImageOnMaskComponent.renderBackgroundImageOnMask( canvas, 'http://background.com', 1, loadedImages, '#000000', ); expect(canvasImageOnMaskComponent.drawOnCanvas).toHaveBeenCalledWith( canvasCtx, loadedImages[0], true, 0, 0, canvas.width, canvas.height, 1, ); }); test('Sets globalCompositeOperation to source in', () => { const { canvasImageOnMaskComponent, canvas, canvasCtx, loadedImages, } = initRenderMaskOverlay(); canvasImageOnMaskComponent.renderBackgroundImageOnMask( canvas, 'http://background.com', 1, loadedImages, '#000000', ); expect(canvasCtx.globalCompositeOperation).toBe('source-in'); }); test('Sets the fill style to colorOverlay', () => { const { canvasImageOnMaskComponent, canvas, canvasCtx, loadedImages, } = initRenderMaskOverlay(); canvasImageOnMaskComponent.renderBackgroundImageOnMask( canvas, 'http://background.com', 1, loadedImages, '#000000', ); expect(canvasCtx.fillStyle).toBe('#000000'); }); test('Draws the color overlay on the canvas', () => { const { canvasImageOnMaskComponent, canvas, canvasCtx, loadedImages, } = initRenderMaskOverlay(); canvasImageOnMaskComponent.renderBackgroundImageOnMask( canvas, 'http://background.com', 1, loadedImages, '#000000', ); expect(canvasCtx.fillRect).toHaveBeenCalledWith( 0, 0, canvas.width, canvas.height, ); }); // tslint:disable-next-line test('With no color overlay does not set the globalCompositeOperation', () => { const { canvasImageOnMaskComponent, canvas, canvasCtx, loadedImages, } = initRenderMaskOverlay(); canvasImageOnMaskComponent.renderBackgroundImageOnMask( canvas, 'http://background.com', 1, loadedImages, ); expect(canvasCtx.globalCompositeOperation).toBeUndefined(); }); // tslint:disable-next-line test('With no color overlay does not set the fillStyle', () => { const { canvasImageOnMaskComponent, canvas, canvasCtx, loadedImages, } = initRenderMaskOverlay(); canvasImageOnMaskComponent.renderBackgroundImageOnMask( canvas, 'http://background.com', 1, loadedImages, ); expect(canvasCtx.fillStyle).toBeUndefined(); }); test('With no color overlay do not draw any rectangle on the canvas', () => { const { canvasImageOnMaskComponent, canvas, canvasCtx, loadedImages, } = initRenderMaskOverlay(); canvasImageOnMaskComponent.renderBackgroundImageOnMask( canvas, 'http://background.com', 1, loadedImages, ); expect(canvasCtx.fillRect).not.toHaveBeenCalled(); }); }); describe('renderUserImageOnMask', () => { const initRenderUserImageOnMaskData = () => { const firstHtmlImage = { src: 'firstImage', } as HTMLImageElement; const secondHtmlImage = { src: 'secondImage', } as HTMLImageElement; const canvasUtilitiesService = {} as CanvasUtilitiesService; canvasUtilitiesService.getImageWithUrl = jest.fn() .mockReturnValueOnce(firstHtmlImage) .mockReturnValueOnce(secondHtmlImage); const scaledEnclosingQuad = { size: { height: 200, width: 100, }, topLeft: {x: 10, y: 10}, }; canvasUtilitiesService.getScaledEnclosingQuad = jest.fn().mockReturnValue(scaledEnclosingQuad); canvasUtilitiesService.calculateScale = jest.fn(); const canvasCoordX = 50; const canvasCoordY = 60; canvasUtilitiesService.toCanvasCoordinateSystem = jest.fn() .mockReturnValueOnce(canvasCoordX) .mockReturnValueOnce(canvasCoordY); const mockTempCanvasContext = {} as CanvasRenderingContext2D; const mockTempCanvas = {} as HTMLCanvasElement; mockTempCanvas.getContext = jest.fn().mockReturnValue( mockTempCanvasContext, ); mockTempCanvasContext.strokeRect = jest.fn(); const canvasImageOnMaskComponent = initCanvasImageOnMaskComponent( canvasUtilitiesService, undefined, undefined, undefined, document, ); canvasImageOnMaskComponent.generateTemporaryCanvas = jest.fn() .mockReturnValueOnce(mockTempCanvas); const assetSize = { height: 500, width: 1000, }; canvasImageOnMaskComponent.assetSize = assetSize; canvasImageOnMaskComponent.drawOnCanvas = jest.fn(); canvasImageOnMaskComponent.imageOverSpillBorderColor = '#CCCCCC'; const maskImageMap = { imageEditorImage: { mirror: true, rotate_degrees: 50, scale: 1, tx: 5, ty: 11, urlToRender: 'urlToRenderTest', }, mask: { bleed_area: { maskScale: 5, }, mask: 'maskUrl', }, } as MaskImageMapInterface; const canvasCtx = {} as CanvasRenderingContext2D; const canvas = { height: 100, width: 50, } as HTMLCanvasElement; canvas.getContext = jest.fn().mockReturnValue(canvasCtx); canvasCtx.drawImage = jest.fn(); canvasCtx.strokeRect = jest.fn(); const loadedImages = [{}] as HTMLImageElement[]; const dpi = 300; canvasImageOnMaskComponent.dpi = dpi; return { canvas, canvasCoordX, canvasCoordY, canvasCtx, canvasImageOnMaskComponent, canvasUtilitiesService, document, firstHtmlImage, loadedImages, maskImageMap, mockTempCanvas, mockTempCanvasContext, scaledEnclosingQuad, secondHtmlImage, dpi, assetSize, }; }; const initMaskImageMapWithText = () => ({ imageEditorImage: { mirror: true, rotate_degrees: 50, scale: 1, text: 'Some text', textColorHex: '#FFFFFF', textFontFamily: 'sans serif', textFontSize: 10, tx: 5, ty: 11, }, mask: { bleed_area: { maskScale: 5, }, mask: 'maskUrl', }, } as MaskImageMapInterface); // tslint:disable-next-line test('Calls canvasUtilitiesService.getImageWithUrl for the user image url ', () => { const { canvas, canvasImageOnMaskComponent, canvasUtilitiesService, maskImageMap, loadedImages, } = initRenderUserImageOnMaskData(); canvasImageOnMaskComponent.renderUserImageOnMask( canvas, maskImageMap, loadedImages, ); expect(canvasUtilitiesService.getImageWithUrl).toHaveBeenCalledWith( loadedImages, maskImageMap.imageEditorImage.urlToRender, ); }); // tslint:disable-next-line test('Calls canvasUtilitiesService.getImageWithUrl for the mask image url', () => { const { canvas, canvasImageOnMaskComponent, canvasUtilitiesService, maskImageMap, loadedImages, } = initRenderUserImageOnMaskData(); canvasImageOnMaskComponent.renderUserImageOnMask( canvas, maskImageMap, loadedImages, ); expect(canvasUtilitiesService.getImageWithUrl).toHaveBeenCalledWith( loadedImages, maskImageMap.mask.mask, ); }); // tslint:disable-next-line test('Does not call drawOnCanvas if the component has no user image loaded', () => { const { canvas, canvasImageOnMaskComponent, canvasUtilitiesService, maskImageMap, loadedImages, } = initRenderUserImageOnMaskData(); canvasUtilitiesService.getImageWithUrl = jest.fn() .mockReturnValueOnce(undefined) .mockReturnValueOnce({} as HTMLImageElement); canvasImageOnMaskComponent.renderUserImageOnMask( canvas, maskImageMap, loadedImages, ); expect(canvasImageOnMaskComponent.drawOnCanvas).not.toHaveBeenCalled(); }); // tslint:disable-next-line test('Does not call drawOnCanvas if maskImageMap.imageEditorImage is null', () => { const { canvas, canvasImageOnMaskComponent, maskImageMap, loadedImages, } = initRenderUserImageOnMaskData(); maskImageMap.imageEditorImage = null; canvasImageOnMaskComponent.renderUserImageOnMask( canvas, maskImageMap, loadedImages, ); expect(canvasImageOnMaskComponent.drawOnCanvas).not.toHaveBeenCalled(); }); // tslint:disable-next-line test('Does not call drawOnCanvas if the component has no mask image loaded', () => { const { canvas, canvasImageOnMaskComponent, canvasUtilitiesService, maskImageMap, loadedImages, } = initRenderUserImageOnMaskData(); canvasUtilitiesService.getImageWithUrl = jest.fn() .mockReturnValueOnce({} as HTMLImageElement) .mockReturnValueOnce(undefined); canvasImageOnMaskComponent.renderUserImageOnMask( canvas, maskImageMap, loadedImages, ); expect(canvasImageOnMaskComponent.drawOnCanvas).not.toHaveBeenCalled(); }); test('Creates a temporary canvas', () => { const { canvas, canvasImageOnMaskComponent, maskImageMap, loadedImages, } = initRenderUserImageOnMaskData(); canvasImageOnMaskComponent.renderUserImageOnMask( canvas, maskImageMap, loadedImages, ); expect( canvasImageOnMaskComponent.generateTemporaryCanvas, ).toHaveBeenCalledWith(canvas); }); test('Calls drawOnCanvas with the maskImage', () => { const { canvas, canvasImageOnMaskComponent, maskImageMap, mockTempCanvasContext, mockTempCanvas, secondHtmlImage, loadedImages, } = initRenderUserImageOnMaskData(); canvasImageOnMaskComponent.renderUserImageOnMask( canvas, maskImageMap, loadedImages, ); expect(canvasImageOnMaskComponent.drawOnCanvas).toHaveBeenCalledWith( mockTempCanvasContext, secondHtmlImage, true, 0, 0, mockTempCanvas.width, mockTempCanvas.height, maskImageMap.mask.bleed_area.maskScale, ); }); test('Gets the scaledEnclosingQuad for the mask', () => { const { canvas, canvasImageOnMaskComponent, canvasUtilitiesService, maskImageMap, mockTempCanvas, secondHtmlImage, loadedImages, dpi, assetSize, } = initRenderUserImageOnMaskData(); canvasImageOnMaskComponent.renderUserImageOnMask( canvas, maskImageMap, loadedImages, ); expect(canvasUtilitiesService.getScaledEnclosingQuad) .toHaveBeenCalledWith( mockTempCanvas, secondHtmlImage, maskImageMap, assetSize, dpi, ); }); test('Gets the canvas coord x for placing the user image', () => { const { canvas, canvasImageOnMaskComponent, canvasUtilitiesService, maskImageMap, mockTempCanvas, secondHtmlImage, loadedImages, dpi, } = initRenderUserImageOnMaskData(); canvasImageOnMaskComponent.renderUserImageOnMask( canvas, maskImageMap, loadedImages, ); expect(canvasUtilitiesService.toCanvasCoordinateSystem) .toHaveBeenCalledWith( mockTempCanvas, maskImageMap.imageEditorImage.tx, maskImageMap, secondHtmlImage, canvasImageOnMaskComponent.assetSize, dpi, ); }); test('Gets the canvas coord y for placing the user image', () => { const { canvas, canvasImageOnMaskComponent, canvasUtilitiesService, maskImageMap, mockTempCanvas, secondHtmlImage, loadedImages, dpi, } = initRenderUserImageOnMaskData(); canvasImageOnMaskComponent.renderUserImageOnMask( canvas, maskImageMap, loadedImages, ); expect(canvasUtilitiesService.toCanvasCoordinateSystem) .toHaveBeenCalledWith( mockTempCanvas, maskImageMap.imageEditorImage.ty, maskImageMap, secondHtmlImage, canvasImageOnMaskComponent.assetSize, dpi, ); }); test('Calls drawOnCanvas with the userImage', () => { const { canvas, canvasCoordX, canvasCoordY, canvasImageOnMaskComponent, maskImageMap, mockTempCanvasContext, firstHtmlImage, loadedImages, scaledEnclosingQuad, } = initRenderUserImageOnMaskData(); canvasImageOnMaskComponent.renderUserImageOnMask( canvas, maskImageMap, loadedImages, ); expect(canvasImageOnMaskComponent.drawOnCanvas).toHaveBeenCalledWith( mockTempCanvasContext, firstHtmlImage, false, canvasCoordX + scaledEnclosingQuad.topLeft.x, canvasCoordY + scaledEnclosingQuad.topLeft.y, scaledEnclosingQuad.size.width, scaledEnclosingQuad.size.height, maskImageMap.imageEditorImage.scale, maskImageMap.imageEditorImage.mirror, maskImageMap.imageEditorImage.rotate_degrees, undefined, undefined, undefined, undefined, ); }); test('Calls drawOnCanvas with the text variables if supplied', () => { const { canvas, canvasCoordX, canvasCoordY, canvasImageOnMaskComponent, loadedImages, mockTempCanvasContext, scaledEnclosingQuad, firstHtmlImage, } = initRenderUserImageOnMaskData(); const maskImageMap = initMaskImageMapWithText(); const imageEditorText = maskImageMap.imageEditorImage as ImageEditorTextInterface; canvasImageOnMaskComponent.renderUserImageOnMask( canvas, maskImageMap, loadedImages, ); expect(canvasImageOnMaskComponent.drawOnCanvas).toHaveBeenCalledWith( mockTempCanvasContext, firstHtmlImage, false, canvasCoordX + scaledEnclosingQuad.topLeft.x, canvasCoordY + scaledEnclosingQuad.topLeft.y, scaledEnclosingQuad.size.width, scaledEnclosingQuad.size.height, maskImageMap.imageEditorImage.scale, maskImageMap.imageEditorImage.mirror, maskImageMap.imageEditorImage.rotate_degrees, imageEditorText.text, imageEditorText.textColorHex, imageEditorText.textFontFamily, imageEditorText.textFontSize, ); }); // tslint:disable-next-line test('Calls drawOnCanvas to overlay the userImage overspill if showImageOverspill is true', () => { const { canvas, canvasCoordX, canvasCoordY, canvasImageOnMaskComponent, firstHtmlImage, loadedImages, mockTempCanvasContext, scaledEnclosingQuad, } = initRenderUserImageOnMaskData(); const maskImageMap = initMaskImageMapWithText(); canvasImageOnMaskComponent.showImageOverspill = true; const imageEditorText = maskImageMap.imageEditorImage as ImageEditorTextInterface; canvasImageOnMaskComponent.renderUserImageOnMask( canvas, maskImageMap, loadedImages, ); expect(canvasImageOnMaskComponent.drawOnCanvas).toHaveBeenCalledWith( mockTempCanvasContext, firstHtmlImage, false, canvasCoordX + scaledEnclosingQuad.topLeft.x, canvasCoordY + scaledEnclosingQuad.topLeft.y, scaledEnclosingQuad.size.width, scaledEnclosingQuad.size.height, maskImageMap.imageEditorImage.scale, maskImageMap.imageEditorImage.mirror, maskImageMap.imageEditorImage.rotate_degrees, imageEditorText.text, imageEditorText.textColorHex, imageEditorText.textFontFamily, imageEditorText.textFontSize, 0.1, canvasImageOnMaskComponent.imageOverSpillBorderColor, ); }); test('Calls drawOnCanvas to overlay the text overspill if showImageOverspill is true', () => { const { canvas, canvasCoordX, canvasCoordY, canvasImageOnMaskComponent, maskImageMap, mockTempCanvasContext, firstHtmlImage, loadedImages, scaledEnclosingQuad, } = initRenderUserImageOnMaskData(); canvasImageOnMaskComponent.showImageOverspill = true; canvasImageOnMaskComponent.renderUserImageOnMask( canvas, maskImageMap, loadedImages, ); expect(canvasImageOnMaskComponent.drawOnCanvas).toHaveBeenCalledWith( mockTempCanvasContext, firstHtmlImage, false, canvasCoordX + scaledEnclosingQuad.topLeft.x, canvasCoordY + scaledEnclosingQuad.topLeft.y, scaledEnclosingQuad.size.width, scaledEnclosingQuad.size.height, maskImageMap.imageEditorImage.scale, maskImageMap.imageEditorImage.mirror, maskImageMap.imageEditorImage.rotate_degrees, undefined, undefined, undefined, undefined, 0.1, canvasImageOnMaskComponent.imageOverSpillBorderColor, ); }); test('Gets the canvas 2d context', () => { const { canvas, canvasImageOnMaskComponent, maskImageMap, loadedImages, } = initRenderUserImageOnMaskData(); canvasImageOnMaskComponent.renderUserImageOnMask( canvas, maskImageMap, loadedImages, ); expect(canvas.getContext).toHaveBeenCalledWith('2d'); }); // tslint:disable-next-line test('Sets the globalCompositeOperation of the original canvas to source-over', () => { const { canvas, canvasCtx, canvasImageOnMaskComponent, maskImageMap, loadedImages, } = initRenderUserImageOnMaskData(); canvasImageOnMaskComponent.renderUserImageOnMask( canvas, maskImageMap, loadedImages, ); expect(canvasCtx.globalCompositeOperation).toBe('source-over'); }); test('Draws the new temporary canvas content on the original canvas', () => { const { canvas, canvasCtx, canvasImageOnMaskComponent, maskImageMap, mockTempCanvas, loadedImages, } = initRenderUserImageOnMaskData(); canvasImageOnMaskComponent.renderUserImageOnMask( canvas, maskImageMap, loadedImages, ); expect(canvasCtx.drawImage).toHaveBeenCalledWith( mockTempCanvas, 0, 0, canvas.width, canvas.height, ); }); }); describe('renderMaskOverlay', () => { test('Does not error if foregroundUrl is not defined', () => { const canvasImageOnMaskComponent = initCanvasImageOnMaskComponent(); canvasImageOnMaskComponent.foregroundUrl = undefined; expect(() => { canvasImageOnMaskComponent.renderMaskOverlay( undefined, undefined, ); }).not.toThrow(); }); test('Does not draw an overlay if the browser is IE', () => { const browserDetectorService = {} as BrowserDetectorService; browserDetectorService.isIE = jest.fn().mockReturnValue(true); const canvasImageOnMaskComponent = initCanvasImageOnMaskComponent( undefined, undefined, browserDetectorService, ); canvasImageOnMaskComponent.foregroundUrl = 'https://loadimaged'; canvasImageOnMaskComponent.drawOnCanvas = jest.fn(); canvasImageOnMaskComponent.renderMaskOverlay( {} as HTMLCanvasElement, [], ); expect(canvasImageOnMaskComponent.drawOnCanvas).not.toHaveBeenCalled(); }); const initRenderMaskOverlay = () => { const browserDetectorService = {} as BrowserDetectorService; browserDetectorService.isIE = jest.fn().mockReturnValue(false); const canvasImageOnMaskComponent = initCanvasImageOnMaskComponent( undefined, undefined, browserDetectorService, ); const loadedImages = [{ src: 'loadedImage1', }, { src: 'https://loadimaged', }] as HTMLImageElement[]; canvasImageOnMaskComponent.drawOnCanvas = jest.fn(); const canvasCtx = {} as CanvasRenderingContext2D; const canvas = { height: 10, width: 15, } as HTMLCanvasElement; canvas.getContext = jest.fn().mockReturnValue(canvasCtx); canvasImageOnMaskComponent.foregroundUrl = 'https://loadimaged'; canvasImageOnMaskComponent.backgroundScale = 2; return { browserDetectorService, canvas, canvasCtx, canvasImageOnMaskComponent, loadedImages, }; }; test('Calls browserDetectorService.isIE', () => { const { browserDetectorService, canvasImageOnMaskComponent, canvas, loadedImages, } = initRenderMaskOverlay(); canvasImageOnMaskComponent.renderMaskOverlay( canvas, loadedImages, ); expect(browserDetectorService.isIE).toHaveBeenCalled(); }); test('Gets the 2d context of the canvas', () => { const { canvasImageOnMaskComponent, canvas, loadedImages, } = initRenderMaskOverlay(); canvasImageOnMaskComponent.renderMaskOverlay( canvas, loadedImages, ); expect(canvas.getContext).toHaveBeenCalledWith('2d'); }); // tslint:disable-next-line test('Sets the canvasCtx globalCompositeOperation to source-over if foregroundBlendMode is undefined', () => { const { canvasImageOnMaskComponent, canvas, canvasCtx, loadedImages, } = initRenderMaskOverlay(); canvasImageOnMaskComponent.foregroundBlendMode = undefined; canvasImageOnMaskComponent.renderMaskOverlay( canvas, loadedImages, ); expect(canvasCtx.globalCompositeOperation).toBe('source-over'); }); // tslint:disable-next-line test('Sets the canvasCtx globalCompositeOperation to the value of foregroundBlendMode', () => { const { canvasImageOnMaskComponent, canvas, canvasCtx, loadedImages, } = initRenderMaskOverlay(); canvasImageOnMaskComponent.foregroundBlendMode = 'multiply'; canvasImageOnMaskComponent.renderMaskOverlay( canvas, loadedImages, ); expect(canvasCtx.globalCompositeOperation).toBe('multiply'); }); // tslint:disable-next-line test('Sets the canvasCtx globalCompositeOperation to source-over if the value of foregroundBlendMode is normal', () => { const { canvasImageOnMaskComponent, canvas, canvasCtx, loadedImages, } = initRenderMaskOverlay(); canvasImageOnMaskComponent.foregroundBlendMode = 'normal'; canvasImageOnMaskComponent.renderMaskOverlay( canvas, loadedImages, ); expect(canvasCtx.globalCompositeOperation).toBe('source-over'); }); test('Calls drawOnCanvas with the expected arguments', () => { const { canvasImageOnMaskComponent, canvas, canvasCtx, loadedImages, } = initRenderMaskOverlay(); canvasImageOnMaskComponent.renderMaskOverlay( canvas, loadedImages, ); expect(canvasImageOnMaskComponent.drawOnCanvas).toHaveBeenCalledWith( canvasCtx, loadedImages[1], true, 0, 0, canvas.width, canvas.height, canvasImageOnMaskComponent.backgroundScale, ); }); }); describe('renderAllUserImages', () => { const initRenderAllUserImagesData = () => { const canvasImageOnMaskComponent = initCanvasImageOnMaskComponent(); canvasImageOnMaskComponent.imageCanvas = { nativeElement: {}, }; canvasImageOnMaskComponent.maskImagesMap = [{}, {}] as MaskImageMapInterface[]; canvasImageOnMaskComponent.colorOverlay = '#000000'; canvasImageOnMaskComponent.clearCanvas = jest.fn(); canvasImageOnMaskComponent.renderBackgroundImageOnMask = jest.fn(); canvasImageOnMaskComponent.renderUserImageOnMask = jest.fn(); canvasImageOnMaskComponent.renderMaskOverlay = jest.fn(); const loadedImages = [{}] as HTMLImageElement[]; return { canvasImageOnMaskComponent, loadedImages, }; }; test('Calls clearCanvas on the canvas', () => { const { canvasImageOnMaskComponent, } = initRenderAllUserImagesData(); canvasImageOnMaskComponent.renderAllUserImages([]); expect(canvasImageOnMaskComponent.clearCanvas).toHaveBeenCalledWith( canvasImageOnMaskComponent.canvas, ); }); test('Calls renderUserImageOnMask for each maskImageMap', () => { const { canvasImageOnMaskComponent, loadedImages, } = initRenderAllUserImagesData(); canvasImageOnMaskComponent.renderAllUserImages(loadedImages); expect(canvasImageOnMaskComponent.renderUserImageOnMask) .toHaveBeenCalledWith( canvasImageOnMaskComponent.canvas, canvasImageOnMaskComponent.maskImagesMap[0], loadedImages, ); expect(canvasImageOnMaskComponent.renderUserImageOnMask) .toHaveBeenCalledWith( canvasImageOnMaskComponent.canvas, canvasImageOnMaskComponent.maskImagesMap[1], loadedImages, ); }); test('Calls renderMaskOverlay', () => { const { canvasImageOnMaskComponent, loadedImages, } = initRenderAllUserImagesData(); canvasImageOnMaskComponent.renderAllUserImages(loadedImages); expect(canvasImageOnMaskComponent.renderMaskOverlay) .toHaveBeenCalledWith( canvasImageOnMaskComponent.canvas, loadedImages, ); }); test('Calls renderBackgroundImageOnMask', () => { const { canvasImageOnMaskComponent, loadedImages, } = initRenderAllUserImagesData(); canvasImageOnMaskComponent.renderAllUserImages(loadedImages); expect(canvasImageOnMaskComponent.renderBackgroundImageOnMask) .toHaveBeenCalledWith( canvasImageOnMaskComponent.canvas, canvasImageOnMaskComponent.backgroundUrl, canvasImageOnMaskComponent.backgroundScale, loadedImages, canvasImageOnMaskComponent.colorOverlay, ); }); }); describe('drawText', () => { const initDrawTextData = () => { const mockTextWidth = 10; const canvasContext = initCanvasRenderingContext2d(); canvasContext.measureText = jest.fn().mockReturnValue({ width: mockTextWidth, }); const canvasImageOnMaskComponent = initCanvasImageOnMaskComponent(); return { canvasContext, canvasImageOnMaskComponent, mockTextWidth, }; }; const initAndRunDrawText = () => { const { canvasContext, canvasImageOnMaskComponent, mockTextWidth, } = initDrawTextData(); const text = 'some text'; const textColorHex = '#FFFFFF'; const textFontFamily = 'sans serif'; const textFontSize = 12; const scale = 5; const widthToFitWithin = 100; const imageWidth = 200; canvasImageOnMaskComponent.drawText( canvasContext, text, textColorHex, textFontFamily, textFontSize, scale, widthToFitWithin, imageWidth, ); return { canvasContext, canvasImageOnMaskComponent, imageWidth, mockTextWidth, scale, text, textColorHex, textFontFamily, textFontSize, widthToFitWithin, }; }; test('Sets fillStyle to the provided textColorHex', () => { const { canvasContext, textColorHex, } = initAndRunDrawText(); expect(canvasContext.fillStyle).toBe(textColorHex); }); test('Sets the font to the scaled font and font family', () => { const { canvasContext, textFontFamily, } = initAndRunDrawText(); expect(canvasContext.font).toBe(`30px ${textFontFamily}`); }); test('Calls ctx.measureText with the supplied text', () => { const { canvasContext, text, } = initAndRunDrawText(); expect(canvasContext.measureText).toHaveBeenCalledWith(text); }); // tslint:disable-next-line test('Calls ctx.fillText with the supplied text, the result of measuring the text and the size of the text', () => { const { canvasContext, mockTextWidth, text, } = initAndRunDrawText(); expect(canvasContext.fillText).toHaveBeenCalledWith( text, -mockTextWidth / 2, -15, ); }); }); describe('drawImage', () => { test('Calls ctx.drawImage with the image drawn in the center', () => { const canvasContext = initCanvasRenderingContext2d(); const canvasImageOnMaskComponent = initCanvasImageOnMaskComponent(); const image = { height: 30, width: 60, } as HTMLImageElement; canvasImageOnMaskComponent.drawImage( canvasContext, image, 100, 200, ); expect(canvasContext.drawImage).toHaveBeenCalledWith( image, -50, -100, 100, 200, ); }); // tslint:disable-next-line test('Does not calls ctx.drawImage with the image drawn in the center', () => { const canvasContext = initCanvasRenderingContext2d(); const canvasImageOnMaskComponent = initCanvasImageOnMaskComponent(); const image = { height: 30, width: 60, } as HTMLImageElement; canvasImageOnMaskComponent.drawImage( canvasContext, image, 0, 0, ); expect(canvasContext.drawImage).not.toHaveBeenCalled(); }); }); describe('drawImageBorder', () => { const initDrawImageBorderData = () => { const ctx = {} as CanvasRenderingContext2D; ctx.strokeRect = jest.fn(); const canvasImageOnMaskComponent = initCanvasImageOnMaskComponent(); return { canvasImageOnMaskComponent, ctx, }; }; const runDrawImageBorder = () => { const data = initDrawImageBorderData(); data.canvasImageOnMaskComponent.drawImageBorder( '#CCCCCC', data.ctx, 10, 15, ); return data; }; test('Sets the ctx globalAlpha to 1', () => { const { ctx, } = runDrawImageBorder(); expect(ctx.globalAlpha).toBe(1); }); test('Sets ctx lineWidth to 0.5', () => { const { ctx, } = runDrawImageBorder(); expect(ctx.lineWidth).toBe(0.5); }); test('Sets ctx strokeStyle to borderColor', () => { const { ctx, } = runDrawImageBorder(); expect(ctx.strokeStyle).toBe('#CCCCCC'); }); test('Calls ctx.strokeRect to draw the border', () => { const { ctx, } = runDrawImageBorder(); expect(ctx.strokeRect).toHaveBeenCalledWith( -4.5, -7, 9, 14, ); }); }); describe('drawOnCanvas', () => { const initDrawOnCanvasData = () => { const canvasImageOnMaskComponent = initCanvasImageOnMaskComponent(); const canvasContext = initCanvasRenderingContext2d(); const img = { height: 30, width: 60, } as HTMLImageElement; canvasImageOnMaskComponent.drawImage = jest.fn(); canvasImageOnMaskComponent.drawText = jest.fn(); canvasImageOnMaskComponent.drawImageBorder = jest.fn(); return { canvasContext, canvasImageOnMaskComponent, img, }; }; test('Calls canvas context save', () => { const { canvasImageOnMaskComponent, canvasContext, img, } = initDrawOnCanvasData(); canvasImageOnMaskComponent.drawOnCanvas( canvasContext, img, true, 0, 0, 10, 15, ); expect(canvasContext.save).toHaveBeenCalled(); }); test('Translates the canvas context so the image is centered', () => { const { canvasImageOnMaskComponent, canvasContext, img, } = initDrawOnCanvasData(); canvasImageOnMaskComponent.drawOnCanvas( canvasContext, img, true, 0, 0, 10, 15, ); expect(canvasContext.translate).toHaveBeenCalledWith( 5, 7.5, ); }); // tslint:disable-next-line test('Translates the canvas context so the image is centered when the image is higher than it is wide', () => { const { canvasImageOnMaskComponent, canvasContext, img, } = initDrawOnCanvasData(); img.height = 120; canvasImageOnMaskComponent.drawOnCanvas( canvasContext, img, true, 0, 0, 10, 20, ); expect(canvasContext.translate).toHaveBeenCalledWith( 5, 10, ); }); test('Does not scale the canvas context if flipHorizontal is false', () => { const { canvasImageOnMaskComponent, canvasContext, img, } = initDrawOnCanvasData(); canvasImageOnMaskComponent.drawOnCanvas( canvasContext, img, true, 0, 0, 10, 15, 1, false, ); expect(canvasContext.scale).not.toHaveBeenCalled(); }); test('Does not scale the canvas context if flipHorizontal is not set', () => { const { canvasImageOnMaskComponent, canvasContext, img, } = initDrawOnCanvasData(); canvasImageOnMaskComponent.drawOnCanvas( canvasContext, img, true, 0, 0, 10, 15, ); expect(canvasContext.scale).not.toHaveBeenCalled(); }); test('Flips the canvas context if flipHorizontal is true', () => { const { canvasImageOnMaskComponent, canvasContext, img, } = initDrawOnCanvasData(); canvasImageOnMaskComponent.drawOnCanvas( canvasContext, img, true, 0, 0, 10, 15, 1, true, ); expect(canvasContext.scale).toHaveBeenCalledWith(-1, 1); }); test('Sets the canvas context globalAlpha to the opacity argument', () => { const { canvasImageOnMaskComponent, canvasContext, img, } = initDrawOnCanvasData(); canvasImageOnMaskComponent.drawOnCanvas( canvasContext, img, true, 0, 0, 10, 15, 1, true, 0, undefined, undefined, undefined, undefined, 15, ); expect(canvasContext.globalAlpha).toBe(15); }); // tslint:disable-next-line test('Sets the canvas context globalAlpha to 1 if no opacity argument is set', () => { const { canvasImageOnMaskComponent, canvasContext, img, } = initDrawOnCanvasData(); canvasImageOnMaskComponent.drawOnCanvas( canvasContext, img, true, 0, 0, 10, 15, 1, ); expect(canvasContext.globalAlpha).toBe(1); }); // tslint:disable-next-line test('Rotates the canvas context by the rotation argument if it is set', () => { const { canvasImageOnMaskComponent, canvasContext, img, } = initDrawOnCanvasData(); canvasImageOnMaskComponent.drawOnCanvas( canvasContext, img, true, 0, 0, 10, 15, 1, false, 90, ); expect(canvasContext.rotate).toHaveBeenCalledWith( -90 * (Math.PI / 180), ); }); // tslint:disable-next-line test('Rotates the canvas context by 0 if no rotation argument is set', () => { const { canvasImageOnMaskComponent, canvasContext, img, } = initDrawOnCanvasData(); canvasImageOnMaskComponent.drawOnCanvas( canvasContext, img, true, 0, 0, 10, 15, ); expect(canvasContext.rotate).toHaveBeenCalledWith(-0); }); // tslint:disable-next-line test('Calls drawText if the text field is defined', () => { const { canvasImageOnMaskComponent, canvasContext, img, } = initDrawOnCanvasData(); canvasImageOnMaskComponent.drawOnCanvas( canvasContext, img, true, 0, 0, 15, 15, 2, false, 0, 'Some text', '#FFFFFF', 'sans serif', 14, ); expect(canvasImageOnMaskComponent.drawText).toHaveBeenCalledWith( canvasContext, 'Some text', '#FFFFFF', 'sans serif', 14, 2, 30, img.width, ); }); // tslint:disable-next-line test('Calls drawImage if the text field is not defined', () => { const { canvasImageOnMaskComponent, canvasContext, img, } = initDrawOnCanvasData(); canvasImageOnMaskComponent.drawOnCanvas( canvasContext, img, true, 0, 0, 15, 15, ); expect(canvasImageOnMaskComponent.drawImage).toHaveBeenCalledWith( canvasContext, img, 15, 7.5, ); }); // tslint:disable-next-line test('Does not calls drawImageBorder if no value is set for imageOverSpillBorderColor', () => { const { canvasImageOnMaskComponent, canvasContext, img, } = initDrawOnCanvasData(); canvasImageOnMaskComponent.drawOnCanvas( canvasContext, img, true, 0, 0, 15, 15, ); expect(canvasImageOnMaskComponent.drawImageBorder) .not.toHaveBeenCalled(); }); // tslint:disable-next-line test('Calls drawImageBorder if a value is set for imageOverSpillBorderColor', () => { const { canvasImageOnMaskComponent, canvasContext, img, } = initDrawOnCanvasData(); canvasImageOnMaskComponent.drawOnCanvas( canvasContext, img, true, 0, 0, 15, 15, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, '#CCCCCC', ); expect(canvasImageOnMaskComponent.drawImageBorder).toHaveBeenCalled(); }); test('Calls canvas context restore', () => { const { canvasImageOnMaskComponent, canvasContext, img, } = initDrawOnCanvasData(); canvasImageOnMaskComponent.drawOnCanvas( canvasContext, img, true, 0, 0, 10, 15, ); expect(canvasContext.restore).toHaveBeenCalled(); }); }); describe('trackByMaskImage', () => { test('Returns null if maskImage is not defined', () => { const canvasImageOnMaskComponent = initCanvasImageOnMaskComponent(); const maskImage = undefined; const result = canvasImageOnMaskComponent.trackByMaskImage(maskImage); expect(result).toBeNull(); }); test('Returns null if maskImage.imageEditorImage is not defined', () => { const canvasImageOnMaskComponent = initCanvasImageOnMaskComponent(); const maskImage = { imageEditorImage: undefined, } as MaskImageMapInterface; const result = canvasImageOnMaskComponent.trackByMaskImage(maskImage); expect(result).toBeNull(); }); test('Returns maskImage.imageEditorImage.url_full if possible', () => { const canvasImageOnMaskComponent = initCanvasImageOnMaskComponent(); const maskImage = { imageEditorImage: { url_full: 'url_full', }, } as MaskImageMapInterface; const result = canvasImageOnMaskComponent.trackByMaskImage(maskImage); expect(result).toBe('url_full'); }); }); describe('generateTemporaryCanvas', () => { const initGenerateTemporaryCanvasData = () => { const mockCreatedCanvas = {} as HTMLCanvasElement; const document = {} as Document; document.createElement = jest.fn() .mockReturnValueOnce(mockCreatedCanvas); const canvasImageOnMaskComponent = initCanvasImageOnMaskComponent( undefined, undefined, undefined, undefined, document, ); const existingCanvas = { height: 10, width: 50, } as HTMLCanvasElement; return { canvasImageOnMaskComponent, document, existingCanvas, mockCreatedCanvas, }; }; test('Creates a temporary canvas', () => { const { canvasImageOnMaskComponent, document, existingCanvas, } = initGenerateTemporaryCanvasData(); canvasImageOnMaskComponent.generateTemporaryCanvas(existingCanvas); expect(document.createElement).toHaveBeenCalledWith('canvas'); }); // tslint:disable-next-line test('Sets the width and height of the temporary canvas to that of the original canvas', () => { const { canvasImageOnMaskComponent, existingCanvas, } = initGenerateTemporaryCanvasData(); const result = canvasImageOnMaskComponent.generateTemporaryCanvas(existingCanvas); expect(result.height).toBe(existingCanvas.height); expect(result.width).toBe(existingCanvas.width); }); test('Returns the generated canvas', () => { const { canvasImageOnMaskComponent, existingCanvas, mockCreatedCanvas, } = initGenerateTemporaryCanvasData(); expect( canvasImageOnMaskComponent.generateTemporaryCanvas(existingCanvas), ).toBe(mockCreatedCanvas); }); describe('drawBorder', () => { test('given "borderInPx" greater than 0, when calling "drawBorder", then expect "strokeRect" to be called', () => { const sut = initCanvasImageOnMaskComponent(); const strokeRectSpy = jest.fn(); const enclosingQuad = { topLeft: {x: 0, y: 0}, size: { height: 10, width: 10, }, borderInPx: 10, } as EnclosingQuadrilateral; const contextMock = { strokeRect: strokeRectSpy } as unknown as CanvasRenderingContext2D; sut.drawBorder(contextMock, enclosingQuad); expect(strokeRectSpy).toHaveBeenCalled(); }); test(`given "borderInPx" greater than 0, when calling "drawBorder" and "showImageOverspill" is true, then "strokeStyle" should have opacity equal 0.9`, () => { const sut = initCanvasImageOnMaskComponent(); sut.showImageOverspill = true; const enclosingQuad = { topLeft: {x: 0, y: 0}, size: { height: 10, width: 10, }, borderInPx: 10, } as EnclosingQuadrilateral; const contextMock = { strokeRect: jest.fn() } as unknown as CanvasRenderingContext2D; sut.drawBorder(contextMock, enclosingQuad); expect(contextMock.strokeStyle).toBe('rgba(255, 255, 255, 0.9)'); }); test(`given "borderInPx" greater than 0, when calling "drawBorder" and "showImageOverspill" is false, then "strokeStyle" should have opacity equal 1`, () => { const sut = initCanvasImageOnMaskComponent(); sut.showImageOverspill = false; const enclosingQuad = { topLeft: {x: 0, y: 0}, size: { height: 10, width: 10, }, borderInPx: 10, } as EnclosingQuadrilateral; const contextMock = { strokeRect: jest.fn() } as unknown as CanvasRenderingContext2D; sut.drawBorder(contextMock, enclosingQuad); expect(contextMock.strokeStyle).toBe('rgba(255, 255, 255, 1)'); }); }); });