diff --git a/locales/en.json b/locales/en.json index 8f173fcfc..8510cdf58 100644 --- a/locales/en.json +++ b/locales/en.json @@ -468,6 +468,7 @@ "migrations_success_forward": "Migration {id} completed", "migrations_to_be_ran_manually": "Migration {id} has to be run manually. Please go to Tools → Migrations on the webadmin page, or run `yunohost tools migrations migrate`.", "not_enough_disk_space": "Not enough free space on '{path:s}'", + "invalid_number": "Must be a number", "operation_interrupted": "The operation was manually interrupted?", "packages_upgrade_failed": "Could not upgrade all the packages", "password_listed": "This password is among the most used passwords in the world. Please choose something more unique.", diff --git a/src/yunohost/app.py b/src/yunohost/app.py index c42c5c3cb..ee3f22e95 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -2593,6 +2593,29 @@ class UserArgumentParser(YunoHostArgumentFormatParser): error=m18n.n('user_unknown', user=question.value)) +class NumberArgumentParser(YunoHostArgumentFormatParser): + argument_type = "number" + default_value = "" + + def parse_question(self, question, user_answers): + question = super(NumberArgumentParser, self).parse_question(question, user_answers) + + if question.default is None: + question.default = 0 + + return question + + def _post_parse_value(self, question): + if isinstance(question.value, int): + return super(NumberArgumentParser, self)._post_parse_value(question) + + try: + return int(question.value) + except ValueError: + raise YunohostError('app_argument_invalid', name=question.name, + error=m18n.n('invalid_number')) + + class DisplayTextArgumentParser(YunoHostArgumentFormatParser): argument_type = "display_text" @@ -2607,6 +2630,7 @@ ARGUMENTS_TYPE_PARSERS = { "boolean": BooleanArgumentParser, "domain": DomainArgumentParser, "user": UserArgumentParser, + "number": NumberArgumentParser, "display_text": DisplayTextArgumentParser, } diff --git a/src/yunohost/tests/test_apps_arguments_parsing.py b/src/yunohost/tests/test_apps_arguments_parsing.py index c8203f2c5..543e5a9b5 100644 --- a/src/yunohost/tests/test_apps_arguments_parsing.py +++ b/src/yunohost/tests/test_apps_arguments_parsing.py @@ -1091,6 +1091,161 @@ def test_parse_args_in_yunohost_format_user_two_users_default_input(): assert _parse_args_in_yunohost_format(answers, questions) == expected_result + +def test_parse_args_in_yunohost_format_number(): + questions = [{"name": "some_number", "type": "number", }] + answers = {"some_number": 1337} + expected_result = OrderedDict({"some_number": (1337, "number")}) + assert _parse_args_in_yunohost_format(answers, questions) == expected_result + + +def test_parse_args_in_yunohost_format_number_no_input(): + questions = [{"name": "some_number", "type": "number", }] + answers = {} + + expected_result = OrderedDict({"some_number": (0, "number")}) + assert _parse_args_in_yunohost_format(answers, questions) == expected_result + + +def test_parse_args_in_yunohost_format_number_bad_input(): + questions = [{"name": "some_number", "type": "number", }] + answers = {"some_number": "stuff"} + + with pytest.raises(YunohostError): + _parse_args_in_yunohost_format(answers, questions) + + +def test_parse_args_in_yunohost_format_number_input(): + questions = [{"name": "some_number", "type": "number", "ask": "some question", }] + answers = {} + + expected_result = OrderedDict({"some_number": (1337, "number")}) + with patch.object(msignals, "prompt", return_value="1337"): + assert _parse_args_in_yunohost_format(answers, questions) == expected_result + + with patch.object(msignals, "prompt", return_value=1337): + assert _parse_args_in_yunohost_format(answers, questions) == expected_result + + expected_result = OrderedDict({"some_number": (0, "number")}) + with patch.object(msignals, "prompt", return_value=""): + assert _parse_args_in_yunohost_format(answers, questions) == expected_result + + +def test_parse_args_in_yunohost_format_number_input_no_ask(): + questions = [{"name": "some_number", "type": "number", }] + answers = {} + expected_result = OrderedDict({"some_number": (1337, "number")}) + + with patch.object(msignals, "prompt", return_value="1337"): + assert _parse_args_in_yunohost_format(answers, questions) == expected_result + + +def test_parse_args_in_yunohost_format_number_no_input_optional(): + questions = [{"name": "some_number", "type": "number", "optional": True, }] + answers = {} + expected_result = OrderedDict({"some_number": (0, "number")}) # default to 0 + assert _parse_args_in_yunohost_format(answers, questions) == expected_result + + +def test_parse_args_in_yunohost_format_number_optional_with_input(): + questions = [ + { + "name": "some_number", + "ask": "some question", + "type": "number", + "optional": True, + } + ] + answers = {} + expected_result = OrderedDict({"some_number": (1337, "number")}) + + with patch.object(msignals, "prompt", return_value="1337"): + assert _parse_args_in_yunohost_format(answers, questions) == expected_result + + +def test_parse_args_in_yunohost_format_number_optional_with_input_without_ask(): + questions = [{"name": "some_number", "type": "number", "optional": True, }] + answers = {} + expected_result = OrderedDict({"some_number": (0, "number")}) + + with patch.object(msignals, "prompt", return_value="0"): + assert _parse_args_in_yunohost_format(answers, questions) == expected_result + + +def test_parse_args_in_yunohost_format_number_no_input_default(): + questions = [ + { + "name": "some_number", + "ask": "some question", + "type": "number", + "default": 1337, + } + ] + answers = {} + expected_result = OrderedDict({"some_number": (1337, "number")}) + assert _parse_args_in_yunohost_format(answers, questions) == expected_result + + +def test_parse_args_in_yunohost_format_number_bad_default(): + questions = [ + { + "name": "some_number", + "ask": "some question", + "type": "number", + "default": "bad default", + } + ] + answers = {} + with pytest.raises(YunohostError): + _parse_args_in_yunohost_format(answers, questions) + + +def test_parse_args_in_yunohost_format_number_input_test_ask(): + ask_text = "some question" + questions = [{"name": "some_number", "type": "number", "ask": ask_text, }] + answers = {} + + with patch.object(msignals, "prompt", return_value="1111") as prompt: + _parse_args_in_yunohost_format(answers, questions) + prompt.assert_called_with(ask_text, False) + + +def test_parse_args_in_yunohost_format_number_input_test_ask_with_default(): + ask_text = "some question" + default_value = 1337 + questions = [{"name": "some_number", "type": "number", "ask": ask_text, "default": default_value, }] + answers = {} + + with patch.object(msignals, "prompt", return_value="1111") as prompt: + _parse_args_in_yunohost_format(answers, questions) + prompt.assert_called_with("%s (default: %s)" % (ask_text, default_value), False) + + +@pytest.mark.skip # we should do something with this example +def test_parse_args_in_yunohost_format_number_input_test_ask_with_example(): + ask_text = "some question" + example_value = 1337 + questions = [{"name": "some_number", "type": "number", "ask": ask_text, "example": example_value, }] + answers = {} + + with patch.object(msignals, "prompt", return_value="1111") as prompt: + _parse_args_in_yunohost_format(answers, questions) + assert ask_text in prompt.call_args[0][0] + assert example_value in prompt.call_args[0][0] + + +@pytest.mark.skip # we should do something with this help +def test_parse_args_in_yunohost_format_number_input_test_ask_with_help(): + ask_text = "some question" + help_value = 1337 + questions = [{"name": "some_number", "type": "number", "ask": ask_text, "help": help_value, }] + answers = {} + + with patch.object(msignals, "prompt", return_value="1111") as prompt: + _parse_args_in_yunohost_format(answers, questions) + assert ask_text in prompt.call_args[0][0] + assert help_value in prompt.call_args[0][0] + def test_parse_args_in_yunohost_format_display_text(): questions = [{"name": "some_app", "type": "display_text", "ask": "foobar"}] answers = {}