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

Yicong-Huang 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 933f7fed77 test(frontend): unblock 7 specs from jsdom (#4950)
933f7fed77 is described below

commit 933f7fed772a84fd47726c5579c1b7624453a2d9
Author: Yicong Huang <[email protected]>
AuthorDate: Wed May 6 00:02:30 2026 -0700

    test(frontend): unblock 7 specs from jsdom (#4950)
    
    ### What changes were proposed in this PR?
    
    Most specs that #4866 listed as needing Vitest browser mode actually
    weren't blocked by jsdom's missing layout — they were tripping on infra
    and migration leftovers. Fix the root causes so they run under jsdom;
    narrow #4866 to the real layout-dependent jointjs paper suite.
    
    Three changes:
    
    - `vitest.config.ts` inlines `monaco-breakpoints` so its `import
    './style.css'` goes through Vite instead of Node's ESM loader (which
    crashes on `.css`).
    - `jsdom-svg-polyfill.ts` stubs `requestIdleCallback` (Chrome-only).
    - 6 specs get `declarations` → `imports` (post-#4873 standalone) and
    `beforeEach(waitForAsync)` → `beforeEach(async)` (the ProxyZone wrapper
    in `test-zone-setup.ts` only covers `it`/`test`).
    
    Plus three small per-spec fixes: `vi.spyOn` → `.mockImplementation(() =>
    {})` in `code-debugger` (vitest spies call through by default, unlike
    Jasmine), `innerText` → `textContent` in `operator-property-edit-frame`
    (jsdom's `innerText` is layout-dependent), and `it.skip` for the 2
    drag-drop tests that genuinely need real geometry.
    
    After this, the only spec excluded for jsdom-vs-real-browser reasons is
    `workflow-editor.component.spec.ts`.
    
    ### Any related issues, documentation, discussions?
    
    Part of #4861. Narrows #4866 to the jointjs paper suite + 2 drag-drop
    geometry tests.
    
    ### How was this PR tested?
    
    `yarn ng test --watch=false`: 251 pass, 11 skip, 2 todo. `yarn
    format:ci` clean.
    
    ### Was this PR authored or co-authored using generative AI tooling?
    
    Generated-by: Claude Opus 4.7 (1M context)
    
    Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
---
 frontend/angular.json                              |  7 -------
 .../code-debugger.component.spec.ts                | 11 ++++++++---
 .../code-editor.component.spec.ts                  | 11 ++++-------
 .../codearea-custom-template.component.spec.ts     | 11 ++++-------
 .../time-travel/time-travel.component.spec.ts      | 12 +++++------
 .../versions-list/versions-list.component.spec.ts  | 12 +++++------
 .../operator-property-edit-frame.component.spec.ts | 15 +++++++-------
 .../service/drag-drop/drag-drop.service.spec.ts    | 10 ++++++++--
 frontend/src/jsdom-svg-polyfill.ts                 | 19 ++++++++++++++++++
 frontend/src/tsconfig.spec.json                    | 23 +++++-----------------
 frontend/vitest.config.ts                          | 11 +++++++++++
 11 files changed, 76 insertions(+), 66 deletions(-)

diff --git a/frontend/angular.json b/frontend/angular.json
index fabc6ba8ce..e521d7cee9 100644
--- a/frontend/angular.json
+++ b/frontend/angular.json
@@ -94,17 +94,10 @@
             "include": ["**/*.spec.ts"],
             "setupFiles": ["src/jsdom-svg-polyfill.ts"],
             "exclude": [
-              "**/drag-drop.service.spec.ts",
               
"**/app/common/formly/preset-wrapper/preset-wrapper.component.spec.ts",
               "**/app/common/service/user/config/user-config.service.spec.ts",
-              
"**/app/workspace/component/code-editor-dialog/code-debugger.component.spec.ts",
-              
"**/app/workspace/component/code-editor-dialog/code-editor.component.spec.ts",
-              
"**/app/workspace/component/codearea-custom-template/codearea-custom-template.component.spec.ts",
               
"**/app/workspace/component/left-panel/settings/settings.component.spec.ts",
-              
"**/app/workspace/component/left-panel/time-travel/time-travel.component.spec.ts",
-              
"**/app/workspace/component/left-panel/versions-list/versions-list.component.spec.ts",
               "**/app/workspace/component/menu/menu.component.spec.ts",
-              
"**/app/workspace/component/property-editor/operator-property-edit-frame/operator-property-edit-frame.component.spec.ts",
               
"**/app/workspace/component/workflow-editor/workflow-editor.component.spec.ts",
               "**/app/workspace/component/workspace.component.spec.ts",
               "**/app/workspace/service/preset/preset.service.spec.ts"
diff --git 
a/frontend/src/app/workspace/component/code-editor-dialog/code-debugger.component.spec.ts
 
b/frontend/src/app/workspace/component/code-editor-dialog/code-debugger.component.spec.ts
index c974ee8cd5..dcac22c6b3 100644
--- 
a/frontend/src/app/workspace/component/code-editor-dialog/code-debugger.component.spec.ts
+++ 
b/frontend/src/app/workspace/component/code-editor-dialog/code-debugger.component.spec.ts
@@ -51,7 +51,7 @@ describe("CodeDebuggerComponent", () => {
     mockUdfDebugService.getDebugState.mockReturnValue(debugState);
 
     await TestBed.configureTestingModule({
-      declarations: [CodeDebuggerComponent],
+      imports: [CodeDebuggerComponent],
       schemas: [CUSTOM_ELEMENTS_SCHEMA],
       providers: [
         { provide: WorkflowStatusService, useValue: mockWorkflowStatusService 
},
@@ -82,8 +82,13 @@ describe("CodeDebuggerComponent", () => {
   });
 
   it("should setup monaco breakpoint methods when state is Running", 
fakeAsync(() => {
-    const setupSpy = vi.spyOn(component, "setupMonacoBreakpointMethods");
-    const rerenderSpy = vi.spyOn(component, "rerenderExistingBreakpoints");
+    // Stub the real implementations: setupMonacoBreakpointMethods constructs
+    // a `MonacoBreakpoint` over a real monaco editor instance, which calls
+    // editor.onMouseMove / onMouseDown — APIs the test's minimal
+    // `monacoEditor` mock doesn't expose. The behavior under test is the
+    // state-machine wiring, not the breakpoint plumbing itself.
+    const setupSpy = vi.spyOn(component, 
"setupMonacoBreakpointMethods").mockImplementation(() => {});
+    const rerenderSpy = vi.spyOn(component, 
"rerenderExistingBreakpoints").mockImplementation(() => {});
 
     // Emit a Running state event
     statusUpdateStream.next({
diff --git 
a/frontend/src/app/workspace/component/code-editor-dialog/code-editor.component.spec.ts
 
b/frontend/src/app/workspace/component/code-editor-dialog/code-editor.component.spec.ts
index 7e59206eff..bb8dfcce98 100644
--- 
a/frontend/src/app/workspace/component/code-editor-dialog/code-editor.component.spec.ts
+++ 
b/frontend/src/app/workspace/component/code-editor-dialog/code-editor.component.spec.ts
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-import { ComponentFixture, TestBed, waitForAsync } from 
"@angular/core/testing";
+import { ComponentFixture, TestBed } from "@angular/core/testing";
 import { CodeEditorComponent } from "./code-editor.component";
 import { HttpClientTestingModule } from "@angular/common/http/testing";
 import { WorkflowActionService } from 
"../../service/workflow-graph/model/workflow-action.service";
@@ -31,9 +31,8 @@ describe("CodeEditorDialogComponent", () => {
   let fixture: ComponentFixture<CodeEditorComponent>;
   let workflowActionService: WorkflowActionService;
 
-  beforeEach(waitForAsync(() => {
-    TestBed.configureTestingModule({
-      declarations: [CodeEditorComponent],
+  beforeEach(async () => {
+    await TestBed.configureTestingModule({
       providers: [
         WorkflowActionService,
         {
@@ -42,11 +41,9 @@ describe("CodeEditorDialogComponent", () => {
         },
         ...commonTestProviders,
       ],
-      imports: [HttpClientTestingModule],
+      imports: [CodeEditorComponent, HttpClientTestingModule],
     }).compileComponents();
-  }));
 
-  beforeEach(() => {
     workflowActionService = TestBed.inject(WorkflowActionService);
     workflowActionService.addOperator(mockJavaUDFPredicate, mockPoint);
     
workflowActionService.getJointGraphWrapper().highlightOperators(mockJavaUDFPredicate.operatorID);
diff --git 
a/frontend/src/app/workspace/component/codearea-custom-template/codearea-custom-template.component.spec.ts
 
b/frontend/src/app/workspace/component/codearea-custom-template/codearea-custom-template.component.spec.ts
index 49271ef7c3..d94217f7c2 100644
--- 
a/frontend/src/app/workspace/component/codearea-custom-template/codearea-custom-template.component.spec.ts
+++ 
b/frontend/src/app/workspace/component/codearea-custom-template/codearea-custom-template.component.spec.ts
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-import { ComponentFixture, TestBed, waitForAsync } from 
"@angular/core/testing";
+import { ComponentFixture, TestBed } from "@angular/core/testing";
 import { CodeareaCustomTemplateComponent } from 
"./codearea-custom-template.component";
 import { HttpClientTestingModule } from "@angular/common/http/testing";
 import { WorkflowActionService } from 
"../../service/workflow-graph/model/workflow-action.service";
@@ -30,10 +30,9 @@ describe("CodeareaCustomTemplateComponent", () => {
   let component: CodeareaCustomTemplateComponent;
   let fixture: ComponentFixture<CodeareaCustomTemplateComponent>;
 
-  beforeEach(waitForAsync(() => {
-    TestBed.configureTestingModule({
-      declarations: [CodeareaCustomTemplateComponent],
-      imports: [HttpClientTestingModule],
+  beforeEach(async () => {
+    await TestBed.configureTestingModule({
+      imports: [CodeareaCustomTemplateComponent, HttpClientTestingModule],
       providers: [
         WorkflowActionService,
         {
@@ -43,9 +42,7 @@ describe("CodeareaCustomTemplateComponent", () => {
         ...commonTestProviders,
       ],
     }).compileComponents();
-  }));
 
-  beforeEach(() => {
     fixture = TestBed.createComponent(CodeareaCustomTemplateComponent);
     component = fixture.componentInstance;
     component.field = { props: {}, formControl: new FormControl() };
diff --git 
a/frontend/src/app/workspace/component/left-panel/time-travel/time-travel.component.spec.ts
 
b/frontend/src/app/workspace/component/left-panel/time-travel/time-travel.component.spec.ts
index f9b7eb0ba7..7453e1b402 100644
--- 
a/frontend/src/app/workspace/component/left-panel/time-travel/time-travel.component.spec.ts
+++ 
b/frontend/src/app/workspace/component/left-panel/time-travel/time-travel.component.spec.ts
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-import { ComponentFixture, TestBed, waitForAsync } from 
"@angular/core/testing";
+import { ComponentFixture, TestBed } from "@angular/core/testing";
 import { WorkflowActionService } from 
"../../../service/workflow-graph/model/workflow-action.service";
 import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
 import { FormsModule, ReactiveFormsModule } from "@angular/forms";
@@ -29,20 +29,20 @@ import { ComputingUnitStatusService } from 
"../../../../common/service/computing
 import { MockComputingUnitStatusService } from 
"../../../../common/service/computing-unit/computing-unit-status/mock-computing-unit-status.service";
 import { commonTestProviders } from "../../../../common/testing/test-utils";
 
-describe("VersionsListDisplayComponent", () => {
+describe("TimeTravelComponent", () => {
   let component: TimeTravelComponent;
   let fixture: ComponentFixture<TimeTravelComponent>;
   let workflowActionService: WorkflowActionService;
 
-  beforeEach(waitForAsync(() => {
-    TestBed.configureTestingModule({
-      declarations: [TimeTravelComponent],
+  beforeEach(async () => {
+    await TestBed.configureTestingModule({
       providers: [
         WorkflowActionService,
         { provide: ComputingUnitStatusService, useClass: 
MockComputingUnitStatusService },
         ...commonTestProviders,
       ],
       imports: [
+        TimeTravelComponent,
         BrowserAnimationsModule,
         FormsModule,
         FormlyModule.forRoot(TEXERA_FORMLY_CONFIG),
@@ -50,9 +50,7 @@ describe("VersionsListDisplayComponent", () => {
         HttpClientTestingModule,
       ],
     }).compileComponents();
-  }));
 
-  beforeEach(() => {
     fixture = TestBed.createComponent(TimeTravelComponent);
     component = fixture.componentInstance;
     workflowActionService = TestBed.inject(WorkflowActionService);
diff --git 
a/frontend/src/app/workspace/component/left-panel/versions-list/versions-list.component.spec.ts
 
b/frontend/src/app/workspace/component/left-panel/versions-list/versions-list.component.spec.ts
index 96065d8072..a449218b8f 100644
--- 
a/frontend/src/app/workspace/component/left-panel/versions-list/versions-list.component.spec.ts
+++ 
b/frontend/src/app/workspace/component/left-panel/versions-list/versions-list.component.spec.ts
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-import { ComponentFixture, TestBed, waitForAsync } from 
"@angular/core/testing";
+import { ComponentFixture, TestBed } from "@angular/core/testing";
 import { WorkflowActionService } from 
"../../../service/workflow-graph/model/workflow-action.service";
 import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
 import { FormsModule, ReactiveFormsModule } from "@angular/forms";
@@ -28,16 +28,16 @@ import { VersionsListComponent } from 
"./versions-list.component";
 import { RouterTestingModule } from "@angular/router/testing";
 import { commonTestProviders } from "../../../../common/testing/test-utils";
 
-describe("VersionsListDisplayComponent", () => {
+describe("VersionsListComponent", () => {
   let component: VersionsListComponent;
   let fixture: ComponentFixture<VersionsListComponent>;
   let workflowActionService: WorkflowActionService;
 
-  beforeEach(waitForAsync(() => {
-    TestBed.configureTestingModule({
-      declarations: [VersionsListComponent],
+  beforeEach(async () => {
+    await TestBed.configureTestingModule({
       providers: [WorkflowActionService, ...commonTestProviders],
       imports: [
+        VersionsListComponent,
         BrowserAnimationsModule,
         FormsModule,
         FormlyModule.forRoot(TEXERA_FORMLY_CONFIG),
@@ -46,9 +46,7 @@ describe("VersionsListDisplayComponent", () => {
         RouterTestingModule.withRoutes([]),
       ],
     }).compileComponents();
-  }));
 
-  beforeEach(() => {
     fixture = TestBed.createComponent(VersionsListComponent);
     component = fixture.componentInstance;
     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 8661bec28a..cbb0f19726 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
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-import { ComponentFixture, discardPeriodicTasks, fakeAsync, TestBed, tick, 
waitForAsync } from "@angular/core/testing";
+import { ComponentFixture, discardPeriodicTasks, fakeAsync, TestBed, tick } 
from "@angular/core/testing";
 
 import { OperatorPropertyEditFrameComponent } from 
"./operator-property-edit-frame.component";
 import { WorkflowActionService } from 
"../../../service/workflow-graph/model/workflow-action.service";
@@ -57,7 +57,7 @@ describe("OperatorPropertyEditFrameComponent", () => {
   let fixture: ComponentFixture<OperatorPropertyEditFrameComponent>;
   let workflowActionService: WorkflowActionService;
 
-  beforeEach(waitForAsync(() => {
+  beforeEach(async () => {
     TestBed.overrideComponent(OperatorPropertyEditFrameComponent, {
       set: {
         template:
@@ -65,8 +65,7 @@ describe("OperatorPropertyEditFrameComponent", () => {
       },
     });
 
-    TestBed.configureTestingModule({
-      declarations: [OperatorPropertyEditFrameComponent],
+    await TestBed.configureTestingModule({
       providers: [
         WorkflowActionService,
         {
@@ -78,6 +77,7 @@ describe("OperatorPropertyEditFrameComponent", () => {
         ...commonTestProviders,
       ],
       imports: [
+        OperatorPropertyEditFrameComponent,
         BrowserAnimationsModule,
         FormsModule,
         FormlyModule.forRoot(TEXERA_FORMLY_CONFIG),
@@ -87,9 +87,7 @@ describe("OperatorPropertyEditFrameComponent", () => {
       ],
       schemas: [NO_ERRORS_SCHEMA],
     }).compileComponents();
-  }));
 
-  beforeEach(() => {
     fixture = TestBed.createComponent(OperatorPropertyEditFrameComponent);
     component = fixture.componentInstance;
     workflowActionService = TestBed.inject(WorkflowActionService);
@@ -125,8 +123,9 @@ describe("OperatorPropertyEditFrameComponent", () => {
     // check HTML form are displayed
     const formTitleElement = 
fixture.debugElement.query(By.css(".texera-workspace-property-editor-title"));
     const jsonSchemaFormElement = 
fixture.debugElement.query(By.css(".texera-workspace-property-editor-form"));
-    // check the panel title
-    expect((formTitleElement.nativeElement as HTMLElement).innerText).toEqual(
+    // check the panel title (use textContent — jsdom doesn't compute the
+    // layout-dependent innerText getter, which returns undefined here)
+    expect((formTitleElement.nativeElement as 
HTMLElement).textContent?.trim()).toEqual(
       mockScanSourceSchema.additionalMetadata.userFriendlyName
     );
 
diff --git 
a/frontend/src/app/workspace/service/drag-drop/drag-drop.service.spec.ts 
b/frontend/src/app/workspace/service/drag-drop/drag-drop.service.spec.ts
index 627d1e7291..3109dc6943 100644
--- a/frontend/src/app/workspace/service/drag-drop/drag-drop.service.spec.ts
+++ b/frontend/src/app/workspace/service/drag-drop/drag-drop.service.spec.ts
@@ -81,7 +81,11 @@ describe("DragDropService", () => {
     expect(createdLink.target).toEqual(mockScanResultLink.target);
   });
 
-  it("should find 3 input operatorPredicates and 3 output operatorPredicates 
for an operatorPredicate with 3 input / 3 output ports", () => {
+  // findClosestOperators consults real SVG geometry (getBBox / getScreenCTM).
+  // The jsdom polyfill returns identity matrices and zero-size boxes, so all
+  // operators report position (0,0) and the closest-N query yields []. Tracked
+  // for re-enable under Vitest browser mode in #4866.
+  it.skip("should find 3 input operatorPredicates and 3 output 
operatorPredicates for an operatorPredicate with 3 input / 3 output ports", () 
=> {
     const workflowActionService: WorkflowActionService = 
TestBed.inject(WorkflowActionService);
     const workflowUtilService: WorkflowUtilService = 
TestBed.inject(WorkflowUtilService);
 
@@ -151,7 +155,9 @@ describe("DragDropService", () => {
     expect(inputOps).toEqual([]);
   });
 
-  it(
+  // Same root cause as the skipped test above — link inference depends on
+  // findClosestOperators returning real geometry. Tracked in #4866.
+  it.skip(
     "should update highlighting, add operator, and add links when an operator 
is dropped",
     marbles(async () => {
       const workflowActionService: WorkflowActionService = 
TestBed.inject(WorkflowActionService);
diff --git a/frontend/src/jsdom-svg-polyfill.ts 
b/frontend/src/jsdom-svg-polyfill.ts
index 1bc9a65170..4d296fac54 100644
--- a/frontend/src/jsdom-svg-polyfill.ts
+++ b/frontend/src/jsdom-svg-polyfill.ts
@@ -105,6 +105,25 @@ if (docProto && typeof docProto.queryCommandSupported !== 
"function") {
   docProto.queryCommandSupported = (() => false) as AnyFn;
 }
 
+/**
+ * jsdom doesn't implement `requestIdleCallback` / `cancelIdleCallback`
+ * (a Chrome-only API). Specs that pull in monaco-related modules
+ * crash at construction with `ReferenceError: requestIdleCallback is
+ * not defined`.
+ *
+ * Approximate with `setTimeout` so callbacks still fire. The deadline
+ * argument is a coarse stub — enough for callers that only read
+ * `didTimeout`.
+ */
+const idleGlobal = globalThis as unknown as Record<string, AnyFn | undefined>;
+if (typeof idleGlobal.requestIdleCallback !== "function") {
+  idleGlobal.requestIdleCallback = ((cb: (d: { didTimeout: boolean; 
timeRemaining: () => number }) => void) =>
+    setTimeout(() => cb({ didTimeout: false, timeRemaining: () => 50 }), 0)) 
as AnyFn;
+}
+if (typeof idleGlobal.cancelIdleCallback !== "function") {
+  idleGlobal.cancelIdleCallback = ((id: number) => clearTimeout(id)) as AnyFn;
+}
+
 /**
  * y-websocket schedules a reconnect timer the moment a service that uses
  * collaborative editing is constructed. When that timer fires AFTER vitest
diff --git a/frontend/src/tsconfig.spec.json b/frontend/src/tsconfig.spec.json
index 474fe72573..e9ebccf86d 100644
--- a/frontend/src/tsconfig.spec.json
+++ b/frontend/src/tsconfig.spec.json
@@ -11,11 +11,6 @@
   },
   "include": ["**/*.spec.ts", "**/*.d.ts", "vitest-globals.d.ts", 
"jsdom-svg-polyfill.ts"],
   "exclude": [
-    // jsdom polyfill is enough to instantiate jointjs but its zero-
-    // dimension fake matrices break the actual graph-geometry math
-    // these tests assert on. Real fix is Vitest browser mode (#4866).
-    "**/drag-drop.service.spec.ts",
-
     // Specs whose body is entirely commented out / placeholder — these
     // need real test cases written before they can be re-enabled.
     "**/app/common/formly/preset-wrapper/preset-wrapper.component.spec.ts",
@@ -25,19 +20,11 @@
     "**/app/workspace/component/workspace.component.spec.ts",
     "**/app/workspace/service/preset/preset.service.spec.ts",
 
-    // Specs that compile but fail at runtime against jsdom's gaps:
-    // monaco-editor needs CSS parsing + queryCommandSupported, jointjs
-    // needs real getScreenCTM, formly+nz fetches icons over HTTP. These
-    // belong under Vitest browser mode (#4866) and aren't an Angular
-    // standalone-migration issue any more.
-    
"**/app/workspace/component/code-editor-dialog/code-debugger.component.spec.ts",
-    
"**/app/workspace/component/code-editor-dialog/code-editor.component.spec.ts",
-    
"**/app/workspace/component/codearea-custom-template/codearea-custom-template.component.spec.ts",
-    // time-travel & versions-list pull in `monaco-breakpoints` whose entry 
imports a `.css`
-    // file at runtime; Vitest can't transform that asset and crashes during 
module load.
-    
"**/app/workspace/component/left-panel/time-travel/time-travel.component.spec.ts",
-    
"**/app/workspace/component/left-panel/versions-list/versions-list.component.spec.ts",
-    
"**/app/workspace/component/property-editor/operator-property-edit-frame/operator-property-edit-frame.component.spec.ts",
+    // jointjs paper geometry: every test in this suite asserts on
+    // graph layout math (positions, link routing, hit testing) that
+    // depends on real getScreenCTM / getBBox. The jsdom polyfill
+    // returns identity-only stubs, so all assertions fail. Real fix is
+    // Vitest browser mode (#4866).
     
"**/app/workspace/component/workflow-editor/workflow-editor.component.spec.ts"
   ]
 }
diff --git a/frontend/vitest.config.ts b/frontend/vitest.config.ts
index de18fc5700..5462d42cf4 100644
--- a/frontend/vitest.config.ts
+++ b/frontend/vitest.config.ts
@@ -29,6 +29,17 @@ export default defineConfig({
     // which Angular's `fakeAsync` requires. Karma+Jasmine installed this
     // implicitly; the @angular/build:unit-test path doesn't.
     setupFiles: ["src/test-zone-setup.ts"],
+    // monaco-breakpoints' entry does `import './style.css'`. By default
+    // Vitest leaves third-party deps externalized, so Node's ESM loader
+    // tries to import the .css and crashes with
+    // `TypeError: Unknown file extension ".css"`. Inlining the package
+    // routes its imports through Vite/esbuild, which rewrites the CSS
+    // import to a no-op.
+    server: {
+      deps: {
+        inline: [/monaco-breakpoints/],
+      },
+    },
     // Per-spec exclusions live in `angular.json` (the unit-test builder
     // applies them at the discovery stage, before Vitest's own filter,
     // which is what the Vitest team recommends — see the Vite warning

Reply via email to