You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@texera.apache.org by gi...@apache.org on 2026/07/18 23:50:09 UTC
(texera) branch main updated: test(frontend): add unit test coverage for ResultExportationComponent (#6509)
This is an automated email from the ASF dual-hosted git repository.
github-merge-queue[bot] pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/texera.git
The following commit(s) were added to refs/heads/main by this push:
new 8d7664d288 test(frontend): add unit test coverage for ResultExportationComponent (#6509)
8d7664d288 is described below
commit 8d7664d288ddb5bfd7816a1dc6eb8d2df893a7c3
Author: Meng Wang <me...@uci.edu>
AuthorDate: Sat Jul 18 16:39:17 2026 -0700
test(frontend): add unit test coverage for ResultExportationComponent (#6509)
### What changes were proposed in this PR?
Adds a behavior-focused Vitest spec for `ResultExportationComponent`
(`frontend/src/app/workspace/component/result-exportation/result-exportation.component.ts`),
which previously had no dedicated spec (codecov ~6%). It is the ng-zorro
modal
that drives result export to a dataset or a local download.
5 tests, using `TestBed` with all seven collaborators stubbed and
`NZ_MODAL_DATA` / `NzModalRef` provided (no backend, no real modal):
- **creation** — the component instantiates and `detectChanges()`
renders.
- **`ngOnInit`** — keeps only `WRITE`-accessible datasets, stores the
downloadability result, and picks up the selected computing unit.
- **`onClickExportResult("dataset", ds)`** — calls the export service
with the
dataset destination and the resolved dataset id, then closes the modal.
- **`onClickExportResult("local")`** — calls the export service with the
local
destination and no dataset ids, then closes the modal.
- **`onClickCreateNewDataset()`** — opens the dataset-creator modal
(`UserDatasetVersionCreatorComponent`) and adopts the dataset returned
on
close.
No production code was changed.
### Any related issues, documentation, discussions?
Closes #6504
### How was this PR tested?
New unit tests, run locally in `frontend/` (all green; the failure path
was
verified by breaking an assertion to confirm the suite goes red):
```
ng test --watch=false --include src/app/workspace/component/result-exportation/result-exportation.component.spec.ts
# Tests 5 passed (5)
eslint <spec> # clean
prettier --check # clean
```
### Was this PR authored or co-authored using generative AI tooling?
Generated-by: Claude Code (Opus 4.8 [1M context])
---
.../result-exportation.component.spec.ts | 168 +++++++++++++++++++++
1 file changed, 168 insertions(+)
diff --git a/frontend/src/app/workspace/component/result-exportation/result-exportation.component.spec.ts b/frontend/src/app/workspace/component/result-exportation/result-exportation.component.spec.ts
new file mode 100644
index 0000000000..866b294e56
--- /dev/null
+++ b/frontend/src/app/workspace/component/result-exportation/result-exportation.component.spec.ts
@@ -0,0 +1,168 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { ComponentFixture, TestBed } from "@angular/core/testing";
+import { of } from "rxjs";
+import { ResultExportationComponent } from "./result-exportation.component";
+import {
+ WorkflowResultDownloadability,
+ WorkflowResultExportService,
+} from "../../service/workflow-result-export/workflow-result-export.service";
+import { DatasetService } from "../../../dashboard/service/user/dataset/dataset.service";
+import { WorkflowActionService } from "../../service/workflow-graph/model/workflow-action.service";
+import { WorkflowResultService } from "../../service/workflow-result/workflow-result.service";
+import { ComputingUnitStatusService } from "../../../common/service/computing-unit/computing-unit-status/computing-unit-status.service";
+import { UserDatasetVersionCreatorComponent } from "../../../dashboard/component/user/user-dataset/user-dataset-explorer/user-dataset-version-creator/user-dataset-version-creator.component";
+import { DashboardDataset } from "../../../dashboard/type/dashboard-dataset.interface";
+import { NZ_MODAL_DATA, NzModalRef, NzModalService } from "ng-zorro-antd/modal";
+
+const writeDataset = {
+ dataset: { did: 1, name: "writable" },
+ accessPrivilege: "WRITE",
+} as unknown as DashboardDataset;
+
+const readDataset = {
+ dataset: { did: 2, name: "readonly" },
+ accessPrivilege: "READ",
+} as unknown as DashboardDataset;
+
+const MODAL_DATA = {
+ sourceTriggered: "menu",
+ workflowName: "my-workflow",
+ defaultFileName: "out.csv",
+ rowIndex: -1,
+ columnIndex: -1,
+ exportType: "csv",
+};
+
+describe("ResultExportationComponent", () => {
+ let component: ResultExportationComponent;
+ let fixture: ComponentFixture<ResultExportationComponent>;
+
+ let exportWorkflowExecutionResult: ReturnType<typeof vi.fn>;
+ let modalClose: ReturnType<typeof vi.fn>;
+ let modalCreate: ReturnType<typeof vi.fn>;
+
+ beforeEach(async () => {
+ exportWorkflowExecutionResult = vi.fn();
+ modalClose = vi.fn();
+ modalCreate = vi.fn().mockReturnValue({ afterClose: of(null) });
+
+ await TestBed.configureTestingModule({
+ imports: [ResultExportationComponent],
+ providers: [
+ { provide: NZ_MODAL_DATA, useValue: MODAL_DATA },
+ { provide: NzModalRef, useValue: { close: modalClose, getConfig: () => ({}) } },
+ { provide: NzModalService, useValue: { create: modalCreate } },
+ {
+ provide: WorkflowResultExportService,
+ useValue: {
+ computeRestrictionAnalysis: vi.fn().mockReturnValue(of(new WorkflowResultDownloadability(new Map()))),
+ exportWorkflowExecutionResult,
+ },
+ },
+ {
+ provide: DatasetService,
+ useValue: { retrieveAccessibleDatasets: vi.fn().mockReturnValue(of([writeDataset, readDataset])) },
+ },
+ {
+ provide: WorkflowActionService,
+ useValue: {
+ getTexeraGraph: vi.fn().mockReturnValue({ getAllOperators: vi.fn().mockReturnValue([]) }),
+ getJointGraphWrapper: vi
+ .fn()
+ .mockReturnValue({ getCurrentHighlightedOperatorIDs: vi.fn().mockReturnValue([]) }),
+ },
+ },
+ {
+ provide: WorkflowResultService,
+ useValue: {
+ determineOutputTypes: vi.fn().mockReturnValue({
+ hasAnyResult: false,
+ isTableOutput: false,
+ isVisualizationOutput: false,
+ containsBinaryData: false,
+ }),
+ },
+ },
+ {
+ provide: ComputingUnitStatusService,
+ useValue: { getSelectedComputingUnit: vi.fn().mockReturnValue(of(null)) },
+ },
+ ],
+ }).compileComponents();
+
+ fixture = TestBed.createComponent(ResultExportationComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it("should create and render", () => {
+ expect(component).toBeTruthy();
+ });
+
+ it("ngOnInit keeps only WRITE-accessible datasets and stores the downloadability", () => {
+ // detectChanges() in beforeEach already ran ngOnInit with its synchronous mock streams.
+ expect(component.userAccessibleDatasets).toEqual([writeDataset]);
+ expect(component.filteredUserAccessibleDatasets).toEqual([writeDataset]);
+ expect(component.downloadability).toBeDefined();
+ expect(component.selectedComputingUnit).toBeNull();
+ });
+
+ it("onClickExportResult('dataset', ds) exports to the dataset destination and closes the modal", () => {
+ component.onClickExportResult("dataset", writeDataset);
+
+ expect(exportWorkflowExecutionResult).toHaveBeenCalledTimes(1);
+ const args = exportWorkflowExecutionResult.mock.calls[0];
+ expect(args[0]).toBe("csv"); // exportType, from modal data
+ expect(args[1]).toBe("my-workflow"); // workflowName
+ expect(args[2]).toEqual([1]); // datasetIds resolved from ds.dataset.did
+ expect(args[6]).toBe(true); // exportAll, because sourceTriggered === "menu"
+ expect(args[7]).toBe("dataset"); // destination
+ expect(modalClose).toHaveBeenCalledTimes(1);
+ });
+
+ it("onClickExportResult('local') exports to local download with no dataset ids", () => {
+ component.onClickExportResult("local");
+
+ expect(exportWorkflowExecutionResult).toHaveBeenCalledTimes(1);
+ const args = exportWorkflowExecutionResult.mock.calls[0];
+ expect(args[2]).toEqual([]); // local download carries no dataset ids
+ expect(args[7]).toBe("local");
+ expect(modalClose).toHaveBeenCalledTimes(1);
+ });
+
+ it("onClickCreateNewDataset opens the dataset-creator modal and adopts the created dataset", () => {
+ const created = {
+ dataset: { did: 9, name: "brand-new" },
+ accessPrivilege: "WRITE",
+ } as unknown as DashboardDataset;
+ modalCreate.mockReturnValue({ afterClose: of(created) });
+
+ component.onClickCreateNewDataset();
+
+ expect(modalCreate).toHaveBeenCalledTimes(1);
+ const config = modalCreate.mock.calls[0][0];
+ expect(config.nzTitle).toBe("Create New Dataset");
+ expect(config.nzContent).toBe(UserDatasetVersionCreatorComponent);
+ // afterClose emitted a dataset, so the component adopts it into its lists.
+ expect(component.userAccessibleDatasets[0]).toBe(created);
+ expect(component.inputDatasetName).toBe("brand-new");
+ });
+});