Skip to content
Snippets Groups Projects
Commit beb5e971 authored by Giuseppe Digilio's avatar Giuseppe Digilio
Browse files

Merge remote-tracking branch 'remotes/origin/master' into authentication

# Conflicts:
#	package.json
#	src/app/app.reducer.ts
#	src/app/shared/shared.module.ts
parents 1726c8af 605c3524
No related branches found
No related tags found
No related merge requests found
Showing
with 388 additions and 227 deletions
sudo: required
dist: trusty
addons:
- chrome: stable
apt:
sources:
- google-chrome
packages:
- google-chrome-stable
language: node_js
......@@ -21,6 +25,8 @@ install:
- travis_retry yarn install
script:
# Use Chromium instead of Chrome.
- export CHROME_BIN=chromium-browser
- yarn run build
- yarn run ci
- cat coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js
......@@ -137,17 +137,32 @@ yarn run clean:dist
Testing
-------
### Unit Test
### Test a Pull Request
Unit tests use Karma. You can find the configuration file at the same level of this README file:`./karma.conf.js` If you are going to use a remote test enviroment you need to edit the './karma.conf.js'. Follow the instructions you will find inside it. To executing tests whenever any file changes you can modify the 'autoWatch' option to 'true' and 'singleRun' option to 'false'. A coverage report is also available at: http://localhost:9876/ after you run:`yarn run coverage`.
If you would like to contribute by testing a Pull Request (PR), here's how to do so. Keep in mind, you **do not need to have a DSpace backend / REST API installed locally to test a PR**. By default, the dspace-angular project points at our demo REST API
To correctly run the tests you need to run the build once with:`yarn run build`.
1. Pull down the branch that the Pull Request was built from. Easy instructions for doing so can be found on the Pull Request itself.
* Next to the "Merge" button, you'll see a link that says "command line instructions".
* Click it, and follow "Step 1" of those instructions to checkout the pull down the PR branch.
2. `yarn run clean` (This resets your local dependencies to ensure you are up-to-date with this PR)
3. `yarn install` (Updates your local dependencies to those in the PR)
4. `yarn start` (Rebuilds the project, and deploys to localhost:3000, by default)
5. At this point, the code from the PR will be deployed to http://localhost:3000. Test it out, and ensure that it does what is described in the PR (or fixes the bug described in the ticket linked to the PR).
Once you have tested the Pull Request, please add a comment and/or approval to the PR to let us know whether you found it to be successful (or not). Thanks!
### Unit Tests
Unit tests use Karma. You can find the configuration file at the same level of this README file:`./karma.conf.js` If you are going to use a remote test enviroment you need to edit the `./karma.conf.js`. Follow the instructions you will find inside it. To executing tests whenever any file changes you can modify the 'autoWatch' option to 'true' and 'singleRun' option to 'false'. A coverage report is also available at: http://localhost:9876/ after you run: `yarn run coverage`.
To correctly run the tests you need to run the build once with: `yarn run build`.
The default browser is Google Chrome.
Place your tests in the same location of the application source code files that they test.
and run:`yarn run test`
and run: `yarn run test`
### E2E test
......@@ -161,15 +176,18 @@ Protractor needs a functional instance of the DSpace interface to run the E2E te
or any command that bring up the DSpace interface.
Place your tests at the following path:`./e2e`
Place your tests at the following path: `./e2e`
and run:`yarn run e2e`
and run: `yarn run e2e`
### Continuous Integration (CI) Test
To run all the tests (e.g.: to run tests with Continuous Integration software) you can execute:`yarn run ci` Keep in mind that this command prerequisites are the sum of unit test and E2E tests.
##Documentation To build the code documentation we use [TYPEDOC](http://typedoc.org). TYPEDOC is a documentation generator for TypeScript projects. It extracts informations from properly formatted comments that can be written within the code files. Follow the instructions [here](http://typedoc.org/guides/doccomments/) to know how to make those comments.
Documentation
--------------
To build the code documentation we use [TYPEDOC](http://typedoc.org). TYPEDOC is a documentation generator for TypeScript projects. It extracts informations from properly formatted comments that can be written within the code files. Follow the instructions [here](http://typedoc.org/guides/doccomments/) to know how to make those comments.
Run:`yarn run docs` to produce the documentation that will be available in the 'doc' folder.
......@@ -322,8 +340,8 @@ Install your library via `yarn add lib-name --save` and import it in your code.
If the library does not include typings, you can install them using yarn:
```bash
yarn add d3 --save
yarn add @types/d3 --save-dev
yarn add d3
yarn add @types/d3 --dev
```
If the library doesn't have typings available at `@types/`, you can still use it by manually adding typings for it:
......@@ -349,14 +367,18 @@ If you're importing a module that uses CommonJS you need to import as
import * as _ from 'lodash';
```
yarn lockfile
Managing Dependencies (via yarn)
-------------
This project makes use of yarn to ensure that the exact same dependency versions are used every time you install it.
This project makes use of [`yarn`](https://yarnpkg.com/en/) to ensure that the exact same dependency versions are used every time you install it.
yarn creates the file [`yarn.lock`](https://yarnpkg.com/en/docs/yarn-lock) to track those versions. That file is updated automatically every time you install a new dependency from the commandline (by using `yarn add some-lib --save` or `yarn add some-lib --save-dev`).
* `yarn` creates a [`yarn.lock`](https://yarnpkg.com/en/docs/yarn-lock) to track those versions. That file is updated automatically by whenever dependencies are added/updated/removed via yarn.
* **Adding new dependencies**: To install/add a new dependency (third party library), use [`yarn add`](https://yarnpkg.com/en/docs/cli/add). For example: `yarn add some-lib`.
* If you are adding a new build tool dependency (to `devDependencies`), use `yarn add some-lib --dev`
* **Upgrading existing dependencies**: To upgrade existing dependencies, you can use [`yarn upgrade`](https://yarnpkg.com/en/docs/cli/upgrade). For example: `yarn upgrade some-lib` or `yarn upgrade some-lib@version`
* **Removing dependencies**: If a dependency is no longer needed, or replaced, use [`yarn remove`](https://yarnpkg.com/en/docs/cli/remove) to remove it.
If you manually add a package or change a version in `package.json` you'll have to update yarn's lock file as well. You can do so by running `yarn upgrade`
As you can see above, using `yarn` commandline tools means that you should never need to modify the `package.json` manually. *We recommend always using `yarn` to keep dependencies updated / in sync.*
Frequently asked questions
--------------------------
......
......@@ -69,26 +69,26 @@
"coverage": "http-server -c-1 -o -p 9875 ./coverage"
},
"dependencies": {
"@angular/animations": "5.2.1",
"@angular/common": "5.2.1",
"@angular/core": "5.2.1",
"@angular/forms": "5.2.1",
"@angular/http": "5.2.1",
"@angular/platform-browser": "5.2.1",
"@angular/platform-browser-dynamic": "5.2.1",
"@angular/platform-server": "5.2.1",
"@angular/router": "5.2.1",
"@angular/animations": "^5.2.5",
"@angular/common": "^5.2.5",
"@angular/core": "^5.2.5",
"@angular/forms": "^5.2.5",
"@angular/http": "^5.2.5",
"@angular/platform-browser": "^5.2.5",
"@angular/platform-browser-dynamic": "^5.2.5",
"@angular/platform-server": "^5.2.5",
"@angular/router": "^5.2.5",
"@angularclass/bootloader": "1.0.1",
"@ng-bootstrap/ng-bootstrap": "1.0.0-beta.9",
"@ngrx/effects": "4.1.1",
"@ngrx/router-store": "4.1.1",
"@ngrx/store": "4.1.1",
"@ng-bootstrap/ng-bootstrap": "^1.0.0",
"@ngrx/effects": "^5.1.0",
"@ngrx/router-store": "^5.0.1",
"@ngrx/store": "^5.1.0",
"@nguniversal/express-engine": "5.0.0-beta.5",
"@ngx-translate/core": "9.1.1",
"@ngx-translate/http-loader": "2.0.1",
"angular-idle-preload": "2.0.4",
"body-parser": "1.18.2",
"bootstrap": "4.0.0-beta",
"bootstrap": "^4.0.0",
"cerialize": "0.1.18",
"compression": "1.7.1",
"cookie-parser": "1.4.3",
......@@ -108,46 +108,46 @@
"pem": "1.12.3",
"reflect-metadata": "0.1.12",
"rxjs": "5.5.6",
"ts-md5": "1.2.3",
"ts-md5": "^1.2.4",
"uuid": "^3.2.1",
"webfontloader": "1.6.28",
"zone.js": "0.8.20"
},
"devDependencies": {
"@angular/compiler": "^5.2.1",
"@angular/compiler-cli": "^5.2.1",
"@ngrx/store-devtools": "4.1.1",
"@ngtools/webpack": "1.9.5",
"@angular/compiler": "^5.2.5",
"@angular/compiler-cli": "^5.2.5",
"@ngrx/store-devtools": "^5.1.0",
"@ngtools/webpack": "^1.10.0",
"@types/cookie-parser": "1.4.1",
"@types/deep-freeze": "0.1.1",
"@types/express": "4.11.0",
"@types/express": "^4.11.1",
"@types/express-serve-static-core": "4.11.1",
"@types/hammerjs": "2.0.35",
"@types/jasmine": "2.8.4",
"@types/jasmine": "^2.8.6",
"@types/js-cookie": "2.1.0",
"@types/memory-cache": "0.2.0",
"@types/mime": "2.0.0",
"@types/node": "^9.3.0",
"@types/node": "^9.4.6",
"@types/serve-static": "1.13.1",
"@types/uuid": "^3.4.3",
"@types/webfontloader": "1.6.29",
"ajv": "6.0.1",
"ajv-keywords": "3.0.0",
"ajv": "^6.1.1",
"ajv-keywords": "^3.1.0",
"angular2-template-loader": "0.6.2",
"autoprefixer": "7.2.5",
"autoprefixer": "^8.0.0",
"awesome-typescript-loader": "3.4.1",
"caniuse-lite": "1.0.30000792",
"caniuse-lite": "^1.0.30000697",
"codelyzer": "^4.1.0",
"compression-webpack-plugin": "1.1.3",
"copy-webpack-plugin": "4.3.1",
"compression-webpack-plugin": "^1.1.6",
"copy-webpack-plugin": "^4.4.1",
"coveralls": "3.0.0",
"css-loader": "0.28.9",
"deep-freeze": "0.0.1",
"exports-loader": "0.6.4",
"exports-loader": "^0.7.0",
"html-webpack-plugin": "2.30.1",
"imports-loader": "0.7.1",
"istanbul-instrumenter-loader": "3.0.0",
"jasmine-core": "2.9.1",
"jasmine-core": "^2.99.1",
"jasmine-marbles": "0.2.0",
"jasmine-spec-reporter": "4.2.1",
"json-loader": "0.5.7",
......@@ -159,31 +159,31 @@
"karma-jasmine": "1.1.1",
"karma-mocha-reporter": "2.2.5",
"karma-phantomjs-launcher": "1.0.4",
"karma-remap-coverage": "0.1.4",
"karma-remap-coverage": "^0.1.5",
"karma-remap-istanbul": "0.6.0",
"karma-sourcemap-loader": "0.3.7",
"karma-webdriver-launcher": "1.0.5",
"karma-webpack": "2.0.9",
"ngrx-store-freeze": "0.2.0",
"node-sass": "4.7.2",
"nodemon": "1.14.11",
"ngrx-store-freeze": "^0.2.1",
"node-sass": "^4.7.2",
"nodemon": "^1.15.0",
"npm-run-all": "4.1.2",
"postcss": "6.0.16",
"postcss": "^6.0.18",
"postcss-apply": "0.8.0",
"postcss-cli": "4.1.1",
"postcss-cli": "^5.0.0",
"postcss-cssnext": "3.1.0",
"postcss-loader": "2.0.10",
"postcss-loader": "^2.1.0",
"postcss-responsive-type": "1.0.0",
"postcss-smart-import": "0.7.6",
"protractor": "5.2.2",
"protractor": "^5.3.0",
"protractor-istanbul-plugin": "2.0.0",
"raw-loader": "0.5.1",
"resolve-url-loader": "2.2.1",
"rimraf": "2.6.2",
"rollup": "0.54.1",
"rollup-plugin-commonjs": "8.2.6",
"rollup": "^0.56.0",
"rollup-plugin-commonjs": "^8.3.0",
"rollup-plugin-node-globals": "1.1.0",
"rollup-plugin-node-resolve": "3.0.2",
"rollup-plugin-node-resolve": "^3.0.3",
"rollup-plugin-uglify": "3.0.0",
"sass-loader": "6.0.6",
"script-ext-html-webpack-plugin": "1.8.8",
......@@ -194,11 +194,11 @@
"ts-helpers": "1.1.2",
"ts-node": "4.1.0",
"tslint": "5.9.1",
"typedoc": "0.9.0",
"typedoc": "^0.9.0",
"typescript": "2.6.2",
"webpack": "^3.10.0",
"webpack-bundle-analyzer": "2.9.2",
"webpack-dev-middleware": "2.0.4",
"webpack": "^3.11.0",
"webpack-bundle-analyzer": "^2.10.0",
"webpack-dev-middleware": "^2.0.5",
"webpack-dev-server": "2.11.1",
"webpack-merge": "4.1.1",
"webpack-node-externals": "1.6.0"
......
<div *ngIf="searchResults?.hasSucceeded" @fadeIn>
<div *ngIf="searchResults?.hasSucceeded && !searchResults?.isLoading" @fadeIn>
<h2 *ngIf="searchResults?.payload ?.length > 0">{{ 'search.results.head' | translate }}</h2>
<ds-viewable-collection
[config]="searchConfig.pagination"
......
......@@ -23,6 +23,7 @@ body {
display: flex;
min-height: 100vh;
flex-direction: column;
width: 100%;
}
.main-content {
......
......@@ -11,7 +11,7 @@ import {
filterReducer,
SearchFiltersState
} from './+search-page/search-filters/search-filter/search-filter.reducer';
import { authReducer, AuthState } from './core/auth/auth.reducers';
import { truncatableReducer, TruncatablesState } from './shared/truncatable/truncatable.reducer';
export interface AppState {
router: fromRouter.RouterReducerState;
......@@ -19,6 +19,7 @@ export interface AppState {
header: HeaderState;
searchSidebar: SearchSidebarState;
searchFilter: SearchFiltersState;
truncatable: TruncatablesState;
}
export const appReducers: ActionReducerMap<AppState> = {
......@@ -26,7 +27,8 @@ export const appReducers: ActionReducerMap<AppState> = {
hostWindow: hostWindowReducer,
header: headerReducer,
searchSidebar: sidebarReducer,
searchFilter: filterReducer
searchFilter: filterReducer,
truncatable: truncatableReducer
};
export const routerStateSelector = (state: AppState) => state.router;
<header>
<nav class="navbar navbar-expand-md navbar-dark bg-dark">
<nav class="navbar navbar-dark bg-primary navbar-expand-md">
<div [ngClass]="{'clearfix': !(isNavBarCollapsed | async)}">
<a class="navbar-brand" routerLink="/home">{{ 'title' | translate }}</a>
</div>
......
import { animate, state, transition, trigger, style } from '@angular/animations';
export const focusShadow = trigger('focusShadow', [
state('focus', style({ 'box-shadow': 'rgba(119, 119, 119, 0.6) 0px 0px 6px' })),
state('blur', style({ 'box-shadow': 'none' })),
transition('focus <=> blur', animate(250))
]);
export const focusBackground = trigger('focusBackground', [
state('focus', style({ 'background-color': 'rgba(119, 119, 119, 0.1)' })),
state('blur', style({ 'background-color': 'transparent' })),
transition('focus <=> blur', animate(250))
]);
import { animate, state, transition, trigger, style } from '@angular/animations';
export const overlay = trigger('overlay', [
state('show', style({ opacity: 0.5 })),
state('hide', style({ opacity: 0 })),
transition('show <=> hide', animate(250))
]);
import { SearchResult } from '../../../../+search-page/search-result.model';
import { Collection } from '../../../../core/shared/collection.model';
import { Collection } from '../../../core/shared/collection.model';
import { SearchResult } from '../../../+search-page/search-result.model';
export class CollectionSearchResult extends SearchResult<Collection> {
}
import { SearchResult } from '../../../../+search-page/search-result.model';
import { Community } from '../../../../core/shared/community.model';
import { SearchResult } from '../../../+search-page/search-result.model';
import { Community } from '../../../core/shared/community.model';
export class CommunitySearchResult extends SearchResult<Community> {
}
import { CollectionGridElementComponent } from './collection-grid-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 { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
import { By } from '@angular/platform-browser';
import { Collection } from '../../../core/shared/collection.model';
let collectionGridElementComponent: CollectionGridElementComponent;
let fixture: ComponentFixture<CollectionGridElementComponent>;
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(), {
const mockCollectionWithAbstract: Collection = Object.assign(new Collection(), {
metadata: [
{
key: 'dc.description.abstract',
......@@ -23,37 +15,56 @@ const mockCollection: Collection = Object.assign(new Collection(), {
value: 'Short description'
}]
});
const createdGridElementComponent:CollectionGridElementComponent= new CollectionGridElementComponent(mockCollection);
const mockCollectionWithoutAbstract: Collection = Object.assign(new Collection(), {
metadata: [
{
key: 'dc.title',
language: 'en_US',
value: 'Test title'
}]
});
describe('CollectionGridElementComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ CollectionGridElementComponent ],
providers: [
{ provide: ActivatedRoute, useValue: activatedRouteStub },
{ provide: Router, useClass: RouterStub },
{ provide: 'objectElementProvider', useValue: (createdGridElementComponent)}
{ provide: 'objectElementProvider', useValue: (mockCollectionWithAbstract)}
],
schemas: [ NO_ERRORS_SCHEMA ]
}).compileComponents(); // compile template and css
}).overrideComponent(CollectionGridElementComponent, {
set: { changeDetection: ChangeDetectionStrategy.Default }
}).compileComponents();
}));
beforeEach(async(() => {
fixture = TestBed.createComponent(CollectionGridElementComponent);
collectionGridElementComponent = fixture.componentInstance;
}));
it('should show the collection cards in the grid element',() => {
expect(fixture.debugElement.query(By.css('ds-collection-grid-element'))).toBeDefined();
describe('When the collection has an abstract', () => {
beforeEach(() => {
collectionGridElementComponent.object = mockCollectionWithAbstract;
fixture.detectChanges();
});
it('should show the description paragraph', () => {
const collectionAbstractField = fixture.debugElement.query(By.css('p.card-text'));
expect(collectionAbstractField).not.toBeNull();
});
});
it('should only show the description if "short description" metadata is present',() => {
const descriptionText = expect(fixture.debugElement.query(By.css('p.card-text')));
describe('When the collection has no abstract', () => {
beforeEach(() => {
collectionGridElementComponent.object = mockCollectionWithoutAbstract;
fixture.detectChanges();
});
if (mockCollection.shortDescription.length > 0) {
expect(descriptionText).toBeDefined();
} else {
expect(descriptionText).not.toBeDefined();
}
it('should not show the description paragraph', () => {
const collectionAbstractField = fixture.debugElement.query(By.css('p.card-text'));
expect(collectionAbstractField).toBeNull();
});
});
})
});
import { CommunityGridElementComponent } from './community-grid-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 { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
import { By } from '@angular/platform-browser';
import { ListableObject } from '../../object-collection/shared/listable-object.model';
import { Community } from '../../../core/shared/community.model';
let communityGridElementComponent: CommunityGridElementComponent;
let fixture: ComponentFixture<CommunityGridElementComponent>;
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(), {
const mockCommunityWithAbstract: Community = Object.assign(new Community(), {
metadata: [
{
key: 'dc.description.abstract',
......@@ -28,39 +16,55 @@ const mockCommunity: Community = Object.assign(new Community(), {
}]
});
const createdGridElementComponent:CommunityGridElementComponent= new CommunityGridElementComponent(mockCommunity);
const mockCommunityWithoutAbstract: Community = Object.assign(new Community(), {
metadata: [
{
key: 'dc.title',
language: 'en_US',
value: 'Test title'
}]
});
describe('CommunityGridElementComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ CommunityGridElementComponent ],
providers: [
{ provide: ActivatedRoute, useValue: activatedRouteStub },
{ provide: Router, useClass: RouterStub },
{ provide: 'objectElementProvider', useValue: (createdGridElementComponent)}
{ provide: 'objectElementProvider', useValue: (mockCommunityWithAbstract)}
],
schemas: [ NO_ERRORS_SCHEMA ]
}).compileComponents(); // compile template and css
}).overrideComponent(CommunityGridElementComponent, {
set: { changeDetection: ChangeDetectionStrategy.Default }
}).compileComponents();
}));
beforeEach(async(() => {
fixture = TestBed.createComponent(CommunityGridElementComponent);
communityGridElementComponent = fixture.componentInstance;
}));
it('should show the community cards in the grid element',() => {
expect(fixture.debugElement.query(By.css('ds-community-grid-element'))).toBeDefined();
})
describe('When the community has an abstract', () => {
beforeEach(() => {
communityGridElementComponent.object = mockCommunityWithAbstract;
fixture.detectChanges();
});
it('should show the description paragraph', () => {
const communityAbstractField = fixture.debugElement.query(By.css('p.card-text'));
expect(communityAbstractField).not.toBeNull();
});
});
it('should only show the description if "short description" metadata is present',() => {
const descriptionText = expect(fixture.debugElement.query(By.css('p.card-text')));
describe('When the community has no abstract', () => {
beforeEach(() => {
communityGridElementComponent.object = mockCommunityWithoutAbstract;
fixture.detectChanges();
});
if (mockCommunity.shortDescription.length > 0) {
expect(descriptionText).toBeDefined();
} else {
expect(descriptionText).not.toBeDefined();
}
it('should not show the description paragraph', () => {
const communityAbstractField = fixture.debugElement.query(By.css('p.card-text'));
expect(communityAbstractField).toBeNull();
});
});
});
......@@ -6,12 +6,11 @@
</a>
<div class="card-body">
<h4 class="card-title">{{object.findMetadata('dc.title')}}</h4>
<p *ngIf="object.filterMetadata(['dc.contributor.author', 'dc.creator', 'dc.contributor.*']);" class="item-authors card-text text-muted">
<p *ngIf="object.filterMetadata(['dc.contributor.author', 'dc.creator', 'dc.contributor.*']).length > 0" class="item-authors card-text text-muted">
<span *ngFor="let authorMd of object.filterMetadata(['dc.contributor.author', 'dc.creator', 'dc.contributor.*']); let last=last;">{{authorMd.value}}
<span *ngIf="!last">; </span>
</span>
<span *ngIf="object.findMetadata('dc.date.issued')">{{object.findMetadata("dc.date.issued")}}</span>
<span *ngIf="object.findMetadata('dc.date.issued')" class="item-date">{{object.findMetadata("dc.date.issued")}}</span>
</p>
<p *ngIf="object.findMetadata('dc.description.abstract')" class="item-abstract card-text">{{object.findMetadata("dc.description.abstract") | dsTruncate:[200] }}</p>
......
import { ItemGridElementComponent } from './item-grid-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 { ChangeDetectionStrategy, 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 { Observable } from 'rxjs/Observable';
let itemGridElementComponent: ItemGridElementComponent;
let fixture: ComponentFixture<ItemGridElementComponent>;
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(), {
const mockItemWithAuthorAndDate: Item = Object.assign(new Item(), {
bitstreams: Observable.of({}),
metadata: [
{
key: 'dc.contributor.author',
language: 'en_US',
value: 'Smith, Donald'
},
{
key: 'dc.date.issued',
language: null,
value: '2015-06-26'
}]
});
const mockItemWithoutAuthorAndDate: Item = Object.assign(new Item(), {
bitstreams: Observable.of({}),
metadata: [
{
key: 'dc.title',
language: 'en_US',
value: 'This is just another title'
},
{
key: 'dc.type',
language: null,
value: 'Article'
}]
});
const createdGridElementComponent:ItemGridElementComponent= new ItemGridElementComponent(mockItem);
describe('ItemGridElementComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ ItemGridElementComponent , TruncatePipe],
providers: [
{ provide: ActivatedRoute, useValue: activatedRouteStub },
{ provide: Router, useClass: RouterStub },
{ provide: 'objectElementProvider', useValue: {createdGridElementComponent}}
{ provide: 'objectElementProvider', useValue: {mockItemWithAuthorAndDate}}
],
schemas: [ NO_ERRORS_SCHEMA ]
}).compileComponents(); // compile template and css
}).overrideComponent(ItemGridElementComponent, {
set: { changeDetection: ChangeDetectionStrategy.Default }
}).compileComponents();
}));
beforeEach(async(() => {
......@@ -50,18 +58,51 @@ describe('ItemGridElementComponent', () => {
}));
it('should show the item cards in the grid element',() => {
expect(fixture.debugElement.query(By.css('ds-item-grid-element'))).toBeDefined()
describe('When the item has an author', () => {
beforeEach(() => {
itemGridElementComponent.object = mockItemWithAuthorAndDate;
fixture.detectChanges();
});
it('should show the author paragraph', () => {
const itemAuthorField = fixture.debugElement.query(By.css('p.item-authors'));
expect(itemAuthorField).not.toBeNull();
});
});
it('should only show the author span if the author metadata is present',() => {
const itemAuthorField = expect(fixture.debugElement.query(By.css('p.item-authors')));
describe('When the item has no author', () => {
beforeEach(() => {
itemGridElementComponent.object = mockItemWithoutAuthorAndDate;
fixture.detectChanges();
});
if (mockItem.filterMetadata(['dc.contributor.author', 'dc.creator', 'dc.contributor.*']).length > 0) {
expect(itemAuthorField).toBeDefined();
} else {
expect(itemAuthorField).toBeDefined();
}
it('should not show the author paragraph', () => {
const itemAuthorField = fixture.debugElement.query(By.css('p.item-authors'));
expect(itemAuthorField).toBeNull();
});
});
})
describe('When the item has an issuedate', () => {
beforeEach(() => {
itemGridElementComponent.object = mockItemWithAuthorAndDate;
fixture.detectChanges();
});
it('should show the issuedate span', () => {
const itemAuthorField = fixture.debugElement.query(By.css('span.item-date'));
expect(itemAuthorField).not.toBeNull();
});
});
describe('When the item has no issuedate', () => {
beforeEach(() => {
itemGridElementComponent.object = mockItemWithoutAuthorAndDate;
fixture.detectChanges();
});
it('should not show the issuedate span', () => {
const dateField = fixture.debugElement.query(By.css('span.item-date'));
expect(dateField).toBeNull();
});
});
});
......@@ -10,8 +10,8 @@
(sortDirectionChange)="onSortDirectionChange($event)"
(sortFieldChange)="onSortFieldChange($event)"
(paginationChange)="onPaginationChange($event)">
<div class="row mt-2" *ngIf="objects?.hasSucceeded" @fadeIn>
<div class="col-lg-4 col-sm-6 col-xs-12 "
<div class="card-columns" *ngIf="objects?.hasSucceeded" @fadeIn>
<div
*ngFor="let object of objects?.payload?.page">
<ds-wrapper-grid-element [object]="object"></ds-wrapper-grid-element>
</div>
......
@import '../../../styles/variables';
@import '../../../styles/mixins';
ds-wrapper-grid-element ::ng-deep {
div.thumbnail > img {
height: $card-thumbnail-height;
width: 100%;
}
.card-title {
line-height: $headings-line-height;
height: ($headings-line-height*3) +em;
overflow: hidden;
text-overflow: ellipsis;
div.card {
margin-bottom: $spacer;
}
.item-abstract {
line-height: $line-height-base;
height: ($line-height-base*5)+em;
overflow: hidden;
text-overflow: ellipsis;
}
.card-columns {
@include media-breakpoint-only(lg) {
column-count: 3;
}
.item-authors{
line-height: $line-height-base;
height: ($line-height-base*1.5)+em;
@include media-breakpoint-only(sm) {
column-count: 2;
}
div.card {
margin-bottom: 20px;
@include media-breakpoint-only(xs) {
column-count: 1;
}
}
}
\ No newline at end of file
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';
import { RouterStub } from '../../../testing/router-stub';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { ChangeDetectionStrategy, 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 { Collection } from '../../../../core/shared/collection.model';
import { TruncatableService } from '../../../truncatable/truncatable.service';
import { CollectionSearchResult } from '../../../object-collection/shared/collection-search-result.model';
let collectionSearchResultGridElementComponent: CollectionSearchResultGridElementComponent;
let fixture: ComponentFixture<CollectionSearchResultGridElementComponent>;
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(), {
const mockCollectionWithAbstract: CollectionSearchResult = new CollectionSearchResult();
mockCollectionWithAbstract.hitHighlights = [];
mockCollectionWithAbstract.dspaceObject = Object.assign(new Collection(), {
metadata: [
{
key: 'dc.description.abstract',
language: 'en_US',
value: 'Short description'
} ]
});
const createdGridElementComponent: CollectionSearchResultGridElementComponent = new CollectionSearchResultGridElementComponent(mockCollection);
const mockCollectionWithoutAbstract: CollectionSearchResult = new CollectionSearchResult();
mockCollectionWithoutAbstract.hitHighlights = [];
mockCollectionWithoutAbstract.dspaceObject = Object.assign(new Collection(), {
metadata: [
{
key: 'dc.title',
language: 'en_US',
value: 'Test title'
} ]
});
describe('CollectionSearchResultGridElementComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ CollectionSearchResultGridElementComponent, TruncatePipe ],
providers: [
{ provide: ActivatedRoute, useValue: activatedRouteStub },
{ provide: Router, useClass: RouterStub },
{ provide: 'objectElementProvider', useValue: (createdGridElementComponent) }
{ provide: TruncatableService, useValue: truncatableServiceStub },
{ provide: 'objectElementProvider', useValue: (mockCollectionWithAbstract) }
],
schemas: [ NO_ERRORS_SCHEMA ]
}).compileComponents(); // compile template and css
}).overrideComponent(CollectionSearchResultGridElementComponent, {
set: { changeDetection: ChangeDetectionStrategy.Default }
}).compileComponents();
}));
beforeEach(async(() => {
fixture = TestBed.createComponent(CollectionSearchResultGridElementComponent);
collectionSearchResultGridElementComponent = fixture.componentInstance;
}));
it('should show the item result cards in the grid element', () => {
expect(fixture.debugElement.query(By.css('ds-collection-search-result-grid-element'))).toBeDefined();
describe('When the collection has an abstract', () => {
beforeEach(() => {
collectionSearchResultGridElementComponent.dso = mockCollectionWithAbstract.dspaceObject;
fixture.detectChanges();
});
it('should show the description paragraph', () => {
const collectionAbstractField = fixture.debugElement.query(By.css('p.card-text'));
expect(collectionAbstractField).not.toBeNull();
});
});
it('should only show the description if "short description" metadata is present',() => {
const descriptionText = expect(fixture.debugElement.query(By.css('p.card-text')));
describe('When the collection has no abstract', () => {
beforeEach(() => {
collectionSearchResultGridElementComponent.dso = mockCollectionWithoutAbstract.dspaceObject;
fixture.detectChanges();
});
if (mockCollection.shortDescription.length > 0) {
expect(descriptionText).toBeDefined();
} else {
expect(descriptionText).not.toBeDefined();
}
it('should not show the description paragraph', () => {
const collectionAbstractField = fixture.debugElement.query(By.css('p.card-text'));
expect(collectionAbstractField).toBeNull();
});
});
});
......@@ -2,7 +2,7 @@ import { Component } from '@angular/core';
import { renderElementsFor} from '../../../object-collection/shared/dso-element-decorator';
import { CollectionSearchResult } from './collection-search-result.model';
import { CollectionSearchResult } from '../../../object-collection/shared/collection-search-result.model';
import { SearchResultGridElementComponent } from '../search-result-grid-element.component';
import { Collection } from '../../../../core/shared/collection.model';
import { ViewMode } from '../../../../+search-page/search-options.model';
......
import { CommunitySearchResultGridElementComponent } from './community-search-result-grid-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 { ChangeDetectionStrategy, 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';
import { CommunitySearchResult } from '../../../object-collection/shared/community-search-result.model';
let communitySearchResultGridElementComponent: CommunitySearchResultGridElementComponent;
let fixture: ComponentFixture<CommunitySearchResultGridElementComponent>;
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(), {
const mockCommunityWithAbstract: CommunitySearchResult = new CommunitySearchResult();
mockCommunityWithAbstract.hitHighlights = [];
mockCommunityWithAbstract.dspaceObject = Object.assign(new Community(), {
metadata: [
{
key: 'dc.description.abstract',
language: 'en_US',
value: 'Short description'
} ]
});
const createdGridElementComponent: CommunitySearchResultGridElementComponent = new CommunitySearchResultGridElementComponent(mockCommunity);
const mockCommunityWithoutAbstract: CommunitySearchResult = new CommunitySearchResult();
mockCommunityWithoutAbstract.hitHighlights = [];
mockCommunityWithoutAbstract.dspaceObject = Object.assign(new Community(), {
metadata: [
{
key: 'dc.title',
language: 'en_US',
value: 'Test title'
} ]
});
describe('CommunitySearchResultGridElementComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ CommunitySearchResultGridElementComponent, TruncatePipe ],
providers: [
{ provide: ActivatedRoute, useValue: activatedRouteStub },
{ provide: Router, useClass: RouterStub },
{ provide: 'objectElementProvider', useValue: (createdGridElementComponent) }
{ provide: TruncatableService, useValue: truncatableServiceStub },
{ provide: 'objectElementProvider', useValue: (mockCommunityWithAbstract) }
],
schemas: [ NO_ERRORS_SCHEMA ]
}).compileComponents(); // compile template and css
}).overrideComponent(CommunitySearchResultGridElementComponent, {
set: { changeDetection: ChangeDetectionStrategy.Default }
}).compileComponents();
}));
beforeEach(async(() => {
fixture = TestBed.createComponent(CommunitySearchResultGridElementComponent);
communitySearchResultGridElementComponent = fixture.componentInstance;
}));
it('should show the item result cards in the grid element', () => {
expect(fixture.debugElement.query(By.css('ds-community-search-result-grid-element'))).toBeDefined();
describe('When the community has an abstract', () => {
beforeEach(() => {
communitySearchResultGridElementComponent.dso = mockCommunityWithAbstract.dspaceObject;
fixture.detectChanges();
});
it('should show the description paragraph', () => {
const communityAbstractField = fixture.debugElement.query(By.css('p.card-text'));
expect(communityAbstractField).not.toBeNull();
});
});
it('should only show the description if "short description" metadata is present',() => {
const descriptionText = expect(fixture.debugElement.query(By.css('p.card-text')));
describe('When the community has no abstract', () => {
beforeEach(() => {
communitySearchResultGridElementComponent.dso = mockCommunityWithoutAbstract.dspaceObject;
fixture.detectChanges();
});
if (mockCommunity.shortDescription.length > 0) {
expect(descriptionText).toBeDefined();
} else {
expect(descriptionText).not.toBeDefined();
}
it('should not show the description paragraph', () => {
const communityAbstractField = fixture.debugElement.query(By.css('p.card-text'));
expect(communityAbstractField).toBeNull();
});
});
});
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment