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
JavaScript
/**
* 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