diff --git a/.github/workflows/build-test.yaml b/.github/workflows/build-test.yaml
index 3678926a..5e3e5c23 100644
--- a/.github/workflows/build-test.yaml
+++ b/.github/workflows/build-test.yaml
@@ -84,3 +84,24 @@ jobs:
for OPTS_NAME in ANMS_COMPOSE_OPTS TESTENV_COMPOSE_OPTS; do
${DOCKER_CMD} compose ${!OPTS_NAME} down --remove-orphans
done
+
+ anms-ui-unit-test:
+ runs-on: ubuntu-24.04
+ name: ANMS UI Unit Tests
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v6
+ with:
+ submodules: true
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: 22
+ cache: 'npm'
+ cache-dependency-path: anms-ui/package-lock.json
+ - name: Install dependencies
+ working-directory: anms-ui
+ run: npm ci
+ - name: Run unit tests
+ working-directory: anms-ui
+ run: npx ng test --runner=vitest --no-watch
diff --git a/anms-ui/src/app/app.spec.ts b/anms-ui/src/app/app.spec.ts
index e9c46769..770b0d29 100644
--- a/anms-ui/src/app/app.spec.ts
+++ b/anms-ui/src/app/app.spec.ts
@@ -1,3 +1,4 @@
+import { provideRouter } from '@angular/router';
import { TestBed } from '@angular/core/testing';
import { App } from './app';
@@ -5,6 +6,7 @@ describe('App', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [App],
+ providers: [provideRouter([])],
}).compileComponents();
});
@@ -14,10 +16,10 @@ describe('App', () => {
expect(app).toBeTruthy();
});
- it('should render title', async () => {
+ it('should render router outlet', async () => {
const fixture = TestBed.createComponent(App);
await fixture.whenStable();
const compiled = fixture.nativeElement as HTMLElement;
- expect(compiled.querySelector('h1')?.textContent).toContain('Hello, anms-ui');
+ expect(compiled.querySelector('router-outlet')).toBeTruthy();
});
});
diff --git a/anms-ui/src/app/features/adms/adms.spec.ts b/anms-ui/src/app/features/adms/adms.spec.ts
index 35f792be..b0486dae 100644
--- a/anms-ui/src/app/features/adms/adms.spec.ts
+++ b/anms-ui/src/app/features/adms/adms.spec.ts
@@ -1,3 +1,6 @@
+import { provideHttpClient } from '@angular/common/http';
+import { HttpClientTestingModule } from '@angular/common/http/testing';
+import { provideToastr } from 'ngx-toastr';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { Adms } from './adms';
@@ -8,7 +11,8 @@ describe('Adms', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
- imports: [Adms],
+ imports: [Adms, HttpClientTestingModule],
+ providers: [provideHttpClient(), provideToastr()],
}).compileComponents();
fixture = TestBed.createComponent(Adms);
diff --git a/anms-ui/src/app/features/dashboard/components/dashboard-home/dashboard-home.spec.ts b/anms-ui/src/app/features/dashboard/components/dashboard-home/dashboard-home.spec.ts
index 3712b29c..3543a2c7 100644
--- a/anms-ui/src/app/features/dashboard/components/dashboard-home/dashboard-home.spec.ts
+++ b/anms-ui/src/app/features/dashboard/components/dashboard-home/dashboard-home.spec.ts
@@ -1,3 +1,4 @@
+import { provideRouter } from '@angular/router';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { DashboardHome } from './dashboard-home';
@@ -9,6 +10,7 @@ describe('DashboardHome', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [DashboardHome],
+ providers: [provideRouter([])],
}).compileComponents();
fixture = TestBed.createComponent(DashboardHome);
diff --git a/anms-ui/src/app/features/dashboard/dashboard.spec.ts b/anms-ui/src/app/features/dashboard/dashboard.spec.ts
index 2ce950a7..77f9e0e5 100644
--- a/anms-ui/src/app/features/dashboard/dashboard.spec.ts
+++ b/anms-ui/src/app/features/dashboard/dashboard.spec.ts
@@ -1,3 +1,7 @@
+import { provideHttpClient } from '@angular/common/http';
+import { HttpClientTestingModule } from '@angular/common/http/testing';
+import { provideToastr } from 'ngx-toastr';
+import { provideRouter } from '@angular/router';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { Dashboard } from './dashboard';
@@ -8,9 +12,17 @@ describe('Dashboard', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
- imports: [Dashboard],
+ imports: [Dashboard, HttpClientTestingModule],
+ providers: [
+ provideHttpClient(),
+ provideToastr(),
+ provideRouter([]),
+ ],
}).compileComponents();
+ // Provide DOM element that sidebar.ngAfterViewInit looks for
+ document.body.innerHTML = ' ';
+
fixture = TestBed.createComponent(Dashboard);
component = fixture.componentInstance;
await fixture.whenStable();
diff --git a/anms-ui/src/app/features/management/agents/agent-modal/agent-modal.spec.ts b/anms-ui/src/app/features/management/agents/agent-modal/agent-modal.spec.ts
index c933085f..9ac238f6 100644
--- a/anms-ui/src/app/features/management/agents/agent-modal/agent-modal.spec.ts
+++ b/anms-ui/src/app/features/management/agents/agent-modal/agent-modal.spec.ts
@@ -1,3 +1,7 @@
+import { provideHttpClient } from '@angular/common/http';
+import { HttpClientTestingModule } from '@angular/common/http/testing';
+import { provideToastr } from 'ngx-toastr';
+import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import {AgentModal} from './agent-modal';
@@ -8,7 +12,13 @@ describe('AgentModal', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
- imports: [AgentModal],
+ imports: [AgentModal, HttpClientTestingModule],
+ providers: [
+ provideHttpClient(),
+ provideToastr(),
+ { provide: MatDialogRef, useValue: { close: () => {} } },
+ { provide: MAT_DIALOG_DATA, useValue: {} },
+ ],
}).compileComponents();
fixture = TestBed.createComponent(AgentModal);
diff --git a/anms-ui/src/app/features/management/agents/agents.spec.ts b/anms-ui/src/app/features/management/agents/agents.spec.ts
index 02cd1076..872f2192 100644
--- a/anms-ui/src/app/features/management/agents/agents.spec.ts
+++ b/anms-ui/src/app/features/management/agents/agents.spec.ts
@@ -1,3 +1,6 @@
+import { provideHttpClient } from '@angular/common/http';
+import { HttpClientTestingModule } from '@angular/common/http/testing';
+import { provideToastr } from 'ngx-toastr';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { Agents } from './agents';
@@ -8,7 +11,8 @@ describe('Agents', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
- imports: [Agents],
+ imports: [Agents, HttpClientTestingModule],
+ providers: [provideHttpClient(), provideToastr()],
}).compileComponents();
fixture = TestBed.createComponent(Agents);
diff --git a/anms-ui/src/app/features/management/agents/crud/crud.spec.ts b/anms-ui/src/app/features/management/agents/crud/crud.spec.ts
index 3bec17ff..f14077de 100644
--- a/anms-ui/src/app/features/management/agents/crud/crud.spec.ts
+++ b/anms-ui/src/app/features/management/agents/crud/crud.spec.ts
@@ -1,3 +1,6 @@
+import { provideHttpClient } from '@angular/common/http';
+import { HttpClientTestingModule } from '@angular/common/http/testing';
+import { provideToastr } from 'ngx-toastr';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { Crud } from './crud';
@@ -8,7 +11,8 @@ describe('Crud', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
- imports: [Crud],
+ imports: [Crud, HttpClientTestingModule],
+ providers: [provideHttpClient(), provideToastr()],
}).compileComponents();
fixture = TestBed.createComponent(Crud);
diff --git a/anms-ui/src/app/features/management/agents/manage-agents-dialog/manage-agents-dialog.spec.ts b/anms-ui/src/app/features/management/agents/manage-agents-dialog/manage-agents-dialog.spec.ts
index 45c0227c..2b18b211 100644
--- a/anms-ui/src/app/features/management/agents/manage-agents-dialog/manage-agents-dialog.spec.ts
+++ b/anms-ui/src/app/features/management/agents/manage-agents-dialog/manage-agents-dialog.spec.ts
@@ -1,3 +1,7 @@
+import { provideHttpClient } from '@angular/common/http';
+import { HttpClientTestingModule } from '@angular/common/http/testing';
+import { provideToastr } from 'ngx-toastr';
+import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ManageAgentsDialog } from './manage-agents-dialog';
@@ -8,7 +12,13 @@ describe('ManageAgentsDialog', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
- imports: [ManageAgentsDialog],
+ imports: [ManageAgentsDialog, HttpClientTestingModule],
+ providers: [
+ provideHttpClient(),
+ provideToastr(),
+ { provide: MatDialogRef, useValue: { close: () => {} } },
+ { provide: MAT_DIALOG_DATA, useValue: { agents: [] } },
+ ],
}).compileComponents();
fixture = TestBed.createComponent(ManageAgentsDialog);
diff --git a/anms-ui/src/app/features/management/agents/reports/reports.spec.ts b/anms-ui/src/app/features/management/agents/reports/reports.spec.ts
index b150686b..2be4bad6 100644
--- a/anms-ui/src/app/features/management/agents/reports/reports.spec.ts
+++ b/anms-ui/src/app/features/management/agents/reports/reports.spec.ts
@@ -1,3 +1,6 @@
+import { provideHttpClient } from '@angular/common/http';
+import { HttpClientTestingModule } from '@angular/common/http/testing';
+import { provideToastr } from 'ngx-toastr';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { Reports } from './reports';
@@ -8,7 +11,8 @@ describe('Reports', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
- imports: [Reports],
+ imports: [Reports, HttpClientTestingModule],
+ providers: [provideHttpClient(), provideToastr()],
}).compileComponents();
fixture = TestBed.createComponent(Reports);
diff --git a/anms-ui/src/app/features/management/builder/builder.spec.ts b/anms-ui/src/app/features/management/builder/builder.spec.ts
index ebd1aa59..baeb7753 100644
--- a/anms-ui/src/app/features/management/builder/builder.spec.ts
+++ b/anms-ui/src/app/features/management/builder/builder.spec.ts
@@ -1,3 +1,6 @@
+import { provideHttpClient } from '@angular/common/http';
+import { HttpClientTestingModule } from '@angular/common/http/testing';
+import { provideToastr } from 'ngx-toastr';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { Builder } from './builder';
@@ -8,7 +11,8 @@ describe('Builder', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
- imports: [Builder],
+ imports: [Builder, HttpClientTestingModule],
+ providers: [provideHttpClient(), provideToastr()],
}).compileComponents();
fixture = TestBed.createComponent(Builder);
diff --git a/anms-ui/src/app/features/page-not-found/page-not-found.spec.ts b/anms-ui/src/app/features/page-not-found/page-not-found.spec.ts
index 8024e435..62cd0225 100644
--- a/anms-ui/src/app/features/page-not-found/page-not-found.spec.ts
+++ b/anms-ui/src/app/features/page-not-found/page-not-found.spec.ts
@@ -1,3 +1,4 @@
+import { provideRouter } from '@angular/router';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { PageNotFound } from './page-not-found';
@@ -9,6 +10,7 @@ describe('PageNotFound', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [PageNotFound],
+ providers: [provideRouter([])],
}).compileComponents();
fixture = TestBed.createComponent(PageNotFound);
diff --git a/anms-ui/src/app/features/status/status.spec.ts b/anms-ui/src/app/features/status/status.spec.ts
index 38397a7b..67f64de1 100644
--- a/anms-ui/src/app/features/status/status.spec.ts
+++ b/anms-ui/src/app/features/status/status.spec.ts
@@ -1,3 +1,7 @@
+import { provideHttpClient } from '@angular/common/http';
+import { HttpClientTestingModule } from '@angular/common/http/testing';
+import { provideToastr } from 'ngx-toastr';
+import { provideRouter } from '@angular/router';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { Status } from './status';
@@ -8,7 +12,12 @@ describe('Status', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
- imports: [Status],
+ imports: [Status, HttpClientTestingModule],
+ providers: [
+ provideHttpClient(),
+ provideToastr(),
+ provideRouter([]),
+ ],
}).compileComponents();
fixture = TestBed.createComponent(Status);
diff --git a/anms-ui/src/app/layout/header/header.spec.ts b/anms-ui/src/app/layout/header/header.spec.ts
index 9ef7403f..5a648ba7 100644
--- a/anms-ui/src/app/layout/header/header.spec.ts
+++ b/anms-ui/src/app/layout/header/header.spec.ts
@@ -1,3 +1,4 @@
+import { provideRouter } from '@angular/router';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { Header } from './header';
@@ -9,6 +10,7 @@ describe('Header', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [Header],
+ providers: [provideRouter([])],
}).compileComponents();
fixture = TestBed.createComponent(Header);
diff --git a/anms-ui/src/app/layout/sidebar/sidebar.spec.ts b/anms-ui/src/app/layout/sidebar/sidebar.spec.ts
index 2f291a9a..e42f35f5 100644
--- a/anms-ui/src/app/layout/sidebar/sidebar.spec.ts
+++ b/anms-ui/src/app/layout/sidebar/sidebar.spec.ts
@@ -1,3 +1,7 @@
+import { provideHttpClient } from '@angular/common/http';
+import { HttpClientTestingModule } from '@angular/common/http/testing';
+import { provideToastr } from 'ngx-toastr';
+import { provideRouter } from '@angular/router';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { Sidebar } from './sidebar';
@@ -8,9 +12,17 @@ describe('Sidebar', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
- imports: [Sidebar],
+ imports: [Sidebar, HttpClientTestingModule],
+ providers: [
+ provideHttpClient(),
+ provideToastr(),
+ provideRouter([]),
+ ],
}).compileComponents();
+ // Provide DOM element that sidebar.ngAfterViewInit looks for
+ document.body.innerHTML = ' ';
+
fixture = TestBed.createComponent(Sidebar);
component = fixture.componentInstance;
await fixture.whenStable();
diff --git a/anms-ui/src/app/store/modules/service-status.spec.ts b/anms-ui/src/app/store/modules/service-status.spec.ts
index 12de1b8c..c3731b4b 100644
--- a/anms-ui/src/app/store/modules/service-status.spec.ts
+++ b/anms-ui/src/app/store/modules/service-status.spec.ts
@@ -1,3 +1,6 @@
+import { provideHttpClient } from '@angular/common/http';
+import { HttpClientTestingModule } from '@angular/common/http/testing';
+import { provideToastr } from 'ngx-toastr';
import { TestBed } from '@angular/core/testing';
import {ServiceStatusService} from './service-status.service';
@@ -6,7 +9,10 @@ describe('StatusStore', () => {
let service: ServiceStatusService;
beforeEach(() => {
- TestBed.configureTestingModule({});
+ TestBed.configureTestingModule({
+ imports: [HttpClientTestingModule],
+ providers: [provideHttpClient(), provideToastr()],
+ });
service = TestBed.inject(ServiceStatusService);
});