diff --git a/eeg_backend/src/test/java/at/mueller/eeg/backend/dashboard/api/DashboardControllerTest.java b/eeg_backend/src/test/java/at/mueller/eeg/backend/dashboard/api/DashboardControllerTest.java new file mode 100644 index 0000000..0573b6d --- /dev/null +++ b/eeg_backend/src/test/java/at/mueller/eeg/backend/dashboard/api/DashboardControllerTest.java @@ -0,0 +1,77 @@ +package at.mueller.eeg.backend.dashboard.api; + +import at.mueller.eeg.backend.dashboard.api.dto.DashboardStatsDto; +import at.mueller.eeg.backend.dashboard.service.DashboardService; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.http.ResponseEntity; + +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +class DashboardControllerTest { + + @Mock + private DashboardService dashboardService; + + @InjectMocks + private DashboardController dashboardController; + + @Test + void getAdminDashboard() { + DashboardStatsDto.AdminStats stats = new DashboardStatsDto.AdminStats(5, 3, 12); + when(dashboardService.getAdminStats()).thenReturn(stats); + + ResponseEntity response = dashboardController.getAdminDashboard(); + + assertEquals(200, response.getStatusCode().value()); + assertEquals(5, response.getBody().totalCommunities()); + assertEquals(3, response.getBody().pendingUsers()); + assertEquals(12, response.getBody().totalActiveMemberships()); + } + + @Test + void getAdminDashboard_empty() { + DashboardStatsDto.AdminStats stats = new DashboardStatsDto.AdminStats(0, 0, 0); + when(dashboardService.getAdminStats()).thenReturn(stats); + + ResponseEntity response = dashboardController.getAdminDashboard(); + + assertEquals(0, response.getBody().totalCommunities()); + assertEquals(0, response.getBody().pendingUsers()); + assertEquals(0, response.getBody().totalActiveMemberships()); + } + + @Test + void getUserDashboard() { + UUID userId = UUID.randomUUID(); + DashboardStatsDto.UserStats stats = new DashboardStatsDto.UserStats(3, 2, 1); + when(dashboardService.getUserStats(userId)).thenReturn(stats); + + ResponseEntity response = dashboardController.getUserDashboard(userId.toString()); + + assertEquals(200, response.getStatusCode().value()); + assertEquals(3, response.getBody().totalMeteringPoints()); + assertEquals(2, response.getBody().activeMemberships()); + assertEquals(1, response.getBody().pendingMemberships()); + } + + @Test + void getUserDashboard_empty() { + UUID userId = UUID.randomUUID(); + DashboardStatsDto.UserStats stats = new DashboardStatsDto.UserStats(0, 0, 0); + when(dashboardService.getUserStats(userId)).thenReturn(stats); + + ResponseEntity response = dashboardController.getUserDashboard(userId.toString()); + + assertEquals(0, response.getBody().totalMeteringPoints()); + assertEquals(0, response.getBody().activeMemberships()); + assertEquals(0, response.getBody().pendingMemberships()); + } +} diff --git a/eeg_frontend/src/app/app.spec.ts b/eeg_frontend/src/app/app.spec.ts index 90086ef..104854e 100644 --- a/eeg_frontend/src/app/app.spec.ts +++ b/eeg_frontend/src/app/app.spec.ts @@ -1,10 +1,15 @@ import { TestBed } from '@angular/core/testing'; +import { ActivatedRoute, Router } from '@angular/router'; import { App } from './app'; describe('App', () => { beforeEach(async () => { await TestBed.configureTestingModule({ imports: [App], + providers: [ + { provide: ActivatedRoute, useValue: { snapshot: { queryParamMap: { get: () => null } } } }, + { provide: Router, useValue: { events: { subscribe: () => {} }, navigate: () => {} } } + ] }).compileComponents(); }); @@ -13,11 +18,4 @@ describe('App', () => { const app = fixture.componentInstance; expect(app).toBeTruthy(); }); - - it('should render title', async () => { - const fixture = TestBed.createComponent(App); - await fixture.whenStable(); - const compiled = fixture.nativeElement as HTMLElement; - expect(compiled.querySelector('h1')?.textContent).toContain('Hello, eeg_frontend'); - }); }); diff --git a/eeg_frontend/src/app/layout/dashboard-layout/dashboard-layout.spec.ts b/eeg_frontend/src/app/layout/dashboard-layout/dashboard-layout.spec.ts index 68bbeec..b39229a 100644 --- a/eeg_frontend/src/app/layout/dashboard-layout/dashboard-layout.spec.ts +++ b/eeg_frontend/src/app/layout/dashboard-layout/dashboard-layout.spec.ts @@ -1,6 +1,8 @@ import {ComponentFixture, TestBed} from '@angular/core/testing'; - +import {ActivatedRoute, Router} from '@angular/router'; +import {of} from 'rxjs'; import {DashboardLayout} from './dashboard-layout'; +import {AuthService} from '../../services/auth'; describe('DashboardLayout', () => { let component: DashboardLayout; @@ -9,6 +11,11 @@ describe('DashboardLayout', () => { beforeEach(async () => { await TestBed.configureTestingModule({ imports: [DashboardLayout], + providers: [ + {provide: ActivatedRoute, useValue: {snapshot: {queryParamMap: {get: () => null}}}}, + {provide: Router, useValue: {events: of(), navigate: () => {}}}, + {provide: AuthService, useValue: {currentUserRole: () => 'MEMBER', logout: () => {}}} + ] }).compileComponents(); fixture = TestBed.createComponent(DashboardLayout); diff --git a/eeg_frontend/src/app/layout/header/header.spec.ts b/eeg_frontend/src/app/layout/header/header.spec.ts index 9ef7403..e871017 100644 --- a/eeg_frontend/src/app/layout/header/header.spec.ts +++ b/eeg_frontend/src/app/layout/header/header.spec.ts @@ -1,17 +1,23 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { Header } from './header'; +import { ActivatedRoute, Router } from '@angular/router'; +import { HeaderComponent } from './header'; +import { AuthService } from '../../services/auth'; describe('Header', () => { - let component: Header; - let fixture: ComponentFixture
; + let component: HeaderComponent; + let fixture: ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [Header], + imports: [HeaderComponent], + providers: [ + { provide: ActivatedRoute, useValue: { snapshot: { queryParamMap: { get: () => null } } } }, + { provide: Router, useValue: { navigate: () => {} } }, + { provide: AuthService, useValue: { isLoggedIn: () => false, currentUserRole: () => null, logout: () => {} } } + ] }).compileComponents(); - fixture = TestBed.createComponent(Header); + fixture = TestBed.createComponent(HeaderComponent); component = fixture.componentInstance; await fixture.whenStable(); }); diff --git a/eeg_frontend/src/app/pages/admin-user-approval/admin-user-approval.spec.ts b/eeg_frontend/src/app/pages/admin-user-approval/admin-user-approval.spec.ts index 4bc4d94..95b1f7c 100644 --- a/eeg_frontend/src/app/pages/admin-user-approval/admin-user-approval.spec.ts +++ b/eeg_frontend/src/app/pages/admin-user-approval/admin-user-approval.spec.ts @@ -1,17 +1,17 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { AdminUserApproval } from './admin-user-approval'; +import { AdminUserApprovalComponent } from './admin-user-approval'; describe('AdminUserApproval', () => { - let component: AdminUserApproval; - let fixture: ComponentFixture; + let component: AdminUserApprovalComponent; + let fixture: ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [AdminUserApproval], + imports: [AdminUserApprovalComponent], }).compileComponents(); - fixture = TestBed.createComponent(AdminUserApproval); + fixture = TestBed.createComponent(AdminUserApprovalComponent); component = fixture.componentInstance; await fixture.whenStable(); }); diff --git a/eeg_frontend/src/app/pages/landing-page/landing-page.spec.ts b/eeg_frontend/src/app/pages/landing-page/landing-page.spec.ts index 3a195fe..5a67bd0 100644 --- a/eeg_frontend/src/app/pages/landing-page/landing-page.spec.ts +++ b/eeg_frontend/src/app/pages/landing-page/landing-page.spec.ts @@ -1,5 +1,5 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; - +import { ActivatedRoute } from '@angular/router'; import { LandingPage } from './landing-page'; describe('LandingPage', () => { @@ -9,6 +9,9 @@ describe('LandingPage', () => { beforeEach(async () => { await TestBed.configureTestingModule({ imports: [LandingPage], + providers: [ + { provide: ActivatedRoute, useValue: { snapshot: { queryParamMap: { get: () => null } } } } + ] }).compileComponents(); fixture = TestBed.createComponent(LandingPage); diff --git a/eeg_frontend/src/app/pages/login/login.spec.ts b/eeg_frontend/src/app/pages/login/login.spec.ts index 1ee4152..cc7017b 100644 --- a/eeg_frontend/src/app/pages/login/login.spec.ts +++ b/eeg_frontend/src/app/pages/login/login.spec.ts @@ -1,22 +1,104 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { ReactiveFormsModule } from '@angular/forms'; +import { ActivatedRoute, Router } from '@angular/router'; +import { vi, expect, it, describe, beforeEach } from 'vitest'; +import { of, throwError } from 'rxjs'; +import { LoginComponent } from './login'; +import { AuthService } from '../../services/auth'; -import { Login } from './login'; - -describe('Login', () => { - let component: Login; - let fixture: ComponentFixture; +describe('LoginComponent', () => { + let component: LoginComponent; + let fixture: ComponentFixture; + let authService: { login: ReturnType }; + let router: { navigate: ReturnType }; beforeEach(async () => { + authService = { login: vi.fn() }; + router = { navigate: vi.fn() }; + await TestBed.configureTestingModule({ - imports: [Login], + imports: [LoginComponent, ReactiveFormsModule], + providers: [ + { provide: AuthService, useValue: authService }, + { provide: Router, useValue: router }, + { provide: ActivatedRoute, useValue: { snapshot: { queryParamMap: { get: () => null } } } } + ] }).compileComponents(); - fixture = TestBed.createComponent(Login); + fixture = TestBed.createComponent(LoginComponent); component = fixture.componentInstance; - await fixture.whenStable(); + fixture.detectChanges(); }); it('should create', () => { expect(component).toBeTruthy(); }); + + it('should have invalid form when empty', () => { + expect(component.loginForm.valid).toBe(false); + }); + + it('should have valid form when email and password are filled', () => { + component.loginForm.setValue({ email: 'test@example.at', password: 'password123' }); + expect(component.loginForm.valid).toBe(true); + }); + + it('should not call login when form is invalid', () => { + component.onSubmit(); + expect(authService.login).not.toHaveBeenCalled(); + }); + + it('should call login and redirect to /dashboard for ADMIN role', () => { + authService.login.mockReturnValue(of({ token: 'abc', role: 'ADMIN' })); + component.loginForm.setValue({ email: 'admin@example.at', password: 'password123' }); + + component.onSubmit(); + + expect(authService.login).toHaveBeenCalledWith({ email: 'admin@example.at', password: 'password123' }); + expect(router.navigate).toHaveBeenCalledWith(['/dashboard']); + }); + + it('should call login and redirect to /dashboard for MEMBER role', () => { + authService.login.mockReturnValue(of({ token: 'abc', role: 'MEMBER' })); + component.loginForm.setValue({ email: 'member@example.at', password: 'password123' }); + + component.onSubmit(); + + expect(router.navigate).toHaveBeenCalledWith(['/dashboard']); + }); + + it('should redirect to / for unknown role', () => { + authService.login.mockReturnValue(of({ token: 'abc', role: 'GUEST' })); + component.loginForm.setValue({ email: 'guest@example.at', password: 'password123' }); + + component.onSubmit(); + + expect(router.navigate).toHaveBeenCalledWith(['/']); + }); + + it('should display error message on login failure', () => { + authService.login.mockReturnValue(throwError(() => ({ + error: { message: 'Ungültige Anmeldedaten' } + }))); + component.loginForm.setValue({ email: 'test@example.at', password: 'wrong' }); + + component.onSubmit(); + + expect(component.errorMessage).toBe('Ungültige Anmeldedaten'); + }); + + it('should display generic error when server returns no message', () => { + authService.login.mockReturnValue(throwError(() => ({ error: {} }))); + component.loginForm.setValue({ email: 'test@example.at', password: 'wrong' }); + + component.onSubmit(); + + expect(component.errorMessage).toBe('Ein unbekannter Fehler ist aufgetreten.'); + }); + + it('should have disabled submit button when form is invalid', () => { + fixture.detectChanges(); + const button = fixture.nativeElement.querySelector('button[type="submit"]'); + expect(button.disabled).toBe(true); + }); }); diff --git a/eeg_frontend/src/app/pages/metering-points/metering-points.spec.ts b/eeg_frontend/src/app/pages/metering-points/metering-points.spec.ts index d9ffd1f..3b02ea0 100644 --- a/eeg_frontend/src/app/pages/metering-points/metering-points.spec.ts +++ b/eeg_frontend/src/app/pages/metering-points/metering-points.spec.ts @@ -1,17 +1,17 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { MeteringPoints } from './metering-points'; +import { MeteringPointsComponent } from './metering-points'; describe('MeteringPoints', () => { - let component: MeteringPoints; - let fixture: ComponentFixture; + let component: MeteringPointsComponent; + let fixture: ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [MeteringPoints], + imports: [MeteringPointsComponent], }).compileComponents(); - fixture = TestBed.createComponent(MeteringPoints); + fixture = TestBed.createComponent(MeteringPointsComponent); component = fixture.componentInstance; await fixture.whenStable(); }); diff --git a/eeg_frontend/src/app/pages/register/register.spec.ts b/eeg_frontend/src/app/pages/register/register.spec.ts index 02f8661..a5c1be4 100644 --- a/eeg_frontend/src/app/pages/register/register.spec.ts +++ b/eeg_frontend/src/app/pages/register/register.spec.ts @@ -1,5 +1,5 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; - +import { ActivatedRoute } from '@angular/router'; import { Register } from './register'; describe('Register', () => { @@ -9,6 +9,9 @@ describe('Register', () => { beforeEach(async () => { await TestBed.configureTestingModule({ imports: [Register], + providers: [ + { provide: ActivatedRoute, useValue: { snapshot: { queryParamMap: { get: () => null } } } } + ] }).compileComponents(); fixture = TestBed.createComponent(Register); diff --git a/eeg_frontend/src/app/pages/verify-email/verify-email.spec.ts b/eeg_frontend/src/app/pages/verify-email/verify-email.spec.ts index 901eb04..5849972 100644 --- a/eeg_frontend/src/app/pages/verify-email/verify-email.spec.ts +++ b/eeg_frontend/src/app/pages/verify-email/verify-email.spec.ts @@ -1,17 +1,20 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { VerifyEmail } from './verify-email'; +import { ActivatedRoute } from '@angular/router'; +import { VerifyEmailComponent } from './verify-email'; describe('VerifyEmail', () => { - let component: VerifyEmail; - let fixture: ComponentFixture; + let component: VerifyEmailComponent; + let fixture: ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [VerifyEmail], + imports: [VerifyEmailComponent], + providers: [ + { provide: ActivatedRoute, useValue: { snapshot: { queryParamMap: { get: () => null } } } } + ] }).compileComponents(); - fixture = TestBed.createComponent(VerifyEmail); + fixture = TestBed.createComponent(VerifyEmailComponent); component = fixture.componentInstance; await fixture.whenStable(); });