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); });
