This is an automated email from the ASF dual-hosted git repository.

github-merge-queue[bot] pushed a commit to branch 
gh-readonly-queue/main/pr-6818-87415b56dbcc8a5b2320ddb0a98ff92c600d95b7
in repository https://gitbox.apache.org/repos/asf/texera.git

commit 58e9e0acab6ca45be362d1ab3acaa3ffa70a189b
Author: Xinyuan Lin <[email protected]>
AuthorDate: Thu Jul 23 17:21:40 2026 -0700

    test(frontend): extend AdminExecutionComponent unit test coverage (#6818)
    
    ### What changes were proposed in this PR?
    
    Extends `admin-execution.component.spec.ts` with 9 tests (30 -> 39),
    covering `getStatusColor`'s remaining status cases
    (READY/PAUSED/FAILED/JUST COMPLETED), `dataCheck`'s name/workflow-change
    and all-unchanged branches, `onSortChange`'s descend->desc path and
    non-active-field no-op, the `filterByStatus` predicate, and the
    `ngOnInit` clock-tick + 5s poll lifecycle plus `ngOnDestroy` interval
    cleanup (via fake timers). Coverage 85% -> 100%. No existing tests
    modified.
    
    ### Any related issues, documentation, discussions?
    
    Closes #6814.
    
    ### How was this PR tested?
    
    `ng test --include='**/admin-execution.component.spec.ts'` -> 39/39
    passing. `yarn format:ci` passes.
    
    ### Was this PR authored or co-authored using generative AI tooling?
    
    Generated-by: Claude Code (Opus 4.8 [1M context])
    
    Co-authored-by: Yicong Huang 
<[email protected]>
---
 .../execution/admin-execution.component.spec.ts    | 120 +++++++++++++++++++++
 1 file changed, 120 insertions(+)

diff --git 
a/frontend/src/app/dashboard/component/admin/execution/admin-execution.component.spec.ts
 
b/frontend/src/app/dashboard/component/admin/execution/admin-execution.component.spec.ts
index 3b8a7e52bd..734123b441 100644
--- 
a/frontend/src/app/dashboard/component/admin/execution/admin-execution.component.spec.ts
+++ 
b/frontend/src/app/dashboard/component/admin/execution/admin-execution.component.spec.ts
@@ -262,6 +262,13 @@ describe("AdminExecutionComponent methods (#6550)", () => {
       expect(component.getStatusColor("SOMETHING_ELSE")).toBe("black");
     });
 
+    it("getStatusColor maps the remaining statuses", () => {
+      expect(component.getStatusColor("READY")).toBe("lightgreen");
+      expect(component.getStatusColor("PAUSED")).toBe("purple");
+      expect(component.getStatusColor("FAILED")).toBe("gray");
+      expect(component.getStatusColor("JUST COMPLETED")).toBe("blue");
+    });
+
     it("convertTimeToTimestamp renders the timestamp via toLocaleString", () 
=> {
       // Assert against the same locale call so the test is 
timezone-independent.
       const expected = new Date(NOW).toLocaleString("en-US", { timeZoneName: 
"short" });
@@ -325,6 +332,23 @@ describe("AdminExecutionComponent methods (#6550)", () => {
       const newFresh = makeExecution({ executionStatus: "COMPLETED", endTime: 
NOW - 2000 });
       expect(component.dataCheck(oldJustCompleted, newFresh)).toBe(false);
     });
+
+    it("dataCheck flags execution-name and workflow-name changes when the 
status is unchanged", () => {
+      const base = makeExecution({ executionStatus: "RUNNING", executionName: 
"e1", workflowName: "w1" });
+
+      const renamedExecution = makeExecution({ executionStatus: "RUNNING", 
executionName: "e2", workflowName: "w1" });
+      expect(component.dataCheck(base, renamedExecution)).toBe(true);
+
+      const renamedWorkflow = makeExecution({ executionStatus: "RUNNING", 
executionName: "e1", workflowName: "w2" });
+      expect(component.dataCheck(base, renamedWorkflow)).toBe(true);
+    });
+
+    it("dataCheck returns false when status, execution name and workflow name 
are all unchanged", () => {
+      const base = makeExecution({ executionStatus: "RUNNING", executionName: 
"e1", workflowName: "w1" });
+      const identical = makeExecution({ executionStatus: "RUNNING", 
executionName: "e1", workflowName: "w1" });
+
+      expect(component.dataCheck(base, identical)).toBe(false);
+    });
   });
 
   describe("data + table", () => {
@@ -368,6 +392,38 @@ describe("AdminExecutionComponent methods (#6550)", () => {
       expect(component.sortField).toBe(NO_SORT);
       expect(component.sortDirection).toBe(NO_SORT);
     });
+
+    it("onSortChange maps a descend order to the 'desc' direction", () => {
+      vi.mocked(service.getExecutionList).mockClear();
+
+      component.onSortChange("workflowName", "descend");
+
+      expect(component.sortField).toBe("workflowName");
+      expect(component.sortDirection).toBe("desc");
+      expect(service.getExecutionList).toHaveBeenCalledTimes(1);
+    });
+
+    it("onSortChange is a no-op when a non-active field is cleared", () => {
+      component.sortField = "executionName";
+      component.sortDirection = "asc";
+      vi.mocked(service.getExecutionList).mockClear();
+
+      // Clearing (sortOrder null) a field that is not the active sort field 
neither
+      // resets the sort nor refetches.
+      component.onSortChange("workflowName", null);
+
+      expect(component.sortField).toBe("executionName");
+      expect(component.sortDirection).toBe("asc");
+      expect(service.getExecutionList).not.toHaveBeenCalled();
+    });
+
+    it("filterByStatus matches only executions whose status contains a 
selected value", () => {
+      const running = makeExecution({ executionStatus: "RUNNING" });
+
+      expect(component.filterByStatus(["RUN"], running)).toBe(true);
+      expect(component.filterByStatus(["FAILED"], running)).toBe(false);
+      expect(component.filterByStatus(["FAILED", "RUNNING"], 
running)).toBe(true);
+    });
   });
 
   describe("execution actions", () => {
@@ -416,4 +472,68 @@ describe("AdminExecutionComponent methods (#6550)", () => {
       });
     });
   });
+
+  describe("lifecycle polling", () => {
+    // The clock tick and background-refresh intervals are hard-coded in the 
component
+    // (1s and 5s respectively); mirror them here to drive the fake timers.
+    const TICK_MS = 1000;
+    const REFRESH_MS = 5000;
+
+    it("ngOnInit loads the current page and starts the 1s clock tick", () => {
+      const firstExec = makeExecution({ workflowId: 5 });
+      vi.mocked(service.getExecutionList).mockReturnValue(of([firstExec]));
+      vi.mocked(service.getTotalWorkflows).mockReturnValue(of(2));
+      const updateSpy = vi.spyOn(component, "updateTimeStatus");
+
+      component.ngOnInit();
+
+      // The initial fetch resolves synchronously (of(...)) and populates the 
view.
+      expect(component.listOfExecutions).toEqual([firstExec]);
+      expect(component.totalWorkflows).toBe(2);
+      expect(component.isLoading).toBe(false);
+
+      updateSpy.mockClear();
+      vi.mocked(service.getExecutionList).mockClear();
+
+      // A clock tick recomputes elapsed time client-side without hitting the 
service.
+      vi.advanceTimersByTime(TICK_MS);
+
+      expect(updateSpy).toHaveBeenCalled();
+      expect(service.getExecutionList).not.toHaveBeenCalled();
+    });
+
+    it("ngOnInit polls the current page every 5s and leaves the total 
untouched when it did not change", () => {
+      vi.mocked(service.getExecutionList).mockReturnValue(of([makeExecution({ 
workflowId: 5 })]));
+      vi.mocked(service.getTotalWorkflows).mockReturnValue(of(2));
+
+      component.ngOnInit();
+      expect(component.totalWorkflows).toBe(2);
+
+      // Next poll returns fresh rows but the same total.
+      const polledExec = makeExecution({ workflowId: 9, executionStatus: 
"RUNNING" });
+      vi.mocked(service.getExecutionList).mockClear();
+      vi.mocked(service.getExecutionList).mockReturnValue(of([polledExec]));
+      vi.mocked(service.getTotalWorkflows).mockReturnValue(of(2));
+
+      vi.advanceTimersByTime(REFRESH_MS);
+
+      expect(service.getExecutionList).toHaveBeenCalledTimes(1);
+      expect(component.listOfExecutions).toEqual([polledExec]);
+      // The total is unchanged, so applyCurrentPage must not reassign/churn 
it.
+      expect(component.totalWorkflows).toBe(2);
+    });
+
+    it("ngOnDestroy clears the clock interval so it stops ticking", () => {
+      vi.mocked(service.getExecutionList).mockReturnValue(of([]));
+      vi.mocked(service.getTotalWorkflows).mockReturnValue(of(0));
+      component.ngOnInit();
+
+      const updateSpy = vi.spyOn(component, "updateTimeStatus");
+      component.ngOnDestroy();
+
+      vi.advanceTimersByTime(TICK_MS * 3);
+
+      expect(updateSpy).not.toHaveBeenCalled();
+    });
+  });
 });

Reply via email to