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();
+ });
+ });
});
});