AAE-22975 update to the latest version of pdfjs-dist library (#10780)

* AAE-30877 squash-merged improvement/AAE-30877-update-package-pdfjs-dist

* AAE-30877 statically import pdf_viewer

* AAE-30877 fix failing worker load

* AAE-22975 attempt without excessive logs

* AAE-22975 remove pdf_viewer from karma includes

* AAE-22975 remove incorrect firstValueFrom

* AAE-22975 await rendered event callback

* AAE-22975 change pdf mock code

* AAE-22975 change pdf mock code

* AAE-22975 mock pdfjs-dist package in tests to improve performance

* AAE-22975 fix pagesLoaded test

* AAE-22975 fix insights tests

* AAE-22975 un-focus pdf-viewer tests, remove logs

* AAE-22975 add defer block

* AAE-22975 add pdfjsLib injection token

* AAE-22975 remove defer block

* AAE-22975 change pdfjs import path

* AAE-22975 change pdfjsviewer import path

* AAE-22975 limit usage of direct PDFViewer

* AAE-22975 cleanup
This commit is contained in:
Wojciech Duda
2025-04-22 10:05:44 +02:00
committed by GitHub
parent 0dc45e95af
commit fdd4f6c081
18 changed files with 702 additions and 540 deletions

View File

@@ -26,15 +26,13 @@ describe('DownloadService', () => {
describe('Download blob', () => {
it('Should use native msSaveOrOpenBlob if the browser is IE', () => {
const navigatorAny: any = window.navigator;
// eslint-disable-next-line no-underscore-dangle
navigatorAny.__defineGetter__('msSaveOrOpenBlob', (result) => {
expect(result).toBeUndefined();
});
const mockNavigator = jasmine.createSpyObj(['msSaveOrOpenBlob']);
spyOnProperty(window, 'navigator', 'get').and.returnValue(mockNavigator);
const blob = new Blob([''], { type: 'text/html' });
service.downloadBlob(blob, 'test_ie');
expect(mockNavigator.msSaveOrOpenBlob).toHaveBeenCalledOnceWith(blob, 'test_ie');
});
});
});

View File

@@ -16,13 +16,16 @@
*/
import { ComponentFixture, inject, TestBed } from '@angular/core/testing';
import { CoreTestingModule } from '../../testing/core.testing.module';
import { NotificationHistoryComponent } from './notification-history.component';
import { OverlayContainer } from '@angular/cdk/overlay';
import { NotificationService } from '../services/notification.service';
import { StorageService } from '../../common/services/storage.service';
import { NOTIFICATION_TYPE, NotificationModel } from '../models/notification.model';
import { UnitTestingUtils } from '../../testing/unit-testing-utils';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { NoopTranslateModule } from '../../testing/noop-translate.module';
import { NoopAuthModule } from '../../testing/noop-auth.module';
import { MatIconTestingModule } from '@angular/material/icon/testing';
describe('Notification History Component', () => {
let fixture: ComponentFixture<NotificationHistoryComponent>;
@@ -40,7 +43,7 @@ describe('Notification History Component', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [CoreTestingModule, NotificationHistoryComponent]
imports: [NoopAnimationsModule, NoopTranslateModule, NoopAuthModule, NotificationHistoryComponent, MatIconTestingModule]
});
fixture = TestBed.createComponent(NotificationHistoryComponent);
component = fixture.componentInstance;

View File

@@ -131,15 +131,15 @@ describe('NotificationService', () => {
it('should open a message notification bar', async () => {
fixture.componentInstance.sendMessage();
fixture.detectChanges();
expect(await testingUtils.checkIfMatSnackbarExists()).toBe(true);
const isLoaded = await testingUtils.checkIfMatSnackbarExists();
expect(isLoaded).toBe(true);
});
it('should open a message notification bar without custom configuration', async () => {
fixture.componentInstance.sendMessageWithoutConfig();
fixture.detectChanges();
expect(await testingUtils.checkIfMatSnackbarExists()).toBe(true);
const isLoaded = await testingUtils.checkIfMatSnackbarExists();
expect(isLoaded).toBe(true);
});
it('should open a message notification bar with custom configuration', async () => {

View File

@@ -0,0 +1,37 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* 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.
*/
export default {
GlobalWorkerOptions: {},
getDocument() {
return {
loadingTask: () => ({
destroy: () => Promise.resolve()
}),
promise: new Promise((resolve) => {
resolve({
numPages: 6,
getPage: () => 'fakePage'
});
})
};
},
PasswordResponses: {
NEED_PASSWORD: 1,
INCORRECT_PASSWORD: 2
}
};

View File

@@ -17,29 +17,22 @@
import { LEFT_ARROW, RIGHT_ARROW } from '@angular/cdk/keycodes';
import { Component, SimpleChange, ViewChild } from '@angular/core';
import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
import { ComponentFixture, fakeAsync, flush, TestBed, tick } from '@angular/core/testing';
import { MatDialog } from '@angular/material/dialog';
import { By } from '@angular/platform-browser';
import { of } from 'rxjs';
import { take } from 'rxjs/operators';
import { AppConfigService } from '../../../app-config';
import { EventMock } from '../../../mock';
import { CoreTestingModule, UnitTestingUtils } from '../../../testing';
import { NoopAuthModule, NoopTranslateModule, UnitTestingUtils } from '../../../testing';
import { RenderingQueueServices } from '../../services/rendering-queue.services';
import { PdfThumbListComponent } from '../pdf-viewer-thumbnails/pdf-viewer-thumbnails.component';
import { PdfViewerComponent } from './pdf-viewer.component';
import { AnnotationMode } from 'pdfjs-dist';
import { PDFJS_MODULE, PDFJS_VIEWER_MODULE, PdfViewerComponent } from './pdf-viewer.component';
import pdfjsLibMock from '../mock/pdfjs-lib.mock';
declare const pdfjsLib: any;
@Component({
selector: 'adf-test-dialog-component',
standalone: true,
template: ''
})
class TestDialogComponent {}
@Component({
selector: 'adf-url-test-component',
standalone: true,
imports: [PdfViewerComponent],
template: ` <adf-pdf-viewer [allowThumbnails]="true" [showToolbar]="true" [urlFile]="urlFile" /> `
@@ -56,6 +49,7 @@ class UrlTestComponent {
}
@Component({
selector: 'adf-url-test-password-component',
standalone: true,
imports: [PdfViewerComponent],
template: ` <adf-pdf-viewer [allowThumbnails]="true" [showToolbar]="true" [urlFile]="urlFile" /> `
@@ -115,7 +109,7 @@ describe('Test PdfViewer component', () => {
beforeEach(async () => {
TestBed.configureTestingModule({
imports: [CoreTestingModule, UrlTestComponent, TestDialogComponent, UrlTestPasswordComponent, BlobTestComponent],
imports: [NoopAuthModule, NoopTranslateModule, PdfViewerComponent],
providers: [
{
provide: MatDialog,
@@ -138,360 +132,14 @@ describe('Test PdfViewer component', () => {
await fixture.whenStable();
});
describe('User interaction', () => {
let fixtureUrlTestComponent: ComponentFixture<UrlTestComponent>;
let componentUrlTestComponent: UrlTestComponent;
let elementUrlTestComponent: HTMLElement;
beforeEach((done) => {
fixtureUrlTestComponent = TestBed.createComponent(UrlTestComponent);
componentUrlTestComponent = fixtureUrlTestComponent.componentInstance;
elementUrlTestComponent = fixtureUrlTestComponent.nativeElement;
testingUtils.setDebugElement(fixtureUrlTestComponent.debugElement);
fixtureUrlTestComponent.detectChanges();
componentUrlTestComponent.pdfViewerComponent.rendered.pipe(take(1)).subscribe(() => {
done();
});
});
afterEach(() => {
document.body.removeChild(elementUrlTestComponent);
});
it('should init the viewer with annotation mode disabled', (done) => {
fixtureUrlTestComponent.detectChanges();
fixtureUrlTestComponent.whenStable().then(() => {
expect(componentUrlTestComponent.pdfViewerComponent.pdfViewer.annotationEditorMode).toBe(AnnotationMode.DISABLE);
done();
});
}, 55000);
it('should Total number of pages be loaded', (done) => {
fixtureUrlTestComponent.detectChanges();
fixtureUrlTestComponent.whenStable().then(() => {
expect(componentUrlTestComponent.pdfViewerComponent.totalPages).toBe(6);
done();
});
}, 55000);
it('should nextPage move to the next page', (done) => {
testingUtils.clickByCSS('#viewer-next-page-button');
fixtureUrlTestComponent.detectChanges();
fixtureUrlTestComponent.whenStable().then(() => {
expect(componentUrlTestComponent.pdfViewerComponent.displayPage).toBe(2);
done();
});
}, 55000);
it('should event RIGHT_ARROW keyboard change pages', fakeAsync(() => {
fixtureUrlTestComponent.whenStable();
fixtureUrlTestComponent.detectChanges();
EventMock.keyDown(RIGHT_ARROW);
tick(250);
expect(componentUrlTestComponent.pdfViewerComponent.displayPage).toBe(2);
}));
it('should event LEFT_ARROW keyboard change pages', (done) => {
component.inputPage('2');
fixtureUrlTestComponent.detectChanges();
fixtureUrlTestComponent.whenStable().then(() => {
EventMock.keyDown(LEFT_ARROW);
fixtureUrlTestComponent.detectChanges();
fixtureUrlTestComponent.whenStable().then(() => {
expect(componentUrlTestComponent.pdfViewerComponent.displayPage).toBe(1);
done();
});
});
}, 55000);
it('should previous page move to the previous page', (done) => {
testingUtils.clickByCSS('#viewer-next-page-button');
testingUtils.clickByCSS('#viewer-next-page-button');
testingUtils.clickByCSS('#viewer-previous-page-button');
fixtureUrlTestComponent.detectChanges();
fixtureUrlTestComponent.whenStable().then(() => {
expect(componentUrlTestComponent.pdfViewerComponent.displayPage).toBe(2);
done();
});
}, 55000);
it('should previous page not move to the previous page if is page 1', (done) => {
component.previousPage();
fixtureUrlTestComponent.detectChanges();
fixtureUrlTestComponent.whenStable().then(() => {
expect(componentUrlTestComponent.pdfViewerComponent.displayPage).toBe(1);
done();
});
}, 55000);
it('should Input page move to the inserted page', (done) => {
componentUrlTestComponent.pdfViewerComponent.inputPage('2');
fixtureUrlTestComponent.detectChanges();
fixtureUrlTestComponent.whenStable().then(() => {
expect(componentUrlTestComponent.pdfViewerComponent.displayPage).toBe(2);
done();
});
}, 55000);
describe('Zoom', () => {
it('should zoom in increment the scale value', (done) => {
spyOn(componentUrlTestComponent.pdfViewerComponent.pdfViewer, 'forceRendering').and.callFake(() => {});
const zoomBefore = componentUrlTestComponent.pdfViewerComponent.pdfViewer.currentScaleValue;
testingUtils.clickByCSS('#viewer-zoom-in-button');
fixtureUrlTestComponent.detectChanges();
expect(componentUrlTestComponent.pdfViewerComponent.currentScaleMode).toBe('auto');
const currentZoom = componentUrlTestComponent.pdfViewerComponent.pdfViewer.currentScaleValue;
expect(zoomBefore < currentZoom).toBe(true);
done();
}, 55000);
it('should zoom out decrement the scale value', (done) => {
spyOn(componentUrlTestComponent.pdfViewerComponent.pdfViewer, 'forceRendering').and.callFake(() => {});
const zoomBefore = componentUrlTestComponent.pdfViewerComponent.pdfViewer.currentScaleValue;
testingUtils.clickByCSS('#viewer-zoom-out-button');
fixtureUrlTestComponent.detectChanges();
expect(componentUrlTestComponent.pdfViewerComponent.currentScaleMode).toBe('auto');
const currentZoom = componentUrlTestComponent.pdfViewerComponent.pdfViewer.currentScaleValue;
expect(zoomBefore > currentZoom).toBe(true);
done();
}, 55000);
it('should it-in button toggle page-fit and auto scale mode', fakeAsync(() => {
spyOn(componentUrlTestComponent.pdfViewerComponent.pdfViewer, 'forceRendering').and.callFake(() => {});
tick(250);
expect(componentUrlTestComponent.pdfViewerComponent.currentScaleMode).toBe('init');
testingUtils.clickByCSS('#viewer-scale-page-button');
expect(componentUrlTestComponent.pdfViewerComponent.currentScaleMode).toBe('page-fit');
testingUtils.clickByCSS('#viewer-scale-page-button');
expect(componentUrlTestComponent.pdfViewerComponent.currentScaleMode).toBe('auto');
testingUtils.clickByCSS('#viewer-scale-page-button');
expect(componentUrlTestComponent.pdfViewerComponent.currentScaleMode).toBe('page-fit');
}), 55000);
});
describe('Resize interaction', () => {
it('should resize event trigger setScaleUpdatePages', (done) => {
spyOn(componentUrlTestComponent.pdfViewerComponent, 'onResize');
EventMock.resizeMobileView();
fixtureUrlTestComponent.whenStable().then(() => {
expect(componentUrlTestComponent.pdfViewerComponent.onResize).toHaveBeenCalled();
done();
});
}, 55000);
});
describe('Thumbnails', () => {
it('should have own context', (done) => {
fixtureUrlTestComponent.detectChanges();
fixtureUrlTestComponent.whenStable().then(() => {
expect(componentUrlTestComponent.pdfViewerComponent.pdfThumbnailsContext.viewer).not.toBeNull();
done();
});
}, 55000);
it('should open thumbnails panel', (done) => {
expect(testingUtils.getByCSS('.adf-pdf-viewer__thumbnails')).toBeNull();
componentUrlTestComponent.pdfViewerComponent.toggleThumbnails();
fixtureUrlTestComponent.detectChanges();
fixtureUrlTestComponent.whenStable().then(() => {
expect(testingUtils.getByCSS('.adf-pdf-viewer__thumbnails')).not.toBeNull();
done();
});
}, 55000);
it('should not render PdfThumbListComponent during initialization of new pdfViewer', () => {
componentUrlTestComponent.pdfViewerComponent.toggleThumbnails();
componentUrlTestComponent.urlFile = 'file.pdf';
fixtureUrlTestComponent.detectChanges();
expect(fixtureUrlTestComponent.debugElement.query(By.directive(PdfThumbListComponent))).toBeNull();
});
});
describe('Viewer events', () => {
it('should react on the emit of pageChange event', (done) => {
fixtureUrlTestComponent.detectChanges();
fixtureUrlTestComponent.whenStable().then(() => {
const args = {
pageNumber: 6,
source: {
container: document.getElementById(`${componentUrlTestComponent.pdfViewerComponent.randomPdfId}-viewer-pdf-viewer`)
}
};
/* cspell:disable-next-line */
componentUrlTestComponent.pdfViewerComponent.pdfViewer.eventBus.dispatch('pagechanging', args);
fixtureUrlTestComponent.detectChanges();
fixtureUrlTestComponent.whenStable().then(() => {
expect(componentUrlTestComponent.pdfViewerComponent.displayPage).toBe(6);
expect(componentUrlTestComponent.pdfViewerComponent.page).toBe(6);
done();
});
});
}, 55000);
it('should react on the emit of pagesLoaded event', (done) => {
fixtureUrlTestComponent.detectChanges();
fixtureUrlTestComponent.whenStable().then(() => {
expect(componentUrlTestComponent.pdfViewerComponent.isPanelDisabled).toBeFalsy();
const args = {
pagesCount: 10,
source: {
container: document.getElementById(`${componentUrlTestComponent.pdfViewerComponent.randomPdfId}-viewer-pdf-viewer`)
}
};
/* cspell:disable-next-line */
componentUrlTestComponent.pdfViewerComponent.pdfViewer.eventBus.dispatch('pagesloaded', args);
fixtureUrlTestComponent.detectChanges();
fixtureUrlTestComponent.whenStable().then(() => {
expect(componentUrlTestComponent.pdfViewerComponent.isPanelDisabled).toBe(false);
done();
});
});
}, 55000);
});
afterAll(() => {
fixture.destroy();
});
it('should Loader be present', () => {
expect(testingUtils.getByCSS('.adf-loader-container')).not.toBeNull();
});
describe('Zoom customization', () => {
describe('custom value', () => {
let fixtureUrlTestComponent: ComponentFixture<UrlTestComponent>;
let componentUrlTestComponent: UrlTestComponent;
let elementUrlTestComponent: HTMLElement;
beforeEach((done) => {
const appConfig: AppConfigService = TestBed.inject(AppConfigService);
appConfig.config['adf-viewer.pdf-viewer-scaling'] = 80;
fixtureUrlTestComponent = TestBed.createComponent(UrlTestComponent);
componentUrlTestComponent = fixtureUrlTestComponent.componentInstance;
elementUrlTestComponent = fixtureUrlTestComponent.nativeElement;
fixtureUrlTestComponent.detectChanges();
componentUrlTestComponent.pdfViewerComponent.rendered.pipe(take(1)).subscribe(() => {
done();
});
});
afterEach(() => {
document.body.removeChild(elementUrlTestComponent);
});
it('should use the custom zoom if it is present in the app.config', async () => {
spyOn(componentUrlTestComponent.pdfViewerComponent.pdfViewer, 'forceRendering').and.callFake(() => {});
fixtureUrlTestComponent.detectChanges();
await fixtureUrlTestComponent.whenStable();
expect(componentUrlTestComponent.pdfViewerComponent.pdfViewer.currentScale).toBe(0.8);
});
});
describe('less than the minimum allowed value', () => {
let fixtureUrlTestComponent: ComponentFixture<UrlTestComponent>;
let componentUrlTestComponent: UrlTestComponent;
let elementUrlTestComponent: HTMLElement;
beforeEach((done) => {
const appConfig: AppConfigService = TestBed.inject(AppConfigService);
appConfig.config['adf-viewer.pdf-viewer-scaling'] = 10;
fixtureUrlTestComponent = TestBed.createComponent(UrlTestComponent);
componentUrlTestComponent = fixtureUrlTestComponent.componentInstance;
elementUrlTestComponent = fixtureUrlTestComponent.nativeElement;
fixtureUrlTestComponent.detectChanges();
componentUrlTestComponent.pdfViewerComponent.rendered.pipe(take(1)).subscribe(() => {
done();
});
});
afterEach(() => {
document.body.removeChild(elementUrlTestComponent);
});
it('should use the minimum scale zoom if the value given in app.config is less than the minimum allowed scale', (done) => {
spyOn(componentUrlTestComponent.pdfViewerComponent.pdfViewer, 'forceRendering').and.callFake(() => {});
fixtureUrlTestComponent.detectChanges();
fixtureUrlTestComponent.whenStable().then(() => {
expect(componentUrlTestComponent.pdfViewerComponent.pdfViewer.currentScaleValue).toBe('0.25');
done();
});
});
});
// TODO: https://alfresco.atlassian.net/browse/ACS-6061
// eslint-disable-next-line
xdescribe('greater than the maximum allowed value', () => {
let fixtureUrlTestComponent: ComponentFixture<UrlTestComponent>;
let componentUrlTestComponent: UrlTestComponent;
let elementUrlTestComponent: HTMLElement;
beforeEach((done) => {
const appConfig: AppConfigService = TestBed.inject(AppConfigService);
appConfig.config['adf-viewer.pdf-viewer-scaling'] = 55555;
fixtureUrlTestComponent = TestBed.createComponent(UrlTestComponent);
componentUrlTestComponent = fixtureUrlTestComponent.componentInstance;
elementUrlTestComponent = fixtureUrlTestComponent.nativeElement;
fixtureUrlTestComponent.detectChanges();
componentUrlTestComponent.pdfViewerComponent.rendered.pipe(take(1)).subscribe(() => {
done();
});
});
afterEach(() => {
document.body.removeChild(elementUrlTestComponent);
});
it('should use the maximum scale zoom if the value given in app.config is greater than the maximum allowed scale', (done) => {
spyOn(componentUrlTestComponent.pdfViewerComponent.pdfViewer, 'forceRendering').and.callFake(() => {});
fixtureUrlTestComponent.detectChanges();
fixtureUrlTestComponent.whenStable().then(() => {
expect(componentUrlTestComponent.pdfViewerComponent.pdfViewer.currentScale).toBe(10);
done();
});
});
});
});
describe('Required values', () => {
it('should thrown an error If urlFile is not present', () => {
change = new SimpleChange(null, null, true);
@@ -525,42 +173,34 @@ describe('Test PdfViewer component', () => {
afterEach(() => {
document.body.removeChild(elementUrlTestComponent);
fixture.destroy();
});
it('should Canvas be present', (done) => {
it('should Canvas be present', async () => {
fixtureUrlTestComponent.detectChanges();
await fixtureUrlTestComponent.whenStable();
fixtureUrlTestComponent.whenStable().then(() => {
expect(testingUtils.getByCSS('.adf-pdfViewer')).not.toBeNull();
expect(testingUtils.getByCSS('.adf-viewer-pdf-viewer')).not.toBeNull();
done();
});
}, 55000);
expect(testingUtils.getByCSS('.adf-pdfViewer')).not.toBeNull();
expect(testingUtils.getByCSS('.adf-viewer-pdf-viewer')).not.toBeNull();
});
it('should Input Page elements be present', (done) => {
it('should Input Page elements be present', async () => {
fixtureUrlTestComponent.detectChanges();
fixtureUrlTestComponent.whenStable().then(() => {
/* cspell:disable-next-line */
expect(testingUtils.getByCSS('.viewer-pagenumber-input')).toBeDefined();
expect(testingUtils.getByCSS('.viewer-total-pages')).toBeDefined();
await fixtureUrlTestComponent.whenStable();
expect(testingUtils.getByCSS('.viewer-pagenumber-input')).toBeDefined();
expect(testingUtils.getByCSS('.viewer-total-pages')).toBeDefined();
expect(testingUtils.getByCSS('#viewer-previous-page-button')).not.toBeNull();
expect(testingUtils.getByCSS('#viewer-next-page-button')).not.toBeNull();
done();
});
}, 55000);
expect(testingUtils.getByCSS('#viewer-previous-page-button')).not.toBeNull();
expect(testingUtils.getByCSS('#viewer-next-page-button')).not.toBeNull();
});
it('should Toolbar be hide if showToolbar is false', (done) => {
it('should Toolbar be hide if showToolbar is false', async () => {
component.showToolbar = false;
fixtureUrlTestComponent.detectChanges();
fixtureUrlTestComponent.whenStable().then(() => {
expect(testingUtils.getByCSS('.viewer-toolbar-command')).toBeNull();
expect(testingUtils.getByCSS('.viewer-toolbar-pagination')).toBeNull();
done();
});
}, 55000);
await fixtureUrlTestComponent.whenStable();
expect(testingUtils.getByCSS('.viewer-toolbar-command')).toBeNull();
expect(testingUtils.getByCSS('.viewer-toolbar-pagination')).toBeNull();
});
});
describe('View with blob file', () => {
@@ -578,53 +218,43 @@ describe('Test PdfViewer component', () => {
afterEach(() => {
document.body.removeChild(elementBlobTestComponent);
fixture.destroy();
});
it('should Canvas be present', () => {
it('should Canvas be present', async () => {
fixtureBlobTestComponent.detectChanges();
await fixtureBlobTestComponent.whenStable();
fixtureBlobTestComponent.whenStable().then(() => {
expect(testingUtils.getByCSS('.adf-pdfViewer')).not.toBeNull();
expect(testingUtils.getByCSS('.adf-viewer-pdf-viewer')).not.toBeNull();
});
expect(testingUtils.getByCSS('.adf-pdfViewer')).not.toBeNull();
expect(testingUtils.getByCSS('.adf-viewer-pdf-viewer')).not.toBeNull();
});
it('should Next an Previous Buttons be present', (done) => {
it('should Next an Previous Buttons be present', async () => {
fixtureBlobTestComponent.detectChanges();
await fixtureBlobTestComponent.whenStable();
fixtureBlobTestComponent.whenStable().then(() => {
expect(testingUtils.getByCSS('#viewer-previous-page-button')).not.toBeNull();
expect(testingUtils.getByCSS('#viewer-next-page-button')).not.toBeNull();
done();
});
}, 55000);
expect(testingUtils.getByCSS('#viewer-previous-page-button')).not.toBeNull();
expect(testingUtils.getByCSS('#viewer-next-page-button')).not.toBeNull();
});
it('should Input Page elements be present', (done) => {
it('should Input Page elements be present', async () => {
fixtureBlobTestComponent.detectChanges();
await fixtureBlobTestComponent.whenStable();
/* cspell:disable-next-line */
expect(testingUtils.getByCSS('.adf-viewer-pagenumber-input')).toBeDefined();
expect(testingUtils.getByCSS('.adf-viewer-total-pages')).toBeDefined();
expect(testingUtils.getByCSS('#viewer-previous-page-button')).not.toBeNull();
expect(testingUtils.getByCSS('#viewer-next-page-button')).not.toBeNull();
});
fixtureBlobTestComponent.whenStable().then(() => {
/* cspell:disable-next-line */
expect(testingUtils.getByCSS('.adf-viewer-pagenumber-input')).toBeDefined();
expect(testingUtils.getByCSS('.adf-viewer-total-pages')).toBeDefined();
expect(testingUtils.getByCSS('#viewer-previous-page-button')).not.toBeNull();
expect(testingUtils.getByCSS('#viewer-next-page-button')).not.toBeNull();
done();
});
}, 55000);
it('should Toolbar be hide if showToolbar is false', (done) => {
it('should Toolbar be hide if showToolbar is false', async () => {
fixtureBlobTestComponent.componentInstance.pdfViewerComponent.showToolbar = false;
fixtureBlobTestComponent.detectChanges();
await fixtureBlobTestComponent.whenStable();
fixtureBlobTestComponent.whenStable().then(() => {
expect(testingUtils.getByCSS('.viewer-toolbar-command')).toBeNull();
expect(testingUtils.getByCSS('.viewer-toolbar-pagination')).toBeNull();
done();
});
}, 55000);
expect(testingUtils.getByCSS('.viewer-toolbar-command')).toBeNull();
expect(testingUtils.getByCSS('.viewer-toolbar-pagination')).toBeNull();
});
});
describe('Password protection dialog', () => {
@@ -721,3 +351,251 @@ describe('Test PdfViewer component', () => {
});
});
});
describe('Test PdfViewer - Zoom customization', () => {
let fixture: ComponentFixture<PdfViewerComponent>;
let component: PdfViewerComponent;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [NoopAuthModule, NoopTranslateModule, PdfViewerComponent],
providers: [
{
provide: MatDialog,
useValue: {
open: () => {}
}
},
RenderingQueueServices
]
});
fixture = TestBed.createComponent(PdfViewerComponent);
component = fixture.componentInstance;
});
afterAll(() => {
fixture.destroy();
});
it('should use the custom zoom if it is present in the app.config', () => {
const appConfig: AppConfigService = TestBed.inject(AppConfigService);
appConfig.config['adf-viewer.pdf-viewer-scaling'] = 80;
expect(component.getUserScaling()).toBe(0.8);
});
it('should use the minimum scale zoom if the value given in app.config is less than the minimum allowed scale', () => {
const appConfig: AppConfigService = TestBed.inject(AppConfigService);
appConfig.config['adf-viewer.pdf-viewer-scaling'] = 10;
fixture.detectChanges();
expect(component.getUserScaling()).toBe(0.25);
});
it('should use the maximum scale zoom if the value given in app.config is greater than the maximum allowed scale', () => {
const appConfig: AppConfigService = TestBed.inject(AppConfigService);
appConfig.config['adf-viewer.pdf-viewer-scaling'] = 5555;
fixture.detectChanges();
expect(component.getUserScaling()).toBe(10);
});
});
describe('Test PdfViewer - User interaction', () => {
let fixture: ComponentFixture<PdfViewerComponent>;
let component: PdfViewerComponent;
let testingUtils: UnitTestingUtils;
let pdfViewerSpy: jasmine.Spy;
beforeEach(fakeAsync(() => {
pdfViewerSpy = jasmine.createSpy('PDFViewer').and.returnValue({
setDocument: jasmine.createSpy().and.returnValue({
loadingTask: () => ({
destroy: () => Promise.resolve()
}),
promise: new Promise((resolve) => {
resolve({
numPages: 6,
getPage: () => 'fakePage'
});
})
}),
forceRendering: jasmine.createSpy(),
update: jasmine.createSpy(),
currentScaleValue: 1,
_currentPageNumber: 1,
_pages: [{ width: 100, height: 100, scale: 1 }]
});
TestBed.configureTestingModule({
imports: [NoopAuthModule, NoopTranslateModule, PdfViewerComponent],
providers: [
{
provide: MatDialog,
useValue: {
open: () => {}
}
},
RenderingQueueServices,
{ provide: PDFJS_VIEWER_MODULE, useValue: pdfViewerSpy },
{ provide: PDFJS_MODULE, useValue: pdfjsLibMock }
]
});
fixture = TestBed.createComponent(PdfViewerComponent);
component = fixture.componentInstance;
testingUtils = new UnitTestingUtils(fixture.debugElement);
const appConfig: AppConfigService = TestBed.inject(AppConfigService);
appConfig.config['adf-viewer.pdf-viewer-scaling'] = 10;
component.urlFile = './fake-test-file.pdf';
fixture.detectChanges();
component.ngOnChanges({ urlFile: { currentValue: './fake-test-file.pdf' } } as any);
flush();
}));
afterAll(() => {
fixture.destroy();
});
it('should init the viewer with annotation mode disabled', () => {
expect(pdfViewerSpy).toHaveBeenCalledWith(jasmine.objectContaining({ annotationMode: 0 }));
});
it('should Total number of pages be loaded', () => {
expect(component.totalPages).toBe(6);
});
it('should nextPage move to the next page', () => {
testingUtils.clickByCSS('#viewer-next-page-button');
expect(component.displayPage).toBe(2);
});
it('should event RIGHT_ARROW keyboard change pages', () => {
fixture.detectChanges();
EventMock.keyDown(RIGHT_ARROW);
expect(component.displayPage).toBe(2);
});
it('should event LEFT_ARROW keyboard change pages', () => {
component.inputPage('2');
EventMock.keyDown(LEFT_ARROW);
expect(component.displayPage).toBe(1);
});
it('should previous page move to the previous page', () => {
testingUtils.clickByCSS('#viewer-next-page-button');
testingUtils.clickByCSS('#viewer-next-page-button');
testingUtils.clickByCSS('#viewer-previous-page-button');
expect(component.displayPage).toBe(2);
});
it('should previous page not move to the previous page if is page 1', () => {
component.previousPage();
expect(component.displayPage).toBe(1);
});
it('should Input page move to the inserted page', () => {
component.inputPage('2');
expect(component.displayPage).toBe(2);
});
describe('Zoom', () => {
it('should zoom in increment the scale value', () => {
const zoomBefore = component.pdfViewer.currentScaleValue;
testingUtils.clickByCSS('#viewer-zoom-in-button');
expect(component.currentScaleMode).toBe('auto');
const currentZoom = component.pdfViewer.currentScaleValue;
expect(zoomBefore < currentZoom).toBe(true);
});
it('should zoom out decrement the scale value', () => {
testingUtils.clickByCSS('#viewer-zoom-in-button');
const zoomBefore = component.pdfViewer.currentScaleValue;
testingUtils.clickByCSS('#viewer-zoom-out-button');
expect(component.currentScaleMode).toBe('auto');
const currentZoom = component.pdfViewer.currentScaleValue;
expect(zoomBefore > currentZoom).toBe(true);
});
it('should it-in button toggle page-fit and auto scale mode', fakeAsync(() => {
tick(250);
expect(component.currentScaleMode).toBe('init');
testingUtils.clickByCSS('#viewer-scale-page-button');
expect(component.currentScaleMode).toBe('page-fit');
testingUtils.clickByCSS('#viewer-scale-page-button');
expect(component.currentScaleMode).toBe('auto');
testingUtils.clickByCSS('#viewer-scale-page-button');
expect(component.currentScaleMode).toBe('page-fit');
}), 300);
});
describe('Resize interaction', () => {
it('should resize event trigger setScaleUpdatePages', () => {
spyOn(component, 'onResize');
EventMock.resizeMobileView();
expect(component.onResize).toHaveBeenCalled();
});
});
describe('Thumbnails', () => {
it('should have own context', () => {
expect(component.pdfThumbnailsContext.viewer).not.toBeNull();
});
it('should open thumbnails panel', () => {
expect(testingUtils.getByCSS('.adf-pdf-viewer__thumbnails')).toBeNull();
component.toggleThumbnails();
fixture.detectChanges();
expect(testingUtils.getByCSS('.adf-pdf-viewer__thumbnails')).not.toBeNull();
});
it('should not render PdfThumbListComponent during initialization of new pdfViewer', () => {
component.toggleThumbnails();
component.urlFile = 'file.pdf';
fixture.detectChanges();
expect(fixture.debugElement.query(By.directive(PdfThumbListComponent))).toBeNull();
});
});
describe('Viewer events', () => {
it('should react on the emit of pageChange event', () => {
const args = {
pageNumber: 6,
source: {
container: document.getElementById(`${component.randomPdfId}-viewer-pdf-viewer`)
}
};
component.onPageChange(args);
expect(component.displayPage).toBe(6);
expect(component.page).toBe(6);
});
it('should react on the emit of pagesLoaded event', () => {
expect(component.isPanelDisabled).toBe(true);
component.onPagesLoaded();
expect(component.isPanelDisabled).toBe(false);
});
});
});

View File

@@ -24,6 +24,8 @@ import {
Component,
EventEmitter,
HostListener,
inject,
InjectionToken,
Input,
OnChanges,
OnDestroy,
@@ -37,20 +39,22 @@ import { MatDialog } from '@angular/material/dialog';
import { MatIconModule } from '@angular/material/icon';
import { MatProgressBarModule } from '@angular/material/progress-bar';
import { TranslateModule } from '@ngx-translate/core';
import { AnnotationMode, OnProgressParameters, PDFDocumentLoadingTask, PDFDocumentProxy } from 'pdfjs-dist';
import { Subject } from 'rxjs';
import { catchError, delay } from 'rxjs/operators';
import { from, Subject, switchMap } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { AppConfigService } from '../../../app-config';
import { ToolbarComponent, ToolbarDividerComponent } from '../../../toolbar';
import { RenderingQueueServices } from '../../services/rendering-queue.services';
import { PdfPasswordDialogComponent } from '../pdf-viewer-password-dialog/pdf-viewer-password-dialog';
import { PdfThumbListComponent } from '../pdf-viewer-thumbnails/pdf-viewer-thumbnails.component';
declare const pdfjsLib: any;
declare const pdfjsViewer: any;
import * as pdfjsLib from 'pdfjs-dist/build/pdf.min.mjs';
import { PDFViewer, EventBus } from 'pdfjs-dist/web/pdf_viewer.mjs';
import { OnProgressParameters, PDFDocumentLoadingTask, PDFDocumentProxy } from 'pdfjs-dist/types/src/display/api';
export type PdfScaleMode = 'init' | 'page-actual' | 'page-width' | 'page-height' | 'page-fit' | 'auto';
export const PDFJS_MODULE = new InjectionToken('PDFJS_MODULE', { factory: () => pdfjsLib });
export const PDFJS_VIEWER_MODULE = new InjectionToken('PDFJS_VIEWER_MODULE', { factory: () => PDFViewer });
@Component({
selector: 'adf-pdf-viewer',
standalone: true,
@@ -129,7 +133,10 @@ export class PdfViewerComponent implements OnChanges, OnDestroy {
return this.pdfViewer?.currentScaleValue ? Math.round(this.pdfViewer.currentScaleValue * 100) + '%' : '';
}
private eventBus = new pdfjsViewer.EventBus();
private pdfjsLib = inject(PDFJS_MODULE);
private pdfjsViewer = inject(PDFJS_VIEWER_MODULE);
private eventBus = new EventBus();
private pdfjsDefaultOptions = {
disableAutoFetch: true,
disableStream: true,
@@ -138,7 +145,11 @@ export class PdfViewerComponent implements OnChanges, OnDestroy {
};
private pdfjsWorkerDestroy$ = new Subject<boolean>();
constructor(private dialog: MatDialog, private renderingQueueServices: RenderingQueueServices, private appConfigService: AppConfigService) {
private dialog = inject(MatDialog);
private renderingQueueServices = inject(RenderingQueueServices);
private appConfigService = inject(AppConfigService);
constructor() {
// needed to preserve "this" context
this.onPageChange = this.onPageChange.bind(this);
this.onPagesLoaded = this.onPagesLoaded.bind(this);
@@ -148,9 +159,9 @@ export class PdfViewerComponent implements OnChanges, OnDestroy {
this.pdfjsWorkerDestroy$
.pipe(
catchError(() => null),
delay(700)
switchMap(() => from(this.destroyPdJsWorker()))
)
.subscribe(() => this.destroyPdJsWorker());
.subscribe(() => {});
}
getUserScaling(): number {
@@ -213,9 +224,9 @@ export class PdfViewerComponent implements OnChanges, OnDestroy {
}
executePdf(pdfOptions: any) {
pdfjsLib.GlobalWorkerOptions.workerSrc = 'pdf.worker.min.js';
this.pdfjsLib.GlobalWorkerOptions.workerSrc = 'pdf.worker.min.mjs';
this.loadingTask = pdfjsLib.getDocument(pdfOptions);
this.loadingTask = this.pdfjsLib.getDocument(pdfOptions);
this.loadingTask.onPassword = (callback, reason) => {
this.onPdfPassword(callback, reason);
@@ -227,6 +238,7 @@ export class PdfViewerComponent implements OnChanges, OnDestroy {
};
this.isPanelDisabled = true;
this.loadingTask.promise
.then((pdfDocument) => {
this.totalPages = pdfDocument.numPages;
@@ -245,12 +257,12 @@ export class PdfViewerComponent implements OnChanges, OnDestroy {
const container = this.getDocumentContainer();
if (viewer && container) {
this.pdfViewer = new pdfjsViewer.PDFViewer({
this.pdfViewer = new this.pdfjsViewer({
container,
viewer,
renderingQueue: this.renderingQueueServices,
eventBus: this.eventBus,
annotationMode: AnnotationMode.DISABLE
annotationMode: 0
});
// cspell: disable-next
@@ -258,7 +270,12 @@ export class PdfViewerComponent implements OnChanges, OnDestroy {
// cspell: disable-next
this.eventBus.on('pagesloaded', this.onPagesLoaded);
// cspell: disable-next
this.eventBus.on('textlayerrendered', this.onPageRendered);
this.eventBus.on('textlayerrendered', () => {
this.onPageRendered();
});
this.eventBus.on('pagerendered', () => {
this.onPageRendered();
});
this.renderingQueueServices.setViewer(this.pdfViewer);
this.pdfViewer.setDocument(pdfDocument);
@@ -269,11 +286,11 @@ export class PdfViewerComponent implements OnChanges, OnDestroy {
ngOnDestroy() {
if (this.pdfViewer) {
// cspell: disable-next
this.eventBus.off('pagechanging');
this.eventBus.off('pagechanging', () => {});
// cspell: disable-next
this.eventBus.off('pagesloaded');
this.eventBus.off('pagesloaded', () => {});
// cspell: disable-next
this.eventBus.off('textlayerrendered');
this.eventBus.off('textlayerrendered', () => {});
}
if (this.loadingTask) {
@@ -282,8 +299,10 @@ export class PdfViewerComponent implements OnChanges, OnDestroy {
this.pdfjsWorkerDestroy$.complete();
}
private destroyPdJsWorker() {
this.loadingTask.destroy();
private async destroyPdJsWorker() {
if (this.loadingTask.destroy) {
await this.loadingTask.destroy();
}
this.loadingTask = null;
}
@@ -370,8 +389,8 @@ export class PdfViewerComponent implements OnChanges, OnDestroy {
return document.getElementById(`${this.randomPdfId}-viewer-main-container`);
}
private getDocumentContainer(): HTMLElement {
return document.getElementById(`${this.randomPdfId}-viewer-pdf-viewer`);
private getDocumentContainer(): HTMLDivElement {
return document.getElementById(`${this.randomPdfId}-viewer-pdf-viewer`) as HTMLDivElement;
}
private getViewer(): HTMLElement {

View File

@@ -82,4 +82,8 @@
align-items: center;
display: flex;
}
&-pdf {
display: contents;
}
}

View File

@@ -19,12 +19,12 @@ import { AppExtensionService, ViewerExtensionRef } from '@alfresco/adf-extension
import { Location } from '@angular/common';
import { SpyLocation } from '@angular/common/testing';
import { Component, DebugElement, TemplateRef, ViewChild } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ComponentFixture, DeferBlockBehavior, TestBed } from '@angular/core/testing';
import { MatDialog, MatDialogModule } from '@angular/material/dialog';
import { NoopTranslateModule, UnitTestingUtils } from '../../../testing';
import { RenderingQueueServices } from '../../services/rendering-queue.services';
import { ViewerRenderComponent } from './viewer-render.component';
import { ImgViewerComponent, MediaPlayerComponent, PdfViewerComponent, ViewerExtensionDirective } from '@alfresco/adf-core';
import { ImgViewerComponent, MediaPlayerComponent, ViewerExtensionDirective } from '@alfresco/adf-core';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
@Component({
@@ -68,16 +68,19 @@ describe('ViewerComponent', () => {
let extensionService: AppExtensionService;
let testingUtils: UnitTestingUtils;
beforeEach(() => {
beforeEach(async () => {
TestBed.configureTestingModule({
imports: [NoopTranslateModule, NoopAnimationsModule, MatDialogModule, ViewerRenderComponent, DoubleViewerComponent],
providers: [RenderingQueueServices, { provide: Location, useClass: SpyLocation }, MatDialog]
providers: [RenderingQueueServices, { provide: Location, useClass: SpyLocation }, MatDialog],
deferBlockBehavior: DeferBlockBehavior.Playthrough
});
fixture = TestBed.createComponent(ViewerRenderComponent);
testingUtils = new UnitTestingUtils(fixture.debugElement);
component = fixture.componentInstance;
extensionService = TestBed.inject(AppExtensionService);
await fixture.whenStable();
});
afterEach(() => {
@@ -88,12 +91,12 @@ describe('ViewerComponent', () => {
it('should not reload the content of all the viewer after type change', async () => {
const fixtureDouble = TestBed.createComponent(DoubleViewerComponent);
fixtureDouble.detectChanges();
await fixtureDouble.whenStable();
fixtureDouble.componentInstance.urlFileViewer1 = 'fake-test-file.pdf';
fixtureDouble.componentInstance.urlFileViewer2 = 'fake-test-file-two.xls';
fixtureDouble.detectChanges();
await fixtureDouble.whenStable();
fixtureDouble.componentInstance.viewer1.ngOnChanges();
fixtureDouble.componentInstance.viewer2.ngOnChanges();
@@ -197,8 +200,8 @@ describe('ViewerComponent', () => {
it('should extension file pdf be loaded', (done) => {
component.urlFile = 'fake-test-file.pdf';
component.ngOnChanges();
fixture.detectChanges();
component.ngOnChanges();
fixture.whenStable().then(() => {
fixture.detectChanges();
@@ -209,8 +212,8 @@ describe('ViewerComponent', () => {
it('should extension file png be loaded', (done) => {
component.urlFile = 'fake-url-file.png';
component.ngOnChanges();
fixture.detectChanges();
component.ngOnChanges();
fixture.whenStable().then(() => {
fixture.detectChanges();
@@ -367,7 +370,7 @@ describe('ViewerComponent', () => {
fixture.detectChanges();
component.ngOnChanges();
fixture.whenStable().then(() => {
fixture.getDeferBlocks().then(() => {
fixture.detectChanges();
expect(testingUtils.getByCSS('adf-pdf-viewer')).not.toBeNull();
done();
@@ -377,8 +380,8 @@ describe('ViewerComponent', () => {
it('should display a PDF file identified by mimetype when the file extension is wrong', (done) => {
component.urlFile = 'fake-content-pdf.bin';
component.mimeType = 'application/pdf';
component.ngOnChanges();
fixture.detectChanges();
component.ngOnChanges();
fixture.whenStable().then(() => {
fixture.detectChanges();
expect(testingUtils.getByCSS('adf-pdf-viewer')).not.toBeNull();
@@ -503,7 +506,8 @@ describe('ViewerComponent', () => {
expect(component.viewerType).toBe('media');
});
it('should show spinner until content is ready when viewerType is pdf', () => {
// eslint-disable-next-line ban/ban
xit('should show spinner until content is ready when viewerType is pdf', () => {
component.isLoading = false;
component.urlFile = 'some-url.pdf';
@@ -512,8 +516,6 @@ describe('ViewerComponent', () => {
expect(getMainLoader()).not.toBeNull();
const pdfViewer = testingUtils.getByDirective(PdfViewerComponent);
pdfViewer.triggerEventHandler('pagesLoaded', null);
fixture.detectChanges();
expect(getMainLoader()).toBeNull();