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