diff --git a/src/app/thumbnail/thumbnail.component.spec.ts b/src/app/thumbnail/thumbnail.component.spec.ts index 10e461112b..f81089e57c 100644 --- a/src/app/thumbnail/thumbnail.component.spec.ts +++ b/src/app/thumbnail/thumbnail.component.spec.ts @@ -2,6 +2,7 @@ import { DebugElement, Pipe, PipeTransform, + PLATFORM_ID, } from '@angular/core'; import { ComponentFixture, @@ -48,298 +49,261 @@ describe('ThumbnailComponent', () => { let authService; let authorizationService; let fileService; + let spy; - beforeEach(waitForAsync(() => { - authService = jasmine.createSpyObj('AuthService', { - isAuthenticated: observableOf(true), - }); - authorizationService = jasmine.createSpyObj('AuthorizationService', { - isAuthorized: observableOf(true), - }); - fileService = jasmine.createSpyObj('FileService', { - retrieveFileDownloadLink: null, - }); - fileService.retrieveFileDownloadLink.and.callFake((url) => observableOf(`${url}?authentication-token=fake`)); - - TestBed.configureTestingModule({ - imports: [ - TranslateModule.forRoot(), - ThumbnailComponent, - SafeUrlPipe, - MockTranslatePipe, - VarDirective, - ], - providers: [ - { provide: AuthService, useValue: authService }, - { provide: AuthorizationDataService, useValue: authorizationService }, - { provide: FileService, useValue: fileService }, - { provide: ThemeService, useValue: getMockThemeService() }, - ], - }).overrideComponent(ThumbnailComponent, { - add: { - imports: [MockTranslatePipe], - }, - }) - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(ThumbnailComponent); - fixture.detectChanges(); - - authService = TestBed.inject(AuthService); - - comp = fixture.componentInstance; // ThumbnailComponent test instance - de = fixture.debugElement.query(By.css('div.thumbnail')); - el = de.nativeElement; - }); - - describe('loading', () => { - it('should start out with isLoading$ true', () => { - expect(comp.isLoading).toBeTrue(); - }); - - it('should set isLoading$ to false once an image is successfully loaded', () => { - comp.setSrc('http://bit.stream'); - fixture.debugElement.query(By.css('img.thumbnail-content')).triggerEventHandler('load', new Event('load')); - expect(comp.isLoading).toBeFalse(); - }); - - it('should set isLoading$ to false once the src is set to null', () => { - comp.setSrc(null); - expect(comp.isLoading).toBeFalse(); - }); - - it('should show a loading animation while isLoading$ is true', () => { - expect(de.query(By.css('ds-loading'))).toBeTruthy(); - - comp.isLoading = false; - fixture.detectChanges(); - expect(fixture.debugElement.query(By.css('ds-loading'))).toBeFalsy(); - }); - - describe('with a thumbnail image', () => { - beforeEach(() => { - comp.src = 'https://bit.stream'; - fixture.detectChanges(); + describe('when platform is browser', () => { + beforeEach(waitForAsync(() => { + authService = jasmine.createSpyObj('AuthService', { + isAuthenticated: observableOf(true), }); - - it('should render but hide the image while loading and show it once done', () => { - let img = fixture.debugElement.query(By.css('img.thumbnail-content')); - expect(img).toBeTruthy(); - expect(img.classes['d-none']).toBeTrue(); - - comp.isLoading = false; - fixture.detectChanges(); - img = fixture.debugElement.query(By.css('img.thumbnail-content')); - expect(img).toBeTruthy(); - expect(img.classes['d-none']).toBeFalsy(); + authorizationService = jasmine.createSpyObj('AuthorizationService', { + isAuthorized: observableOf(true), }); - - }); - - describe('without a thumbnail image', () => { - beforeEach(() => { - comp.src = null; - fixture.detectChanges(); + fileService = jasmine.createSpyObj('FileService', { + retrieveFileDownloadLink: null, }); + fileService.retrieveFileDownloadLink.and.callFake((url) => observableOf(`${url}?authentication-token=fake`)); - it('should only show the HTML placeholder once done loading', () => { - expect(fixture.debugElement.query(By.css('div.thumbnail-placeholder'))).toBeFalsy(); - - comp.isLoading = false; - fixture.detectChanges(); - expect(fixture.debugElement.query(By.css('div.thumbnail-placeholder'))).toBeTruthy(); - }); - }); - - }); - - const errorHandler = () => { - let setSrcSpy; + TestBed.configureTestingModule({ + imports: [ + TranslateModule.forRoot(), + ThumbnailComponent, + SafeUrlPipe, + MockTranslatePipe, + VarDirective, + ], + providers: [ + { provide: AuthService, useValue: authService }, + { provide: AuthorizationDataService, useValue: authorizationService }, + { provide: FileService, useValue: fileService }, + { provide: ThemeService, useValue: getMockThemeService() }, + { provide: PLATFORM_ID, useValue: 'browser' }, + ], + }).overrideComponent(ThumbnailComponent, { + add: { + imports: [MockTranslatePipe], + }, + }) + .compileComponents(); + })); beforeEach(() => { - // disconnect error handler to be sure it's only called once - const img = fixture.debugElement.query(By.css('img.thumbnail-content')); - img.nativeNode.onerror = null; + fixture = TestBed.createComponent(ThumbnailComponent); + fixture.detectChanges(); - comp.ngOnChanges({}); - setSrcSpy = spyOn(comp, 'setSrc').and.callThrough(); + authService = TestBed.inject(AuthService); + + comp = fixture.componentInstance; // ThumbnailComponent test instance + de = fixture.debugElement.query(By.css('div.thumbnail')); + el = de.nativeElement; }); - describe('retry with authentication token', () => { - it('should remember that it already retried once', () => { - expect(comp.retriedWithToken).toBeFalse(); - comp.errorHandler(); - expect(comp.retriedWithToken).toBeTrue(); + describe('loading', () => { + it('should start out with isLoading$ true', () => { + expect(comp.isLoading).toBeTrue(); }); - describe('if not logged in', () => { + it('should set isLoading$ to false once an image is successfully loaded', () => { + comp.setSrc('http://bit.stream'); + fixture.debugElement.query(By.css('img.thumbnail-content')).triggerEventHandler('load', new Event('load')); + expect(comp.isLoading).toBeFalse(); + }); + + it('should set isLoading$ to false once the src is set to null', () => { + comp.setSrc(null); + expect(comp.isLoading).toBeFalse(); + }); + + it('should show a loading animation while isLoading$ is true', () => { + expect(de.query(By.css('ds-loading'))).toBeTruthy(); + + comp.isLoading = false; + fixture.detectChanges(); + expect(fixture.debugElement.query(By.css('ds-loading'))).toBeFalsy(); + }); + + describe('with a thumbnail image', () => { beforeEach(() => { - authService.isAuthenticated.and.returnValue(observableOf(false)); + comp.src = 'https://bit.stream'; + fixture.detectChanges(); }); - it('should fall back to default', () => { + it('should render but hide the image while loading and show it once done', () => { + let img = fixture.debugElement.query(By.css('img.thumbnail-content')); + expect(img).toBeTruthy(); + expect(img.classes['d-none']).toBeTrue(); + + comp.isLoading = false; + fixture.detectChanges(); + img = fixture.debugElement.query(By.css('img.thumbnail-content')); + expect(img).toBeTruthy(); + expect(img.classes['d-none']).toBeFalsy(); + }); + + }); + + describe('without a thumbnail image', () => { + beforeEach(() => { + comp.src = null; + fixture.detectChanges(); + }); + + it('should only show the HTML placeholder once done loading', () => { + expect(fixture.debugElement.query(By.css('div.thumbnail-placeholder'))).toBeFalsy(); + + comp.isLoading = false; + fixture.detectChanges(); + expect(fixture.debugElement.query(By.css('div.thumbnail-placeholder'))).toBeTruthy(); + }); + }); + + }); + + const errorHandler = () => { + let setSrcSpy; + + beforeEach(() => { + // disconnect error handler to be sure it's only called once + const img = fixture.debugElement.query(By.css('img.thumbnail-content')); + img.nativeNode.onerror = null; + + comp.ngOnChanges({}); + setSrcSpy = spyOn(comp, 'setSrc').and.callThrough(); + }); + + describe('retry with authentication token', () => { + it('should remember that it already retried once', () => { + expect(comp.retriedWithToken).toBeFalse(); comp.errorHandler(); - expect(setSrcSpy).toHaveBeenCalledWith(comp.defaultImage); - }); - }); - - describe('if logged in', () => { - beforeEach(() => { - authService.isAuthenticated.and.returnValue(observableOf(true)); + expect(comp.retriedWithToken).toBeTrue(); }); - describe('and authorized to download the thumbnail', () => { + describe('if not logged in', () => { beforeEach(() => { - authorizationService.isAuthorized.and.returnValue(observableOf(true)); - }); - - it('should add an authentication token to the thumbnail URL', () => { - comp.errorHandler(); - - if ((comp.thumbnail as RemoteData)?.hasFailed) { - // If we failed to retrieve the Bitstream in the first place, fall back to the default - expect(setSrcSpy).toHaveBeenCalledWith(comp.defaultImage); - } else { - expect(setSrcSpy).toHaveBeenCalledWith(CONTENT + '?authentication-token=fake'); - } - }); - }); - - describe('but not authorized to download the thumbnail', () => { - beforeEach(() => { - authorizationService.isAuthorized.and.returnValue(observableOf(false)); + authService.isAuthenticated.and.returnValue(observableOf(false)); }); it('should fall back to default', () => { comp.errorHandler(); - expect(setSrcSpy).toHaveBeenCalledWith(comp.defaultImage); - - // We don't need to check authorization if we failed to retrieve the Bitstreamin the first place - if (!(comp.thumbnail as RemoteData)?.hasFailed) { - expect(authorizationService.isAuthorized).toHaveBeenCalled(); - } }); }); + + describe('if logged in', () => { + beforeEach(() => { + authService.isAuthenticated.and.returnValue(observableOf(true)); + }); + + describe('and authorized to download the thumbnail', () => { + beforeEach(() => { + authorizationService.isAuthorized.and.returnValue(observableOf(true)); + }); + + it('should add an authentication token to the thumbnail URL', () => { + comp.errorHandler(); + + if ((comp.thumbnail as RemoteData)?.hasFailed) { + // If we failed to retrieve the Bitstream in the first place, fall back to the default + expect(setSrcSpy).toHaveBeenCalledWith(comp.defaultImage); + } else { + expect(setSrcSpy).toHaveBeenCalledWith(CONTENT + '?authentication-token=fake'); + } + }); + }); + + describe('but not authorized to download the thumbnail', () => { + beforeEach(() => { + authorizationService.isAuthorized.and.returnValue(observableOf(false)); + }); + + it('should fall back to default', () => { + comp.errorHandler(); + + expect(setSrcSpy).toHaveBeenCalledWith(comp.defaultImage); + + // We don't need to check authorization if we failed to retrieve the Bitstreamin the first place + if (!(comp.thumbnail as RemoteData)?.hasFailed) { + expect(authorizationService.isAuthorized).toHaveBeenCalled(); + } + }); + }); + }); + }); + + describe('after retrying with token', () => { + beforeEach(() => { + comp.retriedWithToken = true; + }); + + it('should fall back to default', () => { + comp.errorHandler(); + expect(authService.isAuthenticated).not.toHaveBeenCalled(); + expect(fileService.retrieveFileDownloadLink).not.toHaveBeenCalled(); + expect(setSrcSpy).toHaveBeenCalledWith(comp.defaultImage); + }); + }); + }; + + describe('fallback', () => { + describe('if there is a default image', () => { + it('should display the default image', () => { + comp.src = 'http://bit.stream'; + comp.defaultImage = 'http://default.img'; + comp.errorHandler(); + expect(comp.src).toBe(comp.defaultImage); + }); + + it('should include the alt text', () => { + comp.src = 'http://bit.stream'; + comp.defaultImage = 'http://default.img'; + comp.errorHandler(); + + fixture.detectChanges(); + const image: HTMLElement = fixture.debugElement.query(By.css('img')).nativeElement; + expect(image.getAttribute('alt')).toBe('TRANSLATED ' + comp.alt); + }); + }); + + describe('if there is no default image', () => { + it('should display the HTML placeholder', () => { + comp.src = 'http://default.img'; + comp.defaultImage = null; + comp.errorHandler(); + expect(comp.src).toBe(null); + + fixture.detectChanges(); + const placeholder = fixture.debugElement.query(By.css('div.thumbnail-placeholder')).nativeElement; + expect(placeholder.innerHTML).toContain('TRANSLATED ' + comp.placeholder); + }); }); }); - describe('after retrying with token', () => { + describe('with thumbnail as Bitstream', () => { + let thumbnail; beforeEach(() => { - comp.retriedWithToken = true; - }); - - it('should fall back to default', () => { - comp.errorHandler(); - expect(authService.isAuthenticated).not.toHaveBeenCalled(); - expect(fileService.retrieveFileDownloadLink).not.toHaveBeenCalled(); - expect(setSrcSpy).toHaveBeenCalledWith(comp.defaultImage); - }); - }); - }; - - describe('fallback', () => { - describe('if there is a default image', () => { - it('should display the default image', () => { - comp.src = 'http://bit.stream'; - comp.defaultImage = 'http://default.img'; - comp.errorHandler(); - expect(comp.src).toBe(comp.defaultImage); - }); - - it('should include the alt text', () => { - comp.src = 'http://bit.stream'; - comp.defaultImage = 'http://default.img'; - comp.errorHandler(); - - fixture.detectChanges(); - const image: HTMLElement = fixture.debugElement.query(By.css('img')).nativeElement; - expect(image.getAttribute('alt')).toBe('TRANSLATED ' + comp.alt); - }); - }); - - describe('if there is no default image', () => { - it('should display the HTML placeholder', () => { - comp.src = 'http://default.img'; - comp.defaultImage = null; - comp.errorHandler(); - expect(comp.src).toBe(null); - - fixture.detectChanges(); - const placeholder = fixture.debugElement.query(By.css('div.thumbnail-placeholder')).nativeElement; - expect(placeholder.innerHTML).toContain('TRANSLATED ' + comp.placeholder); - }); - }); - }); - - describe('with thumbnail as Bitstream', () => { - let thumbnail; - beforeEach(() => { - thumbnail = new Bitstream(); - thumbnail._links = { - self: { href: 'self.url' }, - bundle: { href: 'bundle.url' }, - format: { href: 'format.url' }, - content: { href: CONTENT }, - thumbnail: undefined, - }; - comp.thumbnail = thumbnail; - }); - - describe('if content can be loaded', () => { - it('should display an image', () => { - comp.ngOnChanges({}); - fixture.detectChanges(); - const image: HTMLElement = fixture.debugElement.query(By.css('img')).nativeElement; - expect(image.getAttribute('src')).toBe(thumbnail._links.content.href); - }); - - it('should include the alt text', () => { - comp.ngOnChanges({}); - fixture.detectChanges(); - const image: HTMLElement = fixture.debugElement.query(By.css('img')).nativeElement; - expect(image.getAttribute('alt')).toBe('TRANSLATED ' + comp.alt); - }); - }); - - describe('if content can\'t be loaded', () => { - errorHandler(); - }); - }); - - describe('with thumbnail as RemoteData', () => { - let thumbnail: Bitstream; - - beforeEach(() => { - thumbnail = new Bitstream(); - thumbnail._links = { - self: { href: 'self.url' }, - bundle: { href: 'bundle.url' }, - format: { href: 'format.url' }, - content: { href: CONTENT }, - thumbnail: undefined, - }; - }); - - describe('if RemoteData succeeded', () => { - beforeEach(() => { - comp.thumbnail = createSuccessfulRemoteDataObject(thumbnail); + thumbnail = new Bitstream(); + thumbnail._links = { + self: { href: 'self.url' }, + bundle: { href: 'bundle.url' }, + format: { href: 'format.url' }, + content: { href: CONTENT }, + thumbnail: undefined, + }; + comp.thumbnail = thumbnail; }); describe('if content can be loaded', () => { it('should display an image', () => { comp.ngOnChanges({}); fixture.detectChanges(); - const image: HTMLElement = de.query(By.css('img')).nativeElement; + const image: HTMLElement = fixture.debugElement.query(By.css('img')).nativeElement; expect(image.getAttribute('src')).toBe(thumbnail._links.content.href); }); - it('should display the alt text', () => { + it('should include the alt text', () => { comp.ngOnChanges({}); fixture.detectChanges(); - const image: HTMLElement = de.query(By.css('img')).nativeElement; + const image: HTMLElement = fixture.debugElement.query(By.css('img')).nativeElement; expect(image.getAttribute('alt')).toBe('TRANSLATED ' + comp.alt); }); }); @@ -349,16 +313,117 @@ describe('ThumbnailComponent', () => { }); }); - describe('if RemoteData failed', () => { + describe('with thumbnail as RemoteData', () => { + let thumbnail: Bitstream; + beforeEach(() => { - comp.thumbnail = createFailedRemoteDataObject(); + thumbnail = new Bitstream(); + thumbnail._links = { + self: { href: 'self.url' }, + bundle: { href: 'bundle.url' }, + format: { href: 'format.url' }, + content: { href: CONTENT }, + thumbnail: undefined, + }; }); - it('should show the default image', () => { - comp.defaultImage = 'default/image.jpg'; - comp.ngOnChanges({}); - expect(comp.src).toBe('default/image.jpg'); + describe('if RemoteData succeeded', () => { + beforeEach(() => { + comp.thumbnail = createSuccessfulRemoteDataObject(thumbnail); + }); + + describe('if content can be loaded', () => { + it('should display an image', () => { + comp.ngOnChanges({}); + fixture.detectChanges(); + const image: HTMLElement = de.query(By.css('img')).nativeElement; + expect(image.getAttribute('src')).toBe(thumbnail._links.content.href); + }); + + it('should display the alt text', () => { + comp.ngOnChanges({}); + fixture.detectChanges(); + const image: HTMLElement = de.query(By.css('img')).nativeElement; + expect(image.getAttribute('alt')).toBe('TRANSLATED ' + comp.alt); + }); + }); + + describe('if content can\'t be loaded', () => { + errorHandler(); + }); + }); + + describe('if RemoteData failed', () => { + beforeEach(() => { + comp.thumbnail = createFailedRemoteDataObject(); + }); + + it('should show the default image', () => { + comp.defaultImage = 'default/image.jpg'; + comp.ngOnChanges({}); + expect(comp.src).toBe('default/image.jpg'); + }); }); }); }); + + describe('when platform is server', () => { + beforeEach(waitForAsync(() => { + + authService = jasmine.createSpyObj('AuthService', { + isAuthenticated: observableOf(true), + }); + authorizationService = jasmine.createSpyObj('AuthorizationService', { + isAuthorized: observableOf(true), + }); + fileService = jasmine.createSpyObj('FileService', { + retrieveFileDownloadLink: null, + }); + fileService.retrieveFileDownloadLink.and.callFake((url) => observableOf(`${url}?authentication-token=fake`)); + + TestBed.configureTestingModule({ + imports: [ + TranslateModule.forRoot(), + ThumbnailComponent, + SafeUrlPipe, + MockTranslatePipe, + VarDirective, + ], + providers: [ + { provide: AuthService, useValue: authService }, + { provide: AuthorizationDataService, useValue: authorizationService }, + { provide: FileService, useValue: fileService }, + { provide: ThemeService, useValue: getMockThemeService() }, + { provide: PLATFORM_ID, useValue: 'server' }, + ], + }).overrideComponent(ThumbnailComponent, { + add: { + imports: [MockTranslatePipe], + }, + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ThumbnailComponent); + spyOn(fixture.componentInstance, 'setSrc').and.callThrough(); + fixture.detectChanges(); + + authService = TestBed.inject(AuthService); + + comp = fixture.componentInstance; // ThumbnailComponent test instance + de = fixture.debugElement.query(By.css('div.thumbnail')); + el = de.nativeElement; + }); + + it('should start out with isLoading$ true', () => { + expect(comp.isLoading).toBeTrue(); + expect(de.query(By.css('ds-loading'))).toBeTruthy(); + }); + + it('should not call setSrc', () => { + expect(comp.setSrc).not.toHaveBeenCalled(); + }); + + }); });