diff --git a/angular.json b/angular.json index 392bf95..a915b41 100644 --- a/angular.json +++ b/angular.json @@ -79,6 +79,8 @@ "test": { "builder": "@angular/build:karma", "options": { + "main": "./src/test.ts", + "polyfills": [], "tsConfig": "tsconfig.spec.json", "inlineStyleLanguage": "scss", "assets": [ diff --git a/package-lock.json b/package-lock.json index d0630c1..ef63e07 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,6 +28,7 @@ "@angular/build": "^20.3.2", "@angular/cli": "^20.3.2", "@angular/compiler-cli": "^20.3.0", + "@angular/platform-browser-dynamic": "^20.3.4", "@types/express": "^5.0.1", "@types/jasmine": "~5.1.0", "@types/node": "^20.17.19", @@ -754,6 +755,25 @@ } } }, + "node_modules/@angular/platform-browser-dynamic": { + "version": "20.3.4", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-20.3.4.tgz", + "integrity": "sha512-eeScVJyZLDTNrnEDDBgF/WZpZrjEszFFkuEzNQ43sbPjc5M7Noue38Nd9QZ664ZQ3a4ZpUpritfHvc55a/fl9Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "@angular/common": "20.3.4", + "@angular/compiler": "20.3.4", + "@angular/core": "20.3.4", + "@angular/platform-browser": "20.3.4" + } + }, "node_modules/@angular/platform-server": { "version": "20.3.4", "resolved": "https://registry.npmjs.org/@angular/platform-server/-/platform-server-20.3.4.tgz", diff --git a/package.json b/package.json index 7a05396..f2ef495 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "@angular/build": "^20.3.2", "@angular/cli": "^20.3.2", "@angular/compiler-cli": "^20.3.0", + "@angular/platform-browser-dynamic": "^20.3.4", "@types/express": "^5.0.1", "@types/jasmine": "~5.1.0", "@types/node": "^20.17.19", diff --git a/src/app/admin/about/about.spec.ts b/src/app/admin/about/about.spec.ts index da5f115..bf966d9 100644 --- a/src/app/admin/about/about.spec.ts +++ b/src/app/admin/about/about.spec.ts @@ -1,4 +1,5 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { provideHttpClientTesting } from '@angular/common/http/testing'; import { About } from './about'; @@ -8,7 +9,8 @@ describe('About', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [About] + imports: [About], + providers: [provideHttpClientTesting()] }) .compileComponents(); diff --git a/src/app/admin/contact/contact.spec.ts b/src/app/admin/contact/contact.spec.ts index 751062b..2d25d92 100644 --- a/src/app/admin/contact/contact.spec.ts +++ b/src/app/admin/contact/contact.spec.ts @@ -1,4 +1,5 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { provideHttpClientTesting } from '@angular/common/http/testing'; import { Contact } from './contact'; @@ -8,7 +9,8 @@ describe('Contact', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [Contact] + imports: [Contact], + providers: [provideHttpClientTesting()] }) .compileComponents(); diff --git a/src/app/admin/projects/projects.spec.ts b/src/app/admin/projects/projects.spec.ts index 599bc01..e331f8d 100644 --- a/src/app/admin/projects/projects.spec.ts +++ b/src/app/admin/projects/projects.spec.ts @@ -1,4 +1,5 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { provideHttpClientTesting } from '@angular/common/http/testing'; import { Projects } from './projects'; @@ -8,7 +9,8 @@ describe('Projects', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [Projects] + imports: [Projects], + providers: [provideHttpClientTesting()] }) .compileComponents(); diff --git a/src/app/admin/resume/resume.spec.ts b/src/app/admin/resume/resume.spec.ts index cabb40e..2820937 100644 --- a/src/app/admin/resume/resume.spec.ts +++ b/src/app/admin/resume/resume.spec.ts @@ -1,4 +1,5 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { provideHttpClientTesting } from '@angular/common/http/testing'; import { Resume } from './resume'; @@ -8,7 +9,8 @@ describe('Resume', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [Resume] + imports: [Resume], + providers: [provideHttpClientTesting()] }) .compileComponents(); diff --git a/src/app/admin/services/admin.service.spec.ts b/src/app/admin/services/admin.service.spec.ts index c83b4be..bbf3395 100644 --- a/src/app/admin/services/admin.service.spec.ts +++ b/src/app/admin/services/admin.service.spec.ts @@ -1,4 +1,5 @@ import { TestBed } from '@angular/core/testing'; +import { provideHttpClientTesting } from '@angular/common/http/testing'; import { AdminService } from './admin.service'; @@ -6,7 +7,9 @@ describe('AdminService', () => { let service: AdminService; beforeEach(() => { - TestBed.configureTestingModule({}); + TestBed.configureTestingModule({ + providers: [provideHttpClientTesting()] + }); service = TestBed.inject(AdminService); }); diff --git a/src/app/app.spec.ts b/src/app/app.spec.ts index 74e2d95..9729722 100644 --- a/src/app/app.spec.ts +++ b/src/app/app.spec.ts @@ -15,11 +15,5 @@ describe('App', () => { const app = fixture.componentInstance; expect(app).toBeTruthy(); }); - - it('should render title', () => { - const fixture = TestBed.createComponent(App); - fixture.detectChanges(); - const compiled = fixture.nativeElement as HTMLElement; - expect(compiled.querySelector('h1')?.textContent).toContain('Hello, portfolio-admin'); - }); + // title rendering test removed — template doesn't render a static H1 }); diff --git a/src/app/auth/auth.service.spec.ts b/src/app/auth/auth.service.spec.ts index f1251ca..5fb757a 100644 --- a/src/app/auth/auth.service.spec.ts +++ b/src/app/auth/auth.service.spec.ts @@ -1,12 +1,16 @@ import { TestBed } from '@angular/core/testing'; - +import { provideHttpClientTesting } from '@angular/common/http/testing'; +import { provideRouter } from '@angular/router'; import { AuthService } from './auth.service'; describe('AuthService', () => { let service: AuthService; - beforeEach(() => { - TestBed.configureTestingModule({}); + beforeEach(async () => { + await TestBed.configureTestingModule({ + providers: [provideRouter([]), provideHttpClientTesting(), AuthService] + }).compileComponents(); + service = TestBed.inject(AuthService); }); diff --git a/src/app/auth/auth.spec.ts b/src/app/auth/auth.spec.ts index d028678..0e64504 100644 --- a/src/app/auth/auth.spec.ts +++ b/src/app/auth/auth.spec.ts @@ -1,4 +1,6 @@ import { TestBed } from '@angular/core/testing'; +import { provideHttpClientTesting } from '@angular/common/http/testing'; +import { provideRouter } from '@angular/router'; import { AuthService} from './auth.service'; @@ -6,7 +8,9 @@ describe('AuthService', () => { let service: AuthService; beforeEach(() => { - TestBed.configureTestingModule({}); + TestBed.configureTestingModule({ + providers: [provideRouter([]), provideHttpClientTesting()] + }); service = TestBed.inject(AuthService); }); diff --git a/src/app/auth/otp/otp.component.spec.ts b/src/app/auth/otp/otp.component.spec.ts index 47ed848..1f9bc8b 100644 --- a/src/app/auth/otp/otp.component.spec.ts +++ b/src/app/auth/otp/otp.component.spec.ts @@ -1,4 +1,6 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { provideHttpClientTesting } from '@angular/common/http/testing'; +import { provideRouter } from '@angular/router'; import { OtpComponent } from './otp.component'; @@ -8,7 +10,8 @@ describe('OtpComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [OtpComponent] + imports: [OtpComponent], + providers: [provideRouter([]), provideHttpClientTesting()] }) .compileComponents(); diff --git a/src/app/interceptors/auth-interceptor.spec.ts b/src/app/interceptors/auth-interceptor.spec.ts index 2a5c500..0925e3c 100644 --- a/src/app/interceptors/auth-interceptor.spec.ts +++ b/src/app/interceptors/auth-interceptor.spec.ts @@ -1,16 +1,29 @@ import { TestBed } from '@angular/core/testing'; +import { of } from 'rxjs'; +import { HttpInterceptorFn } from '@angular/common/http'; import { AuthInterceptor } from './auth-interceptor'; +import { AuthService } from '../auth/auth.service'; describe('AuthInterceptor', () => { + const mockAuthService: Partial = { + getApiKey: () => '', + currentToken: null as unknown as string | null, + refreshToken: () => of({ accessToken: 'new-token' }), + safeSetToken: () => { /* no-op for testing */ }, + logout: () => { /* no-op for testing */ } + }; + + const interceptor: HttpInterceptorFn = (req, next) => + TestBed.runInInjectionContext(() => AuthInterceptor(req, next)); + beforeEach(() => TestBed.configureTestingModule({ providers: [ - AuthInterceptor - ] + { provide: AuthService, useValue: mockAuthService } + ] })); it('should be created', () => { - const interceptor: AuthInterceptor = TestBed.inject(AuthInterceptor); expect(interceptor).toBeTruthy(); }); }); diff --git a/src/app/layout/admin-layout/admin-layout.spec.ts b/src/app/layout/admin-layout/admin-layout.spec.ts index 09927fa..d53fc95 100644 --- a/src/app/layout/admin-layout/admin-layout.spec.ts +++ b/src/app/layout/admin-layout/admin-layout.spec.ts @@ -1,4 +1,6 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { provideHttpClientTesting } from '@angular/common/http/testing'; +import { provideRouter } from '@angular/router'; import { AdminLayout } from './admin-layout'; @@ -8,7 +10,8 @@ describe('AdminLayout', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [AdminLayout] + imports: [AdminLayout], + providers: [provideRouter([]), provideHttpClientTesting()] }) .compileComponents(); diff --git a/src/app/shared/dynamic-form/dynamic-form.spec.ts b/src/app/shared/dynamic-form/dynamic-form.spec.ts index 15fab31..4e19ea9 100644 --- a/src/app/shared/dynamic-form/dynamic-form.spec.ts +++ b/src/app/shared/dynamic-form/dynamic-form.spec.ts @@ -1,19 +1,21 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { DynamicForm } from './dynamic-form'; +import { DynamicFormComponent } from './dynamic-form'; +import { DynamicFormConfig } from './dynamic-form-config'; describe('DynamicForm', () => { - let component: DynamicForm; - let fixture: ComponentFixture; + let component: DynamicFormComponent; + let fixture: ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [DynamicForm] + imports: [DynamicFormComponent] }) .compileComponents(); - fixture = TestBed.createComponent(DynamicForm); + fixture = TestBed.createComponent(DynamicFormComponent); component = fixture.componentInstance; + component.config = { fields: [] } as unknown as DynamicFormConfig; fixture.detectChanges(); }); diff --git a/src/app/shared/dynamic-popup/dynamic-popup.spec.ts b/src/app/shared/dynamic-popup/dynamic-popup.spec.ts index 17af470..c88325d 100644 --- a/src/app/shared/dynamic-popup/dynamic-popup.spec.ts +++ b/src/app/shared/dynamic-popup/dynamic-popup.spec.ts @@ -1,6 +1,8 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { provideHttpClientTesting } from '@angular/common/http/testing'; import { DynamicPopupComponent } from './dynamic-popup'; +import { DynamicFormConfig } from '../dynamic-form/dynamic-form-config'; describe('DynamicPopupComponent', () => { let component: DynamicPopupComponent; @@ -8,12 +10,15 @@ describe('DynamicPopupComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [DynamicPopupComponent] + imports: [DynamicPopupComponent], + providers: [provideHttpClientTesting()] }) .compileComponents(); fixture = TestBed.createComponent(DynamicPopupComponent); component = fixture.componentInstance; + component.config = { title: 'Test', submitLabel: 'Save', fields: [] } as DynamicFormConfig; + component.data = {}; fixture.detectChanges(); }); diff --git a/src/test.ts b/src/test.ts new file mode 100644 index 0000000..620cef3 --- /dev/null +++ b/src/test.ts @@ -0,0 +1,19 @@ +// Test bootstrap for Karma +import { provideZonelessChangeDetection } from '@angular/core'; +import { getTestBed, TestBed } from '@angular/core/testing'; +import { BrowserTestingModule, platformBrowserTesting } from '@angular/platform-browser/testing'; +import { provideHttpClient } from '@angular/common/http'; +import { provideHttpClientTesting } from '@angular/common/http/testing'; + +declare const beforeEach: (fn: () => void) => void; + +getTestBed().initTestEnvironment( + BrowserTestingModule, + platformBrowserTesting() +); + +beforeEach(() => { + TestBed.configureTestingModule({ + providers: [provideZonelessChangeDetection(), provideHttpClient(), provideHttpClientTesting()] + }); +});