mengw15 commented on code in PR #7682:
URL: https://github.com/apache/texera/pull/7682#discussion_r3788416103
##########
frontend/src/app/dashboard/component/user/user-dataset/user-dataset-explorer/dataset-detail.component.spec.ts:
##########
@@ -2050,5 +2109,99 @@ describe("DatasetDetailComponent behavior", () => {
switches[1].triggerEventHandler("ngModelChange", false);
expect(datasetServiceStub.updateDatasetDownloadable).toHaveBeenCalledWith(5);
});
+
+ // ─── contributor management ─────────────────────────────────────────────
+ const contributors = [
+ { name: "Ada", email: "[email protected]", affiliation: "" } as Contributor,
+ { name: "Grace", email: "[email protected]", affiliation: "" } as Contributor,
+ ];
+
+ it("renders a row per contributor with the actions trigger", () => {
+ renderWith({ did: 5, datasetContributors: [...contributors],
userDatasetAccessLevel: "WRITE" });
+
+ const rendered = fixture.nativeElement.textContent ?? "";
+ expect(rendered).toContain("Ada");
+ expect(rendered).toContain("Grace");
+ // each row carries the dropdown trigger that hosts Edit/Delete
+ const triggers = fixture.debugElement
+ .queryAll(By.css("button[nz-dropdown]"))
+ .filter(btn => btn.nativeElement.querySelector("i.anticon-more"));
+ expect(triggers.length).toBe(contributors.length);
+ });
+
+ // Edit/Delete live inside an nz-dropdown-menu, which only mounts into a
CDK overlay on a
+ // real user open — jsdom does not drive that. Assert the handlers those
menu items bind to
+ // instead; the rendered trigger is covered above.
+ it("edits the chosen contributor through the menu's binding target", () =>
{
+ const updated = { ...contributors[0], affiliation: "Lab" };
+ modalServiceStub.create.mockReturnValue({ afterClose: of(updated) });
+ renderWith({ did: 5, datasetContributors: [...contributors],
userDatasetAccessLevel: "WRITE" });
+
+ component.onEditContributor(contributors[0]);
+
+ expect(component.datasetContributors[0]).toEqual(updated);
+ });
+
+ it("deletes the chosen contributor through the popconfirm's binding
target", () => {
+ renderWith({ did: 5, datasetContributors: [...contributors],
userDatasetAccessLevel: "WRITE" });
+
+ component.onDeleteContributor(contributors[0]);
+
+ expect(component.datasetContributors.map(c =>
c.name)).toEqual(["Grace"]);
+ });
+
+ // ─── view controls ──────────────────────────────────────────────────────
+
+ it("downloads the current file from the toolbar", () => {
+ // the toolbar controls are behind *ngIf="selectedVersion"
+ renderWith({ did: 5, selectedVersion: { dvid: 1, name: "v1" } as
DatasetVersion });
+ openTab("Versions & Files");
+ const onDownload = vi.spyOn(component,
"onClickDownloadCurrentFile").mockImplementation(() => {});
+
+ const downloadBtn = fixture.debugElement
+ .queryAll(By.css("button"))
+ .find(btn => btn.nativeElement.querySelector("i.anticon-download"));
+ expect(downloadBtn).toBeTruthy();
+ downloadBtn!.triggerEventHandler("click", null);
+
+ expect(onDownload).toHaveBeenCalled();
+ });
+
+ it("toggles the scaled view from the toolbar", () => {
+ renderWith({ did: 5, isMaximized: false, selectedVersion: { dvid: 1,
name: "v1" } as DatasetVersion });
+ openTab("Versions & Files");
+
+ const scaleBtn = fixture.debugElement
+ .queryAll(By.css("button"))
+ .find(btn => btn.nativeElement.querySelector("i.anticon-expand,
i.anticon-compress"));
+ expect(scaleBtn).toBeTruthy();
+ scaleBtn!.triggerEventHandler("click", null);
+ fixture.detectChanges();
+
+ expect(component.isMaximized).toBe(true);
+ });
+
+ // ─── sider resize ───────────────────────────────────────────────────────
+
+ it("applies the dragged sider width on the next animation frame", async ()
=> {
+ renderWith({ did: 5 });
+
+ component.onSideResize({ width: 321 } as NzResizeEvent);
+ // the handler defers to requestAnimationFrame; let that frame run
+ await new Promise(resolve => requestAnimationFrame(() => resolve(null)));
+
+ expect(component.siderWidth).toBe(321);
+ });
+
+ it("cancels a pending resize frame before scheduling the next one", () => {
+ renderWith({ did: 5 });
+ const cancel = vi.spyOn(globalThis, "cancelAnimationFrame");
+
+ component.onSideResize({ width: 100 } as NzResizeEvent);
+ component.onSideResize({ width: 200 } as NzResizeEvent);
+
+ expect(cancel).toHaveBeenCalled();
+ cancel.mockRestore();
+ });
Review Comment:
Right — the component starts with `id = -1`, so the first `onSideResize`
already called `cancelAnimationFrame` and the assertion couldn't fail. Now
`requestAnimationFrame` is stubbed to hand out id `100`, the spy is cleared
after the first call, and the second call is asserted to cancel `100`. Verified
it now fails if the component stops tracking the frame id (the old assertion
still passed in that case).
--
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]