UNPKG

passbolt-styleguide

Version:

Passbolt styleguide contains common styling assets used by the different sites, plugin, etc.

488 lines (438 loc) 22.7 kB
/** * Passbolt ~ Open source password manager for teams * Copyright (c) 2020 Passbolt SA (https://www.passbolt.com) * * Licensed under GNU Affero General Public License version 3 of the or any later version. * For full copyright and license information, please see the LICENSE.txt * Redistributions of files must retain the above copyright notice. * * @copyright Copyright (c) 2020 Passbolt SA (https://www.passbolt.com) * @license https://opensource.org/licenses/AGPL-3.0 AGPL License * @link https://www.passbolt.com Passbolt(tm) * @since 2.11.0 */ /** * Unit tests on DisplayGridContextualMenuContextualMenu in regard of specifications */ import "../../../../../test/mocks/mockClipboard"; import { defaultProps, propsDenyUIActions, propsResourceStandaloneTotp, propsResourceTotp, propsResourceWithReadOnlyPermission, propsResourceWithUpdatePermission, } from "./DisplayResourcesListContextualMenu.test.data"; import { ActionFeedbackContext } from "../../../contexts/ActionFeedbackContext"; import DeleteResource from "../DeleteResource/DeleteResource"; import EditResource from "../EditResource/EditResource"; import ShareDialog from "../../Share/ShareDialog"; import DisplayResourcesListContextualMenuPage from "./DisplayResourcesListContextualMenu.test.page"; import { plaintextSecretPasswordDescriptionTotpDto, plaintextSecretPasswordStringDto, } from "../../../../shared/models/entity/plaintextSecret/plaintextSecretEntity.test.data"; import PasswordExpiryDialog from "../PasswordExpiryDialog/PasswordExpiryDialog"; import { defaultPasswordExpirySettingsContext } from "../../../contexts/PasswordExpirySettingsContext.test.data"; import { waitForTrue } from "../../../../../test/utils/waitFor"; import { defaultResourceDto } from "../../../../shared/models/entity/resource/resourceEntity.test.data"; import { TEST_RESOURCE_TYPE_V5_DEFAULT } from "../../../../shared/models/entity/resourceType/resourceTypeEntity.test.data"; import { defaultUserAppContext } from "../../../contexts/ExtAppContext.test.data"; import { defaultUserDto } from "../../../../shared/models/entity/user/userEntity.test.data"; import MetadataKeysSettingsEntity from "../../../../shared/models/entity/metadata/metadataKeysSettingsEntity"; import { defaultMetadataKeysSettingsDto } from "../../../../shared/models/entity/metadata/metadataKeysSettingsEntity.test.data"; import ActionAbortedMissingMetadataKeys from "../../Metadata/ActionAbortedMissingMetadataKeys/ActionAbortedMissingMetadataKeys"; import { v4 as uuidv4 } from "uuid"; import DisplayResourceSecretHistory from "../../SecretHistory/DisplayResourceSecretHistory"; import SecretRevisionsSettingsEntity from "../../../../shared/models/entity/secretRevision/secretRevisionsSettingsEntity"; beforeEach(() => { jest.resetModules(); }); describe("DisplayResourcesListContextualMenu", () => { let page; // The page to test against describe("As LU I should be able to access all the offered capabilities on resources I have owner access", () => { const props = defaultProps(); // The props to pass jest.spyOn(ActionFeedbackContext._currentValue, "displaySuccess").mockImplementation(() => {}); jest.spyOn(ActionFeedbackContext._currentValue, "displayError").mockImplementation(() => {}); beforeEach(() => { page = new DisplayResourcesListContextualMenuPage(props); }); /** * Given an organization with 1 resource * Then I should see the 11 menu */ it("As LU I should see all menu name", () => { expect.assertions(21); expect(page.copyUsernameItem).not.toBeNull(); expect(page.copyUsernameItem.hasAttribute("disabled")).toBeFalsy(); expect(page.copyPasswordItem).not.toBeNull(); expect(page.copyPasswordItem.hasAttribute("disabled")).toBeFalsy(); expect(page.copyUriItem).not.toBeNull(); expect(page.copyUriItem.hasAttribute("disabled")).toBeFalsy(); expect(page.copyPermalinkItem).not.toBeNull(); expect(page.copyPermalinkItem.hasAttribute("disabled")).toBeFalsy(); expect(page.copyTotpItem).toBeNull(); expect(page.openUriItem).not.toBeNull(); expect(page.openUriItem.hasAttribute("disabled")).toBeFalsy(); expect(page.editItem).not.toBeNull(); expect(page.editItem.hasAttribute("disabled")).toBeFalsy(); expect(page.shareItem).not.toBeNull(); expect(page.shareItem.hasAttribute("disabled")).toBeFalsy(); expect(page.deleteItem).not.toBeNull(); expect(page.deleteItem.hasAttribute("disabled")).toBeFalsy(); expect(page.markAsExpiredItem).not.toBeNull(); expect(page.markAsExpiredItem.hasAttribute("disabled")).toBeFalsy(); expect(page.setExpiryDateItem).not.toBeNull(); expect(page.setExpiryDateItem.hasAttribute("disabled")).toBeFalsy(); }); it("As LU I can start to copy the username of a resource", async () => { expect.assertions(2); await page.copyUsername(); expect(props.clipboardContext.copy).toHaveBeenCalledWith( props.resource.metadata.username, "The username has been copied to clipboard.", ); expect(props.hide).toHaveBeenCalled(); }); it("As LU I can start to copy the password of a resource", async () => { expect.assertions(3); jest.spyOn(props.context.port, "request").mockImplementationOnce(() => plaintextSecretPasswordStringDto()); await page.copyPassword(); expect(props.context.port.request).toHaveBeenCalledWith("passbolt.secret.find-by-resource-id", props.resource.id); expect(props.clipboardContext.copyTemporarily).toHaveBeenCalledWith( "secret-password", "The secret has been copied to clipboard.", ); expect(props.hide).toHaveBeenCalled(); }); it("As LU I can start to copy the uri of a resource", async () => { expect.assertions(2); await page.copyUri(); expect(props.clipboardContext.copy).toHaveBeenCalledWith( props.resource.metadata.uris[0], "The uri has been copied to clipboard.", ); expect(props.hide).toHaveBeenCalled(); }); it("As LU I can start to copy the permalink of a resource", async () => { expect.assertions(2); await page.copyPermalink(); expect(props.clipboardContext.copy).toHaveBeenCalledWith( `${props.context.userSettings.getTrustedDomain()}/app/passwords/view/${props.resource.id}`, "The permalink has been copied to clipboard.", ); expect(props.hide).toHaveBeenCalled(); }); it("As LU I can open a resource uri in new tab", async () => { jest.spyOn(props.resourceWorkspaceContext, "onGoToResourceUriRequested").mockImplementationOnce(() => {}); await page.openUri(); expect(props.resourceWorkspaceContext.onGoToResourceUriRequested).toHaveBeenCalled(); }); it("As LU I can start to edit a resource", async () => { await page.edit(); expect(props.dialogContext.open).toHaveBeenCalledWith(EditResource, { resource: props.resource }); expect(props.hide).toHaveBeenCalled(); }); it("As LU I can start to share a resource", async () => { await page.share(); expect(props.dialogContext.open).toHaveBeenCalledWith(ShareDialog); expect(props.hide).toHaveBeenCalled(); }); it("As LU I can start to delete a resource", async () => { await page.delete(); expect(props.dialogContext.open).toHaveBeenCalledWith(DeleteResource, { resources: [props.resource] }); expect(props.hide).toHaveBeenCalled(); }); it("As LU I can start to mark a resource as expired", async () => { expect.assertions(3); jest.spyOn(props.context.port, "request"); await page.markAsExpired(); await waitForTrue(() => ActionFeedbackContext._currentValue.displaySuccess.mock.calls.length > 0); expect(props.context.port.request).toHaveBeenCalledWith("passbolt.resources.set-expiration-date", [ { id: props.resource.id, expired: expect.any(String) }, ]); expect(ActionFeedbackContext._currentValue.displaySuccess).toHaveBeenCalled(); expect(props.hide).toHaveBeenCalled(); }); it("As LU I cannot start to mark a resource as expired if there is an error", async () => { expect.assertions(3); jest.spyOn(props.context.port, "request").mockImplementationOnce(() => { throw new Error("error"); }); await page.markAsExpired(); expect(props.context.port.request).toHaveBeenCalledWith("passbolt.resources.set-expiration-date", [ { id: props.resource.id, expired: expect.any(String) }, ]); expect(ActionFeedbackContext._currentValue.displayError).toHaveBeenCalled(); expect(props.hide).toHaveBeenCalled(); }); it("As LU I can mark a resource as expired", async () => { expect.assertions(3); jest.spyOn(props.context.port, "request"); await page.markAsExpired(); expect(props.context.port.request).toHaveBeenCalledWith("passbolt.resources.set-expiration-date", [ { id: props.resource.id, expired: expect.any(String) }, ]); expect(ActionFeedbackContext._currentValue.displaySuccess).toHaveBeenCalled(); expect(props.hide).toHaveBeenCalled(); }); it("As LU I can start to set expiry expiry date of a resource", async () => { await page.setExpiryDate(); expect(props.dialogContext.open).toHaveBeenCalledWith(PasswordExpiryDialog, { resources: [props.resource], }); expect(props.hide).toHaveBeenCalled(); }); it("As LU I can start to display a resource secret history", async () => { await page.displaySecretHistory(); expect(props.dialogContext.open).toHaveBeenCalledWith(DisplayResourceSecretHistory, { resource: props.resource }); expect(props.hide).toHaveBeenCalled(); }); }); describe("As LU I should be able to access all the offered capabilities on totp resources I have owner access", () => { const props = propsResourceTotp(); // The props to pass jest.spyOn(ActionFeedbackContext._currentValue, "displaySuccess").mockImplementationOnce(() => {}); beforeEach(() => { page = new DisplayResourcesListContextualMenuPage(props); }); /** * Given an organization with 1 resource * Then I should see the 11 menu */ it("As LU I should see all menu name", () => { expect(page.copyUsernameItem).not.toBeNull(); expect(page.copyUsernameItem.hasAttribute("disabled")).toBeFalsy(); expect(page.copyPasswordItem).not.toBeNull(); expect(page.copyPasswordItem.hasAttribute("disabled")).toBeFalsy(); expect(page.copyUriItem).not.toBeNull(); expect(page.copyUriItem.hasAttribute("disabled")).toBeFalsy(); expect(page.copyPermalinkItem).not.toBeNull(); expect(page.copyPermalinkItem.hasAttribute("disabled")).toBeFalsy(); expect(page.copyTotpItem).not.toBeNull(); expect(page.copyTotpItem.hasAttribute("disabled")).toBeFalsy(); expect(page.openUriItem).not.toBeNull(); expect(page.openUriItem.hasAttribute("disabled")).toBeFalsy(); expect(page.editItem).not.toBeNull(); expect(page.editItem.hasAttribute("disabled")).toBeFalsy(); expect(page.shareItem).not.toBeNull(); expect(page.shareItem.hasAttribute("disabled")).toBeFalsy(); expect(page.deleteItem).not.toBeNull(); expect(page.deleteItem.hasAttribute("disabled")).toBeFalsy(); expect(page.markAsExpiredItem).not.toBeNull(); expect(page.markAsExpiredItem.hasAttribute("disabled")).toBeFalsy(); expect(page.setExpiryDateItem).not.toBeNull(); expect(page.setExpiryDateItem.hasAttribute("disabled")).toBeFalsy(); expect(page.secretHistoryItem).not.toBeNull(); expect(page.secretHistoryItem.hasAttribute("disabled")).toBeFalsy(); }); it("As LU I can start to copy the totp of a resource", async () => { expect.assertions(3); jest .spyOn(props.context.port, "request") .mockImplementationOnce(() => plaintextSecretPasswordDescriptionTotpDto()); await page.copyTotp(); expect(props.context.port.request).toHaveBeenCalledWith("passbolt.secret.find-by-resource-id", props.resource.id); expect(props.clipboardContext.copyTemporarily).toHaveBeenCalledWith( expect.stringMatching(/^\d{6}/), "The TOTP has been copied to clipboard.", ); expect(props.hide).toHaveBeenCalled(); }); }); describe("As LU I should be able to access all the offered capabilities on a standalone totp resources I have owner access", () => { const props = propsResourceStandaloneTotp(); // The props to pass jest.spyOn(ActionFeedbackContext._currentValue, "displaySuccess").mockImplementationOnce(() => {}); beforeEach(() => { page = new DisplayResourcesListContextualMenuPage(props); }); /** * Given an organization with 1 resource * Then I should see the 9 menu */ it("As LU I should see only totp options", () => { expect.assertions(16); expect(page.copyUsernameItem).toBeNull(); expect(page.copyPasswordItem).toBeNull(); expect(page.copyUriItem).not.toBeNull(); expect(page.copyUriItem.hasAttribute("disabled")).toBeFalsy(); expect(page.copyPermalinkItem).not.toBeNull(); expect(page.copyPermalinkItem.hasAttribute("disabled")).toBeFalsy(); expect(page.copyTotpItem).not.toBeNull(); expect(page.copyTotpItem.hasAttribute("disabled")).toBeFalsy(); expect(page.openUriItem).not.toBeNull(); expect(page.openUriItem.hasAttribute("disabled")).toBeFalsy(); expect(page.editItem).not.toBeNull(); expect(page.editItem.hasAttribute("disabled")).toBeFalsy(); expect(page.shareItem).not.toBeNull(); expect(page.shareItem.hasAttribute("disabled")).toBeFalsy(); expect(page.deleteItem).not.toBeNull(); expect(page.deleteItem.hasAttribute("disabled")).toBeFalsy(); }); it("As LU I can start to copy the totp of a resource", async () => { expect.assertions(3); jest .spyOn(props.context.port, "request") .mockImplementationOnce(() => plaintextSecretPasswordDescriptionTotpDto()); await page.copyTotp(); expect(props.context.port.request).toHaveBeenCalledWith("passbolt.secret.find-by-resource-id", props.resource.id); expect(props.clipboardContext.copyTemporarily).toHaveBeenCalledWith( expect.stringMatching(/^\d{6}/), "The TOTP has been copied to clipboard.", ); expect(props.hide).toHaveBeenCalled(); }); }); describe("As LU I should have limited offered capabilities on resources I have read only access", () => { const props = propsResourceWithReadOnlyPermission(); // The props to pass beforeEach(() => { page = new DisplayResourcesListContextualMenuPage(props); }); it("As LU I should not be able to edit/share/delete/expire a password I have read only access", async () => { expect.assertions(15); expect(page.copyUsernameItem).not.toBeNull(); expect(page.copyUsernameItem.hasAttribute("disabled")).toBeFalsy(); expect(page.copyPasswordItem).not.toBeNull(); expect(page.copyPasswordItem.hasAttribute("disabled")).toBeFalsy(); expect(page.copyUriItem).not.toBeNull(); expect(page.copyUriItem.hasAttribute("disabled")).toBeFalsy(); expect(page.copyPermalinkItem).not.toBeNull(); expect(page.copyPermalinkItem.hasAttribute("disabled")).toBeFalsy(); expect(page.openUriItem).not.toBeNull(); expect(page.openUriItem.hasAttribute("disabled")).toBeFalsy(); expect(page.editItem).toBeNull(); expect(page.shareItem).toBeNull(); expect(page.deleteItem).toBeNull(); expect(page.markAsExpiredItem).toBeNull(); expect(page.setExpiryDateItem).toBeNull(); }); }); describe("As LU I should have limited offered capabilities on resources I have update access", () => { const props = propsResourceWithUpdatePermission(); // The props to pass beforeEach(() => { page = new DisplayResourcesListContextualMenuPage(props); }); it("As LU I should not be able to share a password I have update access", async () => { expect.assertions(18); expect(page.copyUsernameItem).not.toBeNull(); expect(page.copyUsernameItem.hasAttribute("disabled")).toBeFalsy(); expect(page.copyPasswordItem).not.toBeNull(); expect(page.copyPasswordItem.hasAttribute("disabled")).toBeFalsy(); expect(page.copyUriItem).not.toBeNull(); expect(page.copyUriItem.hasAttribute("disabled")).toBeFalsy(); expect(page.copyPermalinkItem).not.toBeNull(); expect(page.copyPermalinkItem.hasAttribute("disabled")).toBeFalsy(); expect(page.openUriItem).not.toBeNull(); expect(page.openUriItem.hasAttribute("disabled")).toBeFalsy(); expect(page.editItem).not.toBeNull(); expect(page.editItem.hasAttribute("disabled")).toBeFalsy(); expect(page.shareItem).toBeNull(); expect(page.deleteItem).toBeNull(); expect(page.markAsExpiredItem).not.toBeNull(); expect(page.markAsExpiredItem.hasAttribute("disabled")).toBeFalsy(); expect(page.setExpiryDateItem).not.toBeNull(); expect(page.setExpiryDateItem.hasAttribute("disabled")).toBeFalsy(); }); }); describe("As LU I should have limited offered capabilities if constraint by rbac", () => { const props = propsDenyUIActions(); beforeEach(() => { page = new DisplayResourcesListContextualMenuPage(props); }); it("As LU I should not see the copy password to clipboard if denied by rbac", async () => { expect(page.copyUsernameItem).not.toBeNull(); expect(page.copyUsernameItem.hasAttribute("disabled")).toBeFalsy(); expect(page.copyPasswordItem).toBeNull(); expect(page.copyUriItem).not.toBeNull(); expect(page.copyUriItem.hasAttribute("disabled")).toBeFalsy(); expect(page.copyPermalinkItem).not.toBeNull(); expect(page.copyPermalinkItem.hasAttribute("disabled")).toBeFalsy(); expect(page.openUriItem).not.toBeNull(); expect(page.openUriItem.hasAttribute("disabled")).toBeFalsy(); expect(page.editItem).not.toBeNull(); expect(page.editItem.hasAttribute("disabled")).toBeFalsy(); expect(page.shareItem).toBeNull(); expect(page.deleteItem).not.toBeNull(); expect(page.deleteItem.hasAttribute("disabled")).toBeFalsy(); }); }); describe("As LU I should not see expiry feature items", () => { it("when the feature flag is disabled", () => { const props = propsResourceWithUpdatePermission(); props.passwordExpiryContext = defaultPasswordExpirySettingsContext(); props.passwordExpiryContext.isFeatureEnabled = () => false; page = new DisplayResourcesListContextualMenuPage(props); expect(page.markAsExpiredItem).toBeNull(); expect(page.setExpiryDateItem).toBeNull(); }); it("when the feature flag is enabled but the settings are set to disabled", () => { const props = propsResourceWithUpdatePermission(); props.passwordExpiryContext = defaultPasswordExpirySettingsContext({ policy_override: false, automatic_update: false, automatic_expiry: false, }); page = new DisplayResourcesListContextualMenuPage(props); expect(page.markAsExpiredItem).toBeNull(); expect(page.setExpiryDateItem).toBeNull(); }); }); describe("As LU I should not see secret history feature items", () => { it("when the feature flag is disabled", () => { const props = propsResourceWithUpdatePermission(); props.secretRevisionsSettings = SecretRevisionsSettingsEntity.createFromDefault(); jest.spyOn(props.context.siteSettings, "canIUse").mockImplementation((plugin) => plugin !== "secretRevisions"); page = new DisplayResourcesListContextualMenuPage(props); expect(page.secretHistoryItem).toBeNull(); }); it("when the feature flag is enabled but the settings are set to disabled", () => { const props = propsResourceWithUpdatePermission(); props.secretRevisionsSettings = SecretRevisionsSettingsEntity.createFromDefault(); page = new DisplayResourcesListContextualMenuPage(props); expect(page.secretHistoryItem).toBeNull(); }); }); describe("As LU I should see action aborted", () => { it("As LU I cannot edit a resource v5 if metadata keys settings enforced metadata shared key and user has missing keys", async () => { expect.assertions(2); const props = defaultProps({ context: defaultUserAppContext({ loggedInUser: defaultUserDto({ missing_metadata_key_ids: [uuidv4()] }, { withRole: true }), }), metadataKeysSettings: new MetadataKeysSettingsEntity( defaultMetadataKeysSettingsDto({ allow_usage_of_personal_keys: false }), ), resource: defaultResourceDto({ personal: true, resource_type_id: TEST_RESOURCE_TYPE_V5_DEFAULT }), }); // The props to pass const page = new DisplayResourcesListContextualMenuPage(props); await page.edit(); expect(props.dialogContext.open).toHaveBeenNthCalledWith(1, ActionAbortedMissingMetadataKeys); expect(props.hide).toHaveBeenCalledTimes(1); }); it("As LU I cannot edit a shared resource v5 if user has missing keys", async () => { expect.assertions(2); const props = defaultProps({ context: defaultUserAppContext({ loggedInUser: defaultUserDto({ missing_metadata_key_ids: [uuidv4()] }, { withRole: true }), }), resource: defaultResourceDto({ resource_type_id: TEST_RESOURCE_TYPE_V5_DEFAULT }), }); // The props to pass const page = new DisplayResourcesListContextualMenuPage(props); await page.edit(); expect(props.dialogContext.open).toHaveBeenNthCalledWith(1, ActionAbortedMissingMetadataKeys); expect(props.hide).toHaveBeenCalledTimes(1); }); it("As LU I cannot share a resource v5 if user has missing keys", async () => { expect.assertions(2); const props = defaultProps({ context: defaultUserAppContext({ loggedInUser: defaultUserDto({ missing_metadata_key_ids: [uuidv4()] }, { withRole: true }), }), resource: defaultResourceDto({ resource_type_id: TEST_RESOURCE_TYPE_V5_DEFAULT }), }); // The props to pass const page = new DisplayResourcesListContextualMenuPage(props); await page.share(); expect(props.dialogContext.open).toHaveBeenNthCalledWith(1, ActionAbortedMissingMetadataKeys); expect(props.hide).toHaveBeenCalledTimes(1); }); }); });