This is an automated email from the ASF dual-hosted git repository.

github-merge-queue[bot] pushed a commit to branch 
gh-readonly-queue/main/pr-5233-d5bc8b7cf8d2e515280934a9c3e52c305fe3c002
in repository https://gitbox.apache.org/repos/asf/texera.git

commit da64ffb3c921be4608a22bd68109f9649e3cdb1b
Author: Matthew B. <[email protected]>
AuthorDate: Tue May 26 21:49:46 2026 -0700

    test: cover ShareAccessComponent behaviors in spec (#5233)
    
    ### What changes were proposed in this PR?
    - Expand `share-access.component.spec.ts` from 4 routing-only tests to
    44 tests grouped by behavior: `ngOnInit`, `handleInputConfirm`,
    `onPaste`, `grantAccess`, `hasWriteAccess`,
    `verifyRevokeAccess`/`revokeAccess`, `changeAccessLevel`,
    `verifyPublish`/`verifyUnpublish`, and the individual publish/unpublish
    methods.
    - Drive modal-confirmation flows by capturing the config passed to
    `NzModalService.create` and invoking the relevant `nzFooter` button's
    `onClick`, so each test asserts against the specific Confirm/Cancel path
    it cares about.
    - Parameterize the `setupComponent` helper with `type`, `id`,
    `inWorkspace`, and `currentEmail` overrides, and reset the TestBed in
    `beforeEach` so each test reconfigures providers (e.g., a WRITE access
    entry for `hasWriteAccess`, an already-published item for
    `verifyPublish`) without bleed-over.
    ### Any related issues, documentation, or discussions?
    Closes: #5223
    ### How was this PR tested?
    - Ran `yarn format:fix` (touched the new spec only).
    - Ran `yarn test
    
--include="src/app/dashboard/component/user/share-access/share-access.component.spec.ts"`:
    44/44 passing.
    ### Was this PR authored or co-authored using generative AI tooling?
    Co-authored with Claude Opus 4.7 in compliance with ASF
---
 .../share-access/share-access.component.spec.ts    | 473 +++++++++++++++++++--
 1 file changed, 438 insertions(+), 35 deletions(-)

diff --git 
a/frontend/src/app/dashboard/component/user/share-access/share-access.component.spec.ts
 
b/frontend/src/app/dashboard/component/user/share-access/share-access.component.spec.ts
index 9227790988..2a40a06e20 100644
--- 
a/frontend/src/app/dashboard/component/user/share-access/share-access.component.spec.ts
+++ 
b/frontend/src/app/dashboard/component/user/share-access/share-access.component.spec.ts
@@ -20,7 +20,8 @@
 import { TestBed } from "@angular/core/testing";
 import { HttpClientTestingModule } from "@angular/common/http/testing";
 import { NoopAnimationsModule } from "@angular/platform-browser/animations";
-import { of } from "rxjs";
+import { HttpErrorResponse } from "@angular/common/http";
+import { of, throwError } from "rxjs";
 
 import { NZ_MODAL_DATA, NzModalRef, NzModalService } from 
"ng-zorro-antd/modal";
 import { NzMessageService } from "ng-zorro-antd/message";
@@ -33,41 +34,62 @@ import { NotificationService } from 
"../../../../common/service/notification/not
 import { DatasetService } from "../../../service/user/dataset/dataset.service";
 import { WorkflowPersistService } from 
"src/app/common/service/workflow-persist/workflow-persist.service";
 import { WorkflowActionService } from 
"src/app/workspace/service/workflow-graph/model/workflow-action.service";
+import { Privilege } from "../../../type/share-access.interface";
 
-describe("ShareAccessComponent.grantAccess", () => {
+interface SetupOptions {
+  type?: string;
+  id?: number;
+  inWorkspace?: boolean;
+  currentEmail?: string | undefined;
+}
+
+describe("ShareAccessComponent", () => {
   let gmailSpy: { sendEmail: ReturnType<typeof vi.fn> };
   let accessServiceSpy: {
     grantAccess: ReturnType<typeof vi.fn>;
     getAccessList: ReturnType<typeof vi.fn>;
     getOwner: ReturnType<typeof vi.fn>;
+    revokeAccess: ReturnType<typeof vi.fn>;
+  };
+  let notificationSpy: { success: ReturnType<typeof vi.fn>; error: 
ReturnType<typeof vi.fn> };
+  let messageSpy: { error: ReturnType<typeof vi.fn> };
+  let modalRefSpy: { close: ReturnType<typeof vi.fn> };
+  let modalServiceSpy: { create: ReturnType<typeof vi.fn> };
+  let workflowPersistSpy: {
+    getWorkflowIsPublished: ReturnType<typeof vi.fn>;
+    updateWorkflowIsPublished: ReturnType<typeof vi.fn>;
   };
+  let datasetServiceSpy: {
+    getDataset: ReturnType<typeof vi.fn>;
+    updateDatasetPublicity: ReturnType<typeof vi.fn>;
+  };
+  let workflowActionSpy: { setWorkflowIsPublished: ReturnType<typeof vi.fn> };
+  let userServiceCurrentEmail: string | undefined;
+  let capturedModalConfigs: any[];
+
+  function setupComponent(opts: SetupOptions = {}): ShareAccessComponent {
+    const { type = "workflow", id = 1, inWorkspace = false, currentEmail = 
"[email protected]" } = opts;
+    userServiceCurrentEmail = currentEmail;
 
-  function setupComponent(type: string, id: number): ShareAccessComponent {
     TestBed.configureTestingModule({
       imports: [HttpClientTestingModule, NoopAnimationsModule, 
ShareAccessComponent],
       providers: [
-        { provide: NZ_MODAL_DATA, useValue: { type, id, allOwners: [], 
inWorkspace: false } },
+        { provide: NZ_MODAL_DATA, useValue: { type, id, allOwners: [], 
inWorkspace } },
         { provide: ShareAccessService, useValue: accessServiceSpy },
         {
           provide: UserService,
-          useValue: { getCurrentUser: () => ({ email: "[email protected]" }) },
-        },
-        { provide: GmailService, useValue: gmailSpy },
-        { provide: NotificationService, useValue: { success: vi.fn(), error: 
vi.fn() } },
-        { provide: NzMessageService, useValue: { error: vi.fn() } },
-        { provide: NzModalService, useValue: {} },
-        { provide: NzModalRef, useValue: { close: vi.fn() } },
-        {
-          provide: WorkflowPersistService,
-          useValue: { getWorkflowIsPublished: 
vi.fn().mockReturnValue(of("Private")) },
-        },
-        {
-          provide: DatasetService,
           useValue: {
-            getDataset: vi.fn().mockReturnValue(of({ dataset: { isPublic: 
false } })),
+            getCurrentUser: () => (userServiceCurrentEmail ? { email: 
userServiceCurrentEmail } : undefined),
           },
         },
-        { provide: WorkflowActionService, useValue: {} },
+        { provide: GmailService, useValue: gmailSpy },
+        { provide: NotificationService, useValue: notificationSpy },
+        { provide: NzMessageService, useValue: messageSpy },
+        { provide: NzModalService, useValue: modalServiceSpy },
+        { provide: NzModalRef, useValue: modalRefSpy },
+        { provide: WorkflowPersistService, useValue: workflowPersistSpy },
+        { provide: DatasetService, useValue: datasetServiceSpy },
+        { provide: WorkflowActionService, useValue: workflowActionSpy },
       ],
     });
     const fixture = TestBed.createComponent(ShareAccessComponent);
@@ -76,37 +98,418 @@ describe("ShareAccessComponent.grantAccess", () => {
   }
 
   beforeEach(() => {
+    TestBed.resetTestingModule();
+    capturedModalConfigs = [];
     gmailSpy = { sendEmail: vi.fn() };
     accessServiceSpy = {
       grantAccess: vi.fn().mockReturnValue(of(null)),
       getAccessList: vi.fn().mockReturnValue(of([])),
       getOwner: vi.fn().mockReturnValue(of("[email protected]")),
+      revokeAccess: vi.fn().mockReturnValue(of(null)),
+    };
+    notificationSpy = { success: vi.fn(), error: vi.fn() };
+    messageSpy = { error: vi.fn() };
+    modalRefSpy = { close: vi.fn() };
+    modalServiceSpy = {
+      create: vi.fn().mockImplementation((config: any) => {
+        capturedModalConfigs.push(config);
+        return { close: vi.fn() };
+      }),
+    };
+    workflowPersistSpy = {
+      getWorkflowIsPublished: vi.fn().mockReturnValue(of("Private")),
+      updateWorkflowIsPublished: vi.fn().mockReturnValue(of(null)),
+    };
+    datasetServiceSpy = {
+      getDataset: vi.fn().mockReturnValue(of({ dataset: { isPublic: false } 
})),
+      updateDatasetPublicity: vi.fn().mockReturnValue(of(null)),
     };
+    workflowActionSpy = { setWorkflowIsPublished: vi.fn() };
   });
 
-  function grantAndCaptureMessage(c: ShareAccessComponent): string {
-    c.emailTags = ["[email protected]"];
-    c.grantAccess();
-    return gmailSpy.sendEmail.mock.calls[0][1] as string;
+  function getFooterButton(config: any, label: string): { onClick: () => void 
} {
+    return config.nzFooter.find((b: any) => b.label === label);
   }
 
-  it("uses the workflow dashboard path when sharing a workflow", () => {
-    const message = grantAndCaptureMessage(setupComponent("workflow", 11));
-    expect(message).toContain("/dashboard/user/workflow/11");
+  describe("ngOnInit", () => {
+    it("loads access list and owner from ShareAccessService", () => {
+      const accessList = [{ email: "[email protected]", name: "A", privilege: 
Privilege.READ }];
+      accessServiceSpy.getAccessList.mockReturnValue(of(accessList));
+      accessServiceSpy.getOwner.mockReturnValue(of("[email protected]"));
+      const c = setupComponent({ type: "workflow", id: 7 });
+      expect(accessServiceSpy.getAccessList).toHaveBeenCalledWith("workflow", 
7);
+      expect(accessServiceSpy.getOwner).toHaveBeenCalledWith("workflow", 7);
+      expect(c.accessList).toEqual(accessList);
+      expect(c.owner).toBe("[email protected]");
+    });
+
+    it("loads publish state for workflow via WorkflowPersistService", () => {
+      workflowPersistSpy.getWorkflowIsPublished.mockReturnValue(of("Public"));
+      const c = setupComponent({ type: "workflow", id: 9 });
+      
expect(workflowPersistSpy.getWorkflowIsPublished).toHaveBeenCalledWith(9);
+      expect(c.isPublic).toBe(true);
+    });
+
+    it("sets isPublic to false when workflow publish state is Private", () => {
+      workflowPersistSpy.getWorkflowIsPublished.mockReturnValue(of("Private"));
+      const c = setupComponent({ type: "workflow" });
+      expect(c.isPublic).toBe(false);
+    });
+
+    it("loads publish state for dataset via DatasetService.getDataset", () => {
+      datasetServiceSpy.getDataset.mockReturnValue(of({ dataset: { isPublic: 
true } }));
+      const c = setupComponent({ type: "dataset", id: 12 });
+      expect(datasetServiceSpy.getDataset).toHaveBeenCalledWith(12);
+      expect(c.isPublic).toBe(true);
+    });
+
+    it("does not query publish state for non-workflow/dataset types", () => {
+      setupComponent({ type: "project", id: 4 });
+      expect(workflowPersistSpy.getWorkflowIsPublished).not.toHaveBeenCalled();
+      expect(datasetServiceSpy.getDataset).not.toHaveBeenCalled();
+    });
+  });
+
+  describe("handleInputConfirm", () => {
+    it("splits input on whitespace, commas, and semicolons into emailTags", () 
=> {
+      const c = setupComponent();
+      c.validateForm.get("email")?.setValue("[email protected], 
[email protected];[email protected] [email protected]");
+      c.handleInputConfirm();
+      expect(c.emailTags).toEqual(["[email protected]", "[email protected]", 
"[email protected]", "[email protected]"]);
+    });
+
+    it("rejects invalid emails via NzMessageService.error", () => {
+      const c = setupComponent();
+      c.validateForm.get("email")?.setValue("not-an-email");
+      c.handleInputConfirm();
+      expect(messageSpy.error).toHaveBeenCalledWith("not-an-email is not a 
valid email");
+      expect(c.emailTags).toEqual([]);
+    });
+
+    it("rejects duplicate emails via NzMessageService.error", () => {
+      const c = setupComponent();
+      c.emailTags = ["[email protected]"];
+      c.validateForm.get("email")?.setValue("[email protected]");
+      c.handleInputConfirm();
+      expect(messageSpy.error).toHaveBeenCalledWith("[email protected] is 
already in the tags");
+      expect(c.emailTags).toEqual(["[email protected]"]);
+    });
+
+    it("resets the email form control after processing", () => {
+      const c = setupComponent();
+      c.validateForm.get("email")?.setValue("[email protected]");
+      c.handleInputConfirm();
+      expect(c.validateForm.get("email")?.value).toBeNull();
+    });
+
+    it("calls event.preventDefault when an event is provided", () => {
+      const c = setupComponent();
+      const event = { preventDefault: vi.fn() } as unknown as Event;
+      c.handleInputConfirm(event);
+      expect(event.preventDefault).toHaveBeenCalled();
+    });
   });
 
-  it("uses the dataset dashboard path when sharing a dataset", () => {
-    const message = grantAndCaptureMessage(setupComponent("dataset", 22));
-    expect(message).toContain("/dashboard/user/dataset/22");
+  describe("onPaste", () => {
+    it("concatenates clipboard text to the existing email value and runs 
handleInputConfirm", () => {
+      const c = setupComponent();
+      c.validateForm.get("email")?.setValue("[email protected],");
+      const event = {
+        preventDefault: vi.fn(),
+        clipboardData: { getData: 
vi.fn().mockReturnValue("[email protected]") },
+      } as unknown as ClipboardEvent;
+      c.onPaste(event);
+      expect(event.preventDefault).toHaveBeenCalled();
+      expect(c.emailTags).toEqual(["[email protected]", "[email protected]"]);
+    });
+
+    it("is a no-op when clipboard data is empty", () => {
+      const c = setupComponent();
+      const event = {
+        preventDefault: vi.fn(),
+        clipboardData: { getData: vi.fn().mockReturnValue("") },
+      } as unknown as ClipboardEvent;
+      c.onPaste(event);
+      expect(c.emailTags).toEqual([]);
+    });
   });
 
-  it("uses the project dashboard path when sharing a project", () => {
-    const message = grantAndCaptureMessage(setupComponent("project", 33));
-    expect(message).toContain("/dashboard/user/project/33");
+  describe("grantAccess", () => {
+    function grantAndCaptureMessage(c: ShareAccessComponent): string {
+      c.emailTags = ["[email protected]"];
+      c.grantAccess();
+      return gmailSpy.sendEmail.mock.calls[0][1] as string;
+    }
+
+    it("uses the workflow dashboard path when sharing a workflow", () => {
+      const message = grantAndCaptureMessage(setupComponent({ type: 
"workflow", id: 11 }));
+      expect(message).toContain("/dashboard/user/workflow/11");
+    });
+
+    it("uses the dataset dashboard path when sharing a dataset", () => {
+      const message = grantAndCaptureMessage(setupComponent({ type: "dataset", 
id: 22 }));
+      expect(message).toContain("/dashboard/user/dataset/22");
+    });
+
+    it("uses the project dashboard path when sharing a project", () => {
+      const message = grantAndCaptureMessage(setupComponent({ type: "project", 
id: 33 }));
+      expect(message).toContain("/dashboard/user/project/33");
+    });
+
+    it("omits the access URL when sharing a computing-unit", () => {
+      const message = grantAndCaptureMessage(setupComponent({ type: 
"computing-unit", id: 44 }));
+      expect(message).not.toContain("/dashboard/user/");
+    });
+
+    it("calls ShareAccessService.grantAccess with the selected access level 
for each tag", () => {
+      const c = setupComponent({ type: "workflow", id: 5 });
+      c.validateForm.get("accessLevel")?.setValue("READ");
+      c.emailTags = ["[email protected]", "[email protected]"];
+      c.grantAccess();
+      expect(accessServiceSpy.grantAccess).toHaveBeenCalledWith("workflow", 5, 
"[email protected]", "READ");
+      expect(accessServiceSpy.grantAccess).toHaveBeenCalledWith("workflow", 5, 
"[email protected]", "READ");
+    });
+
+    it("shows a success notification and clears emailTags after granting", () 
=> {
+      const c = setupComponent({ type: "workflow", id: 5 });
+      c.emailTags = ["[email protected]"];
+      c.grantAccess();
+      expect(notificationSpy.success).toHaveBeenCalledWith("workflow shared 
with [email protected] successfully.");
+      expect(c.emailTags).toEqual([]);
+    });
+
+    it("surfaces HttpErrorResponse via NotificationService.error", () => {
+      accessServiceSpy.grantAccess.mockReturnValue(
+        throwError(() => new HttpErrorResponse({ error: { message: "boom" }, 
status: 500 }))
+      );
+      const c = setupComponent();
+      c.emailTags = ["[email protected]"];
+      c.grantAccess();
+      expect(notificationSpy.error).toHaveBeenCalledWith("boom");
+    });
   });
 
-  it("omits the access URL when sharing a computing-unit", () => {
-    const message = grantAndCaptureMessage(setupComponent("computing-unit", 
44));
-    expect(message).not.toContain("/dashboard/user/");
+  describe("hasWriteAccess", () => {
+    it("returns false when there is no current user email", () => {
+      const c = setupComponent({ currentEmail: undefined });
+      expect(c.hasWriteAccess).toBe(false);
+    });
+
+    it("returns true when the current user is the owner", () => {
+      accessServiceSpy.getOwner.mockReturnValue(of("[email protected]"));
+      const c = setupComponent({ currentEmail: "[email protected]" });
+      expect(c.hasWriteAccess).toBe(true);
+    });
+
+    it("returns true when the current user has WRITE privilege in the access 
list", () => {
+      accessServiceSpy.getAccessList.mockReturnValue(
+        of([{ email: "[email protected]", name: "Me", privilege: 
Privilege.WRITE }])
+      );
+      const c = setupComponent({ currentEmail: "[email protected]" });
+      expect(c.hasWriteAccess).toBe(true);
+    });
+
+    it("returns false when the current user has READ privilege", () => {
+      accessServiceSpy.getAccessList.mockReturnValue(
+        of([{ email: "[email protected]", name: "Me", privilege: Privilege.READ 
}])
+      );
+      const c = setupComponent({ currentEmail: "[email protected]" });
+      expect(c.hasWriteAccess).toBe(false);
+    });
+  });
+
+  describe("verifyRevokeAccess / revokeAccess", () => {
+    it("opens a self-revoke modal when revoking own access", () => {
+      const c = setupComponent({ currentEmail: "[email protected]", type: 
"workflow" });
+      c.verifyRevokeAccess("[email protected]");
+      const config = capturedModalConfigs[0];
+      expect(config.nzTitle).toBe("Revoke Your Access");
+      expect(config.nzContent).toContain("your own access");
+    });
+
+    it("opens an other-user revoke modal when revoking someone else", () => {
+      const c = setupComponent({ currentEmail: "[email protected]", type: 
"workflow" });
+      c.verifyRevokeAccess("[email protected]");
+      const config = capturedModalConfigs[0];
+      expect(config.nzTitle).toBe("Revoke Access");
+      expect(config.nzContent).toContain("[email protected]");
+    });
+
+    it("calls revokeAccess on confirm and emits refresh on destroy for 
self-revoke", () => {
+      const c = setupComponent({ currentEmail: "[email protected]" });
+      const refreshSpy = vi.fn();
+      c.refresh.subscribe(refreshSpy);
+      c.verifyRevokeAccess("[email protected]");
+      getFooterButton(capturedModalConfigs[0], "Revoke").onClick();
+      expect(accessServiceSpy.revokeAccess).toHaveBeenCalledWith("workflow", 
1, "[email protected]");
+      expect(modalRefSpy.close).toHaveBeenCalledWith({ userRevokedOwnAccess: 
true });
+      c.ngOnDestroy();
+      expect(refreshSpy).toHaveBeenCalled();
+    });
+
+    it("does not close the outer modal when revoking another user", () => {
+      const c = setupComponent({ currentEmail: "[email protected]" });
+      c.verifyRevokeAccess("[email protected]");
+      getFooterButton(capturedModalConfigs[0], "Revoke").onClick();
+      expect(accessServiceSpy.revokeAccess).toHaveBeenCalledWith("workflow", 
1, "[email protected]");
+      expect(modalRefSpy.close).not.toHaveBeenCalled();
+    });
+
+    it("surfaces revoke HttpErrorResponse via NotificationService.error", () 
=> {
+      accessServiceSpy.revokeAccess.mockReturnValue(
+        throwError(() => new HttpErrorResponse({ error: { message: "nope" }, 
status: 403 }))
+      );
+      const c = setupComponent({ currentEmail: "[email protected]" });
+      c.verifyRevokeAccess("[email protected]");
+      getFooterButton(capturedModalConfigs[0], "Revoke").onClick();
+      expect(notificationSpy.error).toHaveBeenCalledWith("nope");
+    });
+  });
+
+  describe("changeAccessLevel", () => {
+    it("calls applyAccessLevelChange directly when not a self-downgrade", () 
=> {
+      const c = setupComponent({ currentEmail: "[email protected]", type: 
"workflow", id: 3 });
+      accessServiceSpy.grantAccess.mockClear();
+      c.changeAccessLevel("[email protected]", "READ");
+      expect(modalServiceSpy.create).not.toHaveBeenCalled();
+      expect(accessServiceSpy.grantAccess).toHaveBeenCalledWith("workflow", 3, 
"[email protected]", "READ");
+    });
+
+    it("opens a downgrade-confirmation modal when downgrading own WRITE access 
to READ", () => {
+      accessServiceSpy.getAccessList.mockReturnValue(
+        of([{ email: "[email protected]", name: "Me", privilege: 
Privilege.WRITE }])
+      );
+      const c = setupComponent({ currentEmail: "[email protected]", type: 
"workflow", id: 3 });
+      accessServiceSpy.grantAccess.mockClear();
+      c.changeAccessLevel("[email protected]", "READ");
+      expect(modalServiceSpy.create).toHaveBeenCalled();
+      expect(capturedModalConfigs[0].nzTitle).toBe("Downgrade Your Access");
+      expect(accessServiceSpy.grantAccess).not.toHaveBeenCalled();
+      getFooterButton(capturedModalConfigs[0], "Confirm").onClick();
+      expect(accessServiceSpy.grantAccess).toHaveBeenCalledWith("workflow", 3, 
"[email protected]", "READ");
+    });
+
+    it("does not open the downgrade modal when upgrading own access from READ 
to WRITE", () => {
+      accessServiceSpy.getAccessList.mockReturnValue(
+        of([{ email: "[email protected]", name: "Me", privilege: Privilege.READ 
}])
+      );
+      const c = setupComponent({ currentEmail: "[email protected]" });
+      accessServiceSpy.grantAccess.mockClear();
+      c.changeAccessLevel("[email protected]", "WRITE");
+      expect(modalServiceSpy.create).not.toHaveBeenCalled();
+      expect(accessServiceSpy.grantAccess).toHaveBeenCalled();
+    });
+  });
+
+  describe("verifyPublish / verifyUnpublish", () => {
+    it("publishes a workflow on confirm and updates the action service when 
inWorkspace", () => {
+      workflowPersistSpy.getWorkflowIsPublished.mockReturnValue(of("Private"));
+      const c = setupComponent({ type: "workflow", id: 8, inWorkspace: true });
+      c.verifyPublish();
+      getFooterButton(capturedModalConfigs[0], "Publish").onClick();
+      
expect(workflowPersistSpy.updateWorkflowIsPublished).toHaveBeenCalledWith(8, 
true);
+      expect(workflowActionSpy.setWorkflowIsPublished).toHaveBeenCalledWith(1);
+    });
+
+    it("does not call WorkflowActionService.setWorkflowIsPublished when not 
inWorkspace", () => {
+      workflowPersistSpy.getWorkflowIsPublished.mockReturnValue(of("Private"));
+      const c = setupComponent({ type: "workflow", id: 8, inWorkspace: false 
});
+      c.verifyPublish();
+      getFooterButton(capturedModalConfigs[0], "Publish").onClick();
+      expect(workflowActionSpy.setWorkflowIsPublished).not.toHaveBeenCalled();
+    });
+
+    it("publishes a dataset on confirm", () => {
+      datasetServiceSpy.getDataset.mockReturnValue(of({ dataset: { isPublic: 
false } }));
+      const c = setupComponent({ type: "dataset", id: 9 });
+      c.verifyPublish();
+      getFooterButton(capturedModalConfigs[0], "Publish").onClick();
+      expect(datasetServiceSpy.updateDatasetPublicity).toHaveBeenCalledWith(9);
+    });
+
+    it("does not open the publish modal when the item is already public", () 
=> {
+      workflowPersistSpy.getWorkflowIsPublished.mockReturnValue(of("Public"));
+      const c = setupComponent({ type: "workflow" });
+      c.verifyPublish();
+      expect(modalServiceSpy.create).not.toHaveBeenCalled();
+    });
+
+    it("unpublishes a workflow on confirm and updates the action service when 
inWorkspace", () => {
+      workflowPersistSpy.getWorkflowIsPublished.mockReturnValue(of("Public"));
+      const c = setupComponent({ type: "workflow", id: 8, inWorkspace: true });
+      c.verifyUnpublish();
+      getFooterButton(capturedModalConfigs[0], "Unpublish").onClick();
+      
expect(workflowPersistSpy.updateWorkflowIsPublished).toHaveBeenCalledWith(8, 
false);
+      expect(workflowActionSpy.setWorkflowIsPublished).toHaveBeenCalledWith(0);
+    });
+
+    it("unpublishes a dataset on confirm", () => {
+      datasetServiceSpy.getDataset.mockReturnValue(of({ dataset: { isPublic: 
true } }));
+      const c = setupComponent({ type: "dataset", id: 9 });
+      c.verifyUnpublish();
+      getFooterButton(capturedModalConfigs[0], "Unpublish").onClick();
+      expect(datasetServiceSpy.updateDatasetPublicity).toHaveBeenCalledWith(9);
+    });
+
+    it("does not open the unpublish modal when the item is already private", 
() => {
+      workflowPersistSpy.getWorkflowIsPublished.mockReturnValue(of("Private"));
+      const c = setupComponent({ type: "workflow" });
+      c.verifyUnpublish();
+      expect(modalServiceSpy.create).not.toHaveBeenCalled();
+    });
+  });
+
+  describe("publish / unpublish methods", () => {
+    it("publishWorkflow flips isPublic and shows a success notification", () 
=> {
+      workflowPersistSpy.getWorkflowIsPublished.mockReturnValue(of("Private"));
+      const c = setupComponent({ type: "workflow" });
+      c.publishWorkflow();
+      expect(c.isPublic).toBe(true);
+      expect(notificationSpy.success).toHaveBeenCalledWith("Workflow published 
successfully");
+    });
+
+    it("publishWorkflow surfaces HttpErrorResponse via 
NotificationService.error", () => {
+      workflowPersistSpy.getWorkflowIsPublished.mockReturnValue(of("Private"));
+      workflowPersistSpy.updateWorkflowIsPublished.mockReturnValue(
+        throwError(() => new HttpErrorResponse({ error: { message: "publish 
failed" }, status: 500 }))
+      );
+      const c = setupComponent({ type: "workflow" });
+      c.publishWorkflow();
+      expect(notificationSpy.error).toHaveBeenCalledWith("publish failed");
+    });
+
+    it("unpublishWorkflow flips isPublic to false and shows a success 
notification", () => {
+      workflowPersistSpy.getWorkflowIsPublished.mockReturnValue(of("Public"));
+      const c = setupComponent({ type: "workflow" });
+      c.unpublishWorkflow();
+      expect(c.isPublic).toBe(false);
+      expect(notificationSpy.success).toHaveBeenCalledWith("Workflow 
unpublished successfully");
+    });
+
+    it("publishDataset flips isPublic and shows a success notification", () => 
{
+      datasetServiceSpy.getDataset.mockReturnValue(of({ dataset: { isPublic: 
false } }));
+      const c = setupComponent({ type: "dataset" });
+      c.publishDataset();
+      expect(c.isPublic).toBe(true);
+      expect(notificationSpy.success).toHaveBeenCalledWith("Dataset published 
successfully");
+    });
+
+    it("publishDataset surfaces HttpErrorResponse via 
NotificationService.error", () => {
+      datasetServiceSpy.getDataset.mockReturnValue(of({ dataset: { isPublic: 
false } }));
+      datasetServiceSpy.updateDatasetPublicity.mockReturnValue(
+        throwError(() => new HttpErrorResponse({ error: { message: "dataset 
publish failed" }, status: 500 }))
+      );
+      const c = setupComponent({ type: "dataset" });
+      c.publishDataset();
+      expect(notificationSpy.error).toHaveBeenCalledWith("dataset publish 
failed");
+    });
+
+    it("unpublishDataset flips isPublic to false and shows a success 
notification", () => {
+      datasetServiceSpy.getDataset.mockReturnValue(of({ dataset: { isPublic: 
true } }));
+      const c = setupComponent({ type: "dataset" });
+      c.unpublishDataset();
+      expect(c.isPublic).toBe(false);
+      expect(notificationSpy.success).toHaveBeenCalledWith("Dataset 
unpublished successfully");
+    });
   });
 });

Reply via email to