Spaces:
Running
Running
| import { fabric } from 'fabric'; | |
| import Graphics from '@/graphics'; | |
| import Cropper from '@/component/cropper'; | |
| import { eventNames, CROPZONE_DEFAULT_OPTIONS } from '@/consts'; | |
| describe('Cropper', () => { | |
| let cropper, graphics, canvas; | |
| beforeEach(() => { | |
| graphics = new Graphics(document.createElement('canvas')); | |
| canvas = graphics.getCanvas(); | |
| cropper = new Cropper(graphics); | |
| }); | |
| describe('start()', () => { | |
| it('should create a cropzone', () => { | |
| cropper.start(); | |
| expect(cropper._cropzone).toBeDefined(); | |
| }); | |
| it('should be applied predefined default options When creating a cropzone', () => { | |
| cropper.start(); | |
| const cropzone = cropper._cropzone; | |
| Object.entries(CROPZONE_DEFAULT_OPTIONS).forEach(([optionName, optionValue]) => { | |
| expect(cropzone[optionName]).toBe(optionValue); | |
| }); | |
| }); | |
| it('should add a cropzone to canvas', () => { | |
| const addSpy = jest.spyOn(canvas, 'add'); | |
| cropper.start(); | |
| expect(addSpy).toHaveBeenCalledWith(cropper._cropzone); | |
| }); | |
| it('should no action if a croppzone has been defined', () => { | |
| cropper._cropzone = {}; | |
| const addSpy = jest.spyOn(canvas, 'add'); | |
| cropper.start(); | |
| expect(addSpy).not.toHaveBeenCalled(); | |
| }); | |
| it('should set "evented" of all objects to false', () => { | |
| const eventedOptions = { evented: true }; | |
| const objects = [ | |
| new fabric.Rect(eventedOptions), | |
| new fabric.Rect(eventedOptions), | |
| new fabric.Rect(eventedOptions), | |
| ]; | |
| canvas.add(...objects); | |
| cropper.start(); | |
| expect(objects[0].evented).toBe(false); | |
| expect(objects[1].evented).toBe(false); | |
| expect(objects[2].evented).toBe(false); | |
| }); | |
| }); | |
| describe('onFabricMouseDown()', () => { | |
| let fEvent; | |
| beforeEach(() => { | |
| fEvent = { e: {} }; | |
| jest.spyOn(canvas, 'getPointer').mockReturnValue({ x: 10, y: 20 }); | |
| }); | |
| it('should set "selection" to false', () => { | |
| cropper._onFabricMouseDown(fEvent); | |
| expect(canvas.selection).toBe(false); | |
| }); | |
| it('should set "startX, startY"', () => { | |
| cropper._onFabricMouseDown(fEvent); | |
| expect(cropper._startX).toEqual(10); | |
| expect(cropper._startY).toEqual(20); | |
| }); | |
| }); | |
| describe('onFabricMouseMove()', () => { | |
| beforeEach(() => { | |
| jest.spyOn(canvas, 'getPointer').mockReturnValue({ x: 10, y: 20 }); | |
| jest.spyOn(canvas, 'getWidth').mockReturnValue(100); | |
| jest.spyOn(canvas, 'getHeight').mockReturnValue(200); | |
| }); | |
| it('should re-render(remove->set->add) cropzone if the mouse moving is over the threshold(=10)', () => { | |
| cropper._startX = 0; | |
| cropper._startY = 0; | |
| cropper.start(); | |
| const removeSpy = jest.spyOn(canvas, 'remove'); | |
| const setSpy = jest.spyOn(cropper._cropzone, 'set'); | |
| const addSpy = jest.spyOn(canvas, 'add'); | |
| cropper._onFabricMouseMove({ e: {} }); | |
| expect(removeSpy).toHaveBeenCalled(); | |
| expect(setSpy).toHaveBeenCalled(); | |
| expect(addSpy).toHaveBeenCalled(); | |
| }); | |
| it('should not re-render cropzone if the mouse moving is under the threshold', () => { | |
| cropper._startX = 14; | |
| cropper._startY = 18; | |
| cropper.start(); | |
| const removeSpy = jest.spyOn(canvas, 'remove'); | |
| const setSpy = jest.spyOn(cropper._cropzone, 'set'); | |
| const addSpy = jest.spyOn(canvas, 'add'); | |
| cropper._onFabricMouseMove({ e: {} }); | |
| expect(removeSpy).not.toHaveBeenCalled(); | |
| expect(setSpy).not.toHaveBeenCalled(); | |
| expect(addSpy).not.toHaveBeenCalled(); | |
| }); | |
| }); | |
| describe('_calcRectDimensionFromPoint()', () => { | |
| beforeEach(() => { | |
| cropper._startX = 10; | |
| cropper._startY = 20; | |
| jest.spyOn(canvas, 'getWidth').mockReturnValue(100); | |
| jest.spyOn(canvas, 'getHeight').mockReturnValue(200); | |
| }); | |
| it('should return cropzone-left&top (min: 0, max: startX,Y)', () => { | |
| const dimension = cropper._calcRectDimensionFromPoint(20, -1); | |
| expect(dimension).toEqual({ | |
| left: 10, | |
| top: 0, | |
| width: expect.any(Number), | |
| height: expect.any(Number), | |
| }); | |
| }); | |
| it('should calculate and return cropzone-width&height', () => { | |
| let dimension; | |
| dimension = cropper._calcRectDimensionFromPoint(30, 40); | |
| expect(dimension).toEqual({ | |
| left: 10, | |
| top: 20, | |
| width: 20, | |
| height: 20, | |
| }); | |
| dimension = cropper._calcRectDimensionFromPoint(300, 400); | |
| expect(dimension).toEqual({ | |
| left: 10, | |
| top: 20, | |
| width: 90, | |
| height: 180, | |
| }); | |
| }); | |
| it('should create cropzone that has fixed ratio during shift key is pressed.', () => { | |
| cropper._withShiftKey = true; | |
| const dimension = cropper._calcRectDimensionFromPoint(100, 200); | |
| expect(dimension).toEqual({ | |
| left: 10, | |
| top: 20, | |
| width: 180, | |
| height: 180, | |
| }); | |
| }); | |
| it('should create cropzone that inverted current mouse position during shift key is pressed.', () => { | |
| cropper._withShiftKey = true; | |
| const dimension = cropper._calcRectDimensionFromPoint(-10, -20); | |
| expect(dimension).toEqual({ | |
| left: -10, | |
| top: 0, | |
| width: 20, | |
| height: 20, | |
| }); | |
| }); | |
| it('should restrict cropzone dimensions to presetRatio', () => { | |
| const dimension = cropper._calcRectDimensionFromPoint(50, 100, 16 / 9); | |
| expect(dimension).toEqual({ | |
| left: 10, | |
| top: 20, | |
| width: 40, | |
| height: 22.5, // width / presetRatio -> 60 / 1,777777778 | |
| }); | |
| }); | |
| it('should restrict cropzone within canvas and keep presetRatio when width too large', () => { | |
| const dimension = cropper._calcRectDimensionFromPoint(110, 100, 16 / 9); | |
| expect(dimension).toEqual({ | |
| left: 10, | |
| top: 20, | |
| width: 90, // maxwidth (100) minus start (10) | |
| height: 50.625, // width / presetRatio -> 90 / (16/9) | |
| }); | |
| }); | |
| it('should restrict cropzone within canvas and keep presetRatio when height too large', () => { | |
| cropper._startY = 177.5; | |
| const dimension = cropper._calcRectDimensionFromPoint(100, 250, 16 / 9); | |
| expect(dimension).toEqual({ | |
| left: 10, | |
| top: 177.5, | |
| width: 40, // height * presetRatio -> 22.5 * (16/9) | |
| height: 22.5, // maxwidth (200) minus start (177.5) | |
| }); | |
| }); | |
| }); | |
| it('should activate cropzone', () => { | |
| canvas.setActiveObject = jest.fn(); | |
| cropper.start(); | |
| cropper._onFabricMouseUp(); | |
| expect(canvas.setActiveObject).toHaveBeenCalledWith(cropper._cropzone); | |
| }); | |
| describe('crop()', () => { | |
| beforeEach(() => { | |
| cropper.start(); | |
| }); | |
| afterEach(() => { | |
| cropper.end(); | |
| }); | |
| it('should return cropzone rect', () => { | |
| jest.spyOn(cropper._cropzone, 'isValid').mockReturnValue(true); | |
| const cropzoneRect = cropper.getCropzoneRect(); | |
| expect(cropzoneRect).not.toBeNull(); | |
| }); | |
| it('should return cropzone data if the cropzone is valid', () => { | |
| jest.spyOn(cropper._cropzone, 'isValid').mockReturnValue(true); | |
| const cropzoneRect = cropper.getCropzoneRect(); | |
| const croppedImageData = cropper.getCroppedImageData(cropzoneRect); | |
| expect(croppedImageData).toEqual({ | |
| imageName: expect.any(String), | |
| url: expect.any(String), | |
| }); | |
| }); | |
| }); | |
| describe('presets - setCropzoneRect()', () => { | |
| beforeEach(() => { | |
| cropper.start(); | |
| }); | |
| afterEach(() => { | |
| cropper.end(); | |
| }); | |
| it('should return cropzone rect as a square', () => { | |
| jest.spyOn(cropper._cropzone, 'isValid').mockReturnValue(true); | |
| cropper.setCropzoneRect(1); | |
| const { width, height } = cropper.getCropzoneRect(); | |
| expect(width).toBe(height); | |
| }); | |
| it('should return cropzone rect as a 3:2 aspect box', () => { | |
| jest.spyOn(cropper._cropzone, 'isValid').mockReturnValue(true); | |
| cropper.setCropzoneRect(3 / 2); | |
| const { width, height } = cropper.getCropzoneRect(); | |
| expect((width / height).toFixed(1)).toBe((3 / 2).toFixed(1)); | |
| }); | |
| it('should return cropzone rect as a 4:3 aspect box', () => { | |
| jest.spyOn(cropper._cropzone, 'isValid').mockReturnValue(true); | |
| cropper.setCropzoneRect(4 / 3); | |
| const { width, height } = cropper.getCropzoneRect(); | |
| expect((width / height).toFixed(1)).toBe((4 / 3).toFixed(1)); | |
| }); | |
| it('should return cropzone rect as a 5:4 aspect box', () => { | |
| jest.spyOn(cropper._cropzone, 'isValid').mockReturnValue(true); | |
| cropper.setCropzoneRect(5 / 4); | |
| const { width, height } = cropper.getCropzoneRect(); | |
| expect((width / height).toFixed(1)).toBe((5 / 4).toFixed(1)); | |
| }); | |
| it('should return cropzone rect as a 7:5 aspect box', () => { | |
| jest.spyOn(cropper._cropzone, 'isValid').mockReturnValue(true); | |
| cropper.setCropzoneRect(7 / 5); | |
| const { width, height } = cropper.getCropzoneRect(); | |
| expect((width / height).toFixed(1)).toBe((7 / 5).toFixed(1)); | |
| }); | |
| it('should return cropzone rect as a 16:9 aspect box', () => { | |
| jest.spyOn(cropper._cropzone, 'isValid').mockReturnValue(true); | |
| cropper.setCropzoneRect(16 / 9); | |
| const { width, height } = cropper.getCropzoneRect(); | |
| expect((width / height).toFixed(1)).toBe((16 / 9).toFixed(1)); | |
| }); | |
| it('Even in situations with floating point problems, should calculate the exact width you expect.', () => { | |
| jest.spyOn(canvas, 'getWidth').mockReturnValue(408); | |
| jest.spyOn(canvas, 'getHeight').mockReturnValue(312); | |
| const setSpy = jest.spyOn(cropper._cropzone, 'set'); | |
| cropper.setCropzoneRect(16 / 9); | |
| expect(setSpy).toHaveBeenCalledWith(expect.objectContaining({ width: 408 })); | |
| }); | |
| it('should remove cropzone of cropper when falsy is passed', () => { | |
| cropper.setCropzoneRect(); | |
| expect(cropper.getCropzoneRect()).toBeNull(); | |
| cropper.setCropzoneRect(0); | |
| expect(cropper.getCropzoneRect()).toBeNull(); | |
| cropper.setCropzoneRect(null); | |
| expect(cropper.getCropzoneRect()).toBeNull(); | |
| }); | |
| }); | |
| describe('end()', () => { | |
| it('should set cropzone of cropper to null', () => { | |
| cropper.start(); | |
| cropper.end(); | |
| expect(cropper._cropzone).toBeNull(); | |
| }); | |
| it('should set "evented" of all objects to true', () => { | |
| const eventedOptions = { evented: false }; | |
| const objects = [ | |
| new fabric.Rect(eventedOptions), | |
| new fabric.Rect(eventedOptions), | |
| new fabric.Rect(eventedOptions), | |
| ]; | |
| canvas.add(...objects); | |
| cropper.start(); | |
| cropper.end(); | |
| expect(objects[0].evented).toBe(true); | |
| expect(objects[1].evented).toBe(true); | |
| expect(objects[2].evented).toBe(true); | |
| }); | |
| }); | |
| describe('canvas event delegator', () => { | |
| it('The event of an object with an eventDelegator must fire the graphics.fire registered with the trigger.', () => { | |
| cropper.start(); | |
| const fireSpy = jest.spyOn(graphics, 'fire'); | |
| const cropzone = cropper._cropzone; | |
| canvas.fire('object:scaling', { target: cropper._cropzone }); | |
| expect(fireSpy).not.toHaveBeenCalled(); | |
| cropzone.canvasEventTrigger[eventNames.OBJECT_SCALED](cropzone); | |
| expect(fireSpy).toHaveBeenCalled(); | |
| }); | |
| }); | |
| }); | |