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]