This is an automated email from the ASF dual-hosted git repository. github-merge-queue[bot] pushed a commit to branch gh-readonly-queue/main/pr-5189-cfc6d9aa81ee01225fe67d5d58af23a5c816d289 in repository https://gitbox.apache.org/repos/asf/texera.git
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],
