/*! * @license * Copyright 2019 Alfresco Software, Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import { SimpleChange } from '@angular/core'; import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing'; import { ContentService } from '../../services/content.service'; import { ImgViewerComponent } from './img-viewer.component'; import { setupTestBed, CoreTestingModule } from '../../testing'; import { AppConfigService } from '../../app-config/app-config.service'; import { TranslateModule } from '@ngx-translate/core'; import { By } from '@angular/platform-browser'; describe('Test Img viewer component ', () => { let component: ImgViewerComponent; let service: ContentService; let fixture: ComponentFixture; let element: HTMLElement; const createFakeBlob = () => { const data = atob('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=='); return new Blob([data], { type: 'image/png' }); }; setupTestBed({ imports: [ TranslateModule.forRoot(), CoreTestingModule ] }); describe('Url', () => { beforeEach(() => { service = TestBed.inject(ContentService); fixture = TestBed.createComponent(ImgViewerComponent); element = fixture.nativeElement; component = fixture.componentInstance; component.urlFile = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=='; fixture.detectChanges(); fixture.componentInstance.ngAfterViewInit(); component.ngAfterViewInit(); fixture.detectChanges(); }); it('should display current scale as percent string', () => { component.scale = 0.5; expect(component.currentScaleText).toBe('50%'); component.scale = 1.0; expect(component.currentScaleText).toBe('100%'); }); it('should define cropper after init', () => { fixture.componentInstance.ngAfterViewInit(); expect(component.cropper).toBeDefined(); }); }); describe('Blob', () => { beforeEach(() => { service = TestBed.inject(ContentService); fixture = TestBed.createComponent(ImgViewerComponent); element = fixture.nativeElement; component = fixture.componentInstance; fixture.detectChanges(); }); it('If no url or blob are passed should thrown an error', () => { const change = new SimpleChange(null, null, true); expect(() => { component.ngOnChanges({ blobFile: change }); }).toThrow(new Error('Attribute urlFile or blobFile is required')); }); it('If url is passed should not thrown an error', () => { component.urlFile = 'fake-url'; expect(() => { component.ngOnChanges(null); }).not.toThrow(new Error('Attribute urlFile or blobFile is required')); }); it('The file Name should be present in the alt attribute', () => { component.nameFile = 'fake-name'; fixture.detectChanges(); expect(element.querySelector('#viewer-image').getAttribute('alt')).toEqual('fake-name'); }); it('If blob is passed should not thrown an error', () => { const blob = createFakeBlob(); spyOn(service, 'createTrustedUrl').and.returnValue('fake-blob-url'); const change = new SimpleChange(null, blob, true); expect(() => { component.ngOnChanges({ blobFile: change }); }).not.toThrow(new Error('Attribute urlFile or blobFile is required')); expect(component.urlFile).toEqual('fake-blob-url'); }); }); describe('Zoom customization', () => { beforeEach(() => { service = TestBed.inject(ContentService); fixture = TestBed.createComponent(ImgViewerComponent); element = fixture.nativeElement; component = fixture.componentInstance; component.urlFile = 'fake-url-file.png'; fixture.detectChanges(); }); describe('default value', () => { it('should use default zoom if is not present a custom zoom in the app.config', () => { fixture.detectChanges(); expect(component.scale).toBe(1.0); }); }); describe('custom value', () => { beforeEach(() => { const appConfig: AppConfigService = TestBed.inject(AppConfigService); appConfig.config['adf-viewer.image-viewer-scaling'] = 70; component.initializeScaling(); }); it('should use the custom zoom if it is present in the app.config', (done) => { fixture.detectChanges(); fixture.whenStable().then(() => { expect(component.scale).toBe(0.70); done(); }); }); }); }); describe('toolbar actions', () => { beforeEach(() => { fixture = TestBed.createComponent(ImgViewerComponent); element = fixture.nativeElement; component = fixture.componentInstance; component.blobFile = createFakeBlob(); const change = new SimpleChange(null, component.blobFile, true); component.ngOnChanges({ blobFile: change }); fixture.detectChanges(); }); it('should update scales on zoom in', fakeAsync(() => { spyOn(component, 'zoomIn').and.callThrough(); spyOn(component.cropper, 'zoom'); component.scale = 1.0; tick(); component.zoomIn(); expect(component.scale).toBe(1.2); expect(component.cropper.zoom).toHaveBeenCalledWith(0.2); component.zoomIn(); expect(component.scale).toBe(1.4); expect(component.cropper.zoom).toHaveBeenCalledWith(0.2); })); it('should update scales on zoom out', fakeAsync(() => { spyOn(component, 'zoomOut').and.callThrough(); spyOn(component.cropper, 'zoom'); component.scale = 1.0; tick(); component.zoomOut(); expect(component.scale).toBe(0.8); expect(component.cropper.zoom).toHaveBeenCalledWith(-0.2); component.zoomOut(); expect(component.scale).toBe(0.6); expect(component.cropper.zoom).toHaveBeenCalledWith(-0.2); })); it('should not zoom out past 20%', fakeAsync(() => { component.scale = 0.2; tick(); component.zoomOut(); component.zoomOut(); component.zoomOut(); expect(component.scale).toBe(0.2); })); it('should show rotate button if not in read only mode', () => { component.readOnly = false; fixture.detectChanges(); const rotateButtonElement = element.querySelector('#viewer-rotate-button'); expect(rotateButtonElement).not.toEqual(null); }); it('should not show rotate button by default', () => { const rotateButtonElement = element.querySelector('#viewer-rotate-button'); expect(rotateButtonElement).toEqual(null); }); it('should not show crop button by default', () => { const rotateButtonElement = element.querySelector('#viewer-crop-button'); expect(rotateButtonElement).toEqual(null); }); it('should start cropping when clicking the crop button', fakeAsync(() => { component.readOnly = false; spyOn(component, 'cropImage').and.callThrough(); spyOn(component.cropper, 'crop'); spyOn(component.cropper, 'setDragMode'); fixture.detectChanges(); const cropButtonElement = fixture.debugElement.query(By.css('#viewer-crop-button')); cropButtonElement.triggerEventHandler('click', null); tick(); expect(component.cropImage).toHaveBeenCalled(); expect(component.cropper.crop).toHaveBeenCalled(); expect(component.cropper.setDragMode).toHaveBeenCalledWith('crop'); })); it('should rotate image by -90 degrees on button click', fakeAsync(() => { component.readOnly = false; spyOn(component, 'rotateImage').and.callThrough(); spyOn(component.cropper, 'rotate'); fixture.detectChanges(); const rotateButtonElement = fixture.debugElement.query(By.css('#viewer-rotate-button')); rotateButtonElement.triggerEventHandler('click', null); tick(); expect(component.rotateImage).toHaveBeenCalled(); expect(component.cropper.rotate).toHaveBeenCalledWith(-90); })); it('should display the second toolbar when in editing and not in read only mode', fakeAsync(() => { component.readOnly = false; component.isEditing = true; fixture.detectChanges(); const secondaryToolbar = document.querySelector('.adf-secondary-toolbar'); expect(secondaryToolbar).not.toEqual(null); })); it('should not display the second toolbar when in read only mode', () => { component.readOnly = true; fixture.detectChanges(); const secondaryToolbar = document.querySelector('.adf-secondary-toolbar'); expect(secondaryToolbar).toEqual(null); }); it('should not display the second toolbar when not in editing', () => { component.readOnly = true; component.isEditing = false; fixture.detectChanges(); const secondaryToolbar = document.querySelector('.adf-secondary-toolbar'); expect(secondaryToolbar).toEqual(null); }); it('should display second toolbar in edit mode', fakeAsync(() => { component.readOnly = false; component.isEditing = true; fixture.detectChanges(); const secondaryToolbar = document.querySelector('.adf-secondary-toolbar'); const resetButton = document.querySelector('#viewer-cancel-button'); const saveButton = document.querySelector('#viewer-save-button'); expect(secondaryToolbar).not.toEqual(null); expect(resetButton).not.toEqual(null); expect(saveButton).not.toEqual(null); })); it('should not be in editing mode by default', () => { component.readOnly = false; expect(component.isEditing).toEqual(false); }); it('should get in editing mode when the image gets rotated', () => { component.readOnly = false; component.rotateImage(); expect(component.isEditing).toEqual(true); }); it('should get in editing mode when the image gets cropped', () => { component.readOnly = false; component.cropImage(); expect(component.isEditing).toEqual(true); }); it('should reset the scale and hide second toolbar', fakeAsync(() => { component.readOnly = false; component.isEditing = true; spyOn(component, 'reset').and.callThrough(); spyOn(component, 'updateCanvasContainer'); spyOn(component.cropper, 'reset'); spyOn(component.cropper, 'clear'); spyOn(component.cropper, 'zoomTo'); fixture.detectChanges(); const cancelButtonElement = fixture.debugElement.query(By.css('#viewer-cancel-button')); cancelButtonElement.triggerEventHandler('click', null); tick(); expect(component.reset).toHaveBeenCalled(); expect(component.scale).toEqual(1.0); expect(component.isEditing).toEqual(false); expect(component.cropper.reset).toHaveBeenCalled(); expect(component.cropper.clear).toHaveBeenCalled(); expect(component.updateCanvasContainer).toHaveBeenCalled(); })); it('should save when clicked on toolbar button', fakeAsync(() => { component.readOnly = false; component.isEditing = true; spyOn(component, 'save'); fixture.detectChanges(); const saveButtonElement = fixture.debugElement.query(By.css('#viewer-save-button')); saveButtonElement.triggerEventHandler('click', null); tick(); expect(component.save).toHaveBeenCalled(); })); }); });