Skip to content

Commit

Permalink
Merge branch 'develop' into feat/xd-244
Browse files Browse the repository at this point in the history
  • Loading branch information
PatrickSchm1dt authored Jul 15, 2024
2 parents adb83f1 + 5ea7880 commit ed94d05
Show file tree
Hide file tree
Showing 61 changed files with 1,796 additions and 953 deletions.
5 changes: 5 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ POSTGRES_DB=amos
# Full database connection URL constructed from the above PostgreSQL variables used for Prisma
DATABASE_URL=postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}

# The Api Url for the Insight Hub API
INSIGHT_HUB_API_URL=https://gateway.eu1.mindsphere.io/api
# The Api Key for the Insight Hub API
INSIGHT_HUB_API_KEY=KEY

# Frontend
XD_API_URL=http://${BACKEND_HOST}:${BACKEND_PORT}
SECRET_KEY=SecretKey
Expand Down
2 changes: 2 additions & 0 deletions apps/backend/src/app/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { XdMetricsModule } from '@frontend/facilities/backend/metrics';
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { XdCaseManagementModule } from 'cases-backend-case-management';
Expand All @@ -24,6 +25,7 @@ import { validateConfig } from './config/validation';
XdTimeseriesModule,
XdCaseManagementModule,
XdFacilitiesBackendFacilitiesModule,
XdMetricsModule
],
controllers: [],
providers: [],
Expand Down
4 changes: 2 additions & 2 deletions apps/backend/src/app/config/classes/environment.class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,15 +82,15 @@ export class EnvironmentVariables implements IEnvironmentVariables {
/**
* The URL of the API to use for the IotTimeSeriesService
*/
@IsDefined()
@IsOptional()
@IsString()
@MinLength(1)
INSIGHT_HUB_API_URL?: string;

/**
* The API key to use for the IotTimeSeriesService
*/
@IsDefined()
@IsOptional()
@IsString()
@MinLength(1)
INSIGHT_HUB_API_KEY?: string;
Expand Down
158 changes: 93 additions & 65 deletions apps/frontend/src/app/components/header/header.component.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ActivatedRoute, NavigationEnd, Router, RouterEvent } from '@angular/router';
import { AuthenticationService, LocalStorageService } from 'common-frontend-models';
import { ReplaySubject } from 'rxjs';

import { HeaderComponent } from './header.component';
Expand All @@ -12,73 +13,100 @@ const HEADER_ROUTES = {
}
},
},
root: {
snapshot: {
data: {
breadcrumb: 'Layer 1',
},
},
firstChild: {
snapshot: {
data: {
breadcrumb: 'Layer 2',
},
},
},
},
root: {
snapshot: {
data: {
breadcrumb: 'Layer 1',
},
},
firstChild: {
snapshot: {
data: {
breadcrumb: 'Layer 2',
},
},
},
},
};

describe('HeaderComponent', () => {
let component: HeaderComponent;
let fixture: ComponentFixture<HeaderComponent>;
const eventsSubject = new ReplaySubject<RouterEvent>(1);
let routerMock: Router;

beforeEach(async () => {
routerMock = {
events: eventsSubject.asObservable(),
let component: HeaderComponent;
let fixture: ComponentFixture<HeaderComponent>;
const eventsSubject = new ReplaySubject<RouterEvent>(1);
let routerMock: Router;
let localStorageServiceMock: LocalStorageService;
let authenticationServiceMock: AuthenticationService;

beforeEach(async () => {
routerMock = {
events: eventsSubject.asObservable(),
url: '/Layer1/Layer2',
} as unknown as Router;

await TestBed.configureTestingModule({
imports: [ HeaderComponent ],
providers: [
{
provide: ActivatedRoute,
useValue: HEADER_ROUTES,
},
{
provide: Router,
useValue: routerMock,
},
],
}).compileComponents();

fixture = TestBed.createComponent(HeaderComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});

it('should trigger router events correctly', () => {
eventsSubject.next(new NavigationEnd(1, '', ''));

const routerEvents = component.routerEvents();
expect(routerEvents).toEqual({
id: 1,
type: 1,
url: '',
urlAfterRedirects: '',
} as NavigationEnd);
});

it('should compute breadcrumbs correctly', () => {
eventsSubject.next(new NavigationEnd(1, '', ''));

const breadcrumbs = component.breadcrumbs();
expect(breadcrumbs).toEqual([ 'Layer 1', 'Layer 2' ]);
});
navigate: jest.fn()
} as unknown as Router;

localStorageServiceMock = {
getOrCreate: jest.fn().mockReturnValue(jest.fn().mockReturnValue('theme-classic-dark')),
set: jest.fn()
} as unknown as LocalStorageService;

authenticationServiceMock = {
getUserMail: jest.fn().mockReturnValue('[email protected]'),
logout: jest.fn()
} as unknown as AuthenticationService;

await TestBed.configureTestingModule({
imports: [ HeaderComponent ],
providers: [
{ provide: ActivatedRoute, useValue: HEADER_ROUTES },
{ provide: Router, useValue: routerMock },
{ provide: LocalStorageService, useValue: localStorageServiceMock },
{ provide: AuthenticationService, useValue: authenticationServiceMock },
],
}).compileComponents();

fixture = TestBed.createComponent(HeaderComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});

it('should trigger router events correctly', () => {
eventsSubject.next(new NavigationEnd(1, '', ''));

const routerEvents = component.routerEvents();
expect(routerEvents).toBeInstanceOf(NavigationEnd);
});

it('should compute breadcrumbs correctly', () => {
eventsSubject.next(new NavigationEnd(1, '', ''));

const breadcrumbs = component.breadcrumbs();
expect(breadcrumbs).toEqual([ 'Layer 1', 'Layer 2' ]);
});

it('should determine if it is home page correctly', () => {
eventsSubject.next(new NavigationEnd(1, '', ''));

const isHomePage = component.isHomePage();
expect(isHomePage).toBe(true);
});

it('should toggle theme mode', () => {
component.toggleMode();
expect(localStorageServiceMock.set).toHaveBeenCalledWith('theme', 'theme-classic-light');
});

it('should logout and navigate to login', () => {
component.logout();
expect(authenticationServiceMock.logout).toHaveBeenCalled();
expect(routerMock.navigate).toHaveBeenCalledWith([ '/account/login' ]);
});

it('should cut URL correctly', () => {
const cutUrl = component.cutUrl(1);
expect(cutUrl).toBe('/Layer1');
});
});
Original file line number Diff line number Diff line change
@@ -1,22 +1,57 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { Router } from '@angular/router';
import { AuthenticationService } from 'common-frontend-models';
import { of } from 'rxjs';

import { LoginPage } from './login.page';

describe('LoginComponent', () => {
let component: LoginPage;
let fixture: ComponentFixture<LoginPage>;
describe('LoginPage', () => {
let component: LoginPage;
let fixture: ComponentFixture<LoginPage>;
let authServiceMock: jest.Mocked<AuthenticationService>;
let routerMock: jest.Mocked<Router>;

beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [ LoginPage ],
}).compileComponents();
beforeEach(async () => {
authServiceMock = {
login: jest.fn().mockReturnValue(of(true)),
} as unknown as jest.Mocked<AuthenticationService>;

fixture = TestBed.createComponent(LoginPage);
component = fixture.componentInstance;
fixture.detectChanges();
});
routerMock = {
navigate: jest.fn(),
} as unknown as jest.Mocked<Router>;

it('should create', () => {
expect(component).toBeTruthy();
});
await TestBed.configureTestingModule({
imports: [ LoginPage ],
providers: [
{ provide: AuthenticationService, useValue: authServiceMock },
{ provide: Router, useValue: routerMock },
],
}).compileComponents();

fixture = TestBed.createComponent(LoginPage);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});

describe('onSubmit', () => {
it('should not validate form if email or password is invalid', () => {
component.email = 'invalid-email';
component.password = '';
component.onSubmit();

expect(routerMock.navigate).not.toHaveBeenCalled();
});

it('should validate form and login successfully', () => {
component.email = '[email protected]';
component.password = 'password';
component.onSubmit();

expect(routerMock.navigate).toHaveBeenCalledWith([ '/' ]);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ import { AuthenticationService } from 'common-frontend-models';
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class LoginPage {
protected email = '';
protected password = '';
public email = '';
public password = '';

protected formValid = signal(false);
protected loginSuccess = signal(false);
Expand Down
3 changes: 3 additions & 0 deletions libs/cases/backend/case-management/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
"options": {
"jestConfig": "libs/cases/backend/case-management/jest.config.ts"
}
},
"lint": {
"executor": "@nx/eslint:lint"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ describe('XdCasesFacade', () => {
const timeseriesRequestServiceMock = {
getTimeSeries: jest.fn().mockReturnValue(of({})),
getAllCases: jest.fn().mockReturnValue(of([])),
createCase: jest.fn().mockReturnValue(of({})),
updateCase: jest.fn().mockReturnValue(of({})),
deleteCase: jest.fn().mockReturnValue(of({})),
};

TestBed.configureTestingModule({
Expand Down Expand Up @@ -49,4 +52,35 @@ describe('XdCasesFacade', () => {
expect(response).toEqual({});
});
});

describe('createCase', () => {
it('should call createCase of CasesRequestService with correct parameters', async () => {
const body = { id: 1 } as any;

await firstValueFrom(facade.createCase(body));

expect(casesRequestService.createCase).toHaveBeenCalledWith(body);
});
});

describe('updateCase', () => {
it('should call updateCase of CasesRequestService with correct parameters', async () => {
const params: ICaseParams = { id: 1 } as ICaseParams;
const body = { id: 1 } as any;

await firstValueFrom(facade.updateCase(params, body));

expect(casesRequestService.updateCase).toHaveBeenCalledWith(params, body);
});
});

describe('deleteCase', () => {
it('should call deleteCase of CasesRequestService with correct parameters', async () => {
const params: ICaseParams = { id: 1 } as ICaseParams;

await firstValueFrom(facade.deleteCase(params));

expect(casesRequestService.deleteCase).toHaveBeenCalledWith(params);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,31 @@ export class XdCasesFacade {
return this._scanService.getTimeSeries(params);
}

public createCase(body: ICreateCaseBody) {
/**
* Creates a new Case
*
* @param body
*/
public createCase(body: ICreateCaseBody): Observable<ICaseResponse> {
return this._scanService.createCase(body);
}

public updateCase(params: ICaseParams, body: ICreateCaseBody) {
return this._scanService.updateCase(params,body);
}
/**
* Updates an existing Case
*
* @param params
* @param body
*/
public updateCase(params: ICaseParams, body: ICreateCaseBody): Observable<ICaseResponse> {
return this._scanService.updateCase(params, body);
}

public deleteCase(params: ICaseParams){
return this._scanService.deleteCase(params);
}
/**
* Deletes an undesired Case
*
* @param params
*/
public deleteCase(params: ICaseParams): Observable<ICaseResponse> {
return this._scanService.deleteCase(params);
}
}
Loading

0 comments on commit ed94d05

Please sign in to comment.