Merge remote-tracking branch 'upstream/develop' into true-yaml-exporter

# Conflicts:
#	features/format.feature
#	tests/bdd/features/format.feature
This commit is contained in:
MinchinWeb 2021-10-11 11:11:48 -06:00
commit a3da498571
173 changed files with 4321 additions and 3381 deletions

2
.gitattributes vendored
View file

@ -1,2 +1,4 @@
*.journal text eol=lf
*.feature text eol=lf
poetry.lock text eol=lf
pyrpoject.toml text eol=lf

View file

@ -2,7 +2,7 @@
name: Documentation Change
about: Request or report any updates to our documentation (https://jrnl.sh)
title: ''
labels: ":new:, Documentation"
labels: ":new:, documentation"
assignees: ''
---

View file

@ -14,11 +14,9 @@ Here are some key points to include in your description:
- [ ] I have read the [contributing doc](https://github.com/jrnl-org/jrnl/blob/develop/CONTRIBUTING.md).
- [ ] I have included a link to the relevant issue number.
- [ ] I have tested this code locally.
- [ ] I have checked to ensure there aren't other open [pull requests](../pulls)
for the same issue.
- [ ] I have written new tests for these changes, as needed.
- [ ] All tests pass.
<!--
NOTE: Your PR may not be reviewed if there are any failing tests. You can run
tests locally with `make test` (see the contributing doc if you need help with

View file

@ -85,6 +85,13 @@ jobs:
exit 1
fi
if [[ $tagline == 1 ]]; then
echo "::error::Something is wrong."
echo "::error::The latest release ${SINCE_TAG} is the first line in the changelog,"
echo "::error::but the h1 '# Changelog' should always be the first line."
exit 1
fi
sed -i "1,$(expr $tagline - 1)d" "$FILENAME"
# delete generated line (or it will be added multiple times)
sed -i '/This Changelog was automatically generated by/d' "$FILENAME"
@ -124,6 +131,7 @@ jobs:
git diff
if [[ $(grep -c '^# Changelog$' "$FILENAME") != 1 ]]; then
echo '::error::Something is wrong with the changelog.'
exit 1
fi
SOMETHING_CHANGED=false
git diff --exit-code || SOMETHING_CHANGED=true
@ -142,6 +150,7 @@ jobs:
- name: Merge to Release branch
if: env.FULL_RELEASE == 'true'
run: |
git fetch --unshallow origin
git checkout release
git merge --ff-only $BRANCH
git push origin release

View file

@ -5,6 +5,18 @@ on:
version:
description: 'Version (e.g. v2.5, v2.5.1-beta, v2.6-beta2)'
required: true
include_repo_version:
description: 'Update version in repo? (true/false)'
require: true
default: true
include_pypi:
description: 'Publish to PyPI? (true/false)'
required: true
default: true
include_brew:
description: 'Publish to Homebrew? (true/false)'
required: true
default: true
jobs:
validate:
@ -65,12 +77,13 @@ jobs:
run: pip install poetry
- name: Update version in files
if: ${{ github.event.inputs.include_repo_version == 'true' }}
run: |
poetry version "$JRNL_VERSION"
echo __version__ = \"$JRNL_VERSION\" > jrnl/__version__.py
- name: Commit updated files
if: ${{ github.repository == env.HOME_REPO }}
if: ${{ github.event.inputs.include_repo_version == 'true' && github.repository == env.HOME_REPO }}
run: |
git add pyproject.toml jrnl/__version__.py
git commit -m "Increment version to ${JRNL_VERSION}"
@ -82,7 +95,7 @@ jobs:
run: poetry build
- name: Deploy to PyPI
if: ${{ github.repository == env.HOME_REPO }}
if: ${{ github.event.inputs.include_pypi == 'true' && github.repository == env.HOME_REPO }}
env:
POETRY_PYPI_TOKEN_PYPI: ${{ secrets.PYPI_TOKEN }}
run: poetry publish
@ -94,6 +107,7 @@ jobs:
echo "::set-output name=pypi_version::$pypi_version"
release_homebrew:
if: ${{ github.event.inputs.include_brew == 'true' }}
needs: release_pypi
name: "Release to Homebrew"
runs-on: macos-latest
@ -102,7 +116,6 @@ jobs:
HOMEBREW_NO_INSTALL_CLEANUP: 1
HOME_REPO: ${{ secrets.HOME_REPO }}
steps:
- name: Get version
run: |
JRNL_VERSION="${{ github.event.inputs.version }}"
@ -114,7 +127,7 @@ jobs:
echo "JRNL_VERSION=$JRNL_VERSION" >> $GITHUB_ENV
echo "PYPI_VERSION=$PYPI_VERSION" >> $GITHUB_ENV
- name: Determine type of release
- name: Set env variables
env:
REPO_OWNER: ${{ github.repository_owner }}
run: |
@ -139,26 +152,11 @@ jobs:
} >> $GITHUB_ENV
fi
- name: Checkout homebrew repo
uses: actions/checkout@v2
with:
token: ${{ secrets.JRNL_BOT_TOKEN }}
repository: ${{ env.FORMULA_REPO }}
- name: Config git user
- name: Tap formula
run: |
git config --global user.name "${{ secrets.JRNL_BOT_NAME }}"
git config --global user.email "${{ secrets.JRNL_BOT_EMAIL }}"
- name: Create branch
run: |
BRANCH_NAME="jrnl-${JRNL_VERSION}--${RANDOM}"
git remote rename origin upstream
git remote add origin "https://github.com/${BOT_REPO}.git"
git fetch origin
git checkout -b $BRANCH_NAME
git push -u origin $BRANCH_NAME
brew tap ${FORMULA_REPO}
echo '::debug::Set tap directory'
echo "BREW_TAP_DIRECTORY=$(brew --repo ${FORMULA_REPO})" >> $GITHUB_ENV
- name: Install dependencies
run: brew install pipgrip
@ -187,32 +185,31 @@ jobs:
max_attempts: 6
retry_wait_seconds: 30
command: >
brew bump-formula-pr "Formula/${FORMULA_NAME}.rb"
brew bump-formula-pr "${FORMULA_NAME}"
--url $(jq ".releases[\"${PYPI_VERSION}\"][1].url" -r api_response.json)
--sha256 $(jq ".releases[\"${PYPI_VERSION}\"][1].digests.sha256" -r api_response.json)
--version=$PYPI_VERSION
--no-audit
--write
--commit
--force
--verbose
- name: Update commit message
run: |
git commit --amend \
-m "jrnl ${JRNL_VERSION}" \
-m "Update jrnl to ${JRNL_VERSION}" \
-m '${{ secrets.RELEASE_COAUTHORS }}'
- name: Create Pull Request
uses: peter-evans/create-pull-request@v3
with:
path: ${{ env.BREW_TAP_DIRECTORY }}
token: ${{ secrets.JRNL_BOT_TOKEN }}
push-to-fork: ${{ env.BOT_REPO }}
- name: Push commit
run: |
git show head
git push
committer: ${{ secrets.JRNL_BOT_NAME }} <${{ secrets.JRNL_BOT_EMAIL }}>
author: ${{ secrets.JRNL_BOT_NAME }} <${{ secrets.JRNL_BOT_EMAIL }}>
- name: Open pull request
env:
GH_TOKEN: ${{ secrets.JRNL_BOT_TOKEN }}
run: >
gh pr create
--title "jrnl ${JRNL_VERSION}"
--body 'Created with `brew bump-formula-pr`.'
title: jrnl ${{ env.JRNL_VERSION }}
body: Created with `brew bump-formula-pr`
branch: jrnl-${{ env.JRNL_VERSION }}--
branch-suffix: random
commit-message: |
jrnl ${{ env.JRNL_VERSION }}
Update jrnl to ${{ env.JRNL_VERSION }}
${{ secrets.RELEASE_COAUTHORS }}

View file

@ -9,6 +9,7 @@ on:
- 'tests/**'
- 'poetry.lock'
- 'pyproject.toml'
- '.github/workflows/testing.yaml'
pull_request:
branches: [ develop ]
paths:
@ -17,21 +18,26 @@ on:
- 'tests/**'
- 'poetry.lock'
- 'pyproject.toml'
- '.github/workflows/testing.yaml'
defaults:
run:
shell: bash # needed to prevent Windows from using PowerShell
jobs:
test:
if: >
! contains(github.event.head_commit.message, '[ci skip]')
runs-on: ${{ matrix.os }}
continue-on-error: ${{ matrix.python-version == '3.10-dev' }}
strategy:
fail-fast: false
matrix:
python-version: [ 3.7, 3.8, 3.9 ]
python-version: [ 3.7, 3.8, 3.9, 3.10-dev ]
os: [ ubuntu-latest, macos-latest, windows-latest ]
exclude: # Added for GitHub Actions PR problem 2020-12-19 -- remove later!
- os: windows-latest
python-version: 3.9
steps:
- run: git config --global core.autocrlf false
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
@ -39,34 +45,53 @@ jobs:
with:
python-version: ${{ matrix.python-version }}
- name: poetry cache
- name: poetry cache # Change CACHE_STRING secret to bust the cache. Useful with minor Python version changes.
uses: actions/cache@v2
with:
path: .venv
key: ${{ runner.os }}-${{ hashFiles('poetry.lock') }}-${{ matrix.python-version }}-v2
key: ${{ runner.os }}-${{ hashFiles('poetry.lock') }}-${{ matrix.python-version }}-${{ secrets.CACHE_STRING }}
- name: Install dependencies
if: ${{ matrix.python-version != '3.10-dev' }}
run: |
pip install poetry
echo '::group::poetry'
pip --disable-pip-version-check install poetry
poetry config --local virtualenvs.in-project true
echo '::endgroup::'
echo '::group::Other dependencies'
poetry install --remove-untracked
echo '::endgroup::'
echo 'DEPS_INSTALLED=true' >> $GITHUB_ENV
- name: Install dependencies (Prerelease)
if: ${{ matrix.python-version == '3.10-dev' }}
run: |
echo '::group::poetry'
pip --disable-pip-version-check install poetry==1.2.0a2
poetry config --local virtualenvs.in-project true
echo '::endgroup::'
echo '::group::Other dependencies'
poetry install --remove-untracked --no-dev --extras testing
echo '::endgroup::'
echo 'DEPS_INSTALLED=true' >> $GITHUB_ENV
- name: Code formatting (Black)
if: success() || failure()
if: ${{ matrix.python-version != '3.10-dev' && env.DEPS_INSTALLED == 'true' }}
run: |
poetry run black --version
poetry run black --check --diff .
- name: Code Style (PyFlakes)
if: success() || failure()
- name: Code Style (flake8)
if: ${{ matrix.python-version != '3.10-dev' && env.DEPS_INSTALLED == 'true' }}
run: |
poetry run pyflakes --version
poetry run pyflakes jrnl features tests
poetry run pflake8 --version
poetry run pflake8 jrnl tests
- name: Test with pytest
if: success() || failure()
if: ${{ env.DEPS_INSTALLED == 'true' }}
run: poetry run pytest --junitxml=reports/pytest/results.xml
- name: Test with behave
if: success() || failure()
run: poetry run behave --no-skipped --format progress2 --junit --junit-directory reports/behave

8
.gitignore vendored
View file

@ -16,7 +16,6 @@ var
sdist
develop-eggs
.installed.cfg
lib
lib64
.python-version
@ -54,3 +53,10 @@ exp/
_extras/
*.sublime-*
site/
.vscode/settings.json
coverage.xml
.vscode/launch.json
.coverage
.vscode/tasks.json
todo.txt

View file

@ -2,29 +2,168 @@
## [Unreleased](https://github.com/jrnl-org/jrnl/)
[Full Changelog](https://github.com/jrnl-org/jrnl/compare/v2.6...HEAD)
[Full Changelog](https://github.com/jrnl-org/jrnl/compare/v2.8.3...HEAD)
**Build:**
- Add more steps to `pytest`, fully remove `behave` [\#1347](https://github.com/jrnl-org/jrnl/pull/1347) ([wren](https://github.com/wren))
**Documentation:**
- Document folder journal and DayOne journal types [\#1326](https://github.com/jrnl-org/jrnl/issues/1326)
- Fix readme splash and add changelog link to readme [\#1339](https://github.com/jrnl-org/jrnl/pull/1339) ([micahellison](https://github.com/micahellison))
- Document journal types [\#1331](https://github.com/jrnl-org/jrnl/pull/1331) ([micahellison](https://github.com/micahellison))
**Packaging:**
- Bump pytz from 2021.1 to 2021.3 [\#1348](https://github.com/jrnl-org/jrnl/pull/1348) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump cryptography from 3.4.8 to 35.0.0 [\#1345](https://github.com/jrnl-org/jrnl/pull/1345) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump black from 21.8b0 to 21.9b0 [\#1343](https://github.com/jrnl-org/jrnl/pull/1343) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump keyring from 23.1.0 to 23.2.1 [\#1342](https://github.com/jrnl-org/jrnl/pull/1342) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump pytest from 6.2.4 to 6.2.5 [\#1334](https://github.com/jrnl-org/jrnl/pull/1334) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump black from 21.7b0 to 21.8b0 [\#1333](https://github.com/jrnl-org/jrnl/pull/1333) ([dependabot[bot]](https://github.com/apps/dependabot))
## [v2.8.3](https://pypi.org/project/jrnl/v2.8.3/) (2021-09-06)
[Full Changelog](https://github.com/jrnl-org/jrnl/compare/v2.8.3-beta...v2.8.3)
**Fixed bugs:**
- Fix deletion of entries on folder journal through `--delete` flag [\#1328](https://github.com/jrnl-org/jrnl/pull/1328) ([micahellison](https://github.com/micahellison))
- Warn when DayOne/directory journals have encrypt: true in config [\#1325](https://github.com/jrnl-org/jrnl/pull/1325) ([micahellison](https://github.com/micahellison))
- Fix failure to import into directory journal [\#1314](https://github.com/jrnl-org/jrnl/pull/1314) ([micahellison](https://github.com/micahellison))
- Allow emoji in config file in Windows by always opening it as unicode [\#1313](https://github.com/jrnl-org/jrnl/pull/1313) ([micahellison](https://github.com/micahellison))
**Build:**
- Set bash as default shell [\#1324](https://github.com/jrnl-org/jrnl/pull/1324) ([micahellison](https://github.com/micahellison))
**Packaging:**
- Bump cryptography from 3.4.7 to 3.4.8 [\#1329](https://github.com/jrnl-org/jrnl/pull/1329) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump keyring from 23.0.1 to 23.1.0 [\#1318](https://github.com/jrnl-org/jrnl/pull/1318) ([dependabot[bot]](https://github.com/apps/dependabot))
## [v2.8.2](https://pypi.org/project/jrnl/v2.8.2/) (2021-07-31)
[Full Changelog](https://github.com/jrnl-org/jrnl/compare/v2.8.2-beta...v2.8.2)
**Build:**
- Add CI tests for latest dev Python build [\#1273](https://github.com/jrnl-org/jrnl/issues/1273)
- Fix lock file from stable Poetry version [\#1298](https://github.com/jrnl-org/jrnl/pull/1298) ([wren](https://github.com/wren))
- Change all YAML FullLoader calls to SafeLoader [\#1285](https://github.com/jrnl-org/jrnl/pull/1285) ([micahellison](https://github.com/micahellison))
- Remove useless shebangs and executable permissions [\#1283](https://github.com/jrnl-org/jrnl/pull/1283) ([musicinmybrain](https://github.com/musicinmybrain))
- Add Python 3.10 support [\#1271](https://github.com/jrnl-org/jrnl/pull/1271) ([micahellison](https://github.com/micahellison))
- Ensure that line endings in all py files are Linux style instead of Windows [\#1250](https://github.com/jrnl-org/jrnl/pull/1250) ([micahellison](https://github.com/micahellison))
- Remove `--version` from brew release workflow [\#1233](https://github.com/jrnl-org/jrnl/pull/1233) ([wren](https://github.com/wren))
- Move test suite to Pytest \(replace Behave\) [\#1193](https://github.com/jrnl-org/jrnl/pull/1193) ([wren](https://github.com/wren))
**Documentation:**
- Add documentation about saved passwords in Windows [\#1301](https://github.com/jrnl-org/jrnl/pull/1301) ([micahellison](https://github.com/micahellison))
- Add security.md [\#1299](https://github.com/jrnl-org/jrnl/pull/1299) ([micahellison](https://github.com/micahellison))
**Packaging:**
- Bump mkdocs from 1.2.1 to 1.2.2 [\#1307](https://github.com/jrnl-org/jrnl/pull/1307) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump python-dateutil from 2.8.1 to 2.8.2 [\#1302](https://github.com/jrnl-org/jrnl/pull/1302) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump black from 21.5b1 to 21.5b2 [\#1254](https://github.com/jrnl-org/jrnl/pull/1254) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump black from 21.5b0 to 21.5b1 [\#1244](https://github.com/jrnl-org/jrnl/pull/1244) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump black from 20.8b1 to 21.5b0 [\#1241](https://github.com/jrnl-org/jrnl/pull/1241) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump pytest from 6.2.3 to 6.2.4 [\#1240](https://github.com/jrnl-org/jrnl/pull/1240) ([dependabot[bot]](https://github.com/apps/dependabot))
## [v2.8.1](https://pypi.org/project/jrnl/v2.8.1/) (2021-04-24)
[Full Changelog](https://github.com/jrnl-org/jrnl/compare/v2.8.1-beta...v2.8.1)
**Fixed bugs:**
- More graceful handling of low linewrap values [\#1219](https://github.com/jrnl-org/jrnl/pull/1219) ([sriniv27](https://github.com/sriniv27))
**Documentation:**
- Update absolute URLs to preview images in metatags [\#1229](https://github.com/jrnl-org/jrnl/pull/1229) ([maebert](https://github.com/maebert))
- Docs: Add emacs as external editor to recipes [\#1220](https://github.com/jrnl-org/jrnl/pull/1220) ([mandarvaze](https://github.com/mandarvaze))
**Packaging:**
- Bump pytest from 6.2.2 to 6.2.3 [\#1228](https://github.com/jrnl-org/jrnl/pull/1228) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump cryptography from 3.4.6 to 3.4.7 [\#1223](https://github.com/jrnl-org/jrnl/pull/1223) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump keyring from 23.0.0 to 23.0.1 [\#1222](https://github.com/jrnl-org/jrnl/pull/1222) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump pyflakes from 2.3.0 to 2.3.1 [\#1221](https://github.com/jrnl-org/jrnl/pull/1221) ([dependabot[bot]](https://github.com/apps/dependabot))
## [v2.8](https://pypi.org/project/jrnl/v2.8/) (2021-03-27)
[Full Changelog](https://github.com/jrnl-org/jrnl/compare/v2.8-beta...v2.8)
**Implemented enhancements:**
- Implement dependency tracker/updater [\#1120](https://github.com/jrnl-org/jrnl/issues/1120)
- Automate Arch deployment [\#1112](https://github.com/jrnl-org/jrnl/issues/1112)
- Change temporary file names for better text editor integration [\#1080](https://github.com/jrnl-org/jrnl/issues/1080)
- Allow custom file extension for `jrnl --edit` command [\#1059](https://github.com/jrnl-org/jrnl/issues/1059)
- Add `--config-override` feature [\#1169](https://github.com/jrnl-org/jrnl/pull/1169) ([sriniv27](https://github.com/sriniv27))
**Fixed bugs:**
- Fix bug that prevented --format pretty and --format short from working [\#1177](https://github.com/jrnl-org/jrnl/pull/1177) ([sriniv27](https://github.com/sriniv27))
**Build:**
- Fix broken brew release process [\#1211](https://github.com/jrnl-org/jrnl/pull/1211) ([micahellison](https://github.com/micahellison))
**Packaging:**
- Bump pyflakes from 2.2.0 to 2.3.0 [\#1215](https://github.com/jrnl-org/jrnl/pull/1215) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump keyring from 22.3.0 to 23.0.0 [\#1213](https://github.com/jrnl-org/jrnl/pull/1213) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump keyring from 22.0.1 to 22.3.0 [\#1210](https://github.com/jrnl-org/jrnl/pull/1210) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump asteval from 0.9.22 to 0.9.23 [\#1209](https://github.com/jrnl-org/jrnl/pull/1209) ([dependabot[bot]](https://github.com/apps/dependabot))
## [v2.7.1](https://pypi.org/project/jrnl/v2.7.1/) (2021-02-27)
[Full Changelog](https://github.com/jrnl-org/jrnl/compare/v2.7...v2.7.1)
**Fixed bugs:**
- Make journal selection behavior more consistent when there's a colon with no date [\#1164](https://github.com/jrnl-org/jrnl/pull/1164) ([wren](https://github.com/wren))
**Documentation:**
- Update documentation about journal-level config values [\#1196](https://github.com/jrnl-org/jrnl/issues/1196)
- update per-journal config documentation [\#1199](https://github.com/jrnl-org/jrnl/pull/1199) ([sriniv27](https://github.com/sriniv27))
**Packaging:**
- Bump cryptography from 3.4.4 to 3.4.6 [\#1195](https://github.com/jrnl-org/jrnl/pull/1195) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump asteval from 0.9.21 to 0.9.22 [\#1189](https://github.com/jrnl-org/jrnl/pull/1189) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump cryptography from 3.3.1 to 3.4.4 [\#1188](https://github.com/jrnl-org/jrnl/pull/1188) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump yq from 2.11.1 to 2.12.0 [\#1186](https://github.com/jrnl-org/jrnl/pull/1186) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump pytz from 2020.5 to 2021.1 [\#1174](https://github.com/jrnl-org/jrnl/pull/1174) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump keyring from 21.8.0 to 22.0.1 [\#1168](https://github.com/jrnl-org/jrnl/pull/1168) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump pytest from 6.2.1 to 6.2.2 [\#1167](https://github.com/jrnl-org/jrnl/pull/1167) ([dependabot[bot]](https://github.com/apps/dependabot))
## [v2.7](https://pypi.org/project/jrnl/v2.7/) (2021-01-23)
[Full Changelog](https://github.com/jrnl-org/jrnl/compare/v2.7-beta...v2.7)
**Implemented enhancements:**
- Add new date format \(`--format date`\) for heatmapping [\#1146](https://github.com/jrnl-org/jrnl/pull/1146) ([KarimPwnz](https://github.com/KarimPwnz))
- Add new `-today-in-history`, `-month`, `-day`, and `-year` search filters [\#1145](https://github.com/jrnl-org/jrnl/pull/1145) ([KarimPwnz](https://github.com/KarimPwnz))
- Allow custom extensions when editing \(for easier syntax highlighting\) [\#1139](https://github.com/jrnl-org/jrnl/pull/1139) ([KarimPwnz](https://github.com/KarimPwnz))
**Fixed bugs:**
- Error if password exists in keyring, but retrieval fails for any reason [\#1020](https://github.com/jrnl-org/jrnl/issues/1020)
- Editor can't be launched on Windows when using full path to editor executable [\#1096](https://github.com/jrnl-org/jrnl/issues/1096)
- Fix OS compatibility issues for editors with spaces, slashes, and quotes [\#1153](https://github.com/jrnl-org/jrnl/pull/1153) ([micahellison](https://github.com/micahellison))
- Add delimiters in YAML format [\#1150](https://github.com/jrnl-org/jrnl/pull/1150) ([Seopril](https://github.com/Seopril))
- Fix keyring error handling [\#1138](https://github.com/jrnl-org/jrnl/pull/1138) ([KarimPwnz](https://github.com/KarimPwnz))
- Notify user when config directory can't be created because there is already a file with the same name [\#1134](https://github.com/jrnl-org/jrnl/pull/1134) ([micahellison](https://github.com/micahellison))
**Build:**
- Fix homebrew release, add options for release pipeline [\#1154](https://github.com/jrnl-org/jrnl/pull/1154) ([wren](https://github.com/wren))
- Fix changelog generator [\#1127](https://github.com/jrnl-org/jrnl/pull/1127) ([wren](https://github.com/wren))
**Documentation:**
- Clarify installation docs [\#1097](https://github.com/jrnl-org/jrnl/issues/1097)
- add instructions to add VSCode as an external editor for Windows [\#1155](https://github.com/jrnl-org/jrnl/issues/1155)
- Clarify editor documentation for PATH variable and VS Code [\#1160](https://github.com/jrnl-org/jrnl/pull/1160) ([micahellison](https://github.com/micahellison))
- Emphasize installing dependencies before testing [\#1148](https://github.com/jrnl-org/jrnl/pull/1148) ([gumatias](https://github.com/gumatias))
- Clarify installation documentation \(\#1097\) [\#1137](https://github.com/jrnl-org/jrnl/pull/1137) ([Seopril](https://github.com/Seopril))
- Fix broken search bar in docs site [\#1135](https://github.com/jrnl-org/jrnl/pull/1135) ([wren](https://github.com/wren))
@ -34,6 +173,7 @@
**Packaging:**
- Bump pyyaml from 5.3.1 to 5.4.1 [\#1158](https://github.com/jrnl-org/jrnl/pull/1158) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump keyring from 21.7.0 to 21.8.0 [\#1136](https://github.com/jrnl-org/jrnl/pull/1136) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump pytz from 2020.4 to 2020.5 [\#1130](https://github.com/jrnl-org/jrnl/pull/1130) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
- Bump pytest from 6.2.0 to 6.2.1 [\#1129](https://github.com/jrnl-org/jrnl/pull/1129) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))

View file

@ -96,9 +96,9 @@ A typical development workflow includes:
When resolving bugs or adding new functionality, please add tests to prevent that functionality from breaking in the future. If you notice any functionality that isn't covered in the tests, feel free to submit a test-only pull request as well.
For integration testing, jrnl uses [behave](https://behave.readthedocs.io/) tests, which are all in the `features` folder.
For testing, jrnl uses [pytest](https://docs.pytest.org) for unit tests, and [pytest-bdd](https://pytest-bdd.readthedocs.io/) for integration testing. All tests are in the `tests` folder.
Many tests can be created by only editing `feature` files with the same format as other tests. For more complicated functionality, you may need to implement steps in `features/steps` which are then executed by your tests in the `feature` files.
Many tests can be created by only editing `*.feature` files with the same format as other tests. For more complicated functionality, you may need to implement steps in `tests/lib/` which are then executed by your tests in the `feature` files.
Starting in 2020, jrnl is also using [pytest](https://docs.pytest.org/) for unit tests. These tests are in the `tests` folder.

View file

@ -18,12 +18,19 @@ format: ## Format files to match style
lint: ## Check style with various tools
poetry check
poetry run pyflakes .
poetry run pflake8 jrnl tests
poetry run black --check --diff .
test: lint ## Run unit tests and behave tests
poetry run pytest
poetry run behave --no-skipped --format progress2
unit: # unit tests
poetry run pytest tests/unit
bdd: # bdd tests
poetry run pytest tests/bdd --gherkin-terminal-reporter --tb=native
bdd-debug: # bdd tests
poetry run pytest tests/bdd --gherkin-terminal-reporter --tb=native -x -vv
test: lint unit bdd
build:
poetry build

View file

@ -1,6 +1,6 @@
<p align="center">
<a href="https://jrnl.sh">
<img align="center" src="https://github.com/jrnl-org/jrnl/blob/develop/docs_theme/assets/readme-header.png"/>
<img align="center" src="https://raw.githubusercontent.com/jrnl-org/jrnl/develop/docs_theme/assets/readme-header.png"/>
</a>
</p>
@ -8,6 +8,9 @@ jrnl
[![Testing](https://github.com/jrnl-org/jrnl/workflows/Testing/badge.svg)](https://github.com/jrnl-org/jrnl/actions?query=workflow%3ATesting)
[![Downloads](https://pepy.tech/badge/jrnl)](https://pepy.tech/project/jrnl)
[![Version](http://img.shields.io/pypi/v/jrnl.svg?style=flat)](https://pypi.python.org/pypi/jrnl/)
[![Homebrew](https://img.shields.io/homebrew/v/jrnl?style=flat-square)](https://formulae.brew.sh/formula/jrnl)
[![Gitter](https://img.shields.io/gitter/room/jrnl-org/jrnl)](https://gitter.im/jrnl-org/jrnl)
[![Changelog](https://img.shields.io/badge/changelog-on%20github-green)](https://github.com/jrnl-org/jrnl/blob/develop/CHANGELOG.md)
====
_To get help, [submit an issue](https://github.com/jrnl-org/jrnl/issues/new/choose) on

7
SECURITY.md Normal file
View file

@ -0,0 +1,7 @@
# Security
If you've discovered a potential security issue in jrnl, please contact the maintainers at [jrnl-sh@googlegroups.com](mailto:jrnl-sh@googlegroups.com).
You can also feel free to [open an issue](https://github.com/jrnl-org/jrnl/issues/new/choose) (but please don't disclose the vulnerability) in case the email goes to spam.
You can find [known privacy and security issues in our documentation](https://jrnl.sh/en/stable/privacy-and-security/).

View file

@ -1,5 +1,6 @@
<!-- Copyright (C) 2012-2021 jrnl contributors
License: https://www.gnu.org/licenses/gpl-3.0.html -->
# Advanced Usage
## Configuration File
@ -19,28 +20,28 @@ and can be edited with a plain text editor.
Backup your journal and config file before editing. Changes to the config file
can have destructive effects on your journal!
- `journals`
- `journals`
paths to your journal files
- `editor`
- `editor`
if set, executes this command to launch an external editor for
writing your entries, e.g. `vim`. Some editors require special
options to work properly, see `FAQ <recipes>` for details.
- `encrypt`
- `encrypt`
if `true`, encrypts your journal using AES.
- `tagsymbols`
- `tagsymbols`
Symbols to be interpreted as tags. (See note below)
- `default_hour` and `default_minute`
- `default_hour` and `default_minute`
if you supply a date, such as `last thursday`, but no specific
time, the entry will be created at this time
- `timeformat`
- `timeformat`
how to format the timestamps in your journal, see the [python docs](http://docs.python.org/library/time.html#time.strftime) for reference
- `highlight`
- `highlight`
if `true`, tags will be highlighted in cyan.
- `linewrap`
- `linewrap`
controls the width of the output. Set to `false` if you don't want to wrap long lines.
- `colors`
- `colors`
dictionary that controls the colors used to display journal entries. It has four subkeys, which are: `body`, `date`, `tags`, and `title`. Current valid values are: `BLACK`, `RED`, `GREEN`, `YELLOW`, `BLUE`, `MAGENTA`, `CYAN`, `WHITE`, and `NONE`. `colorama.Fore` is used for colorization, and you can find the [docs here](https://github.com/tartley/colorama#colored-output). To disable colored output, set the value to `NONE`. If you set the value of any color subkey to an invalid color, no color will be used.
- `display_format`
- `display_format`
specifies formatter to use, formatters available are:
`boxed`, `fancy`, `json`, `markdown`, `md`, `tags`, `text`, `txt`, `xml`, or `yaml`.
@ -61,6 +62,29 @@ and can be edited with a plain text editor.
Or use the built-in prompt or an external editor to compose your
entries.
### Modifying Configurations from the Command line
You can override a configuration field for the current instance of `jrnl` using `--config-override CONFIG_KEY CONFIG_VALUE` where `CONFIG_KEY` is a valid configuration field, specified in dot-notation and `CONFIG_VALUE` is the (valid) desired override value.
You can specify multiple overrides as multiple calls to `--config-override`.
!!! note
These overrides allow you to modify ***any*** field of your jrnl configuration. We trust that you know what you are doing.
#### Examples:
``` sh
#Create an entry using the `stdin` prompt, for rapid logging
jrnl --config-override editor ""
#Populate a project's log
jrnl --config-override journals.todo "$(git rev-parse --show-toplevel)/todo.txt" todo find my towel
#Pass multiple overrides
jrnl --config-override display_format fancy --config-override linewrap 20 \
--config-override colors.title green
```
## Multiple journal files
You can configure `jrnl`to use with multiple journals (eg.
@ -99,11 +123,31 @@ food: ~/my_recipes.txt
```
Your `default` and your `food` journals won't be encrypted, however your
`work` journal will! You can override all options that are present at
`work` journal will!
You can override all options that are present at
the top level of `jrnl.yaml`, just make sure that at the very least
you specify a `journal: ...` key that points to the journal file of
that journal.
Consider the following example configuration
```yaml
editor: vi -c startinsert
journals:
default: ~/journal.txt
work:
journal: ~/work.txt
encrypt: true
display_format: json
editor: code -rw
food:
display_format: markdown
journal: ~/recipes.txt
```
The `work` journal is encrypted, prints to `json` by default, and is edited using an existing window of VSCode. Similarly, the `food` journal prints to markdown by default, but uses all the other defaults.
!!! note
Changing `encrypt` to a different value will not encrypt or decrypt your
journal file, it merely says whether or not your journal

61
docs/journal-types.md Normal file
View file

@ -0,0 +1,61 @@
<!-- Copyright (C) 2012-2021 jrnl contributors
License: https://www.gnu.org/licenses/gpl-3.0.html -->
# Journal Types
`jrnl` can store your journal in a few different ways:
- a single text file (encrypted or otherwise)
- a folder structure organized by date containing unencrypted text files
- the DayOne Classic format
There is no need to specify what type of journal you'd like to use. Instead,
`jrnl` will automatically detect the journal type based on whether you're
referencing a file or a folder in your [config file](advanced.md),
and if it's a folder, whether or not DayOne Classic content exists in it.
## Single File
The single file format is the most flexible, as it can be [encrypted](encryption.md).
To use it, enter any path that is a file or does not already exist. You can
use any extension. `jrnl` will automatically create the file when you save
your first entry.
## Folder
The folder journal format organizes your entries into subfolders for the year
and month and `.txt` files for each day. If there are multiple entries in a day,
they all appear in the same `.txt` file.
The directory tree structure is in this format: `YYYY/MM/DD.txt`. For instance, if
you have an entry on May 5th, 2021 in a folder journal at `~/folderjournal`, it will
be located in: `~/folderjournal/2021/05/05.txt`
!!! note
When creating a new folder journal, you will need to create the folder before running
`jrnl`. Otherwise, when you run `jrnl` for the first time, it will assume that you
are creating a single file journal instead, and it will create a file at that path.
!!! note
Folder journals can't be encrypted.
## Day One Classic
`jrnl` supports the original data format used by DayOne. It's similar to the folder
journal format, except it's identified by either of these characteristics:
* the folder has a `.dayone` extension
* the folder has a subfolder named `entries`
This is not to be confused with the DayOne 2.0 format, [which is very different](https://help.dayoneapp.com/en/articles/1187337-day-one-classic-is-retired).
!!! note
DayOne Classic journals can't be encrypted.
## Changing your journal type
You can't simply modify a journal's configuration to change its type. Instead,
define a new journal as the type you'd like, and use
[piping](https://en.wikipedia.org/wiki/Redirection_(computing)#Piping)
to export your old journal as `txt` to an import command on your new journal.
For instance, if you have a `projects` journal you would like to import into
a `new` journal, you would run the following after setting up the configuration
for your `new` journal:
```
jrnl projects --format txt | jrnl new --import
```

View file

@ -86,6 +86,18 @@ you have a journal, where that journal file is, and when you last edited it.
With a sufficient power imbalance, someone may be able to force you to unencrypt
it through non-technical means.
## Saved Passwords
When creating an encrypted journal, you'll be prompted as to whether or not you
want to "store the password in your keychain." This keychain is accessed using
the [Python keyring library](https://pypi.org/project/keyring/), which has different
behavior depending on your operating system.
In Windows, the keychain is the Windows Credential Manager (WCM), which can't be locked
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.
## Notice any other risks?
Please let the maintainers know by [filing an issue on GitHub](https://github.com/jrnl-org/jrnl/issues).

View file

@ -154,6 +154,33 @@ only field 1.
jrnl -on "$(jrnl --short | shuf -n 1 | cut -d' ' -f1,2)"
```
### Launch a terminal for rapid logging
You can use this to launch a terminal that is the `jrnl` stdin prompt so you can start typing away immediately.
```bash
jrnl now --config-override editor:""
```
Bind this to a keyboard shortcut.
Map `Super+Alt+J` to launch the terminal with jrnl prompt
- **xbindkeys**
In your `.xbindkeysrc`
```ini
Mod4+Mod1+j
alacritty -t floating-jrnl -e jrnl now --config-override editor:"",
```
- **I3 WM** Launch a floating terminal with the `jrnl` prompt
```ini
bindsym Mod4+Mod1+j exec --no-startup-id alacritty -t floating-jrnl -e jrnl --config-override editor:""
for_window[title="floating *"] floating enable
```
## External editors
Configure your preferred external editor by updating the `editor` option
@ -162,10 +189,13 @@ in your `jrnl.yaml` file. (See [advanced usage](./advanced.md) for details).
!!! note
To save and log any entry edits, save and close the file.
If your editor is not in your operating system's `PATH` environment variable,
then you will have to enter in the full path of your editor.
### Sublime Text
To use Sublime Text, install the command line tools for Sublime Text and
configure your `jrnl.yaml` like this:
To use [Sublime Text](https://www.sublimetext.com/), install the command line
tools for Sublime Text and configure your `jrnl.yaml` like this:
```yaml
editor: "subl -w"
@ -174,9 +204,21 @@ editor: "subl -w"
Note the `-w` flag to make sure jrnl waits for Sublime Text to close the
file before writing into the journal.
### Visual Studio Code
[Visual Studio Code](https://code.visualstudio.com) also requires a flag
that tells the process to wait until the file is closed before exiting:
```yaml
editor: "code --wait"
```
On Windows, `code` is not added to the path by default, so you'll need to
enter the full path to your `code.exe` file, or add it to the `PATH` variable.
### MacVim
Similar to Sublime Text, MacVim must be started with a flag that tells
Also similar to Sublime Text, MacVim must be started with a flag that tells
the the process to wait until the file is closed before passing control
back to journal. In the case of MacVim, this is `-f`:
@ -220,28 +262,13 @@ The double backslashes are needed so jrnl can read the file path
correctly. The `-multiInst -nosession` options will cause jrnl to open
its own Notepad++ window.
### Visual Studio Code
To set [Visual Studo Code](https://code.visualstudio.com) as your editor on Linux, edit `jrnl.yaml` like this:
### emacs
To use `emacs` as your editor, edit the jrnl config file (`jrnl.yaml`) like this:
```yaml
editor: "/usr/bin/code --wait"
editor: emacsclient -a "" -c
```
The `--wait` argument tells VS Code to wait for files to be written out before handing back control to jrnl.
On MacOS you will need to add VS Code to your PATH. You can do that by adding:
```sh
export PATH="\$PATH:/Applications/Visual Studio Code.app/Contents/Resources/app/bin"
```
to your `.bash_profile`, or by running the **Install 'code' command in PATH** command from the command pallet in VS Code.
Then you can add:
```yaml
editor: "code --wait"
```
to `jrnl.yaml`. See also the [Visual Studio Code documentation](https://code.visualstudio.com/docs/setup/mac)
When you're done editing the message, save and `C-x #` to close the buffer and stop the emacsclient process.

12
docs_theme/index.html Executable file → Normal file
View file

@ -7,18 +7,18 @@
<meta charset="utf-8">
<title>jrnl - The Command Line Journal</title>
<meta name="description" content="Collect your thoughts and notes without leaving the command line.">
<meta name="image" content="https://jrnl.sh/img/banner.png">
<meta name="image" content="https://jrnl.sh/en/stable/img/banner_og.png">
<meta itemprop="name" content="jrnl - The Command Line Journal">
<meta itemprop="description" content="Collect your thoughts and notes without leaving the command line.">
<meta itemprop="image" content="https://jrnl.sh/img/banner_og.png">
<meta itemprop="image" content="https://jrnl.sh/en/stable/img/banner_og.png">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="jrnl - The Command Line Journal">
<meta name="twitter:description" content="Collect your thoughts and notes without leaving the command line.">
<meta name="twitter:creator" content="jrnl">
<meta name="twitter:image:src" content="https://jrnl.sh/img/banner_twitter.png">
<meta name="twitter:image:src" content="https://jrnl.sh/en/stable/img/banner_twitter.png">
<meta name="og:title" content="jrnl - The Command Line Journal">
<meta name="og:description" content="Collect your thoughts and notes without leaving the command line.">
<meta name="og:image" content="https://jrnl.sh/img/banner_og.png">
<meta name="og:image" content="https://jrnl.sh/en/stable/img/banner_og.png">
<meta name="og:url" content="https://jrnl.sh">
<meta name="og:site_name" content="jrnl - The Command Line Journal">
<meta name="og:type" content="website">
@ -35,8 +35,8 @@
"name": "jrnl",
"description": "Collect your thoughts and notes without leaving the command line.",
"operatingSystem": ["macOS", "Windows", "Linux"],
"thumbnailUrl": "https://jrnl.sh/img/banner_og.png",
"installUrl": "https://jrnl.sh/installation",
"thumbnailUrl": "https://jrnl.sh/en/stable/img/banner_og.png",
"installUrl": "https://jrnl.sh/en/stable/installation",
"softwareVersion": "2.5"
}
</script>

View file

@ -1,155 +0,0 @@
Feature: Reading and writing to journal with custom date formats
Scenario: Dates can include a time
# https://github.com/jrnl-org/jrnl/issues/117
Given we use the config "simple.yaml"
When we run "jrnl 2013-11-30 15:42: Project Started."
Then we should see the message "Entry added"
And the journal should contain "[2013-11-30 15:42] Project Started."
Scenario: Dates can be in the future
# https://github.com/jrnl-org/jrnl/issues/185
Given we use the config "simple.yaml"
When we run "jrnl 26/06/2099: Planet? Earth. Year? 2099."
Then we should see the message "Entry added"
And the journal should contain "[2099-06-26 09:00] Planet?"
Scenario: Loading a sample journal with custom date
Given we use the config "little_endian_dates.yaml"
When we run "jrnl -n 2"
Then we should get no error
And the output should be
"""
09.06.2013 15:39 My first entry.
| Everything is alright
10.07.2013 15:40 Life is good.
| But I'm better.
"""
Scenario Outline: Writing an entry from command line with custom date
Given we use the config "<config>.yaml"
When we run "jrnl <input>"
Then we should see the message "Entry added"
When we run "jrnl -n 1"
Then the output should contain "<output>"
Examples: Day-first Dates
| config | input | output |
| little_endian_dates | 2020-09-19: My first entry. | 19.09.2020 09:00 My first entry. |
| little_endian_dates | 2020-08-09: My second entry. | 09.08.2020 09:00 My second entry. |
| little_endian_dates | 2020-02-29: Test. | 29.02.2020 09:00 Test. |
| little_endian_dates | 2019-02-29: Test. | 2019-02-29: Test. |
| little_endian_dates | 2020-08-32: Test. | 2020-08-32: Test. |
| little_endian_dates | 2032-02-01: Test. | 01.02.2032 09:00 Test. |
| little_endian_dates | 2020-01-01: Test. | 01.01.2020 09:00 Test. |
| little_endian_dates | 2020-12-31: Test. | 31.12.2020 09:00 Test. |
Scenario Outline: Searching for dates with custom date
Given we use the config "<config>.yaml"
When we run "jrnl -on '<input>' --short"
Then the output should be "<output>"
Examples: Day-first Dates
| config | input | output |
| little_endian_dates | 2013-07-10 | 10.07.2013 15:40 Life is good. |
| little_endian_dates | june 9 2013 | 09.06.2013 15:39 My first entry. |
| little_endian_dates | july 10 2013 | 10.07.2013 15:40 Life is good. |
| little_endian_dates | june 2013 | 09.06.2013 15:39 My first entry. |
| little_endian_dates | july 2013 | 10.07.2013 15:40 Life is good. |
# @todo month alone with no year should work
# | little_endian_dates | june | 09.06.2013 15:39 My first entry. |
# | little_endian_dates | july | 10.07.2013 15:40 Life is good. |
Scenario: Writing an entry at the prompt with custom date
Given we use the config "little_endian_dates.yaml"
When we run "jrnl" and enter "2013-05-10: I saw Elvis. He's alive."
Then we should get no error
And the journal should contain "[10.05.2013 09:00] I saw Elvis."
And the journal should contain "He's alive."
Scenario: Viewing today's entries does not print the entire journal
# https://github.com/jrnl-org/jrnl/issues/741
Given we use the config "simple.yaml"
When we run "jrnl -on today"
Then the output should not contain "Life is good"
And the output should not contain "But I'm better."
Scenario Outline: Create entry using day of the week as entry date.
Given we use the config "simple.yaml"
When we run "jrnl <day>: This is an entry on a <day>."
Then we should see the message "Entry added"
When we run "jrnl -1"
Then the output should contain "<day> at 9am" in the local time
And the output should contain "This is an entry on a <day>."
Examples: Days of the week
| day |
| Monday |
| Tuesday |
| Wednesday |
| Thursday |
| Friday |
| Saturday |
| Sunday |
| sunday |
| sUndAy |
Scenario Outline: Create entry using day of the week abbreviations as entry date.
Given we use the config "simple.yaml"
When we run "jrnl <day>: This is an entry on a <weekday>."
Then we should see the message "Entry added"
When we run "jrnl -1"
Then the output should contain "<weekday> at 9am" in the local time
Examples: Days of the week
| day | weekday |
| mon | Monday |
| tue | Tuesday |
| wed | Wednesday |
| thu | Thursday |
| fri | Friday |
| sat | Saturday |
| sun | Sunday |
Scenario: Journals with unreadable dates should still be loaded
Given we use the config "unreadabledates.yaml"
When we run "jrnl -2"
Then the output should contain "I've lost track of time."
And the output should contain "Time has no meaning."
Scenario: Journals with readable dates AND unreadable dates should still contain all data.
Given we use the config "mostlyreadabledates.yaml"
When we run "jrnl -3"
Then the output should contain "Time machines are possible."
Then the output should contain "I'm going to activate the machine."
And the output should contain "I've crossed so many timelines. Is there any going back?"
And the journal should have 3 entries
Scenario: Update near-valid dates after journal is edited
Given we use the config "mostlyreadabledates.yaml"
When we run "jrnl 2222-08-19: I have made it exactly one month into the future."
Then the journal should contain "[2019-07-01 14:23] Entry subject"
Scenario: Integers in square brackets should not be read as dates
Given we use the config "brackets.yaml"
When we run "jrnl -1"
Then the output should contain "[1] line starting with 1"
# broken still
@skip
Scenario: Dayone entries without timezone information are interpreted in current timezone
Given we use the config "dayone.yaml"
When we run "jrnl -until 'feb 2013'"
Then we should get no error
And the output should contain "2013-01-17T18:37Z" in the local time
Scenario: Loading entry with ambiguous time stamp in timezone-aware journal (like Dayone)
#https://github.com/jrnl-org/jrnl/issues/153
Given we use the config "bug153.yaml"
When we run "jrnl -1"
Then we should get no error
And the output should be
"""
2013-10-27 03:27 Some text.
"""

View file

@ -1,229 +0,0 @@
Feature: Delete entries from journal
Scenario Outline: Delete flag allows deletion of single entry
Given we use the config "<config>.yaml"
And we use the password "test" if prompted
When we run "jrnl -1"
Then the output should contain "2020-09-24 09:14 The third entry finally"
When we run "jrnl --delete" and enter
"""
N
N
Y
"""
Then we flush the output
When we run "jrnl -99 --short"
Then the output should be
"""
2020-08-29 11:11 Entry the first.
2020-08-31 14:32 A second entry in what I hope to be a long series.
"""
Examples: Configs
| config |
| basic_onefile |
| basic_encrypted |
# | basic_folder | @todo
# | basic_dayone | @todo
Scenario Outline: Backing out of interactive delete does not change journal
Given we use the config "<config>.yaml"
When we run "jrnl --delete -n 1" and enter
"""
N
"""
Then we flush the output
When we run "jrnl -99 --short"
Then the output should be
"""
2020-08-29 11:11 Entry the first.
2020-08-31 14:32 A second entry in what I hope to be a long series.
2020-09-24 09:14 The third entry finally after weeks without writing.
"""
Examples: Configs
| config |
| basic_onefile |
| basic_folder |
| basic_dayone |
Scenario Outline: Delete flag with nonsense input deletes nothing (issue #932)
Given we use the config "<config>.yaml"
When we run "jrnl --delete asdfasdf"
Then we flush the output
When we run "jrnl -99 --short"
Then the output should be
"""
2020-08-29 11:11 Entry the first.
2020-08-31 14:32 A second entry in what I hope to be a long series.
2020-09-24 09:14 The third entry finally after weeks without writing.
"""
Examples: Configs
| config |
| basic_onefile |
| basic_folder |
| basic_dayone |
Scenario Outline: Delete flag with tag only deletes tagged entries
Given we use the config "<config>.yaml"
When we run "jrnl --delete @ipsum" and enter
"""
Y
"""
Then we flush the output
When we run "jrnl -99 --short"
Then the output should be
"""
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.
"""
Examples: Configs
| config |
| basic_onefile |
# | basic_folder | @todo
# | basic_dayone | @todo
Scenario Outline: Delete flag with multiple tags deletes all entries matching any of the tags
Given we use the config "<config>.yaml"
When we run "jrnl --delete @ipsum @tagthree" and enter
"""
Y
Y
"""
Then we flush the output
When we run "jrnl -99 --short"
Then the output should be
"""
2020-08-31 14:32 A second entry in what I hope to be a long series.
"""
Examples: Configs
| config |
| basic_onefile |
# | basic_folder | @todo
# | basic_dayone | @todo
Scenario Outline: Delete flag with -and deletes boolean AND of tagged entries
Given we use the config "<config>.yaml"
When we run "jrnl --delete -and @tagone @tagtwo" and enter
"""
Y
"""
Then we flush the output
When we run "jrnl -99 --short"
Then the output should be
"""
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.
"""
Examples: Configs
| config |
| basic_onefile |
# | basic_folder | @todo
# | basic_dayone | @todo
Scenario Outline: Delete flag with -not does not delete entries from given tag
Given we use the config "<config>.yaml"
When we run "jrnl --delete @tagone -not @ipsum" and enter
"""
Y
"""
Then we flush the output
When we run "jrnl -99 --short"
Then the output should be
"""
2020-08-29 11:11 Entry the first.
2020-08-31 14:32 A second entry in what I hope to be a long series.
"""
Examples: Configs
| config |
| basic_onefile |
# | basic_folder | @todo
# | basic_dayone | @todo
Scenario Outline: Delete flag with -from search operator only deletes entries since that date
Given we use the config "<config>.yaml"
When we run "jrnl --delete -from 2020-09-01" and enter
"""
Y
"""
Then we flush the output
When we run "jrnl -99 --short"
Then the output should be
"""
2020-08-29 11:11 Entry the first.
2020-08-31 14:32 A second entry in what I hope to be a long series.
"""
Examples: Configs
| config |
| basic_onefile |
# | basic_folder | @todo
# | basic_dayone | @todo
Scenario Outline: Delete flag with -to only deletes entries up to specified date
Given we use the config "<config>.yaml"
When we run "jrnl --delete -to 2020-08-31" and enter
"""
Y
Y
"""
Then we flush the output
When we run "jrnl -99 --short"
Then the output should be
"""
2020-09-24 09:14 The third entry finally after weeks without writing.
"""
Examples: Configs
| config |
| basic_onefile |
# | basic_folder | @todo
# | basic_dayone | @todo
Scenario Outline: Delete flag with -starred only deletes starred entries
Given we use the config "<config>.yaml"
When we run "jrnl --delete -starred" and enter
"""
Y
"""
Then we flush the output
When we run "jrnl -99 --short"
Then the output should be
"""
2020-08-29 11:11 Entry the first.
2020-09-24 09:14 The third entry finally after weeks without writing.
"""
Examples: Configs
| config |
| basic_onefile |
# | basic_folder | @todo
# | basic_dayone | @todo
Scenario Outline: Delete flag with -contains only entries containing expression
Given we use the config "<config>.yaml"
When we run "jrnl --delete -contains dignissim" and enter
"""
Y
"""
Then we flush the output
When we run "jrnl -99 --short"
Then the output should be
"""
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.
"""
Examples: Configs
| config |
| basic_onefile |
# | basic_folder | @todo
# | basic_dayone | @todo

View file

@ -1,35 +0,0 @@
Feature: Encrypting and decrypting journals
Scenario: Decrypting a journal
Given we use the config "encrypted.yaml"
When we run "jrnl --decrypt" and enter "bad doggie no biscuit"
Then the config for journal "default" should have "encrypt" set to "bool:False"
And we should see the message "Journal decrypted"
And the journal should have 2 entries
@todo
Scenario: Trying to decrypt an already unencrypted journal
# This should warn the user that the journal is already encrypted
Given we use the config "simple.yaml"
When we run "jrnl --decrypt"
Then the config for journal "default" should have "encrypt" set to "bool:False"
And the journal should have 2 entries
@todo
Scenario: Trying to encrypt an already encrypted journal
# This should warn the user that the journal is already encrypted
Scenario: Encrypting a journal
Given we use the config "simple.yaml"
When we run "jrnl --encrypt" and enter
"""
swordfish
swordfish
n
"""
Then we should see the message "Journal encrypted"
And the config for journal "default" should have "encrypt" set to "bool:True"
When we run "jrnl -n 1" and enter "swordfish"
Then we should be prompted for a password
And the output should contain "2013-06-10 15:40 Life is good"

View file

@ -1,86 +0,0 @@
import os
import shutil
from jrnl.os_compat import on_windows
CWD = os.getcwd()
# @see https://behave.readthedocs.io/en/latest/tutorial.html#debug-on-error-in-case-of-step-failures
BEHAVE_DEBUG_ON_ERROR = False
def setup_debug_on_error(userdata):
global BEHAVE_DEBUG_ON_ERROR
BEHAVE_DEBUG_ON_ERROR = userdata.getbool("BEHAVE_DEBUG_ON_ERROR")
def before_all(context):
setup_debug_on_error(context.config.userdata)
# def after_step(context, step):
# if BEHAVE_DEBUG_ON_ERROR and step.status == "failed":
# -- ENTER DEBUGGER: Zoom in on failure location.
# NOTE: Use IPython debugger, same for pdb (basic python debugger).
# import ipdb
# ipdb.post_mortem(step.exc_traceback)
def clean_all_working_dirs():
if os.path.exists("test.txt"):
os.remove("test.txt")
for folder in ("configs", "journals", "cache"):
working_dir = os.path.join("features", folder)
if os.path.exists(working_dir):
shutil.rmtree(working_dir)
def before_feature(context, feature):
# add "skip" tag
# https://stackoverflow.com/a/42721605/4276230
if "skip" in feature.tags:
feature.skip()
return
if "skip_win" in feature.tags and on_windows:
feature.skip("Skipping on Windows")
return
def before_scenario(context, scenario):
"""Before each scenario, backup all config and journal test data."""
# Clean up in case something went wrong
clean_all_working_dirs()
for folder in ("configs", "journals"):
original = os.path.join("features", "data", folder)
working_dir = os.path.join("features", folder)
if not os.path.exists(working_dir):
os.mkdir(working_dir)
for filename in os.listdir(original):
source = os.path.join(original, filename)
if os.path.isdir(source):
shutil.copytree(source, os.path.join(working_dir, filename))
else:
shutil.copy2(source, working_dir)
# add "skip" tag
# https://stackoverflow.com/a/42721605/4276230
if "skip" in scenario.effective_tags:
scenario.skip()
return
if "skip_win" in scenario.effective_tags and on_windows:
scenario.skip("Skipping on Windows")
return
def after_scenario(context, scenario):
"""After each scenario, restore all test data and remove working_dirs."""
if os.getcwd() != CWD:
os.chdir(CWD)
# only clean up if debugging is off and the scenario passed
if BEHAVE_DEBUG_ON_ERROR and scenario.status != "failed":
clean_all_working_dirs()
else:
clean_all_working_dirs()

View file

@ -1,643 +0,0 @@
Feature: Custom formats
Scenario Outline: JSON format
Given we use the config "<config>.yaml"
And we use the password "test" if prompted
When we run "jrnl --format json"
Then we should get no error
And the output should be parsable as json
And "entries" in the json output should have 3 elements
And "tags" in the json output should contain "@ipsum"
And "tags" in the json output should contain "@tagone"
And "tags" in the json output should contain "@tagthree"
And "tags" in the json output should contain "@tagtwo"
And entry 1 should have an array "tags" with 3 elements
And entry 2 should have an array "tags" with 1 elements
And entry 3 should have an array "tags" with 2 elements
Examples: configs
| config |
| basic_onefile |
| basic_encrypted |
| basic_folder |
| basic_dayone |
Scenario: Exporting dayone to json
Given we use the config "dayone.yaml"
When we run "jrnl --export json"
Then we should get no error
And the output should be parsable as json
And the json output should contain entries.0.uuid = "4BB1F46946AD439996C9B59DE7C4DDC1"
Scenario Outline: Printing a journal that has multiline entries with tags
Given we use the config "<config>.yaml"
And we use the password "test" if prompted
When we run "jrnl -n 1 @ipsum"
Then we should get no error
And the output should be
"""
2020-08-29 11:11 Entry the first.
| Lorem @ipsum dolor sit amet, consectetur adipiscing elit. Praesent malesuada
| quis est ac dignissim. Aliquam dignissim rutrum pretium. Phasellus
| pellentesque
| augue et venenatis facilisis. Suspendisse potenti. Sed dignissim sed nisl eu
| consequat. Aenean ante ex, elementum ut interdum et, mattis eget lacus. In
| commodo nulla nec tellus placerat, sed ultricies metus bibendum. Duis eget
| venenatis erat. In at dolor dui. @tagone and maybe also @tagtwo.
|
| Curabitur accumsan nunc ac neque tristique, eleifend faucibus justo
| ullamcorper. Suspendisse at mattis nunc. Nullam eget lacinia urna. Suspendisse
| potenti. Ut urna est, venenatis sed ante in, ultrices congue mi. Maecenas eget
| molestie metus. Mauris porttitor dui ornare gravida porta. Quisque sed lectus
| hendrerit, lacinia ante eget, vulputate ante. Aliquam vitae erat non felis
| feugiat sagittis. Phasellus quis arcu fringilla, mattis ligula id, vestibulum
| urna. Vivamus facilisis leo a mi tincidunt condimentum. Donec eu euismod enim.
| Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam eu ligula eget
| velit scelerisque fringilla. Phasellus pharetra justo et nulla fringilla, ac
| porta sapien accumsan. Class aptent taciti sociosqu ad litora torquent per
| conubia nostra, per inceptos himenaeos.
"""
Examples: configs
| config |
| basic_onefile |
| basic_encrypted |
| basic_folder |
| basic_dayone |
Scenario Outline: Exporting using filters should only export parts of the journal
Given we use the config "<config>.yaml"
And we use the password "test" if prompted
When we run "jrnl -until 'August 2020' --format json"
Then the output should be parsable as json
Then we should get no error
And the output should be parsable as json
And "entries" in the json output should have 2 elements
And "tags" in the json output should contain "@ipsum"
And "tags" in the json output should contain "@tagone"
And "tags" in the json output should contain "@tagtwo"
And entry 1 should have an array "tags" with 3 elements
And entry 2 should have an array "tags" with 1 elements
Examples: configs
| config |
| basic_onefile |
| basic_encrypted |
| basic_folder |
| basic_dayone |
Scenario Outline: Exporting using custom templates
Given we use the config "<config>.yaml"
And we load template "sample.template"
And we use the password "test" if prompted
When we run "jrnl -1 --format sample"
Then the output should be
"""
The third entry finally after weeks without writing.
----------------------------------------------------
I'm so excited about emojis. 💯 🎶 💩
Donec semper pellentesque iaculis. Nullam cursus et justo sit amet venenatis.
Vivamus tempus ex dictum metus vehicula gravida. Aliquam sed sem dolor. Nulla
eget ultrices purus. Quisque at nunc at quam pharetra consectetur vitae quis
dolor. Fusce ultricies purus eu est feugiat, quis scelerisque nibh malesuada.
Quisque egestas semper nibh in hendrerit. Nam finibus ex in mi mattis
vulputate. Sed mauris urna, consectetur in justo eu, volutpat accumsan justo.
Phasellus aliquam lacus placerat convallis vestibulum. Curabitur maximus at
ante eget fringilla. @tagthree and also @tagone
"""
Examples: configs
| config |
| basic_onefile |
| basic_encrypted |
| basic_folder |
| basic_dayone |
Scenario Outline: Increasing Headings on Markdown export
Given we use the config "<config>.yaml"
And we use the password "test" if prompted
When we open the editor and append
"""
[2020-10-14 13:23] Heading Test
H1-1
=
H1-2
===
H1-3
============================
H2-1
-
H2-2
---
H2-3
----------------------------------
Horizontal Rules (ignore)
---
===
# ATX H1
## ATX H2
### ATX H3
#### ATX H4
##### ATX H5
###### ATX H6
Stuff
More stuff
more stuff again
"""
Then we flush the output
When we run "jrnl -1 --export markdown"
Then the output should be
"""
# 2020
## October
### 2020-10-14 13:23 Heading Test
#### H1-1
#### H1-2
#### H1-3
##### H2-1
##### H2-2
##### H2-3
Horizontal Rules (ignore)
---
===
#### ATX H1
##### ATX H2
###### ATX H3
####### ATX H4
######## ATX H5
######### ATX H6
Stuff
More stuff
more stuff again
"""
Examples: configs
| config |
| basic_onefile |
| basic_encrypted |
| basic_folder |
# | basic_dayone | @todo
Scenario Outline: Add a blank line to Markdown export if there isn't one already
# https://github.com/jrnl-org/jrnl/issues/768
# https://github.com/jrnl-org/jrnl/issues/881
Given we use the config "<config>.yaml"
And we use the password "test" if prompted
When we open the editor and append
"""
[2020-10-29 11:11] First entry.
[2020-10-29 11:11] Second entry.
[2020-10-29 11:13] Third entry.
"""
Then we flush the output
When we run "jrnl -3 --format markdown"
Then the output should be
"""
# 2020
## October
### 2020-10-29 11:11 First entry.
### 2020-10-29 11:11 Second entry.
### 2020-10-29 11:13 Third entry.
"""
Examples: configs
| config |
| basic_onefile |
| basic_encrypted |
| basic_folder |
# | basic_dayone | @todo
@skip
Scenario Outline: Exporting to XML
Given we use the config "<config>.yaml"
And we use the password "test" if prompted
When we run "jrnl --export xml"
Then the output should be a valid XML string
And "entries" node in the xml output should have 3 elements
And "tags" in the xml output should contain ["@ipsum", "@tagone", "@tagtwo", "@tagthree"]
And there should be 10 "tag" elements
Examples: configs
| config |
# | basic_onefile | @todo
# | basic_encrypted | @todo
# | basic_folder | @todo
# | basic_dayone | @todo
Scenario: Exporting to XML
Given we use the config "tags.yaml"
And we use the password "test" if prompted
When we run "jrnl --export xml"
Then the output should be a valid XML string
And "entries" node in the xml output should have 2 elements
And "tags" in the xml output should contain ["@idea", "@journal", "@dan"]
And there should be 7 "tag" elements
Scenario Outline: Exporting tags
Given we use the config "<config>.yaml"
And we use the password "test" if prompted
When we run "jrnl --export tags"
Then the output should be
"""
@tagtwo : 2
@tagone : 2
@tagthree : 1
@ipsum : 1
"""
Examples: configs
| config |
| basic_onefile |
| basic_encrypted |
| basic_folder |
| basic_dayone |
@todo
Scenario Outline: Exporting fancy
# Needs better emoji support
Given we use the config "<config>.yaml"
And we use the password "test" if prompted
When we run "jrnl --export fancy"
Then the output should be
"""
2020-08-29 11:11
Entry the first.
Lorem @ipsum dolor sit amet, consectetur adipiscing elit. Praesent malesuada
quis est ac dignissim. Aliquam dignissim rutrum pretium. Phasellus
pellentesque
augue et venenatis facilisis. Suspendisse potenti. Sed dignissim sed nisl eu
consequat. Aenean ante ex, elementum ut interdum et, mattis eget lacus. In
commodo nulla nec tellus placerat, sed ultricies metus bibendum. Duis eget
venenatis erat. In at dolor dui. @tagone and maybe also @tagtwo.
Curabitur accumsan nunc ac neque tristique, eleifend faucibus justo
ullamcorper. Suspendisse at mattis nunc. Nullam eget lacinia urna.
Suspendisse
potenti. Ut urna est, venenatis sed ante in, ultrices congue mi. Maecenas
eget
molestie metus. Mauris porttitor dui ornare gravida porta. Quisque sed
lectus
hendrerit, lacinia ante eget, vulputate ante. Aliquam vitae erat non felis
feugiat sagittis. Phasellus quis arcu fringilla, mattis ligula id,
vestibulum
urna. Vivamus facilisis leo a mi tincidunt condimentum. Donec eu euismod
enim.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam eu ligula eget
velit scelerisque fringilla. Phasellus pharetra justo et nulla fringilla, ac
porta sapien accumsan. Class aptent taciti sociosqu ad litora torquent per
conubia nostra, per inceptos himenaeos.
2020-08-31 14:32
A second entry in what I hope to be a long series.
Sed sit amet metus et sapien feugiat elementum. Aliquam bibendum lobortis
leo
vitae tempus. Donec eleifend nec mi non volutpat. Lorem ipsum dolor sit
amet,
consectetur adipiscing elit. Praesent ut sodales libero. Maecenas nisl
lorem,
vestibulum in tempus sit amet, fermentum ut arcu. Donec vel vestibulum
lectus,
eget pretium enim. Maecenas diam nunc, imperdiet vitae pharetra sed, pretium
id
lectus. Donec eu metus et turpis tempor tristique ac non ex. In tellus arcu,
egestas at efficitur et, ultrices vel est. Sed commodo et nibh non
elementum.
Mauris tempus vitae neque vel viverra. @tagtwo all by its lonesome.
Nulla mattis elementum magna, viverra pretium dui fermentum et. Cras vel
vestibulum odio. Quisque sit amet turpis et urna finibus maximus. Interdum
et
malesuada fames ac ante ipsum primis in faucibus. Fusce porttitor iaculis
sem,
non dictum ipsum varius nec. Nulla eu erat at risus gravida blandit non vel
ante. Nam egestas ipsum leo, eu ultricies ipsum tincidunt vel. Morbi a
commodo
eros.
Nullam dictum, nisl ac varius tempus, ex tortor fermentum nisl, non
tempus dolor neque a lorem. Suspendisse a faucibus ex, vel ornare tortor.
Maecenas tincidunt id felis quis semper. Pellentesque enim libero, fermentum
quis metus id, rhoncus euismod magna. Nulla finibus velit eu purus bibendum
interdum. Integer id justo dui. Integer eu tellus in turpis bibendum
blandit.
Quisque auctor lacinia consectetur.
2020-09-24 09:14
The third entry finally after weeks without writing.
I'm so excited about emojis. 💯 🎶 💩
Donec semper pellentesque iaculis. Nullam cursus et justo sit amet
venenatis.
Vivamus tempus ex dictum metus vehicula gravida. Aliquam sed sem dolor.
Nulla
eget ultrices purus. Quisque at nunc at quam pharetra consectetur vitae quis
dolor. Fusce ultricies purus eu est feugiat, quis scelerisque nibh
malesuada.
Quisque egestas semper nibh in hendrerit. Nam finibus ex in mi mattis
vulputate. Sed mauris urna, consectetur in justo eu, volutpat accumsan
justo.
Phasellus aliquam lacus placerat convallis vestibulum. Curabitur maximus at
ante eget fringilla. @tagthree and also @tagone
"""
Examples: configs
| config |
| basic_onefile |
| basic_encrypted |
| basic_folder |
| basic_dayone |
@skip_win
Scenario Outline: Export to yaml
Given we use the config "<config>.yaml"
And we use the password "test" if prompted
And we create a cache directory
When we run "jrnl --export yaml -o {cache_dir}"
Then the cache should contain the files
"""
2020-08-29_entry-the-first.md
2020-08-31_a-second-entry-in-what-i-hope-to-be-a-long-series.md
2020-09-24_the-third-entry-finally-after-weeks-without-writing.md
"""
And the content of file "2020-08-29_entry-the-first.md" in the cache should be
"""
title: Entry the first.
date: 2020-08-29 11:11
starred: False
tags: tagone, ipsum, tagtwo
Lorem @ipsum dolor sit amet, consectetur adipiscing elit. Praesent malesuada
quis est ac dignissim. Aliquam dignissim rutrum pretium. Phasellus pellentesque
augue et venenatis facilisis. Suspendisse potenti. Sed dignissim sed nisl eu
consequat. Aenean ante ex, elementum ut interdum et, mattis eget lacus. In
commodo nulla nec tellus placerat, sed ultricies metus bibendum. Duis eget
venenatis erat. In at dolor dui. @tagone and maybe also @tagtwo.
Curabitur accumsan nunc ac neque tristique, eleifend faucibus justo
ullamcorper. Suspendisse at mattis nunc. Nullam eget lacinia urna. Suspendisse
potenti. Ut urna est, venenatis sed ante in, ultrices congue mi. Maecenas eget
molestie metus. Mauris porttitor dui ornare gravida porta. Quisque sed lectus
hendrerit, lacinia ante eget, vulputate ante. Aliquam vitae erat non felis
feugiat sagittis. Phasellus quis arcu fringilla, mattis ligula id, vestibulum
urna. Vivamus facilisis leo a mi tincidunt condimentum. Donec eu euismod enim.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam eu ligula eget
velit scelerisque fringilla. Phasellus pharetra justo et nulla fringilla, ac
porta sapien accumsan. Class aptent taciti sociosqu ad litora torquent per
conubia nostra, per inceptos himenaeos.
"""
Examples: configs
| config |
| basic_onefile |
| basic_encrypted |
| basic_folder |
# | basic_dayone |
@skip_win # @todo YAML exporter does not correctly export emoji on Windows
Scenario Outline: Add a blank line to YAML export if there isn't one already
# https://github.com/jrnl-org/jrnl/issues/768
# https://github.com/jrnl-org/jrnl/issues/881
Given we use the config "<config>.yaml"
And we use the password "test" if prompted
And we create a cache directory
When we run "jrnl --export yaml -o {cache_dir}"
Then the cache should contain the files
"""
2020-08-29_entry-the-first.md
2020-08-31_a-second-entry-in-what-i-hope-to-be-a-long-series.md
2020-09-24_the-third-entry-finally-after-weeks-without-writing.md
"""
And the content of file "2020-09-24_the-third-entry-finally-after-weeks-without-writing.md" in the cache should be
"""
title: The third entry finally after weeks without writing.
date: 2020-09-24 09:14
starred: False
tags: tagone, tagthree
I'm so excited about emojis. 💯 🎶 💩
Donec semper pellentesque iaculis. Nullam cursus et justo sit amet venenatis.
Vivamus tempus ex dictum metus vehicula gravida. Aliquam sed sem dolor. Nulla
eget ultrices purus. Quisque at nunc at quam pharetra consectetur vitae quis
dolor. Fusce ultricies purus eu est feugiat, quis scelerisque nibh malesuada.
Quisque egestas semper nibh in hendrerit. Nam finibus ex in mi mattis
vulputate. Sed mauris urna, consectetur in justo eu, volutpat accumsan justo.
Phasellus aliquam lacus placerat convallis vestibulum. Curabitur maximus at
ante eget fringilla. @tagthree and also @tagone
"""
Examples: configs
| config |
| basic_onefile |
| basic_encrypted |
| basic_folder |
# | basic_dayone | @todo
@skip_win
Scenario Outline: Export to Pelican Markdown
Given we use the config "<config>.yaml"
And we use the password "test" if prompted
And we create a cache directory
When we run "jrnl --export pelican-markdown -o {cache_dir}"
Then the cache should contain the files
"""
2020-08-29_entry-the-first.md
2020-08-31_a-second-entry-in-what-i-hope-to-be-a-long-series.md
2020-09-24_the-third-entry-finally-after-weeks-without-writing.md
"""
And the content of file "2020-08-29_entry-the-first.md" in the cache should be
"""
title: Entry the first.
date: 2020-08-29 11:11
starred: False
tags: tagone, ipsum, tagtwo
Lorem @ipsum dolor sit amet, consectetur adipiscing elit. Praesent malesuada
quis est ac dignissim. Aliquam dignissim rutrum pretium. Phasellus pellentesque
augue et venenatis facilisis. Suspendisse potenti. Sed dignissim sed nisl eu
consequat. Aenean ante ex, elementum ut interdum et, mattis eget lacus. In
commodo nulla nec tellus placerat, sed ultricies metus bibendum. Duis eget
venenatis erat. In at dolor dui. @tagone and maybe also @tagtwo.
Curabitur accumsan nunc ac neque tristique, eleifend faucibus justo
ullamcorper. Suspendisse at mattis nunc. Nullam eget lacinia urna. Suspendisse
potenti. Ut urna est, venenatis sed ante in, ultrices congue mi. Maecenas eget
molestie metus. Mauris porttitor dui ornare gravida porta. Quisque sed lectus
hendrerit, lacinia ante eget, vulputate ante. Aliquam vitae erat non felis
feugiat sagittis. Phasellus quis arcu fringilla, mattis ligula id, vestibulum
urna. Vivamus facilisis leo a mi tincidunt condimentum. Donec eu euismod enim.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam eu ligula eget
velit scelerisque fringilla. Phasellus pharetra justo et nulla fringilla, ac
porta sapien accumsan. Class aptent taciti sociosqu ad litora torquent per
conubia nostra, per inceptos himenaeos.
"""
Examples: configs
| config |
| basic_onefile |
| basic_encrypted |
| basic_folder |
# | basic_dayone |
@skip_win # @todo Pelican-Markdown exporter does not correctly export emoji on Windows
Scenario Outline: Add a blank line to Pelican Markdown export if there isn't one already
# https://github.com/jrnl-org/jrnl/issues/768
# https://github.com/jrnl-org/jrnl/issues/881
Given we use the config "<config>.yaml"
And we use the password "test" if prompted
And we create a cache directory
When we run "jrnl --export pelican-markdown -o {cache_dir}"
Then the cache should contain the files
"""
2020-08-29_entry-the-first.md
2020-08-31_a-second-entry-in-what-i-hope-to-be-a-long-series.md
2020-09-24_the-third-entry-finally-after-weeks-without-writing.md
"""
And the content of file "2020-09-24_the-third-entry-finally-after-weeks-without-writing.md" in the cache should be
"""
title: The third entry finally after weeks without writing.
date: 2020-09-24 09:14
starred: False
tags: tagone, tagthree
I'm so excited about emojis. 💯 🎶 💩
Donec semper pellentesque iaculis. Nullam cursus et justo sit amet venenatis.
Vivamus tempus ex dictum metus vehicula gravida. Aliquam sed sem dolor. Nulla
eget ultrices purus. Quisque at nunc at quam pharetra consectetur vitae quis
dolor. Fusce ultricies purus eu est feugiat, quis scelerisque nibh malesuada.
Quisque egestas semper nibh in hendrerit. Nam finibus ex in mi mattis
vulputate. Sed mauris urna, consectetur in justo eu, volutpat accumsan justo.
Phasellus aliquam lacus placerat convallis vestibulum. Curabitur maximus at
ante eget fringilla. @tagthree and also @tagone
"""
Examples: configs
| config |
| basic_onefile |
| basic_encrypted |
| basic_folder |
# | basic_dayone | @todo
Scenario: Empty DayOne entry bodies should not error
# https://github.com/jrnl-org/jrnl/issues/780
Given we use the config "bug780.yaml"
When we run "jrnl --short"
Then we should get no error
Scenario Outline: --short displays the short version of entries (only the title)
Given we use the config "<config>.yaml"
And we use the password "test" if prompted
When we run "jrnl -on 2020-08-31 --short"
Then the output should be "2020-08-31 14:32 A second entry in what I hope to be a long series."
Examples: configs
| config |
| basic_onefile |
| basic_encrypted |
| basic_folder |
| basic_dayone |
Scenario Outline: -s displays the short version of entries (only the title)
Given we use the config "<config>.yaml"
And we use the password "test" if prompted
When we run "jrnl -on 2020-08-31 -s"
Then the output should be "2020-08-31 14:32 A second entry in what I hope to be a long series."
Examples: configs
| config |
| basic_onefile |
| basic_encrypted |
| basic_folder |
| basic_dayone |
Scenario: Markdown Support from config file
Given we use the config "format_md.yaml"
When we run "jrnl -n 1"
Then the output should be
"""
# 2013
## June
### 2013-06-10 15:40 Life is good.
But I'm better.
"""
Scenario: Text Formatter from config file
Given we use the config "format_text.yaml"
When we run "jrnl -n 1"
Then the output should be
"""
[2013-06-10 15:40] Life is good.
But I'm better.
"""
Scenario Outline: Exporting entries with Cyrillic characters to directory should not fail
Given we use the config "<config>.yaml"
And we use the password "test" if prompted
And we create a cache directory
When we run "jrnl 2020-11-21: Первая"
When we run "jrnl --format md --file {cache_dir} -on 2020-11-21"
Then the cache should contain the files
"""
2020-11-21_первая.md
"""
Examples: configs
| config |
| basic_onefile |
| basic_encrypted |
| basic_folder |
| basic_dayone |

View file

@ -1,93 +0,0 @@
Feature: Importing data
Scenario Outline: --import allows new entry from stdin
Given we use the config "<config>.yaml"
And we use the password "test" if prompted
When we run "jrnl --import" and pipe "[2020-07-05 15:00] Observe and import."
Then we flush the output
When we run "jrnl -c import"
Then the output should contain "Observe and import"
Examples: Configs
| config |
| basic_onefile |
| basic_encrypted |
# | basic_folder | @todo
# | basic_dayone | @todo
Scenario Outline: --import allows new large entry from stdin
Given we use the config "<config>.yaml"
And we use the password "test" if prompted
When we run "jrnl --import" and pipe
"""
[2020-07-05 15:00] Observe and import.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent malesuada quis
est ac dignissim. Aliquam dignissim rutrum pretium. Phasellus pellentesque augue
et venenatis facilisis. Suspendisse potenti. Sed dignissim sed nisl eu consequat.
Aenean ante ex, elementum ut interdum et, mattis eget lacus. In commodo nulla nec
tellus placerat, sed ultricies metus bibendum. Duis eget venenatis erat. In at
dolor dui end of entry.
"""
Then we flush the output
When we run "jrnl -on 2020-07-05"
Then the output should contain "2020-07-05 15:00 Observe and import."
And the output should contain "Lorem ipsum"
And the output should contain "end of entry."
Examples: Configs
| config |
| basic_onefile |
| basic_encrypted |
# | basic_folder | @todo
# | basic_dayone | @todo
Scenario Outline: --import allows multiple new entries from stdin
Given we use the config "<config>.yaml"
And we use the password "test" if prompted
When we run "jrnl --import" and pipe
"""
[2020-07-05 15:00] Observe and import.
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
[2020-07-05 15:01] Twice as nice.
Sed dignissim sed nisl eu consequat.
"""
Then we flush the output
When we run "jrnl -on 2020-07-05"
Then the output should contain "2020-07-05 15:00 Observe and import."
And the output should contain "Lorem ipsum"
And the output should contain "2020-07-05 15:01 Twice as nice."
And the output should contain "Sed dignissim"
Examples: Configs
| config |
| basic_onefile |
| basic_encrypted |
# | basic_folder | @todo
# | basic_dayone | @todo
Scenario: --import allows import new entries from file
Given we use the config "simple.yaml"
Then the journal should contain "My first entry."
And the journal should contain "Life is good."
But the journal should not contain "I have an @idea"
And the journal should not contain "I met with"
When we run "jrnl --import --file features/journals/tags.journal"
Then the journal should contain "My first entry."
And the journal should contain "Life is good."
And the journal should contain "PROFIT!"
Scenario: --import prioritizes --file over pipe data if both are given
Given we use the config "simple.yaml"
Then the journal should contain "My first entry."
And the journal should contain "Life is good."
But the journal should not contain "I have an @idea"
And the journal should not contain "I met with"
When we run "jrnl --import --file features/journals/tags.journal" and pipe
"""
[2020-07-05 15:00] I should not exist!
"""
Then the journal should contain "My first entry."
And the journal should contain "PROFIT!"
But the journal should not contain "I should not exist!"

View file

@ -1,51 +0,0 @@
Feature: Multiple journals
Scenario: Loading a config with two journals
Given we use the config "multiple.yaml"
Then journal "default" should have 2 entries
And journal "work" should have 0 entries
Scenario: Write to default config by default
Given we use the config "multiple.yaml"
When we run "jrnl this goes to default"
Then journal "default" should have 3 entries
And journal "work" should have 0 entries
Scenario: Write to specified journal
Given we use the config "multiple.yaml"
When we run "jrnl work a long day in the office"
Then journal "default" should have 2 entries
And journal "work" should have 1 entry
Scenario: Tell user which journal was used
Given we use the config "multiple.yaml"
When we run "jrnl work a long day in the office"
Then we should see the message "Entry added to work journal"
Scenario: Write to specified journal with a timestamp
Given we use the config "multiple.yaml"
When we run "jrnl work 23 july 2012: a long day in the office"
Then journal "default" should have 2 entries
And journal "work" should have 1 entry
And journal "work" should contain "2012-07-23"
Scenario: Create new journals as required
Given we use the config "multiple.yaml"
Then journal "ideas" should not exist
When we run "jrnl ideas 23 july 2012: sell my junk on ebay and make lots of money"
Then journal "ideas" should have 1 entry
Scenario: Don't crash if no default journal is specified
Given we use the config "bug343.yaml"
When we run "jrnl a long day in the office"
Then we should see the message "No default journal configured"
Scenario: Don't crash if no file exists for a configured encrypted journal
Given we use the config "multiple.yaml"
When we run "jrnl new_encrypted Adding first entry" and enter
"""
these three eyes
these three eyes
n
"""
Then we should see the message "Encrypted journal 'new_encrypted' created"

View file

@ -1,228 +0,0 @@
Feature: Searching in a journal
Scenario Outline: Displaying entries using -on today should display entries created today
Given we use the config "<config>.yaml"
When we run "jrnl today: Adding an entry right now."
Then we should see the message "Entry added"
When we run "jrnl -on today"
Then the output should contain "Adding an entry right now."
But the output should not contain "Everything is alright"
And the output should not contain "Life is good"
Examples: configs
| config |
| simple |
| empty_folder |
| dayone |
Scenario Outline: Displaying entries using -from day should display correct entries
Given we use the config "<config>.yaml"
When we run "jrnl yesterday: This thing happened yesterday"
Then we should see the message "Entry added"
When we run "jrnl today at 11:59pm: Adding an entry right now."
Then we should see the message "Entry added"
When we run "jrnl tomorrow: A future entry."
Then we should see the message "Entry added"
When we run "jrnl -from today"
Then the output should contain "Adding an entry right now."
And the output should contain "A future entry."
And the output should not contain "This thing happened yesterday"
Examples: configs
| config |
| simple |
| empty_folder |
| dayone |
Scenario Outline: Displaying entries using -from and -to day should display correct entries
Given we use the config "<config>.yaml"
When we run "jrnl yesterday: This thing happened yesterday"
Then we should see the message "Entry added"
When we run "jrnl today at 11:59pm: Adding an entry right now."
Then we should see the message "Entry added"
When we run "jrnl tomorrow: A future entry."
Then we should see the message "Entry added"
When we run "jrnl -from yesterday -to today"
Then the output should contain "This thing happened yesterday"
And the output should contain "Adding an entry right now."
And the output should not contain "A future entry."
Examples: configs
| config |
| simple |
| empty_folder |
| dayone |
Scenario Outline: Searching for a string
Given we use the config "<config>.yaml"
When we run "jrnl -contains first --short"
Then we should get no error
And the output should be
"""
2020-08-29 11:11 Entry the first.
"""
Examples: configs
| config |
| basic_onefile |
| basic_folder |
| basic_dayone |
Scenario Outline: Searching for a string within tag results
Given we use the config "<config>.yaml"
When we run "jrnl @tagone -contains maybe"
Then we should get no error
And the output should contain "maybe"
Examples: configs
| config |
| basic_onefile |
| basic_folder |
| basic_dayone |
Scenario Outline: Searching for a string within AND tag results
Given we use the config "<config>.yaml"
When we run "jrnl -and @tagone @tagtwo -contains maybe"
Then we should get no error
And the output should contain "maybe"
Examples: configs
| config |
| basic_onefile |
| basic_folder |
| basic_dayone |
Scenario Outline: Searching for a string within NOT tag results
Given we use the config "<config>.yaml"
When we run "jrnl -not @tagone -contains lonesome"
Then we should get no error
And the output should contain "lonesome"
Examples: configs
| config |
| basic_onefile |
| basic_folder |
| basic_dayone |
Scenario Outline: Searching for dates
Given we use the config "<config>.yaml"
When we run "jrnl -on 2020-08-31 --short"
Then the output should be "2020-08-31 14:32 A second entry in what I hope to be a long series."
Then we flush the output
When we run "jrnl -on 'august 31 2020' --short"
Then the output should be "2020-08-31 14:32 A second entry in what I hope to be a long series."
Examples: configs
| config |
| basic_onefile |
| basic_folder |
| basic_dayone |
Scenario: Out of order entries to a Folder journal should be listed in date order
Given we use the config "empty_folder.yaml"
When we run "jrnl 3/7/2014 4:37pm: Second entry of journal."
Then we should see the message "Entry added"
When we run "jrnl 23 July 2013: Testing folder journal."
Then we should see the message "Entry added"
When we run "jrnl -2"
Then the output should be
"""
2013-07-23 09:00 Testing folder journal.
2014-03-07 16:37 Second entry of journal.
"""
Scenario Outline: Searching for all tags should show counts of each tag
Given we use the config "<config>.yaml"
When we run "jrnl --tags"
Then we should get no error
And the output should be
"""
@tagtwo : 2
@tagone : 2
@tagthree : 1
@ipsum : 1
"""
Examples: configs
| config |
| basic_onefile |
| basic_folder |
| basic_dayone |
Scenario Outline: Filtering journals should also filter tags
Given we use the config "<config>.yaml"
When we run "jrnl -from 'september 2020' --tags"
Then we should get no error
And the output should be
"""
@tagthree : 1
@tagone : 1
"""
Examples: configs
| config |
| basic_onefile |
| basic_folder |
| basic_dayone |
Scenario Outline: Excluding a tag should filter out all entries with that tag
Given we use the config "<config>.yaml"
When we run "jrnl --tags -not @tagtwo"
Then the output should be
"""
@tagthree : 1
@tagone : 1
"""
Examples: configs
| config |
| basic_onefile |
| basic_folder |
| basic_dayone |
Scenario Outline: Excluding multiple tags should filter out all entries with those tags
Given we use the config "<config>.yaml"
When we run "jrnl --tags -not @tagone -not @tagthree"
Then the output should be
"""
@tagtwo : 1
"""
Examples: configs
| config |
| basic_onefile |
| basic_folder |
| basic_dayone |
Scenario: DayOne tag searching should work with tags containing a mixture of upper and lower case.
# https://github.com/jrnl-org/jrnl/issues/354
Given we use the config "dayone.yaml"
When we run "jrnl @plAy"
Then the output should contain "2013-05-17 11:39 This entry has tags!"
Scenario: Loading a sample journal
Given we use the config "simple.yaml"
When we run "jrnl -2"
Then we should get no error
And the output should be
"""
2013-06-09 15:39 My first entry.
| Everything is alright
2013-06-10 15:40 Life is good.
| But I'm better.
"""
Scenario: Loading a DayOne Journal
Given we use the config "dayone.yaml"
When we run "jrnl -from 'feb 2013'"
Then we should get no error
And the output should be
"""
2013-05-17 11:39 This entry has tags!
2013-06-17 20:38 This entry has a location.
2013-07-17 11:38 This entry is starred!
"""

View file

@ -1,632 +0,0 @@
# Copyright (C) 2012-2021 jrnl contributors
# License: https://www.gnu.org/licenses/gpl-3.0.html
import ast
from collections import defaultdict
import os
from pathlib import Path
import re
import shlex
import time
from unittest.mock import patch
from behave import given
from behave import then
from behave import when
import keyring
import toml
import yaml
from jrnl import Journal
from jrnl import __version__
from jrnl import plugins
from jrnl.cli import cli
from jrnl.config import load_config
from jrnl.os_compat import on_windows
try:
import parsedatetime.parsedatetime_consts as pdt
except ImportError:
import parsedatetime as pdt
consts = pdt.Constants(usePyICU=False)
consts.DOWParseStyle = -1 # Prefers past weekdays
CALENDAR = pdt.Calendar(consts)
class TestKeyring(keyring.backend.KeyringBackend):
"""A test keyring that just stores its values in a hash"""
priority = 1
keys = defaultdict(dict)
def set_password(self, servicename, username, password):
self.keys[servicename][username] = password
def get_password(self, servicename, username):
return self.keys[servicename].get(username)
def delete_password(self, servicename, username):
self.keys[servicename][username] = None
class NoKeyring(keyring.backend.KeyringBackend):
"""A keyring that simulated an environment with no keyring backend."""
priority = 2
keys = defaultdict(dict)
def set_password(self, servicename, username, password):
raise keyring.errors.NoKeyringError
def get_password(self, servicename, username):
raise keyring.errors.NoKeyringError
def delete_password(self, servicename, username):
raise keyring.errors.NoKeyringError
class FailedKeyring(keyring.backend.KeyringBackend):
"""
A keyring that cannot be retrieved.
"""
priority = 2
def set_password(self, servicename, username, password):
raise keyring.errors.KeyringError
def get_password(self, servicename, username):
raise keyring.errors.KeyringError
def delete_password(self, servicename, username):
raise keyring.errors.KeyringError
# set a default keyring
keyring.set_keyring(TestKeyring())
def ushlex(command):
return shlex.split(command, posix=not on_windows)
def read_journal(context, journal_name="default"):
configuration = load_config(context.config_path)
with open(configuration["journals"][journal_name]) as journal_file:
journal = journal_file.read()
return journal
def open_journal(context, journal_name="default"):
configuration = load_config(context.config_path)
journal_conf = configuration["journals"][journal_name]
# We can override the default config on a by-journal basis
if type(journal_conf) is dict:
configuration.update(journal_conf)
# But also just give them a string to point to the journal file
else:
configuration["journal"] = journal_conf
return Journal.open_journal(journal_name, configuration)
def read_value_from_string(string):
if string[0] == "{":
# Handle value being a dictionary
return ast.literal_eval(string)
# Takes strings like "bool:true" or "int:32" and coerces them into proper type
t, value = string.split(":")
value = {"bool": lambda v: v.lower() == "true", "int": int, "str": str}[t](value)
return value
@given('we use the config "{config_file}"')
def set_config(context, config_file):
full_path = os.path.join("features/configs", config_file)
context.config_path = os.path.abspath(full_path)
if config_file.endswith("yaml") and os.path.exists(full_path):
# Add jrnl version to file for 2.x journals
with open(context.config_path, "a") as cf:
cf.write("version: {}".format(__version__))
@given('we use the password "{password}" if prompted')
def use_password_forever(context, password):
context.password = password
@given('we use the password "{password}" {num:d} times if prompted')
def use_password(context, password, num=1):
context.password = iter([password] * num)
@given("we have a keyring")
@given("we have a {type} keyring")
def set_keyring(context, type=""):
if type == "failed":
keyring.set_keyring(FailedKeyring())
else:
keyring.set_keyring(TestKeyring())
@given("we do not have a keyring")
def disable_keyring(context):
keyring.core.set_keyring(NoKeyring())
@when('we change directory to "{path}"')
def move_up_dir(context, path):
os.chdir(path)
@when("we open the editor and {method}")
@when('we open the editor and {method} "{text}"')
@when("we open the editor and {method} nothing")
@when("we open the editor and {method} nothing")
def open_editor_and_enter(context, method, text=""):
text = text or context.text or ""
if method == "enter":
file_method = "w+"
elif method == "append":
file_method = "a"
else:
file_method = "r+"
def _mock_editor(command):
context.editor_command = command
tmpfile = command[-1]
with open(tmpfile, file_method) as f:
f.write(text)
return tmpfile
if "password" in context:
password = context.password
else:
password = ""
# fmt: off
# see: https://github.com/psf/black/issues/664
with \
patch("subprocess.call", side_effect=_mock_editor) as mock_editor, \
patch("getpass.getpass", side_effect=_mock_getpass(password)) as mock_getpass, \
patch("sys.stdin.isatty", return_value=True), \
patch("jrnl.config.get_config_path", side_effect=lambda: context.config_path), \
patch("jrnl.install.get_config_path", side_effect=lambda: context.config_path) \
:
context.editor = mock_editor
context.getpass = mock_getpass
try:
cli(["--edit"])
context.exit_status = 0
except SystemExit as e:
context.exit_status = e.code
# fmt: on
@then("the editor should have been called")
@then("the editor should have been called with {num} arguments")
def count_editor_args(context, num=None):
assert context.editor.called
if isinstance(num, int):
assert len(context.editor_command) == int(num)
@then("the editor should not have been called")
def no_editor_called(context, num=None):
assert "editor" not in context or not context.editor.called
@then('one editor argument should be "{arg}"')
def contains_editor_arg(context, arg):
args = context.editor_command
assert (
arg in args and args.count(arg) == 1
), f"\narg not in args exactly 1 time:\n{arg}\n{str(args)}"
@then('one editor argument should match "{regex}"')
def matches_editor_arg(context, regex):
args = context.editor_command
matches = list(filter(lambda x: re.search(regex, x), args))
assert (
len(matches) == 1
), f"\nRegex didn't match exactly 1 time:\n{regex}\n{str(args)}"
@then("the editor file content should {method}")
@then("the editor file content should {method} empty")
@then('the editor file content should {method} "{text}"')
def contains_editor_file(context, method, text=""):
text = text or context.text or ""
content = context.editor_file.get("content")
format = f'\n"""\n{content}\n"""\n'
if method == "be":
assert content == text, format
elif method == "contain":
assert text in content, format
else:
assert False, f"Method '{method}' not supported"
@then('the temporary filename suffix should be "{suffix}"')
def extension_editor_file(context, suffix):
filename = Path(context.editor_file["name"]).name
delimiter = "-" if "-" in filename else "."
filename_suffix = delimiter + filename.split(delimiter)[-1]
assert filename_suffix == suffix
def _mock_getpass(inputs):
def prompt_return(prompt=""):
if type(inputs) == str:
return inputs
try:
return next(inputs)
except StopIteration:
raise KeyboardInterrupt
return prompt_return
def _mock_input(inputs):
def prompt_return(prompt=""):
try:
val = next(inputs)
print(prompt, val)
return val
except StopIteration:
raise KeyboardInterrupt
return prompt_return
@when('we run "{command}" and enter')
@when('we run "{command}" and enter nothing')
@when('we run "{command}" and enter "{inputs}"')
def run_with_input(context, command, inputs=""):
# create an iterator through all inputs. These inputs will be fed one by one
# to the mocked calls for 'input()', 'util.getpass()' and 'sys.stdin.read()'
if context.text:
text = iter(context.text.split("\n"))
else:
text = iter([inputs])
args = ushlex(command)[1:]
def _mock_editor(command):
context.editor_command = command
tmpfile = command[-1]
with open(tmpfile, "r") as editor_file:
file_content = editor_file.read()
context.editor_file = {"name": tmpfile, "content": file_content}
Path(tmpfile).touch()
if "password" in context:
password = context.password
else:
password = text
# fmt: off
# see: https://github.com/psf/black/issues/664
with \
patch("builtins.input", side_effect=_mock_input(text)) as mock_input, \
patch("getpass.getpass", side_effect=_mock_getpass(password)) as mock_getpass, \
patch("sys.stdin.read", side_effect=text) as mock_read, \
patch("subprocess.call", side_effect=_mock_editor) as mock_editor, \
patch("jrnl.config.get_config_path", side_effect=lambda: context.config_path), \
patch("jrnl.install.get_config_path", side_effect=lambda: context.config_path) \
:
try:
cli(args or [])
context.exit_status = 0
except SystemExit as e:
context.exit_status = e.code
# put mocks into context so they can be checked later in "then" statements
context.editor = mock_editor
context.input = mock_input
context.getpass = mock_getpass
context.read = mock_read
context.iter_text = text
context.execute_steps('''
Then all input was used
And at least one input method was called
''')
# fmt: on
@then("at least one input method was called")
def inputs_were_called(context):
assert (
context.input.called
or context.getpass.called
or context.read.called
or context.editor.called
)
@then("we should be prompted for a password")
def password_was_called(context):
assert context.getpass.called
@then("we should not be prompted for a password")
def password_was_not_called(context):
assert not context.getpass.called
@then("all input was used")
def all_input_was_used(context):
# all inputs were used (ignore if empty string)
for temp in context.iter_text:
assert "" == temp, "Not all inputs were consumed"
@when('we run "{command}"')
@when('we run "{command}" and pipe')
@when('we run "{command}" and pipe "{text}"')
def run(context, command, text=""):
text = text or context.text or ""
if "cache_dir" in context and context.cache_dir is not None:
cache_dir = os.path.join("features", "cache", context.cache_dir)
command = command.format(cache_dir=cache_dir)
args = ushlex(command)
def _mock_editor(command):
context.editor_command = command
tmpfile = command[-1]
with open(tmpfile, "r") as editor_file:
file_content = editor_file.read()
context.editor_file = {"name": tmpfile, "content": file_content}
Path(tmpfile).touch()
if "password" in context:
password = context.password
else:
password = iter(text)
try:
# fmt: off
# see: https://github.com/psf/black/issues/664
with \
patch("sys.argv", args), \
patch("getpass.getpass", side_effect=_mock_getpass(password)) as mock_getpass, \
patch("subprocess.call", side_effect=_mock_editor) as mock_editor, \
patch("sys.stdin.read", side_effect=lambda: text), \
patch("jrnl.config.get_config_path", side_effect=lambda: context.config_path), \
patch("jrnl.install.get_config_path", side_effect=lambda: context.config_path) \
:
context.editor = mock_editor
context.getpass = mock_getpass
cli(args[1:])
context.exit_status = 0
# fmt: on
except SystemExit as e:
context.exit_status = e.code
@given('we load template "{filename}"')
def load_template(context, filename):
full_path = os.path.join("features/data/templates", filename)
exporter = plugins.template_exporter.__exporter_from_file(full_path)
plugins.__exporter_types[exporter.names[0]] = exporter
@when('we set the keyring password of "{journal}" to "{password}"')
def set_keyring_password(context, journal, password):
keyring.set_password("jrnl", journal, password)
@then("we should get an error")
def has_error(context):
assert context.exit_status != 0, context.exit_status
@then("we should get no error")
def no_error(context):
assert context.exit_status == 0, context.exit_status
@then("we flush the output")
def flush_stdout(context):
context.stdout_capture.truncate(0)
context.stdout_capture.seek(0)
@then("we flush the error output")
def flush_stderr(context):
context.stderr_capture.truncate(0)
context.stderr_capture.seek(0)
@then("we flush all the output")
def flush_all_output(context):
context.execute_steps(
"""
Then we flush the output
Then we flush the error output
"""
)
@then("the output should be")
@then("the output should be empty")
@then('the output should be "{text}"')
def check_output(context, text=None):
text = (text or context.text or "").strip().splitlines()
out = context.stdout_capture.getvalue().strip().splitlines()
assert len(text) == len(out), "Output has {} lines (expected: {})".format(
len(out), len(text)
)
for line_text, line_out in zip(text, out):
assert line_text.strip() == line_out.strip(), [
line_text.strip(),
line_out.strip(),
]
@then('the output should contain "{text}" in the local time')
def check_output_time_inline(context, text):
out = context.stdout_capture.getvalue()
date, flag = CALENDAR.parse(text)
output_date = time.strftime("%Y-%m-%d %H:%M", date)
assert output_date in out, output_date
@then("the output should contain pyproject.toml version")
def check_output_version_inline(context):
out = context.stdout_capture.getvalue()
pyproject = (Path(__file__) / ".." / ".." / ".." / "pyproject.toml").resolve()
pyproject_contents = toml.load(pyproject)
pyproject_version = pyproject_contents["tool"]["poetry"]["version"]
assert pyproject_version in out, pyproject_version
@then("the output should contain")
@then('the output should contain "{text}"')
@then('the output should contain "{text}" or "{text2}"')
def check_output_inline(context, text=None, text2=None):
text = text or context.text
out = context.stdout_capture.getvalue()
assert (text and text in out) or (text2 and text2 in out)
@then("the error output should contain")
@then('the error output should contain "{text}"')
@then('the error output should contain "{text}" or "{text2}"')
def check_error_output_inline(context, text=None, text2=None):
text = text or context.text
out = context.stderr_capture.getvalue()
assert (text and text in out) or (text2 and text2 in out)
@then('the output should match "{regex}"')
@then('the output should match "{regex}" {num} times')
def matches_std_output(context, regex, num=1):
out = context.stdout_capture.getvalue()
matches = re.findall(regex, out)
assert (
matches and len(matches) == num
), f"\nRegex didn't match exactly {num} time(s):\n{regex}\n{str(out)}\n{str(matches)}"
@then('the error output should match "{regex}"')
@then('the error output should match "{regex}" {num} times')
def matches_err_ouput(context, regex, num=1):
out = context.stderr_capture.getvalue()
matches = re.findall(regex, out)
assert (
matches and len(matches) == num
), f"\nRegex didn't match exactly {num} time(s):\n{regex}\n{str(out)}\n{str(matches)}"
@then('the output should not contain "{text}"')
def check_output_not_inline(context, text):
out = context.stdout_capture.getvalue()
assert text not in out
@then('we should see the message "{text}"')
@then('the error output should be "{text}"')
def check_message(context, text):
out = context.stderr_capture.getvalue()
assert text in out, [text, out]
@then('we should not see the message "{text}"')
def check_not_message(context, text):
out = context.stderr_capture.getvalue()
assert text not in out, [text, out]
@then('the journal should contain "{text}"')
@then('journal "{journal_name}" should contain "{text}"')
def check_journal_content(context, text, journal_name="default"):
journal = read_journal(context, journal_name)
assert text in journal, journal
@then('the journal should not contain "{text}"')
@then('journal "{journal_name}" should not contain "{text}"')
def check_not_journal_content(context, text, journal_name="default"):
journal = read_journal(context, journal_name)
assert text not in journal, journal
@then("the journal should not exist")
@then('journal "{journal_name}" should not exist')
def journal_doesnt_exist(context, journal_name="default"):
configuration = load_config(context.config_path)
journal_path = configuration["journals"][journal_name]
assert not os.path.exists(journal_path)
@then("the journal should exist")
@then('journal "{journal_name}" should exist')
def journal_exists(context, journal_name="default"):
configuration = load_config(context.config_path)
journal_path = configuration["journals"][journal_name]
assert os.path.exists(journal_path)
@then('the config should have "{key}" set to')
@then('the config should have "{key}" set to "{value}"')
@then('the config for journal "{journal}" should have "{key}" set to "{value}"')
def config_var(context, key, value="", journal=None):
value = read_value_from_string(value or context.text or "")
configuration = load_config(context.config_path)
if journal:
configuration = configuration["journals"][journal]
assert key in configuration
assert configuration[key] == value
@then('the config for journal "{journal}" should not have "{key}" set')
def config_no_var(context, key, value="", journal=None):
configuration = load_config(context.config_path)
if journal:
configuration = configuration["journals"][journal]
assert key not in configuration
@then("the journal should have {number:d} entries")
@then("the journal should have {number:d} entry")
@then('journal "{journal_name}" should have {number:d} entries')
@then('journal "{journal_name}" should have {number:d} entry')
def check_journal_entries(context, number, journal_name="default"):
journal = open_journal(context, journal_name)
assert len(journal.entries) == number
@when("the journal directory is listed")
def list_journal_directory(context, journal="default"):
with open(context.config_path) as config_file:
configuration = yaml.load(config_file, Loader=yaml.FullLoader)
journal_path = configuration["journals"][journal]
for root, dirnames, f in os.walk(journal_path):
for file in f:
print(os.path.join(root, file))
@then("fail")
def debug_fail(context):
assert False

View file

@ -1,185 +0,0 @@
# Copyright (C) 2012-2021 jrnl contributors
# License: https://www.gnu.org/licenses/gpl-3.0.html
import json
import os
import shutil
import random
import string
from xml.etree import ElementTree
from behave import given
from behave import then
@then("the output should be parsable as json")
def check_output_json(context):
out = context.stdout_capture.getvalue()
assert json.loads(out), out
@then('"{field}" in the json output should have {number:d} elements')
@then('"{field}" in the json output should have 1 element')
def check_output_field(context, field, number=1):
out = context.stdout_capture.getvalue()
out_json = json.loads(out)
assert field in out_json, [field, out_json]
assert len(out_json[field]) == number, len(out_json[field])
@then('"{field}" in the json output should not contain "{key}"')
def check_output_field_not_key(context, field, key):
out = context.stdout_capture.getvalue()
out_json = json.loads(out)
assert field in out_json
assert key not in out_json[field]
@then('"{field}" in the json output should contain "{key}"')
def check_output_field_key(context, field, key):
out = context.stdout_capture.getvalue()
struct = json.loads(out)
for node in field.split("."):
try:
struct = struct[int(node)]
except ValueError:
assert node in struct
struct = struct[node]
assert key in struct
@then("the json output should contain {path}")
@then('the json output should contain {path} = "{value}"')
def check_json_output_path(context, path, value=None):
"""E.g.
the json output should contain entries.0.title = "hello"
"""
out = context.stdout_capture.getvalue()
struct = json.loads(out)
for node in path.split("."):
try:
struct = struct[int(node)]
except ValueError:
struct = struct[node]
if value is not None:
assert struct == value, struct
else:
assert struct is not None
@then(
'entry {entry_number:d} should have an array "{name}" with {items_number:d} elements'
)
def entry_array_count(context, entry_number, name, items_number):
# note that entry_number is 1-indexed.
out = context.stdout_capture.getvalue()
out_json = json.loads(out)
assert len(out_json["entries"][entry_number - 1][name]) == items_number
@then("the output should be a valid XML string")
def assert_valid_xml_string(context):
output = context.stdout_capture.getvalue()
xml_tree = ElementTree.fromstring(output)
assert xml_tree, output
@then('"{item}" node in the xml output should have {number:d} elements')
def assert_xml_output_entries_count(context, item, number):
output = context.stdout_capture.getvalue()
xml_tree = ElementTree.fromstring(output)
xml_tags = (node.tag for node in xml_tree)
assert item in xml_tags, str(list(xml_tags))
actual_entry_count = len(xml_tree.find(item))
assert actual_entry_count == number, actual_entry_count
@then('there should be {number:d} "{item}" elements')
def count_elements(context, number, item):
output = context.stdout_capture.getvalue()
xml_tree = ElementTree.fromstring(output)
assert len(xml_tree.findall(".//" + item)) == number
@then('"tags" in the xml output should contain {expected_tags_json_list}')
def assert_xml_output_tags(context, expected_tags_json_list):
output = context.stdout_capture.getvalue()
xml_tree = ElementTree.fromstring(output)
xml_tags = (node.tag for node in xml_tree)
assert "tags" in xml_tags, str(list(xml_tags))
expected_tags = json.loads(expected_tags_json_list)
actual_tags = set(t.attrib["name"] for t in xml_tree.find("tags"))
assert actual_tags == set(expected_tags), [actual_tags, set(expected_tags)]
@given('we create cache directory "{dir_name}"')
@given("we create a cache directory")
def create_directory(context, dir_name=None):
if not dir_name:
dir_name = "cache_" + "".join(
random.choices(string.ascii_uppercase + string.digits, k=20)
)
working_dir = os.path.join("features", "cache", dir_name)
if os.path.exists(working_dir):
shutil.rmtree(working_dir)
os.makedirs(working_dir)
context.cache_dir = dir_name
@then('cache "{dir_name}" should contain the files')
@then('cache "{dir_name}" should contain the files {expected_files_json_list}')
@then("the cache should contain the files")
def assert_dir_contains_files(context, dir_name=None, expected_files_json_list=""):
if not dir_name:
dir_name = context.cache_dir
working_dir = os.path.join("features", "cache", dir_name)
actual_files = os.listdir(working_dir)
expected_files = context.text or expected_files_json_list
expected_files = expected_files.split("\n")
# sort to deal with inconsistent default file ordering on different OS's
actual_files.sort()
expected_files.sort()
assert actual_files == expected_files, [actual_files, expected_files]
@then('the content of file "{file_path}" in cache directory "{cache_dir}" should be')
@then('the content of file "{file_path}" in the cache should be')
def assert_exported_yaml_file_content(context, file_path, cache_dir=None):
if not cache_dir:
cache_dir = context.cache_dir
expected_content = context.text.strip().splitlines()
full_file_path = os.path.join("features", "cache", cache_dir, file_path)
with open(full_file_path, "r") as f:
actual_content = f.read().strip().splitlines()
for actual_line, expected_line in zip(actual_content, expected_content):
if actual_line.startswith("tags: ") and expected_line.startswith("tags: "):
assert_equal_tags_ignoring_order(actual_line, expected_line)
else:
assert actual_line.strip() == expected_line.strip(), [
actual_line.strip(),
expected_line.strip(),
]
def assert_equal_tags_ignoring_order(actual_line, expected_line):
actual_tags = set(tag.strip() for tag in actual_line[len("tags: ") :].split(","))
expected_tags = set(
tag.strip() for tag in expected_line[len("tags: ") :].split(",")
)
assert actual_tags == expected_tags, [actual_tags, expected_tags]

View file

@ -1,6 +1,4 @@
#!/usr/bin/env python
from datetime import datetime
import datetime
import fnmatch
import os
from pathlib import Path
@ -34,6 +32,7 @@ class DayOne(Journal.Journal):
def __init__(self, **kwargs):
self.entries = []
self._deleted_entries = []
self.can_be_encrypted = False
super().__init__(**kwargs)
def open(self):
@ -80,35 +79,35 @@ class DayOne(Journal.Journal):
entry.creator_device_agent = dict_entry["Creator"][
"Device Agent"
]
except:
except: # noqa: E722
pass
try:
entry.creator_generation_date = dict_entry["Creator"][
"Generation Date"
]
except:
except: # noqa: E722
entry.creator_generation_date = date
try:
entry.creator_host_name = dict_entry["Creator"]["Host Name"]
except:
except: # noqa: E722
pass
try:
entry.creator_os_agent = dict_entry["Creator"]["OS Agent"]
except:
except: # noqa: E722
pass
try:
entry.creator_software_agent = dict_entry["Creator"][
"Software Agent"
]
except:
except: # noqa: E722
pass
try:
entry.location = dict_entry["Location"]
except:
except: # noqa: E722
pass
try:
entry.weather = dict_entry["Weather"]
except:
except: # noqa: E722
pass
self.entries.append(entry)
self.sort()
@ -118,7 +117,7 @@ class DayOne(Journal.Journal):
"""Writes only the entries that have been modified into plist files."""
for entry in self.entries:
if entry.modified:
utc_time = datetime.utcfromtimestamp(
utc_time = datetime.datetime.utcfromtimestamp(
time.mktime(entry.date.timetuple())
)

18
jrnl/Entry.py Executable file → Normal file
View file

@ -1,9 +1,8 @@
#!/usr/bin/env python
# Copyright (C) 2012-2021 jrnl contributors
# License: https://www.gnu.org/licenses/gpl-3.0.html
from datetime import datetime
import datetime
import re
import ansiwrap
@ -15,7 +14,7 @@ from .color import highlight_tags_with_background_color
class Entry:
def __init__(self, journal, date=None, text="", starred=False):
self.journal = journal # Reference to journal mainly to access its config
self.date = date or datetime.now()
self.date = date or datetime.datetime.now()
self.text = text
self._title = None
self._body = None
@ -204,14 +203,17 @@ class Entry:
# https://github.com/fnl/segtok
SENTENCE_SPLITTER = re.compile(
r"""
( # A sentence ends at one of two sequences:
[.!?\u2026\u203C\u203D\u2047\u2048\u2049\u22EF\u3002\uFE52\uFE57\uFF01\uFF0E\uFF1F\uFF61] # Either, a sequence starting with a sentence terminal,
(
[.!?\u2026\u203C\u203D\u2047\u2048\u2049\u22EF\uFE52\uFE57] # Sequence starting with a sentence terminal,
[\'\u2019\"\u201D]? # an optional right quote,
[\]\)]* # optional closing brackets and
\s+ # a sequence of required spaces.
)""",
[\]\)]* # optional closing bracket
\s+ # AND a sequence of required spaces.
)
|[\uFF01\uFF0E\uFF1F\uFF61\u3002] # CJK full/half width terminals usually do not have following spaces.
""",
re.VERBOSE,
)
SENTENCE_SPLITTER_ONLY_NEWLINE = re.compile("\n")

View file

@ -1,4 +1,3 @@
#!/usr/bin/env python
# encoding: utf-8
# Copyright (C) 2012-2021 jrnl contributors
# License: https://www.gnu.org/licenses/gpl-3.0.html
@ -23,10 +22,11 @@ def get_files(journal_config):
class Folder(Journal.Journal):
"""A Journal handling multiple files in a folder"""
def __init__(self, **kwargs):
def __init__(self, name="default", **kwargs):
self.entries = []
self._diff_entry_dates = []
super(Folder, self).__init__(**kwargs)
self.can_be_encrypted = False
super().__init__(name, **kwargs)
def open(self):
filenames = []
@ -44,10 +44,12 @@ class Folder(Journal.Journal):
# Create a list of dates of modified entries. Start with diff_entry_dates
modified_dates = self._diff_entry_dates
seen_dates = set(self._diff_entry_dates)
for e in self.entries:
if e.modified:
if e.date not in seen_dates:
if e.date not in modified_dates:
modified_dates.append(e.date)
if e.date not in seen_dates:
seen_dates.add(e.date)
# For every date that had a modified entry, write to a file
@ -81,8 +83,14 @@ class Folder(Journal.Journal):
# print("empty file: {}".format(filename))
os.remove(filename)
def delete_entries(self, entries_to_delete):
"""Deletes specific entries from a journal."""
for entry in entries_to_delete:
self.entries.remove(entry)
self._diff_entry_dates.append(entry.date)
def parse_editable_str(self, edited):
"""Parses the output of self.editable_str and updates it's entries."""
"""Parses the output of self.editable_str and updates its entries."""
mod_entries = self._parse(edited)
diff_entries = set(self.entries) - set(mod_entries)
for e in diff_entries:

View file

@ -1,9 +1,8 @@
#!/usr/bin/env python
# Copyright (C) 2012-2021 jrnl contributors
# License: https://www.gnu.org/licenses/gpl-3.0.html
from datetime import datetime
import datetime
import logging
import os
import re
@ -68,9 +67,11 @@ class Journal:
return new_journal
def import_(self, other_journal_txt):
self.entries = list(
frozenset(self.entries) | frozenset(self._parse(other_journal_txt))
)
imported_entries = self._parse(other_journal_txt)
for entry in imported_entries:
entry.modified = True
self.entries = list(frozenset(self.entries) | frozenset(imported_entries))
self.sort()
def open(self, filename=None):
@ -135,7 +136,9 @@ class Journal:
for match in date_blob_re.finditer(journal_txt):
date_blob = match.groups()[0]
try:
new_date = datetime.strptime(date_blob, self.config["timeformat"])
new_date = datetime.datetime.strptime(
date_blob, self.config["timeformat"]
)
except ValueError:
# Passing in a date that had brackets around it
new_date = time.parse(date_blob, bracketed=True)
@ -189,6 +192,9 @@ class Journal:
def filter(
self,
tags=[],
month=None,
day=None,
year=None,
start_date=None,
end_date=None,
starred=False,
@ -216,15 +222,26 @@ class Journal:
# 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
excluded = lambda tags: len([tag for tag in tags if tag in excluded_tags]) > 0
def excluded(tags):
return 0 < len([tag for tag in tags if tag in excluded_tags])
if contains:
contains_lower = contains.casefold()
# Create datetime object for comparison below
# this approach allows various formats
if month or day or year:
compare_d = time.parse(f"{month or 1}.{day or 1}.{year or 1}")
result = [
entry
for entry in self.entries
if (not tags or tagged(entry.tags))
and (not starred or entry.starred)
and (not month or entry.date.month == compare_d.month)
and (not day or entry.date.day == compare_d.day)
and (not year or entry.date.year == compare_d.year)
and (not start_date or entry.date >= start_date)
and (not end_date or entry.date <= end_date)
and (not exclude or not excluded(entry.tags))
@ -337,7 +354,7 @@ class LegacyJournal(Journal):
"""Parses a journal that's stored in a string and returns a list of entries"""
# Entries start with a line that looks like 'date title' - let's figure out how
# long the date will be by constructing one
date_length = len(datetime.today().strftime(self.config["timeformat"]))
date_length = len(datetime.datetime.today().strftime(self.config["timeformat"]))
# Initialise our current entry
entries = []
@ -347,7 +364,7 @@ class LegacyJournal(Journal):
line = line.rstrip()
try:
# try to parse line as date => new entry begins
new_date = datetime.strptime(
new_date = datetime.datetime.strptime(
line[:date_length], self.config["timeformat"]
)
@ -390,6 +407,12 @@ def open_journal(journal_name, config, legacy=False):
config["journal"] = os.path.expanduser(os.path.expandvars(config["journal"]))
if os.path.isdir(config["journal"]):
if config["encrypt"]:
print(
"Warning: This journal's config has 'encrypt' set to true, but this type of journal can't be encrypted.",
file=sys.stderr,
)
if config["journal"].strip("/").endswith(".dayone") or "entries" in os.listdir(
config["journal"]
):
@ -399,7 +422,7 @@ def open_journal(journal_name, config, legacy=False):
else:
from . import FolderJournal
return FolderJournal.Folder(**config).open()
return FolderJournal.Folder(journal_name, **config).open()
if not config["encrypt"]:
if legacy:

View file

@ -1,4 +1,3 @@
#!/usr/bin/env python
# Copyright (C) 2012-2021 jrnl contributors
# License: https://www.gnu.org/licenses/gpl-3.0.html

View file

@ -1,4 +1,3 @@
#!/usr/bin/env python
# Copyright (C) 2012-2021 jrnl contributors
# License: https://www.gnu.org/licenses/gpl-3.0.html

View file

@ -1 +1 @@
__version__ = "v2.6"
__version__ = "v2.8.3"

View file

@ -176,6 +176,30 @@ def parse_args(args=[]):
reading.add_argument(
"-on", dest="on_date", metavar="DATE", help="Show entries on this date"
)
reading.add_argument(
"-today-in-history",
dest="today_in_history",
action="store_true",
help="Show entries of today over the years",
)
reading.add_argument(
"-month",
dest="month",
metavar="DATE",
help="Show entries on this month of any year",
)
reading.add_argument(
"-day",
dest="day",
metavar="DATE",
help="Show entries on this day of any month",
)
reading.add_argument(
"-year",
dest="year",
metavar="DATE",
help="Show entries of a specific year",
)
reading.add_argument(
"-from",
dest="start_date",
@ -290,6 +314,29 @@ def parse_args(args=[]):
help=argparse.SUPPRESS,
)
config_overrides = parser.add_argument_group(
"Config file override",
textwrap.dedent("Apply a one-off override of the config file option"),
)
config_overrides.add_argument(
"--config-override",
dest="config_override",
action="append",
type=str,
nargs=2,
default=[],
metavar="CONFIG_KV_PAIR",
help="""
Override configured key-value pair with CONFIG_KV_PAIR for this command invocation only.
Examples: \n
\t - Use a different editor for this jrnl entry, call: \n
\t jrnl --config-override editor "nano" \n
\t - Override color selections\n
\t jrnl --config-override colors.body blue --config-override colors.title green
""",
)
# Handle '-123' as a shortcut for '-n 123'
num = re.compile(r"^-(\d+)$")
args = [num.sub(r"-n \1", arg) for arg in args]

View file

@ -1,4 +1,3 @@
#!/usr/bin/env python
# Copyright (C) 2012-2021 jrnl contributors
# License: https://www.gnu.org/licenses/gpl-3.0.html

View file

@ -1,4 +1,3 @@
#!/usr/bin/env python
import re
from string import punctuation
from string import whitespace
@ -7,7 +6,7 @@ import colorama
from .os_compat import on_windows
if on_windows:
if on_windows():
colorama.init()
WARNING_COLOR = colorama.Fore.YELLOW

View file

@ -13,6 +13,7 @@ avoid any possible overhead for these standalone commands.
"""
import platform
import sys
from .exception import JrnlError
def preconfig_diagnostic(_):
@ -68,6 +69,13 @@ def postconfig_encrypt(args, config, original_config, **kwargs):
# Open the journal
journal = open_journal(args.journal_name, config)
if hasattr(journal, "can_be_encrypted") and not journal.can_be_encrypted:
raise JrnlError(
"CannotEncryptJournalType",
journal_name=args.journal_name,
journal_type=journal.__class__.__name__,
)
journal.config["encrypt"] = True
new_journal = EncryptedJournal.from_journal(journal)
@ -87,7 +95,7 @@ def postconfig_encrypt(args, config, original_config, **kwargs):
def postconfig_decrypt(args, config, original_config, **kwargs):
""" Decrypts into new file. If filename is not set, we encrypt the journal file itself. """
"""Decrypts into new file. If filename is not set, we encrypt the journal file itself."""
from .Journal import PlainJournal
from .Journal import open_journal
from .config import update_config

View file

@ -19,12 +19,43 @@ XDG_RESOURCE = "jrnl"
DEFAULT_JOURNAL_NAME = "journal.txt"
DEFAULT_JOURNAL_KEY = "default"
YAML_SEPARATOR = ": "
YAML_FILE_ENCODING = "utf-8"
def make_yaml_valid_dict(input: list) -> dict:
"""
Convert a two-element list of configuration key-value pair into a flat dict.
The dict is created through the yaml loader, with the assumption that
"input[0]: input[1]" is valid yaml.
:param input: list of configuration keys in dot-notation and their respective values.
:type input: list
:return: A single level dict of the configuration keys in dot-notation and their respective desired values
:rtype: dict
"""
assert len(input) == 2
# yaml compatible strings are of the form Key:Value
yamlstr = YAML_SEPARATOR.join(input)
runtime_modifications = yaml.load(yamlstr, Loader=yaml.SafeLoader)
return runtime_modifications
def save_config(config):
config["version"] = __version__
with open(get_config_path(), "w") as f:
with open(get_config_path(), "w", encoding=YAML_FILE_ENCODING) as f:
yaml.safe_dump(
config, f, encoding="utf-8", allow_unicode=True, default_flow_style=False
config,
f,
encoding=YAML_FILE_ENCODING,
allow_unicode=True,
default_flow_style=False,
)
@ -113,8 +144,8 @@ def verify_config_colors(config):
def load_config(config_path):
"""Tries to load a config file from YAML."""
with open(config_path) as f:
return yaml.load(f, Loader=yaml.FullLoader)
with open(config_path, encoding=YAML_FILE_ENCODING) as f:
return yaml.load(f, Loader=yaml.SafeLoader)
def is_config_json(config_path):
@ -138,10 +169,18 @@ def update_config(config, new_config, scope, force_local=False):
def get_journal_name(args, config):
args.journal_name = DEFAULT_JOURNAL_KEY
if args.text and args.text[0] in config["journals"]:
args.journal_name = args.text[0]
# The first arg might be a journal name
if args.text:
potential_journal_name = args.text[0]
if potential_journal_name[-1] == ":":
potential_journal_name = potential_journal_name[0:-1]
if potential_journal_name in config["journals"]:
args.journal_name = potential_journal_name
args.text = args.text[1:]
elif DEFAULT_JOURNAL_KEY not in config["journals"]:
if args.journal_name not in config["journals"]:
print("No default journal configured.", file=sys.stderr)
print(list_journals(config), file=sys.stderr)
sys.exit(1)

View file

@ -1,6 +1,5 @@
import logging
import os
import shlex
import subprocess
import sys
import tempfile
@ -10,6 +9,7 @@ from pathlib import Path
from .color import ERROR_COLOR
from .color import RESET_COLOR
from .os_compat import on_windows
from .os_compat import split_args
def get_text_from_editor(config, template=""):
@ -25,8 +25,8 @@ def get_text_from_editor(config, template=""):
f.write(template)
try:
subprocess.call(shlex.split(config["editor"], posix=on_windows) + [tmpfile])
except Exception as e:
subprocess.call(split_args(config["editor"]) + [tmpfile])
except FileNotFoundError as e:
error_msg = f"""
{ERROR_COLOR}{str(e)}{RESET_COLOR}
@ -47,7 +47,7 @@ def get_text_from_editor(config, template=""):
def get_text_from_stdin():
_how_to_quit = "Ctrl+z and then Enter" if on_windows else "Ctrl+d"
_how_to_quit = "Ctrl+z and then Enter" if on_windows() else "Ctrl+d"
print(
f"[Writing Entry; on a blank line, press {_how_to_quit} to finish writing]\n",
file=sys.stderr,

View file

@ -14,7 +14,7 @@ class UpgradeValidationException(Exception):
class JrnlError(Exception):
"""Common exceptions raised by jrnl. """
"""Common exceptions raised by jrnl."""
def __init__(self, error_type, **kwargs):
self.error_type = error_type
@ -22,17 +22,31 @@ class JrnlError(Exception):
def _get_error_message(self, **kwargs):
error_messages = {
"ConfigDirectoryIsFile": textwrap.dedent(
"""
"ConfigDirectoryIsFile": """
The path to your jrnl configuration directory is a file, not a directory:
{config_directory_path}
Removing this file will allow jrnl to save its configuration.
"""
)
""",
"LineWrapTooSmallForDateFormat": """
The provided linewrap value of {config_linewrap} is too small by
{columns} columns to display the timestamps in the configured time
format for journal {journal}.
You can avoid this error by specifying a linewrap value that is larger
by at least {columns} in the configuration file or by using
--config-override at the command line
""",
"CannotEncryptJournalType": """
The journal {journal_name} can't be encrypted because it is a
{journal_type} journal.
To encrypt it, create a new journal referencing a file, export
this journal to the new journal, then encrypt the new journal.
""",
}
return error_messages[self.error_type].format(**kwargs)
pass
msg = error_messages[self.error_type].format(**kwargs)
msg = textwrap.dedent(msg)
return msg

View file

@ -1,4 +1,3 @@
#!/usr/bin/env python
# Copyright (C) 2012-2021 jrnl contributors
# License: https://www.gnu.org/licenses/gpl-3.0.html

View file

@ -15,6 +15,8 @@ from .config import get_config_path
from .editor import get_text_from_editor
from .editor import get_text_from_stdin
from .exception import UserAbort
from . import time
from .override import apply_overrides
def run(args):
@ -36,6 +38,10 @@ def run(args):
try:
config = install.load_or_install_jrnl()
original_config = config.copy()
# Apply config overrides
config = apply_overrides(args, config)
args = get_journal_name(args, config)
config = scope_config(config, args.journal_name)
except UserAbort as err:
@ -77,6 +83,10 @@ def _is_write_mode(args, config, **kwargs):
args.edit,
args.export,
args.end_date,
args.today_in_history,
args.month,
args.day,
args.year,
args.limit,
args.on_date,
args.short,
@ -202,12 +212,20 @@ def _get_editor_template(config, **kwargs):
def _search_journal(args, journal, **kwargs):
""" Search the journal with the given args"""
"""Search the journal with the given args"""
if args.on_date:
args.start_date = args.end_date = args.on_date
if args.today_in_history:
now = time.parse("now")
args.day = now.day
args.month = now.month
journal.filter(
tags=args.text,
month=args.month,
day=args.day,
year=args.year,
start_date=args.start_date,
end_date=args.end_date,
strict=args.strict,
@ -303,9 +321,12 @@ def _delete_search_results(journal, old_entries, **kwargs):
def _display_search_results(args, journal, **kwargs):
if args.short:
if args.short or args.export == "short":
print(journal.pprint(short=True))
elif args.export == "pretty":
print(journal.pprint())
elif args.tags:
print(plugins.get_exporter("tags").export(journal))

View file

@ -1,6 +1,18 @@
# Copyright (C) 2012-2021 jrnl contributors
# License: https://www.gnu.org/licenses/gpl-3.0.html
import shlex
from sys import platform
on_windows = "win32" in platform
def on_windows():
return "win32" in platform
def on_posix():
return not on_windows()
def split_args(args):
"""Split arguments and add escape characters as appropriate for the OS"""
return shlex.split(args, posix=on_posix())

70
jrnl/override.py Normal file
View file

@ -0,0 +1,70 @@
from .config import update_config, make_yaml_valid_dict
from argparse import Namespace
# import logging
def apply_overrides(args: Namespace, base_config: dict) -> dict:
"""Unpack CLI provided overrides into the configuration tree.
:param overrides: List of configuration key-value pairs collected from the CLI
:type overrides: list
:param base_config: Configuration Loaded from the saved YAML
:type base_config: dict
:return: Configuration to be used during runtime with the overrides applied
:rtype: dict
"""
overrides = vars(args).get("config_override", None)
if not overrides:
return base_config
cfg_with_overrides = base_config.copy()
for pairs in overrides:
pairs = make_yaml_valid_dict(pairs)
key_as_dots, override_value = _get_key_and_value_from_pair(pairs)
keys = _convert_dots_to_list(key_as_dots)
cfg_with_overrides = _recursively_apply(
cfg_with_overrides, keys, override_value
)
update_config(base_config, cfg_with_overrides, None)
return base_config
def _get_key_and_value_from_pair(pairs):
key_as_dots, override_value = list(pairs.items())[0]
return key_as_dots, override_value
def _convert_dots_to_list(key_as_dots):
keys = key_as_dots.split(".")
keys = [k for k in keys if k != ""] # remove empty elements
return keys
def _recursively_apply(tree: dict, nodes: list, override_value) -> dict:
"""Recurse through configuration and apply overrides at the leaf of the config tree
Credit to iJames on SO: https://stackoverflow.com/a/47276490 for algorithm
Args:
config (dict): Configuration to modify
nodes (list): Vector of override keys; the length of the vector indicates tree depth
override_value (str): Runtime override passed from the command-line
"""
key = nodes[0]
if len(nodes) == 1:
tree[key] = override_value
else:
next_key = nodes[1:]
next_node = _get_config_node(tree, key)
_recursively_apply(next_node, next_key, override_value)
return tree
def _get_config_node(config: dict, key: str):
if key in config:
pass
else:
config[key] = None
return config[key]

View file

@ -1,4 +1,3 @@
#!/usr/bin/env python
# encoding: utf-8
# Copyright (C) 2012-2021 jrnl contributors
# License: https://www.gnu.org/licenses/gpl-3.0.html
@ -9,6 +8,7 @@ from .json_exporter import JSONExporter
from .markdown_exporter import MarkdownExporter
from .pelican_markdown_exporter import PelicanMarkdownExporter
from .tag_exporter import TagExporter
from .dates_exporter import DatesExporter
from .template_exporter import __all__ as template_exporters
from .text_exporter import TextExporter
from .xml_exporter import XMLExporter
@ -18,6 +18,7 @@ __exporters = [
MarkdownExporter,
PelicanMarkdownExporter,
TagExporter,
DatesExporter,
TextExporter,
XMLExporter,
FancyExporter,
@ -25,6 +26,8 @@ __exporters = [
__importers = [JRNLImporter]
__exporter_types = {name: plugin for plugin in __exporters for name in plugin.names}
__exporter_types["pretty"] = None
__exporter_types["short"] = None
__importer_types = {name: plugin for plugin in __importers for name in plugin.names}
EXPORT_FORMATS = sorted(__exporter_types.keys())

View file

@ -0,0 +1,28 @@
# encoding: utf-8
# Copyright (C) 2012-2021 jrnl contributors
# License: https://www.gnu.org/licenses/gpl-3.0.html
from collections import Counter
from .text_exporter import TextExporter
class DatesExporter(TextExporter):
"""This Exporter lists dates and their respective counts, for heatingmapping etc."""
names = ["dates"]
extension = "dates"
@classmethod
def export_entry(cls, entry):
raise NotImplementedError
@classmethod
def export_journal(cls, journal):
"""Returns dates and their frequencies for an entire journal."""
date_counts = Counter()
for entry in journal.entries:
# entry.date.date() gets date without time
date = str(entry.date.date())
date_counts[date] += 1
result = "\n".join(f"{date}, {count}" for date, count in date_counts.items())
return result

View file

@ -1,8 +1,8 @@
#!/usr/bin/env python
# encoding: utf-8
# Copyright (C) 2012-2021 jrnl contributors
# License: https://www.gnu.org/licenses/gpl-3.0.html
from jrnl.exception import JrnlError
from textwrap import TextWrapper
from .text_exporter import TextExporter
@ -14,12 +14,14 @@ class FancyExporter(TextExporter):
names = ["fancy", "boxed"]
extension = "txt"
# Top border of the card
border_a = ""
border_b = ""
border_c = ""
border_d = ""
border_e = ""
border_f = ""
border_g = ""
border_h = ""
border_i = ""
@ -33,16 +35,19 @@ class FancyExporter(TextExporter):
"""Returns a fancy unicode representation of a single entry."""
date_str = entry.date.strftime(entry.journal.config["timeformat"])
linewrap = entry.journal.config["linewrap"] or 78
initial_linewrap = linewrap - len(date_str) - 2
initial_linewrap = max((1, linewrap - len(date_str) - 2))
body_linewrap = linewrap - 2
card = [
cls.border_a + cls.border_b * (initial_linewrap) + cls.border_c + date_str
]
check_provided_linewrap_viability(linewrap, card, entry.journal)
w = TextWrapper(
width=initial_linewrap,
initial_indent=cls.border_g + " ",
subsequent_indent=cls.border_g + " ",
)
title_lines = w.wrap(entry.title)
card.append(
title_lines[0].ljust(initial_linewrap + 1)
@ -74,3 +79,14 @@ class FancyExporter(TextExporter):
def export_journal(cls, journal):
"""Returns a unicode representation of an entire journal."""
return "\n".join(cls.export_entry(entry) for entry in journal)
def check_provided_linewrap_viability(linewrap, card, journal):
if len(card[0]) > linewrap:
width_violation = len(card[0]) - linewrap
raise JrnlError(
"LineWrapTooSmallForDateFormat",
config_linewrap=linewrap,
columns=width_violation,
journal=journal,
)

View file

@ -1,4 +1,3 @@
#!/usr/bin/env python
# encoding: utf-8
# Copyright (C) 2012-2021 jrnl contributors
# License: https://www.gnu.org/licenses/gpl-3.0.html

View file

@ -1,4 +1,3 @@
#!/usr/bin/env python
# encoding: utf-8
# Copyright (C) 2012-2021 jrnl contributors
# License: https://www.gnu.org/licenses/gpl-3.0.html

View file

@ -1,4 +1,3 @@
#!/usr/bin/env python
# encoding: utf-8
# Copyright (C) 2012-2021 jrnl contributors
# License: https://www.gnu.org/licenses/gpl-3.0.html

View file

@ -1,4 +1,3 @@
#!/usr/bin/env python
# encoding: utf-8
# Copyright (C) 2012-2021 jrnl contributors
# License: https://www.gnu.org/licenses/gpl-3.0.html

View file

@ -1,4 +1,3 @@
#!/usr/bin/env python
# encoding: utf-8
# Copyright (C) 2012-2021 jrnl contributors
# License: https://www.gnu.org/licenses/gpl-3.0.html

View file

@ -26,7 +26,7 @@ class Template:
def from_file(cls, filename):
with open(filename) as f:
front_matter, body = f.read().strip("-\n").split("---", 2)
front_matter = yaml.load(front_matter, Loader=yaml.FullLoader)
front_matter = yaml.load(front_matter, Loader=yaml.SafeLoader)
template = cls(body)
template.__dict__.update(front_matter)
return template

View file

@ -1,4 +1,3 @@
#!/usr/bin/env python
# encoding: utf-8
# Copyright (C) 2012-2021 jrnl contributors
# License: https://www.gnu.org/licenses/gpl-3.0.html

View file

@ -1,4 +1,3 @@
#!/usr/bin/env python
# encoding: utf-8
# Copyright (C) 2012-2021 jrnl contributors
# License: https://www.gnu.org/licenses/gpl-3.0.html

View file

@ -1,4 +1,3 @@
#!/usr/bin/env python
# encoding: utf-8
# Copyright (C) 2012-2021 jrnl contributors
# License: https://www.gnu.org/licenses/gpl-3.0.html

View file

@ -1,4 +1,3 @@
#!/usr/bin/env python
# encoding: utf-8
# Copyright (C) 2012-2021 jrnl contributors
# License: https://www.gnu.org/licenses/gpl-3.0.html

View file

@ -1,11 +1,11 @@
# Copyright (C) 2012-2021 jrnl contributors
# License: https://www.gnu.org/licenses/gpl-3.0.html
from datetime import datetime
import datetime
FAKE_YEAR = 9999
DEFAULT_FUTURE = datetime(FAKE_YEAR, 12, 31, 23, 59, 59)
DEFAULT_PAST = datetime(FAKE_YEAR, 1, 1, 0, 0)
DEFAULT_FUTURE = datetime.datetime(FAKE_YEAR, 12, 31, 23, 59, 59)
DEFAULT_PAST = datetime.datetime(FAKE_YEAR, 1, 1, 0, 0)
def __get_pdt_calendar():
@ -27,7 +27,7 @@ def parse(
"""Parses a string containing a fuzzy date and returns a datetime.datetime object"""
if not date_str:
return None
elif isinstance(date_str, datetime):
elif isinstance(date_str, datetime.datetime):
return date_str
# Don't try to parse anything with 6 or less characters and was parsed from the existing journal.
@ -44,7 +44,9 @@ def parse(
date = dateparse(date_str, default=default_date)
if date.year == FAKE_YEAR:
date = datetime(datetime.now().year, date.timetuple()[1:6])
date = datetime.datetime(
datetime.datetime.now().year, date.timetuple()[1:6]
)
else:
year_present = True
flag = 1 if date.hour == date.minute == 0 else 2
@ -52,7 +54,7 @@ def parse(
except Exception as e:
if e.args[0] == "day is out of range for month":
y, m, d, H, M, S = default_date.timetuple()[:6]
default_date = datetime(y, m, d - 1, H, M, S)
default_date = datetime.datetime(y, m, d - 1, H, M, S)
else:
calendar = __get_pdt_calendar()
date, flag = calendar.parse(date_str)
@ -60,26 +62,26 @@ def parse(
if not flag: # Oops, unparsable.
try: # Try and parse this as a single year
year = int(date_str)
return datetime(year, 1, 1)
return datetime.datetime(year, 1, 1)
except ValueError:
return None
except TypeError:
return None
if flag == 1: # Date found, but no time. Use the default time.
date = datetime(
date = datetime.datetime(
*date[:3],
hour=23 if inclusive else default_hour or 0,
minute=59 if inclusive else default_minute or 0,
second=59 if inclusive else 0
)
else:
date = datetime(*date[:6])
date = datetime.datetime(*date[:6])
# Ugly heuristic: if the date is more than 4 weeks in the future, we got the year wrong.
# Rather then this, we would like to see parsedatetime patched so we can tell it to prefer
# past dates
dt = datetime.now() - date
dt = datetime.datetime.now() - date
if dt.days < -28 and not year_present:
date = date.replace(date.year - 1)
return date

View file

@ -21,6 +21,7 @@ nav:
- Quickstart: installation.md
- Basic Usage: usage.md
- Encryption: encryption.md
- Journal Types: journal-types.md
- Privacy and Security: privacy-and-security.md
- Formats: formats.md
- Advanced Usage: advanced.md

1266
poetry.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
[tool.poetry]
name = "jrnl"
version = "v2.6"
version = "v2.8.3"
description = "Collect your thoughts and notes without leaving the command line."
authors = [
"jrnl contributors <jrnl-sh@googlegroups.com>",
@ -21,8 +21,13 @@ classifiers = [
"Operating System :: OS Independent"
]
[tool.poetry.urls]
"Documentation" = "https://jrnl.sh"
"Issue Tracker" = "https://github.com/jrnl-org/jrnl/issues"
"Funding" = "https://opencollective.com/jrnl"
[tool.poetry.dependencies]
python = ">=3.7.0, <3.10"
python = ">=3.7.0, <3.11"
ansiwrap = "^0.8.4"
asteval = "^0.9"
@ -38,14 +43,27 @@ pyyaml = ">=5.1"
pytz = ">=2020" # https://pythonhosted.org/pytz/#issues-limitations
tzlocal = ">2.0, <3.0" # https://github.com/regebro/tzlocal/blob/master/CHANGES.txt
# Minimal deps required for testing
# I don't like repeating deps here, but
# there's no other way to do this yet until poetry v1.2 releases
# see: https://github.com/python-poetry/poetry/issues/1644
pytest = { version = ">=6.2", optional = true }
pytest-bdd = { version = ">=4.0.1", optional = true }
toml = { version = ">=0.10", optional = true }
[tool.poetry.dev-dependencies]
behave = "^1.2"
mkdocs = "^1.0"
black = {version = "^20.8b1",allow-prereleases = true}
mkdocs = ">=1.0"
black = { version = ">=21.5b2", allow-prereleases = true }
toml = ">=0.10"
pyflakes = ">=2.2.0"
pytest = ">=6.2"
yq = ">=2.11"
pytest-bdd = ">=4.0.1"
ipdb = "*"
pytest-clarity = "*"
pyproject-flake8 = "*"
yq = "*"
[tool.poetry.extras]
testing = [ "pytest", "pytest-bdd", "toml" ]
[tool.poetry.scripts]
jrnl = 'jrnl.cli:cli'
@ -57,6 +75,29 @@ line_length = 88
known_first_party = ["jrnl"]
force_sort_within_sections = true
[tool.pytest.ini_options]
minversion = "6.0"
required_plugins = [
"pytest-bdd"
]
markers = [
"todo",
]
addopts = [
"--pdbcls=IPython.terminal.debugger:Pdb"
]
filterwarnings = [
"ignore::DeprecationWarning",
"ignore:Flag style will be deprecated in.*",
"ignore:[WinError 32].*",
"ignore:[WinError 5].*"
]
[tool.flake8]
# ignore formatting warnings and errors because we use Black to autoformat
extend-ignore = "E101,E111,E114,E115,E116,E117,E12,E13,E2,E3,E401,E5,E70,W1,W2,W3,W5"
[build-system]
requires = ["poetry>=1.1"]
build-backend = "poetry.masonry.api"

View file

@ -1,6 +1,5 @@
Feature: Build process
@deployment_tests
Scenario: Version numbers should stay in sync
Given we use the config "simple.yaml"
When we run "jrnl --version"

View file

@ -6,16 +6,13 @@ Feature: Functionality of jrnl outside of actually handling journals
Then we should get no error
Then the output should match "^jrnl version v\d+\.\d+(\.\d+)?(-(alpha|beta)\d*)?"
Scenario: Displaying the version number
Given we use the config "simple.yaml"
When we run "jrnl -v"
Then we should get no error
Then the output should match "^jrnl version v\d+\.\d+(\.\d+)?(-(alpha|beta)\d*)?"
Scenario: Running the diagnostic command
Given we use the config "simple.yaml"
When we run "jrnl --diagnostic"
Then the output should contain "jrnl"
And the output should contain "Python"
And the output should contain "OS"
@todo
Scenario: Listing available journals

View file

@ -0,0 +1,174 @@
Feature: Reading and writing to journal with custom date formats
Scenario: Dates can include a time
# https://github.com/jrnl-org/jrnl/issues/117
Given we use the config "simple.yaml"
When we run "jrnl 2013-11-30 15:42: Project Started."
Then we should see the message "Entry added"
When we run "jrnl -999"
Then the output should contain "2013-11-30 15:42 Project Started."
Scenario: Dates can be in the future
# https://github.com/jrnl-org/jrnl/issues/185
Given we use the config "simple.yaml"
When we run "jrnl 26/06/2099: Planet? Earth. Year? 2099."
Then we should see the message "Entry added"
When we run "jrnl -999"
Then the output should contain "2099-06-26 09:00 Planet?"
Scenario: Loading a sample journal with custom date
Given we use the config "little_endian_dates.yaml"
When we run "jrnl -n 2"
Then we should get no error
When we run "jrnl -n 999"
Then the output should be
09.06.2013 15:39 My first entry.
| Everything is alright
10.07.2013 15:40 Life is good.
| But I'm better.
Scenario Outline: Writing an entry from command line with custom date
Given we use the config "<config_file>"
When we run "jrnl <command>"
Then we should see the message "Entry added"
When we run "jrnl -n 1"
Then the output should contain "<expected_output>"
Examples: Day-first Dates
| config_file | command | expected_output |
| little_endian_dates.yaml | 2020-09-19: My first entry. | 19.09.2020 09:00 My first entry. |
| little_endian_dates.yaml | 2020-08-09: My second entry. | 09.08.2020 09:00 My second entry. |
| little_endian_dates.yaml | 2020-02-29: Test. | 29.02.2020 09:00 Test. |
| little_endian_dates.yaml | 2019-02-29: Test. | 2019-02-29: Test. |
| little_endian_dates.yaml | 2020-08-32: Test. | 2020-08-32: Test. |
| little_endian_dates.yaml | 2032-02-01: Test. | 01.02.2032 09:00 Test. |
| little_endian_dates.yaml | 2020-01-01: Test. | 01.01.2020 09:00 Test. |
| little_endian_dates.yaml | 2020-12-31: Test. | 31.12.2020 09:00 Test. |
Scenario Outline: Searching for dates with custom date
Given we use the config "<config_file>"
When we run "jrnl <command>"
Then the output should be "<expected_output>"
Examples: Day-first Dates
| config_file | command | expected_output |
| little_endian_dates.yaml | -on '2013-07-10' --short | 10.07.2013 15:40 Life is good. |
| little_endian_dates.yaml | -on 'june 9 2013' --short | 09.06.2013 15:39 My first entry. |
| little_endian_dates.yaml | -on 'july 10 2013' --short | 10.07.2013 15:40 Life is good. |
| little_endian_dates.yaml | -on 'june 2013' --short | 09.06.2013 15:39 My first entry. |
| little_endian_dates.yaml | -on 'july 2013' --short | 10.07.2013 15:40 Life is good. |
# @todo month alone with no year should work
# | little_endian_dates.yaml | -on 'june' --short | 09.06.2013 15:39 My first entry. |
# | little_endian_dates.yaml | -on 'july' --short | 10.07.2013 15:40 Life is good. |
Scenario: Writing an entry at the prompt with custom date
Given we use the config "little_endian_dates.yaml"
When we run "jrnl" and enter "2013-05-10: I saw Elvis. He's alive."
Then we should get no error
When we run "jrnl -999"
Then the output should contain "10.05.2013 09:00 I saw Elvis."
And the output should contain "He's alive."
Scenario: Viewing today's entries does not print the entire journal
# see: https://github.com/jrnl-org/jrnl/issues/741
Given we use the config "simple.yaml"
When we run "jrnl -on today"
Then the output should not contain "Life is good"
And the output should not contain "But I'm better."
Scenario Outline: Create entry using day of the week as entry date one.
Given we use the config "simple.yaml"
And now is "2019-03-12 01:30:32 PM"
When we run "jrnl <command>"
Then we should see the message "Entry added"
When we run "jrnl -1"
Then the output should contain "<expected_output>"
Then the output should contain the date "<date>"
Examples: Days of the week
| command | expected_output | date |
| Monday: entry on a monday | entry on a monday | 2019-03-11 09:00 |
| Tuesday: entry on a tuesday | entry on a tuesday | 2019-03-05 09:00 |
| Wednesday: entry on a wednesday | entry on a wednesday | 2019-03-06 09:00 |
| Thursday: entry on a thursday | entry on a thursday | 2019-03-07 09:00 |
| Friday: entry on a friday | entry on a friday | 2019-03-08 09:00 |
| Saturday: entry on a saturday | entry on a saturday | 2019-03-09 09:00 |
| Sunday: entry on a sunday | entry on a sunday | 2019-03-10 09:00 |
| sunday: entry on a sunday | entry on a sunday | 2019-03-10 09:00 |
| sUndAy: entry on a sunday | entry on a sunday | 2019-03-10 09:00 |
Scenario Outline: Create entry using day of the week as entry date two.
Given we use the config "simple.yaml"
And now is "2019-03-12 01:30:32 PM"
When we run "jrnl <command>"
Then we should see the message "Entry added"
When we run "jrnl -1"
Then the output should contain "<expected_output>"
Then the output should contain the date "<date>"
Examples: Days of the week
| command | expected_output | date |
| Mon: entry on a monday | entry on a monday | 2019-03-11 09:00 |
| Tue: entry on a tuesday | entry on a tuesday | 2019-03-05 09:00 |
| Wed: entry on a wednesday | entry on a wednesday | 2019-03-06 09:00 |
| Thu: entry on a thursday | entry on a thursday | 2019-03-07 09:00 |
| Fri: entry on a friday | entry on a friday | 2019-03-08 09:00 |
| Sat: entry on a saturday | entry on a saturday | 2019-03-09 09:00 |
| Sun: entry on a sunday | entry on a sunday | 2019-03-10 09:00 |
| sun: entry on a sunday | entry on a sunday | 2019-03-10 09:00 |
| sUn: entry on a sunday | entry on a sunday | 2019-03-10 09:00 |
Scenario: Journals with unreadable dates should still be loaded
Given we use the config "unreadabledates.yaml"
When we run "jrnl -2"
Then the output should contain "I've lost track of time."
And the output should contain "Time has no meaning."
Scenario: Journals with readable dates AND unreadable dates should still contain all data.
Given we use the config "mostlyreadabledates.yaml"
When we run "jrnl --short"
Then the output should be
2019-07-01 14:23 The third entry
2019-07-18 14:23 The first entry
2019-07-19 14:23 The second entry
Scenario: Update near-valid dates after journal is edited
Given we use the config "mostlyreadabledates.yaml"
When we run "jrnl 2222-08-19: I have made it exactly one month into the future."
When we run "jrnl -2"
Then the output should contain "2019-07-19 14:23 The second entry"
Scenario: Integers in square brackets should not be read as dates
Given we use the config "brackets.yaml"
When we run "jrnl -1"
Then the output should contain "[1] line starting with 1"
# broken still
@skip
Scenario: Dayone entries without timezone information are interpreted in current timezone
Given we use the config "dayone.yaml"
When we run "jrnl -until 'feb 2013'"
Then we should get no error
And the output should contain "2013-01-17T18:37Z" in the local time
Scenario: Loading entry with ambiguous time stamp in timezone-aware journal (like Dayone)
#https://github.com/jrnl-org/jrnl/issues/153
Given we use the config "bug153.yaml"
When we run "jrnl -1"
Then we should get no error
And the output should be
2013-10-27 03:27 Some text.

View file

@ -0,0 +1,182 @@
Feature: Delete entries from journal
Scenario Outline: Delete flag allows deletion of single entry
Given we use the config "<config_file>"
And we use the password "test" if prompted
When we run "jrnl -1"
Then the output should contain "2020-09-24 09:14 The third entry finally"
When we run "jrnl --delete" and enter
N
N
Y
When we run "jrnl -99 --short"
Then the output should be
2020-08-29 11:11 Entry the first.
2020-08-31 14:32 A second entry in what I hope to be a long series.
Examples: Configs
| config_file |
| basic_onefile.yaml |
| basic_encrypted.yaml |
| basic_folder.yaml |
# | basic_dayone.yaml | @todo
Scenario Outline: Backing out of interactive delete does not change journal
Given we use the config "<config_file>"
When we run "jrnl --delete -n 1" and enter
N
When we run "jrnl -99 --short"
Then the output should be
2020-08-29 11:11 Entry the first.
2020-08-31 14:32 A second entry in what I hope to be a long series.
2020-09-24 09:14 The third entry finally after weeks without writing.
Examples: Configs
| config_file |
| basic_onefile.yaml |
| basic_folder.yaml |
| basic_dayone.yaml |
Scenario Outline: Delete flag with nonsense input deletes nothing (issue #932)
Given we use the config "<config_file>"
When we run "jrnl --delete asdfasdf"
When we run "jrnl -99 --short"
Then the output should be
2020-08-29 11:11 Entry the first.
2020-08-31 14:32 A second entry in what I hope to be a long series.
2020-09-24 09:14 The third entry finally after weeks without writing.
Examples: Configs
| config_file |
| basic_onefile.yaml |
| basic_folder.yaml |
| basic_dayone.yaml |
Scenario Outline: Delete flag with tag only deletes tagged entries
Given we use the config "<config_file>"
When we run "jrnl --delete @ipsum" and enter
Y
When we run "jrnl -99 --short"
Then the output should be
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.
Examples: Configs
| config_file |
| basic_onefile.yaml |
| basic_folder.yaml |
# | basic_dayone.yaml | @todo
Scenario Outline: Delete flag with multiple tags deletes all entries matching any of the tags
Given we use the config "<config_file>"
When we run "jrnl --delete @ipsum @tagthree" and enter
Y
Y
When we run "jrnl -99 --short"
Then the output should be
2020-08-31 14:32 A second entry in what I hope to be a long series.
Examples: Configs
| config_file |
| basic_onefile.yaml |
| basic_folder.yaml |
# | basic_dayone.yaml | @todo
Scenario Outline: Delete flag with -and deletes boolean AND of tagged entries
Given we use the config "<config_file>"
When we run "jrnl --delete -and @tagone @tagtwo" and enter
Y
When we run "jrnl -99 --short"
Then the output should be
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.
Examples: Configs
| config_file |
| basic_onefile.yaml |
| basic_folder.yaml |
# | basic_dayone.yaml | @todo
Scenario Outline: Delete flag with -not does not delete entries from given tag
Given we use the config "<config_file>"
When we run "jrnl --delete @tagone -not @ipsum" and enter
Y
When we run "jrnl -99 --short"
Then the output should be
2020-08-29 11:11 Entry the first.
2020-08-31 14:32 A second entry in what I hope to be a long series.
Examples: Configs
| config_file |
| basic_onefile.yaml |
| basic_folder.yaml |
# | basic_dayone.yaml | @todo
Scenario Outline: Delete flag with -from search operator only deletes entries since that date
Given we use the config "<config_file>"
When we run "jrnl --delete -from 2020-09-01" and enter
Y
When we run "jrnl -99 --short"
Then the output should be
2020-08-29 11:11 Entry the first.
2020-08-31 14:32 A second entry in what I hope to be a long series.
Examples: Configs
| config_file |
| basic_onefile.yaml |
| basic_folder.yaml |
# | basic_dayone.yaml | @todo
Scenario Outline: Delete flag with -to only deletes entries up to specified date
Given we use the config "<config_file>"
When we run "jrnl --delete -to 2020-08-31" and enter
Y
Y
When we run "jrnl -99 --short"
Then the output should be
2020-09-24 09:14 The third entry finally after weeks without writing.
Examples: Configs
| config_file |
| basic_onefile.yaml |
| basic_folder.yaml |
# | basic_dayone.yaml | @todo
Scenario Outline: Delete flag with -starred only deletes starred entries
Given we use the config "<config_file>"
When we run "jrnl --delete -starred" and enter
Y
When we run "jrnl -99 --short"
Then the output should be
2020-08-29 11:11 Entry the first.
2020-09-24 09:14 The third entry finally after weeks without writing.
Examples: Configs
| config_file |
| basic_onefile.yaml |
| basic_folder.yaml |
# | basic_dayone.yaml | @todo
Scenario Outline: Delete flag with -contains only entries containing expression
Given we use the config "<config_file>"
When we run "jrnl --delete -contains dignissim" and enter
Y
When we run "jrnl -99 --short"
Then the output should be
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.
Examples: Configs
| config_file |
| basic_onefile.yaml |
| basic_folder.yaml |
# | basic_dayone.yaml | @todo

View file

@ -0,0 +1,64 @@
Feature: Encrypting and decrypting journals
Scenario: Decrypting a journal
Given we use the config "encrypted.yaml"
When we run "jrnl --decrypt" and enter "bad doggie no biscuit"
Then we should see the message "Journal decrypted"
And the config for journal "default" should contain "encrypt: false"
When we run "jrnl -99 --short"
Then the output should be
2013-06-09 15:39 My first entry.
2013-06-10 15:40 Life is good.
@todo
Scenario: Trying to decrypt an already unencrypted journal
# This should warn the user that the journal is already encrypted
Given we use the config "simple.yaml"
When we run "jrnl --decrypt"
Then the config for journal "default" should contain "encrypt: false"
When we run "jrnl -99 --short"
Then the output should be
2013-06-09 15:39 My first entry.
2013-06-10 15:40 Life is good.
@todo
Scenario: Trying to encrypt an already encrypted journal
# This should warn the user that the journal is already encrypted
Scenario Outline: Encrypting a journal
Given we use the config "simple.yaml"
When we run "jrnl --encrypt" and enter
swordfish
swordfish
n
Then we should get no error
And we should see the message "Journal encrypted"
And the config for journal "default" should contain "encrypt: true"
When we run "jrnl -n 1" and enter "swordfish"
Then we should be prompted for a password
And the output should contain "2013-06-10 15:40 Life is good"
Scenario Outline: Running jrnl with encrypt: true on unencryptable journals
Given we use the config "<config_file>"
When we run "jrnl --config-override encrypt true here is a new entry"
Then the error output should contain "this type of journal can't be encrypted"
Examples: configs
| config_file |
| basic_folder.yaml |
| basic_dayone.yaml |
Scenario Outline: Attempt to encrypt a folder or DayOne journal should result in an error
Given we use the config "<config_file>"
When we run "jrnl --encrypt"
Then the error output should contain "can't be encrypted"
Examples: configs
| config_file |
| basic_folder.yaml |
| basic_dayone.yaml |

View file

@ -4,53 +4,49 @@ Feature: Journals iteracting with the file system in a way that users can see
Given we use the config "empty_folder.yaml"
When we run "jrnl 23 July 2013: Testing folder journal."
Then we should see the message "Entry added"
When the journal directory is listed
Then the output should contain "2013/07/23.txt" or "2013\07\23.txt"
And the journal directory should contain
2013/07/23.txt
Scenario: Adding multiple entries to a Folder journal should generate multiple date files
Given we use the config "empty_folder.yaml"
When we run "jrnl 23 July 2013: Testing folder journal."
And we run "jrnl 3/7/2014: Second entry of journal."
Then we should see the message "Entry added"
When the journal directory is listed
Then the output should contain "2013/07/23.txt" or "2013\07\23.txt"
Then the output should contain "2014/03/07.txt" or "2014\03\07.txt"
And the journal directory should contain
2013/07/23.txt
Scenario: If the journal and its parent directory don't exist, they should be created
Given we use the config "missing_directory.yaml"
Then the journal should not exist
When we run "jrnl This is a new entry in my journal"
Then the journal should exist
When we run "jrnl -n 1"
When we run "jrnl -99 --short"
Then the output should contain "This is a new entry in my journal"
And the journal should have 1 entry
Scenario: If the journal file doesn't exist, then it should be created
Given we use the config "missing_journal.yaml"
Then the journal should not exist
When we run "jrnl This is a new entry in my journal"
Then the journal should exist
When we run "jrnl -n 1"
When we run "jrnl -99 --short"
Then the output should contain "This is a new entry in my journal"
And the journal should have 1 entry
Scenario: Creating journal with relative path should update to absolute path
Given we use the config "missingconfig"
When we run "jrnl hello world" and enter
"""
test.txt
n
"""
And we change directory to "features"
Then the output should contain "Journal 'default' created"
When we change directory to "subfolder"
And we run "jrnl -n 1"
Then the output should contain "hello world"
Scenario: the temporary filename suffix should default to ".jrnl"
Given we use the config "editor.yaml"
When we run "jrnl --edit"
Then the temporary filename suffix should be ".jrnl"
Then the editor should have been called
Then the editor filename should end with ".jrnl"
Scenario: the temporary filename suffix should be "-{template_filename}"
Given we use the config "editor_markdown_extension.yaml"
When we run "jrnl --edit"
Then the temporary filename suffix should be "-extension.md"
Then the editor filename should end with "-extension.md"

View file

@ -1,5 +1,33 @@
Feature: Custom formats
Scenario Outline: Short printing via --format flag
Given we use the config "<config_file>"
And we use the password "test" if prompted
When we run "jrnl --format short -3"
Then we should get no error
Examples: configs
| config_file |
| basic_onefile.yaml |
| basic_encrypted.yaml |
| basic_folder.yaml |
| basic_dayone.yaml |
Scenario Outline: Pretty Printing aka the Default
Given we use the config "<config_file>"
And we use the password "test" if prompted
When we run "jrnl --format pretty -3"
Then we should get no error
Examples: configs
| config_file |
| basic_onefile.yaml |
| basic_encrypted.yaml |
| basic_folder.yaml |
| basic_dayone.yaml |
Scenario Outline: JSON format
Given we use the config "<config_file>"
And we use the password "test" if prompted
@ -296,6 +324,22 @@ Feature: Custom formats
| basic_folder.yaml |
| basic_dayone.yaml |
Scenario Outline: Export fancy with small linewrap
Given we use the config "<config_file>"
And we use the password "test" if prompted
When we run "jrnl --config-override linewrap 35 --format fancy -3"
Then we should get no error
And the output should be 35 columns wide
Examples: configs
| config_file |
| basic_onefile.yaml |
| basic_encrypted.yaml |
| basic_folder.yaml |
| basic_dayone.yaml |
@todo
Scenario Outline: Exporting fancy
# Needs better emoji support
@ -393,25 +437,24 @@ Feature: Custom formats
| basic_folder.yaml |
| basic_dayone.yaml |
@skip_win # Travis has formatting issues here, but not locally
@skip_win
Scenario Outline: Export to yaml
Given we use the config "<config_file>"
And we use the password "test" if prompted
And we create a cache directory
When we run "jrnl --format yaml --file {cache_dir}"
Then the cache directory should contain the files
2020-08-29_entry-the-first.yaml
2020-08-31_a-second-entry-in-what-i-hope-to-be-a-long-series.yaml
2020-09-24_the-third-entry-finally-after-weeks-without-writing.yaml
2020-08-29_entry-the-first.md
2020-08-31_a-second-entry-in-what-i-hope-to-be-a-long-series.md
2020-09-24_the-third-entry-finally-after-weeks-without-writing.md
And the content of file "2020-08-29_entry-the-first.yaml" in the cache should be
And the content of file "2020-08-29_entry-the-first.md" in the cache should be
---
title: Entry the first.
date: 2020-08-29 11:11
starred: False
tags: tagone, ipsum, tagtwo
body: |
Lorem @ipsum dolor sit amet, consectetur adipiscing elit. Praesent malesuada
quis est ac dignissim. Aliquam dignissim rutrum pretium. Phasellus pellentesque
augue et venenatis facilisis. Suspendisse potenti. Sed dignissim sed nisl eu
@ -448,17 +491,16 @@ Feature: Custom formats
And we create a cache directory
When we run "jrnl --export yaml -o {cache_dir}"
Then the cache should contain the files
2020-08-29_entry-the-first.yaml
2020-08-31_a-second-entry-in-what-i-hope-to-be-a-long-series.yaml
2020-09-24_the-third-entry-finally-after-weeks-without-writing.yaml
And the content of file "2020-09-24_the-third-entry-finally-after-weeks-without-writing.yaml" in the cache should be
2020-08-29_entry-the-first.md
2020-08-31_a-second-entry-in-what-i-hope-to-be-a-long-series.md
2020-09-24_the-third-entry-finally-after-weeks-without-writing.md
And the content of file "2020-09-24_the-third-entry-finally-after-weeks-without-writing.md" in the cache should be
---
title: The third entry finally after weeks without writing.
date: 2020-09-24 09:14
starred: False
tags: tagone, tagthree
body: |
I'm so excited about emojis. 💯 🎶 💩
Donec semper pellentesque iaculis. Nullam cursus et justo sit amet venenatis.
@ -478,84 +520,6 @@ Feature: Custom formats
| basic_folder.yaml |
# | basic_dayone.yaml | @todo
Scenario Outline: Export to Pelican Markdown
Given we use the config "<config_file>"
And we use the password "test" if prompted
And we create a cache directory
When we run "jrnl --format pelican-markdown --file {cache_dir}"
Then the cache directory should contain the files
2020-08-29_entry-the-first.md
2020-08-31_a-second-entry-in-what-i-hope-to-be-a-long-series.md
2020-09-24_the-third-entry-finally-after-weeks-without-writing.md
And the content of file "2020-08-29_entry-the-first.md" in the cache should be
title: Entry the first.
date: 2020-08-29 11:11
starred: False
tags: tagone, ipsum, tagtwo
Lorem @ipsum dolor sit amet, consectetur adipiscing elit. Praesent malesuada
quis est ac dignissim. Aliquam dignissim rutrum pretium. Phasellus pellentesque
augue et venenatis facilisis. Suspendisse potenti. Sed dignissim sed nisl eu
consequat. Aenean ante ex, elementum ut interdum et, mattis eget lacus. In
commodo nulla nec tellus placerat, sed ultricies metus bibendum. Duis eget
venenatis erat. In at dolor dui. @tagone and maybe also @tagtwo.
Curabitur accumsan nunc ac neque tristique, eleifend faucibus justo
ullamcorper. Suspendisse at mattis nunc. Nullam eget lacinia urna. Suspendisse
potenti. Ut urna est, venenatis sed ante in, ultrices congue mi. Maecenas eget
molestie metus. Mauris porttitor dui ornare gravida porta. Quisque sed lectus
hendrerit, lacinia ante eget, vulputate ante. Aliquam vitae erat non felis
feugiat sagittis. Phasellus quis arcu fringilla, mattis ligula id, vestibulum
urna. Vivamus facilisis leo a mi tincidunt condimentum. Donec eu euismod enim.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam eu ligula eget
velit scelerisque fringilla. Phasellus pharetra justo et nulla fringilla, ac
porta sapien accumsan. Class aptent taciti sociosqu ad litora torquent per
conubia nostra, per inceptos himenaeos.
Examples: configs
| config_file |
| basic_onefile.yaml |
| basic_encrypted.yaml |
| basic_folder.yaml |
# | basic_dayone.yaml |
@skip_win # @todo YAML exporter does not correctly export emoji on Windows
Scenario Outline: Add a blank line to Pelican Markdown export if there isn't one already
# https://github.com/jrnl-org/jrnl/issues/768
# https://github.com/jrnl-org/jrnl/issues/881
Given we use the config "<config_file>"
And we use the password "test" if prompted
And we create a cache directory
When we run "jrnl --export pelican-markdown -o {cache_dir}"
Then the cache should contain the files
2020-08-29_entry-the-first.md
2020-08-31_a-second-entry-in-what-i-hope-to-be-a-long-series.md
2020-09-24_the-third-entry-finally-after-weeks-without-writing.md
And the content of file "2020-09-24_the-third-entry-finally-after-weeks-without-writing.md" in the cache should be
title: The third entry finally after weeks without writing.
date: 2020-09-24 09:14
starred: False
tags: tagone, tagthree
I'm so excited about emojis. 💯 🎶 💩
Donec semper pellentesque iaculis. Nullam cursus et justo sit amet venenatis.
Vivamus tempus ex dictum metus vehicula gravida. Aliquam sed sem dolor. Nulla
eget ultrices purus. Quisque at nunc at quam pharetra consectetur vitae quis
dolor. Fusce ultricies purus eu est feugiat, quis scelerisque nibh malesuada.
Quisque egestas semper nibh in hendrerit. Nam finibus ex in mi mattis
vulputate. Sed mauris urna, consectetur in justo eu, volutpat accumsan justo.
Phasellus aliquam lacus placerat convallis vestibulum. Curabitur maximus at
ante eget fringilla. @tagthree and also @tagone
Examples: configs
| config_file |
| basic_onefile.yaml |
| basic_encrypted.yaml |
| basic_folder.yaml |
# | basic_dayone.yaml | @todo
Scenario: Empty DayOne entry bodies should not error
# https://github.com/jrnl-org/jrnl/issues/780
Given we use the config "bug780.yaml"

View file

@ -0,0 +1,88 @@
Feature: Importing data
Scenario Outline: --import allows new entry from stdin
Given we use the config "<config_file>"
And we use the password "test" if prompted
When we run "jrnl --import" and pipe "[2020-07-05 15:00] Observe and import."
When we run "jrnl -9 --short"
Then the output should contain "Observe and import"
Examples: Configs
| config_file |
| basic_onefile.yaml |
| basic_encrypted.yaml |
| basic_folder.yaml |
# | basic_dayone.yaml | @todo
Scenario Outline: --import allows new large entry from stdin
Given we use the config "<config_file>"
And we use the password "test" if prompted
When we run "jrnl --import" and pipe
[2020-07-05 15:00] Observe and import.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent malesuada quis
est ac dignissim. Aliquam dignissim rutrum pretium. Phasellus pellentesque augue
et venenatis facilisis. Suspendisse potenti. Sed dignissim sed nisl eu consequat.
Aenean ante ex, elementum ut interdum et, mattis eget lacus. In commodo nulla nec
tellus placerat, sed ultricies metus bibendum. Duis eget venenatis erat. In at
dolor dui end of entry.
When we run "jrnl -on 2020-07-05"
Then the output should contain "2020-07-05 15:00 Observe and import."
And the output should contain "Lorem ipsum"
And the output should contain "end of entry."
Examples: Configs
| config_file |
| basic_onefile.yaml |
| basic_encrypted.yaml |
| basic_folder.yaml |
# | basic_dayone.yaml | @todo
Scenario Outline: --import allows multiple new entries from stdin
Given we use the config "<config_file>"
And we use the password "test" if prompted
When we run "jrnl --import" and pipe
[2020-07-05 15:00] Observe and import.
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
[2020-07-05 15:01] Twice as nice.
Sed dignissim sed nisl eu consequat.
When we run "jrnl -on 2020-07-05"
Then the output should contain "2020-07-05 15:00 Observe and import."
And the output should contain "Lorem ipsum"
And the output should contain "2020-07-05 15:01 Twice as nice."
And the output should contain "Sed dignissim"
Examples: Configs
| config_file |
| basic_onefile.yaml |
| basic_encrypted.yaml |
| basic_folder.yaml |
# | basic_dayone.yaml | @todo
Scenario: --import allows import new entries from file
Given we use the config "simple.yaml"
When we run "jrnl -99"
Then the output should contain "My first entry."
And the output should contain "Life is good."
But the output should not contain "I have an @idea"
And the output should not contain "I met with"
When we run "jrnl --import --file features/journals/tags.journal"
And we run "jrnl -99"
Then the output should contain "My first entry."
And the output should contain "Life is good."
And the output should contain "PROFIT!"
Scenario: --import prioritizes --file over pipe data if both are given
Given we use the config "simple.yaml"
When we run "jrnl -99"
Then the output should contain "My first entry."
And the output should contain "Life is good."
But the output should not contain "I have an @idea"
And the output should not contain "I met with"
When we run "jrnl --import --file features/journals/tags.journal" and pipe
[2020-07-05 15:00] I should not exist!
And we run "jrnl -99"
Then the output should contain "My first entry."
And the output should contain "PROFIT!"
But the output should not contain "I should not exist!"

View file

@ -0,0 +1,96 @@
Feature: Multiple journals
Scenario: Loading a config with two journals
Given we use the config "multiple.yaml"
When we run "jrnl -99 --short"
Then the output should be
2013-06-09 15:39 My first entry.
2013-06-10 15:40 Life is good.
When we run "jrnl work -99 --short"
Then the output should be empty
Scenario: Write to default config by default
Given we use the config "multiple.yaml"
When we run "jrnl this goes to default"
When we run "jrnl -99 --short"
Then the output should contain
2013-06-09 15:39 My first entry.
2013-06-10 15:40 Life is good.
Then the output should contain
this goes to default
When we run "jrnl work -99 --short"
Then the output should be empty
Scenario: Write to specified journal
Given we use the config "multiple.yaml"
When we run "jrnl work a long day in the office"
When we run "jrnl -99 --short"
Then the output should be
2013-06-09 15:39 My first entry.
2013-06-10 15:40 Life is good.
When we run "jrnl work -99 --short"
Then the output should contain "a long day in the office"
Scenario: Tell user which journal was used
Given we use the config "multiple.yaml"
When we run "jrnl work a long day in the office"
Then we should see the message "Entry added to work journal"
Scenario: Write to specified journal with a timestamp
Given we use the config "multiple.yaml"
When we run "jrnl work 23 july 2012: a long day in the office"
When we run "jrnl -99 --short"
Then the output should be
2013-06-09 15:39 My first entry.
2013-06-10 15:40 Life is good.
When we run "jrnl work -99 --short"
Then the output should be
2012-07-23 09:00 a long day in the office
Scenario: Write to specified journal without a timestamp but with colon
Given we use the config "multiple.yaml"
When we run "jrnl work : a long day in the office"
Then the output should be
2013-06-09 15:39 My first entry.
2013-06-10 15:40 Life is good.
When we run "jrnl work -99 --short"
Then the output should be contain
a long day in the office
Scenario: Write to specified journal without a timestamp but with colon
Given we use the config "multiple.yaml"
When we run "jrnl work: a long day in the office"
When we run "jrnl -99 --short"
Then the output should be
2013-06-09 15:39 My first entry.
2013-06-10 15:40 Life is good.
When we run "jrnl work -99 --short"
Then the output should contain
a long day in the office
Scenario: Create new journals as required
Given we use the config "multiple.yaml"
Then journal "ideas" should not exist
When we run "jrnl ideas 23 july 2012: sell my junk on ebay and make lots of money"
When we run "jrnl ideas -99 --short"
Then the output should be
2012-07-23 09:00 sell my junk on ebay and make lots of money
Scenario: Don't crash if no default journal is specified
Given we use the config "bug343.yaml"
When we run "jrnl a long day in the office"
Then the output should contain "No default journal configured"
Scenario: Don't crash if no file exists for a configured encrypted journal
Given we use the config "multiple.yaml"
When we run "jrnl new_encrypted Adding first entry" and enter
these three eyes
these three eyes
n
Then the output should contain "Encrypted journal 'new_encrypted' created"
Scenario: Read and write to journal with emoji name
Given we use the config "multiple.yaml"
When we run "jrnl Adding entry to sparkly journal"
When we run "jrnl -1"
Then the output should contain "Adding entry to sparkly journal"

View file

@ -0,0 +1,90 @@
Feature: Implementing Runtime Overrides for Select Configuration Keys
Scenario: Override configured editor with built-in input === editor:''
Given we use the config "basic_encrypted.yaml"
And we use the password "test" if prompted
When we run "jrnl --config-override editor ''"
Then the stdin prompt should have been called
And the editor should not have been called
Scenario: Postconfig commands with overrides
Given we use the config "basic_encrypted.yaml"
And we use the password "test" if prompted
When we run "jrnl --decrypt --config-override highlight false --config-override editor nano"
Then the config in memory should contain "highlight: false"
Then the editor should not have been called
Scenario: Override configured linewrap with a value of 23
Given we use the config "simple.yaml"
And we use the password "test" if prompted
When we run "jrnl -2 --config-override linewrap 23 --format fancy"
Then the output should be
2013-06-09 15:39
My
fir st ent ry.
Everything is
alright
2013-06-10 15:40
Lif
e is goo d.
But I'm better.
Scenario: Override color selections with runtime overrides
Given we use the config "basic_encrypted.yaml"
And we use the password "test" if prompted
When we run "jrnl -1 --config-override colors.body blue"
Then the config in memory should contain "colors.body: blue"
Scenario: Apply multiple config overrides
Given we use the config "basic_encrypted.yaml"
And we use the password "test" if prompted
When we run "jrnl -1 --config-override colors.body green --config-override editor 'nano'"
Then the config in memory should contain
editor: nano
colors:
title: none
body: green
tags: none
date: none
Scenario: Override default journal
Given we use the config "basic_dayone.yaml"
And we use the password "test" if prompted
When we run "jrnl --config-override journals.default features/journals/simple.journal 20 Mar 2000: The rain in Spain comes from clouds"
Then we should get no error
And we should see the message "Entry added"
When we run "jrnl -3 --config-override journals.default features/journals/simple.journal"
Then the output should be
2000-03-20 09:00 The rain in Spain comes from clouds
2013-06-09 15:39 My first entry.
| Everything is alright
2013-06-10 15:40 Life is good.
| But I'm better.
Scenario: Make an entry into an overridden journal
Given we use the config "basic_dayone.yaml"
And we use the password "test" if prompted
When we run "jrnl --config-override journals.temp features/journals/simple.journal temp Sep 06 1969: @say Ni"
Then we should get no error
And we should see the message "Entry added"
When we run "jrnl --config-override journals.temp features/journals/simple.journal temp -3"
Then the output should be
1969-09-06 09:00 @say Ni
2013-06-09 15:39 My first entry.
| Everything is alright
2013-06-10 15:40 Life is good.
| But I'm better.

View file

@ -4,113 +4,119 @@ Feature: Using the installed keyring
Given we use the config "multiple.yaml"
And we have a keyring
When we run "jrnl simple --encrypt" and enter
"""
sabertooth
sabertooth
y
"""
Then the config for journal "simple" should have "encrypt" set to "bool:True"
Y
Then the config for journal "simple" should contain "encrypt: true"
When we run "jrnl simple -n 1"
Then the output should contain "2013-06-10 15:40 Life is good"
Scenario: Encrypt journal with no keyring backend and do not store in keyring
Given we use the config "simple.yaml"
And we do not have a keyring
When we run "jrnl test entry"
And we run "jrnl --encrypt" and enter
"""
password
password
n
"""
Then we should get no error
And we should not see the message "Failed to retrieve keyring"
And the output should not contain "Failed to retrieve keyring"
Scenario: Encrypt journal with no keyring backend and do store in keyring
Given we use the config "simple.yaml"
And we do not have a keyring
When we run "jrnl test entry"
And we run "jrnl --encrypt" and enter
"""
password
password
y
"""
Then we should get no error
And we should not see the message "Failed to retrieve keyring"
And the output should not contain "Failed to retrieve keyring"
# @todo add step to check contents of keyring
@todo
Scenario: Open an encrypted journal with wrong password in keyring
# This should ask the user for the password after the keyring fails
@todo
Scenario: Decrypt journal with password in keyring
@todo
Scenario: Decrypt journal without a keyring
Scenario: Encrypt journal when keyring exists but fails
Given we use the config "simple.yaml"
And we have a failed keyring
When we run "jrnl --encrypt" and enter
"""
this password will not be saved in keyring
this password will not be saved in keyring
y
"""
Then we should see the message "Failed to retrieve keyring"
And we should get no error
And we should be prompted for a password
And the config for journal "default" should have "encrypt" set to "bool:True"
And the config for journal "default" should contain "encrypt: true"
Scenario: Decrypt journal when keyring exists but fails
Given we use the config "encrypted.yaml"
And we have a failed keyring
When we run "jrnl --decrypt" and enter "bad doggie no biscuit"
Then we should see the message "Failed to retrieve keyring"
And we use the password "bad doggie no biscuit" if prompted
When we run "jrnl --decrypt"
Then the error output should contain "Failed to retrieve keyring"
And we should get no error
And we should be prompted for a password
And we should see the message "Journal decrypted"
And the config for journal "default" should have "encrypt" set to "bool:False"
And the journal should have 2 entries
And the config for journal "default" should contain "encrypt: false"
When we run "jrnl --short"
Then we should not be prompted for a password
And the output should be
2013-06-09 15:39 My first entry.
2013-06-10 15:40 Life is good.
Scenario: Open encrypted journal when keyring exists but fails
# This should ask the user for the password after the keyring fails
Given we use the config "encrypted.yaml"
And we have a failed keyring
When we run "jrnl -n 1" and enter "bad doggie no biscuit"
Then we should see the message "Failed to retrieve keyring"
And we should get no error
And we use the password "bad doggie no biscuit" if prompted
When we run "jrnl -n 1"
Then we should get no error
And we should be prompted for a password
And the output should contain "Failed to retrieve keyring"
And the output should contain "2013-06-10 15:40 Life is good"
Scenario: Mistyping your password
Given we use the config "simple.yaml"
When we run "jrnl --encrypt" and enter
"""
swordfish
sordfish
"""
Then we should be prompted for a password
And we should see the message "Passwords did not match"
And the config for journal "default" should not have "encrypt" set
And the journal should have 2 entries
And the config for journal "default" should not contain "encrypt: true"
When we run "jrnl --short"
Then the output should be
2013-06-09 15:39 My first entry.
2013-06-10 15:40 Life is good.
Scenario: Mistyping your password, then getting it right
Given we use the config "simple.yaml"
When we run "jrnl --encrypt" and enter
"""
swordfish
sordfish
swordfish
swordfish
n
"""
Then we should be prompted for a password
And we should see the message "Passwords did not match"
And we should see the message "Journal encrypted"
And the config for journal "default" should have "encrypt" set to "bool:True"
When we run "jrnl -n 1" and enter "swordfish"
And the config for journal "default" should contain "encrypt: true"
When we run "jrnl -1" and enter "swordfish"
Then we should be prompted for a password
And the output should contain "2013-06-10 15:40 Life is good"

View file

@ -0,0 +1,293 @@
Feature: Searching in a journal
Scenario Outline: Displaying entries using -on today should display entries created today
Given we use the config "<config_file>"
When we run "jrnl today: Adding an entry right now."
Then we should see the message "Entry added"
When we run "jrnl -on today"
Then the output should contain "Adding an entry right now."
But the output should not contain "Everything is alright"
And the output should not contain "Life is good"
Examples: configs
| config_file |
| simple.yaml |
| empty_folder.yaml |
| dayone.yaml |
Scenario Outline: Displaying entries using -from day should display correct entries
Given we use the config "<config_file>"
When we run "jrnl yesterday: This thing happened yesterday"
Then we should see the message "Entry added"
When we run "jrnl today at 11:59pm: Adding an entry right now."
Then we should see the message "Entry added"
When we run "jrnl tomorrow: A future entry."
Then we should see the message "Entry added"
When we run "jrnl -from today"
Then the output should contain "Adding an entry right now."
And the output should contain "A future entry."
And the output should not contain "This thing happened yesterday"
Examples: configs
| config_file |
| simple.yaml |
| empty_folder.yaml |
| dayone.yaml |
Scenario Outline: Displaying entries using -from and -to day should display correct entries
Given we use the config "<config_file>"
When we run "jrnl yesterday: This thing happened yesterday"
Then we should see the message "Entry added"
When we run "jrnl today at 11:59pm: Adding an entry right now."
Then we should see the message "Entry added"
When we run "jrnl tomorrow: A future entry."
Then we should see the message "Entry added"
When we run "jrnl -from yesterday -to today"
Then the output should contain "This thing happened yesterday"
And the output should contain "Adding an entry right now."
And the output should not contain "A future entry."
Examples: configs
| config_file |
| simple.yaml |
| empty_folder.yaml |
| dayone.yaml |
Scenario Outline: Searching for a string
Given we use the config "<config_file>"
When we run "jrnl -contains first --short"
Then we should get no error
And the output should be
2020-08-29 11:11 Entry the first.
Examples: configs
| config_file |
| basic_onefile.yaml |
| basic_folder.yaml |
| basic_dayone.yaml |
Scenario Outline: Searching for a string within tag results
Given we use the config "<config_file>"
When we run "jrnl @tagone -contains maybe"
Then we should get no error
And the output should contain "maybe"
Examples: configs
| config_file |
| basic_onefile.yaml |
| basic_folder.yaml |
| basic_dayone.yaml |
Scenario Outline: Searching for a string within AND tag results
Given we use the config "<config_file>"
When we run "jrnl -and @tagone @tagtwo -contains maybe"
Then we should get no error
And the output should contain "maybe"
Examples: configs
| config_file |
| basic_onefile.yaml |
| basic_folder.yaml |
| basic_dayone.yaml |
Scenario Outline: Searching for a string within NOT tag results
Given we use the config "<config_file>"
When we run "jrnl -not @tagone -contains lonesome"
Then we should get no error
And the output should contain "lonesome"
Examples: configs
| config_file |
| basic_onefile.yaml |
| basic_folder.yaml |
| basic_dayone.yaml |
Scenario Outline: Searching for dates
Given we use the config "<config_file>"
When we run "jrnl -on 2020-08-31 --short"
Then the output should be "2020-08-31 14:32 A second entry in what I hope to be a long series."
When we run "jrnl -on 'august 31 2020' --short"
Then the output should be "2020-08-31 14:32 A second entry in what I hope to be a long series."
Examples: configs
| config_file |
| basic_onefile.yaml |
| basic_folder.yaml |
| basic_dayone.yaml |
Scenario: Out of order entries to a Folder journal should be listed in date order
Given we use the config "empty_folder.yaml"
When we run "jrnl 3/7/2014 4:37pm: Second entry of journal."
Then we should see the message "Entry added"
When we run "jrnl 23 July 2013: Testing folder journal."
Then we should see the message "Entry added"
When we run "jrnl -2"
Then the output should be
2013-07-23 09:00 Testing folder journal.
2014-03-07 16:37 Second entry of journal.
Scenario Outline: Searching for all tags should show counts of each tag
Given we use the config "<config_file>"
When we run "jrnl --tags"
Then we should get no error
And the output should be
@tagtwo : 2
@tagone : 2
@tagthree : 1
@ipsum : 1
Examples: configs
| config_file |
| basic_onefile.yaml |
| basic_folder.yaml |
| basic_dayone.yaml |
Scenario Outline: Filtering journals should also filter tags
Given we use the config "<config_file>"
When we run "jrnl -from 'september 2020' --tags"
Then we should get no error
And the output should be
@tagthree : 1
@tagone : 1
Examples: configs
| config_file |
| basic_onefile.yaml |
| basic_folder.yaml |
| basic_dayone.yaml |
Scenario Outline: Excluding a tag should filter out all entries with that tag
Given we use the config "<config_file>"
When we run "jrnl --tags -not @tagtwo"
Then the output should be
@tagthree : 1
@tagone : 1
Examples: configs
| config_file |
| basic_onefile.yaml |
| basic_folder.yaml |
| basic_dayone.yaml |
Scenario Outline: Excluding multiple tags should filter out all entries with those tags
Given we use the config "<config_file>"
When we run "jrnl --tags -not @tagone -not @tagthree"
Then the output should be
@tagtwo : 1
Examples: configs
| config_file |
| basic_onefile.yaml |
| basic_folder.yaml |
| basic_dayone.yaml |
Scenario: DayOne tag searching should work with tags containing a mixture of upper and lower case.
# https://github.com/jrnl-org/jrnl/issues/354
Given we use the config "dayone.yaml"
When we run "jrnl @plAy"
Then the output should contain "2013-05-17 11:39 This entry has tags!"
Scenario: Loading a sample journal
Given we use the config "simple.yaml"
When we run "jrnl -2"
Then we should get no error
And the output should be
2013-06-09 15:39 My first entry.
| Everything is alright
2013-06-10 15:40 Life is good.
| But I'm better.
Scenario Outline: Searching by month
Given we use the config "<config_file>"
And we use the password "test" if prompted
When we run "jrnl -month 9 --short"
Then the output should be "2020-09-24 09:14 The third entry finally after weeks without writing."
When we run "jrnl -month Sept --short"
Then the output should be "2020-09-24 09:14 The third entry finally after weeks without writing."
When we run "jrnl -month September --short"
Then the output should be "2020-09-24 09:14 The third entry finally after weeks without writing."
Examples: configs
| config_file |
| basic_onefile.yaml |
| basic_encrypted.yaml |
| basic_folder.yaml |
| basic_dayone.yaml |
Scenario Outline: Searching by day
Given we use the config "<config_file>"
And we use the password "test" if prompted
When we run "jrnl -day 31 --short"
Then the output should be "2020-08-31 14:32 A second entry in what I hope to be a long series."
Examples: configs
| config_file |
| basic_onefile.yaml |
| basic_encrypted.yaml |
| basic_folder.yaml |
| basic_dayone.yaml |
Scenario Outline: Searching by year
Given we use the config "<config_file>"
And we use the password "test" if prompted
When we run "jrnl 2019-01-01 01:01: I like this year."
And we run "jrnl -year 2019 --short"
Then the output should be "2019-01-01 01:01 I like this year."
When we run "jrnl -year 19 --short"
Then the output should be "2019-01-01 01:01 I like this year."
Examples: configs
| config_file |
| basic_onefile.yaml |
| basic_encrypted.yaml |
| basic_folder.yaml |
| basic_dayone.yaml |
Scenario Outline: Combining month, day, and year search terms
Given we use the config "<config_file>"
And we use the password "test" if prompted
When we run "jrnl -month 08 -day 29 --short"
Then the output should be "2020-08-29 11:11 Entry the first."
When we run "jrnl -day 29 -year 2020 --short"
Then the output should be "2020-08-29 11:11 Entry the first."
When we run "jrnl -month 09 -year 2020 --short"
Then the output should be "2020-09-24 09:14 The third entry finally after weeks without writing."
When we run "jrnl -month 08 -day 29 -year 2020 --short"
Then the output should be "2020-08-29 11:11 Entry the first."
Examples: configs
| config_file |
| basic_onefile.yaml |
| basic_encrypted.yaml |
| basic_folder.yaml |
| basic_dayone.yaml |
Scenario Outline: Searching today in history
Given we use the config "<config_file>"
And we use the password "test" if prompted
And now is "2020-08-31 02:32:00 PM"
When we run "jrnl 2019-08-31 01:01: Hi, from last year."
And we run "jrnl -today-in-history --short"
Then the output should be
2019-08-31 01:01 Hi, from last year.
2020-08-31 14:32 A second entry in what I hope to be a long series.
Examples: configs
| config_file |
| basic_onefile.yaml |
| basic_encrypted.yaml |
| basic_folder.yaml |
| basic_dayone.yaml |
Scenario: Loading a DayOne Journal
Given we use the config "dayone.yaml"
When we run "jrnl -from 'feb 2013'"
Then we should get no error
And the output should be
2013-05-17 11:39 This entry has tags!
2013-06-17 20:38 This entry has a location.
2013-07-17 11:38 This entry is starred!

View file

@ -1,7 +1,7 @@
Feature: Starring entries
Scenario Outline: Starring an entry will mark it in the journal file
Given we use the config "<config_file>.yaml"
Given we use the config "<config_file>"
When we run "jrnl 20 july 2013 *: Best day of my life!"
Then we should see the message "Entry added"
When we run "jrnl -on 2013-07-20 -starred"
@ -9,12 +9,12 @@ Feature: Starring entries
Examples: configs
| config_file |
| simple |
| empty_folder |
| dayone |
| simple.yaml |
| empty_folder.yaml |
| dayone.yaml |
Scenario Outline: Filtering by starred entries will show only starred entries
Given we use the config "<config_file>.yaml"
Given we use the config "<config_file>"
When we run "jrnl -starred"
Then the output should be empty
When we run "jrnl 20 july 2013 *: Best day of my life!"
@ -23,9 +23,9 @@ Feature: Starring entries
Examples: configs
| config_file |
| simple |
| empty_folder |
| dayone_empty |
| simple.yaml |
| empty_folder.yaml |
| dayone_empty.yaml |
Scenario: Starring an entry will mark it in an encrypted journal
Given we use the config "encrypted.yaml"

View file

@ -3,51 +3,46 @@ Feature: Tagging
# And format.feature for tag-related output
Scenario Outline: Tags should allow certain special characters such as /, +, #
Given we use the config "<config>.yaml"
Given we use the config "<config_file>"
When we run "jrnl 2020-09-26: This is an entry about @os/2 and @c++ and @c#"
When we run "jrnl --tags -on 2020-09-26"
Then we should get no error
And the output should be
"""
@os/2 : 1
@c++ : 1
@c# : 1
"""
Examples: configs
| config |
| basic_onefile |
| basic_folder |
| basic_dayone |
| config_file |
| basic_onefile.yaml |
| basic_folder.yaml |
| basic_dayone.yaml |
Scenario Outline: Emails addresses should not be parsed as tags
Given we use the config "<config>.yaml"
Given we use the config "<config_file>"
When we run "jrnl 2020-09-26: The email address test@example.com does not seem to work for me"
When we run "jrnl 2020-09-26: The email address test@example.org also does not work for me"
When we run "jrnl 2020-09-26: I tried test@example.org and test@example.edu too"
Then we flush the output
When we run "jrnl --tags -on 2020-09-26"
Then we should get no error
And the output should be "[No tags found in journal.]"
Examples: configs
| config |
| basic_onefile |
| basic_folder |
| basic_dayone |
| config_file |
| basic_onefile.yaml |
| basic_folder.yaml |
| basic_dayone.yaml |
Scenario Outline: Entry can start and end with tags
Given we use the config "<config>.yaml"
Given we use the config "<config_file>"
When we run "jrnl 2020-09-26: @foo came over, we went to a @bar"
When we run "jrnl --tags -on 2020-09-26"
Then the output should be
"""
@foo : 1
@bar : 1
"""
Examples: configs
| config |
| basic_onefile |
| basic_folder |
| basic_dayone |
| config_file |
| basic_onefile.yaml |
| basic_folder.yaml |
| basic_dayone.yaml |

View file

@ -3,69 +3,52 @@ Feature: Upgrading Journals from 1.x.x to 2.x.x
Scenario: Upgrade and parse journals with square brackets
Given we use the config "upgrade_from_195.json"
When we run "jrnl -9" and enter "Y"
Then the journal should have 2 entries
And the output should contain
"""
When we run "jrnl -99 --short"
Then the output should be
2010-06-10 15:00 A life without chocolate is like a bad analogy.
2013-06-10 15:40 He said "[this] is the best time to be alive".Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent malesuada
And the output should contain
2010-06-10 15:00 A life without chocolate is like a bad analogy.
"""
And the output should contain
"""
2013-06-10 15:40 He said "[this] is the best time to be alive".
"""
Scenario: Upgrading a journal encrypted with jrnl 1.x
Given we use the config "encrypted_old.json"
When we run "jrnl -n 1" and enter
"""
Y
bad doggie no biscuit
bad doggie no biscuit
"""
Then we should be prompted for a password
And the output should contain "2013-06-10 15:40 Life is good"
Scenario: Upgrading a config without colors to colors
Given we use the config "no_colors.yaml"
When we run "jrnl -n 1"
Then the config should have "colors" set to
"""
{
'date':'none',
'title':'none',
'body':'none',
'tags':'none'
}
"""
Then the config should contain
colors:
date: none
title: none
body: none
tags: none
Scenario: Upgrade and parse journals with little endian date format
Given we use the config "upgrade_from_195_little_endian_dates.json"
When we run "jrnl -9" and enter "Y"
Then the journal should have 2 entries
And the output should contain
"""
When we run "jrnl -9 --short" and enter "Y"
Then the output should contain
10.06.2010 15:00 A life without chocolate is like a bad analogy.
"""
And the output should contain
"""
10.06.2013 15:40 He said "[this] is the best time to be alive".
"""
Scenario: Upgrade with missing journal
Given we use the config "upgrade_from_195_with_missing_journal.json"
When we run "jrnl -ls" and enter
""""
Y
"""
When we run "jrnl --list" and enter "Y"
Then the output should contain "Error: features/journals/missing.journal does not exist."
And we should get no error
Scenario: Upgrade with missing encrypted journal
Given we use the config "upgrade_from_195_with_missing_encrypted_journal.json"
When we run "jrnl -ls" and enter
"""
When we run "jrnl --list" and enter
Y
bad doggie no biscuit
"""
Then the output should contain "Error: features/journals/missing.journal does not exist."
And the error output should contain "We're all done"
And the output should contain "We're all done"
And we should get no error

View file

@ -1,7 +1,7 @@
Feature: Writing new entries.
Scenario Outline: Multiline entry with punctuation should keep title punctuation
Given we use the config "<config_file>.yaml"
Given we use the config "<config_file>"
And we use the password "bad doggie no biscuit" if prompted
When we run "jrnl This is. the title\\n This is the second line"
And we run "jrnl -n 1"
@ -9,13 +9,13 @@ Feature: Writing new entries.
Examples: configs
| config_file |
| simple |
| empty_folder |
| dayone |
| encrypted |
| simple.yaml |
| empty_folder.yaml |
| dayone.yaml |
| encrypted.yaml |
Scenario Outline: Single line entry with period should be split at period
Given we use the config "<config_file>.yaml"
Given we use the config "<config_file>"
And we use the password "test" if prompted
When we run "jrnl This is. the title"
And we run "jrnl -1"
@ -23,13 +23,27 @@ Feature: Writing new entries.
Examples: configs
| config_file |
| basic_onefile |
| basic_encrypted |
| basic_folder |
| basic_dayone |
| basic_onefile.yaml |
| basic_encrypted.yaml |
| basic_folder.yaml |
| basic_dayone.yaml |
Scenario Outline: CJK entry should be split at fullwidth period without following space.
Given we use the config "<config_file>"
And we use the password "test" if prompted
When we run "jrnl "
And we run "jrnl -1"
Then the output should contain "| "
Examples: configs
| config_file |
| basic_onefile.yaml |
| basic_encrypted.yaml |
| basic_folder.yaml |
| basic_dayone.yaml |
Scenario Outline: Writing an entry from command line should store the entry
Given we use the config "<config_file>.yaml"
Given we use the config "<config_file>"
And we use the password "bad doggie no biscuit" if prompted
When we run "jrnl 23 july 2013: A cold and stormy day. I ate crisps on the sofa."
Then we should see the message "Entry added"
@ -38,48 +52,46 @@ Feature: Writing new entries.
Examples: configs
| config_file |
| simple |
| empty_folder |
| dayone |
| encrypted |
| simple.yaml |
| empty_folder.yaml |
| dayone.yaml |
| encrypted.yaml |
Scenario Outline: Writing a partial entry from command line with edit flag should go to the editor
Given we use the config "<config_file>.yaml"
Given we use the config "<config_file>"
And we use the password "test" if prompted
When we run "jrnl this is a partial --edit"
Then we should see the message "Entry added"
Then the editor should have been called
And the editor file content should be
"""
this is a partial
"""
When we run "jrnl -n 1"
Then the output should contain "this is a partial"
Examples: configs
| config_file |
| basic_onefile |
| basic_encrypted |
| basic_dayone |
| basic_folder |
| basic_onefile.yaml |
| basic_encrypted.yaml |
| basic_dayone.yaml |
| basic_folder.yaml |
Scenario Outline: Writing an empty entry from the editor should yield "Nothing saved to file" message
Given we use the config "<config_file>.yaml"
Given we use the config "<config_file>"
And we write nothing to the editor if opened
And we use the password "test" if prompted
When we open the editor and enter nothing
When we run "jrnl --edit"
Then the error output should contain "[Nothing saved to file]"
And the editor should have been called
Examples: configs
| config_file |
| editor |
| editor_empty_folder |
| dayone |
| basic_encrypted |
| basic_onefile |
| editor.yaml |
| editor_empty_folder.yaml |
| dayone.yaml |
| basic_encrypted.yaml |
| basic_onefile.yaml |
@skip
Scenario Outline: Writing an empty entry from the command line with no editor should yield nothing
Given we use the config "<config_file>.yaml"
Given we use the config "<config_file>"
And we use the password "bad doggie no biscuit" if prompted
When we run "jrnl" and enter nothing
Then the output should be empty
@ -88,14 +100,14 @@ Feature: Writing new entries.
Examples: configs
| config_file |
| simple |
| empty_folder |
| encrypted |
| config_simple.yaml |
| empty_folder.yaml |
| encrypted.yaml |
# | dayone | @todo
Scenario Outline: Writing an entry does not print the entire journal
# https://github.com/jrnl-org/jrnl/issues/87
Given we use the config "<config_file>.yaml"
Given we use the config "<config_file>"
And we use the password "bad doggie no biscuit" if prompted
When we run "jrnl 23 july 2013: A cold and stormy day. I ate crisps on the sofa."
Then we should see the message "Entry added"
@ -104,32 +116,30 @@ Feature: Writing new entries.
Examples: configs
| config_file |
| editor |
| editor_empty_folder |
| dayone |
| encrypted |
| editor.yaml |
| editor_empty_folder.yaml |
| dayone.yaml |
| encrypted.yaml |
Scenario Outline: Embedded period stays in title
Given we use the config "<config_file>.yaml"
Given we use the config "<config_file>"
And we use the password "bad doggie no biscuit" if prompted
When we run "jrnl 04-24-2014: Created a new website - empty.com. Hope to get a lot of traffic."
Then we should see the message "Entry added"
When we run "jrnl -1"
Then the output should be
"""
2014-04-24 09:00 Created a new website - empty.com.
| Hope to get a lot of traffic.
"""
Examples: configs
| config_file |
| simple |
| empty_folder |
| dayone |
| encrypted |
| simple.yaml |
| empty_folder.yaml |
| dayone.yaml |
| encrypted.yaml |
Scenario Outline: Write and read emoji support
Given we use the config "<config_file>.yaml"
Given we use the config "<config_file>"
And we use the password "bad doggie no biscuit" if prompted
When we run "jrnl 23 july 2013: 🌞 sunny day. Saw an 🐘"
Then we should see the message "Entry added"
@ -139,13 +149,13 @@ Feature: Writing new entries.
Examples: configs
| config_file |
| simple |
| empty_folder |
| dayone |
| encrypted |
| simple.yaml |
| empty_folder.yaml |
| dayone.yaml |
| encrypted.yaml |
Scenario Outline: Writing an entry at the prompt (no editor) should store the entry
Given we use the config "<config_file>.yaml"
Given we use the config "<config_file>"
And we use the password "bad doggie no biscuit" if prompted
When we run "jrnl" and enter "25 jul 2013: I saw Elvis. He's alive."
Then we should get no error
@ -155,9 +165,9 @@ Feature: Writing new entries.
Examples: configs
| config_file |
| simple |
| empty_folder |
| encrypted |
| simple.yaml |
| empty_folder.yaml |
| encrypted.yaml |
@todo
Scenario: Writing an entry at the prompt (no editor) in DayOne journal
@ -173,26 +183,27 @@ Feature: Writing new entries.
Given we use the config "dayone.yaml"
When we run "jrnl 01 may 1979: Being born hurts."
And we run "jrnl --export json"
Then "entries" in the json output should have 5 elements
And the json output should contain entries.0.creator.software_agent
And the json output should contain entries.0.creator.os_agent
And the json output should contain entries.0.creator.host_name
And the json output should contain entries.0.creator.generation_date
And the json output should contain entries.0.creator.device_agent
And "entries.0.creator.software_agent" in the json output should contain "jrnl"
Then we should get no error
And the output should be valid JSON
Given we parse the output as JSON
Then "entries" in the parsed output should have 5 elements
And "entries.0.creator" in the parsed output should be
software_agent
os_agent
host_name
generation_date
device_agent
And "entries.0.creator.software_agent" in the parsed output should contain
jrnl
# fails when system time is UTC (as on Travis-CI)
@skip
Scenario: Title with an embedded period on DayOne journal
Given we use the config "dayone.yaml"
When we run "jrnl 04-24-2014: "Ran 6.2 miles today in 1:02:03. I'm feeling sore because I forgot to stretch.""
When we run "jrnl 04-24-2014: Ran 6.2 miles today in 1:02:03. I am feeling sore because I forgot to stretch."
Then we should see the message "Entry added"
When we run "jrnl -1"
Then the output should be
"""
2014-04-24 09:00 Ran 6.2 miles today in 1:02:03.
| I'm feeling sore because I forgot to stretch.
"""
| I am feeling sore because I forgot to stretch.
Scenario: Opening an folder that's not a DayOne folder should treat as folder journal
Given we use the config "empty_folder.yaml"

View file

@ -0,0 +1,18 @@
from pytest_bdd import scenarios
scenarios("features/build.feature")
scenarios("features/core.feature")
scenarios("features/datetime.feature")
scenarios("features/delete.feature")
scenarios("features/encrypt.feature")
scenarios("features/file_storage.feature")
scenarios("features/format.feature")
scenarios("features/import.feature")
scenarios("features/multiple_journals.feature")
scenarios("features/override.feature")
scenarios("features/password.feature")
scenarios("features/search.feature")
scenarios("features/star.feature")
scenarios("features/tag.feature")
scenarios("features/upgrade.feature")
scenarios("features/write.feature")

29
tests/conftest.py Normal file
View file

@ -0,0 +1,29 @@
# Copyright (C) 2012-2021 jrnl contributors
# License: https://www.gnu.org/licenses/gpl-3.0.html
from pytest import mark
from jrnl.os_compat import on_windows
pytest_plugins = [
"tests.lib.fixtures",
"tests.lib.given_steps",
"tests.lib.when_steps",
"tests.lib.then_steps",
]
def pytest_bdd_apply_tag(tag, function):
if tag == "skip_win":
marker = mark.skipif(on_windows(), reason="Skip test on Windows")
elif tag == "skip_editor":
marker = mark.skip(
reason="Skipping editor-related test. We should come back to this!"
)
else:
# Fall back to pytest-bdd's default behavior
return None
marker(function)
return True

Some files were not shown because too many files have changed in this diff Show more