diff --git a/frontend/src/app/dashboard/component/user/files-uploader/files-uploader.component.spec.ts b/frontend/src/app/dashboard/component/user/files-uploader/files-uploader.component.spec.ts index 43398d7efa4..88f04e2efcc 100644 --- a/frontend/src/app/dashboard/component/user/files-uploader/files-uploader.component.spec.ts +++ b/frontend/src/app/dashboard/component/user/files-uploader/files-uploader.component.spec.ts @@ -219,4 +219,43 @@ describe("FilesUploaderComponent", () => { expect(component.fileUploadBannerType).toBe("warning"); expect(component.fileUploadBannerMessage).toBe("heads up"); }); + + describe("force-restart, owner/name, and conflict prompts", () => { + type Item = { name: string; file: { size: number }; restart?: boolean }; + const asComp = () => + component as unknown as { + markForceRestart: (i: Item) => void; + getOwnerAndName: () => { ownerEmail: string; datasetName: string }; + askResumeOrSkip: (i: Item, showForAll: boolean) => Promise; + askUploadOrSkip: (i: Item, showForAll: boolean) => Promise; + }; + const lastFooter = () => + (modals[modals.length - 1] as unknown as { nzFooter: Array<{ label: string; onClick: () => void }> }).nzFooter; + + it("markForceRestart flags the item for a forced restart", () => { + const item: Item = { name: "f.csv", file: { size: 1 }, restart: false }; + asComp().markForceRestart(item); + expect(item.restart).toBe(true); + }); + + it("getOwnerAndName returns the owner email and dataset name inputs", () => { + expect(asComp().getOwnerAndName()).toEqual({ ownerEmail: "owner@example.com", datasetName: "dataset" }); + }); + + it("askResumeOrSkip resolves with the clicked footer action", async () => { + const promise = asComp().askResumeOrSkip({ name: "owner/data/f.csv", file: { size: 100 } }, true); + lastFooter() + .find(b => b.label === "Resume")! + .onClick(); + await expect(promise).resolves.toBe("resume"); + }); + + it("askUploadOrSkip resolves with the clicked footer action", async () => { + const promise = asComp().askUploadOrSkip({ name: "owner/data/f.csv", file: { size: 100 } }, false); + lastFooter() + .find(b => b.label === "Skip")! + .onClick(); + await expect(promise).resolves.toBe("skip"); + }); + }); }); diff --git a/frontend/src/app/dashboard/component/user/list-item/list-item.component.spec.ts b/frontend/src/app/dashboard/component/user/list-item/list-item.component.spec.ts index eca823a4ee7..3e7bc25c0c7 100644 --- a/frontend/src/app/dashboard/component/user/list-item/list-item.component.spec.ts +++ b/frontend/src/app/dashboard/component/user/list-item/list-item.component.spec.ts @@ -350,4 +350,74 @@ describe("ListItemComponent", () => { expect(component.viewCount).toBe(5); // 4 + 1 }); }); + + describe("editing state, markdown preview, and updateProperty", () => { + const asComp = () => + component as unknown as { + setEditingState: (p: "name" | "description", s: boolean) => void; + renderMarkdownPreview: (t: string | undefined) => void; + updateProperty: ( + m: (id: number, v: string) => unknown, + p: "name" | "description", + v: string, + o: string | undefined + ) => void; + }; + + it("setEditingState toggles the name / description edit flags", () => { + asComp().setEditingState("name", true); + expect(component.editingName).toBe(true); + asComp().setEditingState("description", true); + expect(component.editingDescription).toBe(true); + asComp().setEditingState("name", false); + expect(component.editingName).toBe(false); + }); + + it("renderMarkdownPreview strips markdown syntax and collapses whitespace", () => { + asComp().renderMarkdownPreview("# **Hello** [link](http://x)"); + expect(component.renderedDescription).toBe("Hello link"); + asComp().renderMarkdownPreview(" "); + expect(component.renderedDescription).toBe(""); + asComp().renderMarkdownPreview(undefined); + expect(component.renderedDescription).toBe(""); + }); + + it("updateProperty reports a missing id and skips the update", () => { + const errorSpy = vi.spyOn(TestBed.inject(NotificationService), "error"); + component.entry = { name: "x", type: "workflow" } as unknown as DashboardEntry; + const updateMethod = vi.fn(); + + asComp().updateProperty(updateMethod, "name", "new", "old"); + + expect(errorSpy).toHaveBeenCalledWith("Id is missing"); + expect(updateMethod).not.toHaveBeenCalled(); + }); + + it("updateProperty persists the new value and exits edit mode on success", () => { + component.entry = { id: 1, name: "old", type: "workflow" } as unknown as DashboardEntry; + component.editingName = true; + const updateMethod = vi.fn(() => of({})); + + asComp().updateProperty(updateMethod, "name", "new-name", "old"); + + expect(updateMethod).toHaveBeenCalledWith(1, "new-name"); + expect(component.entry.name).toBe("new-name"); + expect(component.editingName).toBe(false); + }); + + it("updateProperty reverts to the original value and exits edit mode on error", () => { + const errorSpy = vi.spyOn(TestBed.inject(NotificationService), "error"); + // The name field is two-way bound, so entry.name already holds the edited + // value by the time updateProperty runs; the error handler must roll it back. + component.entry = { id: 1, name: "new-name", type: "workflow" } as unknown as DashboardEntry; + component.editingName = true; + const updateMethod = vi.fn(() => throwError(() => new Error("boom"))); + + asComp().updateProperty(updateMethod, "name", "new-name", "old"); + + expect(component.entry.name).toBe("old"); + expect(errorSpy).toHaveBeenCalled(); + expect(component.editingName).toBe(false); + }); + }); });