mirror of
https://github.com/jrnl-org/jrnl.git
synced 2025-05-10 16:48:31 +02:00
Merge branch 'develop' into folder-journal-file-read-1692
This commit is contained in:
commit
a21f3e19df
17 changed files with 175 additions and 140 deletions
|
@ -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))
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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—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 & 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
|
||||
});
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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])
|
||||
|
|
|
@ -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
50
poetry.lock
generated
|
@ -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"},
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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}"'))
|
||||
|
|
11
tests/lib/type_builders.py
Normal file
11
tests/lib/type_builders.py
Normal 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,
|
||||
}
|
||||
)
|
|
@ -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"]
|
||||
|
|
Loading…
Add table
Reference in a new issue