Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

e2e: Proposal Create mock structure. #2676

Merged
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/components/DatePickerField/DatePickerField.jsx
Expand Up @@ -69,7 +69,9 @@ const DatePickerField = ({
};

return (
<div className={classNames("cursor-pointer", className)}>
<div
className={classNames("cursor-pointer", className)}
data-testid="datepicker">
<DatePicker
show={isOpen}
years={years}
Expand Down
2 changes: 2 additions & 0 deletions src/components/ProposalForm/ProposalForm.jsx
Expand Up @@ -308,6 +308,7 @@ const ProposalForm = React.memo(function ProposalForm({
<BoxTextInput
placeholder="Amount (USD)"
name="amount"
data-testid="proposal-amount"
tabIndex={1}
value={values.amount}
onChange={handleChangeWithTouched("amount")}
Expand Down Expand Up @@ -336,6 +337,7 @@ const ProposalForm = React.memo(function ProposalForm({
)}
<SelectField
name="domain"
id="proposal-domain-selector"
onChange={handleSelectFiledChange("domain")}
options={domainOptions}
className={classNames(styles.typeSelectWrapper, "margin-top-m")}
Expand Down
68 changes: 48 additions & 20 deletions teste2e/cypress/e2e/proposal/create.js
@@ -1,41 +1,69 @@
import { buildProposal } from "../../support/generate";

beforeEach(function mockApiCalls() {
// currently mocking pi and ticketvote summaries calls with any status, since
// they aren't used for assertions.
cy.useTicketvoteApi();
cy.useRecordsApi();
cy.usePiApi();
cy.useWwwApi();
cy.useCommentsApi();
});

describe("Proposal Create", () => {
// XXX This test needs changes in the Datepicker and (probably) the Select
// components, in order to fill the new form fields such as: start & end dates
// and amount - issue to track <insert issue link>
//
/*it("Paid user can create proposals manually", () => {
it("should be able to create proposals", () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
it("should be able to create proposals", () => {
it("should allow paid user to create proposals", () => {

// paid user with proposal credits
const user = {
email: "adminuser@example.com",
username: "adminuser",
password: "password"
};
cy.userEnvironment("user", { verifyIdentity: true });
const proposal = buildProposal();
cy.login(user);
cy.visit("/");
cy.identity();
cy.recordsMiddleware("new", {});
cy.visit("/record/new");
cy.typeCreateProposal(proposal);
});*/
cy.findByRole("button", { name: /submit/i }).click();
// needs more time in general to complete this request so we increase the
// responseTimeout
cy.wait("@newProposal").should((xhr) => {
expect(xhr.status).to.equal(200);
cy.piMiddleware("summaries", { amountByStatus: { unvetted: 1 } });
const token = xhr.response.body.record.censorshiprecord.token;
cy.assertProposalPage({
...proposal,
token: token
});
cy.wait("@pi.summaries", { timeout: 500 });
cy.findByTestId("record-title").should("have.text", proposal.name);
});
});

it("Non-paid user can not create proposals", () => {
const user = {
email: "user3@example.com",
username: "user3",
password: "password"
};
cy.login(user);
it("should not be able to create proposals without fill the input", () => {
// paid user with proposal credits
cy.userEnvironment("user", { verifyIdentity: true });
cy.recordsMiddleware("new", {});
cy.visit("/record/new");
cy.findByRole("button", { name: /submit/i }).click();
cy.findByTestId("proposal-name").parent().find("p").contains("Required");
cy.findByTestId("proposal-amount").parent().find("p").contains("Required");
cy.get("[data-testid=datepicker]:eq(0)")
.find("p")
.contains("Please pick a start date");
cy.get("[data-testid=datepicker]:eq(1)")
.find("p")
.contains("Please pick an end date");
});

it("should not be able create proposals with non-paid user", () => {
cy.userEnvironment("unpaid", { verifyIdentity: true });
cy.visit("/");
cy.findByText(/new proposal/i).should("be.disabled");
const proposal = buildProposal();
cy.visit("/record/new");
cy.typeCreateProposal(proposal);
cy.findByText(
/you won't be able to submit comments or proposals before paying the paywall/i
).should("be.visible");
const proposal = buildProposal();
cy.findByTestId("proposal-name").should("be.visible").type(proposal.name);
cy.findByTestId("text-area").type(proposal.description);
cy.findByRole("button", { name: /submit/i }).should("be.disabled");
});
});
2 changes: 1 addition & 1 deletion teste2e/cypress/pki.js
Expand Up @@ -70,7 +70,7 @@ export const keysToHex = ({ publicKey, secretKey }) => ({
secretKey: toHex(secretKey)
});

const keysFromHex = ({ publicKey, secretKey }) => ({
export const keysFromHex = ({ publicKey, secretKey }) => ({
publicKey: toByteArray(publicKey),
secretKey: toByteArray(secretKey)
});
Expand Down
42 changes: 24 additions & 18 deletions teste2e/cypress/support/commands.js
Expand Up @@ -25,13 +25,22 @@
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
//
import { sha3_256 } from "js-sha3";
import { requestWithCsrfToken, setProposalStatus } from "../utils";
import {
PROPOSAL_SUMMARY_STATUS_UNVETTED,
vibros68 marked this conversation as resolved.
Show resolved Hide resolved
requestWithCsrfToken,
setProposalStatus
} from "../utils";
import * as pki from "../pki";
import get from "lodash/fp/get";
// TODO: consider moving general functions like makeProposal and signRegister
// to a more general lib file other than api.
import { makeProposal, signRegister } from "../utils";
import { shortRecordToken } from "../utils";
import {
makeProposal,
signRegister,
shortRecordToken,
RECORD_DOMAINS,
typeDatePicker
vibros68 marked this conversation as resolved.
Show resolved Hide resolved
} from "../utils";
import { middlewares as recordMiddlewares } from "./mock/records";
import { middlewares as ticketVoteMiddlewares } from "./mock/ticketvote";
import { middlewares as commentsMiddlewares } from "./mock/comments";
Expand Down Expand Up @@ -149,22 +158,19 @@ Cypress.Commands.add("approveProposal", ({ token }) =>
Cypress.Commands.add("typeCreateProposal", (proposal) => {
cy.server();
cy.findByTestId("proposal-name").type(proposal.name);
cy.findByTestId("text-area").type(proposal.description);
cy.findByTestId("proposal-amount").type(String(proposal.amount / 100)); // get dollars from cents.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prettier: 80 max char per line.

const startDate = new Date(proposal.startDate * 1000);
cy.findAllByTestId("datepicker").first().children().first().click();
cy.findAllByTestId("days-list")
.first()
.findByText(startDate.getDate())
.click();
cy.findAllByTestId("datepicker").first().next().children().first().click();
cy.get("[data-testid=days-list]:eq(1)").find(">li").last().click();
const domainTxt = RECORD_DOMAINS[proposal.domain];
cy.get("#domain-selector").click().contains(domainTxt).click({ force: true });
cy.route("POST", "/api/records/v1/new").as("newProposal");
cy.findByRole("button", { name: /submit/i }).click();
// needs more time in general to complete this request so we increase the
// responseTimeout
cy.wait("@newProposal", { timeout: 10000 }).should((xhr) => {
expect(xhr.status).to.equal(200);
expect(xhr.response.body.record)
.to.have.property("censorshiprecord")
.and.be.a("object")
.and.have.all.keys("token", "signature", "merkle");
cy.assertProposalPage({
...proposal,
token: xhr.response.body.record.censorshiprecord.token
});
});
cy.findByTestId("text-area").type(proposal.description);
});

Cypress.Commands.add("assertListLengthByTestId", (testid, expectedLength) =>
Expand Down
25 changes: 25 additions & 0 deletions teste2e/cypress/support/core/api.js
Expand Up @@ -101,7 +101,32 @@ export function detailsReply({
return { record };
}

/**
* newRecordReply is the reply to the new command. It returns a new record for the
* request data with given `files`, `publickey`, `signature` and `username` testParams.
*
* @param {Object} { testParams, requestParams }
* @returns Proposal
*/
export function newRecordReply({
testParams: { username },
requestParams: { files = [], publickey, signature }
}) {
// const record = new Proposal(username, { files, publickey, signature });
const record = new Record({
status: 1,
state: 1,
version: 1,
files,
author: username,
publickey,
signature
});
return { record };
}

export const repliers = {
new: newRecordReply,
records: recordsReply,
inventory: inventoryReply,
policy: policyReply,
Expand Down
12 changes: 9 additions & 3 deletions teste2e/cypress/support/core/generate.js
Expand Up @@ -86,6 +86,8 @@ export function Record({
status: recordStatus,
state: recordState,
version: recordVersion,
publickey,
signature: recordSignature,
files = []
} = {}) {
const token = recordToken || Token();
Expand All @@ -106,15 +108,19 @@ export function Record({
signature,
merkle: faker.datatype.hexaDecimal(64, false, /[0-9a-z]/)
};
this.files = [new File(fileIndex), ...files.map((f) => new File(f))];
if (files.length == 2) {
this.files = files;
} else {
this.files = [new File(fileIndex), ...files.map((f) => new File(f))];
}
this.metadata = [
UserMetadata(user),
RecordMetadata({
token,
version,
status,
publickey: user.publickey,
signature,
publickey: publickey || user.publickey,
recordSignature,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Noticed some issues here. RecordMetadata has no recordSignature field. Actually, it's signature. Also, the signature should have a default value.

timestamp
})
];
Expand Down
20 changes: 15 additions & 5 deletions teste2e/cypress/support/users/api.js
@@ -1,6 +1,6 @@
import pick from "lodash/fp/pick";

import { PaymentCredits, User, userByType } from "./generate";
import { PaymentCredits, User, userByType, Identity } from "./generate";

export const API_BASE_URL = "/api/v1/user";

Expand All @@ -11,10 +11,15 @@ export const API_BASE_URL = "/api/v1/user";
* @returns User
*/
export function loginReply({
testParams: { userType, ...userProps },
testParams: { userType, verifyIdentity, ...userProps },
requestParams: { email }
}) {
return userByType(userType, { ...userProps, email });
const user = userByType(userType, { ...userProps, email });
if (verifyIdentity) {
const { userid, publickey } = user;
Identity({ userid, publickey });
}
return user;
}

/**
Expand All @@ -23,8 +28,13 @@ export function loginReply({
* @param {Object} { testParams }
* @returns User
*/
export function meReply({ testParams: { userType } }) {
return userByType(userType);
export function meReply({ testParams: { userType, verifyIdentity } }) {
const user = userByType(userType);
if (verifyIdentity) {
const { userid, publickey } = user;
Identity({ userid, publickey });
}
return user;
}

/**
Expand Down
4 changes: 2 additions & 2 deletions teste2e/cypress/support/users/commands.js
Expand Up @@ -8,13 +8,13 @@ Cypress.Commands.add(
createMiddleware({ packageName: "user", repliers, baseUrl: API_BASE_URL })
);

Cypress.Commands.add("userEnvironment", (userType) => {
Cypress.Commands.add("userEnvironment", (userType, { verifyIdentity } = {}) => {
cy.wwwMiddleware(
"api",
{ isActive: userType !== "noLogin" },
{ headers: { "x-csrf-token": "abcdefghijklmnopqrstuvwxyz" } }
);
cy.userMiddleware("me", { userType });
cy.userMiddleware("me", { userType, verifyIdentity });
cy.userMiddleware("payments/registration", {
haspaid: userType !== "unpaid"
});
Expand Down
17 changes: 14 additions & 3 deletions teste2e/cypress/support/users/generate.js
Expand Up @@ -2,6 +2,7 @@ import faker from "faker";
import compose from "lodash/fp/compose";
import map from "lodash/fp/map";
import times from "lodash/fp/times";
import * as pki from "../../pki";

export function User({
isadmin = false,
Expand All @@ -22,7 +23,7 @@ export function User({
this.paywalladdress = paywalladdress || `Ts${faker.datatype.hexaDecimal(33)}`;
this.paywalltxid =
paywalltxid || faker.datatype.hexaDecimal(64, false, /[0-9a-z]/);
this.publickey = publickey || faker.datatype.hexaDecimal(64);
this.publickey = publickey || pki.toHex(faker.datatype.hexaDecimal(64));
this.paywallamount = 10000000;
this.paywalltxnotbefore = Date.now() / 1000 - 3600;
this.lastlogintime = faker.time.recent() / 1000;
Expand Down Expand Up @@ -60,13 +61,13 @@ export function userByType(userType, props) {
case "unpaid":
return UserUnpaid(props);
case "user":
return User(props);
return new User(props);
case "totp":
return UserTotp(props);
case "noLogin":
return {};
default:
return User(props);
return new User(props);
}
}

Expand All @@ -83,3 +84,13 @@ export function PaymentCredits({ spent = 0, unspent = 0 } = {}) {
this.spentcredits = makeCredits(spent);
this.unspentcredits = makeCredits(unspent);
}

export function Identity({ userid, publickey }) {
return pki.generateKeys().then((keys) => {
const stringKeys = {
secretKey: pki.toHex(keys.secretKey),
publicKey: publickey
};
pki.importKeys(userid, stringKeys);
});
}
21 changes: 21 additions & 0 deletions teste2e/cypress/utils.js
Expand Up @@ -48,6 +48,27 @@ export const PROPOSAL_SUMMARY_STATUS_CLOSED = "closed";
export const PROPOSAL_BILLING_STATUS_CLOSED = 2;
const PROPOSAL_STATE_UNVETTED = 1;
const PROPOSAL_STATE_VETTED = 2;
const monthLabels = [
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec"
];

export const RECORD_DOMAINS = {
development: "Development",
marketing: "Marketing",
research: "Research",
design: "Design"
};

const findRecordFileByName = (record, name) =>
compose(
Expand Down