From 5f76bba05065bc6710080cbf79d3d7d78270a0f9 Mon Sep 17 00:00:00 2001 From: Lotte Hofstede Date: Mon, 5 Feb 2018 11:32:26 +0100 Subject: [PATCH] 46063: truncation finished, still have to fix some existing tests --- src/app/shared/animations/focus.ts | 2 +- ...arch-result-grid-element.component.spec.ts | 21 ++-- ...arch-result-grid-element.component.spec.ts | 9 +- ...-search-result-grid-element.component.html | 16 +-- ...arch-result-grid-element.component.spec.ts | 24 +++-- .../collection-list-element.component.spec.ts | 59 +++++++++++ .../community-list-element.component.spec.ts | 65 ++++++++++++ .../item-list-element.component.spec.ts | 67 +++++++++++++ .../object-list/object-list.component.html | 2 +- ...arch-result-list-element.component.spec.ts | 68 +++++++++++++ ...arch-result-list-element.component.spec.ts | 69 +++++++++++++ ...-search-result-list-element.component.html | 4 - ...arch-result-list-element.component.spec.ts | 86 ++++++++++++++++ .../truncatable-part.component.html | 6 +- .../truncatable-part.component.scss | 42 ++++---- .../truncatable-part.component.spec.ts | 76 ++++++++++++++ .../truncatable-part.component.ts | 13 ++- .../truncatable/truncatable.component.spec.ts | 99 +++++++++++++++++++ .../truncatable/truncatable.component.ts | 6 +- .../truncatable/truncatable.reducer.spec.ts | 96 ++++++++++++++++++ .../truncatable/truncatable.service.spec.ts | 54 ++++++++++ 21 files changed, 826 insertions(+), 58 deletions(-) create mode 100644 src/app/shared/object-list/collection-list-element/collection-list-element.component.spec.ts create mode 100644 src/app/shared/object-list/community-list-element/community-list-element.component.spec.ts create mode 100644 src/app/shared/object-list/item-list-element/item-list-element.component.spec.ts create mode 100644 src/app/shared/object-list/search-result-list-element/collection-search-result/collection-search-result-list-element.component.spec.ts create mode 100644 src/app/shared/object-list/search-result-list-element/community-search-result/community-search-result-list-element.component.spec.ts create mode 100644 src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.spec.ts create mode 100644 src/app/shared/truncatable/truncatable-part/truncatable-part.component.spec.ts create mode 100644 src/app/shared/truncatable/truncatable.component.spec.ts create mode 100644 src/app/shared/truncatable/truncatable.reducer.spec.ts create mode 100644 src/app/shared/truncatable/truncatable.service.spec.ts diff --git a/src/app/shared/animations/focus.ts b/src/app/shared/animations/focus.ts index 6458fd0d78..33a5010629 100644 --- a/src/app/shared/animations/focus.ts +++ b/src/app/shared/animations/focus.ts @@ -2,7 +2,7 @@ import { animate, state, transition, trigger, style } from '@angular/animations' export const focusShadow = trigger('focusShadow', [ - state('focus', style({ 'box-shadow': '0 0 6px #777777' })), + state('focus', style({ 'box-shadow': 'rgba(119, 119, 119, 0.6) 0px 0px 6px' })), state('blur', style({ 'box-shadow': 'none' })), diff --git a/src/app/shared/object-grid/search-result-grid-element/collection-search-result/collection-search-result-grid-element.component.spec.ts b/src/app/shared/object-grid/search-result-grid-element/collection-search-result/collection-search-result-grid-element.component.spec.ts index a3b3710080..9eb65dbbdd 100644 --- a/src/app/shared/object-grid/search-result-grid-element/collection-search-result/collection-search-result-grid-element.component.spec.ts +++ b/src/app/shared/object-grid/search-result-grid-element/collection-search-result/collection-search-result-grid-element.component.spec.ts @@ -1,4 +1,4 @@ -import {CollectionSearchResultGridElementComponent } from './collection-search-result-grid-element.component'; +import { CollectionSearchResultGridElementComponent } from './collection-search-result-grid-element.component'; import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { Observable } from 'rxjs/Observable'; import { ActivatedRoute, Router } from '@angular/router'; @@ -8,6 +8,7 @@ import { By } from '@angular/platform-browser'; import { TruncatePipe } from '../../../utils/truncate.pipe'; import { Community } from '../../../../core/shared/community.model'; import { Collection } from '../../../../core/shared/collection.model'; +import { TruncatableService } from '../../../truncatable/truncatable.service'; let fixture: ComponentFixture; const queryParam = 'test query'; @@ -18,29 +19,33 @@ const activatedRouteStub = { scope: scopeParam }) }; +const truncatableServiceStub: any = { + isCollapsed: (id: number) => Observable.of(true), +}; + const mockCollection: Collection = Object.assign(new Collection(), { metadata: [ { key: 'dc.description.abstract', language: 'en_US', value: 'Short description' - } ] + }] }); - -const createdGridElementComponent: CollectionSearchResultGridElementComponent = new CollectionSearchResultGridElementComponent(mockCollection); +const createdGridElementComponent: CollectionSearchResultGridElementComponent = new CollectionSearchResultGridElementComponent(mockCollection, truncatableServiceStub as TruncatableService); describe('CollectionSearchResultGridElementComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ CollectionSearchResultGridElementComponent, TruncatePipe ], + declarations: [CollectionSearchResultGridElementComponent, TruncatePipe], providers: [ + { provide: TruncatableService, useValue: truncatableServiceStub }, { provide: ActivatedRoute, useValue: activatedRouteStub }, { provide: Router, useClass: RouterStub }, { provide: 'objectElementProvider', useValue: (createdGridElementComponent) } ], - schemas: [ NO_ERRORS_SCHEMA ] + schemas: [NO_ERRORS_SCHEMA] }).compileComponents(); // compile template and css })); @@ -52,12 +57,12 @@ describe('CollectionSearchResultGridElementComponent', () => { expect(fixture.debugElement.query(By.css('ds-collection-search-result-grid-element'))).toBeDefined(); }); - it('should only show the description if "short description" metadata is present',() => { + it('should only show the description if "short description" metadata is present', () => { const descriptionText = expect(fixture.debugElement.query(By.css('p.card-text'))); if (mockCollection.shortDescription.length > 0) { expect(descriptionText).toBeDefined(); - }else { + } else { expect(descriptionText).not.toBeDefined(); } }); diff --git a/src/app/shared/object-grid/search-result-grid-element/community-search-result/community-search-result-grid-element.component.spec.ts b/src/app/shared/object-grid/search-result-grid-element/community-search-result/community-search-result-grid-element.component.spec.ts index dd9e16a120..6d70d08901 100644 --- a/src/app/shared/object-grid/search-result-grid-element/community-search-result/community-search-result-grid-element.component.spec.ts +++ b/src/app/shared/object-grid/search-result-grid-element/community-search-result/community-search-result-grid-element.component.spec.ts @@ -7,6 +7,8 @@ import { NO_ERRORS_SCHEMA } from '@angular/core'; import { By } from '@angular/platform-browser'; import { TruncatePipe } from '../../../utils/truncate.pipe'; import { Community } from '../../../../core/shared/community.model'; +import { TruncatableService } from '../../../truncatable/truncatable.service'; + let fixture: ComponentFixture; const queryParam = 'test query'; @@ -17,6 +19,10 @@ const activatedRouteStub = { scope: scopeParam }) }; +const truncatableServiceStub: any = { + isCollapsed: (id: number) => Observable.of(true), +}; + const mockCommunity: Community = Object.assign(new Community(), { metadata: [ { @@ -27,13 +33,14 @@ const mockCommunity: Community = Object.assign(new Community(), { }); -const createdGridElementComponent: CommunitySearchResultGridElementComponent = new CommunitySearchResultGridElementComponent(mockCommunity); +const createdGridElementComponent: CommunitySearchResultGridElementComponent = new CommunitySearchResultGridElementComponent(mockCommunity, truncatableServiceStub as TruncatableService); describe('CommunitySearchResultGridElementComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ declarations: [ CommunitySearchResultGridElementComponent, TruncatePipe ], providers: [ + { provide: TruncatableService, useValue: truncatableServiceStub }, { provide: ActivatedRoute, useValue: activatedRouteStub }, { provide: Router, useClass: RouterStub }, { provide: 'objectElementProvider', useValue: (createdGridElementComponent) } diff --git a/src/app/shared/object-grid/search-result-grid-element/item-search-result/item-search-result-grid-element.component.html b/src/app/shared/object-grid/search-result-grid-element/item-search-result/item-search-result-grid-element.component.html index e35e06ec03..352ba4881d 100644 --- a/src/app/shared/object-grid/search-result-grid-element/item-search-result/item-search-result-grid-element.component.html +++ b/src/app/shared/object-grid/search-result-grid-element/item-search-result/item-search-result-grid-element.component.html @@ -7,21 +7,23 @@
-

+ +

+

- + {{dso.findMetadata("dc.date.issued")}} ,

- -

-

-
+

+ + + +

View diff --git a/src/app/shared/object-grid/search-result-grid-element/item-search-result/item-search-result-grid-element.component.spec.ts b/src/app/shared/object-grid/search-result-grid-element/item-search-result/item-search-result-grid-element.component.spec.ts index 1a2ca908e2..172888e98c 100644 --- a/src/app/shared/object-grid/search-result-grid-element/item-search-result/item-search-result-grid-element.component.spec.ts +++ b/src/app/shared/object-grid/search-result-grid-element/item-search-result/item-search-result-grid-element.component.spec.ts @@ -7,6 +7,7 @@ import { NO_ERRORS_SCHEMA } from '@angular/core'; import { By } from '@angular/platform-browser'; import { TruncatePipe } from '../../../utils/truncate.pipe'; import { Item } from '../../../../core/shared/item.model'; +import { TruncatableService } from '../../../truncatable/truncatable.service'; let itemSearchResultGridElementComponent: ItemSearchResultGridElementComponent; let fixture: ComponentFixture; @@ -18,6 +19,11 @@ const activatedRouteStub = { scope: scopeParam }) }; + +const truncatableServiceStub: any = { + isCollapsed: (id: number) => Observable.of(true), +}; + const mockItem: Item = Object.assign(new Item(), { metadata: [ { @@ -31,48 +37,48 @@ const mockItem: Item = Object.assign(new Item(), { value: '1650-06-26' }] }); -const createdGridElementComponent:ItemSearchResultGridElementComponent= new ItemSearchResultGridElementComponent(mockItem); +const createdGridElementComponent: ItemSearchResultGridElementComponent = new ItemSearchResultGridElementComponent(mockItem, truncatableServiceStub as TruncatableService); describe('ItemSearchResultGridElementComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ ItemSearchResultGridElementComponent, TruncatePipe ], + declarations: [ItemSearchResultGridElementComponent, TruncatePipe], providers: [ + { provide: TruncatableService, useValue: truncatableServiceStub }, { provide: ActivatedRoute, useValue: activatedRouteStub }, { provide: Router, useClass: RouterStub }, { provide: 'objectElementProvider', useValue: (createdGridElementComponent) } ], - schemas: [ NO_ERRORS_SCHEMA ] + schemas: [NO_ERRORS_SCHEMA] }).compileComponents(); // compile template and css })); beforeEach(async(() => { fixture = TestBed.createComponent(ItemSearchResultGridElementComponent); itemSearchResultGridElementComponent = fixture.componentInstance; - })); - it('should show the item result cards in the grid element',() => { + it('should show the item result cards in the grid element', () => { expect(fixture.debugElement.query(By.css('ds-item-search-result-grid-element'))).toBeDefined(); }); - it('should only show the author span if the author metadata is present',() => { + it('should only show the author span if the author metadata is present', () => { const itemAuthorField = expect(fixture.debugElement.query(By.css('p.item-authors'))); if (mockItem.filterMetadata(['dc.contributor.author', 'dc.creator', 'dc.contributor.*']).length > 0) { expect(itemAuthorField).toBeDefined(); - }else { + } else { expect(itemAuthorField).not.toBeDefined(); } }); - it('should only show the date span if the issuedate is present',() => { + it('should only show the date span if the issuedate is present', () => { const dateField = expect(fixture.debugElement.query(By.css('span.item-list-date'))); if (mockItem.findMetadata('dc.date.issued').length > 0) { expect(dateField).toBeDefined(); - }else { + } else { expect(dateField).not.toBeDefined(); } }); diff --git a/src/app/shared/object-list/collection-list-element/collection-list-element.component.spec.ts b/src/app/shared/object-list/collection-list-element/collection-list-element.component.spec.ts new file mode 100644 index 0000000000..0f1b7c4444 --- /dev/null +++ b/src/app/shared/object-list/collection-list-element/collection-list-element.component.spec.ts @@ -0,0 +1,59 @@ +import { CollectionListElementComponent } from './collection-list-element.component'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { Observable } from 'rxjs/Observable'; +import { ActivatedRoute, Router } from '@angular/router'; +import { RouterStub } from '../../testing/router-stub'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { By } from '@angular/platform-browser'; +import { Collection } from '../../../core/shared/collection.model'; +let fixture: ComponentFixture; +const queryParam = 'test query'; +const scopeParam = '7669c72a-3f2a-451f-a3b9-9210e7a4c02f'; +const activatedRouteStub = { + queryParams: Observable.of({ + query: queryParam, + scope: scopeParam + }) +}; +const mockCollection: Collection = Object.assign(new Collection(), { + metadata: [ + { + key: 'dc.description.abstract', + language: 'en_US', + value: 'Short description' + }] +}); +const createdListElementComponent:CollectionListElementComponent= new CollectionListElementComponent(mockCollection); + +describe('CollectionListElementComponent', () => { + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ CollectionListElementComponent ], + providers: [ + { provide: ActivatedRoute, useValue: activatedRouteStub }, + { provide: Router, useClass: RouterStub }, + { provide: 'objectElementProvider', useValue: (createdListElementComponent)} + ], + + schemas: [ NO_ERRORS_SCHEMA ] + }).compileComponents(); // compile template and css + })); + + beforeEach(async(() => { + fixture = TestBed.createComponent(CollectionListElementComponent); + })); + + it('should show the collection cards in the list element',() => { + expect(fixture.debugElement.query(By.css('ds-collection-list-element'))).toBeDefined(); + }); + + it('should only show the description if "short description" metadata is present',() => { + const descriptionText = expect(fixture.debugElement.query(By.css('p.card-text'))); + + if (mockCollection.shortDescription.length > 0) { + expect(descriptionText).toBeDefined(); + }else { + expect(descriptionText).not.toBeDefined(); + } + }); +}) diff --git a/src/app/shared/object-list/community-list-element/community-list-element.component.spec.ts b/src/app/shared/object-list/community-list-element/community-list-element.component.spec.ts new file mode 100644 index 0000000000..ff5a7fa441 --- /dev/null +++ b/src/app/shared/object-list/community-list-element/community-list-element.component.spec.ts @@ -0,0 +1,65 @@ +import { CommunityListElementComponent } from './community-list-element.component'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { ActivatedRoute, Router } from '@angular/router'; +import { RouterStub } from '../../testing/router-stub'; +import { Observable } from 'rxjs/Observable'; +import { By } from '@angular/platform-browser'; +import { Community } from '../../../core/shared/community.model'; + +let communityListElementComponent: CommunityListElementComponent; +let fixture: ComponentFixture; +const queryParam = 'test query'; +const scopeParam = '7669c72a-3f2a-451f-a3b9-9210e7a4c02f'; +const activatedRouteStub = { + queryParams: Observable.of({ + query: queryParam, + scope: scopeParam + }) +}; + +const mockCommunity: Community = Object.assign(new Community(), { + metadata: [ + { + key: 'dc.description.abstract', + language: 'en_US', + value: 'Short description' + }] +}); + +const createdListElementComponent:CommunityListElementComponent= new CommunityListElementComponent(mockCommunity); + +describe('CommunityListElementComponent', () => { + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ CommunityListElementComponent ], + providers: [ + { provide: ActivatedRoute, useValue: activatedRouteStub }, + { provide: Router, useClass: RouterStub }, + { provide: 'objectElementProvider', useValue: (createdListElementComponent)} + ], + + schemas: [ NO_ERRORS_SCHEMA ] + }).compileComponents(); // compile template and css + })); + + beforeEach(async(() => { + fixture = TestBed.createComponent(CommunityListElementComponent); + communityListElementComponent = fixture.componentInstance; + + })); + + it('should show the community cards in the list element',() => { + expect(fixture.debugElement.query(By.css('ds-community-list-element'))).toBeDefined(); + }) + + it('should only show the description if "short description" metadata is present',() => { + const descriptionText = expect(fixture.debugElement.query(By.css('p.card-text'))); + + if (mockCommunity.shortDescription.length > 0) { + expect(descriptionText).toBeDefined(); + }else { + expect(descriptionText).not.toBeDefined(); + } + }); +}); diff --git a/src/app/shared/object-list/item-list-element/item-list-element.component.spec.ts b/src/app/shared/object-list/item-list-element/item-list-element.component.spec.ts new file mode 100644 index 0000000000..3383ed8454 --- /dev/null +++ b/src/app/shared/object-list/item-list-element/item-list-element.component.spec.ts @@ -0,0 +1,67 @@ +import { ItemListElementComponent } from './item-list-element.component'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { Observable } from 'rxjs/Observable'; +import { ActivatedRoute, Router } from '@angular/router'; +import { RouterStub } from '../../testing/router-stub'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { By } from '@angular/platform-browser'; +import { TruncatePipe } from '../../utils/truncate.pipe'; +import { Item } from '../../../core/shared/item.model'; + +let itemListElementComponent: ItemListElementComponent; +let fixture: ComponentFixture; +const queryParam = 'test query'; +const scopeParam = '7669c72a-3f2a-451f-a3b9-9210e7a4c02f'; +const activatedRouteStub = { + queryParams: Observable.of({ + query: queryParam, + scope: scopeParam + }) +}; +/* tslint:disable:no-shadowed-variable */ +const mockItem: Item = Object.assign(new Item(), { + metadata: [ + { + key: 'dc.contributor.author', + language: 'en_US', + value: 'Smith, Donald' + }] +}); + +const createdListElementComponent:ItemListElementComponent= new ItemListElementComponent(mockItem); + +describe('ItemListElementComponent', () => { + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ ItemListElementComponent , TruncatePipe], + providers: [ + { provide: ActivatedRoute, useValue: activatedRouteStub }, + { provide: Router, useClass: RouterStub }, + { provide: 'objectElementProvider', useValue: {createdListElementComponent}} + ], + + schemas: [ NO_ERRORS_SCHEMA ] + }).compileComponents(); // compile template and css + })); + + beforeEach(async(() => { + fixture = TestBed.createComponent(ItemListElementComponent); + itemListElementComponent = fixture.componentInstance; + + })); + + it('should show the item cards in the list element',() => { + expect(fixture.debugElement.query(By.css('ds-item-list-element'))).toBeDefined() + }); + + it('should only show the author span if the author metadata is present',() => { + const itemAuthorField = expect(fixture.debugElement.query(By.css('p.item-authors'))); + + if (mockItem.filterMetadata(['dc.contributor.author', 'dc.creator', 'dc.contributor.*']).length > 0) { + expect(itemAuthorField).toBeDefined(); + }else { + expect(itemAuthorField).toBeDefined(); + } + }); + +}) diff --git a/src/app/shared/object-list/object-list.component.html b/src/app/shared/object-list/object-list.component.html index 6ccac35464..420886668a 100644 --- a/src/app/shared/object-list/object-list.component.html +++ b/src/app/shared/object-list/object-list.component.html @@ -11,7 +11,7 @@ (sortFieldChange)="onSortFieldChange($event)" (paginationChange)="onPaginationChange($event)">
    -
  • +
diff --git a/src/app/shared/object-list/search-result-list-element/collection-search-result/collection-search-result-list-element.component.spec.ts b/src/app/shared/object-list/search-result-list-element/collection-search-result/collection-search-result-list-element.component.spec.ts new file mode 100644 index 0000000000..37756b5916 --- /dev/null +++ b/src/app/shared/object-list/search-result-list-element/collection-search-result/collection-search-result-list-element.component.spec.ts @@ -0,0 +1,68 @@ +import { CollectionSearchResultListElementComponent } from './collection-search-result-list-element.component'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { Observable } from 'rxjs/Observable'; +import { ActivatedRoute, Router } from '@angular/router'; +import { RouterStub } from '../../../testing/router-stub'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { By } from '@angular/platform-browser'; +import { TruncatePipe } from '../../../utils/truncate.pipe'; +import { Collection } from '../../../../core/shared/collection.model'; +import { TruncatableService } from '../../../truncatable/truncatable.service'; + +let fixture: ComponentFixture; +const queryParam = 'test query'; +const scopeParam = '7669c72a-3f2a-451f-a3b9-9210e7a4c02f'; +const activatedRouteStub = { + queryParams: Observable.of({ + query: queryParam, + scope: scopeParam + }) +}; +const truncatableServiceStub: any = { + isCollapsed: (id: number) => Observable.of(true), +}; + +const mockCollection: Collection = Object.assign(new Collection(), { + metadata: [ + { + key: 'dc.description.abstract', + language: 'en_US', + value: 'Short description' + }] + +}); +const createdListElementComponent: CollectionSearchResultListElementComponent = new CollectionSearchResultListElementComponent(mockCollection, truncatableServiceStub as TruncatableService); + +describe('CollectionSearchResultListElementComponent', () => { + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [CollectionSearchResultListElementComponent, TruncatePipe], + providers: [ + { provide: TruncatableService, useValue: truncatableServiceStub }, + { provide: ActivatedRoute, useValue: activatedRouteStub }, + { provide: Router, useClass: RouterStub }, + { provide: 'objectElementProvider', useValue: (createdListElementComponent) } + ], + + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); // compile template and css + })); + + beforeEach(async(() => { + fixture = TestBed.createComponent(CollectionSearchResultListElementComponent); + })); + + it('should show the item result cards in the list element', () => { + expect(fixture.debugElement.query(By.css('ds-collection-search-result-list-element'))).toBeDefined(); + }); + + it('should only show the description if "short description" metadata is present', () => { + const descriptionText = expect(fixture.debugElement.query(By.css('p.card-text'))); + + if (mockCollection.shortDescription.length > 0) { + expect(descriptionText).toBeDefined(); + } else { + expect(descriptionText).not.toBeDefined(); + } + }); +}); diff --git a/src/app/shared/object-list/search-result-list-element/community-search-result/community-search-result-list-element.component.spec.ts b/src/app/shared/object-list/search-result-list-element/community-search-result/community-search-result-list-element.component.spec.ts new file mode 100644 index 0000000000..3e82e824ec --- /dev/null +++ b/src/app/shared/object-list/search-result-list-element/community-search-result/community-search-result-list-element.component.spec.ts @@ -0,0 +1,69 @@ +import { CommunitySearchResultListElementComponent } from './community-search-result-list-element.component'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { Observable } from 'rxjs/Observable'; +import { ActivatedRoute, Router } from '@angular/router'; +import { RouterStub } from '../../../testing/router-stub'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { By } from '@angular/platform-browser'; +import { TruncatePipe } from '../../../utils/truncate.pipe'; +import { Community } from '../../../../core/shared/community.model'; +import { TruncatableService } from '../../../truncatable/truncatable.service'; + +let fixture: ComponentFixture; +const queryParam = 'test query'; +const scopeParam = '7669c72a-3f2a-451f-a3b9-9210e7a4c02f'; +const activatedRouteStub = { + queryParams: Observable.of({ + query: queryParam, + scope: scopeParam + }) +}; +const truncatableServiceStub: any = { + isCollapsed: (id: number) => Observable.of(true), +}; + +const mockCommunity: Community = Object.assign(new Community(), { + metadata: [ + { + key: 'dc.description.abstract', + language: 'en_US', + value: 'Short description' + } ] + +}); + +const createdListElementComponent: CommunitySearchResultListElementComponent = new CommunitySearchResultListElementComponent(mockCommunity, truncatableServiceStub as TruncatableService); + +describe('CommunitySearchResultListElementComponent', () => { + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ CommunitySearchResultListElementComponent, TruncatePipe ], + providers: [ + { provide: TruncatableService, useValue: truncatableServiceStub }, + { provide: ActivatedRoute, useValue: activatedRouteStub }, + { provide: Router, useClass: RouterStub }, + { provide: 'objectElementProvider', useValue: (createdListElementComponent) } + ], + + schemas: [ NO_ERRORS_SCHEMA ] + }).compileComponents(); // compile template and css + })); + + beforeEach(async(() => { + fixture = TestBed.createComponent(CommunitySearchResultListElementComponent); + })); + + it('should show the item result cards in the list element', () => { + expect(fixture.debugElement.query(By.css('ds-community-search-result-list-element'))).toBeDefined(); + }); + + it('should only show the description if "short description" metadata is present',() => { + const descriptionText = expect(fixture.debugElement.query(By.css('p.card-text'))); + + if (mockCommunity.shortDescription.length > 0) { + expect(descriptionText).toBeDefined(); + }else { + expect(descriptionText).not.toBeDefined(); + } + }); +}); diff --git a/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.html b/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.html index 9aa176fb08..d7ef1c673c 100644 --- a/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.html +++ b/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.html @@ -1,9 +1,7 @@ -
-
(
-
-
\ No newline at end of file diff --git a/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.spec.ts b/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.spec.ts new file mode 100644 index 0000000000..c7bf09ea1e --- /dev/null +++ b/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.spec.ts @@ -0,0 +1,86 @@ +import { ItemSearchResultListElementComponent } from './item-search-result-list-element.component'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { Observable } from 'rxjs/Observable'; +import { ActivatedRoute, Router } from '@angular/router'; +import { RouterStub } from '../../../testing/router-stub'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { By } from '@angular/platform-browser'; +import { TruncatePipe } from '../../../utils/truncate.pipe'; +import { Item } from '../../../../core/shared/item.model'; +import { TruncatableService } from '../../../truncatable/truncatable.service'; + +let itemSearchResultListElementComponent: ItemSearchResultListElementComponent; +let fixture: ComponentFixture; +const queryParam = 'test query'; +const scopeParam = '7669c72a-3f2a-451f-a3b9-9210e7a4c02f'; +const activatedRouteStub = { + queryParams: Observable.of({ + query: queryParam, + scope: scopeParam + }) +}; + +const truncatableServiceStub: any = { + isCollapsed: (id: number) => Observable.of(true), +}; + +const mockItem: Item = Object.assign(new Item(), { + metadata: [ + { + key: 'dc.contributor.author', + language: 'en_US', + value: 'Smith, Donald' + }, + { + key: 'dc.date.issued', + language: null, + value: '1650-06-26' + }] +}); +const createdListElementComponent: ItemSearchResultListElementComponent = new ItemSearchResultListElementComponent(mockItem, truncatableServiceStub as TruncatableService); + +describe('ItemSearchResultListElementComponent', () => { + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ItemSearchResultListElementComponent, TruncatePipe], + providers: [ + { provide: TruncatableService, useValue: truncatableServiceStub }, + { provide: ActivatedRoute, useValue: activatedRouteStub }, + { provide: Router, useClass: RouterStub }, + { provide: 'objectElementProvider', useValue: (createdListElementComponent) } + ], + + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); // compile template and css + })); + + beforeEach(async(() => { + fixture = TestBed.createComponent(ItemSearchResultListElementComponent); + itemSearchResultListElementComponent = fixture.componentInstance; + })); + + it('should show the item result cards in the list element', () => { + expect(fixture.debugElement.query(By.css('ds-item-search-result-list-element'))).toBeDefined(); + }); + + it('should only show the author span if the author metadata is present', () => { + const itemAuthorField = expect(fixture.debugElement.query(By.css('p.item-authors'))); + + if (mockItem.filterMetadata(['dc.contributor.author', 'dc.creator', 'dc.contributor.*']).length > 0) { + expect(itemAuthorField).toBeDefined(); + } else { + expect(itemAuthorField).not.toBeDefined(); + } + }); + + it('should only show the date span if the issuedate is present', () => { + const dateField = expect(fixture.debugElement.query(By.css('span.item-list-date'))); + + if (mockItem.findMetadata('dc.date.issued').length > 0) { + expect(dateField).toBeDefined(); + } else { + expect(dateField).not.toBeDefined(); + } + }); + +}); diff --git a/src/app/shared/truncatable/truncatable-part/truncatable-part.component.html b/src/app/shared/truncatable/truncatable-part/truncatable-part.component.html index 60c474395b..964e52603f 100644 --- a/src/app/shared/truncatable/truncatable-part/truncatable-part.component.html +++ b/src/app/shared/truncatable/truncatable-part/truncatable-part.component.html @@ -1,3 +1,5 @@ -
- +
+
+ +
\ No newline at end of file diff --git a/src/app/shared/truncatable/truncatable-part/truncatable-part.component.scss b/src/app/shared/truncatable/truncatable-part/truncatable-part.component.scss index b66eab7005..698f1fb1aa 100644 --- a/src/app/shared/truncatable/truncatable-part/truncatable-part.component.scss +++ b/src/app/shared/truncatable/truncatable-part/truncatable-part.component.scss @@ -3,22 +3,27 @@ @mixin clamp($lines, $size-factor: 1, $line-height: $line-height-base) { $height: $line-height * $font-size-base * $size-factor; - max-height: $lines * $height; - position: relative; - overflow: hidden; - line-height: $line-height; - overflow-wrap: break-word; - &:after { - content: ""; - position: absolute; - padding-right: 15px; - top: ($lines - 1) * $height; - right: 0; - width: 30%; - min-width: 75px; - max-width: 150px; - height: $height; - background: linear-gradient(to right, rgba(255, 255, 255, 0), $body-bg 50%); + &.fixedHeight { + height: $lines * $height; + } + .content { + max-height: $lines * $height; + position: relative; + overflow: hidden; + line-height: $line-height; + overflow-wrap: break-word; + &:after { + content: ""; + position: absolute; + padding-right: 15px; + top: ($lines - 1) * $height; + right: 0; + width: 30%; + min-width: 75px; + max-width: 150px; + height: $height; + background: linear-gradient(to right, rgba(255, 255, 255, 0), $body-bg 70%); + } } } @@ -26,15 +31,18 @@ $h4-factor: strip-unit($h4-font-size); @for $i from 1 through 15 { .clamp-#{$i} { + transition: height 1s; @include clamp($i); &.title { @include clamp($i, 1.25); } &.h4 { @include clamp($i, $h4-factor, $headings-line-height); - } } } +.clamp-none { + overflow: hidden; +} \ No newline at end of file diff --git a/src/app/shared/truncatable/truncatable-part/truncatable-part.component.spec.ts b/src/app/shared/truncatable/truncatable-part/truncatable-part.component.spec.ts new file mode 100644 index 0000000000..4c3af4cd7d --- /dev/null +++ b/src/app/shared/truncatable/truncatable-part/truncatable-part.component.spec.ts @@ -0,0 +1,76 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { Observable } from 'rxjs/Observable'; +import { TruncatablePartComponent } from './truncatable-part.component'; +import { TruncatableService } from '../truncatable.service'; +import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; + +describe('TruncatablePartComponent', () => { + let comp: TruncatablePartComponent; + let fixture: ComponentFixture; + const id1 = '123'; + const id2 = '456'; + + let truncatableService; + const truncatableServiceStub: any = { + isCollapsed: (id: string) => { + if (id === id1) { + return Observable.of(true) + } else { + return Observable.of(false); + } + } + }; + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [NoopAnimationsModule], + declarations: [TruncatablePartComponent], + providers: [ + { provide: TruncatableService, useValue: truncatableServiceStub }, + ], + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(TruncatablePartComponent, { + set: { changeDetection: ChangeDetectionStrategy.Default } + }).compileComponents(); + })); + beforeEach(() => { + fixture = TestBed.createComponent(TruncatablePartComponent); + comp = fixture.componentInstance; // TruncatablePartComponent test instance + fixture.detectChanges(); + truncatableService = (comp as any).filterService; + }); + + describe('When the item is collapsed', () => { + beforeEach(() => { + comp.id = id1; + comp.minLines = 5; + (comp as any).setLines(); + fixture.detectChanges(); + }) + ; + + it('lines should equal minlines', () => { + expect((comp as any).lines).toEqual(comp.minLines.toString()); + }); + }); + + describe('When the item is expanded', () => { + beforeEach(() => { + comp.id = id2; + }) + ; + + it('lines should equal maxlines when maxlines has a value', () => { + comp.maxLines = 5; + (comp as any).setLines(); + fixture.detectChanges(); + expect((comp as any).lines).toEqual(comp.maxLines.toString()); + }); + + it('lines should equal \'none\' when maxlines has no value', () => { + (comp as any).setLines(); + fixture.detectChanges(); + expect((comp as any).lines).toEqual('none'); + }); + }); +}); diff --git a/src/app/shared/truncatable/truncatable-part/truncatable-part.component.ts b/src/app/shared/truncatable/truncatable-part/truncatable-part.component.ts index 2d643f4a33..1392271db7 100644 --- a/src/app/shared/truncatable/truncatable-part/truncatable-part.component.ts +++ b/src/app/shared/truncatable/truncatable-part/truncatable-part.component.ts @@ -1,21 +1,21 @@ -import { - Component, Input, OnDestroy, OnInit, ElementRef, ViewChild -} from '@angular/core'; +import { Component, Input, OnDestroy, OnInit } from '@angular/core'; import { TruncatableService } from '../truncatable.service'; +import { cardExpand } from '../../animations/card-expand'; @Component({ selector: 'ds-truncatable-part', templateUrl: './truncatable-part.component.html', - styleUrls: ['./truncatable-part.component.scss'] + styleUrls: ['./truncatable-part.component.scss'], + animations: [cardExpand] }) export class TruncatablePartComponent implements OnInit, OnDestroy { @Input() minLines: number; @Input() maxLines = -1; - @Input() initialExpand = false; @Input() id: string; @Input() type: string; - private lines: string; + @Input() fixedHeight = false; + lines: string; private sub; public constructor(private service: TruncatableService) { @@ -38,5 +38,4 @@ export class TruncatablePartComponent implements OnInit, OnDestroy { ngOnDestroy(): void { this.sub.unsubscribe(); } - } diff --git a/src/app/shared/truncatable/truncatable.component.spec.ts b/src/app/shared/truncatable/truncatable.component.spec.ts new file mode 100644 index 0000000000..14a499f4e0 --- /dev/null +++ b/src/app/shared/truncatable/truncatable.component.spec.ts @@ -0,0 +1,99 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { Observable } from 'rxjs/Observable'; +import { TruncatableComponent } from './truncatable.component'; +import { TruncatableService } from './truncatable.service'; +import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; + +describe('TruncatableComponent', () => { + let comp: TruncatableComponent; + let fixture: ComponentFixture; + const identifier = '1234567890'; + let truncatableService; + const truncatableServiceStub: any = { + isCollapsed: (id: string) => { + if (id === '1') { + return Observable.of(true) + } else { + return Observable.of(false); + } + }, + expand: (id: string) => { + }, + collapse: (id: string) => { + }, + toggle: (id: string) => { + } + }; + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [NoopAnimationsModule], + declarations: [TruncatableComponent], + providers: [ + { provide: TruncatableService, useValue: truncatableServiceStub }, + ], + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(TruncatableComponent, { + set: { changeDetection: ChangeDetectionStrategy.Default } + }).compileComponents(); + })); + beforeEach(() => { + fixture = TestBed.createComponent(TruncatableComponent); + comp = fixture.componentInstance; // TruncatableComponent test instance + comp.id = identifier; + fixture.detectChanges(); + truncatableService = (comp as any).service; + }); + + describe('When the item is hoverable', () => { + beforeEach(() => { + comp.onHover = true; + fixture.detectChanges(); + }) + ; + + it('should call collapse on the TruncatableService', () => { + spyOn(truncatableService, 'collapse'); + comp.hoverCollapse(); + expect(truncatableService.collapse).toHaveBeenCalledWith(identifier); + }); + + it('should call expand on the TruncatableService', () => { + spyOn(truncatableService, 'expand'); + comp.hoverExpand(); + expect(truncatableService.expand).toHaveBeenCalledWith(identifier); + }); + }); + + describe('When the item is not hoverable', () => { + beforeEach(() => { + comp.onHover = false; + fixture.detectChanges(); + }) + ; + + it('should not call collapse on the TruncatableService', () => { + spyOn(truncatableService, 'collapse'); + comp.hoverCollapse(); + expect(truncatableService.collapse).not.toHaveBeenCalled(); + }); + + it('should not call expand on the TruncatableService', () => { + spyOn(truncatableService, 'expand'); + comp.hoverExpand(); + expect(truncatableService.expand).not.toHaveBeenCalled(); + }); + }); + + describe('When toggle is called', () => { + beforeEach(() => { + spyOn(truncatableService, 'toggle'); + comp.toggle(); + }); + + it('should call toggle on the TruncatableService', () => { + expect(truncatableService.toggle).toHaveBeenCalledWith(identifier); + }); + }); + +}); diff --git a/src/app/shared/truncatable/truncatable.component.ts b/src/app/shared/truncatable/truncatable.component.ts index b7667f6f51..64477389b9 100644 --- a/src/app/shared/truncatable/truncatable.component.ts +++ b/src/app/shared/truncatable/truncatable.component.ts @@ -2,11 +2,14 @@ import { Component, Input } from '@angular/core'; import { TruncatableService } from './truncatable.service'; +import { Observable } from 'rxjs/Observable'; +import { cardExpand } from '../animations/card-expand'; @Component({ selector: 'ds-truncatable', templateUrl: './truncatable.component.html', - styleUrls: ['./truncatable.component.scss'] + styleUrls: ['./truncatable.component.scss'], + }) export class TruncatableComponent { @Input() initialExpand = false; @@ -39,4 +42,5 @@ export class TruncatableComponent { public toggle() { this.service.toggle(this.id); } + } diff --git a/src/app/shared/truncatable/truncatable.reducer.spec.ts b/src/app/shared/truncatable/truncatable.reducer.spec.ts new file mode 100644 index 0000000000..00949d1113 --- /dev/null +++ b/src/app/shared/truncatable/truncatable.reducer.spec.ts @@ -0,0 +1,96 @@ +import * as deepFreeze from 'deep-freeze'; + +import { truncatableReducer } from './truncatable.reducer'; +import { + TruncatableCollapseAction, TruncatableExpandAction, + TruncatableToggleAction +} from './truncatable.actions'; + +const id1 = '123'; +const id2 = '456'; +class NullAction extends TruncatableCollapseAction { + type = null; + constructor() { + super(undefined); + } +} + +describe('truncatableReducer', () => { + + it('should return the current state when no valid actions have been made', () => { + const state = { 123: { collapsed: true, page: 1 } }; + const action = new NullAction(); + const newState = truncatableReducer(state, action); + + expect(newState).toEqual(state); + }); + + it('should start with an empty object', () => { + const state = Object.create({}); + const action = new NullAction(); + const initialState = truncatableReducer(undefined, action); + + // The search filter starts collapsed + expect(initialState).toEqual(state); + }); + + it('should set collapsed to true in response to the COLLAPSE action', () => { + const state = {}; + state[id1] = { collapsed: false}; + const action = new TruncatableCollapseAction(id1); + const newState = truncatableReducer(state, action); + + expect(newState[id1].collapsed).toEqual(true); + }); + + it('should perform the COLLAPSE action without affecting the previous state', () => { + const state = {}; + state[id1] = { collapsed: false}; + deepFreeze([state]); + + const action = new TruncatableCollapseAction(id1); + truncatableReducer(state, action); + + // no expect required, deepFreeze will ensure an exception is thrown if the state + // is mutated, and any uncaught exception will cause the test to fail + }); + + it('should set filterCollapsed to false in response to the EXPAND action', () => { + const state = {}; + state[id1] = { collapsed: true }; + const action = new TruncatableExpandAction(id1); + const newState = truncatableReducer(state, action); + + expect(newState[id1].collapsed).toEqual(false); + }); + + it('should perform the EXPAND action without affecting the previous state', () => { + const state = {}; + state[id1] = { collapsed: true }; + deepFreeze([state]); + + const action = new TruncatableExpandAction(id1); + truncatableReducer(state, action); + }); + + it('should flip the value of filterCollapsed in response to the TOGGLE action', () => { + const state1 = {}; + state1[id1] = { collapsed: true }; + const action = new TruncatableToggleAction(id1); + + const state2 = truncatableReducer(state1, action); + const state3 = truncatableReducer(state2, action); + + expect(state2[id1].collapsed).toEqual(false); + expect(state3[id1].collapsed).toEqual(true); + }); + + it('should perform the TOGGLE action without affecting the previous state', () => { + const state = {}; + state[id2] = { collapsed: true }; + deepFreeze([state]); + + const action = new TruncatableToggleAction(id2); + truncatableReducer(state, action); + }); +}); diff --git a/src/app/shared/truncatable/truncatable.service.spec.ts b/src/app/shared/truncatable/truncatable.service.spec.ts new file mode 100644 index 0000000000..dafa889b87 --- /dev/null +++ b/src/app/shared/truncatable/truncatable.service.spec.ts @@ -0,0 +1,54 @@ +import { Store } from '@ngrx/store'; +import { async, TestBed } from '@angular/core/testing'; +import { Observable } from 'rxjs/Observable'; +import { TruncatableService } from './truncatable.service'; +import { TruncatableCollapseAction, TruncatableExpandAction } from './truncatable.actions'; +import { TruncatablesState } from './truncatable.reducer'; + +describe('TruncatableService', () => { + const id1 = '123'; + const id2 = '456'; + let service: TruncatableService; + const store: Store = jasmine.createSpyObj('store', { + /* tslint:disable:no-empty */ + dispatch: {}, + /* tslint:enable:no-empty */ + select: Observable.of(true) + }); + beforeEach(async(() => { + TestBed.configureTestingModule({ + + providers: [ + { + provide: Store, useValue: store + } + ] + }).compileComponents(); + })); + + beforeEach(() => { + service = new TruncatableService(store); + }); + + describe('when the collapse method is triggered', () => { + beforeEach(() => { + service.collapse(id1); + }); + + it('TruncatableCollapseAction should be dispatched to the store', () => { + expect(store.dispatch).toHaveBeenCalledWith(new TruncatableCollapseAction(id1)); + }); + + }); + + describe('when the expand method is triggered', () => { + beforeEach(() => { + service.expand(id2); + }); + + it('TruncatableExpandAction should be dispatched to the store', () => { + expect(store.dispatch).toHaveBeenCalledWith(new TruncatableExpandAction(id2)); + }); + }); + +});