Merge pull request #21 in ~VLE2FE/definma-ui from development to master

* commit '9197e8a1875f5a696861e44cfec3547b8a5cccfa':
  started testing
  fixed type filter
  pwa, save sample view preferences
  added prediction prototype
This commit is contained in:
Veit Lukas (PEA4-Fe) 2020-08-16 20:04:43 +02:00
commit 78123c8f92
60 changed files with 789 additions and 160 deletions

View File

@ -1,4 +1,4 @@
# Digital fingerprint of plastics - UI # DeFinMa - UI
This is the Angular front end for the digital fingerprint of plastics web page hosted in the bic This is the Angular front end for the digital fingerprint of plastics web page hosted in the bic

View File

@ -3,7 +3,7 @@
"version": 1, "version": 1,
"newProjectRoot": "projects", "newProjectRoot": "projects",
"projects": { "projects": {
"UI": { "definma": {
"projectType": "application", "projectType": "application",
"schematics": { "schematics": {
"@schematics/angular:component": { "@schematics/angular:component": {
@ -17,7 +17,7 @@
"build": { "build": {
"builder": "@angular-devkit/build-angular:browser", "builder": "@angular-devkit/build-angular:browser",
"options": { "options": {
"outputPath": "dist/UI", "outputPath": "dist/definma",
"index": "src/index.html", "index": "src/index.html",
"main": "src/main.ts", "main": "src/main.ts",
"polyfills": "src/polyfills.ts", "polyfills": "src/polyfills.ts",
@ -30,7 +30,8 @@
"glob": "**/*", "glob": "**/*",
"input": "./node_modules/@inst-iot/bosch-angular-ui-components/assets", "input": "./node_modules/@inst-iot/bosch-angular-ui-components/assets",
"output": "./assets" "output": "./assets"
} },
"src/manifest.webmanifest"
], ],
"styles": [ "styles": [
"src/styles.scss" "src/styles.scss"
@ -64,26 +65,28 @@
"maximumWarning": "6kb", "maximumWarning": "6kb",
"maximumError": "10kb" "maximumError": "10kb"
} }
] ],
"serviceWorker": true,
"ngswConfigPath": "ngsw-config.json"
} }
} }
}, },
"serve": { "serve": {
"builder": "@angular-devkit/build-angular:dev-server", "builder": "@angular-devkit/build-angular:dev-server",
"options": { "options": {
"browserTarget": "UI:build", "browserTarget": "definma:build",
"proxyConfig": "src/proxy.conf.json" "proxyConfig": "src/proxy.conf.json"
}, },
"configurations": { "configurations": {
"production": { "production": {
"browserTarget": "UI:build:production" "browserTarget": "definma:build:production"
} }
} }
}, },
"extract-i18n": { "extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n", "builder": "@angular-devkit/build-angular:extract-i18n",
"options": { "options": {
"browserTarget": "UI:build" "browserTarget": "definma:build"
} }
}, },
"test": { "test": {
@ -95,7 +98,8 @@
"karmaConfig": "karma.conf.js", "karmaConfig": "karma.conf.js",
"assets": [ "assets": [
"src/favicon.ico", "src/favicon.ico",
"src/assets" "src/assets",
"src/manifest.webmanifest"
], ],
"styles": [ "styles": [
"src/styles.scss" "src/styles.scss"
@ -120,15 +124,15 @@
"builder": "@angular-devkit/build-angular:protractor", "builder": "@angular-devkit/build-angular:protractor",
"options": { "options": {
"protractorConfig": "e2e/protractor.conf.js", "protractorConfig": "e2e/protractor.conf.js",
"devServerTarget": "UI:serve" "devServerTarget": "definma:serve"
}, },
"configurations": { "configurations": {
"production": { "production": {
"devServerTarget": "UI:serve:production" "devServerTarget": "definma:serve:production"
} }
} }
} }
} }
}}, }},
"defaultProject": "UI" "defaultProject": "definma"
} }

View File

@ -1,4 +1,4 @@
pushstate: enabled pushstate: enabled
force_https: true force_https: true
root: UI root: definma
location_include: ../../*.conf location_include: ../../*.conf

View File

@ -13,10 +13,13 @@ module.exports = function (config) {
require('@angular-devkit/build-angular/plugins/karma') require('@angular-devkit/build-angular/plugins/karma')
], ],
client: { client: {
clearContext: false // leave Jasmine Spec Runner output visible in browser clearContext: false, // leave Jasmine Spec Runner output visible in browser
jasmine: {
random: false
}
}, },
coverageIstanbulReporter: { coverageIstanbulReporter: {
dir: require('path').join(__dirname, './coverage/UI'), dir: require('path').join(__dirname, './coverage/definma'),
reports: ['html', 'lcovonly', 'text-summary'], reports: ['html', 'lcovonly', 'text-summary'],
fixWebpackSourcePaths: true fixWebpackSourcePaths: true
}, },

29
ngsw-config.json Normal file
View File

@ -0,0 +1,29 @@
{
"$schema": "./node_modules/@angular/service-worker/config/schema.json",
"index": "/index.html",
"assetGroups": [
{
"name": "app",
"installMode": "prefetch",
"resources": {
"files": [
"/favicon.ico",
"/index.html",
"/manifest.webmanifest",
"/*.css",
"/*.js"
]
}
}, {
"name": "assets",
"installMode": "lazy",
"updateMode": "prefetch",
"resources": {
"files": [
"/assets/**",
"/*.(eot|svg|cur|jpg|png|webp|gif|otf|ttf|woff|woff2|ani)"
]
}
}
]
}

9
package-lock.json generated
View File

@ -1,6 +1,6 @@
{ {
"name": "ui", "name": "definma",
"version": "0.0.0", "version": "0.5.5",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
@ -630,6 +630,11 @@
"resolved": "https://registry.npmjs.org/@angular/router/-/router-9.1.7.tgz", "resolved": "https://registry.npmjs.org/@angular/router/-/router-9.1.7.tgz",
"integrity": "sha512-ycrkhkCbfOMCe9PngFjnyk8nH5jt0Kyb2NPtjmaGOtSCuZBZ0kOU0rQGmQnj3d2PiT0Yir59S8eEAf3Fh0iDuw==" "integrity": "sha512-ycrkhkCbfOMCe9PngFjnyk8nH5jt0Kyb2NPtjmaGOtSCuZBZ0kOU0rQGmQnj3d2PiT0Yir59S8eEAf3Fh0iDuw=="
}, },
"@angular/service-worker": {
"version": "9.1.12",
"resolved": "https://registry.npmjs.org/@angular/service-worker/-/service-worker-9.1.12.tgz",
"integrity": "sha512-UsmhPfhIYq9LanuFT6V7Kmkr5Vjl2CMjKkL1gqhChkywNW4vavAYsBkuVji8P76ZKliq41TCG01z6xIAWji8QA=="
},
"@babel/code-frame": { "@babel/code-frame": {
"version": "7.8.3", "version": "7.8.3",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz",

View File

@ -23,6 +23,7 @@
"@angular/platform-browser": "~9.1.7", "@angular/platform-browser": "~9.1.7",
"@angular/platform-browser-dynamic": "~9.1.7", "@angular/platform-browser-dynamic": "~9.1.7",
"@angular/router": "~9.1.7", "@angular/router": "~9.1.7",
"@angular/service-worker": "~9.1.7",
"@hapi/joi": "^17.1.1", "@hapi/joi": "^17.1.1",
"@inst-iot/bosch-angular-ui-components": "^0.7.2", "@inst-iot/bosch-angular-ui-components": "^0.7.2",
"angular-2-local-storage": "^3.0.2", "angular-2-local-storage": "^3.0.2",

View File

@ -10,11 +10,13 @@ import {SettingsComponent} from './settings/settings.component';
import {UsersComponent} from './users/users.component'; import {UsersComponent} from './users/users.component';
import {ChangelogComponent} from './changelog/changelog.component'; import {ChangelogComponent} from './changelog/changelog.component';
import {DocumentationDatabaseComponent} from './documentation-database/documentation-database.component'; import {DocumentationDatabaseComponent} from './documentation-database/documentation-database.component';
import {PredictionComponent} from './prediction/prediction.component';
const routes: Routes = [ const routes: Routes = [
{path: '', component: HomeComponent}, {path: '', component: HomeComponent},
{path: 'home', component: HomeComponent}, {path: 'home', component: HomeComponent},
{path: 'prediction', component: PredictionComponent},
{path: 'samples', component: SamplesComponent, canActivate: [LoginService]}, {path: 'samples', component: SamplesComponent, canActivate: [LoginService]},
{path: 'samples/new', component: SampleComponent, canActivate: [LoginService]}, {path: 'samples/new', component: SampleComponent, canActivate: [LoginService]},
{path: 'samples/edit/:id', component: SampleComponent, canActivate: [LoginService]}, {path: 'samples/edit/:id', component: SampleComponent, canActivate: [LoginService]},

View File

@ -1,6 +1,7 @@
<rb-full-header id="top"> <rb-full-header id="top">
<nav *rbMainNavItems> <nav *rbMainNavItems>
<a routerLink="/home" routerLinkActive="active" rbLoadingLink>Home</a> <a routerLink="/home" routerLinkActive="active" rbLoadingLink>Home</a>
<a routerLink="/prediction" routerLinkActive="active" rbLoadingLink *ngIf="login.isLevel.admin">Prediction</a>
<a routerLink="/samples" routerLinkActive="active" rbLoadingLink *ngIf="login.isLoggedIn">Samples</a> <a routerLink="/samples" routerLinkActive="active" rbLoadingLink *ngIf="login.isLoggedIn">Samples</a>
<a routerLink="/templates" routerLinkActive="active" rbLoadingLink *ngIf="login.isLevel.dev"> <a routerLink="/templates" routerLinkActive="active" rbLoadingLink *ngIf="login.isLevel.dev">
Templates Templates

View File

@ -4,8 +4,13 @@ import {By} from '@angular/platform-browser';
import {RbUiComponentsModule} from '@inst-iot/bosch-angular-ui-components'; import {RbUiComponentsModule} from '@inst-iot/bosch-angular-ui-components';
import {RouterTestingModule} from '@angular/router/testing'; import {RouterTestingModule} from '@angular/router/testing';
import {LoginService} from './services/login.service'; import {LoginService} from './services/login.service';
import {Router} from '@angular/router';
// TODO
let loginServiceSpy: jasmine.SpyObj<LoginService>; let loginServiceSpy: jasmine.SpyObj<LoginService>;
let routerServiceSpy: jasmine.SpyObj<Router>;
let windowServiceSpy: jasmine.SpyObj<Window>;
describe('AppComponent', () => { describe('AppComponent', () => {
let component: AppComponent; let component: AppComponent;
@ -14,6 +19,8 @@ describe('AppComponent', () => {
beforeEach(async(() => { beforeEach(async(() => {
const loginSpy = jasmine.createSpyObj('LoginService', ['login', 'canActivate']); const loginSpy = jasmine.createSpyObj('LoginService', ['login', 'canActivate']);
const routerSpy = jasmine.createSpyObj('Router', ['navigate', 'events']);
const windowSpy = jasmine.createSpyObj('Window', ['location', 'innerWidth', 'innerHeight', 'scroll']);
TestBed.configureTestingModule({ TestBed.configureTestingModule({
declarations: [ AppComponent ], declarations: [ AppComponent ],
@ -22,10 +29,15 @@ describe('AppComponent', () => {
RouterTestingModule RouterTestingModule
], ],
providers: [ providers: [
{provide: LoginService, useValue: loginSpy} {provide: LoginService, useValue: loginSpy},
{provide: Router, useValue: routerSpy},
{provide: Window, useValue: windowSpy}
] ]
}).compileComponents(); }).compileComponents();
loginServiceSpy = TestBed.inject(LoginService) as jasmine.SpyObj<LoginService>; loginServiceSpy = TestBed.inject(LoginService) as jasmine.SpyObj<LoginService>;
routerServiceSpy = TestBed.inject(Router) as jasmine.SpyObj<Router>;
windowServiceSpy = TestBed.inject(Window) as jasmine.SpyObj<Window>;
})); }));
beforeEach(() => { beforeEach(() => {

View File

@ -2,12 +2,6 @@ import { Component, isDevMode} from '@angular/core';
import {LoginService} from './services/login.service'; import {LoginService} from './services/login.service';
import {NavigationStart, Router} from '@angular/router'; import {NavigationStart, Router} from '@angular/router';
// TODO: add multiple samples at once
// TODO: validation: DPT: filename
// TODO: filter by not completely filled/no measurements
// TODO: validation of samples
// TODO: centralize fetching of materials / templates, etc.
// TODO: PWA
// TODO: get rid of chart.js (+moment.js) // TODO: get rid of chart.js (+moment.js)
@ -24,7 +18,8 @@ export class AppComponent {
constructor( constructor(
public login: LoginService, public login: LoginService,
public router: Router public router: Router,
private window: Window
) { ) {
this.devMode = isDevMode(); this.devMode = isDevMode();
this.router.events.subscribe(event => { this.router.events.subscribe(event => {
@ -43,7 +38,7 @@ export class AppComponent {
return `mailto:lukas.veit@de.bosch.com?subject=Bug report&body=Thanks for sending the report! Your bug will be (hopefully) fixed soon. return `mailto:lukas.veit@de.bosch.com?subject=Bug report&body=Thanks for sending the report! Your bug will be (hopefully) fixed soon.
%0D%0A%0D%0A--- REPORT DATA --- %0D%0A%0D%0A--- REPORT DATA ---
%0D%0A%0D%0ATime: ${new Date().toString()}%0D%0A %0D%0A%0D%0ATime: ${new Date().toString()}%0D%0A
URL: ${window.location}%0D%0A%0D%0AWhat did you do?%0D%0A${encodeURIComponent(this.bugReport.do)} URL: ${this.window.location}%0D%0A%0D%0AWhat did you do?%0D%0A${encodeURIComponent(this.bugReport.do)}
%0D%0A%0D%0AWhat did not work?%0D%0A${encodeURIComponent(this.bugReport.work)}%0D%0A%0D%0ABrowser:%0D%0A %0D%0A%0D%0AWhat did not work?%0D%0A${encodeURIComponent(this.bugReport.work)}%0D%0A%0D%0ABrowser:%0D%0A
%0D%0AappCodeName: ${navigator.appCodeName} %0D%0AappCodeName: ${navigator.appCodeName}
%0D%0AappVersion: ${navigator.appVersion} %0D%0AappVersion: ${navigator.appVersion}
@ -52,8 +47,8 @@ URL: ${window.location}%0D%0A%0D%0AWhat did you do?%0D%0A${encodeURIComponent(th
%0D%0Aoscpu: ${navigator.oscpu} %0D%0Aoscpu: ${navigator.oscpu}
%0D%0Aplatform: ${navigator.platform} %0D%0Aplatform: ${navigator.platform}
%0D%0AuserAgent: ${navigator.userAgent} %0D%0AuserAgent: ${navigator.userAgent}
%0D%0AinnerWidth: ${window.innerWidth} %0D%0AinnerWidth: ${this.window.innerWidth}
%0D%0AinnerHeight: ${window.innerHeight}`; %0D%0AinnerHeight: ${this.window.innerHeight}`;
} }
closeBugReport(close) { closeBugReport(close) {
@ -61,7 +56,7 @@ URL: ${window.location}%0D%0A%0D%0AWhat did you do?%0D%0A${encodeURIComponent(th
} }
toTheTop() { toTheTop() {
window.scroll(0, 0); this.window.scroll(0, 0);
} }
} }

View File

@ -27,6 +27,9 @@ import { SettingsComponent } from './settings/settings.component';
import { UsersComponent } from './users/users.component'; import { UsersComponent } from './users/users.component';
import { ChangelogComponent } from './changelog/changelog.component'; import { ChangelogComponent } from './changelog/changelog.component';
import { DocumentationDatabaseComponent } from './documentation-database/documentation-database.component'; import { DocumentationDatabaseComponent } from './documentation-database/documentation-database.component';
import { PredictionComponent } from './prediction/prediction.component';
import { ServiceWorkerModule } from '@angular/service-worker';
import { environment } from '../environments/environment';
@NgModule({ @NgModule({
declarations: [ declarations: [
@ -46,7 +49,8 @@ import { DocumentationDatabaseComponent } from './documentation-database/documen
SettingsComponent, SettingsComponent,
UsersComponent, UsersComponent,
ChangelogComponent, ChangelogComponent,
DocumentationDatabaseComponent DocumentationDatabaseComponent,
PredictionComponent
], ],
imports: [ imports: [
LocalStorageModule.forRoot({ LocalStorageModule.forRoot({
@ -63,7 +67,8 @@ import { DocumentationDatabaseComponent } from './documentation-database/documen
ReactiveFormsModule, ReactiveFormsModule,
FormFieldsModule, FormFieldsModule,
CommonModule, CommonModule,
ChartsModule ChartsModule,
ServiceWorkerModule.register('ngsw-worker.js', { enabled: environment.production })
], ],
providers: [ providers: [
ModalService, ModalService,

View File

@ -1,21 +1,44 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ChangelogComponent } from './changelog.component'; import { ChangelogComponent } from './changelog.component';
import {ApiService} from '../services/api.service';
import {ModalService, RbUiComponentsModule} from '@inst-iot/bosch-angular-ui-components';
import {FormsModule} from '@angular/forms';
import {ValidationService} from '../services/validation.service';
import {DataService} from '../services/data.service';
// TODO
let apiServiceSpy: jasmine.SpyObj<ApiService>;
let modalServiceSpy: jasmine.SpyObj<ModalService>;
describe('ChangelogComponent', () => { describe('ChangelogComponent', () => {
let component: ChangelogComponent; let component: ChangelogComponent;
let fixture: ComponentFixture<ChangelogComponent>; let fixture: ComponentFixture<ChangelogComponent>;
beforeEach(async(() => { beforeEach(async(() => {
const apiSpy = jasmine.createSpyObj('ApiService', ['get']);
const modalSpy = jasmine.createSpyObj('ModalService', ['open']);
TestBed.configureTestingModule({ TestBed.configureTestingModule({
declarations: [ ChangelogComponent ] declarations: [ ChangelogComponent ],
imports: [
],
providers: [
{provide: ApiService, useValue: apiSpy},
{provide: ModalService, useValue: modalSpy}
]
}) })
.compileComponents(); .compileComponents();
apiServiceSpy = TestBed.inject(ApiService) as jasmine.SpyObj<ApiService>;
modalServiceSpy = TestBed.inject(ModalService) as jasmine.SpyObj<ModalService>;
})); }));
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(ChangelogComponent); fixture = TestBed.createComponent(ChangelogComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
component.ngOnInit();
fixture.detectChanges(); fixture.detectChanges();
}); });

View File

@ -18,7 +18,6 @@
<td>Automatically generated unique id</td> <td>Automatically generated unique id</td>
<td>'5f2e63c98d1c020f8cda6e06'</td> <td>'5f2e63c98d1c020f8cda6e06'</td>
</tr> </tr>
<!-- TODO: new names-->
<tr> <tr>
<td>type</td> <td>type</td>
<td> <td>

View File

@ -2,6 +2,8 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { DocumentationDatabaseComponent } from './documentation-database.component'; import { DocumentationDatabaseComponent } from './documentation-database.component';
// TODO
describe('DocumentationDatabaseComponent', () => { describe('DocumentationDatabaseComponent', () => {
let component: DocumentationDatabaseComponent; let component: DocumentationDatabaseComponent;
let fixture: ComponentFixture<DocumentationDatabaseComponent>; let fixture: ComponentFixture<DocumentationDatabaseComponent>;
@ -16,6 +18,7 @@ describe('DocumentationDatabaseComponent', () => {
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(DocumentationDatabaseComponent); fixture = TestBed.createComponent(DocumentationDatabaseComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
component.ngOnInit();
fixture.detectChanges(); fixture.detectChanges();
}); });

View File

@ -1,6 +1,11 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { DocumentationComponent } from './documentation.component'; import { DocumentationComponent } from './documentation.component';
import {RbUiComponentsModule} from '@inst-iot/bosch-angular-ui-components';
import {FormsModule} from '@angular/forms';
import {RbCustomInputsModule} from '../rb-custom-inputs/rb-custom-inputs.module';
// TODO
describe('DocumentationComponent', () => { describe('DocumentationComponent', () => {
let component: DocumentationComponent; let component: DocumentationComponent;
@ -8,7 +13,10 @@ describe('DocumentationComponent', () => {
beforeEach(async(() => { beforeEach(async(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
declarations: [ DocumentationComponent ] declarations: [ DocumentationComponent ],
imports: [
RbCustomInputsModule
]
}) })
.compileComponents(); .compileComponents();
})); }));
@ -16,6 +24,7 @@ describe('DocumentationComponent', () => {
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(DocumentationComponent); fixture = TestBed.createComponent(DocumentationComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
component.ngOnInit();
fixture.detectChanges(); fixture.detectChanges();
}); });

View File

@ -4,6 +4,8 @@ import { ErrorComponent } from './error.component';
import {ModalService, RbUiComponentsModule} from '@inst-iot/bosch-angular-ui-components'; import {ModalService, RbUiComponentsModule} from '@inst-iot/bosch-angular-ui-components';
import {By} from '@angular/platform-browser'; import {By} from '@angular/platform-browser';
// TODO
describe('ErrorComponent', () => { describe('ErrorComponent', () => {
let component: ErrorComponent; let component: ErrorComponent;
let fixture: ComponentFixture<ErrorComponent>; let fixture: ComponentFixture<ErrorComponent>;
@ -25,6 +27,7 @@ describe('ErrorComponent', () => {
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(ErrorComponent); fixture = TestBed.createComponent(ErrorComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
component.ngOnInit();
fixture.detectChanges(); fixture.detectChanges();
css = (selector) => fixture.debugElement.query(By.css(selector)).nativeElement; css = (selector) => fixture.debugElement.query(By.css(selector)).nativeElement;
}); });

View File

@ -1,5 +1,7 @@
import { ExistsPipe } from './exists.pipe'; import { ExistsPipe } from './exists.pipe';
// TODO
describe('ExistsPipe', () => { describe('ExistsPipe', () => {
it('create an instance', () => { it('create an instance', () => {
const pipe = new ExistsPipe(); const pipe = new ExistsPipe();

View File

@ -3,6 +3,8 @@ import { HomeComponent } from './home.component';
import {Component} from '@angular/core'; import {Component} from '@angular/core';
import {By} from '@angular/platform-browser'; import {By} from '@angular/platform-browser';
// TODO
@Component({selector: 'app-login', template: ''}) @Component({selector: 'app-login', template: ''})
class LoginStubComponent {} class LoginStubComponent {}
@ -23,6 +25,7 @@ describe('HomeComponent', () => {
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(HomeComponent); fixture = TestBed.createComponent(HomeComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
component.ngOnInit();
fixture.detectChanges(); fixture.detectChanges();
}); });

View File

@ -1,21 +1,40 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ImgMagnifierComponent } from './img-magnifier.component'; import { ImgMagnifierComponent } from './img-magnifier.component';
import {ApiService} from '../services/api.service';
import {RbUiComponentsModule} from '@inst-iot/bosch-angular-ui-components';
import {FormsModule} from '@angular/forms';
import {ValidationService} from '../services/validation.service';
import {DataService} from '../services/data.service';
// TODO
let windowServiceSpy: jasmine.SpyObj<Window>;
describe('ImgMagnifierComponent', () => { describe('ImgMagnifierComponent', () => {
let component: ImgMagnifierComponent; let component: ImgMagnifierComponent;
let fixture: ComponentFixture<ImgMagnifierComponent>; let fixture: ComponentFixture<ImgMagnifierComponent>;
beforeEach(async(() => { beforeEach(async(() => {
const windowSpy = jasmine.createSpyObj('Window', []);
TestBed.configureTestingModule({ TestBed.configureTestingModule({
declarations: [ ImgMagnifierComponent ] declarations: [ ImgMagnifierComponent ],
imports: [
],
providers: [
{provide: Window, useValue: windowSpy}
]
}) })
.compileComponents(); .compileComponents();
windowServiceSpy = TestBed.inject(Window) as jasmine.SpyObj<Window>;
})); }));
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(ImgMagnifierComponent); fixture = TestBed.createComponent(ImgMagnifierComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
component.ngOnInit();
fixture.detectChanges(); fixture.detectChanges();
}); });

View File

@ -7,6 +7,8 @@ import {By} from '@angular/platform-browser';
import {ValidateDirective} from '../validate.directive'; import {ValidateDirective} from '../validate.directive';
import {RbUiComponentsModule} from '@inst-iot/bosch-angular-ui-components'; import {RbUiComponentsModule} from '@inst-iot/bosch-angular-ui-components';
// TODO
let validationServiceSpy: jasmine.SpyObj<ValidationService>; let validationServiceSpy: jasmine.SpyObj<ValidationService>;
let loginServiceSpy: jasmine.SpyObj<LoginService>; let loginServiceSpy: jasmine.SpyObj<LoginService>;

View File

@ -1,6 +1,6 @@
import { UserModel } from './user.model'; import { UserModel } from './user.model';
describe('User.Model', () => { describe('UserModel', () => {
it('should create an instance', () => { it('should create an instance', () => {
expect(new UserModel()).toBeTruthy(); expect(new UserModel()).toBeTruthy();
}); });

View File

@ -1,5 +1,7 @@
import { ObjectPipe } from './object.pipe'; import { ObjectPipe } from './object.pipe';
// TODO
describe('ObjectPipe', () => { describe('ObjectPipe', () => {
it('create an instance', () => { it('create an instance', () => {
const pipe = new ObjectPipe(); const pipe = new ObjectPipe();

View File

@ -1,5 +1,7 @@
import { ParametersPipe } from './parameters.pipe'; import { ParametersPipe } from './parameters.pipe';
// TODO
describe('ParametersPipe', () => { describe('ParametersPipe', () => {
it('create an instance', () => { it('create an instance', () => {
const pipe = new ParametersPipe(); const pipe = new ParametersPipe();

View File

@ -0,0 +1,20 @@
<h2>Prediction</h2>
<h4 *ngIf="result !== '' || loading" [@inOut]>
Result: {{result}}<rb-loading-spinner *ngIf="loading"></rb-loading-spinner>
</h4>
<rb-form-file name="spectrum-upload" label="spectrum file" maxSize="10000000" class="space-below"
(ngModelChange)="fileToArray($event)" placeholder="Select file or drag and drop" dragDrop ngModel>
</rb-form-file>
<div class="dpt-chart">
<canvas baseChart
class="dpt-chart"
[datasets]="chart"
[labels]="[]"
[options]="chartOptions"
[legend]="false"
chartType="scatter">
</canvas>
</div>

View File

@ -0,0 +1,4 @@
.dpt-chart {
max-width: 800px;
margin: 0 auto;
}

View File

@ -0,0 +1,44 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { PredictionComponent } from './prediction.component';
import {ApiService} from '../services/api.service';
import {RbUiComponentsModule} from '@inst-iot/bosch-angular-ui-components';
import {FormsModule} from '@angular/forms';
import {ValidationService} from '../services/validation.service';
import {DataService} from '../services/data.service';
// TODO
let apiServiceSpy: jasmine.SpyObj<ApiService>;
describe('PredictionComponent', () => {
let component: PredictionComponent;
let fixture: ComponentFixture<PredictionComponent>;
beforeEach(async(() => {
const apiSpy = jasmine.createSpyObj('ApiService', ['post']);
TestBed.configureTestingModule({
declarations: [ PredictionComponent ],
imports: [
],
providers: [
{provide: ApiService, useValue: apiSpy}
]
})
.compileComponents();
apiServiceSpy = TestBed.inject(ApiService) as jasmine.SpyObj<ApiService>;
}));
beforeEach(() => {
fixture = TestBed.createComponent(PredictionComponent);
component = fixture.componentInstance;
component.ngOnInit();
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,74 @@
import { Component, OnInit } from '@angular/core';
import {ChartOptions} from 'chart.js';
import {ApiService} from '../services/api.service';
import {animate, style, transition, trigger} from '@angular/animations';
@Component({
selector: 'app-prediction',
templateUrl: './prediction.component.html',
styleUrls: ['./prediction.component.scss'],
animations: [
trigger(
'inOut', [
transition(':enter', [
style({height: 0, opacity: 0}),
animate('0.5s ease-out', style({height: '*', opacity: 1}))
]),
transition(':leave', [
style({height: '*', opacity: 1}),
animate('0.5s ease-in', style({height: 0, opacity: 0}))
])
]
)
]
})
export class PredictionComponent implements OnInit {
readonly predictionUrl = 'https://definma-model-test.apps.de1.bosch-iot-cloud.com/predict';
result = '';
loading = false;
spectrum: string[][] = [[]];
chart = [{
data: [],
label: 'Spectrum',
showLine: true,
fill: false,
pointRadius: 0,
borderColor: '#00a8b0',
borderWidth: 2
}];
readonly chartOptions: ChartOptions = {
scales: {
xAxes: [{ticks: {min: 400, max: 4000, stepSize: 400, reverse: true}}],
yAxes: [{ticks: {min: 0, max: 1}}]
},
responsive: true,
tooltips: {enabled: false},
hover: {mode: null},
maintainAspectRatio: true,
plugins: {datalabels: {display: false}}
};
constructor(
private api: ApiService
) { }
ngOnInit(): void {
}
fileToArray(files) {
const fileReader = new FileReader();
fileReader.onload = () => {
this.spectrum = fileReader.result.toString().split('\r\n').map(e => e.split(','));
this.loading = true;
this.api.post<{result: string}>(this.predictionUrl, this.spectrum, data => {
this.result = data.result;
this.loading = false;
});
this.chart[0].data = this.spectrum.map(e => ({x: parseFloat(e[0]), y: parseFloat(e[1])}));
console.log(this.chart);
};
fileReader.readAsText(files[0]);
}
}

View File

@ -2,6 +2,8 @@ import { TestBed } from '@angular/core/testing';
import { ArrayInputHelperService } from './array-input-helper.service'; import { ArrayInputHelperService } from './array-input-helper.service';
// TOdo
describe('ArrayInputHelperService', () => { describe('ArrayInputHelperService', () => {
let service: ArrayInputHelperService; let service: ArrayInputHelperService;

View File

@ -16,6 +16,7 @@ describe('RbArrayInputComponent', () => {
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(RbArrayInputComponent); fixture = TestBed.createComponent(RbArrayInputComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
component.ngOnInit();
fixture.detectChanges(); fixture.detectChanges();
}); });

View File

@ -2,6 +2,8 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { RbIconButtonComponent } from './rb-icon-button.component'; import { RbIconButtonComponent } from './rb-icon-button.component';
// TODO
describe('RbIconButtonComponent', () => { describe('RbIconButtonComponent', () => {
let component: RbIconButtonComponent; let component: RbIconButtonComponent;
let fixture: ComponentFixture<RbIconButtonComponent>; let fixture: ComponentFixture<RbIconButtonComponent>;
@ -16,6 +18,7 @@ describe('RbIconButtonComponent', () => {
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(RbIconButtonComponent); fixture = TestBed.createComponent(RbIconButtonComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
component.ngOnInit();
fixture.detectChanges(); fixture.detectChanges();
}); });

View File

@ -34,5 +34,6 @@ table.ellipsis {
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
max-width: 200px; max-width: 200px;
//min-width: 100px;
} }
} }

View File

@ -2,6 +2,8 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { RbTableComponent } from './rb-table.component'; import { RbTableComponent } from './rb-table.component';
// TODO
describe('RbTableComponent', () => { describe('RbTableComponent', () => {
let component: RbTableComponent; let component: RbTableComponent;
let fixture: ComponentFixture<RbTableComponent>; let fixture: ComponentFixture<RbTableComponent>;
@ -16,6 +18,7 @@ describe('RbTableComponent', () => {
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(RbTableComponent); fixture = TestBed.createComponent(RbTableComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
component.ngOnInit();
fixture.detectChanges(); fixture.detectChanges();
}); });

View File

@ -1,4 +1,4 @@
<script src="../models/template.model.ts"></script><h2>{{new ? 'Add new sample' : 'Edit sample ' + sample.number}}</h2> <h2>{{new ? 'Add new sample' : 'Edit sample ' + sample.number}}</h2>
<rb-loading-spinner *ngIf="loading"></rb-loading-spinner> <rb-loading-spinner *ngIf="loading"></rb-loading-spinner>

View File

@ -1,27 +1,70 @@
// import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { async, ComponentFixture, TestBed } from '@angular/core/testing';
//
// import { SampleComponent } from './sample.component'; import { SampleComponent } from './sample.component';
// import {ApiService} from '../services/api.service';
// // TODO: tests import {ValidationService} from '../services/validation.service';
// import {DataService} from '../services/data.service';
// describe('SampleComponent', () => { import {ModalService, RbUiComponentsModule} from '@inst-iot/bosch-angular-ui-components';
// let component: SampleComponent; import {FormsModule} from '@angular/forms';
// let fixture: ComponentFixture<SampleComponent>; import {ActivatedRoute, Router} from '@angular/router';
// import {AutocompleteService} from '../services/autocomplete.service';
// beforeEach(async(() => {
// TestBed.configureTestingModule({ // TODO
// declarations: [ SampleComponent ]
// }) let routerServiceSpy: jasmine.SpyObj<Router>;
// .compileComponents(); let activatedRouteServiceSpy: jasmine.SpyObj<ActivatedRoute>;
// })); let apiServiceSpy: jasmine.SpyObj<ApiService>;
// let validationServiceSpy: jasmine.SpyObj<ValidationService>;
// beforeEach(() => { let autocompleteServiceSpy: jasmine.SpyObj<AutocompleteService>;
// fixture = TestBed.createComponent(SampleComponent); let modalServiceSpy: jasmine.SpyObj<ModalService>;
// component = fixture.componentInstance; let dataServiceSpy: jasmine.SpyObj<DataService>;
// fixture.detectChanges();
// }); describe('SampleComponent', () => {
// let component: SampleComponent;
// it('should create', () => { let fixture: ComponentFixture<SampleComponent>;
// expect(component).toBeTruthy();
// }); beforeEach(async(() => {
// }); const routerSpy = jasmine.createSpyObj('Router', ['navigate']);
const activatedRouteSpy = jasmine.createSpyObj('ActivatedRoute', ['snapshot']);
const apiSpy = jasmine.createSpyObj('ApiService', ['post', 'put']);
const validationSpy = jasmine.createSpyObj('ValidationService', ['generate']);
const autocompleteSpy = jasmine.createSpyObj('AutocompleteService', []);
const modalSpy = jasmine.createSpyObj('ModalService', ['open']);
const dataSpy = jasmine.createSpyObj('DataService', ['load', 'idReload']);
TestBed.configureTestingModule({
declarations: [ SampleComponent ],
imports: [
],
providers: [
{provide: Router, useValue: routerSpy},
{provide: ActivatedRoute, useValue: activatedRouteSpy},
{provide: ApiService, useValue: apiSpy},
{provide: ValidationService, useValue: validationSpy},
{provide: AutocompleteService, useValue: autocompleteSpy},
{provide: ModalService, useValue: modalSpy},
{provide: DataService, useValue: dataSpy}
]
})
.compileComponents();
routerServiceSpy = TestBed.inject(Router) as jasmine.SpyObj<Router>;
activatedRouteServiceSpy = TestBed.inject(ActivatedRoute) as jasmine.SpyObj<ActivatedRoute>;
apiServiceSpy = TestBed.inject(ApiService) as jasmine.SpyObj<ApiService>;
validationServiceSpy = TestBed.inject(ValidationService) as jasmine.SpyObj<ValidationService>;
autocompleteServiceSpy = TestBed.inject(AutocompleteService) as jasmine.SpyObj<AutocompleteService>;
modalServiceSpy = TestBed.inject(ModalService) as jasmine.SpyObj<ModalService>;
dataServiceSpy = TestBed.inject(DataService) as jasmine.SpyObj<DataService>;
}));
beforeEach(() => {
fixture = TestBed.createComponent(SampleComponent);
component = fixture.componentInstance;
component.ngOnInit();
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -22,10 +22,6 @@ import {Observable} from 'rxjs';
import {ModalService} from '@inst-iot/bosch-angular-ui-components'; import {ModalService} from '@inst-iot/bosch-angular-ui-components';
import {DataService} from '../services/data.service'; import {DataService} from '../services/data.service';
// TODO: clean up this mess !!!
// TODO: only show condition (if not set) and measurements in edit sample dialog at first
// TODO: multiple samples for base data, extend multiple measurements, conditions
@Component({ @Component({
selector: 'app-sample', selector: 'app-sample',

View File

@ -73,7 +73,7 @@
(ngModelChange)="updateFilterFields(filter.field)"> (ngModelChange)="updateFilterFields(filter.field)">
<ng-container *rbArrayInputItem="let item" <ng-container *rbArrayInputItem="let item"
[ngSwitch]="(filter.autocomplete.length ? 'autocomplete' : '') + [ngSwitch]="(filter.autocomplete.length ? 'autocomplete' : '') +
(filter.field == 'added' ? 'date' : '')"> (filter.field == 'added' ? 'date' : (filter.field == 'type' ? 'type' : ''))">
<rb-form-date-input *ngSwitchCase="'date'" [rbArrayInputListener]="'filter-' + filter.field" <rb-form-date-input *ngSwitchCase="'date'" [rbArrayInputListener]="'filter-' + filter.field"
[name]="'filter-' + filter.field + item.i" [index]="item.i" [name]="'filter-' + filter.field + item.i" [index]="item.i"
[label]="filter.label" [(ngModel)]="item.value"></rb-form-date-input> [label]="filter.label" [(ngModel)]="item.value"></rb-form-date-input>
@ -86,6 +86,12 @@
[rbDebounceTime]="0" (keydown)="preventDefault($event, 'Enter')" [rbDebounceTime]="0" (keydown)="preventDefault($event, 'Enter')"
[rbFormInputAutocomplete]="autocomplete.bind(this, filter.autocomplete)" [rbFormInputAutocomplete]="autocomplete.bind(this, filter.autocomplete)"
ngModel></rb-form-input> ngModel></rb-form-input>
<rb-form-select *ngSwitchCase="'type'" [rbArrayInputListener]="'filter-' + filter.field"
[name]="'filter-' + filter.field + item.i" [index]="item.i"
[label]="filter.label" [(ngModel)]="item.value">
<option value="as-delivered/raw">as-delivered/raw</option>
<option value="processed">processed</option>
</rb-form-select>
</ng-container> </ng-container>
</rb-array-input> </rb-array-input>
</div> </div>
@ -132,17 +138,17 @@
<th *ngIf="validation"> <th *ngIf="validation">
<rb-form-checkbox name="validate-all" (change)="selectAll($event)">all</rb-form-checkbox> <rb-form-checkbox name="validate-all" (change)="selectAll($event)">all</rb-form-checkbox>
</th> </th>
<th *ngFor="let key of activeKeys"> <th *ngFor="let key of activeKeys" [title]="key.label">
<div class="sort-header"> <div class="sort-header">
<span>{{key.label}}</span> <span>{{key.label}}</span>
<ng-container *ngIf="key.sortable"> <div *ngIf="key.sortable">
<span class="rb-ic rb-ic-up sort-arr-up" (click)="setSort(key.id + '-' + 'desc')"> <span class="rb-ic rb-ic-up sort-arr-up" (click)="setSort(key.id + '-' + 'desc')">
<span *ngIf="filters.sort === key.id + '-' + 'desc'"></span> <span *ngIf="filters.sort === key.id + '-' + 'desc'"></span>
</span> </span>
<span class="rb-ic rb-ic-down sort-arr-down" (click)="setSort(key.id + '-' + 'asc')"> <span class="rb-ic rb-ic-down sort-arr-down" (click)="setSort(key.id + '-' + 'asc')">
<span *ngIf="filters.sort === key.id + '-' + 'asc'"></span> <span *ngIf="filters.sort === key.id + '-' + 'asc'"></span>
</span> </span>
</ng-container> </div>
</div> </div>
</th> </th>
<th *ngIf="login.isLevel.write"></th> <th *ngIf="login.isLevel.write"></th>
@ -165,6 +171,9 @@
<td *ngIf="isActiveKey['type']">{{sample.type}}</td> <td *ngIf="isActiveKey['type']">{{sample.type}}</td>
<td *ngIf="isActiveKey['color']">{{sample.color}}</td> <td *ngIf="isActiveKey['color']">{{sample.color}}</td>
<td *ngIf="isActiveKey['batch']">{{sample.batch}}</td> <td *ngIf="isActiveKey['batch']">{{sample.batch}}</td>
<td *ngFor="let key of activeTemplateKeys.condition">
{{sample.condition ? sample.condition[key[1]] : '' | exists}}
</td>
<td *ngIf="isActiveKey['notes']">{{sample.notes | object: ['_id', 'sample_references']}}</td> <td *ngIf="isActiveKey['notes']">{{sample.notes | object: ['_id', 'sample_references']}}</td>
<td *ngFor="let key of activeTemplateKeys.measurements">{{sample[key[1]] | exists: key[2]}}</td> <td *ngFor="let key of activeTemplateKeys.measurements">{{sample[key[1]] | exists: key[2]}}</td>
<td *ngIf="isActiveKey['status']">{{sample.status}}</td> <td *ngIf="isActiveKey['status']">{{sample.status}}</td>
@ -188,7 +197,7 @@
<button class="rb-btn rb-link" type="button" (click)="loadPage(-1)" [disabled]="page === 1"> <button class="rb-btn rb-link" type="button" (click)="loadPage(-1)" [disabled]="page === 1">
<span class="rb-ic rb-ic-back-left"></span> <span class="rb-ic rb-ic-back-left"></span>
</button> </button>
<rb-form-input label="page" (change)="loadPage({toPage: $event.target.value - page})" [ngModel]="page"> <rb-form-input label="page" (ngModelChange)="loadPage($event - page)" [ngModel]="page">
</rb-form-input> </rb-form-input>
<span> <span>
of {{pages}} ({{totalSamples}} samples) of {{pages}} ({{totalSamples}} samples)

View File

@ -54,7 +54,7 @@ rb-table {
.paging { .paging {
height: 50px; height: 50px;
rb-form-input { rb-form-input {
max-width: 50px; max-width: 65px;
} }
> * { > * {
@ -72,23 +72,34 @@ rb-table {
} }
.sort-header { .sort-header {
display: inline-grid;
grid-template-columns: 1fr auto;
grid-column-gap: 5px;
width: 100%; width: 100%;
position: relative;
:first-child { & > span:first-child {
grid-row: span 2; max-width: 180px;
overflow: hidden;
display: block;
text-overflow: ellipsis;
margin-right: 20px;
} }
:nth-child(2) { div {
margin-bottom: -3px; display: grid;
cursor: pointer; grid-template-columns: 1fr;
} position: absolute;
right: 0;
top: 0;
background: #FFF;
:nth-child(3) { :nth-child(1) {
margin-top: -3px; margin-bottom: -3px;
cursor: pointer; cursor: pointer;
}
:nth-child(2) {
margin-top: -3px;
cursor: pointer;
}
} }
} }

View File

@ -1,27 +1,72 @@
// import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { async, ComponentFixture, TestBed } from '@angular/core/testing';
//
// import { SamplesComponent } from './samples.component'; import { SamplesComponent } from './samples.component';
// import {ApiService} from '../services/api.service';
// // TODO: tests import {AutocompleteService} from '../services/autocomplete.service';
// import {DataService} from '../services/data.service';
// describe('SamplesComponent', () => { import {LoginService} from '../services/login.service';
// let component: SamplesComponent; import {LocalStorageService} from 'angular-2-local-storage';
// let fixture: ComponentFixture<SamplesComponent>; import {ModalService} from '@inst-iot/bosch-angular-ui-components';
// import {ValidationService} from '../services/validation.service';
// beforeEach(async(() => {
// TestBed.configureTestingModule({ // TODO
// declarations: [ SamplesComponent ]
// }) let apiServiceSpy: jasmine.SpyObj<ApiService>;
// .compileComponents(); let autocompleteServiceSpy: jasmine.SpyObj<AutocompleteService>;
// })); let modalServiceSpy: jasmine.SpyObj<ModalService>;
// let dataServiceSpy: jasmine.SpyObj<DataService>;
// beforeEach(() => { let loginServiceSpy: jasmine.SpyObj<LoginService>;
// fixture = TestBed.createComponent(SamplesComponent); let localStorageServiceSpy: jasmine.SpyObj<LocalStorageService>;
// component = fixture.componentInstance; let windowServiceSpy: jasmine.SpyObj<Window>;
// fixture.detectChanges();
// });
//
// it('should create', () => { describe('SamplesComponent', () => {
// expect(component).toBeTruthy(); let component: SamplesComponent;
// }); let fixture: ComponentFixture<SamplesComponent>;
// });
beforeEach(async(() => {
const apiSpy = jasmine.createSpyObj('ApiService', ['post', 'put']);
const autocompleteSpy = jasmine.createSpyObj('AutocompleteService', []);
const loginSpy = jasmine.createSpyObj('LoginService', ['login', 'canActivate']);
const modalSpy = jasmine.createSpyObj('ModalService', ['open']);
const dataSpy = jasmine.createSpyObj('DataService', ['load', 'idReload']);
const localStorageSpy = jasmine.createSpyObj('LocalStorageService', ['set', 'remove']);
const windowSpy = jasmine.createSpyObj('Window', []);
TestBed.configureTestingModule({
declarations: [ SamplesComponent ],
imports: [
],
providers: [
{provide: ApiService, useValue: apiSpy},
{provide: AutocompleteService, useValue: autocompleteSpy},
{provide: ModalService, useValue: modalSpy},
{provide: DataService, useValue: dataSpy},
{provide: LoginService, useValue: loginSpy},
{provide: LocalStorageService, useValue: localStorageSpy},
{provide: Window, useValue: windowSpy}
]
})
.compileComponents();
apiServiceSpy = TestBed.inject(ApiService) as jasmine.SpyObj<ApiService>;
autocompleteServiceSpy = TestBed.inject(AutocompleteService) as jasmine.SpyObj<AutocompleteService>;
modalServiceSpy = TestBed.inject(ModalService) as jasmine.SpyObj<ModalService>;
dataServiceSpy = TestBed.inject(DataService) as jasmine.SpyObj<DataService>;
loginServiceSpy = TestBed.inject(LoginService) as jasmine.SpyObj<LoginService>;
localStorageServiceSpy = TestBed.inject(LocalStorageService) as jasmine.SpyObj<LocalStorageService>;
windowServiceSpy = TestBed.inject(Window) as jasmine.SpyObj<Window>;
}));
beforeEach(() => {
fixture = TestBed.createComponent(SamplesComponent);
component = fixture.componentInstance;
component.ngOnInit();
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -8,6 +8,9 @@ import {SampleModel} from '../models/sample.model';
import {LoginService} from '../services/login.service'; import {LoginService} from '../services/login.service';
import {ModalService} from '@inst-iot/bosch-angular-ui-components'; import {ModalService} from '@inst-iot/bosch-angular-ui-components';
import {DataService} from '../services/data.service'; import {DataService} from '../services/data.service';
import {LocalStorageService} from 'angular-2-local-storage';
// TODO: turn off sort field
interface LoadSamplesOptions { interface LoadSamplesOptions {
@ -28,8 +31,6 @@ interface KeyInterface {
styleUrls: ['./samples.component.scss'] styleUrls: ['./samples.component.scss']
}) })
// TODO: save last settings
export class SamplesComponent implements OnInit { export class SamplesComponent implements OnInit {
@ViewChild('pageSizeSelection') pageSizeSelection: ElementRef<HTMLElement>; @ViewChild('pageSizeSelection') pageSizeSelection: ElementRef<HTMLElement>;
@ -79,7 +80,7 @@ export class SamplesComponent implements OnInit {
]; ];
isActiveKey: {[key: string]: boolean} = {}; isActiveKey: {[key: string]: boolean} = {};
activeKeys: KeyInterface[] = []; activeKeys: KeyInterface[] = [];
activeTemplateKeys = {material: [], measurements: []}; activeTemplateKeys = {material: [], condition: [], measurements: []};
sampleDetailsSample: any = null; sampleDetailsSample: any = null;
validation = false; // true to activate validation mode validation = false; // true to activate validation mode
@ -89,29 +90,41 @@ export class SamplesComponent implements OnInit {
public autocomplete: AutocompleteService, public autocomplete: AutocompleteService,
public login: LoginService, public login: LoginService,
private modalService: ModalService, private modalService: ModalService,
public d: DataService public d: DataService,
private storage: LocalStorageService,
private window: Window
) { ) {
} }
ngOnInit(): void { ngOnInit(): void {
let loading = 8;
const onLoad = () => {
if ((--loading) <= 0) {
this.loadSamples();
}
};
this.calcFieldSelectKeys(); this.calcFieldSelectKeys();
this.d.load('materials', () => { this.d.load('materials', () => {
this.filters.filters.find(e => e.field === 'material.name').autocomplete = this.d.arr.materials.map(e => e.name); this.filters.filters.find(e => e.field === 'material.name').autocomplete = this.d.arr.materials.map(e => e.name);
this.loadSamples(); onLoad();
}); });
this.d.load('materialSuppliers', () => { this.d.load('materialSuppliers', () => {
this.filters.filters.find(e => e.field === 'material.supplier').autocomplete = this.d.arr.materialSuppliers; this.filters.filters.find(e => e.field === 'material.supplier').autocomplete = this.d.arr.materialSuppliers;
onLoad();
}); });
this.d.load('materialGroups', () => { this.d.load('materialGroups', () => {
this.filters.filters.find(e => e.field === 'material.group').autocomplete = this.d.arr.materialGroups; this.filters.filters.find(e => e.field === 'material.group').autocomplete = this.d.arr.materialGroups;
onLoad();
}); });
this.d.load('userKey'); this.d.load('userKey', onLoad);
this.d.load('conditionTemplates'); this.d.load('conditionTemplates', onLoad);
this.loadTemplateKeys('material', 'type'); this.loadTemplateKeys('material', 'type', onLoad);
this.loadTemplateKeys('measurement', 'status'); this.loadTemplateKeys('condition', 'notes', onLoad);
this.loadTemplateKeys('measurement', 'status', onLoad);
} }
loadTemplateKeys(collection, insertBefore) { loadTemplateKeys(collection, insertBefore, f) {
this.d.load(collection + 'Templates', () => { this.d.load(collection + 'Templates', () => {
const templateKeys = []; const templateKeys = [];
this.d.arr[collection + 'Templates'].forEach(item => { this.d.arr[collection + 'Templates'].forEach(item => {
@ -119,15 +132,16 @@ export class SamplesComponent implements OnInit {
const parameterName = encodeURIComponent(parameter.name); const parameterName = encodeURIComponent(parameter.name);
// exclude spectrum and duplicates // exclude spectrum and duplicates
if (parameter.name !== 'dpt' && !templateKeys.find(e => new RegExp('.' + parameterName + '$').test(e.id))) { if (parameter.name !== 'dpt' && !templateKeys.find(e => new RegExp('.' + parameterName + '$').test(e.id))) {
const collectionNames = {material: 'material.properties', condition: 'condition', measurement: 'measurements.' + item.name};
templateKeys.push({ templateKeys.push({
id: `${collection === 'material' ? 'material.properties' : collection + 's.' + item.name}.${parameterName}`, id: `${collectionNames[collection]}.${parameterName}`,
label: `${this.ucFirst(item.name)} ${this.ucFirst(parameter.name)}`, label: `${this.ucFirst(item.name)} ${parameter.name}`,
active: false, active: false,
sortable: true sortable: true
}); });
this.filters.filters.push({ this.filters.filters.push({
field: `${collection === 'material' ? 'material.properties' : collection + 's.' + item.name}.${parameterName}`, field: `${collectionNames[collection]}.${parameterName}`,
label: `${this.ucFirst(item.name)} ${this.ucFirst(parameter.name)}`, label: `${this.ucFirst(item.name)} ${parameter.name}`,
active: false, active: false,
autocomplete: [], autocomplete: [],
mode: 'eq', mode: 'eq',
@ -138,12 +152,13 @@ export class SamplesComponent implements OnInit {
}); });
this.keys.splice(this.keys.findIndex(e => e.id === insertBefore), 0, ...templateKeys); this.keys.splice(this.keys.findIndex(e => e.id === insertBefore), 0, ...templateKeys);
this.keys = [...this.keys]; // complete overwrite array to invoke update in rb-multiselect this.keys = [...this.keys]; // complete overwrite array to invoke update in rb-multiselect
this.updateActiveKeys(); this.loadPreferences();
this.calcFieldSelectKeys(); f();
}); });
} }
loadSamples(options: LoadSamplesOptions = {}, event = null) { // set toPage to null to reload first page, queues calls loadSamples(options: LoadSamplesOptions = {}, event = null) { // set toPage to null to reload first page, queues calls
console.log(this.isActiveKey);
if (event) { // adjust active keys if (event) { // adjust active keys
this.keys.forEach(key => { this.keys.forEach(key => {
if (event.hasOwnProperty(key.id)) { if (event.hasOwnProperty(key.id)) {
@ -156,18 +171,25 @@ export class SamplesComponent implements OnInit {
if (this.loadSamplesQueue.length <= 1) { // nothing queued up if (this.loadSamplesQueue.length <= 1) { // nothing queued up
this.sampleLoader(this.loadSamplesQueue[0]); this.sampleLoader(this.loadSamplesQueue[0]);
} }
this.storePreferences();
} }
private sampleLoader(options: LoadSamplesOptions) { // actual loading of the sample, do not call directly private sampleLoader(options: LoadSamplesOptions) { // actual loading of the sample, do not call directly
this.api.get(this.sampleUrl({paging: true, pagingOptions: options}), (sData, ignore, headers) => { this.api.get(this.sampleUrl({paging: true, pagingOptions: options}), (sData, err, headers) => {
if (!options.toPage && headers['x-total-items']) { if (err) {
this.totalSamples = headers['x-total-items']; this.storage.remove('samplesPreferences');
this.api.requestError(err);
} }
this.pages = Math.ceil(this.totalSamples / this.filters.pageSize); else {
this.samples = sData as any; if (!options.toPage && headers['x-total-items']) {
this.loadSamplesQueue.shift(); this.totalSamples = headers['x-total-items'];
if (this.loadSamplesQueue.length > 0) { // execute next queue item }
this.sampleLoader(this.loadSamplesQueue[0]); this.pages = Math.ceil(this.totalSamples / this.filters.pageSize);
this.samples = sData as any;
this.loadSamplesQueue.shift();
if (this.loadSamplesQueue.length > 0) { // execute next queue item
this.sampleLoader(this.loadSamplesQueue[0]);
}
} }
}); });
} }
@ -206,7 +228,7 @@ export class SamplesComponent implements OnInit {
} }
this.keys.forEach(key => { this.keys.forEach(key => {
// do not load material properties for table // do not load material properties for table
if (key.active && (options.export || (!options.export && key.id.indexOf('material') < 0))) { if (key.active && (options.export || (!options.export && key.id.indexOf('material.') < 0))) {
query.push('fields[]=' + key.id); query.push('fields[]=' + key.id);
} }
}); });
@ -243,12 +265,13 @@ export class SamplesComponent implements OnInit {
query.push('fields[]=condition'); query.push('fields[]=condition');
} }
} }
return (options.host && isDevMode() ? window.location.host : '') + return (options.host && isDevMode() ? this.window.location.host : '') +
(options.export ? this.api.hostName : '') + (options.export ? this.api.hostName : '') +
'/samples?' + query.join('&'); '/samples?' + query.join('&');
} }
loadPage(delta) { loadPage(delta) {
console.log(delta);
if (!/[0-9]+/.test(delta) || (this.page <= 1 && delta < 0)) { // invalid delta if (!/[0-9]+/.test(delta) || (this.page <= 1 && delta < 0)) { // invalid delta
return; return;
} }
@ -256,6 +279,38 @@ export class SamplesComponent implements OnInit {
this.loadSamples({toPage: delta}); this.loadSamples({toPage: delta});
} }
storePreferences() {
const store = {
filters: {
...pick(this.filters, ['status', 'pageSize', 'toPage', 'sort']),
filters: this.filters.filters.map(e => pick(e, ['field', 'active', 'mode', 'values']))
},
keys: this.keys.map(e => pick(e, ['id', 'active']))
};
this.storage.set('samplesPreferences', store);
}
loadPreferences() {
const store: any = this.storage.get('samplesPreferences');
if (store) {
this.filters = {...this.filters, ...pick(store.filters, ['status', 'pageSize', 'toPage', 'sort'])};
store.filters.filters.forEach(filter => {
const filterIndex = this.filters.filters.findIndex(e => e.field === filter.field);
if (filterIndex >= 0) {
this.filters.filters[filterIndex] = {...this.filters.filters[filterIndex], ...filter};
}
});
store.keys.forEach(key => {
const keyIndex = this.keys.findIndex(e => e.id === key.id);
if (keyIndex >= 0) {
this.keys[keyIndex].active = key.active;
}
});
}
this.calcFieldSelectKeys();
this.updateActiveKeys();
}
updateFilterFields(field) { updateFilterFields(field) {
const filter = this.filters.filters.find(e => e.field === field); const filter = this.filters.filters.find(e => e.field === field);
filter.active = true; filter.active = true;
@ -271,10 +326,18 @@ export class SamplesComponent implements OnInit {
updateActiveKeys() { // array with all activeKeys updateActiveKeys() { // array with all activeKeys
this.activeKeys = this.keys.filter(e => e.active); this.activeKeys = this.keys.filter(e => e.active);
this.filters.filters.forEach(filter => {
if (!this.isActiveKey[filter.field]) {
filter.active = false;
}
});
this.activeTemplateKeys.material = this.keys this.activeTemplateKeys.material = this.keys
.filter(e => e.id.indexOf('material.properties.') >= 0 && e.active) .filter(e => e.id.indexOf('material.properties.') >= 0 && e.active)
.map(e => e.id.split('.') .map(e => e.id.split('.')
.map(el => decodeURIComponent(el))); .map(el => decodeURIComponent(el)));
this.activeTemplateKeys.condition = this.keys.filter(e => e.id.indexOf('condition.') >= 0 && e.active)
.map(e => e.id.split('.')
.map(el => decodeURIComponent(el)));
this.activeTemplateKeys.measurements = this.keys.filter(e => e.id.indexOf('measurements.') >= 0 && e.active) this.activeTemplateKeys.measurements = this.keys.filter(e => e.id.indexOf('measurements.') >= 0 && e.active)
.map(e => e.id.split('.') .map(e => e.id.split('.')
.map(el => decodeURIComponent(el))); .map(el => decodeURIComponent(el)));
@ -390,4 +453,6 @@ export class SamplesComponent implements OnInit {
ucFirst(string) { ucFirst(string) {
return string[0].toUpperCase() + string.slice(1); return string[0].toUpperCase() + string.slice(1);
} }
} }

View File

@ -10,6 +10,7 @@ let httpClientSpy: jasmine.SpyObj<HttpClient>;
let localStorageServiceSpy: jasmine.SpyObj<LocalStorageService>; let localStorageServiceSpy: jasmine.SpyObj<LocalStorageService>;
let modalServiceSpy: jasmine.SpyObj<ModalService>; let modalServiceSpy: jasmine.SpyObj<ModalService>;
// TODO
// TODO: test options // TODO: test options
describe('ApiService', () => { describe('ApiService', () => {

View File

@ -25,43 +25,57 @@ export class ApiService {
} }
get<T>(url, f: (data?: T, err?, headers?) => void = () => {}) { get<T>(url, f: (data?: T, err?, headers?) => void = () => {}) {
this.requestErrorHandler<T>(this.http.get(this.host + url, this.options()), f); this.requestErrorHandler<T>(this.http.get(this.url(url), this.options()), f);
} }
post<T>(url, data = null, f: (data?: T, err?, headers?) => void = () => {}) { post<T>(url, data = null, f: (data?: T, err?, headers?) => void = () => {}) {
this.requestErrorHandler<T>(this.http.post(this.host + url, data, this.options()), f); this.requestErrorHandler<T>(this.http.post(this.url(url), data, this.options()), f);
} }
put<T>(url, data = null, f: (data?: T, err?, headers?) => void = () => {}) { put<T>(url, data = null, f: (data?: T, err?, headers?) => void = () => {}) {
this.requestErrorHandler<T>(this.http.put(this.host + url, data, this.options()), f); this.requestErrorHandler<T>(this.http.put(this.url(url), data, this.options()), f);
} }
delete<T>(url, f: (data?: T, err?, headers?) => void = () => {}) { delete<T>(url, f: (data?: T, err?, headers?) => void = () => {}) {
this.requestErrorHandler<T>(this.http.delete(this.host + url, this.options()), f); this.requestErrorHandler<T>(this.http.delete(this.url(url), this.options()), f);
} }
private requestErrorHandler<T>(observable: Observable<any>, f: (data?: T, err?, headers?) => void) { private requestErrorHandler<T>(observable: Observable<any>, f: (data?: T, err?, headers?) => void) {
observable.subscribe(data => { observable.subscribe(data => {
f(data.body, undefined, data.headers.keys().reduce((s, e) => {s[e.toLowerCase()] = data.headers.get(e); return s; }, {})); f(data.body, undefined, data.headers.keys().reduce((s, e) => {s[e.toLowerCase()] = data.headers.get(e); return s; }, {}));
}, err => { }, err => {
if (f.length === 2) { console.log(f.length);
f(undefined, err); if (f.length > 1) {
f(undefined, err, undefined);
} }
else { else {
const modalRef = this.modalService.openComponent(ErrorComponent); this.requestError(err);
modalRef.instance.message = 'Network request failed!';
const details = [err.error.status];
if (err.error.details) {
details.push(err.error.details);
}
modalRef.instance.details = details;
modalRef.result.then(() => {
this.window.location.reload();
});
} }
}); });
} }
requestError(err) {
const modalRef = this.modalService.openComponent(ErrorComponent);
modalRef.instance.message = 'Network request failed!';
const details = [err.error.status];
if (err.error.details) {
details.push(err.error.details);
}
modalRef.instance.details = details;
modalRef.result.then(() => {
this.window.location.reload();
});
}
private url(url) {
if (/http[s]?:\/\//.test(url)) {
return url;
}
else {
return this.host + url;
}
}
private options(): {headers: HttpHeaders, observe: 'body'} { private options(): {headers: HttpHeaders, observe: 'body'} {
return {headers: this.authOptions(), observe: 'response' as 'body'}; return {headers: this.authOptions(), observe: 'response' as 'body'};
} }

View File

@ -2,6 +2,8 @@ import { TestBed } from '@angular/core/testing';
import { AutocompleteService } from './autocomplete.service'; import { AutocompleteService } from './autocomplete.service';
// TODO
let autocompleteService: AutocompleteService; let autocompleteService: AutocompleteService;
describe('AutocompleteService', () => { describe('AutocompleteService', () => {

View File

@ -1,13 +1,30 @@
import { TestBed } from '@angular/core/testing'; import { TestBed } from '@angular/core/testing';
import { DataService } from './data.service'; import { DataService } from './data.service';
import {ApiService} from './api.service';
import {HttpClient} from '@angular/common/http';
import {LocalStorageService} from 'angular-2-local-storage';
import {ModalService} from '@inst-iot/bosch-angular-ui-components';
// TODO
let apiServiceSpy: jasmine.SpyObj<ApiService>;
describe('DataService', () => { describe('DataService', () => {
let service: DataService; let service: DataService;
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({}); const apiSpy = jasmine.createSpyObj('ApiService', ['post', 'put']);
TestBed.configureTestingModule({
providers: [
{provide: ApiService, useValue: apiSpy}
]
});
service = TestBed.inject(DataService); service = TestBed.inject(DataService);
apiServiceSpy = TestBed.inject(ApiService) as jasmine.SpyObj<ApiService>;
}); });
it('should be created', () => { it('should be created', () => {

View File

@ -4,6 +4,8 @@ import { LoginService } from './login.service';
import {LocalStorageService} from 'angular-2-local-storage'; import {LocalStorageService} from 'angular-2-local-storage';
import {ApiService} from './api.service'; import {ApiService} from './api.service';
// TODO
let loginService: LoginService; let loginService: LoginService;
let apiServiceSpy: jasmine.SpyObj<ApiService>; let apiServiceSpy: jasmine.SpyObj<ApiService>;
let localStorageServiceSpy: jasmine.SpyObj<LocalStorageService>; let localStorageServiceSpy: jasmine.SpyObj<LocalStorageService>;

View File

@ -1,6 +1,8 @@
import { TestBed } from '@angular/core/testing'; import { TestBed } from '@angular/core/testing';
import { ValidationService } from './validation.service'; import { ValidationService } from './validation.service';
// TODO
let validationService: ValidationService; let validationService: ValidationService;
describe('ValidationService', () => { describe('ValidationService', () => {

View File

@ -1,21 +1,45 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { SettingsComponent } from './settings.component'; import { SettingsComponent } from './settings.component';
import {ApiService} from '../services/api.service';
import {LoginService} from '../services/login.service';
import {Router} from '@angular/router';
// TODO
let routerServiceSpy: jasmine.SpyObj<Router>;
let apiServiceSpy: jasmine.SpyObj<ApiService>;
let loginServiceSpy: jasmine.SpyObj<LoginService>;
describe('SettingsComponent', () => { describe('SettingsComponent', () => {
let component: SettingsComponent; let component: SettingsComponent;
let fixture: ComponentFixture<SettingsComponent>; let fixture: ComponentFixture<SettingsComponent>;
beforeEach(async(() => { beforeEach(async(() => {
const routerSpy = jasmine.createSpyObj('Router', ['navigate']);
const apiSpy = jasmine.createSpyObj('ApiService', ['post', 'put']);
const loginSpy = jasmine.createSpyObj('LoginService', ['login', 'canActivate']);
TestBed.configureTestingModule({ TestBed.configureTestingModule({
declarations: [ SettingsComponent ] declarations: [ SettingsComponent ],
providers: [
{provide: Router, useValue: routerSpy},
{provide: ApiService, useValue: apiSpy},
{provide: LoginService, useValue: loginSpy},
]
}) })
.compileComponents(); .compileComponents();
routerServiceSpy = TestBed.inject(Router) as jasmine.SpyObj<Router>;
apiServiceSpy = TestBed.inject(ApiService) as jasmine.SpyObj<ApiService>;
loginServiceSpy = TestBed.inject(LoginService) as jasmine.SpyObj<LoginService>;
})); }));
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(SettingsComponent); fixture = TestBed.createComponent(SettingsComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
component.ngOnInit();
fixture.detectChanges(); fixture.detectChanges();
}); });

View File

@ -1,21 +1,51 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { TemplatesComponent } from './templates.component'; import { TemplatesComponent } from './templates.component';
import {LoginService} from '../services/login.service';
import {ValidationService} from '../services/validation.service';
import {ApiService} from '../services/api.service';
import {DataService} from '../services/data.service';
import {RbUiComponentsModule} from '@inst-iot/bosch-angular-ui-components';
import {FormsModule} from '@angular/forms';
// TODO
let apiServiceSpy: jasmine.SpyObj<ApiService>;
let validationServiceSpy: jasmine.SpyObj<ValidationService>;
let dataServiceSpy: jasmine.SpyObj<DataService>;
describe('TemplatesComponent', () => { describe('TemplatesComponent', () => {
let component: TemplatesComponent; let component: TemplatesComponent;
let fixture: ComponentFixture<TemplatesComponent>; let fixture: ComponentFixture<TemplatesComponent>;
beforeEach(async(() => { beforeEach(async(() => {
const apiSpy = jasmine.createSpyObj('ApiService', ['post', 'put']);
const validationSpy = jasmine.createSpyObj('ValidationService', ['string', 'parameterName', 'parameterRange']);
const dataSpy = jasmine.createSpyObj('DataService', ['load', 'idReload']);
TestBed.configureTestingModule({ TestBed.configureTestingModule({
declarations: [ TemplatesComponent ] declarations: [ TemplatesComponent ],
imports: [
RbUiComponentsModule,
FormsModule
],
providers: [
{provide: ApiService, useValue: apiSpy},
{provide: ValidationService, useValue: validationSpy},
{provide: DataService, useValue: dataSpy}
]
}) })
.compileComponents(); .compileComponents();
apiServiceSpy = TestBed.inject(ApiService) as jasmine.SpyObj<ApiService>;
validationServiceSpy = TestBed.inject(ValidationService) as jasmine.SpyObj<ValidationService>;
dataServiceSpy = TestBed.inject(DataService) as jasmine.SpyObj<DataService>;
})); }));
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(TemplatesComponent); fixture = TestBed.createComponent(TemplatesComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
component.ngOnInit();
fixture.detectChanges(); fixture.detectChanges();
}); });

View File

@ -1,21 +1,45 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { UsersComponent } from './users.component'; import { UsersComponent } from './users.component';
import {ApiService} from '../services/api.service';
import {LoginService} from '../services/login.service';
import {ModalService} from '@inst-iot/bosch-angular-ui-components';
// TODO
let apiServiceSpy: jasmine.SpyObj<ApiService>;
let modalServiceSpy: jasmine.SpyObj<ModalService>;
let loginServiceSpy: jasmine.SpyObj<LoginService>;
describe('UsersComponent', () => { describe('UsersComponent', () => {
let component: UsersComponent; let component: UsersComponent;
let fixture: ComponentFixture<UsersComponent>; let fixture: ComponentFixture<UsersComponent>;
beforeEach(async(() => { beforeEach(async(() => {
const apiSpy = jasmine.createSpyObj('ApiService', ['post', 'put']);
const modalSpy = jasmine.createSpyObj('ModalService', ['open']);
const loginSpy = jasmine.createSpyObj('LoginService', ['login', 'canActivate']);
TestBed.configureTestingModule({ TestBed.configureTestingModule({
declarations: [ UsersComponent ] declarations: [ UsersComponent ],
providers: [
{provide: ApiService, useValue: apiSpy},
{provide: ModalService, useValue: modalSpy},
{provide: LoginService, useValue: loginSpy},
]
}) })
.compileComponents(); .compileComponents();
apiServiceSpy = TestBed.inject(ApiService) as jasmine.SpyObj<ApiService>;
modalServiceSpy = TestBed.inject(ModalService) as jasmine.SpyObj<ModalService>;
loginServiceSpy = TestBed.inject(LoginService) as jasmine.SpyObj<LoginService>;
})); }));
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(UsersComponent); fixture = TestBed.createComponent(UsersComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
component.ngOnInit();
fixture.detectChanges(); fixture.detectChanges();
}); });

View File

@ -1,8 +1,14 @@
// import { ValidateDirective } from './validate.directive'; // import { ValidateDirective } from './validate.directive';
// import {ValidationService} from './services/validation.service';
//
// // TODO
//
// const validationSpy = {test: () => {}};
// const validationServiceSpy: jasmine.SpyObj<any> = spyOn(validationSpy, 'test');
// //
// describe('ValidateDirective', () => { // describe('ValidateDirective', () => {
// it('should create an instance', () => { // it('should create an instance', () => {
// const directive = new ValidateDirective(); // const directive = new ValidateDirective(validationServiceSpy);
// expect(directive).toBeTruthy(); // expect(directive).toBeTruthy();
// }); // });
// }); // });

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 235 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -6,8 +6,11 @@
<base href="/"> <base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico"> <link rel="icon" type="image/x-icon" href="favicon.ico">
<link rel="manifest" href="manifest.webmanifest">
<meta name="theme-color" content="#1976d2">
</head> </head>
<body> <body>
<app-root></app-root> <app-root></app-root>
<noscript>Please enable JavaScript to continue using this application.</noscript>
</body> </body>
</html> </html>

59
src/manifest.webmanifest Normal file
View File

@ -0,0 +1,59 @@
{
"name": "definma",
"short_name": "definma",
"theme_color": "#1976d2",
"background_color": "#fafafa",
"display": "standalone",
"scope": "./",
"start_url": "./",
"icons": [
{
"src": "assets/icons/icon-72x72.png",
"sizes": "72x72",
"type": "image/png",
"purpose": "maskable any"
},
{
"src": "assets/icons/icon-96x96.png",
"sizes": "96x96",
"type": "image/png",
"purpose": "maskable any"
},
{
"src": "assets/icons/icon-128x128.png",
"sizes": "128x128",
"type": "image/png",
"purpose": "maskable any"
},
{
"src": "assets/icons/icon-144x144.png",
"sizes": "144x144",
"type": "image/png",
"purpose": "maskable any"
},
{
"src": "assets/icons/icon-152x152.png",
"sizes": "152x152",
"type": "image/png",
"purpose": "maskable any"
},
{
"src": "assets/icons/icon-192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "maskable any"
},
{
"src": "assets/icons/icon-384x384.png",
"sizes": "384x384",
"type": "image/png",
"purpose": "maskable any"
},
{
"src": "assets/icons/icon-512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable any"
}
]
}