console_conf/chooser: UI tweaks
- drop the ABORT button - introduce BACK button, goes back to the correct previous screen - sort actions in the order of run -> recover -> install -> rest - capitalize action title strings - simplify current model actions screen - user hints in the confirm screen for well known modes - generic hint for unknown modes Signed-off-by: Maciej Borzecki <maciej.zenon.borzecki@canonical.com>
This commit is contained in:
parent
d3ac0d6f36
commit
a957c5b889
|
@ -26,6 +26,7 @@ log = logging.getLogger("console_conf.controllers.chooser")
|
||||||
|
|
||||||
|
|
||||||
class RecoveryChooserBaseController(BaseController):
|
class RecoveryChooserBaseController(BaseController):
|
||||||
|
|
||||||
def __init__(self, app):
|
def __init__(self, app):
|
||||||
super().__init__(app)
|
super().__init__(app)
|
||||||
self.model = app.base_model
|
self.model = app.base_model
|
||||||
|
@ -34,25 +35,57 @@ class RecoveryChooserBaseController(BaseController):
|
||||||
# exit without taking any action
|
# exit without taking any action
|
||||||
self.app.exit()
|
self.app.exit()
|
||||||
|
|
||||||
|
def back(self):
|
||||||
|
self.app.prev_sceen()
|
||||||
|
|
||||||
|
|
||||||
class RecoveryChooserController(RecoveryChooserBaseController):
|
class RecoveryChooserController(RecoveryChooserBaseController):
|
||||||
|
|
||||||
|
def __init__(self, app):
|
||||||
|
super().__init__(app)
|
||||||
|
self._model_view, self._all_view = self._make_views()
|
||||||
|
# one of the current views
|
||||||
|
self._current_view = None
|
||||||
|
|
||||||
def start_ui(self):
|
def start_ui(self):
|
||||||
|
# current view is preserved, so going back comes back to the right
|
||||||
|
# screen
|
||||||
|
if self._current_view is None:
|
||||||
|
# right off the start, default to all-systems view
|
||||||
|
self._current_view = self._all_view
|
||||||
|
# unless we can show the current model
|
||||||
|
if self._model_view is not None:
|
||||||
|
self._current_view = self._model_view
|
||||||
|
|
||||||
|
self.ui.set_body(self._current_view)
|
||||||
|
|
||||||
|
def _make_views(self):
|
||||||
|
current_view = None
|
||||||
if self.model.current and self.model.current.actions:
|
if self.model.current and self.model.current.actions:
|
||||||
# only when we have a current system and it has actions available
|
# only when we have a current system and it has actions available
|
||||||
|
|
||||||
more = len(self.model.systems) > 1
|
more = len(self.model.systems) > 1
|
||||||
view = ChooserCurrentSystemView(self, self.model.current,
|
current_view = ChooserCurrentSystemView(self,
|
||||||
|
self.model.current,
|
||||||
has_more=more)
|
has_more=more)
|
||||||
else:
|
|
||||||
view = ChooserView(self, self.model.systems)
|
all_view = ChooserView(self, self.model.systems)
|
||||||
self.ui.set_body(view)
|
return current_view, all_view
|
||||||
|
|
||||||
def select(self, system, action):
|
def select(self, system, action):
|
||||||
self.model.select(system, action)
|
self.model.select(system, action)
|
||||||
self.app.next_screen()
|
self.app.next_screen()
|
||||||
|
|
||||||
def more_options(self):
|
def more_options(self):
|
||||||
self.ui.set_body(ChooserView(self, self.model.systems))
|
self._current_view = self._all_view
|
||||||
|
self.ui.set_body(self._all_view)
|
||||||
|
|
||||||
|
def back(self):
|
||||||
|
if self._current_view == self._all_view and \
|
||||||
|
self._model_view is not None:
|
||||||
|
# back in the all-systems screen goes back to the current model
|
||||||
|
# screen, provided we have one
|
||||||
|
self._current_view = self._model_view
|
||||||
|
self.ui.set_body(self._current_view)
|
||||||
|
|
||||||
|
|
||||||
class RecoveryChooserConfirmController(RecoveryChooserBaseController):
|
class RecoveryChooserConfirmController(RecoveryChooserBaseController):
|
||||||
|
@ -65,3 +98,7 @@ class RecoveryChooserConfirmController(RecoveryChooserBaseController):
|
||||||
# output the choice
|
# output the choice
|
||||||
self.app.respond(self.model.selection)
|
self.app.respond(self.model.selection)
|
||||||
self.app.exit()
|
self.app.exit()
|
||||||
|
|
||||||
|
def back(self):
|
||||||
|
self.model.unselect()
|
||||||
|
self.app.prev_screen()
|
||||||
|
|
|
@ -34,14 +34,18 @@ class MockedApplication:
|
||||||
opts = None
|
opts = None
|
||||||
|
|
||||||
|
|
||||||
def make_app():
|
def make_app(model=None):
|
||||||
app = MockedApplication()
|
app = MockedApplication()
|
||||||
app.ui = mock.Mock()
|
app.ui = mock.Mock()
|
||||||
|
if model is not None:
|
||||||
|
app.base_model = model
|
||||||
|
else:
|
||||||
app.base_model = mock.Mock()
|
app.base_model = mock.Mock()
|
||||||
app.context = Context.new(app)
|
app.context = Context.new(app)
|
||||||
app.exit = mock.Mock()
|
app.exit = mock.Mock()
|
||||||
app.respond = mock.Mock()
|
app.respond = mock.Mock()
|
||||||
app.next_screen = mock.Mock()
|
app.next_screen = mock.Mock()
|
||||||
|
app.prev_screen = mock.Mock()
|
||||||
return app
|
return app
|
||||||
|
|
||||||
|
|
||||||
|
@ -92,12 +96,15 @@ def make_model():
|
||||||
|
|
||||||
class TestChooserConfirmController(unittest.TestCase):
|
class TestChooserConfirmController(unittest.TestCase):
|
||||||
|
|
||||||
def test_abort(self):
|
def test_back(self):
|
||||||
app = make_app()
|
app = make_app()
|
||||||
c = RecoveryChooserConfirmController(app)
|
c = RecoveryChooserConfirmController(app)
|
||||||
c.cancel()
|
c.model = mock.Mock(selection='selection')
|
||||||
|
c.back()
|
||||||
app.respond.assert_not_called()
|
app.respond.assert_not_called()
|
||||||
app.exit.assert_called()
|
app.exit.assert_not_called()
|
||||||
|
app.prev_screen.assert_called()
|
||||||
|
c.model.unselect.assert_called()
|
||||||
|
|
||||||
def test_confirm(self):
|
def test_confirm(self):
|
||||||
app = make_app()
|
app = make_app()
|
||||||
|
@ -119,17 +126,9 @@ class TestChooserConfirmController(unittest.TestCase):
|
||||||
|
|
||||||
class TestChooserController(unittest.TestCase):
|
class TestChooserController(unittest.TestCase):
|
||||||
|
|
||||||
def test_abort(self):
|
|
||||||
app = make_app()
|
|
||||||
c = RecoveryChooserController(app)
|
|
||||||
c.cancel()
|
|
||||||
app.respond.assert_not_called()
|
|
||||||
app.exit.assert_called()
|
|
||||||
|
|
||||||
def test_select(self):
|
def test_select(self):
|
||||||
app = make_app()
|
app = make_app(model=make_model())
|
||||||
c = RecoveryChooserController(app)
|
c = RecoveryChooserController(app)
|
||||||
c.model = make_model()
|
|
||||||
|
|
||||||
c.select(c.model.systems[0], c.model.systems[0].actions[0])
|
c.select(c.model.systems[0], c.model.systems[0].actions[0])
|
||||||
exp = SelectedSystemAction(system=c.model.systems[0],
|
exp = SelectedSystemAction(system=c.model.systems[0],
|
||||||
|
@ -140,50 +139,84 @@ class TestChooserController(unittest.TestCase):
|
||||||
app.respond.assert_not_called()
|
app.respond.assert_not_called()
|
||||||
app.exit.assert_not_called()
|
app.exit.assert_not_called()
|
||||||
|
|
||||||
@mock.patch('console_conf.controllers.chooser.ChooserCurrentSystemView')
|
@mock.patch('console_conf.controllers.chooser.ChooserCurrentSystemView',
|
||||||
@mock.patch('console_conf.controllers.chooser.ChooserView')
|
return_value='current')
|
||||||
|
@mock.patch('console_conf.controllers.chooser.ChooserView',
|
||||||
|
return_value='all')
|
||||||
def test_current_ui_first(self, cv, ccsv):
|
def test_current_ui_first(self, cv, ccsv):
|
||||||
app = make_app()
|
app = make_app(model=make_model())
|
||||||
c = RecoveryChooserController(app)
|
c = RecoveryChooserController(app)
|
||||||
c.model = make_model()
|
c.ui.start_ui = mock.Mock()
|
||||||
|
|
||||||
c.start_ui()
|
|
||||||
# current system view is constructed
|
# current system view is constructed
|
||||||
ccsv.assert_called_with(c, c.model.current, has_more=True)
|
ccsv.assert_called_with(c, c.model.current, has_more=True)
|
||||||
# but the all systems view is not
|
# as well as all systems view
|
||||||
cv.assert_not_called()
|
cv.assert_called_with(c, c.model.systems)
|
||||||
ccsv.reset_mock()
|
c.start_ui()
|
||||||
|
c.ui.set_body.assert_called_with('current')
|
||||||
# user selects more options and the view is replaced
|
# user selects more options and the view is replaced
|
||||||
c.more_options()
|
c.more_options()
|
||||||
# we get the all systems view now
|
c.ui.set_body.assert_called_with('all')
|
||||||
cv.assert_called_with(c, c.model.systems)
|
|
||||||
# and the current system view was not constructed
|
|
||||||
ccsv.assert_not_called()
|
|
||||||
|
|
||||||
@mock.patch('console_conf.controllers.chooser.ChooserCurrentSystemView')
|
@mock.patch('console_conf.controllers.chooser.ChooserCurrentSystemView',
|
||||||
@mock.patch('console_conf.controllers.chooser.ChooserView')
|
return_value='current')
|
||||||
def test_only_one_and_current(self, cv, ccsv):
|
@mock.patch('console_conf.controllers.chooser.ChooserView',
|
||||||
app = make_app()
|
return_value='all')
|
||||||
|
def test_current_current_all_there_and_back(self, cv, ccsv):
|
||||||
|
app = make_app(model=make_model())
|
||||||
c = RecoveryChooserController(app)
|
c = RecoveryChooserController(app)
|
||||||
c.model = RecoverySystemsModel.from_systems([model2_current])
|
c.ui.start_ui = mock.Mock()
|
||||||
|
# sanity
|
||||||
|
ccsv.assert_called_with(c, c.model.current, has_more=True)
|
||||||
|
cv.assert_called_with(c, c.model.systems)
|
||||||
|
|
||||||
c.start_ui()
|
c.start_ui()
|
||||||
# current system view is constructed
|
c.ui.set_body.assert_called_with('current')
|
||||||
ccsv.assert_called_with(c, c.model.current, has_more=False)
|
# user selects more options and the view is replaced
|
||||||
# but the all systems view is not
|
c.more_options()
|
||||||
cv.assert_not_called()
|
c.ui.set_body.assert_called_with('all')
|
||||||
|
# go back now
|
||||||
|
c.back()
|
||||||
|
c.ui.set_body.assert_called_with('current')
|
||||||
|
# nothing
|
||||||
|
c.back()
|
||||||
|
c.ui.set_body.not_called()
|
||||||
|
|
||||||
@mock.patch('console_conf.controllers.chooser.ChooserCurrentSystemView')
|
@mock.patch('console_conf.controllers.chooser.ChooserCurrentSystemView',
|
||||||
@mock.patch('console_conf.controllers.chooser.ChooserView')
|
return_value='current')
|
||||||
def test_all_systems_first_no_current(self, cv, ccsv):
|
@mock.patch('console_conf.controllers.chooser.ChooserView',
|
||||||
app = make_app()
|
return_value='all')
|
||||||
|
def test_only_one_and_current(self, cv, ccsv):
|
||||||
|
model = RecoverySystemsModel.from_systems([model2_current])
|
||||||
|
app = make_app(model=model)
|
||||||
c = RecoveryChooserController(app)
|
c = RecoveryChooserController(app)
|
||||||
c.model = RecoverySystemsModel.from_systems([model1_non_current])
|
c.ui.start_ui = mock.Mock()
|
||||||
|
# both views are constructed
|
||||||
|
ccsv.assert_called_with(c, c.model.current, has_more=False)
|
||||||
|
cv.assert_called_with(c, c.model.systems)
|
||||||
|
|
||||||
|
c.start_ui()
|
||||||
|
c.ui.set_body.assert_called_with('current')
|
||||||
|
# going back does nothing
|
||||||
|
c.back()
|
||||||
|
c.ui.set_body.not_called()
|
||||||
|
|
||||||
|
@mock.patch('console_conf.controllers.chooser.ChooserCurrentSystemView',
|
||||||
|
return_value='current')
|
||||||
|
@mock.patch('console_conf.controllers.chooser.ChooserView',
|
||||||
|
return_value='all')
|
||||||
|
def test_all_systems_first_no_current(self, cv, ccsv):
|
||||||
|
model = RecoverySystemsModel.from_systems([model1_non_current])
|
||||||
|
app = make_app(model=model)
|
||||||
|
c = RecoveryChooserController(app)
|
||||||
|
c.ui.start_ui = mock.Mock()
|
||||||
|
|
||||||
# sanity
|
# sanity
|
||||||
self.assertIsNone(c.model.current)
|
self.assertIsNone(c.model.current)
|
||||||
|
|
||||||
c.start_ui()
|
# we get the all-systems view now
|
||||||
# we get the all systems view now
|
|
||||||
cv.assert_called()
|
cv.assert_called()
|
||||||
# current system view is not constructed
|
# current system view is not constructed at all
|
||||||
ccsv.assert_not_called()
|
ccsv.assert_not_called()
|
||||||
|
|
||||||
|
c.start_ui()
|
||||||
|
c.ui.set_body.assert_called_with('all')
|
||||||
|
|
|
@ -26,8 +26,8 @@ from urwid import (
|
||||||
)
|
)
|
||||||
from subiquitycore.ui.buttons import (
|
from subiquitycore.ui.buttons import (
|
||||||
danger_btn,
|
danger_btn,
|
||||||
reset_btn,
|
|
||||||
forward_btn,
|
forward_btn,
|
||||||
|
back_btn,
|
||||||
)
|
)
|
||||||
from subiquitycore.ui.actionmenu import (
|
from subiquitycore.ui.actionmenu import (
|
||||||
Action,
|
Action,
|
||||||
|
@ -47,23 +47,36 @@ from subiquitycore.view import BaseView
|
||||||
log = logging.getLogger("console_conf.views.chooser")
|
log = logging.getLogger("console_conf.views.chooser")
|
||||||
|
|
||||||
|
|
||||||
class ChooserCurrentSystemView(BaseView):
|
class ChooserBaseView(BaseView):
|
||||||
title = "Ubuntu Core"
|
title = "Ubuntu Core"
|
||||||
|
|
||||||
def __init__(self, controller, current, has_more=False):
|
def __init__(self, current, scr):
|
||||||
fmt = "Select one of available actions for \"{}\" by \"{}\"{}."
|
if current is not None:
|
||||||
maybe_more = " or view all available systems" if has_more else ""
|
self.title = current.model.display_name
|
||||||
excerpt = fmt.format(current.model.display_name,
|
|
||||||
current.brand.display_name,
|
|
||||||
maybe_more)
|
|
||||||
|
|
||||||
|
super().__init__(scr)
|
||||||
|
|
||||||
|
|
||||||
|
def by_preferred_action_type(action):
|
||||||
|
"""Order action entries by having the 'run' mode first, then 'recover', then
|
||||||
|
'install', the rest is ordered alphabetically."""
|
||||||
|
return (action.mode != "run",
|
||||||
|
action.mode != "recover",
|
||||||
|
action.mode != "install",
|
||||||
|
action.title.lower())
|
||||||
|
|
||||||
|
|
||||||
|
class ChooserCurrentSystemView(ChooserBaseView):
|
||||||
|
excerpt = "Select action:"
|
||||||
|
|
||||||
|
def __init__(self, controller, current, has_more=False):
|
||||||
self.controller = controller
|
self.controller = controller
|
||||||
log.debug('current system: %s', current)
|
|
||||||
log.debug('more systems available: %s', has_more)
|
log.debug('more systems available: %s', has_more)
|
||||||
|
log.debug('current system: %s', current)
|
||||||
|
|
||||||
actions = []
|
actions = []
|
||||||
for action in current.actions:
|
for action in sorted(current.actions, key=by_preferred_action_type):
|
||||||
actions.append(forward_btn(label=action.title,
|
actions.append(forward_btn(label=action.title.capitalize(),
|
||||||
on_press=self._current_system_action,
|
on_press=self._current_system_action,
|
||||||
user_arg=(current, action)))
|
user_arg=(current, action)))
|
||||||
|
|
||||||
|
@ -75,16 +88,11 @@ class ChooserCurrentSystemView(BaseView):
|
||||||
|
|
||||||
lb = ListBox(actions)
|
lb = ListBox(actions)
|
||||||
|
|
||||||
buttons = [
|
super().__init__(current,
|
||||||
reset_btn("ABORT", on_press=self.abort),
|
screen(
|
||||||
]
|
|
||||||
|
|
||||||
super().__init__(screen(
|
|
||||||
lb,
|
lb,
|
||||||
buttons=button_pile(buttons),
|
|
||||||
focus_buttons=False,
|
|
||||||
narrow_rows=True,
|
narrow_rows=True,
|
||||||
excerpt=excerpt))
|
excerpt=self.excerpt))
|
||||||
|
|
||||||
def _current_system_action(self, sender, arg):
|
def _current_system_action(self, sender, arg):
|
||||||
current, action = arg
|
current, action = arg
|
||||||
|
@ -93,12 +101,11 @@ class ChooserCurrentSystemView(BaseView):
|
||||||
def _more_options(self, sender):
|
def _more_options(self, sender):
|
||||||
self.controller.more_options()
|
self.controller.more_options()
|
||||||
|
|
||||||
def abort(self, result):
|
def back(self, result):
|
||||||
self.controller.cancel()
|
self.controller.back()
|
||||||
|
|
||||||
|
|
||||||
class ChooserView(BaseView):
|
class ChooserView(ChooserBaseView):
|
||||||
title = "Ubuntu Core"
|
|
||||||
excerpt = ("Select one of available recovery systems and a desired "
|
excerpt = ("Select one of available recovery systems and a desired "
|
||||||
"action to execute.")
|
"action to execute.")
|
||||||
|
|
||||||
|
@ -123,8 +130,8 @@ class ChooserView(BaseView):
|
||||||
for s in systems:
|
for s in systems:
|
||||||
actions = []
|
actions = []
|
||||||
log.debug('actions: %s', s.actions)
|
log.debug('actions: %s', s.actions)
|
||||||
for act in s.actions:
|
for act in sorted(s.actions, key=by_preferred_action_type):
|
||||||
actions.append(Action(label=act.title,
|
actions.append(Action(label=act.title.capitalize(),
|
||||||
value=act,
|
value=act,
|
||||||
enabled=True))
|
enabled=True))
|
||||||
menu = ActionMenu(actions)
|
menu = ActionMenu(actions)
|
||||||
|
@ -133,7 +140,7 @@ class ChooserView(BaseView):
|
||||||
Text(s.label),
|
Text(s.label),
|
||||||
Text(s.model.display_name),
|
Text(s.model.display_name),
|
||||||
Text(s.brand.display_name),
|
Text(s.brand.display_name),
|
||||||
Text("(current)" if s.current else ""),
|
Text("(installed)" if s.current else ""),
|
||||||
menu,
|
menu,
|
||||||
], menu)
|
], menu)
|
||||||
trows.append(srow)
|
trows.append(srow)
|
||||||
|
@ -144,11 +151,13 @@ class ChooserView(BaseView):
|
||||||
Pile([heading_table, systems_table]),
|
Pile([heading_table, systems_table]),
|
||||||
]
|
]
|
||||||
|
|
||||||
buttons = [
|
buttons = []
|
||||||
reset_btn("ABORT", on_press=self.abort),
|
if controller.model.current is not None:
|
||||||
]
|
# back to options of current system
|
||||||
|
buttons.append(back_btn("BACK", on_press=self.back))
|
||||||
|
|
||||||
super().__init__(screen(
|
super().__init__(controller.model.current,
|
||||||
|
screen(
|
||||||
rows=rows,
|
rows=rows,
|
||||||
buttons=button_pile(buttons),
|
buttons=button_pile(buttons),
|
||||||
focus_buttons=False,
|
focus_buttons=False,
|
||||||
|
@ -157,43 +166,52 @@ class ChooserView(BaseView):
|
||||||
def _system_action(self, sender, action, system):
|
def _system_action(self, sender, action, system):
|
||||||
self.controller.select(system, action)
|
self.controller.select(system, action)
|
||||||
|
|
||||||
def abort(self, result):
|
def back(self, result):
|
||||||
self.controller.cancel()
|
self.controller.back()
|
||||||
|
|
||||||
|
|
||||||
class ChooserConfirmView(BaseView):
|
class ChooserConfirmView(ChooserBaseView):
|
||||||
title = "Ubuntu Core"
|
|
||||||
excerpt = ("Summary of the selected action.")
|
canned_summary = {
|
||||||
|
"run": "Continue running the system without any changes.",
|
||||||
|
"recover": ("You have requested to reboot the system into recovery "
|
||||||
|
"mode."),
|
||||||
|
"install": ("You are about to {action} the system version {version} "
|
||||||
|
"for {model} from {publisher}.\n\n"
|
||||||
|
"This will remove all existing user data on the "
|
||||||
|
"device.\n\n"
|
||||||
|
"The system will reboot in the process."),
|
||||||
|
}
|
||||||
|
default_summary = ("You are about to execute action \"{action}\" using "
|
||||||
|
"system version {version} for device {model} from "
|
||||||
|
"{publisher}.\n\n"
|
||||||
|
"Make sure you understand the consequences of doing "
|
||||||
|
"so.")
|
||||||
|
|
||||||
def __init__(self, controller, selection):
|
def __init__(self, controller, selection):
|
||||||
self.controller = controller
|
self.controller = controller
|
||||||
|
|
||||||
buttons = [
|
buttons = [
|
||||||
danger_btn("CONFIRM", on_press=self.confirm),
|
danger_btn("CONFIRM", on_press=self.confirm),
|
||||||
reset_btn("ABORT", on_press=self.abort),
|
back_btn("BACK", on_press=self.back),
|
||||||
]
|
|
||||||
using_summary = "System seed of device {} version {} from {}".format(
|
|
||||||
selection.system.model.display_name,
|
|
||||||
selection.system.label,
|
|
||||||
selection.system.brand.display_name
|
|
||||||
)
|
|
||||||
summary = [
|
|
||||||
TableRow([Text("Action:"), Color.info_error(Text(
|
|
||||||
selection.action.title))]),
|
|
||||||
TableRow([Text("Using:"), Text(using_summary)]),
|
|
||||||
]
|
]
|
||||||
|
fmt = self.canned_summary.get(selection.action.mode,
|
||||||
|
self.default_summary)
|
||||||
|
summary = fmt.format(action=selection.action.title,
|
||||||
|
model=selection.system.model.display_name,
|
||||||
|
publisher=selection.system.brand.display_name,
|
||||||
|
version=selection.system.label)
|
||||||
rows = [
|
rows = [
|
||||||
Pile([Text("")]),
|
Text(summary),
|
||||||
Pile([TablePile(summary)])
|
|
||||||
]
|
]
|
||||||
super().__init__(screen(
|
super().__init__(controller.model.current,
|
||||||
|
screen(
|
||||||
rows=rows,
|
rows=rows,
|
||||||
buttons=button_pile(buttons),
|
buttons=button_pile(buttons),
|
||||||
focus_buttons=False,
|
focus_buttons=False))
|
||||||
excerpt=self.excerpt))
|
|
||||||
|
|
||||||
def abort(self, result):
|
|
||||||
self.controller.cancel()
|
|
||||||
|
|
||||||
def confirm(self, result):
|
def confirm(self, result):
|
||||||
self.controller.confirm()
|
self.controller.confirm()
|
||||||
|
|
||||||
|
def back(self, result):
|
||||||
|
self.controller.back()
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
# Copyright 2020 Canonical, Ltd.
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as
|
||||||
|
# published by the Free Software Foundation, either version 3 of the
|
||||||
|
# License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
from console_conf.models.systems import (
|
||||||
|
SystemAction,
|
||||||
|
)
|
||||||
|
from console_conf.ui.views.chooser import (
|
||||||
|
by_preferred_action_type,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TestSorting(unittest.TestCase):
|
||||||
|
|
||||||
|
def test_simple(self):
|
||||||
|
data = [
|
||||||
|
SystemAction(title="b-other", mode="unknown"),
|
||||||
|
SystemAction(title="reinstall", mode="install"),
|
||||||
|
SystemAction(title="run normally", mode="run"),
|
||||||
|
SystemAction(title="a-other", mode="some-other"),
|
||||||
|
SystemAction(title="recover", mode="recover"),
|
||||||
|
]
|
||||||
|
exp = [
|
||||||
|
SystemAction(title="run normally", mode="run"),
|
||||||
|
SystemAction(title="recover", mode="recover"),
|
||||||
|
SystemAction(title="reinstall", mode="install"),
|
||||||
|
SystemAction(title="a-other", mode="some-other"),
|
||||||
|
SystemAction(title="b-other", mode="unknown"),
|
||||||
|
]
|
||||||
|
self.assertSequenceEqual(sorted(data, key=by_preferred_action_type),
|
||||||
|
exp)
|
Loading…
Reference in New Issue