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


The following commit(s) were added to refs/heads/main by this push:
     new cbe90c74c4 chore(frontend): clean up template-override anti-pattern in 
component specs (#5189)
cbe90c74c4 is described below

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

    chore(frontend): clean up template-override anti-pattern in component specs 
(#5189)
    
    ### What changes were proposed in this PR?
    
    Three coordinated passes against the "specs that replace the parent
    template at test time" anti-pattern:
    
    1. **`frontend/TESTING.md`**: anti-patterns 2 and 3 are rewritten to
    describe what actually goes wrong (the real `.component.html` never
    executes, so v8 coverage stays at 0%), and a new Recipe F + anti-pattern
    8 cover the right way to handle a heavy child component when one truly
    cannot be instantiated — additive `overrideComponent({ remove, add })`
    with a minimal stub child.
    2. **Spec cleanups**: every spec that carried `template: ""`, a
    stub-markup template substitute, `NO_ERRORS_SCHEMA`, or the broader
    `set: { imports: [], schemas: [...] }` override is migrated to render
    the real template. Two specs (Formly-driven and the full
    WorkspaceComponent) keep the old shape behind a scoped `eslint-disable`
    with a TODO; both need a more thorough redesign that isn't in scope
    here.
    3. **ESLint guardrail**: an `*.spec.ts` override in `.eslintrc.json`
    blocks the patterns going forward — `NO_ERRORS_SCHEMA` imports from
    `@angular/core`, any `template` or `imports` key inside
    `overrideComponent({ set: ... })`. Each rule's message points at the
    right replacement.
    
    ### Any related issues, documentation, discussions?
    
    Closes #5188. Builds on the testing guide introduced in #5170 and the
    lint-guardrail pattern from #5185.
    
    ### How was this PR tested?
    
    `yarn ng test --watch=false --include=…` over the touched specs runs 13
    files / 108 tests, all green. `yarn lint` and `yarn format:ci` both
    clean. The three template files that originally pinned at 0% —
    `preset-wrapper.component.html`, `operator-menu.component.html`,
    `menu.component.html` — now report 44 / 79 / 59% line coverage under the
    new shape.
    
    ### Was this PR authored or co-authored using generative AI tooling?
    
    Generated-by: Claude Opus 4.7
---
 frontend/.eslintrc.json                            | 13 +++++
 frontend/TESTING.md                                | 61 ++++++++++++++++++++--
 .../preset-wrapper.component.spec.ts               |  4 --
 .../admin/user/admin-user.component.spec.ts        |  2 -
 .../user/filters/filters.component.spec.ts         |  2 -
 .../user/list-item/list-item.component.spec.ts     |  2 -
 .../user-computing-unit.component.spec.ts          |  2 -
 .../user/user-icon/user-icon.component.spec.ts     |  2 -
 .../user/user-quota/user-quota.component.spec.ts   |  2 -
 .../left-panel/left-panel.component.spec.ts        |  8 ---
 .../operator-menu/operator-menu.component.spec.ts  |  8 ---
 .../component/menu/menu.component.spec.ts          | 11 ++--
 .../operator-property-edit-frame.component.spec.ts | 10 +++-
 .../property-editor.component.spec.ts              |  8 ---
 .../component/workspace.component.spec.ts          | 10 ++++
 15 files changed, 91 insertions(+), 54 deletions(-)

diff --git a/frontend/.eslintrc.json b/frontend/.eslintrc.json
index 5f6979335e..261ea9f2c0 100644
--- a/frontend/.eslintrc.json
+++ b/frontend/.eslintrc.json
@@ -118,6 +118,11 @@
                 "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."
+              },
+              {
+                "name": "@angular/core",
+                "importNames": ["NO_ERRORS_SCHEMA"],
+                "message": "NO_ERRORS_SCHEMA silences template errors at the 
cost of hiding real bugs (Angular docs warn against it). For standalone 
components, the parent's `imports:` array already pulls children transitively, 
so the schema is usually unnecessary. If a child component's ngOnInit fails 
because of a missing service method, expand the service stub instead. If a 
child genuinely cannot be instantiated, stub it via `overrideComponent({ 
remove: { imports: [Real] }, add: { impo [...]
               }
             ]
           }
@@ -127,6 +132,14 @@
           {
             "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."
+          },
+          {
+            "selector": 
"CallExpression[callee.property.name='overrideComponent'] 
Property[key.name='set'] > ObjectExpression > Property[key.name='template']",
+            "message": "Don't replace the component's template via 
`overrideComponent({ set: { template: ... } })` — any substitution (empty 
string, stub markup, anything) pins the real .component.html to 0% coverage and 
skips testing the template-to-class bindings. If a child component blocks 
instantiation, stub it via `overrideComponent({ remove: { imports: [Real] }, 
add: { imports: [Stub] } })`. If a service method is missing for a child's 
ngOnInit, expand the service stub."
+          },
+          {
+            "selector": 
"CallExpression[callee.property.name='overrideComponent'] 
Property[key.name='set'] > ObjectExpression > Property[key.name='imports']",
+            "message": "Avoid `overrideComponent({ set: { imports: ... } })` — 
the `set` form for imports has known bugs in Angular standalone components (see 
angular/angular#48432). Use the additive form `overrideComponent({ remove: { 
imports: [Real] }, add: { imports: [Stub] } })` instead."
           }
         ]
       }
diff --git a/frontend/TESTING.md b/frontend/TESTING.md
index 67048e783e..da28d41e1f 100644
--- a/frontend/TESTING.md
+++ b/frontend/TESTING.md
@@ -206,6 +206,40 @@ it("debounces the query before firing", fakeAsync(() => {
 
 `fakeAsync` works because `test-zone-setup.ts` installs a ProxyZone around 
`it`/`test`. It does **not** install one around `beforeEach`, so do not write 
`beforeEach(fakeAsync(...))` — set component state and call `tick()` from 
inside the `it` body instead.
 
+### F. Stub a child component (rare, only when the child can't be instantiated)
+
+Standalone parents pull their `imports:` graph in transitively, so most child 
components instantiate cleanly. Reach for this recipe only when a child 
genuinely can't run under jsdom — e.g. it requires WebGL, opens a real 
WebSocket, or depends on a service whose stub you cannot reasonably complete.
+
+Declare a minimal `@Component` with the **same selector** as the real child, 
mark it `standalone`, and swap it in via the additive override:
+
+```ts
+import { Component } from "@angular/core";
+import { HeavyChildComponent } from "./heavy-child.component";
+
+@Component({
+  selector: "texera-heavy-child",
+  standalone: true,
+  template: "",
+})
+class StubHeavyChildComponent {}
+
+beforeEach(async () => {
+  TestBed.overrideComponent(ParentComponent, {
+    remove: { imports: [HeavyChildComponent] },
+    add: { imports: [StubHeavyChildComponent] },
+  });
+
+  await TestBed.configureTestingModule({ imports: [ParentComponent] 
}).compileComponents();
+});
+```
+
+The parent's template still renders — the `<texera-heavy-child>` tag now 
resolves to the stub. Coverage of the parent's `.component.html` is unaffected.
+
+Two patterns to **avoid** even when a child is awkward:
+
+- `overrideComponent({ set: { imports: [], schemas: [CUSTOM_ELEMENTS_SCHEMA] } 
})` — the `set` form for imports has known Angular bugs 
([angular/angular#48432](https://github.com/angular/angular/issues/48432)), and 
the schema escape hatch quietly hides real template errors. Use `remove + add` 
instead.
+- `overrideComponent({ set: { template: ... } })` — substituting the parent's 
template (empty or stub) pins the real `.component.html` to 0 % coverage 
forever. See Anti-pattern 3.
+
 ## Standalone components
 
 Newly-generated components are standalone. The component itself goes into 
`imports:`, never `declarations:`:
@@ -279,17 +313,26 @@ The license header is the only live code; the file 
reports "0 tests, 0 failures"
 
 **Fix**: delete the commented code outright (git history keeps it). Either 
replace it with the minimum-viable spec from Recipe A, or remove the spec file 
entirely.
 
-### 2. `NO_ERRORS_SCHEMA` everywhere
+### 2. `NO_ERRORS_SCHEMA`
+
+`schemas: [NO_ERRORS_SCHEMA]` tells Angular to swallow every "unknown element 
/ unknown attribute" error from the template. [Angular's own docs warn against 
it](https://angular.dev/guide/testing/components-scenarios): "you could waste 
hours chasing phantom bugs that the compiler would have caught in an instant." 
Even when the spec only asserts on the component class, the schema masks 
template typos and silently disabled bindings.
+
+For standalone components the schema is also almost always unnecessary. A 
standalone parent declares its template's children in its own `imports:` array, 
and TestBed loads that import graph transitively — so the children are "known" 
without any schema. NO_ERRORS_SCHEMA in a spec is usually a leftover from the 
pre-standalone NgModule era.
+
+**Fix**: remove `NO_ERRORS_SCHEMA` and let the test run. The typical failure 
modes and how to handle them:
 
-A spec adds `schemas: [NO_ERRORS_SCHEMA]` to silence "unknown element" errors 
from un-imported children, but then asserts something about the parent template 
that depends on the child rendering. Branches inside `*ngIf="child.ready"` are 
dead because `child.ready` never fires.
+- _A child component's `ngOnInit` throws because a service method is missing 
on the mock._ Extend the service stub — that's where the real coupling lives, 
not in the template. (Example: the menu spec needed `getAllComputingUnits: () 
=> of([])` added to its `ComputingUnitStatusService` stub once the child's 
`ngOnInit` started running.)
+- _A child component genuinely cannot be instantiated in jsdom (WebGL, native 
module, etc.)._ Stub it via the additive override — see Recipe F.
 
-**Fix**: import the real child, or use `overrideComponent({ set: { imports: 
[], schemas: [CUSTOM_ELEMENTS_SCHEMA] } })` to drop the child's transitive 
imports while letting the unknown element render as an inert tag. 
`CUSTOM_ELEMENTS_SCHEMA` says "I know what I'm doing", `NO_ERRORS_SCHEMA` says 
"swallow all template errors" — the second is almost never what you want.
+ESLint enforces this rule on `*.spec.ts` files; importing `NO_ERRORS_SCHEMA` 
from `@angular/core` is a lint error.
 
 ### 3. `overrideComponent({ set: { template: "" } })`
 
-Used historically to bypass a child template that wouldn't compile. Side 
effect: the parent's template is wiped, so HTML coverage is permanently 0 %.
+Erases the parent's template at test time. `fixture.detectChanges()` then 
renders an empty `<div>`, the original `.component.html` is permanently pinned 
at 0 %, and every `*ngIf` / `(click)` / `{{ binding }}` in the file is silently 
uncovered.
 
-**Fix**: see Anti-pattern 2 — override `imports` and `schemas` instead, keep 
the template intact.
+**Fix**: delete the `overrideComponent({ set: { template: ... } })` block. If 
the spec then errors because of a child, address it the same way as 
Anti-pattern 2 — extend the service stub, or stub the child via Recipe F. Do 
not replace the template with an empty-imports override either (see 
Anti-pattern 8).
+
+ESLint enforces this rule on `*.spec.ts` files; the literal `template: ""` 
inside `overrideComponent` is a lint error.
 
 ### 4. `declarations: [StandaloneComponent]`
 
@@ -315,6 +358,14 @@ Drift: spec A invents a partial mock for 
`OperatorMetadataService`, spec B inven
 
 **Fix**: use `StubOperatorMetadataService`. When you find yourself wanting a 
new method on the stub, add it to the stub class, not to the spec.
 
+### 8. `overrideComponent({ set: { imports: [], schemas: [...] } })`
+
+Strips the parent's transitive imports and combines that with a permissive 
schema so the template renders against unknown tags. The pattern looks clean 
but has two problems: the `set` form for `imports` has documented bugs in 
Angular standalone components 
([angular/angular#48432](https://github.com/angular/angular/issues/48432)) 
where the override silently fails when the same component has been compiled by 
an earlier spec, and the schema escape hatch hides real template errors just 
like  [...]
+
+**Fix**: use the additive `remove + add` form to swap specific heavy children 
for stubs — see Recipe F. If only one or two children are problematic, name 
them explicitly rather than blanket-stripping the whole import list.
+
+ESLint enforces this rule on `*.spec.ts` files; any `template` or `imports` 
key inside `overrideComponent({ set: ... })` is a lint error.
+
 ## Coverage troubleshooting
 
 `yarn test:ci` produces `coverage/lcov.info`; the GitHub-side dashboard is at 
[app.codecov.io/gh/apache/texera](https://app.codecov.io/gh/apache/texera). If 
a `.component.html` shows 0 % even though the spec passes, walk this list 
top-to-bottom:
diff --git 
a/frontend/src/app/common/formly/preset-wrapper/preset-wrapper.component.spec.ts
 
b/frontend/src/app/common/formly/preset-wrapper/preset-wrapper.component.spec.ts
index 09f415f256..474c0ca9a0 100644
--- 
a/frontend/src/app/common/formly/preset-wrapper/preset-wrapper.component.spec.ts
+++ 
b/frontend/src/app/common/formly/preset-wrapper/preset-wrapper.component.spec.ts
@@ -92,10 +92,6 @@ describe("PresetWrapperComponent", () => {
       warning: vi.fn(),
     };
 
-    // Override the template so the spec doesn't depend on the ng-zorro
-    // dropdown machinery — we exercise the public component API directly.
-    TestBed.overrideComponent(PresetWrapperComponent, { set: { template: "" } 
});
-
     await TestBed.configureTestingModule({
       imports: [PresetWrapperComponent],
       providers: [
diff --git 
a/frontend/src/app/dashboard/component/admin/user/admin-user.component.spec.ts 
b/frontend/src/app/dashboard/component/admin/user/admin-user.component.spec.ts
index cbdd262cb4..ff5ef8cf39 100644
--- 
a/frontend/src/app/dashboard/component/admin/user/admin-user.component.spec.ts
+++ 
b/frontend/src/app/dashboard/component/admin/user/admin-user.component.spec.ts
@@ -17,7 +17,6 @@
  * under the License.
  */
 
-import { NO_ERRORS_SCHEMA } from "@angular/core";
 import { ComponentFixture, inject, TestBed } from "@angular/core/testing";
 import { AdminUserComponent } from "./admin-user.component";
 import { UserService } from "../../../../common/service/user/user.service";
@@ -37,7 +36,6 @@ describe("AdminUserComponent", () => {
     await TestBed.configureTestingModule({
       providers: [{ provide: UserService, useClass: StubUserService }, 
AdminUserService, ...commonTestProviders],
       imports: [AdminUserComponent, FormsModule, HttpClientTestingModule, 
NzDropDownModule, NzModalModule],
-      schemas: [NO_ERRORS_SCHEMA],
     }).compileComponents();
   });
 
diff --git 
a/frontend/src/app/dashboard/component/user/filters/filters.component.spec.ts 
b/frontend/src/app/dashboard/component/user/filters/filters.component.spec.ts
index 50deeb4877..f5878fcac3 100644
--- 
a/frontend/src/app/dashboard/component/user/filters/filters.component.spec.ts
+++ 
b/frontend/src/app/dashboard/component/user/filters/filters.component.spec.ts
@@ -17,7 +17,6 @@
  * under the License.
  */
 
-import { NO_ERRORS_SCHEMA } from "@angular/core";
 import { ComponentFixture, TestBed } from "@angular/core/testing";
 
 import { FiltersComponent } from "./filters.component";
@@ -49,7 +48,6 @@ describe("FiltersComponent", () => {
         ...commonTestProviders,
       ],
       imports: [FiltersComponent, NzModalModule, NzDropDownModule, 
FormsModule, HttpClientTestingModule],
-      schemas: [NO_ERRORS_SCHEMA],
     }).compileComponents();
   });
 
diff --git 
a/frontend/src/app/dashboard/component/user/list-item/list-item.component.spec.ts
 
b/frontend/src/app/dashboard/component/user/list-item/list-item.component.spec.ts
index c00d509868..c8840a135b 100644
--- 
a/frontend/src/app/dashboard/component/user/list-item/list-item.component.spec.ts
+++ 
b/frontend/src/app/dashboard/component/user/list-item/list-item.component.spec.ts
@@ -23,7 +23,6 @@ import { WorkflowPersistService } from 
"src/app/common/service/workflow-persist/
 import { HttpClientTestingModule } from "@angular/common/http/testing";
 import { NzModalService } from "ng-zorro-antd/modal";
 import { of, throwError } from "rxjs";
-import { NO_ERRORS_SCHEMA } from "@angular/core";
 import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
 import { RouterTestingModule } from "@angular/router/testing";
 import { StubUserService } from 
"../../../../common/service/user/stub-user.service";
@@ -47,7 +46,6 @@ describe("ListItemComponent", () => {
         NzModalService,
         ...commonTestProviders,
       ],
-      schemas: [NO_ERRORS_SCHEMA],
     }).compileComponents();
 
     fixture = TestBed.createComponent(ListItemComponent);
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 683aa1154d..0a10aae28f 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
@@ -17,7 +17,6 @@
  * under the License.
  */
 
-import { NO_ERRORS_SCHEMA } from "@angular/core";
 import { ComponentFixture, TestBed } from "@angular/core/testing";
 import { UserComputingUnitComponent } from "./user-computing-unit.component";
 import { NzCardModule } from "ng-zorro-antd/card";
@@ -64,7 +63,6 @@ describe("UserComputingUnitComponent", () => {
         NzCardModule,
         NzIconModule.forChild([FileAddOutline]),
       ],
-      schemas: [NO_ERRORS_SCHEMA],
     }).compileComponents();
 
     fixture = TestBed.createComponent(UserComputingUnitComponent);
diff --git 
a/frontend/src/app/dashboard/component/user/user-icon/user-icon.component.spec.ts
 
b/frontend/src/app/dashboard/component/user/user-icon/user-icon.component.spec.ts
index 11731e0cf6..1681e2d1c0 100644
--- 
a/frontend/src/app/dashboard/component/user/user-icon/user-icon.component.spec.ts
+++ 
b/frontend/src/app/dashboard/component/user/user-icon/user-icon.component.spec.ts
@@ -17,7 +17,6 @@
  * under the License.
  */
 
-import { NO_ERRORS_SCHEMA } from "@angular/core";
 import { ComponentFixture, TestBed } from "@angular/core/testing";
 import { UserIconComponent } from "./user-icon.component";
 import { UserService } from "../../../../common/service/user/user.service";
@@ -41,7 +40,6 @@ describe("UserIconComponent", () => {
         HttpClientTestingModule,
         NzDropDownModule,
       ],
-      schemas: [NO_ERRORS_SCHEMA],
     }).compileComponents();
   });
 
diff --git 
a/frontend/src/app/dashboard/component/user/user-quota/user-quota.component.spec.ts
 
b/frontend/src/app/dashboard/component/user/user-quota/user-quota.component.spec.ts
index 9b1f1e5dca..1190a562e9 100644
--- 
a/frontend/src/app/dashboard/component/user/user-quota/user-quota.component.spec.ts
+++ 
b/frontend/src/app/dashboard/component/user/user-quota/user-quota.component.spec.ts
@@ -17,7 +17,6 @@
  * under the License.
  */
 
-import { NO_ERRORS_SCHEMA } from "@angular/core";
 import { ComponentFixture, TestBed } from "@angular/core/testing";
 import { UserQuotaComponent } from "./user-quota.component";
 import { UserQuotaService } from 
"../../../service/user/quota/user-quota.service";
@@ -46,7 +45,6 @@ describe("UserQuotaComponent", () => {
     TestBed.configureTestingModule({
       providers: [{ provide: UserQuotaService, useValue: mockUserQuotaService 
}, ...commonTestProviders],
       imports: [UserQuotaComponent, HttpClientTestingModule],
-      schemas: [NO_ERRORS_SCHEMA],
     });
 
     fixture = TestBed.createComponent(UserQuotaComponent);
diff --git 
a/frontend/src/app/workspace/component/left-panel/left-panel.component.spec.ts 
b/frontend/src/app/workspace/component/left-panel/left-panel.component.spec.ts
index 4de5d4ab01..cee8ab6c57 100644
--- 
a/frontend/src/app/workspace/component/left-panel/left-panel.component.spec.ts
+++ 
b/frontend/src/app/workspace/component/left-panel/left-panel.component.spec.ts
@@ -17,7 +17,6 @@
  * under the License.
  */
 
-import { NO_ERRORS_SCHEMA } from "@angular/core";
 import { ComponentFixture, fakeAsync, TestBed, tick } from 
"@angular/core/testing";
 import { LeftPanelComponent } from "./left-panel.component";
 import { mockPoint, mockScanPredicate } from 
"../../service/workflow-graph/model/mock-workflow-data";
@@ -36,12 +35,6 @@ describe("LeftPanelComponent", () => {
   let fixture: ComponentFixture<LeftPanelComponent>;
 
   beforeEach(async () => {
-    TestBed.overrideComponent(LeftPanelComponent, {
-      set: {
-        template: '<div id="left-container"><div #content></div></div>',
-      },
-    });
-
     await TestBed.configureTestingModule({
       imports: [LeftPanelComponent, HttpClientTestingModule, 
RouterTestingModule.withRoutes([])],
       providers: [
@@ -51,7 +44,6 @@ describe("LeftPanelComponent", () => {
         },
         ...commonTestProviders,
       ],
-      schemas: [NO_ERRORS_SCHEMA],
     }).compileComponents();
   });
 
diff --git 
a/frontend/src/app/workspace/component/left-panel/operator-menu/operator-menu.component.spec.ts
 
b/frontend/src/app/workspace/component/left-panel/operator-menu/operator-menu.component.spec.ts
index da753b0b2b..4ef4b5ab2a 100644
--- 
a/frontend/src/app/workspace/component/left-panel/operator-menu/operator-menu.component.spec.ts
+++ 
b/frontend/src/app/workspace/component/left-panel/operator-menu/operator-menu.component.spec.ts
@@ -33,19 +33,12 @@ import { WorkflowUtilService } from 
"../../../service/workflow-graph/util/workfl
 import { NzDropDownModule } from "ng-zorro-antd/dropdown";
 import { NzCollapseModule } from "ng-zorro-antd/collapse";
 import { commonTestProviders } from "../../../../common/testing/test-utils";
-import { NO_ERRORS_SCHEMA } from "@angular/core";
 
 describe("OperatorPanelComponent", () => {
   let component: OperatorMenuComponent;
   let fixture: ComponentFixture<OperatorMenuComponent>;
 
   beforeEach(async () => {
-    TestBed.overrideComponent(OperatorMenuComponent, {
-      set: {
-        template: "",
-      },
-    });
-
     await TestBed.configureTestingModule({
       providers: [
         {
@@ -67,7 +60,6 @@ describe("OperatorPanelComponent", () => {
         BrowserAnimationsModule,
         RouterTestingModule.withRoutes([]),
       ],
-      schemas: [NO_ERRORS_SCHEMA],
     }).compileComponents();
   });
 
diff --git a/frontend/src/app/workspace/component/menu/menu.component.spec.ts 
b/frontend/src/app/workspace/component/menu/menu.component.spec.ts
index 4c4de6cc5c..76de1c1e1b 100644
--- a/frontend/src/app/workspace/component/menu/menu.component.spec.ts
+++ b/frontend/src/app/workspace/component/menu/menu.component.spec.ts
@@ -18,7 +18,6 @@
  */
 
 import { DatePipe, Location } from "@angular/common";
-import { NO_ERRORS_SCHEMA } from "@angular/core";
 import { ComponentFixture, TestBed } from "@angular/core/testing";
 import { HttpClientTestingModule } from "@angular/common/http/testing";
 import { RouterTestingModule } from "@angular/router/testing";
@@ -44,7 +43,7 @@ import { ComputingUnitState } from 
"../../../common/type/computing-unit-connecti
 import { mockPoint, mockScanPredicate } from 
"../../service/workflow-graph/model/mock-workflow-data";
 import { saveAs } from "file-saver";
 import type { ModalOptions } from "ng-zorro-antd/modal";
-import { ComputingUnitSelectionComponent } from 
"../power-button/computing-unit-selection.component";
+import type { ComputingUnitSelectionComponent } from 
"../power-button/computing-unit-selection.component";
 import { WorkflowContent } from "../../../common/type/workflow";
 import type { Mocked } from "vitest";
 
@@ -65,10 +64,6 @@ describe("MenuComponent", () => {
   let validationStream$: BehaviorSubject<ValidationOutput>;
 
   beforeEach(async () => {
-    TestBed.overrideComponent(MenuComponent, {
-      set: { template: "" },
-    });
-
     await TestBed.configureTestingModule({
       imports: [MenuComponent, HttpClientTestingModule, 
RouterTestingModule.withRoutes([]), NzModalModule],
       providers: [
@@ -79,12 +74,14 @@ describe("MenuComponent", () => {
           useValue: {
             getSelectedComputingUnit: () => of(null),
             getStatus: () => of(ComputingUnitState.NoComputingUnit),
+            // Read by ComputingUnitSelectionComponent.ngOnInit when the menu
+            // template renders the <texera-computing-unit-selection> child.
+            getAllComputingUnits: () => of([]),
           },
         },
         { provide: UserService, useClass: StubUserService },
         ...commonTestProviders,
       ],
-      schemas: [NO_ERRORS_SCHEMA],
     }).compileComponents();
 
     workflowActionService = TestBed.inject(WorkflowActionService);
diff --git 
a/frontend/src/app/workspace/component/property-editor/operator-property-edit-frame/operator-property-edit-frame.component.spec.ts
 
b/frontend/src/app/workspace/component/property-editor/operator-property-edit-frame/operator-property-edit-frame.component.spec.ts
index cbb0f19726..d394040a9f 100644
--- 
a/frontend/src/app/workspace/component/property-editor/operator-property-edit-frame/operator-property-edit-frame.component.spec.ts
+++ 
b/frontend/src/app/workspace/component/property-editor/operator-property-edit-frame/operator-property-edit-frame.component.spec.ts
@@ -41,7 +41,7 @@ import {
   mockViewResultsSchema,
 } from "../../../service/operator-metadata/mock-operator-metadata.data";
 import { configure } from "rxjs-marbles";
-import { NO_ERRORS_SCHEMA, SimpleChange } from "@angular/core";
+import { SimpleChange } from "@angular/core";
 import { cloneDeep } from "lodash-es";
 
 import Ajv from "ajv";
@@ -58,12 +58,19 @@ describe("OperatorPropertyEditFrameComponent", () => {
   let workflowActionService: WorkflowActionService;
 
   beforeEach(async () => {
+    // TODO(coverage): tests in this spec exercise dynamic Formly form 
rendering;
+    // the real OperatorPropertyEditFrame template throws under jsdom when the
+    // Formly tree tries to read child.component from an uninstantiated field.
+    // The stub template lets the class-level tests run while we figure out a
+    // Formly-aware setup. Drop this override once that's done.
+    /* eslint-disable no-restricted-syntax */
     TestBed.overrideComponent(OperatorPropertyEditFrameComponent, {
       set: {
         template:
           '<div class="texera-workspace-property-editor-title">{{ formTitle 
}}</div><div class="texera-workspace-property-editor-form"></div>',
       },
     });
+    /* eslint-enable no-restricted-syntax */
 
     await TestBed.configureTestingModule({
       providers: [
@@ -85,7 +92,6 @@ describe("OperatorPropertyEditFrameComponent", () => {
         ReactiveFormsModule,
         HttpClientTestingModule,
       ],
-      schemas: [NO_ERRORS_SCHEMA],
     }).compileComponents();
 
     fixture = TestBed.createComponent(OperatorPropertyEditFrameComponent);
diff --git 
a/frontend/src/app/workspace/component/property-editor/property-editor.component.spec.ts
 
b/frontend/src/app/workspace/component/property-editor/property-editor.component.spec.ts
index 385ff33cfb..c472e3ef48 100644
--- 
a/frontend/src/app/workspace/component/property-editor/property-editor.component.spec.ts
+++ 
b/frontend/src/app/workspace/component/property-editor/property-editor.component.spec.ts
@@ -18,7 +18,6 @@
  */
 
 import { CommonModule } from "@angular/common";
-import { NO_ERRORS_SCHEMA } from "@angular/core";
 import { ComponentFixture, TestBed } from "@angular/core/testing";
 import { PropertyEditorComponent } from "./property-editor.component";
 import {
@@ -41,12 +40,6 @@ describe("PropertyEditorComponent", () => {
   let workflowActionService: WorkflowActionService;
 
   beforeEach(async () => {
-    TestBed.overrideComponent(PropertyEditorComponent, {
-      set: {
-        template: '<div id="right-container"><div 
#contentWrapper></div></div>',
-      },
-    });
-
     await TestBed.configureTestingModule({
       imports: [PropertyEditorComponent, CommonModule, 
HttpClientTestingModule],
       providers: [
@@ -57,7 +50,6 @@ describe("PropertyEditorComponent", () => {
         { provide: ComputingUnitStatusService, useClass: 
MockComputingUnitStatusService },
         ...commonTestProviders,
       ],
-      schemas: [NO_ERRORS_SCHEMA],
     }).compileComponents();
   });
 
diff --git a/frontend/src/app/workspace/component/workspace.component.spec.ts 
b/frontend/src/app/workspace/component/workspace.component.spec.ts
index 088ac06f3c..f151ec3e4f 100644
--- a/frontend/src/app/workspace/component/workspace.component.spec.ts
+++ b/frontend/src/app/workspace/component/workspace.component.spec.ts
@@ -18,6 +18,12 @@
  */
 
 import { Location } from "@angular/common";
+// TODO(coverage): this spec was set up in #5037 to render the workspace with
+// stripped child imports + CUSTOM_ELEMENTS_SCHEMA so the @ViewChild on
+// #codeEditor resolves while the deep child tree stays out of the bundle.
+// Migrating it off NO_ERRORS_SCHEMA / set:{imports:[]} requires providing
+// each child's transitive deps; tracking separately.
+// eslint-disable-next-line no-restricted-imports
 import { CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA } from "@angular/core";
 import { ComponentFixture, TestBed } from "@angular/core/testing";
 import { HttpClientTestingModule } from "@angular/common/http/testing";
@@ -134,9 +140,13 @@ describe("WorkspaceComponent", () => {
     // CUSTOM_ELEMENTS_SCHEMA. The template still renders, so `<ng-template 
#codeEditor>`
     // is wired up and the @ViewChild query resolves to a real 
ViewContainerRef, while
     // the children's transitive dependencies stay out of the test build.
+    // TODO(coverage): rewrite using stub child components via remove/add so 
the
+    // template participates in coverage. See TESTING.md anti-pattern #9.
+    /* eslint-disable no-restricted-syntax */
     TestBed.overrideComponent(WorkspaceComponent, {
       set: { imports: [], providers: [], schemas: [CUSTOM_ELEMENTS_SCHEMA] },
     });
+    /* eslint-enable no-restricted-syntax */
 
     await TestBed.configureTestingModule({
       imports: [WorkspaceComponent, HttpClientTestingModule],

Reply via email to