UNPKG

passbolt-styleguide

Version:

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

1,278 lines (1,024 loc) 91.7 kB
/** * Passbolt ~ Open source password manager for teams * Copyright (c) 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) Passbolt SA (https://www.passbolt.com) * @license https://opensource.org/licenses/AGPL-3.0 AGPL License * @link https://www.passbolt.com Passbolt(tm) * @since 5.0.0 */ /** * Unit tests on Create Resource in regard of specifications */ import { waitFor } from "@testing-library/react"; import EditResourcePage from "./EditResource.test.page"; import { defaultCustomFieldsProps, defaultProps, defaultTotpProps } from "./EditResource.test.data"; import { SecretGenerator } from "../../../../shared/lib/SecretGenerator/SecretGenerator"; import { TEST_RESOURCE_TYPE_PASSWORD_AND_DESCRIPTION, TEST_RESOURCE_TYPE_PASSWORD_DESCRIPTION_TOTP, TEST_RESOURCE_TYPE_PASSWORD_STRING, TEST_RESOURCE_TYPE_TOTP, TEST_RESOURCE_TYPE_V5_CUSTOM_FIELDS, TEST_RESOURCE_TYPE_V5_DEFAULT, TEST_RESOURCE_TYPE_V5_DEFAULT_TOTP, TEST_RESOURCE_TYPE_V5_TOTP, } from "../../../../shared/models/entity/resourceType/resourceTypeEntity.test.data"; import "../../../../../test/mocks/mockClipboard"; import ConfirmCreateEdit, { ConfirmEditCreateOperationVariations, ConfirmEditCreateRuleVariations, } from "../ConfirmCreateEdit/ConfirmCreateEdit"; import { defaultPasswordPoliciesContext } from "../../../../shared/context/PasswordPoliciesContext/PasswordPoliciesContext.test.data"; import PownedService from "../../../../shared/services/api/secrets/pownedService"; import { SECRET_DATA_OBJECT_TYPE } from "../../../../shared/models/entity/secretData/secretDataEntity"; import { defaultPasswordExpirySettingsContext } from "../../../contexts/PasswordExpirySettingsContext.test.data"; import { overridenPasswordExpirySettingsEntityDto } from "../../../../shared/models/passwordExpirySettings/PasswordExpirySettingsDto.test.data"; import { DateTime } from "luxon"; import { formatDateForApi } from "../../../../shared/utils/dateUtils"; import { defaultTotpDto } from "../../../../shared/models/entity/totp/totpDto.test.data"; import PassboltApiFetchError from "../../../../shared/lib/Error/PassboltApiFetchError"; import NotifyError from "../../Common/Error/NotifyError/NotifyError"; import { defaultResourceDto } from "../../../../shared/models/entity/resource/resourceEntity.test.data"; import { defaultResourceMetadataDto } from "../../../../shared/models/entity/resource/metadata/resourceMetadataEntity.test.data"; import ResourceMetadataEntity from "../../../../shared/models/entity/resource/metadata/resourceMetadataEntity"; import { defaultCustomField } from "../../../../shared/models/entity/customField/customFieldEntity.test.data"; import UserAbortsOperationError from "../../../lib/Error/UserAbortsOperationError"; import { act } from "react"; describe("See the Create Resource", () => { beforeEach(() => { jest.resetModules(); jest.clearAllMocks(); }); const mockContextRequest = (context, implementation) => jest.spyOn(context.port, "request").mockImplementationOnce(implementation); describe("As LU I can start editing a resource", () => { describe("Styleguide", () => { it("matches the styleguide", async () => { expect.assertions(18); const props = defaultProps(); // The props to pass mockContextRequest(props.context, () => ({ object_type: SECRET_DATA_OBJECT_TYPE, password: "RN9n8XuECN3" })); const resource = props.resource; let page; await act(async () => (page = new EditResourcePage(props))); // Dialog title exists and correct expect(page.exists()).toBeTruthy(); expect(page.header.textContent).toBe("Edit a resource"); // Close button exists expect(page.dialogClose).not.toBeNull(); // Name input field exists. expect(page.name.value).toBe(resource.metadata.name); // Uri input field exists. expect(page.uri.value).toBe(resource.metadata.uris[0]); // Username input field exists. expect(page.username.value).toBe(resource.metadata.username); // Password input field exists expect(page.password).not.toBeNull(); expect(page.password.value).toBe("RN9n8XuECN3"); expect(page.password.getAttribute("type")).toBe("password"); const passwordInputStyle = window.getComputedStyle(page.password); expect(passwordInputStyle.background).toBe("white"); expect(passwordInputStyle.color).toBe(""); // Complexity label exists but is not yet defined. expect(page.complexityText.textContent).toBe("Weak Entropy: 65.5 bits"); // Password view button exists. expect(page.passwordViewButton).not.toBeNull(); expect(page.passwordViewButton.classList.contains("eye-open")).toBe(true); expect(page.passwordViewButton.classList.contains("eye-close")).toBe(false); // Password generate button exists. expect(page.passwordGenerateButton).not.toBeNull(); // Save button exists expect(page.saveButton.textContent).toBe("Save"); // Cancel button exists expect(page.cancelButton.textContent).toBe("Cancel"); }); it("should display skeleton if secrets is not decrypted", async () => { expect.assertions(9); const props = defaultProps(); // The props to pass const page = new EditResourcePage(props); // Dialog title exists and correct expect(page.exists()).toBeTruthy(); expect(page.header.textContent).toBe("Edit a resource"); // Close button exists expect(page.dialogClose).not.toBeNull(); // resource info skeleton exists. expect(page.resourceInfoSkeleton).toBeDefined(); // edit workspace skeleton exists. expect(page.editWorkspaceSkeleton).toBeDefined(); // Save button exists expect(page.saveButton.textContent).toBe("Save"); // Save button is disabled expect(page.saveButton.hasAttribute("disabled")).toBeTruthy(); // Cancel button exists expect(page.cancelButton.textContent).toBe("Cancel"); // Cancel button is disabled expect(page.cancelButton.hasAttribute("disabled")).toBeTruthy(); }); }); describe("should select a resource form", () => { it("As a signed-in user I should be able to select description form", async () => { expect.assertions(5); const props = defaultProps(); mockContextRequest(props.context, () => ({ object_type: SECRET_DATA_OBJECT_TYPE, password: "password", description: "description", })); let page; await act(async () => (page = new EditResourcePage(props))); expect(page.exists()).toBeTruthy(); expect(page.sectionItemSelected.textContent).toStrictEqual("Passwords"); // select description form await page.click(page.menuDescription); // expectations expect(page.sectionItemSelected.textContent).toStrictEqual("Description"); expect(page.description).toBeDefined(); expect(page.password).toBeNull(); }); it("As a signed-in user I should be able to select uris form", async () => { expect.assertions(5); const props = defaultProps(); mockContextRequest(props.context, () => ({ object_type: SECRET_DATA_OBJECT_TYPE, password: "password", description: "description", })); let page; await act(async () => (page = new EditResourcePage(props))); expect(page.exists()).toBeTruthy(); expect(page.sectionItemSelected.textContent).toStrictEqual("Passwords"); // select description form await page.click(page.menuUris); // expectations expect(page.sectionItemSelected.textContent).toStrictEqual("URIs"); expect(page.mainUri).toBeDefined(); expect(page.password).toBeNull(); }); }); describe("should add a secret to a resource", () => { it("As a signed-in user I should be able to add secret without a resource type mutation", async () => { expect.assertions(2); const props = defaultProps(); mockContextRequest(props.context, () => ({ object_type: SECRET_DATA_OBJECT_TYPE, password: "password" })); let page; await act(async () => (page = new EditResourcePage(props))); await page.click(page.addSecret); await page.click(page.addSecretNote); // expectations expect(page.sectionItemSelected.textContent).toStrictEqual("Note"); expect(page.note).toBeDefined(); }); it("As a signed-in user I should be able to add secret with a resource type mutation", async () => { expect.assertions(3); const props = defaultProps(); mockContextRequest(props.context, () => ({ object_type: SECRET_DATA_OBJECT_TYPE, password: "password", description: "description", })); let page; await act(async () => (page = new EditResourcePage(props))); await page.click(page.addSecret); await page.click(page.addSecretTotp); // expectations expect(page.sectionItemSelected.textContent).toStrictEqual("TOTP"); expect(page.note).toBeDefined(); expect(page.upgradeCard).toBeNull(); }); it("As a signed-in user I should be able to add secret with a resource type mutation with a standalone totp", async () => { expect.assertions(2); const props = defaultTotpProps(); mockContextRequest(props.context, () => ({ object_type: SECRET_DATA_OBJECT_TYPE, totp: defaultTotpDto() })); let page; await act(async () => (page = new EditResourcePage(props))); await page.click(page.addSecret); await page.click(page.addSecretPassword); // expectations expect(page.sectionItemSelected.textContent).toStrictEqual("Passwords"); expect(page.password).toBeDefined(); }); it("As a signed-in user I should be able to add secret totp for a resource v4 password string", async () => { expect.assertions(3); const props = defaultProps({ resource: defaultResourceDto({ resource_type_id: TEST_RESOURCE_TYPE_PASSWORD_STRING }), }); mockContextRequest(props.context, () => ({ password: "password" })); let page; await act(async () => (page = new EditResourcePage(props))); await page.click(page.addSecret); await page.click(page.addSecretTotp); // expectations expect(page.sectionItemSelected.textContent).toStrictEqual("TOTP"); expect(page.note).toBeDefined(); expect(page.upgradeCard).toBeDefined(); }); }); describe("should delete a secret to a resource", () => { it("As a signed-in user I should be able to delete secret without a resource type mutation", async () => { expect.assertions(3); const props = defaultProps(); mockContextRequest(props.context, () => ({ object_type: SECRET_DATA_OBJECT_TYPE, password: "password", description: "description", })); let page; await act(async () => (page = new EditResourcePage(props))); // select note form await page.click(page.getSectionItem(2)); expect(page.sectionItemSelected.textContent).toStrictEqual("Note"); await page.click(page.deleteSecretNote); // expectations expect(page.sectionItemSelected.textContent).toStrictEqual("Passwords"); expect(page.password).toBeDefined(); }); it("As a signed-in user I should be able to delete secret custom fields without a resource type mutation", async () => { expect.assertions(6); const props = defaultProps(); mockContextRequest(props.context, () => ({ object_type: SECRET_DATA_OBJECT_TYPE, password: "RN9n8XuECN3", description: "description", custom_fields: [defaultCustomField()], })); let page; await act(async () => (page = new EditResourcePage(props))); // select note form await page.click(page.getSectionItem(2)); expect(page.sectionItemSelected.textContent).toStrictEqual("Custom fields"); await page.click(page.deleteSecretCustomFields); // expectations expect(page.sectionItemSelected.textContent).toStrictEqual("Passwords"); expect(page.password).toBeDefined(); mockContextRequest(props.context, jest.fn()); await page.click(page.saveButton); const resourceDtoExpected = { id: props.resource.id, expired: null, folder_parent_id: props.resource.folder_parent_id, resource_type_id: props.resource.resource_type_id, metadata: { object_type: ResourceMetadataEntity.METADATA_OBJECT_TYPE, name: props.resource.metadata.name, username: props.resource.metadata.username, resource_type_id: props.resource.resource_type_id, uris: props.resource.metadata.uris, description: props.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", ); expect(props.onClose).toBeCalled(); }); it("As a signed-in user I should be able to delete secret with a resource type mutation", async () => { expect.assertions(3); const props = defaultProps(); mockContextRequest(props.context, () => ({ object_type: SECRET_DATA_OBJECT_TYPE, password: "password" })); let page; await act(async () => (page = new EditResourcePage(props))); await page.click(page.addSecret); await page.click(page.addSecretTotp); expect(page.sectionItemSelected.textContent).toStrictEqual("TOTP"); await page.click(page.deleteSecretPassword); await page.click(page.menuDescription); // expectations expect(page.sectionItemSelected.textContent).toStrictEqual("Description"); expect(page.description).toBeDefined(); }); it("As a signed-in user I should be able to delete secret totp for a resource v4 password string", async () => { expect.assertions(3); const props = defaultProps({ resource: defaultResourceDto({ resource_type_id: TEST_RESOURCE_TYPE_PASSWORD_STRING }), }); mockContextRequest(props.context, () => ({ password: "password" })); let page; await act(async () => (page = new EditResourcePage(props))); await page.click(page.addSecret); await page.click(page.addSecretTotp); expect(page.sectionItemSelected.textContent).toStrictEqual("TOTP"); await page.click(page.deleteSecretTotp); // expectations expect(page.sectionItemSelected.textContent).toStrictEqual("Passwords"); expect(page.password).toBeDefined(); }); }); describe("should edit password form", () => { it("As a signed-in user I should be able to edit an URI", async () => { expect.assertions(2); const props = defaultProps(); mockContextRequest(props.context, () => ({ object_type: SECRET_DATA_OBJECT_TYPE, password: "password", description: "description", })); let page; await act(async () => (page = new EditResourcePage(props))); expect(page.exists()).toBeTruthy(); await page.fillInput(page.uri, "https://passbolt.com"); // expectations expect(page.uri.value).toBe("https://passbolt.com"); }); it("As a signed-in user I should be aware about the URI maxLength", async () => { expect.assertions(3); const props = defaultProps(); mockContextRequest(props.context, () => ({ object_type: SECRET_DATA_OBJECT_TYPE, password: "password", description: "description", })); let page; await act(async () => (page = new EditResourcePage(props))); await page.fillInput(page.uri, "a".repeat(1024)); // expectations expect(page.uri.value).toEqual("a".repeat(1024)); expect(page.uriWarningMessage.textContent).toEqual( "Warning: this is the maximum size for this field, make sure your data was not truncated.", ); expect(page.uriErrorMessage).toBeNull(); }); it("As a signed-in user I should be blocked if I exceed the URI maxLength", async () => { expect.assertions(5); const props = defaultProps(); mockContextRequest(props.context, () => ({ object_type: SECRET_DATA_OBJECT_TYPE, password: "password", description: "description", })); let page; await act(async () => (page = new EditResourcePage(props))); page.uri.setAttribute("maxLength", 1025); await page.fillInput(page.uri, "a".repeat(1025)); // expectations expect(page.uri.value).toEqual("a".repeat(1025)); expect(page.uriWarningMessage.textContent).toEqual( "Warning: this is the maximum size for this field, make sure your data was not truncated.", ); expect(page.uriErrorMessage).toBeNull(); await page.click(page.saveButton); expect(page.uriWarningMessage).toBeNull(); expect(page.uriErrorMessage.textContent).toEqual( "This is the maximum size for this field, make sure your data was not truncated.", ); }); it("As a signed-in user I should be able to edit an username", async () => { expect.assertions(2); const props = defaultProps(); mockContextRequest(props.context, () => ({ object_type: SECRET_DATA_OBJECT_TYPE, password: "password", description: "description", })); let page; await act(async () => (page = new EditResourcePage(props))); expect(page.exists()).toBeTruthy(); await page.fillInput(page.username, "username"); // expectations expect(page.username.value).toBe("username"); }); it("As a signed-in user I should be aware about the username maxLength", async () => { expect.assertions(3); const props = defaultProps(); mockContextRequest(props.context, () => ({ object_type: SECRET_DATA_OBJECT_TYPE, password: "password", description: "description", })); let page; await act(async () => (page = new EditResourcePage(props))); await page.fillInput(page.username, "a".repeat(255)); // expectations expect(page.username.value).toEqual("a".repeat(255)); expect(page.usernameWarningMessage.textContent).toEqual( "Warning: this is the maximum size for this field, make sure your data was not truncated.", ); expect(page.usernameErrorMessage).toBeNull(); }); it("As a signed-in user I should be blocked if I exceed the username maxLength", async () => { expect.assertions(5); const props = defaultProps(); mockContextRequest(props.context, () => ({ object_type: SECRET_DATA_OBJECT_TYPE, password: "password", description: "description", })); let page; await act(async () => (page = new EditResourcePage(props))); page.username.setAttribute("maxLength", 256); await page.fillInput(page.username, "a".repeat(256)); // expectations expect(page.username.value).toEqual("a".repeat(256)); expect(page.usernameWarningMessage.textContent).toEqual( "Warning: this is the maximum size for this field, make sure your data was not truncated.", ); expect(page.usernameErrorMessage).toBeNull(); await page.click(page.saveButton); expect(page.usernameWarningMessage).toBeNull(); expect(page.usernameErrorMessage.textContent).toEqual( "This is the maximum size for this field, make sure your data was not truncated.", ); }); it("As a signed-in user I should be able to edit a password", async () => { expect.assertions(2); const props = defaultProps(); mockContextRequest(props.context, () => ({ object_type: SECRET_DATA_OBJECT_TYPE, password: "password", description: "description", })); let page; await act(async () => (page = new EditResourcePage(props))); expect(page.exists()).toBeTruthy(); await page.fillInput(page.password, "secret"); // expectations expect(page.password.value).toBe("secret"); }); it("As a signed-in user I should be aware about the password maxLength", async () => { expect.assertions(3); const props = defaultProps(); mockContextRequest(props.context, () => ({ object_type: SECRET_DATA_OBJECT_TYPE, password: "password", description: "description", })); let page; await act(async () => (page = new EditResourcePage(props))); await page.fillInput(page.password, "a".repeat(4096)); // expectations expect(page.password.value).toEqual("a".repeat(4096)); expect(page.passwordWarningMessage.textContent).toEqual( "Warning: this is the maximum size for this field, make sure your data was not truncated.", ); expect(page.passwordErrorMessage).toBeNull(); }); it("As a signed-in user I should be blocked if I exceed the password maxLength", async () => { expect.assertions(5); const props = defaultProps(); mockContextRequest(props.context, () => ({ object_type: SECRET_DATA_OBJECT_TYPE, password: "password", description: "description", })); let page; await act(async () => (page = new EditResourcePage(props))); page.password.setAttribute("maxLength", 4097); await page.fillInput(page.password, "a".repeat(4097)); // expectations expect(page.password.value).toEqual("a".repeat(4097)); expect(page.passwordWarningMessage.textContent).toEqual( "Warning: this is the maximum size for this field, make sure your data was not truncated.", ); expect(page.passwordErrorMessage).toBeNull(); await page.click(page.saveButton); expect(page.passwordWarningMessage).toBeNull(); expect(page.passwordErrorMessage.textContent).toEqual( "This is the maximum size for this field, make sure your data was not truncated.", ); }); it("As a signed-in user I should be able to generate a password", async () => { expect.assertions(5); const props = defaultProps(); mockContextRequest(props.context, () => ({ object_type: SECRET_DATA_OBJECT_TYPE, password: "password", description: "description", })); let page; await act(async () => (page = new EditResourcePage(props))); jest.spyOn(SecretGenerator, "generate").mockImplementation(() => "generate-password"); expect(page.exists()).toBeTruthy(); expect(page.password.value).toBe("password"); await page.click(page.passwordGenerateButton); // expectations expect(page.password.value).toBe("generate-password"); expect(page.complexityText.textContent).not.toBe("Quality Entropy: 0.0 bits"); expect(page.progressBar.classList.contains("error")).toBe(false); }); }); describe("should edit a name to resource", () => { let props, page; beforeEach(async () => { props = defaultProps(); mockContextRequest(props.context, () => ({ object_type: SECRET_DATA_OBJECT_TYPE, password: "password", description: "description", })); await act(() => (page = new EditResourcePage(props))); }); it("As a signed-in user I should be able to edit name to a resource", async () => { expect.assertions(1); await page.fillInput(page.name, "name"); // expectations expect(page.name.value).toEqual("name"); }); it("As a signed-in user I should be aware about the name maxLength", async () => { expect.assertions(3); await page.fillInput(page.name, "a".repeat(255)); // expectations expect(page.name.value).toEqual("a".repeat(255)); expect(page.nameWarningMessage.textContent).toEqual( "Warning: this is the maximum size for this field, make sure your data was not truncated.", ); expect(page.nameErrorMessage).toBeNull(); }); it("As a signed-in user I should be blocked if I exceed the name maxLength", async () => { expect.assertions(5); page.name.setAttribute("maxLength", 256); await page.fillInput(page.name, "a".repeat(256)); // expectations expect(page.name.value).toEqual("a".repeat(256)); expect(page.nameWarningMessage.textContent).toEqual( "Warning: this is the maximum size for this field, make sure your data was not truncated.", ); expect(page.nameErrorMessage).toBeNull(); await page.click(page.saveButton); expect(page.nameWarningMessage).toBeNull(); expect(page.nameErrorMessage.textContent).toEqual( "This is the maximum size for this field, make sure your data was not truncated.", ); }); }); describe("should edit description field", () => { let props, page; beforeEach(async () => { props = defaultProps(); mockContextRequest(props.context, () => ({ object_type: SECRET_DATA_OBJECT_TYPE, password: "password", description: "description", })); await act(() => (page = new EditResourcePage(props))); await waitFor(() => page.exists); await page.click(page.menuDescription); }); it("As a signed-in user I should be able to edit a description", async () => { expect.assertions(1); await page.fillInput(page.description, "description"); // expectations expect(page.description.value).toBe("description"); }); it("As a signed-in user I should be aware about the description maxLength", async () => { expect.assertions(3); await page.fillInput(page.description, "a".repeat(10000)); // expectations expect(page.description.value).toEqual("a".repeat(10000)); expect(page.descriptionWarningMessage.textContent).toEqual( "Warning: this is the maximum size for this field, make sure your data was not truncated.", ); expect(page.descriptionErrorMessage).toBeNull(); }); it("As a signed-in user I should be blocked if I exceed the description maxLength", async () => { expect.assertions(5); page.description.setAttribute("maxLength", 10001); await page.fillInput(page.description, "a".repeat(10001)); // expectations expect(page.description.value).toEqual("a".repeat(10001)); expect(page.descriptionWarningMessage.textContent).toEqual( "Warning: this is the maximum size for this field, make sure your data was not truncated.", ); expect(page.descriptionErrorMessage).toBeNull(); await page.click(page.saveButton); expect(page.descriptionWarningMessage).toBeNull(); expect(page.descriptionErrorMessage.textContent).toEqual( "This is the maximum size for this field, make sure your data was not truncated.", ); }); it("As a signed-in user I should be able to convert a description to a note for a v4 password string", async () => { expect.assertions(2); const props = defaultProps({ resource: defaultResourceDto({ resource_type_id: TEST_RESOURCE_TYPE_PASSWORD_STRING }), }); mockContextRequest(props.context, () => ({ password: "password" })); let page; await act(async () => (page = new EditResourcePage(props))); await page.click(page.menuDescription); await page.fillInput(page.description, "description"); await page.click(page.convertToNote); // expectations expect(page.sectionItemSelected.textContent).toBe("Note"); expect(page.note.value).toBe("description"); }); }); describe("should edit totp form", () => { let props, page; beforeEach(async () => { props = defaultTotpProps(); mockContextRequest(props.context, () => ({ object_type: SECRET_DATA_OBJECT_TYPE, totp: defaultTotpDto() })); await act(() => (page = new EditResourcePage(props))); }); it("As a signed-in user I should be able to edit an URI", async () => { expect.assertions(2); expect(page.exists()).toBeTruthy(); await page.fillInput(page.uri, "https://passbolt.com"); // expectations expect(page.uri.value).toBe("https://passbolt.com"); }); it("As a signed-in user I should be aware about the URI maxLength", async () => { expect.assertions(3); await page.fillInput(page.uri, "a".repeat(1024)); // expectations expect(page.uri.value).toEqual("a".repeat(1024)); expect(page.uriWarningMessage.textContent).toEqual( "Warning: this is the maximum size for this field, make sure your data was not truncated.", ); expect(page.uriErrorMessage).toBeNull(); }); it("As a signed-in user I should be blocked if I exceed the URI maxLength", async () => { expect.assertions(5); await page.fillInput(page.uri, "a".repeat(1025)); // expectations expect(page.uri.value).toEqual("a".repeat(1025)); expect(page.uriWarningMessage.textContent).toEqual( "Warning: this is the maximum size for this field, make sure your data was not truncated.", ); expect(page.uriErrorMessage).toBeNull(); await page.click(page.saveButton); expect(page.uriWarningMessage).toBeNull(); expect(page.uriErrorMessage.textContent).toEqual( "This is the maximum size for this field, make sure your data was not truncated.", ); }); it("As a signed-in user I should be able to edit a resource totp key", async () => { expect.assertions(3); expect(page.resourceTotpCode.hasAttribute("disabled")).toBeFalsy(); await page.fillInput(page.resourceTotpKey, ""); // expectations expect(page.resourceTotpKey.value).toBe(""); expect(page.resourceTotpCode.hasAttribute("disabled")).toBeTruthy(); }); it("As a signed-in user I should see an error message when totp key is empty", async () => { expect.assertions(1); await page.fillInput(page.resourceTotpKey, ""); await page.click(page.saveButton); // expectations expect(page.resourceTotpKeyErrorMessage.textContent).toBe("The key is required."); }); it("As a signed-in user I should see an error message when totp key does not respect pattern", async () => { expect.assertions(1); await page.fillInput(page.resourceTotpKey, "????"); await page.click(page.saveButton); // expectations expect(page.resourceTotpKeyErrorMessage.textContent).toBe("The key is not valid."); }); it("As a signed-in user I should be able to add a totp expiry", async () => { expect.assertions(1); await page.click(page.advancedSettings); await page.fillInput(page.period, "60"); // expectations expect(page.period.value).toBe("60"); }); it("As a signed-in user I should see an error message when period is empty", async () => { expect.assertions(1); await page.click(page.advancedSettings); page.period.setAttribute("type", "string"); await page.fillInput(page.period, ""); await page.click(page.saveButton); // expectations expect(page.resourceTotpPeriodErrorMessage.textContent).toBe("TOTP expiry is required."); }); it("As a signed-in user I should see an error message when period is less than 0", async () => { expect.assertions(1); await page.click(page.advancedSettings); await page.fillInput(page.period, "-1"); await page.click(page.saveButton); // expectations expect(page.resourceTotpPeriodErrorMessage.textContent).toBe("TOTP expiry must be greater than 0."); }); it("As a signed-in user I should be redirected to the totp form if there is an error", async () => { expect.assertions(3); await page.fillInput(page.resourceTotpKey, ""); await page.click(page.menuDescription); expect(page.sectionItemSelected.textContent).toBe("Description"); await page.click(page.saveButton); // expectations expect(page.sectionItemSelected.textContent).toBe("TOTP"); expect(page.resourceTotpKeyErrorMessage.textContent).toBe("The key is required."); }); it("As a signed-in user I should be able to update a totp length", async () => { expect.assertions(1); await page.click(page.advancedSettings); await page.fillInput(page.digits, "8"); // expectations expect(page.digits.value).toBe("8"); }); it("As a signed-in user I should see an error message when length is empty", async () => { expect.assertions(1); await page.click(page.advancedSettings); page.digits.setAttribute("type", "string"); await page.fillInput(page.digits, ""); await page.click(page.saveButton); // expectations expect(page.resourceTotpDigitsErrorMessage.textContent).toBe("TOTP length is required."); }); it("As a signed-in user I should see an error message when length is less than 6", async () => { expect.assertions(1); await page.click(page.advancedSettings); await page.fillInput(page.digits, "5"); await page.click(page.saveButton); // expectations expect(page.resourceTotpDigitsErrorMessage.textContent).toBe("TOTP length must be between 6 and 8."); }); it("As a signed-in user I should see an error message when length is more than 8", async () => { expect.assertions(1); await page.click(page.advancedSettings); await page.fillInput(page.digits, "9"); await page.click(page.saveButton); // expectations expect(page.resourceTotpDigitsErrorMessage.textContent).toBe("TOTP length must be between 6 and 8."); }); it("As a signed-in user I should be able to edit an algorithm", async () => { expect.assertions(1); await page.click(page.advancedSettings); await page.click(page.algorithm); await page.click(page.firstItemOption); // expectations expect(page.algorithm.textContent).toBe("SHA256"); }); it("As a signed-in user I should be able to copy a resource totp from totp code", async () => { expect.assertions(1); await page.click(page.resourceTotpCode); // expectations expect(props.clipboardContext.copyTemporarily).toHaveBeenCalledTimes(1); }); it("As a signed-in user I should be able to copy a resource totp from totp button", async () => { expect.assertions(2); expect(page.exists()).toBeTruthy(); await page.click(page.copyTotpButton); // expectations expect(props.clipboardContext.copyTemporarily).toHaveBeenCalledTimes(1); }); }); describe("should edit custom fields form", () => { let props, page; beforeEach(async () => { props = defaultCustomFieldsProps(); mockContextRequest(props.context, () => ({ object_type: SECRET_DATA_OBJECT_TYPE, custom_fields: [{ id: props.resource.metadata.custom_fields[0].id, type: "text", secret_value: "secret-0" }], })); await act(() => (page = new EditResourcePage(props))); }); it("As a signed-in user I should be able to add and delete a custom field", async () => { expect.assertions(7); expect(page.exists()).toBeTruthy(); expect(page.getDeleteCustomField(0)).toBeNull(); await page.click(page.addCustomField); // expectations expect(page.getDeleteCustomField(0)).toBeDefined(); expect(page.getDeleteCustomField(1)).toBeDefined(); await page.click(page.getDeleteCustomField(0)); expect(page.getDeleteCustomField(0)).toBeNull(); expect(page.getDeleteCustomField(1)).toBeNull(); expect(page.customFieldsLength).toBe(1); }); it("As a signed-in user I should not be able to add a custom field after 32 rows", async () => { expect.assertions(3); expect(page.exists()).toBeTruthy(); for (let i = 0; i < 31; i++) { await page.click(page.addCustomField); } // expectations expect(page.customFieldsLength).toBe(32); expect(page.addCustomField.hasAttribute("disabled")).toBeTruthy(); }); it("As a signed-in user I should not be able to add a custom field after 50_000 characters", async () => { expect.assertions(4); expect(page.exists()).toBeTruthy(); for (let i = 0; i < 9; i++) { await page.fillInput(page.getCustomFieldValue(i), "a".repeat(5000)); await page.click(page.addCustomField); } await page.fillInput(page.getCustomFieldValue(9), "a".repeat(2500)); await page.click(page.addCustomField); await page.fillInput(page.getCustomFieldValue(10), "a".repeat(2500)); // expectations expect(page.customFieldsLength).toBe(11); expect(page.customFieldValueMaxCharactersWarningMessage).toStrictEqual( "You have reached the maximum content size limit.", ); expect(page.addCustomField.hasAttribute("disabled")).toBeTruthy(); }); it("As a signed-in user I should be aware about the key and value maxLength", async () => { expect.assertions(3); await page.fillInput(page.getCustomFieldKey(0), "a".repeat(255)); await page.fillInput(page.getCustomFieldValue(0), "a".repeat(20000)); // expectations expect(page.getCustomFieldKey(0).value).toEqual("a".repeat(255)); expect(page.getCustomFieldValue(0).value).toEqual("a".repeat(20000)); expect(page.getCustomFieldKeyAndValueWarningMessage(0).textContent).toEqual( "The key and the value reach the character limit, make sure your data won’t be truncated.", ); }); it("As a signed-in user I should be aware if the key name is already used", async () => { expect.assertions(1); await page.fillInput(page.getCustomFieldKey(0), "key"); await page.click(page.addCustomField); await page.fillInput(page.getCustomFieldKey(1), "key"); // expectations expect(page.getCustomFieldKeyWarningMessage(1).textContent).toEqual("The key name is already used."); }); it("As a signed-in user I should be aware about the key maxLength", async () => { expect.assertions(2); await page.fillInput(page.getCustomFieldKey(0), "a".repeat(255)); // expectations expect(page.getCustomFieldKey(0).value).toEqual("a".repeat(255)); expect(page.getCustomFieldKeyWarningMessage(0).textContent).toEqual( "The key reaches the character limit, make sure your data won’t be truncated.", ); }); it("As a signed-in user I should be aware about the value maxLength", async () => { expect.assertions(2); await page.fillInput(page.getCustomFieldValue(0), "a".repeat(20000)); // expectations expect(page.getCustomFieldValue(0).value).toEqual("a".repeat(20000)); expect(page.getCustomFieldValueWarningMessage(0).textContent).toEqual( "The value reaches the character limit, make sure your data won’t be truncated.", ); }); }); describe("should edit note form", () => { let props, page; beforeEach(async () => { props = defaultProps(); mockContextRequest(props.context, () => ({ object_type: SECRET_DATA_OBJECT_TYPE, password: "password", description: "description", })); await act(() => (page = new EditResourcePage(props))); await page.click(page.getSectionItem(2)); }); it("As a signed-in user I should be able to edit a note", async () => { expect.assertions(1); await page.fillInput(page.note, "note"); // expectations expect(page.note.value).toBe("note"); }); it("As a signed-in user I should be aware about the note maxLength", async () => { expect.assertions(3); await page.fillInput(page.note, "a".repeat(50000)); // expectations expect(page.note.value).toEqual("a".repeat(50000)); expect(page.noteWarningMessage.textContent).toEqual( "Warning: this is the maximum size for this field, make sure your data was not truncated.", ); expect(page.noteErrorMessage).toBeNull(); }); it("As a signed-in user I should be blocked if I exceed the note maxLength", async () => { expect.assertions(5); await page.fillInput(page.note, "a".repeat(50001)); // expectations expect(page.note.value).toEqual("a".repeat(50001)); expect(page.noteWarningMessage.textContent).toEqual( "Warning: this is the maximum size for this field, make sure your data was not truncated.", ); expect(page.noteErrorMessage).toBeNull(); await page.click(page.saveButton); expect(page.noteWarningMessage).toBeNull(); expect(page.noteErrorMessage.textContent).toEqual( "This is the maximum size for this field, make sure your data was not truncated.", ); }); it("As a signed-in user I should be able to convert a note to a description for a v4 default", async () => { expect.assertions(2); const props = defaultProps({ resource: defaultResourceDto({ resource_type_id: TEST_RESOURCE_TYPE_PASSWORD_AND_DESCRIPTION }), }); mockContextRequest(props.context, () => ({ password: "RN9n8XuECN3", description: "description" })); let page; await act(async () => (page = new EditResourcePage(props))); await page.click(page.getSectionItem(2)); await page.fillInput(page.note, "note"); // expectations expect(page.convertToDescription).toBeNull(); expect(page.upgradeCard).toBeDefined(); }); }); describe("As LU I can start editing uri field", () => { it("As a signed-in user I should be able to see a main uri filled and editing it", async () => { expect.assertions(2); const props = defaultProps(); mockContextRequest(props.context, () => ({ password: "RN9n8XuECN3", description: "description" })); let page; await act(async () => (page = new EditResourcePage(props))); await waitFor(() => page.exists); await page.click(page.menuUris); expect(page.mainUri.value).toBe("https://passbolt.com"); await page.fillInput(page.mainUri, "https://www.passbolt.com/docs"); // expectations expect(page.mainUri.value).toBe("https://www.passbolt.com/docs"); }); it("As a signed-in user I should be able to add additional and delte some", async () => { expect.assertions(3); const props = defaultProps(); mockContextRequest(props.context, () => ({ password: "RN9n8XuECN3", description: "description" })); let page; await act(async () => (page = new EditResourcePage(props))); await waitFor(() => page.exists); await page.click(page.menuUris); expect(page.addUri.hasAttribute("disabled")).toBeFalsy(); await page.click(page.addUri); await page.fillInput(page.getAdditionalUri(1), "https://www.passbolt.com/blog"); await page.click(page.addUri); await page.fillInput(page.getAdditionalUri(2), "https://www.passbolt.com/docs"); await page.click(page.getDeleteAdditionalUri(1)); // expectations expect(page.mainUri.value).toBe("https://passbolt.com"); expect(page.getAdditionalUri(1).value).toBe("https://www.passbolt.com/docs"); }); it("As a signed-in user I should be aware about the URI maxLength", async () => { expect.assertions(6); const props = defaultProps(); mockContextRequest(props.context, () => ({ password: "RN9n8XuECN3", description: "description" })); let page; await act(async () => (page = new EditResourcePage(props))); await waitFor(() => page.exists); await page.click(page.menuUris); await page.fillInput(page.mainUri, "a".repeat(1024)); await page.click(page.addUri); await page.fillInput(page.getAdditionalUri(1), "a".repeat(1024)); // expectations expect(page.mainUri.value).toEqual("a".repeat(1024)); expect(page.mainUriWarningMessage.textContent).toEqual( "Warning: this is the maximum size for this field, make sure your data was not truncated.", ); expect(page.mainUriErrorMessage).toBeNull(); expect(page.getAdditionalUri(1).value).toEqual("a".repeat(1024)); expect(page.getAdditionalUriWarningMessage(1).textContent).toEqual( "Warning: this is the maximum size for this field, make sure your data was not truncated.", ); expect(page.getAdditionalUriErrorMessage(1)).toBeNull(); }); it("As a signed-in user I should be blocked if I exceed the URI maxLength", async () => { expect.assertions(9); const props = defaultProps(); mockContextRequest(props.context, () => ({ password: "RN9n8XuECN3", description: "description" })); let page; await act(async () => (page = new EditResourcePage(props))); await waitFor(() => page.exists); await page.click(page.menuUris); await page.fillInput(page.mainUri, "a".repeat(1025)); await page.click(page.addUri); await page.fillInput(page.getAdditionalUri(1), "a".repeat(1025)); // expectations expect(page.mainUri.value).toEqual("a".repeat(1025)); expect(page.mainUriWarningMessage.textContent).toEqual( "Warning: this is the maximum size for this field, make sure your data was not truncated.", ); expect(page.mainUriErrorMessage).toBeNull(); expect(page.getAdditionalUri(1).value).toEqual("a".repeat(1025)); expect(page.getAdditionalUriWarningMessage(1).textContent).toEqual( "Warning: this is the maximum size for this field, make sure your data was not truncated.", ); expect(page.getAdditionalUriErrorMessage(1)).toBeNull(); await page.click(page.saveButton); expect(page.mainUriWarningMessage).toBeNull(); expect(page.mainUriErrorMessage.textContent).toEqual( "This is the maximum size for this field, make sure your data was not truncated.", ); expect(page.getAdditionalUriErrorMessage(1).textContent).toEqual( "This is the maximum size for this field, make sure your data was not truncated.", ); }); }); describe("should send the form", () => { it("should open the creation confirmation dialog if the entropy of the password is too low", async () => { expect.assertions(3); const props = defaultProps(); mockContextRequest(props.context, () => ({ object_type: SECRET_DATA_OBJECT_TYPE, password: "password", description: "description", })); let page; await act(async () => (page = new EditResourcePage(props))); expect(page.exists()).toBeTruthy(); await page.fillInput(page.password, "test"); await page.click(page.saveButton); const confirmDialogProps = { resourceName: "Passbolt", operation: ConfirmEditCreateOperationVariations.CREATE, rule: ConfirmEditCreateRuleVariations.MINIMUM_ENTROPY, onConfirm: expect.any(Function), onReject: expect.any(Function), }; expect(props.dialogContext.open).toHaveBeenCalledTimes(1); expect(props.dialogContext.open).toHaveBeenCalledWith(ConfirmCreateEdit, confirmDialogProps); }); it("should open the creation confirmation dialog if the entropy of the password is too low and throw an UserAbortsOpera