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 2cd542f613 test(frontend): extend UserWorkflowComponent template 
coverage (#7370)
2cd542f613 is described below

commit 2cd542f613df3a07fccff0fbe97a9a9c012ed2a2
Author: Meng Wang <[email protected]>
AuthorDate: Thu Aug 6 18:37:36 2026 -0700

    test(frontend): extend UserWorkflowComponent template coverage (#7370)
    
    ### What changes were proposed in this PR?
    
    Extends `user-workflow.component.spec.ts` to render the template
    branches the
    existing class-focused tests never reached, taking
    `user-workflow.component.html`
    from ~53% to 100% (104/104; the class was already at 221/221). 11 new
    tests under
    a `template rendering` block:
    
    - **toolbar** — the Create button, the sort-button's `sortMethodChange`,
    and the
      view-type List/Card toggle.
    - **multi-select actions** (rendered only once an entry is checked) —
    Batch
      Select, Download-as-ZIP, Duplicate, and the Delete popconfirm.
    - **project actions** — the add/remove-from-project buttons that appear
    when a
      `pid` is set.
    - **outputs** — the rendered `texera-search-results` outputs (deleted /
    duplicated
    / refresh / notifyWorkflow) and the `#workflowCardTpl` card in card view
    with
    its card-item outputs (deleted / duplicated / refresh /
    checkboxChanged).
    
    Interactions go through the DOM (`By.css` + `triggerEventHandler`); the
    backing
    services are the existing stubs. No production code was changed.
    
    ### Any related issues, documentation, discussions?
    
    Closes #7362.
    
    ### How was this PR tested?
    
    `ng test --watch=false --include
    
src/app/dashboard/component/user/user-workflow/user-workflow.component.spec.ts`
    — 69 passed (58 existing + 11 new), run 3x for determinism. Coverage
    (`--coverage`)
    confirms `user-workflow.component.html` at 104/104 and the class at
    221/221. The
    failure path was verified by breaking an assertion (red, non-zero exit);
    eslint and
    prettier are clean.
    
    ### Was this PR authored or co-authored using generative AI tooling?
    
    Generated-by: Claude Code (Opus 4.8 [1M context])
    
    ---------
    
    Signed-off-by: Xinyuan Lin <[email protected]>
    Co-authored-by: Xinyuan Lin <[email protected]>
    Co-authored-by: Copilot Autofix powered by AI 
<[email protected]>
---
 .../user-workflow/user-workflow.component.spec.ts  | 144 +++++++++++++++++++++
 1 file changed, 144 insertions(+)

diff --git 
a/frontend/src/app/dashboard/component/user/user-workflow/user-workflow.component.spec.ts
 
b/frontend/src/app/dashboard/component/user/user-workflow/user-workflow.component.spec.ts
index fa5c2b3dce..7b71ead98c 100644
--- 
a/frontend/src/app/dashboard/component/user/user-workflow/user-workflow.component.spec.ts
+++ 
b/frontend/src/app/dashboard/component/user/user-workflow/user-workflow.component.spec.ts
@@ -18,6 +18,7 @@
  */
 
 import { ComponentFixture, TestBed } from "@angular/core/testing";
+import { By } from "@angular/platform-browser";
 import { RouterTestingModule } from "@angular/router/testing";
 import { HttpClientTestingModule } from "@angular/common/http/testing";
 import { FormsModule, ReactiveFormsModule } from "@angular/forms";
@@ -1019,5 +1020,148 @@ describe("SavedWorkflowSectionComponent", () => {
         });
       });
     });
+
+    describe("template rendering", () => {
+      const VIEW_MODE_KEY = "texera.userWorkflow.viewMode";
+      const q = (selector: string) => 
fixture.debugElement.query(By.css(selector));
+
+      afterEach(() => {
+        vi.restoreAllMocks();
+        localStorage.removeItem(VIEW_MODE_KEY);
+      });
+      // The multi-select toolbar only renders when at least one entry is 
checked.
+      // Clone a real fixture entry (preserving its prototype) so the rendered
+      // search-results can display it, and flip `checked` on the copy so the 
shared
+      // fixture is not mutated.
+      const renderWithSelection = () => {
+        const source = testWorkflowEntries[0];
+        const checkedEntry = 
Object.assign(Object.create(Object.getPrototypeOf(source)), source);
+        checkedEntry.checked = true;
+        component.searchResultsComponent.entries = [checkedEntry];
+        fixture.detectChanges();
+      };
+
+      it("hides the multi-select actions until an entry is checked", () => {
+        component.searchResultsComponent.entries = [];
+        fixture.detectChanges();
+        expect(q('[title="Batch Select"]')).toBeNull();
+
+        renderWithSelection();
+        expect(q('[title="Batch Select"]')).toBeTruthy();
+      });
+
+      it("wires the Create Workflow button", () => {
+        const spy = vi.spyOn(component, 
"onClickCreateNewWorkflowFromDashboard").mockImplementation(() => {});
+        q(".create-btn").triggerEventHandler("click", null);
+        expect(spy).toHaveBeenCalled();
+      });
+
+      it("re-searches when the sort button changes the sort method", () => {
+        const searchSpy = vi.spyOn(component, 
"search").mockResolvedValue(undefined);
+        q("texera-sort-button").triggerEventHandler("sortMethodChange", 
"NameAsc");
+        expect(component.sortMethod).toBe("NameAsc");
+        expect(searchSpy).toHaveBeenCalled();
+      });
+
+      it("wires the Batch Select button", () => {
+        renderWithSelection();
+        const spy = vi.spyOn(component, 
"toggleSelection").mockImplementation(() => {});
+        q('[title="Batch Select"]').triggerEventHandler("click", null);
+        expect(spy).toHaveBeenCalled();
+      });
+
+      it("wires the Download-as-ZIP button", () => {
+        renderWithSelection();
+        const spy = vi.spyOn(component, 
"onClickOpenDownloadZip").mockResolvedValue(undefined);
+        q('[title="Download added workflow as a ZIP 
file"]').triggerEventHandler("click", null);
+        expect(spy).toHaveBeenCalled();
+      });
+
+      it("wires the Duplicate-selected button", () => {
+        renderWithSelection();
+        const spy = vi.spyOn(component, 
"onClickDuplicateSelectedWorkflows").mockImplementation(() => {});
+        q('[nz-tooltip="Duplicate selected 
workflows"]').triggerEventHandler("click", null);
+        expect(spy).toHaveBeenCalled();
+      });
+
+      it("wires the Delete-selected popconfirm", () => {
+        renderWithSelection();
+        const spy = vi.spyOn(component, 
"handleConfirmDeleteSelectedWorkflows").mockImplementation(() => {});
+        q('[nzPopconfirmTitle="Confirm to delete selected 
workflows."]').triggerEventHandler("nzOnConfirm", null);
+        expect(spy).toHaveBeenCalled();
+      });
+
+      it("shows and wires the project add/remove buttons when a pid is set", 
() => {
+        component.pid = 1;
+        fixture.detectChanges();
+        const addSpy = vi.spyOn(component, 
"onClickOpenAddWorkflow").mockImplementation(() => {});
+        const removeSpy = vi.spyOn(component, 
"onClickOpenRemoveWorkflow").mockImplementation(() => {});
+
+        q('[title="Add workflow(s) to project"]').triggerEventHandler("click", 
null);
+        q('[title="Remove workflow(s) from 
project"]').triggerEventHandler("click", null);
+
+        expect(addSpy).toHaveBeenCalled();
+        expect(removeSpy).toHaveBeenCalled();
+      });
+
+      it("switches the view type through the List/Card buttons", () => {
+        component.viewType = "card";
+        fixture.detectChanges();
+
+        q('[title="List View"]').triggerEventHandler("click", null);
+        expect(component.viewType).toBe("list");
+
+        fixture.detectChanges();
+        q('[title="Card View"]').triggerEventHandler("click", null);
+        expect(component.viewType).toBe("card");
+      });
+
+      it("wires the rendered search-results outputs to the component", () => {
+        const results = 
fixture.debugElement.query(By.directive(SearchResultsComponent));
+        const deleteSpy = vi.spyOn(component, 
"deleteWorkflow").mockImplementation(() => {});
+        const duplicateSpy = vi.spyOn(component, 
"onClickDuplicateWorkflow").mockResolvedValue(undefined);
+        const refreshSpy = vi.spyOn(component, 
"refreshSearchResult").mockImplementation(() => {});
+        const tooltipSpy = vi.spyOn(component, 
"updateTooltip").mockImplementation(() => {});
+        const entry = testWorkflowEntries[0];
+
+        results.triggerEventHandler("deleted", entry);
+        results.triggerEventHandler("duplicated", entry);
+        results.triggerEventHandler("refresh", undefined);
+        results.triggerEventHandler("notifyWorkflow", undefined);
+
+        expect(deleteSpy).toHaveBeenCalledWith(entry);
+        expect(duplicateSpy).toHaveBeenCalledWith(entry);
+        expect(refreshSpy).toHaveBeenCalled();
+        expect(tooltipSpy).toHaveBeenCalled();
+      });
+
+      it("renders the workflow card template in card view and wires its 
outputs", () => {
+        const source = testWorkflowEntries[0];
+        const entry = 
Object.assign(Object.create(Object.getPrototypeOf(source)), source);
+        component.viewType = "card";
+        component.searchResultsComponent.entries = [entry];
+        fixture.detectChanges();
+
+        const card = fixture.debugElement.query(By.css("texera-card-item"));
+        expect(card).toBeTruthy();
+
+        const deleteSpy = vi.spyOn(component, 
"deleteWorkflow").mockImplementation(() => {});
+        const duplicateSpy = vi.spyOn(component, 
"onClickDuplicateWorkflow").mockResolvedValue(undefined);
+        const refreshSpy = vi.spyOn(component, 
"refreshSearchResult").mockImplementation(() => {});
+        const checkboxSpy = vi
+          .spyOn(component.searchResultsComponent, "onEntryCheckboxChange")
+          .mockImplementation(() => {});
+
+        card.triggerEventHandler("deleted", undefined);
+        card.triggerEventHandler("duplicated", undefined);
+        card.triggerEventHandler("refresh", undefined);
+        card.triggerEventHandler("checkboxChanged", undefined);
+
+        expect(deleteSpy).toHaveBeenCalledWith(entry);
+        expect(duplicateSpy).toHaveBeenCalledWith(entry);
+        expect(refreshSpy).toHaveBeenCalled();
+        expect(checkboxSpy).toHaveBeenCalled();
+      });
+    });
   });
 });

Reply via email to