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 ae17c8fafb test(frontend): render ResultExportationComponent template 
branches for coverage (#7365)
ae17c8fafb is described below

commit ae17c8fafbb213897579a574519004bf0d66ad20
Author: Meng Wang <[email protected]>
AuthorDate: Fri Aug 7 04:24:13 2026 -0700

    test(frontend): render ResultExportationComponent template branches for 
coverage (#7365)
    
    ### What changes were proposed in this PR?
    
    Extends `ResultExportationComponent`'s spec so the **template** actually
    renders
    each of its branches. The class was already fully unit-tested, but the
    existing
    tests drive the class directly and never render most of the dialog,
    leaving its
    `*ngIf` / `*ngFor` / `(click)` / `[(ngModel)]` constructs unexecuted. No
    production code was changed.
    
    6 tests, each putting the component in a state the template switches on
    and then
    calling `detectChanges()`:
    
    - the restricted-export error `nz-alert` (every operator blocked);
    - the partial-skip warning `nz-alert` (some but not all operators
    blocked);
    - the export-type `nz-select` and its output-gated `nz-option` arms
      (`isTableOutput` / `isVisualizationOutput` / `containsBinaryData`);
    - the filename input when `exportType === "data"`;
    - the local **Export** button — driven through the DOM
    (`triggerEventHandler("click")`) and asserting the export service double
    was
      called with the `local` destination;
    - the dataset destination — the search input's `(input)` handler, the
    `*ngFor` dataset list, and the **Create New Dataset** button (asserts
    the
      modal-service double opens the creator).
    
    Per the component's determinism notes: no fake timers, no
    layout/geometry
    assertions, and no timezone-sensitive date assertions — the tests assert
    on
    rendered text, element presence, and the injected service doubles.
    
    ### Any related issues, documentation, discussions?
    
    Closes #7361
    
    ### How was this PR tested?
    
    Extended 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
    # Test Files 1 passed (1) | Tests 26 passed (26)
    prettier --write <spec>   # clean
    eslint  <spec>            # 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           | 107 +++++++++++++++++++++
 1 file changed, 107 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
index 53d0aeb13e..47c84273f4 100644
--- 
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
@@ -18,6 +18,7 @@
  */
 
 import { ComponentFixture, TestBed } from "@angular/core/testing";
+import { By } from "@angular/platform-browser";
 import { of } from "rxjs";
 import { ResultExportationComponent } from "./result-exportation.component";
 import {
@@ -367,6 +368,112 @@ describe("ResultExportationComponent", () => {
     expect(component.userAccessibleDatasets).toBe(before);
     expect(component.inputDatasetName).toBe(nameBefore);
   });
+
+  // Renders the template in each of the states it switches on so the *ngIf / 
*ngFor /
+  // (click) / [(ngModel)] constructs actually execute. detectChanges() is the 
coverage switch.
+  describe("template rendering", () => {
+    function setAllOperators(ids: string[]): void {
+      const graph = TestBed.inject(WorkflowActionService) as unknown as {
+        getTexeraGraph: ReturnType<typeof vi.fn>;
+      };
+      graph.getTexeraGraph.mockReturnValue({
+        getAllOperators: () => ids.map(id => ({ operatorID: id })),
+      });
+    }
+
+    function restrict(entries: Record<string, string[]>): void {
+      const map = new Map<string, Set<string>>();
+      Object.entries(entries).forEach(([op, labels]) => map.set(op, new 
Set(labels)));
+      component.downloadability = new WorkflowResultDownloadability(map);
+    }
+
+    it("renders the restricted-export error alert when every operator is 
blocked", () => {
+      setAllOperators(["op-a"]);
+      restrict({ "op-a": ["Sales ([email protected])"] });
+      fixture.detectChanges();
+
+      expect(component.isExportRestricted).toBe(true);
+      const alert = fixture.debugElement.query(By.css("nz-alert"));
+      expect(alert).toBeTruthy();
+      expect(alert.nativeElement.textContent).toContain("Export unavailable");
+    });
+
+    it("renders the partial-skip warning alert when only some operators are 
blocked", () => {
+      setAllOperators(["op-a", "op-b"]);
+      restrict({ "op-a": ["Sales ([email protected])"] });
+      fixture.detectChanges();
+
+      expect(component.hasPartialNonDownloadable).toBe(true);
+      expect(fixture.nativeElement.textContent).toContain("Some operators will 
be skipped");
+    });
+
+    it("renders the export-type select and its output-gated options when 
export is allowed", () => {
+      setAllOperators(["op-a"]);
+      restrict({}); // nothing blocked -> not restricted
+      component.exportType = "csv"; // != "data"
+      component.isTableOutput = true;
+      component.isVisualizationOutput = true;
+      component.containsBinaryData = false;
+      fixture.detectChanges();
+
+      expect(component.isExportRestricted).toBe(false);
+      
expect(fixture.debugElement.query(By.css("#exportTypeInput"))).toBeTruthy();
+    });
+
+    it("renders the filename input when the export type is 'data'", () => {
+      setAllOperators(["op-a"]);
+      restrict({});
+      component.exportType = "data";
+      fixture.detectChanges();
+
+      
expect(fixture.debugElement.query(By.css("#filenameInput"))).toBeTruthy();
+    });
+
+    it("renders the local Export button and exports on click", () => {
+      setAllOperators(["op-a"]);
+      restrict({});
+      component.destination = "local";
+      fixture.detectChanges();
+
+      const exportBtn = fixture.debugElement
+        .queryAll(By.css("button"))
+        .find(btn => btn.nativeElement.textContent.trim() === "Export");
+      expect(exportBtn).toBeTruthy();
+
+      exportBtn!.triggerEventHandler("click", null);
+      expect(exportWorkflowExecutionResult).toHaveBeenCalledTimes(1);
+      const args = exportWorkflowExecutionResult.mock.calls[0];
+      expect(args[7]).toBe("local");
+    });
+
+    it("renders the dataset destination with its list and create button", () 
=> {
+      setAllOperators(["op-a"]);
+      restrict({});
+      component.destination = "dataset";
+      fixture.detectChanges();
+
+      // the dataset search input drives the (input) handler
+      const search = 
fixture.debugElement.query(By.css("input[name='datasetName']"));
+      expect(search).toBeTruthy();
+      search.triggerEventHandler("input", { target: { value: "" } });
+
+      // The nz-auto-option list the *ngFor drives is bound to
+      // `filteredUserAccessibleDatasets`; assert the component fed it exactly 
the
+      // WRITE dataset (the READ one is filtered out), so the list branch has 
real data
+      // behind it. The option content itself only enters the DOM once the 
autocomplete
+      // panel expands, which jsdom does not drive, so it is not asserted here.
+      expect(component.filteredUserAccessibleDatasets.map(d => 
d.dataset.name)).toEqual(["writable"]);
+
+      // the create-new-dataset button opens the creator modal
+      const createBtn = fixture.debugElement
+        .queryAll(By.css("button"))
+        .find(btn => btn.nativeElement.textContent.includes("Create New 
Dataset"));
+      expect(createBtn).toBeTruthy();
+
+      createBtn!.triggerEventHandler("click", null);
+      expect(modalCreate).toHaveBeenCalledTimes(1);
+    });
+  });
 });
 
 describe("ResultExportationComponent (context-menu source with default modal 
data)", () => {

Reply via email to