Skip to content

Commit 968a900

Browse files
committed
feat(commit): implement questions 'filter' support with handlers
Supported APIs: - multiple_line_breaker - required_validator - required_validator_scope - required_validator_subject_strip - required_validator_title_strip Example YAML configurations: --- commitizen: name: cz_customize customize: questions: - ... - type: input name: scope message: 'Scope of the change :' filter: 'required_validator_scope' default: '' - type: input name: subject message: 'Title of the commit (starting in lower case and without period) :' filter: 'required_validator_subject_strip' default: '' - type: input name: body message: 'Additional contextual message (Empty to skip) :' default: 'Issue: #...' filter: 'multiple_line_breaker' --- Signed-off-by: Adrian DC <[email protected]>
1 parent 5e08775 commit 968a900

File tree

4 files changed

+113
-5
lines changed

4 files changed

+113
-5
lines changed

commitizen/commands/commit.py

+25-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,14 @@
88
from commitizen import factory, git, out
99
from commitizen.config import BaseConfig
1010
from commitizen.cz.exceptions import CzException
11-
from commitizen.cz.utils import get_backup_file_path
11+
from commitizen.cz.utils import (
12+
get_backup_file_path,
13+
multiple_line_breaker,
14+
required_validator,
15+
required_validator_scope,
16+
required_validator_subject_strip,
17+
required_validator_title_strip,
18+
)
1219
from commitizen.exceptions import (
1320
CommitError,
1421
CommitMessageLengthExceededError,
@@ -52,6 +59,23 @@ def prompt_commit_questions(self) -> str:
5259

5360
for question in filter(lambda q: q["type"] == "list", questions):
5461
question["use_shortcuts"] = self.config.settings["use_shortcuts"]
62+
63+
for question in filter(
64+
lambda q: isinstance(q.get("filter", None), str), questions
65+
):
66+
if question["filter"] == "multiple_line_breaker":
67+
question["filter"] = multiple_line_breaker
68+
elif question["filter"] == "required_validator":
69+
question["filter"] = required_validator
70+
elif question["filter"] == "required_validator_scope":
71+
question["filter"] = required_validator_scope
72+
elif question["filter"] == "required_validator_subject_strip":
73+
question["filter"] = required_validator_subject_strip
74+
elif question["filter"] == "required_validator_title_strip":
75+
question["filter"] = required_validator_title_strip
76+
else:
77+
raise NotAllowed(f"Unknown value filter: {question['filter']}")
78+
5579
try:
5680
answers = questionary.prompt(questions, style=cz.style)
5781
except ValueError as err:

commitizen/cz/utils.py

+12
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,18 @@ def required_validator(answer, msg=None):
1212
return answer
1313

1414

15+
def required_validator_scope(answer, msg="! Error: Scope is required"):
16+
return required_validator(answer, msg)
17+
18+
19+
def required_validator_subject_strip(answer, msg="! Error: Subject is required"):
20+
return required_validator(answer.strip(".").strip(), msg)
21+
22+
23+
def required_validator_title_strip(answer, msg="! Error: Title is required"):
24+
return required_validator(answer.strip(".").strip(), msg)
25+
26+
1527
def multiple_line_breaker(answer, sep="|"):
1628
return "\n".join(line.strip() for line in answer.split(sep) if line)
1729

docs/customization.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ commitizen:
175175
| `message` | `str` | `None` | Detail description for the question. |
176176
| `choices` | `list` | `None` | (OPTIONAL) The choices when `type = list`. Either use a list of values or a list of dictionaries with `name` and `value` keys. Keyboard shortcuts can be defined via `key`. See examples above. |
177177
| `default` | `Any` | `None` | (OPTIONAL) The default value for this question. |
178-
| `filter` | `str` | `None` | (Optional) Validator for user's answer. **(Work in Progress)** |
178+
| `filter` | `str` | `None` | (OPTIONAL) Validator for user's answer. The string is the name of a `commitizen.cz.utils.NAME(answer...)` function like `multiple_line_breaker` |
179179
[different-question-types]: https://github.com/tmbo/questionary#different-question-types
180180

181181
#### Shortcut keys

tests/test_cz_customize.py

+75-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import pytest
2+
from pytest_mock import MockFixture
23

4+
from commitizen import cmd, commands
35
from commitizen.config import BaseConfig, JsonConfig, TomlConfig, YAMLConfig
46
from commitizen.cz.customize import CustomizeCommitsCz
7+
from commitizen.cz.utils import multiple_line_breaker, required_validator_subject_strip
58
from commitizen.exceptions import MissingCzCustomizeConfigError
69

710
TOML_STR = r"""
@@ -36,10 +39,17 @@
3639
]
3740
message = "Select the type of change you are committing"
3841
42+
[[tool.commitizen.customize.questions]]
43+
type = "input"
44+
name = "subject"
45+
message = "Subject."
46+
filter = "required_validator_subject_strip"
47+
3948
[[tool.commitizen.customize.questions]]
4049
type = "input"
4150
name = "message"
4251
message = "Body."
52+
filter = "multiple_line_breaker"
4353
4454
[[tool.commitizen.customize.questions]]
4555
type = "confirm"
@@ -89,10 +99,17 @@
8999
],
90100
"message": "Select the type of change you are committing"
91101
},
102+
{
103+
"type": "input",
104+
"name": "subject",
105+
"message": "Subject.",
106+
"filter": "required_validator_subject_strip"
107+
},
92108
{
93109
"type": "input",
94110
"name": "message",
95-
"message": "Body."
111+
"message": "Body.",
112+
"filter": "multiple_line_breaker"
96113
},
97114
{
98115
"type": "confirm",
@@ -139,9 +156,14 @@
139156
- value: bug fix
140157
name: 'bug fix: A bug fix.'
141158
message: Select the type of change you are committing
159+
- type: input
160+
name: subject
161+
message: Subject.
162+
filter: required_validator_subject_strip
142163
- type: input
143164
name: message
144165
message: Body.
166+
filter: multiple_line_breaker
145167
- type: confirm
146168
name: show_message
147169
message: Do you want to add body message in commit?
@@ -330,6 +352,13 @@
330352
"""
331353

332354

355+
@pytest.fixture
356+
def staging_is_clean(mocker: MockFixture, tmp_git_project):
357+
is_staging_clean_mock = mocker.patch("commitizen.git.is_staging_clean")
358+
is_staging_clean_mock.return_value = False
359+
return tmp_git_project
360+
361+
333362
@pytest.fixture(
334363
params=[
335364
TomlConfig(data=TOML_STR, path="not_exist.toml"),
@@ -437,7 +466,7 @@ def test_change_type_order_unicode(config_with_unicode):
437466
]
438467

439468

440-
def test_questions(config):
469+
def test_questions_default(config):
441470
cz = CustomizeCommitsCz(config)
442471
questions = cz.questions()
443472
expected_questions = [
@@ -450,7 +479,18 @@ def test_questions(config):
450479
],
451480
"message": "Select the type of change you are committing",
452481
},
453-
{"type": "input", "name": "message", "message": "Body."},
482+
{
483+
"type": "input",
484+
"name": "subject",
485+
"message": "Subject.",
486+
"filter": "required_validator_subject_strip",
487+
},
488+
{
489+
"type": "input",
490+
"name": "message",
491+
"message": "Body.",
492+
"filter": "multiple_line_breaker",
493+
},
454494
{
455495
"type": "confirm",
456496
"name": "show_message",
@@ -460,6 +500,38 @@ def test_questions(config):
460500
assert list(questions) == expected_questions
461501

462502

503+
@pytest.mark.usefixtures("staging_is_clean")
504+
def test_questions_filter(config, mocker: MockFixture):
505+
is_staging_clean_mock = mocker.patch("commitizen.git.is_staging_clean")
506+
is_staging_clean_mock.return_value = False
507+
508+
prompt_mock = mocker.patch("questionary.prompt")
509+
prompt_mock.return_value = {
510+
"change_type": "feature",
511+
"subject": "user created",
512+
"message": "body of the commit",
513+
"show_message": True,
514+
}
515+
516+
commit_mock = mocker.patch("commitizen.git.commit")
517+
commit_mock.return_value = cmd.Command("success", "", b"", b"", 0)
518+
519+
commands.Commit(config, {})()
520+
521+
prompts_questions = prompt_mock.call_args[0][0]
522+
assert prompts_questions[0]["type"] == "list"
523+
assert prompts_questions[0]["name"] == "change_type"
524+
assert prompts_questions[0]["use_shortcuts"] is False
525+
assert prompts_questions[1]["type"] == "input"
526+
assert prompts_questions[1]["name"] == "subject"
527+
assert prompts_questions[1]["filter"] == required_validator_subject_strip
528+
assert prompts_questions[2]["type"] == "input"
529+
assert prompts_questions[2]["name"] == "message"
530+
assert prompts_questions[2]["filter"] == multiple_line_breaker
531+
assert prompts_questions[3]["type"] == "confirm"
532+
assert prompts_questions[3]["name"] == "show_message"
533+
534+
463535
def test_questions_unicode(config_with_unicode):
464536
cz = CustomizeCommitsCz(config_with_unicode)
465537
questions = cz.questions()

0 commit comments

Comments
 (0)