Merge branch 'develop' into folder-journal-file-read-1692

This commit is contained in:
Micah Jerome Ellison 2023-03-06 11:19:52 -08:00
commit a21f3e19df
17 changed files with 175 additions and 140 deletions

View file

@ -6,7 +6,9 @@
**Implemented enhancements:**
- Display locations of config file and documentation after initial install [\#1694](https://github.com/jrnl-org/jrnl/issues/1694)
- Don't import cryptography package if not needed [\#1521](https://github.com/jrnl-org/jrnl/issues/1521)
- Add message with config location and docs location when installation is complete [\#1695](https://github.com/jrnl-org/jrnl/pull/1695) ([micahellison](https://github.com/micahellison))
- Prompt to include colors in config when first running jrnl [\#1687](https://github.com/jrnl-org/jrnl/pull/1687) ([micahellison](https://github.com/micahellison))
- Search for entries with no tags or stars with `-not -starred` and `-not -tagged` [\#1663](https://github.com/jrnl-org/jrnl/pull/1663) ([cjcon90](https://github.com/cjcon90))
- Refactor flow for easier access to some files \(avoid things like `jrnl.Journal.Journal` and `jrnl.jrnl` co-existing\) [\#1662](https://github.com/jrnl-org/jrnl/pull/1662) ([wren](https://github.com/wren))
@ -30,6 +32,7 @@
**Build:**
- Support pytest-bdd 6 [\#1534](https://github.com/jrnl-org/jrnl/issues/1534)
- Update copyright notices for 2023 [\#1660](https://github.com/jrnl-org/jrnl/pull/1660) ([wren](https://github.com/wren))
- Fix bug where changelog is always slightly out of date on release tags [\#1631](https://github.com/jrnl-org/jrnl/pull/1631) ([wren](https://github.com/wren))
- Add `simplify` plugin to linting checks [\#1630](https://github.com/jrnl-org/jrnl/pull/1630) ([wren](https://github.com/wren))
@ -39,9 +42,13 @@
- Document template extension behavior [\#1677](https://github.com/jrnl-org/jrnl/issues/1677)
- Visual Studio Code may store unencrypted temporary files [\#1675](https://github.com/jrnl-org/jrnl/issues/1675)
- Document `-tagged`, `-not -tagged`, and `-not -starred` [\#1668](https://github.com/jrnl-org/jrnl/issues/1668)
- Documentation Change [\#1651](https://github.com/jrnl-org/jrnl/issues/1651)
- Update console examples on jrnl.sh front page [\#1622](https://github.com/jrnl-org/jrnl/issues/1622)
- Update documentation front page text [\#1698](https://github.com/jrnl-org/jrnl/pull/1698) ([micahellison](https://github.com/micahellison))
- Support mkdocs 1.4.2 and fix its missing breadcrumb [\#1691](https://github.com/jrnl-org/jrnl/pull/1691) ([micahellison](https://github.com/micahellison))
- Document temporary file extension behavior when using template [\#1686](https://github.com/jrnl-org/jrnl/pull/1686) ([micahellison](https://github.com/micahellison))
- Document `-tagged`, `-not -tagged`, and `-not -starred` [\#1684](https://github.com/jrnl-org/jrnl/pull/1684) ([micahellison](https://github.com/micahellison))
- Update documentation about privacy and security in VSCode [\#1680](https://github.com/jrnl-org/jrnl/pull/1680) ([giuseppedandrea](https://github.com/giuseppedandrea))
- Update documentation on temporary files naming [\#1673](https://github.com/jrnl-org/jrnl/pull/1673) ([giuseppedandrea](https://github.com/giuseppedandrea))
- Update docs to include time and title in arguments with `--edit` [\#1657](https://github.com/jrnl-org/jrnl/pull/1657) ([pconrad-fb](https://github.com/pconrad-fb))

View file

@ -76,8 +76,11 @@ entries, such as `yesterday`, `today`, `Tuesday`, or `2021-08-01`.
| -contains TEXT | Show entries containing specific text (put quotes around text with spaces) |
| -and | Show only entries that match all conditions, like saying "x AND y" (default: OR) |
| -starred | Show only starred entries (marked with *) |
| -tagged | Show only tagged entries (marked with the [configured tagsymbols](reference-config-file.md#tagsymbols)) |
| -n [NUMBER] | Show a maximum of NUMBER entries (note: '-n 3' and '-3' have the same effect) |
| -not [TAG] | Exclude entries with this tag |
| -not -starred | Exclude entries that are starred |
| -not -tagged | Exclude entries that are tagged |
## Searching Options
These help you do various tasks with the selected entries from your search.

View file

@ -76,22 +76,22 @@ License: https://www.gnu.org/licenses/gpl-3.0.html
<section>
<i class="icon future"></i>
<h3>Future-proof.</h3>
<p>Your journals are stored in plain-text files that will still be readable in 50 years when all your fancy iPad apps will have gone the way of the Dodo.</p>
<p>Your journals are stored in plain-text files that will still be readable in 50 years when your fancy proprietary apps will have gone the way of the dodo.</p>
</section>
<section>
<i class="icon secure"></i>
<h3>Secure.</h3>
<p>Encrypt your journals with industry-strength AES encryption. The NSA won't be able to read your dirty secrets.</p>
<p>Encrypt your journals with industry-strength AES encryption. Nobody will be able to read your dirty secrets&mdash;not even you, if you lose your password!</p>
</section>
<section>
<i class="icon sync"></i>
<h3>Accessible anywhere.</h3>
<p>Sync your journals with Dropbox and capture your thoughts where ever you are.</p>
<p>Sync your journal files with other tools like Dropbox to capture your thoughts wherever you are.</p>
</section>
<section>
<i class="icon github"></i>
<h3>Free &amp; Open Source.</h3>
<p>jrnl is made by a bunch of really friendly and remarkably attractive people. Maybe even <a href="https://www.github.com/jrnl-org/jrnl" title="Fork jrnl on GitHub">you</a>?</p>
<p>jrnl is made by a bunch of really friendly and remarkably amazing people. Maybe even <a href="https://www.github.com/jrnl-org/jrnl" title="Fork jrnl on GitHub">you</a>?</p>
</section>
<section>
<i class="icon folders"></i>
@ -107,17 +107,17 @@ License: https://www.gnu.org/licenses/gpl-3.0.html
<script>
new Typed("#typed", {
strings: [
"jrnl today: Started writing my memoirs. On the command line. Like a boss.",
"jrnl Started writing my memoirs on the command line. 🎉🔥💻🔥🎉",
"jrnl yesterday 2pm: used jrnl to keep track of accomplished tasks. The done.txt for my todo.txt",
"jrnl <b>-from</b> 2009 <b>-until</b> may<br /><i>`(Displays all entries from January 2009 to last may)`</i>",
"jrnl A day on the beach with @beth and @frank. Taggidy-tag-tag.",
"jrnl <b>--tags</b><br /><i>`@idea 7<br />@beth 5</i>`",
"jrnl <b>-from</b> 2019 <b>-until</b> may<br /><i>`(displays all entries from January 2019 to last May)`</i>",
"jrnl A day on the beach with @beth and @frank. Tagging them so I can easily look this up later.",
"jrnl <b>--tags</b><br /><i>`@frank 7<br />@beth 5</i>`",
"jrnl <b>--format</b> json<br /><i>`(Outputs your entire journal as json)</i>`",
"jrnl <b>--encrypt</b><br /><i>`(AES encryption. Crack this, NSA)</i>`"
"jrnl <b>--encrypt</b><br /><i>`(AES encryption. Don't lose your password!)</i>`"
],
typeSpeed: 35,
backSpeed: 15,
backDelay: 2000,
typeSpeed: 20, // less is faster
backSpeed: 10,
backDelay: 2500,
loop: true,
showCursor: false
});

View file

@ -154,6 +154,15 @@ def install() -> dict:
default_config["colors"] = get_default_colors()
save_config(default_config)
print_msg(
Message(
MsgText.InstallComplete,
MsgStyle.NORMAL,
params={"config_path": get_config_path()},
)
)
return default_config

View file

@ -263,7 +263,9 @@ class Journal:
start_date = time.parse(start_date)
# If strict mode is on, all tags have to be present in entry
has_tags = self.search_tags.issubset if strict else self.search_tags.intersection
has_tags = (
self.search_tags.issubset if strict else self.search_tags.intersection
)
def excluded(tags):
return 0 < len([tag for tag in tags if tag in excluded_tags])

View file

@ -28,6 +28,11 @@ class MsgText(Enum):
AllDoneUpgrade = "We're all done here and you can start enjoying jrnl 2"
InstallComplete = """
jrnl configuration created at {config_path}
For advanced features, read the docs at https://jrnl.sh
"""
# --- Prompts --- #
InstallJournalPathQuestion = """
Path to your journal file (leave blank for {default_journal_path}):

50
poetry.lock generated
View file

@ -345,14 +345,6 @@ python-dateutil = ">=2.8.1"
[package.extras]
dev = ["flake8", "markdown", "twine", "wheel"]
[[package]]
name = "glob2"
version = "0.7"
description = "Version of the glob module that can capture patterns and supports recursive wildcards"
category = "dev"
optional = false
python-versions = "*"
[[package]]
name = "idna"
version = "3.4"
@ -819,14 +811,6 @@ python-versions = "*"
[package.extras]
tests = ["pytest"]
[[package]]
name = "py"
version = "1.11.0"
description = "library with cross-python path, ini-parsing, io, code, log facilities"
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
[[package]]
name = "pycodestyle"
version = "2.8.0"
@ -900,19 +884,18 @@ testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.
[[package]]
name = "pytest-bdd"
version = "5.0.0"
version = "6.1.1"
description = "BDD for pytest"
category = "dev"
optional = false
python-versions = ">=3.6"
python-versions = ">=3.7,<4.0"
[package.dependencies]
glob2 = "*"
Mako = "*"
parse = "*"
parse-type = "*"
py = "*"
pytest = ">=4.3"
pytest = ">=6.2.0"
typing-extensions = "*"
[[package]]
name = "pytest-clarity"
@ -1153,6 +1136,14 @@ python-versions = ">=3.7"
docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"]
test = ["argcomplete (>=2.0)", "pre-commit", "pytest", "pytest-mock"]
[[package]]
name = "typing-extensions"
version = "4.5.0"
description = "Backported and Experimental Type Hints for Python 3.7+"
category = "dev"
optional = false
python-versions = ">=3.7"
[[package]]
name = "tzdata"
version = "2022.7"
@ -1249,7 +1240,7 @@ testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools"
[metadata]
lock-version = "1.1"
python-versions = ">=3.10.0, <3.13"
content-hash = "ef292b09387c666923e871b66f975c2d44f646ae478dbaf62e295fbb84d7aca1"
content-hash = "dfc32ee61025dae6033987a8ff8290d4c2a34197502b8030cef02db58b86baf1"
[metadata.files]
ansiwrap = [
@ -1564,9 +1555,6 @@ ghp-import = [
{file = "ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343"},
{file = "ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619"},
]
glob2 = [
{file = "glob2-0.7.tar.gz", hash = "sha256:85c3dbd07c8aa26d63d7aacee34fa86e9a91a3873bc30bf62ec46e531f92ab8c"},
]
idna = [
{file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"},
{file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"},
@ -1766,10 +1754,6 @@ pure-eval = [
{file = "pure_eval-0.2.2-py3-none-any.whl", hash = "sha256:01eaab343580944bc56080ebe0a674b39ec44a945e6d09ba7db3cb8cec289350"},
{file = "pure_eval-0.2.2.tar.gz", hash = "sha256:2b45320af6dfaa1750f543d714b6d1c520a1688dec6fd24d339063ce0aaa9ac3"},
]
py = [
{file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"},
{file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"},
]
pycodestyle = [
{file = "pycodestyle-2.8.0-py2.py3-none-any.whl", hash = "sha256:720f8b39dde8b293825e7ff02c475f3077124006db4f440dcbc9a20b76548a20"},
{file = "pycodestyle-2.8.0.tar.gz", hash = "sha256:eddd5847ef438ea1c7870ca7eb78a9d47ce0cdb4851a5523949f2601d0cbbe7f"},
@ -1795,8 +1779,8 @@ pytest = [
{file = "pytest-7.2.1.tar.gz", hash = "sha256:d45e0952f3727241918b8fd0f376f5ff6b301cc0777c6f9a556935c92d8a7d42"},
]
pytest-bdd = [
{file = "pytest-bdd-5.0.0.tar.gz", hash = "sha256:fab7093ed3d5e51ee0c68de093c90e4f40de345bd9a54a188b2991ce2a2a39cf"},
{file = "pytest_bdd-5.0.0-py3-none-any.whl", hash = "sha256:c7cf12209606421f61f36b5dc63beccd0c82d29446c0592cf68af2dad0a9761d"},
{file = "pytest_bdd-6.1.1-py3-none-any.whl", hash = "sha256:57eba5878d77036f356a85fb1d108cb061d8af4fb4d032b1a424fa9abe9e498b"},
{file = "pytest_bdd-6.1.1.tar.gz", hash = "sha256:138af3592bcce5d4684b0d690777cf199b39ce45d423ca28086047ffe6111010"},
]
pytest-clarity = [
{file = "pytest-clarity-1.0.1.tar.gz", hash = "sha256:505fe345fad4fe11c6a4187fe683f2c7c52c077caa1e135f3e483fe112db7772"},
@ -1947,6 +1931,10 @@ traitlets = [
{file = "traitlets-5.9.0-py3-none-any.whl", hash = "sha256:9e6ec080259b9a5940c797d58b613b5e31441c2257b87c2e795c5228ae80d2d8"},
{file = "traitlets-5.9.0.tar.gz", hash = "sha256:f6cde21a9c68cf756af02035f72d5a723bf607e862e7be33ece505abf4a3bad9"},
]
typing-extensions = [
{file = "typing_extensions-4.5.0-py3-none-any.whl", hash = "sha256:fb33085c39dd998ac16d1431ebc293a8b3eedd00fd4a32de0ff79002c19511b4"},
{file = "typing_extensions-4.5.0.tar.gz", hash = "sha256:5cb5f4a79139d699607b3ef622a1dedafa84e115ab0024e0d9c044a9479ca7cb"},
]
tzdata = [
{file = "tzdata-2022.7-py2.py3-none-any.whl", hash = "sha256:2b88858b0e3120792a3c0635c23daf36a7d7eeeca657c323da299d2094402a0d"},
{file = "tzdata-2022.7.tar.gz", hash = "sha256:fe5f866eddd8b96e9fcba978f8e503c909b19ea7efda11e52e39494bad3a7bfa"},

View file

@ -52,9 +52,10 @@ flake8-simplify = ">=0.19"
ipdb = "*"
isort = ">=5.10"
mkdocs = ">=1.4"
parse-type = ">=0.6.0"
poethepoet = "*"
pytest = ">=6.2"
pytest-bdd = ">=4.0.1,<6.0"
pytest-bdd = ">=6.0"
pytest-clarity = "*"
pytest-xdist = ">=2.5.0"
requests = "*"
@ -174,8 +175,9 @@ isolated_build = True
[testenv]
deps =
pytest >= 6.2
pytest-bdd >=4.0.1,<6.0
pytest-bdd >=6.0
pytest-xdist >=2.5.0
parse-type >=0.6.0
toml >=0.10
commands = pytest {posargs}

View file

@ -25,13 +25,16 @@ Feature: Change entry times in journal
Scenario Outline: Change flag changes prompted entries
Given we use the config "<config_file>"
And we use the password "test" if prompted
When we run "jrnl -1"
Then the output should contain "2020-09-24 09:14 The third entry finally"
When we run "jrnl --short"
Then the output should be
2020-08-29 11:11 Entry the first.
2020-08-31 14:32 A second entry in what I hope to be a long series.
2020-09-24 09:14 The third entry finally after weeks without writing.
When we run "jrnl --change-time '2022-04-23 10:30'" and enter
Y
N
Y
When we run "jrnl -99 --short"
When we run "jrnl --short"
Then the output should be
2020-08-31 14:32 A second entry in what I hope to be a long series.
2022-04-23 10:30 Entry the first.

View file

@ -6,7 +6,9 @@ Feature: Installing jrnl
\n
\n
\n
Then the output should contain "Journal 'default' created"
Then the output should contain "jrnl configuration created at"
And the output should contain "For advanced features, read the docs at https://jrnl.sh"
And the output should contain "Journal 'default' created"
And the default journal "journal.txt" should be in the "." directory
And the config should contain "encrypt: false"
And the version in the config file should be up-to-date

View file

@ -125,7 +125,7 @@ Feature: Searching in a journal
| basic_dayone.yaml |
Scenario: Searching for unstarred entries
Scenario Outline: Searching for unstarred entries
Given we use the config "<config_file>"
And we use the password "test" if prompted
When we run "jrnl -not -starred"
@ -138,7 +138,7 @@ Feature: Searching in a journal
| basic_folder.yaml |
| basic_dayone.yaml |
Scenario: Searching for tagged entries
Scenario Outline: Searching for tagged entries
Given we use the config "<config_file>"
And we use the password "test" if prompted
When we run "jrnl -tagged"
@ -151,7 +151,7 @@ Feature: Searching in a journal
| basic_folder.yaml |
| basic_dayone.yaml |
Scenario: Searching for untagged entries
Scenario Outline: Searching for untagged entries
Given we use the config "empty_folder.yaml"
When we run "jrnl Tagged entry. This one has a @tag."
Then we should get no error

View file

@ -201,6 +201,16 @@ def input_method():
return ""
@fixture
def all_input():
return ""
@fixture
def command():
return ""
@fixture
def cache_dir():
return {"exists": False, "path": ""}
@ -221,13 +231,15 @@ def mock_user_input(request, password_input, stdin_input):
def _mock_user_input():
# user_input needs to be here because we don't know it until cli_run starts
user_input = get_fixture(request, "all_input", None)
if user_input is None:
user_input = Exception("Unexpected call for user input")
else:
user_input = iter(user_input.splitlines())
def mock_console_input(**kwargs):
if kwargs["password"] and not isinstance(password_input, Exception):
pw = kwargs.get("password", False)
if pw and not isinstance(password_input, Exception):
return password_input
if isinstance(user_input, Iterable):
@ -236,7 +248,7 @@ def mock_user_input(request, password_input, stdin_input):
return "" if input_line == r"\n" else input_line
# exceptions
return user_input if not kwargs["password"] else password_input
return user_input if not pw else password_input
mock_console = Mock(wraps=Console(stderr=True))
mock_console.input = Mock(side_effect=mock_console_input)

View file

@ -19,7 +19,6 @@ from jrnl.time import __get_pdt_calendar
from tests.lib.fixtures import FailedKeyring
from tests.lib.fixtures import NoKeyring
from tests.lib.fixtures import TestKeyring
from tests.lib.helpers import get_fixture
@given(parse("we {editor_method} to the editor if opened\n{editor_input}"))
@ -84,17 +83,17 @@ def we_have_type_of_keyring(keyring_type):
return TestKeyring()
@given(parse('we use the config "{config_file}"'), target_fixture="config_path")
@given(parse("we use no config"), target_fixture="config_path")
def we_use_the_config(request, temp_dir, working_dir):
config_file = get_fixture(request, "config_file")
# Move into temp dir as cwd
os.chdir(temp_dir.name)
if not config_file:
def we_use_no_config(temp_dir):
os.chdir(temp_dir.name) # @todo move this step to a more universal place
return os.path.join(temp_dir.name, "non_existing_config.yaml")
@given(parse('we use the config "{config_file}"'), target_fixture="config_path")
def we_use_the_config(request, temp_dir, working_dir, config_file):
# Move into temp dir as cwd
os.chdir(temp_dir.name) # @todo move this step to a more universal place
# Copy the config file over
config_source = os.path.join(working_dir, "data", "configs", config_file)
config_dest = os.path.join(temp_dir.name, config_file)
@ -133,7 +132,7 @@ def config_exists(config_file, temp_dir, working_dir):
shutil.copy2(config_source, config_dest)
@given(parse('we use the password "{password}" if prompted'))
@given(parse('we use the password "{password}" if prompted'), target_fixture="password")
def use_password_forever(password):
return password

View file

@ -32,17 +32,6 @@ def does_directory_contain_n_files(directory_path, number):
return int(number) == count
def parse_should_or_should_not(should_or_should_not):
if should_or_should_not == "should":
return True
elif should_or_should_not == "should not":
return False
else:
raise Exception(
"should_or_should_not valid values are 'should' or 'should not'"
)
def assert_equal_tags_ignoring_order(
actual_line, expected_line, actual_content, expected_content
):
@ -81,7 +70,7 @@ def spy_wrapper(wrapped_function):
def get_fixture(request, name, default=None):
result = default
if name in request.node.fixturenames:
result = request.getfixturevalue(name)
return result
try:
return request.getfixturevalue(name)
except LookupError:
return default

View file

@ -15,7 +15,9 @@ from tests.lib.helpers import assert_equal_tags_ignoring_order
from tests.lib.helpers import does_directory_contain_files
from tests.lib.helpers import does_directory_contain_n_files
from tests.lib.helpers import get_nested_val
from tests.lib.helpers import parse_should_or_should_not
from tests.lib.type_builders import should_choice
SHOULD_DICT = {"Should": should_choice}
@then("we should get no error")
@ -31,40 +33,38 @@ def output_should_match(regex, cli_run):
assert matches, f"\nRegex didn't match:\n{regex}\n{str(out)}\n{str(matches)}"
@then(parse("the output {should_or_should_not} contain\n{expected_output}"))
@then(parse('the output {should_or_should_not} contain "{expected_output}"'))
@then(parse("the output {it_should:Should} contain\n{expected_output}", SHOULD_DICT))
@then(parse('the output {it_should:Should} contain "{expected_output}"', SHOULD_DICT))
@then(
parse(
"the {which_output_stream} output {should_or_should_not} contain\n{expected_output}"
"the {which_output_stream} output {it_should:Should} contain\n{expected_output}",
SHOULD_DICT,
)
)
@then(
parse(
'the {which_output_stream} output {should_or_should_not} contain "{expected_output}"'
'the {which_output_stream} output {it_should:Should} contain "{expected_output}"',
SHOULD_DICT,
)
)
def output_should_contain(
expected_output, which_output_stream, cli_run, should_or_should_not
):
we_should = parse_should_or_should_not(should_or_should_not)
def output_should_contain(expected_output, which_output_stream, cli_run, it_should):
output_str = f"\nEXPECTED:\n{expected_output}\n\nACTUAL STDOUT:\n{cli_run['stdout']}\n\nACTUAL STDERR:\n{cli_run['stderr']}"
assert expected_output
if which_output_stream is None:
assert ((expected_output in cli_run["stdout"]) == we_should) or (
(expected_output in cli_run["stderr"]) == we_should
assert ((expected_output in cli_run["stdout"]) == it_should) or (
(expected_output in cli_run["stderr"]) == it_should
), output_str
elif which_output_stream == "standard":
assert (expected_output in cli_run["stdout"]) == we_should, output_str
assert (expected_output in cli_run["stdout"]) == it_should, output_str
elif which_output_stream == "error":
assert (expected_output in cli_run["stderr"]) == we_should, output_str
assert (expected_output in cli_run["stderr"]) == it_should, output_str
else:
assert (
expected_output in cli_run[which_output_stream]
) == we_should, output_str
) == it_should, output_str
@then(parse("the output should not contain\n{expected_output}"))
@ -78,7 +78,7 @@ def output_should_not_contain(expected_output, cli_run):
def output_should_be(expected_output, cli_run):
actual = cli_run["stdout"].strip()
expected = expected_output.strip()
assert expected == actual
assert actual == expected
@then("the output should be empty")
@ -130,19 +130,19 @@ def default_journal_location(journal_file, journal_dir, config_on_disk, temp_dir
@then(
parse(
'the config for journal "{journal_name}" {should_or_should_not} contain "{some_yaml}"'
'the config for journal "{journal_name}" {it_should:Should} contain "{some_yaml}"',
SHOULD_DICT,
)
)
@then(
parse(
'the config for journal "{journal_name}" {should_or_should_not} contain\n{some_yaml}'
'the config for journal "{journal_name}" {it_should:Should} contain\n{some_yaml}',
SHOULD_DICT,
)
)
@then(parse('the config {should_or_should_not} contain "{some_yaml}"'))
@then(parse("the config {should_or_should_not} contain\n{some_yaml}"))
def config_var_on_disk(config_on_disk, journal_name, should_or_should_not, some_yaml):
we_should = parse_should_or_should_not(should_or_should_not)
@then(parse('the config {it_should:Should} contain "{some_yaml}"', SHOULD_DICT))
@then(parse("the config {it_should:Should} contain\n{some_yaml}", SHOULD_DICT))
def config_var_on_disk(config_on_disk, journal_name, it_should, some_yaml):
actual = config_on_disk
if journal_name:
actual = actual["journals"][journal_name]
@ -154,26 +154,28 @@ def config_var_on_disk(config_on_disk, journal_name, should_or_should_not, some_
# `expected` objects formatted in yaml only compare one level deep
actual_slice = {key: actual.get(key, None) for key in expected.keys()}
assert (expected == actual_slice) == we_should
assert (expected == actual_slice) == it_should
@then(
parse(
'the config in memory for journal "{journal_name}" {should_or_should_not} contain "{some_yaml}"'
'the config in memory for journal "{journal_name}" {it_should:Should} contain "{some_yaml}"',
SHOULD_DICT,
)
)
@then(
parse(
'the config in memory for journal "{journal_name}" {should_or_should_not} contain\n{some_yaml}'
'the config in memory for journal "{journal_name}" {it_should:Should} contain\n{some_yaml}',
SHOULD_DICT,
)
)
@then(parse('the config in memory {should_or_should_not} contain "{some_yaml}"'))
@then(parse("the config in memory {should_or_should_not} contain\n{some_yaml}"))
def config_var_in_memory(
config_in_memory, journal_name, should_or_should_not, some_yaml
):
we_should = parse_should_or_should_not(should_or_should_not)
@then(
parse('the config in memory {it_should:Should} contain "{some_yaml}"', SHOULD_DICT)
)
@then(
parse("the config in memory {it_should:Should} contain\n{some_yaml}", SHOULD_DICT)
)
def config_var_in_memory(config_in_memory, journal_name, it_should, some_yaml):
actual = config_in_memory["overrides"]
if journal_name:
actual = actual["journals"][journal_name]
@ -185,7 +187,7 @@ def config_var_in_memory(
# `expected` objects formatted in yaml only compare one level deep
actual_slice = {key: get_nested_val(actual, key) for key in expected.keys()}
assert (expected == actual_slice) == we_should
assert (expected == actual_slice) == it_should
@then("we should be prompted for a password")
@ -224,31 +226,27 @@ def journal_directory_should_not_exist(config_on_disk, journal_name):
), f'Journal "{journal_name}" does exist'
@then(parse("the journal {should_or_should_not} exist"))
def journal_should_not_exist(config_on_disk, should_or_should_not):
@then(parse("the journal {it_should:Should} exist", SHOULD_DICT))
def journal_should_not_exist(config_on_disk, it_should):
scoped_config = scope_config(config_on_disk, "default")
expected_path = scoped_config["journal"]
contains_files = does_directory_contain_files(expected_path, ".")
if should_or_should_not == "should":
assert contains_files
elif should_or_should_not == "should not":
assert not contains_files
else:
raise Exception(
"should_or_should_not valid values are 'should' or 'should not'"
assert contains_files == it_should
@then(
parse(
'the journal "{journal_name}" directory {it_should:Should} exist', SHOULD_DICT
)
@then(parse('the journal "{journal_name}" directory {should_or_should_not} exist'))
def directory_should_not_exist(config_on_disk, should_or_should_not, journal_name):
)
def directory_should_not_exist(config_on_disk, it_should, journal_name):
scoped_config = scope_config(config_on_disk, journal_name)
expected_path = scoped_config["journal"]
we_should = parse_should_or_should_not(should_or_should_not)
dir_exists = os.path.isdir(expected_path)
assert dir_exists == we_should
assert dir_exists == it_should
@then(parse('the content of file "{file_path}" in the cache should be\n{file_content}'))
@ -383,26 +381,23 @@ def count_elements(number, item, cli_run):
assert len(xml_tree.findall(".//" + item)) == number
@then(parse("the editor {should_or_should_not} have been called"))
@then(parse("the editor {it_should:Should} have been called", SHOULD_DICT))
@then(
parse(
"the editor {should_or_should_not} have been called with {num_args} arguments"
"the editor {it_should:Should} have been called with {num_args} arguments",
SHOULD_DICT,
)
)
def count_editor_args(num_args, cli_run, editor_state, should_or_should_not):
we_should = parse_should_or_should_not(should_or_should_not)
assert cli_run["mocks"]["editor"].called == we_should
def count_editor_args(num_args, cli_run, editor_state, it_should):
assert cli_run["mocks"]["editor"].called == it_should
if isinstance(num_args, int):
assert len(editor_state["command"]) == int(num_args)
@then(parse("the stdin prompt {should_or_should_not} have been called"))
def stdin_prompt_called(cli_run, should_or_should_not):
we_should = parse_should_or_should_not(should_or_should_not)
assert cli_run["mocks"]["stdin_input"].called == we_should
@then(parse("the stdin prompt {it_should:Should} have been called", SHOULD_DICT))
def stdin_prompt_called(cli_run, it_should):
assert cli_run["mocks"]["stdin_input"].called == it_should
@then(parse('the editor filename should end with "{suffix}"'))

View file

@ -0,0 +1,11 @@
# Copyright © 2012-2023 jrnl contributors
# License: https://www.gnu.org/licenses/gpl-3.0.html
from parse_type import TypeBuilder
should_choice = TypeBuilder.make_enum(
{
"should": True,
"should not": False,
}
)

View file

@ -7,6 +7,7 @@ from contextlib import ExitStack
from pytest_bdd import when
from pytest_bdd.parsers import parse
from pytest_bdd.parsers import re
from pytest_bdd.steps import inject_fixture
from jrnl.main import run
@ -29,13 +30,20 @@ all_input = '("(?P<all_input>[^"]*)")'
@when(parse('we run "jrnl {command}" and {input_method}\n{all_input}'))
@when(re(f'we run "jrnl ?{command}" and {input_method} {all_input}'))
@when(parse('we run "jrnl {command}"'))
@when(re(f'we run "jrnl {command}"(?! and)'))
@when('we run "jrnl"')
def we_run_jrnl(cli_run, capsys, keyring):
def we_run_jrnl(capsys, keyring, request, command, input_method, all_input):
from keyring import set_keyring
set_keyring(keyring)
# fixture injection (pytest-bdd >=6.0)
inject_fixture(request, "command", command)
inject_fixture(request, "input_method", input_method)
inject_fixture(request, "all_input", all_input)
cli_run = request.getfixturevalue("cli_run")
with ExitStack() as stack:
mocks = cli_run["mocks"]
factories = cli_run["mock_factories"]