Skip to content

Commit 1f74bb2

Browse files
committed
test(commit): add some test for commit --body-length-limit flag
1 parent aa15fda commit 1f74bb2

File tree

2 files changed

+232
-3
lines changed

2 files changed

+232
-3
lines changed

commitizen/commands/commit.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -123,9 +123,11 @@ def _rewrap_body(self, message: str) -> str:
123123
subject = message_parts[0]
124124
blank_line = message_parts[1]
125125
body = message_parts[2].strip()
126-
wrapped_body = textwrap.fill(
127-
body, width=body_length_limit, replace_whitespace=False
128-
)
126+
body_lines = body.split("\n")
127+
wrapped_body_lines = []
128+
for line in body_lines:
129+
wrapped_body_lines.append(textwrap.fill(line, width=body_length_limit))
130+
wrapped_body = "\n".join(wrapped_body_lines)
129131
return f"{subject}\n{blank_line}\n{wrapped_body}"
130132

131133
def manual_edit(self, message: str) -> str:

tests/commands/test_commit_command.py

Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,3 +365,230 @@ def test_commit_command_with_config_message_length_limit(
365365
success_mock.reset_mock()
366366
commands.Commit(config, {"message_length_limit": 0})()
367367
success_mock.assert_called_once()
368+
369+
370+
@pytest.mark.usefixtures("staging_is_clean")
371+
def test_commit_command_with_body_length_limit_wrapping(
372+
config, success_mock: MockType, mocker: MockFixture
373+
):
374+
"""Test that long body lines are automatically wrapped to the specified limit."""
375+
mocker.patch(
376+
"questionary.prompt",
377+
return_value={
378+
"prefix": "feat",
379+
"subject": "add feature",
380+
"scope": "",
381+
"is_breaking_change": False,
382+
"body": "This is a very long line that exceeds 72 characters and should be automatically wrapped by the system to fit within the limit",
383+
"footer": "",
384+
},
385+
)
386+
387+
commit_mock = mocker.patch(
388+
"commitizen.git.commit", return_value=cmd.Command("success", "", b"", b"", 0)
389+
)
390+
391+
# Execute with body_length_limit
392+
commands.Commit(config, {"body_length_limit": 72})()
393+
success_mock.assert_called_once()
394+
395+
# Verify wrapping occurred
396+
committed_message = commit_mock.call_args[0][0]
397+
lines = committed_message.split("\n")
398+
assert lines[0] == "feat: add feature"
399+
assert lines[1] == ""
400+
body_lines = lines[2:]
401+
for line in body_lines:
402+
if line.strip():
403+
assert len(line) <= 72, (
404+
f"Line exceeds 72 chars: '{line}' ({len(line)} chars)"
405+
)
406+
407+
408+
@pytest.mark.usefixtures("staging_is_clean")
409+
def test_commit_command_with_body_length_limit_preserves_line_breaks(
410+
config, success_mock: MockType, mocker: MockFixture
411+
):
412+
"""Test that intentional line breaks (from | character) are preserved."""
413+
# Simulate what happens after multiple_line_breaker processes "line1 | line2 | line3"
414+
mocker.patch(
415+
"questionary.prompt",
416+
return_value={
417+
"prefix": "feat",
418+
"subject": "add feature",
419+
"scope": "",
420+
"is_breaking_change": False,
421+
"body": "Line1 that is very long and exceeds the limit\nLine2 that is very long and exceeds the limit\nLine3 that is very long and exceeds the limit",
422+
"footer": "",
423+
},
424+
)
425+
426+
commit_mock = mocker.patch(
427+
"commitizen.git.commit", return_value=cmd.Command("success", "", b"", b"", 0)
428+
)
429+
430+
commands.Commit(config, {"body_length_limit": 45})()
431+
success_mock.assert_called_once()
432+
433+
committed_message = commit_mock.call_args[0][0]
434+
lines = committed_message.split("\n")
435+
436+
# Should have a subject, a blank line
437+
assert lines[0] == "feat: add feature"
438+
assert lines[1] == ""
439+
# Each original line should be wrapped separately, preserving the line breaks
440+
body_lines = lines[2:]
441+
# All lines should be <= 45 chars
442+
for line in body_lines:
443+
if line.strip():
444+
assert len(line) == 45, (
445+
f"Line's length is not 45 chars: '{line}' ({len(line)} chars)"
446+
)
447+
448+
449+
@pytest.mark.usefixtures("staging_is_clean")
450+
def test_commit_command_with_body_length_limit_disabled(
451+
config, success_mock: MockType, mocker: MockFixture
452+
):
453+
"""Test that body_length_limit = 0 disables wrapping."""
454+
long_body = "This is a very long line that exceeds 72 characters and should NOT be wrapped when body_length_limit is set to 0"
455+
456+
mocker.patch(
457+
"questionary.prompt",
458+
return_value={
459+
"prefix": "feat",
460+
"subject": "add feature",
461+
"scope": "",
462+
"is_breaking_change": False,
463+
"body": long_body,
464+
"footer": "",
465+
},
466+
)
467+
468+
commit_mock = mocker.patch(
469+
"commitizen.git.commit", return_value=cmd.Command("success", "", b"", b"", 0)
470+
)
471+
472+
# Execute with body_length_limit = 0 (disabled)
473+
commands.Commit(config, {"body_length_limit": 0})()
474+
475+
success_mock.assert_called_once()
476+
477+
# Get the actual commit message
478+
committed_message = commit_mock.call_args[0][0]
479+
480+
# Verify the body was NOT wrapped (should contain the original long line)
481+
assert long_body in committed_message, "Body should not be wrapped when limit is 0"
482+
483+
484+
@pytest.mark.usefixtures("staging_is_clean")
485+
def test_commit_command_with_body_length_limit_from_config(
486+
config, success_mock: MockType, mocker: MockFixture
487+
):
488+
"""Test that body_length_limit can be set via config."""
489+
mocker.patch(
490+
"questionary.prompt",
491+
return_value={
492+
"prefix": "feat",
493+
"subject": "add feature",
494+
"scope": "",
495+
"is_breaking_change": False,
496+
"body": "This is a very long line that exceeds 50 characters and should be wrapped",
497+
"footer": "",
498+
},
499+
)
500+
501+
commit_mock = mocker.patch(
502+
"commitizen.git.commit", return_value=cmd.Command("success", "", b"", b"", 0)
503+
)
504+
505+
# Set body_length_limit in config
506+
config.settings["body_length_limit"] = 50
507+
508+
commands.Commit(config, {})()
509+
510+
success_mock.assert_called_once()
511+
512+
# Get the actual commit message
513+
committed_message = commit_mock.call_args[0][0]
514+
515+
# Verify all body lines are within the limit
516+
lines = committed_message.split("\n")
517+
body_lines = lines[2:]
518+
for line in body_lines:
519+
if line.strip():
520+
assert len(line) <= 50, (
521+
f"Line exceeds 50 chars: '{line}' ({len(line)} chars)"
522+
)
523+
524+
525+
@pytest.mark.usefixtures("staging_is_clean")
526+
def test_commit_command_body_length_limit_cli_overrides_config(
527+
config, success_mock: MockType, mocker: MockFixture
528+
):
529+
"""Test that CLI argument overrides config setting."""
530+
mocker.patch(
531+
"questionary.prompt",
532+
return_value={
533+
"prefix": "feat",
534+
"subject": "add feature",
535+
"scope": "",
536+
"is_breaking_change": False,
537+
"body": "This is a line that is longer than 40 characters but shorter than 80 characters",
538+
"footer": "",
539+
},
540+
)
541+
542+
commit_mock = mocker.patch(
543+
"commitizen.git.commit", return_value=cmd.Command("success", "", b"", b"", 0)
544+
)
545+
546+
# Set config to 40 (would wrap)
547+
config.settings["body_length_limit"] = 40
548+
549+
# Override with CLI argument to 0 (should NOT wrap)
550+
commands.Commit(config, {"body_length_limit": 0})()
551+
552+
success_mock.assert_called_once()
553+
554+
# Get the actual commit message
555+
committed_message = commit_mock.call_args[0][0]
556+
557+
# The line should NOT be wrapped (CLI override to 0 disables wrapping)
558+
assert (
559+
"This is a line that is longer than 40 characters but shorter than 80 characters"
560+
in committed_message
561+
)
562+
563+
564+
@pytest.mark.usefixtures("staging_is_clean")
565+
def test_commit_command_with_body_length_limit_no_body(
566+
config, success_mock: MockType, mocker: MockFixture
567+
):
568+
"""Test that commits without body work correctly with body_length_limit set."""
569+
mocker.patch(
570+
"questionary.prompt",
571+
return_value={
572+
"prefix": "feat",
573+
"subject": "add feature",
574+
"scope": "",
575+
"is_breaking_change": False,
576+
"body": "", # No body
577+
"footer": "",
578+
},
579+
)
580+
581+
commit_mock = mocker.patch(
582+
"commitizen.git.commit", return_value=cmd.Command("success", "", b"", b"", 0)
583+
)
584+
585+
# Execute commit with body_length_limit (should not crash)
586+
commands.Commit(config, {"body_length_limit": 72})()
587+
588+
success_mock.assert_called_once()
589+
590+
# Get the actual commit message
591+
committed_message = commit_mock.call_args[0][0]
592+
593+
# Should just be the subject line
594+
assert committed_message.strip() == "feat: add feature"

0 commit comments

Comments
 (0)