diff --git a/lib/content-services/src/lib/viewer/components/alfresco-viewer.component.html b/lib/content-services/src/lib/viewer/components/alfresco-viewer.component.html index 0dd37bead0..ef482a9822 100644 --- a/lib/content-services/src/lib/viewer/components/alfresco-viewer.component.html +++ b/lib/content-services/src/lib/viewer/components/alfresco-viewer.component.html @@ -183,7 +183,6 @@ [viewerType]="viewerType" [fileName]="fileName" [isLoading]="isLoading" - [allowGoBack]="allowGoBack" [urlFile]="urlFileContent" [tracks]="tracks" [readOnly]="readOnly"> diff --git a/lib/content-services/src/lib/viewer/components/alfresco-viewer.component.spec.ts b/lib/content-services/src/lib/viewer/components/alfresco-viewer.component.spec.ts new file mode 100644 index 0000000000..8919ba00d3 --- /dev/null +++ b/lib/content-services/src/lib/viewer/components/alfresco-viewer.component.spec.ts @@ -0,0 +1,946 @@ +/*! + * @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 { Location } from '@angular/common'; +import { SpyLocation } from '@angular/common/testing'; +import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing'; + +import { TranslateModule } from '@ngx-translate/core'; +import { MatDialog } from '@angular/material/dialog'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { AppExtensionService, ViewerExtensionRef } from '@alfresco/adf-extensions'; +import { MatButtonModule } from '@angular/material/button'; +import { MatIconModule } from '@angular/material/icon'; +import { NodeEntry, VersionEntry } from "@alfresco/js-api"; +import { AlfrescoViewerComponent, RenditionViewerService } from "@alfresco/adf-content-services"; +import { + AlfrescoApiService, + CoreTestingModule, + setupTestBed, + EventMock, + FileModel, UploadService +} from "@alfresco/adf-core"; +import { throwError } from "rxjs"; +import { Component } from '@angular/core'; + +@Component({ + selector: 'adf-viewer-container-toolbar', + template: ` + + +
+
+
+ ` +}) +class ViewerWithCustomToolbarComponent { +} + +@Component({ + selector: 'adf-viewer-container-toolbar-actions', + template: ` + + + + + + ` +}) +class ViewerWithCustomToolbarActionsComponent { +} + +@Component({ + selector: 'adf-viewer-container-sidebar', + template: ` + + +
+
+
+ ` +}) +class ViewerWithCustomSidebarComponent { +} + +@Component({ + selector: 'adf-dialog-dummy', + template: `` +}) +class DummyDialogComponent { +} + +@Component({ + selector: 'adf-viewer-container-open-with', + template: ` + + + + + + + + ` +}) +class ViewerWithCustomOpenWithComponent { +} + +@Component({ + selector: 'adf-viewer-container-more-actions', + template: ` + + + + + + + + ` +}) +class ViewerWithCustomMoreActionsComponent { +} + + +describe('AlfrescoViewerComponent', () => { + + let component: AlfrescoViewerComponent; + let fixture: ComponentFixture; + let element: HTMLElement; + + let alfrescoApiService: AlfrescoApiService; + let dialog: MatDialog; + let uploadService: UploadService; + let extensionService: AppExtensionService; + + setupTestBed({ + imports: [ + NoopAnimationsModule, + TranslateModule.forRoot(), + CoreTestingModule, + MatButtonModule, + MatIconModule + ], + declarations: [ + ViewerWithCustomToolbarComponent, + ViewerWithCustomSidebarComponent, + ViewerWithCustomOpenWithComponent, + ViewerWithCustomMoreActionsComponent, + ViewerWithCustomToolbarActionsComponent + ], + providers: [ + { + provide: RenditionViewerService, useValue: { + getNodeRendition: () => throwError('thrown') + } + }, + {provide: Location, useClass: SpyLocation}, + MatDialog + ] + }); + + beforeEach(() => { + fixture = TestBed.createComponent(AlfrescoViewerComponent); + element = fixture.nativeElement; + component = fixture.componentInstance; + + console.log(element); + console.log(component); + uploadService = TestBed.inject(UploadService); + alfrescoApiService = TestBed.inject(AlfrescoApiService); + dialog = TestBed.inject(MatDialog); + extensionService = TestBed.inject(AppExtensionService); + }); + + afterEach(() => { + fixture.destroy(); + }); + + + describe('Extension Type Test', () => { + + + it('should use external viewer to display node by id', fakeAsync(() => { + const extension: ViewerExtensionRef = { + component: 'custom.component', + id: 'custom.component.id', + fileExtension: '*' + }; + spyOn(extensionService, 'getViewerExtensions').and.returnValue([extension]); + + fixture = TestBed.createComponent(AlfrescoViewerComponent); + element = fixture.nativeElement; + component = fixture.componentInstance; + + spyOn(component.nodesApi, 'getNode').and.callFake(() => Promise.resolve(new NodeEntry({entry: {}}))); + + component.nodeId = '37f7f34d-4e64-4db6-bb3f-5c89f7844251'; + component.ngOnChanges(); + + fixture.detectChanges(); + tick(100); + + expect(component.nodesApi.getNode).toHaveBeenCalled(); + expect(component.viewerType).toBe('external'); + expect(component.isLoading).toBeFalsy(); + expect(element.querySelector('[data-automation-id="custom.component"]')).not.toBeNull(); + })); + + + }); + + describe('MimeType handling', () => { + + it('should node without content show unkonwn', (done) => { + const displayName = 'the-name'; + const contentUrl = '/content/url/path'; + + component.nodeId = '12'; + spyOn(component['nodesApi'], 'getNode').and.returnValue(Promise.resolve(new NodeEntry({ + entry: {content: {name: displayName, id: '12'}} + }))); + + spyOn(component['contentApi'], 'getContentUrl').and.returnValue(contentUrl); + + component.ngOnChanges(); + fixture.whenStable().then(() => { + fixture.detectChanges(); + expect(element.querySelector('adf-viewer-unknown-format')).toBeDefined(); + done(); + }); + }); + }); + + it('should change display name every time node changes', fakeAsync(() => { + spyOn(component['nodesApi'], 'getNode').and.returnValues( + Promise.resolve(new NodeEntry({entry: {name: 'file1', content: {}}})), + Promise.resolve(new NodeEntry({entry: {name: 'file2', content: {}}})) + ); + + component.showViewer = true; + + component.nodeId = 'id1'; + component.ngOnChanges(); + tick(); + + expect(component.fileName).toBe('file1'); + + component.nodeId = 'id2'; + component.ngOnChanges(); + tick(); + + expect(component.fileName).toBe('file2'); + })); + + it('should append version of the file to the file content URL', fakeAsync(() => { + spyOn(component['nodesApi'], 'getNode').and.returnValue( + Promise.resolve(new NodeEntry({ + entry: { + name: 'file1.pdf', + content: {}, + properties: {'cm:versionLabel': '10'} + } + })) + ); + spyOn(component['versionsApi'], 'getVersion').and.returnValue(Promise.resolve(undefined)); + + component.nodeId = 'id1'; + component.showViewer = true; + + component.versionId = null; + component.ngOnChanges(); + tick(); + + expect(component.fileName).toBe('file1.pdf'); + expect(component.urlFileContent).toContain('/public/alfresco/versions/1/nodes/id1/content?attachment=false&10'); + })); + + it('should change display name every time node\`s version changes', fakeAsync(() => { + spyOn(component['nodesApi'], 'getNode').and.returnValue( + Promise.resolve(new NodeEntry({entry: {name: 'node1', content: {}}})) + ); + + spyOn(component['versionsApi'], 'getVersion').and.returnValues( + Promise.resolve(new VersionEntry({entry: {name: 'file1', content: {}}})), + Promise.resolve(new VersionEntry({entry: {name: 'file2', content: {}}})) + ); + + component.nodeId = 'id1'; + component.showViewer = true; + + component.versionId = '1.0'; + component.ngOnChanges(); + tick(); + + expect(component.fileName).toBe('file1'); + + component.versionId = '1.1'; + component.ngOnChanges(); + tick(); + + expect(component.fileName).toBe('file2'); + })); + + it('should update node only if node name changed', fakeAsync(() => { + spyOn(component['nodesApi'], 'getNode').and.returnValues( + Promise.resolve(new NodeEntry({entry: {name: 'file1', content: {}}})) + ); + + component.showViewer = true; + + component.nodeId = 'id1'; + fixture.detectChanges(); + component.ngOnChanges(); + tick(); + + expect(component.fileName).toBe('file1'); + + alfrescoApiService.nodeUpdated.next({id: 'id1', name: 'file2'} as any); + fixture.detectChanges(); + expect(component.fileName).toBe('file2'); + + alfrescoApiService.nodeUpdated.next({id: 'id1', name: 'file3'} as any); + fixture.detectChanges(); + expect(component.fileName).toBe('file3'); + + alfrescoApiService.nodeUpdated.next({id: 'id2', name: 'file4'} as any); + fixture.detectChanges(); + expect(component.fileName).toBe('file3'); + expect(component.nodeId).toBe('id1'); + })); + + describe('Viewer Example Component Rendering', () => { + + it('should use custom toolbar', (done) => { + const customFixture = TestBed.createComponent(ViewerWithCustomToolbarComponent); + const customElement: HTMLElement = customFixture.nativeElement; + + customFixture.detectChanges(); + fixture.whenStable().then(() => { + expect(customElement.querySelector('.custom-toolbar-element')).toBeDefined(); + done(); + }); + }); + + it('should use custom toolbar actions', (done) => { + const customFixture = TestBed.createComponent(ViewerWithCustomToolbarActionsComponent); + const customElement: HTMLElement = customFixture.nativeElement; + + customFixture.detectChanges(); + fixture.whenStable().then(() => { + expect(customElement.querySelector('#custom-button')).toBeDefined(); + done(); + }); + }); + + it('should use custom info drawer', (done) => { + const customFixture = TestBed.createComponent(ViewerWithCustomSidebarComponent); + const customElement: HTMLElement = customFixture.nativeElement; + + customFixture.detectChanges(); + + fixture.whenStable().then(() => { + expect(customElement.querySelector('.custom-info-drawer-element')).toBeDefined(); + done(); + }); + }); + + it('should use custom open with menu', (done) => { + const customFixture = TestBed.createComponent(ViewerWithCustomOpenWithComponent); + const customElement: HTMLElement = customFixture.nativeElement; + + customFixture.detectChanges(); + + fixture.whenStable().then(() => { + expect(customElement.querySelector('.adf-viewer-container-open-with')).toBeDefined(); + done(); + }); + }); + + it('should use custom more actions menu', (done) => { + const customFixture = TestBed.createComponent(ViewerWithCustomMoreActionsComponent); + const customElement: HTMLElement = customFixture.nativeElement; + + customFixture.detectChanges(); + + fixture.whenStable().then(() => { + expect(customElement.querySelector('.adf-viewer-container-more-actions')).toBeDefined(); + done(); + }); + + }); + }); + + describe('error handling', () => { + + it('should show unknown view when node file not found', (done) => { + spyOn(component['nodesApi'], 'getNode') + .and.returnValue(Promise.reject({})); + + component.nodeId = 'the-node-id-of-the-file-to-preview'; + component.mimeType = null; + + component.ngOnChanges(); + fixture.whenStable().then(() => { + fixture.detectChanges(); + expect(element.querySelector('adf-viewer-unknown-format')).not.toBeNull(); + done(); + }); + }); + + it('should show unknown view when sharedLink file not found', (done) => { + spyOn(component['sharedLinksApi'], 'getSharedLink') + .and.returnValue(Promise.reject({})); + + component.sharedLinkId = 'the-Shared-Link-id'; + component.mimeType = null; + component.nodeId = null; + + component.ngOnChanges(); + fixture.whenStable().then(() => { + fixture.detectChanges(); + expect(element.querySelector('adf-viewer-unknown-format')).not.toBeNull(); + done(); + }); + + }); + + it('should raise an event when the shared link is invalid', fakeAsync(() => { + spyOn(component['sharedLinksApi'], 'getSharedLink') + .and.returnValue(Promise.reject({})); + + component.sharedLinkId = 'the-Shared-Link-id'; + component.mimeType = null; + component.nodeId = null; + + component.invalidSharedLink.subscribe((emittedValue) => { + expect(emittedValue).toBeUndefined(); + }); + + component.ngOnChanges(); + })); +// + }); + + describe('Toolbar', () => { + + it('should show only next file button', async () => { + component.allowNavigate = true; + component.canNavigateBefore = false; + component.canNavigateNext = true; + + fixture.detectChanges(); + await fixture.whenStable(); + + const nextButton = element.querySelector('[data-automation-id="adf-toolbar-next-file"]'); + expect(nextButton).not.toBeNull(); + + const prevButton = element.querySelector('[data-automation-id="adf-toolbar-pref-file"]'); + expect(prevButton).toBeNull(); + }); + + it('should provide tooltip for next file button', async () => { + component.allowNavigate = true; + component.canNavigateBefore = false; + component.canNavigateNext = true; + + fixture.detectChanges(); + await fixture.whenStable(); + + const nextButton = element.querySelector('[data-automation-id="adf-toolbar-next-file"]'); + expect(nextButton.title).toBe('ADF_VIEWER.ACTIONS.NEXT_FILE'); + }); + + it('should show only previous file button', async () => { + component.allowNavigate = true; + component.canNavigateBefore = true; + component.canNavigateNext = false; + + fixture.detectChanges(); + await fixture.whenStable(); + + const nextButton = element.querySelector('[data-automation-id="adf-toolbar-next-file"]'); + expect(nextButton).toBeNull(); + + const prevButton = element.querySelector('[data-automation-id="adf-toolbar-pref-file"]'); + expect(prevButton).not.toBeNull(); + }); + + it('should provide tooltip for the previous file button', async () => { + component.allowNavigate = true; + component.canNavigateBefore = true; + component.canNavigateNext = false; + + fixture.detectChanges(); + await fixture.whenStable(); + + const prevButton = element.querySelector('[data-automation-id="adf-toolbar-pref-file"]'); + expect(prevButton.title).toBe('ADF_VIEWER.ACTIONS.PREV_FILE'); + }); + + it('should show both file navigation buttons', async () => { + component.allowNavigate = true; + component.canNavigateBefore = true; + component.canNavigateNext = true; + + fixture.detectChanges(); + await fixture.whenStable(); + + const nextButton = element.querySelector('[data-automation-id="adf-toolbar-next-file"]'); + expect(nextButton).not.toBeNull(); + + const prevButton = element.querySelector('[data-automation-id="adf-toolbar-pref-file"]'); + expect(prevButton).not.toBeNull(); + }); + + it('should not show navigation buttons', async () => { + component.allowNavigate = false; + + fixture.detectChanges(); + await fixture.whenStable(); + + const nextButton = element.querySelector('[data-automation-id="adf-toolbar-next-file"]'); + expect(nextButton).toBeNull(); + + const prevButton = element.querySelector('[data-automation-id="adf-toolbar-pref-file"]'); + expect(prevButton).toBeNull(); + }); + + it('should now show navigation buttons even if navigation enabled', async () => { + component.allowNavigate = true; + component.canNavigateBefore = false; + component.canNavigateNext = false; + + fixture.detectChanges(); + await fixture.whenStable(); + + const nextButton = element.querySelector('[data-automation-id="adf-toolbar-next-file"]'); + expect(nextButton).toBeNull(); + + const prevButton = element.querySelector('[data-automation-id="adf-toolbar-pref-file"]'); + expect(prevButton).toBeNull(); + }); + + it('should render fullscreen button', () => { + expect(element.querySelector('[data-automation-id="adf-toolbar-fullscreen"]')).toBeDefined(); + }); + + it('should render default download button', (done) => { + component.allowDownload = true; + + fixture.whenStable().then(() => { + expect(element.querySelector('[data-automation-id="adf-toolbar-download"]')).toBeDefined(); + done(); + }); + }); + + it('should not render default download button', (done) => { + component.allowDownload = false; + fixture.detectChanges(); + + fixture.whenStable().then(() => { + expect(element.querySelector('[data-automation-id="adf-toolbar-download"]')).toBeNull(); + done(); + }); + }); + + it('should render default print button', (done) => { + component.allowPrint = true; + fixture.detectChanges(); + + fixture.whenStable().then(() => { + expect(element.querySelector('[data-automation-id="adf-toolbar-print"]')).toBeDefined(); + done(); + }); + }); + + it('should not render default print button', (done) => { + component.allowPrint = false; + fixture.detectChanges(); + + fixture.whenStable().then(() => { + expect(element.querySelector('[data-automation-id="adf-toolbar-print"]')).toBeNull(); + done(); + }); + }); + + it('should invoke print action with the toolbar button', (done) => { + component.allowPrint = true; + fixture.detectChanges(); + + spyOn(component, 'onPrintContent').and.stub(); + + const button: HTMLButtonElement = element.querySelector('[data-automation-id="adf-toolbar-print"]') as HTMLButtonElement; + button.click(); + + fixture.whenStable().then(() => { + expect(component.onPrintContent).toHaveBeenCalled(); + done(); + }); + }); + + it('should get and assign node for download', (done) => { + component.nodeId = '12'; + const displayName = 'the-name'; + const nodeDetails = { + entry: {name: displayName, id: '12', content: {mimeType: 'txt'}} + }; + + const contentUrl = '/content/url/path'; + + const node = new NodeEntry(nodeDetails); + + spyOn(component['nodesApi'], 'getNode').and.returnValue(Promise.resolve(node)); + spyOn(component['contentApi'], 'getContentUrl').and.returnValue(contentUrl); + + component.ngOnChanges(); + fixture.whenStable().then(() => { + fixture.detectChanges(); + expect(component.nodeEntry).toBe(node); + done(); + }); + }); + + it('should render close viewer button if it is not a shared link', (done) => { + fixture.detectChanges(); + fixture.whenStable().then(() => { + fixture.detectChanges(); + expect(element.querySelector('[data-automation-id="adf-toolbar-back"]')).toBeDefined(); + expect(element.querySelector('[data-automation-id="adf-toolbar-back"]')).not.toBeNull(); + done(); + }); + }); + + it('should not render close viewer button if it is a shared link', (done) => { + spyOn(component['sharedLinksApi'], 'getSharedLink') + .and.returnValue(Promise.reject({})); + + component.sharedLinkId = 'the-Shared-Link-id'; + component.mimeType = null; + + component.ngOnChanges(); + fixture.whenStable().then(() => { + fixture.detectChanges(); + expect(element.querySelector('[data-automation-id="adf-toolbar-back"]')).toBeNull(); + done(); + }); + }); + + }); + + describe('Base component', () => { + + beforeEach(() => { + component.mimeType = 'application/pdf'; + component.nodeId = 'id1'; + + fixture.detectChanges(); + }); + + describe('SideBar Test', () => { + + it('should NOT display sidebar if is not allowed', (done) => { + component.showRightSidebar = true; + component.allowRightSidebar = false; + fixture.detectChanges(); + + fixture.whenStable().then(() => { + const sidebar = element.querySelector('#adf-right-sidebar'); + expect(sidebar).toBeNull(); + done(); + }); + }); + + it('should display sidebar on the right side', (done) => { + component.allowRightSidebar = true; + component.showRightSidebar = true; + fixture.detectChanges(); + + fixture.whenStable().then(() => { + const sidebar = element.querySelector('#adf-right-sidebar'); + expect(getComputedStyle(sidebar).order).toEqual('4'); + done(); + }); + }); + + it('should NOT display left sidebar if is not allowed', (done) => { + component.showLeftSidebar = true; + component.allowLeftSidebar = false; + fixture.detectChanges(); + + fixture.whenStable().then(() => { + const sidebar = element.querySelector('#adf-left-sidebar'); + expect(sidebar).toBeNull(); + done(); + }); + + }); + + it('should display sidebar on the left side', (done) => { + component.allowLeftSidebar = true; + component.showLeftSidebar = true; + fixture.detectChanges(); + + fixture.whenStable().then(() => { + const sidebar = element.querySelector('#adf-left-sidebar'); + expect(getComputedStyle(sidebar).order).toEqual('1'); + done(); + }); + }); + }); + + describe('View', () => { + + describe('Overlay mode true', () => { + + beforeEach(() => { + component.overlayMode = true; + component.fileName = 'fake-test-file.pdf'; + fixture.detectChanges(); + }); + + it('should header be present if is overlay mode', () => { + expect(element.querySelector('.adf-alfresco-viewer-toolbar')).not.toBeNull(); + }); + + it('should Name File be present if is overlay mode ', (done) => { + component.ngOnChanges(); + fixture.detectChanges(); + fixture.whenStable().then(() => { + fixture.detectChanges(); + expect(element.querySelector('#adf-alfresco-viewer-display-name').textContent).toEqual('fake-test-file.pdf'); + done(); + }); + }); + + it('should Close button be present if overlay mode', (done) => { + fixture.detectChanges(); + fixture.whenStable().then(() => { + fixture.detectChanges(); + expect(element.querySelector('.adf-alfresco-viewer-close-button')).not.toBeNull(); + done(); + }); + }); + + it('should Click on close button hide the viewer', (done) => { + const closeButton: any = element.querySelector('.adf-alfresco-viewer-close-button'); + closeButton.click(); + fixture.detectChanges(); + + fixture.whenStable().then(() => { + expect(element.querySelector('.adf-alfresco-viewer-content')).toBeNull(); + done(); + }); + }); + + it('should Esc button hide the viewer', (done) => { + EventMock.keyDown(27); + + fixture.detectChanges(); + + fixture.whenStable().then(() => { + expect(element.querySelector('.adf-alfresco-viewer-content')).toBeNull(); + done(); + }); + }); + + it('should not close the viewer on Escape event if dialog was opened', (done) => { + const event = new KeyboardEvent('keydown', { + bubbles: true, + keyCode: 27 + } as KeyboardEventInit); + + const dialogRef = dialog.open(DummyDialogComponent); + + dialogRef.afterClosed().subscribe(() => { + document.body.dispatchEvent(event); + fixture.detectChanges(); + expect(element.querySelector('.adf-alfresco-viewer-content')).toBeNull(); + done(); + }); + + fixture.detectChanges(); + + document.body.dispatchEvent(event); + fixture.detectChanges(); + expect(element.querySelector('.adf-alfresco-viewer-content')).not.toBeNull(); + }); + }); + + describe('Overlay mode false', () => { + + beforeEach(() => { + component.overlayMode = false; + fixture.detectChanges(); + }); + + it('should Esc button not hide the viewer if is not overlay mode', (done) => { + EventMock.keyDown(27); + + fixture.detectChanges(); + + fixture.whenStable().then(() => { + expect(element.querySelector('.adf-alfresco-viewer-content')).not.toBeNull(); + done(); + }); + }); + }); + }); + + describe('Attribute', () => { + + it('should FileNodeId present not thrown any error ', () => { + component.showViewer = true; + component.nodeId = 'file-node-id'; + + expect(() => { + component.ngOnChanges(); + }).not.toThrow(); + }); + + + it('should showViewer default value be true', () => { + expect(component.showViewer).toBe(true); + }); + + it('should viewer be hide if showViewer value is false', () => { + component.showViewer = false; + + fixture.detectChanges(); + expect(element.querySelector('.adf-alfresco-viewer-content')).toBeNull(); + }); + }); + + describe('Events', () => { + + it('should update version when emitted by image-viewer and user has update permissions', () => { + spyOn(uploadService, 'uploadFilesInTheQueue').and.callFake(() => { + }); + spyOn(uploadService, 'addToQueue'); + component.readOnly = false; + component.nodeEntry = new NodeEntry({ + entry: { + name: 'fakeImage.png', + id: '12', + content: {mimeType: 'img/png'} + } + }); + const data = atob('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=='); + const fakeBlob = new Blob([data], {type: 'image/png'}); + const newImageFile: File = new File([fakeBlob], component?.nodeEntry?.entry?.name, {type: component?.nodeEntry?.entry?.content?.mimeType}); + const newFile = new FileModel( + newImageFile, + { + majorVersion: false, + newVersion: true, + parentId: component?.nodeEntry?.entry?.parentId, + nodeType: component?.nodeEntry?.entry?.content?.mimeType + }, + component.nodeEntry.entry?.id + ); + component.onSubmitFile(fakeBlob); + fixture.detectChanges(); + + expect(uploadService.addToQueue).toHaveBeenCalledWith(...[newFile]); + expect(uploadService.uploadFilesInTheQueue).toHaveBeenCalled(); + }); + + it('should not update version when emitted by image-viewer and user doesn`t have update permissions', () => { + spyOn(uploadService, 'uploadFilesInTheQueue').and.callFake(() => { + }); + component.readOnly = true; + component.nodeEntry = new NodeEntry({ + entry: { + name: 'fakeImage.png', + id: '12', + content: {mimeType: 'img/png'} + } + }); + const data = atob('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=='); + const fakeBlob = new Blob([data], {type: 'image/png'}); + component.onSubmitFile(fakeBlob); + fixture.detectChanges(); + + expect(uploadService.uploadFilesInTheQueue).not.toHaveBeenCalled(); + }); + }); + + describe('Viewer component - Full Screen Mode - Mocking fixture element', () => { + + beforeEach(() => { + fixture = TestBed.createComponent(AlfrescoViewerComponent); + element = fixture.nativeElement; + component = fixture.componentInstance; + + component.showToolbar = true; + component.nodeId = 'fake-node-id'; + component.mimeType = 'application/pdf'; + fixture.detectChanges(); + }); + + it('should use standard mode', () => { + const domElement = jasmine.createSpyObj('el', ['requestFullscreen']); + spyOn(fixture.nativeElement, 'querySelector').and.returnValue(domElement); + + component.enterFullScreen(); + expect(domElement.requestFullscreen).toHaveBeenCalled(); + }); + + it('should use webkit prefix', () => { + const domElement = jasmine.createSpyObj('el', ['webkitRequestFullscreen']); + spyOn(fixture.nativeElement, 'querySelector').and.returnValue(domElement); + + component.enterFullScreen(); + expect(domElement.webkitRequestFullscreen).toHaveBeenCalled(); + }); + + it('should use moz prefix', () => { + const domElement = jasmine.createSpyObj('el', ['mozRequestFullScreen']); + spyOn(fixture.nativeElement, 'querySelector').and.returnValue(domElement); + + component.enterFullScreen(); + expect(domElement.mozRequestFullScreen).toHaveBeenCalled(); + }); + + it('should use ms prefix', () => { + const domElement = jasmine.createSpyObj('el', ['msRequestFullscreen']); + spyOn(fixture.nativeElement, 'querySelector').and.returnValue(domElement); + + component.enterFullScreen(); + expect(domElement.msRequestFullscreen).toHaveBeenCalled(); + }); + }); + }); +}); diff --git a/lib/content-services/src/lib/viewer/components/alfresco-viewer.component.ts b/lib/content-services/src/lib/viewer/components/alfresco-viewer.component.ts index 9ea38e8b0b..5d01622d45 100644 --- a/lib/content-services/src/lib/viewer/components/alfresco-viewer.component.ts +++ b/lib/content-services/src/lib/viewer/components/alfresco-viewer.component.ts @@ -178,7 +178,7 @@ export class AlfrescoViewerComponent { private keyDown$ = fromEvent(document, 'keydown'); private cacheBusterNumber: number; - private closeViewer: any; + private closeViewer = true; versionEntry: VersionEntry; isLoading: boolean; @@ -289,6 +289,7 @@ export class AlfrescoViewerComponent { this.isLoading = false; this.logService.error('This sharedLink does not exist'); this.invalidSharedLink.next(); + this.viewerType = 'invalid-link'; } } @@ -418,7 +419,7 @@ export class AlfrescoViewerComponent { onPrintContent(event: MouseEvent) { if (this.allowPrint) { if (!event.defaultPrevented) { - this.viewUtilService.printFileGeneric(this.nodeId, this.mimeType); + this.renditionViewerService.printFileGeneric(this.nodeId, this.mimeType); } } } diff --git a/lib/content-services/src/lib/viewer/services/rendition-viewer.service.ts b/lib/content-services/src/lib/viewer/services/rendition-viewer.service.ts index c4360f492f..f8c94d64fb 100644 --- a/lib/content-services/src/lib/viewer/services/rendition-viewer.service.ts +++ b/lib/content-services/src/lib/viewer/services/rendition-viewer.service.ts @@ -24,6 +24,8 @@ import { AlfrescoApiService , LogService, Track,TranslationService } from '@alfr }) export class RenditionViewerService { + static TARGET = '_new'; + /** * Content groups based on categorization of files that can be viewed in the web browser. This * implementation or grouping is tied to the definition the ng component: ViewerRenderComponent @@ -282,4 +284,53 @@ export class RenditionViewerService { return rendition?.entry?.status?.toString() === 'CREATED' || false; } + + /** + * This method takes a url to trigger the print dialog against, and the type of artifact that it + * is. + * This URL should be one that can be rendered in the browser, for example PDF, Image, or Text + */ + printFile(url: string, type: string): void { + const pwa = window.open(url, RenditionViewerService.TARGET); + if (pwa) { + // Because of the way chrome focus and close image window vs. pdf preview window + if (type === RenditionViewerService.ContentGroup.IMAGE) { + pwa.onfocus = () => { + setTimeout(() => { + pwa.close(); + }, 500); + }; + } + + pwa.onload = () => { + pwa.print(); + }; + } + } + + /** + * Launch the File Print dialog from anywhere other than the preview service, which resolves the + * rendition of the object that can be printed from a web browser. + * These are: images, PDF files, or PDF rendition of files. + * We also force PDF rendition for TEXT type objects, otherwise the default URL is to download. + * TODO there are different TEXT type objects, (HTML, plaintext, xml, etc. we should determine how these are handled) + */ + printFileGeneric(objectId: string, mimeType: string): void { + const nodeId = objectId; + const type: string = this.getViewerTypeByMimeType(mimeType); + + this.getRendition(nodeId, RenditionViewerService.ContentGroup.PDF) + .then((value) => { + const url: string = this.getRenditionUrl(nodeId, type, (!!value)); + const printType = (type === RenditionViewerService.ContentGroup.PDF + || type === RenditionViewerService.ContentGroup.TEXT) + ? RenditionViewerService.ContentGroup.PDF : type; + this.printFile(url, printType); + }) + .catch((err) => { + this.logService.error('Error with Printing'); + this.logService.error(err); + }); + } + } diff --git a/lib/core/src/lib/viewer/components/img-viewer.component.spec.ts b/lib/core/src/lib/viewer/components/img-viewer.component.spec.ts index cb82dd6307..3bc0b8a3cf 100644 --- a/lib/core/src/lib/viewer/components/img-viewer.component.spec.ts +++ b/lib/core/src/lib/viewer/components/img-viewer.component.spec.ts @@ -138,7 +138,7 @@ describe('Test Img viewer component ', () => { }); it('The file Name should be present in the alt attribute', () => { - component.nameFile = 'fake-name'; + component.fileName = 'fake-name'; fixture.detectChanges(); expect(element.querySelector('#viewer-image').getAttribute('alt')).toEqual('fake-name'); }); diff --git a/lib/core/src/lib/viewer/components/viewer-render.component.html b/lib/core/src/lib/viewer/components/viewer-render.component.html index 2cbfc82da8..5f1f3fec92 100644 --- a/lib/core/src/lib/viewer/components/viewer-render.component.html +++ b/lib/core/src/lib/viewer/components/viewer-render.component.html @@ -23,7 +23,7 @@ fxFlexOrder="1" fxFlex="1 1 auto">
-
+
@@ -50,7 +50,7 @@ diff --git a/lib/core/src/lib/viewer/components/viewer-render.component.spec.ts b/lib/core/src/lib/viewer/components/viewer-render.component.spec.ts index 0672196fb7..4b8bcba50c 100644 --- a/lib/core/src/lib/viewer/components/viewer-render.component.spec.ts +++ b/lib/core/src/lib/viewer/components/viewer-render.component.spec.ts @@ -18,126 +18,24 @@ import { Location } from '@angular/common'; import { SpyLocation } from '@angular/common/testing'; import { Component, ViewChild } from '@angular/core'; -import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing'; -import { AlfrescoApiService, RenditionsService } from '../../services'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { throwError } from 'rxjs'; -import { EventMock } from '../../mock/event.mock'; import { RenderingQueueServices } from '../services/rendering-queue.services'; -import { ViewerRenderComponent } from './viewer-render.component.ts'; +import { ViewerRenderComponent } from './viewer-render.component'; import { setupTestBed } from '../../testing/setup-test-bed'; -import { NodeEntry, VersionEntry } from '@alfresco/js-api'; import { CoreTestingModule } from '../../testing/core.testing.module'; import { TranslateModule } from '@ngx-translate/core'; import { MatDialog } from '@angular/material/dialog'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; -import { UploadService } from '../../services/upload.service'; -import { FileModel } from '../../models'; import { AppExtensionService, ViewerExtensionRef } from '@alfresco/adf-extensions'; import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; -@Component({ - selector: 'adf-viewer-container-toolbar', - template: ` - - -
-
-
- ` -}) -class ViewerWithCustomToolbarComponent { -} - -@Component({ - selector: 'adf-viewer-container-toolbar-actions', - template: ` - - - - - - ` -}) -class ViewerWithCustomToolbarActionsComponent { -} - -@Component({ - selector: 'adf-viewer-container-sidebar', - template: ` - - -
-
-
- ` -}) -class ViewerWithCustomSidebarComponent { -} - -@Component({ - selector: 'adf-dialog-dummy', - template: `` -}) -class DummyDialogComponent { -} - -@Component({ - selector: 'adf-viewer-container-open-with', - template: ` - - - - - - - - ` -}) -class ViewerWithCustomOpenWithComponent { -} - -@Component({ - selector: 'adf-viewer-container-more-actions', - template: ` - - - - - - - - ` -}) -class ViewerWithCustomMoreActionsComponent { -} - @Component({ selector: 'adf-double-viewer', template: ` - - + + ` }) class DoubleViewerComponent { @@ -147,16 +45,18 @@ class DoubleViewerComponent { @ViewChild('viewer2') viewer2: ViewerRenderComponent; + urlFileViewer1: string; + urlFileViewer2: string; + } + describe('ViewerComponent', () => { let component: ViewerRenderComponent; let fixture: ComponentFixture; - let alfrescoApiService: AlfrescoApiService; let element: HTMLElement; - let dialog: MatDialog; - let uploadService: UploadService; + let extensionService: AppExtensionService; setupTestBed({ @@ -168,21 +68,11 @@ describe('ViewerComponent', () => { MatIconModule ], declarations: [ - DoubleViewerComponent, - ViewerWithCustomToolbarComponent, - ViewerWithCustomSidebarComponent, - ViewerWithCustomOpenWithComponent, - ViewerWithCustomMoreActionsComponent, - ViewerWithCustomToolbarActionsComponent + DoubleViewerComponent ], providers: [ - { - provide: RenditionsService, useValue: { - getRendition: () => throwError('thrown') - } - }, RenderingQueueServices, - { provide: Location, useClass: SpyLocation }, + {provide: Location, useClass: SpyLocation}, MatDialog ] }); @@ -192,9 +82,6 @@ describe('ViewerComponent', () => { element = fixture.nativeElement; component = fixture.componentInstance; - uploadService = TestBed.inject(UploadService); - alfrescoApiService = TestBed.inject(AlfrescoApiService); - dialog = TestBed.inject(MatDialog); extensionService = TestBed.inject(AppExtensionService); }); @@ -210,8 +97,8 @@ describe('ViewerComponent', () => { await fixtureDouble.detectChanges(); await fixtureDouble.whenStable(); - fixtureDouble.componentInstance.viewer1.urlFile = 'fake-test-file.pdf'; - fixtureDouble.componentInstance.viewer2.urlFile = 'fake-test-file-two.xls'; + fixtureDouble.componentInstance.urlFileViewer1 = 'fake-test-file.pdf'; + fixtureDouble.componentInstance.urlFileViewer2 = 'fake-test-file-two.xls'; fixtureDouble.componentInstance.viewer1.ngOnChanges(); fixtureDouble.componentInstance.viewer2.ngOnChanges(); @@ -219,16 +106,20 @@ describe('ViewerComponent', () => { await fixtureDouble.detectChanges(); await fixtureDouble.whenStable(); - expect(fixtureDouble.componentInstance.viewer1.viewerType).toBe('pdf'); - expect(fixtureDouble.componentInstance.viewer2.viewerType).toBe('unknown'); + expect(fixtureDouble.componentInstance.viewer1.internalViewerType).toBe('pdf'); + expect(fixtureDouble.componentInstance.viewer2.internalViewerType).toBe('unknown'); - fixtureDouble.componentInstance.viewer1.urlFile = 'fake-test-file.pdf'; - fixtureDouble.componentInstance.viewer2.urlFile = 'fake-test-file-two.png'; + fixtureDouble.componentInstance.urlFileViewer1 = 'fake-test-file.pdf'; + fixtureDouble.componentInstance.urlFileViewer2 = 'fake-test-file-two.png'; - (fixtureDouble.componentInstance.viewer2 as any).viewUtilService.viewerTypeChange.next('png'); + await fixtureDouble.detectChanges(); + await fixtureDouble.whenStable(); - expect(fixtureDouble.componentInstance.viewer1.viewerType).toBe('pdf'); - expect(fixtureDouble.componentInstance.viewer2.viewerType).toBe('png'); + fixtureDouble.componentInstance.viewer1.ngOnChanges(); + fixtureDouble.componentInstance.viewer2.ngOnChanges(); + + expect(fixtureDouble.componentInstance.viewer1.internalViewerType).toBe('pdf'); + expect(fixtureDouble.componentInstance.viewer2.internalViewerType).toBe('image'); }); }); @@ -253,7 +144,7 @@ describe('ViewerComponent', () => { expect(component.externalExtensions.includes('*')).toBe(true); expect(component.externalViewer).toBe(extension); - expect(component.viewerType).toBe('external'); + expect(component.internalViewerType).toBe('external'); expect(element.querySelector('[data-automation-id="custom.component"]')).not.toBeNull(); }); @@ -306,36 +197,10 @@ describe('ViewerComponent', () => { expect(component.externalExtensions.includes('*')).toBe(true); expect(component.externalViewer).toBe(extension); - expect(component.viewerType).toBe('external'); + expect(component.internalViewerType).toBe('external'); expect(element.querySelector('[data-automation-id="custom.component"]')).not.toBeNull(); }); - it('should use external viewer to display node by id', fakeAsync(() => { - const extension: ViewerExtensionRef = { - component: 'custom.component', - id: 'custom.component.id', - fileExtension: '*' - }; - spyOn(extensionService, 'getViewerExtensions').and.returnValue([extension]); - - fixture = TestBed.createComponent(ViewerRenderComponent); - element = fixture.nativeElement; - component = fixture.componentInstance; - - spyOn(component.nodesApi, 'getNode').and.callFake(() => Promise.resolve(new NodeEntry({ entry: {} }))); - - component.nodeId = '37f7f34d-4e64-4db6-bb3f-5c89f7844251'; - component.ngOnChanges(); - - fixture.detectChanges(); - tick(100); - - expect(component.nodesApi.getNode).toHaveBeenCalled(); - expect(component.viewerType).toBe('external'); - expect(component.isLoading).toBeFalsy(); - expect(element.querySelector('[data-automation-id="custom.component"]')).not.toBeNull(); - })); - it('should extension file pdf be loaded', (done) => { component.urlFile = 'fake-test-file.pdf'; component.ngOnChanges(); @@ -438,27 +303,6 @@ describe('ViewerComponent', () => { }); }); - it('should node without content show unkonwn', (done) => { - const displayName = 'the-name'; - const contentUrl = '/content/url/path'; - - component.nodeId = '12'; - component.urlFile = null; - component.displayName = null; - spyOn(component['nodesApi'], 'getNode').and.returnValue(Promise.resolve(new NodeEntry({ - entry: { content: { name: displayName, id: '12' } } - }))); - - spyOn(component['contentApi'], 'getContentUrl').and.returnValue(contentUrl); - - component.ngOnChanges(); - fixture.whenStable().then(() => { - fixture.detectChanges(); - expect(element.querySelector('adf-viewer-unknown-format')).toBeDefined(); - done(); - }); - }); - it('should display the media player if the file identified by mimetype is a media when the filename has wrong extension', (done) => { component.urlFile = 'fake-content-video.bin'; component.mimeType = 'video/mp4'; @@ -512,672 +356,28 @@ describe('ViewerComponent', () => { }, 25000); }); - it('should change display name every time node changes', fakeAsync(() => { - spyOn(component['nodesApi'], 'getNode').and.returnValues( - Promise.resolve(new NodeEntry({ entry: { name: 'file1', content: {} } })), - Promise.resolve(new NodeEntry({ entry: { name: 'file2', content: {} } })) - ); - - component.urlFile = null; - component.displayName = null; - component.blobFile = null; - component.showViewer = true; - - component.nodeId = 'id1'; - component.ngOnChanges(); - tick(); - - expect(component.fileTitle).toBe('file1'); - - component.nodeId = 'id2'; - component.ngOnChanges(); - tick(); - - expect(component.fileTitle).toBe('file2'); - })); - - it('should append version of the file to the file content URL', fakeAsync(() => { - spyOn(component['nodesApi'], 'getNode').and.returnValue( - Promise.resolve(new NodeEntry({ - entry: { - name: 'file1', - content: {}, - properties: { 'cm:versionLabel': '10' } - } - })) - ); - spyOn(component['versionsApi'], 'getVersion').and.returnValue(Promise.resolve(undefined)); - - component.nodeId = 'id1'; - component.urlFile = null; - component.displayName = null; - component.blobFile = null; - component.showViewer = true; - - component.versionId = null; - component.ngOnChanges(); - tick(); - - expect(component.fileTitle).toBe('file1'); - expect(component.urlFileContent).toContain('/public/alfresco/versions/1/nodes/id1/content?attachment=false&10'); - })); - - it('should change display name every time node\`s version changes', fakeAsync(() => { - spyOn(component['nodesApi'], 'getNode').and.returnValue( - Promise.resolve(new NodeEntry({ entry: { name: 'node1', content: {} } })) - ); - - spyOn(component['versionsApi'], 'getVersion').and.returnValues( - Promise.resolve(new VersionEntry({ entry: { name: 'file1', content: {} } })), - Promise.resolve(new VersionEntry({ entry: { name: 'file2', content: {} } })) - ); - - component.nodeId = 'id1'; - component.urlFile = null; - component.displayName = null; - component.blobFile = null; - component.showViewer = true; - - component.versionId = '1.0'; - component.ngOnChanges(); - tick(); - - expect(component.fileTitle).toBe('file1'); - - component.versionId = '1.1'; - component.ngOnChanges(); - tick(); - - expect(component.fileTitle).toBe('file2'); - })); - - it('should update node only if node name changed', fakeAsync(() => { - spyOn(component['nodesApi'], 'getNode').and.returnValues( - Promise.resolve(new NodeEntry({ entry: { name: 'file1', content: {} } })) - ); - spyOn(component, 'getViewerTypeByExtension').and.returnValue('jpg'); - - component.urlFile = null; - component.displayName = null; - component.blobFile = null; - component.showViewer = true; - - component.nodeId = 'id1'; - fixture.detectChanges(); - component.ngOnChanges(); - tick(); - - expect(component.fileTitle).toBe('file1'); - - alfrescoApiService.nodeUpdated.next({ id: 'id1', name: 'file2' } as any); - fixture.detectChanges(); - expect(component.fileTitle).toBe('file2'); - - alfrescoApiService.nodeUpdated.next({ id: 'id1', name: 'file3' } as any); - fixture.detectChanges(); - expect(component.fileTitle).toBe('file3'); - - alfrescoApiService.nodeUpdated.next({ id: 'id2', name: 'file4' } as any); - fixture.detectChanges(); - expect(component.fileTitle).toBe('file3'); - expect(component.nodeId).toBe('id1'); - })); - - describe('Viewer Example Component Rendering', () => { - - it('should use custom toolbar', (done) => { - const customFixture = TestBed.createComponent(ViewerWithCustomToolbarComponent); - const customElement: HTMLElement = customFixture.nativeElement; - - customFixture.detectChanges(); - fixture.whenStable().then(() => { - expect(customElement.querySelector('.custom-toolbar-element')).toBeDefined(); - done(); - }); - }); - - it('should use custom toolbar actions', (done) => { - const customFixture = TestBed.createComponent(ViewerWithCustomToolbarActionsComponent); - const customElement: HTMLElement = customFixture.nativeElement; - - customFixture.detectChanges(); - fixture.whenStable().then(() => { - expect(customElement.querySelector('#custom-button')).toBeDefined(); - done(); - }); - }); - - it('should use custom info drawer', (done) => { - const customFixture = TestBed.createComponent(ViewerWithCustomSidebarComponent); - const customElement: HTMLElement = customFixture.nativeElement; - - customFixture.detectChanges(); - - fixture.whenStable().then(() => { - expect(customElement.querySelector('.custom-info-drawer-element')).toBeDefined(); - done(); - }); - }); - - it('should use custom open with menu', (done) => { - const customFixture = TestBed.createComponent(ViewerWithCustomOpenWithComponent); - const customElement: HTMLElement = customFixture.nativeElement; - - customFixture.detectChanges(); - - fixture.whenStable().then(() => { - expect(customElement.querySelector('.adf-viewer-container-open-with')).toBeDefined(); - done(); - }); - }); - - it('should use custom more actions menu', (done) => { - const customFixture = TestBed.createComponent(ViewerWithCustomMoreActionsComponent); - const customElement: HTMLElement = customFixture.nativeElement; - - customFixture.detectChanges(); - - fixture.whenStable().then(() => { - expect(customElement.querySelector('.adf-viewer-container-more-actions')).toBeDefined(); - done(); - }); - - }); - }); - describe('Base component', () => { beforeEach(() => { - component.showToolbar = true; component.urlFile = 'fake-test-file.pdf'; component.mimeType = 'application/pdf'; fixture.detectChanges(); }); - describe('SideBar Test', () => { - - it('should NOT display sidebar if is not allowed', (done) => { - component.showRightSidebar = true; - component.allowRightSidebar = false; - fixture.detectChanges(); - - fixture.whenStable().then(() => { - const sidebar = element.querySelector('#adf-right-sidebar'); - expect(sidebar).toBeNull(); - done(); - }); - }); - - it('should display sidebar on the right side', (done) => { - component.allowRightSidebar = true; - component.showRightSidebar = true; - fixture.detectChanges(); - - fixture.whenStable().then(() => { - const sidebar = element.querySelector('#adf-right-sidebar'); - expect(getComputedStyle(sidebar).order).toEqual('4'); - done(); - }); - }); - - it('should NOT display left sidebar if is not allowed', (done) => { - component.showLeftSidebar = true; - component.allowLeftSidebar = false; - fixture.detectChanges(); - - fixture.whenStable().then(() => { - const sidebar = element.querySelector('#adf-left-sidebar'); - expect(sidebar).toBeNull(); - done(); - }); - - }); - - it('should display sidebar on the left side', (done) => { - component.allowLeftSidebar = true; - component.showLeftSidebar = true; - fixture.detectChanges(); - - fixture.whenStable().then(() => { - const sidebar = element.querySelector('#adf-left-sidebar'); - expect(getComputedStyle(sidebar).order).toEqual('1'); - done(); - }); - }); - }); - - describe('Toolbar', () => { - - it('should show only next file button', async () => { - component.allowNavigate = true; - component.canNavigateBefore = false; - component.canNavigateNext = true; - - fixture.detectChanges(); - await fixture.whenStable(); - - const nextButton = element.querySelector('[data-automation-id="adf-toolbar-next-file"]'); - expect(nextButton).not.toBeNull(); - - const prevButton = element.querySelector('[data-automation-id="adf-toolbar-pref-file"]'); - expect(prevButton).toBeNull(); - }); - - it('should provide tooltip for next file button', async () => { - component.allowNavigate = true; - component.canNavigateBefore = false; - component.canNavigateNext = true; - - fixture.detectChanges(); - await fixture.whenStable(); - - const nextButton = element.querySelector('[data-automation-id="adf-toolbar-next-file"]'); - expect(nextButton.title).toBe('ADF_VIEWER.ACTIONS.NEXT_FILE'); - }); - - it('should show only previous file button', async () => { - component.allowNavigate = true; - component.canNavigateBefore = true; - component.canNavigateNext = false; - - fixture.detectChanges(); - await fixture.whenStable(); - - const nextButton = element.querySelector('[data-automation-id="adf-toolbar-next-file"]'); - expect(nextButton).toBeNull(); - - const prevButton = element.querySelector('[data-automation-id="adf-toolbar-pref-file"]'); - expect(prevButton).not.toBeNull(); - }); - - it('should provide tooltip for the previous file button', async () => { - component.allowNavigate = true; - component.canNavigateBefore = true; - component.canNavigateNext = false; - - fixture.detectChanges(); - await fixture.whenStable(); - - const prevButton = element.querySelector('[data-automation-id="adf-toolbar-pref-file"]'); - expect(prevButton.title).toBe('ADF_VIEWER.ACTIONS.PREV_FILE'); - }); - - it('should show both file navigation buttons', async () => { - component.allowNavigate = true; - component.canNavigateBefore = true; - component.canNavigateNext = true; - - fixture.detectChanges(); - await fixture.whenStable(); - - const nextButton = element.querySelector('[data-automation-id="adf-toolbar-next-file"]'); - expect(nextButton).not.toBeNull(); - - const prevButton = element.querySelector('[data-automation-id="adf-toolbar-pref-file"]'); - expect(prevButton).not.toBeNull(); - }); - - it('should not show navigation buttons', async () => { - component.allowNavigate = false; - - fixture.detectChanges(); - await fixture.whenStable(); - - const nextButton = element.querySelector('[data-automation-id="adf-toolbar-next-file"]'); - expect(nextButton).toBeNull(); - - const prevButton = element.querySelector('[data-automation-id="adf-toolbar-pref-file"]'); - expect(prevButton).toBeNull(); - }); - - it('should now show navigation buttons even if navigation enabled', async () => { - component.allowNavigate = true; - component.canNavigateBefore = false; - component.canNavigateNext = false; - - fixture.detectChanges(); - await fixture.whenStable(); - - const nextButton = element.querySelector('[data-automation-id="adf-toolbar-next-file"]'); - expect(nextButton).toBeNull(); - - const prevButton = element.querySelector('[data-automation-id="adf-toolbar-pref-file"]'); - expect(prevButton).toBeNull(); - }); - - it('should render fullscreen button', () => { - expect(element.querySelector('[data-automation-id="adf-toolbar-fullscreen"]')).toBeDefined(); - }); - - it('should not render fullscreen button', (done) => { - component.allowFullScreen = false; - fixture.detectChanges(); - - fixture.whenStable().then(() => { - expect(element.querySelector('[data-automation-id="adf-toolbar-fullscreen"]')).toBeNull(); - done(); - }); - }); - - it('should render default download button', (done) => { - component.allowDownload = true; - - fixture.whenStable().then(() => { - expect(element.querySelector('[data-automation-id="adf-toolbar-download"]')).toBeDefined(); - done(); - }); - }); - - it('should not render default download button', (done) => { - component.allowDownload = false; - fixture.detectChanges(); - - fixture.whenStable().then(() => { - expect(element.querySelector('[data-automation-id="adf-toolbar-download"]')).toBeNull(); - done(); - }); - }); - - it('should render default print button', (done) => { - component.allowPrint = true; - fixture.detectChanges(); - - fixture.whenStable().then(() => { - expect(element.querySelector('[data-automation-id="adf-toolbar-print"]')).toBeDefined(); - done(); - }); - }); - - it('should not render default print button', (done) => { - component.allowPrint = false; - fixture.detectChanges(); - - fixture.whenStable().then(() => { - expect(element.querySelector('[data-automation-id="adf-toolbar-print"]')).toBeNull(); - done(); - }); - }); - - it('should invoke print action with the toolbar button', (done) => { - component.allowPrint = true; - fixture.detectChanges(); - - spyOn(component, 'printContent').and.stub(); - - const button: HTMLButtonElement = element.querySelector('[data-automation-id="adf-toolbar-print"]') as HTMLButtonElement; - button.click(); - - fixture.whenStable().then(() => { - expect(component.printContent).toHaveBeenCalled(); - done(); - }); - }); - - it('should raise the print event with the toolbar button', (done) => { - component.allowPrint = true; - fixture.detectChanges(); - - component.print.subscribe((e) => { - expect(e).not.toBeNull(); - done(); - }); - - const button: HTMLButtonElement = element.querySelector('[data-automation-id="adf-toolbar-print"]') as HTMLButtonElement; - button.click(); - }); - - it('should get and assign node for download', (done) => { - component.nodeId = '12'; - component.urlFile = ''; - const displayName = 'the-name'; - const nodeDetails = { - entry: { name: displayName, id: '12', content: { mimeType: 'txt' } } - }; - - const contentUrl = '/content/url/path'; - - const node = new NodeEntry(nodeDetails); - - spyOn(component['nodesApi'], 'getNode').and.returnValue(Promise.resolve(node)); - spyOn(component['contentApi'], 'getContentUrl').and.returnValue(contentUrl); - - component.ngOnChanges(); - fixture.whenStable().then(() => { - fixture.detectChanges(); - expect(component.nodeEntry).toBe(node); - done(); - }); - }); - - it('should render close viewer button if it is not a shared link', (done) => { - fixture.detectChanges(); - fixture.whenStable().then(() => { - fixture.detectChanges(); - expect(element.querySelector('[data-automation-id="adf-toolbar-back"]')).toBeDefined(); - expect(element.querySelector('[data-automation-id="adf-toolbar-back"]')).not.toBeNull(); - done(); - }); - }); - - it('should emit `showViewerChange` event on close', async () => { - spyOn(component.showViewerChange, 'emit'); - - const button: HTMLButtonElement = element.querySelector('[data-automation-id="adf-toolbar-back"]') as HTMLButtonElement; - button.click(); - - fixture.detectChanges(); - await fixture.whenStable(); - - expect(component.showViewerChange.emit).toHaveBeenCalled(); - }); - - it('should not render close viewer button if it is a shared link', (done) => { - spyOn(component['sharedLinksApi'], 'getSharedLink') - .and.returnValue(Promise.reject({})); - - component.sharedLinkId = 'the-Shared-Link-id'; - component.urlFile = null; - component.mimeType = null; - - component.ngOnChanges(); - fixture.whenStable().then(() => { - fixture.detectChanges(); - expect(element.querySelector('[data-automation-id="adf-toolbar-back"]')).toBeNull(); - done(); - }); - }); - - }); - - describe('View', () => { - - describe('Overlay mode true', () => { - - beforeEach(() => { - component.overlayMode = true; - fixture.detectChanges(); - }); - - it('should header be present if is overlay mode', () => { - expect(element.querySelector('.adf-viewer-toolbar')).not.toBeNull(); - }); - - it('should Name File be present if is overlay mode ', (done) => { - component.ngOnChanges(); - fixture.detectChanges(); - fixture.whenStable().then(() => { - fixture.detectChanges(); - expect(element.querySelector('#adf-viewer-display-name').textContent).toEqual('fake-test-file.pdf'); - done(); - }); - }); - - it('should Close button be present if overlay mode', (done) => { - fixture.detectChanges(); - fixture.whenStable().then(() => { - fixture.detectChanges(); - expect(element.querySelector('.adf-viewer-close-button')).not.toBeNull(); - done(); - }); - }); - - it('should Click on close button hide the viewer', (done) => { - const closebutton: any = element.querySelector('.adf-viewer-close-button'); - closebutton.click(); - fixture.detectChanges(); - - fixture.whenStable().then(() => { - expect(element.querySelector('.adf-viewer-content')).toBeNull(); - done(); - }); - }); - - it('should Esc button hide the viewer', (done) => { - EventMock.keyDown(27); - fixture.detectChanges(); - - fixture.whenStable().then(() => { - expect(element.querySelector('.adf-viewer-content')).toBeNull(); - done(); - }); - }); - - it('should not close the viewer on Escape event if dialog was opened', (done) => { - const event = new KeyboardEvent('keydown', { - bubbles: true, - keyCode: 27 - } as KeyboardEventInit); - const dialogRef = dialog.open(DummyDialogComponent); - - dialogRef.afterClosed().subscribe(() => { - document.body.dispatchEvent(event); - fixture.detectChanges(); - expect(element.querySelector('.adf-viewer-content')).toBeNull(); - done(); - }); - - fixture.detectChanges(); - - document.body.dispatchEvent(event); - fixture.detectChanges(); - expect(element.querySelector('.adf-viewer-content')).not.toBeNull(); - }); - }); - - describe('Overlay mode false', () => { - - beforeEach(() => { - component.overlayMode = false; - fixture.detectChanges(); - }); - - it('should Esc button not hide the viewer if is not overlay mode', (done) => { - EventMock.keyDown(27); - fixture.detectChanges(); - - fixture.whenStable().then(() => { - expect(element.querySelector('.adf-viewer-content')).not.toBeNull(); - done(); - }); - }); - }); - }); - describe('Attribute', () => { - it('should Url or nodeId be mandatory', () => { - component.showViewer = true; - component.nodeId = undefined; - component.urlFile = undefined; - - expect(() => { - component.ngOnChanges(); - }).toThrow(); - }); - - it('should FileNodeId present not thrown any error ', () => { - component.showViewer = true; - component.nodeId = 'file-node-id'; - component.urlFile = undefined; - - expect(() => { - component.ngOnChanges(); - }).not.toThrow(); - }); - it('should urlFile present not thrown any error ', () => { - component.showViewer = true; - component.nodeId = undefined; - expect(() => { component.ngOnChanges(); }).not.toThrow(); }); - it('should showViewer default value be true', () => { - expect(component.showViewer).toBe(true); - }); - - it('should viewer be hide if showViewer value is false', () => { - component.showViewer = false; - - fixture.detectChanges(); - expect(element.querySelector('.adf-viewer-content')).toBeNull(); - }); }); describe('error handling', () => { - it('should show unknown view when node file not found', (done) => { - spyOn(component['nodesApi'], 'getNode') - .and.returnValue(Promise.reject({})); - - component.nodeId = 'the-node-id-of-the-file-to-preview'; - component.urlFile = null; - component.mimeType = null; - - component.ngOnChanges(); - fixture.whenStable().then(() => { - fixture.detectChanges(); - expect(element.querySelector('adf-viewer-unknown-format')).not.toBeNull(); - done(); - }); - }); - - it('should show unknown view when sharedLink file not found', (done) => { - spyOn(component['sharedLinksApi'], 'getSharedLink') - .and.returnValue(Promise.reject({})); - - component.sharedLinkId = 'the-Shared-Link-id'; - component.urlFile = null; - component.mimeType = null; - - component.ngOnChanges(); - fixture.whenStable().then(() => { - fixture.detectChanges(); - expect(element.querySelector('adf-viewer-unknown-format')).not.toBeNull(); - done(); - }); - - }); - - it('should raise an event when the shared link is invalid', fakeAsync(() => { - spyOn(component['sharedLinksApi'], 'getSharedLink') - .and.returnValue(Promise.reject({})); - - component.sharedLinkId = 'the-Shared-Link-id'; - component.urlFile = null; - component.mimeType = null; - - component.invalidSharedLink.subscribe((emittedValue) => { - expect(emittedValue).toBeUndefined(); - }); - - component.ngOnChanges(); - })); - - it('should swicth to the unkwown template if the type specific viewers throw an error', (done) => { + it('should switch to the unknown template if the type specific viewers throw an error', (done) => { component.urlFile = 'fake-url-file.icns'; component.mimeType = 'image/png'; component.ngOnChanges(); @@ -1191,7 +391,6 @@ describe('ViewerComponent', () => { done(); }); }); - }); describe('Events', () => { @@ -1207,64 +406,13 @@ describe('ViewerComponent', () => { component.ngOnChanges(); }); - - it('should update version when emitted by image-viewer and user has update permissions', () => { - spyOn(uploadService, 'uploadFilesInTheQueue').and.callFake(() => { - }); - spyOn(uploadService, 'addToQueue'); - component.readOnly = false; - component.nodeEntry = new NodeEntry({ - entry: { - name: 'fakeImage.png', - id: '12', - content: { mimeType: 'img/png' } - } - }); - const data = atob('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=='); - const fakeBlob = new Blob([data], { type: 'image/png' }); - const newImageFile: File = new File([fakeBlob], component?.nodeEntry?.entry?.name, { type: component?.nodeEntry?.entry?.content?.mimeType }); - const newFile = new FileModel( - newImageFile, - { - majorVersion: false, - newVersion: true, - parentId: component?.nodeEntry?.entry?.parentId, - nodeType: component?.nodeEntry?.entry?.content?.mimeType - }, - component.nodeEntry.entry?.id - ); - component.onSubmitFile(fakeBlob); - fixture.detectChanges(); - - expect(uploadService.addToQueue).toHaveBeenCalledWith(...[newFile]); - expect(uploadService.uploadFilesInTheQueue).toHaveBeenCalled(); - }); - - it('should not update version when emitted by image-viewer and user doesn`t have update permissions', () => { - spyOn(uploadService, 'uploadFilesInTheQueue').and.callFake(() => { - }); - component.readOnly = true; - component.nodeEntry = new NodeEntry({ - entry: { - name: 'fakeImage.png', - id: '12', - content: { mimeType: 'img/png' } - } - }); - const data = atob('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=='); - const fakeBlob = new Blob([data], { type: 'image/png' }); - component.onSubmitFile(fakeBlob); - fixture.detectChanges(); - - expect(uploadService.uploadFilesInTheQueue).not.toHaveBeenCalled(); - }); }); describe('display name property override by urlFile', () => { it('should displayName override the default name if is present and urlFile is set', (done) => { component.urlFile = 'fake-test-file.pdf'; - component.displayName = 'test name'; + component.fileName = 'test name'; fixture.detectChanges(); component.ngOnChanges(); @@ -1277,7 +425,7 @@ describe('ViewerComponent', () => { it('should use the urlFile name if displayName is NOT set and urlFile is set', (done) => { component.urlFile = 'fake-test-file.pdf'; - component.displayName = null; + component.fileName = null; fixture.detectChanges(); component.ngOnChanges(); @@ -1292,8 +440,8 @@ describe('ViewerComponent', () => { describe('display name property override by blobFile', () => { it('should displayName override the name if is present and blobFile is set', (done) => { - component.displayName = 'blob file display name'; - component.blobFile = new Blob(['This is my blob content'], { type: 'text/plain' }); + component.fileName = 'blob file display name'; + component.blobFile = new Blob(['This is my blob content'], {type: 'text/plain'}); fixture.detectChanges(); component.ngOnChanges(); @@ -1305,8 +453,8 @@ describe('ViewerComponent', () => { }); it('should show uknownn name if displayName is NOT set and blobFile is set', (done) => { - component.displayName = null; - component.blobFile = new Blob(['This is my blob content'], { type: 'text/plain' }); + component.fileName = null; + component.blobFile = new Blob(['This is my blob content'], {type: 'text/plain'}); fixture.detectChanges(); component.ngOnChanges(); @@ -1317,89 +465,5 @@ describe('ViewerComponent', () => { }); }); }); - - describe('display name property override by nodeId', () => { - - const contentUrl = '/content/url/path'; - const nodeDetails = new NodeEntry({ - entry: { - name: 'node-id-name', - id: '12', - content: { mimeType: 'txt' } - } - }); - - it('should use the node name if displayName is NOT set and nodeId is set', (done) => { - spyOn(component['nodesApi'], 'getNode').and.returnValue(Promise.resolve(nodeDetails)); - spyOn(component['contentApi'], 'getContentUrl').and.returnValue(contentUrl); - - component.nodeId = '12'; - component.urlFile = null; - component.displayName = 'the-name'; - - component.ngOnChanges(); - fixture.whenStable().then(() => { - fixture.detectChanges(); - expect(element.querySelector('#adf-viewer-display-name').textContent).toEqual('the-name'); - done(); - }); - }); - }); - }); - - describe('Viewer component - Full Screen Mode - Mocking fixture element', () => { - - beforeEach(() => { - fixture = TestBed.createComponent(ViewerRenderComponent); - element = fixture.nativeElement; - component = fixture.componentInstance; - - component.showToolbar = true; - component.urlFile = 'fake-test-file.pdf'; - component.mimeType = 'application/pdf'; - fixture.detectChanges(); - }); - - it('should request only if enabled', () => { - const domElement = jasmine.createSpyObj('el', ['requestFullscreen']); - spyOn(fixture.nativeElement, 'querySelector').and.returnValue(domElement); - - component.allowFullScreen = false; - component.enterFullScreen(); - - expect(domElement.requestFullscreen).not.toHaveBeenCalled(); - }); - - it('should use standard mode', () => { - const domElement = jasmine.createSpyObj('el', ['requestFullscreen']); - spyOn(fixture.nativeElement, 'querySelector').and.returnValue(domElement); - - component.enterFullScreen(); - expect(domElement.requestFullscreen).toHaveBeenCalled(); - }); - - it('should use webkit prefix', () => { - const domElement = jasmine.createSpyObj('el', ['webkitRequestFullscreen']); - spyOn(fixture.nativeElement, 'querySelector').and.returnValue(domElement); - - component.enterFullScreen(); - expect(domElement.webkitRequestFullscreen).toHaveBeenCalled(); - }); - - it('should use moz prefix', () => { - const domElement = jasmine.createSpyObj('el', ['mozRequestFullScreen']); - spyOn(fixture.nativeElement, 'querySelector').and.returnValue(domElement); - - component.enterFullScreen(); - expect(domElement.mozRequestFullScreen).toHaveBeenCalled(); - }); - - it('should use ms prefix', () => { - const domElement = jasmine.createSpyObj('el', ['msRequestFullscreen']); - spyOn(fixture.nativeElement, 'querySelector').and.returnValue(domElement); - - component.enterFullScreen(); - expect(domElement.msRequestFullscreen).toHaveBeenCalled(); - }); }); }); diff --git a/lib/core/src/lib/viewer/components/viewer-render.component.ts b/lib/core/src/lib/viewer/components/viewer-render.component.ts index e68bb444db..fc517db53c 100644 --- a/lib/core/src/lib/viewer/components/viewer-render.component.ts +++ b/lib/core/src/lib/viewer/components/viewer-render.component.ts @@ -16,7 +16,6 @@ */ //TODO FIX documentation -//TODO FIX unit test import { Component, EventEmitter, @@ -82,10 +81,6 @@ export class ViewerRenderComponent implements OnChanges, OnInit, OnDestroy { @Input() viewerType: string = 'unknown'; - /** Allows `back` navigation */ - @Input() - allowGoBack = true; - /** Override loading status */ @Input() isLoading = false; @@ -112,6 +107,8 @@ export class ViewerRenderComponent implements OnChanges, OnInit, OnDestroy { extensionTemplates: { template: TemplateRef; isVisible: boolean }[] = []; extension: string; + internalViewerType: string; + internalFileName: string; /** * Returns a list of the active Viewer content extensions. @@ -159,27 +156,26 @@ export class ViewerRenderComponent implements OnChanges, OnInit, OnDestroy { if (this.blobFile) { this.setUpBlobData(); - this.isLoading = false; } else if (this.urlFile) { this.setUpUrlFile(); - this.isLoading = false; } + this.isLoading = false; } private setUpBlobData() { - this.mimeType = this.blobFile.type; - this.viewerType = this.viewUtilService.getViewerTypeByMimeType(this.mimeType); + this.internalFileName = this.fileName; + this.internalViewerType = this.viewUtilService.getViewerTypeByMimeType(this.blobFile.type); - this.extensionChange.emit(this.mimeType); + this.extensionChange.emit(this.blobFile.type); this.scrollTop(); } private setUpUrlFile() { - this.fileName = this.fileName ? this.fileName : this.viewUtilService.getFilenameFromUrl(this.urlFile); - this.extension = this.viewUtilService.getFileExtension(this.fileName); - this.viewerType = this.viewerType === 'unknown' ? this.viewUtilService.getViewerType(this.extension, this.mimeType) : this.viewerType; + this.internalFileName = this.fileName ? this.fileName : this.viewUtilService.getFilenameFromUrl(this.urlFile); + this.extension = this.viewUtilService.getFileExtension(this.internalFileName); + this.internalViewerType = this.viewerType === 'unknown' ? this.viewUtilService.getViewerType(this.extension, this.mimeType) : this.viewerType; - this.extensionChange.emit(this.extension); + this.extensionChange.emit(this.internalViewerType); this.scrollTop(); } diff --git a/lib/core/src/lib/viewer/directives/viewer-extension.directive.spec.ts b/lib/core/src/lib/viewer/directives/viewer-extension.directive.spec.ts index 4af5122677..be8e8c5298 100644 --- a/lib/core/src/lib/viewer/directives/viewer-extension.directive.spec.ts +++ b/lib/core/src/lib/viewer/directives/viewer-extension.directive.spec.ts @@ -19,7 +19,7 @@ import { Location } from '@angular/common'; import { SpyLocation } from '@angular/common/testing'; import { ChangeDetectorRef, ElementRef } from '@angular/core'; import { TestBed } from '@angular/core/testing'; -import { ViewerRenderComponent } from '../components/viewer-render.component.ts'; +import { ViewerRenderComponent } from '../components/viewer-render.component'; import { ViewerExtensionDirective } from './viewer-extension.directive'; import { setupTestBed } from '../../testing/setup-test-bed'; import { CoreTestingModule } from '../../testing/core.testing.module'; diff --git a/lib/core/src/lib/viewer/services/view-util.service.ts b/lib/core/src/lib/viewer/services/view-util.service.ts index 6c8b208642..cf360a0089 100644 --- a/lib/core/src/lib/viewer/services/view-util.service.ts +++ b/lib/core/src/lib/viewer/services/view-util.service.ts @@ -16,15 +16,12 @@ */ import { Injectable } from '@angular/core'; -import { LogService } from '../../services/log.service'; -import { RenditionViewerService } from "../../../../../content-services/src/lib/viewer/services/rendition-viewer.service"; import { AppExtensionService, ViewerExtensionRef } from '@alfresco/adf-extensions'; @Injectable({ providedIn: 'root' }) export class ViewUtilService { - static TARGET = '_new'; // Extensions that are supported by the Viewer without conversion private extensions = { @@ -56,9 +53,7 @@ export class ViewUtilService { return this.viewerExtensions.map(ext => ext.fileExtension); } - constructor(private renditionViewerService: RenditionViewerService, - private extensionService: AppExtensionService, - private logService: LogService) { + constructor(private extensionService: AppExtensionService) { } /** @@ -91,55 +86,6 @@ export class ViewUtilService { return null; } - /** - * This method takes a url to trigger the print dialog against, and the type of artifact that it - * is. - * This URL should be one that can be rendered in the browser, for example PDF, Image, or Text - */ - printFile(url: string, type: string): void { - const pwa = window.open(url, ViewUtilService.TARGET); - if (pwa) { - // Because of the way chrome focus and close image window vs. pdf preview window - if (type === RenditionViewerService.ContentGroup.IMAGE) { - pwa.onfocus = () => { - setTimeout(() => { - pwa.close(); - }, 500); - }; - } - - pwa.onload = () => { - pwa.print(); - }; - } - } - - /** - * Launch the File Print dialog from anywhere other than the preview service, which resolves the - * rendition of the object that can be printed from a web browser. - * These are: images, PDF files, or PDF rendition of files. - * We also force PDF rendition for TEXT type objects, otherwise the default URL is to download. - * TODO there are different TEXT type objects, (HTML, plaintext, xml, etc. we should determine how these are handled) - */ - printFileGeneric(objectId: string, mimeType: string): void { - const nodeId = objectId; - const type: string = this.renditionViewerService.getViewerTypeByMimeType(mimeType); - - this.renditionViewerService.getRendition(nodeId, RenditionViewerService.ContentGroup.PDF) - .then((value) => { - const url: string = this.renditionViewerService.getRenditionUrl(nodeId, type, (!!value)); - const printType = (type === RenditionViewerService.ContentGroup.PDF - || type === RenditionViewerService.ContentGroup.TEXT) - ? RenditionViewerService.ContentGroup.PDF : type; - this.printFile(url, printType); - }) - .catch((err) => { - this.logService.error('Error with Printing'); - this.logService.error(err); - }); - } - - getViewerType(extension: string, mimeType: string): string { let viewerType = this.getViewerTypeByExtension(extension); @@ -196,7 +142,6 @@ export class ViewUtilService { return 'unknown'; } - private isExternalViewer(): boolean { return !!this.viewerExtensions.find(ext => ext.fileExtension === '*'); } @@ -212,5 +157,4 @@ export class ViewUtilService { return false; } - }