feat: add basic progress page/indicator support
so far "delete results" (for Raw Settings) is the only use case. user cancel is not yet supported
This commit is contained in:
parent
6fa8b0aeaa
commit
1a8900c9f4
13 changed files with 746 additions and 40 deletions
62
tests/test_progress.py
Normal file
62
tests/test_progress.py
Normal file
|
@ -0,0 +1,62 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
from pyramid import testing
|
||||
from beaker.session import Session as BeakerSession
|
||||
|
||||
from wuttaweb import progress as mod
|
||||
|
||||
|
||||
class TestGetBasicSession(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.request = testing.DummyRequest()
|
||||
|
||||
def test_basic(self):
|
||||
session = mod.get_basic_session(self.request)
|
||||
self.assertIsInstance(session, BeakerSession)
|
||||
self.assertFalse(session.use_cookies)
|
||||
|
||||
|
||||
class TestGetProgressSession(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.request = testing.DummyRequest()
|
||||
|
||||
def test_basic(self):
|
||||
self.request.session.id = 'mockid'
|
||||
session = mod.get_progress_session(self.request, 'foo')
|
||||
self.assertIsInstance(session, BeakerSession)
|
||||
self.assertEqual(session.id, 'mockid.progress.foo')
|
||||
|
||||
|
||||
class TestSessionProgress(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.request = testing.DummyRequest()
|
||||
self.request.session.id = 'mockid'
|
||||
|
||||
def test_error_url(self):
|
||||
factory = mod.SessionProgress(self.request, 'foo', success_url='/blart')
|
||||
self.assertEqual(factory.error_url, '/blart')
|
||||
|
||||
def test_basic(self):
|
||||
|
||||
# sanity / coverage check
|
||||
factory = mod.SessionProgress(self.request, 'foo')
|
||||
prog = factory("doing things", 2)
|
||||
prog.update(1)
|
||||
prog.update(2)
|
||||
prog.handle_success()
|
||||
|
||||
def test_error(self):
|
||||
|
||||
# sanity / coverage check
|
||||
factory = mod.SessionProgress(self.request, 'foo')
|
||||
prog = factory("doing things", 2)
|
||||
prog.update(1)
|
||||
try:
|
||||
raise RuntimeError('omg')
|
||||
except Exception as error:
|
||||
prog.handle_error(error)
|
|
@ -14,6 +14,7 @@ from pyramid.httpexceptions import HTTPNotFound
|
|||
from wuttjamaican.conf import WuttaConfig
|
||||
from wuttaweb.views import master as mod
|
||||
from wuttaweb.views import View
|
||||
from wuttaweb.progress import SessionProgress
|
||||
from wuttaweb.subscribers import new_request_set_user
|
||||
from tests.util import WebTestCase
|
||||
|
||||
|
@ -428,6 +429,22 @@ class TestMasterView(WebTestCase):
|
|||
self.assertIn('click me', html)
|
||||
self.assertIn('is-primary', html)
|
||||
|
||||
def test_make_progress(self):
|
||||
|
||||
# basic
|
||||
view = self.make_view()
|
||||
self.request.session.id = 'mockid'
|
||||
progress = view.make_progress('foo')
|
||||
self.assertIsInstance(progress, SessionProgress)
|
||||
|
||||
def test_render_progress(self):
|
||||
self.pyramid_config.add_route('progress', '/progress/{key}')
|
||||
|
||||
# sanity / coverage check
|
||||
view = self.make_view()
|
||||
progress = MagicMock()
|
||||
response = view.render_progress(progress)
|
||||
|
||||
def test_render_to_response(self):
|
||||
self.pyramid_config.include('wuttaweb.views.common')
|
||||
self.pyramid_config.include('wuttaweb.views.auth')
|
||||
|
@ -1098,6 +1115,7 @@ class TestMasterView(WebTestCase):
|
|||
|
||||
def test_delete_bulk(self):
|
||||
self.pyramid_config.add_route('settings', '/settings/')
|
||||
self.pyramid_config.add_route('progress', '/progress/{key}')
|
||||
model = self.app.model
|
||||
sample_data = [
|
||||
{'name': 'foo1', 'value': 'ONE'},
|
||||
|
@ -1132,13 +1150,35 @@ class TestMasterView(WebTestCase):
|
|||
data = grid.get_visible_data()
|
||||
self.assertEqual(len(data), 2)
|
||||
|
||||
# okay now let's delete those (gets redirected)
|
||||
with patch.object(view, 'make_model_grid', return_value=grid):
|
||||
response = view.delete_bulk(session=self.session)
|
||||
# okay now let's delete those via quick method
|
||||
# (user should be redirected back to index)
|
||||
with patch.multiple(view,
|
||||
deletable_bulk_quick=True,
|
||||
make_model_grid=MagicMock(return_value=grid)):
|
||||
response = view.delete_bulk()
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertEqual(self.session.query(model.Setting).count(), 7)
|
||||
|
||||
def test_delete_bulk_data(self):
|
||||
# now use another filter since those records are gone
|
||||
self.request.GET = {'name': 'foo2', 'name.verb': 'equal'}
|
||||
grid = view.make_model_grid(session=self.session)
|
||||
self.assertEqual(len(grid.filters), 2)
|
||||
self.assertEqual(len(grid.active_filters), 1)
|
||||
data = grid.get_visible_data()
|
||||
self.assertEqual(len(data), 1)
|
||||
|
||||
# this time we delete "slowly" with progress
|
||||
self.request.session.id = 'ignorethis'
|
||||
with patch.multiple(view,
|
||||
deletable_bulk_quick=False,
|
||||
make_model_grid=MagicMock(return_value=grid)):
|
||||
with patch.object(mod, 'threading') as threading:
|
||||
response = view.delete_bulk()
|
||||
threading.Thread.return_value.start.assert_called_once_with()
|
||||
# nb. user is shown progress page
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_delete_bulk_action(self):
|
||||
self.pyramid_config.add_route('settings', '/settings/')
|
||||
model = self.app.model
|
||||
sample_data = [
|
||||
|
@ -1167,10 +1207,68 @@ class TestMasterView(WebTestCase):
|
|||
.filter(model.Setting.value.ilike('%s%'))\
|
||||
.all()
|
||||
self.assertEqual(len(settings), 2)
|
||||
view.delete_bulk_data(settings, session=self.session)
|
||||
view.delete_bulk_action(settings)
|
||||
self.session.commit()
|
||||
self.assertEqual(self.session.query(model.Setting).count(), 7)
|
||||
|
||||
def test_delete_bulk_thread(self):
|
||||
self.pyramid_config.add_route('settings', '/settings/')
|
||||
model = self.app.model
|
||||
sample_data = [
|
||||
{'name': 'foo1', 'value': 'ONE'},
|
||||
{'name': 'foo2', 'value': 'two'},
|
||||
{'name': 'foo3', 'value': 'three'},
|
||||
{'name': 'foo4', 'value': 'four'},
|
||||
{'name': 'foo5', 'value': 'five'},
|
||||
{'name': 'foo6', 'value': 'six'},
|
||||
{'name': 'foo7', 'value': 'seven'},
|
||||
{'name': 'foo8', 'value': 'eight'},
|
||||
{'name': 'foo9', 'value': 'nine'},
|
||||
]
|
||||
for setting in sample_data:
|
||||
self.app.save_setting(self.session, setting['name'], setting['value'])
|
||||
self.session.commit()
|
||||
sample_query = self.session.query(model.Setting)
|
||||
|
||||
with patch.multiple(mod.MasterView, create=True,
|
||||
model_class=model.Setting):
|
||||
view = self.make_view()
|
||||
|
||||
# basic delete, no progress
|
||||
self.assertEqual(self.session.query(model.Setting).count(), 9)
|
||||
settings = self.session.query(model.Setting)\
|
||||
.filter(model.Setting.value.ilike('%s%'))
|
||||
self.assertEqual(settings.count(), 2)
|
||||
with patch.object(self.app, 'make_session', return_value=self.session):
|
||||
view.delete_bulk_thread(settings)
|
||||
self.assertEqual(self.session.query(model.Setting).count(), 7)
|
||||
|
||||
# basic delete, with progress
|
||||
settings = self.session.query(model.Setting)\
|
||||
.filter(model.Setting.name == 'foo1')
|
||||
self.assertEqual(settings.count(), 1)
|
||||
with patch.object(self.app, 'make_session', return_value=self.session):
|
||||
view.delete_bulk_thread(settings, progress=MagicMock())
|
||||
self.assertEqual(self.session.query(model.Setting).count(), 6)
|
||||
|
||||
# error, no progress
|
||||
settings = self.session.query(model.Setting)\
|
||||
.filter(model.Setting.name == 'foo2')
|
||||
self.assertEqual(settings.count(), 1)
|
||||
with patch.object(self.app, 'make_session', return_value=self.session):
|
||||
with patch.object(view, 'delete_bulk_action', side_effect=RuntimeError):
|
||||
view.delete_bulk_thread(settings)
|
||||
# nb. nothing was deleted
|
||||
self.assertEqual(self.session.query(model.Setting).count(), 6)
|
||||
|
||||
# error, with progress
|
||||
self.assertEqual(settings.count(), 1)
|
||||
with patch.object(self.app, 'make_session', return_value=self.session):
|
||||
with patch.object(view, 'delete_bulk_action', side_effect=RuntimeError):
|
||||
view.delete_bulk_thread(settings, progress=MagicMock())
|
||||
# nb. nothing was deleted
|
||||
self.assertEqual(self.session.query(model.Setting).count(), 6)
|
||||
|
||||
def test_autocomplete(self):
|
||||
model = self.app.model
|
||||
|
||||
|
|
62
tests/views/test_progress.py
Normal file
62
tests/views/test_progress.py
Normal file
|
@ -0,0 +1,62 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
|
||||
from pyramid import testing
|
||||
|
||||
from wuttaweb.views import progress as mod
|
||||
from wuttaweb.progress import get_progress_session
|
||||
from tests.util import WebTestCase
|
||||
|
||||
|
||||
class TestProgressView(WebTestCase):
|
||||
|
||||
def test_includeme(self):
|
||||
self.pyramid_config.include('wuttaweb.views.progress')
|
||||
|
||||
def test_basic(self):
|
||||
self.request.session.id = 'mockid'
|
||||
self.request.matchdict = {'key': 'foo'}
|
||||
|
||||
# first call with no setup, will create the progress session
|
||||
# but it should be "empty" - except not really since beaker
|
||||
# adds some keys by default
|
||||
context = mod.progress(self.request)
|
||||
self.assertIsInstance(context, dict)
|
||||
|
||||
# now let's establish a progress session of our own
|
||||
progsess = get_progress_session(self.request, 'bar')
|
||||
progsess['maximum'] = 2
|
||||
progsess['value'] = 1
|
||||
progsess.save()
|
||||
|
||||
# then call view, check results
|
||||
self.request.matchdict = {'key': 'bar'}
|
||||
context = mod.progress(self.request)
|
||||
self.assertEqual(context['maximum'], 2)
|
||||
self.assertEqual(context['value'], 1)
|
||||
self.assertNotIn('complete', context)
|
||||
|
||||
# now mark it as complete, check results
|
||||
progsess['complete'] = True
|
||||
progsess['success_msg'] = "yay!"
|
||||
progsess.save()
|
||||
context = mod.progress(self.request)
|
||||
self.assertTrue(context['complete'])
|
||||
self.assertEqual(context['success_msg'], "yay!")
|
||||
|
||||
# now do that all again, with error
|
||||
progsess = get_progress_session(self.request, 'baz')
|
||||
progsess['maximum'] = 2
|
||||
progsess['value'] = 1
|
||||
progsess.save()
|
||||
self.request.matchdict = {'key': 'baz'}
|
||||
context = mod.progress(self.request)
|
||||
self.assertEqual(context['maximum'], 2)
|
||||
self.assertEqual(context['value'], 1)
|
||||
self.assertNotIn('complete', context)
|
||||
self.assertNotIn('error', context)
|
||||
progsess['error'] = True
|
||||
progsess['error_msg'] = "omg!"
|
||||
progsess.save()
|
||||
context = mod.progress(self.request)
|
||||
self.assertTrue(context['error'])
|
||||
self.assertEqual(context['error_msg'], "omg!")
|
Loading…
Add table
Add a link
Reference in a new issue