Commit f2204b92 authored by Vitali Stupin's avatar Vitali Stupin

Merge pull request #8 in XTSS/xtss-catalogue from release to master

* commit '2c156c86': (30 commits)
  Corrected icon color
  Updating packages
  fix instance switching
  Adding catalogue history support
  Scroll to top, warn when more subsystems exist
  Update karma package
  Updating packages
  Creating component for displaying messages
  Getting filter limits from configuration
  Changing unit tests
  Loading configuration from assets
  Adding test-docker
  Fixing coverage directory name
  Rename project name
  fixing lint
  test lint problem
  fixing missing translations
  Adding unit tests
  adding tests
  Adding sonar, remove empty css
  ...
parents d6361b55 2c156c86
......@@ -44,3 +44,6 @@ testem.log
# System Files
.DS_Store
Thumbs.db
### Sonar ###
.scannerwork
......@@ -3,26 +3,38 @@
## Building
```
sudo apt install nodejs
sudo apt install npm
sudo npm install -g npm@latest
sudo npm install -g @angular/cli
sudo apt install nodejs npm
sudo -H npm install -g npm@latest
sudo -H npm install -g @angular/cli
git clone <this_repository>
npm install
cd <this_repository_name>
npm ci
npm run lint
npm run test-headless
```
## Testing locally
## Sonarqube
By default `http://localhost:9000` is used as a sonarqube server.
If you have a remote sonarqube server, update `sonar-project.properties` cunfiguration file and run the test with:
```
ng serve --host 0.0.0.0
npm run sonar
```
Alternatively you can provide hostname and access token with command line:
```
./node_modules/sonar-scanner/bin/sonar-scanner -Dsonar.host.url=<server> -Dsonar.login=<token>
```
## Update angular
## Updating angular version
```
ng update @angular/cli @angular/core
```
## Deploy for local manual testing
```
ng serve --host 0.0.0.0
```
## Build for production
```
ng build --prod --base-href /methods/
ng build --prod --base-href /catalogue/
```
# Methods
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 7.3.2.
## Development server
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
## Code scaffolding
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
## Build
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
## Running unit tests
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
## Running end-to-end tests
Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
## Further help
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
......@@ -3,7 +3,7 @@
"version": 1,
"newProjectRoot": "projects",
"projects": {
"methods": {
"xtss-catalogue": {
"root": "",
"sourceRoot": "src",
"projectType": "application",
......@@ -13,7 +13,7 @@
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/methods",
"outputPath": "dist/catalogue",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
......@@ -58,23 +58,24 @@
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "methods:build"
"browserTarget": "xtss-catalogue:build"
},
"configurations": {
"production": {
"browserTarget": "methods:build:production"
"browserTarget": "xtss-catalogue:build:production"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "methods:build"
"browserTarget": "xtss-catalogue:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"codeCoverage": true,
"main": "src/test.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "src/tsconfig.spec.json",
......@@ -103,7 +104,7 @@
}
}
},
"methods-e2e": {
"xtss-catalogue-e2e": {
"root": "e2e/",
"projectType": "application",
"prefix": "",
......@@ -112,11 +113,11 @@
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "e2e/protractor.conf.js",
"devServerTarget": "methods:serve"
"devServerTarget": "xtss-catalogue:serve"
},
"configurations": {
"production": {
"devServerTarget": "methods:serve:production"
"devServerTarget": "xtss-catalogue:serve:production"
}
}
},
......@@ -132,5 +133,5 @@
}
}
},
"defaultProject": "methods"
"defaultProject": "xtss-catalogue"
}
\ No newline at end of file
......@@ -10,7 +10,7 @@ describe('workspace-project App', () => {
it('should display welcome message', () => {
page.navigateTo();
expect(page.getTitleText()).toEqual('Welcome to methods!');
expect(page.getTitleText()).toEqual('Welcome to xtss-catalogue!');
});
afterEach(async () => {
......
This diff is collapsed.
{
"name": "methods",
"version": "0.0.0",
"name": "xtss-catalogue",
"version": "0.2.0",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"test": "ng test",
"test-headless": "ng test --watch=false --browsers=ChromiumHeadless",
"test-docker": "ng test --watch=false --browsers=ChromiumDocker",
"lint": "ng lint",
"e2e": "ng e2e"
"e2e": "ng e2e",
"sonar": "sonar-scanner"
},
"private": true,
"dependencies": {
"@angular/animations": "^7.2.7",
"@angular/common": "^7.2.7",
"@angular/compiler": "^7.2.7",
"@angular/core": "^7.2.7",
"@angular/forms": "^7.2.7",
"@angular/platform-browser": "^7.2.7",
"@angular/platform-browser-dynamic": "^7.2.7",
"@angular/router": "^7.2.7",
"@angular/animations": "^7.2.13",
"@angular/common": "^7.2.13",
"@angular/compiler": "^7.2.13",
"@angular/core": "^7.2.13",
"@angular/forms": "^7.2.13",
"@angular/platform-browser": "^7.2.13",
"@angular/platform-browser-dynamic": "^7.2.13",
"@angular/router": "^7.2.13",
"@ngx-translate/core": "^11.0.1",
"@ngx-translate/http-loader": "^4.0.0",
"bootstrap": "^4.3.1",
......@@ -28,22 +31,23 @@
"zone.js": "~0.8.26"
},
"devDependencies": {
"@angular-devkit/build-angular": "^0.13.3",
"@angular/cli": "~7.3.3",
"@angular/compiler-cli": "^7.2.7",
"@angular/language-service": "^7.2.7",
"@angular-devkit/build-angular": "^0.13.8",
"@angular/cli": "~7.3.8",
"@angular/compiler-cli": "^7.2.13",
"@angular/language-service": "^7.2.13",
"@types/jasmine": "~2.8.8",
"@types/jasminewd2": "~2.0.3",
"@types/node": "~8.9.4",
"codelyzer": "~4.5.0",
"jasmine-core": "~2.99.1",
"jasmine-spec-reporter": "~4.2.1",
"karma": "~3.1.1",
"karma": "^4.0.1",
"karma-chrome-launcher": "~2.2.0",
"karma-coverage-istanbul-reporter": "~2.0.1",
"karma-jasmine": "~1.1.2",
"karma-jasmine-html-reporter": "^0.2.2",
"protractor": "~5.4.0",
"sonar-scanner": "^3.1.0",
"ts-node": "~7.0.0",
"tslint": "~5.11.0",
"typescript": "~3.2.2"
......
sonar.projectKey=xtss-catalogue
sonar.sources=src
sonar.sourceEncoding=UTF-8
sonar.exclusions=**/node_modules/**,**/*.spec.ts,src/environments/**,src/karma.conf.js,src/main.ts
sonar.tests=src
sonar.test.inclusions=**/*.spec.ts
sonar.typescript.lcov.reportPaths=coverage/xtss-catalogue/lcov.info
sonar.host.url=http://localhost:9000
#sonar.host.url=https://<some-remote-server>
#sonar.login=<secret-token>
import { AppRoutingModule } from './app-routing.module';
describe('AppRoutingModule', () => {
let module: AppRoutingModule;
beforeEach(() => {
module = new AppRoutingModule();
});
it('should be created', () => {
expect(module).toBeTruthy();
});
});
......@@ -15,4 +15,4 @@ const routes: Routes = [
imports: [ RouterModule.forRoot(routes, {scrollPositionRestoration: 'enabled'}) ],
exports: [ RouterModule ]
})
export class AppRoutingModule {}
\ No newline at end of file
export class AppRoutingModule {}
......@@ -2,4 +2,4 @@
<router-outlet></router-outlet>
</div>
\ No newline at end of file
</div>
import { TestBed, async } from '@angular/core/testing';
import {RouterTestingModule} from '@angular/router/testing'
import { RouterTestingModule } from '@angular/router/testing';
import { AppComponent } from './app.component';
describe('AppComponent', () => {
......
......@@ -2,8 +2,7 @@ import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
templateUrl: './app.component.html'
})
export class AppComponent {
}
import { Injectable } from '@angular/core';
import { AppConfig } from './app.config';
@Injectable()
export class AppConfigMock extends AppConfig {
private configMock: any = {
MAX_LIMIT: 1000000,
DEFAULT_LIMIT: 10,
LIMITS: {
10: 10,
20: 20,
50: 50
},
INSTANCES: {
EE: 'https://www.x-tee.ee/catalogue/EE/wsdls/',
'ee-test': 'https://www.x-tee.ee/catalogue/ee-test/wsdls/',
'ee-dev': 'https://www.x-tee.ee/catalogue/ee-dev/wsdls/'
},
API_SERVICE: 'index.json',
API_HISTORY: 'history.json',
HISTORY_LIMIT: 30,
LANGUAGES: {
EST: 'est',
ENG: 'eng'
},
PREVIEW_SIZE: 5,
// Smaller value for faster unit testing
FILTER_DEBOUNCE: 20
};
public getConfig(key: any) {
return this.configMock[key];
}
}
import { AppConfig } from './app.config';
import { of } from 'rxjs';
describe('AppConfig', () => {
let config: AppConfig;
let httpClientSpy: { get: jasmine.Spy };
beforeEach(() => {
httpClientSpy = jasmine.createSpyObj('HttpClient', ['get']);
config = new AppConfig(httpClientSpy as any);
});
it('should be created', () => {
expect(config).toBeTruthy();
});
it('should load configuration', async () => {
httpClientSpy.get.and.returnValue(of({TEST: 'OK'}));
await config.load();
expect(httpClientSpy.get).toHaveBeenCalledWith('./assets/config.json');
});
it('getConfig should work', async () => {
httpClientSpy.get.and.returnValue(of({TEST: 'OK'}));
await config.load();
expect(config.getConfig('TEST')).toBe('OK');
});
});
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
@Injectable()
export class AppConfig {
private config: any = null;
constructor(private http: HttpClient) { }
/**
* Use to get the data found in the config file
*/
public getConfig(key: any) {
return this.config[key];
}
/**
* This method loads "config.json" to get all configuration variables
*/
public load() {
return new Promise(resolve => {
// Not handling errors. App cannot work without valid configuration
this.http.get<any>('./assets/config.json')
.subscribe(responseData => {
this.config = responseData;
resolve(true);
});
});
}
}
import { AppModule, HttpLoaderFactory } from './app.module';
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
import { AppConfig } from './app.config';
import { TestBed } from '@angular/core/testing';
describe('AppModule', () => {
let appModule: AppModule;
let httpClientSpy: { get: jasmine.Spy };
let appConfigSpy: { load: jasmine.Spy };
beforeEach(() => {
httpClientSpy = jasmine.createSpyObj('HttpClient', ['get']);
appConfigSpy = jasmine.createSpyObj('HttpClient', ['load']);
appModule = new AppModule();
});
it('should be created', () => {
expect(appModule).toBeTruthy();
});
it('HttpLoaderFactory should work', () => {
expect(HttpLoaderFactory(httpClientSpy as any) instanceof TranslateHttpLoader).toBeTruthy();
});
it('AppConfig should be initialized', async () => {
TestBed.configureTestingModule({
imports: [ AppModule ],
providers: [
{ provide: AppConfig, useValue: appConfigSpy }
]
});
expect(TestBed.get(AppConfig)).toBeTruthy();
expect(appConfigSpy.load).toHaveBeenCalledTimes(1);
});
});
......@@ -11,6 +11,9 @@ import { SubsystemItemComponent } from './subsystem-list/subsystem-item/subsyste
import { AppRoutingModule } from './app-routing.module';
import { SubsystemComponent } from './subsystem/subsystem.component';
import { HeaderComponent } from './header/header.component';
import { APP_INITIALIZER } from '@angular/core';
import { AppConfig } from './app.config';
import { MessagesComponent } from './messages/messages.component';
@NgModule({
declarations: [
......@@ -19,7 +22,8 @@ import { HeaderComponent } from './header/header.component';
SearchComponent,
SubsystemItemComponent,
SubsystemComponent,
HeaderComponent
HeaderComponent,
MessagesComponent
],
imports: [
BrowserModule,
......@@ -34,7 +38,10 @@ import { HeaderComponent } from './header/header.component';
}
})
],
providers: [],
providers: [
AppConfig,
{ provide: APP_INITIALIZER, useFactory: (config: AppConfig) => () => config.load(), deps: [AppConfig], multi: true }
],
bootstrap: [AppComponent]
})
export class AppModule { }
......@@ -42,4 +49,4 @@ export class AppModule { }
export function HttpLoaderFactory(http: HttpClient) {
// Providing path as a workaround for ngx-translate bug with --base-href option
return new TranslateHttpLoader(http, './assets/i18n/');
}
\ No newline at end of file
}
......@@ -7,4 +7,4 @@
</li>
</ul>
</div>
</div>
\ No newline at end of file
</div>
......@@ -2,6 +2,9 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { TranslateModule } from '@ngx-translate/core';
import { HeaderComponent } from './header.component';
import { HttpClientModule } from '@angular/common/http';
import { LanguagesService } from '../languages.service';
import { AppConfigMock } from 'src/app/app.config-mock';
import { AppConfig } from 'src/app/app.config';
describe('HeaderComponent', () => {
let component: HeaderComponent;
......@@ -13,6 +16,9 @@ describe('HeaderComponent', () => {
imports: [
TranslateModule.forRoot(),
HttpClientModule
],
providers: [
{ provide: AppConfig, useClass: AppConfigMock }
]
})
.compileComponents();
......@@ -27,4 +33,11 @@ describe('HeaderComponent', () => {
it('should create', () => {
expect(component).toBeTruthy();
});
it('should set language', () => {
const languagesService: LanguagesService = TestBed.get(LanguagesService);
spyOn(languagesService, 'setLang').and.returnValue(null);
component.setLang('xxx');
expect(languagesService.setLang).toHaveBeenCalledWith('xxx');
});
});
......@@ -3,25 +3,23 @@ import { LanguagesService } from '../languages.service';
@Component({
selector: 'app-header',
templateUrl: './header.component.html',
styleUrls: ['./header.component.css']
templateUrl: './header.component.html'
})
export class HeaderComponent implements OnInit {
constructor(private languagesService: LanguagesService) { }
ngOnInit() {
getLangs(): string[] {
return this.languagesService.getLangs();
}
getLangs():string[] {
return this.languagesService.getLangs()
}
getLang():string {
return this.languagesService.getLang()
getLang(): string {
return this.languagesService.getLang();
}
setLang(lang: string) {
return this.languagesService.setLang(lang)
return this.languagesService.setLang(lang);
}
ngOnInit() {}
}
export class InstanceVersion {
reportTime: string;
reportTimeCompact: string;
reportPath: string;
}
import { TestBed } from '@angular/core/testing';
import { TranslateModule } from '@ngx-translate/core';
import { LanguagesService } from './languages.service';
import { TranslateModule, TranslateService } from '@ngx-translate/core';
import { AppConfigMock } from 'src/app/app.config-mock';
import { AppConfig } from 'src/app/app.config';
import { HttpClientModule } from '@angular/common/http';
describe('LanguagesService', () => {
beforeEach(() => TestBed.configureTestingModule({
imports: [
TranslateModule.forRoot()
TranslateModule.forRoot(),
HttpClientModule
],
providers: [
{ provide: AppConfig, useClass: AppConfigMock }
]
}));
......@@ -13,4 +20,28 @@ describe('LanguagesService', () => {
const service: LanguagesService = TestBed.get(LanguagesService);
expect(service).toBeTruthy();
});
it('should set default lang with empty localStorage', () => {
const translateService: TranslateService = TestBed.get(TranslateService);
spyOn(translateService, 'setDefaultLang');
spyOn(window.localStorage, 'getItem').and.returnValue(undefined);
TestBed.get(LanguagesService);
expect(translateService.setDefaultLang).toHaveBeenCalledWith('est');
});
it('should set default lang from localStorage', () => {
const translateService: TranslateService = TestBed.get(TranslateService);
spyOn(translateService, 'setDefaultLang');
spyOn(window.localStorage, 'getItem').and.returnValue('ENG');
TestBed.get(LanguagesService);
expect(translateService.setDefaultLang).toHaveBeenCalledWith('eng');
});
it('should set language', () => {
const translateService: TranslateService = TestBed.get(TranslateService);
spyOn(translateService, 'use');
const service = TestBed.get(LanguagesService);
service.setLang('ENG');
expect(translateService.use).toHaveBeenCalledWith('eng');
});
});
import { Injectable } from '@angular/core';
import {TranslateService} from "@ngx-translate/core";
import {TranslateService} from '@ngx-translate/core';
import { Title } from '@angular/platform-browser';
import { Subscription } from 'rxjs';
import { take } from 'rxjs/operators';
const LANGUAGES = {
'EST': 'est',
'ENG': 'eng'
}
import { AppConfig } from './app.config';
@Injectable({
providedIn: 'root'
})
export class LanguagesService {
private selectedLang = ''
private translateSubscription: Subscription
private selectedLang = '';
private translateSubscription: Subscription;