From 6ad89a26c8f88b7bf84e4f7c7c96c1b7fa2568b1 Mon Sep 17 00:00:00 2001 From: Peter Schmidbauer Date: Tue, 29 Oct 2019 18:17:44 +0100 Subject: [PATCH 01/33] Fix crash when no keyring backend available --- jrnl/util.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/jrnl/util.py b/jrnl/util.py index bc36ba9b..5609cd7f 100644 --- a/jrnl/util.py +++ b/jrnl/util.py @@ -80,7 +80,10 @@ def get_password(validator, keychain=None, max_attempts=3): def get_keychain(journal_name): import keyring - return keyring.get_password('jrnl', journal_name) + try: + return keyring.get_password('jrnl', journal_name) + except RuntimeError: + return "" def set_keychain(journal_name, password): @@ -88,7 +91,7 @@ def set_keychain(journal_name, password): if password is None: try: keyring.delete_password('jrnl', journal_name) - except: + except RuntimeError: pass elif not TEST: keyring.set_password('jrnl', journal_name, password) From ea594e599884e463a7ae00c262f922915b04a230 Mon Sep 17 00:00:00 2001 From: MinchinWeb Date: Wed, 13 Nov 2019 18:10:57 -0700 Subject: [PATCH 02/33] [Dayone] re-add tests c.f. commit 7cbca9f60f1fb5cc9b8d075f9b101d29d9656936 c.f. commit 2a401823b5ae37d5891548852cecfdec99a8e1c7 --- features/dayone.feature | 63 +++++++++++++++++++++++++++++ features/dayone_regressions.feature | 27 +++++++++++++ features/environment.py | 14 +++++++ 3 files changed, 104 insertions(+) create mode 100644 features/dayone.feature create mode 100644 features/dayone_regressions.feature diff --git a/features/dayone.feature b/features/dayone.feature new file mode 100644 index 00000000..b8a8a7cd --- /dev/null +++ b/features/dayone.feature @@ -0,0 +1,63 @@ +Feature: Dayone specific implementation details. + + 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! + """ + + Scenario: Entries without timezone information will be interpreted as in the 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 + + @skip + Scenario: Writing into Dayone + Given we use the config "dayone.yaml" + When we run "jrnl 01 may 1979: Being born hurts." + and we run "jrnl -until 1980" + Then the output should be + """ + 1979-05-01 09:00 Being born hurts. + """ + + Scenario: Loading tags from a DayOne Journal + Given we use the config "dayone.yaml" + When we run "jrnl --tags" + Then the output should be + """ + @work : 1 + @play : 1 + """ + + Scenario: Saving tags from a DayOne Journal + Given we use the config "dayone.yaml" + When we run "jrnl A hard day at @work" + and we run "jrnl --tags" + Then the output should be + """ + @work : 2 + @play : 1 + """ + + Scenario: Filtering by tags from a DayOne Journal + Given we use the config "dayone.yaml" + When we run "jrnl @work" + Then the output should be + """ + 2013-05-17 11:39 This entry has tags! + """ + 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" diff --git a/features/dayone_regressions.feature b/features/dayone_regressions.feature new file mode 100644 index 00000000..a707a5fb --- /dev/null +++ b/features/dayone_regressions.feature @@ -0,0 +1,27 @@ +Feature: Zapped Dayone bugs stay dead! + + 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: 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."" + 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. + """ + + Scenario: Opening an folder that's not a DayOne folder gives a nice error message + Given we use the config "empty_folder.yaml" + When we run "jrnl Herro" + Then we should get an error + Then we should see the message "is a directory, but doesn't seem to be a DayOne journal either" diff --git a/features/environment.py b/features/environment.py index 7a918feb..8ba781ac 100644 --- a/features/environment.py +++ b/features/environment.py @@ -2,6 +2,14 @@ import shutil import os +def before_feature(context, feature): + # add "skip" tag + # https://stackoverflow.com/a/42721605/4276230 + if "skip" in feature.tags: + feature.skip("Marked with @skip") + return + + def before_scenario(context, scenario): """Before each scenario, backup all config and journal test data.""" # Clean up in case something went wrong @@ -22,6 +30,12 @@ def before_scenario(context, scenario): else: shutil.copy2(source, working_dir) + # add "skip" tag + # https://stackoverflow.com/a/42721605/4276230 + if "skip" in scenario.effective_tags: + scenario.skip("Marked with @skip") + return + def after_scenario(context, scenario): """After each scenario, restore all test data and remove working_dirs.""" From ad5d3a7856163dfea4a2d44a6ec91bfd716fc781 Mon Sep 17 00:00:00 2001 From: MinchinWeb Date: Wed, 13 Nov 2019 18:26:13 -0700 Subject: [PATCH 03/33] [Dayone] Skip failing tests on Travis Travis sets the timezone to UTC, which causes many tests to fail --- features/dayone.feature | 13 +++++++++++++ features/dayone_regressions.feature | 4 ++++ 2 files changed, 17 insertions(+) diff --git a/features/dayone.feature b/features/dayone.feature index b8a8a7cd..51aa2033 100644 --- a/features/dayone.feature +++ b/features/dayone.feature @@ -1,5 +1,7 @@ Feature: Dayone specific implementation details. + # fails when system time is UTC (as on Travis-CI) + @skip Scenario: Loading a DayOne Journal Given we use the config "dayone.yaml" When we run "jrnl -from 'feb 2013'" @@ -13,6 +15,8 @@ Feature: Dayone specific implementation details. 2013-07-17 11:38 This entry is starred! """ + # fails when system time is UTC (as on Travis-CI) + @skip Scenario: Entries without timezone information will be interpreted as in the current timezone Given we use the config "dayone.yaml" When we run "jrnl -until 'feb 2013'" @@ -29,6 +33,8 @@ Feature: Dayone specific implementation details. 1979-05-01 09:00 Being born hurts. """ + # fails when system time is UTC (as on Travis-CI) + @skip Scenario: Loading tags from a DayOne Journal Given we use the config "dayone.yaml" When we run "jrnl --tags" @@ -38,6 +44,8 @@ Feature: Dayone specific implementation details. @play : 1 """ + # fails when system time is UTC (as on Travis-CI) + @skip Scenario: Saving tags from a DayOne Journal Given we use the config "dayone.yaml" When we run "jrnl A hard day at @work" @@ -48,6 +56,8 @@ Feature: Dayone specific implementation details. @play : 1 """ + # fails when system time is UTC (as on Travis-CI) + @skip Scenario: Filtering by tags from a DayOne Journal Given we use the config "dayone.yaml" When we run "jrnl @work" @@ -55,6 +65,9 @@ Feature: Dayone specific implementation details. """ 2013-05-17 11:39 This entry has tags! """ + + # fails when system time is UTC (as on Travis-CI) + @skip Scenario: Exporting dayone to json Given we use the config "dayone.yaml" When we run "jrnl --export json" diff --git a/features/dayone_regressions.feature b/features/dayone_regressions.feature index a707a5fb..c3b700b9 100644 --- a/features/dayone_regressions.feature +++ b/features/dayone_regressions.feature @@ -1,5 +1,7 @@ Feature: Zapped Dayone bugs stay dead! + # fails when system time is UTC (as on Travis-CI) + @skip 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" @@ -9,6 +11,8 @@ Feature: Zapped Dayone bugs stay dead! 2013-05-17 11:39 This entry has tags! """ + # 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."" From 2890e33cac1acf9d86013b6d0627424d0ea88926 Mon Sep 17 00:00:00 2001 From: Aaron Lichtman Date: Thu, 14 Nov 2019 12:00:23 +0100 Subject: [PATCH 04/33] Exit jrnl if no text entered into editor Fix #589 --- jrnl/cli.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/jrnl/cli.py b/jrnl/cli.py index 65a53516..192e3233 100644 --- a/jrnl/cli.py +++ b/jrnl/cli.py @@ -200,6 +200,8 @@ def run(manual_args=None): util.prompt("[Could not read template at '']".format(config['template'])) sys.exit(1) raw = util.get_text_from_editor(config, template) + if not raw: + sys.exit() else: try: raw = util.py23_read("[Compose Entry; " + _exit_multiline_code + " to finish writing]\n") From 7a90f4076d0b55c290884e468f46430a3bcba99a Mon Sep 17 00:00:00 2001 From: Aaron Lichtman Date: Fri, 15 Nov 2019 09:27:01 +0100 Subject: [PATCH 05/33] Add test for aborting jrnl entry from editor --- features/core.feature | 5 +++++ features/data/configs/editor.yaml | 12 ++++++++++++ features/steps/core.py | 13 +++++++++++++ poetry.lock | 14 +++++++++++++- pyproject.toml | 1 + 5 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 features/data/configs/editor.yaml diff --git a/features/core.feature b/features/core.feature index ab86da42..95ec9351 100644 --- a/features/core.feature +++ b/features/core.feature @@ -20,6 +20,11 @@ Feature: Basic reading and writing to a journal When we run "jrnl -n 1" Then the output should contain "2013-07-23 09:00 A cold and stormy day." + Scenario: Writing an empty entry from the editor + Given we use the config "editor.yaml" + When we open the editor and exit + Then the output should be empty + Scenario: Filtering for dates Given we use the config "basic.yaml" When we run "jrnl -on 2013-06-10 --short" diff --git a/features/data/configs/editor.yaml b/features/data/configs/editor.yaml new file mode 100644 index 00000000..8a06f916 --- /dev/null +++ b/features/data/configs/editor.yaml @@ -0,0 +1,12 @@ +default_hour: 9 +default_minute: 0 +editor: "vim" +encrypt: false +highlight: true +journals: + default: features/journals/simple.journal +linewrap: 80 +tagsymbols: "@" +template: false +timeformat: "%Y-%m-%d %H:%M" +indent_character: "|" diff --git a/features/steps/core.py b/features/steps/core.py index 83981d13..fead2855 100644 --- a/features/steps/core.py +++ b/features/steps/core.py @@ -6,6 +6,7 @@ from jrnl import cli, install, Journal, util, plugins from jrnl import __version__ from dateutil import parser as date_parser from collections import defaultdict +import mock import os import json import yaml @@ -27,6 +28,7 @@ class TestKeyring(keyring.backend.KeyringBackend): def delete_password(self, servicename, username, password): self.keys[servicename][username] = None + # set the keyring for keyring lib keyring.set_keyring(TestKeyring()) @@ -73,6 +75,12 @@ def set_config(context, config_file): cf.write("version: {}".format(__version__)) +@when('we open the editor and exit') +@mock.patch('jrnl.util.get_text_from_editor', return_value="") +def open_editor_and_exit_without_entering_text(context, dead_param_to_satisfy_behave): + run(context, "jrnl") + + @when('we run "{command}" and enter') @when('we run "{command}" and enter "{inputs}"') def run_with_input(context, command, inputs=None): @@ -166,6 +174,11 @@ def check_json_output_path(context, path, value): assert struct == value, struct +@then('the output should be empty') +def check_empty_output(context, text=None): + assert (text or context.text) is None + + @then('the output should be') @then('the output should be "{text}"') def check_output(context, text=None): diff --git a/poetry.lock b/poetry.lock index e772b2d2..d4c35642 100644 --- a/poetry.lock +++ b/poetry.lock @@ -220,6 +220,17 @@ click = ">=3.3" livereload = ">=2.5.1" tornado = ">=5.0" +[[package]] +category = "main" +description = "Rolling backport of unittest.mock for all Pythons" +name = "mock" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "3.0.5" + +[package.dependencies] +six = "*" + [[package]] category = "main" description = "NumPy is the fundamental package for array computing with Python." @@ -384,7 +395,7 @@ version = "1.5.1" pytz = "*" [metadata] -content-hash = "9896cf59c7552b6ad95219ee5555c7445a3fab39c2e4f4c6f3d991a36635e44b" +content-hash = "ab4bffce011d607d66bdb9a3cadc5b7c67870ef4682c765a55af4d75ea3e0f73" python-versions = "^3.7" [metadata.hashes] @@ -409,6 +420,7 @@ markdown = ["2e50876bcdd74517e7b71f3e7a76102050edec255b3983403f1a63e7c8a41e7a", markupsafe = ["00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473", "09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161", "09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235", "1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5", "24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff", "29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b", "43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1", "46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e", "500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183", "535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66", "62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1", "6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1", "717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e", "79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b", "7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905", "88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735", "8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d", "98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e", "9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d", "9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c", "ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21", "b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2", "b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5", "b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b", "ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6", "c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f", "cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f", "e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7"] mccabe = ["ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42", "dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"] mkdocs = ["17d34329aad75d5de604b9ed4e31df3a4d235afefdc46ce7b1964fddb2e1e939", "8cc8b38325456b9e942c981a209eaeb1e9f3f77b493ad755bfef889b9c8d356a"] +mock = ["83657d894c90d5681d62155c82bda9c1187827525880eda8ff5df4ec813437c3", "d157e52d4e5b938c550f39eb2fd15610db062441a9c2747d3dbfa9298211d0f8"] numpy = ["0778076e764e146d3078b17c24c4d89e0ecd4ac5401beff8e1c87879043a0633", "141c7102f20abe6cf0d54c4ced8d565b86df4d3077ba2343b61a6db996cefec7", "14270a1ee8917d11e7753fb54fc7ffd1934f4d529235beec0b275e2ccf00333b", "27e11c7a8ec9d5838bc59f809bfa86efc8a4fd02e58960fa9c49d998e14332d5", "2a04dda79606f3d2f760384c38ccd3d5b9bb79d4c8126b67aff5eb09a253763e", "3c26010c1b51e1224a3ca6b8df807de6e95128b0908c7e34f190e7775455b0ca", "52c40f1a4262c896420c6ea1c6fda62cf67070e3947e3307f5562bd783a90336", "6e4f8d9e8aa79321657079b9ac03f3cf3fd067bf31c1cca4f56d49543f4356a5", "7242be12a58fec245ee9734e625964b97cf7e3f2f7d016603f9e56660ce479c7", "7dc253b542bfd4b4eb88d9dbae4ca079e7bf2e2afd819ee18891a43db66c60c7", "94f5bd885f67bbb25c82d80184abbf7ce4f6c3c3a41fbaa4182f034bba803e69", "a89e188daa119ffa0d03ce5123dee3f8ffd5115c896c2a9d4f0dbb3d8b95bfa3", "ad3399da9b0ca36e2f24de72f67ab2854a62e623274607e37e0ce5f5d5fa9166", "b0348be89275fd1d4c44ffa39530c41a21062f52299b1e3ee7d1c61f060044b8", "b5554368e4ede1856121b0dfa35ce71768102e4aa55e526cb8de7f374ff78722", "cbddc56b2502d3f87fda4f98d948eb5b11f36ff3902e17cb6cc44727f2200525", "d79f18f41751725c56eceab2a886f021d70fd70a6188fd386e29a045945ffc10", "dc2ca26a19ab32dc475dbad9dfe723d3a64c835f4c23f625c2b6566ca32b9f29", "dd9bcd4f294eb0633bb33d1a74febdd2b9018b8b8ed325f861fffcd2c7660bb8", "e8baab1bc7c9152715844f1faca6744f2416929de10d7639ed49555a85549f52", "ec31fe12668af687b99acf1567399632a7c47b0e17cfb9ae47c098644ef36797", "f12b4f7e2d8f9da3141564e6737d79016fe5336cc92de6814eba579744f65b0a", "f58ac38d5ca045a377b3b377c84df8175ab992c970a53332fa8ac2373df44ff7"] parse = ["1b68657434d371e5156048ca4a0c5aea5afc6ca59a2fea4dd1a575354f617142"] parse-type = ["6e906a66f340252e4c324914a60d417d33a4bea01292ea9bbf68b4fc123be8c9", "f596bdc75d3dd93036fbfe3d04127da9f6df0c26c36e01e76da85adef4336b3c"] diff --git a/pyproject.toml b/pyproject.toml index 1b84acde..19eafee4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,6 +25,7 @@ asteval = "^0.9.14" colorama = {version = "^0.4.1",platform = "win32"} python-dateutil = "^2.8" pyyaml = "^5.1" +mock = "^3.0" [tool.poetry.dev-dependencies] behave = "^1.2" From f0e5fe965371ecce4108a71fb6b15a4f3cfd7547 Mon Sep 17 00:00:00 2001 From: Aaron Lichtman Date: Fri, 15 Nov 2019 09:33:47 +0100 Subject: [PATCH 06/33] Use native mocking --- features/steps/core.py | 8 ++++---- poetry.lock | 14 +------------- pyproject.toml | 1 - 3 files changed, 5 insertions(+), 18 deletions(-) diff --git a/features/steps/core.py b/features/steps/core.py index fead2855..7070e10e 100644 --- a/features/steps/core.py +++ b/features/steps/core.py @@ -1,12 +1,12 @@ from __future__ import unicode_literals from __future__ import absolute_import +from unittest.mock import patch from behave import given, when, then from jrnl import cli, install, Journal, util, plugins from jrnl import __version__ from dateutil import parser as date_parser from collections import defaultdict -import mock import os import json import yaml @@ -76,9 +76,9 @@ def set_config(context, config_file): @when('we open the editor and exit') -@mock.patch('jrnl.util.get_text_from_editor', return_value="") -def open_editor_and_exit_without_entering_text(context, dead_param_to_satisfy_behave): - run(context, "jrnl") +def open_editor_and_exit_without_entering_text(context): + with patch('jrnl.util.get_text_from_editor', return_value=""): + run(context, "jrnl") @when('we run "{command}" and enter') diff --git a/poetry.lock b/poetry.lock index d4c35642..e772b2d2 100644 --- a/poetry.lock +++ b/poetry.lock @@ -220,17 +220,6 @@ click = ">=3.3" livereload = ">=2.5.1" tornado = ">=5.0" -[[package]] -category = "main" -description = "Rolling backport of unittest.mock for all Pythons" -name = "mock" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "3.0.5" - -[package.dependencies] -six = "*" - [[package]] category = "main" description = "NumPy is the fundamental package for array computing with Python." @@ -395,7 +384,7 @@ version = "1.5.1" pytz = "*" [metadata] -content-hash = "ab4bffce011d607d66bdb9a3cadc5b7c67870ef4682c765a55af4d75ea3e0f73" +content-hash = "9896cf59c7552b6ad95219ee5555c7445a3fab39c2e4f4c6f3d991a36635e44b" python-versions = "^3.7" [metadata.hashes] @@ -420,7 +409,6 @@ markdown = ["2e50876bcdd74517e7b71f3e7a76102050edec255b3983403f1a63e7c8a41e7a", markupsafe = ["00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473", "09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161", "09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235", "1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5", "24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff", "29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b", "43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1", "46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e", "500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183", "535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66", "62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1", "6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1", "717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e", "79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b", "7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905", "88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735", "8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d", "98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e", "9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d", "9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c", "ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21", "b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2", "b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5", "b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b", "ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6", "c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f", "cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f", "e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7"] mccabe = ["ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42", "dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"] mkdocs = ["17d34329aad75d5de604b9ed4e31df3a4d235afefdc46ce7b1964fddb2e1e939", "8cc8b38325456b9e942c981a209eaeb1e9f3f77b493ad755bfef889b9c8d356a"] -mock = ["83657d894c90d5681d62155c82bda9c1187827525880eda8ff5df4ec813437c3", "d157e52d4e5b938c550f39eb2fd15610db062441a9c2747d3dbfa9298211d0f8"] numpy = ["0778076e764e146d3078b17c24c4d89e0ecd4ac5401beff8e1c87879043a0633", "141c7102f20abe6cf0d54c4ced8d565b86df4d3077ba2343b61a6db996cefec7", "14270a1ee8917d11e7753fb54fc7ffd1934f4d529235beec0b275e2ccf00333b", "27e11c7a8ec9d5838bc59f809bfa86efc8a4fd02e58960fa9c49d998e14332d5", "2a04dda79606f3d2f760384c38ccd3d5b9bb79d4c8126b67aff5eb09a253763e", "3c26010c1b51e1224a3ca6b8df807de6e95128b0908c7e34f190e7775455b0ca", "52c40f1a4262c896420c6ea1c6fda62cf67070e3947e3307f5562bd783a90336", "6e4f8d9e8aa79321657079b9ac03f3cf3fd067bf31c1cca4f56d49543f4356a5", "7242be12a58fec245ee9734e625964b97cf7e3f2f7d016603f9e56660ce479c7", "7dc253b542bfd4b4eb88d9dbae4ca079e7bf2e2afd819ee18891a43db66c60c7", "94f5bd885f67bbb25c82d80184abbf7ce4f6c3c3a41fbaa4182f034bba803e69", "a89e188daa119ffa0d03ce5123dee3f8ffd5115c896c2a9d4f0dbb3d8b95bfa3", "ad3399da9b0ca36e2f24de72f67ab2854a62e623274607e37e0ce5f5d5fa9166", "b0348be89275fd1d4c44ffa39530c41a21062f52299b1e3ee7d1c61f060044b8", "b5554368e4ede1856121b0dfa35ce71768102e4aa55e526cb8de7f374ff78722", "cbddc56b2502d3f87fda4f98d948eb5b11f36ff3902e17cb6cc44727f2200525", "d79f18f41751725c56eceab2a886f021d70fd70a6188fd386e29a045945ffc10", "dc2ca26a19ab32dc475dbad9dfe723d3a64c835f4c23f625c2b6566ca32b9f29", "dd9bcd4f294eb0633bb33d1a74febdd2b9018b8b8ed325f861fffcd2c7660bb8", "e8baab1bc7c9152715844f1faca6744f2416929de10d7639ed49555a85549f52", "ec31fe12668af687b99acf1567399632a7c47b0e17cfb9ae47c098644ef36797", "f12b4f7e2d8f9da3141564e6737d79016fe5336cc92de6814eba579744f65b0a", "f58ac38d5ca045a377b3b377c84df8175ab992c970a53332fa8ac2373df44ff7"] parse = ["1b68657434d371e5156048ca4a0c5aea5afc6ca59a2fea4dd1a575354f617142"] parse-type = ["6e906a66f340252e4c324914a60d417d33a4bea01292ea9bbf68b4fc123be8c9", "f596bdc75d3dd93036fbfe3d04127da9f6df0c26c36e01e76da85adef4336b3c"] diff --git a/pyproject.toml b/pyproject.toml index 19eafee4..1b84acde 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,7 +25,6 @@ asteval = "^0.9.14" colorama = {version = "^0.4.1",platform = "win32"} python-dateutil = "^2.8" pyyaml = "^5.1" -mock = "^3.0" [tool.poetry.dev-dependencies] behave = "^1.2" From d378e2522df11eb485265776cefad2d4b89c36cd Mon Sep 17 00:00:00 2001 From: Aaron Lichtman Date: Fri, 15 Nov 2019 09:38:09 +0100 Subject: [PATCH 07/33] Add comment explaining discrepancy between expected and asserted output --- features/core.feature | 1 + 1 file changed, 1 insertion(+) diff --git a/features/core.feature b/features/core.feature index 95ec9351..2f62c19b 100644 --- a/features/core.feature +++ b/features/core.feature @@ -20,6 +20,7 @@ Feature: Basic reading and writing to a journal When we run "jrnl -n 1" Then the output should contain "2013-07-23 09:00 A cold and stormy day." + # Note that the expected output is actually "[Nothing saved to file]" but due to mocking limitations, we expect no output here. Scenario: Writing an empty entry from the editor Given we use the config "editor.yaml" When we open the editor and exit From c0a1c171f140df0b35c38cff232ce8b91e4fc7d1 Mon Sep 17 00:00:00 2001 From: Aaron Lichtman Date: Sat, 16 Nov 2019 13:06:04 +0100 Subject: [PATCH 08/33] Fix check_empty_output method --- features/steps/core.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/features/steps/core.py b/features/steps/core.py index ef3532d6..8bc73313 100644 --- a/features/steps/core.py +++ b/features/steps/core.py @@ -190,8 +190,8 @@ def check_json_output_path(context, path, value): @then('the output should be empty') -def check_empty_output(context, text=None): - assert (text or context.text) is None +def check_empty_output(context): + assert context.stdout_capture is None @then('the output should be') From ccb55392dd6dc7d539adbbd7142429965e694e35 Mon Sep 17 00:00:00 2001 From: Aaron Lichtman Date: Sat, 16 Nov 2019 13:18:14 +0100 Subject: [PATCH 09/33] Check message on stderr and patch subprocess.call --- features/core.feature | 4 ++-- features/steps/core.py | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/features/core.feature b/features/core.feature index 2f62c19b..854fa53f 100644 --- a/features/core.feature +++ b/features/core.feature @@ -23,8 +23,8 @@ Feature: Basic reading and writing to a journal # Note that the expected output is actually "[Nothing saved to file]" but due to mocking limitations, we expect no output here. Scenario: Writing an empty entry from the editor Given we use the config "editor.yaml" - When we open the editor and exit - Then the output should be empty + When we open the editor and enter "" + Then we should see the message "[Nothing saved to file]" Scenario: Filtering for dates Given we use the config "basic.yaml" diff --git a/features/steps/core.py b/features/steps/core.py index 8bc73313..d4bf1b47 100644 --- a/features/steps/core.py +++ b/features/steps/core.py @@ -67,9 +67,11 @@ def set_config(context, config_file): cf.write("version: {}".format(__version__)) -@when('we open the editor and exit') -def open_editor_and_exit_without_entering_text(context): - with patch('jrnl.util.get_text_from_editor', return_value=""): +@when('we open the editor and enter') +@when('we open the editor and enter {text}') +def open_editor_and_enter(context, text=""): + text = (text or context.text) + with patch('subprocess.call', return_value=text): run(context, "jrnl") From 9b0ebb7d847535603f0e631154e79391cd6d847f Mon Sep 17 00:00:00 2001 From: Aaron Lichtman Date: Sat, 16 Nov 2019 13:42:30 +0100 Subject: [PATCH 10/33] Add _mock_editor_function --- features/steps/core.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/features/steps/core.py b/features/steps/core.py index d4bf1b47..66fa1e11 100644 --- a/features/steps/core.py +++ b/features/steps/core.py @@ -71,7 +71,15 @@ def set_config(context, config_file): @when('we open the editor and enter {text}') def open_editor_and_enter(context, text=""): text = (text or context.text) - with patch('subprocess.call', return_value=text): + print("open_editor_and_enter called") + def _mock_editor_function(command): + print("_mock_editor_function called") + tmpfile = command.split()[-1] + print("TMPFILE:", tmpfile) + with open(tmpfile, "w+") as f: + f.write(text) + + with patch('subprocess.call', side_effect=_mock_editor_function): run(context, "jrnl") From 68772d3afe948137a11e5534c70bd68e739e0dee Mon Sep 17 00:00:00 2001 From: Aaron Lichtman Date: Sat, 16 Nov 2019 13:51:55 +0100 Subject: [PATCH 11/33] Update features/steps/core.py Co-Authored-By: pspeter --- features/steps/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/steps/core.py b/features/steps/core.py index 66fa1e11..7ab7d20d 100644 --- a/features/steps/core.py +++ b/features/steps/core.py @@ -74,7 +74,7 @@ def open_editor_and_enter(context, text=""): print("open_editor_and_enter called") def _mock_editor_function(command): print("_mock_editor_function called") - tmpfile = command.split()[-1] + tmpfile = command[-1] print("TMPFILE:", tmpfile) with open(tmpfile, "w+") as f: f.write(text) From e63ae25433dcd6dfc92569333fd2a85fde0ddb59 Mon Sep 17 00:00:00 2001 From: Aaron Lichtman Date: Sat, 16 Nov 2019 14:07:39 +0100 Subject: [PATCH 12/33] Add return from mock function --- features/steps/core.py | 1 + 1 file changed, 1 insertion(+) diff --git a/features/steps/core.py b/features/steps/core.py index 7ab7d20d..354bb2bb 100644 --- a/features/steps/core.py +++ b/features/steps/core.py @@ -78,6 +78,7 @@ def open_editor_and_enter(context, text=""): print("TMPFILE:", tmpfile) with open(tmpfile, "w+") as f: f.write(text) + return tmpfile with patch('subprocess.call', side_effect=_mock_editor_function): run(context, "jrnl") From 7dcc91431e947943be9d8a4b1415968ca77c5852 Mon Sep 17 00:00:00 2001 From: Aaron Lichtman Date: Sat, 16 Nov 2019 14:16:36 +0100 Subject: [PATCH 13/33] Add debug statements --- features/steps/core.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/features/steps/core.py b/features/steps/core.py index 354bb2bb..84d4ccc9 100644 --- a/features/steps/core.py +++ b/features/steps/core.py @@ -78,6 +78,8 @@ def open_editor_and_enter(context, text=""): print("TMPFILE:", tmpfile) with open(tmpfile, "w+") as f: f.write(text) + + print("File contents:", open(tmpfile, "r").read()) return tmpfile with patch('subprocess.call', side_effect=_mock_editor_function): From 87571fa55f5533af92a11bfd21bf936c849563ee Mon Sep 17 00:00:00 2001 From: Aaron Lichtman Date: Sat, 16 Nov 2019 14:31:05 +0100 Subject: [PATCH 14/33] Debug --- jrnl/util.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/jrnl/util.py b/jrnl/util.py index 959d63a1..3b35c366 100644 --- a/jrnl/util.py +++ b/jrnl/util.py @@ -118,6 +118,8 @@ def get_text_from_editor(config, template=""): os.remove(tmpfile) if not raw: print('[Nothing saved to file]', file=sys.stderr) + else: + print("RAW: '" + raw + "'") return raw From 8d22283b93393316f8d293402e485c77b898d335 Mon Sep 17 00:00:00 2001 From: Aaron Lichtman Date: Sat, 16 Nov 2019 14:35:50 +0100 Subject: [PATCH 15/33] Update features/steps/core.py Co-Authored-By: pspeter --- features/steps/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/steps/core.py b/features/steps/core.py index 84d4ccc9..2175bd28 100644 --- a/features/steps/core.py +++ b/features/steps/core.py @@ -68,7 +68,7 @@ def set_config(context, config_file): @when('we open the editor and enter') -@when('we open the editor and enter {text}') +@when('we open the editor and enter "{text}"') def open_editor_and_enter(context, text=""): text = (text or context.text) print("open_editor_and_enter called") From 4cfff00d0a3f1b7e7458f72815be5128f0d18999 Mon Sep 17 00:00:00 2001 From: Aaron Lichtman Date: Sat, 16 Nov 2019 14:40:31 +0100 Subject: [PATCH 16/33] Move sys.exit() down --- jrnl/cli.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/jrnl/cli.py b/jrnl/cli.py index 3ba6ec94..32cde8dc 100644 --- a/jrnl/cli.py +++ b/jrnl/cli.py @@ -196,8 +196,6 @@ def run(manual_args=None): print(f"[Could not read template at '{config['template']}']", file=sys.stderr) sys.exit(1) raw = util.get_text_from_editor(config, template) - if not raw: - sys.exit() else: try: print("[Compose Entry; " + _exit_multiline_code + " to finish writing]\n", file=sys.stderr) @@ -208,7 +206,7 @@ def run(manual_args=None): if raw: args.text = [raw] else: - mode_compose = False + sys.exit() # This is where we finally open the journal! try: From c23efa7f801550c808e1adb4ec654bc11be70305 Mon Sep 17 00:00:00 2001 From: Aaron Lichtman Date: Sat, 16 Nov 2019 14:42:39 +0100 Subject: [PATCH 17/33] Fix test? --- features/core.feature | 2 +- features/steps/core.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/features/core.feature b/features/core.feature index 854fa53f..bff53e2f 100644 --- a/features/core.feature +++ b/features/core.feature @@ -23,7 +23,7 @@ Feature: Basic reading and writing to a journal # Note that the expected output is actually "[Nothing saved to file]" but due to mocking limitations, we expect no output here. Scenario: Writing an empty entry from the editor Given we use the config "editor.yaml" - When we open the editor and enter "" + When we open the editor and enter nothing Then we should see the message "[Nothing saved to file]" Scenario: Filtering for dates diff --git a/features/steps/core.py b/features/steps/core.py index 2175bd28..95e39af8 100644 --- a/features/steps/core.py +++ b/features/steps/core.py @@ -67,7 +67,7 @@ def set_config(context, config_file): cf.write("version: {}".format(__version__)) -@when('we open the editor and enter') +@when('we open the editor and enter nothing') @when('we open the editor and enter "{text}"') def open_editor_and_enter(context, text=""): text = (text or context.text) From bca12b4b16a6f0586b24f0cd3f53c9636fa1213a Mon Sep 17 00:00:00 2001 From: Aaron Lichtman Date: Sat, 16 Nov 2019 14:46:34 +0100 Subject: [PATCH 18/33] Fix test? --- features/steps/core.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/features/steps/core.py b/features/steps/core.py index 95e39af8..58d50e85 100644 --- a/features/steps/core.py +++ b/features/steps/core.py @@ -67,7 +67,7 @@ def set_config(context, config_file): cf.write("version: {}".format(__version__)) -@when('we open the editor and enter nothing') +@when('we open the editor and enter ""') @when('we open the editor and enter "{text}"') def open_editor_and_enter(context, text=""): text = (text or context.text) @@ -77,7 +77,10 @@ def open_editor_and_enter(context, text=""): tmpfile = command[-1] print("TMPFILE:", tmpfile) with open(tmpfile, "w+") as f: - f.write(text) + if text is not None: + f.write(text) + else: + f.write("") print("File contents:", open(tmpfile, "r").read()) return tmpfile From 5d75bc25c7297a086b81ac8b0963b0289ab9b0b0 Mon Sep 17 00:00:00 2001 From: Aaron Lichtman Date: Sat, 16 Nov 2019 14:49:07 +0100 Subject: [PATCH 19/33] Fix test? --- features/core.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/core.feature b/features/core.feature index bff53e2f..854fa53f 100644 --- a/features/core.feature +++ b/features/core.feature @@ -23,7 +23,7 @@ Feature: Basic reading and writing to a journal # Note that the expected output is actually "[Nothing saved to file]" but due to mocking limitations, we expect no output here. Scenario: Writing an empty entry from the editor Given we use the config "editor.yaml" - When we open the editor and enter nothing + When we open the editor and enter "" Then we should see the message "[Nothing saved to file]" Scenario: Filtering for dates From b1c7deb9b9fc8694372126f5cfe0840c53448cb9 Mon Sep 17 00:00:00 2001 From: Aaron Lichtman Date: Sat, 16 Nov 2019 14:52:19 +0100 Subject: [PATCH 20/33] Clean up debug statements --- features/steps/core.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/features/steps/core.py b/features/steps/core.py index 58d50e85..b48562e7 100644 --- a/features/steps/core.py +++ b/features/steps/core.py @@ -73,16 +73,13 @@ def open_editor_and_enter(context, text=""): text = (text or context.text) print("open_editor_and_enter called") def _mock_editor_function(command): - print("_mock_editor_function called") tmpfile = command[-1] - print("TMPFILE:", tmpfile) with open(tmpfile, "w+") as f: if text is not None: f.write(text) else: f.write("") - print("File contents:", open(tmpfile, "r").read()) return tmpfile with patch('subprocess.call', side_effect=_mock_editor_function): From 7b84935470cbbbb0436c84d03d69dd48a7643be9 Mon Sep 17 00:00:00 2001 From: Aaron Lichtman Date: Sat, 16 Nov 2019 14:53:07 +0100 Subject: [PATCH 21/33] Clean up debug statements --- jrnl/util.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/jrnl/util.py b/jrnl/util.py index 3b35c366..959d63a1 100644 --- a/jrnl/util.py +++ b/jrnl/util.py @@ -118,8 +118,6 @@ def get_text_from_editor(config, template=""): os.remove(tmpfile) if not raw: print('[Nothing saved to file]', file=sys.stderr) - else: - print("RAW: '" + raw + "'") return raw From 59a6aa3cfdf50cdc12c7e428926c6bf93d9b550f Mon Sep 17 00:00:00 2001 From: Aaron Lichtman Date: Sat, 16 Nov 2019 14:54:03 +0100 Subject: [PATCH 22/33] Remove extraneous code --- features/steps/core.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/features/steps/core.py b/features/steps/core.py index b48562e7..847cbeaf 100644 --- a/features/steps/core.py +++ b/features/steps/core.py @@ -202,11 +202,6 @@ def check_json_output_path(context, path, value): assert struct == value, struct -@then('the output should be empty') -def check_empty_output(context): - assert context.stdout_capture is None - - @then('the output should be') @then('the output should be "{text}"') def check_output(context, text=None): From 91dcb812d753dc07e12c24f23c64d5b3215c31a2 Mon Sep 17 00:00:00 2001 From: Aaron Lichtman Date: Sun, 17 Nov 2019 01:49:07 +0100 Subject: [PATCH 23/33] Remove extra space --- jrnl/cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jrnl/cli.py b/jrnl/cli.py index 32cde8dc..738087e4 100644 --- a/jrnl/cli.py +++ b/jrnl/cli.py @@ -176,7 +176,7 @@ def run(manual_args=None): log.debug('Using journal "%s"', journal_name) mode_compose, mode_export, mode_import = guess_mode(args, config) - + # How to quit writing? if "win32" in sys.platform: _exit_multiline_code = "on a blank line, press Ctrl+Z and then Enter" From 455261c55e125433daa32e7c8e259ad7bb810683 Mon Sep 17 00:00:00 2001 From: Aaron Lichtman Date: Sun, 17 Nov 2019 01:49:46 +0100 Subject: [PATCH 24/33] Add test for empty stdin input --- features/core.feature | 10 +++++++++- features/steps/core.py | 9 +++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/features/core.feature b/features/core.feature index 854fa53f..598b99f8 100644 --- a/features/core.feature +++ b/features/core.feature @@ -20,12 +20,20 @@ Feature: Basic reading and writing to a journal When we run "jrnl -n 1" Then the output should contain "2013-07-23 09:00 A cold and stormy day." - # Note that the expected output is actually "[Nothing saved to file]" but due to mocking limitations, we expect no output here. Scenario: Writing an empty entry from the editor Given we use the config "editor.yaml" When we open the editor and enter "" Then we should see the message "[Nothing saved to file]" + Scenario: Writing an empty entry from the command line + Given we use the config "basic.yaml" + When we run "jrnl ''" + Then we should get no error + And the unstripped output should be + """ + + """ + Scenario: Filtering for dates Given we use the config "basic.yaml" When we run "jrnl -on 2013-06-10 --short" diff --git a/features/steps/core.py b/features/steps/core.py index 847cbeaf..40b44f92 100644 --- a/features/steps/core.py +++ b/features/steps/core.py @@ -211,6 +211,15 @@ def check_output(context, text=None): for line_text, line_out in zip(text, out): assert line_text.strip() == line_out.strip(), [line_text.strip(), line_out.strip()] +@then('the unstripped output should be') +@then('the unstripped output should be "{text}"') +def check_output(context, text=None): + text = (text or context.text).splitlines() + out = context.stdout_capture.getvalue().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 == line_out, [line_text, line_out] + @then('the output should contain "{text}" in the local time') def check_output_time_inline(context, text): From fce60a364fdd16b17c404400b383282444a62d91 Mon Sep 17 00:00:00 2001 From: Aaron Lichtman Date: Sun, 17 Nov 2019 01:49:58 +0100 Subject: [PATCH 25/33] Remove extra debug line --- features/steps/core.py | 1 - 1 file changed, 1 deletion(-) diff --git a/features/steps/core.py b/features/steps/core.py index 40b44f92..b9d6c02c 100644 --- a/features/steps/core.py +++ b/features/steps/core.py @@ -71,7 +71,6 @@ def set_config(context, config_file): @when('we open the editor and enter "{text}"') def open_editor_and_enter(context, text=""): text = (text or context.text) - print("open_editor_and_enter called") def _mock_editor_function(command): tmpfile = command[-1] with open(tmpfile, "w+") as f: From 0aee900ffc66437061fc873f003ea96a3c720e58 Mon Sep 17 00:00:00 2001 From: Aaron Lichtman Date: Sun, 17 Nov 2019 01:54:49 +0100 Subject: [PATCH 26/33] Fix test? --- features/core.feature | 3 +-- features/steps/core.py | 9 --------- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/features/core.feature b/features/core.feature index 598b99f8..23058e12 100644 --- a/features/core.feature +++ b/features/core.feature @@ -29,9 +29,8 @@ Feature: Basic reading and writing to a journal Given we use the config "basic.yaml" When we run "jrnl ''" Then we should get no error - And the unstripped output should be + And the output should be """ - """ Scenario: Filtering for dates diff --git a/features/steps/core.py b/features/steps/core.py index b9d6c02c..2ec9e416 100644 --- a/features/steps/core.py +++ b/features/steps/core.py @@ -210,15 +210,6 @@ def check_output(context, text=None): for line_text, line_out in zip(text, out): assert line_text.strip() == line_out.strip(), [line_text.strip(), line_out.strip()] -@then('the unstripped output should be') -@then('the unstripped output should be "{text}"') -def check_output(context, text=None): - text = (text or context.text).splitlines() - out = context.stdout_capture.getvalue().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 == line_out, [line_text, line_out] - @then('the output should contain "{text}" in the local time') def check_output_time_inline(context, text): From b793ce5d6c25ae604cbb1762dbdc5bbb23777007 Mon Sep 17 00:00:00 2001 From: Aaron Lichtman Date: Sun, 17 Nov 2019 01:57:19 +0100 Subject: [PATCH 27/33] Fix test? --- features/core.feature | 1 + 1 file changed, 1 insertion(+) diff --git a/features/core.feature b/features/core.feature index 23058e12..f37e151b 100644 --- a/features/core.feature +++ b/features/core.feature @@ -31,6 +31,7 @@ Feature: Basic reading and writing to a journal Then we should get no error And the output should be """ + """ Scenario: Filtering for dates From e4012426ca8f3750a14d7c0bbf3159b61f28f6a2 Mon Sep 17 00:00:00 2001 From: Aaron Lichtman Date: Sun, 17 Nov 2019 02:07:06 +0100 Subject: [PATCH 28/33] Update features/core.feature Co-Authored-By: pspeter --- features/core.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/core.feature b/features/core.feature index f37e151b..50f40984 100644 --- a/features/core.feature +++ b/features/core.feature @@ -27,7 +27,7 @@ Feature: Basic reading and writing to a journal Scenario: Writing an empty entry from the command line Given we use the config "basic.yaml" - When we run "jrnl ''" + When we run "jrnl" and enter "" Then we should get no error And the output should be """ From 9701401946e6d75b953e580e782f7eb79e103253 Mon Sep 17 00:00:00 2001 From: Aaron Lichtman Date: Mon, 18 Nov 2019 14:51:41 +0100 Subject: [PATCH 29/33] Fix test? --- features/steps/core.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/features/steps/core.py b/features/steps/core.py index 2ec9e416..0e4e2a75 100644 --- a/features/steps/core.py +++ b/features/steps/core.py @@ -101,12 +101,18 @@ def _mock_input(inputs): @when('we run "{command}" and enter') +@when('we run "{command}" and enter ""') @when('we run "{command}" and enter "{inputs1}"') @when('we run "{command}" and enter "{inputs1}" and "{inputs2}"') def run_with_input(context, command, inputs1="", inputs2=""): # 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()' - text = iter((inputs1, inputs2)) if inputs1 else iter(context.text.split("\n")) + if inputs1: + text = iter((inputs1, inputs2)) + elif context.text: + text = iter(context.text.split("\n")) + else: + text = "" args = ushlex(command)[1:] with patch("builtins.input", side_effect=_mock_input(text)) as mock_input: with patch("jrnl.util.getpass", side_effect=_mock_getpass(text)) as mock_getpass: From 7fbb5db5e71c14d47d94d966b681eb8b221f78df Mon Sep 17 00:00:00 2001 From: Aaron Lichtman Date: Mon, 18 Nov 2019 15:34:31 +0100 Subject: [PATCH 30/33] Fix no stdin input test --- features/core.feature | 3 +-- features/steps/core.py | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/features/core.feature b/features/core.feature index 50f40984..36601fde 100644 --- a/features/core.feature +++ b/features/core.feature @@ -28,8 +28,7 @@ Feature: Basic reading and writing to a journal Scenario: Writing an empty entry from the command line Given we use the config "basic.yaml" When we run "jrnl" and enter "" - Then we should get no error - And the output should be + Then the output should be """ """ diff --git a/features/steps/core.py b/features/steps/core.py index 0e4e2a75..af4d19a3 100644 --- a/features/steps/core.py +++ b/features/steps/core.py @@ -112,7 +112,7 @@ def run_with_input(context, command, inputs1="", inputs2=""): elif context.text: text = iter(context.text.split("\n")) else: - text = "" + text = iter(("", "")) args = ushlex(command)[1:] with patch("builtins.input", side_effect=_mock_input(text)) as mock_input: with patch("jrnl.util.getpass", side_effect=_mock_getpass(text)) as mock_getpass: From d947b19bcd34027132514a17c5b461dcd0211c44 Mon Sep 17 00:00:00 2001 From: Micah Ellison <4383304+micahellison@users.noreply.github.com> Date: Mon, 18 Nov 2019 21:39:28 -0800 Subject: [PATCH 31/33] [GH-693] work so far on updating developer documentation --- CONTRIBUTING.md | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0de5982e..8b20e4bd 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,24 +1,28 @@ -Contributing -============ +# Contributing If you use jrnl, you can totally make our day by just saying "thanks for the code." It's your chance to make a programmer happy today! If you have a moment, let us know what you use jrnl for and how; it'll help us to make it even better! -Docs & Typos ------------- +# Table of Contents + * [Docs and Typos](#docs-and-typos) + * [Bugs](#bugs) + * [Feature requests and ideas](#feature-requests-and-ideas) + * [New programmers and programmers new to python](#new-programmers-and-programmers-new-to-python) + * [Developing jrnl](#developing-jrnl) + + +## Docs and Typos If you find a typo or a mistake in the docs, please fix it right away and send a pull request. The Right Way™ to fix the docs is to edit the `docs/*.md` files on the **master** branch. You can see the result if you run `make html` inside the project's root directory, which will open a browser that hot-reloads as you change the docs. This requires [mkdocs](https://www.mkdocs.org) to be installed. The `gh-pages` branch is automatically maintained and updates from `master`; you should never have to edit that. -Bugs ----- +## Bugs -Unfortunately, bugs happen. If you found one, please [open a new issue](https://github.com/jrnl-org/jrnl/issues/new) and describe it as well as possible. If you're a programmer with some time, go ahead and send us a pull request! We'll review as quickly as we can. +Unfortunately, bugs happen. If you found one, please [open a new issue](https://github.com/jrnl-org/jrnl/issues/new/choose) and describe it as well as possible. If you're a programmer with some time, go ahead and send us a pull request that references the issue! We'll review as quickly as we can. -Feature requests and ideas --------------------------- +## Feature requests and ideas -So, you have an idea for a great feature? Awesome! We'd love to hear from you! Please [open a new issue](https://github.com/jrnl-org/jrnl/issues) and describe the goal of the feature, and any relevant use cases. We'll discuss the issue with you, and decide if it's a good fit for the project. +So, you have an idea for a great feature? Awesome! We'd love to hear from you! Please [open a new issue](https://github.com/jrnl-org/jrnl/issues/new/choose) and describe the goal of the feature, and any relevant use cases. We'll discuss the issue with you, and decide if it's a good fit for the project. When discussing new features, please keep in mind our design goals. jrnl strives to do one thing well. To us, that means: @@ -27,9 +31,16 @@ When discussing new features, please keep in mind our design goals. jrnl strives * avoid duplicating functionality -A short note for new programmers and programmers new to python --------------------------------------------------------------- +## New programmers and programmers new to python -Although jrnl has grown quite a bit since its inception. The overall complexity (for an end-user program) is fairly low, and we hope you'll find the code easy enough to understand. +Although jrnl has grown quite a bit since its inception, the overall complexity (for an end-user program) is fairly low, and we hope you'll find the code easy enough to understand. If you have a question, please don't hesitate to ask! Python is known for its welcoming community and openness to novice programmers, so feel free to fork the code and play around with it! If you create something you want to share with us, please create a pull request. We never expect pull requests to be perfect, idiomatic, instantly mergeable code. We can work through it together! + +## Developing jrnl + +The jrnl source uses [poetry](https://poetry.eustace.io/) for dependency management. + + * To run the source: `python3 -m jrnl` + * To run tests: `make test` or `poetry run behave` if on Windows + * To build and install: `poetry install` then `poetry shell` From 13bdd94dfb2bc4c4cedc2614212d833784f4d5a1 Mon Sep 17 00:00:00 2001 From: Micah Ellison <4383304+micahellison@users.noreply.github.com> Date: Mon, 25 Nov 2019 20:22:44 -0800 Subject: [PATCH 32/33] [GH-693] cleaning up "Developing" section and adding a note about behave --- CONTRIBUTING.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8b20e4bd..414a7091 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -19,7 +19,6 @@ If you find a typo or a mistake in the docs, please fix it right away and send a Unfortunately, bugs happen. If you found one, please [open a new issue](https://github.com/jrnl-org/jrnl/issues/new/choose) and describe it as well as possible. If you're a programmer with some time, go ahead and send us a pull request that references the issue! We'll review as quickly as we can. - ## Feature requests and ideas So, you have an idea for a great feature? Awesome! We'd love to hear from you! Please [open a new issue](https://github.com/jrnl-org/jrnl/issues/new/choose) and describe the goal of the feature, and any relevant use cases. We'll discuss the issue with you, and decide if it's a good fit for the project. @@ -30,7 +29,6 @@ When discussing new features, please keep in mind our design goals. jrnl strives * have a simple interface * avoid duplicating functionality - ## New programmers and programmers new to python Although jrnl has grown quite a bit since its inception, the overall complexity (for an end-user program) is fairly low, and we hope you'll find the code easy enough to understand. @@ -39,8 +37,9 @@ If you have a question, please don't hesitate to ask! Python is known for its we ## Developing jrnl -The jrnl source uses [poetry](https://poetry.eustace.io/) for dependency management. +The jrnl source uses [poetry](https://poetry.eustace.io/) for dependency management. You will need to install it to develop journal. - * To run the source: `python3 -m jrnl` - * To run tests: `make test` or `poetry run behave` if on Windows - * To build and install: `poetry install` then `poetry shell` + * To run tests: `make test` (or `poetry run behave` if on Windows) + * To run the source: `poetry install` then `poetry shell` then run `jrnl` with or without arguments as necessary + +For testing, jrnl uses [behave](https://behave.readthedocs.io/). From a529b70614c54d8270252ca1d3f3b70c61718c87 Mon Sep 17 00:00:00 2001 From: Jonathan Wren Date: Mon, 25 Nov 2019 19:59:53 -0800 Subject: [PATCH 33/33] Squashed commit of the following: commit 75113187432939a51486422c3f70b3a9e2bcf0aa Merge: 74d1854 47e10fb Author: Jonathan Wren <9453067+wren@users.noreply.github.com> Date: Thu Oct 24 17:02:10 2019 -0700 Merge pull request #665 from notbalanced/issue_662 Fixes Issue #662 - Day names not treated consistently for new entry commit 74d1854a4bba468221b4eee254bdee2bb40f5d5a Merge: 1bbf074 6a5726a Author: Jonathan Wren <9453067+wren@users.noreply.github.com> Date: Sat Oct 5 15:30:57 2019 -0700 Merge pull request #418 from philipsd6/2.0-fancy_exporter Add exporter to output entries inside unicode box character boxes commit 47e10fbee746dba14f2550610b06301e7925a24f Author: Craig Moyer Date: Sun Sep 29 19:06:53 2019 -0400 Fix issue #662 to properly handle day names as new entry dates and command line (-on, -from, -to). commit 9588913100979b2a33ab87f60b5233f79c5bdf7d Author: Craig Moyer Date: Sun Sep 29 08:27:27 2019 -0400 Syncing with jrnl-org/master commit 4c68eb193d10db13fe9ec1321f75c89e6baf76f9 Merge: 81dfebb 1bbf074 Author: Craig Moyer Date: Sun Sep 29 07:52:02 2019 -0400 Merge remote-tracking branch 'upstream/master' into 2.0-rc1-maebert commit 81dfebb2c0ef4d4ebf16e8c29fdec5f4a9d9afb3 Author: Manuel Ebert Date: Mon Apr 29 20:34:18 2019 +0200 export changes commit 6a5726acd2102a715c2b1ec92190982ce66eaf07 Author: Philip Douglass Date: Fri Dec 22 20:56:36 2017 -0500 Enable FancyExporter plugin commit 3d1b22687131f5adf8c69769e4b6c79232337be8 Author: Philip Douglass Date: Fri Jan 29 11:17:41 2016 -0500 Add exporter to output entries inside unicode box character boxes --- features/regression.feature | 47 ++++++++++++++++++++++++++++ features/steps/core.py | 13 ++++++-- jrnl/Journal.py | 3 +- jrnl/plugins/__init__.py | 3 +- jrnl/plugins/fancy_exporter.py | 56 ++++++++++++++++++++++++++++++++++ jrnl/time.py | 14 ++++++--- 6 files changed, 126 insertions(+), 10 deletions(-) create mode 100644 jrnl/plugins/fancy_exporter.py diff --git a/features/regression.feature b/features/regression.feature index 3e644b19..fb4f0060 100644 --- a/features/regression.feature +++ b/features/regression.feature @@ -62,3 +62,50 @@ Feature: Zapped bugs should stay dead. Then the output should contain "I'm going to activate the machine." Then the output should contain "I've crossed so many timelines. Is there any going back?" + Scenario: Create entry using day of the week as entry date. + Given we use the config "basic.yaml" + When we run "jrnl monday: This is an entry on a Monday." + Then we should see the message "Entry added" + When we run "jrnl -1" + Then the output should contain "monday at 9am" in the local time + Then the output should contain "This is an entry on a Monday." + + Scenario: Create entry using day of the week abbreviations as entry date. + Given we use the config "basic.yaml" + When we run "jrnl fri: This is an entry on a Friday." + Then we should see the message "Entry added" + When we run "jrnl -1" + Then the output should contain "friday at 9am" in the local time + + Scenario: Displaying entries using -on today should display entries created today. + Given we use the config "basic.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." + + Scenario: Displaying entries using -from day should display correct entries + Given we use the config "basic.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." + Then the output should contain "A future entry." + Then the output should not contain "This thing happened yesterday" + + Scenario: Displaying entries using -from and -to day should display correct entries + Given we use the config "basic.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" + Then the output should contain "Adding an entry right now." + Then the output should not contain "A future entry." diff --git a/features/steps/core.py b/features/steps/core.py index af4d19a3..314a5167 100644 --- a/features/steps/core.py +++ b/features/steps/core.py @@ -5,6 +5,9 @@ from jrnl import cli, install, Journal, util, plugins from jrnl import __version__ from dateutil import parser as date_parser from collections import defaultdict +try: import parsedatetime.parsedatetime_consts as pdt +except ImportError: import parsedatetime as pdt +import time import os import json import yaml @@ -13,6 +16,10 @@ import tzlocal import shlex import sys +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""" @@ -221,9 +228,9 @@ def check_output(context, text=None): def check_output_time_inline(context, text): out = context.stdout_capture.getvalue() local_tz = tzlocal.get_localzone() - utc_time = date_parser.parse(text) - local_date = utc_time.astimezone(local_tz).strftime("%Y-%m-%d %H:%M") - assert local_date in out, local_date + 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') diff --git a/jrnl/Journal.py b/jrnl/Journal.py index f6813222..0fd41c22 100644 --- a/jrnl/Journal.py +++ b/jrnl/Journal.py @@ -122,7 +122,8 @@ class Journal: try: new_date = datetime.strptime(date_blob, self.config["timeformat"]) except ValueError: - new_date = time.parse(date_blob) + # Passing in a date that had brackets around it + new_date = time.parse(date_blob, bracketed=True) if new_date: if entries: diff --git a/jrnl/plugins/__init__.py b/jrnl/plugins/__init__.py index 8d59b556..53b595e3 100644 --- a/jrnl/plugins/__init__.py +++ b/jrnl/plugins/__init__.py @@ -9,8 +9,9 @@ from .tag_exporter import TagExporter from .xml_exporter import XMLExporter from .yaml_exporter import YAMLExporter from .template_exporter import __all__ as template_exporters +from .fancy_exporter import FancyExporter -__exporters =[JSONExporter, MarkdownExporter, TagExporter, TextExporter, XMLExporter, YAMLExporter] + template_exporters +__exporters =[JSONExporter, MarkdownExporter, TagExporter, TextExporter, XMLExporter, YAMLExporter, FancyExporter] + template_exporters __importers =[JRNLImporter] __exporter_types = {name: plugin for plugin in __exporters for name in plugin.names} diff --git a/jrnl/plugins/fancy_exporter.py b/jrnl/plugins/fancy_exporter.py new file mode 100644 index 00000000..74d4555f --- /dev/null +++ b/jrnl/plugins/fancy_exporter.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python +# encoding: utf-8 + +from __future__ import absolute_import, unicode_literals, print_function +from .text_exporter import TextExporter +from textwrap import TextWrapper + + +class FancyExporter(TextExporter): + """This Exporter can convert entries and journals into text with unicode box drawing characters.""" + names = ["fancy", "boxed"] + extension = "txt" + + border_a="┎" + border_b="─" + border_c="╮" + border_d="╘" + border_e="═" + border_f="╕" + border_g="┃" + border_h="│" + border_i="┠" + border_j="╌" + border_k="┤" + border_l="┖" + border_m="┘" + + @classmethod + def export_entry(cls, entry): + """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 + body_linewrap = linewrap - 2 + card = [cls.border_a + cls.border_b*(initial_linewrap) + cls.border_c + date_str] + 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) + cls.border_d + cls.border_e*(len(date_str)-1) + cls.border_f) + w.width = body_linewrap + if len(title_lines) > 1: + for line in w.wrap(' '.join([title_line[len(w.subsequent_indent):] + for title_line in title_lines[1:]])): + card.append(line.ljust(body_linewrap+1) + cls.border_h) + if entry.body: + card.append(cls.border_i + cls.border_j*body_linewrap + cls.border_k) + for line in entry.body.splitlines(): + body_lines = w.wrap(line) or [cls.border_g] + for body_line in body_lines: + card.append(body_line.ljust(body_linewrap+1) + cls.border_h) + card.append(cls.border_l + cls.border_b*body_linewrap + cls.border_m) + return "\n".join(card) + + @classmethod + def export_journal(cls, journal): + """Returns a unicode representation of an entire journal.""" + return "\n".join(cls.export_entry(entry) for entry in journal) diff --git a/jrnl/time.py b/jrnl/time.py index eee3fc20..66d3f4f8 100644 --- a/jrnl/time.py +++ b/jrnl/time.py @@ -12,15 +12,16 @@ consts.DOWParseStyle = -1 # "Monday" will be either today or the last Monday CALENDAR = pdt.Calendar(consts) -def parse(date_str, inclusive=False, default_hour=None, default_minute=None): +def parse(date_str, inclusive=False, default_hour=None, default_minute=None, bracketed=False): """Parses a string containing a fuzzy date and returns a datetime.datetime object""" if not date_str: return None elif isinstance(date_str, datetime): return date_str - # Don't try to parse anything with 6 or less characters. It's probably a markdown footnote - if len(date_str) <= 6: + # Don't try to parse anything with 6 or less characters and was parsed from the existing journal. + # It's probably a markdown footnote + if len(date_str) <= 6 and bracketed: return None default_date = DEFAULT_FUTURE if inclusive else DEFAULT_PAST @@ -51,8 +52,11 @@ def parse(date_str, inclusive=False, default_hour=None, default_minute=None): except TypeError: return None - if flag == 1: # Date found, but no time. Use the default time. - date = datetime(*date[:3], hour=default_hour or 0, minute=default_minute or 0) + if flag is 1: # Date found, but no time. Use the default time. + date = 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])