Zcash e2e basics (#329)
* e2e: remove more eth related stuff, update cypress * BE: e2e endpoints and interceptions for emails and blockchain reqs * e2e: expand browse test for proposals and comments * e2e: remove old tests, add new ones * e2e: intercept blockchain_get /contribution/addresses * be: disable sentry for e2e, add DEBUG to e2e endpoint register conditional * post-merge adjustments * fix merge related bug * post merge issue
This commit is contained in:
parent
a043c2dfa6
commit
e018f11018
|
@ -10,10 +10,9 @@ from flask_security import SQLAlchemyUserDatastore
|
||||||
from flask_sslify import SSLify
|
from flask_sslify import SSLify
|
||||||
from sentry_sdk.integrations.flask import FlaskIntegration
|
from sentry_sdk.integrations.flask import FlaskIntegration
|
||||||
from sentry_sdk.integrations.logging import LoggingIntegration
|
from sentry_sdk.integrations.logging import LoggingIntegration
|
||||||
|
from grant import commands, proposal, user, comment, milestone, admin, email, blockchain, task, rfp, e2e
|
||||||
from grant import commands, proposal, user, comment, milestone, admin, email, blockchain, task, rfp
|
|
||||||
from grant.extensions import bcrypt, migrate, db, ma, security, limiter
|
from grant.extensions import bcrypt, migrate, db, ma, security, limiter
|
||||||
from grant.settings import SENTRY_RELEASE, ENV
|
from grant.settings import SENTRY_RELEASE, ENV, E2E_TESTING, DEBUG
|
||||||
from grant.utils.auth import AuthException, handle_auth_error, get_authed_user
|
from grant.utils.auth import AuthException, handle_auth_error, get_authed_user
|
||||||
from grant.utils.exceptions import ValidationException
|
from grant.utils.exceptions import ValidationException
|
||||||
|
|
||||||
|
@ -52,19 +51,16 @@ def create_app(config_objects=["grant.settings"]):
|
||||||
else:
|
else:
|
||||||
return jsonify({"message": error_message}), err.code
|
return jsonify({"message": error_message}), err.code
|
||||||
|
|
||||||
|
|
||||||
@app.errorhandler(404)
|
@app.errorhandler(404)
|
||||||
def handle_notfound_error(err):
|
def handle_notfound_error(err):
|
||||||
error_message = "Unknown route '{} {}'".format(request.method, request.path)
|
error_message = "Unknown route '{} {}'".format(request.method, request.path)
|
||||||
return jsonify({"message": error_message}), 404
|
return jsonify({"message": error_message}), 404
|
||||||
|
|
||||||
|
|
||||||
@app.errorhandler(429)
|
@app.errorhandler(429)
|
||||||
def handle_limit_error(err):
|
def handle_limit_error(err):
|
||||||
app.logger.warn(f'Rate limited request to {request.method} {request.path} from ip {request.remote_addr}')
|
app.logger.warn(f'Rate limited request to {request.method} {request.path} from ip {request.remote_addr}')
|
||||||
return jsonify({"message": "You’ve done that too many times, please wait and try again later"}), 429
|
return jsonify({"message": "You’ve done that too many times, please wait and try again later"}), 429
|
||||||
|
|
||||||
|
|
||||||
@app.errorhandler(Exception)
|
@app.errorhandler(Exception)
|
||||||
def handle_exception(err):
|
def handle_exception(err):
|
||||||
sentry_sdk.capture_exception(err)
|
sentry_sdk.capture_exception(err)
|
||||||
|
@ -80,7 +76,7 @@ def create_app(config_objects=["grant.settings"]):
|
||||||
register_shellcontext(app)
|
register_shellcontext(app)
|
||||||
register_commands(app)
|
register_commands(app)
|
||||||
|
|
||||||
if not app.config.get("TESTING"):
|
if not (app.config.get("TESTING") or E2E_TESTING):
|
||||||
sentry_logging = LoggingIntegration(
|
sentry_logging = LoggingIntegration(
|
||||||
level=logging.INFO,
|
level=logging.INFO,
|
||||||
event_level=logging.ERROR
|
event_level=logging.ERROR
|
||||||
|
@ -130,6 +126,9 @@ def register_blueprints(app):
|
||||||
app.register_blueprint(blockchain.views.blueprint)
|
app.register_blueprint(blockchain.views.blueprint)
|
||||||
app.register_blueprint(task.views.blueprint)
|
app.register_blueprint(task.views.blueprint)
|
||||||
app.register_blueprint(rfp.views.blueprint)
|
app.register_blueprint(rfp.views.blueprint)
|
||||||
|
if E2E_TESTING and DEBUG:
|
||||||
|
print('Warning: e2e end-points are open, this should only be the case for development or testing')
|
||||||
|
app.register_blueprint(e2e.views.blueprint)
|
||||||
|
|
||||||
|
|
||||||
def register_shellcontext(app):
|
def register_shellcontext(app):
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
from . import views
|
|
@ -0,0 +1,150 @@
|
||||||
|
from datetime import datetime
|
||||||
|
from random import randint
|
||||||
|
from math import floor
|
||||||
|
|
||||||
|
from flask import Blueprint
|
||||||
|
|
||||||
|
from grant.comment.models import Comment
|
||||||
|
from grant.extensions import db
|
||||||
|
from grant.milestone.models import Milestone
|
||||||
|
from grant.proposal.models import (
|
||||||
|
Proposal,
|
||||||
|
ProposalArbiter,
|
||||||
|
ProposalContribution,
|
||||||
|
)
|
||||||
|
from grant.user.models import User, admin_user_schema
|
||||||
|
|
||||||
|
from grant.utils.enums import (
|
||||||
|
ProposalStatus,
|
||||||
|
ProposalStage,
|
||||||
|
Category,
|
||||||
|
ContributionStatus,
|
||||||
|
)
|
||||||
|
|
||||||
|
last_email = None
|
||||||
|
|
||||||
|
|
||||||
|
def create_proposals(count, category, stage, with_comments=False):
|
||||||
|
user = User.query.filter_by().first()
|
||||||
|
for i in range(count):
|
||||||
|
p = Proposal.create(
|
||||||
|
stage=stage,
|
||||||
|
status=ProposalStatus.LIVE,
|
||||||
|
title=f'Fake Proposal #{i} {category} {stage}',
|
||||||
|
content=f'My fake proposal content, numero {i}',
|
||||||
|
brief=f'This is proposal {i} generated by e2e testing',
|
||||||
|
category=category,
|
||||||
|
target="123.456",
|
||||||
|
payout_address="fake123",
|
||||||
|
deadline_duration=100
|
||||||
|
)
|
||||||
|
p.date_published = datetime.now()
|
||||||
|
p.team.append(user)
|
||||||
|
db.session.add(p)
|
||||||
|
db.session.flush()
|
||||||
|
num_ms = randint(1, 9)
|
||||||
|
for j in range(num_ms):
|
||||||
|
m = Milestone(
|
||||||
|
title=f'Fake MS {j}',
|
||||||
|
content=f'Fake milestone #{j} on fake proposal #{i} {category} {stage}!',
|
||||||
|
date_estimated=datetime.now(),
|
||||||
|
payout_percent=str(floor(1 / num_ms * 100)),
|
||||||
|
immediate_payout=j == 0,
|
||||||
|
proposal_id=p.id,
|
||||||
|
index=j
|
||||||
|
)
|
||||||
|
db.session.add(m)
|
||||||
|
# limit comment creation as it is slow
|
||||||
|
if i == 0 and with_comments:
|
||||||
|
for j in range(31):
|
||||||
|
c = Comment(
|
||||||
|
proposal_id=p.id,
|
||||||
|
user_id=user.id,
|
||||||
|
parent_comment_id=None,
|
||||||
|
content=f'Fake comment #{j} on fake proposal #{i} {category} {stage}!'
|
||||||
|
)
|
||||||
|
db.session.add(c)
|
||||||
|
if stage == ProposalStage.FUNDING_REQUIRED:
|
||||||
|
stake = p.create_contribution('1', None, True)
|
||||||
|
stake.confirm('fakestaketxid', '1')
|
||||||
|
db.session.add(stake)
|
||||||
|
db.session.flush()
|
||||||
|
fund = p.create_contribution('100', None, False)
|
||||||
|
fund.confirm('fakefundtxid0', '100')
|
||||||
|
db.session.add(fund)
|
||||||
|
db.session.flush()
|
||||||
|
p.status = ProposalStatus.LIVE
|
||||||
|
db.session.add(p)
|
||||||
|
db.session.flush()
|
||||||
|
|
||||||
|
# db.session.flush()
|
||||||
|
|
||||||
|
|
||||||
|
def create_user(key: str):
|
||||||
|
pw = f"e2epassword{key}"
|
||||||
|
user = User.create(
|
||||||
|
email_address=f"{key}@testing.e2e",
|
||||||
|
password=pw,
|
||||||
|
display_name=f"{key} Endtoenderson",
|
||||||
|
title=f"title{key}",
|
||||||
|
)
|
||||||
|
user.email_verification.has_verified = True
|
||||||
|
db.session.add(user)
|
||||||
|
db.session.flush()
|
||||||
|
dump = admin_user_schema.dump(user)
|
||||||
|
dump['password'] = pw
|
||||||
|
return dump
|
||||||
|
|
||||||
|
|
||||||
|
blueprint = Blueprint('e2e', __name__, url_prefix='/api/v1/e2e')
|
||||||
|
|
||||||
|
|
||||||
|
@blueprint.route("/setup", methods=["GET"])
|
||||||
|
def setup():
|
||||||
|
db.session.commit() # important, otherwise drop_all hangs
|
||||||
|
db.drop_all()
|
||||||
|
db.session.commit()
|
||||||
|
db.create_all()
|
||||||
|
db.session.commit()
|
||||||
|
default_user = create_user('default')
|
||||||
|
other_user = create_user('other')
|
||||||
|
create_proposals(12, Category.COMMUNITY, ProposalStage.FUNDING_REQUIRED, True)
|
||||||
|
create_proposals(13, Category.CORE_DEV, ProposalStage.WIP, False)
|
||||||
|
create_proposals(15, Category.DOCUMENTATION, ProposalStage.COMPLETED)
|
||||||
|
create_proposals(5, Category.ACCESSIBILITY, ProposalStage.FAILED)
|
||||||
|
db.session.commit()
|
||||||
|
return {
|
||||||
|
'default_user': default_user,
|
||||||
|
'other_user': other_user,
|
||||||
|
'proposalCounts': {
|
||||||
|
'categories': [
|
||||||
|
{"key": Category.COMMUNITY, "count": 12},
|
||||||
|
{"key": Category.CORE_DEV, "count": 13},
|
||||||
|
{"key": Category.DOCUMENTATION, "count": 15},
|
||||||
|
{"key": Category.ACCESSIBILITY, "count": 5},
|
||||||
|
],
|
||||||
|
'stages': [
|
||||||
|
{"key": ProposalStage.FUNDING_REQUIRED, "count": 12},
|
||||||
|
{"key": ProposalStage.WIP, "count": 13},
|
||||||
|
{"key": ProposalStage.COMPLETED, "count": 15},
|
||||||
|
{"key": ProposalStage.FAILED, "count": 5},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@blueprint.route("/email", methods=["GET"])
|
||||||
|
def get_email():
|
||||||
|
return last_email
|
||||||
|
|
||||||
|
|
||||||
|
@blueprint.route("/contribution/confirm", methods=["GET"])
|
||||||
|
def confirm_contributions():
|
||||||
|
contributions = ProposalContribution.query \
|
||||||
|
.filter(ProposalContribution.status == ContributionStatus.PENDING).all()
|
||||||
|
for c in contributions:
|
||||||
|
c.confirm('fakefundedtxid1', '23.456')
|
||||||
|
db.session.add(c)
|
||||||
|
c.proposal.set_funded_when_ready()
|
||||||
|
db.session.commit()
|
||||||
|
return {}
|
|
@ -1,13 +1,14 @@
|
||||||
|
from .subscription_settings import EmailSubscription, is_subscribed
|
||||||
|
from sendgrid.helpers.mail import Email, Mail, Content
|
||||||
|
from python_http_client import HTTPError
|
||||||
|
from grant.utils.misc import make_url
|
||||||
|
from sentry_sdk import capture_exception
|
||||||
|
from grant.settings import SENDGRID_API_KEY, SENDGRID_DEFAULT_FROM, SENDGRID_DEFAULT_FROMNAME, UI
|
||||||
|
from grant.settings import SENDGRID_API_KEY, SENDGRID_DEFAULT_FROM, UI, E2E_TESTING
|
||||||
import sendgrid
|
import sendgrid
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
from flask import render_template, Markup, current_app
|
from flask import render_template, Markup, current_app
|
||||||
from grant.settings import SENDGRID_API_KEY, SENDGRID_DEFAULT_FROM, SENDGRID_DEFAULT_FROMNAME, UI
|
|
||||||
from sentry_sdk import capture_exception
|
|
||||||
from grant.utils.misc import make_url
|
|
||||||
from python_http_client import HTTPError
|
|
||||||
from sendgrid.helpers.mail import Email, Mail, Content
|
|
||||||
|
|
||||||
from .subscription_settings import EmailSubscription, is_subscribed
|
|
||||||
|
|
||||||
default_template_args = {
|
default_template_args = {
|
||||||
'home_url': make_url('/'),
|
'home_url': make_url('/'),
|
||||||
|
@ -389,8 +390,14 @@ def sendgrid_send(mail):
|
||||||
type = mail.___type
|
type = mail.___type
|
||||||
try:
|
try:
|
||||||
sg = sendgrid.SendGridAPIClient(apikey=SENDGRID_API_KEY)
|
sg = sendgrid.SendGridAPIClient(apikey=SENDGRID_API_KEY)
|
||||||
res = sg.client.mail.send.post(request_body=mail.get())
|
if E2E_TESTING:
|
||||||
current_app.logger.info('Just sent an email to %s of type %s, response code: %s' % (to, type, res.status_code))
|
from grant.e2e import views
|
||||||
|
views.last_email = mail.get()
|
||||||
|
current_app.logger.info(f'Just set last_email for e2e to pickup, to: {to}, type: {type}')
|
||||||
|
else:
|
||||||
|
res = sg.client.mail.send.post(request_body=mail.get())
|
||||||
|
current_app.logger.info('Just sent an email to %s of type %s, response code: %s' %
|
||||||
|
(to, type, res.status_code))
|
||||||
except HTTPError as e:
|
except HTTPError as e:
|
||||||
current_app.logger.info('An HTTP error occured while sending an email to %s - %s: %s' %
|
current_app.logger.info('An HTTP error occured while sending an email to %s - %s: %s' %
|
||||||
(to, e.__class__.__name__, e))
|
(to, e.__class__.__name__, e))
|
||||||
|
|
|
@ -15,7 +15,9 @@ env.read_env()
|
||||||
ENV = env.str("FLASK_ENV", default="production")
|
ENV = env.str("FLASK_ENV", default="production")
|
||||||
DEBUG = ENV == "development"
|
DEBUG = ENV == "development"
|
||||||
SITE_URL = env.str('SITE_URL', default='https://zfnd.org')
|
SITE_URL = env.str('SITE_URL', default='https://zfnd.org')
|
||||||
SQLALCHEMY_DATABASE_URI = env.str("DATABASE_URL")
|
E2E_TESTING = env.str("E2E_TESTING", default=None)
|
||||||
|
E2E_DATABASE_URL = env.str("E2E_DATABASE_URL", default=None)
|
||||||
|
SQLALCHEMY_DATABASE_URI = E2E_DATABASE_URL if E2E_TESTING else env.str("DATABASE_URL")
|
||||||
SQLALCHEMY_ECHO = False # True will print queries to log
|
SQLALCHEMY_ECHO = False # True will print queries to log
|
||||||
QUEUES = ["default"]
|
QUEUES = ["default"]
|
||||||
SECRET_KEY = env.str("SECRET_KEY")
|
SECRET_KEY = env.str("SECRET_KEY")
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import requests
|
import requests
|
||||||
from grant.settings import BLOCKCHAIN_REST_API_URL, BLOCKCHAIN_API_SECRET
|
from grant.settings import BLOCKCHAIN_REST_API_URL, BLOCKCHAIN_API_SECRET, E2E_TESTING
|
||||||
|
|
||||||
|
|
||||||
### REST API ###
|
### REST API ###
|
||||||
|
@ -12,6 +12,8 @@ def handle_res(res):
|
||||||
|
|
||||||
|
|
||||||
def blockchain_get(path, params=None):
|
def blockchain_get(path, params=None):
|
||||||
|
if E2E_TESTING:
|
||||||
|
return blockchain_rest_e2e(path, params)
|
||||||
res = requests.get(
|
res = requests.get(
|
||||||
f'{BLOCKCHAIN_REST_API_URL}{path}',
|
f'{BLOCKCHAIN_REST_API_URL}{path}',
|
||||||
headers={'authorization': BLOCKCHAIN_API_SECRET},
|
headers={'authorization': BLOCKCHAIN_API_SECRET},
|
||||||
|
@ -21,9 +23,25 @@ def blockchain_get(path, params=None):
|
||||||
|
|
||||||
|
|
||||||
def blockchain_post(path, data=None):
|
def blockchain_post(path, data=None):
|
||||||
|
if E2E_TESTING:
|
||||||
|
return blockchain_rest_e2e(path, data)
|
||||||
res = requests.post(
|
res = requests.post(
|
||||||
f'{BLOCKCHAIN_REST_API_URL}{path}',
|
f'{BLOCKCHAIN_REST_API_URL}{path}',
|
||||||
headers={'authorization': BLOCKCHAIN_API_SECRET},
|
headers={'authorization': BLOCKCHAIN_API_SECRET},
|
||||||
json=data,
|
json=data,
|
||||||
)
|
)
|
||||||
return handle_res(res)
|
return handle_res(res)
|
||||||
|
|
||||||
|
|
||||||
|
def blockchain_rest_e2e(path, data):
|
||||||
|
if '/bootstrap' in path:
|
||||||
|
return {
|
||||||
|
'startHeight': 123,
|
||||||
|
'currentHeight': 456,
|
||||||
|
}
|
||||||
|
if '/contribution/addresses' in path:
|
||||||
|
return {
|
||||||
|
'transparent': 't123',
|
||||||
|
}
|
||||||
|
|
||||||
|
raise Exception(f'blockchain_post_e2e does not recognize path: {path}')
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"name": "Using fixtures to represent data",
|
||||||
|
"email": "hello@cypress.io",
|
||||||
|
"body": "Fixtures are a great way to mock data for responses to routes"
|
||||||
|
}
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,46 @@
|
||||||
|
/// <reference types="cypress"/>
|
||||||
|
|
||||||
|
describe("auth.recover", () => {
|
||||||
|
let stubs: any;
|
||||||
|
before(() => {
|
||||||
|
cy.request("http://localhost:5000/api/v1/e2e/setup").then(
|
||||||
|
r => (stubs = r.body)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should be able to recover account", () => {
|
||||||
|
cy.visit("/auth/sign-in");
|
||||||
|
cy.title().should("include", "ZF Grants - Sign in");
|
||||||
|
cy.get("html").then(el => (el[0].style.scrollBehavior = "auto"));
|
||||||
|
|
||||||
|
cy.contains("a", "Recover your acc").click();
|
||||||
|
cy.get("input[placeholder='email address']").type(
|
||||||
|
stubs.defaultUser.emailAddress
|
||||||
|
);
|
||||||
|
cy.contains("button", "Send").click();
|
||||||
|
cy.contains("Please check your email ");
|
||||||
|
|
||||||
|
cy.request("http://localhost:5000/api/v1/e2e/email").then(r => {
|
||||||
|
const txt = r.body.content.find((x: any) => x.type === "text/plain")
|
||||||
|
.value;
|
||||||
|
const url = txt.match(/http.*\/email\/recover.*/)[0];
|
||||||
|
cy.visit(url);
|
||||||
|
});
|
||||||
|
cy.contains("Reset");
|
||||||
|
cy.get("input[name='password']").type("muhnewpassword");
|
||||||
|
cy.get("input[name='passwordConfirm']").type("muhnewpassword");
|
||||||
|
cy.contains("Button", "Change pass").click();
|
||||||
|
cy.contains("Password has been reset");
|
||||||
|
cy.contains("button", "Sign in").click();
|
||||||
|
|
||||||
|
cy.get("input[placeholder='email']")
|
||||||
|
.clear()
|
||||||
|
.type(stubs.defaultUser.emailAddress);
|
||||||
|
cy.get("input[placeholder='password']")
|
||||||
|
.clear()
|
||||||
|
.type("muhnewpassword");
|
||||||
|
cy.contains("button", "Sign in").click();
|
||||||
|
cy.contains("default Endtoenderson");
|
||||||
|
cy.contains("button", "Edit profile");
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,51 @@
|
||||||
|
/// <reference types="cypress"/>
|
||||||
|
|
||||||
|
describe("auth.sign-in", () => {
|
||||||
|
let stubs: any;
|
||||||
|
before(() => {
|
||||||
|
cy.request("http://localhost:5000/api/v1/e2e/setup").then(
|
||||||
|
r => (stubs = r.body)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it("should be able to login", () => {
|
||||||
|
cy.visit("/");
|
||||||
|
cy.title().should("include", "ZF Grants - Home");
|
||||||
|
cy.get("html").then(el => (el[0].style.scrollBehavior = "auto"));
|
||||||
|
|
||||||
|
cy.contains("a", "Sign in").then(x => console.log(stubs));
|
||||||
|
cy.contains("a", "Sign in").click();
|
||||||
|
|
||||||
|
cy.get("input[placeholder='email']").type(stubs.defaultUser.emailAddress);
|
||||||
|
cy.get("input[placeholder='password']").type(
|
||||||
|
stubs.defaultUser.password + "bad"
|
||||||
|
);
|
||||||
|
cy.contains("button", "Sign in").click();
|
||||||
|
cy.contains("Invalid passw");
|
||||||
|
|
||||||
|
cy.get("input[placeholder='email']")
|
||||||
|
.clear()
|
||||||
|
.type("notin@thesystem.com");
|
||||||
|
cy.get("input[placeholder='password']")
|
||||||
|
.clear()
|
||||||
|
.type(stubs.defaultUser.password);
|
||||||
|
cy.contains("button", "Sign in").click();
|
||||||
|
cy.contains("No user exists");
|
||||||
|
|
||||||
|
cy.get("input[placeholder='email']")
|
||||||
|
.clear()
|
||||||
|
.type(stubs.defaultUser.emailAddress);
|
||||||
|
cy.get("input[placeholder='password']")
|
||||||
|
.clear()
|
||||||
|
.type(stubs.defaultUser.password);
|
||||||
|
cy.contains("button", "Sign in").click();
|
||||||
|
|
||||||
|
cy.contains("default Endtoenderson");
|
||||||
|
cy.contains("button", "Edit profile");
|
||||||
|
|
||||||
|
cy.get(".AuthButton").click();
|
||||||
|
cy.contains("a", "Settings").click();
|
||||||
|
cy.contains("Account");
|
||||||
|
cy.contains("Notifications");
|
||||||
|
cy.contains("Change Password");
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,14 +0,0 @@
|
||||||
/// <reference types="cypress"/>
|
|
||||||
import {
|
|
||||||
loadWeb3,
|
|
||||||
increaseTime,
|
|
||||||
syncTimeWithEvm,
|
|
||||||
randomString
|
|
||||||
} from "../helpers";
|
|
||||||
import { createDemoProposal, authenticateUser } from "../parts";
|
|
||||||
|
|
||||||
describe("authenticate", () => {
|
|
||||||
it("authenticates and creates if necessary", () => {
|
|
||||||
authenticateUser(cy, 0);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,47 +1,66 @@
|
||||||
/// <reference types="cypress"/>
|
/// <reference types="cypress"/>
|
||||||
import { loadWeb3, randomString, randomHex } from "../helpers";
|
|
||||||
|
|
||||||
describe("browse", () => {
|
describe("browse", () => {
|
||||||
|
let stubs;
|
||||||
|
before(() => {
|
||||||
|
cy.request("http://localhost:5000/api/v1/e2e/setup").then(
|
||||||
|
r => (stubs = r.body)
|
||||||
|
);
|
||||||
|
});
|
||||||
it("should load and be able to browse pages", () => {
|
it("should load and be able to browse pages", () => {
|
||||||
cy.visit("http://localhost:3000", { onBeforeLoad: loadWeb3(0) });
|
// cy.visit("http://localhost:3000");
|
||||||
cy.title().should("include", "Grant.io - Home");
|
cy.visit("/");
|
||||||
|
cy.title().should("include", "ZF Grants - Home");
|
||||||
|
cy.get("html").then(el => (el[0].style.scrollBehavior = "auto"));
|
||||||
|
|
||||||
// test hero create link
|
cy.contains("a", "Proposals").click();
|
||||||
cy.get('.Home-hero-buttons a[href="/create"]')
|
|
||||||
// {force: true} here overcomes a strange issue where the button moves up under the header
|
|
||||||
// this is likely a cypress scroll related problem
|
|
||||||
.click({ force: true });
|
|
||||||
cy.title().should("include", "Grant.io - Create a Proposal");
|
|
||||||
|
|
||||||
// browse back home
|
cy.contains(".ant-select", "Newest").click();
|
||||||
cy.get('.Header a[href="/"]').click();
|
cy.wait(100);
|
||||||
|
cy.contains(".ant-select-dropdown", "Oldest").click();
|
||||||
|
|
||||||
// test hero explore link
|
cy.contains(
|
||||||
cy.get('.Home-hero-buttons a[href="/proposals"]')
|
".ProposalCard",
|
||||||
// {force: true} here overcomes a strange issue where the button moves up under the header
|
"Fake Proposal #0 COMMUNITY FUNDING_REQ"
|
||||||
// this is likely a cypress-related problem
|
).click();
|
||||||
.click({ force: true });
|
cy.contains("h1", "Fake Proposal #0 COMMUNITY FUNDING_REQ");
|
||||||
cy.title().should("include", "Grant.io - Browse proposals");
|
cy.contains(".ant-tabs-tab", "Discussion").click();
|
||||||
|
cy.contains("Fake comment #30");
|
||||||
|
cy.contains("Fake comment #21");
|
||||||
|
cy.get(".ProposalComments").should("not.contain", "Fake comment #20");
|
||||||
|
cy.contains("button", "Older Comments").click();
|
||||||
|
cy.contains("Fake comment #11");
|
||||||
|
cy.get(".ProposalComments").should("not.contain", "Fake comment #10");
|
||||||
|
|
||||||
// browse back home
|
cy.contains("a", "Proposals").click();
|
||||||
cy.get('.Header a[href="/"]').click();
|
cy.contains(".ant-radio-wrapper", "All").click(); // FILTER
|
||||||
|
cy.contains(".ProposalCard", "Fake Proposal #0 COMMUNITY FUNDING_REQUIRED");
|
||||||
|
cy.contains(".ant-pagination-item", "5");
|
||||||
|
cy.contains(".ant-radio-wrapper", "In prog").click(); // FILTER
|
||||||
|
cy.contains(".ProposalCard", "Fake Proposal #0 CORE_DEV WIP");
|
||||||
|
cy.contains(".ant-pagination-item", "2").click();
|
||||||
|
cy.contains(".ProposalCard", "Fake Proposal #12 CORE_DEV WIP");
|
||||||
|
|
||||||
// browse to create via header link
|
cy.contains("a", "Requests").click();
|
||||||
cy.get('.Header a[href="/create"]').click();
|
|
||||||
cy.title().should("include", "Grant.io - Create a Proposal");
|
|
||||||
|
|
||||||
// browse to explore via header link
|
cy.contains("a", "Start a Proposal").click();
|
||||||
cy.get('.Header a[href="/proposals"]').click();
|
cy.title().should("include", "ZF Grants - Sign in");
|
||||||
cy.title().should("include", "Grant.io - Browse proposals");
|
cy.contains("Authorization required");
|
||||||
|
|
||||||
// browse footer links
|
// cy.contains("a", "About").click(); // external site
|
||||||
cy.get('.Footer a[href="/about"]').click();
|
cy.contains("a", "Contact").click({ force: true });
|
||||||
cy.title().should("include", "Grant.io - About");
|
cy.contains("h1", "Contact");
|
||||||
cy.get('.Footer a[href="/contact"]').click();
|
cy.contains("a", "Terms of").click({ force: true });
|
||||||
cy.title().should("include", "Grant.io - Contact");
|
cy.contains("h1", "Terms");
|
||||||
cy.get('.Footer a[href="/tos"]').click();
|
cy.contains("a", "Privacy").click({ force: true });
|
||||||
cy.title().should("include", "Grant.io - Terms of Service");
|
cy.contains("h1", "Privacy");
|
||||||
cy.get('.Footer a[href="/privacy"]').click();
|
cy.contains("a", "Code of").click({ force: true });
|
||||||
cy.title().should("include", "Grant.io - Privacy Policy");
|
cy.contains("h1", "Community");
|
||||||
|
|
||||||
|
cy.contains("a", "Sign in").click();
|
||||||
|
cy.contains("a", "Recover your").click();
|
||||||
|
cy.contains("h1", "Account Recovery");
|
||||||
|
cy.contains("a", "Sign in").click();
|
||||||
|
cy.contains("a", "Create a").click();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,47 +0,0 @@
|
||||||
/// <reference types="cypress"/>
|
|
||||||
import {
|
|
||||||
loadWeb3,
|
|
||||||
increaseTime,
|
|
||||||
syncTimeWithEvm,
|
|
||||||
randomString
|
|
||||||
} from "../helpers";
|
|
||||||
import { createDemoProposal, authenticateUser } from "../parts";
|
|
||||||
|
|
||||||
describe("proposal", () => {
|
|
||||||
const id = randomString();
|
|
||||||
const title = `[${id}] e2e create cancel`;
|
|
||||||
const amount = "1";
|
|
||||||
|
|
||||||
afterEach(function() {
|
|
||||||
if (this.currentTest.state === "failed") {
|
|
||||||
(Cypress as any).runner.stop();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it("authenticates and creates if necessary", () => {
|
|
||||||
authenticateUser(cy, 0);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("creates demo proposal", () => {
|
|
||||||
createDemoProposal(cy, title, amount);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("cancels the proposal", () => {
|
|
||||||
cy.contains(".Proposal-top-main-menu > .ant-btn", "Actions").click();
|
|
||||||
cy.contains(".ant-dropdown-menu-item", "Cancel proposal").click();
|
|
||||||
cy.contains(".ant-modal-footer > div button", "Confirm").click();
|
|
||||||
cy.contains("body", "Proposal didn’t get funded", { timeout: 20000 });
|
|
||||||
cy.get(".ant-modal-wrap").should("not.be.visible");
|
|
||||||
cy.contains(".Proposal-top-main-menu > .ant-btn", "Actions").click();
|
|
||||||
cy.contains(".ant-dropdown-menu-item", "Cancel proposal").should(
|
|
||||||
"have.attr",
|
|
||||||
"aria-disabled",
|
|
||||||
"true"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should appear unfunded to outsiders (account 9)", () => {
|
|
||||||
cy.url().then(url => cy.visit(url, { onBeforeLoad: loadWeb3(9) }));
|
|
||||||
cy.contains("body", "Proposal didn’t get funded", { timeout: 20000 });
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,173 +0,0 @@
|
||||||
/// <reference types="cypress"/>
|
|
||||||
import { loadWeb3, randomString, randomHex, syncTimeWithEvm } from "../helpers";
|
|
||||||
import { authenticateUser } from "../parts";
|
|
||||||
|
|
||||||
describe("create.flow", () => {
|
|
||||||
const time = new Date().toLocaleString();
|
|
||||||
const id = randomString();
|
|
||||||
const randomEthHex = randomHex(32);
|
|
||||||
const nextYear = new Date().getUTCFullYear() + 1;
|
|
||||||
const proposal = {
|
|
||||||
title: `[${id}] e2e create flow`,
|
|
||||||
brief: "e2e brief",
|
|
||||||
category: "Community", // .anticon-team
|
|
||||||
targetAmount: 5,
|
|
||||||
body: `#### e2e Proposal ${id} {enter} **created** ${time} `,
|
|
||||||
team: [
|
|
||||||
{
|
|
||||||
name: "Alisha Endtoend",
|
|
||||||
title: "QA Robot0",
|
|
||||||
ethAddress: `0x0000${randomEthHex}0000`,
|
|
||||||
emailAddress: `qa.alisha.${id}@grant.io`
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Billy Endtoend",
|
|
||||||
title: "QA Robot1",
|
|
||||||
ethAddress: `0x1111${randomEthHex}1111`,
|
|
||||||
emailAddress: `qa.billy.${id}@grant.io`
|
|
||||||
}
|
|
||||||
],
|
|
||||||
milestones: [
|
|
||||||
{
|
|
||||||
title: `e2e Milestone ${id} 0`,
|
|
||||||
body: `e2e Milestone ${id} {enter} body 0`,
|
|
||||||
date: {
|
|
||||||
y: nextYear,
|
|
||||||
m: "Jan",
|
|
||||||
expect: "January " + nextYear
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: `e2e Milestone ${id} 1`,
|
|
||||||
body: `e2e Milestone ${id} {enter} body 1`,
|
|
||||||
date: {
|
|
||||||
y: nextYear,
|
|
||||||
m: "Feb",
|
|
||||||
expect: "February " + nextYear
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
afterEach(function() {
|
|
||||||
if (this.currentTest.state === "failed") {
|
|
||||||
(Cypress as any).runner.stop();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
context("create flow wizard", () => {
|
|
||||||
it("authenticates and creates if necessary", () => {
|
|
||||||
authenticateUser(cy, 0);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("create flow step 1", () => {
|
|
||||||
cy.get('[href="/create"]').click();
|
|
||||||
syncTimeWithEvm(cy);
|
|
||||||
cy.get('.CreateFlow input[name="title"]').type(proposal.title);
|
|
||||||
cy.get('.CreateFlow textarea[name="brief"]').type(proposal.brief);
|
|
||||||
cy.contains("Select a category").click();
|
|
||||||
cy.get(".ant-select-dropdown li .anticon-team").click();
|
|
||||||
cy.get('.CreateFlow input[name="amountToRaise"]').type(
|
|
||||||
"" + proposal.targetAmount
|
|
||||||
);
|
|
||||||
cy.wait(1000);
|
|
||||||
cy.contains(".CreateFlow-footer-button", "Continue").click();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("create flow step 2", () => {
|
|
||||||
cy.get("button.TeamForm-add").click();
|
|
||||||
cy.get('.TeamMember-info input[name="name"]').type(proposal.team[1].name);
|
|
||||||
cy.get('.TeamMember-info input[name="title"]').type(
|
|
||||||
proposal.team[1].title
|
|
||||||
);
|
|
||||||
cy.get('.TeamMember-info input[name="ethAddress"]').type(
|
|
||||||
proposal.team[1].ethAddress
|
|
||||||
);
|
|
||||||
cy.get('.TeamMember-info input[name="emailAddress"]').type(
|
|
||||||
proposal.team[1].emailAddress
|
|
||||||
);
|
|
||||||
cy.get("button")
|
|
||||||
.contains("Save changes")
|
|
||||||
.click({ force: true });
|
|
||||||
cy.wait(1000);
|
|
||||||
cy.contains(".CreateFlow-footer-button", "Continue").click();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("create flow step 3", () => {
|
|
||||||
cy.get(".DraftEditor-editorContainer > div").type(proposal.body);
|
|
||||||
cy.get(".mde-tabs > :nth-child(2)").click();
|
|
||||||
cy.wait(1000);
|
|
||||||
cy.contains(".CreateFlow-footer-button", "Continue").click();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("create flow step 4", () => {
|
|
||||||
cy.get('input[name="title"]').type(proposal.milestones[0].title);
|
|
||||||
cy.get('textarea[name="body"]').type(proposal.milestones[0].body);
|
|
||||||
cy.get('input[placeholder="Expected completion date"]').click();
|
|
||||||
cy.get(".ant-calendar-month-panel-next-year-btn").click();
|
|
||||||
cy.get(".ant-calendar-month-panel-month")
|
|
||||||
.contains(proposal.milestones[0].date.m)
|
|
||||||
.click();
|
|
||||||
cy.get(".ant-calendar-picker-input").should(
|
|
||||||
"have.value",
|
|
||||||
proposal.milestones[0].date.expect
|
|
||||||
);
|
|
||||||
cy.get("button")
|
|
||||||
.contains("Add another milestone")
|
|
||||||
.click({ force: true });
|
|
||||||
cy.get('input[name="title"]')
|
|
||||||
.eq(1)
|
|
||||||
.type(proposal.milestones[1].title);
|
|
||||||
cy.get('textarea[name="body"]')
|
|
||||||
.eq(1)
|
|
||||||
.type(proposal.milestones[1].body);
|
|
||||||
cy.get('input[placeholder="Expected completion date"]')
|
|
||||||
.eq(1)
|
|
||||||
.click();
|
|
||||||
cy.get(".ant-calendar-month-panel-next-year-btn").click();
|
|
||||||
cy.get(".ant-calendar-month-panel-month")
|
|
||||||
.contains(proposal.milestones[1].date.m)
|
|
||||||
.click();
|
|
||||||
cy.get(".ant-calendar-picker-input")
|
|
||||||
.eq(1)
|
|
||||||
.should("have.value", proposal.milestones[1].date.expect);
|
|
||||||
cy.wait(1000);
|
|
||||||
cy.contains(".CreateFlow-footer-button", "Continue").click();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("create flow step 5", () => {
|
|
||||||
cy.window()
|
|
||||||
.then(w => (w as any).web3.eth.getAccounts())
|
|
||||||
.then(accts => {
|
|
||||||
cy.get('input[name="payOutAddress"]').type(accts[0]);
|
|
||||||
cy.get("button")
|
|
||||||
.contains("Add another trustee")
|
|
||||||
.click({ force: true });
|
|
||||||
cy.get(
|
|
||||||
'input[placeholder="0x8B0B72F8bDE212991135668922fD5acE557DE6aB"]'
|
|
||||||
)
|
|
||||||
.eq(1)
|
|
||||||
.type(accts[1]);
|
|
||||||
cy.get('input[name="deadline"][value="2592000"]').click({
|
|
||||||
force: true
|
|
||||||
});
|
|
||||||
cy.get('input[name="milestoneDeadline"][value="259200"]').click({
|
|
||||||
force: true
|
|
||||||
});
|
|
||||||
});
|
|
||||||
cy.wait(1000);
|
|
||||||
cy.contains(".CreateFlow-footer-button", "Continue").click();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("publishes the proposal", () => {
|
|
||||||
cy.get("button")
|
|
||||||
.contains("Publish")
|
|
||||||
.click();
|
|
||||||
cy.get(".CreateFinal-loader-text").contains("Deploying contract...");
|
|
||||||
cy.get(".CreateFinal-message-text a", { timeout: 20000 })
|
|
||||||
.contains("Click here")
|
|
||||||
.click();
|
|
||||||
cy.get(".Proposal-top-main-title").contains(proposal.title);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,61 +0,0 @@
|
||||||
/// <reference types="cypress"/>
|
|
||||||
import {
|
|
||||||
loadWeb3,
|
|
||||||
increaseTime,
|
|
||||||
syncTimeWithEvm,
|
|
||||||
randomString
|
|
||||||
} from "../helpers";
|
|
||||||
import { createDemoProposal, authenticateUser } from "../parts";
|
|
||||||
|
|
||||||
describe("create.fund.cancel", () => {
|
|
||||||
const id = randomString();
|
|
||||||
const title = `[${id}] e2e create fund cancel`;
|
|
||||||
const amount = "1";
|
|
||||||
|
|
||||||
afterEach(function() {
|
|
||||||
if (this.currentTest.state === "failed") {
|
|
||||||
(Cypress as any).runner.stop();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it("authenticates and creates if necessary", () => {
|
|
||||||
authenticateUser(cy, 0);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("create demo proposal", () => {
|
|
||||||
createDemoProposal(cy, title, amount);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("funds the proposal with account 5", () => {
|
|
||||||
cy.url().then(url => cy.visit(url, { onBeforeLoad: loadWeb3(5) }));
|
|
||||||
cy.get(".ant-input", { timeout: 20000 }).type(amount);
|
|
||||||
cy.get(".ant-form > .ant-btn").click();
|
|
||||||
cy.get(".ProposalCampaignBlock-fundingOver", { timeout: 20000 }).contains(
|
|
||||||
"Proposal has been funded"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("cancels the proposal (refund contributors)", () => {
|
|
||||||
cy.url().then(url => cy.visit(url, { onBeforeLoad: loadWeb3(0) }));
|
|
||||||
cy.contains(".Proposal-top-main-menu > .ant-btn", "Actions").click();
|
|
||||||
cy.contains(".ant-dropdown-menu-item", "Refund contributors").click();
|
|
||||||
cy.contains(".ant-modal-footer > div button", "Confirm").click();
|
|
||||||
cy.get(".ant-modal-wrap", { timeout: 20000 }).should("not.be.visible");
|
|
||||||
cy.contains(".Proposal-top-main-menu > .ant-btn", "Actions").click();
|
|
||||||
cy.contains(".ant-dropdown-menu-item", "Refund contributors").should(
|
|
||||||
"have.attr",
|
|
||||||
"aria-disabled",
|
|
||||||
"true"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("refunds the contributor (account 5)", () => {
|
|
||||||
cy.url().then(url => cy.visit(url, { onBeforeLoad: loadWeb3(5) }));
|
|
||||||
cy.contains(".ant-tabs-nav > :nth-child(1) > :nth-child(4)", "Refund", {
|
|
||||||
timeout: 20000
|
|
||||||
}).click();
|
|
||||||
// force disables cypress' auto scrolling which messes up UI in this case
|
|
||||||
cy.contains(".ant-btn", "Get your refund").click({ force: true });
|
|
||||||
cy.contains("body", "Your refund has been processed", { timeout: 20000 });
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,126 +0,0 @@
|
||||||
/// <reference types="cypress"/>
|
|
||||||
import {
|
|
||||||
loadWeb3,
|
|
||||||
randomString,
|
|
||||||
syncTimeWithEvm,
|
|
||||||
increaseTime
|
|
||||||
} from "../helpers";
|
|
||||||
import { createDemoProposal, fundProposal, authenticateUser } from "../parts";
|
|
||||||
|
|
||||||
describe("create.fund.complete.minority-no-votes", () => {
|
|
||||||
const id = randomString();
|
|
||||||
const title = `[${id}] e2e minority no-votes complete`;
|
|
||||||
const amount = "1";
|
|
||||||
|
|
||||||
afterEach(function() {
|
|
||||||
if (this.currentTest.state === "failed") {
|
|
||||||
//(Cypress as any).runner.stop();
|
|
||||||
this.skip();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it("authenticates and creates if necessary", () => {
|
|
||||||
authenticateUser(cy, 0);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("creates demo proposal", () => {
|
|
||||||
createDemoProposal(cy, title, amount);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("funds the proposal from accounts 5, 6 & 7", () => {
|
|
||||||
fundProposal(cy, 5, 0.1);
|
|
||||||
fundProposal(cy, 6, 0.2);
|
|
||||||
fundProposal(cy, 7, 0.7);
|
|
||||||
cy.get(".ProposalCampaignBlock-fundingOver", { timeout: 20000 }).contains(
|
|
||||||
"Proposal has been funded"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("receives initial payout", () => {
|
|
||||||
// MILESTONE 1
|
|
||||||
syncTimeWithEvm(cy);
|
|
||||||
cy.url().then(url => cy.visit(url, { onBeforeLoad: loadWeb3(0) }));
|
|
||||||
cy.get(".MilestoneAction-top > div > .ant-btn", { timeout: 20000 }).click();
|
|
||||||
cy.contains(
|
|
||||||
".MilestoneAction-top > div > .ant-btn",
|
|
||||||
"Receive initial payout",
|
|
||||||
{ timeout: 20000 }
|
|
||||||
).click();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("requests milestone 2 payout", () => {
|
|
||||||
// MILESTONE 2
|
|
||||||
cy.contains(
|
|
||||||
".MilestoneAction-top > div > .ant-btn",
|
|
||||||
"Request milestone payout",
|
|
||||||
{ timeout: 20000 }
|
|
||||||
).click();
|
|
||||||
cy.contains(".MilestoneAction-progress-text", "voted against payout", {
|
|
||||||
timeout: 20000
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("minority funder (acct 5) votes no", () => {
|
|
||||||
// VOTE NO
|
|
||||||
syncTimeWithEvm(cy);
|
|
||||||
cy.url().then(url => cy.visit(url, { onBeforeLoad: loadWeb3(5) }));
|
|
||||||
cy.contains(".ant-btn", "Vote against payout", { timeout: 20000 })
|
|
||||||
.click()
|
|
||||||
.should("have.class", "ant-btn-loading");
|
|
||||||
cy.contains(".ant-btn", "Revert vote against payout", { timeout: 20000 });
|
|
||||||
});
|
|
||||||
|
|
||||||
it("expires milestone 2 voting period & receives payout", () => {
|
|
||||||
// EXPIRE
|
|
||||||
increaseTime(cy, 70000);
|
|
||||||
// RECEIVE PAYOUT
|
|
||||||
cy.url().then(url => cy.visit(url, { onBeforeLoad: loadWeb3(0) }));
|
|
||||||
cy.contains(
|
|
||||||
".MilestoneAction-top > div > .ant-btn",
|
|
||||||
"Receive milestone payout",
|
|
||||||
{ timeout: 20000 }
|
|
||||||
).click();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("requests milestone 3 payout", () => {
|
|
||||||
// MILESTONE 3
|
|
||||||
cy.contains(
|
|
||||||
".MilestoneAction-top > div > .ant-btn",
|
|
||||||
"Request milestone payout",
|
|
||||||
{ timeout: 20000 }
|
|
||||||
).click();
|
|
||||||
cy.contains(".MilestoneAction-progress-text", "voted against payout", {
|
|
||||||
timeout: 20000
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("minority funder (acct 5) votes no", () => {
|
|
||||||
// VOTE NO
|
|
||||||
syncTimeWithEvm(cy);
|
|
||||||
cy.url().then(url => cy.visit(url, { onBeforeLoad: loadWeb3(5) }));
|
|
||||||
cy.contains(".ant-btn", "Vote against payout", { timeout: 20000 })
|
|
||||||
.click()
|
|
||||||
.should("have.class", "ant-btn-loading");
|
|
||||||
cy.contains(".ant-btn", "Revert vote against payout", { timeout: 20000 });
|
|
||||||
});
|
|
||||||
|
|
||||||
it("expires milestone 3 voting period & receives payout", () => {
|
|
||||||
// EXPIRE
|
|
||||||
increaseTime(cy, 70000);
|
|
||||||
// RECEIVE PAYOUT
|
|
||||||
cy.url().then(url => cy.visit(url, { onBeforeLoad: loadWeb3(0) }));
|
|
||||||
cy.contains(
|
|
||||||
".MilestoneAction-top > div > .ant-btn",
|
|
||||||
"Receive milestone payout",
|
|
||||||
{ timeout: 20000 }
|
|
||||||
).click();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should not have receive button", () => {
|
|
||||||
cy.contains(
|
|
||||||
".MilestoneAction-top > div > .ant-btn",
|
|
||||||
"Receive milestone payout",
|
|
||||||
{ timeout: 20000 }
|
|
||||||
).should("not.exist");
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,103 +0,0 @@
|
||||||
/// <reference types="cypress"/>
|
|
||||||
import {
|
|
||||||
loadWeb3,
|
|
||||||
increaseTime,
|
|
||||||
syncTimeWithEvm,
|
|
||||||
randomString
|
|
||||||
} from "../helpers";
|
|
||||||
import { createDemoProposal, authenticateUser } from "../parts";
|
|
||||||
|
|
||||||
describe("create.fund.complete", () => {
|
|
||||||
const id = randomString();
|
|
||||||
const title = `[${id}] e2e create fund complete`;
|
|
||||||
const amount = "1";
|
|
||||||
|
|
||||||
afterEach(function() {
|
|
||||||
if (this.currentTest.state === "failed") {
|
|
||||||
//(Cypress as any).runner.stop();
|
|
||||||
this.skip();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it("authenticates and creates if necessary", () => {
|
|
||||||
authenticateUser(cy, 0);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("create demo proposal", () => {
|
|
||||||
createDemoProposal(cy, title, amount);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("funds the proposal with account 5", () => {
|
|
||||||
cy.url().then(url => cy.visit(url, { onBeforeLoad: loadWeb3(5) }));
|
|
||||||
cy.get(".ant-input", { timeout: 20000 }).type(amount);
|
|
||||||
cy.get(".ant-form > .ant-btn").click();
|
|
||||||
cy.get(".ProposalCampaignBlock-fundingOver", { timeout: 20000 }).contains(
|
|
||||||
"Proposal has been funded"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("receives initial payout", () => {
|
|
||||||
// MILESTONE 1
|
|
||||||
cy.url().then(url => cy.visit(url, { onBeforeLoad: loadWeb3(0) }));
|
|
||||||
cy.contains(
|
|
||||||
".MilestoneAction-top > div > .ant-btn",
|
|
||||||
"Request initial payout",
|
|
||||||
{ timeout: 20000 }
|
|
||||||
)
|
|
||||||
.as("RequestPayout")
|
|
||||||
.click();
|
|
||||||
cy.contains(
|
|
||||||
".MilestoneAction-top > div > .ant-btn",
|
|
||||||
"Receive initial payout",
|
|
||||||
{ timeout: 20000 }
|
|
||||||
).click();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("requests and receives milestone 2 payout", () => {
|
|
||||||
// MILESTONE 2
|
|
||||||
cy.contains(
|
|
||||||
".MilestoneAction-top > div > .ant-btn",
|
|
||||||
"Request milestone payout",
|
|
||||||
{ timeout: 20000 }
|
|
||||||
).click();
|
|
||||||
cy.contains(".MilestoneAction-progress-text", "voted against payout", {
|
|
||||||
timeout: 20000
|
|
||||||
});
|
|
||||||
// EXPIRE
|
|
||||||
increaseTime(cy, 70000);
|
|
||||||
cy.url().then(url => cy.visit(url, { onBeforeLoad: loadWeb3(0) }));
|
|
||||||
cy.contains(
|
|
||||||
".MilestoneAction-top > div > .ant-btn",
|
|
||||||
"Receive milestone payout",
|
|
||||||
{ timeout: 20000 }
|
|
||||||
).click();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("requests and receives milestone 3 payout", () => {
|
|
||||||
// MILESTONE 3
|
|
||||||
cy.contains(
|
|
||||||
".MilestoneAction-top > div > .ant-btn",
|
|
||||||
"Request milestone payout",
|
|
||||||
{ timeout: 20000 }
|
|
||||||
).click();
|
|
||||||
cy.contains(".MilestoneAction-progress-text", "voted against payout", {
|
|
||||||
timeout: 20000
|
|
||||||
});
|
|
||||||
// EXPIRE
|
|
||||||
increaseTime(cy, 70000);
|
|
||||||
cy.url().then(url => cy.visit(url, { onBeforeLoad: loadWeb3(0) }));
|
|
||||||
cy.contains(
|
|
||||||
".MilestoneAction-top > div > .ant-btn",
|
|
||||||
"Receive milestone payout",
|
|
||||||
{ timeout: 20000 }
|
|
||||||
).click();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should not have receive button", () => {
|
|
||||||
cy.contains(
|
|
||||||
".MilestoneAction-top > div > .ant-btn",
|
|
||||||
"Receive milestone payout",
|
|
||||||
{ timeout: 20000 }
|
|
||||||
).should("not.exist");
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,60 +0,0 @@
|
||||||
/// <reference types="cypress"/>
|
|
||||||
import { loadWeb3, randomString, syncTimeWithEvm } from "../helpers";
|
|
||||||
import { createDemoProposal, fundProposal, authenticateUser } from "../parts";
|
|
||||||
|
|
||||||
describe("create.fund.ms2.majority-no-vote", () => {
|
|
||||||
const id = randomString();
|
|
||||||
const title = `[${id}] e2e ms2 majority no-vote`;
|
|
||||||
const amount = "1";
|
|
||||||
|
|
||||||
it("authenticates and creates if necessary", () => {
|
|
||||||
authenticateUser(cy, 0);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("creates demo proposal", () => {
|
|
||||||
createDemoProposal(cy, title, amount);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("fund the proposal with 5th account", () => {
|
|
||||||
fundProposal(cy, 5, 1);
|
|
||||||
cy.get(".ProposalCampaignBlock-fundingOver", { timeout: 20000 }).contains(
|
|
||||||
"Proposal has been funded"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("receives initial payout for milestone 1", () => {
|
|
||||||
// MILESTONE 1
|
|
||||||
syncTimeWithEvm(cy);
|
|
||||||
cy.url().then(url => cy.visit(url, { onBeforeLoad: loadWeb3(0) }));
|
|
||||||
cy.get(".MilestoneAction-top > div > .ant-btn", { timeout: 20000 }).click();
|
|
||||||
cy.contains(
|
|
||||||
".MilestoneAction-top > div > .ant-btn",
|
|
||||||
"Receive initial payout",
|
|
||||||
{ timeout: 20000 }
|
|
||||||
).click();
|
|
||||||
cy.contains(
|
|
||||||
".MilestoneAction-top > div > .ant-btn",
|
|
||||||
"Request milestone payout",
|
|
||||||
{ timeout: 20000 }
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("requests milestone 2 payout", () => {
|
|
||||||
// MILESTONE 2
|
|
||||||
cy.contains(
|
|
||||||
".MilestoneAction-top > div > .ant-btn",
|
|
||||||
"Request milestone payout",
|
|
||||||
{ timeout: 20000 }
|
|
||||||
).click();
|
|
||||||
cy.contains(".MilestoneAction-progress-text", "voted against payout", {
|
|
||||||
timeout: 20000
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("vote against milestone 2 payout as account 5", () => {
|
|
||||||
syncTimeWithEvm(cy);
|
|
||||||
cy.url().then(url => cy.visit(url, { onBeforeLoad: loadWeb3(5) }));
|
|
||||||
cy.contains(".ant-btn", "Vote against payout", { timeout: 20000 }).click();
|
|
||||||
cy.contains(".ant-btn", "Revert vote against payout", { timeout: 20000 });
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,86 +0,0 @@
|
||||||
/// <reference types="cypress"/>
|
|
||||||
import {
|
|
||||||
loadWeb3,
|
|
||||||
randomString,
|
|
||||||
syncTimeWithEvm,
|
|
||||||
increaseTime
|
|
||||||
} from "../helpers";
|
|
||||||
import { createDemoProposal, fundProposal, authenticateUser } from "../parts";
|
|
||||||
|
|
||||||
describe("create.fund.ms2.no-vote.re-vote", () => {
|
|
||||||
const id = randomString();
|
|
||||||
const title = `[${id}] e2e ms2 no-vote expire re-vote`;
|
|
||||||
const amount = "1";
|
|
||||||
|
|
||||||
afterEach(function() {
|
|
||||||
if (this.currentTest.state === "failed") {
|
|
||||||
//(Cypress as any).runner.stop();
|
|
||||||
this.skip();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it("authenticates and creates if necessary", () => {
|
|
||||||
authenticateUser(cy, 0);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("creates demo proposal", () => {
|
|
||||||
createDemoProposal(cy, title, amount);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("fund the proposal with 5th account", () => {
|
|
||||||
fundProposal(cy, 5, 1);
|
|
||||||
cy.get(".ProposalCampaignBlock-fundingOver", { timeout: 20000 }).contains(
|
|
||||||
"Proposal has been funded"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("receives initial payout for milestone 1", () => {
|
|
||||||
// MILESTONE 1
|
|
||||||
syncTimeWithEvm(cy);
|
|
||||||
cy.url().then(url => cy.visit(url, { onBeforeLoad: loadWeb3(0) }));
|
|
||||||
cy.get(".MilestoneAction-top > div > .ant-btn", { timeout: 20000 }).click();
|
|
||||||
cy.contains(
|
|
||||||
".MilestoneAction-top > div > .ant-btn",
|
|
||||||
"Receive initial payout",
|
|
||||||
{ timeout: 20000 }
|
|
||||||
).click();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("request milestone 2 payout", () => {
|
|
||||||
// MILESTONE 2
|
|
||||||
cy.contains(
|
|
||||||
".MilestoneAction-top > div > .ant-btn",
|
|
||||||
"Request milestone payout",
|
|
||||||
{ timeout: 20000 }
|
|
||||||
).click();
|
|
||||||
cy.contains(".MilestoneAction-progress-text", "voted against payout", {
|
|
||||||
timeout: 20000
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("vote against milestone 2 payout as 5th account", () => {
|
|
||||||
// reload page with 5th account
|
|
||||||
syncTimeWithEvm(cy);
|
|
||||||
cy.url().then(url => cy.visit(url, { onBeforeLoad: loadWeb3(5) }));
|
|
||||||
cy.contains(".ant-btn", "Vote against payout", { timeout: 20000 })
|
|
||||||
.click()
|
|
||||||
.should("have.class", "ant-btn-loading");
|
|
||||||
cy.contains(".ant-btn", "Revert vote against payout", { timeout: 20000 });
|
|
||||||
});
|
|
||||||
|
|
||||||
it("milestone 2 vote expires and payout is requested again", () => {
|
|
||||||
// EXPIRE
|
|
||||||
increaseTime(cy, 70000);
|
|
||||||
cy.url().then(url => cy.visit(url, { onBeforeLoad: loadWeb3(0) }));
|
|
||||||
cy.contains("Payout was voted against");
|
|
||||||
// RE-REQUEST PAYOUT
|
|
||||||
cy.contains(
|
|
||||||
".MilestoneAction-top > div > .ant-btn",
|
|
||||||
"Request milestone payout",
|
|
||||||
{ timeout: 20000 }
|
|
||||||
).click();
|
|
||||||
cy.contains(".MilestoneAction-progress-text", "voted against payout", {
|
|
||||||
timeout: 20000
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,69 +0,0 @@
|
||||||
/// <reference types="cypress"/>
|
|
||||||
import {
|
|
||||||
loadWeb3,
|
|
||||||
randomString,
|
|
||||||
syncTimeWithEvm,
|
|
||||||
increaseTime
|
|
||||||
} from "../helpers";
|
|
||||||
import { createDemoProposal, fundProposal, authenticateUser } from "../parts";
|
|
||||||
|
|
||||||
describe("create.fund.ms2.refund-after-payout", () => {
|
|
||||||
const id = randomString();
|
|
||||||
const title = `[${id}] e2e ms2 refund after payout`;
|
|
||||||
const amount = "1";
|
|
||||||
|
|
||||||
afterEach(function() {
|
|
||||||
if (this.currentTest.state === "failed") {
|
|
||||||
//(Cypress as any).runner.stop();
|
|
||||||
this.skip();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it("authenticates and creates if necessary", () => {
|
|
||||||
authenticateUser(cy, 0);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("creates demo proposal", () => {
|
|
||||||
createDemoProposal(cy, title, amount);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("fund the proposal with account 5", () => {
|
|
||||||
fundProposal(cy, 5, 1);
|
|
||||||
cy.get(".ProposalCampaignBlock-fundingOver", { timeout: 20000 }).contains(
|
|
||||||
"Proposal has been funded"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("receives initial payout for milestone 1", () => {
|
|
||||||
// MILESTONE 1
|
|
||||||
syncTimeWithEvm(cy);
|
|
||||||
cy.url().then(url => cy.visit(url, { onBeforeLoad: loadWeb3(0) }));
|
|
||||||
cy.get(".MilestoneAction-top > div > .ant-btn", { timeout: 20000 }).click();
|
|
||||||
cy.contains(
|
|
||||||
".MilestoneAction-top > div > .ant-btn",
|
|
||||||
"Receive initial payout",
|
|
||||||
{ timeout: 20000 }
|
|
||||||
).click();
|
|
||||||
cy.contains(
|
|
||||||
".MilestoneAction-top > div > .ant-btn",
|
|
||||||
"Request milestone payout",
|
|
||||||
{ timeout: 20000 }
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("majority refund vote and get refund (account 5)", () => {
|
|
||||||
// REFUND
|
|
||||||
cy.url().then(url => cy.visit(url, { onBeforeLoad: loadWeb3(5) }));
|
|
||||||
cy.contains(".ant-tabs-nav > :nth-child(1) > :nth-child(4)", "Refund", {
|
|
||||||
timeout: 20000
|
|
||||||
}).click();
|
|
||||||
// INCREASE TIME
|
|
||||||
increaseTime(cy, 70000);
|
|
||||||
// force disables cypress' auto scrolling which messes up UI in this case
|
|
||||||
cy.contains(".ant-btn", "Vote for refund").click({ force: true });
|
|
||||||
cy.contains(".ant-btn", "Get your refund", { timeout: 20000 }).click({
|
|
||||||
force: true
|
|
||||||
});
|
|
||||||
cy.contains("body", "Your refund has been processed", { timeout: 20000 });
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,93 +0,0 @@
|
||||||
/// <reference types="cypress"/>
|
|
||||||
import {
|
|
||||||
loadWeb3,
|
|
||||||
randomString,
|
|
||||||
syncTimeWithEvm,
|
|
||||||
increaseTime
|
|
||||||
} from "../helpers";
|
|
||||||
import { createDemoProposal, fundProposal, authenticateUser } from "../parts";
|
|
||||||
|
|
||||||
describe("create.fund.ms2.revert-no-vote", () => {
|
|
||||||
const id = randomString();
|
|
||||||
const title = `[${id}] e2e ms2 revert no-vote`;
|
|
||||||
const amount = "1";
|
|
||||||
|
|
||||||
afterEach(function() {
|
|
||||||
if (this.currentTest.state === "failed") {
|
|
||||||
//(Cypress as any).runner.stop();
|
|
||||||
this.skip();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it("authenticates and creates if necessary", () => {
|
|
||||||
authenticateUser(cy, 0);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("creates demo proposal", () => {
|
|
||||||
createDemoProposal(cy, title, amount);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("funds the proposal with 5th account", () => {
|
|
||||||
fundProposal(cy, 5, 1);
|
|
||||||
cy.get(".ProposalCampaignBlock-fundingOver", { timeout: 20000 }).contains(
|
|
||||||
"Proposal has been funded"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("receives initial payout for milestone 1", () => {
|
|
||||||
// MILESTONE 1
|
|
||||||
syncTimeWithEvm(cy);
|
|
||||||
cy.url().then(url => cy.visit(url, { onBeforeLoad: loadWeb3(0) }));
|
|
||||||
cy.get(".MilestoneAction-top > div > .ant-btn", { timeout: 20000 }).click();
|
|
||||||
cy.contains(
|
|
||||||
".MilestoneAction-top > div > .ant-btn",
|
|
||||||
"Receive initial payout",
|
|
||||||
{ timeout: 20000 }
|
|
||||||
).click();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("requests milestone 2 payout", () => {
|
|
||||||
// MILESTONE 2
|
|
||||||
cy.contains(
|
|
||||||
".MilestoneAction-top > div > .ant-btn",
|
|
||||||
"Request milestone payout",
|
|
||||||
{ timeout: 20000 }
|
|
||||||
).click();
|
|
||||||
cy.contains(".MilestoneAction-progress-text", "voted against payout", {
|
|
||||||
timeout: 20000
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("votes against milestone 2 payout as account 5 and then reverts the vote", () => {
|
|
||||||
// NO VOTE... REVERT
|
|
||||||
syncTimeWithEvm(cy);
|
|
||||||
cy.url().then(url => cy.visit(url, { onBeforeLoad: loadWeb3(5) }));
|
|
||||||
cy.contains(".ant-btn", "Vote against payout", { timeout: 20000 })
|
|
||||||
.click()
|
|
||||||
.should("have.class", "ant-btn-loading");
|
|
||||||
cy.contains(".ant-btn", "Revert vote against payout", { timeout: 20000 })
|
|
||||||
.click()
|
|
||||||
.should("have.class", "ant-btn-loading");
|
|
||||||
cy.contains(".ant-btn", "Vote against payout", { timeout: 20000 });
|
|
||||||
});
|
|
||||||
|
|
||||||
it("milestone 2 vote expires and payout is received", () => {
|
|
||||||
// EXPIRE
|
|
||||||
increaseTime(cy, 70000);
|
|
||||||
// PAYOUT
|
|
||||||
cy.url().then(url => cy.visit(url, { onBeforeLoad: loadWeb3(0) }));
|
|
||||||
cy.contains(
|
|
||||||
".MilestoneAction-top > div > .ant-btn",
|
|
||||||
"Receive milestone payout"
|
|
||||||
).click();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("milestone 3 becomes active", () => {
|
|
||||||
// MILESTONE 3
|
|
||||||
cy.contains(
|
|
||||||
".MilestoneAction-top > div > .ant-btn",
|
|
||||||
"Request milestone payout",
|
|
||||||
{ timeout: 20000 }
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -0,0 +1,77 @@
|
||||||
|
/// <reference types="cypress"/>
|
||||||
|
|
||||||
|
describe("proposal.fund", () => {
|
||||||
|
let stubs: any;
|
||||||
|
before(() => {
|
||||||
|
cy.request("http://localhost:5000/api/v1/e2e/setup").then(
|
||||||
|
r => (stubs = r.body)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should be able to make anonymous contributions", () => {
|
||||||
|
cy.visit("/proposals");
|
||||||
|
cy.title().should("include", "ZF Grants - Browse proposals");
|
||||||
|
cy.get("html").then(el => (el[0].style.scrollBehavior = "auto"));
|
||||||
|
|
||||||
|
cy.contains(".ant-radio-wrapper", "Funding req").click();
|
||||||
|
cy.contains(
|
||||||
|
".ProposalCard",
|
||||||
|
"Fake Proposal #11 COMMUNITY FUNDING_REQUIRED"
|
||||||
|
).click();
|
||||||
|
|
||||||
|
cy.get("input[name='amountToRaise']").type("100");
|
||||||
|
cy.contains("Cannot exceed maximum");
|
||||||
|
cy.get("input[name='amountToRaise']")
|
||||||
|
.clear()
|
||||||
|
.type("23.456");
|
||||||
|
cy.contains("button", "Fund this").click();
|
||||||
|
cy.contains("anonymous");
|
||||||
|
cy.contains("button", "I accept").click();
|
||||||
|
cy.contains("button", "I’ve sent it").click();
|
||||||
|
cy.contains("button", "Done").click();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should be able to make contributions", () => {
|
||||||
|
cy.visit("/auth/sign-in");
|
||||||
|
cy.get("html").then(el => (el[0].style.scrollBehavior = "auto"));
|
||||||
|
|
||||||
|
// login
|
||||||
|
cy.get("input[placeholder='email']")
|
||||||
|
.clear()
|
||||||
|
.type(stubs.defaultUser.emailAddress);
|
||||||
|
cy.get("input[placeholder='password']")
|
||||||
|
.clear()
|
||||||
|
.type(stubs.defaultUser.password);
|
||||||
|
cy.contains("button", "Sign in").click();
|
||||||
|
|
||||||
|
cy.contains("default Endtoenderson");
|
||||||
|
|
||||||
|
cy.contains("a", "Proposals").click();
|
||||||
|
cy.title().should("include", "ZF Grants - Browse proposals");
|
||||||
|
cy.contains(".ant-radio-wrapper", "Funding req").click();
|
||||||
|
cy.contains(
|
||||||
|
".ProposalCard",
|
||||||
|
"Fake Proposal #11 COMMUNITY FUNDING_REQUIRED"
|
||||||
|
).click();
|
||||||
|
|
||||||
|
cy.get("input[name='amountToRaise']").type("23.456");
|
||||||
|
cy.contains("button", "Fund this").click();
|
||||||
|
cy.contains("Make your contribution");
|
||||||
|
cy.contains("button", "I’ve sent it").click();
|
||||||
|
cy.contains("a", "funded tab").click();
|
||||||
|
cy.contains(".ant-tabs-tab-active", "Funded");
|
||||||
|
cy.contains(
|
||||||
|
".ProfileContribution",
|
||||||
|
"Fake Proposal #11 COMMUNITY FUNDING_REQUIRED"
|
||||||
|
);
|
||||||
|
cy.contains(".ProfileContribution", "23.456");
|
||||||
|
|
||||||
|
cy.request("http://localhost:5000/api/v1/e2e/contribution/confirm");
|
||||||
|
cy.contains(
|
||||||
|
".ProfileContribution a",
|
||||||
|
"Fake Proposal #11 COMMUNITY FUNDING_REQUIRED"
|
||||||
|
).click();
|
||||||
|
cy.contains("Proposal has been funded");
|
||||||
|
cy.contains("a", "default Endtoenderson").click();
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,15 +0,0 @@
|
||||||
/// <reference types="cypress"/>
|
|
||||||
import {
|
|
||||||
loadWeb3,
|
|
||||||
increaseTime,
|
|
||||||
randomString,
|
|
||||||
syncTimeWithEvm
|
|
||||||
} from "../helpers";
|
|
||||||
|
|
||||||
// describe("sandbox", () => {
|
|
||||||
// it("how to increase time", () => {
|
|
||||||
// cy.visit("http://localhost:3000", { onBeforeLoad: loadWeb3(0) });
|
|
||||||
// // increase time on browser and ganache
|
|
||||||
// increaseTime(cy, 60000);
|
|
||||||
// });
|
|
||||||
// });
|
|
|
@ -14,7 +14,12 @@
|
||||||
// ***********************************************************
|
// ***********************************************************
|
||||||
|
|
||||||
// Import commands.js using ES2015 syntax:
|
// Import commands.js using ES2015 syntax:
|
||||||
import './commands'
|
import "./commands";
|
||||||
|
|
||||||
// Alternatively you can use CommonJS syntax:
|
// Alternatively you can use CommonJS syntax:
|
||||||
// require('./commands')
|
// require('./commands')
|
||||||
|
|
||||||
|
Cypress.on("uncaught:exception", (err, runnable) => {
|
||||||
|
// do not stop for uncaught exceptions (wave.js)
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
|
@ -8,13 +8,10 @@
|
||||||
"cypress:run": "cypress run"
|
"cypress:run": "cypress run"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@cypress/webpack-preprocessor": "^2.0.1",
|
"@cypress/webpack-preprocessor": "^4.0.3",
|
||||||
"@types/web3": "^1.0.3",
|
"cypress": "^3.1.5",
|
||||||
"cypress": "^3.1.0",
|
|
||||||
"eth-sig-util": "^2.1.0",
|
|
||||||
"ts-loader": "^5.0.0",
|
"ts-loader": "^5.0.0",
|
||||||
"typescript": "^3.0.3",
|
"typescript": "^3.0.3",
|
||||||
"web3": "^1.0.0-beta.36",
|
|
||||||
"webpack": "^4.17.2"
|
"webpack": "^4.17.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
4326
e2e/yarn.lock
4326
e2e/yarn.lock
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue