mirror of
https://github.com/jrnl-org/jrnl.git
synced 2025-05-10 16:48:31 +02:00
Merge branch 'develop' into mode-actions-1639
Conflicts: CHANGELOG.md docs/privacy-and-security.md jrnl/controller.py tests/bdd/features/change_time.feature
This commit is contained in:
commit
9bca32b438
28 changed files with 2109 additions and 1626 deletions
16
CHANGELOG.md
16
CHANGELOG.md
|
@ -6,7 +6,11 @@
|
||||||
|
|
||||||
**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))
|
||||||
|
- 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))
|
||||||
- Add more type hints [\#1642](https://github.com/jrnl-org/jrnl/pull/1642) ([outa](https://github.com/outa))
|
- Add more type hints [\#1642](https://github.com/jrnl-org/jrnl/pull/1642) ([outa](https://github.com/outa))
|
||||||
- Add `rich` handler to debug logging [\#1627](https://github.com/jrnl-org/jrnl/pull/1627) ([wren](https://github.com/wren))
|
- Add `rich` handler to debug logging [\#1627](https://github.com/jrnl-org/jrnl/pull/1627) ([wren](https://github.com/wren))
|
||||||
|
@ -16,6 +20,8 @@
|
||||||
|
|
||||||
- Templated entries should not be saved if the raw text is identical to the original template [\#1652](https://github.com/jrnl-org/jrnl/issues/1652)
|
- Templated entries should not be saved if the raw text is identical to the original template [\#1652](https://github.com/jrnl-org/jrnl/issues/1652)
|
||||||
- jrnl does not update version key in config file [\#1638](https://github.com/jrnl-org/jrnl/issues/1638)
|
- jrnl does not update version key in config file [\#1638](https://github.com/jrnl-org/jrnl/issues/1638)
|
||||||
|
- jrnl should not create 0-length "encrypted" file on startup [\#1493](https://github.com/jrnl-org/jrnl/issues/1493)
|
||||||
|
- Save empty journal on install instead of just creating a zero-length file [\#1690](https://github.com/jrnl-org/jrnl/pull/1690) ([micahellison](https://github.com/micahellison))
|
||||||
- Don't save templated journal entries if the received raw text is the same as the template itself [\#1653](https://github.com/jrnl-org/jrnl/pull/1653) ([Briscoooe](https://github.com/Briscoooe))
|
- Don't save templated journal entries if the received raw text is the same as the template itself [\#1653](https://github.com/jrnl-org/jrnl/pull/1653) ([Briscoooe](https://github.com/Briscoooe))
|
||||||
- Add tag to XML file when edited DayOne entry and is searchable afterward [\#1648](https://github.com/jrnl-org/jrnl/pull/1648) ([jonakeys](https://github.com/jonakeys))
|
- Add tag to XML file when edited DayOne entry and is searchable afterward [\#1648](https://github.com/jrnl-org/jrnl/pull/1648) ([jonakeys](https://github.com/jonakeys))
|
||||||
- Update version key in config file after version changes [\#1646](https://github.com/jrnl-org/jrnl/pull/1646) ([jonakeys](https://github.com/jonakeys))
|
- Update version key in config file after version changes [\#1646](https://github.com/jrnl-org/jrnl/pull/1646) ([jonakeys](https://github.com/jonakeys))
|
||||||
|
@ -26,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))
|
||||||
|
@ -33,7 +40,16 @@
|
||||||
|
|
||||||
**Documentation:**
|
**Documentation:**
|
||||||
|
|
||||||
|
- 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)
|
- 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 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))
|
||||||
- Fix markup in "Advanced Usage" doc [\#1655](https://github.com/jrnl-org/jrnl/pull/1655) ([multani](https://github.com/multani))
|
- Fix markup in "Advanced Usage" doc [\#1655](https://github.com/jrnl-org/jrnl/pull/1655) ([multani](https://github.com/multani))
|
||||||
|
|
|
@ -67,12 +67,57 @@ Windows doesn't log history to disk, but it does keep it in your command prompt
|
||||||
session. Close the command prompt or press `Alt`+`F7` to clear your history
|
session. Close the command prompt or press `Alt`+`F7` to clear your history
|
||||||
after journaling.
|
after journaling.
|
||||||
|
|
||||||
|
## Files in transit from editor to jrnl
|
||||||
|
|
||||||
|
When creating or editing an entry, `jrnl` uses a unencrypted temporary file on
|
||||||
|
disk in order to give your editor access to your journal. After you close your
|
||||||
|
editor, `jrnl` then deletes this temporary file.
|
||||||
|
|
||||||
|
So, if you have saved a journal entry but haven't closed your editor yet, the
|
||||||
|
unencrypted temporary remains on your disk. If your computer were to shut off
|
||||||
|
during this time, or the `jrnl` process were killed unexpectedly, then the
|
||||||
|
unencrypted temporary file will remain on your disk. You can mitigate this
|
||||||
|
issue by only saving with your editor right before closing it. You can also
|
||||||
|
manually delete these files from your temporary folder. By default, they
|
||||||
|
are named `jrnl*.jrnl`, but if you use a
|
||||||
|
[template](reference-config-file.md#template), they will have the same
|
||||||
|
extension as the template.
|
||||||
|
|
||||||
## Editor history
|
## Editor history
|
||||||
|
|
||||||
Some editors keep usage history stored on disk for future use. This can be a
|
Some editors keep usage history stored on disk for future use. This can be a
|
||||||
security risk in the sense that sensitive information can leak via recent
|
security risk in the sense that sensitive information can leak via recent
|
||||||
search patterns or editor commands.
|
search patterns or editor commands.
|
||||||
|
|
||||||
|
### Visual Studio Code
|
||||||
|
|
||||||
|
Visual Studio Code stores the contents of saved files to allow you to restore or
|
||||||
|
review the contents later. You can disable this feature for all files by unchecking
|
||||||
|
the `workbench.localHistory.enabled` setting in the
|
||||||
|
[Settings editor](https://code.visualstudio.com/docs/getstarted/settings#_settings-editor).
|
||||||
|
|
||||||
|
Alternatively, you can disable this feature for specific files by configuring a
|
||||||
|
[pattern](https://code.visualstudio.com/docs/editor/codebasics#_advanced-search-options)
|
||||||
|
in the `workbench.localHistory.exclude` setting. To exclude unencrypted temporary files generated
|
||||||
|
by `jrnl`, you can set the `**/jrnl*.jrnl` (unless you are using a
|
||||||
|
[template](reference-config-file.md#template)) pattern for the `workbench.localHistory.exclude` setting
|
||||||
|
in the [Settings editor](https://code.visualstudio.com/docs/getstarted/settings#_settings-editor).
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
On Windows, the history location is typically found at
|
||||||
|
`%APPDATA%\Code\User\History`.
|
||||||
|
|
||||||
|
Visual Studio Code also creates a copy of all unsaved files that are open.
|
||||||
|
It stores these copies in a backup location that's automatically cleaned when
|
||||||
|
you save the file. However, if your computer shuts off before you save the file,
|
||||||
|
or the Visual Studio Code process stops unexpectedly, then an unencrypted
|
||||||
|
temporary file may remain on your disk. You can manually delete these files
|
||||||
|
from the backup location.
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
On Windows, the backup location is typically found at
|
||||||
|
`%APPDATA%\Code\Backups`.
|
||||||
|
|
||||||
### Vim
|
### Vim
|
||||||
|
|
||||||
Vim stores progress data in a so called Viminfo file located at `~/.viminfo`
|
Vim stores progress data in a so called Viminfo file located at `~/.viminfo`
|
||||||
|
@ -101,7 +146,11 @@ autocommand can be used. Place this in your `~/.vimrc`:
|
||||||
autocmd BufNewFile,BufReadPre *.jrnl setlocal viminfo= noswapfile noundofile nobackup nowritebackup noshelltemp history=0 nomodeline secure
|
autocmd BufNewFile,BufReadPre *.jrnl setlocal viminfo= noswapfile noundofile nobackup nowritebackup noshelltemp history=0 nomodeline secure
|
||||||
```
|
```
|
||||||
|
|
||||||
Please see `:h <option>` in Vim for more information about the options mentioned.
|
!!! note
|
||||||
|
If you're using a [template](reference-config-file.md#template), you will
|
||||||
|
have to use the template's file extension instead of `.jrnl`.
|
||||||
|
|
||||||
|
See `:h <option>` in Vim for more information about the options mentioned.
|
||||||
|
|
||||||
### Neovim
|
### Neovim
|
||||||
|
|
||||||
|
@ -143,22 +192,12 @@ vim.api.nvim_create_autocmd( {"BufNewFile","BufReadPre" }, {
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
If you're using a [template](reference-config-file.md#template), you will
|
||||||
|
have to use the template's file extension instead of `.jrnl`.
|
||||||
|
|
||||||
Please see `:h <option>` in Neovim for more information about the options mentioned.
|
Please see `:h <option>` in Neovim for more information about the options mentioned.
|
||||||
|
|
||||||
## Files in transit from editor to jrnl
|
|
||||||
|
|
||||||
When creating or editing an entry, `jrnl` uses a unencrypted temporary file on
|
|
||||||
disk in order to give your editor access to your journal. After you close your
|
|
||||||
editor, `jrnl` then deletes this temporary file.
|
|
||||||
|
|
||||||
So, if you have saved a journal entry but haven't closed your editor yet, the
|
|
||||||
unencrypted temporary remains on your disk. If your computer were to shut off
|
|
||||||
during this time, or the `jrnl` process were killed unexpectedly, then the
|
|
||||||
unencrypted temporary file will remain on your disk. You can mitigate this
|
|
||||||
issue by only saving with your editor right before closing it. You can also
|
|
||||||
manually delete these files (i.e. files named `jrnl*.jrnl`) from your temporary
|
|
||||||
folder.
|
|
||||||
|
|
||||||
## Plausible deniability
|
## Plausible deniability
|
||||||
|
|
||||||
You may be able to hide the contents of your journal behind a layer of encryption,
|
You may be able to hide the contents of your journal behind a layer of encryption,
|
||||||
|
@ -178,7 +217,6 @@ In Windows, the keychain is the Windows Credential Manager (WCM), which can't be
|
||||||
and can be accessed by any other application running under your username. If this is
|
and can be accessed by any other application running under your username. If this is
|
||||||
a concern for you, you may not want to store your password.
|
a concern for you, you may not want to store your password.
|
||||||
|
|
||||||
|
|
||||||
## Notice any other risks?
|
## Notice any other risks?
|
||||||
|
|
||||||
Please let the maintainers know by [filing an issue on GitHub](https://github.com/jrnl-org/jrnl/issues).
|
Please let the maintainers know by [filing an issue on GitHub](https://github.com/jrnl-org/jrnl/issues).
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -59,7 +59,9 @@ value for journals that already have data in them.
|
||||||
|
|
||||||
### template
|
### template
|
||||||
The path to a text file to use as a template for new entries. Only works when you
|
The path to a text file to use as a template for new entries. Only works when you
|
||||||
have the `editor` field configured.
|
have the `editor` field configured. If you use a template, the editor's
|
||||||
|
[temporary files](privacy-and-security.md#files-in-transit-from-editor-to-jrnl)
|
||||||
|
will have the same extension as the template.
|
||||||
|
|
||||||
### tagsymbols
|
### tagsymbols
|
||||||
Symbols to be interpreted as tags.
|
Symbols to be interpreted as tags.
|
||||||
|
|
|
@ -118,7 +118,7 @@ div.rst-content {
|
||||||
background-size: 100%;
|
background-size: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
a.icon-home:before {
|
.wy-side-nav-search a.icon-home:before {
|
||||||
display: block;
|
display: block;
|
||||||
width: 84px;
|
width: 84px;
|
||||||
height: 70px;
|
height: 70px;
|
||||||
|
|
49
docs_theme/breadcrumbs.html
Normal file
49
docs_theme/breadcrumbs.html
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
<!--
|
||||||
|
Copied from https://github.com/mkdocs/mkdocs/blob/master/mkdocs/themes/readthedocs/breadcrumbs.html
|
||||||
|
Then lightly modified for accessibility
|
||||||
|
-->
|
||||||
|
|
||||||
|
<div role="navigation" aria-label="breadcrumbs navigation">
|
||||||
|
<ul class="wy-breadcrumbs">
|
||||||
|
<li><a href="{{ nav.homepage.url|url }}" class="icon icon-home" aria-label="{% trans %}Docs{% endtrans %}"></a> »</li>
|
||||||
|
{%- if page %}
|
||||||
|
{%- for doc in page.ancestors[::-1] %}
|
||||||
|
{%- if doc.link %}
|
||||||
|
<li><a href="{{ doc.link|e }}">{{ doc.title }}</a> »</li>
|
||||||
|
{%- else %}
|
||||||
|
<li>{{ doc.title }} »</li>
|
||||||
|
{%- endif %}
|
||||||
|
{%- endfor %}
|
||||||
|
<li>{{ page.title }}</li>
|
||||||
|
{%- endif %}
|
||||||
|
<li class="wy-breadcrumbs-aside">
|
||||||
|
{%- block repo %}
|
||||||
|
{%- if page and page.edit_url %}
|
||||||
|
{%- if config.repo_name|lower == 'github' %}
|
||||||
|
<a href="{{ page.edit_url }}" class="icon icon-github"> {% trans repo_name=config.repo_name %}Edit on {{ repo_name }}{% endtrans %}</a>
|
||||||
|
{%- elif config.repo_name|lower == 'bitbucket' %}
|
||||||
|
<a href="{{ page.edit_url }}" class="icon icon-bitbucket"> {% trans repo_name=config.repo_name %}Edit on {{ repo_name }}{% endtrans %}</a>
|
||||||
|
{%- elif config.repo_name|lower == 'gitlab' %}
|
||||||
|
<a href="{{ page.edit_url }}" class="icon icon-gitlab"> {% trans repo_name=config.repo_name %}Edit on {{ repo_name }}{% endtrans %}</a>
|
||||||
|
{%- elif config.repo_name %}
|
||||||
|
<a href="{{ page.edit_url }}">{% trans repo_name=config.repo_name %}Edit on {{ repo_name }}{% endtrans %}</a>
|
||||||
|
{%- else %}
|
||||||
|
<a href="{{ page.edit_url }}">{% trans %}Edit{% endtrans %}</a>
|
||||||
|
{%- endif %}
|
||||||
|
{%- endif %}
|
||||||
|
{%- endblock %}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
{%- if config.theme.prev_next_buttons_location|lower in ['top', 'both']
|
||||||
|
and page and (page.next_page or page.previous_page) %}
|
||||||
|
<div class="rst-breadcrumbs-buttons" role="navigation" aria-label="{% trans %}Breadcrumb Navigation{% endtrans %}">
|
||||||
|
{%- if page.previous_page %}
|
||||||
|
<a href="{{ page.previous_page.url|url }}" class="btn btn-neutral float-left" title="{{ page.previous_page.title }}"><span class="icon icon-circle-arrow-left" aria-hidden="true"></span> {% trans %}Previous{% endtrans %}</a>
|
||||||
|
{%- endif %}
|
||||||
|
{%- if page.next_page %}
|
||||||
|
<a href="{{ page.next_page.url|url }}" class="btn btn-neutral float-right" title="{{ page.next_page.title }}">{% trans %}Next{% endtrans %} <span class="icon icon-circle-arrow-right" aria-hidden="true"></span></a>
|
||||||
|
{%- endif %}
|
||||||
|
</div>
|
||||||
|
{%- endif %}
|
||||||
|
<hr/>
|
||||||
|
</div>
|
|
@ -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
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
mkdocs==1.2.4
|
mkdocs>=1.4
|
||||||
jinja2==3.1.2
|
jinja2==3.1.2
|
||||||
|
|
59
jrnl/args.py
59
jrnl/args.py
|
@ -28,6 +28,43 @@ class WrappingFormatter(argparse.RawTextHelpFormatter):
|
||||||
return text
|
return text
|
||||||
|
|
||||||
|
|
||||||
|
class IgnoreNoneAppendAction(argparse._AppendAction):
|
||||||
|
"""
|
||||||
|
Pass -not without a following string and avoid appending
|
||||||
|
a None value to the excluded list
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __call__(self, parser, namespace, values, option_string=None):
|
||||||
|
if values is not None:
|
||||||
|
super().__call__(parser, namespace, values, option_string)
|
||||||
|
|
||||||
|
|
||||||
|
def parse_not_arg(
|
||||||
|
args: list[str], parsed_args: argparse.Namespace, parser: argparse.ArgumentParser
|
||||||
|
) -> argparse.Namespace:
|
||||||
|
"""
|
||||||
|
It's possible to use -not as a precursor to -starred and -tagged
|
||||||
|
to reverse their behaviour, however this requires some extra logic
|
||||||
|
to parse, and to ensure we still do not allow passing an empty -not
|
||||||
|
"""
|
||||||
|
|
||||||
|
parsed_args.exclude_starred = False
|
||||||
|
parsed_args.exclude_tagged = False
|
||||||
|
|
||||||
|
if "-not-starred" in "".join(args):
|
||||||
|
parsed_args.starred = False
|
||||||
|
parsed_args.exclude_starred = True
|
||||||
|
if "-not-tagged" in "".join(args):
|
||||||
|
parsed_args.tagged = False
|
||||||
|
parsed_args.exclude_tagged = True
|
||||||
|
if "-not" in args and not any(
|
||||||
|
[parsed_args.exclude_starred, parsed_args.exclude_tagged, parsed_args.excluded]
|
||||||
|
):
|
||||||
|
parser.error("argument -not: expected 1 argument")
|
||||||
|
|
||||||
|
return parsed_args
|
||||||
|
|
||||||
|
|
||||||
def parse_args(args: list[str] = []) -> argparse.Namespace:
|
def parse_args(args: list[str] = []) -> argparse.Namespace:
|
||||||
"""
|
"""
|
||||||
Argument parsing that is doable before the config is available.
|
Argument parsing that is doable before the config is available.
|
||||||
|
@ -237,6 +274,12 @@ def parse_args(args: list[str] = []) -> argparse.Namespace:
|
||||||
action="store_true",
|
action="store_true",
|
||||||
help="Show only starred entries (marked with *)",
|
help="Show only starred entries (marked with *)",
|
||||||
)
|
)
|
||||||
|
reading.add_argument(
|
||||||
|
"-tagged",
|
||||||
|
dest="tagged",
|
||||||
|
action="store_true",
|
||||||
|
help="Show only entries that have at least one tag",
|
||||||
|
)
|
||||||
reading.add_argument(
|
reading.add_argument(
|
||||||
"-n",
|
"-n",
|
||||||
dest="limit",
|
dest="limit",
|
||||||
|
@ -249,11 +292,15 @@ def parse_args(args: list[str] = []) -> argparse.Namespace:
|
||||||
reading.add_argument(
|
reading.add_argument(
|
||||||
"-not",
|
"-not",
|
||||||
dest="excluded",
|
dest="excluded",
|
||||||
nargs=1,
|
nargs="?",
|
||||||
default=[],
|
default=[],
|
||||||
metavar="TAG",
|
metavar="TAG/FLAG",
|
||||||
action="extend",
|
action=IgnoreNoneAppendAction,
|
||||||
help="Exclude entries with this tag",
|
help=(
|
||||||
|
"If passed a string, will exclude entries with that tag. "
|
||||||
|
"Can be also used before -starred or -tagged flags, to exclude "
|
||||||
|
"starred or tagged entries respectively."
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
search_options_msg = """ These help you do various tasks with the selected entries from your search.
|
search_options_msg = """ These help you do various tasks with the selected entries from your search.
|
||||||
|
@ -388,5 +435,7 @@ def parse_args(args: list[str] = []) -> argparse.Namespace:
|
||||||
# Handle '-123' as a shortcut for '-n 123'
|
# Handle '-123' as a shortcut for '-n 123'
|
||||||
num = re.compile(r"^-(\d+)$")
|
num = re.compile(r"^-(\d+)$")
|
||||||
args = [num.sub(r"-n \1", arg) for arg in args]
|
args = [num.sub(r"-n \1", arg) for arg in args]
|
||||||
|
parsed_args = parser.parse_intermixed_args(args)
|
||||||
|
parsed_args = parse_not_arg(args, parsed_args, parser)
|
||||||
|
|
||||||
return parser.parse_intermixed_args(args)
|
return parsed_args
|
||||||
|
|
|
@ -107,14 +107,23 @@ def get_default_config() -> dict[str, Any]:
|
||||||
"linewrap": 79,
|
"linewrap": 79,
|
||||||
"indent_character": "|",
|
"indent_character": "|",
|
||||||
"colors": {
|
"colors": {
|
||||||
"date": "none",
|
|
||||||
"title": "none",
|
|
||||||
"body": "none",
|
"body": "none",
|
||||||
|
"date": "none",
|
||||||
"tags": "none",
|
"tags": "none",
|
||||||
|
"title": "none",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def get_default_colors() -> dict[str, Any]:
|
||||||
|
return {
|
||||||
|
"body": "none",
|
||||||
|
"date": "black",
|
||||||
|
"tags": "yellow",
|
||||||
|
"title": "cyan",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def get_default_journal_path() -> str:
|
def get_default_journal_path() -> str:
|
||||||
journal_data_path = xdg.BaseDirectory.save_data_path(XDG_RESOURCE) or home_dir()
|
journal_data_path = xdg.BaseDirectory.save_data_path(XDG_RESOURCE) or home_dir()
|
||||||
return os.path.join(journal_data_path, DEFAULT_JOURNAL_NAME)
|
return os.path.join(journal_data_path, DEFAULT_JOURNAL_NAME)
|
||||||
|
|
|
@ -250,7 +250,10 @@ def _filter_journal_entries(args: "Namespace", journal: "Journal", **kwargs) ->
|
||||||
end_date=args.end_date,
|
end_date=args.end_date,
|
||||||
strict=args.strict,
|
strict=args.strict,
|
||||||
starred=args.starred,
|
starred=args.starred,
|
||||||
|
tagged=args.tagged,
|
||||||
exclude=args.excluded,
|
exclude=args.excluded,
|
||||||
|
exclude_starred=args.exclude_starred,
|
||||||
|
exclude_tagged=args.exclude_tagged,
|
||||||
contains=args.contains,
|
contains=args.contains,
|
||||||
)
|
)
|
||||||
journal.limit(args.limit)
|
journal.limit(args.limit)
|
||||||
|
@ -419,7 +422,10 @@ def _has_search_args(args: "Namespace") -> bool:
|
||||||
return any(
|
return any(
|
||||||
(
|
(
|
||||||
args.contains,
|
args.contains,
|
||||||
args.excluded, # -not
|
args.tagged,
|
||||||
|
args.excluded,
|
||||||
|
args.exclude_starred,
|
||||||
|
args.exclude_tagged,
|
||||||
args.end_date,
|
args.end_date,
|
||||||
args.today_in_history,
|
args.today_in_history,
|
||||||
args.month,
|
args.month,
|
||||||
|
|
|
@ -12,6 +12,7 @@ from rich.pretty import pretty_repr
|
||||||
from jrnl import __version__
|
from jrnl import __version__
|
||||||
from jrnl.config import DEFAULT_JOURNAL_KEY
|
from jrnl.config import DEFAULT_JOURNAL_KEY
|
||||||
from jrnl.config import get_config_path
|
from jrnl.config import get_config_path
|
||||||
|
from jrnl.config import get_default_colors
|
||||||
from jrnl.config import get_default_config
|
from jrnl.config import get_default_config
|
||||||
from jrnl.config import get_default_journal_path
|
from jrnl.config import get_default_journal_path
|
||||||
from jrnl.config import load_config
|
from jrnl.config import load_config
|
||||||
|
@ -147,7 +148,21 @@ def install() -> dict:
|
||||||
default_config["encrypt"] = True
|
default_config["encrypt"] = True
|
||||||
print_msg(Message(MsgText.JournalEncrypted, MsgStyle.NORMAL))
|
print_msg(Message(MsgText.JournalEncrypted, MsgStyle.NORMAL))
|
||||||
|
|
||||||
|
# Use colors?
|
||||||
|
use_colors = yesno(Message(MsgText.UseColorsQuestion), default=True)
|
||||||
|
if use_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
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -127,6 +127,7 @@ class Journal:
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
self.write()
|
||||||
|
|
||||||
text = self._load(filename)
|
text = self._load(filename)
|
||||||
text = self._decrypt(text)
|
text = self._decrypt(text)
|
||||||
|
@ -233,16 +234,19 @@ class Journal:
|
||||||
|
|
||||||
def filter(
|
def filter(
|
||||||
self,
|
self,
|
||||||
tags: list = [],
|
tags=[],
|
||||||
month: str | int | None = None,
|
month=None,
|
||||||
day: str | int | None = None,
|
day=None,
|
||||||
year: str | None = None,
|
year=None,
|
||||||
start_date: str | None = None,
|
start_date=None,
|
||||||
end_date: str | None = None,
|
end_date=None,
|
||||||
starred: bool = False,
|
starred=False,
|
||||||
strict: bool = False,
|
tagged=False,
|
||||||
contains: bool = None,
|
exclude_starred=False,
|
||||||
exclude: list = [],
|
exclude_tagged=False,
|
||||||
|
strict=False,
|
||||||
|
contains=None,
|
||||||
|
exclude=[],
|
||||||
):
|
):
|
||||||
"""Removes all entries from the journal that don't match the filter.
|
"""Removes all entries from the journal that don't match the filter.
|
||||||
|
|
||||||
|
@ -263,7 +267,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
|
||||||
tagged = 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])
|
||||||
|
@ -279,8 +285,9 @@ class Journal:
|
||||||
result = [
|
result = [
|
||||||
entry
|
entry
|
||||||
for entry in self.entries
|
for entry in self.entries
|
||||||
if (not tags or tagged(entry.tags))
|
if (not tags or has_tags(entry.tags))
|
||||||
and (not starred or entry.starred)
|
and (not (starred or exclude_starred) or entry.starred == starred)
|
||||||
|
and (not (tagged or exclude_tagged) or bool(entry.tags) == tagged)
|
||||||
and (not month or entry.date.month == compare_d.month)
|
and (not month or entry.date.month == compare_d.month)
|
||||||
and (not day or entry.date.day == compare_d.day)
|
and (not day or entry.date.day == compare_d.day)
|
||||||
and (not year or entry.date.year == compare_d.year)
|
and (not year or entry.date.year == compare_d.year)
|
||||||
|
|
|
@ -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}):
|
||||||
|
@ -37,6 +42,9 @@ class MsgText(Enum):
|
||||||
EncryptJournalQuestion = """
|
EncryptJournalQuestion = """
|
||||||
Do you want to encrypt your journal? (You can always change this later)
|
Do you want to encrypt your journal? (You can always change this later)
|
||||||
"""
|
"""
|
||||||
|
UseColorsQuestion = """
|
||||||
|
Do you want jrnl to use colors when displaying entries? (You can always change this later)
|
||||||
|
"""
|
||||||
YesOrNoPromptDefaultYes = "[Y/n]"
|
YesOrNoPromptDefaultYes = "[Y/n]"
|
||||||
YesOrNoPromptDefaultNo = "[y/N]"
|
YesOrNoPromptDefaultNo = "[y/N]"
|
||||||
ContinueUpgrade = "Continue upgrading jrnl?"
|
ContinueUpgrade = "Continue upgrading jrnl?"
|
||||||
|
|
|
@ -5,6 +5,9 @@ theme:
|
||||||
custom_dir: docs_theme
|
custom_dir: docs_theme
|
||||||
static_templates:
|
static_templates:
|
||||||
- index.html
|
- index.html
|
||||||
|
watch:
|
||||||
|
- docs
|
||||||
|
- docs_theme
|
||||||
extra_css:
|
extra_css:
|
||||||
- https://fonts.googleapis.com/css?family=Open+Sans:300,600
|
- https://fonts.googleapis.com/css?family=Open+Sans:300,600
|
||||||
- assets/colors.css
|
- assets/colors.css
|
||||||
|
|
3117
poetry.lock
generated
3117
poetry.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -51,10 +51,11 @@ flake8-type-checking = ">=2.2.0"
|
||||||
flake8-simplify = ">=0.19"
|
flake8-simplify = ">=0.19"
|
||||||
ipdb = "*"
|
ipdb = "*"
|
||||||
isort = ">=5.10"
|
isort = ">=5.10"
|
||||||
mkdocs = ">=1.0,<1.3"
|
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 = "*"
|
||||||
|
@ -174,8 +175,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}
|
||||||
|
|
|
@ -27,15 +27,18 @@ 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
|
||||||
Then the error output should contain "3 entries found"
|
Then the error output should contain "3 entries found"
|
||||||
And the error output should contain "2 entries modified"
|
And the error output should contain "2 entries modified"
|
||||||
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.
|
||||||
|
|
|
@ -57,6 +57,7 @@ Feature: Journals iteracting with the file system in a way that users can see
|
||||||
When we run "jrnl hello world" and enter
|
When we run "jrnl hello world" and enter
|
||||||
test.txt
|
test.txt
|
||||||
n
|
n
|
||||||
|
\n
|
||||||
Then the output should contain "Journal 'default' created"
|
Then the output should contain "Journal 'default' created"
|
||||||
When we change directory to "subfolder"
|
When we change directory to "subfolder"
|
||||||
And we run "jrnl -n 1"
|
And we run "jrnl -n 1"
|
||||||
|
|
|
@ -5,7 +5,10 @@ Feature: Installing jrnl
|
||||||
When we run "jrnl hello world" and enter
|
When we run "jrnl hello world" and enter
|
||||||
\n
|
\n
|
||||||
\n
|
\n
|
||||||
Then the output should contain "Journal 'default' created"
|
\n
|
||||||
|
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
|
||||||
|
@ -15,6 +18,7 @@ Feature: Installing jrnl
|
||||||
When we run "jrnl hello world" and enter
|
When we run "jrnl hello world" and enter
|
||||||
default/custom.txt
|
default/custom.txt
|
||||||
n
|
n
|
||||||
|
\n
|
||||||
Then the output should contain "Journal 'default' created"
|
Then the output should contain "Journal 'default' created"
|
||||||
And the default journal "custom.txt" should be in the "default" directory
|
And the default journal "custom.txt" should be in the "default" directory
|
||||||
And the config should contain "encrypt: false"
|
And the config should contain "encrypt: false"
|
||||||
|
@ -26,6 +30,7 @@ Feature: Installing jrnl
|
||||||
When we run "jrnl hello world" and enter
|
When we run "jrnl hello world" and enter
|
||||||
~/custom.txt
|
~/custom.txt
|
||||||
n
|
n
|
||||||
|
\n
|
||||||
Then the output should contain "Journal 'default' created"
|
Then the output should contain "Journal 'default' created"
|
||||||
And the default journal "custom.txt" should be in the "home" directory
|
And the default journal "custom.txt" should be in the "home" directory
|
||||||
And the config should contain "encrypt: false"
|
And the config should contain "encrypt: false"
|
||||||
|
@ -36,10 +41,57 @@ Feature: Installing jrnl
|
||||||
When we run "jrnl hello world" and enter
|
When we run "jrnl hello world" and enter
|
||||||
encrypted.txt
|
encrypted.txt
|
||||||
y
|
y
|
||||||
|
\n
|
||||||
Then the output should contain "Journal will be encrypted"
|
Then the output should contain "Journal will be encrypted"
|
||||||
And the default journal "encrypted.txt" should be in the "." directory
|
And the default journal "encrypted.txt" should be in the "." directory
|
||||||
And the config should contain "encrypt: true"
|
And the config should contain "encrypt: true"
|
||||||
And the version in the config file should be up-to-date
|
And the version in the config file should be up-to-date
|
||||||
When we run "jrnl"
|
When we run "jrnl"
|
||||||
Then we should be prompted for a password
|
Then we should be prompted for a password
|
||||||
|
|
||||||
|
Scenario: Install jrnl with colors by default
|
||||||
|
Given we use no config
|
||||||
|
When we run "jrnl hello world" and enter
|
||||||
|
\n
|
||||||
|
\n
|
||||||
|
\n
|
||||||
|
Then the output should contain "Journal 'default' created"
|
||||||
|
And the config should contain
|
||||||
|
colors:
|
||||||
|
body: none
|
||||||
|
date: black
|
||||||
|
tags: yellow
|
||||||
|
title: cyan
|
||||||
|
|
||||||
|
Scenario: Install jrnl without colors
|
||||||
|
Given we use no config
|
||||||
|
When we run "jrnl hello world" and enter
|
||||||
|
\n
|
||||||
|
\n
|
||||||
|
N
|
||||||
|
Then the output should contain "Journal 'default' created"
|
||||||
|
And the config should contain
|
||||||
|
colors:
|
||||||
|
body: none
|
||||||
|
date: none
|
||||||
|
tags: none
|
||||||
|
title: none
|
||||||
|
|
||||||
|
Scenario: Install jrnl with encrypted default journal with no entries
|
||||||
|
Given we use no config
|
||||||
|
When we run "jrnl -1" and enter
|
||||||
|
encrypted.txt
|
||||||
|
y
|
||||||
|
n
|
||||||
|
test
|
||||||
|
test
|
||||||
|
n
|
||||||
|
Then the error output should contain "Journal will be encrypted"
|
||||||
|
And the default journal "encrypted.txt" should be in the "." directory
|
||||||
|
And the config should contain "encrypt: true"
|
||||||
|
And the version in the config file should be up-to-date
|
||||||
|
When we run "jrnl -1" and enter
|
||||||
|
test
|
||||||
|
Then we should be prompted for a password
|
||||||
|
And the error output should contain "no entries found"
|
||||||
|
And the error output should not contain "Wrong password, try again"
|
||||||
|
|
|
@ -124,6 +124,50 @@ Feature: Searching in a journal
|
||||||
| basic_folder.yaml |
|
| basic_folder.yaml |
|
||||||
| basic_dayone.yaml |
|
| basic_dayone.yaml |
|
||||||
|
|
||||||
|
|
||||||
|
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"
|
||||||
|
Then we should get no error
|
||||||
|
And the output should contain "2 entries found"
|
||||||
|
|
||||||
|
Examples: configs
|
||||||
|
| config_file |
|
||||||
|
| basic_onefile.yaml |
|
||||||
|
| basic_folder.yaml |
|
||||||
|
| basic_dayone.yaml |
|
||||||
|
|
||||||
|
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"
|
||||||
|
Then we should get no error
|
||||||
|
And the output should contain "3 entries found"
|
||||||
|
|
||||||
|
Examples: configs
|
||||||
|
| config_file |
|
||||||
|
| basic_onefile.yaml |
|
||||||
|
| basic_folder.yaml |
|
||||||
|
| basic_dayone.yaml |
|
||||||
|
|
||||||
|
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
|
||||||
|
When we run "jrnl Untagged entry. This one has no tag."
|
||||||
|
Then we should get no error
|
||||||
|
When we run "jrnl -not -tagged"
|
||||||
|
Then we should get no error
|
||||||
|
And the output should contain "1 entry found"
|
||||||
|
And the output should contain "This one has no tag"
|
||||||
|
|
||||||
|
Examples: configs
|
||||||
|
| config_file |
|
||||||
|
| basic_onefile.yaml |
|
||||||
|
| basic_folder.yaml |
|
||||||
|
| basic_dayone.yaml |
|
||||||
|
|
||||||
Scenario Outline: Searching for dates
|
Scenario Outline: Searching for dates
|
||||||
Given we use the config "<config_file>"
|
Given we use the config "<config_file>"
|
||||||
When we run "jrnl -on 2020-08-31 --short"
|
When we run "jrnl -on 2020-08-31 --short"
|
||||||
|
|
|
@ -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"]
|
||||||
|
|
|
@ -23,6 +23,8 @@ def expected_args(**kwargs):
|
||||||
"change_time": None,
|
"change_time": None,
|
||||||
"edit": False,
|
"edit": False,
|
||||||
"end_date": None,
|
"end_date": None,
|
||||||
|
"exclude_starred": False,
|
||||||
|
"exclude_tagged": False,
|
||||||
"today_in_history": False,
|
"today_in_history": False,
|
||||||
"month": None,
|
"month": None,
|
||||||
"day": None,
|
"day": None,
|
||||||
|
@ -38,6 +40,7 @@ def expected_args(**kwargs):
|
||||||
"starred": False,
|
"starred": False,
|
||||||
"start_date": None,
|
"start_date": None,
|
||||||
"strict": False,
|
"strict": False,
|
||||||
|
"tagged": False,
|
||||||
"tags": False,
|
"tags": False,
|
||||||
"text": [],
|
"text": [],
|
||||||
"config_override": [],
|
"config_override": [],
|
||||||
|
|
Loading…
Add table
Reference in a new issue