472 lines
13 KiB
Python
472 lines
13 KiB
Python
import os
|
|
|
|
import pytest
|
|
|
|
from flask import Flask, request, abort, url_for
|
|
from flask.views import MethodView
|
|
|
|
from flask_admin import base
|
|
|
|
|
|
class MockView(base.BaseView):
|
|
# Various properties
|
|
allow_call = True
|
|
allow_access = True
|
|
visible = True
|
|
|
|
@base.expose('/')
|
|
def index(self):
|
|
return 'Success!'
|
|
|
|
@base.expose('/test/')
|
|
def test(self):
|
|
return self.render('mock.html')
|
|
|
|
def _handle_view(self, name, **kwargs):
|
|
if self.allow_call:
|
|
return super(MockView, self)._handle_view(name, **kwargs)
|
|
else:
|
|
return 'Failure!'
|
|
|
|
def is_accessible(self):
|
|
if self.allow_access:
|
|
return super(MockView, self).is_accessible()
|
|
|
|
return False
|
|
|
|
def is_visible(self):
|
|
if self.visible:
|
|
return super(MockView, self).is_visible()
|
|
|
|
return False
|
|
|
|
|
|
class MockMethodView(base.BaseView):
|
|
@base.expose('/')
|
|
def index(self):
|
|
return 'Success!'
|
|
|
|
@base.expose_plugview('/_api/1')
|
|
class API1(MethodView):
|
|
def get(self, cls):
|
|
return cls.render('method.html', request=request, name='API1')
|
|
|
|
def post(self, cls):
|
|
return cls.render('method.html', request=request, name='API1')
|
|
|
|
def put(self, cls):
|
|
return cls.render('method.html', request=request, name='API1')
|
|
|
|
def delete(self, cls):
|
|
return cls.render('method.html', request=request, name='API1')
|
|
|
|
@base.expose_plugview('/_api/2')
|
|
class API2(MethodView):
|
|
def get(self, cls):
|
|
return cls.render('method.html', request=request, name='API2')
|
|
|
|
def post(self, cls):
|
|
return cls.render('method.html', request=request, name='API2')
|
|
|
|
@base.expose_plugview('/_api/3')
|
|
@base.expose_plugview('/_api/4')
|
|
class DoubleExpose(MethodView):
|
|
def get(self, cls):
|
|
return cls.render('method.html', request=request, name='API3')
|
|
|
|
|
|
def test_baseview_defaults():
|
|
view = MockView()
|
|
assert view.name is None
|
|
assert view.category is None
|
|
assert view.endpoint == 'mockview'
|
|
assert view.url is None
|
|
assert view.static_folder is None
|
|
assert view.admin is None
|
|
assert view.blueprint is None
|
|
|
|
|
|
def test_base_defaults():
|
|
admin = base.Admin()
|
|
assert admin.name == 'Admin'
|
|
assert admin.url == '/admin'
|
|
assert admin.endpoint == 'admin'
|
|
assert admin.app is None
|
|
assert admin.index_view is not None
|
|
assert admin.index_view._template == 'admin/index.html'
|
|
|
|
# Check if default view was added
|
|
assert len(admin._views) == 1
|
|
assert admin._views[0] == admin.index_view
|
|
|
|
|
|
def test_custom_index_view():
|
|
view = base.AdminIndexView(name='a', category='b', endpoint='c',
|
|
url='/d', template='e')
|
|
admin = base.Admin(index_view=view)
|
|
|
|
assert admin.endpoint == 'c'
|
|
assert admin.url == '/d'
|
|
assert admin.index_view is view
|
|
assert view.name == 'a'
|
|
assert view.category == 'b'
|
|
assert view._template == 'e'
|
|
|
|
# Check if view was added
|
|
assert len(admin._views) == 1
|
|
assert admin._views[0] == view
|
|
|
|
|
|
def test_custom_index_view_in_init_app():
|
|
view = base.AdminIndexView(name='a', category='b', endpoint='c',
|
|
url='/d', template='e')
|
|
app = Flask(__name__)
|
|
admin = base.Admin()
|
|
admin.init_app(app, index_view=view)
|
|
|
|
assert admin.endpoint == 'c'
|
|
assert admin.url == '/d'
|
|
assert admin.index_view is view
|
|
assert view.name == 'a'
|
|
assert view.category == 'b'
|
|
assert view._template == 'e'
|
|
|
|
# Check if view was added
|
|
assert len(admin._views) == 1
|
|
assert admin._views[0] == view
|
|
|
|
|
|
def test_base_registration():
|
|
app = Flask(__name__)
|
|
admin = base.Admin(app)
|
|
|
|
assert admin.app == app
|
|
assert admin.index_view.blueprint is not None
|
|
|
|
|
|
def test_admin_customizations():
|
|
app = Flask(__name__)
|
|
admin = base.Admin(app, name='Test', url='/foobar', static_url_path='/static/my/admin')
|
|
assert admin.name == 'Test'
|
|
assert admin.url == '/foobar'
|
|
assert admin.index_view.blueprint.static_url_path == '/static/my/admin'
|
|
|
|
client = app.test_client()
|
|
rv = client.get('/foobar/')
|
|
assert rv.status_code == 200
|
|
|
|
# test custom static_url_path
|
|
with app.test_request_context('/'):
|
|
rv = client.get(url_for('admin.static', filename='bootstrap/bootstrap2/css/bootstrap.css'))
|
|
assert rv.status_code == 200
|
|
|
|
|
|
def test_baseview_registration():
|
|
admin = base.Admin()
|
|
|
|
view = MockView()
|
|
bp = view.create_blueprint(admin)
|
|
|
|
# Base properties
|
|
assert view.admin == admin
|
|
assert view.blueprint is not None
|
|
|
|
# Calculated properties
|
|
assert view.endpoint == 'mockview'
|
|
assert view.url == '/admin/mockview'
|
|
assert view.name == 'Mock View'
|
|
|
|
# Verify generated blueprint properties
|
|
assert bp.name == view.endpoint
|
|
assert bp.url_prefix == view.url
|
|
assert bp.template_folder == os.path.join('templates', 'bootstrap2')
|
|
assert bp.static_folder == view.static_folder
|
|
|
|
# Verify customizations
|
|
view = MockView(name='Test', endpoint='foobar')
|
|
view.create_blueprint(base.Admin())
|
|
|
|
assert view.name == 'Test'
|
|
assert view.endpoint == 'foobar'
|
|
assert view.url == '/admin/foobar'
|
|
|
|
view = MockView(url='test')
|
|
view.create_blueprint(base.Admin())
|
|
assert view.url == '/admin/test'
|
|
|
|
view = MockView(url='/test/test')
|
|
view.create_blueprint(base.Admin())
|
|
assert view.url == '/test/test'
|
|
|
|
view = MockView(endpoint='test')
|
|
view.create_blueprint(base.Admin(url='/'))
|
|
assert view.url == '/test'
|
|
|
|
view = MockView(static_url_path='/static/my/test')
|
|
view.create_blueprint(base.Admin())
|
|
assert view.blueprint.static_url_path == '/static/my/test'
|
|
|
|
|
|
def test_baseview_urls():
|
|
app = Flask(__name__)
|
|
admin = base.Admin(app)
|
|
|
|
view = MockView()
|
|
admin.add_view(view)
|
|
|
|
assert len(view._urls) == 2
|
|
|
|
|
|
def test_add_views():
|
|
app = Flask(__name__)
|
|
admin = base.Admin(app)
|
|
|
|
admin.add_views(MockView(endpoint='test1'), MockView(endpoint='test2'))
|
|
|
|
assert len(admin.menu()) == 3
|
|
|
|
|
|
def test_add_category():
|
|
app = Flask(__name__)
|
|
admin = base.Admin(app)
|
|
|
|
admin.add_category('Category1', 'class-name', 'icon-type', 'icon-value')
|
|
admin.add_view(MockView(name='Test 1', endpoint='test1', category='Category1'))
|
|
admin.add_view(MockView(name='Test 2', endpoint='test2', category='Category2'))
|
|
|
|
assert len(admin.menu()) == 3
|
|
|
|
# Test 1 should be underneath Category1
|
|
assert admin.menu()[1].name == 'Category1'
|
|
assert admin.menu()[1].get_class_name() == 'class-name'
|
|
assert admin.menu()[1].get_icon_type() == 'icon-type'
|
|
assert admin.menu()[1].get_icon_value() == 'icon-value'
|
|
assert len(admin.menu()[1].get_children()) == 1
|
|
assert admin.menu()[1].get_children()[0].name == 'Test 1'
|
|
|
|
# Test 2 should be underneath Category2
|
|
assert admin.menu()[2].name == 'Category2'
|
|
assert admin.menu()[2].get_class_name() is None
|
|
assert admin.menu()[2].get_icon_type() is None
|
|
assert admin.menu()[2].get_icon_value() is None
|
|
assert len(admin.menu()[2].get_children()) == 1
|
|
assert admin.menu()[2].get_children()[0].name == 'Test 2'
|
|
|
|
|
|
@pytest.mark.xfail(raises=Exception)
|
|
def test_no_default():
|
|
app = Flask(__name__)
|
|
admin = base.Admin(app)
|
|
admin.add_view(base.BaseView())
|
|
|
|
|
|
def test_call():
|
|
app = Flask(__name__)
|
|
admin = base.Admin(app)
|
|
view = MockView()
|
|
admin.add_view(view)
|
|
client = app.test_client()
|
|
|
|
rv = client.get('/admin/')
|
|
assert rv.status_code == 200
|
|
|
|
rv = client.get('/admin/mockview/')
|
|
assert rv.data == b'Success!'
|
|
|
|
rv = client.get('/admin/mockview/test/')
|
|
assert rv.data == b'Success!'
|
|
|
|
# Check authentication failure
|
|
view.allow_call = False
|
|
rv = client.get('/admin/mockview/')
|
|
assert rv.data == b'Failure!'
|
|
|
|
|
|
def test_permissions():
|
|
app = Flask(__name__)
|
|
admin = base.Admin(app)
|
|
view = MockView()
|
|
admin.add_view(view)
|
|
client = app.test_client()
|
|
|
|
view.allow_access = False
|
|
|
|
rv = client.get('/admin/mockview/')
|
|
assert rv.status_code == 403
|
|
|
|
|
|
def test_inaccessible_callback():
|
|
app = Flask(__name__)
|
|
admin = base.Admin(app)
|
|
view = MockView()
|
|
admin.add_view(view)
|
|
client = app.test_client()
|
|
|
|
view.allow_access = False
|
|
view.inaccessible_callback = lambda *args, **kwargs: abort(418)
|
|
|
|
rv = client.get('/admin/mockview/')
|
|
assert rv.status_code == 418
|
|
|
|
|
|
def get_visibility():
|
|
app = Flask(__name__)
|
|
admin = base.Admin(app)
|
|
|
|
view = MockView(name='TestMenuItem')
|
|
view.visible = False
|
|
|
|
admin.add_view(view)
|
|
|
|
client = app.test_client()
|
|
|
|
rv = client.get('/admin/mockview/')
|
|
assert 'TestMenuItem' not in rv.data.decode('utf-8')
|
|
|
|
|
|
def test_submenu():
|
|
app = Flask(__name__)
|
|
admin = base.Admin(app)
|
|
admin.add_view(MockView(name='Test 1', category='Test', endpoint='test1'))
|
|
|
|
# Second view is not normally accessible
|
|
view = MockView(name='Test 2', category='Test', endpoint='test2')
|
|
view.allow_access = False
|
|
admin.add_view(view)
|
|
|
|
assert 'Test' in admin._menu_categories
|
|
assert len(admin._menu) == 2
|
|
assert admin._menu[1].name == 'Test'
|
|
assert len(admin._menu[1]._children) == 2
|
|
|
|
# Categories don't have URLs
|
|
assert admin._menu[1].get_url() is None
|
|
|
|
# Categories are only accessible if there is at least one accessible child
|
|
assert admin._menu[1].is_accessible()
|
|
|
|
children = admin._menu[1].get_children()
|
|
assert len(children) == 1
|
|
|
|
assert children[0].is_accessible()
|
|
|
|
|
|
def test_delayed_init():
|
|
app = Flask(__name__)
|
|
admin = base.Admin()
|
|
admin.add_view(MockView())
|
|
admin.init_app(app)
|
|
|
|
client = app.test_client()
|
|
|
|
rv = client.get('/admin/mockview/')
|
|
assert rv.data == b'Success!'
|
|
|
|
|
|
def test_multi_instances_init():
|
|
app = Flask(__name__)
|
|
_ = base.Admin(app)
|
|
|
|
class ManageIndex(base.AdminIndexView):
|
|
pass
|
|
|
|
_ = base.Admin(app, index_view=ManageIndex(url='/manage', endpoint='manage')) # noqa: F841
|
|
|
|
|
|
@pytest.mark.xfail(raises=Exception)
|
|
def test_double_init():
|
|
app = Flask(__name__)
|
|
admin = base.Admin(app)
|
|
admin.init_app(app)
|
|
|
|
|
|
def test_nested_flask_views():
|
|
app = Flask(__name__)
|
|
admin = base.Admin(app)
|
|
|
|
view = MockMethodView()
|
|
admin.add_view(view)
|
|
|
|
client = app.test_client()
|
|
|
|
rv = client.get('/admin/mockmethodview/_api/1')
|
|
print('"', rv.data, '"')
|
|
assert rv.data == b'GET - API1'
|
|
rv = client.put('/admin/mockmethodview/_api/1')
|
|
assert rv.data == b'PUT - API1'
|
|
rv = client.post('/admin/mockmethodview/_api/1')
|
|
assert rv.data == b'POST - API1'
|
|
rv = client.delete('/admin/mockmethodview/_api/1')
|
|
assert rv.data == b'DELETE - API1'
|
|
|
|
rv = client.get('/admin/mockmethodview/_api/2')
|
|
assert rv.data == b'GET - API2'
|
|
rv = client.post('/admin/mockmethodview/_api/2')
|
|
assert rv.data == b'POST - API2'
|
|
rv = client.delete('/admin/mockmethodview/_api/2')
|
|
assert rv.status_code == 405
|
|
rv = client.put('/admin/mockmethodview/_api/2')
|
|
assert rv.status_code == 405
|
|
|
|
rv = client.get('/admin/mockmethodview/_api/3')
|
|
assert rv.data == b'GET - API3'
|
|
rv = client.get('/admin/mockmethodview/_api/4')
|
|
assert rv.data == b'GET - API3'
|
|
|
|
|
|
def test_root_mount():
|
|
app = Flask(__name__)
|
|
admin = base.Admin(app, url='/')
|
|
admin.add_view(MockView())
|
|
|
|
client = app.test_client()
|
|
rv = client.get('/mockview/')
|
|
assert rv.data == b'Success!'
|
|
|
|
# test static files when url='/'
|
|
with app.test_request_context('/'):
|
|
rv = client.get(url_for('admin.static', filename='bootstrap/bootstrap2/css/bootstrap.css'))
|
|
assert rv.status_code == 200
|
|
|
|
|
|
def test_menu_links():
|
|
app = Flask(__name__)
|
|
admin = base.Admin(app)
|
|
admin.add_link(base.MenuLink('TestMenuLink1', endpoint='.index'))
|
|
admin.add_link(base.MenuLink('TestMenuLink2', url='http://python.org/'))
|
|
|
|
client = app.test_client()
|
|
rv = client.get('/admin/')
|
|
|
|
data = rv.data.decode('utf-8')
|
|
assert 'TestMenuLink1' in data
|
|
assert 'TestMenuLink2' in data
|
|
|
|
|
|
def test_add_links():
|
|
app = Flask(__name__)
|
|
admin = base.Admin(app)
|
|
admin.add_links(base.MenuLink('TestMenuLink1', endpoint='.index'),
|
|
base.MenuLink('TestMenuLink2', url='http://python.org/'))
|
|
|
|
client = app.test_client()
|
|
rv = client.get('/admin/')
|
|
|
|
data = rv.data.decode('utf-8')
|
|
assert 'TestMenuLink1' in data
|
|
assert 'TestMenuLink2' in data
|
|
|
|
|
|
def check_class_name():
|
|
view = MockView()
|
|
assert view.name == 'Mock View'
|
|
|
|
|
|
def check_endpoint():
|
|
class CustomView(MockView):
|
|
def _get_endpoint(self, endpoint):
|
|
return 'admin.' + super(CustomView, self)._get_endpoint(endpoint)
|
|
|
|
view = CustomView()
|
|
assert view.endpoint == 'admin.customview'
|