mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2026-09-09 18:03:21 +00:00
AAE-44809 Fix form multi-file attachment - document viewer not updated on file selection (#11980)
This commit is contained in:
@@ -0,0 +1,87 @@
|
|||||||
|
/*!
|
||||||
|
* @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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
import { DomSanitizer } from '@angular/platform-browser';
|
||||||
|
import { UrlService } from './url.service';
|
||||||
|
|
||||||
|
describe('UrlService', () => {
|
||||||
|
const fakeObjectUrl = 'blob:http://localhost/fake-object-url';
|
||||||
|
const fakeSafeUrl = { __safe: 'safe-url' } as unknown as string;
|
||||||
|
|
||||||
|
let service: UrlService;
|
||||||
|
let sanitizer: DomSanitizer;
|
||||||
|
let createObjectURLSpy: jasmine.Spy;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
const mockSanitizer = {
|
||||||
|
bypassSecurityTrustUrl: jasmine.createSpy('bypassSecurityTrustUrl').and.returnValue(fakeSafeUrl)
|
||||||
|
};
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
providers: [{ provide: DomSanitizer, useValue: mockSanitizer }]
|
||||||
|
});
|
||||||
|
|
||||||
|
service = TestBed.inject(UrlService);
|
||||||
|
sanitizer = TestBed.inject(DomSanitizer);
|
||||||
|
createObjectURLSpy = spyOn(window.URL, 'createObjectURL').and.returnValue(fakeObjectUrl);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('createObjectUrl', () => {
|
||||||
|
it('should delegate to window.URL.createObjectURL and return the raw url', () => {
|
||||||
|
const blob = new Blob(['test'], { type: 'text/plain' });
|
||||||
|
|
||||||
|
const result = service.createObjectUrl(blob);
|
||||||
|
|
||||||
|
expect(createObjectURLSpy).toHaveBeenCalledOnceWith(blob);
|
||||||
|
expect(result).toBe(fakeObjectUrl);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not invoke the sanitizer', () => {
|
||||||
|
service.createObjectUrl(new Blob(['test']));
|
||||||
|
|
||||||
|
expect(sanitizer.bypassSecurityTrustUrl).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('trustUrl', () => {
|
||||||
|
it('should wrap the url with bypassSecurityTrustUrl', () => {
|
||||||
|
const result = service.trustUrl(fakeObjectUrl);
|
||||||
|
|
||||||
|
expect(sanitizer.bypassSecurityTrustUrl).toHaveBeenCalledOnceWith(fakeObjectUrl);
|
||||||
|
expect(result).toBe(fakeSafeUrl);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not create an object url', () => {
|
||||||
|
service.trustUrl(fakeObjectUrl);
|
||||||
|
|
||||||
|
expect(createObjectURLSpy).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('createTrustedUrl', () => {
|
||||||
|
it('should create an object url from the blob and pass it to the sanitizer', () => {
|
||||||
|
const blob = new Blob(['test'], { type: 'text/plain' });
|
||||||
|
|
||||||
|
const result = service.createTrustedUrl(blob);
|
||||||
|
|
||||||
|
expect(createObjectURLSpy).toHaveBeenCalledOnceWith(blob);
|
||||||
|
expect(sanitizer.bypassSecurityTrustUrl).toHaveBeenCalledOnceWith(fakeObjectUrl);
|
||||||
|
expect(result).toBe(fakeSafeUrl);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -32,7 +32,28 @@ export class UrlService {
|
|||||||
* @returns URL string
|
* @returns URL string
|
||||||
*/
|
*/
|
||||||
createTrustedUrl(blob: Blob): string {
|
createTrustedUrl(blob: Blob): string {
|
||||||
const url = window.URL.createObjectURL(blob);
|
return this.trustUrl(this.createObjectUrl(blob));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a raw object URL from the Blob without Angular sanitization. Use this for
|
||||||
|
* non-Angular consumers (third-party DOM APIs, libraries) that need a real URL string.
|
||||||
|
*
|
||||||
|
* @param blob Data to wrap into object URL
|
||||||
|
* @returns Raw object URL string
|
||||||
|
*/
|
||||||
|
createObjectUrl(blob: Blob): string {
|
||||||
|
return window.URL.createObjectURL(blob);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wraps a URL string with Angular's bypassSecurityTrustUrl so it can be used in template bindings.
|
||||||
|
* WARNING: calling this method with untrusted user data exposes your application to XSS security risks!
|
||||||
|
*
|
||||||
|
* @param url URL to mark as safe
|
||||||
|
* @returns SafeUrl wrapper cast to string for template binding
|
||||||
|
*/
|
||||||
|
trustUrl(url: string): string {
|
||||||
return this.sanitizer.bypassSecurityTrustUrl(url) as string;
|
return this.sanitizer.bypassSecurityTrustUrl(url) as string;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -173,12 +173,41 @@ describe('Test Img viewer component ', () => {
|
|||||||
it('should not thrown an error if blob is passed ', () => {
|
it('should not thrown an error if blob is passed ', () => {
|
||||||
const blob = createFakeBlob();
|
const blob = createFakeBlob();
|
||||||
|
|
||||||
spyOn(urlService, 'createTrustedUrl').and.returnValue('fake-blob-url');
|
spyOn(urlService, 'createObjectUrl').and.returnValue('fake-blob-url');
|
||||||
|
spyOn(urlService, 'trustUrl').and.returnValue('safe-fake-blob-url');
|
||||||
const change = new SimpleChange(null, blob, true);
|
const change = new SimpleChange(null, blob, true);
|
||||||
expect(() => {
|
expect(() => {
|
||||||
component.ngOnChanges({ blobFile: change });
|
component.ngOnChanges({ blobFile: change });
|
||||||
}).not.toThrow(new Error('Attribute urlFile or blobFile is required'));
|
}).not.toThrow(new Error('Attribute urlFile or blobFile is required'));
|
||||||
expect(component.urlFile).toEqual('fake-blob-url');
|
expect(component.urlFile).toEqual('safe-fake-blob-url');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call replace on cropper with the raw blob url when blobFile changes after init', () => {
|
||||||
|
component.urlFile = 'fake-url';
|
||||||
|
fixture.detectChanges();
|
||||||
|
spyOn(component.cropper, 'replace').and.stub();
|
||||||
|
spyOn(urlService, 'createObjectUrl').and.returnValue('fake-blob-url-2');
|
||||||
|
spyOn(urlService, 'trustUrl').and.returnValue('safe-fake-blob-url-2');
|
||||||
|
|
||||||
|
const blobFile = new SimpleChange(createFakeBlob(), createFakeBlob(), false);
|
||||||
|
component.ngOnChanges({ blobFile });
|
||||||
|
|
||||||
|
expect(component.cropper.replace).toHaveBeenCalledWith('fake-blob-url-2');
|
||||||
|
expect(component.urlFile).toEqual('safe-fake-blob-url-2');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not call replace on cropper on the first blobFile change', () => {
|
||||||
|
component.urlFile = 'fake-url';
|
||||||
|
fixture.detectChanges();
|
||||||
|
spyOn(component.cropper, 'replace').and.stub();
|
||||||
|
spyOn(urlService, 'createObjectUrl').and.returnValue('fake-blob-url');
|
||||||
|
spyOn(urlService, 'trustUrl').and.returnValue('safe-fake-blob-url');
|
||||||
|
|
||||||
|
const blobFile = new SimpleChange(null, createFakeBlob(), true);
|
||||||
|
component.ngOnChanges({ blobFile });
|
||||||
|
|
||||||
|
expect(component.cropper.replace).not.toHaveBeenCalled();
|
||||||
|
expect(component.urlFile).toEqual('safe-fake-blob-url');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -150,7 +150,11 @@ export class ImgViewerComponent implements AfterViewInit, OnChanges, OnDestroy {
|
|||||||
ngOnChanges(changes: SimpleChanges) {
|
ngOnChanges(changes: SimpleChanges) {
|
||||||
const blobFile = changes['blobFile'];
|
const blobFile = changes['blobFile'];
|
||||||
if (blobFile?.currentValue) {
|
if (blobFile?.currentValue) {
|
||||||
this.urlFile = this.urlService.createTrustedUrl(this.blobFile);
|
const rawUrl = this.urlService.createObjectUrl(this.blobFile);
|
||||||
|
if (!blobFile.firstChange && this.cropper) {
|
||||||
|
this.cropper.replace(rawUrl);
|
||||||
|
}
|
||||||
|
this.urlFile = this.urlService.trustUrl(rawUrl);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user