This is an automated email from the ASF dual-hosted git repository.
github-merge-queue[bot] pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/texera.git
The following commit(s) were added to refs/heads/main by this push:
new 34be37d60f test: cover ShareAccessComponent behaviors in spec (#5233)
34be37d60f is described below
commit 34be37d60f0931db692cb70a0d002178f9ce44d3
Author: Matthew B. <[email protected]>
AuthorDate: Sat May 30 21:33:05 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");
+ });
});
});