From 6aff818dcd0ffdea0dfbbe028cb53945dc7e3650 Mon Sep 17 00:00:00 2001 From: Lotte Hofstede Date: Tue, 23 May 2017 10:21:26 +0200 Subject: [PATCH] first tests and docs --- .../simple-item-page.e2e-spec.ts | 19 +++ e2e/simple-item-page/simple-item-page.po.ts | 19 +++ src/app/core/shared/item.model.spec.ts | 120 ++++++++++++++++++ src/app/core/shared/item.model.ts | 21 ++- src/app/item-page/item-page.component.ts | 6 + .../item-page-specific-field.component.ts | 16 +++ src/app/thumbnail/thumbnail.component.spec.ts | 49 +++++++ src/app/thumbnail/thumbnail.component.ts | 6 + 8 files changed, 255 insertions(+), 1 deletion(-) create mode 100644 e2e/simple-item-page/simple-item-page.e2e-spec.ts create mode 100644 e2e/simple-item-page/simple-item-page.po.ts create mode 100644 src/app/core/shared/item.model.spec.ts create mode 100644 src/app/thumbnail/thumbnail.component.spec.ts diff --git a/e2e/simple-item-page/simple-item-page.e2e-spec.ts b/e2e/simple-item-page/simple-item-page.e2e-spec.ts new file mode 100644 index 0000000000..9fdcb1bec2 --- /dev/null +++ b/e2e/simple-item-page/simple-item-page.e2e-spec.ts @@ -0,0 +1,19 @@ +import { ProtractorPage } from './simple-item-page.po'; + +describe('protractor Simple Item Page', function() { + let page: ProtractorPage; + + beforeEach(() => { + page = new ProtractorPage(); + page.navigateToSimpleItemPage(); + }); + + it('should contain element ds-thumbnail"', () => { + expect(page.elementTagExists("ds-thumbnail")).toEqual(true); + }); + + it('should contain element h1.item-page-title-field"', () => { + expect(page.elementSelectorExists("h1.selector")).toEqual(true); + }); + +}); diff --git a/e2e/simple-item-page/simple-item-page.po.ts b/e2e/simple-item-page/simple-item-page.po.ts new file mode 100644 index 0000000000..f0b36dd6eb --- /dev/null +++ b/e2e/simple-item-page/simple-item-page.po.ts @@ -0,0 +1,19 @@ +import { browser, element, by } from 'protractor'; + +export class ProtractorPage { + ITEMPAGE : string = "/items/8766"; + + navigateToSimpleItemPage() { + return browser.get(this.ITEMPAGE); + } + + elementTagExists(tag : string) { + return element(by.tagName(tag)).isPresent(); + } + + elementSelectorExists(selector : string) { + return element(by.css(selector)).isPresent(); + } + + +} \ No newline at end of file diff --git a/src/app/core/shared/item.model.spec.ts b/src/app/core/shared/item.model.spec.ts new file mode 100644 index 0000000000..99ce0271d1 --- /dev/null +++ b/src/app/core/shared/item.model.spec.ts @@ -0,0 +1,120 @@ +import { TestBed, async } from '@angular/core/testing'; +import { Item } from "./item.model"; +import { Bundle } from "./bundle.model"; +import { Observable } from "rxjs"; +import { RemoteData } from "../data/remote-data"; +import { Bitstream } from "./bitstream.model"; + + +describe('Item', () => { + + + let item: Item; + const thumbnailBundleName = "THUMBNAIL"; + const originalBundleName = "ORIGINAL"; + const thumbnailPath = "thumbnail.jpg"; + const bitstream1Path = "document.pdf"; + const bitstream2Path = "otherfile.doc"; + + const nonExistingBundleName = "c1e568f7-d14e-496b-bdd7-07026998cc00"; + let remoteThumbnailBundle; + let remoteOriginalBundle; + let thumbnailBundle; + let originalBundle; + + beforeEach(() => { + const thumbnail = { + retrieve: thumbnailPath + }; + + const bitstream1 = { + retrieve: bitstream1Path + }; + const bitstream2 = { + retrieve: bitstream2Path + }; + + const remoteDataThumbnail = createRemoteDataObject(thumbnail); + const remoteDataFile1 = createRemoteDataObject(bitstream1); + const remoteDataFile2 = createRemoteDataObject(bitstream2); + + + // Create Bundles + + thumbnailBundle = { + name: thumbnailBundleName, + primaryBitstream: remoteDataThumbnail + }; + + originalBundle = { + name: originalBundleName, + bitstreams: [remoteDataFile1, remoteDataFile2] + }; + + remoteThumbnailBundle = createRemoteDataObject(thumbnailBundle); + remoteOriginalBundle = createRemoteDataObject(originalBundle); + + item = Object.assign(new Item(), { bundles: [remoteThumbnailBundle, remoteOriginalBundle] }); + + }); + + + it('should return the bundle with the given name of this item when the bundle exists', () => { + let name: string = thumbnailBundleName; + let bundle: Observable = item.getBundle(name); + bundle.map(b => expect(b.name).toBe(name)); + }); + + it('should return null when no bundle with this name exists for this item', () => { + let name: string = nonExistingBundleName; + let bundle: Observable = item.getBundle(name); + bundle.map(b => expect(b).toBeNull()); + }); + + + + describe("get thumbnail", () => { + beforeEach(() => { + spyOn(item, 'getBundle').and.returnValue(Observable.of(thumbnailBundle)); + }); + + it('should return the thumbnail (the primaryBitstream in the bundle "THUMBNAIL") of this item', () => { + let path: string = thumbnailPath; + let bitstream: Observable = item.getThumbnail(); + bitstream.map(b => expect(b.retrieve).toBe(path)); + }); + }); + + + describe("get files", () => { + beforeEach(() => { + spyOn(item, 'getBundle').and.returnValue(Observable.of(originalBundle)); + }); + + it('should return all files in the ORIGINAL bundle', () => { + let paths = [bitstream1Path, bitstream2Path]; + + let files: Observable>> = item.getFiles(); + let index = 0; + files.map(f => expect(f.length).toBe(2)); + files.subscribe( + array => array.forEach( + observableFile => observableFile.subscribe( + file => { + expect(file.retrieve).toBe(paths[index]); + index++; + } + ) + ) + ) + }); + + }); + + +}); + +function createRemoteDataObject(object: Object) { + return new RemoteData("", Observable.of(false), Observable.of(false), Observable.of(true), Observable.of(undefined), Observable.of(object)); + +} \ No newline at end of file diff --git a/src/app/core/shared/item.model.ts b/src/app/core/shared/item.model.ts index 92a05263a4..8a589822d1 100644 --- a/src/app/core/shared/item.model.ts +++ b/src/app/core/shared/item.model.ts @@ -4,6 +4,7 @@ import { RemoteData } from "../data/remote-data"; import { Bundle } from "./bundle.model"; import { Bitstream } from "./bitstream.model"; import { Observable } from "rxjs"; +import { hasValue } from "../../shared/empty.util"; export class Item extends DSpaceObject { @@ -39,6 +40,11 @@ export class Item extends DSpaceObject { bundles: Array>; + + /** + * Retrieves the thumbnail of this item + * @returns {Observable} the primaryBitstream of the "THUMBNAIL" bundle + */ getThumbnail(): Observable { const bundle: Observable = this.getBundle("THUMBNAIL"); return bundle.flatMap( @@ -53,15 +59,24 @@ export class Item extends DSpaceObject { ); } + /** + * Retrieves all files that should be displayed on the item page of this item + * @returns {Observable>>} an array of all Bitstreams in the "ORIGINAL" bundle + */ getFiles(): Observable>> { const bundle: Observable = this.getBundle("ORIGINAL"); return bundle.map(bundle => { - if (bundle != null) { + if (hasValue(bundle) && Array.isArray(bundle.bitstreams)) { return bundle.bitstreams.map(bitstream => bitstream.payload) } }); } + /** + * Retrieves the bundle of this item by its name + * @param name The name of the Bundle that should be returned + * @returns {Observable} the Bundle that belongs to this item with the given name + */ getBundle(name: String): Observable { return Observable.combineLatest( ...this.bundles.map(b => b.payload), @@ -73,6 +88,10 @@ export class Item extends DSpaceObject { }); } + /** + * Retrieves all direct parent collections of this item + * @returns {Array>} an array of all Collections that contain this item + */ getCollections(): Array> { return this.parents.map(collection => collection.payload.map(parent => parent)); } diff --git a/src/app/item-page/item-page.component.ts b/src/app/item-page/item-page.component.ts index c0dcafb3b0..52fb155184 100644 --- a/src/app/item-page/item-page.component.ts +++ b/src/app/item-page/item-page.component.ts @@ -6,6 +6,12 @@ import { RemoteData } from "../core/data/remote-data"; import { Observable } from "rxjs"; import { Bitstream } from "../core/shared/bitstream.model"; +/** + * This component renders a simple item page. + * The route parameter 'id' is used to request the item it represents. + * All fields of the item that should be displayed, are defined in its template. + */ + @Component({ selector: 'ds-item-page', styleUrls: ['./item-page.component.css'], diff --git a/src/app/item-page/specific-field/item-page-specific-field.component.ts b/src/app/item-page/specific-field/item-page-specific-field.component.ts index 4161505ccc..ebb7a29c6f 100644 --- a/src/app/item-page/specific-field/item-page-specific-field.component.ts +++ b/src/app/item-page/specific-field/item-page-specific-field.component.ts @@ -1,6 +1,12 @@ import { Component, OnInit, Input } from '@angular/core'; import { Item } from "../../core/shared/item.model"; +/** + * This component can be used to represent metadata on a simple item page. + * It expects one input parameter of type Item to which the metadata belongs. + * This class can be extended to print certain metadata. + */ + @Component({ templateUrl: './item-page-specific-field.component.html' }) @@ -8,10 +14,20 @@ export class ItemPageSpecificFieldComponent { @Input() item: Item; + /** + * Fields (schema.element.qualifier) used to render their values. + */ fields : string[]; + /** + * Label i18n key for the rendered metadata + */ label : string; + /** + * Separator string between multiple values of the metadata fields defined + * @type {string} + */ separator : string = "
"; constructor() { diff --git a/src/app/thumbnail/thumbnail.component.spec.ts b/src/app/thumbnail/thumbnail.component.spec.ts new file mode 100644 index 0000000000..eb126287ba --- /dev/null +++ b/src/app/thumbnail/thumbnail.component.spec.ts @@ -0,0 +1,49 @@ +import { ComponentFixture, TestBed, async } from '@angular/core/testing'; +import { By } from '@angular/platform-browser'; +import { DebugElement } from '@angular/core'; + +import { ThumbnailComponent } from "./thumbnail.component"; +import { Bitstream } from "../core/shared/bitstream.model"; +import { SafeUrlPipe } from "../shared/utils/safe-url-pipe"; + + +describe('ThumbnailComponent', () => { + let comp: ThumbnailComponent; + let fixture: ComponentFixture; + let de: DebugElement; + let el: HTMLElement; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ThumbnailComponent, SafeUrlPipe] + }) + .compileComponents(); + + })); + + + beforeEach(() => { + fixture = TestBed.createComponent(ThumbnailComponent); + + comp = fixture.componentInstance; // BannerComponent test instance + de = fixture.debugElement.query(By.css('div.thumbnail')); + el = de.nativeElement; + }); + + + it('should display image', () => { + comp.thumbnail = new Bitstream(); + comp.thumbnail.retrieve = "test.url"; + fixture.detectChanges(); + let image : HTMLElement = de.query(By.css('img')).nativeElement; + expect(image.getAttribute("src")).toBe(comp.thumbnail.retrieve); + }); + + it('should display placeholder', () => { + fixture.detectChanges(); + let image : HTMLElement = de.query(By.css('img')).nativeElement; + expect(image.getAttribute("src")).toBe(comp.holderSource); + }); + + +}); \ No newline at end of file diff --git a/src/app/thumbnail/thumbnail.component.ts b/src/app/thumbnail/thumbnail.component.ts index e8e226c004..61b9e31fbe 100644 --- a/src/app/thumbnail/thumbnail.component.ts +++ b/src/app/thumbnail/thumbnail.component.ts @@ -1,6 +1,12 @@ import { Component, Input, OnInit } from '@angular/core'; import { Bitstream } from "../core/shared/bitstream.model"; +/** + * This component renders a given Bitstream as a thumbnail. + * One input parameter of type Bitstream is expected. + * If no Bitstream is provided, a holderjs image will be rendered instead. + */ + @Component({ selector: 'ds-thumbnail', styleUrls: ['./thumbnail.component.css'],