mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 10:04:11 +00:00
Merge branch 'master' of https://github.com/DSpace/dspace-angular into all-test
This commit is contained in:
@@ -28,6 +28,10 @@ npm start
|
|||||||
```
|
```
|
||||||
Then go to [http://localhost:3000](http://localhost:3000) in your browser
|
Then go to [http://localhost:3000](http://localhost:3000) in your browser
|
||||||
|
|
||||||
|
NOTE: currently there's not much to see at that URL. We really do need your help. If you're interested in jumping in, and you've made it this far, please look at the [the project board (waffle.io)](https://waffle.io/DSpace/dspace-angular), grab a card, and get to work. Thanks!
|
||||||
|
|
||||||
|
Not sure where to start? watch the training videos linked in the [Introduction to the technology](#introduction-to-the-technology) section below.
|
||||||
|
|
||||||
## Table of Contents
|
## Table of Contents
|
||||||
* [Introduction to the technology](#introduction-to-the-technology)
|
* [Introduction to the technology](#introduction-to-the-technology)
|
||||||
* [Requirements](#requirements)
|
* [Requirements](#requirements)
|
||||||
|
19
e2e/pagenotfound/pagenotfound.e2e-spec.ts
Normal file
19
e2e/pagenotfound/pagenotfound.e2e-spec.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import { ProtractorPage } from './pagenotfound.po';
|
||||||
|
|
||||||
|
describe('protractor PageNotFound', function() {
|
||||||
|
let page: ProtractorPage;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
page = new ProtractorPage();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should contain element ds-pagenotfound when navigating to page that doesnt exist"', () => {
|
||||||
|
page.navigateToNonExistingPage();
|
||||||
|
expect(page.elementTagExists("ds-pagenotfound")).toEqual(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not contain element ds-pagenotfound when navigating to existing page"', () => {
|
||||||
|
page.navigateToExistingPage();
|
||||||
|
expect(page.elementTagExists("ds-pagenotfound")).toEqual(false);
|
||||||
|
});
|
||||||
|
});
|
19
e2e/pagenotfound/pagenotfound.po.ts
Normal file
19
e2e/pagenotfound/pagenotfound.po.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import { browser, element, by } from 'protractor';
|
||||||
|
|
||||||
|
export class ProtractorPage {
|
||||||
|
HOMEPAGE : string = "/home";
|
||||||
|
NONEXISTINGPAGE : string = "/e9019a69-d4f1-4773-b6a3-bd362caa46f2";
|
||||||
|
|
||||||
|
navigateToNonExistingPage() {
|
||||||
|
return browser.get(this.NONEXISTINGPAGE);
|
||||||
|
}
|
||||||
|
navigateToExistingPage() {
|
||||||
|
return browser.get(this.HOMEPAGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
elementTagExists(tag : string) {
|
||||||
|
return element(by.tagName(tag)).isPresent();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
1452
npm-shrinkwrap.json
generated
1452
npm-shrinkwrap.json
generated
File diff suppressed because it is too large
Load Diff
12
package.json
12
package.json
@@ -36,11 +36,12 @@
|
|||||||
"ngc": "ngc -p tsconfig.aot.json",
|
"ngc": "ngc -p tsconfig.aot.json",
|
||||||
"prestart": "npm run build:prod:ngc:json",
|
"prestart": "npm run build:prod:ngc:json",
|
||||||
"server": "node dist/server/build.js",
|
"server": "node dist/server/build.js",
|
||||||
"server:dev": "nodemon --debug dist/server/index.js",
|
"server:dev": "node dist/server/index.js",
|
||||||
|
"server:dev:watch": "nodemon --debug dist/server/index.js",
|
||||||
"start": "npm run server",
|
"start": "npm run server",
|
||||||
"start:dev": "npm run clean:prod && npm run build && npm run server",
|
"start:dev": "npm run clean:prod && npm run build && npm run server:dev",
|
||||||
"watch": "webpack -w & npm run style:watch",
|
"watch": "webpack -w & npm run style:watch",
|
||||||
"watch:dev:server": "concurrently \"npm run server:dev\" \"npm run watch\"",
|
"watch:dev:server": "concurrently \"npm run server:dev:watch\" \"npm run watch\"",
|
||||||
"watch:dev": "npm run clean:prod && npm run build && npm run watch:dev:server",
|
"watch:dev": "npm run clean:prod && npm run build && npm run watch:dev:server",
|
||||||
"watch:prod:server": "concurrently \"npm run server\" \"npm run watch\"",
|
"watch:prod:server": "concurrently \"npm run server\" \"npm run watch\"",
|
||||||
"watch:prod": "npm run build:prod:ngc:json && npm run watch:prod:server",
|
"watch:prod": "npm run build:prod:ngc:json && npm run watch:prod:server",
|
||||||
@@ -62,7 +63,7 @@
|
|||||||
"coverage": "http-server -c-1 -o -p 9875 ./coverage",
|
"coverage": "http-server -c-1 -o -p 9875 ./coverage",
|
||||||
"webdriver:start": "node node_modules/protractor/bin/webdriver-manager start --seleniumPort 4444",
|
"webdriver:start": "node node_modules/protractor/bin/webdriver-manager start --seleniumPort 4444",
|
||||||
"webdriver:update": "node node_modules/protractor/bin/webdriver-manager update --standalone",
|
"webdriver:update": "node node_modules/protractor/bin/webdriver-manager update --standalone",
|
||||||
"rewrap": "npm shrinkwrap --dev"
|
"rewrap": "npm run clean && npm install --no-optional && npm shrinkwrap --dev"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular/common": "2.2.3",
|
"@angular/common": "2.2.3",
|
||||||
@@ -84,6 +85,7 @@
|
|||||||
"@ngrx/router-store": "^1.2.5",
|
"@ngrx/router-store": "^1.2.5",
|
||||||
"@ngrx/store": "^2.2.1",
|
"@ngrx/store": "^2.2.1",
|
||||||
"@ngrx/store-devtools": "^3.2.2",
|
"@ngrx/store-devtools": "^3.2.2",
|
||||||
|
"@types/jsonschema": "0.0.5",
|
||||||
"angular2-express-engine": "2.1.0-rc.1",
|
"angular2-express-engine": "2.1.0-rc.1",
|
||||||
"angular2-platform-node": "2.1.0-rc.1",
|
"angular2-platform-node": "2.1.0-rc.1",
|
||||||
"angular2-universal": "2.1.0-rc.1",
|
"angular2-universal": "2.1.0-rc.1",
|
||||||
@@ -91,11 +93,13 @@
|
|||||||
"autoprefixer": "^6.5.4",
|
"autoprefixer": "^6.5.4",
|
||||||
"body-parser": "1.15.2",
|
"body-parser": "1.15.2",
|
||||||
"bootstrap": "4.0.0-alpha.5",
|
"bootstrap": "4.0.0-alpha.5",
|
||||||
|
"cerialize": "^0.1.13",
|
||||||
"compression": "1.6.2",
|
"compression": "1.6.2",
|
||||||
"express": "4.14.0",
|
"express": "4.14.0",
|
||||||
"font-awesome": "4.7.0",
|
"font-awesome": "4.7.0",
|
||||||
"http-server": "^0.9.0",
|
"http-server": "^0.9.0",
|
||||||
"js.clone": "0.0.3",
|
"js.clone": "0.0.3",
|
||||||
|
"jsonschema": "^1.1.1",
|
||||||
"methods": "1.1.2",
|
"methods": "1.1.2",
|
||||||
"morgan": "1.7.0",
|
"morgan": "1.7.0",
|
||||||
"ng2-translate": "4.2.0",
|
"ng2-translate": "4.2.0",
|
||||||
|
@@ -1,13 +1,27 @@
|
|||||||
{
|
{
|
||||||
"title": "DSpace",
|
"example": {
|
||||||
|
"with": {
|
||||||
|
"data": "{{greeting}}, {{recipient}}!"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"footer": {
|
||||||
|
"copyright": "copyright © 2002-{{ year }}",
|
||||||
|
"link.dspace": "DSpace software",
|
||||||
|
"link.duraspace": "DuraSpace"
|
||||||
|
},
|
||||||
|
|
||||||
"nav": {
|
"nav": {
|
||||||
"home": "Home"
|
"home": "Home"
|
||||||
},
|
},
|
||||||
|
|
||||||
"example": {
|
"title": "DSpace",
|
||||||
"with": {
|
|
||||||
"data": "{{greeting}}, {{recipient}}!"
|
"404": {
|
||||||
|
"help": "We can't find the page you're looking for. The page may have been moved or deleted. You can use the button below to get back to the home page. ",
|
||||||
|
"page-not-found": "page not found",
|
||||||
|
"link": {
|
||||||
|
"home-page": "Take me to the home page"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,10 +1,12 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { RouterModule } from '@angular/router';
|
import { RouterModule } from '@angular/router';
|
||||||
|
|
||||||
|
import { PageNotFoundComponent } from './pagenotfound/pagenotfound.component';
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
RouterModule.forChild([
|
RouterModule.forChild([
|
||||||
{ path: '', redirectTo: '/home', pathMatch: 'full' }
|
{ path: '', redirectTo: '/home', pathMatch: 'full' },
|
||||||
|
{ path: '**', pathMatch: 'full', component: PageNotFoundComponent},
|
||||||
])
|
])
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
@@ -1,11 +1,17 @@
|
|||||||
<ds-header></ds-header>
|
<div class="outer-wrapper">
|
||||||
|
<div class="inner-wrapper">
|
||||||
|
<ds-header></ds-header>
|
||||||
|
|
||||||
<div class="container-fluid">
|
<main class="main-content">
|
||||||
<main>
|
<div class="container-fluid">
|
||||||
<p>{{ 'example.with.data' | translate:data }}</p>
|
<p>{{ 'example.with.data' | translate:data }}</p>
|
||||||
<p>{{ example }}</p>
|
<p>{{ example }}</p>
|
||||||
<h2 *ngIf="!env" style="color:green">development</h2>
|
<h2 *ngIf="!env" style="color:green">development</h2>
|
||||||
<h2 *ngIf="env" style="color:red">production</h2>
|
<h2 *ngIf="env" style="color:red">production</h2>
|
||||||
<router-outlet></router-outlet>
|
<router-outlet></router-outlet>
|
||||||
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
<ds-footer></ds-footer>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1 +1,18 @@
|
|||||||
|
// Sticky Footer
|
||||||
|
|
||||||
|
.outer-wrapper {
|
||||||
|
display: flex;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inner-wrapper {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
flex-flow: column nowrap;
|
||||||
|
display: flex;
|
||||||
|
min-height: 100vh;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-content {
|
||||||
|
flex: 1 0 auto;
|
||||||
|
}
|
||||||
|
@@ -1,4 +1,6 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
|
|
||||||
|
import { CoreModule } from './core/core.module';
|
||||||
import { HomeModule } from './home/home.module';
|
import { HomeModule } from './home/home.module';
|
||||||
|
|
||||||
import { SharedModule } from './shared/shared.module';
|
import { SharedModule } from './shared/shared.module';
|
||||||
@@ -6,6 +8,7 @@ import { SharedModule } from './shared/shared.module';
|
|||||||
import { AppRoutingModule } from './app-routing.module';
|
import { AppRoutingModule } from './app-routing.module';
|
||||||
import { AppComponent } from './app.component';
|
import { AppComponent } from './app.component';
|
||||||
import { HeaderComponent } from './header/header.component';
|
import { HeaderComponent } from './header/header.component';
|
||||||
|
import { PageNotFoundComponent } from './pagenotfound/pagenotfound.component';
|
||||||
|
|
||||||
import { StoreModule } from "@ngrx/store";
|
import { StoreModule } from "@ngrx/store";
|
||||||
import { RouterStoreModule } from "@ngrx/router-store";
|
import { RouterStoreModule } from "@ngrx/router-store";
|
||||||
@@ -17,11 +20,13 @@ import { effects } from './app.effects';
|
|||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
AppComponent,
|
AppComponent,
|
||||||
HeaderComponent
|
HeaderComponent,
|
||||||
|
PageNotFoundComponent
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
SharedModule,
|
SharedModule,
|
||||||
HomeModule,
|
HomeModule,
|
||||||
|
CoreModule.forRoot(),
|
||||||
AppRoutingModule,
|
AppRoutingModule,
|
||||||
/**
|
/**
|
||||||
* StoreModule.provideStore is imported once in the root module, accepting a reducer
|
* StoreModule.provideStore is imported once in the root module, accepting a reducer
|
||||||
|
45
src/app/core/core.module.ts
Normal file
45
src/app/core/core.module.ts
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import { NgModule, Optional, SkipSelf, ModuleWithProviders } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { SharedModule } from "../shared/shared.module";
|
||||||
|
import { isNotEmpty } from "../shared/empty.util";
|
||||||
|
import { FooterComponent } from "./footer/footer.component";
|
||||||
|
|
||||||
|
const IMPORTS = [
|
||||||
|
CommonModule,
|
||||||
|
SharedModule
|
||||||
|
];
|
||||||
|
|
||||||
|
const DECLARATIONS = [
|
||||||
|
FooterComponent
|
||||||
|
];
|
||||||
|
|
||||||
|
const EXPORTS = [
|
||||||
|
FooterComponent
|
||||||
|
];
|
||||||
|
|
||||||
|
const PROVIDERS = [
|
||||||
|
];
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [ ...IMPORTS ],
|
||||||
|
declarations: [...DECLARATIONS],
|
||||||
|
exports: [...EXPORTS],
|
||||||
|
providers: [...PROVIDERS]
|
||||||
|
})
|
||||||
|
export class CoreModule {
|
||||||
|
constructor (@Optional() @SkipSelf() parentModule: CoreModule) {
|
||||||
|
if (isNotEmpty(parentModule)) {
|
||||||
|
throw new Error(
|
||||||
|
'CoreModule is already loaded. Import it in the AppModule only');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static forRoot(): ModuleWithProviders {
|
||||||
|
return {
|
||||||
|
ngModule: CoreModule,
|
||||||
|
providers: [
|
||||||
|
...PROVIDERS
|
||||||
|
]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,4 @@
|
|||||||
|
export interface DSpaceRESTV2Response {
|
||||||
|
_embedded?: any;
|
||||||
|
_links?: any;
|
||||||
|
}
|
93
src/app/core/dspace-rest-v2/dspace-rest-v2.schema.json
Normal file
93
src/app/core/dspace-rest-v2/dspace-rest-v2.schema.json
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
{
|
||||||
|
"title": "DSpace REST v2 json-schema",
|
||||||
|
"description": "Based on http://hyperschema.org/mediatypes/hal",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"_links": {
|
||||||
|
"$ref": "#/definitions/links"
|
||||||
|
},
|
||||||
|
"_embedded": {
|
||||||
|
"$ref": "#/definitions/embedded"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"definitions": {
|
||||||
|
"links": {
|
||||||
|
"title": "HAL Links",
|
||||||
|
"description": "Object of links with the rels as the keys",
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": {
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/linkObject"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/linkArray"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"linkArray": {
|
||||||
|
"title": "HAL Link Array",
|
||||||
|
"description": "An array of linkObjects of the same link relation",
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/linkObject"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"linkObject": {
|
||||||
|
"title": "HAL Link Object",
|
||||||
|
"description": "An object with link information",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"$ref": "http://hyperschema.org/core/base#/definitions/name"
|
||||||
|
},
|
||||||
|
"href": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"$ref": "http://hyperschema.org/core/link#/definitions/href"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "http://hyperschema.org/core/link#/definitions/hrefTemplated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"templated": {
|
||||||
|
"$ref": "http://hyperschema.org/core/link#/definitions/isTemplated"
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"$ref": "http://hyperschema.org/core/base#/definitions/mediaType"
|
||||||
|
},
|
||||||
|
"deprecation": {
|
||||||
|
"$ref": "http://hyperschema.org/core/link#/definitions/isDeprecated"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"href"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"embedded": {
|
||||||
|
"title": "HAL Embedded Resource",
|
||||||
|
"description": "An embedded HAL resource",
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": {
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/embeddedArray"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"embeddedArray": {
|
||||||
|
"title": "HAL Embedded Array",
|
||||||
|
"description": "An array of embedded resources with the same link relation",
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
201
src/app/core/dspace-rest-v2/dspace-rest-v2.serializer.spec.ts
Normal file
201
src/app/core/dspace-rest-v2/dspace-rest-v2.serializer.spec.ts
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
import { DSpaceRESTv2Serializer } from "./dspace-rest-v2.serializer";
|
||||||
|
import { autoserialize, autoserializeAs } from "cerialize";
|
||||||
|
|
||||||
|
class TestModel {
|
||||||
|
@autoserialize
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
@autoserialize
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
@autoserializeAs(TestModel)
|
||||||
|
parents?: Array<TestModel>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const testModels = [
|
||||||
|
{
|
||||||
|
"id": "d4466d54-d73b-4d8f-b73f-c702020baa14",
|
||||||
|
"name": "Model 1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "752a1250-949a-46ad-9bea-fbc45f0b656d",
|
||||||
|
"name": "Model 2",
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const testResponses = [
|
||||||
|
{
|
||||||
|
"_links": {
|
||||||
|
"self": "/testmodels/9e32a2e2-6b91-4236-a361-995ccdc14c60",
|
||||||
|
"parents": [
|
||||||
|
{ "href": "/testmodels/21539b1d-9ef1-4eda-9c77-49565b5bfb78" },
|
||||||
|
{ "href": "/testmodels/be8325f7-243b-49f4-8a4b-df2b793ff3b5" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"id": "9e32a2e2-6b91-4236-a361-995ccdc14c60",
|
||||||
|
"type": "testModels",
|
||||||
|
"name": "A Test Model"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_links": {
|
||||||
|
"self": "/testmodels/598ce822-c357-46f3-ab70-63724d02d6ad",
|
||||||
|
"parents": [
|
||||||
|
{ "href": "/testmodels/be8325f7-243b-49f4-8a4b-df2b793ff3b5" },
|
||||||
|
{ "href": "/testmodels/21539b1d-9ef1-4eda-9c77-49565b5bfb78" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"id": "598ce822-c357-46f3-ab70-63724d02d6ad",
|
||||||
|
"type": "testModels",
|
||||||
|
"name": "Another Test Model"
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const parentHrefRegex = /^\/testmodels\/(.+)$/g;
|
||||||
|
|
||||||
|
|
||||||
|
describe("DSpaceRESTv2Serializer", () => {
|
||||||
|
|
||||||
|
describe("serialize", () => {
|
||||||
|
|
||||||
|
it("should turn a model in to a valid document", () => {
|
||||||
|
const serializer = new DSpaceRESTv2Serializer(TestModel);
|
||||||
|
const doc = serializer.serialize(testModels[0]);
|
||||||
|
expect(testModels[0].id).toBe(doc._embedded.id);
|
||||||
|
expect(testModels[0].name).toBe(doc._embedded.name);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("serializeArray", () => {
|
||||||
|
|
||||||
|
it("should turn an array of models in to a valid document", () => {
|
||||||
|
const serializer = new DSpaceRESTv2Serializer(TestModel);
|
||||||
|
const doc = serializer.serializeArray(testModels);
|
||||||
|
|
||||||
|
expect(testModels[0].id).toBe(doc._embedded[0].id);
|
||||||
|
expect(testModels[0].name).toBe(doc._embedded[0].name);
|
||||||
|
expect(testModels[1].id).toBe(doc._embedded[1].id);
|
||||||
|
expect(testModels[1].name).toBe(doc._embedded[1].name);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("deserialize", () => {
|
||||||
|
|
||||||
|
it("should turn a valid document describing a single entity in to a valid model", () => {
|
||||||
|
const serializer = new DSpaceRESTv2Serializer(TestModel);
|
||||||
|
const doc = {
|
||||||
|
"_embedded": testResponses[0],
|
||||||
|
};
|
||||||
|
|
||||||
|
const model = serializer.deserialize(doc);
|
||||||
|
|
||||||
|
expect(model.id).toBe(doc._embedded.id);
|
||||||
|
expect(model.name).toBe(doc._embedded.name);
|
||||||
|
});
|
||||||
|
|
||||||
|
//TODO cant implement/test this yet - depends on how relationships
|
||||||
|
// will be handled in the rest api
|
||||||
|
// it("should retain relationship information", () => {
|
||||||
|
// const serializer = new DSpaceRESTv2Serializer(TestModel);
|
||||||
|
// const doc = {
|
||||||
|
// "_embedded": testResponses[0],
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// const model = serializer.deserialize(doc);
|
||||||
|
//
|
||||||
|
// console.log(model);
|
||||||
|
//
|
||||||
|
// const modelParentIds = model.parents.map(parent => parent.id).sort();
|
||||||
|
// const responseParentIds = doc._embedded._links.parents
|
||||||
|
// .map(parent => parent.href)
|
||||||
|
// .map(href => href.replace(parentHrefRegex, '$1'))
|
||||||
|
// .sort();
|
||||||
|
//
|
||||||
|
// expect(modelParentIds).toEqual(responseParentIds);
|
||||||
|
// });
|
||||||
|
|
||||||
|
// TODO enable once validation is enabled in the serializer
|
||||||
|
// it("should throw an error when dealing with an invalid document", () => {
|
||||||
|
// const serializer = new DSpaceRESTv2Serializer(TestModel);
|
||||||
|
// const doc = testResponses[0];
|
||||||
|
//
|
||||||
|
// expect(() => {
|
||||||
|
// serializer.deserialize(doc);
|
||||||
|
// }).toThrow();
|
||||||
|
// });
|
||||||
|
|
||||||
|
it("should throw an error when dealing with a document describing an array", () => {
|
||||||
|
const serializer = new DSpaceRESTv2Serializer(TestModel);
|
||||||
|
const doc = {
|
||||||
|
"_embedded": testResponses
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(() => {
|
||||||
|
serializer.deserialize(doc);
|
||||||
|
}).toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("deserializeArray", () => {
|
||||||
|
|
||||||
|
it("should turn a valid document describing a collection of objects in to an array of valid models", () => {
|
||||||
|
const serializer = new DSpaceRESTv2Serializer(TestModel);
|
||||||
|
const doc = {
|
||||||
|
"_embedded": testResponses
|
||||||
|
};
|
||||||
|
|
||||||
|
const models = serializer.deserializeArray(doc);
|
||||||
|
|
||||||
|
expect(models[0].id).toBe(doc._embedded[0].id);
|
||||||
|
expect(models[0].name).toBe(doc._embedded[0].name);
|
||||||
|
expect(models[1].id).toBe(doc._embedded[1].id);
|
||||||
|
expect(models[1].name).toBe(doc._embedded[1].name);
|
||||||
|
});
|
||||||
|
|
||||||
|
//TODO cant implement/test this yet - depends on how relationships
|
||||||
|
// will be handled in the rest api
|
||||||
|
// it("should retain relationship information", () => {
|
||||||
|
// const serializer = new DSpaceRESTv2Serializer(TestModel);
|
||||||
|
// const doc = {
|
||||||
|
// "_embedded": testResponses,
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// const models = serializer.deserializeArray(doc);
|
||||||
|
//
|
||||||
|
// models.forEach((model, i) => {
|
||||||
|
// const modelParentIds = model.parents.map(parent => parent.id).sort();
|
||||||
|
// const responseParentIds = doc._embedded[i]._links.parents
|
||||||
|
// .map(parent => parent.href)
|
||||||
|
// .map(href => href.replace(parentHrefRegex, '$1'))
|
||||||
|
// .sort();
|
||||||
|
//
|
||||||
|
// expect(modelParentIds).toEqual(responseParentIds);
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
|
||||||
|
// TODO enable once validation is enabled in the serializer
|
||||||
|
// it("should throw an error when dealing with an invalid document", () => {
|
||||||
|
// const serializer = new DSpaceRESTv2Serializer(TestModel);
|
||||||
|
// const doc = testResponses[0];
|
||||||
|
//
|
||||||
|
// expect(() => {
|
||||||
|
// serializer.deserializeArray(doc);
|
||||||
|
// }).toThrow();
|
||||||
|
// });
|
||||||
|
|
||||||
|
it("should throw an error when dealing with a document describing a single model", () => {
|
||||||
|
const serializer = new DSpaceRESTv2Serializer(TestModel);
|
||||||
|
const doc = {
|
||||||
|
"_embedded": testResponses[0]
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(() => {
|
||||||
|
serializer.deserializeArray(doc);
|
||||||
|
}).toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
82
src/app/core/dspace-rest-v2/dspace-rest-v2.serializer.ts
Normal file
82
src/app/core/dspace-rest-v2/dspace-rest-v2.serializer.ts
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
import { Serialize, Deserialize } from "cerialize";
|
||||||
|
import { Serializer } from "../serializer";
|
||||||
|
import { DSpaceRESTV2Response } from "./dspace-rest-v2-response.model";
|
||||||
|
import { DSpaceRESTv2Validator } from "./dspace-rest-v2.validator";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ensures we can use 'typeof T' as a type
|
||||||
|
* more details:
|
||||||
|
* https://github.com/Microsoft/TypeScript/issues/204#issuecomment-257722306
|
||||||
|
*/
|
||||||
|
type Constructor<T> = { new (...args: any[]): T } | ((...args: any[]) => T) | Function;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This Serializer turns responses from v2 of DSpace's REST API
|
||||||
|
* to models and vice versa
|
||||||
|
*/
|
||||||
|
export class DSpaceRESTv2Serializer<T> implements Serializer<T> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new DSpaceRESTv2Serializer instance
|
||||||
|
*
|
||||||
|
* @param modelType a class or interface to indicate
|
||||||
|
* the kind of model this serializer should work with
|
||||||
|
*/
|
||||||
|
constructor(private modelType: Constructor<T>) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a model in to the format expected by the backend
|
||||||
|
*
|
||||||
|
* @param model The model to serialize
|
||||||
|
* @returns An object to send to the backend
|
||||||
|
*/
|
||||||
|
serialize(model: T): DSpaceRESTV2Response {
|
||||||
|
return {
|
||||||
|
"_embedded": Serialize(model, this.modelType)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert an array of models in to the format expected by the backend
|
||||||
|
*
|
||||||
|
* @param models The array of models to serialize
|
||||||
|
* @returns An object to send to the backend
|
||||||
|
*/
|
||||||
|
serializeArray(models: Array<T>): DSpaceRESTV2Response {
|
||||||
|
return {
|
||||||
|
"_embedded": Serialize(models, this.modelType)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a response from the backend in to a model.
|
||||||
|
*
|
||||||
|
* @param response An object returned by the backend
|
||||||
|
* @returns a model of type T
|
||||||
|
*/
|
||||||
|
deserialize(response: DSpaceRESTV2Response): T {
|
||||||
|
// TODO enable validation, once rest data stabilizes
|
||||||
|
// new DSpaceRESTv2Validator(response).validate();
|
||||||
|
if (Array.isArray(response._embedded)) {
|
||||||
|
throw new Error('Expected a single model, use deserializeArray() instead');
|
||||||
|
}
|
||||||
|
return <T> Deserialize(response._embedded, this.modelType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a response from the backend in to an array of models
|
||||||
|
*
|
||||||
|
* @param response An object returned by the backend
|
||||||
|
* @returns an array of models of type T
|
||||||
|
*/
|
||||||
|
deserializeArray(response: DSpaceRESTV2Response): Array<T> {
|
||||||
|
//TODO enable validation, once rest data stabilizes
|
||||||
|
// new DSpaceRESTv2Validator(response).validate();
|
||||||
|
if (!Array.isArray(response._embedded)) {
|
||||||
|
throw new Error('Expected an Array, use deserialize() instead');
|
||||||
|
}
|
||||||
|
return <Array<T>> Deserialize(response._embedded, this.modelType);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
34
src/app/core/dspace-rest-v2/dspace-rest-v2.validator.ts
Normal file
34
src/app/core/dspace-rest-v2/dspace-rest-v2.validator.ts
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import * as schema from './dspace-rest-v2.schema.json'
|
||||||
|
import { Validator } from "jsonschema";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies a document is a valid response from
|
||||||
|
* a DSpace REST API v2
|
||||||
|
*/
|
||||||
|
export class DSpaceRESTv2Validator {
|
||||||
|
|
||||||
|
constructor(private document: any) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Throws an exception if this.document isn't a valid response from
|
||||||
|
* a DSpace REST API v2. Succeeds otherwise.
|
||||||
|
*/
|
||||||
|
validate(): void {
|
||||||
|
const validator = new Validator();
|
||||||
|
const result = validator.validate(this.document, schema);
|
||||||
|
if (!result.valid) {
|
||||||
|
if (result.errors && result.errors.length > 0) {
|
||||||
|
const message = result.errors
|
||||||
|
.map((error) => error.message)
|
||||||
|
.join("\n");
|
||||||
|
throw new Error(message);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new Error("JSON API validation failed for an unknown reason");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
9
src/app/core/footer/footer.component.html
Normal file
9
src/app/core/footer/footer.component.html
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<footer class="footer">
|
||||||
|
<div class="container-fluid content-container-fluid">
|
||||||
|
<p>
|
||||||
|
<a href="http://www.dspace.org/">{{ 'footer.link.dspace' | translate}}</a>
|
||||||
|
{{ 'footer.copyright' | translate:{year : dateObj | date:'y'} }}
|
||||||
|
<a href="http://www.duraspace.org/">{{ 'footer.link.duraspace' | translate}}</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</footer>
|
17
src/app/core/footer/footer.component.scss
Normal file
17
src/app/core/footer/footer.component.scss
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
@import '../../../styles/variables.scss';
|
||||||
|
@import '../../../../node_modules/bootstrap/scss/_variables.scss';
|
||||||
|
|
||||||
|
$footer-bg: $gray-lighter;
|
||||||
|
$footer-border: 1px solid darken($footer-bg, 10%);
|
||||||
|
$footer-padding: $spacer * 1.5;
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
background-color: $footer-bg;
|
||||||
|
border-top: $footer-border;
|
||||||
|
text-align:center;
|
||||||
|
padding: $footer-padding;
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
60
src/app/core/footer/footer.component.spec.ts
Normal file
60
src/app/core/footer/footer.component.spec.ts
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
// ... test imports
|
||||||
|
import {
|
||||||
|
async,
|
||||||
|
ComponentFixture,
|
||||||
|
inject,
|
||||||
|
TestBed
|
||||||
|
} from '@angular/core/testing';
|
||||||
|
import {
|
||||||
|
CUSTOM_ELEMENTS_SCHEMA,
|
||||||
|
DebugElement
|
||||||
|
} from "@angular/core";
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
import { TranslateModule, TranslateLoader } from "ng2-translate";
|
||||||
|
import { Store, StoreModule } from "@ngrx/store";
|
||||||
|
|
||||||
|
// Load the implementations that should be tested
|
||||||
|
import { FooterComponent } from './footer.component';
|
||||||
|
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { MockTranslateLoader } from "../../shared/testing/mock-translate-loader";
|
||||||
|
|
||||||
|
let comp: FooterComponent;
|
||||||
|
let fixture: ComponentFixture<FooterComponent>;
|
||||||
|
let de: DebugElement;
|
||||||
|
let el: HTMLElement;
|
||||||
|
|
||||||
|
describe('Footer component', () => {
|
||||||
|
|
||||||
|
// async beforeEach
|
||||||
|
beforeEach(async(() => {
|
||||||
|
return TestBed.configureTestingModule({
|
||||||
|
imports: [CommonModule, StoreModule.provideStore({}), TranslateModule.forRoot({
|
||||||
|
provide: TranslateLoader,
|
||||||
|
useClass: MockTranslateLoader
|
||||||
|
})],
|
||||||
|
declarations: [FooterComponent], // declare the test component
|
||||||
|
providers: [
|
||||||
|
FooterComponent
|
||||||
|
],
|
||||||
|
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
||||||
|
})
|
||||||
|
}));
|
||||||
|
|
||||||
|
// synchronous beforeEach
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(FooterComponent);
|
||||||
|
|
||||||
|
comp = fixture.componentInstance; // component test instance
|
||||||
|
|
||||||
|
// query for the title <p> by CSS element selector
|
||||||
|
de = fixture.debugElement.query(By.css('p'));
|
||||||
|
el = de.nativeElement;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create footer', inject([FooterComponent], (app: FooterComponent) => {
|
||||||
|
// Perform test using fixture and service
|
||||||
|
expect(app).toBeTruthy();
|
||||||
|
}));
|
||||||
|
|
||||||
|
});
|
18
src/app/core/footer/footer.component.ts
Normal file
18
src/app/core/footer/footer.component.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { Component, OnInit } from "@angular/core";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-footer',
|
||||||
|
styleUrls: ['footer.component.css'],
|
||||||
|
templateUrl: 'footer.component.html'
|
||||||
|
})
|
||||||
|
export class FooterComponent implements OnInit {
|
||||||
|
|
||||||
|
dateObj: number = Date.now();
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
38
src/app/core/serializer.ts
Normal file
38
src/app/core/serializer.ts
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
/**
|
||||||
|
* A Serializer turns responses from the backend to models
|
||||||
|
* and vice versa
|
||||||
|
*/
|
||||||
|
export interface Serializer<T> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a model in to the format expected by the backend
|
||||||
|
*
|
||||||
|
* @param model The model to serialize
|
||||||
|
* @returns An object to send to the backend
|
||||||
|
*/
|
||||||
|
serialize(model: T): any;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert an array of models in to the format expected by the backend
|
||||||
|
*
|
||||||
|
* @param models The array of models to serialize
|
||||||
|
* @returns An object to send to the backend
|
||||||
|
*/
|
||||||
|
serializeArray(models: Array<T>): any;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a response from the backend in to a model.
|
||||||
|
*
|
||||||
|
* @param response An object returned by the backend
|
||||||
|
* @returns a model of type T
|
||||||
|
*/
|
||||||
|
deserialize(response: any): T;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a response from the backend in to an array of models
|
||||||
|
*
|
||||||
|
* @param response An object returned by the backend
|
||||||
|
* @returns an array of models of type T
|
||||||
|
*/
|
||||||
|
deserializeArray(response: any): Array<T>;
|
||||||
|
}
|
10
src/app/pagenotfound/pagenotfound.component.html
Normal file
10
src/app/pagenotfound/pagenotfound.component.html
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<div class="page-not-found">
|
||||||
|
<h1>404</h1>
|
||||||
|
<h2><small>{{"404.page-not-found" | translate}}</small></h2>
|
||||||
|
<br>
|
||||||
|
<p>{{"404.help" | translate}}</p>
|
||||||
|
<br>
|
||||||
|
<p class="text-center">
|
||||||
|
<a routerLink="/home" class="btn btn-primary">{{"404.link.home-page" | translate}}</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
1
src/app/pagenotfound/pagenotfound.component.scss
Normal file
1
src/app/pagenotfound/pagenotfound.component.scss
Normal file
@@ -0,0 +1 @@
|
|||||||
|
@import '../../styles/variables.scss';
|
21
src/app/pagenotfound/pagenotfound.component.ts
Normal file
21
src/app/pagenotfound/pagenotfound.component.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-pagenotfound',
|
||||||
|
styleUrls: ['./pagenotfound.component.css'],
|
||||||
|
templateUrl: './pagenotfound.component.html'
|
||||||
|
})
|
||||||
|
export class PageNotFoundComponent {
|
||||||
|
|
||||||
|
data: any = {};
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.universalInit();
|
||||||
|
}
|
||||||
|
|
||||||
|
universalInit() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
219
src/app/shared/empty.util.spec.ts
Normal file
219
src/app/shared/empty.util.spec.ts
Normal file
@@ -0,0 +1,219 @@
|
|||||||
|
import { isEmpty, hasNoValue, hasValue, isNotEmpty } from "./empty.util";
|
||||||
|
|
||||||
|
describe("Empty Utils", () => {
|
||||||
|
const string: string = 'string';
|
||||||
|
const fn: () => void = function () {};
|
||||||
|
const object: any = { length: 0 };
|
||||||
|
const emptyMap: Map<any, any> = new Map();
|
||||||
|
let fullMap: Map<string,string> = new Map();
|
||||||
|
fullMap.set('foo', 'bar');
|
||||||
|
|
||||||
|
describe("hasNoValue", () => {
|
||||||
|
it("should return true for null", () => {
|
||||||
|
expect(hasNoValue(null)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return true for undefined", () => {
|
||||||
|
expect(hasNoValue(undefined)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return false for an empty String", () => {
|
||||||
|
expect(hasNoValue('')).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return false for true", () => {
|
||||||
|
expect(hasNoValue(true)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return false for false", () => {
|
||||||
|
expect(hasNoValue(false)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return false for a String", () => {
|
||||||
|
expect(hasNoValue(string)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return false for a Function", () => {
|
||||||
|
expect(hasNoValue(fn)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return false for 0", () => {
|
||||||
|
expect(hasNoValue(0)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return false for an empty Array", () => {
|
||||||
|
expect(hasNoValue([])).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return false for an empty Object", () => {
|
||||||
|
expect(hasNoValue({})).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("hasValue", () => {
|
||||||
|
|
||||||
|
it("should return false for null", () => {
|
||||||
|
expect(hasValue(null)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return false for undefined", () => {
|
||||||
|
expect(hasValue(undefined)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return true for an empty String", () => {
|
||||||
|
expect(hasValue('')).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return true for false", () => {
|
||||||
|
expect(hasValue(false)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return true for true", () => {
|
||||||
|
expect(hasValue(true)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return true for a String", () => {
|
||||||
|
expect(hasValue(string)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return true for a Function", () => {
|
||||||
|
expect(hasValue(fn)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return true for 0", () => {
|
||||||
|
expect(hasValue(0)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return true for an empty Array", () => {
|
||||||
|
expect(hasValue([])).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return true for an empty Object", () => {
|
||||||
|
expect(hasValue({})).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("isEmpty", () => {
|
||||||
|
it("should return true for null", () => {
|
||||||
|
expect(isEmpty(null)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return true for undefined", () => {
|
||||||
|
expect(isEmpty(undefined)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return true for an empty String", () => {
|
||||||
|
expect(isEmpty('')).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return false for a whitespace String", () => {
|
||||||
|
expect(isEmpty(' ')).toBe(false);
|
||||||
|
expect(isEmpty('\n\t')).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return false for true", () => {
|
||||||
|
expect(isEmpty(true)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return false for false", () => {
|
||||||
|
expect(isEmpty(false)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return false for a String", () => {
|
||||||
|
expect(isEmpty(string)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return false for a Function", () => {
|
||||||
|
expect(isEmpty(fn)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return false for 0", () => {
|
||||||
|
expect(isEmpty(0)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return true for an empty Array", () => {
|
||||||
|
expect(isEmpty([])).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return false for an empty Object", () => {
|
||||||
|
expect(isEmpty({})).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return true for an Object that has zero \'length\'", () => {
|
||||||
|
expect(isEmpty(object)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return true for an Empty map", () => {
|
||||||
|
expect(isEmpty(emptyMap)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return false for a Map that is not empty", () => {
|
||||||
|
expect(isEmpty(fullMap)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("isNotEmpty", () => {
|
||||||
|
it("should return false for null", () => {
|
||||||
|
expect(isNotEmpty(null)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return false for undefined", () => {
|
||||||
|
expect(isNotEmpty(undefined)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return false for an empty String", () => {
|
||||||
|
expect(isNotEmpty('')).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return true for a whitespace String", () => {
|
||||||
|
expect(isNotEmpty(' ')).toBe(true);
|
||||||
|
expect(isNotEmpty('\n\t')).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return true for false", () => {
|
||||||
|
expect(isNotEmpty(false)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return true for true", () => {
|
||||||
|
expect(isNotEmpty(true)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return true for a String", () => {
|
||||||
|
expect(isNotEmpty(string)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return true for a Function", () => {
|
||||||
|
expect(isNotEmpty(fn)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return true for 0", () => {
|
||||||
|
expect(isNotEmpty(0)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return false for an empty Array", () => {
|
||||||
|
expect(isNotEmpty([])).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return true for an empty Object", () => {
|
||||||
|
expect(isNotEmpty({})).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return false for an Object that has zero length", () => {
|
||||||
|
expect(isNotEmpty(object)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return false for an Empty map", () => {
|
||||||
|
expect(isNotEmpty(emptyMap)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return true for a Map that is not empty", () => {
|
||||||
|
expect(isNotEmpty(fullMap)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
94
src/app/shared/empty.util.ts
Normal file
94
src/app/shared/empty.util.ts
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
/**
|
||||||
|
Returns true if the passed value is null or undefined.
|
||||||
|
|
||||||
|
hasNoValue(); // true
|
||||||
|
hasNoValue(null); // true
|
||||||
|
hasNoValue(undefined); // true
|
||||||
|
hasNoValue(''); // false
|
||||||
|
hasNoValue([]); // false
|
||||||
|
hasNoValue(function() {}); // false
|
||||||
|
*/
|
||||||
|
export function hasNoValue(obj?: any): boolean {
|
||||||
|
return obj === null || obj === undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Returns true if the passed value is not null or undefined.
|
||||||
|
|
||||||
|
hasValue(); // false
|
||||||
|
hasValue(null); // false
|
||||||
|
hasValue(undefined); // false
|
||||||
|
hasValue(''); // true
|
||||||
|
hasValue([]); // true
|
||||||
|
hasValue(function() {}); // true
|
||||||
|
*/
|
||||||
|
export function hasValue(obj?: any): boolean {
|
||||||
|
return !hasNoValue(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Verifies that a value is `null` or an empty string, empty array,
|
||||||
|
or empty function.
|
||||||
|
|
||||||
|
isEmpty(); // true
|
||||||
|
isEmpty(null); // true
|
||||||
|
isEmpty(undefined); // true
|
||||||
|
isEmpty(''); // true
|
||||||
|
isEmpty([]); // true
|
||||||
|
isEmpty({}); // false
|
||||||
|
isEmpty('Adam Hawkins'); // false
|
||||||
|
isEmpty([0,1,2]); // false
|
||||||
|
isEmpty('\n\t'); // false
|
||||||
|
isEmpty(' '); // false
|
||||||
|
*/
|
||||||
|
export function isEmpty(obj?: any): boolean {
|
||||||
|
if (hasNoValue(obj)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof obj.size === 'number') {
|
||||||
|
return !obj.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
let objectType = typeof obj;
|
||||||
|
|
||||||
|
if (objectType === 'object') {
|
||||||
|
let size = obj['size'];
|
||||||
|
if (typeof size === 'number') {
|
||||||
|
return !size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof obj.length === 'number' && objectType !== 'function') {
|
||||||
|
return !obj.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (objectType === 'object') {
|
||||||
|
let length = obj['length'];
|
||||||
|
if (typeof length === 'number') {
|
||||||
|
return !length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Verifies that a value is not `null`, an empty string, empty array,
|
||||||
|
or empty function.
|
||||||
|
|
||||||
|
isNotEmpty(); // false
|
||||||
|
isNotEmpty(null); // false
|
||||||
|
isNotEmpty(undefined); // false
|
||||||
|
isNotEmpty(''); // false
|
||||||
|
isNotEmpty([]); // false
|
||||||
|
isNotEmpty({}); // true
|
||||||
|
isNotEmpty('Adam Hawkins'); // true
|
||||||
|
isNotEmpty([0,1,2]); // true
|
||||||
|
isNotEmpty('\n\t'); // true
|
||||||
|
isNotEmpty(' '); // true
|
||||||
|
*/
|
||||||
|
export function isNotEmpty(obj?: any): boolean {
|
||||||
|
return !isEmpty(obj);
|
||||||
|
}
|
@@ -11,6 +11,7 @@ import { TranslateLoader, TranslateModule, TranslateStaticLoader } from 'ng2-tra
|
|||||||
import { AppModule, AppComponent } from './app/app.module';
|
import { AppModule, AppComponent } from './app/app.module';
|
||||||
import { SharedModule } from './app/shared/shared.module';
|
import { SharedModule } from './app/shared/shared.module';
|
||||||
import { CacheService } from './app/shared/cache.service';
|
import { CacheService } from './app/shared/cache.service';
|
||||||
|
import { CoreModule } from "./app/core/core.module";
|
||||||
|
|
||||||
// Will be merged into @angular/platform-browser in a later release
|
// Will be merged into @angular/platform-browser in a later release
|
||||||
// see https://github.com/angular/angular/pull/12322
|
// see https://github.com/angular/angular/pull/12322
|
||||||
@@ -56,7 +57,8 @@ export const UNIVERSAL_KEY = 'UNIVERSAL_CACHE';
|
|||||||
RouterModule.forRoot([], { useHash: false, preloadingStrategy: IdlePreload }),
|
RouterModule.forRoot([], { useHash: false, preloadingStrategy: IdlePreload }),
|
||||||
|
|
||||||
IdlePreloadModule.forRoot(),
|
IdlePreloadModule.forRoot(),
|
||||||
SharedModule.forRoot(),
|
CoreModule.forRoot(),
|
||||||
|
SharedModule,
|
||||||
AppModule,
|
AppModule,
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
|
@@ -10,6 +10,7 @@ import { TranslateLoader, TranslateModule, TranslateStaticLoader } from 'ng2-tra
|
|||||||
import { AppModule, AppComponent } from './app/app.module';
|
import { AppModule, AppComponent } from './app/app.module';
|
||||||
import { SharedModule } from './app/shared/shared.module';
|
import { SharedModule } from './app/shared/shared.module';
|
||||||
import { CacheService } from './app/shared/cache.service';
|
import { CacheService } from './app/shared/cache.service';
|
||||||
|
import { CoreModule } from "./app/core/core.module";
|
||||||
|
|
||||||
// Will be merged into @angular/platform-browser in a later release
|
// Will be merged into @angular/platform-browser in a later release
|
||||||
// see https://github.com/angular/angular/pull/12322
|
// see https://github.com/angular/angular/pull/12322
|
||||||
@@ -47,7 +48,8 @@ export const UNIVERSAL_KEY = 'UNIVERSAL_CACHE';
|
|||||||
FormsModule,
|
FormsModule,
|
||||||
RouterModule.forRoot([], { useHash: false }),
|
RouterModule.forRoot([], { useHash: false }),
|
||||||
|
|
||||||
SharedModule.forRoot(),
|
CoreModule.forRoot(),
|
||||||
|
SharedModule,
|
||||||
AppModule,
|
AppModule,
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
|
@@ -10,5 +10,5 @@
|
|||||||
* ];
|
* ];
|
||||||
**/
|
**/
|
||||||
export const routes: string[] = [
|
export const routes: string[] = [
|
||||||
'home'
|
'home', '**'
|
||||||
];
|
];
|
||||||
|
7
src/typings.d.ts
vendored
7
src/typings.d.ts
vendored
@@ -71,3 +71,10 @@ interface WebpackRequire {
|
|||||||
interface NodeRequire extends WebpackRequire { }
|
interface NodeRequire extends WebpackRequire { }
|
||||||
interface NodeModule extends WebpackModule { }
|
interface NodeModule extends WebpackModule { }
|
||||||
interface Global extends GlobalEnvironment { }
|
interface Global extends GlobalEnvironment { }
|
||||||
|
|
||||||
|
// Allows us to import json files in typescript
|
||||||
|
// See https://hackernoon.com/import-json-into-typescript-8d465beded79#.88tfoy2df
|
||||||
|
declare module "*.json" {
|
||||||
|
const value: any;
|
||||||
|
export default value;
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user