passbolt-styleguide
Version:
Passbolt styleguide contains common styling assets used by the different sites, plugin, etc.
486 lines (412 loc) • 19.2 kB
JavaScript
/**
* 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 PasswordSidebar in regard of specifications
*/
import "../../../../../test/mocks/mockClipboard";
import "../../../../shared/components/Icons/ResourceIcon.test.init";
import React from "react";
import { defaultProps } from "./DisplayResourceDetails.test.data";
import DisplayResourceDetailsPage from "./DisplayResourceDetails.test.page";
import {
defaultResourceDto,
resourceLegacyDto,
resourceStandaloneTotpDto,
resourceWithReadPermissionDto,
resourceWithTotpDto,
} from "../../../../shared/models/entity/resource/resourceEntity.test.data";
import { denyRbacContext } from "../../../../shared/context/Rbac/RbacContext.test.data";
import ResourceTypesCollection from "../../../../shared/models/entity/resourceType/resourceTypesCollection";
import { resourceTypesV4CollectionDto } from "../../../../shared/models/entity/resourceType/resourceTypesCollection.test.data";
import MetadataTypesSettingsEntity from "../../../../shared/models/entity/metadata/metadataTypesSettingsEntity";
import { defaultMetadataTypesSettingsV50FreshDto } from "../../../../shared/models/entity/metadata/metadataTypesSettingsEntity.test.data";
import { defaultResourceWorkspaceContext } from "../../../contexts/ResourceWorkspaceContext.test.data";
import { TEST_RESOURCE_TYPE_V5_DEFAULT } from "../../../../shared/models/entity/resourceType/resourceTypeEntity.test.data";
import ResourceMetadataEntity from "../../../../shared/models/entity/resource/metadata/resourceMetadataEntity";
import { SECRET_DATA_OBJECT_TYPE } from "../../../../shared/models/entity/secretData/secretDataEntity";
import UserAbortsOperationError from "../../../lib/Error/UserAbortsOperationError";
import { resourceWithCustomFields } from "./DisplayResourceDetailsCustomFields.test.data";
import { resourceWithMultipleUris, resourceWithOneUris } from "./DisplayResourceDetailsURIs.test.data";
import ActionAbortedMissingMetadataKeys from "../../Metadata/ActionAbortedMissingMetadataKeys/ActionAbortedMissingMetadataKeys";
import { defaultUserAppContext } from "../../../contexts/ExtAppContext.test.data";
import { defaultUserDto } from "../../../../shared/models/entity/user/userEntity.test.data";
import { v4 as uuidv4 } from "uuid";
import MetadataKeysSettingsEntity from "../../../../shared/models/entity/metadata/metadataKeysSettingsEntity";
import { defaultMetadataKeysSettingsDto } from "../../../../shared/models/entity/metadata/metadataKeysSettingsEntity.test.data";
jest.mock("./DisplayResourceDetailsInformation", () => () => <></>);
jest.mock("./DisplayResourceDetailsPassword", () => () => <div className="password"></div>);
jest.mock("./DisplayResourceDetailsTotp", () => () => <div className="totp"></div>);
jest.mock("./DisplayResourceDetailsCustomFields", () => () => <div className="custom-fields"></div>);
jest.mock("./DisplayResourceDetailsActivity", () => () => <></>);
jest.mock("./DisplayResourceDetailsPermission", () => () => <></>);
jest.mock("./DisplayResourceDetailsDescription", () => () => <div className="description"></div>);
jest.mock("./DisplayResourceDetailsTag", () => () => <></>);
jest.mock("./DisplayResourceDetailsComment", () => () => <></>);
beforeEach(() => {
jest.resetModules();
});
describe("DisplayResourceDetails", () => {
describe("As LU I can see the resource sidebar common part", () => {
let props, page;
beforeEach(() => {
props = defaultProps(); // The props to pass
page = new DisplayResourceDetailsPage(props);
});
it("I should see a resource details sidebar", () => {
expect(page.exists()).toBeTruthy();
});
it("I can see the name of the selected resource", async () => {
expect.assertions(2);
expect(page.name).toBe(props.resourceWorkspaceContext.details.resource.metadata.name);
expect(page.subtitle).toBe("Password and Note");
});
it("I can copy the resource permalink", async () => {
expect.assertions(1);
const mockContextRequest = (implementation) =>
jest.spyOn(props.context.port, "request").mockImplementation(implementation);
const copyClipboardMockImpl = jest.fn((_, data) => data);
const props = defaultProps(); // The props to pass
const page = new DisplayResourceDetailsPage(props);
mockContextRequest(copyClipboardMockImpl);
jest.spyOn(props.actionFeedbackContext, "displaySuccess").mockImplementation(() => {});
await page.selectPermalink();
expect(props.clipboardContext.copy).toHaveBeenCalledWith(
`${props.context.userSettings.getTrustedDomain()}/app/passwords/view/${props.resourceWorkspaceContext.details.resource.id}`,
"The permalink has been copied to clipboard.",
);
});
});
describe("As LU I can use tabs in the resource sidebar", () => {
let props, page;
beforeEach(() => {
props = defaultProps(); // The props to pass
page = new DisplayResourceDetailsPage(props);
});
it("I should see tabs a in the resource sidebar", () => {
expect.assertions(3);
expect(page.tabs()).toBeTruthy();
expect(page.tab(0).textContent).toStrictEqual("Details");
expect(page.tab(1).textContent).toStrictEqual("Activity");
});
it("I should not see tabs a in the resource sidebar if RBAC denies it", () => {
expect.assertions(1);
const props = defaultProps({
rbacContext: denyRbacContext(),
});
page = new DisplayResourceDetailsPage(props);
expect(page.tabs()).toBeFalsy();
});
it("I can see activity tab context", async () => {
expect.assertions(2);
page = new DisplayResourceDetailsPage(props);
expect(page.activeTab.textContent).toStrictEqual("Details");
await page.click(page.tab(1));
expect(page.activeTab.textContent).toStrictEqual("Activity");
});
});
describe("As LU I can see the details section", () => {
it.todo("As LU I can see the details section");
});
describe("As LU I can see the password section", () => {
it("As LU I can see the password section", () => {
expect.assertions(1);
const props = defaultProps(); // The props to pass
const page = new DisplayResourceDetailsPage(props);
expect(page.password).toBeDefined();
});
it("As LU I cannot see the password section if resource type has no password", () => {
expect.assertions(1);
const props = defaultProps({ resourceWorkspaceContext: { details: { resource: resourceStandaloneTotpDto() } } }); // The props to pass
const page = new DisplayResourceDetailsPage(props);
expect(page.password).toBeNull();
});
});
describe("As LU I can see the totp section", () => {
it("As LU I can see the totp section for a resource with password description and totp", () => {
expect.assertions(1);
const props = defaultProps({ resourceWorkspaceContext: { details: { resource: resourceWithTotpDto() } } }); // The props to pass
const page = new DisplayResourceDetailsPage(props);
expect(page.totp).toBeDefined();
});
it("As LU I can see the totp section for a standalone totp", () => {
expect.assertions(1);
const props = defaultProps({ resourceWorkspaceContext: { details: { resource: resourceStandaloneTotpDto() } } }); // The props to pass
const page = new DisplayResourceDetailsPage(props);
expect(page.totp).toBeDefined();
});
it("As LU I cannot see the totp section if resource type has no totp", () => {
expect.assertions(1);
const props = defaultProps(); // The props to pass
const page = new DisplayResourceDetailsPage(props);
expect(page.totp).toBeNull();
});
});
describe("As LU I can see the description section", () => {
it("As LU I can see the description section", () => {
expect.assertions(1);
const props = defaultProps({ resourceWorkspaceContext: { details: { resource: resourceLegacyDto() } } }); // The props to pass
const page = new DisplayResourceDetailsPage(props);
expect(page.description).toBeDefined();
});
it("As LU I cannot see the description section if resource type has no description", () => {
expect.assertions(1);
const props = defaultProps(); // The props to pass
const page = new DisplayResourceDetailsPage(props);
expect(page.description).toBeNull();
});
});
describe("As LU I can see the card section", () => {
it("As LU I can see the card section", () => {
expect.assertions(1);
const props = defaultProps();
const page = new DisplayResourceDetailsPage(props);
expect(page.upgradeCard).toBeDefined();
});
it("As LU I cannot see the card section if there is no resource v5 corresponding", () => {
expect.assertions(1);
const props = defaultProps({ resourceTypes: new ResourceTypesCollection(resourceTypesV4CollectionDto()) });
const page = new DisplayResourceDetailsPage(props);
expect(page.upgradeCard).toBeNull();
});
it("As LU I cannot see the card section if user is not allowed to upgrade", () => {
expect.assertions(1);
const props = defaultProps({
metadataTypeSettings: new MetadataTypesSettingsEntity(defaultMetadataTypesSettingsV50FreshDto()),
});
const page = new DisplayResourceDetailsPage(props);
expect(page.upgradeCard).toBeNull();
});
it("As LU I cannot see the card section if resource is v5", () => {
expect.assertions(1);
const resourceWorkspaceContext = defaultResourceWorkspaceContext({
details: {
resource: defaultResourceDto({
resource_type_id: TEST_RESOURCE_TYPE_V5_DEFAULT,
}),
},
});
const props = defaultProps({ resourceWorkspaceContext });
const page = new DisplayResourceDetailsPage(props);
expect(page.upgradeCard).toBeNull();
});
});
describe("As LU I can upgrade a resource", () => {
it("As LU I can upgrade a resource v4 to v5", async () => {
expect.assertions(2);
const props = defaultProps();
jest
.spyOn(props.context.port, "request")
.mockImplementationOnce(() => ({ password: "RN9n8XuECN3", description: "description" }));
const page = new DisplayResourceDetailsPage(props);
jest.spyOn(props.context.port, "request").mockImplementationOnce(jest.fn());
await page.click(page.upgradeButton);
const resourceDtoExpected = {
id: props.resourceWorkspaceContext.details.resource.id,
expired: null,
folder_parent_id: null,
resource_type_id: TEST_RESOURCE_TYPE_V5_DEFAULT,
metadata: {
object_type: ResourceMetadataEntity.METADATA_OBJECT_TYPE,
name: props.resourceWorkspaceContext.details.resource.metadata.name,
username: props.resourceWorkspaceContext.details.resource.metadata.username,
resource_type_id: TEST_RESOURCE_TYPE_V5_DEFAULT,
uris: props.resourceWorkspaceContext.details.resource.metadata.uris,
description: props.resourceWorkspaceContext.details.resource.metadata.description,
},
};
const secretDtoExpected = {
object_type: SECRET_DATA_OBJECT_TYPE,
password: "RN9n8XuECN3",
description: "description",
};
// expectations
expect(props.context.port.request).toHaveBeenCalledWith(
"passbolt.resources.update",
resourceDtoExpected,
secretDtoExpected,
);
expect(props.actionFeedbackContext.displaySuccess).toHaveBeenCalledWith(
"The resource has been updated successfully",
);
});
it("As LU I cannot upgrade a v4 to V5 if an unexpected error happens", async () => {
expect.assertions(2);
const props = defaultProps();
jest
.spyOn(props.context.port, "request")
.mockImplementationOnce(() => ({ password: "RN9n8XuECN3", description: "description" }));
const page = new DisplayResourceDetailsPage(props);
jest.spyOn(props.context.port, "request").mockImplementationOnce(() => {
throw new Error("Error");
});
await page.click(page.upgradeButton);
const resourceDtoExpected = {
id: props.resourceWorkspaceContext.details.resource.id,
expired: null,
folder_parent_id: null,
resource_type_id: TEST_RESOURCE_TYPE_V5_DEFAULT,
metadata: {
object_type: ResourceMetadataEntity.METADATA_OBJECT_TYPE,
name: props.resourceWorkspaceContext.details.resource.metadata.name,
username: props.resourceWorkspaceContext.details.resource.metadata.username,
resource_type_id: TEST_RESOURCE_TYPE_V5_DEFAULT,
uris: props.resourceWorkspaceContext.details.resource.metadata.uris,
description: props.resourceWorkspaceContext.details.resource.metadata.description,
},
};
const secretDtoExpected = {
object_type: SECRET_DATA_OBJECT_TYPE,
password: "RN9n8XuECN3",
description: "description",
};
// expectations
expect(props.context.port.request).toHaveBeenCalledWith(
"passbolt.resources.update",
resourceDtoExpected,
secretDtoExpected,
);
expect(props.actionFeedbackContext.displayError).toHaveBeenCalledWith("Error");
});
it("As LU I cannot upgrade a v4 to v5 if resource permission is read", async () => {
expect.assertions(1);
const props = defaultProps({
resourceWorkspaceContext: defaultResourceWorkspaceContext({
details: {
resource: resourceWithReadPermissionDto(),
},
}),
});
jest
.spyOn(props.context.port, "request")
.mockImplementationOnce(() => ({ password: "RN9n8XuECN3", description: "description" }));
const page = new DisplayResourceDetailsPage(props);
// expectations
expect(page.upgradeButton).toBeNull();
});
it("As LU I cannot upgrade a v4 to V5 if user aborts operation", async () => {
expect.assertions(3);
const props = defaultProps();
jest.spyOn(props.context.port, "request").mockImplementationOnce(() => {
throw new UserAbortsOperationError("Error");
});
const page = new DisplayResourceDetailsPage(props);
await page.click(page.upgradeButton);
// expectations
expect(props.context.port.request).toHaveBeenCalledTimes(1);
expect(props.context.port.request).not.toHaveBeenCalledWith("passbolt.resources.update");
expect(props.actionFeedbackContext.displayError).not.toHaveBeenCalled();
});
it("As LU I cannot upgrade a v4 to v5 if resource is shared and user has missing metadata keys", async () => {
expect.assertions(1);
const props = defaultProps({
context: defaultUserAppContext({
loggedInUser: defaultUserDto({ missing_metadata_key_ids: [uuidv4()] }, { withRole: true }),
}),
});
const page = new DisplayResourceDetailsPage(props);
await page.click(page.upgradeButton);
// expectations
expect(props.dialogContext.open).toHaveBeenCalledWith(ActionAbortedMissingMetadataKeys);
});
it("As LU I cannot upgrade a v4 to v5 resource if share metadata key is enforced and user has missing metadata keys", async () => {
expect.assertions(1);
const props = defaultProps({
context: defaultUserAppContext({
loggedInUser: defaultUserDto({ missing_metadata_key_ids: [uuidv4()] }, { withRole: true }),
}),
metadataKeysSettings: new MetadataKeysSettingsEntity(
defaultMetadataKeysSettingsDto({ allow_usage_of_personal_keys: false }),
),
});
const page = new DisplayResourceDetailsPage(props);
await page.click(page.upgradeButton);
// expectations
expect(props.dialogContext.open).toHaveBeenCalledWith(ActionAbortedMissingMetadataKeys);
});
});
describe("As LU I can see the share section", () => {
it.todo("As LU I can see the share section");
it.todo("As LU I cannot see the share section if denied by RBAC");
});
describe("As LU I can see the tags section", () => {
it.todo("As LU I can see the tags section");
it.todo("As LU I cannot see the tags section if denied by RBAC");
});
describe("As LU I can see the comments section", () => {
it.todo("As LU I can see the comments section");
it.todo("As LU I cannot see the comments section if denied by RBAC");
});
describe("As LU I can see the activity section", () => {
it.todo("As LU I can see the activity section");
it.todo("As LU I cannot see the activity section if denied by RBAC");
});
describe("As LU I can see the custom fields section", () => {
let page;
beforeEach(() => {
const props = defaultProps({
resourceWorkspaceContext: defaultResourceWorkspaceContext({
details: {
resource: resourceWithCustomFields,
},
}),
});
page = new DisplayResourceDetailsPage(props);
});
it("As LU I can see the custom field section", async () => {
expect.assertions(1);
expect(page.customField).toBeDefined();
});
it("As LU I cannot see the section if resource does not contain custom fields", async () => {
expect.assertions(1);
const props = defaultProps({
resourceWorkspaceContext: defaultResourceWorkspaceContext({
details: {
resource: resourceWithReadPermissionDto(),
},
}),
});
const page = new DisplayResourceDetailsPage(props);
expect(page.customField).toBeNull();
});
});
describe("As LU I can see the URIs section", () => {
let page;
beforeEach(() => {
const props = defaultProps({
resourceWorkspaceContext: defaultResourceWorkspaceContext({
details: {
resource: resourceWithMultipleUris,
},
}),
});
page = new DisplayResourceDetailsPage(props);
});
it("As LU I can see the multiple uris section", async () => {
expect.assertions(1);
expect(page.urisTab).toBeDefined();
});
it("As LU I cannot see the section if resource does not contain more than 1 uri", async () => {
expect.assertions(1);
const props = defaultProps({
resourceWorkspaceContext: defaultResourceWorkspaceContext({
details: {
resource: resourceWithOneUris,
},
}),
});
const page = new DisplayResourceDetailsPage(props);
expect(page.customField).toBeNull();
});
});
});