2016-05-07 21:33:50 +02:00
|
|
|
# Copyright (c) Amber Brown, 2015
|
|
|
|
# See LICENSE for details.
|
2018-06-12 15:53:15 +02:00
|
|
|
|
2016-05-07 21:33:50 +02:00
|
|
|
import os
|
2022-04-11 15:43:12 +02:00
|
|
|
|
2017-05-31 01:13:22 +02:00
|
|
|
from subprocess import call
|
2017-08-07 06:55:05 +02:00
|
|
|
from textwrap import dedent
|
2016-05-07 21:33:50 +02:00
|
|
|
|
|
|
|
from click.testing import CliRunner
|
2022-04-11 15:43:12 +02:00
|
|
|
from twisted.trial.unittest import TestCase
|
2019-03-16 14:41:30 +01:00
|
|
|
|
|
|
|
from .._shell import cli
|
2022-04-11 15:43:12 +02:00
|
|
|
from ..build import _main
|
2016-05-07 21:33:50 +02:00
|
|
|
|
|
|
|
|
2017-08-03 07:28:39 +02:00
|
|
|
def setup_simple_project():
|
2018-06-12 15:53:15 +02:00
|
|
|
with open("pyproject.toml", "w") as f:
|
|
|
|
f.write("[tool.towncrier]\n" 'package = "foo"\n')
|
|
|
|
os.mkdir("foo")
|
|
|
|
with open("foo/__init__.py", "w") as f:
|
2017-08-03 07:28:39 +02:00
|
|
|
f.write('__version__ = "1.2.3"\n')
|
2018-06-12 15:53:15 +02:00
|
|
|
os.mkdir("foo/newsfragments")
|
2017-08-03 07:28:39 +02:00
|
|
|
|
|
|
|
|
2016-05-07 21:33:50 +02:00
|
|
|
class TestCli(TestCase):
|
2016-12-07 11:31:03 +01:00
|
|
|
maxDiff = None
|
|
|
|
|
2019-03-16 14:41:30 +01:00
|
|
|
def _test_command(self, command):
|
2016-12-07 11:31:03 +01:00
|
|
|
runner = CliRunner()
|
|
|
|
|
|
|
|
with runner.isolated_filesystem():
|
2017-12-30 01:47:27 +01:00
|
|
|
setup_simple_project()
|
2018-06-12 15:53:15 +02:00
|
|
|
with open("foo/newsfragments/123.feature", "w") as f:
|
|
|
|
f.write("Adds levitation")
|
2017-12-30 02:09:33 +01:00
|
|
|
# Towncrier treats this as 124.feature, ignoring .rst extension
|
2018-06-12 15:53:15 +02:00
|
|
|
with open("foo/newsfragments/124.feature.rst", "w") as f:
|
|
|
|
f.write("Extends levitation")
|
2019-03-11 15:01:51 +01:00
|
|
|
# Towncrier supports non-numeric newsfragment names.
|
|
|
|
with open("foo/newsfragments/baz.feature.rst", "w") as f:
|
|
|
|
f.write("Baz levitation")
|
2019-03-11 15:01:53 +01:00
|
|
|
# Towncrier supports files that have a dot in the name of the
|
|
|
|
# newsfragment
|
|
|
|
with open("foo/newsfragments/fix-1.2.feature", "w") as f:
|
|
|
|
f.write("Baz fix levitation")
|
2017-12-30 02:09:33 +01:00
|
|
|
# Towncrier ignores files that don't have a dot
|
2018-06-12 15:53:15 +02:00
|
|
|
with open("foo/newsfragments/README", "w") as f:
|
|
|
|
f.write("Blah blah")
|
2017-12-30 02:09:33 +01:00
|
|
|
# And files that don't have a valid category
|
2018-06-12 15:53:15 +02:00
|
|
|
with open("foo/newsfragments/README.rst", "w") as f:
|
|
|
|
f.write("**Blah blah**")
|
2016-12-07 11:31:03 +01:00
|
|
|
|
2019-03-16 14:41:30 +01:00
|
|
|
result = runner.invoke(command, ["--draft", "--date", "01-01-2001"])
|
2016-12-07 11:31:03 +01:00
|
|
|
|
|
|
|
self.assertEqual(0, result.exit_code)
|
|
|
|
self.assertEqual(
|
|
|
|
result.output,
|
2019-08-11 19:54:52 +02:00
|
|
|
dedent(
|
|
|
|
"""\
|
2019-03-11 15:01:50 +01:00
|
|
|
Loading template...
|
|
|
|
Finding news fragments...
|
|
|
|
Rendering news fragments...
|
|
|
|
Draft only -- nothing has been written.
|
|
|
|
What is seen below is what would be written.
|
|
|
|
|
|
|
|
Foo 1.2.3 (01-01-2001)
|
|
|
|
======================
|
|
|
|
|
|
|
|
Features
|
|
|
|
--------
|
|
|
|
|
2019-03-11 15:01:51 +01:00
|
|
|
- Baz levitation (baz)
|
2019-03-11 15:01:53 +01:00
|
|
|
- Baz fix levitation (#2)
|
2019-03-11 15:01:50 +01:00
|
|
|
- Adds levitation (#123)
|
|
|
|
- Extends levitation (#124)
|
|
|
|
|
2019-08-11 19:54:52 +02:00
|
|
|
"""
|
|
|
|
),
|
2016-12-07 11:31:03 +01:00
|
|
|
)
|
2017-08-03 07:28:39 +02:00
|
|
|
|
2019-03-16 14:41:30 +01:00
|
|
|
def test_command(self):
|
|
|
|
self._test_command(cli)
|
|
|
|
|
|
|
|
def test_subcommand(self):
|
|
|
|
self._test_command(_main)
|
|
|
|
|
2020-12-19 20:17:19 +01:00
|
|
|
def test_no_newsfragment_directory(self):
|
|
|
|
"""
|
|
|
|
A missing newsfragment directory acts as if there are no changes.
|
|
|
|
"""
|
|
|
|
runner = CliRunner()
|
|
|
|
|
|
|
|
with runner.isolated_filesystem():
|
|
|
|
setup_simple_project()
|
|
|
|
os.rmdir("foo/newsfragments")
|
|
|
|
|
|
|
|
result = runner.invoke(_main, ["--draft", "--date", "01-01-2001"])
|
|
|
|
|
|
|
|
self.assertEqual(1, result.exit_code, result.output)
|
|
|
|
self.assertIn("Failed to list the news fragment files.\n", result.output)
|
|
|
|
|
|
|
|
def test_no_newsfragments(self):
|
|
|
|
"""
|
|
|
|
An empty newsfragment directory acts as if there are no changes.
|
|
|
|
"""
|
|
|
|
runner = CliRunner()
|
|
|
|
|
|
|
|
with runner.isolated_filesystem():
|
|
|
|
setup_simple_project()
|
|
|
|
|
|
|
|
result = runner.invoke(_main, ["--draft", "--date", "01-01-2001"])
|
|
|
|
|
|
|
|
self.assertEqual(0, result.exit_code)
|
|
|
|
self.assertIn("No significant changes.\n", result.output)
|
|
|
|
|
2017-12-27 11:16:23 +01:00
|
|
|
def test_collision(self):
|
|
|
|
runner = CliRunner()
|
|
|
|
|
|
|
|
with runner.isolated_filesystem():
|
|
|
|
setup_simple_project()
|
|
|
|
# Note that both are 123.feature
|
2018-06-12 15:53:15 +02:00
|
|
|
with open("foo/newsfragments/123.feature", "w") as f:
|
|
|
|
f.write("Adds levitation")
|
|
|
|
with open("foo/newsfragments/123.feature.rst", "w") as f:
|
|
|
|
f.write("Extends levitation")
|
2017-12-27 11:16:23 +01:00
|
|
|
|
2018-06-12 15:53:15 +02:00
|
|
|
result = runner.invoke(_main, ["--draft", "--date", "01-01-2001"])
|
2017-12-27 11:16:23 +01:00
|
|
|
|
|
|
|
# This should fail
|
|
|
|
self.assertEqual(type(result.exception), ValueError)
|
|
|
|
self.assertIn("multiple files for 123.feature", str(result.exception))
|
|
|
|
|
2017-08-07 06:55:05 +02:00
|
|
|
def test_section_and_type_sorting(self):
|
|
|
|
"""
|
|
|
|
Sections and types should be output in the same order that they're
|
|
|
|
defined in the config file.
|
|
|
|
"""
|
|
|
|
|
2017-08-03 07:28:39 +02:00
|
|
|
runner = CliRunner()
|
|
|
|
|
2017-08-07 06:55:05 +02:00
|
|
|
def run_order_scenario(sections, types):
|
|
|
|
with runner.isolated_filesystem():
|
2018-06-12 15:53:15 +02:00
|
|
|
with open("pyproject.toml", "w") as f:
|
|
|
|
f.write(
|
|
|
|
dedent(
|
|
|
|
"""
|
2017-08-07 06:55:05 +02:00
|
|
|
[tool.towncrier]
|
|
|
|
package = "foo"
|
|
|
|
directory = "news"
|
|
|
|
|
2018-06-12 15:53:15 +02:00
|
|
|
"""
|
|
|
|
)
|
|
|
|
)
|
2017-08-07 06:55:05 +02:00
|
|
|
|
|
|
|
for section in sections:
|
2018-06-12 15:53:15 +02:00
|
|
|
f.write(
|
|
|
|
dedent(
|
|
|
|
"""
|
2017-08-07 06:55:05 +02:00
|
|
|
[[tool.towncrier.section]]
|
|
|
|
path = "{section}"
|
|
|
|
name = "{section}"
|
2018-06-12 15:53:15 +02:00
|
|
|
""".format(
|
|
|
|
section=section
|
|
|
|
)
|
|
|
|
)
|
|
|
|
)
|
2017-08-07 06:55:05 +02:00
|
|
|
|
|
|
|
for type_ in types:
|
2018-06-12 15:53:15 +02:00
|
|
|
f.write(
|
|
|
|
dedent(
|
|
|
|
"""
|
2017-08-07 06:55:05 +02:00
|
|
|
[[tool.towncrier.type]]
|
|
|
|
directory = "{type_}"
|
|
|
|
name = "{type_}"
|
|
|
|
showcontent = true
|
2018-06-12 15:53:15 +02:00
|
|
|
""".format(
|
|
|
|
type_=type_
|
|
|
|
)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
os.mkdir("foo")
|
|
|
|
with open("foo/__init__.py", "w") as f:
|
2017-08-07 06:55:05 +02:00
|
|
|
f.write('__version__ = "1.2.3"\n')
|
2018-06-12 15:53:15 +02:00
|
|
|
os.mkdir("news")
|
2017-08-07 06:55:05 +02:00
|
|
|
for section in sections:
|
|
|
|
sectdir = "news/" + section
|
|
|
|
os.mkdir(sectdir)
|
|
|
|
for type_ in types:
|
2022-04-09 09:51:07 +02:00
|
|
|
with open(f"{sectdir}/1.{type_}", "w") as f:
|
|
|
|
f.write(f"{section} {type_}")
|
2017-08-07 06:55:05 +02:00
|
|
|
|
|
|
|
return runner.invoke(
|
2018-06-12 15:53:15 +02:00
|
|
|
_main, ["--draft", "--date", "01-01-2001"], catch_exceptions=False
|
2017-08-07 06:55:05 +02:00
|
|
|
)
|
2017-08-03 07:28:39 +02:00
|
|
|
|
2018-06-12 15:53:15 +02:00
|
|
|
result = run_order_scenario(["section-a", "section-b"], ["type-1", "type-2"])
|
2017-08-07 06:55:05 +02:00
|
|
|
self.assertEqual(0, result.exit_code)
|
|
|
|
self.assertEqual(
|
|
|
|
result.output,
|
2022-04-09 09:51:07 +02:00
|
|
|
"Loading template...\nFinding news fragments...\nRendering news "
|
|
|
|
"fragments...\nDraft only -- nothing has been written.\nWhat is "
|
|
|
|
"seen below is what would be written.\n\nFoo 1.2.3 (01-01-2001)"
|
|
|
|
"\n======================"
|
2018-06-12 15:53:15 +02:00
|
|
|
+ dedent(
|
|
|
|
"""
|
2017-08-07 06:55:05 +02:00
|
|
|
section-a
|
|
|
|
---------
|
|
|
|
|
|
|
|
type-1
|
|
|
|
~~~~~~
|
|
|
|
|
|
|
|
- section-a type-1 (#1)
|
|
|
|
|
|
|
|
|
|
|
|
type-2
|
|
|
|
~~~~~~
|
|
|
|
|
|
|
|
- section-a type-2 (#1)
|
|
|
|
|
|
|
|
|
|
|
|
section-b
|
|
|
|
---------
|
2017-08-03 07:28:39 +02:00
|
|
|
|
2017-08-07 06:55:05 +02:00
|
|
|
type-1
|
|
|
|
~~~~~~
|
2017-08-03 07:28:39 +02:00
|
|
|
|
2017-08-07 06:55:05 +02:00
|
|
|
- section-b type-1 (#1)
|
|
|
|
|
|
|
|
|
|
|
|
type-2
|
|
|
|
~~~~~~
|
|
|
|
|
|
|
|
- section-b type-2 (#1)
|
|
|
|
|
2018-06-12 15:53:15 +02:00
|
|
|
"""
|
|
|
|
),
|
2017-08-07 06:55:05 +02:00
|
|
|
)
|
|
|
|
|
2018-06-12 15:53:15 +02:00
|
|
|
result = run_order_scenario(["section-b", "section-a"], ["type-2", "type-1"])
|
2016-12-07 11:31:03 +01:00
|
|
|
self.assertEqual(0, result.exit_code)
|
|
|
|
self.assertEqual(
|
|
|
|
result.output,
|
2022-04-09 09:51:07 +02:00
|
|
|
"Loading template...\nFinding news fragments...\nRendering news "
|
|
|
|
"fragments...\nDraft only -- nothing has been written.\nWhat is "
|
|
|
|
"seen below is what would be written.\n\nFoo 1.2.3 (01-01-2001)"
|
|
|
|
"\n======================"
|
2018-06-12 15:53:15 +02:00
|
|
|
+ dedent(
|
|
|
|
"""
|
2017-08-07 06:55:05 +02:00
|
|
|
section-b
|
|
|
|
---------
|
|
|
|
|
|
|
|
type-2
|
|
|
|
~~~~~~
|
|
|
|
|
|
|
|
- section-b type-2 (#1)
|
|
|
|
|
|
|
|
|
|
|
|
type-1
|
|
|
|
~~~~~~
|
|
|
|
|
|
|
|
- section-b type-1 (#1)
|
|
|
|
|
|
|
|
|
|
|
|
section-a
|
|
|
|
---------
|
|
|
|
|
|
|
|
type-2
|
|
|
|
~~~~~~
|
|
|
|
|
|
|
|
- section-a type-2 (#1)
|
|
|
|
|
|
|
|
|
|
|
|
type-1
|
|
|
|
~~~~~~
|
|
|
|
|
|
|
|
- section-a type-1 (#1)
|
|
|
|
|
2018-06-12 15:53:15 +02:00
|
|
|
"""
|
|
|
|
),
|
2016-12-07 11:31:03 +01:00
|
|
|
)
|
2017-05-31 01:13:22 +02:00
|
|
|
|
|
|
|
def test_no_confirmation(self):
|
|
|
|
runner = CliRunner()
|
|
|
|
|
|
|
|
with runner.isolated_filesystem():
|
2017-12-30 01:47:27 +01:00
|
|
|
setup_simple_project()
|
2018-06-12 15:53:15 +02:00
|
|
|
fragment_path1 = "foo/newsfragments/123.feature"
|
|
|
|
fragment_path2 = "foo/newsfragments/124.feature.rst"
|
|
|
|
with open(fragment_path1, "w") as f:
|
|
|
|
f.write("Adds levitation")
|
|
|
|
with open(fragment_path2, "w") as f:
|
|
|
|
f.write("Extends levitation")
|
2017-05-31 01:13:22 +02:00
|
|
|
|
|
|
|
call(["git", "init"])
|
|
|
|
call(["git", "config", "user.name", "user"])
|
|
|
|
call(["git", "config", "user.email", "user@example.com"])
|
|
|
|
call(["git", "add", "."])
|
|
|
|
call(["git", "commit", "-m", "Initial Commit"])
|
|
|
|
|
2018-06-12 15:53:15 +02:00
|
|
|
result = runner.invoke(_main, ["--date", "01-01-2001", "--yes"])
|
2017-05-31 01:13:22 +02:00
|
|
|
|
|
|
|
self.assertEqual(0, result.exit_code)
|
2018-06-12 15:53:15 +02:00
|
|
|
path = "NEWS.rst"
|
2017-05-31 01:13:22 +02:00
|
|
|
self.assertTrue(os.path.isfile(path))
|
2017-12-27 11:08:57 +01:00
|
|
|
self.assertFalse(os.path.isfile(fragment_path1))
|
|
|
|
self.assertFalse(os.path.isfile(fragment_path2))
|
2018-03-26 15:25:47 +02:00
|
|
|
|
2019-09-15 01:11:24 +02:00
|
|
|
def test_needs_config(self):
|
|
|
|
"""
|
|
|
|
Towncrier needs a configuration file.
|
|
|
|
"""
|
|
|
|
runner = CliRunner()
|
|
|
|
|
|
|
|
with runner.isolated_filesystem():
|
|
|
|
result = runner.invoke(_main, ["--draft"])
|
|
|
|
|
|
|
|
self.assertEqual(1, result.exit_code, result.output)
|
|
|
|
self.assertTrue(result.output.startswith("No configuration file found."))
|
|
|
|
|
2018-03-26 15:25:47 +02:00
|
|
|
def test_projectless_changelog(self):
|
|
|
|
"""In which a directory containing news files is built into a changelog
|
|
|
|
|
|
|
|
- without a Python project or version number. We override the
|
|
|
|
project title from the commandline.
|
|
|
|
"""
|
|
|
|
runner = CliRunner()
|
|
|
|
|
|
|
|
with runner.isolated_filesystem():
|
2018-06-12 15:53:15 +02:00
|
|
|
with open("pyproject.toml", "w") as f:
|
|
|
|
f.write("[tool.towncrier]\n" 'package = "foo"\n')
|
|
|
|
os.mkdir("foo")
|
|
|
|
os.mkdir("foo/newsfragments")
|
|
|
|
with open("foo/newsfragments/123.feature", "w") as f:
|
|
|
|
f.write("Adds levitation")
|
2018-03-26 15:25:47 +02:00
|
|
|
# Towncrier ignores .rst extension
|
2018-06-12 15:53:15 +02:00
|
|
|
with open("foo/newsfragments/124.feature.rst", "w") as f:
|
|
|
|
f.write("Extends levitation")
|
|
|
|
|
|
|
|
result = runner.invoke(
|
|
|
|
_main,
|
|
|
|
[
|
|
|
|
"--name",
|
|
|
|
"FooBarBaz",
|
|
|
|
"--version",
|
|
|
|
"7.8.9",
|
|
|
|
"--date",
|
|
|
|
"01-01-2001",
|
|
|
|
"--draft",
|
|
|
|
],
|
|
|
|
)
|
2018-03-26 15:25:47 +02:00
|
|
|
|
|
|
|
self.assertEqual(0, result.exit_code)
|
|
|
|
self.assertEqual(
|
|
|
|
result.output,
|
2018-06-12 15:53:15 +02:00
|
|
|
dedent(
|
|
|
|
"""
|
2018-03-28 15:40:06 +02:00
|
|
|
Loading template...
|
|
|
|
Finding news fragments...
|
|
|
|
Rendering news fragments...
|
|
|
|
Draft only -- nothing has been written.
|
|
|
|
What is seen below is what would be written.
|
|
|
|
|
|
|
|
FooBarBaz 7.8.9 (01-01-2001)
|
|
|
|
============================
|
|
|
|
|
|
|
|
Features
|
|
|
|
--------
|
|
|
|
|
|
|
|
- Adds levitation (#123)
|
|
|
|
- Extends levitation (#124)
|
|
|
|
|
2018-06-12 15:53:15 +02:00
|
|
|
"""
|
|
|
|
).lstrip(),
|
2018-03-26 15:25:47 +02:00
|
|
|
)
|
2018-04-23 09:53:07 +02:00
|
|
|
|
2020-12-07 04:50:58 +01:00
|
|
|
def test_version_in_config(self):
|
|
|
|
"""The calling towncrier with version defined in configfile.
|
|
|
|
|
|
|
|
Specifying a version in toml file will be helpful if version
|
|
|
|
is maintained by i.e. bumpversion and it's not a python project.
|
|
|
|
"""
|
|
|
|
runner = CliRunner()
|
|
|
|
|
|
|
|
with runner.isolated_filesystem():
|
|
|
|
with open("pyproject.toml", "w") as f:
|
|
|
|
f.write("[tool.towncrier]\n" 'version = "7.8.9"\n')
|
|
|
|
os.mkdir("newsfragments")
|
|
|
|
with open("newsfragments/123.feature", "w") as f:
|
|
|
|
f.write("Adds levitation")
|
|
|
|
|
2022-04-11 15:43:12 +02:00
|
|
|
result = runner.invoke(_main, ["--date", "01-01-2001", "--draft"])
|
2020-12-07 04:50:58 +01:00
|
|
|
|
|
|
|
self.assertEqual(0, result.exit_code, result.output)
|
|
|
|
self.assertEqual(
|
|
|
|
result.output,
|
|
|
|
dedent(
|
|
|
|
"""
|
|
|
|
Loading template...
|
|
|
|
Finding news fragments...
|
|
|
|
Rendering news fragments...
|
|
|
|
Draft only -- nothing has been written.
|
|
|
|
What is seen below is what would be written.
|
|
|
|
|
|
|
|
7.8.9 (01-01-2001)
|
|
|
|
==================
|
|
|
|
|
|
|
|
Features
|
|
|
|
--------
|
|
|
|
|
|
|
|
- Adds levitation (#123)
|
|
|
|
|
|
|
|
"""
|
|
|
|
).lstrip(),
|
|
|
|
)
|
|
|
|
|
|
|
|
def test_project_name_in_config(self):
|
|
|
|
"""The calling towncrier with project name defined in configfile.
|
|
|
|
|
|
|
|
Specifying a project name in toml file will be helpful to keep the
|
|
|
|
project name consistent as part of the towncrier configuration, not call.
|
|
|
|
"""
|
|
|
|
runner = CliRunner()
|
|
|
|
|
|
|
|
with runner.isolated_filesystem():
|
|
|
|
with open("pyproject.toml", "w") as f:
|
|
|
|
f.write("[tool.towncrier]\n" 'name = "ImGoProject"\n')
|
|
|
|
os.mkdir("newsfragments")
|
|
|
|
with open("newsfragments/123.feature", "w") as f:
|
|
|
|
f.write("Adds levitation")
|
|
|
|
|
|
|
|
result = runner.invoke(
|
|
|
|
_main, ["--version", "7.8.9", "--date", "01-01-2001", "--draft"]
|
|
|
|
)
|
|
|
|
|
|
|
|
self.assertEqual(0, result.exit_code, result.output)
|
|
|
|
self.assertEqual(
|
|
|
|
result.output,
|
|
|
|
dedent(
|
|
|
|
"""
|
|
|
|
Loading template...
|
|
|
|
Finding news fragments...
|
|
|
|
Rendering news fragments...
|
|
|
|
Draft only -- nothing has been written.
|
|
|
|
What is seen below is what would be written.
|
|
|
|
|
|
|
|
ImGoProject 7.8.9 (01-01-2001)
|
|
|
|
==============================
|
|
|
|
|
|
|
|
Features
|
|
|
|
--------
|
|
|
|
|
|
|
|
- Adds levitation (#123)
|
|
|
|
|
|
|
|
"""
|
|
|
|
).lstrip(),
|
|
|
|
)
|
|
|
|
|
2018-04-23 09:53:07 +02:00
|
|
|
def test_no_package_changelog(self):
|
|
|
|
"""The calling towncrier with any package argument.
|
|
|
|
|
|
|
|
Specifying a package in the toml file or the command line
|
|
|
|
should not always be needed:
|
|
|
|
- we can set the version number on the command line,
|
|
|
|
so we do not need the package for that.
|
|
|
|
- we don't need to include the package in the changelog header.
|
|
|
|
"""
|
|
|
|
runner = CliRunner()
|
|
|
|
|
|
|
|
with runner.isolated_filesystem():
|
2019-09-15 01:11:24 +02:00
|
|
|
with open("pyproject.toml", "w") as f:
|
|
|
|
f.write("[tool.towncrier]")
|
2018-06-12 15:53:15 +02:00
|
|
|
os.mkdir("newsfragments")
|
|
|
|
with open("newsfragments/123.feature", "w") as f:
|
|
|
|
f.write("Adds levitation")
|
2018-04-23 09:53:07 +02:00
|
|
|
|
2018-06-12 15:53:15 +02:00
|
|
|
result = runner.invoke(
|
|
|
|
_main, ["--version", "7.8.9", "--date", "01-01-2001", "--draft"]
|
|
|
|
)
|
2018-04-23 09:53:07 +02:00
|
|
|
|
2019-09-15 01:11:24 +02:00
|
|
|
self.assertEqual(0, result.exit_code, result.output)
|
2018-04-23 09:53:07 +02:00
|
|
|
self.assertEqual(
|
|
|
|
result.output,
|
2018-06-12 15:53:15 +02:00
|
|
|
dedent(
|
|
|
|
"""
|
2018-04-23 09:53:07 +02:00
|
|
|
Loading template...
|
|
|
|
Finding news fragments...
|
|
|
|
Rendering news fragments...
|
|
|
|
Draft only -- nothing has been written.
|
|
|
|
What is seen below is what would be written.
|
|
|
|
|
|
|
|
7.8.9 (01-01-2001)
|
|
|
|
==================
|
|
|
|
|
|
|
|
Features
|
|
|
|
--------
|
|
|
|
|
|
|
|
- Adds levitation (#123)
|
|
|
|
|
2018-06-12 15:53:15 +02:00
|
|
|
"""
|
|
|
|
).lstrip(),
|
2018-04-23 09:53:07 +02:00
|
|
|
)
|
2019-09-01 10:33:33 +02:00
|
|
|
|
|
|
|
def test_single_file(self):
|
|
|
|
"""
|
|
|
|
Enabling the single file mode will write the changelog to a filename
|
|
|
|
that is formatted from the filename args.
|
|
|
|
"""
|
|
|
|
runner = CliRunner()
|
|
|
|
|
|
|
|
with runner.isolated_filesystem():
|
|
|
|
with open("pyproject.toml", "w") as f:
|
|
|
|
f.write(
|
|
|
|
'[tool.towncrier]\n single_file=true\n filename="{version}-notes.rst"'
|
|
|
|
)
|
|
|
|
os.mkdir("newsfragments")
|
|
|
|
with open("newsfragments/123.feature", "w") as f:
|
|
|
|
f.write("Adds levitation")
|
|
|
|
|
|
|
|
result = runner.invoke(
|
|
|
|
_main,
|
|
|
|
[
|
|
|
|
"--version",
|
|
|
|
"7.8.9",
|
|
|
|
"--name",
|
|
|
|
"foo",
|
|
|
|
"--date",
|
|
|
|
"01-01-2001",
|
|
|
|
"--yes",
|
|
|
|
],
|
|
|
|
)
|
|
|
|
|
|
|
|
self.assertEqual(0, result.exit_code, result.output)
|
|
|
|
self.assertTrue(os.path.exists("7.8.9-notes.rst"), os.listdir("."))
|
2022-04-09 09:51:07 +02:00
|
|
|
with open("7.8.9-notes.rst") as f:
|
2019-09-01 10:33:33 +02:00
|
|
|
output = f.read()
|
|
|
|
|
|
|
|
self.assertEqual(
|
|
|
|
output,
|
|
|
|
dedent(
|
|
|
|
"""
|
|
|
|
foo 7.8.9 (01-01-2001)
|
|
|
|
======================
|
|
|
|
|
|
|
|
Features
|
|
|
|
--------
|
|
|
|
|
|
|
|
- Adds levitation (#123)
|
|
|
|
"""
|
|
|
|
).lstrip(),
|
|
|
|
)
|
|
|
|
|
2020-12-19 17:02:44 +01:00
|
|
|
def test_singlefile_errors_and_explains_cleanly(self):
|
|
|
|
"""
|
|
|
|
Failure to find the configuration file results in a clean explanation
|
|
|
|
without a traceback.
|
|
|
|
"""
|
|
|
|
runner = CliRunner()
|
|
|
|
|
|
|
|
with runner.isolated_filesystem():
|
|
|
|
with open("pyproject.toml", "w") as f:
|
|
|
|
f.write('[tool.towncrier]\n singlefile="fail!"\n')
|
|
|
|
|
|
|
|
result = runner.invoke(_main)
|
|
|
|
|
|
|
|
self.assertEqual(1, result.exit_code)
|
|
|
|
self.assertEqual(
|
2022-04-11 15:43:12 +02:00
|
|
|
"`singlefile` is not a valid option. Did you mean `single_file`?\n",
|
2020-12-19 17:02:44 +01:00
|
|
|
result.output,
|
|
|
|
)
|
|
|
|
|
2019-09-01 10:33:33 +02:00
|
|
|
def test_single_file_false(self):
|
|
|
|
"""
|
|
|
|
If formatting arguments are given in the filename arg and single_file is
|
|
|
|
false, the filename will not be formatted.
|
|
|
|
"""
|
|
|
|
runner = CliRunner()
|
|
|
|
|
|
|
|
with runner.isolated_filesystem():
|
|
|
|
with open("pyproject.toml", "w") as f:
|
|
|
|
f.write(
|
|
|
|
'[tool.towncrier]\n single_file=false\n filename="{version}-notes.rst"'
|
|
|
|
)
|
|
|
|
os.mkdir("newsfragments")
|
|
|
|
with open("newsfragments/123.feature", "w") as f:
|
|
|
|
f.write("Adds levitation")
|
|
|
|
|
|
|
|
result = runner.invoke(
|
|
|
|
_main,
|
|
|
|
[
|
|
|
|
"--version",
|
|
|
|
"7.8.9",
|
|
|
|
"--name",
|
|
|
|
"foo",
|
|
|
|
"--date",
|
|
|
|
"01-01-2001",
|
|
|
|
"--yes",
|
|
|
|
],
|
|
|
|
)
|
|
|
|
|
|
|
|
self.assertEqual(0, result.exit_code, result.output)
|
|
|
|
self.assertTrue(os.path.exists("{version}-notes.rst"), os.listdir("."))
|
|
|
|
self.assertFalse(os.path.exists("7.8.9-notes.rst"), os.listdir("."))
|
2022-04-09 09:51:07 +02:00
|
|
|
with open("{version}-notes.rst") as f:
|
2019-09-01 10:33:33 +02:00
|
|
|
output = f.read()
|
|
|
|
|
|
|
|
self.assertEqual(
|
|
|
|
output,
|
|
|
|
dedent(
|
|
|
|
"""
|
|
|
|
foo 7.8.9 (01-01-2001)
|
|
|
|
======================
|
|
|
|
|
|
|
|
Features
|
|
|
|
--------
|
|
|
|
|
|
|
|
- Adds levitation (#123)
|
|
|
|
"""
|
|
|
|
).lstrip(),
|
|
|
|
)
|
2019-09-15 01:11:24 +02:00
|
|
|
|
|
|
|
def test_bullet_points_false(self):
|
|
|
|
"""
|
|
|
|
When all_bullets is false, subsequent lines are not indented.
|
2022-04-10 17:36:14 +02:00
|
|
|
|
|
|
|
The automatic ticket number inserted by towcier will allign with the
|
|
|
|
manual bullet.
|
2019-09-15 01:11:24 +02:00
|
|
|
"""
|
|
|
|
runner = CliRunner()
|
|
|
|
|
|
|
|
with runner.isolated_filesystem():
|
|
|
|
with open("pyproject.toml", "w") as f:
|
|
|
|
f.write(
|
2022-04-11 15:43:12 +02:00
|
|
|
"[tool.towncrier]\n"
|
2019-09-15 01:11:24 +02:00
|
|
|
'template="towncrier:single-file-no-bullets"\n'
|
2022-04-11 15:43:12 +02:00
|
|
|
"all_bullets=false"
|
2019-09-15 01:11:24 +02:00
|
|
|
)
|
|
|
|
os.mkdir("newsfragments")
|
|
|
|
with open("newsfragments/123.feature", "w") as f:
|
2022-04-11 15:43:12 +02:00
|
|
|
f.write("wow!\n" "~~~~\n" "\n" "No indentation at all.")
|
2022-04-10 17:36:14 +02:00
|
|
|
with open("newsfragments/124.bugfix", "w") as f:
|
2022-04-11 15:43:12 +02:00
|
|
|
f.write("#. Numbered bullet list.")
|
2022-04-10 17:36:14 +02:00
|
|
|
with open("newsfragments/125.removal", "w") as f:
|
2022-04-11 15:43:12 +02:00
|
|
|
f.write("- Hyphen based bullet list.")
|
2022-04-10 17:36:14 +02:00
|
|
|
with open("newsfragments/126.doc", "w") as f:
|
2022-04-11 15:43:12 +02:00
|
|
|
f.write("* Asterisk based bullet list.")
|
2019-09-15 01:11:24 +02:00
|
|
|
|
|
|
|
result = runner.invoke(
|
|
|
|
_main,
|
|
|
|
[
|
|
|
|
"--version",
|
|
|
|
"7.8.9",
|
|
|
|
"--name",
|
|
|
|
"foo",
|
|
|
|
"--date",
|
|
|
|
"01-01-2001",
|
|
|
|
"--yes",
|
|
|
|
],
|
|
|
|
)
|
|
|
|
|
|
|
|
self.assertEqual(0, result.exit_code, result.output)
|
2022-04-11 07:13:38 +02:00
|
|
|
with open("NEWS.rst") as f:
|
2019-09-15 01:11:24 +02:00
|
|
|
output = f.read()
|
|
|
|
|
|
|
|
self.assertEqual(
|
|
|
|
output,
|
2022-04-10 17:36:14 +02:00
|
|
|
"""
|
|
|
|
foo 7.8.9 (01-01-2001)
|
|
|
|
======================
|
2019-09-15 01:11:24 +02:00
|
|
|
|
2022-04-10 17:36:14 +02:00
|
|
|
Features
|
|
|
|
--------
|
2019-09-15 01:11:24 +02:00
|
|
|
|
2022-04-10 17:36:14 +02:00
|
|
|
wow!
|
|
|
|
~~~~
|
2019-09-15 01:11:24 +02:00
|
|
|
|
2022-04-10 17:36:14 +02:00
|
|
|
No indentation at all.
|
|
|
|
(#123)
|
|
|
|
|
|
|
|
|
|
|
|
Bugfixes
|
|
|
|
--------
|
|
|
|
|
|
|
|
#. Numbered bullet list.
|
|
|
|
(#124)
|
|
|
|
|
|
|
|
|
|
|
|
Improved Documentation
|
|
|
|
----------------------
|
|
|
|
|
|
|
|
* Asterisk based bullet list.
|
|
|
|
(#126)
|
|
|
|
|
|
|
|
|
|
|
|
Deprecations and Removals
|
|
|
|
-------------------------
|
|
|
|
|
|
|
|
- Hyphen based bullet list.
|
|
|
|
(#125)
|
|
|
|
""".lstrip(),
|
2019-09-15 01:11:24 +02:00
|
|
|
)
|
2020-12-15 04:48:57 +01:00
|
|
|
|
2020-12-20 20:43:07 +01:00
|
|
|
def test_title_format_custom(self):
|
|
|
|
"""
|
|
|
|
A non-empty title format adds the specified title.
|
|
|
|
"""
|
|
|
|
runner = CliRunner()
|
|
|
|
|
|
|
|
with runner.isolated_filesystem():
|
|
|
|
with open("pyproject.toml", "w") as f:
|
2022-04-11 15:43:12 +02:00
|
|
|
f.write(
|
|
|
|
dedent(
|
|
|
|
"""\
|
2020-12-20 20:43:07 +01:00
|
|
|
[tool.towncrier]
|
|
|
|
package = "foo"
|
|
|
|
title_format = "[{project_date}] CUSTOM RELEASE for {name} version {version}"
|
2022-04-11 15:43:12 +02:00
|
|
|
"""
|
|
|
|
)
|
|
|
|
)
|
2020-12-20 20:43:07 +01:00
|
|
|
os.mkdir("foo")
|
|
|
|
os.mkdir("foo/newsfragments")
|
|
|
|
with open("foo/newsfragments/123.feature", "w") as f:
|
|
|
|
f.write("Adds levitation")
|
|
|
|
# Towncrier ignores .rst extension
|
|
|
|
with open("foo/newsfragments/124.feature.rst", "w") as f:
|
|
|
|
f.write("Extends levitation")
|
|
|
|
|
|
|
|
result = runner.invoke(
|
|
|
|
_main,
|
|
|
|
[
|
|
|
|
"--name",
|
|
|
|
"FooBarBaz",
|
|
|
|
"--version",
|
|
|
|
"7.8.9",
|
|
|
|
"--date",
|
|
|
|
"20-01-2001",
|
|
|
|
"--draft",
|
|
|
|
],
|
|
|
|
)
|
|
|
|
|
2022-04-11 15:43:12 +02:00
|
|
|
expected_output = dedent(
|
|
|
|
"""\
|
2020-12-20 20:43:07 +01:00
|
|
|
Loading template...
|
|
|
|
Finding news fragments...
|
|
|
|
Rendering news fragments...
|
|
|
|
Draft only -- nothing has been written.
|
|
|
|
What is seen below is what would be written.
|
|
|
|
|
|
|
|
[20-01-2001] CUSTOM RELEASE for FooBarBaz version 7.8.9
|
|
|
|
=======================================================
|
|
|
|
|
|
|
|
Features
|
|
|
|
--------
|
|
|
|
|
|
|
|
- Adds levitation (#123)
|
|
|
|
- Extends levitation (#124)
|
|
|
|
|
2022-04-11 15:43:12 +02:00
|
|
|
"""
|
|
|
|
)
|
2020-12-20 20:43:07 +01:00
|
|
|
|
|
|
|
self.assertEqual(0, result.exit_code)
|
|
|
|
self.assertEqual(expected_output, result.output)
|
|
|
|
|
|
|
|
def test_title_format_false(self):
|
|
|
|
"""
|
|
|
|
Setting the title format to false disables the explicit title. This
|
|
|
|
would be used, for example, when the template creates the title itself.
|
|
|
|
"""
|
|
|
|
runner = CliRunner()
|
|
|
|
|
|
|
|
with runner.isolated_filesystem():
|
|
|
|
with open("pyproject.toml", "w") as f:
|
2022-04-11 15:43:12 +02:00
|
|
|
f.write(
|
|
|
|
dedent(
|
|
|
|
"""\
|
2020-12-20 20:43:07 +01:00
|
|
|
[tool.towncrier]
|
|
|
|
package = "foo"
|
|
|
|
title_format = false
|
2020-12-21 19:56:10 +01:00
|
|
|
template = "template.rst"
|
2022-04-11 15:43:12 +02:00
|
|
|
"""
|
|
|
|
)
|
|
|
|
)
|
2020-12-20 20:43:07 +01:00
|
|
|
os.mkdir("foo")
|
|
|
|
os.mkdir("foo/newsfragments")
|
2020-12-21 19:56:10 +01:00
|
|
|
with open("template.rst", "w") as f:
|
2022-04-11 15:43:12 +02:00
|
|
|
f.write(
|
|
|
|
dedent(
|
|
|
|
"""\
|
2020-12-21 19:56:10 +01:00
|
|
|
Here's a hardcoded title added by the template
|
|
|
|
==============================================
|
|
|
|
{% for section in sections %}
|
|
|
|
{% set underline = "-" %}
|
|
|
|
{% for category, val in definitions.items() if category in sections[section] %}
|
|
|
|
|
|
|
|
{% for text, values in sections[section][category]|dictsort(by='value') %}
|
|
|
|
- {{ text }}
|
|
|
|
|
|
|
|
{% endfor %}
|
|
|
|
{% endfor %}
|
|
|
|
{% endfor %}
|
2022-04-11 15:43:12 +02:00
|
|
|
"""
|
|
|
|
)
|
|
|
|
)
|
2020-12-20 20:43:07 +01:00
|
|
|
|
|
|
|
result = runner.invoke(
|
|
|
|
_main,
|
|
|
|
[
|
|
|
|
"--name",
|
|
|
|
"FooBarBaz",
|
|
|
|
"--version",
|
|
|
|
"7.8.9",
|
|
|
|
"--date",
|
|
|
|
"20-01-2001",
|
|
|
|
"--draft",
|
|
|
|
],
|
|
|
|
)
|
|
|
|
|
2022-04-11 15:43:12 +02:00
|
|
|
expected_output = dedent(
|
|
|
|
"""\
|
2020-12-20 20:43:07 +01:00
|
|
|
Loading template...
|
|
|
|
Finding news fragments...
|
|
|
|
Rendering news fragments...
|
|
|
|
Draft only -- nothing has been written.
|
|
|
|
What is seen below is what would be written.
|
|
|
|
|
2020-12-21 19:56:10 +01:00
|
|
|
Here's a hardcoded title added by the template
|
|
|
|
==============================================
|
2020-12-20 20:43:07 +01:00
|
|
|
|
2022-04-11 15:43:12 +02:00
|
|
|
"""
|
|
|
|
)
|
2020-12-20 20:43:07 +01:00
|
|
|
|
|
|
|
self.assertEqual(0, result.exit_code)
|
|
|
|
self.assertEqual(expected_output, result.output)
|
2020-12-20 22:23:17 +01:00
|
|
|
|
|
|
|
def test_start_string(self):
|
|
|
|
"""
|
|
|
|
The `start_string` configuration is used to detect the starting point
|
|
|
|
for inserting the generated release notes. A newline is automatically
|
|
|
|
added to the configured value.
|
|
|
|
"""
|
|
|
|
runner = CliRunner()
|
|
|
|
|
|
|
|
with runner.isolated_filesystem():
|
|
|
|
with open("pyproject.toml", "w") as f:
|
2022-04-11 15:43:12 +02:00
|
|
|
f.write(
|
|
|
|
dedent(
|
|
|
|
"""\
|
2020-12-20 22:23:17 +01:00
|
|
|
[tool.towncrier]
|
|
|
|
start_string="Release notes start marker"
|
2022-04-11 15:43:12 +02:00
|
|
|
"""
|
|
|
|
)
|
|
|
|
)
|
2020-12-20 22:23:17 +01:00
|
|
|
os.mkdir("newsfragments")
|
|
|
|
with open("newsfragments/123.feature", "w") as f:
|
|
|
|
f.write("Adds levitation")
|
|
|
|
with open("NEWS.rst", "w") as f:
|
|
|
|
f.write("a line\n\nanother\n\nRelease notes start marker\n")
|
|
|
|
|
|
|
|
result = runner.invoke(
|
|
|
|
_main,
|
|
|
|
[
|
|
|
|
"--version",
|
|
|
|
"7.8.9",
|
|
|
|
"--name",
|
|
|
|
"foo",
|
|
|
|
"--date",
|
|
|
|
"01-01-2001",
|
|
|
|
"--yes",
|
|
|
|
],
|
|
|
|
)
|
|
|
|
|
|
|
|
self.assertEqual(0, result.exit_code, result.output)
|
|
|
|
self.assertTrue(os.path.exists("NEWS.rst"), os.listdir("."))
|
2022-04-09 09:51:07 +02:00
|
|
|
with open("NEWS.rst") as f:
|
2020-12-20 22:23:17 +01:00
|
|
|
output = f.read()
|
|
|
|
|
2022-04-11 15:43:12 +02:00
|
|
|
expected_output = dedent(
|
|
|
|
"""\
|
2020-12-20 22:23:17 +01:00
|
|
|
a line
|
|
|
|
|
|
|
|
another
|
|
|
|
|
|
|
|
Release notes start marker
|
|
|
|
foo 7.8.9 (01-01-2001)
|
|
|
|
======================
|
|
|
|
|
|
|
|
Features
|
|
|
|
--------
|
|
|
|
|
|
|
|
- Adds levitation (#123)
|
|
|
|
|
|
|
|
|
2022-04-11 15:43:12 +02:00
|
|
|
"""
|
|
|
|
)
|
2020-12-20 22:23:17 +01:00
|
|
|
|
|
|
|
self.assertEqual(expected_output, output)
|
2020-12-20 22:44:19 +01:00
|
|
|
|
2020-12-16 04:51:29 +01:00
|
|
|
def test_with_topline_and_template_and_draft(self):
|
|
|
|
"""
|
|
|
|
Spacing is proper when drafting with a topline and a template.
|
|
|
|
"""
|
2020-12-15 04:48:57 +01:00
|
|
|
runner = CliRunner()
|
|
|
|
|
|
|
|
with runner.isolated_filesystem():
|
|
|
|
with open("pyproject.toml", "w") as f:
|
|