Copilot commented on code in PR #7336:
URL: https://github.com/apache/texera/pull/7336#discussion_r3720189258


##########
frontend/src/app/dashboard/component/user/list-item/card-item/card-item.component.spec.ts:
##########
@@ -894,4 +895,153 @@ describe("CardItemComponent", () => {
       expect(component.likeCount).toBe(0);
     });
   });
+
+  describe("template rendering", () => {
+    const stop = { stopPropagation: () => {} };
+
+    it("renders the full private-search action set for an owned workflow (and 
no like button)", () => {
+      component.entry = makeWorkflowEntry();
+      component.isPrivateSearch = true;
+      component.currentUid = 1;
+      fixture.detectChanges();
+
+      const de = fixture.debugElement;
+      expect(de.query(By.css(".card-checkbox"))).toBeTruthy();
+      expect(de.query(By.css(".edit-btn"))).toBeTruthy();
+      expect(de.query(By.css('button[title="Detail"]'))).toBeTruthy();
+      expect(de.query(By.css('button[title="Share"]'))).toBeTruthy();
+      expect(de.query(By.css('button[title="Copy"]'))).toBeTruthy();
+      expect(de.query(By.css('button[title="Download"]'))).toBeTruthy();
+      expect(de.query(By.css(".delete-btn"))).toBeTruthy();
+      // the like button is only rendered in non-private mode
+      expect(de.query(By.css(".like-btn"))).toBeNull();
+    });
+
+    it("wires each private-search action click to its handler / output", () => 
{
+      component.entry = makeWorkflowEntry();
+      component.isPrivateSearch = true;
+      component.currentUid = 1;
+      fixture.detectChanges();
+      const de = fixture.debugElement;
+
+      const detailSpy = vi.spyOn(component, 
"openDetailModal").mockImplementation(() => {});
+      const shareSpy = vi.spyOn(component, 
"onClickOpenShareAccess").mockImplementation(async () => {});
+      const downloadSpy = vi.spyOn(component, 
"onClickDownload").mockImplementation(async () => {});
+      let duplicated = false;
+      component.duplicated.subscribe(() => (duplicated = true));
+      let deleted = false;
+      component.deleted.subscribe(() => (deleted = true));
+
+      de.query(By.css('button[title="Detail"]')).triggerEventHandler("click", 
stop);
+      de.query(By.css('button[title="Share"]')).triggerEventHandler("click", 
stop);
+      
de.query(By.css('button[title="Download"]')).triggerEventHandler("click", stop);
+      de.query(By.css('button[title="Copy"]')).triggerEventHandler("click", 
stop);
+      de.query(By.css(".delete-btn")).triggerEventHandler("nzOnConfirm", 
undefined);
+
+      expect(detailSpy).toHaveBeenCalled();
+      expect(shareSpy).toHaveBeenCalled();
+      expect(downloadSpy).toHaveBeenCalled();
+      expect(duplicated).toBe(true);
+      expect(deleted).toBe(true);
+    });
+
+    it("enters name-editing mode from the edit button and swaps the display 
for the input", () => {
+      component.entry = makeWorkflowEntry();
+      component.isPrivateSearch = true;
+      fixture.detectChanges();
+
+      // Trigger via the DOM; onEditName sets editingName synchronously (its 
setTimeout
+      // focus callback never runs in this synchronous test — no fake timers 
needed).
+      
fixture.debugElement.query(By.css(".edit-btn")).triggerEventHandler("click", 
stop);
+      expect(component.editingName).toBe(true);
+
+      fixture.detectChanges();
+      
expect(fixture.debugElement.query(By.css(".resource-name-edit-input"))).toBeTruthy();
+      expect(fixture.debugElement.query(By.css(".resource-name"))).toBeNull();
+
+      // pressing Enter in the edit input confirms the rename (real key event 
so the
+      // Angular keydown.enter binding fires through its event plugin)
+      const confirmSpy = vi.spyOn(component, 
"confirmUpdateCustomName").mockImplementation(() => {});
+      const editInput = 
fixture.debugElement.query(By.css(".resource-name-edit-input"))
+        .nativeElement as HTMLInputElement;
+      editInput.dispatchEvent(new KeyboardEvent("keydown", { key: "Enter" }));
+      expect(confirmSpy).toHaveBeenCalled();
+    });
+
+    it("renders and wires the cover-image controls when the cover is 
editable", () => {
+      component.entry = makeWorkflowEntry();
+      component.isPrivateSearch = true;
+      // hasCustomImage is driven by the private customImage field.
+      (component as unknown as { customImage?: string }).customImage = 
"http://example.com/cover.png";;

Review Comment:
   This test reaches into a private implementation detail (`customImage`) via 
an `unknown` cast. That makes the spec brittle against internal refactors 
(e.g., renaming the field, changing how `hasCustomImage` is derived) while 
still keeping the public behavior the same. Prefer setting up state through a 
public input/API or test fixture data that the component already consumes 
(e.g., configuring the entry so the component naturally computes 
`hasCustomImage`), or providing a dedicated test-only helper/setup path in the 
spec that doesn't depend on private fields.



##########
frontend/src/app/dashboard/component/user/list-item/card-item/card-item.component.spec.ts:
##########
@@ -894,4 +895,153 @@ describe("CardItemComponent", () => {
       expect(component.likeCount).toBe(0);
     });
   });
+
+  describe("template rendering", () => {
+    const stop = { stopPropagation: () => {} };
+
+    it("renders the full private-search action set for an owned workflow (and 
no like button)", () => {
+      component.entry = makeWorkflowEntry();
+      component.isPrivateSearch = true;
+      component.currentUid = 1;
+      fixture.detectChanges();
+
+      const de = fixture.debugElement;
+      expect(de.query(By.css(".card-checkbox"))).toBeTruthy();
+      expect(de.query(By.css(".edit-btn"))).toBeTruthy();
+      expect(de.query(By.css('button[title="Detail"]'))).toBeTruthy();
+      expect(de.query(By.css('button[title="Share"]'))).toBeTruthy();
+      expect(de.query(By.css('button[title="Copy"]'))).toBeTruthy();
+      expect(de.query(By.css('button[title="Download"]'))).toBeTruthy();
+      expect(de.query(By.css(".delete-btn"))).toBeTruthy();
+      // the like button is only rendered in non-private mode
+      expect(de.query(By.css(".like-btn"))).toBeNull();
+    });
+
+    it("wires each private-search action click to its handler / output", () => 
{
+      component.entry = makeWorkflowEntry();
+      component.isPrivateSearch = true;
+      component.currentUid = 1;
+      fixture.detectChanges();
+      const de = fixture.debugElement;
+
+      const detailSpy = vi.spyOn(component, 
"openDetailModal").mockImplementation(() => {});
+      const shareSpy = vi.spyOn(component, 
"onClickOpenShareAccess").mockImplementation(async () => {});
+      const downloadSpy = vi.spyOn(component, 
"onClickDownload").mockImplementation(async () => {});
+      let duplicated = false;
+      component.duplicated.subscribe(() => (duplicated = true));
+      let deleted = false;
+      component.deleted.subscribe(() => (deleted = true));
+
+      de.query(By.css('button[title="Detail"]')).triggerEventHandler("click", 
stop);
+      de.query(By.css('button[title="Share"]')).triggerEventHandler("click", 
stop);
+      
de.query(By.css('button[title="Download"]')).triggerEventHandler("click", stop);
+      de.query(By.css('button[title="Copy"]')).triggerEventHandler("click", 
stop);
+      de.query(By.css(".delete-btn")).triggerEventHandler("nzOnConfirm", 
undefined);
+
+      expect(detailSpy).toHaveBeenCalled();
+      expect(shareSpy).toHaveBeenCalled();
+      expect(downloadSpy).toHaveBeenCalled();
+      expect(duplicated).toBe(true);
+      expect(deleted).toBe(true);
+    });
+
+    it("enters name-editing mode from the edit button and swaps the display 
for the input", () => {
+      component.entry = makeWorkflowEntry();
+      component.isPrivateSearch = true;
+      fixture.detectChanges();
+
+      // Trigger via the DOM; onEditName sets editingName synchronously (its 
setTimeout
+      // focus callback never runs in this synchronous test — no fake timers 
needed).
+      
fixture.debugElement.query(By.css(".edit-btn")).triggerEventHandler("click", 
stop);

Review Comment:
   This test explicitly notes that `onEditName` schedules a `setTimeout` focus 
callback. Even if assertions are synchronous, the timer can still fire later 
during/after the test and introduce flakiness (e.g., focusing an element after 
teardown or affecting later tests). To keep the suite deterministic, consider 
using fake timers and flushing/clearing pending timers within the test (or 
stubbing the timer) so no asynchronous work leaks past the test boundary.



##########
frontend/src/app/dashboard/component/user/list-item/card-item/card-item.component.spec.ts:
##########
@@ -894,4 +895,153 @@ describe("CardItemComponent", () => {
       expect(component.likeCount).toBe(0);
     });
   });
+
+  describe("template rendering", () => {
+    const stop = { stopPropagation: () => {} };

Review Comment:
   The mocked event object only provides `stopPropagation()`. If any of the 
click handlers call other common event APIs (e.g., `preventDefault()`), these 
tests will start failing with runtime errors unrelated to the intended 
behavior. Using a real `MouseEvent` (or expanding the mock to include the 
commonly used members) would make the interaction tests more robust.



##########
frontend/src/app/dashboard/component/user/list-item/card-item/card-item.component.spec.ts:
##########
@@ -894,4 +895,153 @@ describe("CardItemComponent", () => {
       expect(component.likeCount).toBe(0);
     });
   });
+
+  describe("template rendering", () => {
+    const stop = { stopPropagation: () => {} };
+
+    it("renders the full private-search action set for an owned workflow (and 
no like button)", () => {
+      component.entry = makeWorkflowEntry();
+      component.isPrivateSearch = true;
+      component.currentUid = 1;
+      fixture.detectChanges();
+
+      const de = fixture.debugElement;
+      expect(de.query(By.css(".card-checkbox"))).toBeTruthy();
+      expect(de.query(By.css(".edit-btn"))).toBeTruthy();
+      expect(de.query(By.css('button[title="Detail"]'))).toBeTruthy();
+      expect(de.query(By.css('button[title="Share"]'))).toBeTruthy();
+      expect(de.query(By.css('button[title="Copy"]'))).toBeTruthy();
+      expect(de.query(By.css('button[title="Download"]'))).toBeTruthy();
+      expect(de.query(By.css(".delete-btn"))).toBeTruthy();
+      // the like button is only rendered in non-private mode
+      expect(de.query(By.css(".like-btn"))).toBeNull();
+    });
+
+    it("wires each private-search action click to its handler / output", () => 
{
+      component.entry = makeWorkflowEntry();
+      component.isPrivateSearch = true;
+      component.currentUid = 1;
+      fixture.detectChanges();
+      const de = fixture.debugElement;
+
+      const detailSpy = vi.spyOn(component, 
"openDetailModal").mockImplementation(() => {});
+      const shareSpy = vi.spyOn(component, 
"onClickOpenShareAccess").mockImplementation(async () => {});
+      const downloadSpy = vi.spyOn(component, 
"onClickDownload").mockImplementation(async () => {});
+      let duplicated = false;
+      component.duplicated.subscribe(() => (duplicated = true));
+      let deleted = false;
+      component.deleted.subscribe(() => (deleted = true));
+
+      de.query(By.css('button[title="Detail"]')).triggerEventHandler("click", 
stop);
+      de.query(By.css('button[title="Share"]')).triggerEventHandler("click", 
stop);
+      
de.query(By.css('button[title="Download"]')).triggerEventHandler("click", stop);
+      de.query(By.css('button[title="Copy"]')).triggerEventHandler("click", 
stop);
+      de.query(By.css(".delete-btn")).triggerEventHandler("nzOnConfirm", 
undefined);

Review Comment:
   These lines call `triggerEventHandler` on the result of `query(...)` without 
asserting the element exists first. If the selector stops matching, the test 
will fail with a less-informative null dereference rather than clearly 
indicating which element was missing. Consider capturing each DebugElement into 
a const and asserting it’s truthy (or using a helper that throws with a 
descriptive message) before triggering the event.



-- 
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