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

github-merge-queue[bot] pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/texera.git

commit cfc6d9aa81ee01225fe67d5d58af23a5c816d289
Author: Yicong Huang <[email protected]>
AuthorDate: Sun May 24 15:11:19 2026 -0700

    test(frontend): route specs through HttpClientTestingModule and 
lint-enforce it (#5185)
    
    ### What changes were proposed in this PR?
    
    Two passes against the same anti-pattern — specs that wire the real
    Angular `HttpClient` instead of routing through
    `HttpClientTestingModule`:
    
    1. **Data cleanup.** Migrate every spec that was importing
    `HttpClientModule`, listing `HttpClient` as a bare provider, or
    hand-rolling an `HttpClient` stub over to `HttpClientTestingModule`.
    2. **Lint guardrail.** Add an `*.spec.ts` override to
    `frontend/.eslintrc.json` so the pattern can't grow back.
    `no-restricted-imports` blocks `HttpClientModule`;
    `no-restricted-syntax` flags `HttpClient` as a bare provider entry. Type
    usage (`let h: HttpClient`) and `TestBed.inject(HttpClient)` remain
    allowed.
    
    The lint rule is what surfaced the last five offenders — the original
    grep audit missed them because they imported `HttpClientTestingModule`
    alongside the offending provider or import.
    
    ### Any related issues, documentation, discussions?
    
    Closes #5182, closes #5184.
    
    ### How was this PR tested?
    
    `yarn lint` exits 0; `yarn format:ci` clean. `yarn ng test --watch=false
    --include=…` on every touched spec passes locally.
    
    ### Was this PR authored or co-authored using generative AI tooling?
    
    Generated-by: Claude Opus 4.7
---
 frontend/.eslintrc.json                            | 24 ++++++++++++++++++++++
 .../user/user-avatar/user-avatar.component.spec.ts |  3 +--
 .../user-computing-unit.component.spec.ts          | 10 ++++++---
 .../user-workflow/user-workflow.component.spec.ts  |  2 --
 .../coeditor-user-icon.component.spec.ts           |  2 --
 .../execute-workflow.service.spec.ts               | 12 +++--------
 .../operator-menu/operator-menu.service.spec.ts    |  4 ++--
 .../operator-metadata.service.spec.ts              |  2 +-
 .../model/coeditor-presence.service.spec.ts        |  2 --
 .../operator-reuse-cache-status.service.spec.ts    |  4 ++--
 10 files changed, 40 insertions(+), 25 deletions(-)

diff --git a/frontend/.eslintrc.json b/frontend/.eslintrc.json
index 2329e18203..5f6979335e 100644
--- a/frontend/.eslintrc.json
+++ b/frontend/.eslintrc.json
@@ -106,6 +106,30 @@
       "rules": {
         "@angular-eslint/template/prefer-control-flow": "off"
       }
+    },
+    {
+      "files": ["*.spec.ts"],
+      "rules": {
+        "no-restricted-imports": [
+          "error",
+          {
+            "paths": [
+              {
+                "name": "@angular/common/http",
+                "importNames": ["HttpClientModule"],
+                "message": "Use HttpClientTestingModule from 
'@angular/common/http/testing' in specs. HttpClientTestingModule supplies 
HttpClient and routes requests through HttpTestingController instead of hitting 
a real backend."
+              }
+            ]
+          }
+        ],
+        "no-restricted-syntax": [
+          "error",
+          {
+            "selector": "Property[key.name='providers'] > ArrayExpression > 
Identifier[name='HttpClient']",
+            "message": "Don't list HttpClient as a bare provider in a spec. 
HttpClientTestingModule (imported in 'imports') provides it; adding it to 
'providers' on top of that overrides the testing backend with the real 
HttpClient."
+          }
+        ]
+      }
     }
   ]
 }
diff --git 
a/frontend/src/app/dashboard/component/user/user-avatar/user-avatar.component.spec.ts
 
b/frontend/src/app/dashboard/component/user/user-avatar/user-avatar.component.spec.ts
index 1afed37712..68f6c39e27 100644
--- 
a/frontend/src/app/dashboard/component/user/user-avatar/user-avatar.component.spec.ts
+++ 
b/frontend/src/app/dashboard/component/user/user-avatar/user-avatar.component.spec.ts
@@ -18,7 +18,6 @@
  */
 
 import { ComponentFixture, TestBed } from "@angular/core/testing";
-import { HttpClientModule } from "@angular/common/http";
 import { UserAvatarComponent } from "./user-avatar.component";
 import { HttpClientTestingModule } from "@angular/common/http/testing";
 import { NzAvatarModule } from "ng-zorro-antd/avatar";
@@ -32,7 +31,7 @@ describe("UserAvatarComponent", () => {
 
   beforeEach(async () => {
     await TestBed.configureTestingModule({
-      imports: [UserAvatarComponent, HttpClientModule, 
HttpClientTestingModule, NzAvatarModule],
+      imports: [UserAvatarComponent, HttpClientTestingModule, NzAvatarModule],
       providers: [{ provide: UserService, useClass: StubUserService }, 
...commonTestProviders],
     }).compileComponents();
   });
diff --git 
a/frontend/src/app/dashboard/component/user/user-computing-unit/user-computing-unit.component.spec.ts
 
b/frontend/src/app/dashboard/component/user/user-computing-unit/user-computing-unit.component.spec.ts
index 1abacaa016..683aa1154d 100644
--- 
a/frontend/src/app/dashboard/component/user/user-computing-unit/user-computing-unit.component.spec.ts
+++ 
b/frontend/src/app/dashboard/component/user/user-computing-unit/user-computing-unit.component.spec.ts
@@ -24,7 +24,7 @@ import { NzCardModule } from "ng-zorro-antd/card";
 import { NzIconModule } from "ng-zorro-antd/icon";
 import { NzModalService } from "ng-zorro-antd/modal";
 import { FileAddOutline } from "@ant-design/icons-angular/icons";
-import { HttpClient } from "@angular/common/http";
+import { HttpClientTestingModule } from "@angular/common/http/testing";
 import { UserService } from "../../../../common/service/user/user.service";
 import { StubUserService } from 
"../../../../common/service/user/stub-user.service";
 import { commonTestProviders } from "../../../../common/testing/test-utils";
@@ -53,13 +53,17 @@ describe("UserComputingUnitComponent", () => {
     await TestBed.configureTestingModule({
       providers: [
         NzModalService,
-        HttpClient,
         { provide: UserService, useClass: StubUserService },
         { provide: WorkflowComputingUnitManagingService, useValue: 
mockComputingUnitService },
         { provide: ComputingUnitStatusService, useClass: 
MockComputingUnitStatusService },
         ...commonTestProviders,
       ],
-      imports: [UserComputingUnitComponent, NzCardModule, 
NzIconModule.forChild([FileAddOutline])],
+      imports: [
+        UserComputingUnitComponent,
+        HttpClientTestingModule,
+        NzCardModule,
+        NzIconModule.forChild([FileAddOutline]),
+      ],
       schemas: [NO_ERRORS_SCHEMA],
     }).compileComponents();
 
diff --git 
a/frontend/src/app/dashboard/component/user/user-workflow/user-workflow.component.spec.ts
 
b/frontend/src/app/dashboard/component/user/user-workflow/user-workflow.component.spec.ts
index cc43d47fef..959977cd15 100644
--- 
a/frontend/src/app/dashboard/component/user/user-workflow/user-workflow.component.spec.ts
+++ 
b/frontend/src/app/dashboard/component/user/user-workflow/user-workflow.component.spec.ts
@@ -25,7 +25,6 @@ import { UserWorkflowComponent } from 
"./user-workflow.component";
 import { WorkflowPersistService } from 
"../../../../common/service/workflow-persist/workflow-persist.service";
 import { StubWorkflowPersistService } from 
"../../../../common/service/workflow-persist/stub-workflow-persist.service";
 import { ShareAccessComponent } from "../share-access/share-access.component";
-import { HttpClient } from "@angular/common/http";
 import { ShareAccessService } from 
"../../../service/user/share-access/share-access.service";
 import { UserService } from "../../../../common/service/user/user.service";
 import { StubUserService } from 
"../../../../common/service/user/stub-user.service";
@@ -76,7 +75,6 @@ describe("SavedWorkflowSectionComponent", () => {
         NzModalService,
         { provide: WorkflowPersistService, useValue: new 
StubWorkflowPersistService(testWorkflowEntries) },
         { provide: UserProjectService, useValue: new StubUserProjectService() 
},
-        HttpClient,
         ShareAccessService,
         { provide: OperatorMetadataService, useClass: 
StubOperatorMetadataService },
         { provide: NZ_I18N, useValue: en_US },
diff --git 
a/frontend/src/app/workspace/component/menu/coeditor-user-icon/coeditor-user-icon.component.spec.ts
 
b/frontend/src/app/workspace/component/menu/coeditor-user-icon/coeditor-user-icon.component.spec.ts
index 96568516f3..0f3f07170d 100644
--- 
a/frontend/src/app/workspace/component/menu/coeditor-user-icon/coeditor-user-icon.component.spec.ts
+++ 
b/frontend/src/app/workspace/component/menu/coeditor-user-icon/coeditor-user-icon.component.spec.ts
@@ -22,7 +22,6 @@ import { ComponentFixture, TestBed } from 
"@angular/core/testing";
 import { CoeditorUserIconComponent } from "./coeditor-user-icon.component";
 import { CoeditorPresenceService } from 
"../../../service/workflow-graph/model/coeditor-presence.service";
 import { WorkflowActionService } from 
"../../../service/workflow-graph/model/workflow-action.service";
-import { HttpClient } from "@angular/common/http";
 import { HttpClientTestingModule } from "@angular/common/http/testing";
 import { NzDropdownMenuComponent, NzDropDownModule } from 
"ng-zorro-antd/dropdown";
 import { StubUserService } from 
"../../../../common/service/user/stub-user.service";
@@ -40,7 +39,6 @@ describe("CoeditorUserIconComponent", () => {
       providers: [
         WorkflowActionService,
         CoeditorPresenceService,
-        HttpClient,
         NzDropdownMenuComponent,
         { provide: UserService, useClass: StubUserService },
         ...commonTestProviders,
diff --git 
a/frontend/src/app/workspace/service/execute-workflow/execute-workflow.service.spec.ts
 
b/frontend/src/app/workspace/service/execute-workflow/execute-workflow.service.spec.ts
index f38890eb2b..b8d2779c0c 100644
--- 
a/frontend/src/app/workspace/service/execute-workflow/execute-workflow.service.spec.ts
+++ 
b/frontend/src/app/workspace/service/execute-workflow/execute-workflow.service.spec.ts
@@ -30,10 +30,10 @@ import { UndoRedoService } from 
"../undo-redo/undo-redo.service";
 import { OperatorMetadataService } from 
"../operator-metadata/operator-metadata.service";
 import { StubOperatorMetadataService } from 
"../operator-metadata/stub-operator-metadata.service";
 import { JointUIService } from "../joint-ui/joint-ui.service";
-import { Observable, of } from "rxjs";
+import { of } from "rxjs";
 
 import { mockLogicalPlan_scan_result, mockWorkflowPlan_scan_result } from 
"./mock-workflow-plan";
-import { HttpClient } from "@angular/common/http";
+import { HttpClientTestingModule } from "@angular/common/http/testing";
 import { WorkflowUtilService } from 
"../workflow-graph/util/workflow-util.service";
 import { WorkflowSnapshotService } from 
"../../../dashboard/service/user/workflow-snapshot/workflow-snapshot.service";
 
@@ -46,12 +46,6 @@ import { StubUserService } from 
"src/app/common/service/user/stub-user.service";
 import { MockComputingUnitStatusService } from 
"../../../common/service/computing-unit/computing-unit-status/mock-computing-unit-status.service";
 import { commonTestProviders } from "../../../common/testing/test-utils";
 
-class StubHttpClient {
-  public post(): Observable<string> {
-    return of("a");
-  }
-}
-
 describe("ExecuteWorkflowService", () => {
   let service: ExecuteWorkflowService;
   let mockWorkflowSnapshotService: WorkflowSnapshotService;
@@ -65,6 +59,7 @@ describe("ExecuteWorkflowService", () => {
     } as Document;
 
     TestBed.configureTestingModule({
+      imports: [HttpClientTestingModule],
       providers: [
         ExecuteWorkflowService,
         WorkflowActionService,
@@ -76,7 +71,6 @@ describe("ExecuteWorkflowService", () => {
           provide: OperatorMetadataService,
           useClass: StubOperatorMetadataService,
         },
-        { provide: HttpClient, useClass: StubHttpClient },
         { provide: DOCUMENT, useValue: mockDocument },
         { provide: AuthService, useClass: StubAuthService },
         { provide: UserService, useClass: StubUserService },
diff --git 
a/frontend/src/app/workspace/service/operator-menu/operator-menu.service.spec.ts
 
b/frontend/src/app/workspace/service/operator-menu/operator-menu.service.spec.ts
index 88e4358028..88faa7659c 100644
--- 
a/frontend/src/app/workspace/service/operator-menu/operator-menu.service.spec.ts
+++ 
b/frontend/src/app/workspace/service/operator-menu/operator-menu.service.spec.ts
@@ -22,7 +22,7 @@ import { OperatorMetadataService } from 
"../operator-metadata/operator-metadata.
 import { StubOperatorMetadataService } from 
"../operator-metadata/stub-operator-metadata.service";
 
 import { OperatorMenuService } from "./operator-menu.service";
-import { HttpClientModule } from "@angular/common/http";
+import { HttpClientTestingModule } from "@angular/common/http/testing";
 import { ComputingUnitStatusService } from 
"../../../common/service/computing-unit/computing-unit-status/computing-unit-status.service";
 import { MockComputingUnitStatusService } from 
"../../../common/service/computing-unit/computing-unit-status/mock-computing-unit-status.service";
 import { commonTestProviders } from "../../../common/testing/test-utils";
@@ -50,7 +50,7 @@ describe("OperatorMenuService", () => {
         { provide: ComputingUnitStatusService, useClass: 
MockComputingUnitStatusService },
         ...commonTestProviders,
       ],
-      imports: [HttpClientModule],
+      imports: [HttpClientTestingModule],
     });
     workflowActionService = TestBed.inject(WorkflowActionService);
     service = TestBed.inject(OperatorMenuService);
diff --git 
a/frontend/src/app/workspace/service/operator-metadata/operator-metadata.service.spec.ts
 
b/frontend/src/app/workspace/service/operator-metadata/operator-metadata.service.spec.ts
index e1bf27c370..901c99bf6a 100644
--- 
a/frontend/src/app/workspace/service/operator-metadata/operator-metadata.service.spec.ts
+++ 
b/frontend/src/app/workspace/service/operator-metadata/operator-metadata.service.spec.ts
@@ -32,7 +32,7 @@ describe("OperatorMetadataService", () => {
   beforeEach(() => {
     TestBed.configureTestingModule({
       imports: [HttpClientTestingModule],
-      providers: [OperatorMetadataService, HttpClient, ...commonTestProviders],
+      providers: [OperatorMetadataService, ...commonTestProviders],
     });
 
     httpClient = TestBed.inject(HttpClient);
diff --git 
a/frontend/src/app/workspace/service/workflow-graph/model/coeditor-presence.service.spec.ts
 
b/frontend/src/app/workspace/service/workflow-graph/model/coeditor-presence.service.spec.ts
index b524abe6d7..661180fd67 100644
--- 
a/frontend/src/app/workspace/service/workflow-graph/model/coeditor-presence.service.spec.ts
+++ 
b/frontend/src/app/workspace/service/workflow-graph/model/coeditor-presence.service.spec.ts
@@ -24,7 +24,6 @@ import { HttpClientTestingModule } from 
"@angular/common/http/testing";
 import { NzDropdownMenuComponent, NzDropDownModule } from 
"ng-zorro-antd/dropdown";
 import { CoeditorUserIconComponent } from 
"../../../component/menu/coeditor-user-icon/coeditor-user-icon.component";
 import { WorkflowActionService } from "./workflow-action.service";
-import { HttpClient } from "@angular/common/http";
 import { StubUserService } from 
"../../../../common/service/user/stub-user.service";
 import { UserService } from "../../../../common/service/user/user.service";
 import { commonTestProviders } from "../../../../common/testing/test-utils";
@@ -39,7 +38,6 @@ describe("CoeditorPresenceService", () => {
       providers: [
         WorkflowActionService,
         CoeditorPresenceService,
-        HttpClient,
         NzDropdownMenuComponent,
         { provide: UserService, useClass: StubUserService },
         ...commonTestProviders,
diff --git 
a/frontend/src/app/workspace/service/workflow-status/operator-reuse-cache-status.service.spec.ts
 
b/frontend/src/app/workspace/service/workflow-status/operator-reuse-cache-status.service.spec.ts
index ca9ebce8bf..0c3ccb89fd 100644
--- 
a/frontend/src/app/workspace/service/workflow-status/operator-reuse-cache-status.service.spec.ts
+++ 
b/frontend/src/app/workspace/service/workflow-status/operator-reuse-cache-status.service.spec.ts
@@ -22,7 +22,7 @@ import { OperatorMetadataService } from 
"../operator-metadata/operator-metadata.
 import { StubOperatorMetadataService } from 
"../operator-metadata/stub-operator-metadata.service";
 
 import { OperatorReuseCacheStatusService } from 
"./operator-reuse-cache-status.service";
-import { HttpClientModule } from "@angular/common/http";
+import { HttpClientTestingModule } from "@angular/common/http/testing";
 import { commonTestProviders } from "../../../common/testing/test-utils";
 
 describe("OperatorCacheStatusService", () => {
@@ -37,7 +37,7 @@ describe("OperatorCacheStatusService", () => {
         },
         ...commonTestProviders,
       ],
-      imports: [HttpClientModule],
+      imports: [HttpClientTestingModule],
     });
     service = TestBed.inject(OperatorReuseCacheStatusService);
   });

Reply via email to