# -*- coding: utf8 -*-
# This file is part of PyBossa.
#
# Copyright (C) 2015 SciFabric LTD.
#
# PyBossa is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# PyBossa is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with PyBossa. If not, see .
import json
import os
import shutil
import zipfile
from StringIO import StringIO
from default import db, Fixtures, with_context, FakeResponse, mock_contributions_guard
from helper import web
from mock import patch, Mock, call
from flask import Response, redirect
from itsdangerous import BadSignature
from collections import namedtuple
from pybossa.util import get_user_signup_method, unicode_csv_reader
from pybossa.ckan import Ckan
from bs4 import BeautifulSoup
from requests.exceptions import ConnectionError
from werkzeug.exceptions import NotFound
from pybossa.model.project import Project
from pybossa.model.category import Category
from pybossa.model.task import Task
from pybossa.model.task_run import TaskRun
from pybossa.model.user import User
from pybossa.core import user_repo, sentinel, project_repo, result_repo, signer
from pybossa.jobs import send_mail, import_tasks
from pybossa.importers import ImportReport
from factories import ProjectFactory, CategoryFactory, TaskFactory, TaskRunFactory, UserFactory
from unidecode import unidecode
from werkzeug.utils import secure_filename
class TestWeb(web.Helper):
pkg_json_not_found = {
"help": "Return ...",
"success": False,
"error": {
"message": "Not found",
"__type": "Not Found Error"}}
def clear_temp_container(self, user_id):
"""Helper function which deletes all files in temp folder of a given owner_id"""
temp_folder = os.path.join('/tmp', 'user_%d' % user_id)
if os.path.isdir(temp_folder):
shutil.rmtree(temp_folder)
@with_context
def test_01_index(self):
"""Test WEB home page works"""
res = self.app.get("/", follow_redirects=True)
assert self.html_title() in res.data, res
assert "Create a Project" in res.data, res
@with_context
def test_01_search(self):
"""Test WEB search page works."""
res = self.app.get('/search')
err_msg = "Search page should be accessible"
assert "Search" in res.data, err_msg
@with_context
def test_leaderboard(self):
"""Test WEB leaderboard works"""
user = UserFactory.create()
TaskRunFactory.create(user=user)
res = self.app.get('/leaderboard', follow_redirects=True)
assert self.html_title("Community Leaderboard") in res.data, res
assert user.name in res.data, res.data
@with_context
@patch('pybossa.cache.project_stats.pygeoip', autospec=True)
def test_project_stats(self, mock1):
"""Test WEB project stats page works"""
res = self.register()
res = self.signin()
res = self.new_project(short_name="igil")
returns = [Mock()]
returns[0].GeoIP.return_value = 'gic'
returns[0].GeoIP.record_by_addr.return_value = {}
mock1.side_effects = returns
project = db.session.query(Project).first()
user = db.session.query(User).first()
# Without stats
url = '/project/%s/stats' % project.short_name
res = self.app.get(url)
assert "Sorry" in res.data, res.data
# We use a string here to check that it works too
task = Task(project_id=project.id, n_answers=10)
db.session.add(task)
db.session.commit()
for i in range(10):
task_run = TaskRun(project_id=project.id, task_id=1,
user_id=user.id,
info={'answer': 1})
db.session.add(task_run)
db.session.commit()
self.app.get('api/project/%s/newtask' % project.id)
# With stats
url = '/project/%s/stats' % project.short_name
res = self.app.get(url)
assert res.status_code == 200, res.status_code
assert "Distribution" in res.data, res.data
with patch.dict(self.flask_app.config, {'GEO': True}):
url = '/project/%s/stats' % project.short_name
res = self.app.get(url)
assert "GeoLite" in res.data, res.data
def test_contribution_time_shown_for_admins_for_every_project(self):
admin = UserFactory.create(admin=True)
admin.set_password('1234')
user_repo.save(admin)
owner = UserFactory.create(pro=False)
project = ProjectFactory.create(owner=owner)
task = TaskFactory.create(project=project)
TaskRunFactory.create(task=task)
url = '/project/%s/stats' % project.short_name
self.signin(email=admin.email_addr, password='1234')
assert 'Average contribution time' in self.app.get(url).data
def test_contribution_time_shown_in_pro_owned_projects(self):
pro_owner = UserFactory.create(pro=True)
pro_owned_project = ProjectFactory.create(owner=pro_owner)
task = TaskFactory.create(project=pro_owned_project)
TaskRunFactory.create(task=task)
pro_url = '/project/%s/stats' % pro_owned_project.short_name
assert 'Average contribution time' in self.app.get(pro_url).data
def test_contribution_time_not_shown_in_regular_user_owned_projects(self):
project = ProjectFactory.create()
task = TaskFactory.create(project=project)
TaskRunFactory.create(task=task)
url = '/project/%s/stats' % project.short_name
assert 'Average contribution time' not in self.app.get(url).data
@with_context
def test_03_account_index(self):
"""Test WEB account index works."""
# Without users
res = self.app.get('/account/page/15', follow_redirects=True)
assert res.status_code == 404, res.status_code
self.create()
res = self.app.get('/account', follow_redirects=True)
assert res.status_code == 200, res.status_code
err_msg = "There should be a Community page"
assert "Community" in res.data, err_msg
@with_context
def test_register_get(self):
"""Test WEB register user works"""
res = self.app.get('/account/register')
# The output should have a mime-type: text/html
assert res.mimetype == 'text/html', res
assert self.html_title("Register") in res.data, res
@with_context
@patch('pybossa.view.account.mail_queue', autospec=True)
@patch('pybossa.view.account.render_template')
@patch('pybossa.view.account.signer')
def test_register_post_creates_email_with_link(self, signer, render, queue):
"""Test WEB register post creates and sends the confirmation email if
account validation is enabled"""
from flask import current_app
current_app.config['ACCOUNT_CONFIRMATION_DISABLED'] = False
data = dict(fullname="John Doe", name="johndoe",
password="p4ssw0rd", confirm="p4ssw0rd",
email_addr="johndoe@example.com")
signer.dumps.return_value = ''
render.return_value = ''
res = self.app.post('/account/register', data=data)
del data['confirm']
current_app.config['ACCOUNT_CONFIRMATION_DISABLED'] = True
signer.dumps.assert_called_with(data, salt='account-validation')
render.assert_any_call('/account/email/validate_account.md',
user=data,
confirm_url='http://localhost/account/register/confirmation?key=')
assert send_mail == queue.enqueue.call_args[0][0], "send_mail not called"
mail_data = queue.enqueue.call_args[0][1]
assert 'subject' in mail_data.keys()
assert 'recipients' in mail_data.keys()
assert 'body' in mail_data.keys()
assert 'html' in mail_data.keys()
@with_context
@patch('pybossa.view.account.mail_queue', autospec=True)
@patch('pybossa.view.account.render_template')
@patch('pybossa.view.account.signer')
def test_update_email_validates_email(self, signer, render, queue):
"""Test WEB update user email creates and sends the confirmation email
if account validation is enabled"""
from flask import current_app
current_app.config['ACCOUNT_CONFIRMATION_DISABLED'] = False
self.register()
signer.dumps.return_value = ''
render.return_value = ''
self.update_profile(email_addr="new@mail.com")
current_app.config['ACCOUNT_CONFIRMATION_DISABLED'] = True
data = dict(fullname="John Doe", name="johndoe",
email_addr="new@mail.com")
signer.dumps.assert_called_with(data, salt='account-validation')
render.assert_any_call('/account/email/validate_email.md',
user=data,
confirm_url='http://localhost/account/register/confirmation?key=')
assert send_mail == queue.enqueue.call_args[0][0], "send_mail not called"
mail_data = queue.enqueue.call_args[0][1]
assert 'subject' in mail_data.keys()
assert 'recipients' in mail_data.keys()
assert 'body' in mail_data.keys()
assert 'html' in mail_data.keys()
assert mail_data['recipients'][0] == data['email_addr']
user = db.session.query(User).get(1)
msg = "Confirmation email flag not updated"
assert user.confirmation_email_sent, msg
msg = "Email not marked as invalid"
assert user.valid_email is False, msg
msg = "Email should remain not updated, as it's not been validated"
assert user.email_addr != 'new@email.com', msg
@with_context
def test_confirm_email_returns_404(self):
"""Test WEB confirm_email returns 404 when disabled."""
res = self.app.get('/account/confir-email', follow_redirects=True)
assert res.status_code == 404, res.status_code
@with_context
@patch('pybossa.view.account.mail_queue', autospec=True)
@patch('pybossa.view.account.render_template')
@patch('pybossa.view.account.signer')
def test_validate_email(self, signer, render, queue):
"""Test WEB validate email sends the confirmation email
if account validation is enabled"""
from flask import current_app
current_app.config['ACCOUNT_CONFIRMATION_DISABLED'] = False
self.register()
user = db.session.query(User).get(1)
user.valid_email = False
db.session.commit()
signer.dumps.return_value = ''
render.return_value = ''
data = dict(fullname=user.fullname, name=user.name,
email_addr=user.email_addr)
res = self.app.get('/account/confirm-email', follow_redirects=True)
signer.dumps.assert_called_with(data, salt='account-validation')
render.assert_any_call('/account/email/validate_email.md',
user=data,
confirm_url='http://localhost/account/register/confirmation?key=')
assert send_mail == queue.enqueue.call_args[0][0], "send_mail not called"
mail_data = queue.enqueue.call_args[0][1]
assert 'subject' in mail_data.keys()
assert 'recipients' in mail_data.keys()
assert 'body' in mail_data.keys()
assert 'html' in mail_data.keys()
assert mail_data['recipients'][0] == data['email_addr']
user = db.session.query(User).get(1)
msg = "Confirmation email flag not updated"
assert user.confirmation_email_sent, msg
msg = "Email not marked as invalid"
assert user.valid_email is False, msg
current_app.config['ACCOUNT_CONFIRMATION_DISABLED'] = True
@with_context
def test_register_post_valid_data_validation_enabled(self):
"""Test WEB register post with valid form data and account validation
enabled"""
from flask import current_app
current_app.config['ACCOUNT_CONFIRMATION_DISABLED'] = False
data = dict(fullname="John Doe", name="johndoe",
password="p4ssw0rd", confirm="p4ssw0rd",
email_addr="johndoe@example.com")
res = self.app.post('/account/register', data=data)
current_app.config['ACCOUNT_CONFIRMATION_DISABLED'] = True
assert self.html_title() in res.data, res
assert "Just one more step, please" in res.data, res.data
@with_context
@patch('pybossa.view.account.redirect', wraps=redirect)
def test_register_post_valid_data_validation_disabled(self, redirect):
"""Test WEB register post with valid form data and account validation
disabled redirects to home page"""
data = dict(fullname="John Doe", name="johndoe",
password="p4ssw0rd", confirm="p4ssw0rd",
email_addr="johndoe@example.com")
res = self.app.post('/account/register', data=data)
print dir(redirect)
redirect.assert_called_with('/')
def test_register_confirmation_fails_without_key(self):
"""Test WEB register confirmation returns 403 if no 'key' param is present"""
res = self.app.get('/account/register/confirmation')
assert res.status_code == 403, res.status
def test_register_confirmation_fails_with_invalid_key(self):
"""Test WEB register confirmation returns 403 if an invalid key is given"""
res = self.app.get('/account/register/confirmation?key=invalid')
assert res.status_code == 403, res.status
@patch('pybossa.view.account.signer')
def test_register_confirmation_gets_account_data_from_key(self, fake_signer):
"""Test WEB register confirmation gets the account data from the key"""
exp_time = self.flask_app.config.get('ACCOUNT_LINK_EXPIRATION')
fake_signer.loads.return_value = dict(fullname='FN', name='name',
email_addr='email', password='password')
res = self.app.get('/account/register/confirmation?key=valid-key')
fake_signer.loads.assert_called_with('valid-key', max_age=exp_time, salt='account-validation')
@patch('pybossa.view.account.signer')
def test_register_confirmation_validates_email(self, fake_signer):
"""Test WEB validates email"""
self.register()
user = db.session.query(User).get(1)
user.valid_email = False
user.confirmation_email_sent = True
db.session.commit()
fake_signer.loads.return_value = dict(fullname=user.fullname,
name=user.name,
email_addr=user.email_addr)
self.app.get('/account/register/confirmation?key=valid-key')
user = db.session.query(User).get(1)
assert user is not None
msg = "Email has not been validated"
assert user.valid_email, msg
msg = "Confirmation email flag has not been restored"
assert user.confirmation_email_sent is False, msg
@patch('pybossa.view.account.signer')
def test_register_confirmation_validates_n_updates_email(self, fake_signer):
"""Test WEB validates and updates email"""
self.register()
user = db.session.query(User).get(1)
user.valid_email = False
user.confirmation_email_sent = True
db.session.commit()
fake_signer.loads.return_value = dict(fullname=user.fullname,
name=user.name,
email_addr='new@email.com')
self.app.get('/account/register/confirmation?key=valid-key')
user = db.session.query(User).get(1)
assert user is not None
msg = "Email has not been validated"
assert user.valid_email, msg
msg = "Confirmation email flag has not been restored"
assert user.confirmation_email_sent is False, msg
msg = 'Email should be updated after validation.'
assert user.email_addr == 'new@email.com', msg
@patch('pybossa.view.account.newsletter', autospec=True)
@patch('pybossa.view.account.url_for')
@patch('pybossa.view.account.signer')
def test_confirm_account_newsletter(self, fake_signer, url_for, newsletter):
"""Test WEB confirm email shows newsletter or home."""
newsletter.ask_user_to_subscribe.return_value = True
self.register()
user = db.session.query(User).get(1)
user.valid_email = False
db.session.commit()
fake_signer.loads.return_value = dict(fullname=user.fullname,
name=user.name,
email_addr=user.email_addr)
self.app.get('/account/register/confirmation?key=valid-key')
url_for.assert_called_with('account.newsletter_subscribe', next=None)
newsletter.ask_user_to_subscribe.return_value = False
self.app.get('/account/register/confirmation?key=valid-key')
url_for.assert_called_with('home.home')
@patch('pybossa.view.account.signer')
def test_register_confirmation_creates_new_account(self, fake_signer):
"""Test WEB register confirmation creates the new account"""
fake_signer.loads.return_value = dict(fullname='FN', name='name',
email_addr='email', password='password')
res = self.app.get('/account/register/confirmation?key=valid-key')
user = db.session.query(User).filter_by(name='name').first()
assert user is not None
assert user.check_password('password')
@with_context
def test_04_signin_signout(self):
"""Test WEB sign in and sign out works"""
res = self.register()
# Log out as the registration already logs in the user
res = self.signout()
res = self.signin(method="GET")
assert self.html_title("Sign in") in res.data, res.data
assert "Sign in" in res.data, res.data
res = self.signin(email='')
assert "Please correct the errors" in res.data, res
assert "The e-mail is required" in res.data, res
res = self.signin(password='')
assert "Please correct the errors" in res.data, res
assert "You must provide a password" in res.data, res
res = self.signin(email='', password='')
assert "Please correct the errors" in res.data, res
assert "The e-mail is required" in res.data, res
assert "You must provide a password" in res.data, res
# Non-existant user
msg = "Ooops, we didn't find you in the system"
res = self.signin(email='wrongemail')
assert msg in res.data, res.data
res = self.signin(email='wrongemail', password='wrongpassword')
assert msg in res.data, res
# Real user but wrong password or username
msg = "Ooops, Incorrect email/password"
res = self.signin(password='wrongpassword')
assert msg in res.data, res
res = self.signin()
assert self.html_title() in res.data, res
assert "Welcome back %s" % "John Doe" in res.data, res
# Check profile page with several information chunks
res = self.profile()
assert self.html_title("Profile") in res.data, res
assert "John Doe" in res.data, res
assert "johndoe@example.com" in res.data, res
# Log out
res = self.signout()
assert self.html_title() in res.data, res
assert "You are now signed out" in res.data, res
# Request profile as an anonymous user
# Check profile page with several information chunks
res = self.profile()
assert "John Doe" in res.data, res
assert "johndoe@example.com" not in res.data, res
# Try to access protected areas like update
res = self.app.get('/account/johndoe/update', follow_redirects=True)
# As a user must be signed in to access, the page the title will be the
# redirection to log in
assert self.html_title("Sign in") in res.data, res.data
assert "Please sign in to access this page." in res.data, res.data
res = self.signin(next='%2Faccount%2Fprofile')
assert self.html_title("Profile") in res.data, res
assert "Welcome back %s" % "John Doe" in res.data, res
@with_context
@patch('pybossa.view.projects.uploader.upload_file', return_value=True)
def test_profile_applications(self, mock):
"""Test WEB user profile project page works."""
self.create()
self.signin(email=Fixtures.email_addr, password=Fixtures.password)
self.new_project()
url = '/account/%s/applications' % Fixtures.name
res = self.app.get(url)
assert "Projects" in res.data, res.data
assert "Published" in res.data, res.data
assert "Draft" in res.data, res.data
assert Fixtures.project_name in res.data, res.data
url = '/account/fakename/applications'
res = self.app.get(url)
assert res.status_code == 404, res.status_code
url = '/account/%s/applications' % Fixtures.name2
res = self.app.get(url)
assert res.status_code == 403, res.status_code
@with_context
def test_05_update_user_profile(self):
"""Test WEB update user profile"""
# Create an account and log in
self.register()
url = "/account/fake/update"
res = self.app.get(url, follow_redirects=True)
assert res.status_code == 404, res.status_code
# Update profile with new data
res = self.update_profile(method="GET")
msg = "Update your profile: %s" % "John Doe"
assert self.html_title(msg) in res.data, res.data
msg = 'input id="id" name="id" type="hidden" value="1"'
assert msg in res.data, res
assert "John Doe" in res.data, res
assert "Save the changes" in res.data, res
msg = 'Cancel'
assert msg in res.data, res.data
res = self.update_profile(fullname="John Doe 2",
email_addr="johndoe2@example",
locale="en")
assert "Please correct the errors" in res.data, res.data
res = self.update_profile(fullname="John Doe 2",
email_addr="johndoe2@example.com",
locale="en")
title = "Update your profile: John Doe 2"
assert self.html_title(title) in res.data, res.data
user = user_repo.get_by(email_addr='johndoe2@example.com')
assert "Your profile has been updated!" in res.data, res.data
assert "John Doe 2" in res.data, res
assert "John Doe 2" == user.fullname, user.fullname
assert "johndoe" in res.data, res
assert "johndoe" == user.name, user.name
assert "johndoe2@example.com" in res.data, res
assert "johndoe2@example.com" == user.email_addr, user.email_addr
assert user.subscribed is False, user.subscribed
# Updating the username field forces the user to re-log in
res = self.update_profile(fullname="John Doe 2",
email_addr="johndoe2@example.com",
locale="en",
new_name="johndoe2")
assert "Your profile has been updated!" in res.data, res
assert "Please sign in" in res.data, res.data
res = self.signin(method="POST", email="johndoe2@example.com",
password="p4ssw0rd",
next="%2Faccount%2Fprofile")
assert "Welcome back John Doe 2" in res.data, res.data
assert "John Doe 2" in res.data, res
assert "johndoe2" in res.data, res
assert "johndoe2@example.com" in res.data, res
res = self.signout()
assert self.html_title() in res.data, res
assert "You are now signed out" in res.data, res
# A user must be signed in to access the update page, the page
# the title will be the redirection to log in
res = self.update_profile(method="GET")
assert self.html_title("Sign in") in res.data, res
assert "Please sign in to access this page." in res.data, res
# A user must be signed in to access the update page, the page
# the title will be the redirection to log in
res = self.update_profile()
assert self.html_title("Sign in") in res.data, res
assert "Please sign in to access this page." in res.data, res
self.register(fullname="new", name="new")
url = "/account/johndoe2/update"
res = self.app.get(url)
assert res.status_code == 403
@with_context
def test_05a_get_nonexistant_app(self):
"""Test WEB get not existant project should return 404"""
res = self.app.get('/project/nonapp', follow_redirects=True)
assert res.status == '404 NOT FOUND', res.status
@with_context
def test_05b_get_nonexistant_app_newtask(self):
"""Test WEB get non existant project newtask should return 404"""
res = self.app.get('/project/noapp/presenter', follow_redirects=True)
assert res.status == '404 NOT FOUND', res.status
res = self.app.get('/project/noapp/newtask', follow_redirects=True)
assert res.status == '404 NOT FOUND', res.status
@with_context
def test_05c_get_nonexistant_app_tutorial(self):
"""Test WEB get non existant project tutorial should return 404"""
res = self.app.get('/project/noapp/tutorial', follow_redirects=True)
assert res.status == '404 NOT FOUND', res.status
@with_context
def test_05d_get_nonexistant_app_delete(self):
"""Test WEB get non existant project delete should return 404"""
self.register()
# GET
res = self.app.get('/project/noapp/delete', follow_redirects=True)
assert res.status == '404 NOT FOUND', res.data
# POST
res = self.delete_project(short_name="noapp")
assert res.status == '404 NOT FOUND', res.status
@with_context
def test_05d_get_nonexistant_app_update(self):
"""Test WEB get non existant project update should return 404"""
self.register()
# GET
res = self.app.get('/project/noapp/update', follow_redirects=True)
assert res.status == '404 NOT FOUND', res.status
# POST
res = self.update_project(short_name="noapp")
assert res.status == '404 NOT FOUND', res.status
@with_context
def test_05d_get_nonexistant_app_import(self):
"""Test WEB get non existant project import should return 404"""
self.register()
# GET
res = self.app.get('/project/noapp/import', follow_redirects=True)
assert res.status == '404 NOT FOUND', res.status
# POST
res = self.app.post('/project/noapp/import', follow_redirects=True)
assert res.status == '404 NOT FOUND', res.status
@with_context
def test_05d_get_nonexistant_app_task(self):
"""Test WEB get non existant project task should return 404"""
res = self.app.get('/project/noapp/task', follow_redirects=True)
assert res.status == '404 NOT FOUND', res.status
# Pagination
res = self.app.get('/project/noapp/task/25', follow_redirects=True)
assert res.status == '404 NOT FOUND', res.status
@with_context
def test_05d_get_nonexistant_app_results_json(self):
"""Test WEB get non existant project results json should return 404"""
res = self.app.get('/project/noapp/24/results.json', follow_redirects=True)
assert res.status == '404 NOT FOUND', res.status
@with_context
def test_06_applications_without_apps(self):
"""Test WEB projects index without projects works"""
# Check first without apps
self.create_categories()
res = self.app.get('/project', follow_redirects=True)
assert "Projects" in res.data, res.data
assert Fixtures.cat_1 in res.data, res.data
@with_context
def test_06_applications_2(self):
"""Test WEB projects index with projects"""
self.create()
res = self.app.get('/project', follow_redirects=True)
assert self.html_title("Projects") in res.data, res.data
assert "Projects" in res.data, res.data
assert Fixtures.project_short_name in res.data, res.data
@with_context
def test_06_featured_apps(self):
"""Test WEB projects index shows featured projects in all the pages works"""
self.create()
project = db.session.query(Project).get(1)
project.featured = True
db.session.add(project)
db.session.commit()
res = self.app.get('/project', follow_redirects=True)
assert self.html_title("Projects") in res.data, res.data
assert "Projects" in res.data, res.data
assert '/project/test-app' in res.data, res.data
assert '
' in res.data, res.data
# Update one task to have more answers than expected
task = db.session.query(Task).get(1)
task.n_answers=1
db.session.add(task)
db.session.commit()
task = db.session.query(Task).get(1)
cat = db.session.query(Category).get(1)
url = '/project/category/featured/'
res = self.app.get(url, follow_redirects=True)
assert '1 Featured Projects' in res.data, res.data
@with_context
@patch('pybossa.ckan.requests.get')
@patch('pybossa.view.projects.uploader.upload_file', return_value=True)
def test_10_get_application(self, Mock, mock2):
"""Test WEB project URL/ works"""
# Sign in and create a project
html_request = FakeResponse(text=json.dumps(self.pkg_json_not_found),
status_code=200,
headers={'content-type': 'application/json'},
encoding='utf-8')
Mock.return_value = html_request
self.register()
res = self.new_project()
project = db.session.query(Project).first()
project.published = True
db.session.commit()
TaskFactory.create(project=project)
res = self.app.get('/project/sampleapp', follow_redirects=True)
msg = "Project: Sample Project"
assert self.html_title(msg) in res.data, res
err_msg = "There should be a contribute button"
assert "Start Contributing Now!" in res.data, err_msg
res = self.app.get('/project/sampleapp/settings', follow_redirects=True)
assert res.status == '200 OK', res.status
self.signout()
# Now as an anonymous user
res = self.app.get('/project/sampleapp', follow_redirects=True)
assert self.html_title("Project: Sample Project") in res.data, res
assert "Start Contributing Now!" in res.data, err_msg
res = self.app.get('/project/sampleapp/settings', follow_redirects=True)
assert res.status == '200 OK', res.status
err_msg = "Anonymous user should be redirected to sign in page"
assert "Please sign in to access this page" in res.data, err_msg
# Now with a different user
self.register(fullname="Perico Palotes", name="perico")
res = self.app.get('/project/sampleapp', follow_redirects=True)
assert self.html_title("Project: Sample Project") in res.data, res
assert "Start Contributing Now!" in res.data, err_msg
res = self.app.get('/project/sampleapp/settings')
assert res.status == '403 FORBIDDEN', res.status
@with_context
@patch('pybossa.view.projects.uploader.upload_file', return_value=True)
def test_10b_application_long_description_allows_markdown(self, mock):
"""Test WEB long description markdown is supported"""
markdown_description = u'Markdown\n======='
self.register()
self.new_project(long_description=markdown_description)
res = self.app.get('/project/sampleapp', follow_redirects=True)
data = res.data
assert 'Markdown
' in data, 'Markdown text not being rendered!'
@with_context
@patch('pybossa.view.projects.uploader.upload_file', return_value=True)
def test_11_create_application(self, mock):
"""Test WEB create a project works"""
# Create a project as an anonymous user
res = self.new_project(method="GET")
assert self.html_title("Sign in") in res.data, res
assert "Please sign in to access this page" in res.data, res
res = self.new_project()
assert self.html_title("Sign in") in res.data, res.data
assert "Please sign in to access this page." in res.data, res.data
# Sign in and create a project
res = self.register()
res = self.new_project(method="GET")
assert self.html_title("Create a Project") in res.data, res
assert "Create the project" in res.data, res
res = self.new_project(long_description='My Description')
assert "Sample Project: Update the project" in res.data
assert "Project created!" in res.data, res
project = db.session.query(Project).first()
assert project.name == 'Sample Project', 'Different names %s' % project.name
assert project.short_name == 'sampleapp', \
'Different names %s' % project.short_name
assert project.long_description == 'My Description', \
"Long desc should be the same: %s" % project.long_description
assert project.category is not None, \
"A project should have a category after being created"
@with_context
def test_description_is_generated_only_if_not_provided(self):
"""Test WEB when when creating a project and a description is provided,
then it is not generated from the long_description"""
self.register()
res = self.new_project(long_description="a"*300, description='b')
project = db.session.query(Project).first()
assert project.description == 'b', project.description
@with_context
def test_description_is_generated_from_long_desc(self):
"""Test WEB when creating a project, the description field is
automatically filled in by truncating the long_description"""
self.register()
res = self.new_project(long_description="Hello", description='')
project = db.session.query(Project).first()
assert project.description == "Hello", project.description
@with_context
def test_description_is_generated_from_long_desc_formats(self):
"""Test WEB when when creating a project, the description generated
from the long_description is only text (no html, no markdown)"""
self.register()
res = self.new_project(long_description="## Hello", description='')
project = db.session.query(Project).first()
assert '##' not in project.description, project.description
assert '' not in project.description, project.description
@with_context
def test_description_is_generated_from_long_desc_truncates(self):
"""Test WEB when when creating a project, the description generated
from the long_description is truncated to 255 chars"""
self.register()
res = self.new_project(long_description="a"*300, description='')
project = db.session.query(Project).first()
assert len(project.description) == 255, len(project.description)
assert project.description[-3:] == '...'
@with_context
@patch('pybossa.view.projects.uploader.upload_file', return_value=True)
def test_11_a_create_application_errors(self, mock):
"""Test WEB create a project issues the errors"""
self.register()
# Required fields checks
# Issue the error for the project.name
res = self.new_project(name="")
err_msg = "A project must have a name"
assert "This field is required" in res.data, err_msg
# Issue the error for the project.short_name
res = self.new_project(short_name="")
err_msg = "A project must have a short_name"
assert "This field is required" in res.data, err_msg
# Issue the error for the project.description
res = self.new_project(long_description="")
err_msg = "A project must have a description"
assert "This field is required" in res.data, err_msg
# Issue the error for the project.short_name
res = self.new_project(short_name='$#/|')
err_msg = "A project must have a short_name without |/$# chars"
assert '$#&\/| and space symbols are forbidden' in res.data, err_msg
# Now Unique checks
self.new_project()
res = self.new_project()
err_msg = "There should be a Unique field"
assert "Name is already taken" in res.data, err_msg
assert "Short Name is already taken" in res.data, err_msg
@patch('pybossa.ckan.requests.get')
@patch('pybossa.view.projects.uploader.upload_file', return_value=True)
@patch('pybossa.forms.validator.requests.get')
def test_12_update_application(self, Mock, mock, mock_webhook):
"""Test WEB update project works"""
html_request = FakeResponse(text=json.dumps(self.pkg_json_not_found),
status_code=200,
headers={'content-type': 'application/json'},
encoding='utf-8')
Mock.return_value = html_request
mock_webhook.return_value = html_request
self.register()
self.new_project()
# Get the Update Project web page
res = self.update_project(method="GET")
msg = "Project: Sample Project · Update"
assert self.html_title(msg) in res.data, res
msg = 'input id="id" name="id" type="hidden" value="1"'
assert msg in res.data, res
assert "Save the changes" in res.data, res
# Check form validation
res = self.update_project(new_name="",
new_short_name="",
new_description="New description",
new_long_description='New long desc')
assert "Please correct the errors" in res.data, res.data
# Update the project
res = self.update_project(new_name="New Sample Project",
new_short_name="newshortname",
new_description="New description",
new_long_description='New long desc')
project = db.session.query(Project).first()
assert "Project updated!" in res.data, res.data
err_msg = "Project name not updated %s" % project.name
assert project.name == "New Sample Project", err_msg
err_msg = "Project short name not updated %s" % project.short_name
assert project.short_name == "newshortname", err_msg
err_msg = "Project description not updated %s" % project.description
assert project.description == "New description", err_msg
err_msg = "Project long description not updated %s" % project.long_description
assert project.long_description == "New long desc", err_msg
@with_context
@patch('pybossa.forms.validator.requests.get')
def test_webhook_to_project(self, mock):
"""Test WEB update sets a webhook for the project"""
html_request = FakeResponse(text=json.dumps(self.pkg_json_not_found),
status_code=200,
headers={'content-type': 'application/json'},
encoding='utf-8')
mock.return_value = html_request
self.register()
owner = db.session.query(User).first()
project = ProjectFactory.create(owner=owner)
new_webhook = 'http://mynewserver.com/'
self.update_project(id=project.id, short_name=project.short_name,
new_webhook=new_webhook)
err_msg = "There should be an updated webhook url."
assert project.webhook == new_webhook, err_msg
@with_context
@patch('pybossa.forms.validator.requests.get')
def test_webhook_to_project_fails(self, mock):
"""Test WEB update does not set a webhook for the project"""
html_request = FakeResponse(text=json.dumps(self.pkg_json_not_found),
status_code=404,
headers={'content-type': 'application/json'},
encoding='utf-8')
mock.return_value = html_request
self.register()
owner = db.session.query(User).first()
project = ProjectFactory.create(owner=owner)
new_webhook = 'http://mynewserver.com/'
self.update_project(id=project.id, short_name=project.short_name,
new_webhook=new_webhook)
err_msg = "There should not be an updated webhook url."
assert project.webhook != new_webhook, err_msg
@with_context
@patch('pybossa.forms.validator.requests.get')
def test_webhook_to_project_conn_err(self, mock):
"""Test WEB update does not set a webhook for the project"""
from requests.exceptions import ConnectionError
mock.side_effect = ConnectionError
self.register()
owner = db.session.query(User).first()
project = ProjectFactory.create(owner=owner)
new_webhook = 'http://mynewserver.com/'
res = self.update_project(id=project.id, short_name=project.short_name,
new_webhook=new_webhook)
err_msg = "There should not be an updated webhook url."
assert project.webhook != new_webhook, err_msg
@with_context
@patch('pybossa.forms.validator.requests.get')
def test_add_password_to_project(self, mock_webhook):
"""Test WEB update sets a password for the project"""
html_request = FakeResponse(text=json.dumps(self.pkg_json_not_found),
status_code=200,
headers={'content-type': 'application/json'},
encoding='utf-8')
mock_webhook.return_value = html_request
self.register()
owner = db.session.query(User).first()
project = ProjectFactory.create(owner=owner)
self.update_project(id=project.id, short_name=project.short_name,
new_protect='true', new_password='mysecret')
assert project.needs_password(), 'Password not set'
@with_context
@patch('pybossa.forms.validator.requests.get')
def test_remove_password_from_project(self, mock_webhook):
"""Test WEB update removes the password of the project"""
html_request = FakeResponse(text=json.dumps(self.pkg_json_not_found),
status_code=200,
headers={'content-type': 'application/json'},
encoding='utf-8')
mock_webhook.return_value = html_request
self.register()
owner = db.session.query(User).first()
project = ProjectFactory.create(info={'passwd_hash': 'mysecret'}, owner=owner)
self.update_project(id=project.id, short_name=project.short_name,
new_protect='false', new_password='')
assert not project.needs_password(), 'Password not deleted'
@with_context
def test_update_application_errors(self):
"""Test WEB update form validation issues the errors"""
self.register()
self.new_project()
res = self.update_project(new_name="")
assert "This field is required" in res.data
res = self.update_project(new_short_name="")
assert "This field is required" in res.data
res = self.update_project(new_description="")
assert "You must provide a description." in res.data
res = self.update_project(new_description="a"*256)
assert "Field cannot be longer than 255 characters." in res.data
res = self.update_project(new_long_description="")
assert "This field is required" not in res.data
@with_context
@patch('pybossa.view.projects.uploader.upload_file', return_value=True)
def test_14_delete_application(self, mock):
"""Test WEB delete project works"""
self.create()
self.register()
self.new_project()
res = self.delete_project(method="GET")
msg = "Project: Sample Project · Delete"
assert self.html_title(msg) in res.data, res
assert "No, do not delete it" in res.data, res
project = db.session.query(Project).filter_by(short_name='sampleapp').first()
res = self.delete_project(method="GET")
msg = "Project: Sample Project · Delete"
assert self.html_title(msg) in res.data, res
assert "No, do not delete it" in res.data, res
res = self.delete_project()
assert "Project deleted!" in res.data, res
self.signin(email=Fixtures.email_addr2, password=Fixtures.password)
res = self.delete_project(short_name=Fixtures.project_short_name)
assert res.status_code == 403, res.status_code
@patch('pybossa.repositories.project_repository.uploader')
def test_delete_project_deletes_task_zip_files_too(self, uploader):
"""Test WEB delete project also deletes zip files for task and taskruns"""
Fixtures.create()
self.signin(email=u'tester@tester.com', password=u'tester')
res = self.app.post('/project/test-app/delete', follow_redirects=True)
expected = [call('1_test-app_task_json.zip', 'user_2'),
call('1_test-app_task_csv.zip', 'user_2'),
call('1_test-app_task_run_json.zip', 'user_2'),
call('1_test-app_task_run_csv.zip', 'user_2')]
assert uploader.delete_file.call_args_list == expected
@with_context
def test_15_twitter_email_warning(self):
"""Test WEB Twitter email warning works"""
# This test assumes that the user allows Twitter to authenticate,
# returning a valid resp. The only difference is a user object
# without a password
# Register a user and sign out
user = User(name="tester", passwd_hash="tester",
fullname="tester",
email_addr="tester")
user.set_password('tester')
db.session.add(user)
db.session.commit()
db.session.query(User).all()
# Sign in again and check the warning message
self.signin(email="tester", password="tester")
res = self.app.get('/', follow_redirects=True)
msg = ("Please update your e-mail address in your"
" profile page, right now it is empty!")
user = db.session.query(User).get(1)
assert msg in res.data, res.data
@patch('pybossa.view.projects.uploader.upload_file', return_value=True)
def test_16_task_status_completed(self, mock):
"""Test WEB Task Status Completed works"""
self.register()
self.new_project()
project = db.session.query(Project).first()
# We use a string here to check that it works too
project.published = True
task = Task(project_id=project.id, n_answers = 10)
db.session.add(task)
db.session.commit()
res = self.app.get('project/%s/tasks/browse' % (project.short_name),
follow_redirects=True)
dom = BeautifulSoup(res.data)
assert "Sample Project" in res.data, res.data
assert '0 of 10' in res.data, res.data
err_msg = "Download button should be disabled"
assert dom.find(id='nothingtodownload') is not None, err_msg
for i in range(5):
task_run = TaskRun(project_id=project.id, task_id=1,
info={'answer': 1})
db.session.add(task_run)
db.session.commit()
self.app.get('api/project/%s/newtask' % project.id)
res = self.app.get('project/%s/tasks/browse' % (project.short_name),
follow_redirects=True)
dom = BeautifulSoup(res.data)
assert "Sample Project" in res.data, res.data
assert '5 of 10' in res.data, res.data
err_msg = "Download Partial results button should be shown"
assert dom.find(id='partialdownload') is not None, err_msg
for i in range(5):
task_run = TaskRun(project_id=project.id, task_id=1,
info={'answer': 1})
db.session.add(task_run)
db.session.commit()
self.app.get('api/project/%s/newtask' % project.id)
self.signout()
project = db.session.query(Project).first()
res = self.app.get('project/%s/tasks/browse' % (project.short_name),
follow_redirects=True)
assert "Sample Project" in res.data, res.data
msg = 'Task #1'
assert msg in res.data, res.data
assert '10 of 10' in res.data, res.data
dom = BeautifulSoup(res.data)
err_msg = "Download Full results button should be shown"
assert dom.find(id='fulldownload') is not None, err_msg
@patch('pybossa.view.projects.uploader.upload_file', return_value=True)
def test_17_export_task_runs(self, mock):
"""Test WEB TaskRun export works"""
self.register()
self.new_project()
project = db.session.query(Project).first()
task = Task(project_id=project.id, n_answers = 10)
db.session.add(task)
db.session.commit()
for i in range(10):
task_run = TaskRun(project_id=project.id, task_id=1, info={'answer': 1})
db.session.add(task_run)
db.session.commit()
project = db.session.query(Project).first()
res = self.app.get('project/%s/%s/results.json' % (project.short_name, 1),
follow_redirects=True)
data = json.loads(res.data)
assert len(data) == 10, data
for tr in data:
assert tr['info']['answer'] == 1, tr
# Check with correct project but wrong task id
res = self.app.get('project/%s/%s/results.json' % (project.short_name, 5000),
follow_redirects=True)
assert res.status_code == 404, res.status_code
@with_context
@patch('pybossa.view.projects.uploader.upload_file', return_value=True)
def test_18_task_status_wip(self, mock):
"""Test WEB Task Status on going works"""
self.register()
self.new_project()
project = db.session.query(Project).first()
project.published = True
task = Task(project_id=project.id, n_answers = 10)
db.session.add(task)
db.session.commit()
self.signout()
project = db.session.query(Project).first()
res = self.app.get('project/%s/tasks/browse' % (project.short_name),
follow_redirects=True)
assert "Sample Project" in res.data, res.data
msg = 'Task #1'
assert msg in res.data, res.data
assert '0 of 10' in res.data, res.data
# For a non existing page
res = self.app.get('project/%s/tasks/browse/5000' % (project.short_name),
follow_redirects=True)
assert res.status_code == 404, res.status_code
@with_context
def test_19_app_index_categories(self):
"""Test WEB Project Index categories works"""
self.register()
self.create()
self.signout()
res = self.app.get('project', follow_redirects=True)
assert "Projects" in res.data, res.data
assert Fixtures.cat_1 in res.data, res.data
task = db.session.query(Task).get(1)
# Update one task to have more answers than expected
task.n_answers=1
db.session.add(task)
db.session.commit()
task = db.session.query(Task).get(1)
cat = db.session.query(Category).get(1)
url = '/project/category/%s/' % Fixtures.cat_1
res = self.app.get(url, follow_redirects=True)
tmp = '1 %s Projects' % Fixtures.cat_1
assert tmp in res.data, res
@with_context
def test_app_index_categories_pagination(self):
"""Test WEB Project Index categories pagination works"""
from flask import current_app
n_apps = current_app.config.get('APPS_PER_PAGE')
current_app.config['APPS_PER_PAGE'] = 1
category = CategoryFactory.create(name='category', short_name='cat')
for project in ProjectFactory.create_batch(2, category=category):
TaskFactory.create(project=project)
page1 = self.app.get('/project/category/%s/' % category.short_name)
page2 = self.app.get('/project/category/%s/page/2/' % category.short_name)
current_app.config['APPS_PER_PAGE'] = n_apps
assert 'Next »' in page1.data
assert page2.status_code == 200, page2.status_code
assert '« Prev ' in page2.data
@with_context
@patch('pybossa.view.projects.uploader.upload_file', return_value=True)
def test_20_app_index_published(self, mock):
"""Test WEB Project Index published works"""
self.register()
self.new_project()
self.update_project(new_category_id="1")
project = db.session.query(Project).first()
project.published = True
db.session.commit()
self.signout()
res = self.app.get('project', follow_redirects=True)
assert "%s Projects" % Fixtures.cat_1 in res.data, res.data
assert "draft" not in res.data, res.data
assert "Sample Project" in res.data, res.data
@with_context
@patch('pybossa.view.projects.uploader.upload_file', return_value=True)
def test_20_app_index_draft(self, mock):
"""Test WEB Project Index draft works"""
# Create root
self.register()
self.new_project()
self.signout()
# Create a user
self.register(fullname="jane", name="jane", email="jane@jane.com")
self.signout()
# As Anonymous
res = self.app.get('/project/category/draft', follow_redirects=True)
dom = BeautifulSoup(res.data)
err_msg = "Anonymous should not see draft apps"
assert dom.find(id='signin') is not None, err_msg
# As authenticated but not admin
self.signin(email="jane@jane.com", password="p4ssw0rd")
res = self.app.get('/project/category/draft', follow_redirects=True)
assert res.status_code == 403, "Non-admin should not see draft apps"
self.signout()
# As Admin
self.signin()
res = self.app.get('/project/category/draft', follow_redirects=True)
assert "project-published" not in res.data, res.data
assert "draft" in res.data, res.data
assert "Sample Project" in res.data, res.data
assert '1 Draft Projects' in res.data, res.data
@with_context
def test_21_get_specific_ongoing_task_anonymous(self):
"""Test WEB get specific ongoing task_id for
a project works as anonymous"""
self.create()
self.delete_task_runs()
project = db.session.query(Project).first()
task = db.session.query(Task)\
.filter(Project.id == project.id)\
.first()
res = self.app.get('project/%s/task/%s' % (project.short_name, task.id),
follow_redirects=True)
assert 'TaskPresenter' in res.data, res.data
msg = "?next=%2Fproject%2F" + project.short_name + "%2Ftask%2F" + str(task.id)
assert msg in res.data, res.data
# Try with only registered users
project.allow_anonymous_contributors = False
db.session.add(project)
db.session.commit()
res = self.app.get('project/%s/task/%s' % (project.short_name, task.id),
follow_redirects=True)
assert "sign in to participate" in res.data
@with_context
def test_23_get_specific_ongoing_task_user(self):
"""Test WEB get specific ongoing task_id for a project works as an user"""
self.create()
self.delete_task_runs()
self.register()
self.signin()
project = db.session.query(Project).first()
task = db.session.query(Task).filter(Project.id == project.id).first()
res = self.app.get('project/%s/task/%s' % (project.short_name, task.id),
follow_redirects=True)
assert 'TaskPresenter' in res.data, res.data
@patch('pybossa.view.projects.ContributionsGuard')
def test_get_specific_ongoing_task_marks_task_as_requested(self, guard):
fake_guard_instance = mock_contributions_guard()
guard.return_value = fake_guard_instance
self.create()
self.register()
project = db.session.query(Project).first()
task = db.session.query(Task).filter(Project.id == project.id).first()
res = self.app.get('project/%s/task/%s' % (project.short_name, task.id),
follow_redirects=True)
assert fake_guard_instance.stamp.called
@with_context
@patch('pybossa.view.projects.uploader.upload_file', return_value=True)
def test_25_get_wrong_task_app(self, mock):
"""Test WEB get wrong task.id for a project works"""
self.create()
project1 = db.session.query(Project).get(1)
project1_short_name = project1.short_name
db.session.query(Task).filter(Task.project_id == 1).first()
self.register()
self.new_project()
app2 = db.session.query(Project).get(2)
self.new_task(app2.id)
task2 = db.session.query(Task).filter(Task.project_id == 2).first()
task2_id = task2.id
self.signout()
res = self.app.get('/project/%s/task/%s' % (project1_short_name, task2_id))
assert "Error" in res.data, res.data
msg = "This task does not belong to %s" % project1_short_name
assert msg in res.data, res.data
@with_context
def test_26_tutorial_signed_user(self):
"""Test WEB tutorials work as signed in user"""
self.create()
project1 = db.session.query(Project).get(1)
project1.info = dict(tutorial="some help", task_presenter="presenter")
db.session.commit()
self.register()
# First time accessing the project should redirect me to the tutorial
res = self.app.get('/project/test-app/newtask', follow_redirects=True)
err_msg = "There should be some tutorial for the project"
assert "some help" in res.data, err_msg
# Second time should give me a task, and not the tutorial
res = self.app.get('/project/test-app/newtask', follow_redirects=True)
assert "some help" not in res.data
# Check if the tutorial can be accessed directly
res = self.app.get('/project/test-app/tutorial', follow_redirects=True)
err_msg = "There should be some tutorial for the project"
assert "some help" in res.data, err_msg
@with_context
def test_27_tutorial_anonymous_user(self):
"""Test WEB tutorials work as an anonymous user"""
self.create()
project = db.session.query(Project).get(1)
project.info = dict(tutorial="some help", task_presenter="presenter")
db.session.commit()
# First time accessing the project should redirect me to the tutorial
res = self.app.get('/project/test-app/newtask', follow_redirects=True)
err_msg = "There should be some tutorial for the project"
assert "some help" in res.data, err_msg
# Second time should give me a task, and not the tutorial
res = self.app.get('/project/test-app/newtask', follow_redirects=True)
assert "some help" not in res.data
# Check if the tutorial can be accessed directly
res = self.app.get('/project/test-app/tutorial', follow_redirects=True)
err_msg = "There should be some tutorial for the project"
assert "some help" in res.data, err_msg
@with_context
def test_28_non_tutorial_signed_user(self):
"""Test WEB project without tutorial work as signed in user"""
self.create()
project = db.session.query(Project).get(1)
project.info = dict(task_presenter="the real presenter")
db.session.commit()
self.register()
# First time accessing the project should show the presenter
res = self.app.get('/project/test-app/newtask', follow_redirects=True)
err_msg = "There should be a presenter for the project"
assert "the real presenter" in res.data, err_msg
# Second time accessing the project should show the presenter
res = self.app.get('/project/test-app/newtask', follow_redirects=True)
assert "the real presenter" in res.data, err_msg
@with_context
def test_29_non_tutorial_anonymous_user(self):
"""Test WEB project without tutorials work as an anonymous user"""
self.create()
project = db.session.query(Project).get(1)
project.info = dict(task_presenter="the real presenter")
db.session.commit()
# First time accessing the project should show the presenter
res = self.app.get('/project/test-app/newtask', follow_redirects=True)
err_msg = "There should be a presenter for the project"
assert "the real presenter" in res.data, err_msg
# Second time accessing the project should show the presenter
res = self.app.get('/project/test-app/newtask', follow_redirects=True)
assert "the real presenter" in res.data, err_msg
def test_message_is_flashed_contributing_to_project_without_presenter(self):
project = ProjectFactory.create(info={})
task = TaskFactory.create(project=project)
newtask_url = '/project/%s/newtask' % project.short_name
task_url = '/project/%s/task/%s' % (project.short_name, task.id)
message = ("Sorry, but this project is still a draft and does "
"not have a task presenter.")
newtask_response = self.app.get(newtask_url, follow_redirects=True)
task_response = self.app.get(task_url, follow_redirects=True)
assert message in newtask_response.data
assert message in task_response.data
@with_context
@patch('pybossa.view.projects.uploader.upload_file', return_value=True)
def test_30_app_id_owner(self, mock):
"""Test WEB project settings page shows the ID to the owner"""
self.register()
self.new_project()
res = self.app.get('/project/sampleapp/settings', follow_redirects=True)
assert "Sample Project" in res.data, ("Project should be shown to "
"the owner")
msg = ' ID: 1'
err_msg = "Project ID should be shown to the owner"
assert msg in res.data, err_msg
self.signout()
self.create()
self.signin(email=Fixtures.email_addr2, password=Fixtures.password)
res = self.app.get('/project/sampleapp/settings', follow_redirects=True)
assert res.status_code == 403, res.status_code
@with_context
@patch('pybossa.view.projects.uploader.upload_file', return_value=True)
@patch('pybossa.ckan.requests.get')
def test_30_app_id_anonymous_user(self, Mock, mock):
"""Test WEB project page does not show the ID to anonymous users"""
html_request = FakeResponse(text=json.dumps(self.pkg_json_not_found),
status_code=200,
headers={'content-type': 'application/json'},
encoding='utf-8')
Mock.return_value = html_request
self.register()
self.new_project()
project = db.session.query(Project).first()
project.published = True
db.session.commit()
self.signout()
res = self.app.get('/project/sampleapp', follow_redirects=True)
assert "Sample Project" in res.data, ("Project name should be shown"
" to users")
assert ' ID: 1' not in \
res.data, "Project ID should be shown to the owner"
@with_context
@patch('pybossa.view.projects.uploader.upload_file', return_value=True)
def test_31_user_profile_progress(self, mock):
"""Test WEB user progress profile page works"""
self.register()
self.new_project()
project = db.session.query(Project).first()
task = Task(project_id=project.id, n_answers = 10)
db.session.add(task)
task_run = TaskRun(project_id=project.id, task_id=1, user_id=1,
info={'answer': 1})
db.session.add(task_run)
db.session.commit()
res = self.app.get('account/johndoe', follow_redirects=True)
assert "Sample Project" in res.data
assert "Contribute!" in res.data, "There should be a Contribute button"
@with_context
def test_32_oauth_password(self):
"""Test WEB user sign in without password works"""
user = User(email_addr="johndoe@johndoe.com",
name="John Doe",
passwd_hash=None,
fullname="johndoe",
api_key="api-key")
db.session.add(user)
db.session.commit()
res = self.signin()
assert "Ooops, we didn't find you in the system" in res.data, res.data
@with_context
def test_39_google_oauth_creation(self):
"""Test WEB Google OAuth creation of user works"""
fake_response = {
u'access_token': u'access_token',
u'token_type': u'Bearer',
u'expires_in': 3600,
u'id_token': u'token'}
fake_user = {
u'family_name': u'Doe', u'name': u'John Doe',
u'picture': u'https://goo.gl/img.jpg',
u'locale': u'en',
u'gender': u'male',
u'email': u'john@gmail.com',
u'birthday': u'0000-01-15',
u'link': u'https://plus.google.com/id',
u'given_name': u'John',
u'id': u'111111111111111111111',
u'verified_email': True}
from pybossa.view import google
response_user = google.manage_user(fake_response['access_token'],
fake_user)
user = db.session.query(User).get(1)
assert user.email_addr == response_user.email_addr, response_user
@with_context
def test_40_google_oauth_creation(self):
"""Test WEB Google OAuth detects same user name/email works"""
fake_response = {
u'access_token': u'access_token',
u'token_type': u'Bearer',
u'expires_in': 3600,
u'id_token': u'token'}
fake_user = {
u'family_name': u'Doe', u'name': u'John Doe',
u'picture': u'https://goo.gl/img.jpg',
u'locale': u'en',
u'gender': u'male',
u'email': u'john@gmail.com',
u'birthday': u'0000-01-15',
u'link': u'https://plus.google.com/id',
u'given_name': u'John',
u'id': u'111111111111111111111',
u'verified_email': True}
self.register()
self.signout()
from pybossa.view import google
response_user = google.manage_user(fake_response['access_token'],
fake_user)
assert response_user is None, response_user
@with_context
def test_39_facebook_oauth_creation(self):
"""Test WEB Facebook OAuth creation of user works"""
fake_response = {
u'access_token': u'access_token',
u'token_type': u'Bearer',
u'expires_in': 3600,
u'id_token': u'token'}
fake_user = {
u'username': u'teleyinex',
u'first_name': u'John',
u'last_name': u'Doe',
u'verified': True,
u'name': u'John Doe',
u'locale': u'en_US',
u'gender': u'male',
u'email': u'johndoe@example.com',
u'quotes': u'"quote',
u'link': u'http://www.facebook.com/johndoe',
u'timezone': 1,
u'updated_time': u'2011-11-11T12:33:52+0000',
u'id': u'11111'}
from pybossa.view import facebook
response_user = facebook.manage_user(fake_response['access_token'],
fake_user)
user = db.session.query(User).get(1)
assert user.email_addr == response_user.email_addr, response_user
@with_context
def test_40_facebook_oauth_creation(self):
"""Test WEB Facebook OAuth detects same user name/email works"""
fake_response = {
u'access_token': u'access_token',
u'token_type': u'Bearer',
u'expires_in': 3600,
u'id_token': u'token'}
fake_user = {
u'username': u'teleyinex',
u'first_name': u'John',
u'last_name': u'Doe',
u'verified': True,
u'name': u'John Doe',
u'locale': u'en_US',
u'gender': u'male',
u'email': u'johndoe@example.com',
u'quotes': u'"quote',
u'link': u'http://www.facebook.com/johndoe',
u'timezone': 1,
u'updated_time': u'2011-11-11T12:33:52+0000',
u'id': u'11111'}
self.register()
self.signout()
from pybossa.view import facebook
response_user = facebook.manage_user(fake_response['access_token'],
fake_user)
assert response_user is None, response_user
@with_context
def test_39_twitter_oauth_creation(self):
"""Test WEB Twitter OAuth creation of user works"""
fake_response = {
u'access_token': {u'oauth_token': u'oauth_token',
u'oauth_token_secret': u'oauth_token_secret'},
u'token_type': u'Bearer',
u'expires_in': 3600,
u'id_token': u'token'}
fake_user = {u'screen_name': u'johndoe',
u'user_id': u'11111'}
from pybossa.view import twitter
response_user = twitter.manage_user(fake_response['access_token'],
fake_user)
user = db.session.query(User).get(1)
assert user.email_addr == response_user.email_addr, response_user
res = self.signin(email=user.email_addr, password='wrong')
msg = "It seems like you signed up with your Twitter account"
assert msg in res.data, msg
@with_context
def test_40_twitter_oauth_creation(self):
"""Test WEB Twitter OAuth detects same user name/email works"""
fake_response = {
u'access_token': {u'oauth_token': u'oauth_token',
u'oauth_token_secret': u'oauth_token_secret'},
u'token_type': u'Bearer',
u'expires_in': 3600,
u'id_token': u'token'}
fake_user = {u'screen_name': u'johndoe',
u'user_id': u'11111'}
self.register()
self.signout()
from pybossa.view import twitter
response_user = twitter.manage_user(fake_response['access_token'],
fake_user)
assert response_user is None, response_user
@with_context
def test_41_password_change(self):
"""Test WEB password changing"""
password = "mehpassword"
self.register(password=password)
res = self.app.post('/account/johndoe/update',
data={'current_password': password,
'new_password': "p4ssw0rd",
'confirm': "p4ssw0rd",
'btn': 'Password'},
follow_redirects=True)
assert "Yay, you changed your password succesfully!" in res.data, res.data
password = "p4ssw0rd"
self.signin(password=password)
res = self.app.post('/account/johndoe/update',
data={'current_password': "wrongpassword",
'new_password': "p4ssw0rd",
'confirm': "p4ssw0rd",
'btn': 'Password'},
follow_redirects=True)
msg = "Your current password doesn't match the one in our records"
assert msg in res.data
res = self.app.post('/account/johndoe/update',
data={'current_password': '',
'new_password':'',
'confirm': '',
'btn': 'Password'},
follow_redirects=True)
msg = "Please correct the errors"
assert msg in res.data
@with_context
def test_42_password_link(self):
"""Test WEB visibility of password change link"""
self.register()
res = self.app.get('/account/johndoe/update')
assert "Change your Password" in res.data
user = User.query.get(1)
user.twitter_user_id = 1234
db.session.add(user)
db.session.commit()
res = self.app.get('/account/johndoe/update')
assert "Change your Password" not in res.data, res.data
@with_context
def test_43_terms_of_use_and_data(self):
"""Test WEB terms of use is working"""
res = self.app.get('account/signin', follow_redirects=True)
assert "/help/terms-of-use" in res.data, res.data
assert "http://opendatacommons.org/licenses/by/" in res.data, res.data
res = self.app.get('account/register', follow_redirects=True)
assert "http://okfn.org/terms-of-use/" in res.data, res.data
assert "http://opendatacommons.org/licenses/by/" in res.data, res.data
@with_context
@patch('pybossa.view.account.signer.loads')
def test_44_password_reset_key_errors(self, Mock):
"""Test WEB password reset key errors are caught"""
self.register()
user = User.query.get(1)
userdict = {'user': user.name, 'password': user.passwd_hash}
fakeuserdict = {'user': user.name, 'password': 'wronghash'}
fakeuserdict_err = {'user': user.name, 'passwd': 'some'}
fakeuserdict_form = {'user': user.name, 'passwd': 'p4ssw0rD'}
key = signer.dumps(userdict, salt='password-reset')
returns = [BadSignature('Fake Error'), BadSignature('Fake Error'), userdict,
fakeuserdict, userdict, userdict, fakeuserdict_err]
def side_effects(*args, **kwargs):
result = returns.pop(0)
if isinstance(result, BadSignature):
raise result
return result
Mock.side_effect = side_effects
# Request with no key
res = self.app.get('/account/reset-password', follow_redirects=True)
assert 403 == res.status_code
# Request with invalid key
res = self.app.get('/account/reset-password?key=foo', follow_redirects=True)
assert 403 == res.status_code
# Request with key exception
res = self.app.get('/account/reset-password?key=%s' % (key), follow_redirects=True)
assert 403 == res.status_code
res = self.app.get('/account/reset-password?key=%s' % (key), follow_redirects=True)
assert 200 == res.status_code
res = self.app.get('/account/reset-password?key=%s' % (key), follow_redirects=True)
assert 403 == res.status_code
# Check validation
res = self.app.post('/account/reset-password?key=%s' % (key),
data={'new_password': '',
'confirm': '#4a4'},
follow_redirects=True)
assert "Please correct the errors" in res.data, res.data
res = self.app.post('/account/reset-password?key=%s' % (key),
data={'new_password': 'p4ssw0rD',
'confirm': 'p4ssw0rD'},
follow_redirects=True)
assert "You reset your password successfully!" in res.data
# Request without password
res = self.app.get('/account/reset-password?key=%s' % (key), follow_redirects=True)
assert 403 == res.status_code
@with_context
@patch('pybossa.view.account.mail_queue', autospec=True)
@patch('pybossa.view.account.signer')
def test_45_password_reset_link(self, signer, queue):
"""Test WEB password reset email form"""
res = self.app.post('/account/forgot-password',
data={'email_addr': "johndoe@example.com"},
follow_redirects=True)
assert ("We don't have this email in our records. You may have"
" signed up with a different email or used Twitter, "
"Facebook, or Google to sign-in") in res.data
self.register()
self.register(name='janedoe')
self.register(name='google')
self.register(name='facebook')
user = User.query.get(1)
jane = User.query.get(2)
jane.twitter_user_id = 10
google = User.query.get(3)
google.google_user_id = 103
facebook = User.query.get(4)
facebook.facebook_user_id = 104
db.session.add_all([jane, google, facebook])
db.session.commit()
data = {'password': user.passwd_hash, 'user': user.name}
self.app.post('/account/forgot-password',
data={'email_addr': user.email_addr},
follow_redirects=True)
signer.dumps.assert_called_with(data, salt='password-reset')
enqueue_call = queue.enqueue.call_args_list[0]
assert send_mail == enqueue_call[0][0], "send_mail not called"
assert 'Click here to recover your account' in enqueue_call[0][1]['body']
assert 'To recover your password' in enqueue_call[0][1]['html']
data = {'password': jane.passwd_hash, 'user': jane.name}
self.app.post('/account/forgot-password',
data={'email_addr': 'janedoe@example.com'},
follow_redirects=True)
enqueue_call = queue.enqueue.call_args_list[1]
assert send_mail == enqueue_call[0][0], "send_mail not called"
assert 'your Twitter account to ' in enqueue_call[0][1]['body']
assert 'your Twitter account to ' in enqueue_call[0][1]['html']
data = {'password': google.passwd_hash, 'user': google.name}
self.app.post('/account/forgot-password',
data={'email_addr': 'google@example.com'},
follow_redirects=True)
enqueue_call = queue.enqueue.call_args_list[2]
assert send_mail == enqueue_call[0][0], "send_mail not called"
assert 'your Google account to ' in enqueue_call[0][1]['body']
assert 'your Google account to ' in enqueue_call[0][1]['html']
data = {'password': facebook.passwd_hash, 'user': facebook.name}
self.app.post('/account/forgot-password',
data={'email_addr': 'facebook@example.com'},
follow_redirects=True)
enqueue_call = queue.enqueue.call_args_list[3]
assert send_mail == enqueue_call[0][0], "send_mail not called"
assert 'your Facebook account to ' in enqueue_call[0][1]['body']
assert 'your Facebook account to ' in enqueue_call[0][1]['html']
# Test with not valid form
res = self.app.post('/account/forgot-password',
data={'email_addr': ''},
follow_redirects=True)
msg = "Something went wrong, please correct the errors"
assert msg in res.data, res.data
@patch('pybossa.view.projects.uploader.upload_file', return_value=True)
def test_46_tasks_exists(self, mock):
"""Test WEB tasks page works."""
self.register()
self.new_project()
res = self.app.get('/project/sampleapp/tasks/', follow_redirects=True)
assert "Edit the task presenter" in res.data, \
"Task Presenter Editor should be an option"
@with_context
@patch('pybossa.view.projects.uploader.upload_file', return_value=True)
def test_47_task_presenter_editor_loads(self, mock):
"""Test WEB task presenter editor loads"""
self.register()
self.new_project()
res = self.app.get('/project/sampleapp/tasks/taskpresentereditor',
follow_redirects=True)
err_msg = "Task Presenter options not found"
assert "Task Presenter Editor" in res.data, err_msg
err_msg = "Basic template not found"
assert "The most basic template" in res.data, err_msg
err_msg = "Image Pattern Recognition not found"
assert "Flickr Person Finder template" in res.data, err_msg
err_msg = "Geo-coding"
assert "Urban Park template" in res.data, err_msg
err_msg = "Transcribing documents"
assert "PDF transcription template" in res.data, err_msg
@patch('pybossa.view.projects.uploader.upload_file', return_value=True)
def test_48_task_presenter_editor_works(self, mock):
"""Test WEB task presenter editor works"""
self.register()
self.new_project()
project = db.session.query(Project).first()
err_msg = "Task Presenter should be empty"
assert not project.info.get('task_presenter'), err_msg
res = self.app.get('/project/sampleapp/tasks/taskpresentereditor?template=basic',
follow_redirects=True)
assert "var editor" in res.data, "CodeMirror Editor not found"
assert "Task Presenter" in res.data, "CodeMirror Editor not found"
assert "Task Presenter Preview" in res.data, "CodeMirror View not found"
res = self.app.post('/project/sampleapp/tasks/taskpresentereditor',
data={'editor': 'Some HTML code!'},
follow_redirects=True)
assert "Sample Project" in res.data, "Does not return to project details"
project = db.session.query(Project).first()
err_msg = "Task Presenter failed to update"
assert project.info['task_presenter'] == 'Some HTML code!', err_msg
# Check it loads the previous posted code:
res = self.app.get('/project/sampleapp/tasks/taskpresentereditor',
follow_redirects=True)
assert "Some HTML code" in res.data, res.data
@patch('pybossa.ckan.requests.get')
@patch('pybossa.view.projects.uploader.upload_file', return_value=True)
@patch('pybossa.forms.validator.requests.get')
def test_48_update_app_info(self, Mock, mock, mock_webhook):
"""Test WEB project update/edit works keeping previous info values"""
html_request = FakeResponse(text=json.dumps(self.pkg_json_not_found),
status_code=200,
headers={'content-type': 'application/json'},
encoding='utf-8')
Mock.return_value = html_request
mock_webhook.return_value = html_request
self.register()
self.new_project()
project = db.session.query(Project).first()
err_msg = "Task Presenter should be empty"
assert not project.info.get('task_presenter'), err_msg
res = self.app.post('/project/sampleapp/tasks/taskpresentereditor',
data={'editor': 'Some HTML code!'},
follow_redirects=True)
assert "Sample Project" in res.data, "Does not return to project details"
project = db.session.query(Project).first()
for i in range(10):
key = "key_%s" % i
project.info[key] = i
db.session.add(project)
db.session.commit()
_info = project.info
self.update_project()
project = db.session.query(Project).first()
for key in _info:
assert key in project.info.keys(), \
"The key %s is lost and it should be here" % key
assert project.name == "Sample Project", "The project has not been updated"
error_msg = "The project description has not been updated"
assert project.description == "Description", error_msg
error_msg = "The project long description has not been updated"
assert project.long_description == "Long desc", error_msg
@with_context
@patch('pybossa.view.projects.uploader.upload_file', return_value=True)
def test_49_announcement_messages(self, mock):
"""Test WEB announcement messages works"""
self.register()
res = self.app.get("/", follow_redirects=True)
error_msg = "There should be a message for the root user"
print res.data
assert "Root Message" in res.data, error_msg
error_msg = "There should be a message for the user"
assert "User Message" in res.data, error_msg
error_msg = "There should not be an owner message"
assert "Owner Message" not in res.data, error_msg
# Now make the user a project owner
self.new_project()
res = self.app.get("/", follow_redirects=True)
error_msg = "There should be a message for the root user"
assert "Root Message" in res.data, error_msg
error_msg = "There should be a message for the user"
assert "User Message" in res.data, error_msg
error_msg = "There should be an owner message"
assert "Owner Message" in res.data, error_msg
self.signout()
# Register another user
self.register(fullname="Jane Doe", name="janedoe",
password="janedoe", email="jane@jane.com")
res = self.app.get("/", follow_redirects=True)
error_msg = "There should not be a message for the root user"
assert "Root Message" not in res.data, error_msg
error_msg = "There should be a message for the user"
assert "User Message" in res.data, error_msg
error_msg = "There should not be an owner message"
assert "Owner Message" not in res.data, error_msg
self.signout()
# Now as an anonymous user
res = self.app.get("/", follow_redirects=True)
error_msg = "There should not be a message for the root user"
assert "Root Message" not in res.data, error_msg
error_msg = "There should not be a message for the user"
assert "User Message" not in res.data, error_msg
error_msg = "There should not be an owner message"
assert "Owner Message" not in res.data, error_msg
@with_context
def test_50_export_task_json(self):
"""Test WEB export Tasks to JSON works"""
Fixtures.create()
# First test for a non-existant project
uri = '/project/somethingnotexists/tasks/export'
res = self.app.get(uri, follow_redirects=True)
assert res.status == '404 NOT FOUND', res.status
# Now get the tasks in JSON format
uri = "/project/somethingnotexists/tasks/export?type=task&format=json"
res = self.app.get(uri, follow_redirects=True)
assert res.status == '404 NOT FOUND', res.status
# Now with a real project
uri = '/project/%s/tasks/export' % Fixtures.project_short_name
res = self.app.get(uri, follow_redirects=True)
heading = "%s: Export All Tasks and Task Runs" % Fixtures.project_name
assert heading in res.data, "Export page should be available\n %s" % res.data
# Now test that a 404 is raised when an arg is invalid
uri = "/project/%s/tasks/export?type=ask&format=json" % Fixtures.project_short_name
res = self.app.get(uri, follow_redirects=True)
assert res.status == '404 NOT FOUND', res.status
uri = "/project/%s/tasks/export?format=json" % Fixtures.project_short_name
res = self.app.get(uri, follow_redirects=True)
assert res.status == '404 NOT FOUND', res.status
uri = "/project/%s/tasks/export?type=task" % Fixtures.project_short_name
res = self.app.get(uri, follow_redirects=True)
assert res.status == '404 NOT FOUND', res.status
# And a 415 is raised if the requested format is not supported or invalid
uri = "/project/%s/tasks/export?type=task&format=gson" % Fixtures.project_short_name
res = self.app.get(uri, follow_redirects=True)
assert res.status == '415 UNSUPPORTED MEDIA TYPE', res.status
# Now get the tasks in JSON format
self.clear_temp_container(1) # Project ID 1 is assumed here. See project.id below.
uri = "/project/%s/tasks/export?type=task&format=json" % Fixtures.project_short_name
res = self.app.get(uri, follow_redirects=True)
zip = zipfile.ZipFile(StringIO(res.data))
# Check only one file in zipfile
err_msg = "filename count in ZIP is not 1"
assert len(zip.namelist()) == 1, err_msg
# Check ZIP filename
extracted_filename = zip.namelist()[0]
assert extracted_filename == 'test-app_task.json', zip.namelist()[0]
exported_tasks = json.loads(zip.read(extracted_filename))
project = db.session.query(Project)\
.filter_by(short_name=Fixtures.project_short_name)\
.first()
err_msg = "The number of exported tasks is different from Project Tasks"
assert len(exported_tasks) == len(project.tasks), err_msg
# Tasks are exported as an attached file
content_disposition = 'attachment; filename=%d_test-app_task_json.zip' % project.id
assert res.headers.get('Content-Disposition') == content_disposition, res.headers
def test_export_task_json_support_non_latin1_project_names(self):
project = ProjectFactory.create(name=u'Измени Киев!', short_name=u'Измени Киев!')
self.clear_temp_container(project.owner_id)
res = self.app.get('project/%s/tasks/export?type=task&format=json' % project.short_name,
follow_redirects=True)
filename = secure_filename(unidecode(u'Измени Киев!'))
assert filename in res.headers.get('Content-Disposition'), res.headers
def test_export_taskrun_json_support_non_latin1_project_names(self):
project = ProjectFactory.create(name=u'Измени Киев!', short_name=u'Измени Киев!')
res = self.app.get('project/%s/tasks/export?type=task_run&format=json' % project.short_name,
follow_redirects=True)
filename = secure_filename(unidecode(u'Измени Киев!'))
assert filename in res.headers.get('Content-Disposition'), res.headers
def test_export_task_csv_support_non_latin1_project_names(self):
project = ProjectFactory.create(name=u'Измени Киев!', short_name=u'Измени Киев!')
TaskFactory.create(project=project)
res = self.app.get('/project/%s/tasks/export?type=task&format=csv' % project.short_name,
follow_redirects=True)
filename = secure_filename(unidecode(u'Измени Киев!'))
assert filename in res.headers.get('Content-Disposition'), res.headers
def test_export_taskrun_csv_support_non_latin1_project_names(self):
project = ProjectFactory.create(name=u'Измени Киев!', short_name=u'Измени Киев!')
task = TaskFactory.create(project=project)
TaskRunFactory.create(task=task)
res = self.app.get('/project/%s/tasks/export?type=task_run&format=csv' % project.short_name,
follow_redirects=True)
filename = secure_filename(unidecode(u'Измени Киев!'))
assert filename in res.headers.get('Content-Disposition'), res.headers
@with_context
def test_export_taskruns_json(self):
"""Test WEB export Task Runs to JSON works"""
Fixtures.create()
# First test for a non-existant project
uri = '/project/somethingnotexists/tasks/export'
res = self.app.get(uri, follow_redirects=True)
assert res.status == '404 NOT FOUND', res.status
# Now get the tasks in JSON format
uri = "/project/somethingnotexists/tasks/export?type=task&format=json"
res = self.app.get(uri, follow_redirects=True)
assert res.status == '404 NOT FOUND', res.status
# Now with a real project
self.clear_temp_container(1) # Project ID 1 is assumed here. See project.id below.
uri = '/project/%s/tasks/export' % Fixtures.project_short_name
res = self.app.get(uri, follow_redirects=True)
heading = "%s: Export All Tasks and Task Runs" % Fixtures.project_name
assert heading in res.data, "Export page should be available\n %s" % res.data
# Now get the tasks in JSON format
uri = "/project/%s/tasks/export?type=task_run&format=json" % Fixtures.project_short_name
res = self.app.get(uri, follow_redirects=True)
zip = zipfile.ZipFile(StringIO(res.data))
# Check only one file in zipfile
err_msg = "filename count in ZIP is not 1"
assert len(zip.namelist()) == 1, err_msg
# Check ZIP filename
extracted_filename = zip.namelist()[0]
assert extracted_filename == 'test-app_task_run.json', zip.namelist()[0]
exported_task_runs = json.loads(zip.read(extracted_filename))
project = db.session.query(Project)\
.filter_by(short_name=Fixtures.project_short_name)\
.first()
err_msg = "The number of exported task runs is different from Project Tasks"
assert len(exported_task_runs) == len(project.task_runs), err_msg
# Task runs are exported as an attached file
content_disposition = 'attachment; filename=%d_test-app_task_run_json.zip' % project.id
assert res.headers.get('Content-Disposition') == content_disposition, res.headers
@with_context
def test_export_task_json_no_tasks_returns_file_with_empty_list(self):
"""Test WEB export Tasks to JSON returns empty list if no tasks in project"""
project = ProjectFactory.create(short_name='no_tasks_here')
uri = "/project/%s/tasks/export?type=task&format=json" % project.short_name
res = self.app.get(uri, follow_redirects=True)
zip = zipfile.ZipFile(StringIO(res.data))
extracted_filename = zip.namelist()[0]
exported_task_runs = json.loads(zip.read(extracted_filename))
assert exported_task_runs == [], exported_task_runs
@with_context
def test_export_task_csv(self):
"""Test WEB export Tasks to CSV works"""
#Fixtures.create()
# First test for a non-existant project
uri = '/project/somethingnotexists/tasks/export'
res = self.app.get(uri, follow_redirects=True)
assert res.status == '404 NOT FOUND', res.status
# Now get the tasks in CSV format
uri = "/project/somethingnotexists/tasks/export?type=task&format=csv"
res = self.app.get(uri, follow_redirects=True)
assert res.status == '404 NOT FOUND', res.status
# Now get the wrong table name in CSV format
uri = "/project/%s/tasks/export?type=wrong&format=csv" % Fixtures.project_short_name
res = self.app.get(uri, follow_redirects=True)
assert res.status == '404 NOT FOUND', res.status
# Now with a real project
project = ProjectFactory.create()
self.clear_temp_container(project.owner_id)
for i in range(0, 5):
task = TaskFactory.create(project=project, info={'question': i})
uri = '/project/%s/tasks/export' % project.short_name
res = self.app.get(uri, follow_redirects=True)
heading = "%s: Export All Tasks and Task Runs" % project.name
data = res.data.decode('utf-8')
assert heading in data, "Export page should be available\n %s" % data
# Now get the tasks in CSV format
uri = "/project/%s/tasks/export?type=task&format=csv" % project.short_name
res = self.app.get(uri, follow_redirects=True)
zip = zipfile.ZipFile(StringIO(res.data))
# Check only one file in zipfile
err_msg = "filename count in ZIP is not 1"
assert len(zip.namelist()) == 1, err_msg
# Check ZIP filename
extracted_filename = zip.namelist()[0]
assert extracted_filename == 'project1_task.csv', zip.namelist()[0]
csv_content = StringIO(zip.read(extracted_filename))
csvreader = unicode_csv_reader(csv_content)
project = db.session.query(Project)\
.filter_by(short_name=project.short_name)\
.first()
exported_tasks = []
n = 0
for row in csvreader:
print row
if n != 0:
exported_tasks.append(row)
else:
keys = row
n = n + 1
err_msg = "The number of exported tasks is different from Project Tasks"
assert len(exported_tasks) == len(project.tasks), err_msg
for t in project.tasks:
err_msg = "All the task column names should be included"
for tk in t.dictize().keys():
expected_key = "task__%s" % tk
assert expected_key in keys, err_msg
err_msg = "All the task.info column names should be included"
for tk in t.info.keys():
expected_key = "taskinfo__%s" % tk
assert expected_key in keys, err_msg
for et in exported_tasks:
task_id = et[keys.index('task__id')]
task = db.session.query(Task).get(task_id)
task_dict = task.dictize()
for k in task_dict:
slug = 'task__%s' % k
err_msg = "%s != %s" % (task_dict[k], et[keys.index(slug)])
if k != 'info':
assert unicode(task_dict[k]) == et[keys.index(slug)], err_msg
else:
assert json.dumps(task_dict[k]) == et[keys.index(slug)], err_msg
for k in task_dict['info'].keys():
slug = 'taskinfo__%s' % k
err_msg = "%s != %s" % (task_dict['info'][k], et[keys.index(slug)])
assert unicode(task_dict['info'][k]) == et[keys.index(slug)], err_msg
# Tasks are exported as an attached file
content_disposition = 'attachment; filename=%d_project1_task_csv.zip' % project.id
assert res.headers.get('Content-Disposition') == content_disposition, res.headers
@with_context
def test_export_task_csv_no_tasks_returns_empty_file(self):
"""Test WEB export Tasks to CSV returns empty file if no tasks in project"""
project = ProjectFactory.create(short_name='no_tasks_here')
uri = "/project/%s/tasks/export?type=task&format=csv" % project.short_name
res = self.app.get(uri, follow_redirects=True)
zip = zipfile.ZipFile(StringIO(res.data))
extracted_filename = zip.namelist()[0]
csv_content = StringIO(zip.read(extracted_filename))
csvreader = unicode_csv_reader(csv_content)
is_empty = True
for line in csvreader:
is_empty = False, line
assert is_empty
@with_context
def test_53_export_task_runs_csv(self):
"""Test WEB export Task Runs to CSV works"""
# First test for a non-existant project
uri = '/project/somethingnotexists/tasks/export'
res = self.app.get(uri, follow_redirects=True)
assert res.status == '404 NOT FOUND', res.status
# Now get the tasks in CSV format
uri = "/project/somethingnotexists/tasks/export?type=tas&format=csv"
res = self.app.get(uri, follow_redirects=True)
assert res.status == '404 NOT FOUND', res.status
# Now with a real project
project = ProjectFactory.create()
self.clear_temp_container(project.owner_id)
task = TaskFactory.create(project=project)
for i in range(2):
task_run = TaskRunFactory.create(project=project, task=task, info={'answer': i})
uri = '/project/%s/tasks/export' % project.short_name
res = self.app.get(uri, follow_redirects=True)
heading = "%s: Export All Tasks and Task Runs" % project.name
data = res.data.decode('utf-8')
assert heading in data, "Export page should be available\n %s" % data
# Now get the tasks in CSV format
uri = "/project/%s/tasks/export?type=task_run&format=csv" % project.short_name
res = self.app.get(uri, follow_redirects=True)
zip = zipfile.ZipFile(StringIO(res.data))
# Check only one file in zipfile
err_msg = "filename count in ZIP is not 1"
assert len(zip.namelist()) == 1, err_msg
# Check ZIP filename
extracted_filename = zip.namelist()[0]
assert extracted_filename == 'project1_task_run.csv', zip.namelist()[0]
csv_content = StringIO(zip.read(extracted_filename))
csvreader = unicode_csv_reader(csv_content)
project = db.session.query(Project)\
.filter_by(short_name=project.short_name)\
.first()
exported_task_runs = []
n = 0
for row in csvreader:
if n != 0:
exported_task_runs.append(row)
else:
keys = row
n = n + 1
err_msg = "The number of exported task runs is different \
from Project Tasks Runs: %s != %s" % (len(exported_task_runs), len(project.task_runs))
assert len(exported_task_runs) == len(project.task_runs), err_msg
for t in project.tasks[0].task_runs:
for tk in t.dictize().keys():
expected_key = "task_run__%s" % tk
assert expected_key in keys, expected_key
for tk in t.info.keys():
expected_key = "task_runinfo__%s" % tk
assert expected_key in keys, expected_key
for et in exported_task_runs:
task_run_id = et[keys.index('task_run__id')]
task_run = db.session.query(TaskRun).get(task_run_id)
task_run_dict = task_run.dictize()
for k in task_run_dict:
slug = 'task_run__%s' % k
err_msg = "%s != %s" % (task_run_dict[k], et[keys.index(slug)])
if k != 'info':
assert unicode(task_run_dict[k]) == et[keys.index(slug)], err_msg
else:
assert json.dumps(task_run_dict[k]) == et[keys.index(slug)], err_msg
for k in task_run_dict['info'].keys():
slug = 'task_runinfo__%s' % k
err_msg = "%s != %s" % (task_run_dict['info'][k], et[keys.index(slug)])
assert unicode(task_run_dict['info'][k]) == et[keys.index(slug)], err_msg
# Task runs are exported as an attached file
content_disposition = 'attachment; filename=%d_project1_task_run_csv.zip' % project.id
assert res.headers.get('Content-Disposition') == content_disposition, res.headers
@with_context
@patch('pybossa.view.projects.Ckan', autospec=True)
def test_export_tasks_ckan_exception(self, mock1):
mocks = [Mock()]
from test_ckan import TestCkanModule
fake_ckn = TestCkanModule()
package = fake_ckn.pkg_json_found
package['id'] = 3
mocks[0].package_exists.return_value = (False,
Exception("CKAN: error",
"error", 500))
# mocks[0].package_create.return_value = fake_ckn.pkg_json_found
# mocks[0].resource_create.return_value = dict(result=dict(id=3))
# mocks[0].datastore_create.return_value = 'datastore'
# mocks[0].datastore_upsert.return_value = 'datastore'
mock1.side_effect = mocks
"""Test WEB Export CKAN Tasks works."""
Fixtures.create()
user = db.session.query(User).filter_by(name=Fixtures.name).first()
project = db.session.query(Project).first()
user.ckan_api = 'ckan-api-key'
project.owner_id = user.id
db.session.add(user)
db.session.add(project)
db.session.commit()
self.signin(email=user.email_addr, password=Fixtures.password)
# Now with a real project
uri = '/project/%s/tasks/export' % Fixtures.project_short_name
res = self.app.get(uri, follow_redirects=True)
heading = "%s: Export All Tasks and Task Runs" % Fixtures.project_name
assert heading in res.data, "Export page should be available\n %s" % res.data
# Now get the tasks in CKAN format
uri = "/project/%s/tasks/export?type=task&format=ckan" % Fixtures.project_short_name
with patch.dict(self.flask_app.config, {'CKAN_URL': 'http://ckan.com'}):
# First time exporting the package
res = self.app.get(uri, follow_redirects=True)
msg = 'Error'
err_msg = "An exception should be raised"
assert msg in res.data, err_msg
@with_context
@patch('pybossa.view.projects.Ckan', autospec=True)
def test_export_tasks_ckan_connection_error(self, mock1):
mocks = [Mock()]
from test_ckan import TestCkanModule
fake_ckn = TestCkanModule()
package = fake_ckn.pkg_json_found
package['id'] = 3
mocks[0].package_exists.return_value = (False, ConnectionError)
# mocks[0].package_create.return_value = fake_ckn.pkg_json_found
# mocks[0].resource_create.return_value = dict(result=dict(id=3))
# mocks[0].datastore_create.return_value = 'datastore'
# mocks[0].datastore_upsert.return_value = 'datastore'
mock1.side_effect = mocks
"""Test WEB Export CKAN Tasks works."""
Fixtures.create()
user = db.session.query(User).filter_by(name=Fixtures.name).first()
project = db.session.query(Project).first()
user.ckan_api = 'ckan-api-key'
project.owner_id = user.id
db.session.add(user)
db.session.add(project)
db.session.commit()
self.signin(email=user.email_addr, password=Fixtures.password)
# Now with a real project
uri = '/project/%s/tasks/export' % Fixtures.project_short_name
res = self.app.get(uri, follow_redirects=True)
heading = "%s: Export All Tasks and Task Runs" % Fixtures.project_name
assert heading in res.data, "Export page should be available\n %s" % res.data
# Now get the tasks in CKAN format
uri = "/project/%s/tasks/export?type=task&format=ckan" % Fixtures.project_short_name
with patch.dict(self.flask_app.config, {'CKAN_URL': 'http://ckan.com'}):
# First time exporting the package
res = self.app.get(uri, follow_redirects=True)
msg = 'CKAN server seems to be down'
err_msg = "A connection exception should be raised"
assert msg in res.data, err_msg
@with_context
@patch('pybossa.view.projects.Ckan', autospec=True)
def test_task_export_tasks_ckan_first_time(self, mock1):
"""Test WEB Export CKAN Tasks works without an existing package."""
# Second time exporting the package
mocks = [Mock()]
resource = dict(name='task', id=1)
package = dict(id=3, resources=[resource])
mocks[0].package_exists.return_value = (None, None)
mocks[0].package_create.return_value = package
#mocks[0].datastore_delete.return_value = None
mocks[0].datastore_create.return_value = None
mocks[0].datastore_upsert.return_value = None
mocks[0].resource_create.return_value = dict(result=dict(id=3))
mocks[0].datastore_create.return_value = 'datastore'
mocks[0].datastore_upsert.return_value = 'datastore'
mock1.side_effect = mocks
Fixtures.create()
user = db.session.query(User).filter_by(name=Fixtures.name).first()
project = db.session.query(Project).first()
user.ckan_api = 'ckan-api-key'
project.owner_id = user.id
db.session.add(user)
db.session.add(project)
db.session.commit()
self.signin(email=user.email_addr, password=Fixtures.password)
# First test for a non-existant project
uri = '/project/somethingnotexists/tasks/export'
res = self.app.get(uri, follow_redirects=True)
assert res.status == '404 NOT FOUND', res.status
# Now get the tasks in CKAN format
uri = "/project/somethingnotexists/tasks/export?type=task&format=ckan"
res = self.app.get(uri, follow_redirects=True)
assert res.status == '404 NOT FOUND', res.status
# Now get the tasks in CKAN format
uri = "/project/somethingnotexists/tasks/export?type=other&format=ckan"
res = self.app.get(uri, follow_redirects=True)
assert res.status == '404 NOT FOUND', res.status
# Now with a real project
uri = '/project/%s/tasks/export' % Fixtures.project_short_name
res = self.app.get(uri, follow_redirects=True)
heading = "%s: Export All Tasks and Task Runs" % Fixtures.project_name
assert heading in res.data, "Export page should be available\n %s" % res.data
# Now get the tasks in CKAN format
uri = "/project/%s/tasks/export?type=task&format=ckan" % Fixtures.project_short_name
with patch.dict(self.flask_app.config, {'CKAN_URL': 'http://ckan.com'}):
# First time exporting the package
res = self.app.get(uri, follow_redirects=True)
msg = 'Data exported to http://ckan.com'
err_msg = "Tasks should be exported to CKAN"
assert msg in res.data, err_msg
@with_context
@patch('pybossa.view.projects.Ckan', autospec=True)
def test_task_export_tasks_ckan_second_time(self, mock1):
"""Test WEB Export CKAN Tasks works with an existing package."""
# Second time exporting the package
mocks = [Mock()]
resource = dict(name='task', id=1)
package = dict(id=3, resources=[resource])
mocks[0].package_exists.return_value = (package, None)
mocks[0].package_update.return_value = package
mocks[0].datastore_delete.return_value = None
mocks[0].datastore_create.return_value = None
mocks[0].datastore_upsert.return_value = None
mocks[0].resource_create.return_value = dict(result=dict(id=3))
mocks[0].datastore_create.return_value = 'datastore'
mocks[0].datastore_upsert.return_value = 'datastore'
mock1.side_effect = mocks
Fixtures.create()
user = db.session.query(User).filter_by(name=Fixtures.name).first()
project = db.session.query(Project).first()
user.ckan_api = 'ckan-api-key'
project.owner_id = user.id
db.session.add(user)
db.session.add(project)
db.session.commit()
self.signin(email=user.email_addr, password=Fixtures.password)
# First test for a non-existant project
uri = '/project/somethingnotexists/tasks/export'
res = self.app.get(uri, follow_redirects=True)
assert res.status == '404 NOT FOUND', res.status
# Now get the tasks in CKAN format
uri = "/project/somethingnotexists/tasks/export?type=task&format=ckan"
res = self.app.get(uri, follow_redirects=True)
assert res.status == '404 NOT FOUND', res.status
# Now with a real project
uri = '/project/%s/tasks/export' % Fixtures.project_short_name
res = self.app.get(uri, follow_redirects=True)
heading = "%s: Export All Tasks and Task Runs" % Fixtures.project_name
assert heading in res.data, "Export page should be available\n %s" % res.data
# Now get the tasks in CKAN format
uri = "/project/%s/tasks/export?type=task&format=ckan" % Fixtures.project_short_name
#res = self.app.get(uri, follow_redirects=True)
with patch.dict(self.flask_app.config, {'CKAN_URL': 'http://ckan.com'}):
# First time exporting the package
res = self.app.get(uri, follow_redirects=True)
msg = 'Data exported to http://ckan.com'
err_msg = "Tasks should be exported to CKAN"
assert msg in res.data, err_msg
@with_context
@patch('pybossa.view.projects.Ckan', autospec=True)
def test_task_export_tasks_ckan_without_resources(self, mock1):
"""Test WEB Export CKAN Tasks works without resources."""
mocks = [Mock()]
package = dict(id=3, resources=[])
mocks[0].package_exists.return_value = (package, None)
mocks[0].package_update.return_value = package
mocks[0].resource_create.return_value = dict(result=dict(id=3))
mocks[0].datastore_create.return_value = 'datastore'
mocks[0].datastore_upsert.return_value = 'datastore'
mock1.side_effect = mocks
Fixtures.create()
user = db.session.query(User).filter_by(name=Fixtures.name).first()
project = db.session.query(Project).first()
user.ckan_api = 'ckan-api-key'
project.owner_id = user.id
db.session.add(user)
db.session.add(project)
db.session.commit()
self.signin(email=user.email_addr, password=Fixtures.password)
# First test for a non-existant project
uri = '/project/somethingnotexists/tasks/export'
res = self.app.get(uri, follow_redirects=True)
assert res.status == '404 NOT FOUND', res.status
# Now get the tasks in CKAN format
uri = "/project/somethingnotexists/tasks/export?type=task&format=ckan"
res = self.app.get(uri, follow_redirects=True)
assert res.status == '404 NOT FOUND', res.status
# Now with a real project
uri = '/project/%s/tasks/export' % Fixtures.project_short_name
res = self.app.get(uri, follow_redirects=True)
heading = "%s: Export All Tasks and Task Runs" % Fixtures.project_name
assert heading in res.data, "Export page should be available\n %s" % res.data
# Now get the tasks in CKAN format
uri = "/project/%s/tasks/export?type=task&format=ckan" % Fixtures.project_short_name
#res = self.app.get(uri, follow_redirects=True)
with patch.dict(self.flask_app.config, {'CKAN_URL': 'http://ckan.com'}):
# First time exporting the package
res = self.app.get(uri, follow_redirects=True)
msg = 'Data exported to http://ckan.com'
err_msg = "Tasks should be exported to CKAN"
assert msg in res.data, err_msg
@patch('pybossa.view.projects.uploader.upload_file', return_value=True)
def test_get_import_tasks_no_params_shows_options_and_templates(self, mock):
"""Test WEB import tasks displays the different importers and template
tasks"""
Fixtures.create()
self.register()
self.new_project()
res = self.app.get('/project/sampleapp/tasks/import', follow_redirects=True)
err_msg = "There should be a CSV importer"
assert "type=csv" in res.data, err_msg
err_msg = "There should be a GDocs importer"
assert "type=gdocs" in res.data, err_msg
err_msg = "There should be an Epicollect importer"
assert "type=epicollect" in res.data, err_msg
err_msg = "There should be a Flickr importer"
assert "type=flickr" in res.data, err_msg
err_msg = "There should be a Dropbox importer"
assert "type=dropbox" in res.data, err_msg
err_msg = "There should be a Twitter importer"
assert "type=twitter" in res.data, err_msg
err_msg = "There should be an S3 importer"
assert "type=s3" in res.data, err_msg
err_msg = "There should be an Image template"
assert "template=image" in res.data, err_msg
err_msg = "There should be a Map template"
assert "template=map" in res.data, err_msg
err_msg = "There should be a PDF template"
assert "template=pdf" in res.data, err_msg
err_msg = "There should be a Sound template"
assert "template=sound" in res.data, err_msg
err_msg = "There should be a Video template"
assert "template=video" in res.data, err_msg
self.signout()
self.signin(email=Fixtures.email_addr2, password=Fixtures.password)
res = self.app.get('/project/sampleapp/tasks/import', follow_redirects=True)
assert res.status_code == 403, res.status_code
def test_get_import_tasks_with_specific_variant_argument(self):
"""Test task importer with specific importer variant argument
shows the form for it, for each of the variants"""
self.register()
owner = db.session.query(User).first()
project = ProjectFactory.create(owner=owner)
# CSV
url = "/project/%s/tasks/import?type=csv" % project.short_name
res = self.app.get(url, follow_redirects=True)
data = res.data.decode('utf-8')
assert "From a CSV file" in data
assert 'action="/project/%E2%9C%93project1/tasks/import"' in data
# Google Docs
url = "/project/%s/tasks/import?type=gdocs" % project.short_name
res = self.app.get(url, follow_redirects=True)
data = res.data.decode('utf-8')
assert "From a Google Docs Spreadsheet" in data
assert 'action="/project/%E2%9C%93project1/tasks/import"' in data
# Epicollect Plus
url = "/project/%s/tasks/import?type=epicollect" % project.short_name
res = self.app.get(url, follow_redirects=True)
data = res.data.decode('utf-8')
assert "From an EpiCollect Plus project" in data
assert 'action="/project/%E2%9C%93project1/tasks/import"' in data
# Flickr
url = "/project/%s/tasks/import?type=flickr" % project.short_name
res = self.app.get(url, follow_redirects=True)
data = res.data.decode('utf-8')
assert "From a Flickr Album" in data
assert 'action="/project/%E2%9C%93project1/tasks/import"' in data
# Dropbox
url = "/project/%s/tasks/import?type=dropbox" % project.short_name
res = self.app.get(url, follow_redirects=True)
data = res.data.decode('utf-8')
assert "From your Dropbox account" in data
assert 'action="/project/%E2%9C%93project1/tasks/import"' in data
# Twitter
url = "/project/%s/tasks/import?type=twitter" % project.short_name
res = self.app.get(url, follow_redirects=True)
data = res.data.decode('utf-8')
assert "From a Twitter hashtag or account" in data
assert 'action="/project/%E2%9C%93project1/tasks/import"' in data
# S3
url = "/project/%s/tasks/import?type=s3" % project.short_name
res = self.app.get(url, follow_redirects=True)
data = res.data.decode('utf-8')
assert "From an Amazon S3 bucket" in data
assert 'action="/project/%E2%9C%93project1/tasks/import"' in data
# Invalid
url = "/project/%s/tasks/import?type=invalid" % project.short_name
res = self.app.get(url, follow_redirects=True)
assert res.status_code == 404, res.status_code
@patch('pybossa.core.importer.get_all_importer_names')
def test_get_importer_doesnt_show_unavailable_importers(self, names):
names.return_value = ['csv', 'gdocs', 'epicollect', 's3']
self.register()
owner = db.session.query(User).first()
project = ProjectFactory.create(owner=owner)
url = "/project/%s/tasks/import" % project.short_name
res = self.app.get(url, follow_redirects=True)
assert "type=flickr" not in res.data
assert "type=dropbox" not in res.data
assert "type=twitter" not in res.data
@patch('pybossa.view.projects.redirect', wraps=redirect)
@patch('pybossa.importers.csv.requests.get')
def test_import_tasks_redirects_on_success(self, request, redirect):
"""Test WEB when importing tasks succeeds, user is redirected to tasks main page"""
csv_file = FakeResponse(text='Foo,Bar,Baz\n1,2,3', status_code=200,
headers={'content-type': 'text/plain'},
encoding='utf-8')
request.return_value = csv_file
self.register()
self.new_project()
project = db.session.query(Project).first()
url = '/project/%s/tasks/import' % project.short_name
res = self.app.post(url, data={'csv_url': 'http://myfakecsvurl.com',
'formtype': 'csv', 'form_name': 'csv'},
follow_redirects=True)
assert "1 new task was imported successfully" in res.data
redirect.assert_called_with('/project/%s/tasks/' % project.short_name)
@patch('pybossa.view.projects.importer.count_tasks_to_import')
@patch('pybossa.view.projects.importer.create_tasks')
def test_import_few_tasks_is_done_synchronously(self, create, count):
"""Test WEB importing a small amount of tasks is done synchronously"""
count.return_value = 1
create.return_value = ImportReport(message='1 new task was imported successfully', metadata=None, total=1)
self.register()
self.new_project()
project = db.session.query(Project).first()
url = '/project/%s/tasks/import' % project.short_name
res = self.app.post(url, data={'csv_url': 'http://myfakecsvurl.com',
'formtype': 'csv', 'form_name': 'csv'},
follow_redirects=True)
assert "1 new task was imported successfully" in res.data
@patch('pybossa.view.projects.importer_queue', autospec=True)
@patch('pybossa.view.projects.importer.count_tasks_to_import')
def test_import_tasks_as_background_job(self, count_tasks, queue):
"""Test WEB importing a big amount of tasks is done in the background"""
from pybossa.view.projects import MAX_NUM_SYNCHRONOUS_TASKS_IMPORT
count_tasks.return_value = MAX_NUM_SYNCHRONOUS_TASKS_IMPORT + 1
self.register()
self.new_project()
project = db.session.query(Project).first()
url = '/project/%s/tasks/import' % project.short_name
res = self.app.post(url, data={'csv_url': 'http://myfakecsvurl.com',
'formtype': 'csv', 'form_name': 'csv'},
follow_redirects=True)
tasks = db.session.query(Task).all()
assert tasks == [], "Tasks should not be immediately added"
data = {'type': 'csv', 'csv_url': 'http://myfakecsvurl.com'}
queue.enqueue.assert_called_once_with(import_tasks, project.id, **data)
msg = "You're trying to import a large amount of tasks, so please be patient.\
You will receive an email when the tasks are ready."
assert msg in res.data
@patch('pybossa.view.projects.uploader.upload_file', return_value=True)
@patch('pybossa.importers.csv.requests.get')
def test_bulk_csv_import_works(self, Mock, mock):
"""Test WEB bulk import works"""
csv_file = FakeResponse(text='Foo,Bar,priority_0\n1,2,3', status_code=200,
headers={'content-type': 'text/plain'},
encoding='utf-8')
Mock.return_value = csv_file
self.register()
self.new_project()
project = db.session.query(Project).first()
url = '/project/%s/tasks/import' % (project.short_name)
res = self.app.post(url, data={'csv_url': 'http://myfakecsvurl.com',
'formtype': 'csv', 'form_name': 'csv'},
follow_redirects=True)
task = db.session.query(Task).first()
assert {u'Bar': u'2', u'Foo': u'1'} == task.info
assert task.priority_0 == 3
assert "1 new task was imported successfully" in res.data
# Check that only new items are imported
empty_file = FakeResponse(text='Foo,Bar,priority_0\n1,2,3\n4,5,6',
status_code=200,
headers={'content-type': 'text/plain'},
encoding='utf-8')
Mock.return_value = empty_file
project = db.session.query(Project).first()
url = '/project/%s/tasks/import' % (project.short_name)
res = self.app.post(url, data={'csv_url': 'http://myfakecsvurl.com',
'formtype': 'csv', 'form_name': 'csv'},
follow_redirects=True)
project = db.session.query(Project).first()
assert len(project.tasks) == 2, "There should be only 2 tasks"
n = 0
csv_tasks = [{u'Foo': u'1', u'Bar': u'2'}, {u'Foo': u'4', u'Bar': u'5'}]
for t in project.tasks:
assert t.info == csv_tasks[n], "The task info should be the same"
n += 1
@patch('pybossa.view.projects.uploader.upload_file', return_value=True)
@patch('pybossa.importers.csv.requests.get')
def test_bulk_gdocs_import_works(self, Mock, mock):
"""Test WEB bulk GDocs import works."""
csv_file = FakeResponse(text='Foo,Bar,priority_0\n1,2,3', status_code=200,
headers={'content-type': 'text/plain'},
encoding='utf-8')
Mock.return_value = csv_file
self.register()
self.new_project()
project = db.session.query(Project).first()
url = '/project/%s/tasks/import' % (project.short_name)
res = self.app.post(url, data={'googledocs_url': 'http://drive.google.com',
'formtype': 'gdocs', 'form_name': 'gdocs'},
follow_redirects=True)
task = db.session.query(Task).first()
assert {u'Bar': u'2', u'Foo': u'1'} == task.info
assert task.priority_0 == 3
assert "1 new task was imported successfully" in res.data
# Check that only new items are imported
empty_file = FakeResponse(text='Foo,Bar,priority_0\n1,2,3\n4,5,6',
status_code=200,
headers={'content-type': 'text/plain'},
encoding='utf-8')
Mock.return_value = empty_file
project = db.session.query(Project).first()
url = '/project/%s/tasks/import' % (project.short_name)
res = self.app.post(url, data={'googledocs_url': 'http://drive.google.com',
'formtype': 'gdocs', 'form_name': 'gdocs'},
follow_redirects=True)
project = db.session.query(Project).first()
assert len(project.tasks) == 2, "There should be only 2 tasks"
n = 0
csv_tasks = [{u'Foo': u'1', u'Bar': u'2'}, {u'Foo': u'4', u'Bar': u'5'}]
for t in project.tasks:
assert t.info == csv_tasks[n], "The task info should be the same"
n += 1
# Check that only new items are imported
project = db.session.query(Project).first()
url = '/project/%s/tasks/import' % (project.short_name)
res = self.app.post(url, data={'googledocs_url': 'http://drive.google.com',
'formtype': 'gdocs', 'form_name': 'gdocs'},
follow_redirects=True)
project = db.session.query(Project).first()
assert len(project.tasks) == 2, "There should be only 2 tasks"
n = 0
csv_tasks = [{u'Foo': u'1', u'Bar': u'2'}, {u'Foo': u'4', u'Bar': u'5'}]
for t in project.tasks:
assert t.info == csv_tasks[n], "The task info should be the same"
n += 1
assert "no new records" in res.data, res.data
@patch('pybossa.view.projects.uploader.upload_file', return_value=True)
@patch('pybossa.importers.epicollect.requests.get')
def test_bulk_epicollect_import_works(self, Mock, mock):
"""Test WEB bulk Epicollect import works"""
data = [dict(DeviceID=23)]
fake_response = FakeResponse(text=json.dumps(data), status_code=200,
headers={'content-type': 'application/json'},
encoding='utf-8')
Mock.return_value = fake_response
self.register()
self.new_project()
project = db.session.query(Project).first()
res = self.app.post(('/project/%s/tasks/import' % (project.short_name)),
data={'epicollect_project': 'fakeproject',
'epicollect_form': 'fakeform',
'formtype': 'json', 'form_name': 'epicollect'},
follow_redirects=True)
project = db.session.query(Project).first()
err_msg = "Tasks should be imported"
assert "1 new task was imported successfully" in res.data, err_msg
tasks = db.session.query(Task).filter_by(project_id=project.id).all()
err_msg = "The imported task from EpiCollect is wrong"
assert tasks[0].info['DeviceID'] == 23, err_msg
data = [dict(DeviceID=23), dict(DeviceID=24)]
fake_response = FakeResponse(text=json.dumps(data), status_code=200,
headers={'content-type': 'application/json'},
encoding='utf-8')
Mock.return_value = fake_response
res = self.app.post(('/project/%s/tasks/import' % (project.short_name)),
data={'epicollect_project': 'fakeproject',
'epicollect_form': 'fakeform',
'formtype': 'json', 'form_name': 'epicollect'},
follow_redirects=True)
project = db.session.query(Project).first()
assert len(project.tasks) == 2, "There should be only 2 tasks"
n = 0
epi_tasks = [{u'DeviceID': 23}, {u'DeviceID': 24}]
for t in project.tasks:
assert t.info == epi_tasks[n], "The task info should be the same"
n += 1
@patch('pybossa.importers.flickr.requests.get')
def test_bulk_flickr_import_works(self, request):
"""Test WEB bulk Flickr import works"""
data = {
"photoset": {
"id": "72157633923521788",
"primary": "8947113500",
"owner": "32985084@N00",
"ownername": "Teleyinex",
"photo": [{ "id": "8947115130", "secret": "00e2301a0d",
"server": "5441", "farm": 6, "title": "Title",
"isprimary": 0, "ispublic": 1, "isfriend": 0,
"isfamily": 0 }
],
"page": 1,
"per_page": "500",
"perpage": "500",
"pages": 1,
"total": 1,
"title": "Science Hack Day Balloon Mapping Workshop" },
"stat": "ok" }
fake_response = FakeResponse(text=json.dumps(data), status_code=200,
headers={'content-type': 'application/json'},
encoding='utf-8')
request.return_value = fake_response
self.register()
self.new_project()
project = db.session.query(Project).first()
res = self.app.post(('/project/%s/tasks/import' % (project.short_name)),
data={'album_id': '1234',
'form_name': 'flickr'},
follow_redirects=True)
project = db.session.query(Project).first()
err_msg = "Tasks should be imported"
assert "1 new task was imported successfully" in res.data, err_msg
tasks = db.session.query(Task).filter_by(project_id=project.id).all()
expected_info = {
u'url': u'https://farm6.staticflickr.com/5441/8947115130_00e2301a0d.jpg',
u'url_m': u'https://farm6.staticflickr.com/5441/8947115130_00e2301a0d_m.jpg',
u'url_b': u'https://farm6.staticflickr.com/5441/8947115130_00e2301a0d_b.jpg',
u'link': u'https://www.flickr.com/photos/32985084@N00/8947115130',
u'title': u'Title'}
assert tasks[0].info == expected_info, tasks[0].info
def test_flickr_importer_page_shows_option_to_log_into_flickr(self):
self.register()
owner = db.session.query(User).first()
project = ProjectFactory.create(owner=owner)
url = "/project/%s/tasks/import?type=flickr" % project.short_name
res = self.app.get(url)
login_url = '/flickr/?next=%2Fproject%2F%25E2%259C%2593project1%2Ftasks%2Fimport%3Ftype%3Dflickr'
assert login_url in res.data
def test_bulk_dropbox_import_works(self):
"""Test WEB bulk Dropbox import works"""
dropbox_file_data = (u'{"bytes":286,'
u'"link":"https://www.dropbox.com/s/l2b77qvlrequ6gl/test.txt?dl=0",'
u'"name":"test.txt",'
u'"icon":"https://www.dropbox.com/static/images/icons64/page_white_text.png"}')
self.register()
self.new_project()
project = db.session.query(Project).first()
res = self.app.post('/project/%s/tasks/import' % project.short_name,
data={'files-0': dropbox_file_data,
'form_name': 'dropbox'},
follow_redirects=True)
project = db.session.query(Project).first()
err_msg = "Tasks should be imported"
tasks = db.session.query(Task).filter_by(project_id=project.id).all()
expected_info = {
u'link_raw': u'https://www.dropbox.com/s/l2b77qvlrequ6gl/test.txt?raw=1',
u'link': u'https://www.dropbox.com/s/l2b77qvlrequ6gl/test.txt?dl=0',
u'filename': u'test.txt'}
assert tasks[0].info == expected_info, tasks[0].info
@patch('pybossa.importers.twitterapi.Twitter')
@patch('pybossa.importers.twitterapi.oauth2_dance')
def test_bulk_twitter_import_works(self, oauth, client):
"""Test WEB bulk Twitter import works"""
tweet_data = {
'statuses': [
{
u'created_at': 'created',
u'favorite_count': 77,
u'coordinates': 'coords',
u'id_str': u'1',
u'id': 1,
u'retweet_count': 44,
u'user': {'screen_name': 'fulanito'},
u'text': 'this is a tweet #match'
}
]
}
client_instance = Mock()
client_instance.search.tweets.return_value = tweet_data
client.return_value = client_instance
self.register()
self.new_project()
project = db.session.query(Project).first()
res = self.app.post('/project/%s/tasks/import' % project.short_name,
data={'source': '#match',
'max_tweets': 1,
'form_name': 'twitter'},
follow_redirects=True)
project = db.session.query(Project).first()
err_msg = "Tasks should be imported"
tasks = db.session.query(Task).filter_by(project_id=project.id).all()
expected_info = {
u'created_at': 'created',
u'favorite_count': 77,
u'coordinates': 'coords',
u'id_str': u'1',
u'id': 1,
u'retweet_count': 44,
u'user': {'screen_name': 'fulanito'},
u'user_screen_name': 'fulanito',
u'text': 'this is a tweet #match'
}
assert tasks[0].info == expected_info, tasks[0].info
def test_bulk_s3_import_works(self):
"""Test WEB bulk S3 import works"""
self.register()
self.new_project()
project = db.session.query(Project).first()
res = self.app.post('/project/%s/tasks/import' % project.short_name,
data={'files-0': 'myfile.txt',
'bucket': 'mybucket',
'form_name': 's3'},
follow_redirects=True)
project = db.session.query(Project).first()
err_msg = "Tasks should be imported"
tasks = db.session.query(Task).filter_by(project_id=project.id).all()
expected_info = {
u'url': u'https://mybucket.s3.amazonaws.com/myfile.txt',
u'filename': u'myfile.txt',
u'link': u'https://mybucket.s3.amazonaws.com/myfile.txt'
}
assert tasks[0].info == expected_info, tasks[0].info
@with_context
def test_55_facebook_account_warning(self):
"""Test WEB Facebook OAuth user gets a hint to sign in"""
user = User(fullname='John',
name='john',
email_addr='john@john.com',
info={})
user.info = dict(facebook_token=u'facebook')
msg, method = get_user_signup_method(user)
err_msg = "Should return 'facebook' but returned %s" % method
assert method == 'facebook', err_msg
user.info = dict(google_token=u'google')
msg, method = get_user_signup_method(user)
err_msg = "Should return 'google' but returned %s" % method
assert method == 'google', err_msg
user.info = dict(twitter_token=u'twitter')
msg, method = get_user_signup_method(user)
err_msg = "Should return 'twitter' but returned %s" % method
assert method == 'twitter', err_msg
user.info = {}
msg, method = get_user_signup_method(user)
err_msg = "Should return 'local' but returned %s" % method
assert method == 'local', err_msg
@with_context
def test_56_delete_tasks(self):
"""Test WEB delete tasks works"""
Fixtures.create()
# Anonymous user
res = self.app.get('/project/test-app/tasks/delete', follow_redirects=True)
err_msg = "Anonymous user should be redirected for authentication"
assert "Please sign in to access this page" in res.data, err_msg
err_msg = "Anonymous user should not be allowed to delete tasks"
res = self.app.post('/project/test-app/tasks/delete', follow_redirects=True)
err_msg = "Anonymous user should not be allowed to delete tasks"
assert "Please sign in to access this page" in res.data, err_msg
# Authenticated user but not owner
self.register()
res = self.app.get('/project/test-app/tasks/delete', follow_redirects=True)
err_msg = "Authenticated user but not owner should get 403 FORBIDDEN in GET"
assert res.status == '403 FORBIDDEN', err_msg
res = self.app.post('/project/test-app/tasks/delete', follow_redirects=True)
err_msg = "Authenticated user but not owner should get 403 FORBIDDEN in POST"
assert res.status == '403 FORBIDDEN', err_msg
self.signout()
# Owner
tasks = db.session.query(Task).filter_by(project_id=1).all()
res = self.signin(email=u'tester@tester.com', password=u'tester')
res = self.app.get('/project/test-app/tasks/delete', follow_redirects=True)
err_msg = "Owner user should get 200 in GET"
assert res.status == '200 OK', err_msg
assert len(tasks) > 0, "len(project.tasks) > 0"
res = self.app.post('/project/test-app/tasks/delete', follow_redirects=True)
err_msg = "Owner should get 200 in POST"
assert res.status == '200 OK', err_msg
tasks = db.session.query(Task).filter_by(project_id=1).all()
assert len(tasks) == 0, "len(project.tasks) != 0"
# Admin
res = self.signin(email=u'root@root.com', password=u'tester' + 'root')
res = self.app.get('/project/test-app/tasks/delete', follow_redirects=True)
err_msg = "Admin user should get 200 in GET"
assert res.status_code == 200, err_msg
res = self.app.post('/project/test-app/tasks/delete', follow_redirects=True)
err_msg = "Admin should get 200 in POST"
assert res.status_code == 200, err_msg
@patch('pybossa.repositories.task_repository.uploader')
def test_delete_tasks_removes_existing_zip_files(self, uploader):
"""Test WEB delete tasks also deletes zip files for task and taskruns"""
Fixtures.create()
self.signin(email=u'tester@tester.com', password=u'tester')
res = self.app.post('/project/test-app/tasks/delete', follow_redirects=True)
expected = [call('1_test-app_task_json.zip', 'user_2'),
call('1_test-app_task_csv.zip', 'user_2'),
call('1_test-app_task_run_json.zip', 'user_2'),
call('1_test-app_task_run_csv.zip', 'user_2')]
assert uploader.delete_file.call_args_list == expected
@with_context
def test_57_reset_api_key(self):
"""Test WEB reset api key works"""
url = "/account/johndoe/update"
# Anonymous user
res = self.app.get(url, follow_redirects=True)
err_msg = "Anonymous user should be redirected for authentication"
assert "Please sign in to access this page" in res.data, err_msg
res = self.app.post(url, follow_redirects=True)
assert "Please sign in to access this page" in res.data, err_msg
# Authenticated user
self.register()
user = db.session.query(User).get(1)
url = "/account/%s/update" % user.name
api_key = user.api_key
res = self.app.get(url, follow_redirects=True)
err_msg = "Authenticated user should get access to reset api key page"
assert res.status_code == 200, err_msg
assert "reset your personal API Key" in res.data, err_msg
url = "/account/%s/resetapikey" % user.name
res = self.app.post(url, follow_redirects=True)
err_msg = "Authenticated user should be able to reset his api key"
assert res.status_code == 200, err_msg
user = db.session.query(User).get(1)
err_msg = "New generated API key should be different from old one"
assert api_key != user.api_key, err_msg
self.signout()
self.register(fullname="new", name="new")
res = self.app.post(url)
assert res.status_code == 403, res.status_code
url = "/account/fake/resetapikey"
res = self.app.post(url)
assert res.status_code == 404, res.status_code
@with_context
@patch('pybossa.cache.site_stats.get_locs', return_value=[{'latitude':0, 'longitude':0}])
def test_58_global_stats(self, mock1):
"""Test WEB global stats of the site works"""
Fixtures.create()
url = "/stats"
res = self.app.get(url, follow_redirects=True)
err_msg = "There should be a Global Statistics page of the project"
assert "General Statistics" in res.data, err_msg
with patch.dict(self.flask_app.config, {'GEO': True}):
res = self.app.get(url, follow_redirects=True)
assert "GeoLite" in res.data, res.data
@with_context
def test_59_help_api(self):
"""Test WEB help api page exists"""
Fixtures.create()
url = "/help/api"
res = self.app.get(url, follow_redirects=True)
err_msg = "There should be a help api page"
assert "API Help" in res.data, err_msg
@with_context
def test_59_help_license(self):
"""Test WEB help license page exists."""
url = "/help/license"
res = self.app.get(url, follow_redirects=True)
err_msg = "There should be a help license page"
assert "Licenses" in res.data, err_msg
@with_context
def test_59_about(self):
"""Test WEB help about page exists."""
url = "/about"
res = self.app.get(url, follow_redirects=True)
err_msg = "There should be an about page"
assert "About" in res.data, err_msg
@with_context
def test_59_help_tos(self):
"""Test WEB help TOS page exists."""
url = "/help/terms-of-use"
res = self.app.get(url, follow_redirects=True)
err_msg = "There should be a TOS page"
assert "Terms for use" in res.data, err_msg
@with_context
def test_59_help_policy(self):
"""Test WEB help policy page exists."""
url = "/help/cookies-policy"
res = self.app.get(url, follow_redirects=True)
err_msg = "There should be a TOS page"
assert "uses cookies" in res.data, err_msg
@with_context
def test_59_help_privacy(self):
"""Test WEB help privacy page exists."""
url = "/help/privacy"
res = self.app.get(url, follow_redirects=True)
err_msg = "There should be a privacy policy page"
assert "Privacy" in res.data, err_msg
@with_context
def test_69_allow_anonymous_contributors(self):
"""Test WEB allow anonymous contributors works"""
Fixtures.create()
project = db.session.query(Project).first()
url = '/project/%s/newtask' % project.short_name
# All users are allowed to participate by default
# As Anonymous user
res = self.app.get(url, follow_redirects=True)
err_msg = "The anonymous user should be able to participate"
assert project.name in res.data, err_msg
# As registered user
self.register()
self.signin()
res = self.app.get(url, follow_redirects=True)
err_msg = "The anonymous user should be able to participate"
assert project.name in res.data, err_msg
self.signout()
# Now only allow authenticated users
project.allow_anonymous_contributors = False
db.session.add(project)
db.session.commit()
# As Anonymous user
res = self.app.get(url, follow_redirects=True)
err_msg = "User should be redirected to sign in"
project = db.session.query(Project).first()
msg = "Oops! You have to sign in to participate in %s" % project.name
assert msg in res.data, err_msg
# As registered user
res = self.signin()
res = self.app.get(url, follow_redirects=True)
err_msg = "The authenticated user should be able to participate"
assert project.name in res.data, err_msg
self.signout()
# Now only allow authenticated users
project.allow_anonymous_contributors = False
db.session.add(project)
db.session.commit()
res = self.app.get(url, follow_redirects=True)
err_msg = "Only authenticated users can participate"
assert "You have to sign in" in res.data, err_msg
@with_context
def test_70_public_user_profile(self):
"""Test WEB public user profile works"""
Fixtures.create()
# Should work as an anonymous user
url = '/account/%s/' % Fixtures.name
res = self.app.get(url, follow_redirects=True)
err_msg = "There should be a public profile page for the user"
assert Fixtures.fullname in res.data, err_msg
# Should work as an authenticated user
self.signin()
res = self.app.get(url, follow_redirects=True)
assert Fixtures.fullname in res.data, err_msg
# Should return 404 when a user does not exist
url = '/account/a-fake-name-that-does-not-exist/'
res = self.app.get(url, follow_redirects=True)
err_msg = "It should return a 404"
assert res.status_code == 404, err_msg
@with_context
@patch('pybossa.view.projects.uploader.upload_file', return_value=True)
def test_74_task_settings_page(self, mock):
"""Test WEB TASK SETTINGS page works"""
# Creat root user
self.register()
self.signout()
# As owner
self.register(fullname="owner", name="owner")
res = self.new_project()
url = "/project/sampleapp/tasks/settings"
res = self.app.get(url, follow_redirects=True)
dom = BeautifulSoup(res.data)
divs = ['task_scheduler', 'task_delete', 'task_redundancy']
for div in divs:
err_msg = "There should be a %s section" % div
assert dom.find(id=div) is not None, err_msg
self.signout()
# As an authenticated user
self.register(fullname="juan", name="juan")
res = self.app.get(url, follow_redirects=True)
err_msg = "User should not be allowed to access this page"
assert res.status_code == 403, err_msg
self.signout()
# As an anonymous user
res = self.app.get(url, follow_redirects=True)
dom = BeautifulSoup(res.data)
err_msg = "User should be redirected to sign in"
assert dom.find(id="signin") is not None, err_msg
# As root
self.signin()
res = self.app.get(url, follow_redirects=True)
dom = BeautifulSoup(res.data)
divs = ['task_scheduler', 'task_delete', 'task_redundancy']
for div in divs:
err_msg = "There should be a %s section" % div
assert dom.find(id=div) is not None, err_msg
@with_context
@patch('pybossa.view.projects.uploader.upload_file', return_value=True)
def test_75_task_settings_scheduler(self, mock):
"""Test WEB TASK SETTINGS scheduler page works"""
# Creat root user
self.register()
self.signout()
# Create owner
self.register(fullname="owner", name="owner")
self.new_project()
url = "/project/sampleapp/tasks/scheduler"
form_id = 'task_scheduler'
self.signout()
# As owner and root
for i in range(0, 1):
if i == 0:
# As owner
self.signin(email="owner@example.com")
sched = 'depth_first'
else:
sched = 'default'
self.signin()
res = self.app.get(url, follow_redirects=True)
dom = BeautifulSoup(res.data)
err_msg = "There should be a %s section" % form_id
assert dom.find(id=form_id) is not None, err_msg
res = self.task_settings_scheduler(short_name="sampleapp",
sched=sched)
dom = BeautifulSoup(res.data)
err_msg = "Task Scheduler should be updated"
assert dom.find(id='msg_success') is not None, err_msg
project = db.session.query(Project).get(1)
assert project.info['sched'] == sched, err_msg
self.signout()
# As an authenticated user
self.register(fullname="juan", name="juan")
res = self.app.get(url, follow_redirects=True)
err_msg = "User should not be allowed to access this page"
assert res.status_code == 403, err_msg
self.signout()
# As an anonymous user
res = self.app.get(url, follow_redirects=True)
dom = BeautifulSoup(res.data)
err_msg = "User should be redirected to sign in"
assert dom.find(id="signin") is not None, err_msg
@with_context
@patch('pybossa.view.projects.uploader.upload_file', return_value=True)
def test_76_task_settings_redundancy(self, mock):
"""Test WEB TASK SETTINGS redundancy page works"""
# Creat root user
self.register()
self.signout()
# Create owner
self.register(fullname="owner", name="owner")
self.new_project()
self.new_task(1)
url = "/project/sampleapp/tasks/redundancy"
form_id = 'task_redundancy'
self.signout()
# As owner and root
for i in range(0, 1):
if i == 0:
# As owner
self.signin(email="owner@example.com")
n_answers = 20
else:
n_answers = 10
self.signin()
res = self.app.get(url, follow_redirects=True)
dom = BeautifulSoup(res.data)
# Correct values
err_msg = "There should be a %s section" % form_id
assert dom.find(id=form_id) is not None, err_msg
res = self.task_settings_redundancy(short_name="sampleapp",
n_answers=n_answers)
db.session.close()
dom = BeautifulSoup(res.data)
err_msg = "Task Redundancy should be updated"
assert dom.find(id='msg_success') is not None, err_msg
project = db.session.query(Project).get(1)
for t in project.tasks:
assert t.n_answers == n_answers, err_msg
# Wrong values, triggering the validators
res = self.task_settings_redundancy(short_name="sampleapp",
n_answers=0)
dom = BeautifulSoup(res.data)
err_msg = "Task Redundancy should be a value between 0 and 1000"
assert dom.find(id='msg_error') is not None, err_msg
res = self.task_settings_redundancy(short_name="sampleapp",
n_answers=10000000)
dom = BeautifulSoup(res.data)
err_msg = "Task Redundancy should be a value between 0 and 1000"
assert dom.find(id='msg_error') is not None, err_msg
self.signout()
# As an authenticated user
self.register(fullname="juan", name="juan")
res = self.app.get(url, follow_redirects=True)
err_msg = "User should not be allowed to access this page"
assert res.status_code == 403, err_msg
self.signout()
# As an anonymous user
res = self.app.get(url, follow_redirects=True)
dom = BeautifulSoup(res.data)
err_msg = "User should be redirected to sign in"
assert dom.find(id="signin") is not None, err_msg
@with_context
def test_task_redundancy_update_updates_task_state(self):
"""Test WEB when updating the redundancy of the tasks in a project, the
state of the task is updated in consecuence"""
# Creat root user
self.register()
self.new_project()
self.new_task(1)
url = "/project/sampleapp/tasks/redundancy"
project = db.session.query(Project).get(1)
for t in project.tasks:
tr = TaskRun(project_id=project.id, task_id=t.id)
db.session.add(tr)
db.session.commit()
err_msg = "Task state should be completed"
res = self.task_settings_redundancy(short_name="sampleapp",
n_answers=1)
for t in project.tasks:
assert t.state == 'completed', err_msg
res = self.task_settings_redundancy(short_name="sampleapp",
n_answers=2)
err_msg = "Task state should be ongoing"
db.session.add(project)
db.session.commit()
for t in project.tasks:
assert t.state == 'ongoing', t.state
@with_context
@patch('pybossa.view.projects.uploader.upload_file', return_value=True)
def test_77_task_settings_priority(self, mock):
"""Test WEB TASK SETTINGS priority page works"""
# Creat root user
self.register()
self.signout()
# Create owner
self.register(fullname="owner", name="owner")
self.new_project()
self.new_task(1)
url = "/project/sampleapp/tasks/priority"
form_id = 'task_priority'
self.signout()
# As owner and root
project = db.session.query(Project).get(1)
_id = project.tasks[0].id
for i in range(0, 1):
if i == 0:
# As owner
self.signin(email="owner@example.com")
task_ids = str(_id)
priority_0 = 1.0
else:
task_ids = "1"
priority_0 = 0.5
self.signin()
res = self.app.get(url, follow_redirects=True)
dom = BeautifulSoup(res.data)
# Correct values
err_msg = "There should be a %s section" % form_id
assert dom.find(id=form_id) is not None, err_msg
res = self.task_settings_priority(short_name="sampleapp",
task_ids=task_ids,
priority_0=priority_0)
dom = BeautifulSoup(res.data)
err_msg = "Task Priority should be updated"
assert dom.find(id='msg_success') is not None, err_msg
task = db.session.query(Task).get(_id)
assert task.id == int(task_ids), err_msg
assert task.priority_0 == priority_0, err_msg
# Wrong values, triggering the validators
res = self.task_settings_priority(short_name="sampleapp",
priority_0=3,
task_ids="1")
dom = BeautifulSoup(res.data)
err_msg = "Task Priority should be a value between 0.0 and 1.0"
assert dom.find(id='msg_error') is not None, err_msg
res = self.task_settings_priority(short_name="sampleapp",
task_ids="1, 2")
dom = BeautifulSoup(res.data)
err_msg = "Task Priority task_ids should be a comma separated, no spaces, integers"
assert dom.find(id='msg_error') is not None, err_msg
res = self.task_settings_priority(short_name="sampleapp",
task_ids="1,a")
dom = BeautifulSoup(res.data)
err_msg = "Task Priority task_ids should be a comma separated, no spaces, integers"
assert dom.find(id='msg_error') is not None, err_msg
self.signout()
# As an authenticated user
self.register(fullname="juan", name="juan")
res = self.app.get(url, follow_redirects=True)
err_msg = "User should not be allowed to access this page"
assert res.status_code == 403, err_msg
self.signout()
# As an anonymous user
res = self.app.get(url, follow_redirects=True)
dom = BeautifulSoup(res.data)
err_msg = "User should be redirected to sign in"
assert dom.find(id="signin") is not None, err_msg
@with_context
def test_78_cookies_warning(self):
"""Test WEB cookies warning is displayed"""
# As Anonymous
res = self.app.get('/', follow_redirects=True)
dom = BeautifulSoup(res.data)
err_msg = "If cookies are not accepted, cookies banner should be shown"
assert dom.find(id='cookies_warning') is not None, err_msg
# As user
self.signin(email=Fixtures.email_addr2, password=Fixtures.password)
res = self.app.get('/', follow_redirects=True)
dom = BeautifulSoup(res.data)
err_msg = "If cookies are not accepted, cookies banner should be shown"
assert dom.find(id='cookies_warning') is not None, err_msg
self.signout()
# As admin
self.signin(email=Fixtures.root_addr, password=Fixtures.root_password)
res = self.app.get('/', follow_redirects=True)
dom = BeautifulSoup(res.data)
err_msg = "If cookies are not accepted, cookies banner should be shown"
assert dom.find(id='cookies_warning') is not None, err_msg
self.signout()
@with_context
def test_79_cookies_warning2(self):
"""Test WEB cookies warning is hidden"""
# As Anonymous
self.app.set_cookie("localhost", "PyBossa_accept_cookies", "Yes")
res = self.app.get('/', follow_redirects=True, headers={})
dom = BeautifulSoup(res.data)
err_msg = "If cookies are not accepted, cookies banner should be hidden"
assert dom.find(id='cookies_warning') is None, err_msg
# As user
self.signin(email=Fixtures.email_addr2, password=Fixtures.password)
res = self.app.get('/', follow_redirects=True)
dom = BeautifulSoup(res.data)
err_msg = "If cookies are not accepted, cookies banner should be hidden"
assert dom.find(id='cookies_warning') is None, err_msg
self.signout()
# As admin
self.signin(email=Fixtures.root_addr, password=Fixtures.root_password)
res = self.app.get('/', follow_redirects=True)
dom = BeautifulSoup(res.data)
err_msg = "If cookies are not accepted, cookies banner should be hidden"
assert dom.find(id='cookies_warning') is None, err_msg
self.signout()
@with_context
def test_user_with_no_more_tasks_find_volunteers(self):
"""Test WEB when a user has contributed to all available tasks, he is
asked to find new volunteers for a project, if the project is not
completed yet (overall progress < 100%)"""
self.register()
user = User.query.first()
project = ProjectFactory.create(owner=user)
task = TaskFactory.create(project=project)
taskrun = TaskRunFactory.create(task=task, user=user)
res = self.app.get('/project/%s/newtask' % project.short_name)
message = "Sorry, you've contributed to all the tasks for this project, but this project still needs more volunteers, so please spread the word!"
assert message in res.data
self.signout()
@with_context
def test_user_with_no_more_tasks_find_volunteers_project_completed(self):
"""Test WEB when a user has contributed to all available tasks, he is
not asked to find new volunteers for a project, if the project is
completed (overall progress = 100%)"""
self.register()
user = User.query.first()
project = ProjectFactory.create(owner=user)
task = TaskFactory.create(project=project, n_answers=1)
taskrun = TaskRunFactory.create(task=task, user=user)
res = self.app.get('/project/%s/newtask' % project.short_name)
assert task.state == 'completed', task.state
message = "Sorry, you've contributed to all the tasks for this project, but this project still needs more volunteers, so please spread the word!"
assert message not in res.data
self.signout()
@with_context
def test_results(self):
"""Test WEB results shows no data as no template and no data."""
tr = TaskRunFactory.create()
project = project_repo.get(tr.project_id)
url = '/project/%s/results' % project.short_name
res = self.app.get(url, follow_redirects=True)
assert "No results" in res.data, res.data
@with_context
def test_results_with_values(self):
"""Test WEB results with values are not shown as no template but data."""
task = TaskFactory.create(n_answers=1)
tr = TaskRunFactory.create(task=task)
project = project_repo.get(tr.project_id)
url = '/project/%s/results' % project.short_name
result = result_repo.get_by(project_id=project.id)
result.info = dict(foo='bar')
result_repo.update(result)
res = self.app.get(url, follow_redirects=True)
assert "No results" in res.data, res.data
@with_context
def test_results_with_values_and_template(self):
"""Test WEB results with values and template is shown."""
task = TaskFactory.create(n_answers=1)
tr = TaskRunFactory.create(task=task)
project = project_repo.get(tr.project_id)
project.info['results'] = "The results"
project_repo.update(project)
url = '/project/%s/results' % project.short_name
result = result_repo.get_by(project_id=project.id)
result.info = dict(foo='bar')
result_repo.update(result)
res = self.app.get(url, follow_redirects=True)
assert "The results" in res.data, res.data