')
+ def something(page):
+ return str(page)
+
+ app = flask.Flask(__name__)
+ app.register_blueprint(bp)
+
+ c = app.test_client()
+ self.assert_equal(c.get('/').data, '1')
+ self.assert_equal(c.get('/page/2').data, '2')
+
+ def test_route_decorator_custom_endpoint(self):
+
+ bp = flask.Blueprint('bp', __name__)
+
+ @bp.route('/foo')
+ def foo():
+ return flask.request.endpoint
+
+ @bp.route('/bar', endpoint='bar')
+ def foo_bar():
+ return flask.request.endpoint
+
+ @bp.route('/bar/123', endpoint='123')
+ def foo_bar_foo():
+ return flask.request.endpoint
+
+ @bp.route('/bar/foo')
+ def bar_foo():
+ return flask.request.endpoint
+
+ app = flask.Flask(__name__)
+ app.register_blueprint(bp, url_prefix='/py')
+
+ @app.route('/')
+ def index():
+ return flask.request.endpoint
+
+ c = app.test_client()
+ self.assertEqual(c.get('/').data, 'index')
+ self.assertEqual(c.get('/py/foo').data, 'bp.foo')
+ self.assertEqual(c.get('/py/bar').data, 'bp.bar')
+ self.assertEqual(c.get('/py/bar/123').data, 'bp.123')
+ self.assertEqual(c.get('/py/bar/foo').data, 'bp.bar_foo')
+
+ def test_route_decorator_custom_endpoint_with_dots(self):
+ bp = flask.Blueprint('bp', __name__)
+
+ @bp.route('/foo')
+ def foo():
+ return flask.request.endpoint
+
+ try:
+ @bp.route('/bar', endpoint='bar.bar')
+ def foo_bar():
+ return flask.request.endpoint
+ except AssertionError:
+ pass
+ else:
+ raise AssertionError('expected AssertionError not raised')
+
+ try:
+ @bp.route('/bar/123', endpoint='bar.123')
+ def foo_bar_foo():
+ return flask.request.endpoint
+ except AssertionError:
+ pass
+ else:
+ raise AssertionError('expected AssertionError not raised')
+
+ def foo_foo_foo():
+ pass
+
+ self.assertRaises(
+ AssertionError,
+ lambda: bp.add_url_rule(
+ '/bar/123', endpoint='bar.123', view_func=foo_foo_foo
+ )
+ )
+
+ self.assertRaises(
+ AssertionError,
+ bp.route('/bar/123', endpoint='bar.123'),
+ lambda: None
+ )
+
+ app = flask.Flask(__name__)
+ app.register_blueprint(bp, url_prefix='/py')
+
+ c = app.test_client()
+ self.assertEqual(c.get('/py/foo').data, 'bp.foo')
+ # The rule's din't actually made it through
+ rv = c.get('/py/bar')
+ assert rv.status_code == 404
+ rv = c.get('/py/bar/123')
+ assert rv.status_code == 404
+
+
+def suite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(BlueprintTestCase))
+ suite.addTest(unittest.makeSuite(ModuleTestCase))
+ return suite
diff --git a/flask/testsuite/config.py b/flask/testsuite/config.py
new file mode 100644
index 00000000..2ed726c8
--- /dev/null
+++ b/flask/testsuite/config.py
@@ -0,0 +1,177 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.testsuite.config
+ ~~~~~~~~~~~~~~~~~~~~~~
+
+ Configuration and instances.
+
+ :copyright: (c) 2011 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+import os
+import sys
+import flask
+import unittest
+from flask.testsuite import FlaskTestCase
+
+
+# config keys used for the ConfigTestCase
+TEST_KEY = 'foo'
+SECRET_KEY = 'devkey'
+
+
+class ConfigTestCase(FlaskTestCase):
+
+ def common_object_test(self, app):
+ self.assert_equal(app.secret_key, 'devkey')
+ self.assert_equal(app.config['TEST_KEY'], 'foo')
+ self.assert_('ConfigTestCase' not in app.config)
+
+ def test_config_from_file(self):
+ app = flask.Flask(__name__)
+ app.config.from_pyfile(__file__.rsplit('.', 1)[0] + '.py')
+ self.common_object_test(app)
+
+ def test_config_from_object(self):
+ app = flask.Flask(__name__)
+ app.config.from_object(__name__)
+ self.common_object_test(app)
+
+ def test_config_from_class(self):
+ class Base(object):
+ TEST_KEY = 'foo'
+ class Test(Base):
+ SECRET_KEY = 'devkey'
+ app = flask.Flask(__name__)
+ app.config.from_object(Test)
+ self.common_object_test(app)
+
+ def test_config_from_envvar(self):
+ env = os.environ
+ try:
+ os.environ = {}
+ app = flask.Flask(__name__)
+ try:
+ app.config.from_envvar('FOO_SETTINGS')
+ except RuntimeError, e:
+ self.assert_("'FOO_SETTINGS' is not set" in str(e))
+ else:
+ self.assert_(0, 'expected exception')
+ self.assert_(not app.config.from_envvar('FOO_SETTINGS', silent=True))
+
+ os.environ = {'FOO_SETTINGS': __file__.rsplit('.', 1)[0] + '.py'}
+ self.assert_(app.config.from_envvar('FOO_SETTINGS'))
+ self.common_object_test(app)
+ finally:
+ os.environ = env
+
+ def test_config_missing(self):
+ app = flask.Flask(__name__)
+ try:
+ app.config.from_pyfile('missing.cfg')
+ except IOError, e:
+ msg = str(e)
+ self.assert_(msg.startswith('[Errno 2] Unable to load configuration '
+ 'file (No such file or directory):'))
+ self.assert_(msg.endswith("missing.cfg'"))
+ else:
+ self.assert_(0, 'expected config')
+ self.assert_(not app.config.from_pyfile('missing.cfg', silent=True))
+
+
+class InstanceTestCase(FlaskTestCase):
+
+ def test_explicit_instance_paths(self):
+ here = os.path.abspath(os.path.dirname(__file__))
+ try:
+ flask.Flask(__name__, instance_path='instance')
+ except ValueError, e:
+ self.assert_('must be absolute' in str(e))
+ else:
+ self.fail('Expected value error')
+
+ app = flask.Flask(__name__, instance_path=here)
+ self.assert_equal(app.instance_path, here)
+
+ def test_uninstalled_module_paths(self):
+ from config_module_app import app
+ here = os.path.abspath(os.path.dirname(__file__))
+ self.assert_equal(app.instance_path, os.path.join(here, 'test_apps', 'instance'))
+
+ def test_uninstalled_package_paths(self):
+ from config_package_app import app
+ here = os.path.abspath(os.path.dirname(__file__))
+ self.assert_equal(app.instance_path, os.path.join(here, 'test_apps', 'instance'))
+
+ def test_installed_module_paths(self):
+ import types
+ expected_prefix = os.path.abspath('foo')
+ mod = types.ModuleType('myapp')
+ mod.__file__ = os.path.join(expected_prefix, 'lib', 'python2.5',
+ 'site-packages', 'myapp.py')
+ sys.modules['myapp'] = mod
+ try:
+ mod.app = flask.Flask(mod.__name__)
+ self.assert_equal(mod.app.instance_path,
+ os.path.join(expected_prefix, 'var',
+ 'myapp-instance'))
+ finally:
+ sys.modules['myapp'] = None
+
+ def test_installed_package_paths(self):
+ import types
+ expected_prefix = os.path.abspath('foo')
+ package_path = os.path.join(expected_prefix, 'lib', 'python2.5',
+ 'site-packages', 'myapp')
+ mod = types.ModuleType('myapp')
+ mod.__path__ = [package_path]
+ mod.__file__ = os.path.join(package_path, '__init__.py')
+ sys.modules['myapp'] = mod
+ try:
+ mod.app = flask.Flask(mod.__name__)
+ self.assert_equal(mod.app.instance_path,
+ os.path.join(expected_prefix, 'var',
+ 'myapp-instance'))
+ finally:
+ sys.modules['myapp'] = None
+
+ def test_prefix_installed_paths(self):
+ import types
+ expected_prefix = os.path.abspath(sys.prefix)
+ package_path = os.path.join(expected_prefix, 'lib', 'python2.5',
+ 'site-packages', 'myapp')
+ mod = types.ModuleType('myapp')
+ mod.__path__ = [package_path]
+ mod.__file__ = os.path.join(package_path, '__init__.py')
+ sys.modules['myapp'] = mod
+ try:
+ mod.app = flask.Flask(mod.__name__)
+ self.assert_equal(mod.app.instance_path,
+ os.path.join(expected_prefix, 'var',
+ 'myapp-instance'))
+ finally:
+ sys.modules['myapp'] = None
+
+ def test_egg_installed_paths(self):
+ import types
+ expected_prefix = os.path.abspath(sys.prefix)
+ package_path = os.path.join(expected_prefix, 'lib', 'python2.5',
+ 'site-packages', 'MyApp.egg', 'myapp')
+ mod = types.ModuleType('myapp')
+ mod.__path__ = [package_path]
+ mod.__file__ = os.path.join(package_path, '__init__.py')
+ sys.modules['myapp'] = mod
+ try:
+ mod.app = flask.Flask(mod.__name__)
+ self.assert_equal(mod.app.instance_path,
+ os.path.join(expected_prefix, 'var',
+ 'myapp-instance'))
+ finally:
+ sys.modules['myapp'] = None
+
+
+def suite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(ConfigTestCase))
+ suite.addTest(unittest.makeSuite(InstanceTestCase))
+ return suite
diff --git a/flask/testsuite/deprecations.py b/flask/testsuite/deprecations.py
new file mode 100644
index 00000000..062f40b0
--- /dev/null
+++ b/flask/testsuite/deprecations.py
@@ -0,0 +1,38 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.testsuite.deprecations
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Tests deprecation support.
+
+ :copyright: (c) 2011 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+import flask
+import unittest
+from flask.testsuite import FlaskTestCase, catch_warnings
+
+
+class DeprecationsTestCase(FlaskTestCase):
+
+ def test_init_jinja_globals(self):
+ class MyFlask(flask.Flask):
+ def init_jinja_globals(self):
+ self.jinja_env.globals['foo'] = '42'
+
+ with catch_warnings() as log:
+ app = MyFlask(__name__)
+ @app.route('/')
+ def foo():
+ return app.jinja_env.globals['foo']
+
+ c = app.test_client()
+ self.assert_equal(c.get('/').data, '42')
+ self.assert_equal(len(log), 1)
+ self.assert_('init_jinja_globals' in str(log[0]['message']))
+
+
+def suite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(DeprecationsTestCase))
+ return suite
diff --git a/flask/testsuite/examples.py b/flask/testsuite/examples.py
new file mode 100644
index 00000000..2d30958f
--- /dev/null
+++ b/flask/testsuite/examples.py
@@ -0,0 +1,38 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.testsuite.examples
+ ~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Tests the examples.
+
+ :copyright: (c) 2011 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+import os
+import unittest
+from flask.testsuite import add_to_path
+
+
+def setup_path():
+ example_path = os.path.join(os.path.dirname(__file__),
+ os.pardir, os.pardir, 'examples')
+ add_to_path(os.path.join(example_path, 'flaskr'))
+ add_to_path(os.path.join(example_path, 'minitwit'))
+
+
+def suite():
+ setup_path()
+ suite = unittest.TestSuite()
+ try:
+ from minitwit_tests import MiniTwitTestCase
+ except ImportError:
+ pass
+ else:
+ suite.addTest(unittest.makeSuite(MiniTwitTestCase))
+ try:
+ from flaskr_tests import FlaskrTestCase
+ except ImportError:
+ pass
+ else:
+ suite.addTest(unittest.makeSuite(FlaskrTestCase))
+ return suite
diff --git a/flask/testsuite/helpers.py b/flask/testsuite/helpers.py
new file mode 100644
index 00000000..56264f70
--- /dev/null
+++ b/flask/testsuite/helpers.py
@@ -0,0 +1,295 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.testsuite.helpers
+ ~~~~~~~~~~~~~~~~~~~~~~~
+
+ Various helpers.
+
+ :copyright: (c) 2011 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+import os
+import flask
+import unittest
+from logging import StreamHandler
+from StringIO import StringIO
+from flask.testsuite import FlaskTestCase, catch_warnings, catch_stderr
+from werkzeug.http import parse_options_header
+
+
+def has_encoding(name):
+ try:
+ import codecs
+ codecs.lookup(name)
+ return True
+ except LookupError:
+ return False
+
+
+class JSONTestCase(FlaskTestCase):
+
+ def test_json_bad_requests(self):
+ app = flask.Flask(__name__)
+ @app.route('/json', methods=['POST'])
+ def return_json():
+ return unicode(flask.request.json)
+ c = app.test_client()
+ rv = c.post('/json', data='malformed', content_type='application/json')
+ self.assert_equal(rv.status_code, 400)
+
+ def test_json_body_encoding(self):
+ app = flask.Flask(__name__)
+ app.testing = True
+ @app.route('/')
+ def index():
+ return flask.request.json
+
+ c = app.test_client()
+ resp = c.get('/', data=u'"Hällo Wörld"'.encode('iso-8859-15'),
+ content_type='application/json; charset=iso-8859-15')
+ self.assert_equal(resp.data, u'Hällo Wörld'.encode('utf-8'))
+
+ def test_jsonify(self):
+ d = dict(a=23, b=42, c=[1, 2, 3])
+ app = flask.Flask(__name__)
+ @app.route('/kw')
+ def return_kwargs():
+ return flask.jsonify(**d)
+ @app.route('/dict')
+ def return_dict():
+ return flask.jsonify(d)
+ c = app.test_client()
+ for url in '/kw', '/dict':
+ rv = c.get(url)
+ self.assert_equal(rv.mimetype, 'application/json')
+ self.assert_equal(flask.json.loads(rv.data), d)
+
+ def test_json_attr(self):
+ app = flask.Flask(__name__)
+ @app.route('/add', methods=['POST'])
+ def add():
+ return unicode(flask.request.json['a'] + flask.request.json['b'])
+ c = app.test_client()
+ rv = c.post('/add', data=flask.json.dumps({'a': 1, 'b': 2}),
+ content_type='application/json')
+ self.assert_equal(rv.data, '3')
+
+ def test_template_escaping(self):
+ app = flask.Flask(__name__)
+ render = flask.render_template_string
+ with app.test_request_context():
+ rv = render('{{ ""|tojson|safe }}')
+ self.assert_equal(rv, '"<\\/script>"')
+ rv = render('{{ "<\0/script>"|tojson|safe }}')
+ self.assert_equal(rv, '"<\\u0000\\/script>"')
+
+ def test_modified_url_encoding(self):
+ class ModifiedRequest(flask.Request):
+ url_charset = 'euc-kr'
+ app = flask.Flask(__name__)
+ app.request_class = ModifiedRequest
+ app.url_map.charset = 'euc-kr'
+
+ @app.route('/')
+ def index():
+ return flask.request.args['foo']
+
+ rv = app.test_client().get(u'/?foo=정상처리'.encode('euc-kr'))
+ self.assert_equal(rv.status_code, 200)
+ self.assert_equal(rv.data, u'정상처리'.encode('utf-8'))
+
+ if not has_encoding('euc-kr'):
+ test_modified_url_encoding = None
+
+
+class SendfileTestCase(FlaskTestCase):
+
+ def test_send_file_regular(self):
+ app = flask.Flask(__name__)
+ with app.test_request_context():
+ rv = flask.send_file('static/index.html')
+ self.assert_(rv.direct_passthrough)
+ self.assert_equal(rv.mimetype, 'text/html')
+ with app.open_resource('static/index.html') as f:
+ self.assert_equal(rv.data, f.read())
+
+ def test_send_file_xsendfile(self):
+ app = flask.Flask(__name__)
+ app.use_x_sendfile = True
+ with app.test_request_context():
+ rv = flask.send_file('static/index.html')
+ self.assert_(rv.direct_passthrough)
+ self.assert_('x-sendfile' in rv.headers)
+ self.assert_equal(rv.headers['x-sendfile'],
+ os.path.join(app.root_path, 'static/index.html'))
+ self.assert_equal(rv.mimetype, 'text/html')
+
+ def test_send_file_object(self):
+ app = flask.Flask(__name__)
+ with catch_warnings() as captured:
+ with app.test_request_context():
+ f = open(os.path.join(app.root_path, 'static/index.html'))
+ rv = flask.send_file(f)
+ with app.open_resource('static/index.html') as f:
+ self.assert_equal(rv.data, f.read())
+ self.assert_equal(rv.mimetype, 'text/html')
+ # mimetypes + etag
+ self.assert_equal(len(captured), 2)
+
+ app.use_x_sendfile = True
+ with catch_warnings() as captured:
+ with app.test_request_context():
+ f = open(os.path.join(app.root_path, 'static/index.html'))
+ rv = flask.send_file(f)
+ self.assert_equal(rv.mimetype, 'text/html')
+ self.assert_('x-sendfile' in rv.headers)
+ self.assert_equal(rv.headers['x-sendfile'],
+ os.path.join(app.root_path, 'static/index.html'))
+ # mimetypes + etag
+ self.assert_equal(len(captured), 2)
+
+ app.use_x_sendfile = False
+ with app.test_request_context():
+ with catch_warnings() as captured:
+ f = StringIO('Test')
+ rv = flask.send_file(f)
+ self.assert_equal(rv.data, 'Test')
+ self.assert_equal(rv.mimetype, 'application/octet-stream')
+ # etags
+ self.assert_equal(len(captured), 1)
+ with catch_warnings() as captured:
+ f = StringIO('Test')
+ rv = flask.send_file(f, mimetype='text/plain')
+ self.assert_equal(rv.data, 'Test')
+ self.assert_equal(rv.mimetype, 'text/plain')
+ # etags
+ self.assert_equal(len(captured), 1)
+
+ app.use_x_sendfile = True
+ with catch_warnings() as captured:
+ with app.test_request_context():
+ f = StringIO('Test')
+ rv = flask.send_file(f)
+ self.assert_('x-sendfile' not in rv.headers)
+ # etags
+ self.assert_equal(len(captured), 1)
+
+ def test_attachment(self):
+ app = flask.Flask(__name__)
+ with catch_warnings() as captured:
+ with app.test_request_context():
+ f = open(os.path.join(app.root_path, 'static/index.html'))
+ rv = flask.send_file(f, as_attachment=True)
+ value, options = parse_options_header(rv.headers['Content-Disposition'])
+ self.assert_equal(value, 'attachment')
+ # mimetypes + etag
+ self.assert_equal(len(captured), 2)
+
+ with app.test_request_context():
+ self.assert_equal(options['filename'], 'index.html')
+ rv = flask.send_file('static/index.html', as_attachment=True)
+ value, options = parse_options_header(rv.headers['Content-Disposition'])
+ self.assert_equal(value, 'attachment')
+ self.assert_equal(options['filename'], 'index.html')
+
+ with app.test_request_context():
+ rv = flask.send_file(StringIO('Test'), as_attachment=True,
+ attachment_filename='index.txt',
+ add_etags=False)
+ self.assert_equal(rv.mimetype, 'text/plain')
+ value, options = parse_options_header(rv.headers['Content-Disposition'])
+ self.assert_equal(value, 'attachment')
+ self.assert_equal(options['filename'], 'index.txt')
+
+
+class LoggingTestCase(FlaskTestCase):
+
+ def test_logger_cache(self):
+ app = flask.Flask(__name__)
+ logger1 = app.logger
+ self.assert_(app.logger is logger1)
+ self.assert_equal(logger1.name, __name__)
+ app.logger_name = __name__ + '/test_logger_cache'
+ self.assert_(app.logger is not logger1)
+
+ def test_debug_log(self):
+ app = flask.Flask(__name__)
+ app.debug = True
+
+ @app.route('/')
+ def index():
+ app.logger.warning('the standard library is dead')
+ app.logger.debug('this is a debug statement')
+ return ''
+
+ @app.route('/exc')
+ def exc():
+ 1/0
+
+ with app.test_client() as c:
+ with catch_stderr() as err:
+ c.get('/')
+ out = err.getvalue()
+ self.assert_('WARNING in helpers [' in out)
+ self.assert_(os.path.basename(__file__.rsplit('.', 1)[0] + '.py') in out)
+ self.assert_('the standard library is dead' in out)
+ self.assert_('this is a debug statement' in out)
+
+ with catch_stderr() as err:
+ try:
+ c.get('/exc')
+ except ZeroDivisionError:
+ pass
+ else:
+ self.assert_(False, 'debug log ate the exception')
+
+ def test_exception_logging(self):
+ out = StringIO()
+ app = flask.Flask(__name__)
+ app.logger_name = 'flask_tests/test_exception_logging'
+ app.logger.addHandler(StreamHandler(out))
+
+ @app.route('/')
+ def index():
+ 1/0
+
+ rv = app.test_client().get('/')
+ self.assert_equal(rv.status_code, 500)
+ self.assert_('Internal Server Error' in rv.data)
+
+ err = out.getvalue()
+ self.assert_('Exception on / [GET]' in err)
+ self.assert_('Traceback (most recent call last):' in err)
+ self.assert_('1/0' in err)
+ self.assert_('ZeroDivisionError:' in err)
+
+ def test_processor_exceptions(self):
+ app = flask.Flask(__name__)
+ @app.before_request
+ def before_request():
+ if trigger == 'before':
+ 1/0
+ @app.after_request
+ def after_request(response):
+ if trigger == 'after':
+ 1/0
+ return response
+ @app.route('/')
+ def index():
+ return 'Foo'
+ @app.errorhandler(500)
+ def internal_server_error(e):
+ return 'Hello Server Error', 500
+ for trigger in 'before', 'after':
+ rv = app.test_client().get('/')
+ self.assert_equal(rv.status_code, 500)
+ self.assert_equal(rv.data, 'Hello Server Error')
+
+
+def suite():
+ suite = unittest.TestSuite()
+ if flask.json_available:
+ suite.addTest(unittest.makeSuite(JSONTestCase))
+ suite.addTest(unittest.makeSuite(SendfileTestCase))
+ suite.addTest(unittest.makeSuite(LoggingTestCase))
+ return suite
diff --git a/flask/testsuite/signals.py b/flask/testsuite/signals.py
new file mode 100644
index 00000000..da1a68ca
--- /dev/null
+++ b/flask/testsuite/signals.py
@@ -0,0 +1,103 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.testsuite.signals
+ ~~~~~~~~~~~~~~~~~~~~~~~
+
+ Signalling.
+
+ :copyright: (c) 2011 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+import flask
+import unittest
+from flask.testsuite import FlaskTestCase
+
+
+class SignalsTestCase(FlaskTestCase):
+
+ def test_template_rendered(self):
+ app = flask.Flask(__name__)
+
+ @app.route('/')
+ def index():
+ return flask.render_template('simple_template.html', whiskey=42)
+
+ recorded = []
+ def record(sender, template, context):
+ recorded.append((template, context))
+
+ flask.template_rendered.connect(record, app)
+ try:
+ app.test_client().get('/')
+ self.assert_equal(len(recorded), 1)
+ template, context = recorded[0]
+ self.assert_equal(template.name, 'simple_template.html')
+ self.assert_equal(context['whiskey'], 42)
+ finally:
+ flask.template_rendered.disconnect(record, app)
+
+ def test_request_signals(self):
+ app = flask.Flask(__name__)
+ calls = []
+
+ def before_request_signal(sender):
+ calls.append('before-signal')
+
+ def after_request_signal(sender, response):
+ self.assert_equal(response.data, 'stuff')
+ calls.append('after-signal')
+
+ @app.before_request
+ def before_request_handler():
+ calls.append('before-handler')
+
+ @app.after_request
+ def after_request_handler(response):
+ calls.append('after-handler')
+ response.data = 'stuff'
+ return response
+
+ @app.route('/')
+ def index():
+ calls.append('handler')
+ return 'ignored anyway'
+
+ flask.request_started.connect(before_request_signal, app)
+ flask.request_finished.connect(after_request_signal, app)
+
+ try:
+ rv = app.test_client().get('/')
+ self.assert_equal(rv.data, 'stuff')
+
+ self.assert_equal(calls, ['before-signal', 'before-handler',
+ 'handler', 'after-handler',
+ 'after-signal'])
+ finally:
+ flask.request_started.disconnect(before_request_signal, app)
+ flask.request_finished.disconnect(after_request_signal, app)
+
+ def test_request_exception_signal(self):
+ app = flask.Flask(__name__)
+ recorded = []
+
+ @app.route('/')
+ def index():
+ 1/0
+
+ def record(sender, exception):
+ recorded.append(exception)
+
+ flask.got_request_exception.connect(record, app)
+ try:
+ self.assert_equal(app.test_client().get('/').status_code, 500)
+ self.assert_equal(len(recorded), 1)
+ self.assert_(isinstance(recorded[0], ZeroDivisionError))
+ finally:
+ flask.got_request_exception.disconnect(record, app)
+
+
+def suite():
+ suite = unittest.TestSuite()
+ if flask.signals_available:
+ suite.addTest(unittest.makeSuite(SignalsTestCase))
+ return suite
diff --git a/tests/static/index.html b/flask/testsuite/static/index.html
similarity index 100%
rename from tests/static/index.html
rename to flask/testsuite/static/index.html
diff --git a/tests/templates/_macro.html b/flask/testsuite/templates/_macro.html
similarity index 100%
rename from tests/templates/_macro.html
rename to flask/testsuite/templates/_macro.html
diff --git a/tests/templates/context_template.html b/flask/testsuite/templates/context_template.html
similarity index 100%
rename from tests/templates/context_template.html
rename to flask/testsuite/templates/context_template.html
diff --git a/tests/templates/escaping_template.html b/flask/testsuite/templates/escaping_template.html
similarity index 100%
rename from tests/templates/escaping_template.html
rename to flask/testsuite/templates/escaping_template.html
diff --git a/tests/templates/mail.txt b/flask/testsuite/templates/mail.txt
similarity index 100%
rename from tests/templates/mail.txt
rename to flask/testsuite/templates/mail.txt
diff --git a/tests/templates/nested/nested.txt b/flask/testsuite/templates/nested/nested.txt
similarity index 100%
rename from tests/templates/nested/nested.txt
rename to flask/testsuite/templates/nested/nested.txt
diff --git a/tests/templates/simple_template.html b/flask/testsuite/templates/simple_template.html
similarity index 100%
rename from tests/templates/simple_template.html
rename to flask/testsuite/templates/simple_template.html
diff --git a/tests/templates/template_filter.html b/flask/testsuite/templates/template_filter.html
similarity index 100%
rename from tests/templates/template_filter.html
rename to flask/testsuite/templates/template_filter.html
diff --git a/flask/testsuite/templating.py b/flask/testsuite/templating.py
new file mode 100644
index 00000000..eadbdcf6
--- /dev/null
+++ b/flask/testsuite/templating.py
@@ -0,0 +1,141 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.testsuite.templating
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Template functionality
+
+ :copyright: (c) 2011 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+import flask
+import unittest
+from flask.testsuite import FlaskTestCase
+
+
+class TemplatingTestCase(FlaskTestCase):
+
+ def test_context_processing(self):
+ app = flask.Flask(__name__)
+ @app.context_processor
+ def context_processor():
+ return {'injected_value': 42}
+ @app.route('/')
+ def index():
+ return flask.render_template('context_template.html', value=23)
+ rv = app.test_client().get('/')
+ self.assert_equal(rv.data, '23|42')
+
+ def test_original_win(self):
+ app = flask.Flask(__name__)
+ @app.route('/')
+ def index():
+ return flask.render_template_string('{{ config }}', config=42)
+ rv = app.test_client().get('/')
+ self.assert_equal(rv.data, '42')
+
+ def test_standard_context(self):
+ app = flask.Flask(__name__)
+ app.secret_key = 'development key'
+ @app.route('/')
+ def index():
+ flask.g.foo = 23
+ flask.session['test'] = 'aha'
+ return flask.render_template_string('''
+ {{ request.args.foo }}
+ {{ g.foo }}
+ {{ config.DEBUG }}
+ {{ session.test }}
+ ''')
+ rv = app.test_client().get('/?foo=42')
+ self.assert_equal(rv.data.split(), ['42', '23', 'False', 'aha'])
+
+ def test_escaping(self):
+ text = '
Hello World!'
+ app = flask.Flask(__name__)
+ @app.route('/')
+ def index():
+ return flask.render_template('escaping_template.html', text=text,
+ html=flask.Markup(text))
+ lines = app.test_client().get('/').data.splitlines()
+ self.assert_equal(lines, [
+ '<p>Hello World!',
+ '
Hello World!',
+ '
Hello World!',
+ '
Hello World!',
+ '<p>Hello World!',
+ '
Hello World!'
+ ])
+
+ def test_no_escaping(self):
+ app = flask.Flask(__name__)
+ with app.test_request_context():
+ self.assert_equal(flask.render_template_string('{{ foo }}',
+ foo=''), '')
+ self.assert_equal(flask.render_template('mail.txt', foo=''),
+ ' Mail')
+
+ def test_macros(self):
+ app = flask.Flask(__name__)
+ with app.test_request_context():
+ macro = flask.get_template_attribute('_macro.html', 'hello')
+ self.assert_equal(macro('World'), 'Hello World!')
+
+ def test_template_filter(self):
+ app = flask.Flask(__name__)
+ @app.template_filter()
+ def my_reverse(s):
+ return s[::-1]
+ self.assert_('my_reverse' in app.jinja_env.filters.keys())
+ self.assert_equal(app.jinja_env.filters['my_reverse'], my_reverse)
+ self.assert_equal(app.jinja_env.filters['my_reverse']('abcd'), 'dcba')
+
+ def test_template_filter_with_name(self):
+ app = flask.Flask(__name__)
+ @app.template_filter('strrev')
+ def my_reverse(s):
+ return s[::-1]
+ self.assert_('strrev' in app.jinja_env.filters.keys())
+ self.assert_equal(app.jinja_env.filters['strrev'], my_reverse)
+ self.assert_equal(app.jinja_env.filters['strrev']('abcd'), 'dcba')
+
+ def test_template_filter_with_template(self):
+ app = flask.Flask(__name__)
+ @app.template_filter()
+ def super_reverse(s):
+ return s[::-1]
+ @app.route('/')
+ def index():
+ return flask.render_template('template_filter.html', value='abcd')
+ rv = app.test_client().get('/')
+ self.assert_equal(rv.data, 'dcba')
+
+ def test_template_filter_with_name_and_template(self):
+ app = flask.Flask(__name__)
+ @app.template_filter('super_reverse')
+ def my_reverse(s):
+ return s[::-1]
+ @app.route('/')
+ def index():
+ return flask.render_template('template_filter.html', value='abcd')
+ rv = app.test_client().get('/')
+ self.assert_equal(rv.data, 'dcba')
+
+ def test_custom_template_loader(self):
+ class MyFlask(flask.Flask):
+ def create_global_jinja_loader(self):
+ from jinja2 import DictLoader
+ return DictLoader({'index.html': 'Hello Custom World!'})
+ app = MyFlask(__name__)
+ @app.route('/')
+ def index():
+ return flask.render_template('index.html')
+ c = app.test_client()
+ rv = c.get('/')
+ self.assert_equal(rv.data, 'Hello Custom World!')
+
+
+def suite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(TemplatingTestCase))
+ return suite
diff --git a/tests/blueprintapp/__init__.py b/flask/testsuite/test_apps/blueprintapp/__init__.py
similarity index 100%
rename from tests/blueprintapp/__init__.py
rename to flask/testsuite/test_apps/blueprintapp/__init__.py
diff --git a/tests/blueprintapp/apps/__init__.py b/flask/testsuite/test_apps/blueprintapp/apps/__init__.py
similarity index 100%
rename from tests/blueprintapp/apps/__init__.py
rename to flask/testsuite/test_apps/blueprintapp/apps/__init__.py
diff --git a/tests/blueprintapp/apps/admin/__init__.py b/flask/testsuite/test_apps/blueprintapp/apps/admin/__init__.py
similarity index 100%
rename from tests/blueprintapp/apps/admin/__init__.py
rename to flask/testsuite/test_apps/blueprintapp/apps/admin/__init__.py
diff --git a/tests/blueprintapp/apps/admin/static/css/test.css b/flask/testsuite/test_apps/blueprintapp/apps/admin/static/css/test.css
similarity index 100%
rename from tests/blueprintapp/apps/admin/static/css/test.css
rename to flask/testsuite/test_apps/blueprintapp/apps/admin/static/css/test.css
diff --git a/tests/blueprintapp/apps/admin/static/test.txt b/flask/testsuite/test_apps/blueprintapp/apps/admin/static/test.txt
similarity index 100%
rename from tests/blueprintapp/apps/admin/static/test.txt
rename to flask/testsuite/test_apps/blueprintapp/apps/admin/static/test.txt
diff --git a/tests/blueprintapp/apps/admin/templates/admin/index.html b/flask/testsuite/test_apps/blueprintapp/apps/admin/templates/admin/index.html
similarity index 100%
rename from tests/blueprintapp/apps/admin/templates/admin/index.html
rename to flask/testsuite/test_apps/blueprintapp/apps/admin/templates/admin/index.html
diff --git a/tests/blueprintapp/apps/frontend/__init__.py b/flask/testsuite/test_apps/blueprintapp/apps/frontend/__init__.py
similarity index 100%
rename from tests/blueprintapp/apps/frontend/__init__.py
rename to flask/testsuite/test_apps/blueprintapp/apps/frontend/__init__.py
diff --git a/tests/blueprintapp/apps/frontend/templates/frontend/index.html b/flask/testsuite/test_apps/blueprintapp/apps/frontend/templates/frontend/index.html
similarity index 100%
rename from tests/blueprintapp/apps/frontend/templates/frontend/index.html
rename to flask/testsuite/test_apps/blueprintapp/apps/frontend/templates/frontend/index.html
diff --git a/flask/testsuite/test_apps/config_module_app.py b/flask/testsuite/test_apps/config_module_app.py
new file mode 100644
index 00000000..380d46bf
--- /dev/null
+++ b/flask/testsuite/test_apps/config_module_app.py
@@ -0,0 +1,4 @@
+import os
+import flask
+here = os.path.abspath(os.path.dirname(__file__))
+app = flask.Flask(__name__)
diff --git a/flask/testsuite/test_apps/config_package_app/__init__.py b/flask/testsuite/test_apps/config_package_app/__init__.py
new file mode 100644
index 00000000..380d46bf
--- /dev/null
+++ b/flask/testsuite/test_apps/config_package_app/__init__.py
@@ -0,0 +1,4 @@
+import os
+import flask
+here = os.path.abspath(os.path.dirname(__file__))
+app = flask.Flask(__name__)
diff --git a/tests/moduleapp/__init__.py b/flask/testsuite/test_apps/moduleapp/__init__.py
similarity index 100%
rename from tests/moduleapp/__init__.py
rename to flask/testsuite/test_apps/moduleapp/__init__.py
diff --git a/tests/moduleapp/apps/__init__.py b/flask/testsuite/test_apps/moduleapp/apps/__init__.py
similarity index 100%
rename from tests/moduleapp/apps/__init__.py
rename to flask/testsuite/test_apps/moduleapp/apps/__init__.py
diff --git a/tests/moduleapp/apps/admin/__init__.py b/flask/testsuite/test_apps/moduleapp/apps/admin/__init__.py
similarity index 100%
rename from tests/moduleapp/apps/admin/__init__.py
rename to flask/testsuite/test_apps/moduleapp/apps/admin/__init__.py
diff --git a/tests/moduleapp/apps/admin/static/css/test.css b/flask/testsuite/test_apps/moduleapp/apps/admin/static/css/test.css
similarity index 100%
rename from tests/moduleapp/apps/admin/static/css/test.css
rename to flask/testsuite/test_apps/moduleapp/apps/admin/static/css/test.css
diff --git a/tests/moduleapp/apps/admin/static/test.txt b/flask/testsuite/test_apps/moduleapp/apps/admin/static/test.txt
similarity index 100%
rename from tests/moduleapp/apps/admin/static/test.txt
rename to flask/testsuite/test_apps/moduleapp/apps/admin/static/test.txt
diff --git a/tests/moduleapp/apps/admin/templates/index.html b/flask/testsuite/test_apps/moduleapp/apps/admin/templates/index.html
similarity index 100%
rename from tests/moduleapp/apps/admin/templates/index.html
rename to flask/testsuite/test_apps/moduleapp/apps/admin/templates/index.html
diff --git a/tests/moduleapp/apps/frontend/__init__.py b/flask/testsuite/test_apps/moduleapp/apps/frontend/__init__.py
similarity index 100%
rename from tests/moduleapp/apps/frontend/__init__.py
rename to flask/testsuite/test_apps/moduleapp/apps/frontend/__init__.py
diff --git a/tests/moduleapp/apps/frontend/templates/index.html b/flask/testsuite/test_apps/moduleapp/apps/frontend/templates/index.html
similarity index 100%
rename from tests/moduleapp/apps/frontend/templates/index.html
rename to flask/testsuite/test_apps/moduleapp/apps/frontend/templates/index.html
diff --git a/tests/subdomaintestmodule/__init__.py b/flask/testsuite/test_apps/subdomaintestmodule/__init__.py
similarity index 100%
rename from tests/subdomaintestmodule/__init__.py
rename to flask/testsuite/test_apps/subdomaintestmodule/__init__.py
diff --git a/tests/subdomaintestmodule/static/hello.txt b/flask/testsuite/test_apps/subdomaintestmodule/static/hello.txt
similarity index 100%
rename from tests/subdomaintestmodule/static/hello.txt
rename to flask/testsuite/test_apps/subdomaintestmodule/static/hello.txt
diff --git a/flask/testsuite/testing.py b/flask/testsuite/testing.py
new file mode 100644
index 00000000..32867c3f
--- /dev/null
+++ b/flask/testsuite/testing.py
@@ -0,0 +1,165 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.testsuite.testing
+ ~~~~~~~~~~~~~~~~~~~~~~~
+
+ Test client and more.
+
+ :copyright: (c) 2011 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+import flask
+import unittest
+from flask.testsuite import FlaskTestCase
+
+
+class TestToolsTestCase(FlaskTestCase):
+
+ def test_environ_defaults_from_config(self):
+ app = flask.Flask(__name__)
+ app.testing = True
+ app.config['SERVER_NAME'] = 'example.com:1234'
+ app.config['APPLICATION_ROOT'] = '/foo'
+ @app.route('/')
+ def index():
+ return flask.request.url
+
+ ctx = app.test_request_context()
+ self.assert_equal(ctx.request.url, 'http://example.com:1234/foo/')
+ with app.test_client() as c:
+ rv = c.get('/')
+ self.assert_equal(rv.data, 'http://example.com:1234/foo/')
+
+ def test_environ_defaults(self):
+ app = flask.Flask(__name__)
+ app.testing = True
+ @app.route('/')
+ def index():
+ return flask.request.url
+
+ ctx = app.test_request_context()
+ self.assert_equal(ctx.request.url, 'http://localhost/')
+ with app.test_client() as c:
+ rv = c.get('/')
+ self.assert_equal(rv.data, 'http://localhost/')
+
+ def test_session_transactions(self):
+ app = flask.Flask(__name__)
+ app.testing = True
+ app.secret_key = 'testing'
+
+ @app.route('/')
+ def index():
+ return unicode(flask.session['foo'])
+
+ with app.test_client() as c:
+ with c.session_transaction() as sess:
+ self.assert_equal(len(sess), 0)
+ sess['foo'] = [42]
+ self.assert_equal(len(sess), 1)
+ rv = c.get('/')
+ self.assert_equal(rv.data, '[42]')
+
+ def test_session_transactions_no_null_sessions(self):
+ app = flask.Flask(__name__)
+ app.testing = True
+
+ with app.test_client() as c:
+ try:
+ with c.session_transaction() as sess:
+ pass
+ except RuntimeError, e:
+ self.assert_('Session backend did not open a session' in str(e))
+ else:
+ self.fail('Expected runtime error')
+
+ def test_session_transactions_keep_context(self):
+ app = flask.Flask(__name__)
+ app.testing = True
+ app.secret_key = 'testing'
+
+ with app.test_client() as c:
+ rv = c.get('/')
+ req = flask.request._get_current_object()
+ with c.session_transaction():
+ self.assert_(req is flask.request._get_current_object())
+
+ def test_session_transaction_needs_cookies(self):
+ app = flask.Flask(__name__)
+ app.testing = True
+ c = app.test_client(use_cookies=False)
+ try:
+ with c.session_transaction() as s:
+ pass
+ except RuntimeError, e:
+ self.assert_('cookies' in str(e))
+ else:
+ self.fail('Expected runtime error')
+
+ def test_test_client_context_binding(self):
+ app = flask.Flask(__name__)
+ @app.route('/')
+ def index():
+ flask.g.value = 42
+ return 'Hello World!'
+
+ @app.route('/other')
+ def other():
+ 1/0
+
+ with app.test_client() as c:
+ resp = c.get('/')
+ self.assert_equal(flask.g.value, 42)
+ self.assert_equal(resp.data, 'Hello World!')
+ self.assert_equal(resp.status_code, 200)
+
+ resp = c.get('/other')
+ self.assert_(not hasattr(flask.g, 'value'))
+ self.assert_('Internal Server Error' in resp.data)
+ self.assert_equal(resp.status_code, 500)
+ flask.g.value = 23
+
+ try:
+ flask.g.value
+ except (AttributeError, RuntimeError):
+ pass
+ else:
+ raise AssertionError('some kind of exception expected')
+
+ def test_reuse_client(self):
+ app = flask.Flask(__name__)
+ c = app.test_client()
+
+ with c:
+ self.assert_equal(c.get('/').status_code, 404)
+
+ with c:
+ self.assert_equal(c.get('/').status_code, 404)
+
+ def test_test_client_calls_teardown_handlers(self):
+ app = flask.Flask(__name__)
+ called = []
+ @app.teardown_request
+ def remember(error):
+ called.append(error)
+
+ with app.test_client() as c:
+ self.assert_equal(called, [])
+ c.get('/')
+ self.assert_equal(called, [])
+ self.assert_equal(called, [None])
+
+ del called[:]
+ with app.test_client() as c:
+ self.assert_equal(called, [])
+ c.get('/')
+ self.assert_equal(called, [])
+ c.get('/')
+ self.assert_equal(called, [None])
+ self.assert_equal(called, [None, None])
+
+
+def suite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(TestToolsTestCase))
+ return suite
diff --git a/flask/testsuite/views.py b/flask/testsuite/views.py
new file mode 100644
index 00000000..a89c44ac
--- /dev/null
+++ b/flask/testsuite/views.py
@@ -0,0 +1,117 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.testsuite.views
+ ~~~~~~~~~~~~~~~~~~~~~
+
+ Pluggable views.
+
+ :copyright: (c) 2011 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+import flask
+import flask.views
+import unittest
+from flask.testsuite import FlaskTestCase
+from werkzeug.http import parse_set_header
+
+
+class ViewTestCase(FlaskTestCase):
+
+ def common_test(self, app):
+ c = app.test_client()
+
+ self.assert_equal(c.get('/').data, 'GET')
+ self.assert_equal(c.post('/').data, 'POST')
+ self.assert_equal(c.put('/').status_code, 405)
+ meths = parse_set_header(c.open('/', method='OPTIONS').headers['Allow'])
+ self.assert_equal(sorted(meths), ['GET', 'HEAD', 'OPTIONS', 'POST'])
+
+ def test_basic_view(self):
+ app = flask.Flask(__name__)
+
+ class Index(flask.views.View):
+ methods = ['GET', 'POST']
+ def dispatch_request(self):
+ return flask.request.method
+
+ app.add_url_rule('/', view_func=Index.as_view('index'))
+ self.common_test(app)
+
+ def test_method_based_view(self):
+ app = flask.Flask(__name__)
+
+ class Index(flask.views.MethodView):
+ def get(self):
+ return 'GET'
+ def post(self):
+ return 'POST'
+
+ app.add_url_rule('/', view_func=Index.as_view('index'))
+
+ self.common_test(app)
+
+ def test_view_patching(self):
+ app = flask.Flask(__name__)
+
+ class Index(flask.views.MethodView):
+ def get(self):
+ 1/0
+ def post(self):
+ 1/0
+
+ class Other(Index):
+ def get(self):
+ return 'GET'
+ def post(self):
+ return 'POST'
+
+ view = Index.as_view('index')
+ view.view_class = Other
+ app.add_url_rule('/', view_func=view)
+ self.common_test(app)
+
+ def test_view_inheritance(self):
+ app = flask.Flask(__name__)
+
+ class Index(flask.views.MethodView):
+ def get(self):
+ return 'GET'
+ def post(self):
+ return 'POST'
+
+ class BetterIndex(Index):
+ def delete(self):
+ return 'DELETE'
+
+ app.add_url_rule('/', view_func=BetterIndex.as_view('index'))
+ c = app.test_client()
+
+ meths = parse_set_header(c.open('/', method='OPTIONS').headers['Allow'])
+ self.assert_equal(sorted(meths), ['DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST'])
+
+ def test_view_decorators(self):
+ app = flask.Flask(__name__)
+
+ def add_x_parachute(f):
+ def new_function(*args, **kwargs):
+ resp = flask.make_response(f(*args, **kwargs))
+ resp.headers['X-Parachute'] = 'awesome'
+ return resp
+ return new_function
+
+ class Index(flask.views.View):
+ decorators = [add_x_parachute]
+ def dispatch_request(self):
+ return 'Awesome'
+
+ app.add_url_rule('/', view_func=Index.as_view('index'))
+ c = app.test_client()
+ rv = c.get('/')
+ self.assert_equal(rv.headers['X-Parachute'], 'awesome')
+ self.assert_equal(rv.data, 'Awesome')
+
+
+def suite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(ViewTestCase))
+ return suite
diff --git a/flask/views.py b/flask/views.py
index 9a185570..2fe34622 100644
--- a/flask/views.py
+++ b/flask/views.py
@@ -30,10 +30,38 @@ class View(object):
return 'Hello %s!' % name
app.add_url_rule('/hello/', view_func=MyView.as_view('myview'))
+
+ When you want to decorate a pluggable view you will have to either do that
+ when the view function is created (by wrapping the return value of
+ :meth:`as_view`) or you can use the :attr:`decorators` attribute::
+
+ class SecretView(View):
+ methods = ['GET']
+ decorators = [superuser_required]
+
+ def dispatch_request(self):
+ ...
+
+ The decorators stored in the decorators list are applied one after another
+ when the view function is created. Note that you can *not* use the class
+ based decorators since those would decorate the view class and not the
+ generated view function!
"""
+ #: A for which methods this pluggable view can handle.
methods = None
+ #: The canonical way to decorate class based views is to decorate the
+ #: return value of as_view(). However since this moves parts of the
+ #: logic from the class declaration to the place where it's hooked
+ #: into the routing system.
+ #:
+ #: You can place one or more decorators in this list and whenever the
+ #: view function is created the result is automatically decorated.
+ #:
+ #: .. versionadded:: 0.8
+ decorators = []
+
def dispatch_request(self):
"""Subclasses have to override this method to implement the
actual view functionc ode. This method is called with all
@@ -54,6 +82,13 @@ class View(object):
def view(*args, **kwargs):
self = view.view_class(*class_args, **class_kwargs)
return self.dispatch_request(*args, **kwargs)
+
+ if cls.decorators:
+ view.__name__ = name
+ view.__module__ = cls.__module__
+ for decorator in cls.decorators:
+ view = decorator(view)
+
# we attach the view class to the view function for two reasons:
# first of all it allows us to easily figure out what class based
# view this thing came from, secondly it's also used for instanciating
diff --git a/run-tests.py b/run-tests.py
new file mode 100644
index 00000000..4ef8a72d
--- /dev/null
+++ b/run-tests.py
@@ -0,0 +1,3 @@
+#!/usr/bin/env python
+from flask.testsuite import main
+main()
diff --git a/tests/flaskext_test.py b/scripts/flaskext_test.py
similarity index 100%
rename from tests/flaskext_test.py
rename to scripts/flaskext_test.py
diff --git a/setup.py b/setup.py
index 1809e05e..db2eb9c7 100644
--- a/setup.py
+++ b/setup.py
@@ -77,12 +77,6 @@ class run_audit(Command):
else:
print ("No problems found in sourcecode.")
-def run_tests():
- import os, sys
- sys.path.append(os.path.join(os.path.dirname(__file__), 'tests'))
- from flask_tests import suite
- return suite()
-
setup(
name='Flask',
@@ -94,7 +88,10 @@ setup(
description='A microframework based on Werkzeug, Jinja2 '
'and good intentions',
long_description=__doc__,
- packages=['flask'],
+ packages=['flask', 'flask.testsuite'],
+ package_data={
+ 'flask.testsuite': ['test_apps/*', 'static/*', 'templates/*']
+ },
zip_safe=False,
platforms='any',
install_requires=[
@@ -112,5 +109,5 @@ setup(
'Topic :: Software Development :: Libraries :: Python Modules'
],
cmdclass={'audit': run_audit},
- test_suite='__main__.run_tests'
+ test_suite='flask.testsuite.suite'
)
diff --git a/tests/flask_tests.py b/tests/flask_tests.py
deleted file mode 100644
index d5f7413d..00000000
--- a/tests/flask_tests.py
+++ /dev/null
@@ -1,2312 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- Flask Tests
- ~~~~~~~~~~~
-
- Tests Flask itself. The majority of Flask is already tested
- as part of Werkzeug.
-
- :copyright: (c) 2010 by Armin Ronacher.
- :license: BSD, see LICENSE for more details.
-"""
-from __future__ import with_statement
-import os
-import re
-import sys
-import flask
-import flask.views
-import unittest
-import warnings
-from threading import Thread
-from logging import StreamHandler
-from contextlib import contextmanager
-from functools import update_wrapper
-from datetime import datetime
-from werkzeug import parse_date, parse_options_header
-from werkzeug.exceptions import NotFound, BadRequest
-from werkzeug.http import parse_set_header
-from jinja2 import TemplateNotFound
-from cStringIO import StringIO
-
-example_path = os.path.join(os.path.dirname(__file__), '..', 'examples')
-sys.path.append(os.path.join(example_path, 'flaskr'))
-sys.path.append(os.path.join(example_path, 'minitwit'))
-
-
-def has_encoding(name):
- try:
- import codecs
- codecs.lookup(name)
- return True
- except LookupError:
- return False
-
-
-# config keys used for the ConfigTestCase
-TEST_KEY = 'foo'
-SECRET_KEY = 'devkey'
-
-
-# import moduleapp here because it uses deprecated features and we don't
-# want to see the warnings
-warnings.simplefilter('ignore', DeprecationWarning)
-from moduleapp import app as moduleapp
-warnings.simplefilter('default', DeprecationWarning)
-
-
-@contextmanager
-def catch_warnings():
- """Catch warnings in a with block in a list"""
- # make sure deprecation warnings are active in tests
- warnings.simplefilter('default', category=DeprecationWarning)
-
- filters = warnings.filters
- warnings.filters = filters[:]
- old_showwarning = warnings.showwarning
- log = []
- def showwarning(message, category, filename, lineno, file=None, line=None):
- log.append(locals())
- try:
- warnings.showwarning = showwarning
- yield log
- finally:
- warnings.filters = filters
- warnings.showwarning = old_showwarning
-
-
-@contextmanager
-def catch_stderr():
- """Catch stderr in a StringIO"""
- old_stderr = sys.stderr
- sys.stderr = rv = StringIO()
- try:
- yield rv
- finally:
- sys.stderr = old_stderr
-
-
-def emits_module_deprecation_warning(f):
- def new_f(*args, **kwargs):
- with catch_warnings() as log:
- f(*args, **kwargs)
- assert log, 'expected deprecation warning'
- for entry in log:
- assert 'Modules are deprecated' in str(entry['message'])
- return update_wrapper(new_f, f)
-
-
-class ContextTestCase(unittest.TestCase):
-
- def test_context_binding(self):
- app = flask.Flask(__name__)
- @app.route('/')
- def index():
- return 'Hello %s!' % flask.request.args['name']
- @app.route('/meh')
- def meh():
- return flask.request.url
-
- with app.test_request_context('/?name=World'):
- assert index() == 'Hello World!'
- with app.test_request_context('/meh'):
- assert meh() == 'http://localhost/meh'
- assert flask._request_ctx_stack.top is None
-
- def test_context_test(self):
- app = flask.Flask(__name__)
- assert not flask.request
- assert not flask.has_request_context()
- ctx = app.test_request_context()
- ctx.push()
- try:
- assert flask.request
- assert flask.has_request_context()
- finally:
- ctx.pop()
-
- def test_manual_context_binding(self):
- app = flask.Flask(__name__)
- @app.route('/')
- def index():
- return 'Hello %s!' % flask.request.args['name']
-
- ctx = app.test_request_context('/?name=World')
- ctx.push()
- assert index() == 'Hello World!'
- ctx.pop()
- try:
- index()
- except RuntimeError:
- pass
- else:
- assert 0, 'expected runtime error'
-
- def test_test_client_context_binding(self):
- app = flask.Flask(__name__)
- @app.route('/')
- def index():
- flask.g.value = 42
- return 'Hello World!'
-
- @app.route('/other')
- def other():
- 1/0
-
- with app.test_client() as c:
- resp = c.get('/')
- assert flask.g.value == 42
- assert resp.data == 'Hello World!'
- assert resp.status_code == 200
-
- resp = c.get('/other')
- assert not hasattr(flask.g, 'value')
- assert 'Internal Server Error' in resp.data
- assert resp.status_code == 500
- flask.g.value = 23
-
- try:
- flask.g.value
- except (AttributeError, RuntimeError):
- pass
- else:
- raise AssertionError('some kind of exception expected')
-
-
-class BasicFunctionalityTestCase(unittest.TestCase):
-
- def test_options_work(self):
- app = flask.Flask(__name__)
- @app.route('/', methods=['GET', 'POST'])
- def index():
- return 'Hello World'
- rv = app.test_client().open('/', method='OPTIONS')
- assert sorted(rv.allow) == ['GET', 'HEAD', 'OPTIONS', 'POST']
- assert rv.data == ''
-
- def test_options_on_multiple_rules(self):
- app = flask.Flask(__name__)
- @app.route('/', methods=['GET', 'POST'])
- def index():
- return 'Hello World'
- @app.route('/', methods=['PUT'])
- def index_put():
- return 'Aha!'
- rv = app.test_client().open('/', method='OPTIONS')
- assert sorted(rv.allow) == ['GET', 'HEAD', 'OPTIONS', 'POST', 'PUT']
-
- def test_options_handling_disabled(self):
- app = flask.Flask(__name__)
- def index():
- return 'Hello World!'
- index.provide_automatic_options = False
- app.route('/')(index)
- rv = app.test_client().open('/', method='OPTIONS')
- assert rv.status_code == 405
-
- app = flask.Flask(__name__)
- def index2():
- return 'Hello World!'
- index2.provide_automatic_options = True
- app.route('/', methods=['OPTIONS'])(index2)
- rv = app.test_client().open('/', method='OPTIONS')
- assert sorted(rv.allow) == ['OPTIONS']
-
- def test_request_dispatching(self):
- app = flask.Flask(__name__)
- @app.route('/')
- def index():
- return flask.request.method
- @app.route('/more', methods=['GET', 'POST'])
- def more():
- return flask.request.method
-
- c = app.test_client()
- assert c.get('/').data == 'GET'
- rv = c.post('/')
- assert rv.status_code == 405
- assert sorted(rv.allow) == ['GET', 'HEAD', 'OPTIONS']
- rv = c.head('/')
- assert rv.status_code == 200
- assert not rv.data # head truncates
- assert c.post('/more').data == 'POST'
- assert c.get('/more').data == 'GET'
- rv = c.delete('/more')
- assert rv.status_code == 405
- assert sorted(rv.allow) == ['GET', 'HEAD', 'OPTIONS', 'POST']
-
- def test_url_mapping(self):
- app = flask.Flask(__name__)
- def index():
- return flask.request.method
- def more():
- return flask.request.method
-
- app.add_url_rule('/', 'index', index)
- app.add_url_rule('/more', 'more', more, methods=['GET', 'POST'])
-
- c = app.test_client()
- assert c.get('/').data == 'GET'
- rv = c.post('/')
- assert rv.status_code == 405
- assert sorted(rv.allow) == ['GET', 'HEAD', 'OPTIONS']
- rv = c.head('/')
- assert rv.status_code == 200
- assert not rv.data # head truncates
- assert c.post('/more').data == 'POST'
- assert c.get('/more').data == 'GET'
- rv = c.delete('/more')
- assert rv.status_code == 405
- assert sorted(rv.allow) == ['GET', 'HEAD', 'OPTIONS', 'POST']
-
- def test_werkzeug_routing(self):
- from werkzeug.routing import Submount, Rule
- app = flask.Flask(__name__)
- app.url_map.add(Submount('/foo', [
- Rule('/bar', endpoint='bar'),
- Rule('/', endpoint='index')
- ]))
- def bar():
- return 'bar'
- def index():
- return 'index'
- app.view_functions['bar'] = bar
- app.view_functions['index'] = index
-
- c = app.test_client()
- assert c.get('/foo/').data == 'index'
- assert c.get('/foo/bar').data == 'bar'
-
- def test_endpoint_decorator(self):
- from werkzeug.routing import Submount, Rule
- app = flask.Flask(__name__)
- app.url_map.add(Submount('/foo', [
- Rule('/bar', endpoint='bar'),
- Rule('/', endpoint='index')
- ]))
-
- @app.endpoint('bar')
- def bar():
- return 'bar'
-
- @app.endpoint('index')
- def index():
- return 'index'
-
- c = app.test_client()
- assert c.get('/foo/').data == 'index'
- assert c.get('/foo/bar').data == 'bar'
-
- def test_session(self):
- app = flask.Flask(__name__)
- app.secret_key = 'testkey'
- @app.route('/set', methods=['POST'])
- def set():
- flask.session['value'] = flask.request.form['value']
- return 'value set'
- @app.route('/get')
- def get():
- return flask.session['value']
-
- c = app.test_client()
- assert c.post('/set', data={'value': '42'}).data == 'value set'
- assert c.get('/get').data == '42'
-
- def test_session_using_server_name(self):
- app = flask.Flask(__name__)
- app.config.update(
- SECRET_KEY='foo',
- SERVER_NAME='example.com'
- )
- @app.route('/')
- def index():
- flask.session['testing'] = 42
- return 'Hello World'
- rv = app.test_client().get('/', 'http://example.com/')
- assert 'domain=.example.com' in rv.headers['set-cookie'].lower()
- assert 'httponly' in rv.headers['set-cookie'].lower()
-
- def test_session_using_server_name_and_port(self):
- app = flask.Flask(__name__)
- app.config.update(
- SECRET_KEY='foo',
- SERVER_NAME='example.com:8080'
- )
- @app.route('/')
- def index():
- flask.session['testing'] = 42
- return 'Hello World'
- rv = app.test_client().get('/', 'http://example.com:8080/')
- assert 'domain=.example.com' in rv.headers['set-cookie'].lower()
- assert 'httponly' in rv.headers['set-cookie'].lower()
-
- def test_missing_session(self):
- app = flask.Flask(__name__)
- def expect_exception(f, *args, **kwargs):
- try:
- f(*args, **kwargs)
- except RuntimeError, e:
- assert e.args and 'session is unavailable' in e.args[0]
- else:
- assert False, 'expected exception'
- with app.test_request_context():
- assert flask.session.get('missing_key') is None
- expect_exception(flask.session.__setitem__, 'foo', 42)
- expect_exception(flask.session.pop, 'foo')
-
- def test_session_expiration(self):
- permanent = True
- app = flask.Flask(__name__)
- app.secret_key = 'testkey'
- @app.route('/')
- def index():
- flask.session['test'] = 42
- flask.session.permanent = permanent
- return ''
-
- @app.route('/test')
- def test():
- return unicode(flask.session.permanent)
-
- client = app.test_client()
- rv = client.get('/')
- assert 'set-cookie' in rv.headers
- match = re.search(r'\bexpires=([^;]+)', rv.headers['set-cookie'])
- expires = parse_date(match.group())
- expected = datetime.utcnow() + app.permanent_session_lifetime
- assert expires.year == expected.year
- assert expires.month == expected.month
- assert expires.day == expected.day
-
- rv = client.get('/test')
- assert rv.data == 'True'
-
- permanent = False
- rv = app.test_client().get('/')
- assert 'set-cookie' in rv.headers
- match = re.search(r'\bexpires=([^;]+)', rv.headers['set-cookie'])
- assert match is None
-
- def test_flashes(self):
- app = flask.Flask(__name__)
- app.secret_key = 'testkey'
-
- with app.test_request_context():
- assert not flask.session.modified
- flask.flash('Zap')
- flask.session.modified = False
- flask.flash('Zip')
- assert flask.session.modified
- assert list(flask.get_flashed_messages()) == ['Zap', 'Zip']
-
- def test_extended_flashing(self):
- app = flask.Flask(__name__)
- app.secret_key = 'testkey'
-
- @app.route('/')
- def index():
- flask.flash(u'Hello World')
- flask.flash(u'Hello World', 'error')
- flask.flash(flask.Markup(u'Testing'), 'warning')
- return ''
-
- @app.route('/test')
- def test():
- messages = flask.get_flashed_messages(with_categories=True)
- assert len(messages) == 3
- assert messages[0] == ('message', u'Hello World')
- assert messages[1] == ('error', u'Hello World')
- assert messages[2] == ('warning', flask.Markup(u'Testing'))
- return ''
- messages = flask.get_flashed_messages()
- assert len(messages) == 3
- assert messages[0] == u'Hello World'
- assert messages[1] == u'Hello World'
- assert messages[2] == flask.Markup(u'Testing')
-
- c = app.test_client()
- c.get('/')
- c.get('/test')
-
- def test_request_processing(self):
- app = flask.Flask(__name__)
- evts = []
- @app.before_request
- def before_request():
- evts.append('before')
- @app.after_request
- def after_request(response):
- response.data += '|after'
- evts.append('after')
- return response
- @app.route('/')
- def index():
- assert 'before' in evts
- assert 'after' not in evts
- return 'request'
- assert 'after' not in evts
- rv = app.test_client().get('/').data
- assert 'after' in evts
- assert rv == 'request|after'
-
- def test_teardown_request_handler(self):
- called = []
- app = flask.Flask(__name__)
- @app.teardown_request
- def teardown_request(exc):
- called.append(True)
- return "Ignored"
- @app.route('/')
- def root():
- return "Response"
- rv = app.test_client().get('/')
- assert rv.status_code == 200
- assert 'Response' in rv.data
- assert len(called) == 1
-
- def test_teardown_request_handler_debug_mode(self):
- called = []
- app = flask.Flask(__name__)
- app.testing = True
- @app.teardown_request
- def teardown_request(exc):
- called.append(True)
- return "Ignored"
- @app.route('/')
- def root():
- return "Response"
- rv = app.test_client().get('/')
- assert rv.status_code == 200
- assert 'Response' in rv.data
- assert len(called) == 1
-
- def test_teardown_request_handler_error(self):
- called = []
- app = flask.Flask(__name__)
- @app.teardown_request
- def teardown_request1(exc):
- assert type(exc) == ZeroDivisionError
- called.append(True)
- # This raises a new error and blows away sys.exc_info(), so we can
- # test that all teardown_requests get passed the same original
- # exception.
- try:
- raise TypeError
- except:
- pass
- @app.teardown_request
- def teardown_request2(exc):
- assert type(exc) == ZeroDivisionError
- called.append(True)
- # This raises a new error and blows away sys.exc_info(), so we can
- # test that all teardown_requests get passed the same original
- # exception.
- try:
- raise TypeError
- except:
- pass
- @app.route('/')
- def fails():
- 1/0
- rv = app.test_client().get('/')
- assert rv.status_code == 500
- assert 'Internal Server Error' in rv.data
- assert len(called) == 2
-
- def test_before_after_request_order(self):
- called = []
- app = flask.Flask(__name__)
- @app.before_request
- def before1():
- called.append(1)
- @app.before_request
- def before2():
- called.append(2)
- @app.after_request
- def after1(response):
- called.append(4)
- return response
- @app.after_request
- def after2(response):
- called.append(3)
- return response
- @app.teardown_request
- def finish1(exc):
- called.append(6)
- @app.teardown_request
- def finish2(exc):
- called.append(5)
- @app.route('/')
- def index():
- return '42'
- rv = app.test_client().get('/')
- assert rv.data == '42'
- assert called == [1, 2, 3, 4, 5, 6]
-
- def test_error_handling(self):
- app = flask.Flask(__name__)
- @app.errorhandler(404)
- def not_found(e):
- return 'not found', 404
- @app.errorhandler(500)
- def internal_server_error(e):
- return 'internal server error', 500
- @app.route('/')
- def index():
- flask.abort(404)
- @app.route('/error')
- def error():
- 1 // 0
- c = app.test_client()
- rv = c.get('/')
- assert rv.status_code == 404
- assert rv.data == 'not found'
- rv = c.get('/error')
- assert rv.status_code == 500
- assert 'internal server error' == rv.data
-
- def test_before_request_and_routing_errors(self):
- app = flask.Flask(__name__)
- @app.before_request
- def attach_something():
- flask.g.something = 'value'
- @app.errorhandler(404)
- def return_something(error):
- return flask.g.something, 404
- rv = app.test_client().get('/')
- assert rv.status_code == 404
- assert rv.data == 'value'
-
- def test_user_error_handling(self):
- class MyException(Exception):
- pass
-
- app = flask.Flask(__name__)
- @app.errorhandler(MyException)
- def handle_my_exception(e):
- assert isinstance(e, MyException)
- return '42'
- @app.route('/')
- def index():
- raise MyException()
-
- c = app.test_client()
- assert c.get('/').data == '42'
-
- def test_trapping_of_bad_request_key_errors(self):
- app = flask.Flask(__name__)
- app.testing = True
- @app.route('/fail')
- def fail():
- flask.request.form['missing_key']
- c = app.test_client()
- assert c.get('/fail').status_code == 400
-
- app.config['TRAP_BAD_REQUEST_ERRORS'] = True
- c = app.test_client()
- try:
- c.get('/fail')
- except KeyError, e:
- assert isinstance(e, BadRequest)
- else:
- self.fail('Expected exception')
-
- def test_trapping_of_all_http_exceptions(self):
- app = flask.Flask(__name__)
- app.testing = True
- app.config['TRAP_HTTP_EXCEPTIONS'] = True
- @app.route('/fail')
- def fail():
- flask.abort(404)
-
- c = app.test_client()
- try:
- c.get('/fail')
- except NotFound, e:
- pass
- else:
- self.fail('Expected exception')
-
- def test_enctype_debug_helper(self):
- from flask.debughelpers import DebugFilesKeyError
- app = flask.Flask(__name__)
- app.debug = True
- @app.route('/fail', methods=['POST'])
- def index():
- return flask.request.files['foo'].filename
-
- # with statement is important because we leave an exception on the
- # stack otherwise and we want to ensure that this is not the case
- # to not negatively affect other tests.
- with app.test_client() as c:
- try:
- c.post('/fail', data={'foo': 'index.txt'})
- except DebugFilesKeyError, e:
- assert 'no file contents were transmitted' in str(e)
- assert 'This was submitted: "index.txt"' in str(e)
- else:
- self.fail('Expected exception')
-
- def test_teardown_on_pop(self):
- buffer = []
- app = flask.Flask(__name__)
- @app.teardown_request
- def end_of_request(exception):
- buffer.append(exception)
-
- ctx = app.test_request_context()
- ctx.push()
- assert buffer == []
- ctx.pop()
- assert buffer == [None]
-
- def test_response_creation(self):
- app = flask.Flask(__name__)
- @app.route('/unicode')
- def from_unicode():
- return u'Hällo Wörld'
- @app.route('/string')
- def from_string():
- return u'Hällo Wörld'.encode('utf-8')
- @app.route('/args')
- def from_tuple():
- return 'Meh', 400, {'X-Foo': 'Testing'}, 'text/plain'
- c = app.test_client()
- assert c.get('/unicode').data == u'Hällo Wörld'.encode('utf-8')
- assert c.get('/string').data == u'Hällo Wörld'.encode('utf-8')
- rv = c.get('/args')
- assert rv.data == 'Meh'
- assert rv.headers['X-Foo'] == 'Testing'
- assert rv.status_code == 400
- assert rv.mimetype == 'text/plain'
-
- def test_make_response(self):
- app = flask.Flask(__name__)
- with app.test_request_context():
- rv = flask.make_response()
- assert rv.status_code == 200
- assert rv.data == ''
- assert rv.mimetype == 'text/html'
-
- rv = flask.make_response('Awesome')
- assert rv.status_code == 200
- assert rv.data == 'Awesome'
- assert rv.mimetype == 'text/html'
-
- rv = flask.make_response('W00t', 404)
- assert rv.status_code == 404
- assert rv.data == 'W00t'
- assert rv.mimetype == 'text/html'
-
- def test_url_generation(self):
- app = flask.Flask(__name__)
- @app.route('/hello/', methods=['POST'])
- def hello():
- pass
- with app.test_request_context():
- assert flask.url_for('hello', name='test x') == '/hello/test%20x'
- assert flask.url_for('hello', name='test x', _external=True) \
- == 'http://localhost/hello/test%20x'
-
- def test_custom_converters(self):
- from werkzeug.routing import BaseConverter
- class ListConverter(BaseConverter):
- def to_python(self, value):
- return value.split(',')
- def to_url(self, value):
- base_to_url = super(ListConverter, self).to_url
- return ','.join(base_to_url(x) for x in value)
- app = flask.Flask(__name__)
- app.url_map.converters['list'] = ListConverter
- @app.route('/')
- def index(args):
- return '|'.join(args)
- c = app.test_client()
- assert c.get('/1,2,3').data == '1|2|3'
-
- def test_static_files(self):
- app = flask.Flask(__name__)
- rv = app.test_client().get('/static/index.html')
- assert rv.status_code == 200
- assert rv.data.strip() == 'Hello World!
'
- with app.test_request_context():
- assert flask.url_for('static', filename='index.html') \
- == '/static/index.html'
-
- def test_none_response(self):
- app = flask.Flask(__name__)
- @app.route('/')
- def test():
- return None
- try:
- app.test_client().get('/')
- except ValueError, e:
- assert str(e) == 'View function did not return a response'
- pass
- else:
- assert "Expected ValueError"
-
- def test_request_locals(self):
- self.assertEqual(repr(flask.g), '')
- self.assertFalse(flask.g)
-
- def test_proper_test_request_context(self):
- app = flask.Flask(__name__)
- app.config.update(
- SERVER_NAME='localhost.localdomain:5000'
- )
-
- @app.route('/')
- def index():
- return None
-
- @app.route('/', subdomain='foo')
- def sub():
- return None
-
- with app.test_request_context('/'):
- assert flask.url_for('index', _external=True) == 'http://localhost.localdomain:5000/'
-
- with app.test_request_context('/'):
- assert flask.url_for('sub', _external=True) == 'http://foo.localhost.localdomain:5000/'
-
- try:
- with app.test_request_context('/', environ_overrides={'HTTP_HOST': 'localhost'}):
- pass
- except Exception, e:
- assert isinstance(e, ValueError)
- assert str(e) == "the server name provided " + \
- "('localhost.localdomain:5000') does not match the " + \
- "server name from the WSGI environment ('localhost')", str(e)
-
- try:
- app.config.update(SERVER_NAME='localhost')
- with app.test_request_context('/', environ_overrides={'SERVER_NAME': 'localhost'}):
- pass
- except ValueError, e:
- raise ValueError(
- "No ValueError exception should have been raised \"%s\"" % e
- )
-
- try:
- app.config.update(SERVER_NAME='localhost:80')
- with app.test_request_context('/', environ_overrides={'SERVER_NAME': 'localhost:80'}):
- pass
- except ValueError, e:
- raise ValueError(
- "No ValueError exception should have been raised \"%s\"" % e
- )
-
- def test_test_app_proper_environ(self):
- app = flask.Flask(__name__)
- app.config.update(
- SERVER_NAME='localhost.localdomain:5000'
- )
- @app.route('/')
- def index():
- return 'Foo'
-
- @app.route('/', subdomain='foo')
- def subdomain():
- return 'Foo SubDomain'
-
- try:
- rv = app.test_client().get('/')
- assert rv.data == 'Foo'
- except ValueError, e:
- raise ValueError(
- "No ValueError exception should have been raised \"%s\"" % e
- )
-
- try:
- rv = app.test_client().get('/', 'http://localhost.localdomain:5000')
- assert rv.data == 'Foo'
- except ValueError, e:
- raise ValueError(
- "No ValueError exception should have been raised \"%s\"" % e
- )
-
- try:
- rv = app.test_client().get('/', 'https://localhost.localdomain:5000')
- assert rv.data == 'Foo'
- except ValueError, e:
- raise ValueError(
- "No ValueError exception should have been raised \"%s\"" % e
- )
-
- try:
- app.config.update(SERVER_NAME='localhost.localdomain')
- rv = app.test_client().get('/', 'https://localhost.localdomain')
- assert rv.data == 'Foo'
- except ValueError, e:
- raise ValueError(
- "No ValueError exception should have been raised \"%s\"" % e
- )
-
- try:
- app.config.update(SERVER_NAME='localhost.localdomain:443')
- rv = app.test_client().get('/', 'https://localhost.localdomain')
- assert rv.data == 'Foo'
- except ValueError, e:
- assert str(e) == "the server name provided " + \
- "('localhost.localdomain:443') does not match the " + \
- "server name from the WSGI environment ('localhost.localdomain')", str(e)
-
- try:
- app.config.update(SERVER_NAME='localhost.localdomain')
- app.test_client().get('/', 'http://foo.localhost')
- except ValueError, e:
- assert str(e) == "the server name provided " + \
- "('localhost.localdomain') does not match the " + \
- "server name from the WSGI environment ('foo.localhost')", str(e)
-
- try:
- rv = app.test_client().get('/', 'http://foo.localhost.localdomain')
- assert rv.data == 'Foo SubDomain'
- except ValueError, e:
- raise ValueError(
- "No ValueError exception should have been raised \"%s\"" % e
- )
-
- def test_exception_propagation(self):
- def apprunner(configkey):
- app = flask.Flask(__name__)
- @app.route('/')
- def index():
- 1/0
- c = app.test_client()
- if config_key is not None:
- app.config[config_key] = True
- try:
- resp = c.get('/')
- except Exception:
- pass
- else:
- self.fail('expected exception')
- else:
- assert c.get('/').status_code == 500
-
- # we have to run this test in an isolated thread because if the
- # debug flag is set to true and an exception happens the context is
- # not torn down. This causes other tests that run after this fail
- # when they expect no exception on the stack.
- for config_key in 'TESTING', 'PROPAGATE_EXCEPTIONS', 'DEBUG', None:
- t = Thread(target=apprunner, args=(config_key,))
- t.start()
- t.join()
-
- def test_max_content_length(self):
- app = flask.Flask(__name__)
- app.config['MAX_CONTENT_LENGTH'] = 64
- @app.before_request
- def always_first():
- flask.request.form['myfile']
- assert False
- @app.route('/accept', methods=['POST'])
- def accept_file():
- flask.request.form['myfile']
- assert False
- @app.errorhandler(413)
- def catcher(error):
- return '42'
-
- c = app.test_client()
- rv = c.post('/accept', data={'myfile': 'foo' * 100})
- assert rv.data == '42'
-
- def test_url_processors(self):
- app = flask.Flask(__name__)
-
- @app.url_defaults
- def add_language_code(endpoint, values):
- if flask.g.lang_code is not None and \
- app.url_map.is_endpoint_expecting(endpoint, 'lang_code'):
- values.setdefault('lang_code', flask.g.lang_code)
-
- @app.url_value_preprocessor
- def pull_lang_code(endpoint, values):
- flask.g.lang_code = values.pop('lang_code', None)
-
- @app.route('//')
- def index():
- return flask.url_for('about')
-
- @app.route('//about')
- def about():
- return flask.url_for('something_else')
-
- @app.route('/foo')
- def something_else():
- return flask.url_for('about', lang_code='en')
-
- c = app.test_client()
-
- self.assertEqual(c.get('/de/').data, '/de/about')
- self.assertEqual(c.get('/de/about').data, '/foo')
- self.assertEqual(c.get('/foo').data, '/en/about')
-
- def test_debug_mode_complains_after_first_request(self):
- app = flask.Flask(__name__)
- app.debug = True
- @app.route('/')
- def index():
- return 'Awesome'
- self.assert_(not app.got_first_request)
- self.assertEqual(app.test_client().get('/').data, 'Awesome')
- try:
- @app.route('/foo')
- def broken():
- return 'Meh'
- except AssertionError, e:
- self.assert_('A setup function was called' in str(e))
- else:
- self.fail('Expected exception')
-
- app.debug = False
- @app.route('/foo')
- def working():
- return 'Meh'
- self.assertEqual(app.test_client().get('/foo').data, 'Meh')
- self.assert_(app.got_first_request)
-
- def test_before_first_request_functions(self):
- got = []
- app = flask.Flask(__name__)
- @app.before_first_request
- def foo():
- got.append(42)
- c = app.test_client()
- c.get('/')
- self.assertEqual(got, [42])
- c.get('/')
- self.assertEqual(got, [42])
- self.assert_(app.got_first_request)
-
- def test_routing_redirect_debugging(self):
- app = flask.Flask(__name__)
- app.debug = True
- @app.route('/foo/', methods=['GET', 'POST'])
- def foo():
- return 'success'
- with app.test_client() as c:
- try:
- c.post('/foo', data={})
- except AssertionError, e:
- self.assert_('http://localhost/foo/' in str(e))
- self.assert_('Make sure to directly send your POST-request '
- 'to this URL' in str(e))
- else:
- self.fail('Expected exception')
-
- rv = c.get('/foo', data={}, follow_redirects=True)
- self.assertEqual(rv.data, 'success')
-
- app.debug = False
- with app.test_client() as c:
- rv = c.post('/foo', data={}, follow_redirects=True)
- self.assertEqual(rv.data, 'success')
-
- def test_route_decorator_custom_endpoint(self):
- app = flask.Flask(__name__)
- app.debug = True
-
- @app.route('/foo/')
- def foo():
- return flask.request.endpoint
-
- @app.route('/bar/', endpoint='bar')
- def for_bar():
- return flask.request.endpoint
-
- @app.route('/bar/123', endpoint='123')
- def for_bar_foo():
- return flask.request.endpoint
-
- with app.test_request_context():
- assert flask.url_for('foo') == '/foo/'
- assert flask.url_for('bar') == '/bar/'
- assert flask.url_for('123') == '/bar/123'
-
- c = app.test_client()
- self.assertEqual(c.get('/foo/').data, 'foo')
- self.assertEqual(c.get('/bar/').data, 'bar')
- self.assertEqual(c.get('/bar/123').data, '123')
-
-
-class InstanceTestCase(unittest.TestCase):
-
- def test_explicit_instance_paths(self):
- here = os.path.abspath(os.path.dirname(__file__))
- try:
- flask.Flask(__name__, instance_path='instance')
- except ValueError, e:
- self.assert_('must be absolute' in str(e))
- else:
- self.fail('Expected value error')
-
- app = flask.Flask(__name__, instance_path=here)
- self.assertEqual(app.instance_path, here)
-
- def test_uninstalled_module_paths(self):
- here = os.path.abspath(os.path.dirname(__file__))
- app = flask.Flask(__name__)
- self.assertEqual(app.instance_path, os.path.join(here, 'instance'))
-
- def test_uninstalled_package_paths(self):
- from blueprintapp import app
- here = os.path.abspath(os.path.dirname(__file__))
- self.assertEqual(app.instance_path, os.path.join(here, 'instance'))
-
- def test_installed_module_paths(self):
- import types
- expected_prefix = os.path.abspath('foo')
- mod = types.ModuleType('myapp')
- mod.__file__ = os.path.join(expected_prefix, 'lib', 'python2.5',
- 'site-packages', 'myapp.py')
- sys.modules['myapp'] = mod
- try:
- mod.app = flask.Flask(mod.__name__)
- self.assertEqual(mod.app.instance_path,
- os.path.join(expected_prefix, 'var',
- 'myapp-instance'))
- finally:
- sys.modules['myapp'] = None
-
- def test_installed_package_paths(self):
- import types
- expected_prefix = os.path.abspath('foo')
- package_path = os.path.join(expected_prefix, 'lib', 'python2.5',
- 'site-packages', 'myapp')
- mod = types.ModuleType('myapp')
- mod.__path__ = [package_path]
- mod.__file__ = os.path.join(package_path, '__init__.py')
- sys.modules['myapp'] = mod
- try:
- mod.app = flask.Flask(mod.__name__)
- self.assertEqual(mod.app.instance_path,
- os.path.join(expected_prefix, 'var',
- 'myapp-instance'))
- finally:
- sys.modules['myapp'] = None
-
- def test_prefix_installed_paths(self):
- import types
- expected_prefix = os.path.abspath(sys.prefix)
- package_path = os.path.join(expected_prefix, 'lib', 'python2.5',
- 'site-packages', 'myapp')
- mod = types.ModuleType('myapp')
- mod.__path__ = [package_path]
- mod.__file__ = os.path.join(package_path, '__init__.py')
- sys.modules['myapp'] = mod
- try:
- mod.app = flask.Flask(mod.__name__)
- self.assertEqual(mod.app.instance_path,
- os.path.join(expected_prefix, 'var',
- 'myapp-instance'))
- finally:
- sys.modules['myapp'] = None
-
- def test_egg_installed_paths(self):
- import types
- expected_prefix = os.path.abspath(sys.prefix)
- package_path = os.path.join(expected_prefix, 'lib', 'python2.5',
- 'site-packages', 'MyApp.egg', 'myapp')
- mod = types.ModuleType('myapp')
- mod.__path__ = [package_path]
- mod.__file__ = os.path.join(package_path, '__init__.py')
- sys.modules['myapp'] = mod
- try:
- mod.app = flask.Flask(mod.__name__)
- self.assertEqual(mod.app.instance_path,
- os.path.join(expected_prefix, 'var',
- 'myapp-instance'))
- finally:
- sys.modules['myapp'] = None
-
-
-class JSONTestCase(unittest.TestCase):
-
- def test_json_bad_requests(self):
- app = flask.Flask(__name__)
- @app.route('/json', methods=['POST'])
- def return_json():
- return unicode(flask.request.json)
- c = app.test_client()
- rv = c.post('/json', data='malformed', content_type='application/json')
- self.assertEqual(rv.status_code, 400)
-
- def test_json_body_encoding(self):
- app = flask.Flask(__name__)
- app.testing = True
- @app.route('/')
- def index():
- return flask.request.json
-
- c = app.test_client()
- resp = c.get('/', data=u'"Hällo Wörld"'.encode('iso-8859-15'),
- content_type='application/json; charset=iso-8859-15')
- assert resp.data == u'Hällo Wörld'.encode('utf-8')
-
- def test_jsonify(self):
- d = dict(a=23, b=42, c=[1, 2, 3])
- app = flask.Flask(__name__)
- @app.route('/kw')
- def return_kwargs():
- return flask.jsonify(**d)
- @app.route('/dict')
- def return_dict():
- return flask.jsonify(d)
- c = app.test_client()
- for url in '/kw', '/dict':
- rv = c.get(url)
- assert rv.mimetype == 'application/json'
- assert flask.json.loads(rv.data) == d
-
- def test_json_attr(self):
- app = flask.Flask(__name__)
- @app.route('/add', methods=['POST'])
- def add():
- return unicode(flask.request.json['a'] + flask.request.json['b'])
- c = app.test_client()
- rv = c.post('/add', data=flask.json.dumps({'a': 1, 'b': 2}),
- content_type='application/json')
- assert rv.data == '3'
-
- def test_template_escaping(self):
- app = flask.Flask(__name__)
- render = flask.render_template_string
- with app.test_request_context():
- rv = render('{{ ""|tojson|safe }}')
- assert rv == '"<\\/script>"'
- rv = render('{{ "<\0/script>"|tojson|safe }}')
- assert rv == '"<\\u0000\\/script>"'
-
- def test_modified_url_encoding(self):
- class ModifiedRequest(flask.Request):
- url_charset = 'euc-kr'
- app = flask.Flask(__name__)
- app.request_class = ModifiedRequest
- app.url_map.charset = 'euc-kr'
-
- @app.route('/')
- def index():
- return flask.request.args['foo']
-
- rv = app.test_client().get(u'/?foo=정상처리'.encode('euc-kr'))
- assert rv.status_code == 200
- assert rv.data == u'정상처리'.encode('utf-8')
-
- if not has_encoding('euc-kr'):
- test_modified_url_encoding = None
-
-
-class TemplatingTestCase(unittest.TestCase):
-
- def test_context_processing(self):
- app = flask.Flask(__name__)
- @app.context_processor
- def context_processor():
- return {'injected_value': 42}
- @app.route('/')
- def index():
- return flask.render_template('context_template.html', value=23)
- rv = app.test_client().get('/')
- assert rv.data == '23|42'
-
- def test_original_win(self):
- app = flask.Flask(__name__)
- @app.route('/')
- def index():
- return flask.render_template_string('{{ config }}', config=42)
- rv = app.test_client().get('/')
- assert rv.data == '42'
-
- def test_standard_context(self):
- app = flask.Flask(__name__)
- app.secret_key = 'development key'
- @app.route('/')
- def index():
- flask.g.foo = 23
- flask.session['test'] = 'aha'
- return flask.render_template_string('''
- {{ request.args.foo }}
- {{ g.foo }}
- {{ config.DEBUG }}
- {{ session.test }}
- ''')
- rv = app.test_client().get('/?foo=42')
- assert rv.data.split() == ['42', '23', 'False', 'aha']
-
- def test_escaping(self):
- text = '
Hello World!'
- app = flask.Flask(__name__)
- @app.route('/')
- def index():
- return flask.render_template('escaping_template.html', text=text,
- html=flask.Markup(text))
- lines = app.test_client().get('/').data.splitlines()
- assert lines == [
- '<p>Hello World!',
- '
Hello World!',
- '
Hello World!',
- '
Hello World!',
- '<p>Hello World!',
- '
Hello World!'
- ]
-
- def test_no_escaping(self):
- app = flask.Flask(__name__)
- with app.test_request_context():
- assert flask.render_template_string('{{ foo }}',
- foo='') == ''
- assert flask.render_template('mail.txt', foo='') \
- == ' Mail'
-
- def test_macros(self):
- app = flask.Flask(__name__)
- with app.test_request_context():
- macro = flask.get_template_attribute('_macro.html', 'hello')
- assert macro('World') == 'Hello World!'
-
- def test_template_filter(self):
- app = flask.Flask(__name__)
- @app.template_filter()
- def my_reverse(s):
- return s[::-1]
- assert 'my_reverse' in app.jinja_env.filters.keys()
- assert app.jinja_env.filters['my_reverse'] == my_reverse
- assert app.jinja_env.filters['my_reverse']('abcd') == 'dcba'
-
- def test_template_filter_with_name(self):
- app = flask.Flask(__name__)
- @app.template_filter('strrev')
- def my_reverse(s):
- return s[::-1]
- assert 'strrev' in app.jinja_env.filters.keys()
- assert app.jinja_env.filters['strrev'] == my_reverse
- assert app.jinja_env.filters['strrev']('abcd') == 'dcba'
-
- def test_template_filter_with_template(self):
- app = flask.Flask(__name__)
- @app.template_filter()
- def super_reverse(s):
- return s[::-1]
- @app.route('/')
- def index():
- return flask.render_template('template_filter.html', value='abcd')
- rv = app.test_client().get('/')
- assert rv.data == 'dcba'
-
- def test_template_filter_with_name_and_template(self):
- app = flask.Flask(__name__)
- @app.template_filter('super_reverse')
- def my_reverse(s):
- return s[::-1]
- @app.route('/')
- def index():
- return flask.render_template('template_filter.html', value='abcd')
- rv = app.test_client().get('/')
- assert rv.data == 'dcba'
-
- def test_custom_template_loader(self):
- class MyFlask(flask.Flask):
- def create_global_jinja_loader(self):
- from jinja2 import DictLoader
- return DictLoader({'index.html': 'Hello Custom World!'})
- app = MyFlask(__name__)
- @app.route('/')
- def index():
- return flask.render_template('index.html')
- c = app.test_client()
- rv = c.get('/')
- assert rv.data == 'Hello Custom World!'
-
-
-class ModuleTestCase(unittest.TestCase):
-
- @emits_module_deprecation_warning
- def test_basic_module(self):
- app = flask.Flask(__name__)
- admin = flask.Module(__name__, 'admin', url_prefix='/admin')
- @admin.route('/')
- def admin_index():
- return 'admin index'
- @admin.route('/login')
- def admin_login():
- return 'admin login'
- @admin.route('/logout')
- def admin_logout():
- return 'admin logout'
- @app.route('/')
- def index():
- return 'the index'
- app.register_module(admin)
- c = app.test_client()
- assert c.get('/').data == 'the index'
- assert c.get('/admin/').data == 'admin index'
- assert c.get('/admin/login').data == 'admin login'
- assert c.get('/admin/logout').data == 'admin logout'
-
- @emits_module_deprecation_warning
- def test_default_endpoint_name(self):
- app = flask.Flask(__name__)
- mod = flask.Module(__name__, 'frontend')
- def index():
- return 'Awesome'
- mod.add_url_rule('/', view_func=index)
- app.register_module(mod)
- rv = app.test_client().get('/')
- assert rv.data == 'Awesome'
- with app.test_request_context():
- assert flask.url_for('frontend.index') == '/'
-
- @emits_module_deprecation_warning
- def test_request_processing(self):
- catched = []
- app = flask.Flask(__name__)
- admin = flask.Module(__name__, 'admin', url_prefix='/admin')
- @admin.before_request
- def before_admin_request():
- catched.append('before-admin')
- @admin.after_request
- def after_admin_request(response):
- catched.append('after-admin')
- return response
- @admin.route('/')
- def admin_index():
- return 'the admin'
- @app.before_request
- def before_request():
- catched.append('before-app')
- @app.after_request
- def after_request(response):
- catched.append('after-app')
- return response
- @app.route('/')
- def index():
- return 'the index'
- app.register_module(admin)
- c = app.test_client()
-
- assert c.get('/').data == 'the index'
- assert catched == ['before-app', 'after-app']
- del catched[:]
-
- assert c.get('/admin/').data == 'the admin'
- assert catched == ['before-app', 'before-admin',
- 'after-admin', 'after-app']
-
- @emits_module_deprecation_warning
- def test_context_processors(self):
- app = flask.Flask(__name__)
- admin = flask.Module(__name__, 'admin', url_prefix='/admin')
- @app.context_processor
- def inject_all_regualr():
- return {'a': 1}
- @admin.context_processor
- def inject_admin():
- return {'b': 2}
- @admin.app_context_processor
- def inject_all_module():
- return {'c': 3}
- @app.route('/')
- def index():
- return flask.render_template_string('{{ a }}{{ b }}{{ c }}')
- @admin.route('/')
- def admin_index():
- return flask.render_template_string('{{ a }}{{ b }}{{ c }}')
- app.register_module(admin)
- c = app.test_client()
- assert c.get('/').data == '13'
- assert c.get('/admin/').data == '123'
-
- @emits_module_deprecation_warning
- def test_late_binding(self):
- app = flask.Flask(__name__)
- admin = flask.Module(__name__, 'admin')
- @admin.route('/')
- def index():
- return '42'
- app.register_module(admin, url_prefix='/admin')
- assert app.test_client().get('/admin/').data == '42'
-
- @emits_module_deprecation_warning
- def test_error_handling(self):
- app = flask.Flask(__name__)
- admin = flask.Module(__name__, 'admin')
- @admin.app_errorhandler(404)
- def not_found(e):
- return 'not found', 404
- @admin.app_errorhandler(500)
- def internal_server_error(e):
- return 'internal server error', 500
- @admin.route('/')
- def index():
- flask.abort(404)
- @admin.route('/error')
- def error():
- 1 // 0
- app.register_module(admin)
- c = app.test_client()
- rv = c.get('/')
- assert rv.status_code == 404
- assert rv.data == 'not found'
- rv = c.get('/error')
- assert rv.status_code == 500
- assert 'internal server error' == rv.data
-
- def test_templates_and_static(self):
- app = moduleapp
- app.testing = True
- c = app.test_client()
-
- rv = c.get('/')
- assert rv.data == 'Hello from the Frontend'
- rv = c.get('/admin/')
- assert rv.data == 'Hello from the Admin'
- rv = c.get('/admin/index2')
- assert rv.data == 'Hello from the Admin'
- rv = c.get('/admin/static/test.txt')
- assert rv.data.strip() == 'Admin File'
- rv = c.get('/admin/static/css/test.css')
- assert rv.data.strip() == '/* nested file */'
-
- with app.test_request_context():
- assert flask.url_for('admin.static', filename='test.txt') \
- == '/admin/static/test.txt'
-
- with app.test_request_context():
- try:
- flask.render_template('missing.html')
- except TemplateNotFound, e:
- assert e.name == 'missing.html'
- else:
- assert 0, 'expected exception'
-
- with flask.Flask(__name__).test_request_context():
- assert flask.render_template('nested/nested.txt') == 'I\'m nested'
-
- def test_safe_access(self):
- app = moduleapp
-
- with app.test_request_context():
- f = app.view_functions['admin.static']
-
- try:
- f('/etc/passwd')
- except NotFound:
- pass
- else:
- assert 0, 'expected exception'
- try:
- f('../__init__.py')
- except NotFound:
- pass
- else:
- assert 0, 'expected exception'
-
- # testcase for a security issue that may exist on windows systems
- import os
- import ntpath
- old_path = os.path
- os.path = ntpath
- try:
- try:
- f('..\\__init__.py')
- except NotFound:
- pass
- else:
- assert 0, 'expected exception'
- finally:
- os.path = old_path
-
- @emits_module_deprecation_warning
- def test_endpoint_decorator(self):
- from werkzeug.routing import Submount, Rule
- from flask import Module
-
- app = flask.Flask(__name__)
- app.testing = True
- app.url_map.add(Submount('/foo', [
- Rule('/bar', endpoint='bar'),
- Rule('/', endpoint='index')
- ]))
- module = Module(__name__, __name__)
-
- @module.endpoint('bar')
- def bar():
- return 'bar'
-
- @module.endpoint('index')
- def index():
- return 'index'
-
- app.register_module(module)
-
- c = app.test_client()
- assert c.get('/foo/').data == 'index'
- assert c.get('/foo/bar').data == 'bar'
-
-
-class BlueprintTestCase(unittest.TestCase):
-
- def test_blueprint_specific_error_handling(self):
- frontend = flask.Blueprint('frontend', __name__)
- backend = flask.Blueprint('backend', __name__)
- sideend = flask.Blueprint('sideend', __name__)
-
- @frontend.errorhandler(403)
- def frontend_forbidden(e):
- return 'frontend says no', 403
-
- @frontend.route('/frontend-no')
- def frontend_no():
- flask.abort(403)
-
- @backend.errorhandler(403)
- def backend_forbidden(e):
- return 'backend says no', 403
-
- @backend.route('/backend-no')
- def backend_no():
- flask.abort(403)
-
- @sideend.route('/what-is-a-sideend')
- def sideend_no():
- flask.abort(403)
-
- app = flask.Flask(__name__)
- app.register_blueprint(frontend)
- app.register_blueprint(backend)
- app.register_blueprint(sideend)
-
- @app.errorhandler(403)
- def app_forbidden(e):
- return 'application itself says no', 403
-
- c = app.test_client()
-
- assert c.get('/frontend-no').data == 'frontend says no'
- assert c.get('/backend-no').data == 'backend says no'
- assert c.get('/what-is-a-sideend').data == 'application itself says no'
-
- def test_blueprint_url_definitions(self):
- bp = flask.Blueprint('test', __name__)
-
- @bp.route('/foo', defaults={'baz': 42})
- def foo(bar, baz):
- return '%s/%d' % (bar, baz)
-
- @bp.route('/bar')
- def bar(bar):
- return unicode(bar)
-
- app = flask.Flask(__name__)
- app.register_blueprint(bp, url_prefix='/1', url_defaults={'bar': 23})
- app.register_blueprint(bp, url_prefix='/2', url_defaults={'bar': 19})
-
- c = app.test_client()
- self.assertEqual(c.get('/1/foo').data, u'23/42')
- self.assertEqual(c.get('/2/foo').data, u'19/42')
- self.assertEqual(c.get('/1/bar').data, u'23')
- self.assertEqual(c.get('/2/bar').data, u'19')
-
- def test_blueprint_url_processors(self):
- bp = flask.Blueprint('frontend', __name__, url_prefix='/')
-
- @bp.url_defaults
- def add_language_code(endpoint, values):
- values.setdefault('lang_code', flask.g.lang_code)
-
- @bp.url_value_preprocessor
- def pull_lang_code(endpoint, values):
- flask.g.lang_code = values.pop('lang_code')
-
- @bp.route('/')
- def index():
- return flask.url_for('.about')
-
- @bp.route('/about')
- def about():
- return flask.url_for('.index')
-
- app = flask.Flask(__name__)
- app.register_blueprint(bp)
-
- c = app.test_client()
-
- self.assertEqual(c.get('/de/').data, '/de/about')
- self.assertEqual(c.get('/de/about').data, '/de/')
-
- def test_templates_and_static(self):
- from blueprintapp import app
- c = app.test_client()
-
- rv = c.get('/')
- assert rv.data == 'Hello from the Frontend'
- rv = c.get('/admin/')
- assert rv.data == 'Hello from the Admin'
- rv = c.get('/admin/index2')
- assert rv.data == 'Hello from the Admin'
- rv = c.get('/admin/static/test.txt')
- assert rv.data.strip() == 'Admin File'
- rv = c.get('/admin/static/css/test.css')
- assert rv.data.strip() == '/* nested file */'
-
- with app.test_request_context():
- assert flask.url_for('admin.static', filename='test.txt') \
- == '/admin/static/test.txt'
-
- with app.test_request_context():
- try:
- flask.render_template('missing.html')
- except TemplateNotFound, e:
- assert e.name == 'missing.html'
- else:
- assert 0, 'expected exception'
-
- with flask.Flask(__name__).test_request_context():
- assert flask.render_template('nested/nested.txt') == 'I\'m nested'
-
- def test_templates_list(self):
- from blueprintapp import app
- templates = sorted(app.jinja_env.list_templates())
- self.assertEqual(templates, ['admin/index.html',
- 'frontend/index.html'])
-
- def test_dotted_names(self):
- frontend = flask.Blueprint('myapp.frontend', __name__)
- backend = flask.Blueprint('myapp.backend', __name__)
-
- @frontend.route('/fe')
- def frontend_index():
- return flask.url_for('myapp.backend.backend_index')
-
- @frontend.route('/fe2')
- def frontend_page2():
- return flask.url_for('.frontend_index')
-
- @backend.route('/be')
- def backend_index():
- return flask.url_for('myapp.frontend.frontend_index')
-
- app = flask.Flask(__name__)
- app.register_blueprint(frontend)
- app.register_blueprint(backend)
-
- c = app.test_client()
- self.assertEqual(c.get('/fe').data.strip(), '/be')
- self.assertEqual(c.get('/fe2').data.strip(), '/fe')
- self.assertEqual(c.get('/be').data.strip(), '/fe')
-
- def test_empty_url_defaults(self):
- bp = flask.Blueprint('bp', __name__)
-
- @bp.route('/', defaults={'page': 1})
- @bp.route('/page/')
- def something(page):
- return str(page)
-
- app = flask.Flask(__name__)
- app.register_blueprint(bp)
-
- c = app.test_client()
- self.assertEqual(c.get('/').data, '1')
- self.assertEqual(c.get('/page/2').data, '2')
-
- def test_route_decorator_custom_endpoint(self):
-
- bp = flask.Blueprint('bp', __name__)
-
- @bp.route('/foo')
- def foo():
- return flask.request.endpoint
-
- @bp.route('/bar', endpoint='bar')
- def foo_bar():
- return flask.request.endpoint
-
- @bp.route('/bar/123', endpoint='123')
- def foo_bar_foo():
- return flask.request.endpoint
-
- @bp.route('/bar/foo')
- def bar_foo():
- return flask.request.endpoint
-
- app = flask.Flask(__name__)
- app.register_blueprint(bp, url_prefix='/py')
-
- @app.route('/')
- def index():
- return flask.request.endpoint
-
- c = app.test_client()
- self.assertEqual(c.get('/').data, 'index')
- self.assertEqual(c.get('/py/foo').data, 'bp.foo')
- self.assertEqual(c.get('/py/bar').data, 'bp.bar')
- self.assertEqual(c.get('/py/bar/123').data, 'bp.123')
- self.assertEqual(c.get('/py/bar/foo').data, 'bp.bar_foo')
-
- def test_route_decorator_custom_endpoint_with_dots(self):
- bp = flask.Blueprint('bp', __name__)
-
- @bp.route('/foo')
- def foo():
- return flask.request.endpoint
-
- try:
- @bp.route('/bar', endpoint='bar.bar')
- def foo_bar():
- return flask.request.endpoint
- except AssertionError:
- pass
- else:
- raise AssertionError('expected AssertionError not raised')
-
- try:
- @bp.route('/bar/123', endpoint='bar.123')
- def foo_bar_foo():
- return flask.request.endpoint
- except AssertionError:
- pass
- else:
- raise AssertionError('expected AssertionError not raised')
-
- def foo_foo_foo():
- pass
-
- self.assertRaises(
- AssertionError,
- lambda: bp.add_url_rule(
- '/bar/123', endpoint='bar.123', view_func=foo_foo_foo
- )
- )
-
- self.assertRaises(
- AssertionError,
- bp.route('/bar/123', endpoint='bar.123'),
- lambda: None
- )
-
- app = flask.Flask(__name__)
- app.register_blueprint(bp, url_prefix='/py')
-
- c = app.test_client()
- self.assertEqual(c.get('/py/foo').data, 'bp.foo')
- # The rule's din't actually made it through
- rv = c.get('/py/bar')
- assert rv.status_code == 404
- rv = c.get('/py/bar/123')
- assert rv.status_code == 404
-
-class SendfileTestCase(unittest.TestCase):
-
- def test_send_file_regular(self):
- app = flask.Flask(__name__)
- with app.test_request_context():
- rv = flask.send_file('static/index.html')
- assert rv.direct_passthrough
- assert rv.mimetype == 'text/html'
- with app.open_resource('static/index.html') as f:
- assert rv.data == f.read()
-
- def test_send_file_xsendfile(self):
- app = flask.Flask(__name__)
- app.use_x_sendfile = True
- with app.test_request_context():
- rv = flask.send_file('static/index.html')
- assert rv.direct_passthrough
- assert 'x-sendfile' in rv.headers
- assert rv.headers['x-sendfile'] == \
- os.path.join(app.root_path, 'static/index.html')
- assert rv.mimetype == 'text/html'
-
- def test_send_file_object(self):
- app = flask.Flask(__name__)
- with catch_warnings() as captured:
- with app.test_request_context():
- f = open(os.path.join(app.root_path, 'static/index.html'))
- rv = flask.send_file(f)
- with app.open_resource('static/index.html') as f:
- assert rv.data == f.read()
- assert rv.mimetype == 'text/html'
- # mimetypes + etag
- assert len(captured) == 2
-
- app.use_x_sendfile = True
- with catch_warnings() as captured:
- with app.test_request_context():
- f = open(os.path.join(app.root_path, 'static/index.html'))
- rv = flask.send_file(f)
- assert rv.mimetype == 'text/html'
- assert 'x-sendfile' in rv.headers
- assert rv.headers['x-sendfile'] == \
- os.path.join(app.root_path, 'static/index.html')
- # mimetypes + etag
- assert len(captured) == 2
-
- app.use_x_sendfile = False
- with app.test_request_context():
- with catch_warnings() as captured:
- f = StringIO('Test')
- rv = flask.send_file(f)
- assert rv.data == 'Test'
- assert rv.mimetype == 'application/octet-stream'
- # etags
- assert len(captured) == 1
- with catch_warnings() as captured:
- f = StringIO('Test')
- rv = flask.send_file(f, mimetype='text/plain')
- assert rv.data == 'Test'
- assert rv.mimetype == 'text/plain'
- # etags
- assert len(captured) == 1
-
- app.use_x_sendfile = True
- with catch_warnings() as captured:
- with app.test_request_context():
- f = StringIO('Test')
- rv = flask.send_file(f)
- assert 'x-sendfile' not in rv.headers
- # etags
- assert len(captured) == 1
-
- def test_attachment(self):
- app = flask.Flask(__name__)
- with catch_warnings() as captured:
- with app.test_request_context():
- f = open(os.path.join(app.root_path, 'static/index.html'))
- rv = flask.send_file(f, as_attachment=True)
- value, options = parse_options_header(rv.headers['Content-Disposition'])
- assert value == 'attachment'
- # mimetypes + etag
- assert len(captured) == 2
-
- with app.test_request_context():
- assert options['filename'] == 'index.html'
- rv = flask.send_file('static/index.html', as_attachment=True)
- value, options = parse_options_header(rv.headers['Content-Disposition'])
- assert value == 'attachment'
- assert options['filename'] == 'index.html'
-
- with app.test_request_context():
- rv = flask.send_file(StringIO('Test'), as_attachment=True,
- attachment_filename='index.txt',
- add_etags=False)
- assert rv.mimetype == 'text/plain'
- value, options = parse_options_header(rv.headers['Content-Disposition'])
- assert value == 'attachment'
- assert options['filename'] == 'index.txt'
-
-
-class LoggingTestCase(unittest.TestCase):
-
- def test_logger_cache(self):
- app = flask.Flask(__name__)
- logger1 = app.logger
- assert app.logger is logger1
- assert logger1.name == __name__
- app.logger_name = __name__ + '/test_logger_cache'
- assert app.logger is not logger1
-
- def test_debug_log(self):
- app = flask.Flask(__name__)
- app.debug = True
-
- @app.route('/')
- def index():
- app.logger.warning('the standard library is dead')
- app.logger.debug('this is a debug statement')
- return ''
-
- @app.route('/exc')
- def exc():
- 1/0
- c = app.test_client()
-
- with catch_stderr() as err:
- c.get('/')
- out = err.getvalue()
- assert 'WARNING in flask_tests [' in out
- assert 'flask_tests.py' in out
- assert 'the standard library is dead' in out
- assert 'this is a debug statement' in out
-
- with catch_stderr() as err:
- try:
- c.get('/exc')
- except ZeroDivisionError:
- pass
- else:
- assert False, 'debug log ate the exception'
-
- def test_exception_logging(self):
- out = StringIO()
- app = flask.Flask(__name__)
- app.logger_name = 'flask_tests/test_exception_logging'
- app.logger.addHandler(StreamHandler(out))
-
- @app.route('/')
- def index():
- 1/0
-
- rv = app.test_client().get('/')
- assert rv.status_code == 500
- assert 'Internal Server Error' in rv.data
-
- err = out.getvalue()
- assert 'Exception on / [GET]' in err
- assert 'Traceback (most recent call last):' in err
- assert '1/0' in err
- assert 'ZeroDivisionError:' in err
-
- def test_processor_exceptions(self):
- app = flask.Flask(__name__)
- @app.before_request
- def before_request():
- if trigger == 'before':
- 1/0
- @app.after_request
- def after_request(response):
- if trigger == 'after':
- 1/0
- return response
- @app.route('/')
- def index():
- return 'Foo'
- @app.errorhandler(500)
- def internal_server_error(e):
- return 'Hello Server Error', 500
- for trigger in 'before', 'after':
- rv = app.test_client().get('/')
- assert rv.status_code == 500
- assert rv.data == 'Hello Server Error'
-
-
-class ConfigTestCase(unittest.TestCase):
-
- def common_object_test(self, app):
- assert app.secret_key == 'devkey'
- assert app.config['TEST_KEY'] == 'foo'
- assert 'ConfigTestCase' not in app.config
-
- def test_config_from_file(self):
- app = flask.Flask(__name__)
- app.config.from_pyfile('flask_tests.py')
- self.common_object_test(app)
-
- def test_config_from_object(self):
- app = flask.Flask(__name__)
- app.config.from_object(__name__)
- self.common_object_test(app)
-
- def test_config_from_class(self):
- class Base(object):
- TEST_KEY = 'foo'
- class Test(Base):
- SECRET_KEY = 'devkey'
- app = flask.Flask(__name__)
- app.config.from_object(Test)
- self.common_object_test(app)
-
- def test_config_from_envvar(self):
- import os
- env = os.environ
- try:
- os.environ = {}
- app = flask.Flask(__name__)
- try:
- app.config.from_envvar('FOO_SETTINGS')
- except RuntimeError, e:
- assert "'FOO_SETTINGS' is not set" in str(e)
- else:
- assert 0, 'expected exception'
- assert not app.config.from_envvar('FOO_SETTINGS', silent=True)
-
- os.environ = {'FOO_SETTINGS': 'flask_tests.py'}
- assert app.config.from_envvar('FOO_SETTINGS')
- self.common_object_test(app)
- finally:
- os.environ = env
-
- def test_config_missing(self):
- app = flask.Flask(__name__)
- try:
- app.config.from_pyfile('missing.cfg')
- except IOError, e:
- msg = str(e)
- assert msg.startswith('[Errno 2] Unable to load configuration '
- 'file (No such file or directory):')
- assert msg.endswith("missing.cfg'")
- else:
- assert 0, 'expected config'
- assert not app.config.from_pyfile('missing.cfg', silent=True)
-
-
-class SubdomainTestCase(unittest.TestCase):
-
- def test_basic_support(self):
- app = flask.Flask(__name__)
- app.config['SERVER_NAME'] = 'localhost'
- @app.route('/')
- def normal_index():
- return 'normal index'
- @app.route('/', subdomain='test')
- def test_index():
- return 'test index'
-
- c = app.test_client()
- rv = c.get('/', 'http://localhost/')
- assert rv.data == 'normal index'
-
- rv = c.get('/', 'http://test.localhost/')
- assert rv.data == 'test index'
-
- @emits_module_deprecation_warning
- def test_module_static_path_subdomain(self):
- app = flask.Flask(__name__)
- app.config['SERVER_NAME'] = 'example.com'
- from subdomaintestmodule import mod
- app.register_module(mod)
- c = app.test_client()
- rv = c.get('/static/hello.txt', 'http://foo.example.com/')
- assert rv.data.strip() == 'Hello Subdomain'
-
- def test_subdomain_matching(self):
- app = flask.Flask(__name__)
- app.config['SERVER_NAME'] = 'localhost'
- @app.route('/', subdomain='')
- def index(user):
- return 'index for %s' % user
-
- c = app.test_client()
- rv = c.get('/', 'http://mitsuhiko.localhost/')
- assert rv.data == 'index for mitsuhiko'
-
- @emits_module_deprecation_warning
- def test_module_subdomain_support(self):
- app = flask.Flask(__name__)
- mod = flask.Module(__name__, 'test', subdomain='testing')
- app.config['SERVER_NAME'] = 'localhost'
-
- @mod.route('/test')
- def test():
- return 'Test'
-
- @mod.route('/outside', subdomain='xtesting')
- def bar():
- return 'Outside'
-
- app.register_module(mod)
-
- c = app.test_client()
- rv = c.get('/test', 'http://testing.localhost/')
- assert rv.data == 'Test'
- rv = c.get('/outside', 'http://xtesting.localhost/')
- assert rv.data == 'Outside'
-
-
-class TestSignals(unittest.TestCase):
-
- def test_template_rendered(self):
- app = flask.Flask(__name__)
-
- @app.route('/')
- def index():
- return flask.render_template('simple_template.html', whiskey=42)
-
- recorded = []
- def record(sender, template, context):
- recorded.append((template, context))
-
- flask.template_rendered.connect(record, app)
- try:
- app.test_client().get('/')
- assert len(recorded) == 1
- template, context = recorded[0]
- assert template.name == 'simple_template.html'
- assert context['whiskey'] == 42
- finally:
- flask.template_rendered.disconnect(record, app)
-
- def test_request_signals(self):
- app = flask.Flask(__name__)
- calls = []
-
- def before_request_signal(sender):
- calls.append('before-signal')
-
- def after_request_signal(sender, response):
- assert response.data == 'stuff'
- calls.append('after-signal')
-
- @app.before_request
- def before_request_handler():
- calls.append('before-handler')
-
- @app.after_request
- def after_request_handler(response):
- calls.append('after-handler')
- response.data = 'stuff'
- return response
-
- @app.route('/')
- def index():
- calls.append('handler')
- return 'ignored anyway'
-
- flask.request_started.connect(before_request_signal, app)
- flask.request_finished.connect(after_request_signal, app)
-
- try:
- rv = app.test_client().get('/')
- assert rv.data == 'stuff'
-
- assert calls == ['before-signal', 'before-handler',
- 'handler', 'after-handler',
- 'after-signal']
- finally:
- flask.request_started.disconnect(before_request_signal, app)
- flask.request_finished.disconnect(after_request_signal, app)
-
- def test_request_exception_signal(self):
- app = flask.Flask(__name__)
- recorded = []
-
- @app.route('/')
- def index():
- 1/0
-
- def record(sender, exception):
- recorded.append(exception)
-
- flask.got_request_exception.connect(record, app)
- try:
- assert app.test_client().get('/').status_code == 500
- assert len(recorded) == 1
- assert isinstance(recorded[0], ZeroDivisionError)
- finally:
- flask.got_request_exception.disconnect(record, app)
-
-
-class ViewTestCase(unittest.TestCase):
-
- def common_test(self, app):
- c = app.test_client()
-
- self.assertEqual(c.get('/').data, 'GET')
- self.assertEqual(c.post('/').data, 'POST')
- self.assertEqual(c.put('/').status_code, 405)
- meths = parse_set_header(c.open('/', method='OPTIONS').headers['Allow'])
- self.assertEqual(sorted(meths), ['GET', 'HEAD', 'OPTIONS', 'POST'])
-
- def test_basic_view(self):
- app = flask.Flask(__name__)
-
- class Index(flask.views.View):
- methods = ['GET', 'POST']
- def dispatch_request(self):
- return flask.request.method
-
- app.add_url_rule('/', view_func=Index.as_view('index'))
- self.common_test(app)
-
- def test_method_based_view(self):
- app = flask.Flask(__name__)
-
- class Index(flask.views.MethodView):
- def get(self):
- return 'GET'
- def post(self):
- return 'POST'
-
- app.add_url_rule('/', view_func=Index.as_view('index'))
-
- self.common_test(app)
-
- def test_view_patching(self):
- app = flask.Flask(__name__)
-
- class Index(flask.views.MethodView):
- def get(self):
- 1/0
- def post(self):
- 1/0
-
- class Other(Index):
- def get(self):
- return 'GET'
- def post(self):
- return 'POST'
-
- view = Index.as_view('index')
- view.view_class = Other
- app.add_url_rule('/', view_func=view)
- self.common_test(app)
-
- def test_view_inheritance(self):
- app = flask.Flask(__name__)
-
- class Index(flask.views.MethodView):
- def get(self):
- return 'GET'
- def post(self):
- return 'POST'
-
- class BetterIndex(Index):
- def delete(self):
- return 'DELETE'
-
- app.add_url_rule('/', view_func=BetterIndex.as_view('index'))
- c = app.test_client()
-
- meths = parse_set_header(c.open('/', method='OPTIONS').headers['Allow'])
- self.assertEqual(sorted(meths), ['DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST'])
-
-
-class DeprecationsTestCase(unittest.TestCase):
-
- def test_init_jinja_globals(self):
- class MyFlask(flask.Flask):
- def init_jinja_globals(self):
- self.jinja_env.globals['foo'] = '42'
-
- with catch_warnings() as log:
- app = MyFlask(__name__)
- @app.route('/')
- def foo():
- return app.jinja_env.globals['foo']
-
- c = app.test_client()
- assert c.get('/').data == '42'
- assert len(log) == 1
- assert 'init_jinja_globals' in str(log[0]['message'])
-
-
-def suite():
- from minitwit_tests import MiniTwitTestCase
- from flaskr_tests import FlaskrTestCase
- suite = unittest.TestSuite()
- suite.addTest(unittest.makeSuite(ContextTestCase))
- suite.addTest(unittest.makeSuite(BasicFunctionalityTestCase))
- suite.addTest(unittest.makeSuite(TemplatingTestCase))
- suite.addTest(unittest.makeSuite(ModuleTestCase))
- suite.addTest(unittest.makeSuite(BlueprintTestCase))
- suite.addTest(unittest.makeSuite(SendfileTestCase))
- suite.addTest(unittest.makeSuite(LoggingTestCase))
- suite.addTest(unittest.makeSuite(ConfigTestCase))
- suite.addTest(unittest.makeSuite(SubdomainTestCase))
- suite.addTest(unittest.makeSuite(ViewTestCase))
- suite.addTest(unittest.makeSuite(DeprecationsTestCase))
- suite.addTest(unittest.makeSuite(InstanceTestCase))
- if flask.json_available:
- suite.addTest(unittest.makeSuite(JSONTestCase))
- if flask.signals_available:
- suite.addTest(unittest.makeSuite(TestSignals))
- suite.addTest(unittest.makeSuite(MiniTwitTestCase))
- suite.addTest(unittest.makeSuite(FlaskrTestCase))
- return suite
-
-
-if __name__ == '__main__':
- unittest.main(defaultTest='suite')