diff --git a/frontend/angular.json b/frontend/angular.json index fabc6ba8ce0..e521d7cee9e 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 c974ee8cd50..dcac22c6b3f 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 7e59206eff5..bb8dfcce983 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; 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 49271ef7c38..d94217f7c21 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; - 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 f9b7eb0ba7a..7453e1b4024 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; 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 96065d8072d..a449218b8f3 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; 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 8661bec28a3..cbb0f19726c 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; 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 627d1e72917..3109dc69435 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 1bc9a651705..4d296fac544 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; +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 474fe725732..e9ebccf86da 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 de18fc57002..5462d42cf49 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