total rewrite
This commit is contained in:
parent
d375fb2542
commit
0ab9c440de
|
@ -1,8 +0,0 @@
|
|||
language: python
|
||||
python:
|
||||
- "2.6"
|
||||
- "2.7"
|
||||
# command to install dependencies
|
||||
install: "python setup.py develop"
|
||||
# command to run tests
|
||||
script: python setup.py test
|
|
@ -1,4 +0,0 @@
|
|||
pytest-bdd-example
|
||||
==================
|
||||
|
||||
Example application and the tests using the pytest-bdd
|
|
@ -0,0 +1,4 @@
|
|||
pytest-bdd-example
|
||||
==================
|
||||
|
||||
Example application and the tests using the pytest-bdd
|
|
@ -1,18 +0,0 @@
|
|||
Scenario: Successful login
|
||||
Given I'm an admin user
|
||||
|
||||
When I go to the admin login page
|
||||
And I fill in the login credentials
|
||||
And I post the form
|
||||
|
||||
Then I shouldn't see an error message
|
||||
And I should see the Dashboard page
|
||||
|
||||
|
||||
Scenario: Unsuccessful login
|
||||
|
||||
When I go to the admin login page
|
||||
And I fill in wrong login credentials
|
||||
And I post the form
|
||||
|
||||
Then I should see an error message
|
|
@ -0,0 +1,32 @@
|
|||
Scenario: Successful login
|
||||
Given I'm not logged in
|
||||
|
||||
When I go to login page
|
||||
And I enter "admin" username
|
||||
And I enter "default" password
|
||||
And I click login button
|
||||
|
||||
Then I should see "You were logged in"
|
||||
|
||||
|
||||
Scenario: Invalid username
|
||||
Given I'm not logged in
|
||||
|
||||
When I go to login page
|
||||
And I enter "wrong" username
|
||||
And I enter "default" password
|
||||
And I click login button
|
||||
|
||||
Then I should see "Error: Invalid username"
|
||||
|
||||
|
||||
Scenario: Invalid password
|
||||
Given I'm not logged in
|
||||
|
||||
When I go to login page
|
||||
And I enter "wrong" username
|
||||
And I enter "default" password
|
||||
And I click login button
|
||||
|
||||
Then I should see "Error: Invalid username"
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
Scenario: Entry is displayed
|
||||
Given I have an entry
|
||||
When I go to the blog
|
||||
Then I should see my entry
|
||||
|
||||
|
||||
Scenario: No form for visitors
|
||||
Given I'm a visitor
|
||||
When I go to the blog
|
||||
Then I shouldn't see an entry form
|
||||
|
||||
|
||||
Scenario: Create an entry
|
||||
Given I'm an admin
|
||||
When I go to the blog
|
||||
And I enter entry title
|
||||
And I enter entry text
|
||||
And I click the Share button
|
||||
Then I should see "New entry was successfully posted"
|
||||
And I should see the entry title
|
||||
And I should see the entry text
|
33
manage.py
33
manage.py
|
@ -1,33 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
from flask.ext.script import Manager, Shell, Server
|
||||
|
||||
from pytest_bdd_example import dashboard
|
||||
from pytest_bdd_example import shop
|
||||
|
||||
|
||||
def create_app(app=None):
|
||||
APPS = {
|
||||
'dashboard': dashboard.app,
|
||||
'shop': shop.app,
|
||||
}
|
||||
|
||||
return APPS[app]
|
||||
|
||||
manager = Manager(create_app)
|
||||
|
||||
|
||||
manager.add_option('-app', '--application', dest='app', required=True)
|
||||
manager.add_command('runserver', Server('127.0.0.1'))
|
||||
manager.add_command('shell', Shell(use_ipython=True))
|
||||
|
||||
|
||||
@manager.command
|
||||
def mkdb():
|
||||
from tests.helpers.mkdb.db import recreate_database
|
||||
from tests.helpers.mkdb.initialdata import create_initial_data
|
||||
recreate_database()
|
||||
create_initial_data()
|
||||
|
||||
if __name__ == '__main__':
|
||||
manager.run()
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
from .decorators import public_endpoint
|
||||
from .blueprint import auth
|
||||
from .security import user_datastore
|
||||
|
||||
|
||||
__all__ = ['auth', 'public_endpoint', 'user_datastore']
|
|
@ -1,21 +0,0 @@
|
|||
from flask import Blueprint
|
||||
from flask.ext.security import Security
|
||||
|
||||
from .manager import login_manager, check_valid_login
|
||||
from .security import user_datastore
|
||||
from .forms import LoginForm
|
||||
|
||||
auth = Blueprint('auth', __name__, template_folder='../')
|
||||
|
||||
|
||||
@auth.record_once
|
||||
def on_registered(state):
|
||||
state.app.config['SECURITY_LOGIN_USER_TEMPLATE'] = 'auth/login.html'
|
||||
Security(
|
||||
state.app,
|
||||
user_datastore,
|
||||
login_form=LoginForm,
|
||||
)
|
||||
|
||||
login_manager.init_app(state.app)
|
||||
state.app.before_request(check_valid_login)
|
|
@ -1,3 +0,0 @@
|
|||
def public_endpoint(function):
|
||||
function.is_public = True
|
||||
return function
|
|
@ -1,47 +0,0 @@
|
|||
from flask.ext.wtf import Form
|
||||
|
||||
from wtforms import TextField, PasswordField, BooleanField
|
||||
from wtforms.validators import Required
|
||||
from flask.ext.security.forms import NextFormMixin
|
||||
from flask.ext.security.utils import verify_and_update_password, get_message
|
||||
from flask.ext.security.confirmable import requires_confirmation
|
||||
|
||||
from .security import user_datastore
|
||||
|
||||
|
||||
class LoginForm(Form, NextFormMixin):
|
||||
username = TextField('Username', [Required()])
|
||||
password = PasswordField('Password', [Required()])
|
||||
remember = BooleanField('Remember me')
|
||||
|
||||
def validate(self):
|
||||
if not super(LoginForm, self).validate():
|
||||
return False
|
||||
|
||||
username = self.username.data.strip()
|
||||
password = self.password.data.strip()
|
||||
if username == '':
|
||||
self.username.errors.append(get_message('EMAIL_NOT_PROVIDED')[0])
|
||||
return False
|
||||
|
||||
if password == '':
|
||||
self.password.errors.append(get_message('PASSWORD_NOT_PROVIDED')[0])
|
||||
return False
|
||||
|
||||
self.user = user_datastore.find_user(username=username)
|
||||
|
||||
if self.user is None:
|
||||
self.username.errors.append(get_message('USER_DOES_NOT_EXIST')[0])
|
||||
return False
|
||||
|
||||
if not verify_and_update_password(password, self.user):
|
||||
self.password.errors.append(get_message('INVALID_PASSWORD')[0])
|
||||
return False
|
||||
|
||||
if requires_confirmation(self.user):
|
||||
self.username.errors.append(get_message('CONFIRMATION_REQUIRED')[0])
|
||||
return False
|
||||
if not self.user.is_active():
|
||||
self.username.errors.append(get_message('DISABLED_ACCOUNT')[0])
|
||||
return False
|
||||
return True
|
|
@ -1,31 +0,0 @@
|
|||
from flask import request, current_app
|
||||
|
||||
from flask.ext.login import LoginManager, current_user
|
||||
|
||||
from .security import user_datastore
|
||||
|
||||
|
||||
login_manager = LoginManager()
|
||||
login_manager.login_view = 'security.login'
|
||||
|
||||
|
||||
PUBLIC_ENDPOINTS = [
|
||||
'static',
|
||||
'security.login',
|
||||
]
|
||||
|
||||
|
||||
def check_valid_login():
|
||||
if request.endpoint in PUBLIC_ENDPOINTS:
|
||||
return
|
||||
|
||||
if getattr(current_app.view_functions.get(request.endpoint), 'is_public', False):
|
||||
return
|
||||
|
||||
if not current_user.is_authenticated():
|
||||
return current_app.login_manager.unauthorized()
|
||||
|
||||
|
||||
@login_manager.user_loader
|
||||
def load_user(userid):
|
||||
return user_datastore.find_user(id=userid)
|
|
@ -1,42 +0,0 @@
|
|||
from flask import current_app
|
||||
|
||||
from flask.ext.security import UserMixin, RoleMixin
|
||||
|
||||
|
||||
db = current_app.extensions['sqlalchemy'].db
|
||||
|
||||
|
||||
class Role(db.Model, RoleMixin):
|
||||
id = db.Column(db.Integer(), primary_key=True)
|
||||
name = db.Column(db.String(80), unique=True)
|
||||
description = db.Column(db.String(255))
|
||||
|
||||
|
||||
class User(db.Model, UserMixin):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
username = db.Column(db.String(64), unique=True)
|
||||
email = db.Column(db.String(120), unique=True)
|
||||
password = db.Column(db.String(20))
|
||||
active = db.Column(db.Boolean, default=False)
|
||||
|
||||
roles = db.relationship(
|
||||
Role,
|
||||
secondary=lambda: roles_users,
|
||||
backref=db.backref('users', lazy='dynamic'),
|
||||
)
|
||||
|
||||
def get_id(self):
|
||||
return unicode(self.id)
|
||||
|
||||
def is_active(self):
|
||||
return self.active
|
||||
|
||||
def is_anonymous(self):
|
||||
return False
|
||||
|
||||
|
||||
roles_users = db.Table(
|
||||
'roles_users',
|
||||
db.Column('user_id', db.Integer(), db.ForeignKey(User.id)),
|
||||
db.Column('role_id', db.Integer(), db.ForeignKey(Role.id)),
|
||||
)
|
|
@ -1,4 +0,0 @@
|
|||
from flask.ext.security import SQLAlchemyUserDatastore
|
||||
from .models import db, User, Role
|
||||
|
||||
user_datastore = SQLAlchemyUserDatastore(db, User, Role)
|
|
@ -1,5 +0,0 @@
|
|||
from .models import db, Author, Book
|
||||
from .blueprint import book
|
||||
|
||||
|
||||
__all__ = ['book', 'db', 'Author', 'Book']
|
|
@ -1,45 +0,0 @@
|
|||
from flask import current_app
|
||||
from flask.ext.admin.contrib.sqlamodel import ModelView
|
||||
|
||||
from flask.ext.principal import Permission, RoleNeed
|
||||
|
||||
from pytest_bdd_example.book import db, Book, Author
|
||||
|
||||
|
||||
admin = current_app.extensions['admin'][0]
|
||||
|
||||
admin_role = Permission(RoleNeed('admin'))
|
||||
|
||||
|
||||
class AuthorView(ModelView):
|
||||
"""Author view.
|
||||
|
||||
Only admin can see it.
|
||||
"""
|
||||
def is_visible(self):
|
||||
return admin_role.can()
|
||||
|
||||
def is_accessible(self):
|
||||
return admin_role.can()
|
||||
|
||||
|
||||
admin.add_view(
|
||||
AuthorView(
|
||||
Author,
|
||||
db.session,
|
||||
'authors',
|
||||
endpoint='authors',
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
admin.add_view(
|
||||
ModelView(
|
||||
Book,
|
||||
db.session,
|
||||
'books',
|
||||
endpoint='books',
|
||||
)
|
||||
)
|
||||
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
from flask import Blueprint
|
||||
from .admin import admin
|
||||
|
||||
book = Blueprint('book', __name__)
|
||||
|
||||
|
||||
__all__ = ['book', 'admin']
|
|
@ -1,32 +0,0 @@
|
|||
from flask import current_app
|
||||
|
||||
db = current_app.extensions['sqlalchemy'].db
|
||||
|
||||
|
||||
class Author(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
|
||||
first_name = db.Column(db.String(30))
|
||||
last_name = db.Column(db.String(30))
|
||||
sur_name = db.Column(db.String(30))
|
||||
|
||||
|
||||
class Book(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
|
||||
title = db.Column(db.String(50))
|
||||
description = db.Column(db.Text)
|
||||
|
||||
authors = db.relationship(
|
||||
Author,
|
||||
secondary=lambda: author_books,
|
||||
backref='books',
|
||||
order_by=Author.id,
|
||||
)
|
||||
|
||||
|
||||
author_books = db.Table(
|
||||
'author_books',
|
||||
db.Column('author_id', db.Integer, db.ForeignKey(Author.id)),
|
||||
db.Column('book_id', db.Integer, db.ForeignKey(Book.id)),
|
||||
)
|
|
@ -1,4 +0,0 @@
|
|||
from .app import app
|
||||
|
||||
|
||||
__all__ = ['app']
|
|
@ -1,14 +0,0 @@
|
|||
from flask.ext.admin import Admin
|
||||
from flask.ext.admin.base import MenuLink
|
||||
|
||||
admin = Admin(
|
||||
name='Dashboard',
|
||||
url='/',
|
||||
)
|
||||
|
||||
admin.add_link(
|
||||
MenuLink(
|
||||
name='Logout',
|
||||
endpoint='security.logout',
|
||||
)
|
||||
)
|
|
@ -1,29 +0,0 @@
|
|||
from flask import Flask
|
||||
|
||||
from flask.ext.sqlalchemy import SQLAlchemy
|
||||
from pytest_bdd_example.dashboard import settings
|
||||
|
||||
from .admin import admin
|
||||
|
||||
app = Flask(
|
||||
__name__,
|
||||
static_folder=settings.MEDIA_ROOT,
|
||||
template_folder=settings.TEMPLATES_ROOT,
|
||||
)
|
||||
|
||||
app.config.from_object('pytest_bdd_example.dashboard.settings')
|
||||
app.secret_key = 'A0Zr98j/3yX R~XHH!jmN]LWX/,?RT'
|
||||
|
||||
admin.init_app(app)
|
||||
|
||||
db = SQLAlchemy(app)
|
||||
|
||||
with app.app_context():
|
||||
from pytest_bdd_example.auth import auth
|
||||
from pytest_bdd_example.book import book
|
||||
|
||||
app.register_blueprint(auth, url_prefix='/auth')
|
||||
app.register_blueprint(book)
|
||||
|
||||
# Register the views
|
||||
from .views import *
|
|
@ -1,8 +0,0 @@
|
|||
import os
|
||||
|
||||
DEBUG = True
|
||||
BASE_PATH = os.path.dirname(os.path.abspath(__file__))
|
||||
MEDIA_ROOT = os.path.join(BASE_PATH, '../media')
|
||||
TEMPLATES_ROOT = os.path.join(BASE_PATH, '../templates', 'dashboard')
|
||||
|
||||
SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(BASE_PATH, '../dashboard.db')
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
Binary file not shown.
Before Width: | Height: | Size: 8.6 KiB |
Binary file not shown.
Before Width: | Height: | Size: 12 KiB |
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
|
@ -1,6 +0,0 @@
|
|||
body {
|
||||
padding-top: 40px;
|
||||
padding-bottom: 40px;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
File diff suppressed because one or more lines are too long
|
@ -1,10 +0,0 @@
|
|||
from flask import Flask
|
||||
|
||||
from .settings import TEMPLATES_ROOT, MEDIA_ROOT
|
||||
|
||||
|
||||
app = Flask(
|
||||
__name__,
|
||||
template_folder=TEMPLATES_ROOT,
|
||||
static_folder=MEDIA_ROOT,
|
||||
)
|
|
@ -1,9 +0,0 @@
|
|||
import os
|
||||
|
||||
DEBUG = True
|
||||
BASE_PATH = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
MEDIA_ROOT = os.path.join(BASE_PATH, 'media')
|
||||
TEMPLATES_ROOT = os.path.join(BASE_PATH, 'templates', 'shop')
|
||||
|
||||
SQLALCHEMY_DATABASE_URI = 'sqlite:////tmp/pytestbdd-example.db'
|
|
@ -1,60 +0,0 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block extra_head %}
|
||||
<style type="text/css">
|
||||
.form-signin {
|
||||
max-width: 300px;
|
||||
padding: 19px 29px 29px;
|
||||
margin: 0 auto 20px;
|
||||
background-color: #fff;
|
||||
border: 1px solid #e5e5e5;
|
||||
-webkit-border-radius: 5px;
|
||||
-moz-border-radius: 5px;
|
||||
border-radius: 5px;
|
||||
-webkit-box-shadow: 0 1px 2px rgba(0,0,0,.05);
|
||||
-moz-box-shadow: 0 1px 2px rgba(0,0,0,.05);
|
||||
box-shadow: 0 1px 2px rgba(0,0,0,.05);
|
||||
}
|
||||
|
||||
.form-signin .form-signin-heading,
|
||||
.form-signin .checkbox {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.form-signin input[type="text"],
|
||||
.form-signin input[type="password"] {
|
||||
font-size: 16px;
|
||||
height: auto;
|
||||
margin-bottom: 15px;
|
||||
padding: 7px 9px;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block menu %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<form class="form-signin" method='POST'>
|
||||
{{ login_user_form.csrf_token }}
|
||||
<h2 class="form-signin-heading">Please sign in</h2>
|
||||
|
||||
<div class="control-group">
|
||||
<div class="controls">
|
||||
{{ login_user_form.username(placeholder=login_user_form.username.label.text, class="input-block-level") }}
|
||||
|
||||
{% for error in login_user_form.username.errors %}<div class="alert alert-error">{{ error|e }}</div>{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<div class="controls">
|
||||
{{ login_user_form.password(placeholder=login_user_form.password.label.text, class="input-block-level") }}
|
||||
{% for error in login_user_form.password.errors %}<div class="alert alert-error">{{ error|e }}</div>{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-large btn-primary" type="submit">Sign in</button>
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
|
@ -1,47 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Shop - {% block title %}Dashboard{% endblock %}</title>
|
||||
<link href="{{ url_for("static", filename="bootstrap/css/bootstrap.css") }}" rel="stylesheet">
|
||||
<link href="{{ url_for("static", filename="dashboard/dashboard.css") }}" rel="stylesheet">
|
||||
|
||||
<script type="text/javascript" src="{{ url_for("static", filename="libs/jquery-1.10.1.min.js") }}"></script>
|
||||
<script src="{{ url_for("static", filename="bootstrap/js/bootstrap.min.js") }}"></script>
|
||||
|
||||
{% block extra_head %}{% endblock %}
|
||||
|
||||
</head>
|
||||
<body>
|
||||
{% block menu %}
|
||||
<div id="form" class="modal hide" role="dialog" tabindex="-1" aria-labelledby="modal-title"></div>
|
||||
<div class="navbar navbar-inverse navbar-fixed-top">
|
||||
<div class="navbar-inner">
|
||||
<div class="container">
|
||||
<a class="brand" href="/">Dashboard</a>
|
||||
<div class="nav-collapse collapse">
|
||||
<ul class="nav">
|
||||
<li class="active"><a href=".">Home</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% with messages = get_flashed_messages() %}
|
||||
{% if messages %}
|
||||
{% for message in messages %}
|
||||
<div class="alert">
|
||||
<button type="button" class="close" data-dismiss="alert">×</button>
|
||||
{{ message }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
|
||||
<div class="container">
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -1,7 +0,0 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block menu %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% endblock %}
|
56
setup.py
56
setup.py
|
@ -1,39 +1,63 @@
|
|||
#!/usr/bin/env python
|
||||
import os
|
||||
import sys
|
||||
|
||||
from setuptools import setup
|
||||
from setuptools import setup, find_packages
|
||||
from setuptools.command.test import test as TestCommand
|
||||
|
||||
|
||||
class PyTest(TestCommand):
|
||||
version = '0.0.1'
|
||||
|
||||
|
||||
class Tox(TestCommand):
|
||||
def finalize_options(self):
|
||||
TestCommand.finalize_options(self)
|
||||
self.test_args = []
|
||||
self.test_args = ['--recreate']
|
||||
self.test_suite = True
|
||||
|
||||
def run_tests(self):
|
||||
import pytest
|
||||
errno = pytest.main(self.test_args)
|
||||
#import here, cause outside the eggs aren't loaded
|
||||
import tox
|
||||
errno = tox.cmdline(self.test_args)
|
||||
sys.exit(errno)
|
||||
|
||||
|
||||
dirname = os.path.dirname(__file__)
|
||||
|
||||
long_description = (
|
||||
open(os.path.join(dirname, 'README.rst')).read() + '\n' +
|
||||
open(os.path.join(dirname, 'CHANGES.rst')).read()
|
||||
)
|
||||
|
||||
setup(
|
||||
name='pytest-bdd-example',
|
||||
description='pytest-bdd example application',
|
||||
description='PyTest BDD example',
|
||||
long_description=long_description,
|
||||
author='Oleg Pidsadnyi',
|
||||
license='MIT license',
|
||||
author_email='oleg.podsadny@gmail.com',
|
||||
version='0.1',
|
||||
cmdclass={'test': PyTest},
|
||||
url='https://github.com/olegpidsadnyi/pytest-bdd-example',
|
||||
version=version,
|
||||
classifiers=[
|
||||
'Intended Audience :: Developers',
|
||||
'License :: OSI Approved :: MIT License',
|
||||
'Operating System :: POSIX',
|
||||
'Operating System :: Microsoft :: Windows',
|
||||
'Operating System :: MacOS :: MacOS X',
|
||||
'Topic :: Software Development :: Testing',
|
||||
'Topic :: Software Development :: Libraries',
|
||||
'Topic :: Utilities',
|
||||
'Programming Language :: Python :: 2',
|
||||
'Programming Language :: Python :: 3'
|
||||
] + [('Programming Language :: Python :: %s' % x) for x in '2.6 2.7 3.0 3.1 3.2 3.3'.split()],
|
||||
cmdclass={'test': Tox},
|
||||
install_requires=[
|
||||
'flask-admin',
|
||||
'flask-script',
|
||||
'flask-login',
|
||||
'flask-security',
|
||||
'flask-sqlalchemy',
|
||||
],
|
||||
tests_require=[
|
||||
'pytest',
|
||||
'flask',
|
||||
'pytest-bdd',
|
||||
'pytest-bdd-splinter',
|
||||
],
|
||||
packages=['pytest_bdd_example'],
|
||||
tests_require=['tox'],
|
||||
package_dir={'': 'src'},
|
||||
packages=['flaskr'],
|
||||
)
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
from fixtures.app import *
|
||||
from fixtures.db import *
|
||||
from fixtures.entry import *
|
|
@ -1 +0,0 @@
|
|||
This should contain fixtures that are used in both functional and unit tests
|
|
@ -0,0 +1,9 @@
|
|||
import pytest
|
||||
|
||||
from flaskr import flaskr
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def app():
|
||||
"""Flask application."""
|
||||
return flaskr.app
|
|
@ -1,45 +0,0 @@
|
|||
import pytest
|
||||
|
||||
from pytest_bdd_example.auth.models import db
|
||||
from pytest_bdd_example.auth import user_datastore
|
||||
|
||||
from tests.helpers.random import random_string
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def password():
|
||||
return 'asdasd'
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def user(password):
|
||||
"""Dashboard user."""
|
||||
u = user_datastore.create_user(
|
||||
username=u'{0}'.format(random_string()),
|
||||
email="{0}@example.com".format(random_string(7)),
|
||||
password=password,
|
||||
active=True,
|
||||
)
|
||||
|
||||
db.session.commit()
|
||||
return u
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def admin_user(user):
|
||||
admin_role = user_datastore.find_or_create_role(
|
||||
name='admin',
|
||||
description='Administrators',
|
||||
)
|
||||
user_datastore.add_role_to_user(user, admin_role)
|
||||
db.session.commit()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def author_user(user):
|
||||
author_role = user_datastore.find_or_create_role(
|
||||
name='author',
|
||||
description='Book authors',
|
||||
)
|
||||
user_datastore.add_role_to_user(user, author_role)
|
||||
db.session.commit()
|
|
@ -0,0 +1,9 @@
|
|||
import pytest
|
||||
|
||||
from flaskr import flaskr
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def db():
|
||||
"""Database connection."""
|
||||
return flaskr.connect_db()
|
|
@ -0,0 +1,22 @@
|
|||
import pytest
|
||||
|
||||
import datetime
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def title():
|
||||
return "Entry for {0}".format(datetime.datetime.now())
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def text():
|
||||
"""Entry text."""
|
||||
return "Hello world text!"
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def entry(db, title, text):
|
||||
"""Blog entry."""
|
||||
db.execute('insert into entries (title, text) values (?, ?)', [title, text])
|
||||
db.commit()
|
||||
return title, text
|
|
@ -1,15 +1,23 @@
|
|||
import os
|
||||
import pytest
|
||||
|
||||
import pytest_bdd_example as main_pkg
|
||||
from tests.fixtures.auth import *
|
||||
from .fixtures.browser import *
|
||||
from .fixtures.auth import *
|
||||
from .fixtures.steps import *
|
||||
|
||||
|
||||
# @pytest.fixture(scope='session')
|
||||
# def pytestbdd_selenium_speed():
|
||||
# return 0.5
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def pytestbdd_feature_base_dir():
|
||||
"""Feature files base directory."""
|
||||
return os.path.join(
|
||||
os.path.dirname(
|
||||
os.path.dirname(main_pkg.__file__)
|
||||
), 'features',
|
||||
return os.path.abspath(
|
||||
os.path.join(
|
||||
os.path.dirname(os.path.dirname(__file__)),
|
||||
'..',
|
||||
'features',
|
||||
)
|
||||
)
|
||||
|
|
|
@ -1,54 +0,0 @@
|
|||
import pytest
|
||||
import splinter
|
||||
|
||||
from pytest_bdd import scenario, when, then
|
||||
|
||||
|
||||
test_successful_login = scenario(
|
||||
'auth/dashboard_login.feature',
|
||||
'Successful login',
|
||||
)
|
||||
|
||||
|
||||
@when('I go to the admin login page')
|
||||
def go_to_login_page(browser):
|
||||
browser.visit('http://127.0.0.1:5000/login')
|
||||
|
||||
|
||||
@when('I fill in the login credentials')
|
||||
def fill_in_login_credentials(browser, user, password):
|
||||
browser.fill('username', user.username)
|
||||
browser.fill('password', password)
|
||||
|
||||
|
||||
@when('I post the form')
|
||||
def post_the_form(browser):
|
||||
browser.find_by_css('button[type=submit]').first.click()
|
||||
|
||||
|
||||
@then('I should see an error message')
|
||||
def should_see_error_message(browser):
|
||||
assert browser.find_by_css('.alert-error').first
|
||||
|
||||
|
||||
@then('I shouldn\'t see an error message')
|
||||
def shouldnt_see_error_message(browser):
|
||||
with pytest.raises(splinter.exceptions.ElementDoesNotExist):
|
||||
browser.find_by_css('.alert-error').first
|
||||
|
||||
|
||||
@then('I should see the Dashboard page')
|
||||
def should_see_dashboard(browser):
|
||||
assert not browser.is_text_present('Please sign in')
|
||||
|
||||
|
||||
test_unsuccessful_login = scenario(
|
||||
'auth/dashboard_login.feature',
|
||||
'Unsuccessful login',
|
||||
)
|
||||
|
||||
|
||||
@when('I fill in wrong login credentials')
|
||||
def fill_in_wrong_credentials(browser):
|
||||
browser.fill('username', 'HELP!')
|
||||
browser.fill('password', 'I pressed any key')
|
|
@ -1,8 +0,0 @@
|
|||
from pytest_bdd_example.dashboard import app
|
||||
|
||||
from .steps.given import *
|
||||
|
||||
|
||||
with app.app_context():
|
||||
"""Initialize the application context."""
|
||||
pass
|
|
@ -1,5 +0,0 @@
|
|||
from pytest_bdd import given
|
||||
|
||||
given('I\'m an admin user', fixture='admin_user')
|
||||
|
||||
given('I\'m an author user', fixture='author_user')
|
|
@ -0,0 +1,52 @@
|
|||
from pytest_bdd import scenario, when, then
|
||||
|
||||
test_entry_is_displayed = scenario(
|
||||
"entry/entry.feature",
|
||||
"Entry is displayed",
|
||||
)
|
||||
|
||||
test_no_form_for_visitors = scenario(
|
||||
"entry/entry.feature",
|
||||
"No form for visitors",
|
||||
)
|
||||
|
||||
test_create_entry = scenario(
|
||||
"entry/entry.feature",
|
||||
"Create an entry",
|
||||
)
|
||||
|
||||
|
||||
@then("I should see my entry")
|
||||
def should_see_entry(browser, title):
|
||||
assert browser.is_text_present(title)
|
||||
|
||||
|
||||
@then("I shouldn't see an entry form")
|
||||
def shoudnt_see_entry_form(browser):
|
||||
assert not browser.find_by_css(".add-entry")
|
||||
|
||||
|
||||
@when("I enter entry title")
|
||||
def enter_entry_title(browser, title):
|
||||
browser.fill('title', title)
|
||||
|
||||
|
||||
@when("I enter entry text")
|
||||
def enter_entry_text(browser, text):
|
||||
browser.fill('text', text)
|
||||
|
||||
|
||||
@when("I click the Share button")
|
||||
def click_share_button(browser):
|
||||
button = browser.find_by_css("input[type=submit]").first
|
||||
button.click()
|
||||
|
||||
|
||||
@then("I should see the entry title")
|
||||
def should_see_entry_title(browser, title):
|
||||
assert browser.is_text_present(title)
|
||||
|
||||
|
||||
@then("I should see the entry text")
|
||||
def should_see_entry_text(browser, text):
|
||||
assert browser.is_text_present(text)
|
|
@ -0,0 +1,11 @@
|
|||
import pytest
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def credentials():
|
||||
"""Login credentials."""
|
||||
return {
|
||||
'username': 'admin',
|
||||
'password': 'default',
|
||||
'login': True,
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
from urlparse import urljoin
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def base_url():
|
||||
return "http://127.0.0.1:5000/"
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def browser(browser, base_url, credentials):
|
||||
|
||||
if credentials["login"]:
|
||||
browser.visit(urljoin(base_url, "/login"))
|
||||
|
||||
browser.fill("username", credentials["username"])
|
||||
browser.fill("password", credentials["password"])
|
||||
button = browser.find_by_css("input[type=submit]").first
|
||||
button.click()
|
||||
else:
|
||||
browser.visit(base_url)
|
||||
return browser
|
|
@ -0,0 +1,36 @@
|
|||
"""Common steps."""
|
||||
|
||||
import re
|
||||
from urlparse import urljoin
|
||||
|
||||
from pytest_bdd import given, when, then
|
||||
|
||||
|
||||
@then(re.compile('I should see "(?P<text>.+)"'))
|
||||
def i_should_see(browser, text):
|
||||
assert browser.is_text_present(text)
|
||||
|
||||
|
||||
@given("I'm not logged in")
|
||||
@given("I'm a visitor") # Step alias
|
||||
def not_logged_in(credentials):
|
||||
"""99%% of the cases you test logged in.
|
||||
|
||||
Specify not logged in state to be explicit.
|
||||
|
||||
"""
|
||||
credentials['login'] = False
|
||||
|
||||
|
||||
@given("I'm an admin")
|
||||
def logged_in(credentials):
|
||||
credentials['login'] = True
|
||||
|
||||
|
||||
# Re-use of the fixture
|
||||
given("I have an entry", fixture="entry")
|
||||
|
||||
|
||||
@when("I go to the blog")
|
||||
def go_to_blog(browser):
|
||||
browser.visit(urljoin(browser.url, "/"))
|
|
@ -0,0 +1,41 @@
|
|||
import re
|
||||
from urlparse import urljoin
|
||||
|
||||
from pytest_bdd import given, when, scenario
|
||||
|
||||
|
||||
test_sucsessful_login = scenario(
|
||||
"auth/login.feature",
|
||||
"Successful login",
|
||||
)
|
||||
|
||||
test_invalid_username = scenario(
|
||||
"auth/login.feature",
|
||||
"Invalid username",
|
||||
)
|
||||
|
||||
test_ivalid_password = scenario(
|
||||
"auth/login.feature",
|
||||
"Invalid password",
|
||||
)
|
||||
|
||||
|
||||
@when("I go to login page")
|
||||
def go_to_login_page(browser):
|
||||
browser.visit(urljoin(browser.url, "/login"))
|
||||
|
||||
|
||||
@when(re.compile('I enter "(?P<username>\w+)" username'))
|
||||
def enter_username(browser, username):
|
||||
browser.fill("username", username)
|
||||
|
||||
|
||||
@when(re.compile('I enter "(?P<password>\w+)" password'))
|
||||
def enter_password(browser, password):
|
||||
browser.fill("password", password)
|
||||
|
||||
|
||||
@when('I click login button')
|
||||
def click_login(browser):
|
||||
button = browser.find_by_css("input[type=submit]").first
|
||||
button.click()
|
|
@ -1,8 +0,0 @@
|
|||
from flask import current_app
|
||||
|
||||
|
||||
def recreate_database():
|
||||
db = current_app.extensions['sqlalchemy'].db
|
||||
print 'Recreating database: %s' % db.engine.url.database
|
||||
db.drop_all()
|
||||
db.create_all()
|
|
@ -1,38 +0,0 @@
|
|||
from flask import current_app
|
||||
|
||||
from pytest_bdd_example.auth import user_datastore
|
||||
|
||||
|
||||
def create_initial_data():
|
||||
print 'Populating the initial data...'
|
||||
db = current_app.extensions['sqlalchemy'].db
|
||||
|
||||
print 'Creating roles...'
|
||||
admin_role = user_datastore.find_or_create_role(
|
||||
name='admin',
|
||||
description='Administrators',
|
||||
)
|
||||
|
||||
author_role = user_datastore.find_or_create_role(
|
||||
name='author',
|
||||
description='Book authors',
|
||||
)
|
||||
|
||||
print 'Admin user: admin/asdasd'
|
||||
admin = user_datastore.create_user(
|
||||
username='admin',
|
||||
password='asdasd',
|
||||
active=True,
|
||||
)
|
||||
user_datastore.add_role_to_user(admin, admin_role)
|
||||
|
||||
print 'Author user: author/asdasd'
|
||||
author = user_datastore.create_user(
|
||||
username='author',
|
||||
password='asdasd',
|
||||
active=True,
|
||||
)
|
||||
user_datastore.add_role_to_user(author, author_role)
|
||||
|
||||
#TODO: add other data here...
|
||||
db.session.commit()
|
|
@ -1,10 +0,0 @@
|
|||
from __future__ import absolute_import
|
||||
|
||||
import random
|
||||
import string
|
||||
|
||||
|
||||
def random_string(length=20):
|
||||
"""Create a random string."""
|
||||
alphanums = string.ascii_lowercase + string.digits
|
||||
return ''.join(random.choice(alphanums) for _ in xrange(length))
|
|
@ -0,0 +1,24 @@
|
|||
import pytest
|
||||
|
||||
|
||||
def count_o(value):
|
||||
"""Count occurences of the 'o' in a string."""
|
||||
return value.count('o')
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
('title', 'o_count'),
|
||||
(
|
||||
('Hello', 1),
|
||||
('World', 1),
|
||||
('Hello World', 2),
|
||||
)
|
||||
)
|
||||
def test_count_o(entry, o_count):
|
||||
"""Test count_o counts 'o' in the string.
|
||||
|
||||
:note: entry depends on the title fixture.
|
||||
|
||||
"""
|
||||
title, text = entry
|
||||
assert count_o(title) == o_count
|
Loading…
Reference in New Issue