Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 314f3bf

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 <radian.dc@gmail.com>
1 parent 5e08775 commit 314f3bf

File tree

4 files changed

+185
-8
lines changed

4 files changed

+185
-8
lines changed

‎commitizen/commands/commit.py‎

Lines changed: 25 additions & 1 deletion
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‎

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,34 @@
66
from commitizen.cz import exceptions
77

88

9-
def required_validator(answer, msg=None):
9+
def required_validator(answer: str, msg=None)->str:
1010
if not answer:
1111
raise exceptions.AnswerRequiredError(msg)
1212
return answer
1313

1414

15-
def multiple_line_breaker(answer, sep="|"):
15+
def required_validator_scope(
16+
answer: str,
17+
msg: str = "! Error: Scope is required",
18+
) -> str:
19+
return required_validator(answer, msg)
20+
21+
22+
def required_validator_subject_strip(
23+
answer: str,
24+
msg: str = "! Error: Subject is required",
25+
) -> str:
26+
return required_validator(answer.strip(".").strip(), msg)
27+
28+
29+
def required_validator_title_strip(
30+
answer: str,
31+
msg: str = "! Error: Title is required",
32+
) -> str:
33+
return required_validator(answer.strip(".").strip(), msg)
34+
35+
36+
def multiple_line_breaker(answer: str, sep: str = "|") -> str:
1637
return "\n".join(line.strip() for line in answer.split(sep) if line)
1738

1839

‎docs/customization.md‎

Lines changed: 1 addition & 1 deletion
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‎

Lines changed: 136 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,17 @@
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
5-
from commitizen.exceptions import MissingCzCustomizeConfigError
7+
from commitizen.cz.utils import (
8+
multiple_line_breaker,
9+
required_validator,
10+
required_validator_scope,
11+
required_validator_subject_strip,
12+
required_validator_title_strip,
13+
)
14+
from commitizen.exceptions import MissingCzCustomizeConfigError, NotAllowed
615

716
TOML_STR = r"""
817
[tool.commitizen]
@@ -36,10 +45,17 @@
3645
]
3746
message = "Select the type of change you are committing"
3847
48+
[[tool.commitizen.customize.questions]]
49+
type = "input"
50+
name = "subject"
51+
message = "Subject."
52+
filter = "required_validator_subject_strip"
53+
3954
[[tool.commitizen.customize.questions]]
4055
type = "input"
4156
name = "message"
4257
message = "Body."
58+
filter = "multiple_line_breaker"
4359
4460
[[tool.commitizen.customize.questions]]
4561
type = "confirm"
@@ -89,10 +105,17 @@
89105
],
90106
"message": "Select the type of change you are committing"
91107
},
108+
{
109+
"type": "input",
110+
"name": "subject",
111+
"message": "Subject.",
112+
"filter": "required_validator_subject_strip"
113+
},
92114
{
93115
"type": "input",
94116
"name": "message",
95-
"message": "Body."
117+
"message": "Body.",
118+
"filter": "multiple_line_breaker"
96119
},
97120
{
98121
"type": "confirm",
@@ -139,9 +162,14 @@
139162
- value: bug fix
140163
name: 'bug fix: A bug fix.'
141164
message: Select the type of change you are committing
165+
- type: input
166+
name: subject
167+
message: Subject.
168+
filter: required_validator_subject_strip
142169
- type: input
143170
name: message
144171
message: Body.
172+
filter: multiple_line_breaker
145173
- type: confirm
146174
name: show_message
147175
message: Do you want to add body message in commit?
@@ -330,6 +358,13 @@
330358
"""
331359

332360

361+
@pytest.fixture
362+
def staging_is_clean(mocker: MockFixture, tmp_git_project):
363+
is_staging_clean_mock = mocker.patch("commitizen.git.is_staging_clean")
364+
is_staging_clean_mock.return_value = False
365+
return tmp_git_project
366+
367+
333368
@pytest.fixture(
334369
params=[
335370
TomlConfig(data=TOML_STR, path="not_exist.toml"),
@@ -346,6 +381,15 @@ def config(request):
346381
return request.param
347382

348383

384+
@pytest.fixture(
385+
params=[
386+
YAMLConfig(data=YAML_STR, path="not_exist.yaml"),
387+
]
388+
)
389+
def config_filters(request):
390+
return request.param
391+
392+
349393
@pytest.fixture(
350394
params=[
351395
TomlConfig(data=TOML_STR_INFO_PATH, path="not_exist.toml"),
@@ -437,7 +481,7 @@ def test_change_type_order_unicode(config_with_unicode):
437481
]
438482

439483

440-
def test_questions(config):
484+
def test_questions_default(config):
441485
cz = CustomizeCommitsCz(config)
442486
questions = cz.questions()
443487
expected_questions = [
@@ -450,7 +494,18 @@ def test_questions(config):
450494
],
451495
"message": "Select the type of change you are committing",
452496
},
453-
{"type": "input", "name": "message", "message": "Body."},
497+
{
498+
"type": "input",
499+
"name": "subject",
500+
"message": "Subject.",
501+
"filter": "required_validator_subject_strip",
502+
},
503+
{
504+
"type": "input",
505+
"name": "message",
506+
"message": "Body.",
507+
"filter": "multiple_line_breaker",
508+
},
454509
{
455510
"type": "confirm",
456511
"name": "show_message",
@@ -460,6 +515,83 @@ def test_questions(config):
460515
assert list(questions) == expected_questions
461516

462517

518+
@pytest.mark.usefixtures("staging_is_clean")
519+
def test_questions_filter_default(config, mocker: MockFixture):
520+
is_staging_clean_mock = mocker.patch("commitizen.git.is_staging_clean")
521+
is_staging_clean_mock.return_value = False
522+
523+
prompt_mock = mocker.patch("questionary.prompt")
524+
prompt_mock.return_value = {
525+
"change_type": "feature",
526+
"subject": "user created",
527+
"message": "body of the commit",
528+
"show_message": True,
529+
}
530+
531+
commit_mock = mocker.patch("commitizen.git.commit")
532+
commit_mock.return_value = cmd.Command("success", "", b"", b"", 0)
533+
534+
commands.Commit(config, {})()
535+
536+
prompts_questions = prompt_mock.call_args[0][0]
537+
assert prompts_questions[0]["type"] == "list"
538+
assert prompts_questions[0]["name"] == "change_type"
539+
assert prompts_questions[0]["use_shortcuts"] is False
540+
assert prompts_questions[1]["type"] == "input"
541+
assert prompts_questions[1]["name"] == "subject"
542+
assert prompts_questions[1]["filter"] == required_validator_subject_strip
543+
assert prompts_questions[2]["type"] == "input"
544+
assert prompts_questions[2]["name"] == "message"
545+
assert prompts_questions[2]["filter"] == multiple_line_breaker
546+
assert prompts_questions[3]["type"] == "confirm"
547+
assert prompts_questions[3]["name"] == "show_message"
548+
549+
550+
@pytest.mark.usefixtures("staging_is_clean")
551+
def test_questions_filter_values(config_filters, mocker: MockFixture):
552+
is_staging_clean_mock = mocker.patch("commitizen.git.is_staging_clean")
553+
is_staging_clean_mock.return_value = False
554+
555+
prompt_mock = mocker.patch("questionary.prompt")
556+
prompt_mock.return_value = {
557+
"change_type": "feature",
558+
"subject": "user created",
559+
"message": "body of the commit",
560+
"show_message": True,
561+
}
562+
563+
commit_mock = mocker.patch("commitizen.git.commit")
564+
commit_mock.return_value = cmd.Command("success", "", b"", b"", 0)
565+
566+
commit_cmd = commands.Commit(config_filters, {})
567+
568+
assert isinstance(commit_cmd.cz, CustomizeCommitsCz)
569+
570+
for filter_desc in [
571+
("multiple_line_breaker", multiple_line_breaker),
572+
("required_validator", required_validator),
573+
("required_validator_scope", required_validator_scope),
574+
("required_validator_subject_strip", required_validator_subject_strip),
575+
("required_validator_title_strip", required_validator_title_strip),
576+
]:
577+
commit_cmd.cz.custom_settings["questions"][1]["filter"] = filter_desc[0] # type: ignore[index]
578+
commit_cmd()
579+
580+
assert filter_desc[1]("input")
581+
582+
prompts_questions = prompt_mock.call_args[0][0]
583+
assert prompts_questions[1]["filter"] == filter_desc[1]
584+
585+
for filter_name in [
586+
"",
587+
"faulty_value",
588+
]:
589+
commit_cmd.cz.custom_settings["questions"][1]["filter"] = filter_name # type: ignore[index]
590+
591+
with pytest.raises(NotAllowed):
592+
commit_cmd()
593+
594+
463595
def test_questions_unicode(config_with_unicode):
464596
cz = CustomizeCommitsCz(config_with_unicode)
465597
questions = cz.questions()

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /