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
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,12 +173,41 @@ describe('Test Img viewer component ', () => {
|
||||
it('should not thrown an error if blob is passed ', () => {
|
||||
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);
|
||||
expect(() => {
|
||||
component.ngOnChanges({ blobFile: change });
|
||||
}).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) {
|
||||
const blobFile = changes['blobFile'];
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user