This is an automated email from the ASF dual-hosted git repository.

Duansg pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/hertzbeat.git


The following commit(s) were added to refs/heads/master by this push:
     new 8a62215e8b test(web): make web-app unit tests runnable (#4314)
8a62215e8b is described below

commit 8a62215e8b19d2f16908e517b5230eece694d2e4
Author: NekoPunch <[email protected]>
AuthorDate: Wed Aug 12 08:26:38 2026 -0700

    test(web): make web-app unit tests runnable (#4314)
    
    Co-authored-by: Duansg <[email protected]>
---
 .github/workflows/frontend-build-test.yml          |  4 ++
 web-app/karma.conf.js                              |  3 +-
 web-app/package.json                               |  4 +-
 web-app/src/app/core/i18n/i18n.service.spec.ts     |  2 +-
 .../setting-drawer-i18n.directive.spec.ts          | 41 ++++++------
 .../alert-center/alert-center.component.spec.ts    |  5 +-
 .../alert-group-converge.component.spec.ts         |  5 +-
 .../alert-inhibit/alert-inhibit.component.spec.ts  |  5 +-
 .../alert-integration.component.spec.ts            |  6 +-
 .../alert-notice-receiver.component.spec.ts        |  5 +-
 .../alert-notice-rule.component.spec.ts            |  5 +-
 .../alert-notice-template.component.spec.ts        |  5 +-
 .../alert-notice/alert-notice.component.spec.ts    |  5 +-
 .../alert-setting/alert-setting.component.spec.ts  |  5 +-
 .../alert-silence/alert-silence.component.spec.ts  |  5 +-
 .../app/routes/bulletin/bulletin.component.spec.ts |  5 +-
 .../monitor-data-chart.component.spec.ts           |  5 +-
 .../monitor-data-table.component.spec.ts           |  5 +-
 .../monitor-detail.component.spec.ts               |  5 +-
 .../monitor-edit/monitor-edit.component.spec.ts    |  5 +-
 .../monitor-form/monitor-form.component.spec.ts    |  8 ++-
 .../monitor-list/monitor-list.component.spec.ts    |  5 +-
 .../monitor-new/monitor-new.component.spec.ts      |  5 +-
 .../setting/collector/collector.component.spec.ts  |  5 +-
 .../routes/setting/define/define.component.spec.ts |  5 +-
 .../routes/setting/label/label.component.spec.ts   |  5 +-
 .../setting/plugins/plugin.component.spec.ts       |  5 +-
 .../message-server.component.spec.ts               |  5 +-
 .../object-store/object-store.component.spec.ts    |  5 +-
 .../setting/settings/settings.component.spec.ts    |  7 +-
 .../system-config/system-config.component.spec.ts  |  5 +-
 .../routes/setting/status/status.component.spec.ts |  5 +-
 .../status-public/status-public.component.spec.ts  |  5 +-
 .../src/app/service/alert-define.service.spec.ts   |  3 +-
 .../src/app/service/alert-group.service.spec.ts    |  3 +-
 .../src/app/service/alert-inhibit.service.spec.ts  |  3 +-
 .../src/app/service/alert-silence.service.spec.ts  |  3 +-
 web-app/src/app/service/alert.service.spec.ts      |  3 +-
 web-app/src/app/service/app-define.service.spec.ts |  3 +-
 web-app/src/app/service/auth.service.spec.ts       |  3 +-
 web-app/src/app/service/collector.service.spec.ts  |  3 +-
 web-app/src/app/service/label.service.spec.ts      |  3 +-
 web-app/src/app/service/monitor.service.spec.ts    |  3 +-
 .../app/service/notice-receiver.service.spec.ts    |  3 +-
 .../src/app/service/notice-rule.service.spec.ts    |  3 +-
 .../app/service/notice-template.service.spec.ts    |  3 +-
 web-app/src/app/service/plugin.service.spec.ts     |  3 +-
 .../app/service/status-page-public.service.spec.ts |  3 +-
 .../src/app/service/status-page.service.spec.ts    |  3 +-
 .../configurable-field.component.spec.ts           |  5 +-
 .../form-field/form-field.component.spec.ts        |  6 +-
 .../help-message-show.component.spec.ts            |  5 +-
 .../label-selector.component.spec.ts               |  5 +-
 .../monitor-select-list.component.spec.ts          |  5 +-
 .../monitor-select-menu.component.spec.ts          |  5 +-
 .../multi-func-input.component.spec.ts             |  6 +-
 .../components/toolbar/toolbar.component.spec.ts   |  5 +-
 web-app/src/testing.ts                             | 76 ++++++++++++++++++++++
 web-app/tsconfig.json                              |  3 +
 59 files changed, 219 insertions(+), 150 deletions(-)

diff --git a/.github/workflows/frontend-build-test.yml 
b/.github/workflows/frontend-build-test.yml
index 9a403df8d4..2998dfad75 100644
--- a/.github/workflows/frontend-build-test.yml
+++ b/.github/workflows/frontend-build-test.yml
@@ -37,6 +37,7 @@ concurrency:
 jobs:
   build:
     runs-on: ubuntu-latest
+    timeout-minutes: 30
     steps:
     - uses: actions/checkout@v4
     - uses: pnpm/action-setup@v4
@@ -53,3 +54,6 @@ jobs:
     - name: EsLint Test
       working-directory: web-app
       run: pnpm lint:ts
+    - name: Unit Test
+      working-directory: web-app
+      run: pnpm test
diff --git a/web-app/karma.conf.js b/web-app/karma.conf.js
index 78f65e57f0..ca61318899 100644
--- a/web-app/karma.conf.js
+++ b/web-app/karma.conf.js
@@ -18,8 +18,7 @@ module.exports = function (config) {
         // the possible options are listed at 
https://jasmine.github.io/api/edge/Configuration.html
         // for example, you can disable the random execution with `random: 
false`
         // or set a specific seed with `seed: 4321`
-      },
-      clearContext: false // leave Jasmine Spec Runner output visible in 
browser
+      }
     },
     jasmineHtmlReporter: {
       suppressAll: true // removes the duplicated traces
diff --git a/web-app/package.json b/web-app/package.json
index 4bf1e9bee5..ccb88ce1d1 100644
--- a/web-app/package.json
+++ b/web-app/package.json
@@ -23,12 +23,12 @@
     "start": "ng serve --proxy-config proxy.conf.json",
     "build": "npm run ng-high-memory build",
     "watch": "ng build --watch --configuration development",
-    "test": "ng test",
+    "test": "ng test --watch=false --browsers=ChromeHeadless",
     "ng-high-memory": "node --max_old_space_size=8000 
./node_modules/@angular/cli/bin/ng",
     "hmr": "ng s -o --hmr",
     "analyze": "npm run ng-high-memory build -- --source-map",
     "analyze:view": "source-map-explorer dist/**/*.js",
-    "test-coverage": "ng test --code-coverage --watch=false",
+    "test-coverage": "ng test --code-coverage --watch=false 
--browsers=ChromeHeadless",
     "color-less": "ng-alain-plugin-theme -t=colorLess",
     "theme": "ng-alain-plugin-theme -t=themeCss",
     "icon": "ng g ng-alain:plugin icon",
diff --git a/web-app/src/app/core/i18n/i18n.service.spec.ts 
b/web-app/src/app/core/i18n/i18n.service.spec.ts
index 4bb33b95fe..87afd66bd7 100644
--- a/web-app/src/app/core/i18n/i18n.service.spec.ts
+++ b/web-app/src/app/core/i18n/i18n.service.spec.ts
@@ -71,7 +71,7 @@ describe('Service: I18n', () => {
   it('should be use default language when the browser language is not in the 
list', () => {
     spyOnProperty(navigator, 'languages').and.returnValue(['es-419']);
     genModule();
-    expect(srv.defaultLang).toBe('zh-CN');
+    expect(srv.defaultLang).toBe('en-US');
   });
 
   it('should be trigger notify when changed language', () => {
diff --git 
a/web-app/src/app/layout/basic/directives/setting-drawer-i18n.directive.spec.ts 
b/web-app/src/app/layout/basic/directives/setting-drawer-i18n.directive.spec.ts
index 84fc9c8fbd..d6dcc4a4cd 100644
--- 
a/web-app/src/app/layout/basic/directives/setting-drawer-i18n.directive.spec.ts
+++ 
b/web-app/src/app/layout/basic/directives/setting-drawer-i18n.directive.spec.ts
@@ -18,7 +18,7 @@
  */
 
 import { HttpClientTestingModule, HttpTestingController } from 
'@angular/common/http/testing';
-import { Component, NgZone } from '@angular/core';
+import { Component } from '@angular/core';
 import { ComponentFixture, TestBed, fakeAsync, tick } from 
'@angular/core/testing';
 import { By } from '@angular/platform-browser';
 import { I18NService } from '@core';
@@ -45,6 +45,7 @@ describe('SettingDrawerI18nDirective', () => {
   let i18nService: jasmine.SpyObj<I18NService>;
   let httpMock: HttpTestingController;
   let mockTranslations: { [key: string]: string };
+  const languages = ['zh-CN', 'en-US', 'ja-JP', 'pt-BR', 'zh-TW', 'ko-KR'];
 
   const mockI18nData = {
     'zh-CN': {
@@ -102,7 +103,7 @@ describe('SettingDrawerI18nDirective', () => {
     await TestBed.configureTestingModule({
       imports: [HttpClientTestingModule],
       declarations: [TestComponent, SettingDrawerI18nDirective],
-      providers: [{ provide: ALAIN_I18N_TOKEN, useValue: i18nServiceSpy }, 
NgZone]
+      providers: [{ provide: ALAIN_I18N_TOKEN, useValue: i18nServiceSpy }]
     }).compileComponents();
 
     fixture = TestBed.createComponent(TestComponent);
@@ -120,36 +121,33 @@ describe('SettingDrawerI18nDirective', () => {
     httpMock.verify();
   });
 
-  it('should create', () => {
-    expect(directive).toBeTruthy();
-  });
-
-  it('should load mappings from i18n files', fakeAsync(() => {
+  function loadMappings(): void {
     fixture.detectChanges();
+    languages.forEach(lang => 
httpMock.expectOne(`./assets/i18n/${lang}.json`).flush(mockI18nData[lang as 
keyof typeof mockI18nData]));
+  }
 
-    const languages = ['zh-CN', 'en-US', 'ja-JP', 'pt-BR', 'zh-TW', 'ko-KR'];
-    const requests = languages.map(lang => 
httpMock.expectOne(`./assets/i18n/${lang}.json`));
+  function destroyFixture(): void {
+    fixture.destroy();
+    tick(2000);
+  }
 
-    languages.forEach((lang, index) => {
-      requests[index].flush(mockI18nData[lang as keyof typeof mockI18nData]);
-    });
+  it('should create', fakeAsync(() => {
+    loadMappings();
+    expect(directive).toBeTruthy();
+    destroyFixture();
+  }));
 
+  it('should load mappings from i18n files', fakeAsync(() => {
+    loadMappings();
     tick(100);
     fixture.detectChanges();
 
     expect(i18nService.fanyi).toHaveBeenCalled();
+    destroyFixture();
   }));
 
   it('should replace Chinese text with translations', fakeAsync(() => {
-    fixture.detectChanges();
-
-    const languages = ['zh-CN', 'en-US', 'ja-JP', 'pt-BR', 'zh-TW', 'ko-KR'];
-    const requests = languages.map(lang => 
httpMock.expectOne(`./assets/i18n/${lang}.json`));
-
-    languages.forEach((lang, index) => {
-      requests[index].flush(mockI18nData[lang as keyof typeof mockI18nData]);
-    });
-
+    loadMappings();
     tick(2000);
     fixture.detectChanges();
     tick(100);
@@ -161,5 +159,6 @@ describe('SettingDrawerI18nDirective', () => {
         expect(themeColorDiv.textContent).toContain('Theme Color');
       }
     }
+    destroyFixture();
   }));
 });
diff --git 
a/web-app/src/app/routes/alert/alert-center/alert-center.component.spec.ts 
b/web-app/src/app/routes/alert/alert-center/alert-center.component.spec.ts
index 08d092b8d0..64b70ba9f2 100644
--- a/web-app/src/app/routes/alert/alert-center/alert-center.component.spec.ts
+++ b/web-app/src/app/routes/alert/alert-center/alert-center.component.spec.ts
@@ -18,6 +18,7 @@
  */
 
 import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { configureShallowTest } from '@testing';
 
 import { AlertCenterComponent } from './alert-center.component';
 
@@ -26,9 +27,7 @@ describe('AlertCenterComponent', () => {
   let fixture: ComponentFixture<AlertCenterComponent>;
 
   beforeEach(async () => {
-    await TestBed.configureTestingModule({
-      declarations: [AlertCenterComponent]
-    }).compileComponents();
+    await configureShallowTest(AlertCenterComponent).compileComponents();
   });
 
   beforeEach(() => {
diff --git 
a/web-app/src/app/routes/alert/alert-group/alert-group-converge.component.spec.ts
 
b/web-app/src/app/routes/alert/alert-group/alert-group-converge.component.spec.ts
index f1d775cf00..af6dd8934b 100644
--- 
a/web-app/src/app/routes/alert/alert-group/alert-group-converge.component.spec.ts
+++ 
b/web-app/src/app/routes/alert/alert-group/alert-group-converge.component.spec.ts
@@ -18,6 +18,7 @@
  */
 
 import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { configureShallowTest } from '@testing';
 
 import { AlertGroupConvergeComponent } from './alert-group-converge.component';
 
@@ -26,9 +27,7 @@ describe('AlertConvergeComponent', () => {
   let fixture: ComponentFixture<AlertGroupConvergeComponent>;
 
   beforeEach(async () => {
-    await TestBed.configureTestingModule({
-      declarations: [AlertGroupConvergeComponent]
-    }).compileComponents();
+    await 
configureShallowTest(AlertGroupConvergeComponent).compileComponents();
 
     fixture = TestBed.createComponent(AlertGroupConvergeComponent);
     component = fixture.componentInstance;
diff --git 
a/web-app/src/app/routes/alert/alert-inhibit/alert-inhibit.component.spec.ts 
b/web-app/src/app/routes/alert/alert-inhibit/alert-inhibit.component.spec.ts
index 98fcb9c5d9..63e79b229e 100644
--- a/web-app/src/app/routes/alert/alert-inhibit/alert-inhibit.component.spec.ts
+++ b/web-app/src/app/routes/alert/alert-inhibit/alert-inhibit.component.spec.ts
@@ -18,6 +18,7 @@
  */
 
 import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { configureShallowTest } from '@testing';
 
 import { AlertInhibitComponent } from './alert-inhibit.component';
 
@@ -26,9 +27,7 @@ describe('AlertInhibitComponent', () => {
   let fixture: ComponentFixture<AlertInhibitComponent>;
 
   beforeEach(async () => {
-    await TestBed.configureTestingModule({
-      imports: [AlertInhibitComponent]
-    }).compileComponents();
+    await configureShallowTest(AlertInhibitComponent).compileComponents();
 
     fixture = TestBed.createComponent(AlertInhibitComponent);
     component = fixture.componentInstance;
diff --git 
a/web-app/src/app/routes/alert/alert-integration/alert-integration.component.spec.ts
 
b/web-app/src/app/routes/alert/alert-integration/alert-integration.component.spec.ts
index c5c205dc53..28d6084128 100644
--- 
a/web-app/src/app/routes/alert/alert-integration/alert-integration.component.spec.ts
+++ 
b/web-app/src/app/routes/alert/alert-integration/alert-integration.component.spec.ts
@@ -18,6 +18,8 @@
  */
 
 import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { configureStandaloneTest } from '@testing';
+import { MarkdownModule } from 'ngx-markdown';
 
 import { AlertIntegrationComponent } from './alert-integration.component';
 
@@ -26,9 +28,7 @@ describe('AlertIntegrationComponent', () => {
   let fixture: ComponentFixture<AlertIntegrationComponent>;
 
   beforeEach(async () => {
-    await TestBed.configureTestingModule({
-      imports: [AlertIntegrationComponent]
-    }).compileComponents();
+    await configureStandaloneTest(AlertIntegrationComponent, 
[MarkdownModule.forRoot()]).compileComponents();
 
     fixture = TestBed.createComponent(AlertIntegrationComponent);
     component = fixture.componentInstance;
diff --git 
a/web-app/src/app/routes/alert/alert-notice/alert-notice-receiver/alert-notice-receiver.component.spec.ts
 
b/web-app/src/app/routes/alert/alert-notice/alert-notice-receiver/alert-notice-receiver.component.spec.ts
index 348b6c67ce..cdd7671620 100644
--- 
a/web-app/src/app/routes/alert/alert-notice/alert-notice-receiver/alert-notice-receiver.component.spec.ts
+++ 
b/web-app/src/app/routes/alert/alert-notice/alert-notice-receiver/alert-notice-receiver.component.spec.ts
@@ -18,6 +18,7 @@
  */
 
 import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { configureShallowTest } from '@testing';
 
 import { AlertNoticeReceiverComponent } from 
'./alert-notice-receiver.component';
 
@@ -26,9 +27,7 @@ describe('AlertNoticeReceiverComponent', () => {
   let fixture: ComponentFixture<AlertNoticeReceiverComponent>;
 
   beforeEach(async () => {
-    await TestBed.configureTestingModule({
-      imports: [AlertNoticeReceiverComponent]
-    }).compileComponents();
+    await 
configureShallowTest(AlertNoticeReceiverComponent).compileComponents();
 
     fixture = TestBed.createComponent(AlertNoticeReceiverComponent);
     component = fixture.componentInstance;
diff --git 
a/web-app/src/app/routes/alert/alert-notice/alert-notice-rule/alert-notice-rule.component.spec.ts
 
b/web-app/src/app/routes/alert/alert-notice/alert-notice-rule/alert-notice-rule.component.spec.ts
index 5d53537033..0936badeb5 100644
--- 
a/web-app/src/app/routes/alert/alert-notice/alert-notice-rule/alert-notice-rule.component.spec.ts
+++ 
b/web-app/src/app/routes/alert/alert-notice/alert-notice-rule/alert-notice-rule.component.spec.ts
@@ -18,6 +18,7 @@
  */
 
 import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { configureShallowTest } from '@testing';
 
 import { AlertNoticeRuleComponent } from './alert-notice-rule.component';
 
@@ -26,9 +27,7 @@ describe('AlertNoticeRuleComponent', () => {
   let fixture: ComponentFixture<AlertNoticeRuleComponent>;
 
   beforeEach(async () => {
-    await TestBed.configureTestingModule({
-      imports: [AlertNoticeRuleComponent]
-    }).compileComponents();
+    await configureShallowTest(AlertNoticeRuleComponent).compileComponents();
 
     fixture = TestBed.createComponent(AlertNoticeRuleComponent);
     component = fixture.componentInstance;
diff --git 
a/web-app/src/app/routes/alert/alert-notice/alert-notice-template/alert-notice-template.component.spec.ts
 
b/web-app/src/app/routes/alert/alert-notice/alert-notice-template/alert-notice-template.component.spec.ts
index cca99270e5..328f9aa438 100644
--- 
a/web-app/src/app/routes/alert/alert-notice/alert-notice-template/alert-notice-template.component.spec.ts
+++ 
b/web-app/src/app/routes/alert/alert-notice/alert-notice-template/alert-notice-template.component.spec.ts
@@ -18,6 +18,7 @@
  */
 
 import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { configureShallowTest } from '@testing';
 
 import { AlertNoticeTemplateComponent } from 
'./alert-notice-template.component';
 
@@ -26,9 +27,7 @@ describe('AlertNoticeTemplateComponent', () => {
   let fixture: ComponentFixture<AlertNoticeTemplateComponent>;
 
   beforeEach(async () => {
-    await TestBed.configureTestingModule({
-      imports: [AlertNoticeTemplateComponent]
-    }).compileComponents();
+    await 
configureShallowTest(AlertNoticeTemplateComponent).compileComponents();
 
     fixture = TestBed.createComponent(AlertNoticeTemplateComponent);
     component = fixture.componentInstance;
diff --git 
a/web-app/src/app/routes/alert/alert-notice/alert-notice.component.spec.ts 
b/web-app/src/app/routes/alert/alert-notice/alert-notice.component.spec.ts
index 61f97deb62..e635918d31 100644
--- a/web-app/src/app/routes/alert/alert-notice/alert-notice.component.spec.ts
+++ b/web-app/src/app/routes/alert/alert-notice/alert-notice.component.spec.ts
@@ -18,6 +18,7 @@
  */
 
 import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { configureShallowTest } from '@testing';
 
 import { AlertNoticeComponent } from './alert-notice.component';
 
@@ -26,9 +27,7 @@ describe('AlertNoticeComponent', () => {
   let fixture: ComponentFixture<AlertNoticeComponent>;
 
   beforeEach(async () => {
-    await TestBed.configureTestingModule({
-      declarations: [AlertNoticeComponent]
-    }).compileComponents();
+    await configureShallowTest(AlertNoticeComponent).compileComponents();
   });
 
   beforeEach(() => {
diff --git 
a/web-app/src/app/routes/alert/alert-setting/alert-setting.component.spec.ts 
b/web-app/src/app/routes/alert/alert-setting/alert-setting.component.spec.ts
index b36b6deeb5..5875360bd6 100644
--- a/web-app/src/app/routes/alert/alert-setting/alert-setting.component.spec.ts
+++ b/web-app/src/app/routes/alert/alert-setting/alert-setting.component.spec.ts
@@ -18,6 +18,7 @@
  */
 
 import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { configureShallowTest } from '@testing';
 
 import { AlertSettingComponent } from './alert-setting.component';
 
@@ -26,9 +27,7 @@ describe('AlertSettingComponent', () => {
   let fixture: ComponentFixture<AlertSettingComponent>;
 
   beforeEach(async () => {
-    await TestBed.configureTestingModule({
-      declarations: [AlertSettingComponent]
-    }).compileComponents();
+    await configureShallowTest(AlertSettingComponent).compileComponents();
   });
 
   beforeEach(() => {
diff --git 
a/web-app/src/app/routes/alert/alert-silence/alert-silence.component.spec.ts 
b/web-app/src/app/routes/alert/alert-silence/alert-silence.component.spec.ts
index f6f240176b..3dc81863ca 100644
--- a/web-app/src/app/routes/alert/alert-silence/alert-silence.component.spec.ts
+++ b/web-app/src/app/routes/alert/alert-silence/alert-silence.component.spec.ts
@@ -18,6 +18,7 @@
  */
 
 import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { configureShallowTest } from '@testing';
 
 import { AlertSilenceComponent } from './alert-silence.component';
 
@@ -26,9 +27,7 @@ describe('AlertSilenceComponent', () => {
   let fixture: ComponentFixture<AlertSilenceComponent>;
 
   beforeEach(async () => {
-    await TestBed.configureTestingModule({
-      declarations: [AlertSilenceComponent]
-    }).compileComponents();
+    await configureShallowTest(AlertSilenceComponent).compileComponents();
 
     fixture = TestBed.createComponent(AlertSilenceComponent);
     component = fixture.componentInstance;
diff --git a/web-app/src/app/routes/bulletin/bulletin.component.spec.ts 
b/web-app/src/app/routes/bulletin/bulletin.component.spec.ts
index 5e1fc10402..f2fd28b826 100644
--- a/web-app/src/app/routes/bulletin/bulletin.component.spec.ts
+++ b/web-app/src/app/routes/bulletin/bulletin.component.spec.ts
@@ -18,6 +18,7 @@
  */
 
 import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { configureShallowTest } from '@testing';
 
 import { BulletinComponent } from './bulletin.component';
 
@@ -26,9 +27,7 @@ describe('BulletinComponent', () => {
   let fixture: ComponentFixture<BulletinComponent>;
 
   beforeEach(async () => {
-    await TestBed.configureTestingModule({
-      declarations: [BulletinComponent]
-    }).compileComponents();
+    await configureShallowTest(BulletinComponent).compileComponents();
   });
 
   beforeEach(() => {
diff --git 
a/web-app/src/app/routes/monitor/monitor-data-chart/monitor-data-chart.component.spec.ts
 
b/web-app/src/app/routes/monitor/monitor-data-chart/monitor-data-chart.component.spec.ts
index 6ae8d454c1..a320fd17c1 100644
--- 
a/web-app/src/app/routes/monitor/monitor-data-chart/monitor-data-chart.component.spec.ts
+++ 
b/web-app/src/app/routes/monitor/monitor-data-chart/monitor-data-chart.component.spec.ts
@@ -18,6 +18,7 @@
  */
 
 import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { configureShallowTest } from '@testing';
 
 import { MonitorDataChartComponent } from './monitor-data-chart.component';
 
@@ -26,9 +27,7 @@ describe('MonitorDataChartComponent', () => {
   let fixture: ComponentFixture<MonitorDataChartComponent>;
 
   beforeEach(async () => {
-    await TestBed.configureTestingModule({
-      declarations: [MonitorDataChartComponent]
-    }).compileComponents();
+    await configureShallowTest(MonitorDataChartComponent).compileComponents();
   });
 
   beforeEach(() => {
diff --git 
a/web-app/src/app/routes/monitor/monitor-data-table/monitor-data-table.component.spec.ts
 
b/web-app/src/app/routes/monitor/monitor-data-table/monitor-data-table.component.spec.ts
index de753cfb96..8aee5f90b7 100644
--- 
a/web-app/src/app/routes/monitor/monitor-data-table/monitor-data-table.component.spec.ts
+++ 
b/web-app/src/app/routes/monitor/monitor-data-table/monitor-data-table.component.spec.ts
@@ -18,6 +18,7 @@
  */
 
 import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { configureShallowTest } from '@testing';
 
 import { MonitorDataTableComponent } from './monitor-data-table.component';
 
@@ -26,9 +27,7 @@ describe('MonitorDataChartComponent', () => {
   let fixture: ComponentFixture<MonitorDataTableComponent>;
 
   beforeEach(async () => {
-    await TestBed.configureTestingModule({
-      declarations: [MonitorDataTableComponent]
-    }).compileComponents();
+    await configureShallowTest(MonitorDataTableComponent).compileComponents();
   });
 
   beforeEach(() => {
diff --git 
a/web-app/src/app/routes/monitor/monitor-detail/monitor-detail.component.spec.ts
 
b/web-app/src/app/routes/monitor/monitor-detail/monitor-detail.component.spec.ts
index 74b4b1442d..958c5f401e 100644
--- 
a/web-app/src/app/routes/monitor/monitor-detail/monitor-detail.component.spec.ts
+++ 
b/web-app/src/app/routes/monitor/monitor-detail/monitor-detail.component.spec.ts
@@ -18,6 +18,7 @@
  */
 
 import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { configureShallowTest } from '@testing';
 
 import { MonitorDetailComponent } from './monitor-detail.component';
 
@@ -26,9 +27,7 @@ describe('MonitorDetailComponent', () => {
   let fixture: ComponentFixture<MonitorDetailComponent>;
 
   beforeEach(async () => {
-    await TestBed.configureTestingModule({
-      declarations: [MonitorDetailComponent]
-    }).compileComponents();
+    await configureShallowTest(MonitorDetailComponent).compileComponents();
   });
 
   beforeEach(() => {
diff --git 
a/web-app/src/app/routes/monitor/monitor-edit/monitor-edit.component.spec.ts 
b/web-app/src/app/routes/monitor/monitor-edit/monitor-edit.component.spec.ts
index bed99b721b..688ea06b56 100644
--- a/web-app/src/app/routes/monitor/monitor-edit/monitor-edit.component.spec.ts
+++ b/web-app/src/app/routes/monitor/monitor-edit/monitor-edit.component.spec.ts
@@ -18,6 +18,7 @@
  */
 
 import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { configureShallowTest } from '@testing';
 
 import { MonitorEditComponent } from './monitor-edit.component';
 
@@ -26,9 +27,7 @@ describe('MonitorModifyComponent', () => {
   let fixture: ComponentFixture<MonitorEditComponent>;
 
   beforeEach(async () => {
-    await TestBed.configureTestingModule({
-      declarations: [MonitorEditComponent]
-    }).compileComponents();
+    await configureShallowTest(MonitorEditComponent).compileComponents();
   });
 
   beforeEach(() => {
diff --git 
a/web-app/src/app/routes/monitor/monitor-form/monitor-form.component.spec.ts 
b/web-app/src/app/routes/monitor/monitor-form/monitor-form.component.spec.ts
index 9a3dbd6e28..0b43dad05e 100644
--- a/web-app/src/app/routes/monitor/monitor-form/monitor-form.component.spec.ts
+++ b/web-app/src/app/routes/monitor/monitor-form/monitor-form.component.spec.ts
@@ -18,7 +18,10 @@
  */
 
 import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { FormsModule } from '@angular/forms';
+import { configureShallowTest } from '@testing';
 
+import { Monitor } from '../../../pojo/Monitor';
 import { MonitorFormComponent } from './monitor-form.component';
 
 describe('MonitorFormComponent', () => {
@@ -26,14 +29,13 @@ describe('MonitorFormComponent', () => {
   let fixture: ComponentFixture<MonitorFormComponent>;
 
   beforeEach(async () => {
-    await TestBed.configureTestingModule({
-      declarations: [MonitorFormComponent]
-    }).compileComponents();
+    await configureShallowTest(MonitorFormComponent, 
[FormsModule]).compileComponents();
   });
 
   beforeEach(() => {
     fixture = TestBed.createComponent(MonitorFormComponent);
     component = fixture.componentInstance;
+    component.monitor = new Monitor();
     fixture.detectChanges();
   });
 
diff --git 
a/web-app/src/app/routes/monitor/monitor-list/monitor-list.component.spec.ts 
b/web-app/src/app/routes/monitor/monitor-list/monitor-list.component.spec.ts
index 349dbf6ca2..9538bc22f3 100644
--- a/web-app/src/app/routes/monitor/monitor-list/monitor-list.component.spec.ts
+++ b/web-app/src/app/routes/monitor/monitor-list/monitor-list.component.spec.ts
@@ -18,6 +18,7 @@
  */
 
 import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { configureShallowTest } from '@testing';
 
 import { MonitorListComponent } from './monitor-list.component';
 
@@ -26,9 +27,7 @@ describe('MonitorListComponent', () => {
   let fixture: ComponentFixture<MonitorListComponent>;
 
   beforeEach(async () => {
-    await TestBed.configureTestingModule({
-      declarations: [MonitorListComponent]
-    }).compileComponents();
+    await configureShallowTest(MonitorListComponent).compileComponents();
   });
 
   beforeEach(() => {
diff --git 
a/web-app/src/app/routes/monitor/monitor-new/monitor-new.component.spec.ts 
b/web-app/src/app/routes/monitor/monitor-new/monitor-new.component.spec.ts
index f41a293f2c..5cf96a8b50 100644
--- a/web-app/src/app/routes/monitor/monitor-new/monitor-new.component.spec.ts
+++ b/web-app/src/app/routes/monitor/monitor-new/monitor-new.component.spec.ts
@@ -18,6 +18,7 @@
  */
 
 import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { configureShallowTest } from '@testing';
 
 import { MonitorNewComponent } from './monitor-new.component';
 
@@ -26,9 +27,7 @@ describe('MonitorAddComponent', () => {
   let fixture: ComponentFixture<MonitorNewComponent>;
 
   beforeEach(async () => {
-    await TestBed.configureTestingModule({
-      declarations: [MonitorNewComponent]
-    }).compileComponents();
+    await configureShallowTest(MonitorNewComponent).compileComponents();
   });
 
   beforeEach(() => {
diff --git 
a/web-app/src/app/routes/setting/collector/collector.component.spec.ts 
b/web-app/src/app/routes/setting/collector/collector.component.spec.ts
index 81c9ca07ad..3c41095d60 100644
--- a/web-app/src/app/routes/setting/collector/collector.component.spec.ts
+++ b/web-app/src/app/routes/setting/collector/collector.component.spec.ts
@@ -18,6 +18,7 @@
  */
 
 import { waitForAsync, ComponentFixture, TestBed } from 
'@angular/core/testing';
+import { configureShallowTest } from '@testing';
 
 import { CollectorComponent } from './collector.component';
 
@@ -26,9 +27,7 @@ describe('CollectorComponent', () => {
   let fixture: ComponentFixture<CollectorComponent>;
 
   beforeEach(waitForAsync(() => {
-    TestBed.configureTestingModule({
-      declarations: [CollectorComponent]
-    }).compileComponents();
+    configureShallowTest(CollectorComponent).compileComponents();
   }));
 
   beforeEach(() => {
diff --git a/web-app/src/app/routes/setting/define/define.component.spec.ts 
b/web-app/src/app/routes/setting/define/define.component.spec.ts
index 371e0003e8..71bffbabc5 100644
--- a/web-app/src/app/routes/setting/define/define.component.spec.ts
+++ b/web-app/src/app/routes/setting/define/define.component.spec.ts
@@ -18,6 +18,7 @@
  */
 
 import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { configureShallowTest } from '@testing';
 
 import { DefineComponent } from './define.component';
 
@@ -26,9 +27,7 @@ describe('DefineComponent', () => {
   let fixture: ComponentFixture<DefineComponent>;
 
   beforeEach(async () => {
-    await TestBed.configureTestingModule({
-      declarations: [DefineComponent]
-    }).compileComponents();
+    await configureShallowTest(DefineComponent).compileComponents();
 
     fixture = TestBed.createComponent(DefineComponent);
     component = fixture.componentInstance;
diff --git a/web-app/src/app/routes/setting/label/label.component.spec.ts 
b/web-app/src/app/routes/setting/label/label.component.spec.ts
index 86cd42b462..e6077f5de2 100644
--- a/web-app/src/app/routes/setting/label/label.component.spec.ts
+++ b/web-app/src/app/routes/setting/label/label.component.spec.ts
@@ -18,6 +18,7 @@
  */
 
 import { waitForAsync, ComponentFixture, TestBed } from 
'@angular/core/testing';
+import { configureShallowTest } from '@testing';
 
 import { SettingLabelComponent } from './label.component';
 
@@ -26,9 +27,7 @@ describe('SettingLabelComponent', () => {
   let fixture: ComponentFixture<SettingLabelComponent>;
 
   beforeEach(waitForAsync(() => {
-    TestBed.configureTestingModule({
-      declarations: [SettingLabelComponent]
-    }).compileComponents();
+    configureShallowTest(SettingLabelComponent).compileComponents();
   }));
 
   beforeEach(() => {
diff --git a/web-app/src/app/routes/setting/plugins/plugin.component.spec.ts 
b/web-app/src/app/routes/setting/plugins/plugin.component.spec.ts
index ffc816aa58..4733c00180 100644
--- a/web-app/src/app/routes/setting/plugins/plugin.component.spec.ts
+++ b/web-app/src/app/routes/setting/plugins/plugin.component.spec.ts
@@ -18,6 +18,7 @@
  */
 
 import { waitForAsync, ComponentFixture, TestBed } from 
'@angular/core/testing';
+import { configureShallowTest } from '@testing';
 
 import { SettingPluginsComponent } from './plugin.component';
 
@@ -26,9 +27,7 @@ describe('SettingPluginsComponent', () => {
   let fixture: ComponentFixture<SettingPluginsComponent>;
 
   beforeEach(waitForAsync(() => {
-    TestBed.configureTestingModule({
-      declarations: [SettingPluginsComponent]
-    }).compileComponents();
+    configureShallowTest(SettingPluginsComponent).compileComponents();
   }));
 
   beforeEach(() => {
diff --git 
a/web-app/src/app/routes/setting/settings/message-server/message-server.component.spec.ts
 
b/web-app/src/app/routes/setting/settings/message-server/message-server.component.spec.ts
index 1e293537d4..26adb31da8 100644
--- 
a/web-app/src/app/routes/setting/settings/message-server/message-server.component.spec.ts
+++ 
b/web-app/src/app/routes/setting/settings/message-server/message-server.component.spec.ts
@@ -18,6 +18,7 @@
  */
 
 import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { configureShallowTest } from '@testing';
 
 import { MessageServerComponent } from './message-server.component';
 
@@ -26,9 +27,7 @@ describe('MessageServerComponent', () => {
   let fixture: ComponentFixture<MessageServerComponent>;
 
   beforeEach(async () => {
-    await TestBed.configureTestingModule({
-      declarations: [MessageServerComponent]
-    }).compileComponents();
+    await configureShallowTest(MessageServerComponent).compileComponents();
 
     fixture = TestBed.createComponent(MessageServerComponent);
     component = fixture.componentInstance;
diff --git 
a/web-app/src/app/routes/setting/settings/object-store/object-store.component.spec.ts
 
b/web-app/src/app/routes/setting/settings/object-store/object-store.component.spec.ts
index 2b4215d1e0..ca857add72 100644
--- 
a/web-app/src/app/routes/setting/settings/object-store/object-store.component.spec.ts
+++ 
b/web-app/src/app/routes/setting/settings/object-store/object-store.component.spec.ts
@@ -18,6 +18,7 @@
  */
 
 import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { configureShallowTest } from '@testing';
 
 import { ObjectStoreComponent } from './object-store.component';
 
@@ -26,9 +27,7 @@ describe('ObjectStoreComponent', () => {
   let fixture: ComponentFixture<ObjectStoreComponent>;
 
   beforeEach(async () => {
-    await TestBed.configureTestingModule({
-      declarations: [ObjectStoreComponent]
-    }).compileComponents();
+    await configureShallowTest(ObjectStoreComponent).compileComponents();
 
     fixture = TestBed.createComponent(ObjectStoreComponent);
     component = fixture.componentInstance;
diff --git a/web-app/src/app/routes/setting/settings/settings.component.spec.ts 
b/web-app/src/app/routes/setting/settings/settings.component.spec.ts
index 34f682b177..d8de701aa6 100644
--- a/web-app/src/app/routes/setting/settings/settings.component.spec.ts
+++ b/web-app/src/app/routes/setting/settings/settings.component.spec.ts
@@ -18,6 +18,8 @@
  */
 
 import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { Router } from '@angular/router';
+import { configureShallowTest } from '@testing';
 
 import { SettingsComponent } from './settings.component';
 
@@ -26,10 +28,9 @@ describe('SettingsComponent', () => {
   let fixture: ComponentFixture<SettingsComponent>;
 
   beforeEach(async () => {
-    await TestBed.configureTestingModule({
-      declarations: [SettingsComponent]
-    }).compileComponents();
+    await configureShallowTest(SettingsComponent).compileComponents();
 
+    spyOnProperty(TestBed.inject(Router), 
'url').and.returnValue('/setting/settings/config');
     fixture = TestBed.createComponent(SettingsComponent);
     component = fixture.componentInstance;
     fixture.detectChanges();
diff --git 
a/web-app/src/app/routes/setting/settings/system-config/system-config.component.spec.ts
 
b/web-app/src/app/routes/setting/settings/system-config/system-config.component.spec.ts
index a0da5b178d..2aa8004823 100644
--- 
a/web-app/src/app/routes/setting/settings/system-config/system-config.component.spec.ts
+++ 
b/web-app/src/app/routes/setting/settings/system-config/system-config.component.spec.ts
@@ -18,6 +18,7 @@
  */
 
 import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { configureShallowTest } from '@testing';
 
 import { SystemConfigComponent } from './system-config.component';
 
@@ -26,9 +27,7 @@ describe('SystemConfigComponent', () => {
   let fixture: ComponentFixture<SystemConfigComponent>;
 
   beforeEach(async () => {
-    await TestBed.configureTestingModule({
-      declarations: [SystemConfigComponent]
-    }).compileComponents();
+    await configureShallowTest(SystemConfigComponent).compileComponents();
 
     fixture = TestBed.createComponent(SystemConfigComponent);
     component = fixture.componentInstance;
diff --git a/web-app/src/app/routes/setting/status/status.component.spec.ts 
b/web-app/src/app/routes/setting/status/status.component.spec.ts
index 04e0e93373..00e9ab4ecd 100644
--- a/web-app/src/app/routes/setting/status/status.component.spec.ts
+++ b/web-app/src/app/routes/setting/status/status.component.spec.ts
@@ -18,6 +18,7 @@
  */
 
 import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { configureShallowTest } from '@testing';
 
 import { StatusComponent } from './status.component';
 
@@ -26,9 +27,7 @@ describe('StatusComponent', () => {
   let fixture: ComponentFixture<StatusComponent>;
 
   beforeEach(async () => {
-    await TestBed.configureTestingModule({
-      declarations: [StatusComponent]
-    }).compileComponents();
+    await configureShallowTest(StatusComponent).compileComponents();
 
     fixture = TestBed.createComponent(StatusComponent);
     component = fixture.componentInstance;
diff --git 
a/web-app/src/app/routes/status-public/status-public.component.spec.ts 
b/web-app/src/app/routes/status-public/status-public.component.spec.ts
index 220804c9b7..878261d119 100644
--- a/web-app/src/app/routes/status-public/status-public.component.spec.ts
+++ b/web-app/src/app/routes/status-public/status-public.component.spec.ts
@@ -18,6 +18,7 @@
  */
 
 import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { configureShallowTest } from '@testing';
 
 import { StatusPublicComponent } from './status-public.component';
 
@@ -26,9 +27,7 @@ describe('StatusPublicComponent', () => {
   let fixture: ComponentFixture<StatusPublicComponent>;
 
   beforeEach(async () => {
-    await TestBed.configureTestingModule({
-      declarations: [StatusPublicComponent]
-    }).compileComponents();
+    await configureShallowTest(StatusPublicComponent).compileComponents();
 
     fixture = TestBed.createComponent(StatusPublicComponent);
     component = fixture.componentInstance;
diff --git a/web-app/src/app/service/alert-define.service.spec.ts 
b/web-app/src/app/service/alert-define.service.spec.ts
index d8e963fb6b..aa844c209f 100644
--- a/web-app/src/app/service/alert-define.service.spec.ts
+++ b/web-app/src/app/service/alert-define.service.spec.ts
@@ -18,6 +18,7 @@
  */
 
 import { TestBed } from '@angular/core/testing';
+import { configureHttpServiceTest } from '@testing';
 
 import { AlertDefineService } from './alert-define.service';
 
@@ -25,7 +26,7 @@ describe('AlertDefineService', () => {
   let service: AlertDefineService;
 
   beforeEach(() => {
-    TestBed.configureTestingModule({});
+    configureHttpServiceTest();
     service = TestBed.inject(AlertDefineService);
   });
 
diff --git a/web-app/src/app/service/alert-group.service.spec.ts 
b/web-app/src/app/service/alert-group.service.spec.ts
index 8964c07956..2e53b9fe22 100644
--- a/web-app/src/app/service/alert-group.service.spec.ts
+++ b/web-app/src/app/service/alert-group.service.spec.ts
@@ -18,6 +18,7 @@
  */
 
 import { TestBed } from '@angular/core/testing';
+import { configureHttpServiceTest } from '@testing';
 
 import { AlertGroupService } from './alert-group.service';
 
@@ -25,7 +26,7 @@ describe('AlertConvergeService', () => {
   let service: AlertGroupService;
 
   beforeEach(() => {
-    TestBed.configureTestingModule({});
+    configureHttpServiceTest();
     service = TestBed.inject(AlertGroupService);
   });
 
diff --git a/web-app/src/app/service/alert-inhibit.service.spec.ts 
b/web-app/src/app/service/alert-inhibit.service.spec.ts
index fb23e4ffa3..9e6f0648b5 100644
--- a/web-app/src/app/service/alert-inhibit.service.spec.ts
+++ b/web-app/src/app/service/alert-inhibit.service.spec.ts
@@ -18,6 +18,7 @@
  */
 
 import { TestBed } from '@angular/core/testing';
+import { configureHttpServiceTest } from '@testing';
 
 import { AlertInhibitService } from './alert-inhibit.service';
 
@@ -25,7 +26,7 @@ describe('AlertInhibitService', () => {
   let service: AlertInhibitService;
 
   beforeEach(() => {
-    TestBed.configureTestingModule({});
+    configureHttpServiceTest();
     service = TestBed.inject(AlertInhibitService);
   });
 
diff --git a/web-app/src/app/service/alert-silence.service.spec.ts 
b/web-app/src/app/service/alert-silence.service.spec.ts
index 5619c9a96b..7232756a95 100644
--- a/web-app/src/app/service/alert-silence.service.spec.ts
+++ b/web-app/src/app/service/alert-silence.service.spec.ts
@@ -18,6 +18,7 @@
  */
 
 import { TestBed } from '@angular/core/testing';
+import { configureHttpServiceTest } from '@testing';
 
 import { AlertSilenceService } from './alert-silence.service';
 
@@ -25,7 +26,7 @@ describe('AlertSilenceService', () => {
   let service: AlertSilenceService;
 
   beforeEach(() => {
-    TestBed.configureTestingModule({});
+    configureHttpServiceTest();
     service = TestBed.inject(AlertSilenceService);
   });
 
diff --git a/web-app/src/app/service/alert.service.spec.ts 
b/web-app/src/app/service/alert.service.spec.ts
index 782a0418ea..893d62b166 100644
--- a/web-app/src/app/service/alert.service.spec.ts
+++ b/web-app/src/app/service/alert.service.spec.ts
@@ -18,6 +18,7 @@
  */
 
 import { TestBed } from '@angular/core/testing';
+import { configureHttpServiceTest } from '@testing';
 
 import { AlertService } from './alert.service';
 
@@ -25,7 +26,7 @@ describe('AlertService', () => {
   let service: AlertService;
 
   beforeEach(() => {
-    TestBed.configureTestingModule({});
+    configureHttpServiceTest();
     service = TestBed.inject(AlertService);
   });
 
diff --git a/web-app/src/app/service/app-define.service.spec.ts 
b/web-app/src/app/service/app-define.service.spec.ts
index 540dfab3f7..01be720764 100644
--- a/web-app/src/app/service/app-define.service.spec.ts
+++ b/web-app/src/app/service/app-define.service.spec.ts
@@ -18,6 +18,7 @@
  */
 
 import { TestBed } from '@angular/core/testing';
+import { configureHttpServiceTest } from '@testing';
 
 import { AppDefineService } from './app-define.service';
 
@@ -25,7 +26,7 @@ describe('AppDefineService', () => {
   let service: AppDefineService;
 
   beforeEach(() => {
-    TestBed.configureTestingModule({});
+    configureHttpServiceTest();
     service = TestBed.inject(AppDefineService);
   });
 
diff --git a/web-app/src/app/service/auth.service.spec.ts 
b/web-app/src/app/service/auth.service.spec.ts
index bd5f2bbce6..650102d387 100644
--- a/web-app/src/app/service/auth.service.spec.ts
+++ b/web-app/src/app/service/auth.service.spec.ts
@@ -18,6 +18,7 @@
  */
 
 import { TestBed } from '@angular/core/testing';
+import { configureHttpServiceTest } from '@testing';
 
 import { AuthService } from './auth.service';
 
@@ -25,7 +26,7 @@ describe('AuthService', () => {
   let service: AuthService;
 
   beforeEach(() => {
-    TestBed.configureTestingModule({});
+    configureHttpServiceTest();
     service = TestBed.inject(AuthService);
   });
 
diff --git a/web-app/src/app/service/collector.service.spec.ts 
b/web-app/src/app/service/collector.service.spec.ts
index 235e5a7542..b533e2773b 100644
--- a/web-app/src/app/service/collector.service.spec.ts
+++ b/web-app/src/app/service/collector.service.spec.ts
@@ -18,6 +18,7 @@
  */
 
 import { TestBed } from '@angular/core/testing';
+import { configureHttpServiceTest } from '@testing';
 
 import { CollectorService } from './collector.service';
 
@@ -25,7 +26,7 @@ describe('CollectorService', () => {
   let service: CollectorService;
 
   beforeEach(() => {
-    TestBed.configureTestingModule({});
+    configureHttpServiceTest();
     service = TestBed.inject(CollectorService);
   });
 
diff --git a/web-app/src/app/service/label.service.spec.ts 
b/web-app/src/app/service/label.service.spec.ts
index 5d0950c5e5..b77da248d0 100644
--- a/web-app/src/app/service/label.service.spec.ts
+++ b/web-app/src/app/service/label.service.spec.ts
@@ -18,6 +18,7 @@
  */
 
 import { TestBed } from '@angular/core/testing';
+import { configureHttpServiceTest } from '@testing';
 
 import { LabelService } from './label.service';
 
@@ -25,7 +26,7 @@ describe('LabelService', () => {
   let service: LabelService;
 
   beforeEach(() => {
-    TestBed.configureTestingModule({});
+    configureHttpServiceTest();
     service = TestBed.inject(LabelService);
   });
 
diff --git a/web-app/src/app/service/monitor.service.spec.ts 
b/web-app/src/app/service/monitor.service.spec.ts
index 08cc30e450..c431e2b85c 100644
--- a/web-app/src/app/service/monitor.service.spec.ts
+++ b/web-app/src/app/service/monitor.service.spec.ts
@@ -18,6 +18,7 @@
  */
 
 import { TestBed } from '@angular/core/testing';
+import { configureHttpServiceTest } from '@testing';
 
 import { MonitorService } from './monitor.service';
 
@@ -25,7 +26,7 @@ describe('MonitorService', () => {
   let service: MonitorService;
 
   beforeEach(() => {
-    TestBed.configureTestingModule({});
+    configureHttpServiceTest();
     service = TestBed.inject(MonitorService);
   });
 
diff --git a/web-app/src/app/service/notice-receiver.service.spec.ts 
b/web-app/src/app/service/notice-receiver.service.spec.ts
index e7ef04d912..dad090d9b7 100644
--- a/web-app/src/app/service/notice-receiver.service.spec.ts
+++ b/web-app/src/app/service/notice-receiver.service.spec.ts
@@ -18,6 +18,7 @@
  */
 
 import { TestBed } from '@angular/core/testing';
+import { configureHttpServiceTest } from '@testing';
 
 import { NoticeReceiverService } from './notice-receiver.service';
 
@@ -25,7 +26,7 @@ describe('NoticeReceiverService', () => {
   let service: NoticeReceiverService;
 
   beforeEach(() => {
-    TestBed.configureTestingModule({});
+    configureHttpServiceTest();
     service = TestBed.inject(NoticeReceiverService);
   });
 
diff --git a/web-app/src/app/service/notice-rule.service.spec.ts 
b/web-app/src/app/service/notice-rule.service.spec.ts
index 136c6bcbbf..f0d90348aa 100644
--- a/web-app/src/app/service/notice-rule.service.spec.ts
+++ b/web-app/src/app/service/notice-rule.service.spec.ts
@@ -18,6 +18,7 @@
  */
 
 import { TestBed } from '@angular/core/testing';
+import { configureHttpServiceTest } from '@testing';
 
 import { NoticeRuleService } from './notice-rule.service';
 
@@ -25,7 +26,7 @@ describe('NoticeRuleService', () => {
   let service: NoticeRuleService;
 
   beforeEach(() => {
-    TestBed.configureTestingModule({});
+    configureHttpServiceTest();
     service = TestBed.inject(NoticeRuleService);
   });
 
diff --git a/web-app/src/app/service/notice-template.service.spec.ts 
b/web-app/src/app/service/notice-template.service.spec.ts
index b329d7b084..31c1a02fd9 100644
--- a/web-app/src/app/service/notice-template.service.spec.ts
+++ b/web-app/src/app/service/notice-template.service.spec.ts
@@ -18,6 +18,7 @@
  */
 
 import { TestBed } from '@angular/core/testing';
+import { configureHttpServiceTest } from '@testing';
 
 import { NoticeTemplateService } from './notice-template.service';
 
@@ -25,7 +26,7 @@ describe('NoticeTemplateService', () => {
   let service: NoticeTemplateService;
 
   beforeEach(() => {
-    TestBed.configureTestingModule({});
+    configureHttpServiceTest();
     service = TestBed.inject(NoticeTemplateService);
   });
 
diff --git a/web-app/src/app/service/plugin.service.spec.ts 
b/web-app/src/app/service/plugin.service.spec.ts
index 8855934fea..f7d6fcbe16 100644
--- a/web-app/src/app/service/plugin.service.spec.ts
+++ b/web-app/src/app/service/plugin.service.spec.ts
@@ -18,6 +18,7 @@
  */
 
 import { TestBed } from '@angular/core/testing';
+import { configureHttpServiceTest } from '@testing';
 
 import { PluginService } from './plugin.service';
 
@@ -25,7 +26,7 @@ describe('PluginService', () => {
   let service: PluginService;
 
   beforeEach(() => {
-    TestBed.configureTestingModule({});
+    configureHttpServiceTest();
     service = TestBed.inject(PluginService);
   });
 
diff --git a/web-app/src/app/service/status-page-public.service.spec.ts 
b/web-app/src/app/service/status-page-public.service.spec.ts
index 3f625076a2..fc13e47db8 100644
--- a/web-app/src/app/service/status-page-public.service.spec.ts
+++ b/web-app/src/app/service/status-page-public.service.spec.ts
@@ -18,6 +18,7 @@
  */
 
 import { TestBed } from '@angular/core/testing';
+import { configureHttpServiceTest } from '@testing';
 
 import { StatusPagePublicService } from './status-page-public.service';
 
@@ -25,7 +26,7 @@ describe('StatusPagePublicService', () => {
   let service: StatusPagePublicService;
 
   beforeEach(() => {
-    TestBed.configureTestingModule({});
+    configureHttpServiceTest();
     service = TestBed.inject(StatusPagePublicService);
   });
 
diff --git a/web-app/src/app/service/status-page.service.spec.ts 
b/web-app/src/app/service/status-page.service.spec.ts
index 318c00bb91..6d7c4961a8 100644
--- a/web-app/src/app/service/status-page.service.spec.ts
+++ b/web-app/src/app/service/status-page.service.spec.ts
@@ -18,6 +18,7 @@
  */
 
 import { TestBed } from '@angular/core/testing';
+import { configureHttpServiceTest } from '@testing';
 
 import { StatusPageService } from './status-page.service';
 
@@ -25,7 +26,7 @@ describe('StatusPageService', () => {
   let service: StatusPageService;
 
   beforeEach(() => {
-    TestBed.configureTestingModule({});
+    configureHttpServiceTest();
     service = TestBed.inject(StatusPageService);
   });
 
diff --git 
a/web-app/src/app/shared/components/configurable-field/configurable-field.component.spec.ts
 
b/web-app/src/app/shared/components/configurable-field/configurable-field.component.spec.ts
index 91fad76ccc..b797930f0a 100644
--- 
a/web-app/src/app/shared/components/configurable-field/configurable-field.component.spec.ts
+++ 
b/web-app/src/app/shared/components/configurable-field/configurable-field.component.spec.ts
@@ -18,6 +18,7 @@
  */
 
 import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { configureShallowTest } from '@testing';
 
 import { ConfigurableFieldComponent } from './configurable-field.component';
 
@@ -26,9 +27,7 @@ describe('ConfigurableFieldComponent', () => {
   let fixture: ComponentFixture<ConfigurableFieldComponent>;
 
   beforeEach(async () => {
-    await TestBed.configureTestingModule({
-      declarations: [ConfigurableFieldComponent]
-    }).compileComponents();
+    await configureShallowTest(ConfigurableFieldComponent).compileComponents();
   });
 
   beforeEach(() => {
diff --git 
a/web-app/src/app/shared/components/form-field/form-field.component.spec.ts 
b/web-app/src/app/shared/components/form-field/form-field.component.spec.ts
index 971578a9d5..df92cae1bc 100644
--- a/web-app/src/app/shared/components/form-field/form-field.component.spec.ts
+++ b/web-app/src/app/shared/components/form-field/form-field.component.spec.ts
@@ -18,6 +18,7 @@
  */
 
 import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { configureShallowTest } from '@testing';
 
 import { FormFieldComponent } from './form-field.component';
 
@@ -26,14 +27,13 @@ describe('FormFieldComponent', () => {
   let fixture: ComponentFixture<FormFieldComponent>;
 
   beforeEach(async () => {
-    await TestBed.configureTestingModule({
-      declarations: [FormFieldComponent]
-    }).compileComponents();
+    await configureShallowTest(FormFieldComponent).compileComponents();
   });
 
   beforeEach(() => {
     fixture = TestBed.createComponent(FormFieldComponent);
     component = fixture.componentInstance;
+    component.item = { type: 'text' };
     fixture.detectChanges();
   });
 
diff --git 
a/web-app/src/app/shared/components/help-message-show/help-message-show.component.spec.ts
 
b/web-app/src/app/shared/components/help-message-show/help-message-show.component.spec.ts
index f9089a95ae..787eb3123d 100644
--- 
a/web-app/src/app/shared/components/help-message-show/help-message-show.component.spec.ts
+++ 
b/web-app/src/app/shared/components/help-message-show/help-message-show.component.spec.ts
@@ -18,6 +18,7 @@
  */
 
 import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { configureShallowTest } from '@testing';
 
 import { HelpMessageShowComponent } from './help-message-show.component';
 
@@ -26,9 +27,7 @@ describe('HelpMessageShowComponent', () => {
   let fixture: ComponentFixture<HelpMessageShowComponent>;
 
   beforeEach(async () => {
-    await TestBed.configureTestingModule({
-      declarations: [HelpMessageShowComponent]
-    }).compileComponents();
+    await configureShallowTest(HelpMessageShowComponent).compileComponents();
 
     fixture = TestBed.createComponent(HelpMessageShowComponent);
     component = fixture.componentInstance;
diff --git 
a/web-app/src/app/shared/components/label-selector/label-selector.component.spec.ts
 
b/web-app/src/app/shared/components/label-selector/label-selector.component.spec.ts
index 3a6958faa9..f1b828658f 100644
--- 
a/web-app/src/app/shared/components/label-selector/label-selector.component.spec.ts
+++ 
b/web-app/src/app/shared/components/label-selector/label-selector.component.spec.ts
@@ -18,6 +18,7 @@
  */
 
 import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { configureShallowTest } from '@testing';
 
 import { LabelSelectorComponent } from './label-selector.component';
 
@@ -26,9 +27,7 @@ describe('LabelSelectorComponent', () => {
   let fixture: ComponentFixture<LabelSelectorComponent>;
 
   beforeEach(async () => {
-    await TestBed.configureTestingModule({
-      imports: [LabelSelectorComponent]
-    }).compileComponents();
+    await configureShallowTest(LabelSelectorComponent).compileComponents();
 
     fixture = TestBed.createComponent(LabelSelectorComponent);
     component = fixture.componentInstance;
diff --git 
a/web-app/src/app/shared/components/monitor-select-list/monitor-select-list.component.spec.ts
 
b/web-app/src/app/shared/components/monitor-select-list/monitor-select-list.component.spec.ts
index fd903d3ed3..278e1c6124 100755
--- 
a/web-app/src/app/shared/components/monitor-select-list/monitor-select-list.component.spec.ts
+++ 
b/web-app/src/app/shared/components/monitor-select-list/monitor-select-list.component.spec.ts
@@ -18,6 +18,7 @@
  */
 
 import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { configureShallowTest } from '@testing';
 
 import { MonitorSelectListComponent } from './monitor-select-list.component';
 
@@ -26,9 +27,7 @@ describe('MonitorSelectListComponent', () => {
   let fixture: ComponentFixture<MonitorSelectListComponent>;
 
   beforeEach(async () => {
-    await TestBed.configureTestingModule({
-      declarations: [MonitorSelectListComponent]
-    }).compileComponents();
+    await configureShallowTest(MonitorSelectListComponent).compileComponents();
   });
 
   beforeEach(() => {
diff --git 
a/web-app/src/app/shared/components/monitor-select-menu/monitor-select-menu.component.spec.ts
 
b/web-app/src/app/shared/components/monitor-select-menu/monitor-select-menu.component.spec.ts
index 4d7af44eba..0364619396 100755
--- 
a/web-app/src/app/shared/components/monitor-select-menu/monitor-select-menu.component.spec.ts
+++ 
b/web-app/src/app/shared/components/monitor-select-menu/monitor-select-menu.component.spec.ts
@@ -18,6 +18,7 @@
  */
 
 import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { configureShallowTest } from '@testing';
 
 import { MonitorSelectMenuComponent } from './monitor-select-menu.component';
 
@@ -26,9 +27,7 @@ describe('MonitorSelectMenuComponent', () => {
   let fixture: ComponentFixture<MonitorSelectMenuComponent>;
 
   beforeEach(async () => {
-    await TestBed.configureTestingModule({
-      declarations: [MonitorSelectMenuComponent]
-    }).compileComponents();
+    await configureShallowTest(MonitorSelectMenuComponent).compileComponents();
   });
 
   beforeEach(() => {
diff --git 
a/web-app/src/app/shared/components/multi-func-input/multi-func-input.component.spec.ts
 
b/web-app/src/app/shared/components/multi-func-input/multi-func-input.component.spec.ts
index 6720e9917d..345e77c0a6 100755
--- 
a/web-app/src/app/shared/components/multi-func-input/multi-func-input.component.spec.ts
+++ 
b/web-app/src/app/shared/components/multi-func-input/multi-func-input.component.spec.ts
@@ -18,6 +18,8 @@
  */
 
 import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { FormsModule } from '@angular/forms';
+import { configureShallowTest } from '@testing';
 
 import { MultiFuncInputComponent } from './multi-func-input.component';
 
@@ -26,9 +28,7 @@ describe('MultiFuncInputComponent', () => {
   let fixture: ComponentFixture<MultiFuncInputComponent>;
 
   beforeEach(async () => {
-    await TestBed.configureTestingModule({
-      declarations: [MultiFuncInputComponent]
-    }).compileComponents();
+    await configureShallowTest(MultiFuncInputComponent, 
[FormsModule]).compileComponents();
   });
 
   beforeEach(() => {
diff --git 
a/web-app/src/app/shared/components/toolbar/toolbar.component.spec.ts 
b/web-app/src/app/shared/components/toolbar/toolbar.component.spec.ts
index 7f243894f6..1a7e214e52 100755
--- a/web-app/src/app/shared/components/toolbar/toolbar.component.spec.ts
+++ b/web-app/src/app/shared/components/toolbar/toolbar.component.spec.ts
@@ -18,6 +18,7 @@
  */
 
 import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { configureShallowTest } from '@testing';
 
 import { ToolbarComponent } from './toolbar.component';
 
@@ -26,9 +27,7 @@ describe('ToolbarComponent', () => {
   let fixture: ComponentFixture<ToolbarComponent>;
 
   beforeEach(async () => {
-    await TestBed.configureTestingModule({
-      declarations: [ToolbarComponent]
-    }).compileComponents();
+    await configureShallowTest(ToolbarComponent).compileComponents();
   });
 
   beforeEach(() => {
diff --git a/web-app/src/testing.ts b/web-app/src/testing.ts
new file mode 100644
index 0000000000..b226dadb6b
--- /dev/null
+++ b/web-app/src/testing.ts
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { HttpClientTestingModule } from '@angular/common/http/testing';
+import { CUSTOM_ELEMENTS_SCHEMA, Type } from '@angular/core';
+import { TestBed, TestModuleMetadata } from '@angular/core/testing';
+import { NoopAnimationsModule } from '@angular/platform-browser/animations';
+import { provideRouter, RouterModule } from '@angular/router';
+import { ALAIN_I18N_TOKEN, AlainThemeModule } from '@delon/theme';
+import { NzGridModule } from 'ng-zorro-antd/grid';
+import { NzMenuModule } from 'ng-zorro-antd/menu';
+import { NzModalModule } from 'ng-zorro-antd/modal';
+import { NzTableModule } from 'ng-zorro-antd/table';
+import { NzToolTipModule } from 'ng-zorro-antd/tooltip';
+import { of } from 'rxjs';
+
+const imports = [
+  HttpClientTestingModule,
+  NoopAnimationsModule,
+  RouterModule,
+  NzGridModule,
+  NzMenuModule,
+  NzModalModule,
+  NzTableModule,
+  NzToolTipModule,
+  AlainThemeModule.forRoot()
+];
+
+const providers = [
+  provideRouter([]),
+  {
+    provide: ALAIN_I18N_TOKEN,
+    useValue: {
+      change: of(null),
+      currentLang: 'en-US',
+      defaultLang: 'en-US',
+      fanyi: (key: string) => key
+    }
+  }
+];
+
+export function configureShallowTest(component: Type<unknown>, extraImports: 
NonNullable<TestModuleMetadata['imports']> = []): TestBed {
+  return TestBed.configureTestingModule({
+    imports: [...imports, ...extraImports],
+    declarations: [component],
+    providers,
+    schemas: [CUSTOM_ELEMENTS_SCHEMA]
+  });
+}
+
+export function configureStandaloneTest(component: Type<unknown>, 
extraImports: NonNullable<TestModuleMetadata['imports']> = []): TestBed {
+  return TestBed.configureTestingModule({
+    imports: [HttpClientTestingModule, component, ...extraImports],
+    providers
+  });
+}
+
+export function configureHttpServiceTest(): TestBed {
+  return TestBed.configureTestingModule({ imports: [HttpClientTestingModule] 
});
+}
diff --git a/web-app/tsconfig.json b/web-app/tsconfig.json
index 5428ce4511..511e32fed8 100644
--- a/web-app/tsconfig.json
+++ b/web-app/tsconfig.json
@@ -26,6 +26,9 @@
       "@core": [
         "src/app/core/index"
       ],
+      "@testing": [
+        "src/testing"
+      ],
       "@env/*": [
         "src/environments/*"
       ]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to