mirror of
https://github.com/jrnl-org/jrnl.git
synced 2025-05-10 16:48:31 +02:00
Merge branch 'develop' into rich-instead-of-ansiwrap-1191
# Conflicts: # poetry.lock
This commit is contained in:
commit
e482d63180
17 changed files with 1470 additions and 1408 deletions
|
@ -6,7 +6,9 @@
|
||||||
|
|
||||||
**Implemented enhancements:**
|
**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)
|
- 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))
|
- 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))
|
- 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))
|
- 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:**
|
**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))
|
- 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))
|
- 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))
|
- 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)
|
- 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)
|
- 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)
|
- 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))
|
- 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 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 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 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))
|
- 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) |
|
| -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) |
|
| -and | Show only entries that match all conditions, like saying "x AND y" (default: OR) |
|
||||||
| -starred | Show only starred entries (marked with *) |
|
| -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) |
|
| -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 [TAG] | Exclude entries with this tag |
|
||||||
|
| -not -starred | Exclude entries that are starred |
|
||||||
|
| -not -tagged | Exclude entries that are tagged |
|
||||||
|
|
||||||
## Searching Options
|
## Searching Options
|
||||||
These help you do various tasks with the selected entries from your search.
|
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>
|
<section>
|
||||||
<i class="icon future"></i>
|
<i class="icon future"></i>
|
||||||
<h3>Future-proof.</h3>
|
<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>
|
||||||
<section>
|
<section>
|
||||||
<i class="icon secure"></i>
|
<i class="icon secure"></i>
|
||||||
<h3>Secure.</h3>
|
<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>
|
||||||
<section>
|
<section>
|
||||||
<i class="icon sync"></i>
|
<i class="icon sync"></i>
|
||||||
<h3>Accessible anywhere.</h3>
|
<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>
|
||||||
<section>
|
<section>
|
||||||
<i class="icon github"></i>
|
<i class="icon github"></i>
|
||||||
<h3>Free & Open Source.</h3>
|
<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>
|
||||||
<section>
|
<section>
|
||||||
<i class="icon folders"></i>
|
<i class="icon folders"></i>
|
||||||
|
@ -107,17 +107,17 @@ License: https://www.gnu.org/licenses/gpl-3.0.html
|
||||||
<script>
|
<script>
|
||||||
new Typed("#typed", {
|
new Typed("#typed", {
|
||||||
strings: [
|
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 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 <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. Taggidy-tag-tag.",
|
"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>`@idea 7<br />@beth 5</i>`",
|
"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>--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,
|
typeSpeed: 20, // less is faster
|
||||||
backSpeed: 15,
|
backSpeed: 10,
|
||||||
backDelay: 2000,
|
backDelay: 2500,
|
||||||
loop: true,
|
loop: true,
|
||||||
showCursor: false
|
showCursor: false
|
||||||
});
|
});
|
||||||
|
|
|
@ -154,6 +154,15 @@ def install() -> dict:
|
||||||
default_config["colors"] = get_default_colors()
|
default_config["colors"] = get_default_colors()
|
||||||
|
|
||||||
save_config(default_config)
|
save_config(default_config)
|
||||||
|
|
||||||
|
print_msg(
|
||||||
|
Message(
|
||||||
|
MsgText.InstallComplete,
|
||||||
|
MsgStyle.NORMAL,
|
||||||
|
params={"config_path": get_config_path()},
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
return default_config
|
return default_config
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -263,7 +263,9 @@ class Journal:
|
||||||
start_date = time.parse(start_date)
|
start_date = time.parse(start_date)
|
||||||
|
|
||||||
# If strict mode is on, all tags have to be present in entry
|
# 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):
|
def excluded(tags):
|
||||||
return 0 < len([tag for tag in tags if tag in 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"
|
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 --- #
|
# --- Prompts --- #
|
||||||
InstallJournalPathQuestion = """
|
InstallJournalPathQuestion = """
|
||||||
Path to your journal file (leave blank for {default_journal_path}):
|
Path to your journal file (leave blank for {default_journal_path}):
|
||||||
|
|
2613
poetry.lock
generated
2613
poetry.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -51,9 +51,10 @@ flake8-simplify = ">=0.19"
|
||||||
ipdb = "*"
|
ipdb = "*"
|
||||||
isort = ">=5.10"
|
isort = ">=5.10"
|
||||||
mkdocs = ">=1.4"
|
mkdocs = ">=1.4"
|
||||||
|
parse-type = ">=0.6.0"
|
||||||
poethepoet = "*"
|
poethepoet = "*"
|
||||||
pytest = ">=6.2"
|
pytest = ">=6.2"
|
||||||
pytest-bdd = ">=4.0.1,<6.0"
|
pytest-bdd = ">=6.0"
|
||||||
pytest-clarity = "*"
|
pytest-clarity = "*"
|
||||||
pytest-xdist = ">=2.5.0"
|
pytest-xdist = ">=2.5.0"
|
||||||
requests = "*"
|
requests = "*"
|
||||||
|
@ -173,8 +174,9 @@ isolated_build = True
|
||||||
[testenv]
|
[testenv]
|
||||||
deps =
|
deps =
|
||||||
pytest >= 6.2
|
pytest >= 6.2
|
||||||
pytest-bdd >=4.0.1,<6.0
|
pytest-bdd >=6.0
|
||||||
pytest-xdist >=2.5.0
|
pytest-xdist >=2.5.0
|
||||||
|
parse-type >=0.6.0
|
||||||
toml >=0.10
|
toml >=0.10
|
||||||
|
|
||||||
commands = pytest {posargs}
|
commands = pytest {posargs}
|
||||||
|
|
|
@ -25,13 +25,16 @@ Feature: Change entry times in journal
|
||||||
Scenario Outline: Change flag changes prompted entries
|
Scenario Outline: Change flag changes prompted entries
|
||||||
Given we use the config "<config_file>"
|
Given we use the config "<config_file>"
|
||||||
And we use the password "test" if prompted
|
And we use the password "test" if prompted
|
||||||
When we run "jrnl -1"
|
When we run "jrnl --short"
|
||||||
Then the output should contain "2020-09-24 09:14 The third entry finally"
|
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
|
When we run "jrnl --change-time '2022-04-23 10:30'" and enter
|
||||||
Y
|
Y
|
||||||
N
|
N
|
||||||
Y
|
Y
|
||||||
When we run "jrnl -99 --short"
|
When we run "jrnl --short"
|
||||||
Then the output should be
|
Then the output should be
|
||||||
2020-08-31 14:32 A second entry in what I hope to be a long series.
|
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.
|
2022-04-23 10:30 Entry the first.
|
||||||
|
|
|
@ -6,7 +6,9 @@ Feature: Installing jrnl
|
||||||
\n
|
\n
|
||||||
\n
|
\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 default journal "journal.txt" should be in the "." directory
|
||||||
And the config should contain "encrypt: false"
|
And the config should contain "encrypt: false"
|
||||||
And the version in the config file should be up-to-date
|
And the version in the config file should be up-to-date
|
||||||
|
|
|
@ -125,7 +125,7 @@ Feature: Searching in a journal
|
||||||
| basic_dayone.yaml |
|
| basic_dayone.yaml |
|
||||||
|
|
||||||
|
|
||||||
Scenario: Searching for unstarred entries
|
Scenario Outline: Searching for unstarred entries
|
||||||
Given we use the config "<config_file>"
|
Given we use the config "<config_file>"
|
||||||
And we use the password "test" if prompted
|
And we use the password "test" if prompted
|
||||||
When we run "jrnl -not -starred"
|
When we run "jrnl -not -starred"
|
||||||
|
@ -138,7 +138,7 @@ Feature: Searching in a journal
|
||||||
| basic_folder.yaml |
|
| basic_folder.yaml |
|
||||||
| basic_dayone.yaml |
|
| basic_dayone.yaml |
|
||||||
|
|
||||||
Scenario: Searching for tagged entries
|
Scenario Outline: Searching for tagged entries
|
||||||
Given we use the config "<config_file>"
|
Given we use the config "<config_file>"
|
||||||
And we use the password "test" if prompted
|
And we use the password "test" if prompted
|
||||||
When we run "jrnl -tagged"
|
When we run "jrnl -tagged"
|
||||||
|
@ -151,7 +151,7 @@ Feature: Searching in a journal
|
||||||
| basic_folder.yaml |
|
| basic_folder.yaml |
|
||||||
| basic_dayone.yaml |
|
| basic_dayone.yaml |
|
||||||
|
|
||||||
Scenario: Searching for untagged entries
|
Scenario Outline: Searching for untagged entries
|
||||||
Given we use the config "empty_folder.yaml"
|
Given we use the config "empty_folder.yaml"
|
||||||
When we run "jrnl Tagged entry. This one has a @tag."
|
When we run "jrnl Tagged entry. This one has a @tag."
|
||||||
Then we should get no error
|
Then we should get no error
|
||||||
|
|
|
@ -201,6 +201,16 @@ def input_method():
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
@fixture
|
||||||
|
def all_input():
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
@fixture
|
||||||
|
def command():
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
@fixture
|
@fixture
|
||||||
def cache_dir():
|
def cache_dir():
|
||||||
return {"exists": False, "path": ""}
|
return {"exists": False, "path": ""}
|
||||||
|
@ -221,13 +231,15 @@ def mock_user_input(request, password_input, stdin_input):
|
||||||
def _mock_user_input():
|
def _mock_user_input():
|
||||||
# user_input needs to be here because we don't know it until cli_run starts
|
# user_input needs to be here because we don't know it until cli_run starts
|
||||||
user_input = get_fixture(request, "all_input", None)
|
user_input = get_fixture(request, "all_input", None)
|
||||||
|
|
||||||
if user_input is None:
|
if user_input is None:
|
||||||
user_input = Exception("Unexpected call for user input")
|
user_input = Exception("Unexpected call for user input")
|
||||||
else:
|
else:
|
||||||
user_input = iter(user_input.splitlines())
|
user_input = iter(user_input.splitlines())
|
||||||
|
|
||||||
def mock_console_input(**kwargs):
|
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
|
return password_input
|
||||||
|
|
||||||
if isinstance(user_input, Iterable):
|
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
|
return "" if input_line == r"\n" else input_line
|
||||||
|
|
||||||
# exceptions
|
# 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 = Mock(wraps=Console(stderr=True))
|
||||||
mock_console.input = Mock(side_effect=mock_console_input)
|
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 FailedKeyring
|
||||||
from tests.lib.fixtures import NoKeyring
|
from tests.lib.fixtures import NoKeyring
|
||||||
from tests.lib.fixtures import TestKeyring
|
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}"))
|
@given(parse("we {editor_method} to the editor if opened\n{editor_input}"))
|
||||||
|
@ -84,16 +83,16 @@ def we_have_type_of_keyring(keyring_type):
|
||||||
return TestKeyring()
|
return TestKeyring()
|
||||||
|
|
||||||
|
|
||||||
@given(parse('we use the config "{config_file}"'), target_fixture="config_path")
|
|
||||||
@given(parse("we use no config"), target_fixture="config_path")
|
@given(parse("we use no config"), target_fixture="config_path")
|
||||||
def we_use_the_config(request, temp_dir, working_dir):
|
def we_use_no_config(temp_dir):
|
||||||
config_file = get_fixture(request, "config_file")
|
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
|
# Move into temp dir as cwd
|
||||||
os.chdir(temp_dir.name)
|
os.chdir(temp_dir.name) # @todo move this step to a more universal place
|
||||||
|
|
||||||
if not config_file:
|
|
||||||
return os.path.join(temp_dir.name, "non_existing_config.yaml")
|
|
||||||
|
|
||||||
# Copy the config file over
|
# Copy the config file over
|
||||||
config_source = os.path.join(working_dir, "data", "configs", config_file)
|
config_source = os.path.join(working_dir, "data", "configs", config_file)
|
||||||
|
@ -133,7 +132,7 @@ def config_exists(config_file, temp_dir, working_dir):
|
||||||
shutil.copy2(config_source, config_dest)
|
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):
|
def use_password_forever(password):
|
||||||
return password
|
return password
|
||||||
|
|
||||||
|
|
|
@ -32,17 +32,6 @@ def does_directory_contain_n_files(directory_path, number):
|
||||||
return int(number) == count
|
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(
|
def assert_equal_tags_ignoring_order(
|
||||||
actual_line, expected_line, actual_content, expected_content
|
actual_line, expected_line, actual_content, expected_content
|
||||||
):
|
):
|
||||||
|
@ -81,7 +70,7 @@ def spy_wrapper(wrapped_function):
|
||||||
|
|
||||||
|
|
||||||
def get_fixture(request, name, default=None):
|
def get_fixture(request, name, default=None):
|
||||||
result = default
|
try:
|
||||||
if name in request.node.fixturenames:
|
return request.getfixturevalue(name)
|
||||||
result = request.getfixturevalue(name)
|
except LookupError:
|
||||||
return result
|
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_files
|
||||||
from tests.lib.helpers import does_directory_contain_n_files
|
from tests.lib.helpers import does_directory_contain_n_files
|
||||||
from tests.lib.helpers import get_nested_val
|
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")
|
@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)}"
|
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 {it_should:Should} contain\n{expected_output}", SHOULD_DICT))
|
||||||
@then(parse('the output {should_or_should_not} contain "{expected_output}"'))
|
@then(parse('the output {it_should:Should} contain "{expected_output}"', SHOULD_DICT))
|
||||||
@then(
|
@then(
|
||||||
parse(
|
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(
|
@then(
|
||||||
parse(
|
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(
|
def output_should_contain(expected_output, which_output_stream, cli_run, it_should):
|
||||||
expected_output, which_output_stream, cli_run, should_or_should_not
|
|
||||||
):
|
|
||||||
we_should = parse_should_or_should_not(should_or_should_not)
|
|
||||||
|
|
||||||
output_str = f"\nEXPECTED:\n{expected_output}\n\nACTUAL STDOUT:\n{cli_run['stdout']}\n\nACTUAL STDERR:\n{cli_run['stderr']}"
|
output_str = f"\nEXPECTED:\n{expected_output}\n\nACTUAL STDOUT:\n{cli_run['stdout']}\n\nACTUAL STDERR:\n{cli_run['stderr']}"
|
||||||
assert expected_output
|
assert expected_output
|
||||||
if which_output_stream is None:
|
if which_output_stream is None:
|
||||||
assert ((expected_output in cli_run["stdout"]) == we_should) or (
|
assert ((expected_output in cli_run["stdout"]) == it_should) or (
|
||||||
(expected_output in cli_run["stderr"]) == we_should
|
(expected_output in cli_run["stderr"]) == it_should
|
||||||
), output_str
|
), output_str
|
||||||
|
|
||||||
elif which_output_stream == "standard":
|
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":
|
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:
|
else:
|
||||||
assert (
|
assert (
|
||||||
expected_output in cli_run[which_output_stream]
|
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}"))
|
@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):
|
def output_should_be(expected_output, cli_run):
|
||||||
actual = cli_run["stdout"].strip()
|
actual = cli_run["stdout"].strip()
|
||||||
expected = expected_output.strip()
|
expected = expected_output.strip()
|
||||||
assert expected == actual
|
assert actual == expected
|
||||||
|
|
||||||
|
|
||||||
@then("the output should be empty")
|
@then("the output should be empty")
|
||||||
|
@ -130,19 +130,19 @@ def default_journal_location(journal_file, journal_dir, config_on_disk, temp_dir
|
||||||
|
|
||||||
@then(
|
@then(
|
||||||
parse(
|
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(
|
@then(
|
||||||
parse(
|
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 {it_should:Should} contain "{some_yaml}"', SHOULD_DICT))
|
||||||
@then(parse("the config {should_or_should_not} contain\n{some_yaml}"))
|
@then(parse("the config {it_should:Should} contain\n{some_yaml}", SHOULD_DICT))
|
||||||
def config_var_on_disk(config_on_disk, journal_name, should_or_should_not, some_yaml):
|
def config_var_on_disk(config_on_disk, journal_name, it_should, some_yaml):
|
||||||
we_should = parse_should_or_should_not(should_or_should_not)
|
|
||||||
|
|
||||||
actual = config_on_disk
|
actual = config_on_disk
|
||||||
if journal_name:
|
if journal_name:
|
||||||
actual = actual["journals"][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
|
# `expected` objects formatted in yaml only compare one level deep
|
||||||
actual_slice = {key: actual.get(key, None) for key in expected.keys()}
|
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(
|
@then(
|
||||||
parse(
|
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(
|
@then(
|
||||||
parse(
|
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(
|
||||||
@then(parse("the config in memory {should_or_should_not} contain\n{some_yaml}"))
|
parse('the config in memory {it_should:Should} contain "{some_yaml}"', SHOULD_DICT)
|
||||||
def config_var_in_memory(
|
)
|
||||||
config_in_memory, journal_name, should_or_should_not, some_yaml
|
@then(
|
||||||
):
|
parse("the config in memory {it_should:Should} contain\n{some_yaml}", SHOULD_DICT)
|
||||||
we_should = parse_should_or_should_not(should_or_should_not)
|
)
|
||||||
|
def config_var_in_memory(config_in_memory, journal_name, it_should, some_yaml):
|
||||||
actual = config_in_memory["overrides"]
|
actual = config_in_memory["overrides"]
|
||||||
if journal_name:
|
if journal_name:
|
||||||
actual = actual["journals"][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
|
# `expected` objects formatted in yaml only compare one level deep
|
||||||
actual_slice = {key: get_nested_val(actual, key) for key in expected.keys()}
|
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")
|
@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'
|
), f'Journal "{journal_name}" does exist'
|
||||||
|
|
||||||
|
|
||||||
@then(parse("the journal {should_or_should_not} exist"))
|
@then(parse("the journal {it_should:Should} exist", SHOULD_DICT))
|
||||||
def journal_should_not_exist(config_on_disk, should_or_should_not):
|
def journal_should_not_exist(config_on_disk, it_should):
|
||||||
scoped_config = scope_config(config_on_disk, "default")
|
scoped_config = scope_config(config_on_disk, "default")
|
||||||
expected_path = scoped_config["journal"]
|
expected_path = scoped_config["journal"]
|
||||||
|
|
||||||
contains_files = does_directory_contain_files(expected_path, ".")
|
contains_files = does_directory_contain_files(expected_path, ".")
|
||||||
|
|
||||||
if should_or_should_not == "should":
|
assert contains_files == it_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'"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@then(parse('the journal "{journal_name}" directory {should_or_should_not} exist'))
|
@then(
|
||||||
def directory_should_not_exist(config_on_disk, should_or_should_not, journal_name):
|
parse(
|
||||||
|
'the journal "{journal_name}" directory {it_should:Should} exist', SHOULD_DICT
|
||||||
|
)
|
||||||
|
)
|
||||||
|
def directory_should_not_exist(config_on_disk, it_should, journal_name):
|
||||||
scoped_config = scope_config(config_on_disk, journal_name)
|
scoped_config = scope_config(config_on_disk, journal_name)
|
||||||
expected_path = scoped_config["journal"]
|
expected_path = scoped_config["journal"]
|
||||||
we_should = parse_should_or_should_not(should_or_should_not)
|
|
||||||
dir_exists = os.path.isdir(expected_path)
|
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}'))
|
@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
|
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(
|
@then(
|
||||||
parse(
|
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):
|
def count_editor_args(num_args, cli_run, editor_state, it_should):
|
||||||
we_should = parse_should_or_should_not(should_or_should_not)
|
assert cli_run["mocks"]["editor"].called == it_should
|
||||||
|
|
||||||
assert cli_run["mocks"]["editor"].called == we_should
|
|
||||||
|
|
||||||
if isinstance(num_args, int):
|
if isinstance(num_args, int):
|
||||||
assert len(editor_state["command"]) == int(num_args)
|
assert len(editor_state["command"]) == int(num_args)
|
||||||
|
|
||||||
|
|
||||||
@then(parse("the stdin prompt {should_or_should_not} have been called"))
|
@then(parse("the stdin prompt {it_should:Should} have been called", SHOULD_DICT))
|
||||||
def stdin_prompt_called(cli_run, should_or_should_not):
|
def stdin_prompt_called(cli_run, it_should):
|
||||||
we_should = parse_should_or_should_not(should_or_should_not)
|
assert cli_run["mocks"]["stdin_input"].called == it_should
|
||||||
|
|
||||||
assert cli_run["mocks"]["stdin_input"].called == we_should
|
|
||||||
|
|
||||||
|
|
||||||
@then(parse('the editor filename should end with "{suffix}"'))
|
@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 import when
|
||||||
from pytest_bdd.parsers import parse
|
from pytest_bdd.parsers import parse
|
||||||
from pytest_bdd.parsers import re
|
from pytest_bdd.parsers import re
|
||||||
|
from pytest_bdd.steps import inject_fixture
|
||||||
|
|
||||||
from jrnl.main import run
|
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(parse('we run "jrnl {command}" and {input_method}\n{all_input}'))
|
||||||
@when(re(f'we run "jrnl ?{command}" and {input_method} {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"')
|
@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
|
from keyring import set_keyring
|
||||||
|
|
||||||
set_keyring(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:
|
with ExitStack() as stack:
|
||||||
mocks = cli_run["mocks"]
|
mocks = cli_run["mocks"]
|
||||||
factories = cli_run["mock_factories"]
|
factories = cli_run["mock_factories"]
|
||||||
|
|
Loading…
Add table
Reference in a new issue