diff --git a/python-Flask-Admin.changes b/python-Flask-Admin.changes index 15b0659..634573f 100644 --- a/python-Flask-Admin.changes +++ b/python-Flask-Admin.changes @@ -1,3 +1,9 @@ +------------------------------------------------------------------- +Thu Oct 29 20:46:47 UTC 2020 - Matej Cepl + +- Add remove_nose.patch to remove dependency on nose + (gh#flask-admin/flask-admin#2047). + ------------------------------------------------------------------- Mon Sep 28 11:33:32 UTC 2020 - Hans-Peter Jansen diff --git a/python-Flask-Admin.spec b/python-Flask-Admin.spec index 94ed693..87a3ad4 100644 --- a/python-Flask-Admin.spec +++ b/python-Flask-Admin.spec @@ -25,6 +25,9 @@ Summary: Extensible admin interface framework for Flask License: BSD-3-Clause URL: https://github.com/flask-admin/flask-admin/ Source: https://files.pythonhosted.org/packages/source/F/Flask-Admin/Flask-Admin-%{version}.tar.gz +# PATCH-FEATURE-UPSTREAM remove_nose.patch gh#flask-admin/flask-admin#2047 mcepl@suse.com +# port from nose to pytest (mostly just pure asserts) +Patch0: remove_nose.patch BuildRequires: %{python_module Flask >= 0.7} BuildRequires: %{python_module Flask-BabelEx} BuildRequires: %{python_module Flask-SQLAlchemy} @@ -33,8 +36,8 @@ BuildRequires: %{python_module SQLAlchemy-Utils} BuildRequires: %{python_module WTForms} BuildRequires: %{python_module arrow} BuildRequires: %{python_module colour} -BuildRequires: %{python_module nose >= 1.0} BuildRequires: %{python_module peewee} +BuildRequires: %{python_module pytest} BuildRequires: %{python_module setuptools} BuildRequires: fdupes BuildRequires: python-rpm-macros @@ -60,6 +63,8 @@ the resulting application. %prep %setup -q -n Flask-Admin-%{version} +%autopatch -p1 + # remove contrib tests that pull in too many dependencies rm -rf flask_admin/tests/geoa rm -rf flask_admin/tests/{mongoengine,pymongo} @@ -76,8 +81,7 @@ rm -f flask_admin/tests/test_form_upload.py %python_expand %fdupes %{buildroot}%{$python_sitelib} %check -export PYTHONDONTWRITEBYTECODE=1 -%python_expand nosetests-%{$python_bin_suffix} -e test_ajax_fk +%pytest %files %{python_files} %license LICENSE diff --git a/remove_nose.patch b/remove_nose.patch new file mode 100644 index 0000000..8234924 --- /dev/null +++ b/remove_nose.patch @@ -0,0 +1,7018 @@ +--- + README.rst | 4 + flask_admin/tests/fileadmin/test_fileadmin.py | 106 - + flask_admin/tests/fileadmin/test_fileadmin_azure.py | 2 + flask_admin/tests/geoa/test_basic.py | 95 - + flask_admin/tests/mongoengine/__init__.py | 2 + flask_admin/tests/mongoengine/test_basic.py | 555 ++++----- + flask_admin/tests/peeweemodel/test_basic.py | 626 +++++----- + flask_admin/tests/pymongo/test_basic.py | 44 + flask_admin/tests/sqla/test_basic.py | 1154 +++++++++----------- + flask_admin/tests/sqla/test_form_rules.py | 46 + flask_admin/tests/sqla/test_inlineform.py | 78 - + flask_admin/tests/sqla/test_multi_pk.py | 56 + flask_admin/tests/sqla/test_postgres.py | 50 + flask_admin/tests/sqla/test_translation.py | 5 + flask_admin/tests/test_base.py | 186 +-- + flask_admin/tests/test_form_upload.py | 103 - + flask_admin/tests/test_model.py | 316 ++--- + flask_admin/tests/test_tools.py | 16 + setup.cfg | 7 + setup.py | 4 + 20 files changed, 1696 insertions(+), 1759 deletions(-) + +--- a/flask_admin/tests/mongoengine/__init__.py ++++ b/flask_admin/tests/mongoengine/__init__.py +@@ -1,4 +1,4 @@ +-from nose.plugins.skip import SkipTest ++from unittest import SkipTest + from wtforms import __version__ as wtforms_version + + # Skip test on PY3 +--- a/flask_admin/tests/peeweemodel/test_basic.py ++++ b/flask_admin/tests/peeweemodel/test_basic.py +@@ -1,5 +1,4 @@ +-from nose.tools import eq_, ok_ +-from nose.plugins.skip import SkipTest ++from unittest import SkipTest + + # Skip test on PY3 + from flask_admin._compat import PY2, as_unicode +@@ -115,69 +114,69 @@ def test_model(): + view = CustomModelView(Model1) + admin.add_view(view) + +- eq_(view.model, Model1) +- eq_(view.name, 'Model1') +- eq_(view.endpoint, 'model1') +- +- eq_(view._primary_key, 'id') +- +- ok_('test1' in view._sortable_columns) +- ok_('test2' in view._sortable_columns) +- ok_('test3' in view._sortable_columns) +- ok_('test4' in view._sortable_columns) +- +- ok_(view._create_form_class is not None) +- ok_(view._edit_form_class is not None) +- eq_(view._search_supported, False) +- eq_(view._filters, None) ++ assert view.model == Model1 ++ assert view.name == 'Model1' ++ assert view.endpoint == 'model1' ++ ++ assert view._primary_key == 'id' ++ ++ assert 'test1' in view._sortable_columns ++ assert 'test2' in view._sortable_columns ++ assert 'test3' in view._sortable_columns ++ assert 'test4' in view._sortable_columns ++ ++ assert view._create_form_class is not None ++ assert view._edit_form_class is not None ++ assert not view._search_supported ++ assert view._filters is None + + # Verify form +- eq_(view._create_form_class.test1.field_class, fields.StringField) +- eq_(view._create_form_class.test2.field_class, fields.StringField) +- eq_(view._create_form_class.test3.field_class, fields.TextAreaField) +- eq_(view._create_form_class.test4.field_class, fields.TextAreaField) ++ assert view._create_form_class.test1.field_class == fields.StringField ++ assert view._create_form_class.test2.field_class == fields.StringField ++ assert view._create_form_class.test3.field_class == fields.TextAreaField ++ assert view._create_form_class.test4.field_class == fields.TextAreaField + + # Make some test clients + client = app.test_client() + + rv = client.get('/admin/model1/') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + + rv = client.get('/admin/model1/new/') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + + rv = client.post('/admin/model1/new/', + data=dict(test1='test1large', test2='test2')) +- eq_(rv.status_code, 302) ++ assert rv.status_code == 302 + + model = Model1.select().get() +- eq_(model.test1, 'test1large') +- eq_(model.test2, 'test2') +- ok_(model.test3 is None or model.test3 == '') +- ok_(model.test4 is None or model.test4 == '') ++ assert model.test1 == 'test1large' ++ assert model.test2 == 'test2' ++ assert model.test3 is None or model.test3 == '' ++ assert model.test4 is None or model.test4 == '' + + rv = client.get('/admin/model1/') +- eq_(rv.status_code, 200) +- ok_('test1large' in rv.data) ++ assert rv.status_code == 200 ++ assert 'test1large' in rv.data + + url = '/admin/model1/edit/?id=%s' % model.id + rv = client.get(url) +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + + rv = client.post(url, + data=dict(test1='test1small', test2='test2large')) +- eq_(rv.status_code, 302) ++ assert rv.status_code == 302 + + model = Model1.select().get() +- eq_(model.test1, 'test1small') +- eq_(model.test2, 'test2large') +- ok_(model.test3 is None or model.test3 == '') +- ok_(model.test4 is None or model.test4 == '') ++ assert model.test1 == 'test1small' ++ assert model.test2 == 'test2large' ++ assert model.test3 is None or model.test3 == '' ++ assert model.test4 is None or model.test4 == '' + + url = '/admin/model1/delete/?id=%s' % model.id + rv = client.post(url) +- eq_(rv.status_code, 302) +- eq_(Model1.select().count(), 0) ++ assert rv.status_code == 302 ++ assert Model1.select().count() == 0 + + + def test_column_editable_list(): +@@ -198,7 +197,7 @@ def test_column_editable_list(): + # Test in-line edit field rendering + rv = client.get('/admin/model1/') + data = rv.data.decode('utf-8') +- ok_('data-role="x-editable"' in data) ++ assert 'data-role="x-editable"' in data + + # Form - Test basic in-line edit functionality + rv = client.post('/admin/model1/ajax/update/', data={ +@@ -206,12 +205,12 @@ def test_column_editable_list(): + 'test1': 'change-success-1', + }) + data = rv.data.decode('utf-8') +- ok_('Record was successfully saved.' == data) ++ assert 'Record was successfully saved.' == data + + # ensure the value has changed + rv = client.get('/admin/model1/') + data = rv.data.decode('utf-8') +- ok_('change-success-1' in data) ++ assert 'change-success-1' in data + + # Test validation error + rv = client.post('/admin/model1/ajax/update/', data={ +@@ -220,7 +219,7 @@ def test_column_editable_list(): + 'thantwentycharacterslongerthantwentycharacters'), + }) + data = rv.data.decode('utf-8') +- eq_(rv.status_code, 500) ++ assert rv.status_code == 500 + + # Test invalid primary key + rv = client.post('/admin/model1/ajax/update/', data={ +@@ -228,7 +227,7 @@ def test_column_editable_list(): + 'test1': 'problematic-input', + }) + data = rv.data.decode('utf-8') +- eq_(rv.status_code, 500) ++ assert rv.status_code == 500 + + # Test editing column not in column_editable_list + rv = client.post('/admin/model1/ajax/update/', data={ +@@ -236,7 +235,7 @@ def test_column_editable_list(): + 'test2': 'problematic-input', + }) + data = rv.data.decode('utf-8') +- ok_('problematic-input' not in data) ++ assert 'problematic-input' not in data + + # Test in-line editing for relations + view = CustomModelView(Model2, column_editable_list=['model1']) +@@ -247,12 +246,12 @@ def test_column_editable_list(): + 'model1': '3', + }) + data = rv.data.decode('utf-8') +- ok_('Record was successfully saved.' == data) ++ assert 'Record was successfully saved.' == data + + # confirm the value has changed + rv = client.get('/admin/model2/') + data = rv.data.decode('utf-8') +- ok_('test1_val_3' in data) ++ assert 'test1_val_3' in data + + + def test_details_view(): +@@ -280,32 +279,32 @@ def test_details_view(): + # ensure link to details is hidden when can_view_details is disabled + rv = client.get('/admin/model1/') + data = rv.data.decode('utf-8') +- ok_('/admin/model1/details/' not in data) ++ assert '/admin/model1/details/' not in data + + # ensure link to details view appears + rv = client.get('/admin/model2/') + data = rv.data.decode('utf-8') +- ok_('/admin/model2/details/' in data) ++ assert '/admin/model2/details/' in data + + # test redirection when details are disabled + rv = client.get('/admin/model1/details/?url=%2Fadmin%2Fmodel1%2F&id=3') +- eq_(rv.status_code, 302) ++ assert rv.status_code == 302 + + # test if correct data appears in details view when enabled + rv = client.get('/admin/model2/details/?url=%2Fadmin%2Fmodel2%2F&id=3') + data = rv.data.decode('utf-8') +- ok_('Char Field' in data) +- ok_('char_field_val_3' in data) +- ok_('Int Field' in data) +- ok_('5000' in data) ++ assert 'Char Field' in data ++ assert 'char_field_val_3' in data ++ assert 'Int Field' in data ++ assert '5000' in data + + # test column_details_list + rv = client.get('/admin/cf_view/details/?url=%2Fadmin%2Fcf_view%2F&id=3') + data = rv.data.decode('utf-8') +- ok_('Char Field' in data) +- ok_('char_field_val_3' in data) +- ok_('Int Field' not in data) +- ok_('5000' not in data) ++ assert 'Char Field' in data ++ assert 'char_field_val_3' in data ++ assert 'Int Field' not in data ++ assert '5000' not in data + + + def test_column_filters(): +@@ -319,10 +318,10 @@ def test_column_filters(): + view = CustomModelView(Model1, column_filters=['test1']) + admin.add_view(view) + +- eq_(len(view._filters), 7) ++ assert len(view._filters) == 7 + +- eq_( +- [(f['index'], f['operation']) for f in view._filter_groups[u'Test1']], ++ assert \ ++ [(f['index'], f['operation']) for f in view._filter_groups[u'Test1']] == \ + [ + (0, 'contains'), + (1, 'not contains'), +@@ -332,79 +331,78 @@ def test_column_filters(): + (5, 'in list'), + (6, 'not in list'), + ] +- ) + + # Make some test clients + client = app.test_client() + + # string - equals + rv = client.get('/admin/model1/?flt0_0=test1_val_1') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('test2_val_1' in data) +- ok_('test1_val_2' not in data) ++ assert 'test2_val_1' in data ++ assert 'test1_val_2' not in data + + # string - not equal + rv = client.get('/admin/model1/?flt0_1=test1_val_1') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('test2_val_1' not in data) +- ok_('test1_val_2' in data) ++ assert 'test2_val_1' not in data ++ assert 'test1_val_2' in data + + # string - contains + rv = client.get('/admin/model1/?flt0_2=test1_val_1') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('test2_val_1' in data) +- ok_('test1_val_2' not in data) ++ assert 'test2_val_1' in data ++ assert 'test1_val_2' not in data + + # string - not contains + rv = client.get('/admin/model1/?flt0_3=test1_val_1') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('test2_val_1' not in data) +- ok_('test1_val_2' in data) ++ assert 'test2_val_1' not in data ++ assert 'test1_val_2' in data + + # string - empty + rv = client.get('/admin/model1/?flt0_4=1') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('empty_obj' in data) +- ok_('test1_val_1' not in data) +- ok_('test1_val_2' not in data) ++ assert 'empty_obj' in data ++ assert 'test1_val_1' not in data ++ assert 'test1_val_2' not in data + + # string - not empty + rv = client.get('/admin/model1/?flt0_4=0') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('empty_obj' not in data) +- ok_('test1_val_1' in data) +- ok_('test1_val_2' in data) ++ assert 'empty_obj' not in data ++ assert 'test1_val_1' in data ++ assert 'test1_val_2' in data + + # string - in list + rv = client.get('/admin/model1/?flt0_5=test1_val_1%2Ctest1_val_2') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('test2_val_1' in data) +- ok_('test2_val_2' in data) +- ok_('test1_val_3' not in data) +- ok_('test1_val_4' not in data) ++ assert 'test2_val_1' in data ++ assert 'test2_val_2' in data ++ assert 'test1_val_3' not in data ++ assert 'test1_val_4' not in data + + # string - not in list + rv = client.get('/admin/model1/?flt0_6=test1_val_1%2Ctest1_val_2') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('test2_val_1' not in data) +- ok_('test2_val_2' not in data) +- ok_('test1_val_3' in data) +- ok_('test1_val_4' in data) ++ assert 'test2_val_1' not in data ++ assert 'test2_val_2' not in data ++ assert 'test1_val_3' in data ++ assert 'test1_val_4' in data + + # Test int filter + view = CustomModelView(Model2, column_filters=['int_field']) + admin.add_view(view) + +- eq_( +- [(f['index'], f['operation']) for f in view._filter_groups[u'Int Field']], ++ assert \ ++ [(f['index'], f['operation']) for f in view._filter_groups[u'Int Field']] == \ + [ + (0, 'equals'), + (1, 'not equal'), +@@ -414,150 +412,148 @@ def test_column_filters(): + (5, 'in list'), + (6, 'not in list'), + ] +- ) + + # integer - equals + rv = client.get('/admin/model2/?flt0_0=5000') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('char_field_val_3' in data) +- ok_('char_field_val_4' not in data) ++ assert 'char_field_val_3' in data ++ assert 'char_field_val_4' not in data + + # integer - equals (huge number) + rv = client.get('/admin/model2/?flt0_0=6169453081680413441') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('char_field_val_5' in data) +- ok_('char_field_val_4' not in data) ++ assert 'char_field_val_5' in data ++ assert 'char_field_val_4' not in data + + # integer - equals - test validation + rv = client.get('/admin/model2/?flt0_0=badval') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('Invalid Filter Value' in data) ++ assert 'Invalid Filter Value' in data + + # integer - not equal + rv = client.get('/admin/model2/?flt0_1=5000') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('char_field_val_3' not in data) +- ok_('char_field_val_4' in data) ++ assert 'char_field_val_3' not in data ++ assert 'char_field_val_4' in data + + # integer - greater + rv = client.get('/admin/model2/?flt0_2=6000') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('char_field_val_3' not in data) +- ok_('char_field_val_4' in data) ++ assert 'char_field_val_3' not in data ++ assert 'char_field_val_4' in data + + # integer - smaller + rv = client.get('/admin/model2/?flt0_3=6000') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('char_field_val_3' in data) +- ok_('char_field_val_4' not in data) ++ assert 'char_field_val_3' in data ++ assert 'char_field_val_4' not in data + + # integer - empty + rv = client.get('/admin/model2/?flt0_4=1') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('char_field_val_1' in data) +- ok_('char_field_val_2' in data) +- ok_('char_field_val_3' not in data) +- ok_('char_field_val_4' not in data) ++ assert 'char_field_val_1' in data ++ assert 'char_field_val_2' in data ++ assert 'char_field_val_3' not in data ++ assert 'char_field_val_4' not in data + + # integer - not empty + rv = client.get('/admin/model2/?flt0_4=0') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('char_field_val_1' not in data) +- ok_('char_field_val_2' not in data) +- ok_('char_field_val_3' in data) +- ok_('char_field_val_4' in data) ++ assert 'char_field_val_1' not in data ++ assert 'char_field_val_2' not in data ++ assert 'char_field_val_3' in data ++ assert 'char_field_val_4' in data + + # integer - in list + rv = client.get('/admin/model2/?flt0_5=5000%2C9000') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('char_field_val_1' not in data) +- ok_('char_field_val_2' not in data) +- ok_('char_field_val_3' in data) +- ok_('char_field_val_4' in data) ++ assert 'char_field_val_1' not in data ++ assert 'char_field_val_2' not in data ++ assert 'char_field_val_3' in data ++ assert 'char_field_val_4' in data + + # integer - in list (huge number) + rv = client.get('/admin/model2/?flt0_5=6169453081680413441') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('char_field_val_1' not in data) +- ok_('char_field_val_5' in data) ++ assert 'char_field_val_1' not in data ++ assert 'char_field_val_5' in data + + # integer - in list - test validation + rv = client.get('/admin/model2/?flt0_5=5000%2Cbadval') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('Invalid Filter Value' in data) ++ assert 'Invalid Filter Value' in data + + # integer - not in list + rv = client.get('/admin/model2/?flt0_6=5000%2C9000') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('char_field_val_1' in data) +- ok_('char_field_val_2' in data) +- ok_('char_field_val_3' not in data) +- ok_('char_field_val_4' not in data) ++ assert 'char_field_val_1' in data ++ assert 'char_field_val_2' in data ++ assert 'char_field_val_3' not in data ++ assert 'char_field_val_4' not in data + + # Test boolean filter + view = CustomModelView(Model2, column_filters=['bool_field'], + endpoint="_bools") + admin.add_view(view) + +- eq_( +- [(f['index'], f['operation']) for f in view._filter_groups[u'Bool Field']], ++ assert \ ++ [(f['index'], f['operation']) for f in view._filter_groups[u'Bool Field']] == \ + [ + (0, 'equals'), + (1, 'not equal'), + ] +- ) + + # boolean - equals - Yes + rv = client.get('/admin/_bools/?flt0_0=1') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('char_field_val_1' in data) +- ok_('char_field_val_2' not in data) +- ok_('char_field_val_3' not in data) ++ assert 'char_field_val_1' in data ++ assert 'char_field_val_2' not in data ++ assert 'char_field_val_3' not in data + + # boolean - equals - No + rv = client.get('/admin/_bools/?flt0_0=0') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('char_field_val_1' not in data) +- ok_('char_field_val_2' in data) +- ok_('char_field_val_3' in data) ++ assert 'char_field_val_1' not in data ++ assert 'char_field_val_2' in data ++ assert 'char_field_val_3' in data + + # boolean - not equals - Yes + rv = client.get('/admin/_bools/?flt0_1=1') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('char_field_val_1' not in data) +- ok_('char_field_val_2' in data) +- ok_('char_field_val_3' in data) ++ assert 'char_field_val_1' not in data ++ assert 'char_field_val_2' in data ++ assert 'char_field_val_3' in data + + # boolean - not equals - No + rv = client.get('/admin/_bools/?flt0_1=0') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('char_field_val_1' in data) +- ok_('char_field_val_2' not in data) +- ok_('char_field_val_3' not in data) ++ assert 'char_field_val_1' in data ++ assert 'char_field_val_2' not in data ++ assert 'char_field_val_3' not in data + + # Test float filter + view = CustomModelView(Model2, column_filters=['float_field'], + endpoint="_float") + admin.add_view(view) + +- eq_( +- [(f['index'], f['operation']) for f in view._filter_groups[u'Float Field']], ++ assert \ ++ [(f['index'], f['operation']) for f in view._filter_groups[u'Float Field']] == \ + [ + (0, 'equals'), + (1, 'not equal'), +@@ -567,83 +563,82 @@ def test_column_filters(): + (5, 'in list'), + (6, 'not in list'), + ] +- ) + + # float - equals + rv = client.get('/admin/_float/?flt0_0=25.9') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('char_field_val_3' in data) +- ok_('char_field_val_4' not in data) ++ assert 'char_field_val_3' in data ++ assert 'char_field_val_4' not in data + + # float - equals - test validation + rv = client.get('/admin/_float/?flt0_0=badval') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('Invalid Filter Value' in data) ++ assert 'Invalid Filter Value' in data + + # float - not equal + rv = client.get('/admin/_float/?flt0_1=25.9') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('char_field_val_3' not in data) +- ok_('char_field_val_4' in data) ++ assert 'char_field_val_3' not in data ++ assert 'char_field_val_4' in data + + # float - greater + rv = client.get('/admin/_float/?flt0_2=60.5') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('char_field_val_3' not in data) +- ok_('char_field_val_4' in data) ++ assert 'char_field_val_3' not in data ++ assert 'char_field_val_4' in data + + # float - smaller + rv = client.get('/admin/_float/?flt0_3=60.5') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('char_field_val_3' in data) +- ok_('char_field_val_4' not in data) ++ assert 'char_field_val_3' in data ++ assert 'char_field_val_4' not in data + + # float - empty + rv = client.get('/admin/_float/?flt0_4=1') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('char_field_val_1' in data) +- ok_('char_field_val_2' in data) +- ok_('char_field_val_3' not in data) +- ok_('char_field_val_4' not in data) ++ assert 'char_field_val_1' in data ++ assert 'char_field_val_2' in data ++ assert 'char_field_val_3' not in data ++ assert 'char_field_val_4' not in data + + # float - not empty + rv = client.get('/admin/_float/?flt0_4=0') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('char_field_val_1' not in data) +- ok_('char_field_val_2' not in data) +- ok_('char_field_val_3' in data) +- ok_('char_field_val_4' in data) ++ assert 'char_field_val_1' not in data ++ assert 'char_field_val_2' not in data ++ assert 'char_field_val_3' in data ++ assert 'char_field_val_4' in data + + # float - in list + rv = client.get('/admin/_float/?flt0_5=25.9%2C75.5') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('char_field_val_1' not in data) +- ok_('char_field_val_2' not in data) +- ok_('char_field_val_3' in data) +- ok_('char_field_val_4' in data) ++ assert 'char_field_val_1' not in data ++ assert 'char_field_val_2' not in data ++ assert 'char_field_val_3' in data ++ assert 'char_field_val_4' in data + + # float - in list - test validation + rv = client.get('/admin/_float/?flt0_5=25.9%2Cbadval') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('Invalid Filter Value' in data) ++ assert 'Invalid Filter Value' in data + + # float - not in list + rv = client.get('/admin/_float/?flt0_6=25.9%2C75.5') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('char_field_val_1' in data) +- ok_('char_field_val_2' in data) +- ok_('char_field_val_3' not in data) +- ok_('char_field_val_4' not in data) ++ assert 'char_field_val_1' in data ++ assert 'char_field_val_2' in data ++ assert 'char_field_val_3' not in data ++ assert 'char_field_val_4' not in data + + # Test date, time, and datetime filters + view = CustomModelView(Model1, +@@ -651,8 +646,8 @@ def test_column_filters(): + endpoint="_datetime") + admin.add_view(view) + +- eq_( +- [(f['index'], f['operation']) for f in view._filter_groups[u'Date Field']], ++ assert \ ++ [(f['index'], f['operation']) for f in view._filter_groups[u'Date Field']] == \ + [ + (0, 'equals'), + (1, 'not equal'), +@@ -662,10 +657,9 @@ def test_column_filters(): + (5, 'not between'), + (6, 'empty'), + ] +- ) + +- eq_( +- [(f['index'], f['operation']) for f in view._filter_groups[u'Datetime Field']], ++ assert \ ++ [(f['index'], f['operation']) for f in view._filter_groups[u'Datetime Field']] == \ + [ + (7, 'equals'), + (8, 'not equal'), +@@ -675,10 +669,9 @@ def test_column_filters(): + (12, 'not between'), + (13, 'empty'), + ] +- ) + +- eq_( +- [(f['index'], f['operation']) for f in view._filter_groups[u'Timeonly Field']], ++ assert \ ++ [(f['index'], f['operation']) for f in view._filter_groups[u'Timeonly Field']] == \ + [ + (14, 'equals'), + (15, 'not equal'), +@@ -688,181 +681,180 @@ def test_column_filters(): + (19, 'not between'), + (20, 'empty'), + ] +- ) + + # date - equals + rv = client.get('/admin/_datetime/?flt0_0=2014-11-17') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('date_obj1' in data) +- ok_('date_obj2' not in data) ++ assert 'date_obj1' in data ++ assert 'date_obj2' not in data + + # date - not equal + rv = client.get('/admin/_datetime/?flt0_1=2014-11-17') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('date_obj1' not in data) +- ok_('date_obj2' in data) ++ assert 'date_obj1' not in data ++ assert 'date_obj2' in data + + # date - greater + rv = client.get('/admin/_datetime/?flt0_2=2014-11-16') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('date_obj1' in data) +- ok_('date_obj2' not in data) ++ assert 'date_obj1' in data ++ assert 'date_obj2' not in data + + # date - smaller + rv = client.get('/admin/_datetime/?flt0_3=2014-11-16') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('date_obj1' not in data) +- ok_('date_obj2' in data) ++ assert 'date_obj1' not in data ++ assert 'date_obj2' in data + + # date - between + rv = client.get('/admin/_datetime/?flt0_4=2014-11-13+to+2014-11-20') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('date_obj1' in data) +- ok_('date_obj2' not in data) ++ assert 'date_obj1' in data ++ assert 'date_obj2' not in data + + # date - not between + rv = client.get('/admin/_datetime/?flt0_5=2014-11-13+to+2014-11-20') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('date_obj1' not in data) +- ok_('date_obj2' in data) ++ assert 'date_obj1' not in data ++ assert 'date_obj2' in data + + # date - empty + rv = client.get('/admin/_datetime/?flt0_6=1') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('test1_val_1' in data) +- ok_('date_obj1' not in data) +- ok_('date_obj2' not in data) ++ assert 'test1_val_1' in data ++ assert 'date_obj1' not in data ++ assert 'date_obj2' not in data + + # date - empty + rv = client.get('/admin/_datetime/?flt0_6=0') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('test1_val_1' not in data) +- ok_('date_obj1' in data) +- ok_('date_obj2' in data) ++ assert 'test1_val_1' not in data ++ assert 'date_obj1' in data ++ assert 'date_obj2' in data + + # datetime - equals + rv = client.get('/admin/_datetime/?flt0_7=2014-04-03+01%3A09%3A00') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('datetime_obj1' in data) +- ok_('datetime_obj2' not in data) ++ assert 'datetime_obj1' in data ++ assert 'datetime_obj2' not in data + + # datetime - not equal + rv = client.get('/admin/_datetime/?flt0_8=2014-04-03+01%3A09%3A00') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('datetime_obj1' not in data) +- ok_('datetime_obj2' in data) ++ assert 'datetime_obj1' not in data ++ assert 'datetime_obj2' in data + + # datetime - greater + rv = client.get('/admin/_datetime/?flt0_9=2014-04-03+01%3A08%3A00') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('datetime_obj1' in data) +- ok_('datetime_obj2' not in data) ++ assert 'datetime_obj1' in data ++ assert 'datetime_obj2' not in data + + # datetime - smaller + rv = client.get('/admin/_datetime/?flt0_10=2014-04-03+01%3A08%3A00') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('datetime_obj1' not in data) +- ok_('datetime_obj2' in data) ++ assert 'datetime_obj1' not in data ++ assert 'datetime_obj2' in data + + # datetime - between + rv = client.get('/admin/_datetime/?flt0_11=2014-04-02+00%3A00%3A00+to+2014-11-20+23%3A59%3A59') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('datetime_obj1' in data) +- ok_('datetime_obj2' not in data) ++ assert 'datetime_obj1' in data ++ assert 'datetime_obj2' not in data + + # datetime - not between + rv = client.get('/admin/_datetime/?flt0_12=2014-04-02+00%3A00%3A00+to+2014-11-20+23%3A59%3A59') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('datetime_obj1' not in data) +- ok_('datetime_obj2' in data) ++ assert 'datetime_obj1' not in data ++ assert 'datetime_obj2' in data + + # datetime - empty + rv = client.get('/admin/_datetime/?flt0_13=1') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('test1_val_1' in data) +- ok_('datetime_obj1' not in data) +- ok_('datetime_obj2' not in data) ++ assert 'test1_val_1' in data ++ assert 'datetime_obj1' not in data ++ assert 'datetime_obj2' not in data + + # datetime - not empty + rv = client.get('/admin/_datetime/?flt0_13=0') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('test1_val_1' not in data) +- ok_('datetime_obj1' in data) +- ok_('datetime_obj2' in data) ++ assert 'test1_val_1' not in data ++ assert 'datetime_obj1' in data ++ assert 'datetime_obj2' in data + + # time - equals + rv = client.get('/admin/_datetime/?flt0_14=11%3A10%3A09') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('timeonly_obj1' in data) +- ok_('timeonly_obj2' not in data) ++ assert 'timeonly_obj1' in data ++ assert 'timeonly_obj2' not in data + + # time - not equal + rv = client.get('/admin/_datetime/?flt0_15=11%3A10%3A09') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('timeonly_obj1' not in data) +- ok_('timeonly_obj2' in data) ++ assert 'timeonly_obj1' not in data ++ assert 'timeonly_obj2' in data + + # time - greater + rv = client.get('/admin/_datetime/?flt0_16=11%3A09%3A09') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('timeonly_obj1' in data) +- ok_('timeonly_obj2' not in data) ++ assert 'timeonly_obj1' in data ++ assert 'timeonly_obj2' not in data + + # time - smaller + rv = client.get('/admin/_datetime/?flt0_17=11%3A09%3A09') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('timeonly_obj1' not in data) +- ok_('timeonly_obj2' in data) ++ assert 'timeonly_obj1' not in data ++ assert 'timeonly_obj2' in data + + # time - between + rv = client.get('/admin/_datetime/?flt0_18=10%3A40%3A00+to+11%3A50%3A59') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('timeonly_obj1' in data) +- ok_('timeonly_obj2' not in data) ++ assert 'timeonly_obj1' in data ++ assert 'timeonly_obj2' not in data + + # time - not between + rv = client.get('/admin/_datetime/?flt0_19=10%3A40%3A00+to+11%3A50%3A59') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('timeonly_obj1' not in data) +- ok_('timeonly_obj2' in data) ++ assert 'timeonly_obj1' not in data ++ assert 'timeonly_obj2' in data + + # time - empty + rv = client.get('/admin/_datetime/?flt0_20=1') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('test1_val_1' in data) +- ok_('timeonly_obj1' not in data) +- ok_('timeonly_obj2' not in data) ++ assert 'test1_val_1' in data ++ assert 'timeonly_obj1' not in data ++ assert 'timeonly_obj2' not in data + + # time - not empty + rv = client.get('/admin/_datetime/?flt0_20=0') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('test1_val_1' not in data) +- ok_('timeonly_obj1' in data) +- ok_('timeonly_obj2' in data) ++ assert 'test1_val_1' not in data ++ assert 'timeonly_obj1' in data ++ assert 'timeonly_obj2' in data + + + def test_default_sort(): +@@ -873,16 +865,16 @@ def test_default_sort(): + M1('b', 1).save() + M1('a', 2).save() + +- eq_(M1.select().count(), 3) ++ assert M1.select().count() == 3 + + view = CustomModelView(M1, column_default_sort='test1') + admin.add_view(view) + + _, data = view.get_list(0, None, None, None, None) + +- eq_(data[0].test1, 'a') +- eq_(data[1].test1, 'b') +- eq_(data[2].test1, 'c') ++ assert data[0].test1 == 'a' ++ assert data[1].test1 == 'b' ++ assert data[2].test1 == 'c' + + # test default sort with multiple columns + order = [('test2', False), ('test1', False)] +@@ -891,10 +883,10 @@ def test_default_sort(): + + _, data = view2.get_list(0, None, None, None, None) + +- eq_(len(data), 3) +- eq_(data[0].test1, 'b') +- eq_(data[1].test1, 'c') +- eq_(data[2].test1, 'a') ++ assert len(data) == 3 ++ assert data[0].test1 == 'b' ++ assert data[1].test1 == 'c' ++ assert data[2].test1 == 'a' + + + def test_extra_fields(): +@@ -913,14 +905,14 @@ def test_extra_fields(): + client = app.test_client() + + rv = client.get('/admin/model1/new/') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + + # Check presence and order + data = rv.data.decode('utf-8') +- ok_('Extra Field' in data) ++ assert 'Extra Field' in data + pos1 = data.find('Extra Field') + pos2 = data.find('Test1') +- ok_(pos2 < pos1) ++ assert pos2 < pos1 + + + def test_custom_form_base(): +@@ -937,10 +929,10 @@ def test_custom_form_base(): + ) + admin.add_view(view) + +- ok_(hasattr(view._create_form_class, 'test1')) ++ assert hasattr(view._create_form_class, 'test1') + + create_form = view.create_form() +- ok_(isinstance(create_form, TestForm)) ++ assert isinstance(create_form, TestForm) + + + def test_form_args(): +@@ -963,10 +955,10 @@ def test_form_args(): + # ensure shared field_args don't create duplicate validators + create_form = view.create_form() + +- eq_(len(create_form.test.validators), 2) ++ assert len(create_form.test.validators) == 2 + + edit_form = view.edit_form() +- eq_(len(edit_form.test.validators), 2) ++ assert len(edit_form.test.validators) == 2 + + + def test_ajax_fk(): +@@ -1000,7 +992,7 @@ def test_ajax_fk(): + ) + admin.add_view(view) + +- ok_(u'model1' in view._form_ajax_refs) ++ assert u'model1' in view._form_ajax_refs + + model = Model1(test1=u'first', test2=u'') + model.save() +@@ -1010,42 +1002,42 @@ def test_ajax_fk(): + # Check loader + loader = view._form_ajax_refs[u'model1'] + mdl = loader.get_one(model.id) +- eq_(mdl.test1, model.test1) ++ assert mdl.test1 == model.test1 + + items = loader.get_list(u'fir') +- eq_(len(items), 1) +- eq_(items[0].id, model.id) ++ assert len(items) == 1 ++ assert items[0].id == model.id + + items = loader.get_list(u'bar') +- eq_(len(items), 1) +- eq_(items[0].test1, u'foo') ++ assert len(items) == 1 ++ assert items[0].test1 == u'foo' + + # Check form generation + form = view.create_form() +- eq_(form.model1.__class__.__name__, u'AjaxSelectField') ++ assert form.model1.__class__.__name__ == u'AjaxSelectField' + + with app.test_request_context('/admin/view/'): +- ok_(u'value=""' not in form.model1()) ++ assert u'value=""' not in form.model1() + + form.model1.data = model + needle = u'data-json="[%s, "first"]"' % as_unicode(model.id) +- ok_(needle in form.model1()) +- ok_(u'value="%s"' % as_unicode(model.id) in form.model1()) ++ assert needle in form.model1() ++ assert u'value="%s"' % as_unicode(model.id) in form.model1() + + # Check querying + client = app.test_client() + + req = client.get(u'/admin/view/ajax/lookup/?name=model1&query=foo') +- eq_(req.data, u'[[%s, "foo"]]' % model2.id) ++ assert req.data == u'[[%s, "foo"]]' % model2.id + + # Check submitting + client.post('/admin/view/new/', data={u'model1': as_unicode(model.id)}) + mdl = Model2.select().first() + +- ok_(mdl is not None) +- ok_(mdl.model1 is not None) +- eq_(mdl.model1.id, model.id) +- eq_(mdl.model1.test1, u'first') ++ assert mdl is not None ++ assert mdl.model1 is not None ++ assert mdl.model1.id == model.id ++ assert mdl.model1.test1 == u'first' + + + def test_export_csv(): +@@ -1066,10 +1058,10 @@ def test_export_csv(): + # test export_max_rows + rv = client.get('/admin/row_limit_2/export/csv/') + data = rv.data.decode('utf-8') +- eq_(rv.status_code, 200) +- ok_("Test1,Test2\r\n" +- "test1_val_1,test2_val_1\r\n" +- "test1_val_2,test2_val_2\r\n" == data) ++ assert rv.status_code == 200 ++ assert "Test1,Test2\r\n" + \ ++ "test1_val_1,test2_val_1\r\n" + \ ++ "test1_val_2,test2_val_2\r\n" == data + + view = CustomModelView(Model1, can_export=True, + column_list=['test1', 'test2'], +@@ -1079,5 +1071,5 @@ def test_export_csv(): + # test row limit without export_max_rows + rv = client.get('/admin/no_row_limit/export/csv/') + data = rv.data.decode('utf-8') +- eq_(rv.status_code, 200) +- ok_(len(data.splitlines()) > 21) ++ assert rv.status_code == 200 ++ assert len(data.splitlines()) > 21 +--- a/setup.py ++++ b/setup.py +@@ -63,7 +63,7 @@ setup( + extras_require=extras_require, + install_requires=install_requires, + tests_require=[ +- 'nose>=1.0', ++ 'pytest', + 'pillow>=3.3.2', + 'mongoengine', + 'pymongo', +@@ -90,5 +90,5 @@ setup( + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', + ], +- test_suite='nose.collector' ++ test_suite='flask_admin.tests' + ) +--- a/flask_admin/tests/fileadmin/test_fileadmin.py ++++ b/flask_admin/tests/fileadmin/test_fileadmin.py +@@ -2,8 +2,6 @@ import os + import os.path as op + import unittest + +-from nose.tools import eq_, ok_ +- + from flask_admin.contrib import fileadmin + from flask_admin import Admin + from flask import Flask +@@ -45,98 +43,98 @@ class Base: + + # index + rv = client.get('/admin/myfileadmin/') +- eq_(rv.status_code, 200) +- ok_('path=dummy.txt' in rv.data.decode('utf-8')) ++ assert rv.status_code == 200 ++ assert 'path=dummy.txt' in rv.data.decode('utf-8') + + # edit + rv = client.get('/admin/myfileadmin/edit/?path=dummy.txt') +- eq_(rv.status_code, 200) +- ok_('dummy.txt' in rv.data.decode('utf-8')) ++ assert rv.status_code == 200 ++ assert 'dummy.txt' in rv.data.decode('utf-8') + + rv = client.post('/admin/myfileadmin/edit/?path=dummy.txt', + data=dict(content='new_string')) +- eq_(rv.status_code, 302) ++ assert rv.status_code == 302 + + rv = client.get('/admin/myfileadmin/edit/?path=dummy.txt') +- eq_(rv.status_code, 200) +- ok_('dummy.txt' in rv.data.decode('utf-8')) +- ok_('new_string' in rv.data.decode('utf-8')) ++ assert rv.status_code == 200 ++ assert 'dummy.txt' in rv.data.decode('utf-8') ++ assert 'new_string' in rv.data.decode('utf-8') + + # rename + rv = client.get('/admin/myfileadmin/rename/?path=dummy.txt') +- eq_(rv.status_code, 200) +- ok_('dummy.txt' in rv.data.decode('utf-8')) ++ assert rv.status_code == 200 ++ assert 'dummy.txt' in rv.data.decode('utf-8') + + rv = client.post('/admin/myfileadmin/rename/?path=dummy.txt', + data=dict(name='dummy_renamed.txt', + path='dummy.txt')) +- eq_(rv.status_code, 302) ++ assert rv.status_code == 302 + + rv = client.get('/admin/myfileadmin/') +- eq_(rv.status_code, 200) +- ok_('path=dummy_renamed.txt' in rv.data.decode('utf-8')) +- ok_('path=dummy.txt' not in rv.data.decode('utf-8')) ++ assert rv.status_code == 200 ++ assert 'path=dummy_renamed.txt' in rv.data.decode('utf-8') ++ assert 'path=dummy.txt' not in rv.data.decode('utf-8') + + # upload + rv = client.get('/admin/myfileadmin/upload/') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + + rv = client.post('/admin/myfileadmin/upload/', + data=dict(upload=(StringIO(""), 'dummy.txt'))) +- eq_(rv.status_code, 302) ++ assert rv.status_code == 302 + + rv = client.get('/admin/myfileadmin/') +- eq_(rv.status_code, 200) +- ok_('path=dummy.txt' in rv.data.decode('utf-8')) +- ok_('path=dummy_renamed.txt' in rv.data.decode('utf-8')) ++ assert rv.status_code == 200 ++ assert 'path=dummy.txt' in rv.data.decode('utf-8') ++ assert 'path=dummy_renamed.txt' in rv.data.decode('utf-8') + + # delete + rv = client.post('/admin/myfileadmin/delete/', + data=dict(path='dummy_renamed.txt')) +- eq_(rv.status_code, 302) ++ assert rv.status_code == 302 + + rv = client.get('/admin/myfileadmin/') +- eq_(rv.status_code, 200) +- ok_('path=dummy_renamed.txt' not in rv.data.decode('utf-8')) +- ok_('path=dummy.txt' in rv.data.decode('utf-8')) ++ assert rv.status_code == 200 ++ assert 'path=dummy_renamed.txt' not in rv.data.decode('utf-8') ++ assert 'path=dummy.txt' in rv.data.decode('utf-8') + + # mkdir + rv = client.get('/admin/myfileadmin/mkdir/') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + + rv = client.post('/admin/myfileadmin/mkdir/', + data=dict(name='dummy_dir')) +- eq_(rv.status_code, 302) ++ assert rv.status_code == 302 + + rv = client.get('/admin/myfileadmin/') +- eq_(rv.status_code, 200) +- ok_('path=dummy.txt' in rv.data.decode('utf-8')) +- ok_('path=dummy_dir' in rv.data.decode('utf-8')) ++ assert rv.status_code == 200 ++ assert 'path=dummy.txt' in rv.data.decode('utf-8') ++ assert 'path=dummy_dir' in rv.data.decode('utf-8') + + # rename - directory + rv = client.get('/admin/myfileadmin/rename/?path=dummy_dir') +- eq_(rv.status_code, 200) +- ok_('dummy_dir' in rv.data.decode('utf-8')) ++ assert rv.status_code == 200 ++ assert 'dummy_dir' in rv.data.decode('utf-8') + + rv = client.post('/admin/myfileadmin/rename/?path=dummy_dir', + data=dict(name='dummy_renamed_dir', + path='dummy_dir')) +- eq_(rv.status_code, 302) ++ assert rv.status_code == 302 + + rv = client.get('/admin/myfileadmin/') +- eq_(rv.status_code, 200) +- ok_('path=dummy_renamed_dir' in rv.data.decode('utf-8')) +- ok_('path=dummy_dir' not in rv.data.decode('utf-8')) ++ assert rv.status_code == 200 ++ assert 'path=dummy_renamed_dir' in rv.data.decode('utf-8') ++ assert 'path=dummy_dir' not in rv.data.decode('utf-8') + + # delete - directory + rv = client.post('/admin/myfileadmin/delete/', + data=dict(path='dummy_renamed_dir')) +- eq_(rv.status_code, 302) ++ assert rv.status_code == 302 + + rv = client.get('/admin/myfileadmin/') +- eq_(rv.status_code, 200) +- ok_('path=dummy_renamed_dir' not in rv.data.decode('utf-8')) +- ok_('path=dummy.txt' in rv.data.decode('utf-8')) ++ assert rv.status_code == 200 ++ assert 'path=dummy_renamed_dir' not in rv.data.decode('utf-8') ++ assert 'path=dummy.txt' in rv.data.decode('utf-8') + + def test_modal_edit(self): + # bootstrap 2 - test edit_modal +@@ -170,15 +168,15 @@ class Base: + # bootstrap 2 - ensure modal window is added when edit_modal is + # enabled + rv = client_bs2.get('/admin/edit_modal_on/') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('fa_modal_window' in data) ++ assert 'fa_modal_window' in data + + # bootstrap 2 - test edit modal disabled + rv = client_bs2.get('/admin/edit_modal_off/') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('fa_modal_window' not in data) ++ assert 'fa_modal_window' not in data + + # bootstrap 3 + app_bs3 = Flask(__name__) +@@ -192,15 +190,15 @@ class Base: + # bootstrap 3 - ensure modal window is added when edit_modal is + # enabled + rv = client_bs3.get('/admin/edit_modal_on/') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('fa_modal_window' in data) ++ assert 'fa_modal_window' in data + + # bootstrap 3 - test modal disabled + rv = client_bs3.get('/admin/edit_modal_off/') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('fa_modal_window' not in data) ++ assert 'fa_modal_window' not in data + + + class LocalFileAdminTests(Base.FileAdminTests): +@@ -230,14 +228,14 @@ class LocalFileAdminTests(Base.FileAdmin + fp.write('test') + + rv = client.get('/admin/myfileadmin/?sort=bogus') +- eq_(rv.status_code, 200) +- ok_(rv.data.decode('utf-8').find('path=dummy2.txt') < +- rv.data.decode('utf-8').find('path=dummy.txt')) ++ assert rv.status_code == 200 ++ assert rv.data.decode('utf-8').find('path=dummy2.txt') < \ ++ rv.data.decode('utf-8').find('path=dummy.txt') + + rv = client.get('/admin/myfileadmin/?sort=name') +- eq_(rv.status_code, 200) +- ok_(rv.data.decode('utf-8').find('path=dummy.txt') < +- rv.data.decode('utf-8').find('path=dummy2.txt')) ++ assert rv.status_code == 200 ++ assert rv.data.decode('utf-8').find('path=dummy.txt') < \ ++ rv.data.decode('utf-8').find('path=dummy2.txt') + try: + # clean up + os.remove(op.join(self._test_files_root, 'dummy2.txt')) +--- a/flask_admin/tests/fileadmin/test_fileadmin_azure.py ++++ b/flask_admin/tests/fileadmin/test_fileadmin_azure.py +@@ -2,7 +2,7 @@ import os.path as op + from os import getenv + from uuid import uuid4 + +-from nose import SkipTest ++from unittest import SkipTest + + from flask_admin.contrib.fileadmin import azure + +--- a/flask_admin/tests/geoa/test_basic.py ++++ b/flask_admin/tests/geoa/test_basic.py +@@ -6,7 +6,6 @@ from flask_admin.contrib.geoa import Mod + from flask_admin.contrib.geoa.fields import GeoJSONField + from geoalchemy2 import Geometry + from geoalchemy2.shape import to_shape +-from nose.tools import eq_, ok_ + + from . import setup + +@@ -38,27 +37,27 @@ def test_model(): + view = ModelView(GeoModel, db.session) + admin.add_view(view) + +- eq_(view.model, GeoModel) +- eq_(view._primary_key, 'id') ++ assert view.model == GeoModel ++ assert view._primary_key == 'id' + + # Verify form +- eq_(view._create_form_class.point.field_class, GeoJSONField) +- eq_(view._create_form_class.point.kwargs['geometry_type'], "POINT") +- eq_(view._create_form_class.line.field_class, GeoJSONField) +- eq_(view._create_form_class.line.kwargs['geometry_type'], "LINESTRING") +- eq_(view._create_form_class.polygon.field_class, GeoJSONField) +- eq_(view._create_form_class.polygon.kwargs['geometry_type'], "POLYGON") +- eq_(view._create_form_class.multi.field_class, GeoJSONField) +- eq_(view._create_form_class.multi.kwargs['geometry_type'], "MULTIPOINT") ++ assert view._create_form_class.point.field_class == GeoJSONField ++ assert view._create_form_class.point.kwargs['geometry_type'] == "POINT" ++ assert view._create_form_class.line.field_class == GeoJSONField ++ assert view._create_form_class.line.kwargs['geometry_type'] == "LINESTRING" ++ assert view._create_form_class.polygon.field_class == GeoJSONField ++ assert view._create_form_class.polygon.kwargs['geometry_type'] == "POLYGON" ++ assert view._create_form_class.multi.field_class == GeoJSONField ++ assert view._create_form_class.multi.kwargs['geometry_type'] == "MULTIPOINT" + + # Make some test clients + client = app.test_client() + + rv = client.get('/admin/geomodel/') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + + rv = client.get('/admin/geomodel/new/') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + + rv = client.post('/admin/geomodel/new/', data={ + "name": "test1", +@@ -68,62 +67,62 @@ def test_model(): + ' [101.0, 1.0], [100.0, 1.0], [100.0, 0.0]]]}'), + "multi": '{"type": "MultiPoint", "coordinates": [[100.0, 0.0], [101.0, 1.0]]}', + }) +- eq_(rv.status_code, 302) ++ assert rv.status_code == 302 + + model = db.session.query(GeoModel).first() +- eq_(model.name, "test1") +- eq_(to_shape(model.point).geom_type, "Point") +- eq_(list(to_shape(model.point).coords), [(125.8, 10.0)]) +- eq_(to_shape(model.line).geom_type, "LineString") +- eq_(list(to_shape(model.line).coords), [(50.2345, 94.2), (50.21, 94.87)]) +- eq_(to_shape(model.polygon).geom_type, "Polygon") +- eq_(list(to_shape(model.polygon).exterior.coords), +- [(100.0, 0.0), (101.0, 0.0), (101.0, 1.0), (100.0, 1.0), (100.0, 0.0)]) +- eq_(to_shape(model.multi).geom_type, "MultiPoint") +- eq_(len(to_shape(model.multi).geoms), 2) +- eq_(list(to_shape(model.multi).geoms[0].coords), [(100.0, 0.0)]) +- eq_(list(to_shape(model.multi).geoms[1].coords), [(101.0, 1.0)]) ++ assert model.name == "test1" ++ assert to_shape(model.point).geom_type == "Point" ++ assert list(to_shape(model.point).coords) == [(125.8, 10.0)] ++ assert to_shape(model.line).geom_type == "LineString" ++ assert list(to_shape(model.line).coords) == [(50.2345, 94.2), (50.21, 94.87)] ++ assert to_shape(model.polygon).geom_type == "Polygon" ++ assert list(to_shape(model.polygon).exterior.coords) == \ ++ [(100.0, 0.0), (101.0, 0.0), (101.0, 1.0), (100.0, 1.0), (100.0, 0.0)] ++ assert to_shape(model.multi).geom_type == "MultiPoint" ++ assert len(to_shape(model.multi).geoms) == 2 ++ assert list(to_shape(model.multi).geoms[0].coords) == [(100.0, 0.0)] ++ assert list(to_shape(model.multi).geoms[1].coords) == [(101.0, 1.0)] + + rv = client.get('/admin/geomodel/') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + + html = rv.data.decode('utf-8') + pattern = r'(.|\n)+({.*"type": ?"Point".*})(.|\n)+' + group = re.match(pattern, html).group(2) + p = json.loads(group) +- eq_(p['coordinates'][0], 125.8) +- eq_(p['coordinates'][1], 10.0) ++ assert p['coordinates'][0] == 125.8 ++ assert p['coordinates'][1] == 10.0 + + url = '/admin/geomodel/edit/?id=%s' % model.id + rv = client.get(url) +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_(r' name="multi">{"type":"MultiPoint","coordinates":[[100,0],[101,1]]}' in data) ++ assert r' name="multi">{"type":"MultiPoint","coordinates":[[100,0],[101,1]]}' in data + + # rv = client.post(url, data={ + # "name": "edited", + # "point": '{"type": "Point", "coordinates": [99.9, 10.5]}', + # "line": '', # set to NULL in the database + # }) +- # eq_(rv.status_code, 302) ++ # assert rv.status_code == 302 + # + # model = db.session.query(GeoModel).first() +- # eq_(model.name, "edited") +- # eq_(to_shape(model.point).geom_type, "Point") +- # eq_(list(to_shape(model.point).coords), [(99.9, 10.5)]) +- # eq_(to_shape(model.line), None) +- # eq_(to_shape(model.polygon).geom_type, "Polygon") +- # eq_(list(to_shape(model.polygon).exterior.coords), +- # [(100.0, 0.0), (101.0, 0.0), (101.0, 1.0), (100.0, 1.0), (100.0, 0.0)]) +- # eq_(to_shape(model.multi).geom_type, "MultiPoint") +- # eq_(len(to_shape(model.multi).geoms), 2) +- # eq_(list(to_shape(model.multi).geoms[0].coords), [(100.0, 0.0)]) +- # eq_(list(to_shape(model.multi).geoms[1].coords), [(101.0, 1.0)]) ++ # assert model.name == "edited" ++ # assert to_shape(model.point).geom_type == "Point" ++ # assert list(to_shape(model.point).coords) == [(99.9, 10.5)] ++ # assert to_shape(model.line) == None ++ # assert to_shape(model.polygon).geom_type == "Polygon" ++ # assert list(to_shape(model.polygon).exterior.coords) == \ ++ # [(100.0, 0.0), (101.0, 0.0), (101.0, 1.0), (100.0, 1.0), (100.0, 0.0)] ++ # assert to_shape(model.multi).geom_type == "MultiPoint" ++ # assert len(to_shape(model.multi).geoms) == 2 ++ # assert list(to_shape(model.multi).geoms[0].coords) == [(100.0, 0.0)] ++ # assert list(to_shape(model.multi).geoms[1].coords) == [(101.0, 1.0)] + + url = '/admin/geomodel/delete/?id=%s' % model.id + rv = client.post(url) +- eq_(rv.status_code, 302) +- eq_(db.session.query(GeoModel).count(), 0) ++ assert rv.status_code == 302 ++ assert db.session.query(GeoModel).count() == 0 + + + def test_none(): +@@ -142,12 +141,12 @@ def test_none(): + rv = client.post('/admin/geomodel/new/', data={ + "name": "test1", + }) +- eq_(rv.status_code, 302) ++ assert rv.status_code == 302 + + model = db.session.query(GeoModel).first() + + url = '/admin/geomodel/edit/?id=%s' % model.id + rv = client.get(url) +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_(r' name="point">' in data) ++ assert r' name="point">' in data +--- a/flask_admin/tests/mongoengine/test_basic.py ++++ b/flask_admin/tests/mongoengine/test_basic.py +@@ -1,5 +1,3 @@ +-from nose.tools import eq_, ok_ +- + from wtforms import fields, validators + + from flask_admin import form +@@ -73,69 +71,69 @@ def test_model(): + view = CustomModelView(Model1) + admin.add_view(view) + +- eq_(view.model, Model1) +- eq_(view.name, 'Model1') +- eq_(view.endpoint, 'model1') +- +- eq_(view._primary_key, 'id') +- +- ok_('test1' in view._sortable_columns) +- ok_('test2' in view._sortable_columns) +- ok_('test3' in view._sortable_columns) +- ok_('test4' in view._sortable_columns) +- +- ok_(view._create_form_class is not None) +- ok_(view._edit_form_class is not None) +- eq_(view._search_supported, False) +- eq_(view._filters, None) ++ assert view.model == Model1 ++ assert view.name == 'Model1' ++ assert view.endpoint == 'model1' ++ ++ assert view._primary_key == 'id' ++ ++ assert 'test1' in view._sortable_columns ++ assert 'test2' in view._sortable_columns ++ assert 'test3' in view._sortable_columns ++ assert 'test4' in view._sortable_columns ++ ++ assert view._create_form_class is not None ++ assert view._edit_form_class is not None ++ assert not view._search_supported ++ assert view._filters is None + +- eq_(view._create_form_class.test1.field_class, fields.StringField) +- eq_(view._create_form_class.test2.field_class, fields.StringField) ++ assert view._create_form_class.test1.field_class == fields.StringField ++ assert view._create_form_class.test2.field_class == fields.StringField + +- eq_(view._create_form_class.test3.field_class, fields.TextAreaField) +- eq_(view._create_form_class.test4.field_class, fields.TextAreaField) ++ assert view._create_form_class.test3.field_class == fields.TextAreaField ++ assert view._create_form_class.test4.field_class == fields.TextAreaField + + # Make some test clients + client = app.test_client() + + rv = client.get('/admin/model1/') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + + rv = client.get('/admin/model1/new/') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + + rv = client.post('/admin/model1/new/', + data=dict(test1='test1large', test2='test2')) +- eq_(rv.status_code, 302) ++ assert rv.status_code == 302 + + model = Model1.objects.first() +- eq_(model.test1, 'test1large') +- eq_(model.test2, 'test2') +- eq_(model.test3, '') +- eq_(model.test4, '') ++ assert model.test1 == 'test1large' ++ assert model.test2 == 'test2' ++ assert model.test3 == '' ++ assert model.test4 == '' + + rv = client.get('/admin/model1/') +- eq_(rv.status_code, 200) +- ok_('test1large' in rv.data) ++ assert rv.status_code == 200 ++ assert 'test1large' in rv.data + + url = '/admin/model1/edit/?id=%s' % model.id + rv = client.get(url) +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + + rv = client.post(url, + data=dict(test1='test1small', test2='test2large')) +- eq_(rv.status_code, 302) ++ assert rv.status_code == 302 + + model = Model1.objects.first() +- eq_(model.test1, 'test1small') +- eq_(model.test2, 'test2large') +- eq_(model.test3, '') +- eq_(model.test4, '') ++ assert model.test1 == 'test1small' ++ assert model.test2 == 'test2large' ++ assert model.test3 == '' ++ assert model.test4 == '' + + url = '/admin/model1/delete/?id=%s' % model.id + rv = client.post(url) +- eq_(rv.status_code, 302) +- eq_(Model1.objects.count(), 0) ++ assert rv.status_code == 302 ++ assert Model1.objects.count() == 0 + + + def test_column_editable_list(): +@@ -154,7 +152,7 @@ def test_column_editable_list(): + # Test in-line edit field rendering + rv = client.get('/admin/model1/') + data = rv.data.decode('utf-8') +- ok_('data-role="x-editable"' in data) ++ assert 'data-role="x-editable"' in data + + # Form - Test basic in-line edit functionality + obj1 = Model1.objects.get(test1='test1_val_3') +@@ -163,12 +161,12 @@ def test_column_editable_list(): + 'test1': 'change-success-1', + }) + data = rv.data.decode('utf-8') +- ok_('Record was successfully saved.' == data) ++ assert 'Record was successfully saved.' == data + + # confirm the value has changed + rv = client.get('/admin/model1/') + data = rv.data.decode('utf-8') +- ok_('change-success-1' in data) ++ assert 'change-success-1' in data + + # Test validation error + obj2 = Model1.objects.get(test1='datetime_obj1') +@@ -176,7 +174,7 @@ def test_column_editable_list(): + 'list_form_pk': str(obj2.id), + 'datetime_field': 'problematic-input', + }) +- eq_(rv.status_code, 500) ++ assert rv.status_code == 500 + + # Test invalid primary key + rv = client.post('/admin/model1/ajax/update/', data={ +@@ -184,7 +182,7 @@ def test_column_editable_list(): + 'test1': 'problematic-input', + }) + data = rv.data.decode('utf-8') +- eq_(rv.status_code, 500) ++ assert rv.status_code == 500 + + # Test editing column not in column_editable_list + rv = client.post('/admin/model1/ajax/update/', data={ +@@ -192,7 +190,7 @@ def test_column_editable_list(): + 'test2': 'problematic-input', + }) + data = rv.data.decode('utf-8') +- ok_('problematic-input' not in data) ++ assert 'problematic-input' not in data + + # Test in-line editing for relations + view = CustomModelView(Model2, column_editable_list=['model1']) +@@ -204,12 +202,12 @@ def test_column_editable_list(): + 'model1': str(obj1.id), + }) + data = rv.data.decode('utf-8') +- ok_('Record was successfully saved.' == data) ++ assert 'Record was successfully saved.' == data + + # confirm the value has changed + rv = client.get('/admin/model2/') + data = rv.data.decode('utf-8') +- ok_('test1_val_1' in data) ++ assert 'test1_val_1' in data + + + def test_details_view(): +@@ -240,33 +238,33 @@ def test_details_view(): + # ensure link to details is hidden when can_view_details is disabled + rv = client.get('/admin/model1/') + data = rv.data.decode('utf-8') +- ok_('/admin/model1/details/' not in data) ++ assert '/admin/model1/details/' not in data + + # ensure link to details view appears + rv = client.get('/admin/model2/') + data = rv.data.decode('utf-8') +- ok_('/admin/model2/details/' in data) ++ assert '/admin/model2/details/' in data + + # test redirection when details are disabled + url = '/admin/model1/details/?url=%2Fadmin%2Fmodel1%2F&id=' + str(m1_id) + rv = client.get(url) +- eq_(rv.status_code, 302) ++ assert rv.status_code == 302 + + # test if correct data appears in details view when enabled + url = '/admin/model2/details/?url=%2Fadmin%2Fmodel2%2F&id=' + str(m2_id) + rv = client.get(url) + data = rv.data.decode('utf-8') +- ok_('String Field' in data) +- ok_('string_field_val_1' in data) +- ok_('Int Field' in data) ++ assert 'String Field' in data ++ assert 'string_field_val_1' in data ++ assert 'Int Field' in data + + # test column_details_list + url = '/admin/sf_view/details/?url=%2Fadmin%2Fsf_view%2F&id=' + str(m2_id) + rv = client.get(url) + data = rv.data.decode('utf-8') +- ok_('String Field' in data) +- ok_('string_field_val_1' in data) +- ok_('Int Field' not in data) ++ assert 'String Field' in data ++ assert 'string_field_val_1' in data ++ assert 'Int Field' not in data + + + def test_column_filters(): +@@ -281,10 +279,10 @@ def test_column_filters(): + view = CustomModelView(Model1, column_filters=['test1']) + admin.add_view(view) + +- eq_(len(view._filters), 7) ++ assert len(view._filters) == 7 + +- eq_( +- [(f['index'], f['operation']) for f in view._filter_groups[u'Test1']], ++ assert \ ++ [(f['index'], f['operation']) for f in view._filter_groups[u'Test1']] == \ + [ + (0, 'contains'), + (1, 'not contains'), +@@ -294,79 +292,78 @@ def test_column_filters(): + (5, 'in list'), + (6, 'not in list'), + ] +- ) + + # Make some test clients + client = app.test_client() + + # string - equals + rv = client.get('/admin/model1/?flt0_0=test1_val_1') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('test2_val_1' in data) +- ok_('test1_val_2' not in data) ++ assert 'test2_val_1' in data ++ assert 'test1_val_2' not in data + + # string - not equal + rv = client.get('/admin/model1/?flt0_1=test1_val_1') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('test2_val_1' not in data) +- ok_('test1_val_2' in data) ++ assert 'test2_val_1' not in data ++ assert 'test1_val_2' in data + + # string - contains + rv = client.get('/admin/model1/?flt0_2=test1_val_1') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('test2_val_1' in data) +- ok_('test1_val_2' not in data) ++ assert 'test2_val_1' in data ++ assert 'test1_val_2' not in data + + # string - not contains + rv = client.get('/admin/model1/?flt0_3=test1_val_1') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('test2_val_1' not in data) +- ok_('test1_val_2' in data) ++ assert 'test2_val_1' not in data ++ assert 'test1_val_2' in data + + # string - empty + rv = client.get('/admin/model1/?flt0_4=1') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('empty_obj' in data) +- ok_('test1_val_1' not in data) +- ok_('test1_val_2' not in data) ++ assert 'empty_obj' in data ++ assert 'test1_val_1' not in data ++ assert 'test1_val_2' not in data + + # string - not empty + rv = client.get('/admin/model1/?flt0_4=0') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('empty_obj' not in data) +- ok_('test1_val_1' in data) +- ok_('test1_val_2' in data) ++ assert 'empty_obj' not in data ++ assert 'test1_val_1' in data ++ assert 'test1_val_2' in data + + # string - in list + rv = client.get('/admin/model1/?flt0_5=test1_val_1%2Ctest1_val_2') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('test2_val_1' in data) +- ok_('test2_val_2' in data) +- ok_('test1_val_3' not in data) +- ok_('test1_val_4' not in data) ++ assert 'test2_val_1' in data ++ assert 'test2_val_2' in data ++ assert 'test1_val_3' not in data ++ assert 'test1_val_4' not in data + + # string - not in list + rv = client.get('/admin/model1/?flt0_6=test1_val_1%2Ctest1_val_2') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('test2_val_1' not in data) +- ok_('test2_val_2' not in data) +- ok_('test1_val_3' in data) +- ok_('test1_val_4' in data) ++ assert 'test2_val_1' not in data ++ assert 'test2_val_2' not in data ++ assert 'test1_val_3' in data ++ assert 'test1_val_4' in data + + # Test numeric filter + view = CustomModelView(Model2, column_filters=['int_field']) + admin.add_view(view) + +- eq_( +- [(f['index'], f['operation']) for f in view._filter_groups[u'Int Field']], ++ assert \ ++ [(f['index'], f['operation']) for f in view._filter_groups[u'Int Field']] == \ + [ + (0, 'equals'), + (1, 'not equal'), +@@ -376,146 +373,144 @@ def test_column_filters(): + (5, 'in list'), + (6, 'not in list'), + ] +- ) + + # integer - equals + rv = client.get('/admin/model2/?flt0_0=5000') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('string_field_val_3' in data) +- ok_('string_field_val_4' not in data) ++ assert 'string_field_val_3' in data ++ assert 'string_field_val_4' not in data + + # integer - equals (huge number) + rv = client.get('/admin/model2/?flt0_0=6169453081680413441') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('string_field_val_5' in data) +- ok_('string_field_val_4' not in data) ++ assert 'string_field_val_5' in data ++ assert 'string_field_val_4' not in data + + # integer - equals - test validation + rv = client.get('/admin/model2/?flt0_0=badval') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('Invalid Filter Value' in data) ++ assert 'Invalid Filter Value' in data + + # integer - not equal + rv = client.get('/admin/model2/?flt0_1=5000') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('string_field_val_3' not in data) +- ok_('string_field_val_4' in data) ++ assert 'string_field_val_3' not in data ++ assert 'string_field_val_4' in data + + # integer - greater + rv = client.get('/admin/model2/?flt0_2=6000') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('string_field_val_3' not in data) +- ok_('string_field_val_4' in data) ++ assert 'string_field_val_3' not in data ++ assert 'string_field_val_4' in data + + # integer - smaller + rv = client.get('/admin/model2/?flt0_3=6000') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('string_field_val_3' in data) +- ok_('string_field_val_4' not in data) ++ assert 'string_field_val_3' in data ++ assert 'string_field_val_4' not in data + + # integer - empty + rv = client.get('/admin/model2/?flt0_4=1') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('string_field_val_1' in data) +- ok_('string_field_val_2' in data) +- ok_('string_field_val_3' not in data) +- ok_('string_field_val_4' not in data) ++ assert 'string_field_val_1' in data ++ assert 'string_field_val_2' in data ++ assert 'string_field_val_3' not in data ++ assert 'string_field_val_4' not in data + + # integer - not empty + rv = client.get('/admin/model2/?flt0_4=0') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('string_field_val_1' not in data) +- ok_('string_field_val_2' not in data) +- ok_('string_field_val_3' in data) +- ok_('string_field_val_4' in data) ++ assert 'string_field_val_1' not in data ++ assert 'string_field_val_2' not in data ++ assert 'string_field_val_3' in data ++ assert 'string_field_val_4' in data + + # integer - in list + rv = client.get('/admin/model2/?flt0_5=5000%2C9000') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('string_field_val_1' not in data) +- ok_('string_field_val_2' not in data) +- ok_('string_field_val_3' in data) +- ok_('string_field_val_4' in data) ++ assert 'string_field_val_1' not in data ++ assert 'string_field_val_2' not in data ++ assert 'string_field_val_3' in data ++ assert 'string_field_val_4' in data + + # integer - in list (huge number) + rv = client.get('/admin/model2/?flt0_5=6169453081680413441') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('string_field_val_1' not in data) +- ok_('string_field_val_5' in data) ++ assert 'string_field_val_1' not in data ++ assert 'string_field_val_5' in data + + # integer - in list - test validation + rv = client.get('/admin/model2/?flt0_5=5000%2Cbadval') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('Invalid Filter Value' in data) ++ assert 'Invalid Filter Value' in data + + # integer - not in list + rv = client.get('/admin/model2/?flt0_6=5000%2C9000') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('string_field_val_1' in data) +- ok_('string_field_val_2' in data) +- ok_('string_field_val_3' not in data) +- ok_('string_field_val_4' not in data) ++ assert 'string_field_val_1' in data ++ assert 'string_field_val_2' in data ++ assert 'string_field_val_3' not in data ++ assert 'string_field_val_4' not in data + + # Test boolean filter + view = CustomModelView(Model2, column_filters=['bool_field'], + endpoint="_bools") + admin.add_view(view) + +- eq_( +- [(f['index'], f['operation']) for f in view._filter_groups[u'Bool Field']], ++ assert \ ++ [(f['index'], f['operation']) for f in view._filter_groups[u'Bool Field']] == \ + [ + (0, 'equals'), + (1, 'not equal'), + ] +- ) + + # boolean - equals - Yes + rv = client.get('/admin/_bools/?flt0_0=1') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('string_field_val_1' in data) +- ok_('string_field_val_2' not in data) ++ assert 'string_field_val_1' in data ++ assert 'string_field_val_2' not in data + + # boolean - equals - No + rv = client.get('/admin/_bools/?flt0_0=0') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('string_field_val_1' not in data) +- ok_('string_field_val_2' in data) ++ assert 'string_field_val_1' not in data ++ assert 'string_field_val_2' in data + + # boolean - not equals - Yes + rv = client.get('/admin/_bools/?flt0_1=1') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('string_field_val_1' not in data) +- ok_('string_field_val_2' in data) ++ assert 'string_field_val_1' not in data ++ assert 'string_field_val_2' in data + + # boolean - not equals - No + rv = client.get('/admin/_bools/?flt0_1=0') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('string_field_val_1' in data) +- ok_('string_field_val_2' not in data) ++ assert 'string_field_val_1' in data ++ assert 'string_field_val_2' not in data + + # Test float filter + view = CustomModelView(Model2, column_filters=['float_field'], + endpoint="_float") + admin.add_view(view) + +- eq_( +- [(f['index'], f['operation']) for f in view._filter_groups[u'Float Field']], ++ assert \ ++ [(f['index'], f['operation']) for f in view._filter_groups[u'Float Field']] == \ + [ + (0, 'equals'), + (1, 'not equal'), +@@ -525,83 +520,82 @@ def test_column_filters(): + (5, 'in list'), + (6, 'not in list'), + ] +- ) + + # float - equals + rv = client.get('/admin/_float/?flt0_0=25.9') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('string_field_val_3' in data) +- ok_('string_field_val_4' not in data) ++ assert 'string_field_val_3' in data ++ assert 'string_field_val_4' not in data + + # float - equals - test validation + rv = client.get('/admin/_float/?flt0_0=badval') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('Invalid Filter Value' in data) ++ assert 'Invalid Filter Value' in data + + # float - not equal + rv = client.get('/admin/_float/?flt0_1=25.9') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('string_field_val_3' not in data) +- ok_('string_field_val_4' in data) ++ assert 'string_field_val_3' not in data ++ assert 'string_field_val_4' in data + + # float - greater + rv = client.get('/admin/_float/?flt0_2=60.5') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('string_field_val_3' not in data) +- ok_('string_field_val_4' in data) ++ assert 'string_field_val_3' not in data ++ assert 'string_field_val_4' in data + + # float - smaller + rv = client.get('/admin/_float/?flt0_3=60.5') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('string_field_val_3' in data) +- ok_('string_field_val_4' not in data) ++ assert 'string_field_val_3' in data ++ assert 'string_field_val_4' not in data + + # float - empty + rv = client.get('/admin/_float/?flt0_4=1') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('string_field_val_1' in data) +- ok_('string_field_val_2' in data) +- ok_('string_field_val_3' not in data) +- ok_('string_field_val_4' not in data) ++ assert 'string_field_val_1' in data ++ assert 'string_field_val_2' in data ++ assert 'string_field_val_3' not in data ++ assert 'string_field_val_4' not in data + + # float - not empty + rv = client.get('/admin/_float/?flt0_4=0') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('string_field_val_1' not in data) +- ok_('string_field_val_2' not in data) +- ok_('string_field_val_3' in data) +- ok_('string_field_val_4' in data) ++ assert 'string_field_val_1' not in data ++ assert 'string_field_val_2' not in data ++ assert 'string_field_val_3' in data ++ assert 'string_field_val_4' in data + + # float - in list + rv = client.get('/admin/_float/?flt0_5=25.9%2C75.5') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('string_field_val_1' not in data) +- ok_('string_field_val_2' not in data) +- ok_('string_field_val_3' in data) +- ok_('string_field_val_4' in data) ++ assert 'string_field_val_1' not in data ++ assert 'string_field_val_2' not in data ++ assert 'string_field_val_3' in data ++ assert 'string_field_val_4' in data + + # float - in list - test validation + rv = client.get('/admin/_float/?flt0_5=25.9%2Cbadval') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('Invalid Filter Value' in data) ++ assert 'Invalid Filter Value' in data + + # float - not in list + rv = client.get('/admin/_float/?flt0_6=25.9%2C75.5') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('string_field_val_1' in data) +- ok_('string_field_val_2' in data) +- ok_('string_field_val_3' not in data) +- ok_('string_field_val_4' not in data) ++ assert 'string_field_val_1' in data ++ assert 'string_field_val_2' in data ++ assert 'string_field_val_3' not in data ++ assert 'string_field_val_4' not in data + + # Test datetime filter + view = CustomModelView(Model1, +@@ -609,8 +603,8 @@ def test_column_filters(): + endpoint="_datetime") + admin.add_view(view) + +- eq_( +- [(f['index'], f['operation']) for f in view._filter_groups[u'Datetime Field']], ++ assert \ ++ [(f['index'], f['operation']) for f in view._filter_groups[u'Datetime Field']] == \ + [ + (0, 'equals'), + (1, 'not equal'), +@@ -620,65 +614,64 @@ def test_column_filters(): + (5, 'not between'), + (6, 'empty'), + ] +- ) + + # datetime - equals + rv = client.get('/admin/_datetime/?flt0_0=2014-04-03+01%3A09%3A00') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('datetime_obj1' in data) +- ok_('datetime_obj2' not in data) ++ assert 'datetime_obj1' in data ++ assert 'datetime_obj2' not in data + + # datetime - not equal + rv = client.get('/admin/_datetime/?flt0_1=2014-04-03+01%3A09%3A00') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('datetime_obj1' not in data) +- ok_('datetime_obj2' in data) ++ assert 'datetime_obj1' not in data ++ assert 'datetime_obj2' in data + + # datetime - greater + rv = client.get('/admin/_datetime/?flt0_2=2014-04-03+01%3A08%3A00') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('datetime_obj1' in data) +- ok_('datetime_obj2' not in data) ++ assert 'datetime_obj1' in data ++ assert 'datetime_obj2' not in data + + # datetime - smaller + rv = client.get('/admin/_datetime/?flt0_3=2014-04-03+01%3A08%3A00') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('datetime_obj1' not in data) +- ok_('datetime_obj2' in data) ++ assert 'datetime_obj1' not in data ++ assert 'datetime_obj2' in data + + # datetime - between + rv = client.get('/admin/_datetime/?flt0_4=2014-04-02+00%3A00%3A00+to+2014-11-20+23%3A59%3A59') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('datetime_obj1' in data) +- ok_('datetime_obj2' not in data) ++ assert 'datetime_obj1' in data ++ assert 'datetime_obj2' not in data + + # datetime - not between + rv = client.get('/admin/_datetime/?flt0_5=2014-04-02+00%3A00%3A00+to+2014-11-20+23%3A59%3A59') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('datetime_obj1' not in data) +- ok_('datetime_obj2' in data) ++ assert 'datetime_obj1' not in data ++ assert 'datetime_obj2' in data + + # datetime - empty + rv = client.get('/admin/_datetime/?flt0_6=1') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('test1_val_1' in data) +- ok_('datetime_obj1' not in data) +- ok_('datetime_obj2' not in data) ++ assert 'test1_val_1' in data ++ assert 'datetime_obj1' not in data ++ assert 'datetime_obj2' not in data + + # datetime - not empty + rv = client.get('/admin/_datetime/?flt0_6=0') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('test1_val_1' not in data) +- ok_('datetime_obj1' in data) +- ok_('datetime_obj2' in data) ++ assert 'test1_val_1' not in data ++ assert 'datetime_obj1' in data ++ assert 'datetime_obj2' in data + + + def test_default_sort(): +@@ -689,16 +682,16 @@ def test_default_sort(): + M1(test1='b', test2='x').save() + M1(test1='a', test2='y').save() + +- eq_(M1.objects.count(), 3) ++ assert M1.objects.count() == 3 + + view = CustomModelView(M1, column_default_sort='test1') + admin.add_view(view) + + _, data = view.get_list(0, None, None, None, None) + +- eq_(data[0].test1, 'a') +- eq_(data[1].test1, 'b') +- eq_(data[2].test1, 'c') ++ assert data[0].test1 == 'a' ++ assert data[1].test1 == 'b' ++ assert data[2].test1 == 'c' + + # test default sort with multiple columns + order = [('test2', False), ('test1', False)] +@@ -707,10 +700,10 @@ def test_default_sort(): + + _, data = view2.get_list(0, None, None, None, None) + +- eq_(len(data), 3) +- eq_(data[0].test1, 'b') +- eq_(data[1].test1, 'c') +- eq_(data[2].test1, 'a') ++ assert len(data) == 3 ++ assert data[0].test1 == 'b' ++ assert data[1].test1 == 'c' ++ assert data[2].test1 == 'a' + + + def test_extra_fields(): +@@ -729,14 +722,14 @@ def test_extra_fields(): + client = app.test_client() + + rv = client.get('/admin/model1/new/') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + + # Check presence and order + data = rv.data.decode('utf-8') +- ok_('Extra Field' in data) ++ assert 'Extra Field' in data + pos1 = data.find('Extra Field') + pos2 = data.find('Test1') +- ok_(pos2 < pos1) ++ assert pos2 < pos1 + + + def test_extra_field_order(): +@@ -755,14 +748,14 @@ def test_extra_field_order(): + client = app.test_client() + + rv = client.get('/admin/model1/new/') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + + # Check presence and order + data = rv.data.decode('utf-8') +- ok_('Extra Field' in data) ++ assert 'Extra Field' in data + pos1 = data.find('Extra Field') + pos2 = data.find('Test1') +- ok_(pos2 < pos1) ++ assert pos2 < pos1 + + + def test_custom_form_base(): +@@ -779,10 +772,10 @@ def test_custom_form_base(): + ) + admin.add_view(view) + +- ok_(hasattr(view._create_form_class, 'test1')) ++ assert hasattr(view._create_form_class, 'test1') + + create_form = view.create_form() +- ok_(isinstance(create_form, TestForm)) ++ assert isinstance(create_form, TestForm) + + + def test_subdocument_config(): +@@ -806,11 +799,11 @@ def test_subdocument_config(): + } + ) + +- ok_(hasattr(view1._create_form_class, 'subdoc')) ++ assert hasattr(view1._create_form_class, 'subdoc') + + form = view1.create_form() +- ok_('name' in dir(form.subdoc.form)) +- ok_('value' not in dir(form.subdoc.form)) ++ assert 'name' in dir(form.subdoc.form) ++ assert 'value' not in dir(form.subdoc.form) + + # Check exclude + view2 = CustomModelView( +@@ -823,8 +816,8 @@ def test_subdocument_config(): + ) + + form = view2.create_form() +- ok_('name' in dir(form.subdoc.form)) +- ok_('value' not in dir(form.subdoc.form)) ++ assert 'name' in dir(form.subdoc.form) ++ assert 'value' not in dir(form.subdoc.form) + + + def test_subdocument_class_config(): +@@ -852,8 +845,8 @@ def test_subdocument_class_config(): + ) + + form = view1.create_form() +- ok_('name' in dir(form.subdoc.form)) +- ok_('value' not in dir(form.subdoc.form)) ++ assert 'name' in dir(form.subdoc.form) ++ assert 'value' not in dir(form.subdoc.form) + + + def test_nested_subdocument_config(): +@@ -886,8 +879,8 @@ def test_nested_subdocument_config(): + ) + + form = view1.create_form() +- ok_('name' in dir(form.nested.form.comment.form)) +- ok_('value' not in dir(form.nested.form.comment.form)) ++ assert 'name' in dir(form.nested.form.comment.form) ++ assert 'value' not in dir(form.nested.form.comment.form) + + + def test_nested_list_subdocument(): +@@ -919,8 +912,8 @@ def test_nested_list_subdocument(): + form = view1.create_form() + inline_form = form.subdoc.unbound_field.args[2] + +- ok_('name' in dir(inline_form)) +- ok_('value' not in dir(inline_form)) ++ assert 'name' in dir(inline_form) ++ assert 'value' not in dir(inline_form) + + + def test_nested_sortedlist_subdocument(): +@@ -951,8 +944,8 @@ def test_nested_sortedlist_subdocument() + form = view1.create_form() + inline_form = form.subdoc.unbound_field.args[2] + +- ok_('name' in dir(inline_form)) +- ok_('value' not in dir(inline_form)) ++ assert 'name' in dir(inline_form) ++ assert 'value' not in dir(inline_form) + + + def test_sortedlist_subdocument_validation(): +@@ -972,12 +965,12 @@ def test_sortedlist_subdocument_validati + + rv = client.post('/admin/model1/new/', + data={'test1': 'test1large', 'subdoc-0-name': 'comment', 'subdoc-0-value': 'test'}) +- eq_(rv.status_code, 302) ++ assert rv.status_code == 302 + + rv = client.post('/admin/model1/new/', + data={'test1': 'test1large', 'subdoc-0-name': '', 'subdoc-0-value': 'test'}) +- eq_(rv.status_code, 200) +- ok_('This field is required' in rv.data) ++ assert rv.status_code == 200 ++ assert 'This field is required' in rv.data + + + def test_list_subdocument_validation(): +@@ -997,12 +990,12 @@ def test_list_subdocument_validation(): + + rv = client.post('/admin/model1/new/', + data={'test1': 'test1large', 'subdoc-0-name': 'comment', 'subdoc-0-value': 'test'}) +- eq_(rv.status_code, 302) ++ assert rv.status_code == 302 + + rv = client.post('/admin/model1/new/', + data={'test1': 'test1large', 'subdoc-0-name': '', 'subdoc-0-value': 'test'}) +- eq_(rv.status_code, 200) +- ok_('This field is required' in rv.data) ++ assert rv.status_code == 200 ++ assert 'This field is required' in rv.data + + + def test_ajax_fk(): +@@ -1021,7 +1014,7 @@ def test_ajax_fk(): + ) + admin.add_view(view) + +- ok_(u'model1' in view._form_ajax_refs) ++ assert u'model1' in view._form_ajax_refs + + model = Model1(test1=u'first') + model.save() +@@ -1030,42 +1023,42 @@ def test_ajax_fk(): + # Check loader + loader = view._form_ajax_refs[u'model1'] + mdl = loader.get_one(model.id) +- eq_(mdl.test1, model.test1) ++ assert mdl.test1 == model.test1 + + items = loader.get_list(u'fir') +- eq_(len(items), 1) +- eq_(items[0].id, model.id) ++ assert len(items) == 1 ++ assert items[0].id == model.id + + items = loader.get_list(u'bar') +- eq_(len(items), 1) +- eq_(items[0].test1, u'foo') ++ assert len(items) == 1 ++ assert items[0].test1 == u'foo' + + # Check form generation + form = view.create_form() +- eq_(form.model1.__class__.__name__, u'AjaxSelectField') ++ assert form.model1.__class__.__name__ == u'AjaxSelectField' + + with app.test_request_context('/admin/view/'): +- ok_(u'value=""' not in form.model1()) ++ assert u'value=""' not in form.model1() + + form.model1.data = model + needle = u'data-json="["%s", "first"]"' % as_unicode(model.id) +- ok_(needle in form.model1()) +- ok_(u'value="%s"' % as_unicode(model.id) in form.model1()) ++ assert needle in form.model1() ++ assert u'value="%s"' % as_unicode(model.id) in form.model1() + + # Check querying + client = app.test_client() + + req = client.get(u'/admin/view/ajax/lookup/?name=model1&query=foo') +- eq_(req.data, u'[["%s", "foo"]]' % model2.id) ++ assert req.data == u'[["%s", "foo"]]' % model2.id + + # Check submitting + client.post('/admin/view/new/', data={u'model1': as_unicode(model.id)}) + mdl = Model2.objects.first() + +- ok_(mdl is not None) +- ok_(mdl.model1 is not None) +- eq_(mdl.model1.id, model.id) +- eq_(mdl.model1.test1, u'first') ++ assert mdl is not None ++ assert mdl.model1 is not None ++ assert mdl.model1.id == model.id ++ assert mdl.model1.test1 == u'first' + + + def test_nested_ajax_refs(): +@@ -1098,8 +1091,8 @@ def test_nested_ajax_refs(): + ) + + form = view1.create_form() +- eq_(type(form.nested.form.comment).__name__, 'AjaxSelectField') +- ok_('nested-comment' in view1._form_ajax_refs) ++ assert type(form.nested.form.comment).__name__ == 'AjaxSelectField' ++ assert 'nested-comment' in view1._form_ajax_refs + + + def test_form_flat_choices(): +@@ -1112,7 +1105,7 @@ def test_form_flat_choices(): + admin.add_view(view) + + form = view.create_form() +- eq_(form.name.choices, [('a', 'a'), ('b', 'b'), ('c', 'c')]) ++ assert form.name.choices == [('a', 'a'), ('b', 'b'), ('c', 'c')] + + + def test_form_args(): +@@ -1128,10 +1121,10 @@ def test_form_args(): + + # ensure shared field_args don't create duplicate validators + create_form = view.create_form() +- eq_(len(create_form.test.validators), 2) ++ assert len(create_form.test.validators) == 2 + + edit_form = view.edit_form() +- eq_(len(edit_form.test.validators), 2) ++ assert len(edit_form.test.validators) == 2 + + + def test_form_args_embeddeddoc(): +@@ -1154,9 +1147,9 @@ def test_form_args_embeddeddoc(): + ) + admin.add_view(view) + form = view.create_form() +- eq_(form.timestamp.label.text, 'Last Updated Time') ++ assert form.timestamp.label.text == 'Last Updated Time' + # This is the failure +- eq_(form.info.label.text, 'Information') ++ assert form.info.label.text == 'Information' + + + def test_simple_list_pager(): +@@ -1173,7 +1166,7 @@ def test_simple_list_pager(): + admin.add_view(view) + + count, data = view.get_list(0, None, None, None, None) +- ok_(count is None) ++ assert count is None + + + def test_export_csv(): +@@ -1193,10 +1186,10 @@ def test_export_csv(): + # test export_max_rows + rv = client.get('/admin/row_limit_2/export/csv/') + data = rv.data.decode('utf-8') +- eq_(rv.status_code, 200) +- ok_("Test1,Test2\r\n" +- "test1_val_1,test2_val_1\r\n" +- "test1_val_2,test2_val_2\r\n" == data) ++ assert rv.status_code == 200 ++ assert "Test1,Test2\r\n" + \ ++ "test1_val_1,test2_val_1\r\n" + \ ++ "test1_val_2,test2_val_2\r\n" == data + + view = CustomModelView(Model1, can_export=True, + column_list=['test1', 'test2'], +@@ -1206,5 +1199,5 @@ def test_export_csv(): + # test row limit without export_max_rows + rv = client.get('/admin/no_row_limit/export/csv/') + data = rv.data.decode('utf-8') +- eq_(rv.status_code, 200) +- ok_(len(data.splitlines()) > 21) ++ assert rv.status_code == 200 ++ assert len(data.splitlines()) > 21 +--- a/flask_admin/tests/pymongo/test_basic.py ++++ b/flask_admin/tests/pymongo/test_basic.py +@@ -1,5 +1,3 @@ +-from nose.tools import eq_, ok_ +- + from wtforms import form, fields + + from flask_admin.contrib.pymongo import ModelView +@@ -28,54 +26,54 @@ def test_model(): + # Drop existing data (if any) + db.test.remove() + +- eq_(view.name, 'Test') +- eq_(view.endpoint, 'testview') ++ assert view.name == 'Test' ++ assert view.endpoint == 'testview' + +- ok_('test1' in view._sortable_columns) +- ok_('test2' in view._sortable_columns) ++ assert 'test1' in view._sortable_columns ++ assert 'test2' in view._sortable_columns + +- ok_(view._create_form_class is not None) +- ok_(view._edit_form_class is not None) +- eq_(view._search_supported, False) +- eq_(view._filters, None) ++ assert view._create_form_class is not None ++ assert view._edit_form_class is not None ++ assert not view._search_supported ++ assert view._filters is None + + # Make some test clients + client = app.test_client() + + rv = client.get('/admin/testview/') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + + rv = client.get('/admin/testview/new/') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + + rv = client.post('/admin/testview/new/', + data=dict(test1='test1large', test2='test2')) +- eq_(rv.status_code, 302) ++ assert rv.status_code == 302 + + model = db.test.find()[0] + print(model) +- eq_(model['test1'], 'test1large') +- eq_(model['test2'], 'test2') ++ assert model['test1'] == 'test1large' ++ assert model['test2'] == 'test2' + + rv = client.get('/admin/testview/') +- eq_(rv.status_code, 200) +- ok_('test1large' in rv.data.decode('utf-8')) ++ assert rv.status_code == 200 ++ assert 'test1large' in rv.data.decode('utf-8') + + url = '/admin/testview/edit/?id=%s' % model['_id'] + rv = client.get(url) +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + + rv = client.post(url, + data=dict(test1='test1small', test2='test2large')) +- eq_(rv.status_code, 302) ++ assert rv.status_code == 302 + + print(db.test.find()[0]) + + model = db.test.find()[0] +- eq_(model['test1'], 'test1small') +- eq_(model['test2'], 'test2large') ++ assert model['test1'] == 'test1small' ++ assert model['test2'] == 'test2large' + + url = '/admin/testview/delete/?id=%s' % model['_id'] + rv = client.post(url) +- eq_(rv.status_code, 302) +- eq_(db.test.count(), 0) ++ assert rv.status_code == 302 ++ assert db.test.count() == 0 +--- a/flask_admin/tests/sqla/test_basic.py ++++ b/flask_admin/tests/sqla/test_basic.py +@@ -1,4 +1,5 @@ +-from nose.tools import eq_, ok_, raises, assert_true ++import re ++import pytest + + from wtforms import fields, validators + +@@ -163,49 +164,49 @@ def test_model(): + + admin.add_view(view) + +- eq_(view.model, Model1) +- eq_(view.name, 'Model1') +- eq_(view.endpoint, 'model1') +- +- eq_(view._primary_key, 'id') +- +- ok_('test1' in view._sortable_columns) +- ok_('test2' in view._sortable_columns) +- ok_('test3' in view._sortable_columns) +- ok_('test4' in view._sortable_columns) +- +- ok_(view._create_form_class is not None) +- ok_(view._edit_form_class is not None) +- eq_(view._search_supported, False) +- eq_(view._filters, None) ++ assert view.model == Model1 ++ assert view.name == 'Model1' ++ assert view.endpoint == 'model1' ++ ++ assert view._primary_key == 'id' ++ ++ assert 'test1' in view._sortable_columns ++ assert 'test2' in view._sortable_columns ++ assert 'test3' in view._sortable_columns ++ assert 'test4' in view._sortable_columns ++ ++ assert view._create_form_class is not None ++ assert view._edit_form_class is not None ++ assert not view._search_supported ++ assert view._filters is None + + # Verify form +- eq_(view._create_form_class.test1.field_class, fields.StringField) +- eq_(view._create_form_class.test2.field_class, fields.StringField) +- eq_(view._create_form_class.test3.field_class, fields.TextAreaField) +- eq_(view._create_form_class.test4.field_class, fields.TextAreaField) +- eq_(view._create_form_class.email_field.field_class, fields.StringField) +- eq_(view._create_form_class.choice_field.field_class, Select2Field) +- eq_(view._create_form_class.enum_field.field_class, Select2Field) +- eq_(view._create_form_class.sqla_utils_choice.field_class, Select2Field) +- eq_(view._create_form_class.sqla_utils_enum.field_class, Select2Field) +- eq_(view._create_form_class.sqla_utils_arrow.field_class, DateTimeField) +- eq_(view._create_form_class.sqla_utils_uuid.field_class, fields.StringField) +- eq_(view._create_form_class.sqla_utils_url.field_class, fields.StringField) +- eq_(view._create_form_class.sqla_utils_ip_address.field_class, fields.StringField) +- eq_(view._create_form_class.sqla_utils_currency.field_class, fields.StringField) +- eq_(view._create_form_class.sqla_utils_color.field_class, fields.StringField) ++ assert view._create_form_class.test1.field_class == fields.StringField ++ assert view._create_form_class.test2.field_class == fields.StringField ++ assert view._create_form_class.test3.field_class == fields.TextAreaField ++ assert view._create_form_class.test4.field_class == fields.TextAreaField ++ assert view._create_form_class.email_field.field_class == fields.StringField ++ assert view._create_form_class.choice_field.field_class == Select2Field ++ assert view._create_form_class.enum_field.field_class == Select2Field ++ assert view._create_form_class.sqla_utils_choice.field_class == Select2Field ++ assert view._create_form_class.sqla_utils_enum.field_class == Select2Field ++ assert view._create_form_class.sqla_utils_arrow.field_class == DateTimeField ++ assert view._create_form_class.sqla_utils_uuid.field_class == fields.StringField ++ assert view._create_form_class.sqla_utils_url.field_class == fields.StringField ++ assert view._create_form_class.sqla_utils_ip_address.field_class == fields.StringField ++ assert view._create_form_class.sqla_utils_currency.field_class == fields.StringField ++ assert view._create_form_class.sqla_utils_color.field_class == fields.StringField + + # Make some test clients + client = app.test_client() + + # check that we can retrieve a list view + rv = client.get('/admin/model1/') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + + # check that we can retrieve a 'create' view + rv = client.get('/admin/model1/new/') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + + # create a new record + uuid_obj = uuid.uuid4() +@@ -228,38 +229,38 @@ def test_model(): + sqla_utils_color='#f0f0f0', + ) + ) +- eq_(rv.status_code, 302) ++ assert rv.status_code == 302 + + # check that the new record was persisted + model = db.session.query(Model1).first() +- eq_(model.test1, u'test1large') +- eq_(model.test2, u'test2') +- eq_(model.test3, u'') +- eq_(model.test4, u'') +- eq_(model.email_field, u'test@test.com') +- eq_(model.choice_field, u'choice-1') +- eq_(model.enum_field, u'model1_v1') +- eq_(model.sqla_utils_choice, u'choice-1') +- eq_(model.sqla_utils_enum.value, 1) +- eq_(model.sqla_utils_arrow, arrow.get('2018-10-27 14:17:00')) +- eq_(model.sqla_utils_uuid, uuid_obj) +- eq_(model.sqla_utils_url, "http://www.example.com") +- eq_(str(model.sqla_utils_ip_address), '127.0.0.1') +- eq_(str(model.sqla_utils_currency), 'USD') +- eq_(model.sqla_utils_color.hex, '#f0f0f0') ++ assert model.test1 == u'test1large' ++ assert model.test2 == u'test2' ++ assert model.test3 == u'' ++ assert model.test4 == u'' ++ assert model.email_field == u'test@test.com' ++ assert model.choice_field == u'choice-1' ++ assert model.enum_field == u'model1_v1' ++ assert model.sqla_utils_choice == u'choice-1' ++ assert model.sqla_utils_enum.value == 1 ++ assert model.sqla_utils_arrow == arrow.get('2018-10-27 14:17:00') ++ assert model.sqla_utils_uuid == uuid_obj ++ assert model.sqla_utils_url == "http://www.example.com" ++ assert str(model.sqla_utils_ip_address) == '127.0.0.1' ++ assert str(model.sqla_utils_currency) == 'USD' ++ assert model.sqla_utils_color.hex == '#f0f0f0' + + # check that the new record shows up on the list view + rv = client.get('/admin/model1/') +- eq_(rv.status_code, 200) +- ok_(u'test1large' in rv.data.decode('utf-8')) ++ assert rv.status_code == 200 ++ assert u'test1large' in rv.data.decode('utf-8') + + # check that we can retrieve an edit view + url = '/admin/model1/edit/?id=%s' % model.id + rv = client.get(url) +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + + # verify that midnight does not show as blank +- ok_(u'00:00:00' in rv.data.decode('utf-8')) ++ assert u'00:00:00' in rv.data.decode('utf-8') + + # edit the record + new_uuid_obj = uuid.uuid4() +@@ -278,34 +279,34 @@ def test_model(): + sqla_utils_currency='', + sqla_utils_color='', + )) +- eq_(rv.status_code, 302) ++ assert rv.status_code == 302 + + # check that the changes were persisted + model = db.session.query(Model1).first() +- eq_(model.test1, 'test1small') +- eq_(model.test2, 'test2large') +- eq_(model.test3, '') +- eq_(model.test4, '') +- eq_(model.email_field, u'test2@test.com') +- eq_(model.choice_field, None) +- eq_(model.enum_field, None) +- eq_(model.sqla_utils_choice, None) +- eq_(model.sqla_utils_enum, None) +- eq_(model.sqla_utils_arrow, None) +- eq_(model.sqla_utils_uuid, new_uuid_obj) +- eq_(model.sqla_utils_url, None) +- eq_(model.sqla_utils_ip_address, None) +- eq_(model.sqla_utils_currency, None) +- eq_(model.sqla_utils_color, None) ++ assert model.test1 == 'test1small' ++ assert model.test2 == 'test2large' ++ assert model.test3 == '' ++ assert model.test4 == '' ++ assert model.email_field == u'test2@test.com' ++ assert model.choice_field is None ++ assert model.enum_field is None ++ assert model.sqla_utils_choice is None ++ assert model.sqla_utils_enum is None ++ assert model.sqla_utils_arrow is None ++ assert model.sqla_utils_uuid == new_uuid_obj ++ assert model.sqla_utils_url is None ++ assert model.sqla_utils_ip_address is None ++ assert model.sqla_utils_currency is None ++ assert model.sqla_utils_color is None + + # check that the model can be deleted + url = '/admin/model1/delete/?id=%s' % model.id + rv = client.post(url) +- eq_(rv.status_code, 302) +- eq_(db.session.query(Model1).count(), 0) ++ assert rv.status_code == 302 ++ assert db.session.query(Model1).count() == 0 + + +-@raises(Exception) ++@pytest.mark.xfail(raises=Exception) + def test_no_pk(): + app, db, admin = setup() + +@@ -327,15 +328,15 @@ def test_list_columns(): + column_labels=dict(test1='Column1')) + admin.add_view(view) + +- eq_(len(view._list_columns), 2) +- eq_(view._list_columns, [('test1', 'Column1'), ('test3', 'Test3')]) ++ assert len(view._list_columns) == 2 ++ assert view._list_columns == [('test1', 'Column1'), ('test3', 'Test3')] + + client = app.test_client() + + rv = client.get('/admin/model1/') + data = rv.data.decode('utf-8') +- ok_('Column1' in data) +- ok_('Test2' not in data) ++ assert 'Column1' in data ++ assert 'Test2' not in data + + # test column_list with a list of SQLAlchemy columns + view2 = CustomModelView(Model1, db.session, endpoint='model1_2', +@@ -343,13 +344,13 @@ def test_list_columns(): + column_labels=dict(test1='Column1')) + admin.add_view(view2) + +- eq_(len(view2._list_columns), 2) +- eq_(view2._list_columns, [('test1', 'Column1'), ('test3', 'Test3')]) ++ assert len(view2._list_columns) == 2 ++ assert view2._list_columns == [('test1', 'Column1'), ('test3', 'Test3')] + + rv = client.get('/admin/model1_2/') + data = rv.data.decode('utf-8') +- ok_('Column1' in data) +- ok_('Test2' not in data) ++ assert 'Column1' in data ++ assert 'Test2' not in data + + + def test_complex_list_columns(): +@@ -370,9 +371,9 @@ def test_complex_list_columns(): + client = app.test_client() + + rv = client.get('/admin/model2/') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('model1_val1' in data) ++ assert 'model1_val1' in data + + + def test_exclude_columns(): +@@ -388,18 +389,17 @@ def test_exclude_columns(): + ) + admin.add_view(view) + +- eq_( +- view._list_columns, ++ assert \ ++ view._list_columns == \ + [('test1', 'Test1'), ('test3', 'Test3'), ('bool_field', 'Bool Field'), + ('email_field', 'Email Field'), ('choice_field', 'Choice Field')] +- ) + + client = app.test_client() + + rv = client.get('/admin/model1/') + data = rv.data.decode('utf-8') +- ok_('Test1' in data) +- ok_('Test2' not in data) ++ assert 'Test1' in data ++ assert 'Test2' not in data + + + def test_column_searchable_list(): +@@ -411,13 +411,13 @@ def test_column_searchable_list(): + column_searchable_list=['string_field', 'int_field']) + admin.add_view(view) + +- eq_(view._search_supported, True) +- eq_(len(view._search_fields), 2) ++ assert view._search_supported ++ assert len(view._search_fields) == 2 + +- ok_(isinstance(view._search_fields[0][0], db.Column)) +- ok_(isinstance(view._search_fields[1][0], db.Column)) +- eq_(view._search_fields[0][0].name, 'string_field') +- eq_(view._search_fields[1][0].name, 'int_field') ++ assert isinstance(view._search_fields[0][0], db.Column) ++ assert isinstance(view._search_fields[1][0], db.Column) ++ assert view._search_fields[0][0].name == 'string_field' ++ assert view._search_fields[1][0].name == 'int_field' + + db.session.add(Model2('model1-test', 5000)) + db.session.add(Model2('model2-test', 9000)) +@@ -427,13 +427,13 @@ def test_column_searchable_list(): + + rv = client.get('/admin/model2/?search=model1') + data = rv.data.decode('utf-8') +- ok_('model1-test' in data) +- ok_('model2-test' not in data) ++ assert 'model1-test' in data ++ assert 'model2-test' not in data + + rv = client.get('/admin/model2/?search=9000') + data = rv.data.decode('utf-8') +- ok_('model1-test' not in data) +- ok_('model2-test' in data) ++ assert 'model1-test' not in data ++ assert 'model2-test' in data + + + def test_extra_args_search(): +@@ -454,7 +454,7 @@ def test_extra_args_search(): + # check that extra args in the url are propagated as hidden fields in the search form + rv = client.get('/admin/model1/?search=model1&foo=bar') + data = rv.data.decode('utf-8') +- ok_('' in data) ++ assert '' in data + + + def test_extra_args_filter(): +@@ -474,7 +474,7 @@ def test_extra_args_filter(): + # check that extra args in the url are propagated as hidden fields in the form + rv = client.get('/admin/model2/?flt1_0=5000&foo=bar') + data = rv.data.decode('utf-8') +- ok_('' in data) ++ assert '' in data + + + def test_complex_searchable_list(): +@@ -499,8 +499,8 @@ def test_complex_searchable_list(): + # test relation string - 'model1.test1' + rv = client.get('/admin/model2/?search=model1-test1') + data = rv.data.decode('utf-8') +- ok_('model2-test1-val' in data) +- ok_('model2-test2-val' not in data) ++ assert 'model2-test1-val' in data ++ assert 'model2-test2-val' not in data + + view2 = CustomModelView(Model1, db.session, + column_searchable_list=[Model2.string_field]) +@@ -509,8 +509,8 @@ def test_complex_searchable_list(): + # test relation object - Model2.string_field + rv = client.get('/admin/model1/?search=model2-test1') + data = rv.data.decode('utf-8') +- ok_('model1-test1-val' in data) +- ok_('model1-test2-val' not in data) ++ assert 'model1-test1-val' in data ++ assert 'model1-test2-val' not in data + + + def test_complex_searchable_list_missing_children(): +@@ -530,7 +530,7 @@ def test_complex_searchable_list_missing + + rv = client.get('/admin/model1/?search=magic') + data = rv.data.decode('utf-8') +- ok_('magic string' in data) ++ assert 'magic string' in data + + + def test_column_editable_list(): +@@ -549,7 +549,7 @@ def test_column_editable_list(): + # Test in-line edit field rendering + rv = client.get('/admin/model1/') + data = rv.data.decode('utf-8') +- ok_('data-role="x-editable"' in data) ++ assert 'data-role="x-editable"' in data + + # Form - Test basic in-line edit functionality + rv = client.post('/admin/model1/ajax/update/', data={ +@@ -557,19 +557,19 @@ def test_column_editable_list(): + 'test1': 'change-success-1', + }) + data = rv.data.decode('utf-8') +- ok_('Record was successfully saved.' == data) ++ assert 'Record was successfully saved.' == data + + # ensure the value has changed + rv = client.get('/admin/model1/') + data = rv.data.decode('utf-8') +- ok_('change-success-1' in data) ++ assert 'change-success-1' in data + + # Test validation error + rv = client.post('/admin/model1/ajax/update/', data={ + 'list_form_pk': '1', + 'enum_field': 'problematic-input', + }) +- eq_(rv.status_code, 500) ++ assert rv.status_code == 500 + + # Test invalid primary key + rv = client.post('/admin/model1/ajax/update/', data={ +@@ -577,7 +577,7 @@ def test_column_editable_list(): + 'test1': 'problematic-input', + }) + data = rv.data.decode('utf-8') +- eq_(rv.status_code, 500) ++ assert rv.status_code == 500 + + # Test editing column not in column_editable_list + rv = client.post('/admin/model1/ajax/update/', data={ +@@ -585,7 +585,7 @@ def test_column_editable_list(): + 'test2': 'problematic-input', + }) + data = rv.data.decode('utf-8') +- ok_('problematic-input' not in data) ++ assert 'problematic-input' not in data + + # Test in-line editing for relations + view = CustomModelView(Model2, db.session, column_editable_list=['model1']) +@@ -596,12 +596,12 @@ def test_column_editable_list(): + 'model1': '3', + }) + data = rv.data.decode('utf-8') +- ok_('Record was successfully saved.' == data) ++ assert 'Record was successfully saved.' == data + + # confirm the value has changed + rv = client.get('/admin/model2/') + data = rv.data.decode('utf-8') +- ok_('test1_val_3' in data) ++ assert 'test1_val_3' in data + + + def test_details_view(): +@@ -630,30 +630,30 @@ def test_details_view(): + # ensure link to details is hidden when can_view_details is disabled + rv = client.get('/admin/model1/') + data = rv.data.decode('utf-8') +- ok_('/admin/model1/details/' not in data) ++ assert '/admin/model1/details/' not in data + + # ensure link to details view appears + rv = client.get('/admin/model2/') + data = rv.data.decode('utf-8') +- ok_('/admin/model2/details/' in data) ++ assert '/admin/model2/details/' in data + + # test redirection when details are disabled + rv = client.get('/admin/model1/details/?url=%2Fadmin%2Fmodel1%2F&id=1') +- eq_(rv.status_code, 302) ++ assert rv.status_code == 302 + + # test if correct data appears in details view when enabled + rv = client.get('/admin/model2/details/?url=%2Fadmin%2Fmodel2%2F&id=1') + data = rv.data.decode('utf-8') +- ok_('String Field' in data) +- ok_('test2_val_1' in data) +- ok_('test1_val_1' in data) ++ assert 'String Field' in data ++ assert 'test2_val_1' in data ++ assert 'test1_val_1' in data + + # test column_details_list + rv = client.get('/admin/sf_view/details/?url=%2Fadmin%2Fsf_view%2F&id=1') + data = rv.data.decode('utf-8') +- ok_('String Field' in data) +- ok_('test2_val_1' in data) +- ok_('test1_val_1' not in data) ++ assert 'String Field' in data ++ assert 'test2_val_1' in data ++ assert 'test1_val_1' not in data + + + def test_editable_list_special_pks(): +@@ -686,12 +686,12 @@ def test_editable_list_special_pks(): + 'val1': 'change-success-1', + }) + data = rv.data.decode('utf-8') +- ok_('Record was successfully saved.' == data) ++ assert 'Record was successfully saved.' == data + + # ensure the value has changed + rv = client.get('/admin/model1/') + data = rv.data.decode('utf-8') +- ok_('change-success-1' in data) ++ assert 'change-success-1' in data + + + def test_column_filters(): +@@ -707,10 +707,10 @@ def test_column_filters(): + + client = app.test_client() + +- eq_(len(view._filters), 7) ++ assert len(view._filters) == 7 + +- eq_( +- [(f['index'], f['operation']) for f in view._filter_groups[u'Test1']], ++ assert \ ++ [(f['index'], f['operation']) for f in view._filter_groups[u'Test1']] == \ + [ + (0, u'contains'), + (1, u'not contains'), +@@ -720,14 +720,13 @@ def test_column_filters(): + (5, u'in list'), + (6, u'not in list'), + ] +- ) + + # Test filter that references property + view = CustomModelView(Model2, db.session, + column_filters=['model1']) + +- eq_( +- [(f['index'], f['operation']) for f in view._filter_groups[u'Model1 / Test1']], ++ assert \ ++ [(f['index'], f['operation']) for f in view._filter_groups[u'Model1 / Test1']] == \ + [ + (0, u'contains'), + (1, u'not contains'), +@@ -737,10 +736,9 @@ def test_column_filters(): + (5, u'in list'), + (6, u'not in list'), + ] +- ) + +- eq_( +- [(f['index'], f['operation']) for f in view._filter_groups[u'Model1 / Test2']], ++ assert \ ++ [(f['index'], f['operation']) for f in view._filter_groups[u'Model1 / Test2']] == \ + [ + (7, u'contains'), + (8, u'not contains'), +@@ -750,10 +748,9 @@ def test_column_filters(): + (12, u'in list'), + (13, u'not in list'), + ] +- ) + +- eq_( +- [(f['index'], f['operation']) for f in view._filter_groups[u'Model1 / Test3']], ++ assert \ ++ [(f['index'], f['operation']) for f in view._filter_groups[u'Model1 / Test3']] == \ + [ + (14, u'contains'), + (15, u'not contains'), +@@ -763,10 +760,9 @@ def test_column_filters(): + (19, u'in list'), + (20, u'not in list'), + ] +- ) + +- eq_( +- [(f['index'], f['operation']) for f in view._filter_groups[u'Model1 / Test4']], ++ assert \ ++ [(f['index'], f['operation']) for f in view._filter_groups[u'Model1 / Test4']] == \ + [ + (21, u'contains'), + (22, u'not contains'), +@@ -776,18 +772,16 @@ def test_column_filters(): + (26, u'in list'), + (27, u'not in list'), + ] +- ) + +- eq_( +- [(f['index'], f['operation']) for f in view._filter_groups[u'Model1 / Bool Field']], ++ assert \ ++ [(f['index'], f['operation']) for f in view._filter_groups[u'Model1 / Bool Field']] == \ + [ + (28, u'equals'), + (29, u'not equal'), + ] +- ) + +- eq_( +- [(f['index'], f['operation']) for f in view._filter_groups[u'Model1 / Date Field']], ++ assert \ ++ [(f['index'], f['operation']) for f in view._filter_groups[u'Model1 / Date Field']] == \ + [ + (30, u'equals'), + (31, u'not equal'), +@@ -797,10 +791,9 @@ def test_column_filters(): + (35, u'not between'), + (36, u'empty'), + ] +- ) + +- eq_( +- [(f['index'], f['operation']) for f in view._filter_groups[u'Model1 / Time Field']], ++ assert \ ++ [(f['index'], f['operation']) for f in view._filter_groups[u'Model1 / Time Field']] == \ + [ + (37, u'equals'), + (38, u'not equal'), +@@ -810,10 +803,9 @@ def test_column_filters(): + (42, u'not between'), + (43, u'empty'), + ] +- ) + +- eq_( +- [(f['index'], f['operation']) for f in view._filter_groups[u'Model1 / Datetime Field']], ++ assert \ ++ [(f['index'], f['operation']) for f in view._filter_groups[u'Model1 / Datetime Field']] == \ + [ + (44, u'equals'), + (45, u'not equal'), +@@ -823,10 +815,9 @@ def test_column_filters(): + (49, u'not between'), + (50, u'empty'), + ] +- ) + +- eq_( +- [(f['index'], f['operation']) for f in view._filter_groups[u'Model1 / Email Field']], ++ assert \ ++ [(f['index'], f['operation']) for f in view._filter_groups[u'Model1 / Email Field']] == \ + [ + (51, u'contains'), + (52, u'not contains'), +@@ -836,10 +827,9 @@ def test_column_filters(): + (56, u'in list'), + (57, u'not in list'), + ] +- ) + +- eq_( +- [(f['index'], f['operation']) for f in view._filter_groups[u'Model1 / Enum Field']], ++ assert \ ++ [(f['index'], f['operation']) for f in view._filter_groups[u'Model1 / Enum Field']] == \ + [ + (58, u'equals'), + (59, u'not equal'), +@@ -847,10 +837,9 @@ def test_column_filters(): + (61, u'in list'), + (62, u'not in list'), + ] +- ) + +- eq_( +- [(f['index'], f['operation']) for f in view._filter_groups[u'Model1 / Choice Field']], ++ assert \ ++ [(f['index'], f['operation']) for f in view._filter_groups[u'Model1 / Choice Field']] == \ + [ + (63, u'contains'), + (64, u'not contains'), +@@ -860,10 +849,9 @@ def test_column_filters(): + (68, u'in list'), + (69, u'not in list'), + ] +- ) + +- eq_( +- [(f['index'], f['operation']) for f in view._filter_groups[u'Model1 / Sqla Utils Choice']], ++ assert \ ++ [(f['index'], f['operation']) for f in view._filter_groups[u'Model1 / Sqla Utils Choice']] == \ + [ + (70, u'equals'), + (71, u'not equal'), +@@ -871,10 +859,9 @@ def test_column_filters(): + (73, u'not contains'), + (74, u'empty'), + ] +- ) + +- eq_( +- [(f['index'], f['operation']) for f in view._filter_groups[u'Model1 / Sqla Utils Enum']], ++ assert \ ++ [(f['index'], f['operation']) for f in view._filter_groups[u'Model1 / Sqla Utils Enum']] == \ + [ + (75, u'equals'), + (76, u'not equal'), +@@ -882,19 +869,17 @@ def test_column_filters(): + (78, u'not contains'), + (79, u'empty'), + ] +- ) + + # Test filter with a dot + view = CustomModelView(Model2, db.session, + column_filters=['model1.bool_field']) + +- eq_( +- [(f['index'], f['operation']) for f in view._filter_groups[u'model1 / Model1 / Bool Field']], ++ assert \ ++ [(f['index'], f['operation']) for f in view._filter_groups[u'model1 / Model1 / Bool Field']] == \ + [ + (0, 'equals'), + (1, 'not equal'), + ] +- ) + + # Test column_labels on filters + view = CustomModelView(Model2, db.session, +@@ -904,35 +889,35 @@ def test_column_filters(): + 'string_field': 'Test Filter #2', + }) + +- eq_(list(view._filter_groups.keys()), [u'Test Filter #1', u'Test Filter #2']) ++ assert list(view._filter_groups.keys()) == [u'Test Filter #1', u'Test Filter #2'] + + fill_db(db, Model1, Model2) + + # Test equals + rv = client.get('/admin/model1/?flt0_0=test1_val_1') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') + # the filter value is always in "data" + # need to check a different column than test1 for the expected row + +- ok_('test2_val_1' in data) +- ok_('test1_val_2' not in data) ++ assert 'test2_val_1' in data ++ assert 'test1_val_2' not in data + + # Test NOT IN filter + rv = client.get('/admin/model1/?flt0_6=test1_val_1') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') + +- ok_('test1_val_2' in data) +- ok_('test2_val_1' not in data) ++ assert 'test1_val_2' in data ++ assert 'test2_val_1' not in data + + # Test string filter + view = CustomModelView(Model1, db.session, + column_filters=['test1'], endpoint='_strings') + admin.add_view(view) + +- eq_( +- [(f['index'], f['operation']) for f in view._filter_groups[u'Test1']], ++ assert \ ++ [(f['index'], f['operation']) for f in view._filter_groups[u'Test1']] == \ + [ + (0, 'contains'), + (1, 'not contains'), +@@ -942,77 +927,76 @@ def test_column_filters(): + (5, 'in list'), + (6, 'not in list'), + ] +- ) + + # string - equals + rv = client.get('/admin/_strings/?flt0_0=test1_val_1') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('test2_val_1' in data) +- ok_('test1_val_2' not in data) ++ assert 'test2_val_1' in data ++ assert 'test1_val_2' not in data + + # string - not equal + rv = client.get('/admin/_strings/?flt0_1=test1_val_1') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('test2_val_1' not in data) +- ok_('test1_val_2' in data) ++ assert 'test2_val_1' not in data ++ assert 'test1_val_2' in data + + # string - contains + rv = client.get('/admin/_strings/?flt0_2=test1_val_1') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('test2_val_1' in data) +- ok_('test1_val_2' not in data) ++ assert 'test2_val_1' in data ++ assert 'test1_val_2' not in data + + # string - not contains + rv = client.get('/admin/_strings/?flt0_3=test1_val_1') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('test2_val_1' not in data) +- ok_('test1_val_2' in data) ++ assert 'test2_val_1' not in data ++ assert 'test1_val_2' in data + + # string - empty + rv = client.get('/admin/_strings/?flt0_4=1') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('empty_obj' in data) +- ok_('test1_val_1' not in data) +- ok_('test1_val_2' not in data) ++ assert 'empty_obj' in data ++ assert 'test1_val_1' not in data ++ assert 'test1_val_2' not in data + + # string - not empty + rv = client.get('/admin/_strings/?flt0_4=0') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('empty_obj' not in data) +- ok_('test1_val_1' in data) +- ok_('test1_val_2' in data) ++ assert 'empty_obj' not in data ++ assert 'test1_val_1' in data ++ assert 'test1_val_2' in data + + # string - in list + rv = client.get('/admin/_strings/?flt0_5=test1_val_1%2Ctest1_val_2') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('test2_val_1' in data) +- ok_('test2_val_2' in data) +- ok_('test1_val_3' not in data) +- ok_('test1_val_4' not in data) ++ assert 'test2_val_1' in data ++ assert 'test2_val_2' in data ++ assert 'test1_val_3' not in data ++ assert 'test1_val_4' not in data + + # string - not in list + rv = client.get('/admin/_strings/?flt0_6=test1_val_1%2Ctest1_val_2') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('test2_val_1' not in data) +- ok_('test2_val_2' not in data) +- ok_('test1_val_3' in data) +- ok_('test1_val_4' in data) ++ assert 'test2_val_1' not in data ++ assert 'test2_val_2' not in data ++ assert 'test1_val_3' in data ++ assert 'test1_val_4' in data + + # Test integer filter + view = CustomModelView(Model2, db.session, + column_filters=['int_field']) + admin.add_view(view) + +- eq_( +- [(f['index'], f['operation']) for f in view._filter_groups[u'Int Field']], ++ assert \ ++ [(f['index'], f['operation']) for f in view._filter_groups[u'Int Field']] == \ + [ + (0, 'equals'), + (1, 'not equal'), +@@ -1022,150 +1006,148 @@ def test_column_filters(): + (5, 'in list'), + (6, 'not in list'), + ] +- ) + + # integer - equals + rv = client.get('/admin/model2/?flt0_0=5000') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('test2_val_3' in data) +- ok_('test2_val_4' not in data) ++ assert 'test2_val_3' in data ++ assert 'test2_val_4' not in data + + # integer - equals (huge number) + rv = client.get('/admin/model2/?flt0_0=6169453081680413441') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('test2_val_5' in data) +- ok_('test2_val_4' not in data) ++ assert 'test2_val_5' in data ++ assert 'test2_val_4' not in data + + # integer - equals - test validation + rv = client.get('/admin/model2/?flt0_0=badval') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('Invalid Filter Value' in data) ++ assert 'Invalid Filter Value' in data + + # integer - not equal + rv = client.get('/admin/model2/?flt0_1=5000') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('test2_val_3' not in data) +- ok_('test2_val_4' in data) ++ assert 'test2_val_3' not in data ++ assert 'test2_val_4' in data + + # integer - greater + rv = client.get('/admin/model2/?flt0_2=6000') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('test2_val_3' not in data) +- ok_('test2_val_4' in data) ++ assert 'test2_val_3' not in data ++ assert 'test2_val_4' in data + + # integer - smaller + rv = client.get('/admin/model2/?flt0_3=6000') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('test2_val_3' in data) +- ok_('test2_val_4' not in data) ++ assert 'test2_val_3' in data ++ assert 'test2_val_4' not in data + + # integer - empty + rv = client.get('/admin/model2/?flt0_4=1') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('test2_val_1' in data) +- ok_('test2_val_2' in data) +- ok_('test2_val_3' not in data) +- ok_('test2_val_4' not in data) ++ assert 'test2_val_1' in data ++ assert 'test2_val_2' in data ++ assert 'test2_val_3' not in data ++ assert 'test2_val_4' not in data + + # integer - not empty + rv = client.get('/admin/model2/?flt0_4=0') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('test2_val_1' not in data) +- ok_('test2_val_2' not in data) +- ok_('test2_val_3' in data) +- ok_('test2_val_4' in data) ++ assert 'test2_val_1' not in data ++ assert 'test2_val_2' not in data ++ assert 'test2_val_3' in data ++ assert 'test2_val_4' in data + + # integer - in list + rv = client.get('/admin/model2/?flt0_5=5000%2C9000') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('test2_val_1' not in data) +- ok_('test2_val_2' not in data) +- ok_('test2_val_3' in data) +- ok_('test2_val_4' in data) ++ assert 'test2_val_1' not in data ++ assert 'test2_val_2' not in data ++ assert 'test2_val_3' in data ++ assert 'test2_val_4' in data + + # integer - in list (huge number) + rv = client.get('/admin/model2/?flt0_5=6169453081680413441') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('test2_val_1' not in data) +- ok_('test2_val_5' in data) ++ assert 'test2_val_1' not in data ++ assert 'test2_val_5' in data + + # integer - in list - test validation + rv = client.get('/admin/model2/?flt0_5=5000%2Cbadval') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('Invalid Filter Value' in data) ++ assert 'Invalid Filter Value' in data + + # integer - not in list + rv = client.get('/admin/model2/?flt0_6=5000%2C9000') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('test2_val_1' in data) +- ok_('test2_val_2' in data) +- ok_('test2_val_3' not in data) +- ok_('test2_val_4' not in data) ++ assert 'test2_val_1' in data ++ assert 'test2_val_2' in data ++ assert 'test2_val_3' not in data ++ assert 'test2_val_4' not in data + + # Test boolean filter + view = CustomModelView(Model1, db.session, column_filters=['bool_field'], + endpoint="_bools") + admin.add_view(view) + +- eq_( +- [(f['index'], f['operation']) for f in view._filter_groups[u'Bool Field']], ++ assert \ ++ [(f['index'], f['operation']) for f in view._filter_groups[u'Bool Field']] == \ + [ + (0, 'equals'), + (1, 'not equal'), + ] +- ) + + # boolean - equals - Yes + rv = client.get('/admin/_bools/?flt0_0=1') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('test2_val_1' in data) +- ok_('test2_val_2' not in data) +- ok_('test2_val_3' not in data) ++ assert 'test2_val_1' in data ++ assert 'test2_val_2' not in data ++ assert 'test2_val_3' not in data + + # boolean - equals - No + rv = client.get('/admin/_bools/?flt0_0=0') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('test2_val_1' not in data) +- ok_('test2_val_2' in data) +- ok_('test2_val_3' in data) ++ assert 'test2_val_1' not in data ++ assert 'test2_val_2' in data ++ assert 'test2_val_3' in data + + # boolean - not equals - Yes + rv = client.get('/admin/_bools/?flt0_1=1') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('test2_val_1' not in data) +- ok_('test2_val_2' in data) +- ok_('test2_val_3' in data) ++ assert 'test2_val_1' not in data ++ assert 'test2_val_2' in data ++ assert 'test2_val_3' in data + + # boolean - not equals - No + rv = client.get('/admin/_bools/?flt0_1=0') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('test2_val_1' in data) +- ok_('test2_val_2' not in data) +- ok_('test2_val_3' not in data) ++ assert 'test2_val_1' in data ++ assert 'test2_val_2' not in data ++ assert 'test2_val_3' not in data + + # Test float filter + view = CustomModelView(Model2, db.session, column_filters=['float_field'], + endpoint="_float") + admin.add_view(view) + +- eq_( +- [(f['index'], f['operation']) for f in view._filter_groups[u'Float Field']], ++ assert \ ++ [(f['index'], f['operation']) for f in view._filter_groups[u'Float Field']] == \ + [ + (0, 'equals'), + (1, 'not equal'), +@@ -1175,83 +1157,82 @@ def test_column_filters(): + (5, 'in list'), + (6, 'not in list'), + ] +- ) + + # float - equals + rv = client.get('/admin/_float/?flt0_0=25.9') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('test2_val_3' in data) +- ok_('test2_val_4' not in data) ++ assert 'test2_val_3' in data ++ assert 'test2_val_4' not in data + + # float - equals - test validation + rv = client.get('/admin/_float/?flt0_0=badval') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('Invalid Filter Value' in data) ++ assert 'Invalid Filter Value' in data + + # float - not equal + rv = client.get('/admin/_float/?flt0_1=25.9') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('test2_val_3' not in data) +- ok_('test2_val_4' in data) ++ assert 'test2_val_3' not in data ++ assert 'test2_val_4' in data + + # float - greater + rv = client.get('/admin/_float/?flt0_2=60.5') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('test2_val_3' not in data) +- ok_('test2_val_4' in data) ++ assert 'test2_val_3' not in data ++ assert 'test2_val_4' in data + + # float - smaller + rv = client.get('/admin/_float/?flt0_3=60.5') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('test2_val_3' in data) +- ok_('test2_val_4' not in data) ++ assert 'test2_val_3' in data ++ assert 'test2_val_4' not in data + + # float - empty + rv = client.get('/admin/_float/?flt0_4=1') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('test2_val_1' in data) +- ok_('test2_val_2' in data) +- ok_('test2_val_3' not in data) +- ok_('test2_val_4' not in data) ++ assert 'test2_val_1' in data ++ assert 'test2_val_2' in data ++ assert 'test2_val_3' not in data ++ assert 'test2_val_4' not in data + + # float - not empty + rv = client.get('/admin/_float/?flt0_4=0') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('test2_val_1' not in data) +- ok_('test2_val_2' not in data) +- ok_('test2_val_3' in data) +- ok_('test2_val_4' in data) ++ assert 'test2_val_1' not in data ++ assert 'test2_val_2' not in data ++ assert 'test2_val_3' in data ++ assert 'test2_val_4' in data + + # float - in list + rv = client.get('/admin/_float/?flt0_5=25.9%2C75.5') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('test2_val_1' not in data) +- ok_('test2_val_2' not in data) +- ok_('test2_val_3' in data) +- ok_('test2_val_4' in data) ++ assert 'test2_val_1' not in data ++ assert 'test2_val_2' not in data ++ assert 'test2_val_3' in data ++ assert 'test2_val_4' in data + + # float - in list - test validation + rv = client.get('/admin/_float/?flt0_5=25.9%2Cbadval') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('Invalid Filter Value' in data) ++ assert 'Invalid Filter Value' in data + + # float - not in list + rv = client.get('/admin/_float/?flt0_6=25.9%2C75.5') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('test2_val_1' in data) +- ok_('test2_val_2' in data) +- ok_('test2_val_3' not in data) +- ok_('test2_val_4' not in data) ++ assert 'test2_val_1' in data ++ assert 'test2_val_2' in data ++ assert 'test2_val_3' not in data ++ assert 'test2_val_4' not in data + + # Test filters to joined table field + view = CustomModelView( +@@ -1267,12 +1248,12 @@ def test_column_filters(): + admin.add_view(view) + + rv = client.get('/admin/_model2/?flt1_0=1') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('test2_val_1' in data) +- ok_('test2_val_2' not in data) +- ok_('test2_val_3' not in data) +- ok_('test2_val_4' not in data) ++ assert 'test2_val_1' in data ++ assert 'test2_val_2' not in data ++ assert 'test2_val_3' not in data ++ assert 'test2_val_4' not in data + + # Test human readable URLs + view = CustomModelView( +@@ -1284,10 +1265,10 @@ def test_column_filters(): + admin.add_view(view) + + rv = client.get('/admin/_model3/?flt1_test1_equals=test1_val_1') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('test1_val_1' in data) +- ok_('test1_val_2' not in data) ++ assert 'test1_val_1' in data ++ assert 'test1_val_2' not in data + + # Test date, time, and datetime filters + view = CustomModelView(Model1, db.session, +@@ -1295,8 +1276,8 @@ def test_column_filters(): + endpoint="_datetime") + admin.add_view(view) + +- eq_( +- [(f['index'], f['operation']) for f in view._filter_groups[u'Date Field']], ++ assert \ ++ [(f['index'], f['operation']) for f in view._filter_groups[u'Date Field']] == \ + [ + (0, 'equals'), + (1, 'not equal'), +@@ -1306,10 +1287,9 @@ def test_column_filters(): + (5, 'not between'), + (6, 'empty'), + ] +- ) + +- eq_( +- [(f['index'], f['operation']) for f in view._filter_groups[u'Datetime Field']], ++ assert \ ++ [(f['index'], f['operation']) for f in view._filter_groups[u'Datetime Field']] == \ + [ + (7, 'equals'), + (8, 'not equal'), +@@ -1319,10 +1299,9 @@ def test_column_filters(): + (12, 'not between'), + (13, 'empty'), + ] +- ) + +- eq_( +- [(f['index'], f['operation']) for f in view._filter_groups[u'Time Field']], ++ assert \ ++ [(f['index'], f['operation']) for f in view._filter_groups[u'Time Field']] == \ + [ + (14, 'equals'), + (15, 'not equal'), +@@ -1332,181 +1311,180 @@ def test_column_filters(): + (19, 'not between'), + (20, 'empty'), + ] +- ) + + # date - equals + rv = client.get('/admin/_datetime/?flt0_0=2014-11-17') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('date_obj1' in data) +- ok_('date_obj2' not in data) ++ assert 'date_obj1' in data ++ assert 'date_obj2' not in data + + # date - not equal + rv = client.get('/admin/_datetime/?flt0_1=2014-11-17') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('date_obj1' not in data) +- ok_('date_obj2' in data) ++ assert 'date_obj1' not in data ++ assert 'date_obj2' in data + + # date - greater + rv = client.get('/admin/_datetime/?flt0_2=2014-11-16') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('date_obj1' in data) +- ok_('date_obj2' not in data) ++ assert 'date_obj1' in data ++ assert 'date_obj2' not in data + + # date - smaller + rv = client.get('/admin/_datetime/?flt0_3=2014-11-16') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('date_obj1' not in data) +- ok_('date_obj2' in data) ++ assert 'date_obj1' not in data ++ assert 'date_obj2' in data + + # date - between + rv = client.get('/admin/_datetime/?flt0_4=2014-11-13+to+2014-11-20') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('date_obj1' in data) +- ok_('date_obj2' not in data) ++ assert 'date_obj1' in data ++ assert 'date_obj2' not in data + + # date - not between + rv = client.get('/admin/_datetime/?flt0_5=2014-11-13+to+2014-11-20') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('date_obj1' not in data) +- ok_('date_obj2' in data) ++ assert 'date_obj1' not in data ++ assert 'date_obj2' in data + + # date - empty + rv = client.get('/admin/_datetime/?flt0_6=1') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('test1_val_1' in data) +- ok_('date_obj1' not in data) +- ok_('date_obj2' not in data) ++ assert 'test1_val_1' in data ++ assert 'date_obj1' not in data ++ assert 'date_obj2' not in data + + # date - empty + rv = client.get('/admin/_datetime/?flt0_6=0') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('test1_val_1' not in data) +- ok_('date_obj1' in data) +- ok_('date_obj2' in data) ++ assert 'test1_val_1' not in data ++ assert 'date_obj1' in data ++ assert 'date_obj2' in data + + # datetime - equals + rv = client.get('/admin/_datetime/?flt0_7=2014-04-03+01%3A09%3A00') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('datetime_obj1' in data) +- ok_('datetime_obj2' not in data) ++ assert 'datetime_obj1' in data ++ assert 'datetime_obj2' not in data + + # datetime - not equal + rv = client.get('/admin/_datetime/?flt0_8=2014-04-03+01%3A09%3A00') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('datetime_obj1' not in data) +- ok_('datetime_obj2' in data) ++ assert 'datetime_obj1' not in data ++ assert 'datetime_obj2' in data + + # datetime - greater + rv = client.get('/admin/_datetime/?flt0_9=2014-04-03+01%3A08%3A00') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('datetime_obj1' in data) +- ok_('datetime_obj2' not in data) ++ assert 'datetime_obj1' in data ++ assert 'datetime_obj2' not in data + + # datetime - smaller + rv = client.get('/admin/_datetime/?flt0_10=2014-04-03+01%3A08%3A00') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('datetime_obj1' not in data) +- ok_('datetime_obj2' in data) ++ assert 'datetime_obj1' not in data ++ assert 'datetime_obj2' in data + + # datetime - between + rv = client.get('/admin/_datetime/?flt0_11=2014-04-02+00%3A00%3A00+to+2014-11-20+23%3A59%3A59') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('datetime_obj1' in data) +- ok_('datetime_obj2' not in data) ++ assert 'datetime_obj1' in data ++ assert 'datetime_obj2' not in data + + # datetime - not between + rv = client.get('/admin/_datetime/?flt0_12=2014-04-02+00%3A00%3A00+to+2014-11-20+23%3A59%3A59') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('datetime_obj1' not in data) +- ok_('datetime_obj2' in data) ++ assert 'datetime_obj1' not in data ++ assert 'datetime_obj2' in data + + # datetime - empty + rv = client.get('/admin/_datetime/?flt0_13=1') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('test1_val_1' in data) +- ok_('datetime_obj1' not in data) +- ok_('datetime_obj2' not in data) ++ assert 'test1_val_1' in data ++ assert 'datetime_obj1' not in data ++ assert 'datetime_obj2' not in data + + # datetime - not empty + rv = client.get('/admin/_datetime/?flt0_13=0') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('test1_val_1' not in data) +- ok_('datetime_obj1' in data) +- ok_('datetime_obj2' in data) ++ assert 'test1_val_1' not in data ++ assert 'datetime_obj1' in data ++ assert 'datetime_obj2' in data + + # time - equals + rv = client.get('/admin/_datetime/?flt0_14=11%3A10%3A09') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('timeonly_obj1' in data) +- ok_('timeonly_obj2' not in data) ++ assert 'timeonly_obj1' in data ++ assert 'timeonly_obj2' not in data + + # time - not equal + rv = client.get('/admin/_datetime/?flt0_15=11%3A10%3A09') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('timeonly_obj1' not in data) +- ok_('timeonly_obj2' in data) ++ assert 'timeonly_obj1' not in data ++ assert 'timeonly_obj2' in data + + # time - greater + rv = client.get('/admin/_datetime/?flt0_16=11%3A09%3A09') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('timeonly_obj1' in data) +- ok_('timeonly_obj2' not in data) ++ assert 'timeonly_obj1' in data ++ assert 'timeonly_obj2' not in data + + # time - smaller + rv = client.get('/admin/_datetime/?flt0_17=11%3A09%3A09') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('timeonly_obj1' not in data) +- ok_('timeonly_obj2' in data) ++ assert 'timeonly_obj1' not in data ++ assert 'timeonly_obj2' in data + + # time - between + rv = client.get('/admin/_datetime/?flt0_18=10%3A40%3A00+to+11%3A50%3A59') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('timeonly_obj1' in data) +- ok_('timeonly_obj2' not in data) ++ assert 'timeonly_obj1' in data ++ assert 'timeonly_obj2' not in data + + # time - not between + rv = client.get('/admin/_datetime/?flt0_19=10%3A40%3A00+to+11%3A50%3A59') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('timeonly_obj1' not in data) +- ok_('timeonly_obj2' in data) ++ assert 'timeonly_obj1' not in data ++ assert 'timeonly_obj2' in data + + # time - empty + rv = client.get('/admin/_datetime/?flt0_20=1') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('test1_val_1' in data) +- ok_('timeonly_obj1' not in data) +- ok_('timeonly_obj2' not in data) ++ assert 'test1_val_1' in data ++ assert 'timeonly_obj1' not in data ++ assert 'timeonly_obj2' not in data + + # time - not empty + rv = client.get('/admin/_datetime/?flt0_20=0') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('test1_val_1' not in data) +- ok_('timeonly_obj1' in data) +- ok_('timeonly_obj2' in data) ++ assert 'test1_val_1' not in data ++ assert 'timeonly_obj1' in data ++ assert 'timeonly_obj2' in data + + # Test enum filter + view = CustomModelView(Model1, db.session, +@@ -1516,49 +1494,49 @@ def test_column_filters(): + + # enum - equals + rv = client.get('/admin/_enumfield/?flt0_0=model1_v1') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('enum_obj1' in data) +- ok_('enum_obj2' not in data) ++ assert 'enum_obj1' in data ++ assert 'enum_obj2' not in data + + # enum - not equal + rv = client.get('/admin/_enumfield/?flt0_1=model1_v1') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('enum_obj1' not in data) +- ok_('enum_obj2' in data) ++ assert 'enum_obj1' not in data ++ assert 'enum_obj2' in data + + # enum - empty + rv = client.get('/admin/_enumfield/?flt0_2=1') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('test1_val_1' in data) +- ok_('enum_obj1' not in data) +- ok_('enum_obj2' not in data) ++ assert 'test1_val_1' in data ++ assert 'enum_obj1' not in data ++ assert 'enum_obj2' not in data + + # enum - not empty + rv = client.get('/admin/_enumfield/?flt0_2=0') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('test1_val_1' not in data) +- ok_('enum_obj1' in data) +- ok_('enum_obj2' in data) ++ assert 'test1_val_1' not in data ++ assert 'enum_obj1' in data ++ assert 'enum_obj2' in data + + # enum - in list + rv = client.get('/admin/_enumfield/?flt0_3=model1_v1%2Cmodel1_v2') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('test1_val_1' not in data) +- ok_('enum_obj1' in data) +- ok_('enum_obj2' in data) ++ assert 'test1_val_1' not in data ++ assert 'enum_obj1' in data ++ assert 'enum_obj2' in data + + # enum - not in list + rv = client.get('/admin/_enumfield/?flt0_4=model1_v1%2Cmodel1_v2') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('test1_val_1' in data) +- ok_('enum_obj1' not in data) +- ok_('enum_obj2' not in data) ++ assert 'test1_val_1' in data ++ assert 'enum_obj1' not in data ++ assert 'enum_obj2' not in data + + # Test single custom filter on relation + view = CustomModelView(Model2, db.session, +@@ -1570,8 +1548,8 @@ def test_column_filters(): + rv = client.get('/admin/_relation_test/?flt1_0=test1_val_1') + data = rv.data.decode('utf-8') + +- ok_('test1_val_1' in data) +- ok_('test1_val_2' not in data) ++ assert 'test1_val_1' in data ++ assert 'test1_val_2' not in data + + + def test_column_filters_sqla_obj(): +@@ -1585,7 +1563,7 @@ def test_column_filters_sqla_obj(): + ) + admin.add_view(view) + +- eq_(len(view._filters), 7) ++ assert len(view._filters) == 7 + + + def test_hybrid_property(): +@@ -1628,27 +1606,27 @@ def test_hybrid_property(): + + # filters - hybrid_property integer - greater + rv = client.get('/admin/model1/?flt0_0=600') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('test_row_1' in data) +- ok_('test_row_2' not in data) ++ assert 'test_row_1' in data ++ assert 'test_row_2' not in data + + # sorting + rv = client.get('/admin/model1/?sort=0') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + + _, data = view.get_list(0, None, None, None, None) + +- eq_(len(data), 2) +- eq_(data[0].name, 'test_row_2') +- eq_(data[1].name, 'test_row_1') ++ assert len(data) == 2 ++ assert data[0].name == 'test_row_2' ++ assert data[1].name == 'test_row_1' + + # searching + rv = client.get('/admin/model1/?search=100') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('test_row_2' in data) +- ok_('test_row_1' not in data) ++ assert 'test_row_2' in data ++ assert 'test_row_1' not in data + + + def test_url_args(): +@@ -1672,41 +1650,41 @@ def test_url_args(): + + rv = client.get('/admin/model1/') + data = rv.data.decode('utf-8') +- ok_('data1' in data) +- ok_('data3' not in data) ++ assert 'data1' in data ++ assert 'data3' not in data + + # page + rv = client.get('/admin/model1/?page=1') + data = rv.data.decode('utf-8') +- ok_('data1' not in data) +- ok_('data3' in data) ++ assert 'data1' not in data ++ assert 'data3' in data + + # sort + rv = client.get('/admin/model1/?sort=0&desc=1') + data = rv.data.decode('utf-8') +- ok_('data1' not in data) +- ok_('data3' in data) +- ok_('data4' in data) ++ assert 'data1' not in data ++ assert 'data3' in data ++ assert 'data4' in data + + # search + rv = client.get('/admin/model1/?search=data1') + data = rv.data.decode('utf-8') +- ok_('data1' in data) +- ok_('data2' not in data) ++ assert 'data1' in data ++ assert 'data2' not in data + + rv = client.get('/admin/model1/?search=^data1') + data = rv.data.decode('utf-8') +- ok_('data2' not in data) ++ assert 'data2' not in data + + # like + rv = client.get('/admin/model1/?flt0=0&flt0v=data1') + data = rv.data.decode('utf-8') +- ok_('data1' in data) ++ assert 'data1' in data + + # not like + rv = client.get('/admin/model1/?flt0=1&flt0v=data1') + data = rv.data.decode('utf-8') +- ok_('data2' in data) ++ assert 'data2' in data + + + def test_non_int_pk(): +@@ -1724,21 +1702,21 @@ def test_non_int_pk(): + client = app.test_client() + + rv = client.get('/admin/model/') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + + rv = client.post('/admin/model/new/', + data=dict(id='test1', test='test2')) +- eq_(rv.status_code, 302) ++ assert rv.status_code == 302 + + rv = client.get('/admin/model/') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('test1' in data) ++ assert 'test1' in data + + rv = client.get('/admin/model/edit/?id=test1') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('test2' in data) ++ assert 'test2' in data + + + def test_form_columns(): +@@ -1779,32 +1757,32 @@ def test_form_columns(): + form2 = view2.create_form() + form3 = view3.create_form() + +- ok_('int_field' in form1._fields) +- ok_('text_field' in form1._fields) +- ok_('datetime_field' not in form1._fields) +- ok_('excluded_column' not in form2._fields) ++ assert 'int_field' in form1._fields ++ assert 'text_field' in form1._fields ++ assert 'datetime_field' not in form1._fields ++ assert 'excluded_column' not in form2._fields + + # check that relation shows up as a query select +- ok_(type(form3.model).__name__ == 'QuerySelectField') ++ assert type(form3.model).__name__ == 'QuerySelectField' + + # check that select field is rendered if form_choices were specified +- ok_(type(form3.choice_field).__name__ == 'Select2Field') ++ assert type(form3.choice_field).__name__ == 'Select2Field' + + # check that select field is rendered for enum fields +- ok_(type(form3.enum_field).__name__ == 'Select2Field') ++ assert type(form3.enum_field).__name__ == 'Select2Field' + + # check that sqlalchemy_utils field types are handled appropriately +- ok_(type(form3.sqla_utils_choice).__name__ == 'Select2Field') +- ok_(type(form3.sqla_utils_enum).__name__ == 'Select2Field') ++ assert type(form3.sqla_utils_choice).__name__ == 'Select2Field' ++ assert type(form3.sqla_utils_enum).__name__ == 'Select2Field' + + # test form_columns with model objects + view4 = CustomModelView(Model, db.session, endpoint='view1', + form_columns=[Model.int_field]) + form4 = view4.create_form() +- ok_('int_field' in form4._fields) ++ assert 'int_field' in form4._fields + + +-@raises(Exception) ++@pytest.mark.xfail(raises=Exception) + def test_complex_form_columns(): + app, db, admin = setup() + M1, M2 = create_models(db) +@@ -1829,11 +1807,11 @@ def test_form_args(): + admin.add_view(view) + + create_form = view.create_form() +- eq_(len(create_form.test.validators), 2) ++ assert len(create_form.test.validators) == 2 + + # ensure shared field_args don't create duplicate validators + edit_form = view.edit_form() +- eq_(len(edit_form.test.validators), 2) ++ assert len(edit_form.test.validators) == 2 + + + def test_form_override(): +@@ -1850,8 +1828,8 @@ def test_form_override(): + admin.add_view(view1) + admin.add_view(view2) + +- eq_(view1._create_form_class.test.field_class, fields.StringField) +- eq_(view2._create_form_class.test.field_class, fields.FileField) ++ assert view1._create_form_class.test.field_class == fields.StringField ++ assert view2._create_form_class.test.field_class == fields.FileField + + + def test_form_onetoone(): +@@ -1880,11 +1858,11 @@ def test_form_onetoone(): + db.session.add(model2) + db.session.commit() + +- eq_(model1.model2, model2) +- eq_(model2.model1, model1) ++ assert model1.model2 == model2 ++ assert model2.model1 == model1 + +- eq_(view1._create_form_class.model2.field_class.widget.multiple, False) +- eq_(view2._create_form_class.model1.field_class.widget.multiple, False) ++ assert not view1._create_form_class.model2.field_class.widget.multiple ++ assert not view2._create_form_class.model1.field_class.widget.multiple + + + def test_relations(): +@@ -1912,17 +1890,17 @@ def test_on_model_change_delete(): + data=dict(test1='test1large', test2='test2')) + + model = db.session.query(Model1).first() +- eq_(model.test1, 'TEST1LARGE') ++ assert model.test1 == 'TEST1LARGE' + + url = '/admin/model1/edit/?id=%s' % model.id + client.post(url, data=dict(test1='test1small', test2='test2large')) + + model = db.session.query(Model1).first() +- eq_(model.test1, 'TEST1SMALL') ++ assert model.test1 == 'TEST1SMALL' + + url = '/admin/model1/delete/?id=%s' % model.id + client.post(url) +- ok_(view.deleted) ++ assert view.deleted + + + def test_multiple_delete(): +@@ -1931,7 +1909,7 @@ def test_multiple_delete(): + + db.session.add_all([M1('a'), M1('b'), M1('c')]) + db.session.commit() +- eq_(M1.query.count(), 3) ++ assert M1.query.count() == 3 + + view = ModelView(M1, db.session) + admin.add_view(view) +@@ -1939,8 +1917,8 @@ def test_multiple_delete(): + client = app.test_client() + + rv = client.post('/admin/model1/action/', data=dict(action='delete', rowid=[1, 2, 3])) +- eq_(rv.status_code, 302) +- eq_(M1.query.count(), 0) ++ assert rv.status_code == 302 ++ assert M1.query.count() == 0 + + + def test_default_sort(): +@@ -1949,17 +1927,17 @@ def test_default_sort(): + + db.session.add_all([M1('c', 'x'), M1('b', 'x'), M1('a', 'y')]) + db.session.commit() +- eq_(M1.query.count(), 3) ++ assert M1.query.count() == 3 + + view = CustomModelView(M1, db.session, column_default_sort='test1') + admin.add_view(view) + + _, data = view.get_list(0, None, None, None, None) + +- eq_(len(data), 3) +- eq_(data[0].test1, 'a') +- eq_(data[1].test1, 'b') +- eq_(data[2].test1, 'c') ++ assert len(data) == 3 ++ assert data[0].test1 == 'a' ++ assert data[1].test1 == 'b' ++ assert data[2].test1 == 'c' + + # test default sort on renamed columns - with column_list scaffolding + view2 = CustomModelView(M1, db.session, column_default_sort='test1', +@@ -1968,10 +1946,10 @@ def test_default_sort(): + + _, data = view2.get_list(0, None, None, None, None) + +- eq_(len(data), 3) +- eq_(data[0].test1, 'a') +- eq_(data[1].test1, 'b') +- eq_(data[2].test1, 'c') ++ assert len(data) == 3 ++ assert data[0].test1 == 'a' ++ assert data[1].test1 == 'b' ++ assert data[2].test1 == 'c' + + # test default sort on renamed columns - without column_list scaffolding + view3 = CustomModelView(M1, db.session, column_default_sort='test1', +@@ -1981,10 +1959,10 @@ def test_default_sort(): + + _, data = view3.get_list(0, None, None, None, None) + +- eq_(len(data), 3) +- eq_(data[0].test1, 'a') +- eq_(data[1].test1, 'b') +- eq_(data[2].test1, 'c') ++ assert len(data) == 3 ++ assert data[0].test1 == 'a' ++ assert data[1].test1 == 'b' ++ assert data[2].test1 == 'c' + + # test default sort with multiple columns + order = [('test2', False), ('test1', False)] +@@ -1993,10 +1971,10 @@ def test_default_sort(): + + _, data = view4.get_list(0, None, None, None, None) + +- eq_(len(data), 3) +- eq_(data[0].test1, 'b') +- eq_(data[1].test1, 'c') +- eq_(data[2].test1, 'a') ++ assert len(data) == 3 ++ assert data[0].test1 == 'b' ++ assert data[1].test1 == 'c' ++ assert data[2].test1 == 'a' + + + def test_complex_sort(): +@@ -2026,13 +2004,13 @@ def test_complex_sort(): + client = app.test_client() + + rv = client.get('/admin/model2/?sort=0') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + + _, data = view.get_list(0, 'model1.test1', False, None, None) + +- eq_(data[0].model1.test1, 'a') +- eq_(data[1].model1.test1, 'b') +- eq_(data[2].model1.test1, 'c') ++ assert data[0].model1.test1 == 'a' ++ assert data[1].model1.test1 == 'b' ++ assert data[2].model1.test1 == 'c' + + # test sorting on multiple columns in related model + view2 = CustomModelView(M2, db.session, +@@ -2041,16 +2019,16 @@ def test_complex_sort(): + admin.add_view(view2) + + rv = client.get('/admin/m1_2/?sort=0') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + + _, data = view2.get_list(0, 'model1', False, None, None) + +- eq_(data[0].model1.test1, 'b') +- eq_(data[1].model1.test1, 'c') +- eq_(data[2].model1.test1, 'a') ++ assert data[0].model1.test1 == 'b' ++ assert data[1].model1.test1 == 'c' ++ assert data[2].model1.test1 == 'a' + + +-@raises(Exception) ++@pytest.mark.xfail(raises=Exception) + def test_complex_sort_exception(): + app, db, admin = setup() + M1, M2 = create_models(db) +@@ -2063,9 +2041,9 @@ def test_complex_sort_exception(): + sort_column = view._get_column_by_idx(0)[0] + _, data = view.get_list(0, sort_column, False, None, None) + +- eq_(len(data), 2) +- eq_(data[0].model1.test1, 'a') +- eq_(data[1].model1.test1, 'b') ++ assert len(data) == 2 ++ assert data[0].model1.test1 == 'a' ++ assert data[1].model1.test1 == 'b' + + + def test_default_complex_sort(): +@@ -2087,9 +2065,9 @@ def test_default_complex_sort(): + + _, data = view.get_list(0, None, None, None, None) + +- eq_(len(data), 2) +- eq_(data[0].model1.test1, 'a') +- eq_(data[1].model1.test1, 'b') ++ assert len(data) == 2 ++ assert data[0].model1.test1 == 'a' ++ assert data[1].model1.test1 == 'b' + + # test column_default_sort on a related table's column object + view2 = CustomModelView(M2, db.session, endpoint="model2_2", +@@ -2098,9 +2076,9 @@ def test_default_complex_sort(): + + _, data = view2.get_list(0, None, None, None, None) + +- eq_(len(data), 2) +- eq_(data[0].model1.test1, 'a') +- eq_(data[1].model1.test1, 'b') ++ assert len(data) == 2 ++ assert data[0].model1.test1 == 'a' ++ assert data[1].model1.test1 == 'b' + + + def test_extra_fields(): +@@ -2119,14 +2097,14 @@ def test_extra_fields(): + client = app.test_client() + + rv = client.get('/admin/model1/new/') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + + # Check presence and order + data = rv.data.decode('utf-8') +- ok_('Extra Field' in data) ++ assert 'Extra Field' in data + pos1 = data.find('Extra Field') + pos2 = data.find('Test1') +- ok_(pos2 < pos1) ++ assert pos2 < pos1 + + + def test_extra_field_order(): +@@ -2146,13 +2124,13 @@ def test_extra_field_order(): + client = app.test_client() + + rv = client.get('/admin/model1/new/') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + + # Check presence and order + data = rv.data.decode('utf-8') + pos1 = data.find('Extra Field') + pos2 = data.find('Test1') +- ok_(pos2 > pos1) ++ assert pos2 > pos1 + + + def test_modelview_localization(): +@@ -2175,10 +2153,10 @@ def test_modelview_localization(): + client = app.test_client() + + rv = client.get('/admin/model1/') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + + rv = client.get('/admin/model1/new/') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + except: + print("Error on the following locale:", locale) + raise +@@ -2206,7 +2184,7 @@ def test_modelview_named_filter_localiza + flt = filters[2] + with app.test_request_context(): + flt_name = view.get_filter_arg(2, flt) +- eq_('test1_equals', flt_name) ++ assert 'test1_equals' == flt_name + + + def test_custom_form_base(): +@@ -2223,10 +2201,10 @@ def test_custom_form_base(): + ) + admin.add_view(view) + +- ok_(hasattr(view._create_form_class, 'test1')) ++ assert hasattr(view._create_form_class, 'test1') + + create_form = view.create_form() +- ok_(isinstance(create_form, TestForm)) ++ assert isinstance(create_form, TestForm) + + + def test_ajax_fk(): +@@ -2245,7 +2223,7 @@ def test_ajax_fk(): + ) + admin.add_view(view) + +- ok_(u'model1' in view._form_ajax_refs) ++ assert u'model1' in view._form_ajax_refs + + model = Model1(u'first') + model2 = Model1(u'foo', u'bar') +@@ -2255,41 +2233,41 @@ def test_ajax_fk(): + # Check loader + loader = view._form_ajax_refs[u'model1'] + mdl = loader.get_one(model.id) +- eq_(mdl.test1, model.test1) ++ assert mdl.test1 == model.test1 + + items = loader.get_list(u'fir') +- eq_(len(items), 1) +- eq_(items[0].id, model.id) ++ assert len(items) == 1 ++ assert items[0].id == model.id + + items = loader.get_list(u'bar') +- eq_(len(items), 1) +- eq_(items[0].test1, u'foo') ++ assert len(items) == 1 ++ assert items[0].test1 == u'foo' + + # Check form generation + form = view.create_form() +- eq_(form.model1.__class__.__name__, u'AjaxSelectField') ++ assert form.model1.__class__.__name__ == u'AjaxSelectField' + + with app.test_request_context('/admin/view/'): +- ok_(u'value=""' not in form.model1()) ++ assert u'value=""' not in form.model1() + + form.model1.data = model +- ok_(u'data-json="[%s, "first"]"' % model.id in form.model1()) +- ok_(u'value="1"' in form.model1()) ++ assert re.search(r'data-json="\[%s, ("|")first("|")\]"' % model.id, form.model1()) ++ assert u'value="1"' in form.model1() + + # Check querying + client = app.test_client() + + req = client.get(u'/admin/view/ajax/lookup/?name=model1&query=foo') +- eq_(req.data.decode('utf-8'), u'[[%s, "foo"]]' % model2.id) ++ assert req.data.decode('utf-8') == u'[[%s, "foo"]]' % model2.id + + # Check submitting + req = client.post('/admin/view/new/', data={u'model1': as_unicode(model.id)}) + mdl = db.session.query(Model2).first() + +- ok_(mdl is not None) +- ok_(mdl.model1 is not None) +- eq_(mdl.model1.id, model.id) +- eq_(mdl.model1.test1, u'first') ++ assert mdl is not None ++ assert mdl.model1 is not None ++ assert mdl.model1.id == model.id ++ assert mdl.model1.test1 == u'first' + + + def test_ajax_fk_multi(): +@@ -2331,7 +2309,7 @@ def test_ajax_fk_multi(): + ) + admin.add_view(view) + +- ok_(u'model1' in view._form_ajax_refs) ++ assert u'model1' in view._form_ajax_refs + + model = Model1(name=u'first') + db.session.add_all([model, Model1(name=u'foo')]) +@@ -2339,22 +2317,22 @@ def test_ajax_fk_multi(): + + # Check form generation + form = view.create_form() +- eq_(form.model1.__class__.__name__, u'AjaxSelectMultipleField') ++ assert form.model1.__class__.__name__ == u'AjaxSelectMultipleField' + + with app.test_request_context('/admin/view/'): +- ok_(u'data-json="[]"' in form.model1()) ++ assert u'data-json="[]"' in form.model1() + + form.model1.data = [model] +- ok_(u'data-json="[[1, "first"]]"' in form.model1()) ++ assert re.search(r'data-json="\[\[1, ("|")first("|")\]\]"', form.model1()) + + # Check submitting + client = app.test_client() + client.post('/admin/view/new/', data={u'model1': as_unicode(model.id)}) + mdl = db.session.query(Model2).first() + +- ok_(mdl is not None) +- ok_(mdl.model1 is not None) +- eq_(len(mdl.model1), 1) ++ assert mdl is not None ++ assert mdl.model1 is not None ++ assert len(mdl.model1) == 1 + + + def test_safe_redirect(): +@@ -2370,19 +2348,19 @@ def test_safe_redirect(): + data=dict(test1='test1large', test2='test2', + _continue_editing='Save and Continue Editing')) + +- eq_(rv.status_code, 302) +- assert_true(rv.location.startswith('http://localhost/admin/model1/edit/')) +- assert_true('url=http%3A%2F%2Flocalhost%2Fadmin%2Fmodel2view%2F' in rv.location) +- assert_true('id=1' in rv.location) ++ assert rv.status_code == 302 ++ assert rv.location.startswith('http://localhost/admin/model1/edit/') ++ assert 'url=http%3A%2F%2Flocalhost%2Fadmin%2Fmodel2view%2F' in rv.location ++ assert 'id=1' in rv.location + + rv = client.post('/admin/model1/new/?url=http://google.com/evil/', + data=dict(test1='test1large', test2='test2', + _continue_editing='Save and Continue Editing')) + +- eq_(rv.status_code, 302) +- assert_true(rv.location.startswith('http://localhost/admin/model1/edit/')) +- assert_true('url=%2Fadmin%2Fmodel1%2F' in rv.location) +- assert_true('id=2' in rv.location) ++ assert rv.status_code == 302 ++ assert rv.location.startswith('http://localhost/admin/model1/edit/') ++ assert 'url=%2Fadmin%2Fmodel1%2F' in rv.location ++ assert 'id=2' in rv.location + + + def test_simple_list_pager(): +@@ -2399,7 +2377,7 @@ def test_simple_list_pager(): + admin.add_view(view) + + count, data = view.get_list(0, None, None, None, None) +- assert_true(count is None) ++ assert count is None + + + def test_unlimited_page_size(): +@@ -2416,12 +2394,12 @@ def test_unlimited_page_size(): + # test 0 as page_size + _, data = view.get_list(0, None, None, None, None, execute=True, + page_size=0) +- eq_(len(data), 21) ++ assert len(data) == 21 + + # test False as page_size + _, data = view.get_list(0, None, None, None, None, execute=True, + page_size=False) +- eq_(len(data), 21) ++ assert len(data) == 21 + + + def test_advanced_joins(): +@@ -2457,44 +2435,44 @@ def test_advanced_joins(): + + # Test joins + attr, path = tools.get_field_with_path(Model2, 'model1.val1') +- eq_(attr, Model1.val1) +- eq_(path, [Model2.model1]) ++ assert attr == Model1.val1 ++ assert path == [Model2.model1] + + attr, path = tools.get_field_with_path(Model1, 'model2.val2') +- eq_(attr, Model2.val2) +- eq_(id(path[0]), id(Model1.model2)) ++ assert attr == Model2.val2 ++ assert id(path[0]) == id(Model1.model2) + + attr, path = tools.get_field_with_path(Model3, 'model2.model1.val1') +- eq_(attr, Model1.val1) +- eq_(path, [Model3.model2, Model2.model1]) ++ assert attr == Model1.val1 ++ assert path == [Model3.model2, Model2.model1] + + # Test how joins are applied + query = view3.get_query() + + joins = {} + q1, joins, alias = view3._apply_path_joins(query, joins, path) +- ok_((True, Model3.model2) in joins) +- ok_((True, Model2.model1) in joins) +- ok_(alias is not None) ++ assert (True, Model3.model2) in joins ++ assert (True, Model2.model1) in joins ++ assert alias is not None + + # Check if another join would use same path + attr, path = tools.get_field_with_path(Model2, 'model1.test') + q2, joins, alias = view2._apply_path_joins(query, joins, path) + +- eq_(len(joins), 2) ++ assert len(joins) == 2 + for p in q2._join_entities: +- ok_(p in q1._join_entities) ++ assert p in q1._join_entities + +- ok_(alias is not None) ++ assert alias is not None + + # Check if normal properties are supported by tools.get_field_with_path + attr, path = tools.get_field_with_path(Model2, Model1.test) +- eq_(attr, Model1.test) +- eq_(path, [Model1.__table__]) ++ assert attr == Model1.test ++ assert path == [Model1.__table__] + + q3, joins, alias = view2._apply_path_joins(view2.get_query(), joins, path) +- eq_(len(joins), 3) +- ok_(alias is None) ++ assert len(joins) == 3 ++ assert alias is None + + + def test_multipath_joins(): +@@ -2523,7 +2501,7 @@ def test_multipath_joins(): + client = app.test_client() + + rv = client.get('/admin/model2/') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + + + def test_different_bind_joins(): +@@ -2551,7 +2529,7 @@ def test_different_bind_joins(): + client = app.test_client() + + rv = client.get('/admin/model2/') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + + + def test_model_default(): +@@ -2566,7 +2544,7 @@ def test_model_default(): + + client = app.test_client() + rv = client.post('/admin/model2/new/', data=dict()) +- assert_true(b'This field is required' not in rv.data) ++ assert b'This field is required' not in rv.data + + + def test_export_csv(): +@@ -2586,10 +2564,10 @@ def test_export_csv(): + # test export_max_rows + rv = client.get('/admin/row_limit_2/export/csv/') + data = rv.data.decode('utf-8') +- eq_(rv.status_code, 200) +- ok_("Test1,Test2\r\n" +- "test1_val_1,test2_val_1\r\n" +- "test1_val_2,test2_val_2\r\n" == data) ++ assert rv.status_code == 200 ++ assert "Test1,Test2\r\n" + \ ++ "test1_val_1,test2_val_1\r\n" + \ ++ "test1_val_2,test2_val_2\r\n" == data + + view = CustomModelView(Model1, db.session, can_export=True, + column_list=['test1', 'test2'], +@@ -2599,5 +2577,5 @@ def test_export_csv(): + # test row limit without export_max_rows + rv = client.get('/admin/no_row_limit/export/csv/') + data = rv.data.decode('utf-8') +- eq_(rv.status_code, 200) +- ok_(len(data.splitlines()) > 21) ++ assert rv.status_code == 200 ++ assert len(data.splitlines()) > 21 +--- a/flask_admin/tests/sqla/test_form_rules.py ++++ b/flask_admin/tests/sqla/test_form_rules.py +@@ -1,5 +1,3 @@ +-from nose.tools import eq_, ok_ +- + from . import setup + from .test_basic import CustomModelView, create_models + +@@ -19,16 +17,16 @@ def test_form_rules(): + client = app.test_client() + + rv = client.get('/admin/model1/new/') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + + data = rv.data.decode('utf-8') + pos1 = data.find('Test1') + pos2 = data.find('Test2') + pos3 = data.find('Test3') + pos4 = data.find('Test4') +- ok_(pos1 > pos2) +- ok_(pos4 > pos1) +- ok_(pos3 == -1) ++ assert pos1 > pos2 ++ assert pos4 > pos1 ++ assert pos3 == -1 + + + def test_rule_macro(): +@@ -46,11 +44,11 @@ def test_rule_macro(): + client = app.test_client() + + rv = client.get('/admin/model1/new/') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + + data = rv.data.decode('utf-8') +- ok_('Value = foobar' in data) +- ok_('Hello another_test' in data) ++ assert 'Value = foobar' in data ++ assert 'Hello another_test' in data + + + def test_rule_container(): +@@ -67,16 +65,16 @@ def test_rule_container(): + client = app.test_client() + + rv = client.get('/admin/model1/new/') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + + data = rv.data.decode('utf-8') + pos1 = data.find('') + pos2 = data.find('another_test') + pos3 = data.find('') +- ok_(pos1 != -1) +- ok_(pos2 != -1) +- ok_(pos3 != -1) +- ok_(pos1 < pos2 < pos3) ++ assert pos1 != -1 ++ assert pos2 != -1 ++ assert pos3 != -1 ++ assert pos1 < pos2 < pos3 + + + def test_rule_header(): +@@ -92,10 +90,10 @@ def test_rule_header(): + client = app.test_client() + + rv = client.get('/admin/model1/new/') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + + data = rv.data.decode('utf-8') +- ok_('

hello

' in data) ++ assert '

hello

' in data + + + def test_rule_field_set(): +@@ -111,17 +109,17 @@ def test_rule_field_set(): + client = app.test_client() + + rv = client.get('/admin/model1/new/') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + + data = rv.data.decode('utf-8') +- ok_('

header

' in data) ++ assert '

header

' in data + pos1 = data.find('Test1') + pos2 = data.find('Test2') + pos3 = data.find('Test3') + pos4 = data.find('Test4') +- ok_(pos1 > pos2) +- ok_(pos4 > pos1) +- ok_(pos3 == -1) ++ assert pos1 > pos2 ++ assert pos4 > pos1 ++ assert pos3 == -1 + + + def test_rule_inlinefieldlist(): +@@ -138,7 +136,7 @@ def test_rule_inlinefieldlist(): + client = app.test_client() + + rv = client.get('/admin/model1/new/') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + + + def test_inline_model_rules(): +@@ -154,7 +152,7 @@ def test_inline_model_rules(): + client = app.test_client() + + rv = client.get('/admin/model1/new/') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + + data = rv.data.decode('utf-8') +- ok_('int_field' not in data) ++ assert 'int_field' not in data +--- a/flask_admin/tests/sqla/test_inlineform.py ++++ b/flask_admin/tests/sqla/test_inlineform.py +@@ -1,6 +1,4 @@ + # -*- coding: utf-8 -*- +-from nose.tools import eq_, ok_ +- + from wtforms import fields + + from flask_admin import form +@@ -42,35 +40,35 @@ def test_inline_form(): + admin.add_view(view) + + # Basic tests +- ok_(view._create_form_class is not None) +- ok_(view._edit_form_class is not None) +- eq_(view.endpoint, 'user') ++ assert view._create_form_class is not None ++ assert view._edit_form_class is not None ++ assert view.endpoint == 'user' + + # Verify form +- eq_(view._create_form_class.name.field_class, fields.StringField) +- eq_(view._create_form_class.info.field_class, InlineModelFormList) ++ assert view._create_form_class.name.field_class == fields.StringField ++ assert view._create_form_class.info.field_class == InlineModelFormList + + rv = client.get('/admin/user/') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + + rv = client.get('/admin/user/new/') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + + # Create + rv = client.post('/admin/user/new/', data=dict(name=u'äõüxyz')) +- eq_(rv.status_code, 302) +- eq_(User.query.count(), 1) +- eq_(UserInfo.query.count(), 0) ++ assert rv.status_code == 302 ++ assert User.query.count() == 1 ++ assert UserInfo.query.count() == 0 + + data = {'name': u'fbar', 'info-0-key': 'foo', 'info-0-val': 'bar'} + rv = client.post('/admin/user/new/', data=data) +- eq_(rv.status_code, 302) +- eq_(User.query.count(), 2) +- eq_(UserInfo.query.count(), 1) ++ assert rv.status_code == 302 ++ assert User.query.count() == 2 ++ assert UserInfo.query.count() == 1 + + # Edit + rv = client.get('/admin/user/edit/?id=2') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + # Edit - update + data = { + 'name': u'barfoo', +@@ -79,8 +77,8 @@ def test_inline_form(): + 'info-0-val': u'yyy', + } + rv = client.post('/admin/user/edit/?id=2', data=data) +- eq_(UserInfo.query.count(), 1) +- eq_(UserInfo.query.one().key, u'xxx') ++ assert UserInfo.query.count() == 1 ++ assert UserInfo.query.one().key == u'xxx' + + # Edit - add & delete + data = { +@@ -94,20 +92,20 @@ def test_inline_form(): + 'info-1-val': u'foo', + } + rv = client.post('/admin/user/edit/?id=2', data=data) +- eq_(rv.status_code, 302) +- eq_(User.query.count(), 2) +- eq_(User.query.get(2).name, u'barf') +- eq_(UserInfo.query.count(), 1) +- eq_(UserInfo.query.one().key, u'bar') ++ assert rv.status_code == 302 ++ assert User.query.count() == 2 ++ assert User.query.get(2).name == u'barf' ++ assert UserInfo.query.count() == 1 ++ assert UserInfo.query.one().key == u'bar' + + # Delete + rv = client.post('/admin/user/delete/?id=2') +- eq_(rv.status_code, 302) +- eq_(User.query.count(), 1) ++ assert rv.status_code == 302 ++ assert User.query.count() == 1 + rv = client.post('/admin/user/delete/?id=1') +- eq_(rv.status_code, 302) +- eq_(User.query.count(), 0) +- eq_(UserInfo.query.count(), 0) ++ assert rv.status_code == 302 ++ assert User.query.count() == 0 ++ assert UserInfo.query.count() == 0 + + + def test_inline_form_required(): +@@ -145,17 +143,17 @@ def test_inline_form_required(): + + # Create + rv = client.post('/admin/user/new/', data=dict(name=u'no-email')) +- eq_(rv.status_code, 200) +- eq_(User.query.count(), 0) ++ assert rv.status_code == 200 ++ assert User.query.count() == 0 + + data = { + 'name': 'hasEmail', + 'emails-0-email': 'foo@bar.com', + } + rv = client.post('/admin/user/new/', data=data) +- eq_(rv.status_code, 302) +- eq_(User.query.count(), 1) +- eq_(UserEmail.query.count(), 1) ++ assert rv.status_code == 302 ++ assert User.query.count() == 1 ++ assert UserEmail.query.count() == 1 + + + def test_inline_form_ajax_fk(): +@@ -208,10 +206,10 @@ def test_inline_form_ajax_fk(): + form = view.create_form() + user_info_form = form.info.unbound_field.args[0] + loader = user_info_form.tag.args[0] +- eq_(loader.name, 'userinfo-tag') +- eq_(loader.model, Tag) ++ assert loader.name == 'userinfo-tag' ++ assert loader.model == Tag + +- ok_('userinfo-tag' in view._form_ajax_refs) ++ assert 'userinfo-tag' in view._form_ajax_refs + + + def test_inline_form_self(): +@@ -232,7 +230,7 @@ def test_inline_form_self(): + parent = Tree() + child = Tree(parent=parent) + form = view.edit_form(child) +- eq_(form.parent.data, parent) ++ assert form.parent.data == parent + + + def test_inline_form_base_class(): +@@ -286,6 +284,6 @@ def test_inline_form_base_class(): + 'emails-0-email': '', + } + rv = client.post('/admin/user/new/', data=data) +- eq_(rv.status_code, 200) +- eq_(User.query.count(), 0) +- ok_(b'success!' in rv.data, rv.data) ++ assert rv.status_code == 200 ++ assert User.query.count() == 0 ++ assert b'success!' in rv.data, rv.data +--- a/flask_admin/tests/sqla/test_multi_pk.py ++++ b/flask_admin/tests/sqla/test_multi_pk.py +@@ -1,5 +1,3 @@ +-from nose.tools import eq_, ok_ +- + from . import setup + from .test_basic import CustomModelView + +@@ -24,25 +22,25 @@ def test_multiple_pk(): + client = app.test_client() + + rv = client.get('/admin/model/') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + + rv = client.post('/admin/model/new/', + data=dict(id=1, id2='two', test='test3')) +- eq_(rv.status_code, 302) ++ assert rv.status_code == 302 + + rv = client.get('/admin/model/') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('test3' in data) ++ assert 'test3' in data + + rv = client.get('/admin/model/edit/?id=1,two') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('test3' in data) ++ assert 'test3' in data + + # Correct order is mandatory -> fail here + rv = client.get('/admin/model/edit/?id=two,1') +- eq_(rv.status_code, 302) ++ assert rv.status_code == 302 + + + def test_joined_inheritance(): +@@ -71,17 +69,17 @@ def test_joined_inheritance(): + client = app.test_client() + + rv = client.get('/admin/child/') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + + rv = client.post('/admin/child/new/', + data=dict(id=1, test='foo', name='bar')) +- eq_(rv.status_code, 302) ++ assert rv.status_code == 302 + + rv = client.get('/admin/child/edit/?id=1') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('foo' in data) +- ok_('bar' in data) ++ assert 'foo' in data ++ assert 'bar' in data + + + def test_single_table_inheritance(): +@@ -111,17 +109,17 @@ def test_single_table_inheritance(): + client = app.test_client() + + rv = client.get('/admin/child/') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + + rv = client.post('/admin/child/new/', + data=dict(id=1, test='foo', name='bar')) +- eq_(rv.status_code, 302) ++ assert rv.status_code == 302 + + rv = client.get('/admin/child/edit/?id=1') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('foo' in data) +- ok_('bar' in data) ++ assert 'foo' in data ++ assert 'bar' in data + + + def test_concrete_table_inheritance(): +@@ -146,17 +144,17 @@ def test_concrete_table_inheritance(): + client = app.test_client() + + rv = client.get('/admin/child/') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + + rv = client.post('/admin/child/new/', + data=dict(id=1, test='foo', name='bar')) +- eq_(rv.status_code, 302) ++ assert rv.status_code == 302 + + rv = client.get('/admin/child/edit/?id=1') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('foo' in data) +- ok_('bar' in data) ++ assert 'foo' in data ++ assert 'bar' in data + + + def test_concrete_multipk_inheritance(): +@@ -182,14 +180,14 @@ def test_concrete_multipk_inheritance(): + client = app.test_client() + + rv = client.get('/admin/child/') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + + rv = client.post('/admin/child/new/', + data=dict(id=1, id2=2, test='foo', name='bar')) +- eq_(rv.status_code, 302) ++ assert rv.status_code == 302 + + rv = client.get('/admin/child/edit/?id=1,2') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('foo' in data) +- ok_('bar' in data) ++ assert 'foo' in data ++ assert 'bar' in data +--- a/flask_admin/tests/sqla/test_postgres.py ++++ b/flask_admin/tests/sqla/test_postgres.py +@@ -1,5 +1,3 @@ +-from nose.tools import eq_, ok_ +- + from . import setup_postgres + from .test_basic import CustomModelView + +@@ -22,25 +20,25 @@ def test_hstore(): + client = app.test_client() + + rv = client.get('/admin/model/') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + + rv = client.post('/admin/model/new/', data={ + 'hstore_test-0-key': 'test_val1', + 'hstore_test-0-value': 'test_val2' + }) +- eq_(rv.status_code, 302) ++ assert rv.status_code == 302 + + rv = client.get('/admin/model/') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('test_val1' in data) +- ok_('test_val2' in data) ++ assert 'test_val1' in data ++ assert 'test_val2' in data + + rv = client.get('/admin/model/edit/?id=1') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('test_val1' in data) +- ok_('test_val2' in data) ++ assert 'test_val1' in data ++ assert 'test_val2' in data + + + def test_json(): +@@ -58,24 +56,24 @@ def test_json(): + client = app.test_client() + + rv = client.get('/admin/jsonmodel/') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + + rv = client.post('/admin/jsonmodel/new/', data={ + 'json_test': '{"test_key1": "test_value1"}', + }) +- eq_(rv.status_code, 302) ++ assert rv.status_code == 302 + + rv = client.get('/admin/jsonmodel/') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('json_test' in data) +- ok_('{"test_key1": "test_value1"}' in data) ++ assert 'json_test' in data ++ assert '{"test_key1": "test_value1"}' in data + + rv = client.get('/admin/jsonmodel/edit/?id=1') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('json_test' in data) +- ok_('>{"test_key1": "test_value1"}<' in data) ++ assert 'json_test' in data ++ assert '>{"test_key1": "test_value1"}<' in data + + + def test_citext(): +@@ -94,21 +92,21 @@ def test_citext(): + client = app.test_client() + + rv = client.get('/admin/citextmodel/') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + + rv = client.post('/admin/citextmodel/new/', data={ + 'citext_test': 'Foo', + }) +- eq_(rv.status_code, 302) ++ assert rv.status_code == 302 + + rv = client.get('/admin/citextmodel/') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('citext_test' in data) +- ok_('Foo' in data) ++ assert 'citext_test' in data ++ assert 'Foo' in data + + rv = client.get('/admin/citextmodel/edit/?id=1') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('name="citext_test"' in data) +- ok_('>Foo<' in data) ++ assert 'name="citext_test"' in data ++ assert '>Foo<' in data +--- a/flask_admin/tests/sqla/test_translation.py ++++ b/flask_admin/tests/sqla/test_translation.py +@@ -1,6 +1,5 @@ + from flask_admin.babel import lazy_gettext + from flask_babelex import Babel +-from nose.tools import eq_, ok_ + + from . import setup + from .test_basic import CustomModelView, create_models +@@ -25,5 +24,5 @@ def test_column_label_translation(): + client = app.test_client() + + rv = client.get('/admin/model1/?flt1_0=test') +- eq_(rv.status_code, 200) +- ok_('{"Nombre":' in rv.data.decode('utf-8')) ++ assert rv.status_code == 200 ++ assert '{"Nombre":' in rv.data.decode('utf-8') +--- a/flask_admin/tests/test_base.py ++++ b/flask_admin/tests/test_base.py +@@ -1,6 +1,6 @@ + import os + +-from nose.tools import ok_, eq_, raises ++import pytest + + from flask import Flask, request, abort, url_for + from flask.views import MethodView +@@ -76,27 +76,27 @@ class MockMethodView(base.BaseView): + + def test_baseview_defaults(): + view = MockView() +- eq_(view.name, None) +- eq_(view.category, None) +- eq_(view.endpoint, 'mockview') +- eq_(view.url, None) +- eq_(view.static_folder, None) +- eq_(view.admin, None) +- eq_(view.blueprint, None) ++ 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() +- eq_(admin.name, 'Admin') +- eq_(admin.url, '/admin') +- eq_(admin.endpoint, 'admin') +- eq_(admin.app, None) +- ok_(admin.index_view is not None) +- eq_(admin.index_view._template, 'admin/index.html') ++ 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 +- eq_(len(admin._views), 1) +- eq_(admin._views[0], admin.index_view) ++ assert len(admin._views) == 1 ++ assert admin._views[0] == admin.index_view + + + def test_custom_index_view(): +@@ -104,16 +104,16 @@ def test_custom_index_view(): + url='/d', template='e') + admin = base.Admin(index_view=view) + +- eq_(admin.endpoint, 'c') +- eq_(admin.url, '/d') +- ok_(admin.index_view is view) +- eq_(view.name, 'a') +- eq_(view.category, 'b') +- eq_(view._template, 'e') ++ 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 +- eq_(len(admin._views), 1) +- eq_(admin._views[0], view) ++ assert len(admin._views) == 1 ++ assert admin._views[0] == view + + + def test_custom_index_view_in_init_app(): +@@ -123,41 +123,41 @@ def test_custom_index_view_in_init_app() + admin = base.Admin() + admin.init_app(app, index_view=view) + +- eq_(admin.endpoint, 'c') +- eq_(admin.url, '/d') +- ok_(admin.index_view is view) +- eq_(view.name, 'a') +- eq_(view.category, 'b') +- eq_(view._template, 'e') ++ 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 +- eq_(len(admin._views), 1) +- eq_(admin._views[0], view) ++ assert len(admin._views) == 1 ++ assert admin._views[0] == view + + + def test_base_registration(): + app = Flask(__name__) + admin = base.Admin(app) + +- eq_(admin.app, app) +- ok_(admin.index_view.blueprint is not None) ++ 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') +- eq_(admin.name, 'Test') +- eq_(admin.url, '/foobar') +- eq_(admin.index_view.blueprint.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/') +- eq_(rv.status_code, 200) ++ 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')) +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + + + def test_baseview_registration(): +@@ -167,43 +167,43 @@ def test_baseview_registration(): + bp = view.create_blueprint(admin) + + # Base properties +- eq_(view.admin, admin) +- ok_(view.blueprint is not None) ++ assert view.admin == admin ++ assert view.blueprint is not None + + # Calculated properties +- eq_(view.endpoint, 'mockview') +- eq_(view.url, '/admin/mockview') +- eq_(view.name, 'Mock View') ++ assert view.endpoint == 'mockview' ++ assert view.url == '/admin/mockview' ++ assert view.name == 'Mock View' + + # Verify generated blueprint properties +- eq_(bp.name, view.endpoint) +- eq_(bp.url_prefix, view.url) +- eq_(bp.template_folder, os.path.join('templates', 'bootstrap2')) +- eq_(bp.static_folder, view.static_folder) ++ 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()) + +- eq_(view.name, 'Test') +- eq_(view.endpoint, 'foobar') +- eq_(view.url, '/admin/foobar') ++ assert view.name == 'Test' ++ assert view.endpoint == 'foobar' ++ assert view.url == '/admin/foobar' + + view = MockView(url='test') + view.create_blueprint(base.Admin()) +- eq_(view.url, '/admin/test') ++ assert view.url == '/admin/test' + + view = MockView(url='/test/test') + view.create_blueprint(base.Admin()) +- eq_(view.url, '/test/test') ++ assert view.url == '/test/test' + + view = MockView(endpoint='test') + view.create_blueprint(base.Admin(url='/')) +- eq_(view.url, '/test') ++ assert view.url == '/test' + + view = MockView(static_url_path='/static/my/test') + view.create_blueprint(base.Admin()) +- eq_(view.blueprint.static_url_path, '/static/my/test') ++ assert view.blueprint.static_url_path == '/static/my/test' + + + def test_baseview_urls(): +@@ -213,7 +213,7 @@ def test_baseview_urls(): + view = MockView() + admin.add_view(view) + +- eq_(len(view._urls), 2) ++ assert len(view._urls) == 2 + + + def test_add_views(): +@@ -222,10 +222,10 @@ def test_add_views(): + + admin.add_views(MockView(endpoint='test1'), MockView(endpoint='test2')) + +- eq_(len(admin.menu()), 3) ++ assert len(admin.menu()) == 3 + + +-@raises(Exception) ++@pytest.mark.xfail(raises=Exception) + def test_no_default(): + app = Flask(__name__) + admin = base.Admin(app) +@@ -240,18 +240,18 @@ def test_call(): + client = app.test_client() + + rv = client.get('/admin/') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + + rv = client.get('/admin/mockview/') +- eq_(rv.data, b'Success!') ++ assert rv.data == b'Success!' + + rv = client.get('/admin/mockview/test/') +- eq_(rv.data, b'Success!') ++ assert rv.data == b'Success!' + + # Check authentication failure + view.allow_call = False + rv = client.get('/admin/mockview/') +- eq_(rv.data, b'Failure!') ++ assert rv.data == b'Failure!' + + + def test_permissions(): +@@ -264,7 +264,7 @@ def test_permissions(): + view.allow_access = False + + rv = client.get('/admin/mockview/') +- eq_(rv.status_code, 403) ++ assert rv.status_code == 403 + + + def test_inaccessible_callback(): +@@ -278,7 +278,7 @@ def test_inaccessible_callback(): + view.inaccessible_callback = lambda *args, **kwargs: abort(418) + + rv = client.get('/admin/mockview/') +- eq_(rv.status_code, 418) ++ assert rv.status_code == 418 + + + def get_visibility(): +@@ -293,7 +293,7 @@ def get_visibility(): + client = app.test_client() + + rv = client.get('/admin/mockview/') +- ok_('TestMenuItem' not in rv.data.decode('utf-8')) ++ assert 'TestMenuItem' not in rv.data.decode('utf-8') + + + def test_submenu(): +@@ -306,21 +306,21 @@ def test_submenu(): + view.allow_access = False + admin.add_view(view) + +- ok_('Test' in admin._menu_categories) +- eq_(len(admin._menu), 2) +- eq_(admin._menu[1].name, 'Test') +- eq_(len(admin._menu[1]._children), 2) ++ 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 +- eq_(admin._menu[1].get_url(), None) ++ assert admin._menu[1].get_url() is None + + # Categories are only accessible if there is at least one accessible child +- eq_(admin._menu[1].is_accessible(), True) ++ assert admin._menu[1].is_accessible() + + children = admin._menu[1].get_children() +- eq_(len(children), 1) ++ assert len(children) == 1 + +- ok_(children[0].is_accessible()) ++ assert children[0].is_accessible() + + + def test_delayed_init(): +@@ -332,7 +332,7 @@ def test_delayed_init(): + client = app.test_client() + + rv = client.get('/admin/mockview/') +- eq_(rv.data, b'Success!') ++ assert rv.data == b'Success!' + + + def test_multi_instances_init(): +@@ -345,7 +345,7 @@ def test_multi_instances_init(): + _ = base.Admin(app, index_view=ManageIndex(url='/manage', endpoint='manage')) # noqa: F841 + + +-@raises(Exception) ++@pytest.mark.xfail(raises=Exception) + def test_double_init(): + app = Flask(__name__) + admin = base.Admin(app) +@@ -363,27 +363,27 @@ def test_nested_flask_views(): + + rv = client.get('/admin/mockmethodview/_api/1') + print('"', rv.data, '"') +- eq_(rv.data, b'GET - API1') ++ assert rv.data == b'GET - API1' + rv = client.put('/admin/mockmethodview/_api/1') +- eq_(rv.data, b'PUT - API1') ++ assert rv.data == b'PUT - API1' + rv = client.post('/admin/mockmethodview/_api/1') +- eq_(rv.data, b'POST - API1') ++ assert rv.data == b'POST - API1' + rv = client.delete('/admin/mockmethodview/_api/1') +- eq_(rv.data, b'DELETE - API1') ++ assert rv.data == b'DELETE - API1' + + rv = client.get('/admin/mockmethodview/_api/2') +- eq_(rv.data, b'GET - API2') ++ assert rv.data == b'GET - API2' + rv = client.post('/admin/mockmethodview/_api/2') +- eq_(rv.data, b'POST - API2') ++ assert rv.data == b'POST - API2' + rv = client.delete('/admin/mockmethodview/_api/2') +- eq_(rv.status_code, 405) ++ assert rv.status_code == 405 + rv = client.put('/admin/mockmethodview/_api/2') +- eq_(rv.status_code, 405) ++ assert rv.status_code == 405 + + rv = client.get('/admin/mockmethodview/_api/3') +- eq_(rv.data, b'GET - API3') ++ assert rv.data == b'GET - API3' + rv = client.get('/admin/mockmethodview/_api/4') +- eq_(rv.data, b'GET - API3') ++ assert rv.data == b'GET - API3' + + + def test_root_mount(): +@@ -393,12 +393,12 @@ def test_root_mount(): + + client = app.test_client() + rv = client.get('/mockview/') +- eq_(rv.data, b'Success!') ++ 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')) +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + + + def test_menu_links(): +@@ -411,8 +411,8 @@ def test_menu_links(): + rv = client.get('/admin/') + + data = rv.data.decode('utf-8') +- ok_('TestMenuLink1' in data) +- ok_('TestMenuLink2' in data) ++ assert 'TestMenuLink1' in data ++ assert 'TestMenuLink2' in data + + + def test_add_links(): +@@ -425,13 +425,13 @@ def test_add_links(): + rv = client.get('/admin/') + + data = rv.data.decode('utf-8') +- ok_('TestMenuLink1' in data) +- ok_('TestMenuLink2' in data) ++ assert 'TestMenuLink1' in data ++ assert 'TestMenuLink2' in data + + + def check_class_name(): + view = MockView() +- eq_(view.name, 'Mock View') ++ assert view.name == 'Mock View' + + + def check_endpoint(): +@@ -440,4 +440,4 @@ def check_endpoint(): + return 'admin.' + super(CustomView, self)._get_endpoint(endpoint) + + view = CustomView() +- eq_(view.endpoint, 'admin.customview') ++ assert view.endpoint == 'admin.customview' +--- a/flask_admin/tests/test_form_upload.py ++++ b/flask_admin/tests/test_form_upload.py +@@ -3,8 +3,6 @@ import os.path as op + + from io import BytesIO + +-from nose.tools import eq_, ok_ +- + from flask import Flask, url_for + from flask_admin import form, helpers + +@@ -47,7 +45,7 @@ def test_upload_field(): + pass + + my_form = TestForm() +- eq_(my_form.upload.base_path, path) ++ assert my_form.upload.base_path == path + + _remove_testfiles() + +@@ -57,35 +55,35 @@ def test_upload_field(): + with app.test_request_context(method='POST', data={'upload': (BytesIO(b'Hello World 1'), 'test1.txt')}): + my_form = TestForm(helpers.get_form_data()) + +- ok_(my_form.validate()) ++ assert my_form.validate() + + my_form.populate_obj(dummy) + +- eq_(dummy.upload, 'test1.txt') +- ok_(op.exists(op.join(path, 'test1.txt'))) ++ assert dummy.upload == 'test1.txt' ++ assert op.exists(op.join(path, 'test1.txt')) + + # Check replace + with app.test_request_context(method='POST', data={'upload': (BytesIO(b'Hello World 2'), 'test2.txt')}): + my_form = TestForm(helpers.get_form_data()) + +- ok_(my_form.validate()) ++ assert my_form.validate() + my_form.populate_obj(dummy) + +- eq_(dummy.upload, 'test2.txt') +- ok_(not op.exists(op.join(path, 'test1.txt'))) +- ok_(op.exists(op.join(path, 'test2.txt'))) ++ assert dummy.upload == 'test2.txt' ++ assert not op.exists(op.join(path, 'test1.txt')) ++ assert op.exists(op.join(path, 'test2.txt')) + + # Check delete + with app.test_request_context(method='POST', data={'_upload-delete': 'checked'}): + + my_form = TestForm(helpers.get_form_data()) + +- ok_(my_form.validate()) ++ assert my_form.validate() + + my_form.populate_obj(dummy) +- eq_(dummy.upload, None) ++ assert dummy.upload is None + +- ok_(not op.exists(op.join(path, 'test2.txt'))) ++ assert not op.exists(op.join(path, 'test2.txt')) + + # Check overwrite + _remove_testfiles() +@@ -93,15 +91,15 @@ def test_upload_field(): + with app.test_request_context(method='POST', data={'upload': (BytesIO(b'Hullo'), 'test1.txt')}): + my_form_ow = TestNoOverWriteForm(helpers.get_form_data()) + +- ok_(my_form_ow.validate()) ++ assert my_form_ow.validate() + my_form_ow.populate_obj(dummy) +- eq_(dummy.upload, 'test1.txt') +- ok_(op.exists(op.join(path, 'test1.txt'))) ++ assert dummy.upload == 'test1.txt' ++ assert op.exists(op.join(path, 'test1.txt')) + + with app.test_request_context(method='POST', data={'upload': (BytesIO(b'Hullo'), 'test1.txt')}): + my_form_ow = TestNoOverWriteForm(helpers.get_form_data()) + +- ok_(not my_form_ow.validate()) ++ assert not my_form_ow.validate() + + _remove_testfiles() + +@@ -139,8 +137,8 @@ def test_image_upload_field(): + pass + + my_form = TestForm() +- eq_(my_form.upload.base_path, path) +- eq_(my_form.upload.endpoint, 'static') ++ assert my_form.upload.base_path == path ++ assert my_form.upload.endpoint == 'static' + + _remove_testimages() + +@@ -153,54 +151,54 @@ def test_image_upload_field(): + with app.test_request_context(method='POST', data={'upload': (fp, 'test1.png')}): + my_form = TestForm(helpers.get_form_data()) + +- ok_(my_form.validate()) ++ assert my_form.validate() + + my_form.populate_obj(dummy) + +- eq_(dummy.upload, 'test1.png') +- ok_(op.exists(op.join(path, 'test1.png'))) +- ok_(op.exists(op.join(path, 'test1_thumb.png'))) ++ assert dummy.upload == 'test1.png' ++ assert op.exists(op.join(path, 'test1.png')) ++ assert op.exists(op.join(path, 'test1_thumb.png')) + + # Check replace + with open(filename, 'rb') as fp: + with app.test_request_context(method='POST', data={'upload': (fp, 'test2.png')}): + my_form = TestForm(helpers.get_form_data()) + +- ok_(my_form.validate()) ++ assert my_form.validate() + + my_form.populate_obj(dummy) + +- eq_(dummy.upload, 'test2.png') +- ok_(op.exists(op.join(path, 'test2.png'))) +- ok_(op.exists(op.join(path, 'test2_thumb.png'))) ++ assert dummy.upload == 'test2.png' ++ assert op.exists(op.join(path, 'test2.png')) ++ assert op.exists(op.join(path, 'test2_thumb.png')) + +- ok_(not op.exists(op.join(path, 'test1.png'))) +- ok_(not op.exists(op.join(path, 'test1_thumb.jpg'))) ++ assert not op.exists(op.join(path, 'test1.png')) ++ assert not op.exists(op.join(path, 'test1_thumb.jpg')) + + # Check delete + with app.test_request_context(method='POST', data={'_upload-delete': 'checked'}): + my_form = TestForm(helpers.get_form_data()) + +- ok_(my_form.validate()) ++ assert my_form.validate() + + my_form.populate_obj(dummy) +- eq_(dummy.upload, None) ++ assert dummy.upload is None + +- ok_(not op.exists(op.join(path, 'test2.png'))) +- ok_(not op.exists(op.join(path, 'test2_thumb.png'))) ++ assert not op.exists(op.join(path, 'test2.png')) ++ assert not op.exists(op.join(path, 'test2_thumb.png')) + + # Check upload no-resize + with open(filename, 'rb') as fp: + with app.test_request_context(method='POST', data={'upload': (fp, 'test1.png')}): + my_form = TestNoResizeForm(helpers.get_form_data()) + +- ok_(my_form.validate()) ++ assert my_form.validate() + + my_form.populate_obj(dummy) + +- eq_(dummy.upload, 'test1.png') +- ok_(op.exists(op.join(path, 'test1.png'))) +- ok_(not op.exists(op.join(path, 'test1_thumb.png'))) ++ assert dummy.upload == 'test1.png' ++ assert op.exists(op.join(path, 'test1.png')) ++ assert not op.exists(op.join(path, 'test1_thumb.png')) + + # Check upload, auto-resize + filename = op.join(op.dirname(__file__), 'data', 'copyleft.png') +@@ -209,12 +207,12 @@ def test_image_upload_field(): + with app.test_request_context(method='POST', data={'upload': (fp, 'test1.png')}): + my_form = TestAutoResizeForm(helpers.get_form_data()) + +- ok_(my_form.validate()) ++ assert my_form.validate() + + my_form.populate_obj(dummy) + +- eq_(dummy.upload, 'test1.png') +- ok_(op.exists(op.join(path, 'test1.png'))) ++ assert dummy.upload == 'test1.png' ++ assert op.exists(op.join(path, 'test1.png')) + + filename = op.join(op.dirname(__file__), 'data', 'copyleft.tiff') + +@@ -222,12 +220,12 @@ def test_image_upload_field(): + with app.test_request_context(method='POST', data={'upload': (fp, 'test1.tiff')}): + my_form = TestAutoResizeForm(helpers.get_form_data()) + +- ok_(my_form.validate()) ++ assert my_form.validate() + + my_form.populate_obj(dummy) + +- eq_(dummy.upload, 'test1.jpg') +- ok_(op.exists(op.join(path, 'test1.jpg'))) ++ assert dummy.upload == 'test1.jpg' ++ assert op.exists(op.join(path, 'test1.jpg')) + + # check allowed extensions + for extension in ('gif', 'jpg', 'jpeg', 'png', 'tiff'): +@@ -236,16 +234,16 @@ def test_image_upload_field(): + with open(filepath, 'rb') as fp: + with app.test_request_context(method='POST', data={'upload': (fp, filename)}): + my_form = TestNoResizeForm(helpers.get_form_data()) +- ok_(my_form.validate()) ++ assert my_form.validate() + my_form.populate_obj(dummy) +- eq_(dummy.upload, my_form.upload.data.filename) ++ assert dummy.upload == my_form.upload.data.filename + + # check case-sensitivity for extensions + filename = op.join(op.dirname(__file__), 'data', 'copyleft.jpg') + with open(filename, 'rb') as fp: + with app.test_request_context(method='POST', data={'upload': (fp, 'copyleft.JPG')}): + my_form = TestNoResizeForm(helpers.get_form_data()) +- ok_(my_form.validate()) ++ assert my_form.validate() + + + def test_relative_path(): +@@ -263,8 +261,8 @@ def test_relative_path(): + pass + + my_form = TestForm() +- eq_(my_form.upload.base_path, path) +- eq_(my_form.upload.relative_path, 'inner/') ++ assert my_form.upload.base_path == path ++ assert my_form.upload.relative_path == 'inner/' + + _remove_testfiles() + +@@ -274,11 +272,12 @@ def test_relative_path(): + with app.test_request_context(method='POST', data={'upload': (BytesIO(b'Hello World 1'), 'test1.txt')}): + my_form = TestForm(helpers.get_form_data()) + +- ok_(my_form.validate()) ++ assert my_form.validate() + + my_form.populate_obj(dummy) + +- eq_(dummy.upload, 'inner/test1.txt') +- ok_(op.exists(op.join(path, 'inner/test1.txt'))) ++ assert dummy.upload == 'inner/test1.txt' ++ assert op.exists(op.join(path, 'inner/test1.txt')) + +- eq_(url_for('static', filename=dummy.upload), '/static/inner/test1.txt') ++ assert url_for('static', filename=dummy.upload) \ ++ == '/static/inner/test1.txt' +--- a/flask_admin/tests/test_model.py ++++ b/flask_admin/tests/test_model.py +@@ -1,7 +1,5 @@ + import wtforms + +-from nose.tools import eq_, ok_ +- + from flask import Flask + + try: +@@ -139,62 +137,62 @@ def test_mockview(): + view = MockModelView(Model) + admin.add_view(view) + +- eq_(view.model, Model) ++ assert view.model == Model + +- eq_(view.name, 'Model') +- eq_(view.endpoint, 'model') ++ assert view.name == 'Model' ++ assert view.endpoint == 'model' + + # Verify scaffolding +- eq_(view._sortable_columns, ['col1', 'col2', 'col3']) +- eq_(view._create_form_class, Form) +- eq_(view._edit_form_class, Form) +- eq_(view._search_supported, False) +- eq_(view._filters, None) ++ assert view._sortable_columns == ['col1', 'col2', 'col3'] ++ assert view._create_form_class == Form ++ assert view._edit_form_class == Form ++ assert view._search_supported == False ++ assert view._filters == None + + client = app.test_client() + + # Make model view requests + rv = client.get('/admin/model/') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + + # Test model creation view + rv = client.get('/admin/model/new/') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + + rv = client.post('/admin/model/new/', + data=dict(col1='test1', col2='test2', col3='test3')) +- eq_(rv.status_code, 302) +- eq_(len(view.created_models), 1) ++ assert rv.status_code == 302 ++ assert len(view.created_models) == 1 + + model = view.created_models.pop() +- eq_(model.id, 3) +- eq_(model.col1, 'test1') +- eq_(model.col2, 'test2') +- eq_(model.col3, 'test3') ++ assert model.id == 3 ++ assert model.col1 == 'test1' ++ assert model.col2 == 'test2' ++ assert model.col3 == 'test3' + + # Try model edit view + rv = client.get('/admin/model/edit/?id=3') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('test1' in data) ++ assert 'test1' in data + + rv = client.post('/admin/model/edit/?id=3', + data=dict(col1='test!', col2='test@', col3='test#')) +- eq_(rv.status_code, 302) +- eq_(len(view.updated_models), 1) ++ assert rv.status_code == 302 ++ assert len(view.updated_models) == 1 + + model = view.updated_models.pop() +- eq_(model.col1, 'test!') +- eq_(model.col2, 'test@') +- eq_(model.col3, 'test#') ++ assert model.col1 == 'test!' ++ assert model.col2 == 'test@' ++ assert model.col3 == 'test#' + + rv = client.get('/admin/model/edit/?id=4') +- eq_(rv.status_code, 302) ++ assert rv.status_code == 302 + + # Attempt to delete model + rv = client.post('/admin/model/delete/?id=3') +- eq_(rv.status_code, 302) +- eq_(rv.headers['location'], 'http://localhost/admin/model/') ++ assert rv.status_code == 302 ++ assert rv.headers['location'] == 'http://localhost/admin/model/' + + # Create a dispatched application to test that edit view's "save and + # continue" functionality works when app is not located at root +@@ -206,10 +204,10 @@ def test_mockview(): + '/dispatched/admin/model/edit/?id=3', + data=dict(col1='another test!', col2='test@', col3='test#', _continue_editing='True')) + +- eq_(status, '302 FOUND') +- eq_(headers['Location'], 'http://localhost/dispatched/admin/model/edit/?id=3') ++ assert status == '302 FOUND' ++ assert headers['Location'] == 'http://localhost/dispatched/admin/model/edit/?id=3' + model = view.updated_models.pop() +- eq_(model.col1, 'another test!') ++ assert model.col1 == 'another test!' + + + def test_permissions(): +@@ -222,15 +220,15 @@ def test_permissions(): + + view.can_create = False + rv = client.get('/admin/model/new/') +- eq_(rv.status_code, 302) ++ assert rv.status_code == 302 + + view.can_edit = False + rv = client.get('/admin/model/edit/?id=1') +- eq_(rv.status_code, 302) ++ assert rv.status_code == 302 + + view.can_delete = False + rv = client.post('/admin/model/delete/?id=1') +- eq_(rv.status_code, 302) ++ assert rv.status_code == 302 + + + def test_templates(): +@@ -246,13 +244,13 @@ def test_templates(): + view.edit_template = 'mock.html' + + rv = client.get('/admin/model/') +- eq_(rv.data, b'Success!') ++ assert rv.data == b'Success!' + + rv = client.get('/admin/model/new/') +- eq_(rv.data, b'Success!') ++ assert rv.data == b'Success!' + + rv = client.get('/admin/model/edit/?id=1') +- eq_(rv.data, b'Success!') ++ assert rv.data == b'Success!' + + + def test_list_columns(): +@@ -263,15 +261,15 @@ def test_list_columns(): + column_labels=dict(col1='Column1')) + admin.add_view(view) + +- eq_(len(view._list_columns), 2) +- eq_(view._list_columns, [('col1', 'Column1'), ('col3', 'Col3')]) ++ assert len(view._list_columns) == 2 ++ assert view._list_columns == [('col1', 'Column1'), ('col3', 'Col3')] + + client = app.test_client() + + rv = client.get('/admin/model/') + data = rv.data.decode('utf-8') +- ok_('Column1' in data) +- ok_('Col2' not in data) ++ assert 'Column1' in data ++ assert 'Col2' not in data + + + def test_exclude_columns(): +@@ -280,14 +278,14 @@ def test_exclude_columns(): + view = MockModelView(Model, column_exclude_list=['col2']) + admin.add_view(view) + +- eq_(view._list_columns, [('col1', 'Col1'), ('col3', 'Col3')]) ++ assert view._list_columns == [('col1', 'Col1'), ('col3', 'Col3')] + + client = app.test_client() + + rv = client.get('/admin/model/') + data = rv.data.decode('utf-8') +- ok_('Col1' in data) +- ok_('Col2' not in data) ++ assert 'Col1' in data ++ assert 'Col2' not in data + + + def test_sortable_columns(): +@@ -296,7 +294,7 @@ def test_sortable_columns(): + view = MockModelView(Model, column_sortable_list=['col1', ('col2', 'test1')]) + admin.add_view(view) + +- eq_(view._sortable_columns, dict(col1='col1', col2='test1')) ++ assert view._sortable_columns == dict(col1='col1', col2='test1') + + + def test_column_searchable_list(): +@@ -305,7 +303,7 @@ def test_column_searchable_list(): + view = MockModelView(Model, column_searchable_list=['col1', 'col2']) + admin.add_view(view) + +- eq_(view._search_supported, True) ++ assert view._search_supported == True + + # TODO: Make calls with search + +@@ -316,12 +314,12 @@ def test_column_filters(): + view = MockModelView(Model, column_filters=['col1', 'col2']) + admin.add_view(view) + +- eq_(len(view._filters), 2) +- eq_(view._filters[0].name, 'col1') +- eq_(view._filters[1].name, 'col2') ++ assert len(view._filters) == 2 ++ assert view._filters[0].name == 'col1' ++ assert view._filters[1].name == 'col2' + +- eq_([(f['index'], f['operation']) for f in view._filter_groups[u'col1']], [(0, 'test')]) +- eq_([(f['index'], f['operation']) for f in view._filter_groups[u'col2']], [(1, 'test')]) ++ assert [(f['index'] == f['operation']) for f in view._filter_groups[u'col1']], [(0, 'test')] ++ assert [(f['index'] == f['operation']) for f in view._filter_groups[u'col2']], [(1, 'test')] + + # TODO: Make calls with filters + +@@ -335,8 +333,8 @@ def test_filter_list_callable(): + admin.add_view(view) + + opts = flt.get_options(view) +- eq_(len(opts), 2) +- eq_(opts, [('1', 'Test 1'), ('2', 'Test 2')]) ++ assert len(opts) == 2 ++ assert opts == [('1', 'Test 1'), ('2', 'Test 2')] + + + def test_form(): +@@ -371,68 +369,68 @@ def test_csrf(): + # create_view + ################ + rv = client.get('/admin/secure/new/') +- eq_(rv.status_code, 200) +- ok_(u'name="csrf_token"' in rv.data.decode('utf-8')) ++ assert rv.status_code == 200 ++ assert u'name="csrf_token"' in rv.data.decode('utf-8') + + csrf_token = get_csrf_token(rv.data.decode('utf-8')) + + # Create without CSRF token + rv = client.post('/admin/secure/new/', data=dict(name='test1')) +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + + # Create with CSRF token + rv = client.post('/admin/secure/new/', data=dict(name='test1', + csrf_token=csrf_token)) +- eq_(rv.status_code, 302) ++ assert rv.status_code == 302 + + ############### + # edit_view + ############### + rv = client.get('/admin/secure/edit/?url=%2Fadmin%2Fsecure%2F&id=1') +- eq_(rv.status_code, 200) +- ok_(u'name="csrf_token"' in rv.data.decode('utf-8')) ++ assert rv.status_code == 200 ++ assert u'name="csrf_token"' in rv.data.decode('utf-8') + + csrf_token = get_csrf_token(rv.data.decode('utf-8')) + + # Edit without CSRF token + rv = client.post('/admin/secure/edit/?url=%2Fadmin%2Fsecure%2F&id=1', + data=dict(name='test1')) +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + + # Edit with CSRF token + rv = client.post('/admin/secure/edit/?url=%2Fadmin%2Fsecure%2F&id=1', + data=dict(name='test1', csrf_token=csrf_token)) +- eq_(rv.status_code, 302) ++ assert rv.status_code == 302 + + ################ + # delete_view + ################ + rv = client.get('/admin/secure/') +- eq_(rv.status_code, 200) +- ok_(u'name="csrf_token"' in rv.data.decode('utf-8')) ++ assert rv.status_code == 200 ++ assert u'name="csrf_token"' in rv.data.decode('utf-8') + + csrf_token = get_csrf_token(rv.data.decode('utf-8')) + + # Delete without CSRF token, test validation errors + rv = client.post('/admin/secure/delete/', + data=dict(id="1", url="/admin/secure/"), follow_redirects=True) +- eq_(rv.status_code, 200) +- ok_(u'Record was successfully deleted.' not in rv.data.decode('utf-8')) +- ok_(u'Failed to delete record.' in rv.data.decode('utf-8')) ++ assert rv.status_code == 200 ++ assert u'Record was successfully deleted.' not in rv.data.decode('utf-8') ++ assert u'Failed to delete record.' in rv.data.decode('utf-8') + + # Delete with CSRF token + rv = client.post('/admin/secure/delete/', + data=dict(id="1", url="/admin/secure/", csrf_token=csrf_token), + follow_redirects=True) +- eq_(rv.status_code, 200) +- ok_(u'Record was successfully deleted.' in rv.data.decode('utf-8')) ++ assert rv.status_code == 200 ++ assert u'Record was successfully deleted.' in rv.data.decode('utf-8') + + ################ + # actions + ################ + rv = client.get('/admin/secure/') +- eq_(rv.status_code, 200) +- ok_(u'name="csrf_token"' in rv.data.decode('utf-8')) ++ assert rv.status_code == 200 ++ assert u'name="csrf_token"' in rv.data.decode('utf-8') + + csrf_token = get_csrf_token(rv.data.decode('utf-8')) + +@@ -440,9 +438,9 @@ def test_csrf(): + rv = client.post('/admin/secure/action/', + data=dict(rowid='1', url='/admin/secure/', action='delete'), + follow_redirects=True) +- eq_(rv.status_code, 200) +- ok_(u'Record was successfully deleted.' not in rv.data.decode('utf-8')) +- ok_(u'Failed to perform action.' in rv.data.decode('utf-8')) ++ assert rv.status_code == 200 ++ assert u'Record was successfully deleted.' not in rv.data.decode('utf-8') ++ assert u'Failed to perform action.' in rv.data.decode('utf-8') + + + def test_custom_form(): +@@ -454,10 +452,10 @@ def test_custom_form(): + view = MockModelView(Model, form=TestForm) + admin.add_view(view) + +- eq_(view._create_form_class, TestForm) +- eq_(view._edit_form_class, TestForm) ++ assert view._create_form_class == TestForm ++ assert view._edit_form_class == TestForm + +- ok_(not hasattr(view._create_form_class, 'col1')) ++ assert not hasattr(view._create_form_class, 'col1') + + + def test_modal_edit(): +@@ -482,27 +480,27 @@ def test_modal_edit(): + + # bootstrap 2 - ensure modal window is added when edit_modal is enabled + rv = client_bs2.get('/admin/edit_modal_on/') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('fa_modal_window' in data) ++ assert 'fa_modal_window' in data + + # bootstrap 2 - test edit modal disabled + rv = client_bs2.get('/admin/edit_modal_off/') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('fa_modal_window' not in data) ++ assert 'fa_modal_window' not in data + + # bootstrap 2 - ensure modal window is added when create_modal is enabled + rv = client_bs2.get('/admin/create_modal_on/') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('fa_modal_window' in data) ++ assert 'fa_modal_window' in data + + # bootstrap 2 - test create modal disabled + rv = client_bs2.get('/admin/create_modal_off/') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('fa_modal_window' not in data) ++ assert 'fa_modal_window' not in data + + # bootstrap 3 + app_bs3 = Flask(__name__) +@@ -517,27 +515,27 @@ def test_modal_edit(): + + # bootstrap 3 - ensure modal window is added when edit_modal is enabled + rv = client_bs3.get('/admin/edit_modal_on/') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('fa_modal_window' in data) ++ assert 'fa_modal_window' in data + + # bootstrap 3 - test modal disabled + rv = client_bs3.get('/admin/edit_modal_off/') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('fa_modal_window' not in data) ++ assert 'fa_modal_window' not in data + + # bootstrap 3 - ensure modal window is added when edit_modal is enabled + rv = client_bs3.get('/admin/create_modal_on/') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('fa_modal_window' in data) ++ assert 'fa_modal_window' in data + + # bootstrap 3 - test modal disabled + rv = client_bs3.get('/admin/create_modal_off/') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + data = rv.data.decode('utf-8') +- ok_('fa_modal_window' not in data) ++ assert 'fa_modal_window' not in data + + + def check_class_name(): +@@ -545,7 +543,7 @@ def check_class_name(): + pass + + view = DummyView(Model) +- eq_(view.name, 'Dummy View') ++ assert view.name == 'Dummy View' + + + def test_export_csv(): +@@ -557,7 +555,7 @@ def test_export_csv(): + admin.add_view(view) + + rv = client.get('/admin/test/export/csv/') +- eq_(rv.status_code, 302) ++ assert rv.status_code == 302 + + # basic test of csv export with a few records + view_data = { +@@ -572,12 +570,12 @@ def test_export_csv(): + + rv = client.get('/admin/model/export/csv/') + data = rv.data.decode('utf-8') +- eq_(rv.mimetype, 'text/csv') +- eq_(rv.status_code, 200) +- ok_("Col1,Col2\r\n" +- "col1_1,col2_1\r\n" +- "col1_2,col2_2\r\n" +- "col1_3,col2_3\r\n" == data) ++ assert rv.mimetype == 'text/csv' ++ assert rv.status_code == 200 ++ assert "Col1,Col2\r\n" + \ ++ "col1_1,col2_1\r\n" + \ ++ "col1_2,col2_2\r\n" + \ ++ "col1_3,col2_3\r\n" == data + + # test explicit use of column_export_list + view = MockModelView(Model, view_data, can_export=True, +@@ -588,12 +586,12 @@ def test_export_csv(): + + rv = client.get('/admin/exportinclusion/export/csv/') + data = rv.data.decode('utf-8') +- eq_(rv.mimetype, 'text/csv') +- eq_(rv.status_code, 200) +- ok_("Id,Col1,Col2\r\n" +- "1,col1_1,col2_1\r\n" +- "2,col1_2,col2_2\r\n" +- "3,col1_3,col2_3\r\n" == data) ++ assert rv.mimetype == 'text/csv' ++ assert rv.status_code == 200 ++ assert "Id,Col1,Col2\r\n" + \ ++ "1,col1_1,col2_1\r\n" + \ ++ "2,col1_2,col2_2\r\n" + \ ++ "3,col1_3,col2_3\r\n" == data + + # test explicit use of column_export_exclude_list + view = MockModelView(Model, view_data, can_export=True, +@@ -604,12 +602,12 @@ def test_export_csv(): + + rv = client.get('/admin/exportexclusion/export/csv/') + data = rv.data.decode('utf-8') +- eq_(rv.mimetype, 'text/csv') +- eq_(rv.status_code, 200) +- ok_("Col1\r\n" +- "col1_1\r\n" +- "col1_2\r\n" +- "col1_3\r\n" == data) ++ assert rv.mimetype == 'text/csv' ++ assert rv.status_code == 200 ++ assert "Col1\r\n" + \ ++ "col1_1\r\n" + \ ++ "col1_2\r\n" + \ ++ "col1_3\r\n" == data + + # test utf8 characters in csv export + view_data[4] = Model(1, u'\u2013ut8_1\u2013', u'\u2013utf8_2\u2013') +@@ -619,8 +617,8 @@ def test_export_csv(): + + rv = client.get('/admin/utf8/export/csv/') + data = rv.data.decode('utf-8') +- eq_(rv.status_code, 200) +- ok_(u'\u2013ut8_1\u2013,\u2013utf8_2\u2013\r\n' in data) ++ assert rv.status_code == 200 ++ assert u'\u2013ut8_1\u2013,\u2013utf8_2\u2013\r\n' in data + + # test None type, integer type, column_labels, and column_formatters + view_data = { +@@ -639,11 +637,11 @@ def test_export_csv(): + + rv = client.get('/admin/types_and_formatters/export/csv/') + data = rv.data.decode('utf-8') +- eq_(rv.status_code, 200) +- ok_("Str Field,Int Field\r\n" +- "col1_1,2\r\n" +- "col1_2,4\r\n" +- ",6\r\n" == data) ++ assert rv.status_code == 200 ++ assert "Str Field,Int Field\r\n" + \ ++ "col1_1,2\r\n" + \ ++ "col1_2,4\r\n" + \ ++ ",6\r\n" == data + + # test column_formatters_export and column_formatters_export + type_formatters = {type(None): lambda view, value: "null"} +@@ -659,11 +657,11 @@ def test_export_csv(): + + rv = client.get('/admin/export_types_and_formatters/export/csv/') + data = rv.data.decode('utf-8') +- eq_(rv.status_code, 200) +- ok_("Col1,Col2\r\n" +- "col1_1,3\r\n" +- "col1_2,6\r\n" +- "null,9\r\n" == data) ++ assert rv.status_code == 200 ++ assert "Col1,Col2\r\n" + \ ++ "col1_1,3\r\n" + \ ++ "col1_2,6\r\n" + \ ++ "null,9\r\n" == data + + # Macros are not implemented for csv export yet and will throw an error + view = MockModelView( +@@ -675,7 +673,7 @@ def test_export_csv(): + + rv = client.get('/admin/macro_exception/export/csv/') + data = rv.data.decode('utf-8') +- eq_(rv.status_code, 500) ++ assert rv.status_code == 500 + + # We should be able to specify column_formatters_export + # and not get an exception if a column_formatter is using a macro +@@ -692,11 +690,11 @@ def test_export_csv(): + + rv = client.get('/admin/macro_exception_formatter_override/export/csv/') + data = rv.data.decode('utf-8') +- eq_(rv.status_code, 200) +- ok_("Col1,Col2\r\n" +- "col1_1,1\r\n" +- "col1_2,2\r\n" +- ",3\r\n" == data) ++ assert rv.status_code == 200 ++ assert "Col1,Col2\r\n" + \ ++ "col1_1,1\r\n" + \ ++ "col1_2,2\r\n" + \ ++ ",3\r\n" == data + + # We should not get an exception if a column_formatter is + # using a macro but it is on the column_export_exclude_list +@@ -710,11 +708,11 @@ def test_export_csv(): + + rv = client.get('/admin/macro_exception_exclude_override/export/csv/') + data = rv.data.decode('utf-8') +- eq_(rv.status_code, 200) +- ok_("Col2\r\n" +- "1\r\n" +- "2\r\n" +- "3\r\n" == data) ++ assert rv.status_code == 200 ++ assert "Col2\r\n" + \ ++ "1\r\n" + \ ++ "2\r\n" + \ ++ "3\r\n" == data + + # When we use column_export_list to hide the macro field + # we should not get an exception +@@ -728,11 +726,11 @@ def test_export_csv(): + + rv = client.get('/admin/macro_exception_list_override/export/csv/') + data = rv.data.decode('utf-8') +- eq_(rv.status_code, 200) +- ok_("Col2\r\n" +- "1\r\n" +- "2\r\n" +- "3\r\n" == data) ++ assert rv.status_code == 200 ++ assert "Col2\r\n" + \ ++ "1\r\n" + \ ++ "2\r\n" + \ ++ "3\r\n" == data + + # If they define a macro on the column_formatters_export list + # then raise an exception +@@ -745,7 +743,7 @@ def test_export_csv(): + + rv = client.get('/admin/macro_exception_macro_override/export/csv/') + data = rv.data.decode('utf-8') +- eq_(rv.status_code, 500) ++ assert rv.status_code == 500 + + + def test_list_row_actions(): +@@ -759,22 +757,22 @@ def test_list_row_actions(): + admin.add_view(view) + + actions = view.get_list_row_actions() +- ok_(isinstance(actions[0], template.EditRowAction)) +- ok_(isinstance(actions[1], template.DeleteRowAction)) ++ assert isinstance(actions[0], template.EditRowAction) ++ assert isinstance(actions[1], template.DeleteRowAction) + + rv = client.get('/admin/test/') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + + # Test default actions + view = MockModelView(Model, endpoint='test1', can_edit=False, can_delete=False, can_view_details=True) + admin.add_view(view) + + actions = view.get_list_row_actions() +- eq_(len(actions), 1) +- ok_(isinstance(actions[0], template.ViewRowAction)) ++ assert len(actions) == 1 ++ assert isinstance(actions[0], template.ViewRowAction) + + rv = client.get('/admin/test1/') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + + # Test popups + view = MockModelView(Model, endpoint='test2', +@@ -784,12 +782,12 @@ def test_list_row_actions(): + admin.add_view(view) + + actions = view.get_list_row_actions() +- ok_(isinstance(actions[0], template.ViewPopupRowAction)) +- ok_(isinstance(actions[1], template.EditPopupRowAction)) +- ok_(isinstance(actions[2], template.DeleteRowAction)) ++ assert isinstance(actions[0], template.ViewPopupRowAction) ++ assert isinstance(actions[1], template.EditPopupRowAction) ++ assert isinstance(actions[2], template.DeleteRowAction) + + rv = client.get('/admin/test2/') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + + # Test custom views + view = MockModelView(Model, endpoint='test3', +@@ -800,16 +798,16 @@ def test_list_row_actions(): + admin.add_view(view) + + actions = view.get_list_row_actions() +- ok_(isinstance(actions[0], template.EditRowAction)) +- ok_(isinstance(actions[1], template.DeleteRowAction)) +- ok_(isinstance(actions[2], template.LinkRowAction)) +- ok_(isinstance(actions[3], template.EndpointLinkRowAction)) ++ assert isinstance(actions[0], template.EditRowAction) ++ assert isinstance(actions[1], template.DeleteRowAction) ++ assert isinstance(actions[2], template.LinkRowAction) ++ assert isinstance(actions[3], template.EndpointLinkRowAction) + + rv = client.get('/admin/test3/') +- eq_(rv.status_code, 200) ++ assert rv.status_code == 200 + + data = rv.data.decode('utf-8') + +- ok_('glyphicon-off' in data) +- ok_('http://localhost/?id=' in data) +- ok_('glyphicon-test' in data) ++ assert 'glyphicon-off' in data ++ assert 'http://localhost/?id=' in data ++ assert 'glyphicon-test' in data +--- a/flask_admin/tests/test_tools.py ++++ b/flask_admin/tests/test_tools.py +@@ -1,19 +1,17 @@ +-from nose.tools import eq_, ok_ +- + from flask_admin import tools + + + def test_encode_decode(): +- eq_(tools.iterdecode(tools.iterencode([1, 2, 3])), (u'1', u'2', u'3')) ++ assert tools.iterdecode(tools.iterencode([1, 2, 3])) == (u'1', u'2', u'3') + +- eq_(tools.iterdecode(tools.iterencode([',', ',', ','])), (u',', u',', u',')) ++ assert tools.iterdecode(tools.iterencode([',', ',', ','])) == (u',', u',', u',') + +- eq_(tools.iterdecode(tools.iterencode(['.hello.,', ',', ','])), (u'.hello.,', u',', u',')) ++ assert tools.iterdecode(tools.iterencode(['.hello.,', ',', ','])) == (u'.hello.,', u',', u',') + +- eq_(tools.iterdecode(tools.iterencode(['.....,,,.,,..,.,,.,'])), (u'.....,,,.,,..,.,,.,',)) ++ assert tools.iterdecode(tools.iterencode(['.....,,,.,,..,.,,.,'])) == (u'.....,,,.,,..,.,,.,',) + +- eq_(tools.iterdecode(tools.iterencode([])), tuple()) ++ assert tools.iterdecode(tools.iterencode([])) == tuple() + + # Malformed inputs should not crash +- ok_(tools.iterdecode('.')) +- eq_(tools.iterdecode(','), (u'', u'')) ++ assert tools.iterdecode('.') ++ assert tools.iterdecode(',') == (u'', u'') +--- a/setup.cfg ++++ b/setup.cfg +@@ -1,10 +1,3 @@ +-[nosetests] +-detailed-errors = 1 +-with-coverage = 0 +-cover-package = flask_admin +-debug = nose.loader +-exclude = contrib +- + [egg_info] + tag_build = + tag_date = 0 +--- a/README.rst ++++ b/README.rst +@@ -88,12 +88,12 @@ Or alternatively, you can download the r + + Tests + ----- +-Test are run with *nose*. If you are not familiar with this package you can get some more info from `their website `_. ++Test are run with *pytest*. If you are not familiar with this package you can get some more info from `their website `_. + + To run the tests, from the project directory, simply:: + + pip install -r requirements-dev.txt +- nosetests ++ pytest + + You should see output similar to:: +