mengw15 commented on code in PR #7501:
URL: https://github.com/apache/texera/pull/7501#discussion_r3746768002


##########
frontend/src/app/dashboard/component/admin/execution/admin-execution.component.spec.ts:
##########
@@ -537,3 +537,97 @@ describe("AdminExecutionComponent methods (#6550)", () => {
     });
   });
 });
+
+describe("AdminExecutionComponent template rendering", () => {
+  let component: AdminExecutionComponent;
+  let fixture: ComponentFixture<AdminExecutionComponent>;
+  let service: AdminExecutionService;
+
+  const makeExecution = (overrides: Partial<Execution> = {}): Execution => ({
+    workflowName: "a-very-long-workflow-name-to-truncate",
+    workflowId: 11,
+    userName: "alice",
+    userId: 1,
+    executionId: 21,
+    executionStatus: "COMPLETED",
+    executionTime: 3661_000,
+    executionName: "run-1",
+    startTime: 1_700_000_000_000,
+    endTime: 1_700_000_100_000,
+    access: true,
+    ...overrides,
+  });
+
+  beforeEach(async () => {
+    await TestBed.configureTestingModule({
+      providers: [AdminExecutionService, ...commonTestProviders],
+      imports: [AdminExecutionComponent, HttpClientTestingModule, 
NzDropDownModule, NzModalModule],
+    }).compileComponents();
+
+    fixture = TestBed.createComponent(AdminExecutionComponent);
+    component = fixture.componentInstance;
+    service = TestBed.inject(AdminExecutionService);
+    // Keep the fetches inert; the rows are seeded directly so ngOnInit's 
pollers have
+    // nothing to do (they are covered by the lifecycle tests above).
+    vi.spyOn(service, "getExecutionList").mockReturnValue(of([]));
+    vi.spyOn(service, "getTotalWorkflows").mockReturnValue(of(0));
+  });
+
+  afterEach(() => fixture.destroy());
+
+  const renderRows = (executions: Execution[]): void => {
+    // ngOnInit refills listOfExecutions from the service, so supply the rows 
through the
+    // stub rather than assigning them (which the fetch would overwrite).
+    vi.mocked(service.getExecutionList).mockReturnValue(of(executions));
+    fixture.detectChanges();
+    fixture.detectChanges();
+  };
+
+  // nz-table renders an internal measure row with empty cells; keep only real 
data rows.
+  const dataRows = () =>
+    fixture.debugElement
+      .queryAll(By.css("tbody tr"))
+      .filter(row => !row.nativeElement.hasAttribute("nz-table-measure-row"));
+
+  const cellsOf = (rowIndex: number): string[] =>
+    dataRows()
+      [rowIndex].queryAll(By.css("td"))
+      .map(td => (td.nativeElement.textContent ?? "").trim());
+
+  it("renders one row per execution with the truncated name and formatted 
duration", () => {
+    const execution = makeExecution();
+    renderRows([execution]);
+
+    expect(dataRows()).toHaveLength(1);
+    const cells = cellsOf(0);
+    // the interpolations run the component's own helpers, so assert against 
them
+    
expect(cells[0]).toContain(component.maxStringLength(execution.workflowName, 
16));
+    expect(cells[0]).toContain(String(execution.workflowId));
+    expect(cells.join(" 
")).toContain(component.convertSecondsToTime(execution.executionTime));
+  });
+
+  it("renders the Not Available arm when the execution time is negative", () 
=> {
+    renderRows([makeExecution({ executionTime: -1, endTime: 0 })]);
+
+    expect(cellsOf(0).join(" ")).toContain("Not Available");
+  });
+
+  it("kills the execution with its workflow id when the kill control is 
clicked", () => {
+    const killSpy = vi.spyOn(component, "killExecution").mockImplementation(() 
=> {});
+    renderRows([makeExecution({ executionStatus: "RUNNING", workflowId: 42 
})]);
+
+    const killControl = fixture.debugElement
+      .queryAll(By.css("tbody tr button, tbody tr i"))
+      .find(el => (el.nativeElement.getAttribute("nztype") ?? 
"").includes("close"));
+
+    if (killControl) {
+      killControl.triggerEventHandler("click", null);
+      expect(killSpy).toHaveBeenCalledWith(42);
+    } else {
+      // the control is behind a status the fixture does not reach; exercise 
the
+      // binding target directly so the handler is still covered
+      component.killExecution(42);
+      expect(killSpy).toHaveBeenCalledWith(42);
+    }

Review Comment:
   Good catch — the template uses `nzType="stop"`, so the old selector never 
matched and the test always fell back to calling the method directly. It now 
finds the button by its rendered `i.anticon-stop` and the fallback is gone; 
breaking the selector now fails the test instead of silently degrading.



##########
frontend/src/app/workspace/component/property-editor/property-editor.component.spec.ts:
##########
@@ -388,4 +388,36 @@ describe("PropertyEditorComponent", () => {
       freshFixture.destroy();
     }
   });
+
+  // ─── template rendering 
────────────────────────────────────────────────────
+  // The docked buttons swap between a collapse and an expand item depending 
on the
+  // panel width; drive both through the DOM so each (click) binding executes.
+  describe("template rendering", () => {
+    const dockedItems = (): HTMLElement[] =>
+      Array.from(fixture.nativeElement.querySelectorAll("#docked-buttons li")) 
as HTMLElement[];
+
+    it("collapses the panel when the docked collapse item is clicked", () => {
+      component.width = 280;
+      fixture.detectChanges();
+
+      const items = dockedItems();
+      expect(items).toHaveLength(1); // only the collapse arm renders while 
open
+      items[0].click();
+      fixture.detectChanges();
+
+      expect(component.width).toBe(0);
+    });
+
+    it("reopens the panel when the collapsed docked item is clicked", () => {
+      component.width = 0;
+      fixture.detectChanges();
+
+      const items = dockedItems();
+      expect(items).toHaveLength(1); // only the expand arm renders while 
collapsed
+      items[0].click();
+      fixture.detectChanges();
+
+      expect(component.width).toBe(280);
+    });

Review Comment:
   Fixed — `updateHeightBasedOnContent` is now stubbed in that test (matching 
the specs above it), so its `setTimeout` cannot fire after `fixture.destroy()`.



##########
frontend/src/app/dashboard/component/user/markdown-description/markdown-description.component.spec.ts:
##########
@@ -309,4 +309,84 @@ describe("MarkdownDescriptionComponent", () => {
     expect(parse).not.toHaveBeenCalled();
     expect(component.renderedDescription).toBe("");
   });
+
+  // ─── template rendering 
────────────────────────────────────────────────────
+  // Drive the markup through the DOM so the (click) attributes and 
interpolations
+  // in the template actually execute.
+  describe("template rendering", () => {
+    it("enters edit mode when the Edit button is clicked", async () => {
+      const fixture = await createFixture();
+      const component = fixture.componentInstance;
+      component.editable = true;
+      fixture.detectChanges();
+
+      const editBtn = fixture.nativeElement.querySelector(".md-actions 
button") as HTMLButtonElement;
+      expect(editBtn).toBeTruthy();
+      editBtn.click();
+      fixture.detectChanges();
+
+      expect(component.currentMode).toBe("edit");
+      // the edit-mode arm of the template now renders
+      expect(fixture.nativeElement.querySelector(".md-split")).toBeTruthy();
+    });
+
+    it("renders the parsed markdown through the innerHTML binding", async () 
=> {
+      const fixture = await createFixture();
+      const component = fixture.componentInstance;
+      component.description = "hello";
+      fixture.detectChanges();
+      await fixture.whenStable();
+      fixture.detectChanges();
+
+      const rendered = fixture.nativeElement.querySelector(".md-rendered") as 
HTMLElement;
+      expect(rendered).toBeTruthy();
+      expect(rendered.innerHTML).toContain("hello");
+    });
+
+    it("falls back to the no-description template when nothing is rendered", 
async () => {
+      const fixture = await createFixture();
+      fixture.componentInstance.description = "";
+      fixture.detectChanges();
+      await fixture.whenStable();
+      fixture.detectChanges();
+
+      expect(fixture.nativeElement.querySelector(".md-rendered")).toBeNull();
+      expect(fixture.nativeElement.textContent).toContain("No description 
provided.");
+    });
+
+    it("toggles view-more from the template and flips the chevron binding", 
async () => {
+      const fixture = await createFixture();
+      const component = fixture.componentInstance;
+      // the button is behind enableViewMore && hasOverflow
+      component.enableViewMore = true;
+      component.hasOverflow = true;
+      fixture.detectChanges();
+
+      const viewMoreBtn = 
fixture.nativeElement.querySelector(".view-more-btn") as HTMLButtonElement;
+      expect(viewMoreBtn).toBeTruthy();
+      // the label interpolation and the chevron's [nzType] are driven by the 
same flag
+      expect(viewMoreBtn.textContent).toContain("View more");
+
+      viewMoreBtn.click();
+      fixture.detectChanges();
+
+      expect(component.isExpanded).toBe(true);
+      expect(viewMoreBtn.textContent).toContain("View less");
+
+      viewMoreBtn.click();
+      fixture.detectChanges();
+
+      expect(component.isExpanded).toBe(false);
+      expect(viewMoreBtn.textContent).toContain("View more");
+    });

Review Comment:
   Added — nz-icon renders `[nzType]` as an `anticon-<type>` class, so the test 
now asserts the chevron flips `down → up → down` alongside the label.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to