diff --git a/src/app/auth/auth.component.html b/src/app/auth/auth.component.html
index 44840fb1..5e29c4d0 100644
--- a/src/app/auth/auth.component.html
+++ b/src/app/auth/auth.component.html
@@ -54,7 +54,7 @@
-
+
diff --git a/src/app/auth/auth.component.spec.ts b/src/app/auth/auth.component.spec.ts
new file mode 100644
index 00000000..5fb22ff6
--- /dev/null
+++ b/src/app/auth/auth.component.spec.ts
@@ -0,0 +1,119 @@
+import { asyncScheduler, observeOn, of, throwError } from 'rxjs';
+
+import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing';
+import { ReactiveFormsModule } from '@angular/forms';
+import { MatButtonModule } from '@angular/material/button';
+import { MatCardModule } from '@angular/material/card';
+import { MatFormFieldModule } from '@angular/material/form-field';
+import { MatIconModule } from '@angular/material/icon';
+import { MatInputModule } from '@angular/material/input';
+import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
+import { By } from '@angular/platform-browser';
+
+import { AuthService } from '@services/access/auth.service';
+import { AlertService } from '@services/shared/alert.service';
+import { ConfigService } from '@services/shared/config.service';
+import { UnsubscribeService } from '@services/unsubscribe.service';
+
+import { AuthComponent } from '@src/app/auth/auth.component';
+import { ButtonsModule } from '@src/app/shared/buttons/buttons.module';
+import { ComponentsModule } from '@src/app/shared/components.module';
+import { InputModule } from '@src/app/shared/input/input.module';
+
+describe('AuthComponent', () => {
+ let component: AuthComponent;
+ let fixture: ComponentFixture;
+
+ // Mocks
+ let mockAuthService: jasmine.SpyObj;
+ let mockAlertService: jasmine.SpyObj;
+ let mockUnsubscribeService: jasmine.SpyObj;
+ let mockConfigService: jasmine.SpyObj;
+
+ beforeEach(async () => {
+ mockAuthService = jasmine.createSpyObj('AuthService', ['logIn']);
+ mockAlertService = jasmine.createSpyObj('AlertService', ['showErrorMessage']);
+ mockUnsubscribeService = jasmine.createSpyObj('UnsubscribeService', ['add', 'unsubscribeAll']);
+ mockConfigService = jasmine.createSpyObj('ConfigService', ['getEndpoint']);
+
+ await TestBed.configureTestingModule({
+ imports: [
+ ReactiveFormsModule,
+ MatCardModule,
+ MatFormFieldModule,
+ MatInputModule,
+ MatIconModule,
+ MatButtonModule,
+ InputModule,
+ ButtonsModule,
+ ComponentsModule,
+ MatProgressSpinnerModule
+ ],
+ declarations: [AuthComponent],
+ providers: [
+ { provide: AuthService, useValue: mockAuthService },
+ { provide: AlertService, useValue: mockAlertService },
+ { provide: UnsubscribeService, useValue: mockUnsubscribeService },
+ { provide: ConfigService, useValue: mockConfigService }
+ ]
+ }).compileComponents();
+
+ fixture = TestBed.createComponent(AuthComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create the component and initialize form', () => {
+ expect(component).toBeTruthy();
+ expect(component.loginForm).toBeTruthy();
+ expect(component.loginForm.controls['username']).toBeTruthy();
+ expect(component.loginForm.controls['password']).toBeTruthy();
+ });
+
+ it('should mark form invalid if empty', () => {
+ component.loginForm.setValue({ username: '', password: '' });
+ expect(component.loginForm.invalid).toBeTrue();
+ });
+
+ it('should call authService.logIn on valid form submission via button click', fakeAsync(() => {
+ const fakeResponse = of({ token: '123' }).pipe(observeOn(asyncScheduler));
+ mockAuthService.logIn.and.returnValue(fakeResponse);
+ component.loginForm.setValue({ username: 'user', password: 'pass' });
+
+ const submitButtonDebugEl = fixture.debugElement.query(By.css('button-submit button'));
+ submitButtonDebugEl.nativeElement.click();
+
+ expect(component.isLoading).toBeTrue();
+ expect(mockAuthService.logIn).toHaveBeenCalledWith('user', 'pass');
+
+ tick();
+
+ expect(component.isLoading).toBeFalse();
+ expect(component.loginForm.value.username).toBeNull();
+ expect(component.loginForm.value.password).toBeNull();
+ expect(mockUnsubscribeService.add).toHaveBeenCalled();
+ }));
+
+ it('should show error message when login fails on submit button click', fakeAsync(() => {
+ const errorResponse = throwError(() => new Error('Login failed')).pipe(observeOn(asyncScheduler));
+ mockAuthService.logIn.and.returnValue(errorResponse);
+
+ component.loginForm.setValue({ username: 'user', password: 'wrong' });
+
+ const submitButtonDebugEl = fixture.debugElement.query(By.css('button-submit button'));
+ submitButtonDebugEl.nativeElement.click();
+
+ expect(component.isLoading).toBeTrue();
+ expect(mockAuthService.logIn).toHaveBeenCalledWith('user', 'wrong');
+
+ tick();
+
+ expect(component.isLoading).toBeFalse();
+ expect(mockAlertService.showErrorMessage).toHaveBeenCalled();
+ }));
+
+ it('should call unsubscribeAll on destroy', () => {
+ component.ngOnDestroy();
+ expect(mockUnsubscribeService.unsubscribeAll).toHaveBeenCalled();
+ });
+});
diff --git a/src/app/auth/auth.component.ts b/src/app/auth/auth.component.ts
index 3f80d692..028d6129 100644
--- a/src/app/auth/auth.component.ts
+++ b/src/app/auth/auth.component.ts
@@ -15,8 +15,8 @@ import { LocalStorageService } from '@services/storage/local-storage.service';
import { UnsubscribeService } from '@services/unsubscribe.service';
import { UISettingsUtilityClass } from '@src/app/shared/utils/config';
-import { environment } from '@src/environments/environment';
import { HeaderConfig } from '@src/config/default/app/config.model';
+import { environment } from '@src/environments/environment';
@Component({
selector: 'app-login',