diff --git a/features/build.feature b/features/build.feature new file mode 100644 index 000000000..4725ea855 --- /dev/null +++ b/features/build.feature @@ -0,0 +1,8 @@ +Feature: Build process + + @deployment_tests + Scenario: Version numbers should stay in sync + Given we use the config "simple.yaml" + When we run "jrnl --version" + Then we should get no error + And the output should contain pyproject.toml version diff --git a/features/contains.feature b/features/contains.feature deleted file mode 100644 index eac3a464f..000000000 --- a/features/contains.feature +++ /dev/null @@ -1,29 +0,0 @@ -Feature: Contains - - Scenario: Searching for a string - Given we use the config "basic.yaml" - When we run "jrnl -contains life" - Then we should get no error - and the output should be - """ - 2013-06-10 15:40 Life is good. - | But I'm better. - """ - - Scenario: Searching for a string within tag results - Given we use the config "tags.yaml" - When we run "jrnl @idea -contains software" - Then we should get no error - And the output should contain "software" - - Scenario: Searching for a string within AND tag results - Given we use the config "tags.yaml" - When we run "jrnl -and @journal @idea -contains software" - Then we should get no error - and the output should contain "software" - - Scenario: Searching for a string within NOT tag results - Given we use the config "tags.yaml" - When we run "jrnl -not @dan -contains software" - Then we should get no error - and the output should contain "software" diff --git a/features/core.feature b/features/core.feature index b8a987ec8..dc78a7a39 100644 --- a/features/core.feature +++ b/features/core.feature @@ -1,201 +1,21 @@ -Feature: Basic reading and writing to a journal +Feature: Functionality of jrnl outside of actually handling journals - Scenario: Loading a sample journal - Given we use the config "basic.yaml" - When we run "jrnl -n 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: Printing a journal that has multiline entries - Given we use the config "multiline.yaml" - When we run "jrnl -n 1" - Then we should get no error - and the output should be - """ - 2013-06-09 15:39 Multiple line entry. - | This is the first line. - | This line doesn't have any ending punctuation - | - | There is a blank line above this. - """ - - Scenario: Multiline entry with punctuation - Given we use the config "basic.yaml" - When we run "jrnl This is. the title\\n This is the second line" - and we run "jrnl -n 1" - Then the output should contain "This is. the title" - - Scenario: Single line entry with punctuation - Given we use the config "basic.yaml" - When we run "jrnl This is. the title" - and we run "jrnl -n 1" - Then the output should contain "| the title" - - Scenario: Writing an entry from command line - Given we use the config "basic.yaml" - 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" - 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 enter nothing - Then we should see the message "[Nothing saved to file]" - - Scenario: Sending an argument with spaces to the editor should work - Given we use the config "editor-args.yaml" - When we open the editor and enter "lorem ipsum" - Then the editor should have been called with 5 arguments - And one editor argument should be "vim" - And one editor argument should be "-f" - And one editor argument should be "-c" - And one editor argument should match "'?setf markdown'?" - - Scenario: Writing an empty entry from the command line - Given we use the config "basic.yaml" - When we run "jrnl" and enter nothing - Then the output should be - """ - - """ - - Scenario: Filtering for dates - Given we use the config "basic.yaml" - When we run "jrnl -on 2013-06-10 --short" - Then the output should be "2013-06-10 15:40 Life is good." - When we run "jrnl -on 'june 6 2013' --short" - Then the output should be "2013-06-10 15:40 Life is good." - - Scenario: Emoji support - Given we use the config "basic.yaml" - When we run "jrnl 23 july 2013: 🌞 sunny day. Saw an 🐘" - Then we should see the message "Entry added" - When we run "jrnl -n 1" - Then the output should contain "🌞" - and the output should contain "🐘" - - Scenario: Writing an entry at the prompt - Given we use the config "basic.yaml" - When we run "jrnl" and enter "25 jul 2013: I saw Elvis. He's alive." + Scenario: Displaying the version number + Given we use the config "simple.yaml" + When we run "jrnl --version" Then we should get no error - and the journal should contain "[2013-07-25 09:00] I saw Elvis." - and the journal should contain "He's alive." + Then the output should match "^jrnl version v\d+\.\d+\.\d+(-(alpha|beta))?$" Scenario: Displaying the version number - Given we use the config "basic.yaml" + Given we use the config "simple.yaml" When we run "jrnl -v" Then we should get no error - Then the output should contain "version" - - Scenario: --short displays the short version of entries (only the title) - Given we use the config "basic.yaml" - When we run "jrnl -on 2013-06-10 --short" - Then the output should be "2013-06-10 15:40 Life is good." - - Scenario: -s displays the short version of entries (only the title) - Given we use the config "basic.yaml" - When we run "jrnl -on 2013-06-10 -s" - Then the output should be "2013-06-10 15:40 Life is good." - - Scenario: Invalid color configuration - Given we use the config "invalid_color.yaml" - When we run "jrnl -on 2013-06-10 -s" - Then the output should be - """ - 2013-06-10 15:40 Life is good. - """ - And we should get no error - - Scenario: Journal directory does not exist - Given we use the config "missing_directory.yaml" - When we run "jrnl Life is good" - and we run "jrnl -n 1" - Then the output should contain "Life is good" - - Scenario: Installation with relative journal and referencing from another folder - Given we use the config "missingconfig" - When we run "jrnl hello world" and enter - """ - test.txt - n - """ - and we change directory to "features" - and we run "jrnl -n 1" - Then the output should contain "hello world" + Then the output should match "^jrnl version v\d+\.\d+\.\d+(-(alpha|beta))?$" - Scenario: --diagnostic runs without exceptions + Scenario: Running the diagnostic command When we run "jrnl --diagnostic" Then the output should contain "jrnl" And the output should contain "Python" - Scenario: --import allows new entry from stdin - Given we use the config "basic.yaml" - When we run "jrnl --import" and pipe "[2020-07-05 15:00] Observe and import." - And we run "jrnl -1" - Then the journal should contain "[2020-07-05 15:00] Observe and import." - And the output should contain "Observe and import" - - Scenario: --import allows new large entry from stdin - Given we use the config "basic.yaml" - 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. - """ - And we run "jrnl -1" - Then the journal should contain "[2020-07-05 15:00] Observe and import." - And the output should contain "Observe and import" - And the output should contain "Lorem ipsum" - And the output should contain "end of entry." - - Scenario: --import allows multiple new entries from stdin - Given we use the config "basic.yaml" - 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 the journal should contain "[2020-07-05 15:00] Observe and import." - Then the journal should contain "[2020-07-05 15:01] Twice as nice." - - Scenario: --import allows import new entries from file - Given we use the config "basic.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 doesn't get confused with piping and file - Given we use the config "basic.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!" - + @todo + Scenario: Listing available journals diff --git a/features/custom_dates.feature b/features/custom_dates.feature deleted file mode 100644 index d3489648e..000000000 --- a/features/custom_dates.feature +++ /dev/null @@ -1,35 +0,0 @@ -Feature: Reading and writing to journal with custom date formats - - Scenario: Loading a sample journal - 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.06.2013 15:40 Life is good. - | But I'm better. - """ - - Scenario: Writing an entry from command line - Given we use the config "little_endian_dates.yaml" - When we run "jrnl 2013-07-12: A cold and stormy day. I ate crisps on the sofa." - Then we should see the message "Entry added" - When we run "jrnl -n 1" - Then the output should contain "12.07.2013 09:00 A cold and stormy day." - - Scenario: Filtering for dates - Given we use the config "little_endian_dates.yaml" - When we run "jrnl -on 2013-06-10 --short" - Then the output should be "10.06.2013 15:40 Life is good." - When we run "jrnl -on 'june 6 2013' --short" - Then the output should be "10.06.2013 15:40 Life is good." - - Scenario: Writing an entry at the prompt - 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." \ No newline at end of file diff --git a/features/data/configs/basic_dayone.yaml b/features/data/configs/basic_dayone.yaml new file mode 100644 index 000000000..0209f2f74 --- /dev/null +++ b/features/data/configs/basic_dayone.yaml @@ -0,0 +1,17 @@ +colors: + date: none + title: none + body: none + tags: none +default_hour: 9 +default_minute: 0 +editor: noop +encrypt: false +highlight: true +journals: + default: features/journals/basic_dayone.dayone +linewrap: 80 +tagsymbols: "@" +template: false +timeformat: "%Y-%m-%d %H:%M" +indent_character: "|" diff --git a/features/data/configs/basic_encrypted.yaml b/features/data/configs/basic_encrypted.yaml new file mode 100644 index 000000000..77f4e48d3 --- /dev/null +++ b/features/data/configs/basic_encrypted.yaml @@ -0,0 +1,17 @@ +colors: + date: none + title: none + body: none + tags: none +default_hour: 9 +default_minute: 0 +editor: noop +encrypt: true +highlight: true +journals: + default: features/journals/basic_encrypted.journal +linewrap: 80 +tagsymbols: "@" +template: false +timeformat: "%Y-%m-%d %H:%M" +indent_character: "|" diff --git a/features/data/configs/basic_folder.yaml b/features/data/configs/basic_folder.yaml new file mode 100644 index 000000000..ba0de638d --- /dev/null +++ b/features/data/configs/basic_folder.yaml @@ -0,0 +1,17 @@ +colors: + date: none + title: none + body: none + tags: none +default_hour: 9 +default_minute: 0 +editor: noop +encrypt: false +highlight: true +journals: + default: features/journals/basic_folder +linewrap: 80 +tagsymbols: "@" +template: false +timeformat: "%Y-%m-%d %H:%M" +indent_character: "|" diff --git a/features/data/configs/basic_onefile.yaml b/features/data/configs/basic_onefile.yaml new file mode 100644 index 000000000..fb48c6f84 --- /dev/null +++ b/features/data/configs/basic_onefile.yaml @@ -0,0 +1,17 @@ +colors: + date: none + title: none + body: none + tags: none +default_hour: 9 +default_minute: 0 +editor: noop +encrypt: false +highlight: true +journals: + default: features/journals/basic_onefile.journal +linewrap: 80 +tagsymbols: "@" +template: false +timeformat: "%Y-%m-%d %H:%M" +indent_character: "|" diff --git a/features/data/configs/dayone_empty.yaml b/features/data/configs/dayone_empty.yaml new file mode 100644 index 000000000..7750d3896 --- /dev/null +++ b/features/data/configs/dayone_empty.yaml @@ -0,0 +1,17 @@ +default_hour: 9 +default_minute: 0 +editor: noop +template: false +encrypt: false +highlight: true +journals: + default: features/journals/dayone_empty.dayone +linewrap: 80 +tagsymbols: '@' +timeformat: '%Y-%m-%d %H:%M' +indent_character: "|" +colors: + date: none + title: none + body: none + tags: none diff --git a/features/data/configs/editor_empty_folder.yaml b/features/data/configs/editor_empty_folder.yaml new file mode 100644 index 000000000..1724bbfb7 --- /dev/null +++ b/features/data/configs/editor_empty_folder.yaml @@ -0,0 +1,12 @@ +default_hour: 9 +default_minute: 0 +editor: 'vim' +template: false +encrypt: false +highlight: true +journals: + default: features/journals/empty_folder +linewrap: 80 +tagsymbols: '@' +timeformat: '%Y-%m-%d %H:%M' +indent_character: "|" diff --git a/features/data/configs/editor_encrypted.yaml b/features/data/configs/editor_encrypted.yaml new file mode 100644 index 000000000..75273c964 --- /dev/null +++ b/features/data/configs/editor_encrypted.yaml @@ -0,0 +1,17 @@ +colors: + body: green + date: blue + tags: none + title: yellow +default_hour: 9 +default_minute: 0 +editor: "vim" +encrypt: true +template: false +highlight: true +journals: + default: features/journals/encrypted.journal +linewrap: 80 +tagsymbols: '@' +timeformat: '%Y-%m-%d %H:%M' +indent_character: "|" diff --git a/features/data/configs/missing_directory.yaml b/features/data/configs/missing_directory.yaml new file mode 100644 index 000000000..d600404ca --- /dev/null +++ b/features/data/configs/missing_directory.yaml @@ -0,0 +1,17 @@ +default_hour: 9 +default_minute: 0 +editor: "" +encrypt: false +highlight: true +journals: + default: features/journals/missing_directory/simple.journal +linewrap: 80 +tagsymbols: "@" +template: false +timeformat: "%Y-%m-%d %H:%M" +indent_character: "|" +colors: + date: none + title: none + body: none + tags: none diff --git a/features/data/configs/missing_journal.yaml b/features/data/configs/missing_journal.yaml new file mode 100644 index 000000000..a1f6f8cf6 --- /dev/null +++ b/features/data/configs/missing_journal.yaml @@ -0,0 +1,17 @@ +default_hour: 9 +default_minute: 0 +editor: "" +encrypt: false +highlight: true +journals: + default: features/journals/missing.journal +linewrap: 80 +tagsymbols: "@" +template: false +timeformat: "%Y-%m-%d %H:%M" +indent_character: "|" +colors: + date: none + title: none + body: none + tags: none diff --git a/features/data/configs/basic.yaml b/features/data/configs/simple.yaml similarity index 100% rename from features/data/configs/basic.yaml rename to features/data/configs/simple.yaml diff --git a/features/data/journals/basic_dayone.dayone/entries/D04D335AFED711EABA18FAFFC2100C3D.doentry b/features/data/journals/basic_dayone.dayone/entries/D04D335AFED711EABA18FAFFC2100C3D.doentry new file mode 100644 index 000000000..9721dd55f --- /dev/null +++ b/features/data/journals/basic_dayone.dayone/entries/D04D335AFED711EABA18FAFFC2100C3D.doentry @@ -0,0 +1,53 @@ + + + + + Creation Date + 2020-08-29T18:11:00Z + Starred + + Entry Text + 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. + Time Zone + America/Los_Angeles + UUID + D04D335AFED711EABA18FAFFC2100C3D + Tags + + ipsum + tagone + tagtwo + + Creator + + Device Agent + + Generation Date + 2020-09-25T02:35:45Z + Host Name + iris.lan + OS Agent + Darwin/19.3.0 + Software Agent + jrnl/v2.4.5 + + + diff --git a/features/data/journals/basic_dayone.dayone/entries/FC8A86CAFED711EA8892FAFFC2100C3D.doentry b/features/data/journals/basic_dayone.dayone/entries/FC8A86CAFED711EA8892FAFFC2100C3D.doentry new file mode 100644 index 000000000..8c2f3c520 --- /dev/null +++ b/features/data/journals/basic_dayone.dayone/entries/FC8A86CAFED711EA8892FAFFC2100C3D.doentry @@ -0,0 +1,55 @@ + + + + + Creation Date + 2020-08-31T21:32:00Z + Starred + + Entry Text + 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. + Time Zone + America/Los_Angeles + UUID + FC8A86CAFED711EA8892FAFFC2100C3D + Tags + + tagtwo + + Creator + + Device Agent + + Generation Date + 2020-09-25T02:36:59Z + Host Name + iris.lan + OS Agent + Darwin/19.3.0 + Software Agent + jrnl/v2.4.5 + + + diff --git a/features/data/journals/basic_dayone.dayone/entries/FD8ABC8EFED711EABC35FAFFC2100C3D.doentry b/features/data/journals/basic_dayone.dayone/entries/FD8ABC8EFED711EABC35FAFFC2100C3D.doentry new file mode 100644 index 000000000..d998c36bd --- /dev/null +++ b/features/data/journals/basic_dayone.dayone/entries/FD8ABC8EFED711EABC35FAFFC2100C3D.doentry @@ -0,0 +1,44 @@ + + + + + Creation Date + 2020-09-24T16:14:00Z + Starred + + Entry Text + 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 + Time Zone + America/Los_Angeles + UUID + FD8ABC8EFED711EABC35FAFFC2100C3D + Tags + + tagthree + tagone + + Creator + + Device Agent + + Generation Date + 2020-09-25T02:37:01Z + Host Name + iris.lan + OS Agent + Darwin/19.3.0 + Software Agent + jrnl/v2.4.5 + + + diff --git a/features/data/journals/basic_encrypted.journal b/features/data/journals/basic_encrypted.journal new file mode 100644 index 000000000..ffc122dfe --- /dev/null +++ b/features/data/journals/basic_encrypted.journal @@ -0,0 +1 @@ +gAAAAABfb4gQBMqqGn_W8v_s7qCi14bX7inuCOKbsBqIUf7_ch14vTUp7lrysPFvhBp5vGijTwDIbk4LKoIISj8NwM31I8L0zEbMx9y6iyF_zseGGNxBvNN0wzAXa67bs-ohiQhhebcdIc_52sltxL2ELh8JAKUaXRwyapgnMgJ7z6deJppLK-B7RE7BiT0eKjWTDMd2x6cZDswvHs9opDp5yjuKWV5m7x6ggCKYgHT3savT9Tg7V0Fq6K3LGWaE59lCrqlAB0u6dnrDX3qcF4SKyckaniXzRShZGebdkUKDcLFun2V2syZwYQN772xjznIsJ16iXicox2uYKg8CnTefsyCwaOZyBvySGEy3CrlBiuIRIcxCtjKbYJ2B-Aq7LZitnBR7Ny_6_Wm8HsBf3N-cFCp4GShiCKrxuXKcOZ7vszG5EKb78JS85bb0mswU5CSdgp6UAHjIZqfJq00qQsViBCbXq3oklCPZXdQkOf5U0KpG2MVUiD-Zcn5Qj3gnUhSEr-5wKU9tWrE63MGPyE6KjZlArZX2W2LeGnW2CEYw9eREGon06AzLJ4mj3BgtjVWLIdGcCwORXvHRjUqazWgbEmXNVTbtp_cKnkW-rFzRBrUoVme9v-1Y3sH0VvHBq7QIj915VzBklzWs1qzIyTPZG5Db9LvdQ7SiV8slf1Jo7l-ayUUdVj6igvKZcgfB4RUHolJoMps5p4lZ5sPqv59KtSa8DCpuoRczIj71OCpuRVARZgy1m5sUD9xSMxOBdy46u1Jnry6iMtzXWI3mEZe5m7UhmW_L4Zcv4bbk8XjkBeHjPdgm2B69jkLmCBFecD5ztoGesCGt_pNo_sWSKqLHV1-coKFB2Nn__a4utU9NJNdeNRkr8_ahU6tn3jmaFjfQ7cKfrXG_NCcYBRX9fja8EQIeBEp_3TCoXQqhuV_bGsNPA2qL63Pt6YiRaUf1g9FNBqJRlKCSOYNixSXQZN_rTePzx0SQ0aIQhADWls62WX-LG5-byJcB6W2P_cH21hDOXkoNEIyLnCz9HQ6Yd6Fbv7298ps3F6jiUDdWES23zv8sDgBuKUN94qSN34j6MDYGFnGI9zsJ-Y-I2frdlLfWPx3pUL7afcKh1nRgXdjctsTSxU2BDrsu03eBz2IoZjoOR0U51IrNMOD1NNT3kctXxHLuOHSEkwAzS3doncQbdRLi5Gc1dQuOUa4sC-p8gVjUKXO-oi_49kp9Km2Ay9wFg0epBbXx2QMzyMsN2dXeSbHF-BDXD6sULaq5syC0fOHqaMLycTCMk2wLfNyXgEt05WvAiDn-LDsRdylMRW2hXp5HWq3Poaul-7VNg6UEMlwVfgJ-7hNreuO6IRtwmx6YdqMscw0ms6mU_MQZU_dTIPg3JU4KL0YyMqPBPSGNCx3gMp41O05Ubir45FoJSnT5Dkj4v3N0S87Ys3HuFLverASsGt9bkcSzd2uMKCJjkspemPPi9VhrY4IOO03DWSWbHmxYzFc1SJ-24WM8Ch404QKpe1qy5LNzFgLvDwQhSIHjluezHXqrD-DVh1lWNNY3WmHI2ubOZfaorvLKqzBPZ6AhpIa60rKjm0OZIQOmJwWXwkdnzut6m8PtoiLzRN897YMgeztf1nmDwp0xE-EhknVZ3WV3TeqgZJ5ykfHQ5BU8x0Db57-UtKSuesKbqPPdBe91OdsPpkGlyl6psHj1_gPm4nLvzXQePwiPaEemR_gYCWGPvl9l1ANJufgCV9qQTmZGof3fb9mjv-9lS-9l_m8KirPPRpSBToNeDtk50ceYUsOlDGzIyusppG9pOcIGyiln1IO5aZ8d4_1E83qjcHTSaKGizICZU7a-pt5STBPMesy3JgBm23A2jO4m68ayBRMcLnw_RirHvvBaj0C6UR2tac45F0Ob3PpXcvFuK0g54ziIAhzGqwF9I-LZ6asXQWMW4y4EBOak8JJBorkfztzfkMaIgGu-4ZoRKOkVfdr4uzcghk3r6KUxD4-nv1ioX69-G5RwhMHppYk7z8RXS1cq5FkvzXbfEQ-Uv6M-sx32DcUy9dH-ZYhc7UWm75JJfiNXLaXT_bsc6VqQ7KPkg2-RA7CywUFCW9S0S-XdO03VdwqlUVo7fp1SKywEfhZv_9bhDCdMJBwZmigv2KP9Iz7fF6LrpLwZkzHuQGFPcyTHFpsVIFrFyJjNYCXpET9y0Q5Vt4fnea5fy-9ZiCt3S8aS0YOFJ35_kM5i3ss8eFPL0v7fIQS3ZilzdGB3bWL0J7kppHN_ekHu-wVk3UZxauoFh7hXLjPcipua-FYUIklLjcK6DG1bYP7_q6OnkC8Jl650FNezeWPomHEv7l_DO3y0tjI6SGdWvL3ZJns7Xp3ew8KsCREAUO7ffqumD03uF9N-9uWbDDjM7rk0vcg0ggfOs9Ni725mxqYpu4R285XCOVWHDvw7iU6eAvE6ry8TDXQBbNgGjTuTYFYYli7GuOqMxFIe1op2s7sRnoJE8O0J76S6APhjhjcnZRSuONWkVG_5o83uFMPSF8DtqLwuRA5E8AGfIwAUcj324sw-DA0ixBGUqomb-osUIisv3x0b044xn-FvD-8R3PZDnPbPsao8XYNxfQWStrNcZSrX2Ua-WAcv9qbQ73_57RKW4pao4ajOu7K5800D231WGiIa6aJzDnFUlzXEzYxFQyx7qegkm_9rrEp_v8TC9mfAcjWX5DMrCkxUskx9YKDfpFYq4NuxO_414gReKzd-lmorfigvttgS10N1XD74SwFluXJv-bqTbI5-SuYAhDGMv1dqrn38i3rOMQqqnQomvaUJRprqxUsKz14sSE1Y-cNqq1FXzZ6vIJq-K3YTfFWPRLeqi6gHzqS_R2YBXXUduKuYgmakiVdP3bWc-Ca8WKh5sVi6P51MO-cS7i9AZWOaOz7F8PsB4JZxAJjSOr3NBmv3EEve9auTFCudRjfC6668I_NMHaTP5CCV4cuhuAxUuKUGgd6WFjDcvoYPyn_lu3bQiqD9MEag4CaJYI9PlraRv5mbqptwxv3pca7usd0GmXN_2No_nwxB4gVb48LsBBkH35njCa5iv2EKXUSOf0k3swaTSEahqbyI4EDzPXtU5uBO39iQzNpgfV_sUpnGdysjqueUVcdWGI_s5CnrNJ-_yDAY06AoXfLrjP8_3NXB2058xZ2rfmTNJNCULz9634dICJReXNnmplxIg3i6GbzFvjfNtqjrWr_iqBShyIwuOUJRbXzdJNggx2BDNG-PEWDXl89SaudFICkDvyZKEcATIss6ZXfULIMfCrqmWmFwgXfNEd9TuvjqoxFlLSaY4UfDMiYa_arUMblFfoo5nV07GANhUoQd-6HRe7LjYeX5VRodOx6ZmZjIAUq-DYr-hatJJFR2tjT_qZht2MJeYT3GZ3o54m8zBBt0JTN7HVpKaOaM3A2hEM_Ah0QZ-DkLDxtCzMuv987GDiLT2-Riya97a47yHIJhZFzFpflW2FcuC8RFWXlfUKTQfZkFmxh3MUekUuS4yu4Z121xojVswk_4P7-FqLaSnGT2epI69I_cvalRx3wjds9-5TFYqf4GridlFBRx6Fv2fpNB9Zvp9k7NQ9oYcPuXGLoXH5kmWBagPhEGKHA_pjFUZmCuwUIoeP4nP8lhFrX8OGezsbSBG773CRJzEdfcgAc5G-p6M_24WZLZHDrsVBAvgrNt6R9eQbEviWU28t_417QCp-or9qqt4OTKv1dp_4MlZh8YBg2-dtpvzSc1l5e4kQFJu7oWlpbgsjB6pl1oRRKp1maedX-gOAf559zC4l85gfEpPln9Cnl6xvERQzfO0Ey4q91SdsgK7i7FBrKKmi2wGiemFvnaQsrjZ_IFujLo8-2c8g9zTiyH1knyoVOAAnQxqGpsz6z6PNfSxr3_G8tOlNFTV-yqN_LdVHMgXtXjn3U9koGsfMulyUcBDdR3d_0Yn6iEjBt77tbxKi2ry-0gQrB1fdGsgKjyE_tMrW8D_lQz0IXsVOzd2ixsFVXMFzD6OOD8JldV0FbA-VDAS-Tp_ezIZVp6lRq54XBgvsjzDyOmOgDbSOQN6SQmvxPnIsml1wgmtm80z-9gHBqmimHBtLKB6L7CtLmmPICMS2pX3eWOmakxscxqs8AVjijJdz_NYNfcdBeDj_fhm6dqD6iwk3EBZZfsrmMGdXtAMqf1r9ng9tsz-FriXwQiJ3IM3loBsk5DKr9CcaJtKSPuwDDlRynD2vwcD-XyF6YTQdSJa9fEcq-qXya2Scj4mqQ4RDemJgErdradRfwJfII3fWHh18XxmYVqi9Bwn3YRgwEadyo0-HjbNq6vJXi12igmP99ciRAfMVQLjfUfTwoOHj44Y2Ru_hPjJcvB6FIn6KLrrCSrZnrshFdFn4L36z1CrS8fbtdvrG3kdZQxsUJnMqttuwKRpLnDWTWkIwj_GRBFrzCFgbwGp1XYhemxggyKVuhZPfyyTIM9rhlPth6eGyrpYfap24Av_mGPRBLnzcjtpGbACGdKQL034kVmI7yENGvmY40KSrWsVG_BE9bSJhx0EptFsT2IxnxbuFD4hGb4fFag9V0BDiKpUoOZqIVqVO8cAp-5w4twvWZKkrhu16JNlLoXWMoFANrw-tp5LKSin1CUeRa4LWVI1GR8tRkIad_GnCHRv9JEMswlNy9wi2sDNsSxWT7WNasUW5-glgK9pR7d2pXGGOWfHj1U6CKIqmAiO3iw8igzhvyx_dAxMxPo \ No newline at end of file diff --git a/features/data/journals/basic_folder/2020/08/29.txt b/features/data/journals/basic_folder/2020/08/29.txt new file mode 100644 index 000000000..c8af54caf --- /dev/null +++ b/features/data/journals/basic_folder/2020/08/29.txt @@ -0,0 +1,19 @@ +[2020-08-29 11:11:00 AM] 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. diff --git a/features/data/journals/basic_folder/2020/08/31.txt b/features/data/journals/basic_folder/2020/08/31.txt new file mode 100644 index 000000000..826e7cdb9 --- /dev/null +++ b/features/data/journals/basic_folder/2020/08/31.txt @@ -0,0 +1,23 @@ +[2020-08-31 02:32:00 PM] 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. diff --git a/features/data/journals/basic_folder/2020/09/24.txt b/features/data/journals/basic_folder/2020/09/24.txt new file mode 100644 index 000000000..2bd885ce7 --- /dev/null +++ b/features/data/journals/basic_folder/2020/09/24.txt @@ -0,0 +1,11 @@ +[2020-09-24 09:14:00 AM] 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 diff --git a/features/data/journals/basic_onefile.journal b/features/data/journals/basic_onefile.journal new file mode 100644 index 000000000..0d9880492 --- /dev/null +++ b/features/data/journals/basic_onefile.journal @@ -0,0 +1,58 @@ +[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 diff --git a/features/data/journals/dayone_empty.dayone/entries/empty.txt b/features/data/journals/dayone_empty.dayone/entries/empty.txt new file mode 100644 index 000000000..c86b8f66a --- /dev/null +++ b/features/data/journals/dayone_empty.dayone/entries/empty.txt @@ -0,0 +1 @@ +This file exists to preserve the directory structure, but should be ignored by jrnl. diff --git a/features/data/journals/little_endian_dates.journal b/features/data/journals/little_endian_dates.journal index a3c5c3019..d74929692 100644 --- a/features/data/journals/little_endian_dates.journal +++ b/features/data/journals/little_endian_dates.journal @@ -1,5 +1,5 @@ [09.06.2013 15:39] My first entry. Everything is alright -[10.06.2013 15:40] Life is good. +[10.07.2013 15:40] Life is good. But I'm better. diff --git a/features/datetime.feature b/features/datetime.feature new file mode 100644 index 000000000..8fe335c96 --- /dev/null +++ b/features/datetime.feature @@ -0,0 +1,155 @@ +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 ".yaml" + When we run "jrnl " + Then we should see the message "Entry added" + When we run "jrnl -n 1" + Then the output should contain "" + + 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 ".yaml" + When we run "jrnl -on '' --short" + Then the output should be "" + + 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 : This is an entry on a ." + Then we should see the message "Entry added" + When we run "jrnl -1" + Then the output should contain " at 9am" in the local time + And the output should contain "This is an entry on a ." + + 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 : This is an entry on a ." + Then we should see the message "Entry added" + When we run "jrnl -1" + Then the output should contain " 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. + """ diff --git a/features/dayone.feature b/features/dayone.feature deleted file mode 100644 index 39abae951..000000000 --- a/features/dayone.feature +++ /dev/null @@ -1,95 +0,0 @@ -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! - """ - - # broken still - @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'" - Then we should get no error - and the output should contain "2013-01-17T18:37Z" in the local time - - 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" - - Scenario: Writing into Dayone adds extended metadata - 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" - - Scenario: Editing Dayone with mock editor - Given we use the config "dayone.yaml" - When we run "jrnl --edit" - Then we should get no error - - Scenario: Editing Dayone entries - Given we use the config "dayone.yaml" - When we open the editor and append - """ - Here is the first line. - Here is the second line. - """ - When we run "jrnl -n 1" - Then we should get no error - and the output should contain "This entry is starred!" - and the output should contain "Here is the first line" - and the output should contain "Here is the second line" diff --git a/features/dayone_regressions.feature b/features/dayone_regressions.feature deleted file mode 100644 index 3e98f9e95..000000000 --- a/features/dayone_regressions.feature +++ /dev/null @@ -1,25 +0,0 @@ -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" - When we run "jrnl @plAy" - Then the output should contain - """ - 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."" - 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. - """ diff --git a/features/delete.feature b/features/delete.feature index d12fe8f87..2fc3f8f72 100644 --- a/features/delete.feature +++ b/features/delete.feature @@ -1,149 +1,229 @@ Feature: Delete entries from journal - Scenario: --delete flag allows deletion of single entry - Given we use the config "deletion.yaml" - When we run "jrnl -n 1" - Then the output should contain - """ - 2019-10-29 11:13 Third entry. - """ + Scenario Outline: Delete flag allows deletion of single entry + Given we use the 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 """ - When we run "jrnl -n 1" - Then the output should contain + Then we flush the output + When we run "jrnl -99 --short" + Then the output should be """ - 2019-10-29 11:11 Second entry. + 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. """ - Scenario: Backing out of interactive delete does not change journal - Given we use the config "deletion.yaml" + 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 ".yaml" When we run "jrnl --delete -n 1" and enter """ N """ - Then the journal should have 3 entries - And the journal should contain "[2019-10-29 11:11] First entry." - And the journal should contain "[2019-10-29 11:11] Second entry." - And the journal should contain "[2019-10-29 11:13] Third entry." + 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. + """ - Scenario: --delete flag with nonsense input deletes nothing (issue #932) - Given we use the config "deletion.yaml" + 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 ".yaml" When we run "jrnl --delete asdfasdf" - When we run "jrnl -n 1" - Then the output should contain + Then we flush the output + When we run "jrnl -99 --short" + Then the output should be """ - 2019-10-29 11:13 Third entry. + 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. """ - And the journal should have 3 entries - Scenario: --delete flag with tag only deletes tagged entries - Given we use the config "deletion_filters.yaml" - When we run "jrnl --delete @holidays" and enter + Examples: Configs + | config | + | basic_onefile | + | basic_folder | + | basic_dayone | + + Scenario Outline: Delete flag with tag only deletes tagged entries + Given we use the config ".yaml" + When we run "jrnl --delete @ipsum" and enter """ Y - Y """ - Then the journal should have 3 entries - Then the journal should contain "[2019-10-01 08:00] It's just another day in October." - and the journal should contain "[2020-03-01 08:00] It's just another day in March." - and the journal should contain "[2020-05-02 12:10] Writing tests." + 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: --delete flag with multiple tags deletes all entries matching any of the tags - Given we use the config "deletion_filters.yaml" - When we run "jrnl --delete @holidays @springtime" and enter + Scenario Outline: Delete flag with multiple tags deletes all entries matching any of the tags + Given we use the config ".yaml" + When we run "jrnl --delete @ipsum @tagthree" and enter """ Y Y - Y """ - Then the journal should contain "[2019-10-01 08:00] It's just another day in October." - and the journal should not contain "[2020-01-01 08:00] Happy New Year!" - and the journal should contain "[2020-03-01 08:00] It's just another day in March." - and the journal should not contain "[2020-05-01 09:00] Happy May Day!" - and the journal should not contain "[2020-05-02 12:10] Writing tests. *" - and the journal should have 2 entries + 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: --delete flag with -and and tags only deletes boolean AND of tagged entries - Given we use the config "deletion_filters.yaml" - When we run "jrnl --delete -and @holidays @springtime" and enter + Scenario Outline: Delete flag with -and deletes boolean AND of tagged entries + Given we use the config ".yaml" + When we run "jrnl --delete -and @tagone @tagtwo" and enter """ Y """ - Then the journal should contain "[2019-10-01 08:00] It's just another day in October." - and the journal should contain "[2020-01-01 08:00] Happy New Year!" - and the journal should contain "[2020-03-01 08:00] It's just another day in March." - and the journal should not contain "[2020-05-01 09:00] Happy May Day!" - and the journal should contain "[2020-05-02 12:10] Writing tests. *" - and the journal should have 4 entries + 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: --delete flag with -not does not delete entries with -not tag - Given we use the config "deletion_filters.yaml" - When we run "jrnl --delete @holidays -not @springtime" and enter + Scenario Outline: Delete flag with -not does not delete entries from given tag + Given we use the config ".yaml" + When we run "jrnl --delete @tagone -not @ipsum" and enter """ Y """ - Then the journal should contain "[2019-10-01 08:00] It's just another day in October." - and the journal should not contain "[2020-01-01 08:00] Happy New Year!" - and the journal should contain "[2020-03-01 08:00] It's just another day in March." - and the journal should contain "[2020-05-01 09:00] Happy May Day!" - and the journal should contain "[2020-05-02 12:10] Writing tests. *" - and the journal should have 4 entries + 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: --delete flag with -from only deletes entries since a specified date - Given we use the config "deletion_filters.yaml" - When we run "jrnl --delete -from 2020-01-02" and enter + Scenario Outline: Delete flag with -from search operator only deletes entries since that date + Given we use the config ".yaml" + When we run "jrnl --delete -from 2020-09-01" and enter """ Y - Y - Y """ - Then the journal should contain "[2019-10-01 08:00] It's just another day in October." - and the journal should contain "[2020-01-01 08:00] Happy New Year!" - and the journal should not contain "[2020-03-01 08:00] It's just another day in March." - and the journal should not contain "[2020-05-01 09:00] Happy May Day!" - and the journal should not contain "[2020-05-02 12:10] Writing tests." - and the journal should have 2 entries + 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: --delete flag with -to only deletes entries up to specified date - Given we use the config "deletion_filters.yaml" - When we run "jrnl --delete -to 2020-01-02" and enter + Scenario Outline: Delete flag with -to only deletes entries up to specified date + Given we use the config ".yaml" + When we run "jrnl --delete -to 2020-08-31" and enter """ Y Y """ - Then the journal should not contain "[2019-10-01 08:00] It's just another day in October." - and the journal should not contain "[2020-01-01 08:00] Happy New Year!" - and the journal should contain "[2020-03-01 08:00] It's just another day in March." - and the journal should contain "[2020-05-01 09:00] Happy May Day!" - and the journal should contain "[2020-05-02 12:10] Writing tests." - and the journal should have 3 entries + 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. + """ - Scenario: --delete flag with -starred only deletes starred entries - Given we use the config "deletion_filters.yaml" + 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 ".yaml" When we run "jrnl --delete -starred" and enter """ Y """ - Then the journal should contain "[2019-10-01 08:00] It's just another day in October." - and the journal should contain "[2020-01-01 08:00] Happy New Year!" - and the journal should contain "[2020-03-01 08:00] It's just another day in March." - and the journal should contain "[2020-05-01 09:00] Happy May Day!" - and the journal should not contain "[2020-05-02 12:10] Writing tests. *" - and the journal should have 4 entries + 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: --delete flag with -contains only entries containing expression - Given we use the config "deletion_filters.yaml" - When we run "jrnl --delete -contains happy" and enter + Scenario Outline: Delete flag with -contains only entries containing expression + Given we use the config ".yaml" + When we run "jrnl --delete -contains dignissim" and enter """ Y - Y """ - Then the journal should contain "[2019-10-01 08:00] It's just another day in October." - and the journal should not contain "[2020-01-01 08:00] Happy New Year!" - and the journal should contain "[2020-03-01 08:00] It's just another day in March." - and the journal should not contain "[2020-05-01 09:00] Happy May Day!" - and the journal should contain "[2020-05-02 12:10] Writing tests. *" - and the journal should have 3 entries + 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 + diff --git a/features/encrypt.feature b/features/encrypt.feature new file mode 100644 index 000000000..f2d0a62f4 --- /dev/null +++ b/features/encrypt.feature @@ -0,0 +1,35 @@ +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" + diff --git a/features/encryption.feature b/features/encryption.feature deleted file mode 100644 index 02f8b4232..000000000 --- a/features/encryption.feature +++ /dev/null @@ -1,81 +0,0 @@ - Feature: Encrypted journals - Scenario: Loading an encrypted journal - Given we use the config "encrypted.yaml" - When we run "jrnl -n 1" and enter "bad doggie no biscuit" - Then the output should contain "Password" - And the output should contain "2013-06-10 15:40 Life is good" - - 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" - Then we should see the message "Journal decrypted" - And the journal should have 2 entries - - Scenario: Encrypting a journal - Given we use the config "basic.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 the output should contain "Password" - And the output should contain "2013-06-10 15:40 Life is good" - - Scenario: Mistyping your password - Given we use the config "basic.yaml" - When we run "jrnl --encrypt" and enter - """ - swordfish - sordfish - swordfish - swordfish - n - """ - Then 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" - Then the output should contain "Password" - And the output should contain "2013-06-10 15:40 Life is good" - - Scenario: Storing a password in Keychain - Given we use the config "multiple.yaml" - When we run "jrnl simple --encrypt" and enter - """ - sabertooth - sabertooth - y - """ - When we set the keychain password of "simple" to "sabertooth" - Then the config for journal "simple" should have "encrypt" set to "bool: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 "basic.yaml" - When we disable the keychain - and we run "jrnl test entry" - and we run "jrnl --encrypt" and enter - """ - password - password - n - """ - Then we should get no error - - Scenario: Encrypt journal with no keyring backend and do store in keyring - Given we use the config "basic.yaml" - When we disable the keychain - and we run "jrnl test entry" - and we run "jrnl --encrypt" and enter - """ - password - password - y - """ - Then we should get no error diff --git a/features/environment.py b/features/environment.py index 63fec4a64..71ed89690 100644 --- a/features/environment.py +++ b/features/environment.py @@ -5,8 +5,30 @@ 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): @@ -17,7 +39,7 @@ def before_feature(context, feature): # add "skip" tag # https://stackoverflow.com/a/42721605/4276230 if "skip" in feature.tags: - feature.skip("Marked with @skip") + feature.skip() return if "skip_win" in feature.tags and on_windows: @@ -44,7 +66,7 @@ def before_scenario(context, scenario): # add "skip" tag # https://stackoverflow.com/a/42721605/4276230 if "skip" in scenario.effective_tags: - scenario.skip("Marked with @skip") + scenario.skip() return if "skip_win" in scenario.effective_tags and on_windows: @@ -56,4 +78,9 @@ def after_scenario(context, scenario): """After each scenario, restore all test data and remove working_dirs.""" if os.getcwd() != CWD: os.chdir(CWD) - clean_all_working_dirs() + + # 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() diff --git a/features/exporting.feature b/features/exporting.feature deleted file mode 100644 index 96a9558f1..000000000 --- a/features/exporting.feature +++ /dev/null @@ -1,147 +0,0 @@ -Feature: Exporting a Journal - - Scenario: Exporting to json - Given we use the config "tags.yaml" - When we run "jrnl --export 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 "@idea" - And "tags" in the json output should contain "@journal" - And "tags" in the json output should contain "@dan" - And entry 1 should have an array "tags" with 2 elements - And entry 2 should have an array "tags" with 2 elements - - Scenario: Exporting using filters should only export parts of the journal - Given we use the config "tags.yaml" - When we run "jrnl -until 'may 2013' --export json" - Then the output should be parsable as json - And "entries" in the json output should have 1 element - And "tags" in the json output should contain "@idea" - And "tags" in the json output should contain "@journal" - And "tags" in the json output should not contain "@dan" - - Scenario: Exporting using custom templates - Given we use the config "basic.yaml" - Given we load template "sample.template" - When we run "jrnl --export sample" - Then the output should be - """ - My first entry. - --------------- - - Everything is alright - - Life is good. - ------------- - - But I'm better. - """ - - Scenario: Increasing Headings on Markdown export - Given we use the config "markdown-headings-335.yaml" - When we run "jrnl --export markdown" - Then the output should be - """ - # 2015 - - ## April - - ### 2015-04-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 - """ - - Scenario: Exporting to XML - Given we use the config "tags.yaml" - 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: Exporting tags - Given we use the config "tags.yaml" - When we run "jrnl --export tags" - Then the output should be - """ - @idea : 2 - @journal : 1 - @dan : 1 - """ - - Scenario: Exporting fancy - Given we use the config "tags.yaml" - When we run "jrnl --export fancy" - Then the output should be - """ - ┎──────────────────────────────────────────────────────────────╮2013-04-09 15:39 - ┃ I have an @idea: ╘═══════════════╕ - ┠╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ - ┃ (1) write a command line @journal software │ - ┃ (2) ??? │ - ┃ (3) PROFIT! │ - ┖──────────────────────────────────────────────────────────────────────────────┘ - ┎──────────────────────────────────────────────────────────────╮2013-06-10 15:40 - ┃ I met with @dan. ╘═══════════════╕ - ┠╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ - ┃ As alway's he shared his latest @idea on how to rule the world with me. │ - ┃ inst │ - ┖──────────────────────────────────────────────────────────────────────────────┘ - """ - - Scenario: Export to yaml - Given we use the config "tags.yaml" - And we create cache directory "exported_journal" - When we run "jrnl --export yaml -o {cache_dir}" with cache directory "exported_journal" - Then cache directory "exported_journal" should contain the files - """ - [ - "2013-04-09_i-have-an-idea.md", - "2013-06-10_i-met-with-dan.md" - ] - """ - And the content of file "2013-04-09_i-have-an-idea.md" in cache directory "exported_journal" should be - """ - title: I have an @idea: - date: 2013-04-09 15:39 - starred: False - tags: idea, journal - - (1) write a command line @journal software - (2) ??? - (3) PROFIT! - """ diff --git a/features/file_storage.feature b/features/file_storage.feature new file mode 100644 index 000000000..0e2c0b3ca --- /dev/null +++ b/features/file_storage.feature @@ -0,0 +1,46 @@ +Feature: Journals iteracting with the file system in a way that users can see + + Scenario: Adding entries to a Folder journal should generate date files + 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" + + 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" + + 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" + 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" + 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" + And we run "jrnl -n 1" + Then the output should contain "hello world" diff --git a/features/folder.feature b/features/folder.feature deleted file mode 100644 index 650ac53e8..000000000 --- a/features/folder.feature +++ /dev/null @@ -1,42 +0,0 @@ -Feature: Testing a journal with a root directory and multiple files in the format of yyyy/mm/dd.txt - - Scenario: Opening an folder that's not a DayOne folder should treat as folder journal - 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 we run "jrnl -1" - Then the output should be - """ - 2013-07-23 09:00 Testing folder journal. - """ - - Scenario: Adding entries to a Folder journal should generate date files - 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" - - - 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" - - 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. - """ diff --git a/features/format.feature b/features/format.feature new file mode 100644 index 000000000..e689a9f35 --- /dev/null +++ b/features/format.feature @@ -0,0 +1,514 @@ +Feature: Custom formats + + Scenario Outline: JSON format + Given we use the 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 ".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 ".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 ".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 ".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 ".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 ".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 ".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 ".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 | + + Scenario Outline: Export to yaml + Given we use the 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 ".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 + + 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 ".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 ".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 | diff --git a/features/import.feature b/features/import.feature new file mode 100644 index 000000000..63b042fcd --- /dev/null +++ b/features/import.feature @@ -0,0 +1,93 @@ +Feature: Importing data + + Scenario Outline: --import allows new entry from stdin + Given we use the 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 ".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 ".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!" + diff --git a/features/multiple_journals.feature b/features/multiple_journals.feature index 0f41b0d94..6a9efe783 100644 --- a/features/multiple_journals.feature +++ b/features/multiple_journals.feature @@ -3,19 +3,19 @@ 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 + 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 + 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 + And journal "work" should have 1 entry Scenario: Tell user which journal was used Given we use the config "multiple.yaml" @@ -26,8 +26,8 @@ Feature: Multiple journals 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" + 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" @@ -48,4 +48,4 @@ Feature: Multiple journals these three eyes n """ - Then we should see the message "Encrypted journal 'new_encrypted' created" + Then we should see the message "Encrypted journal 'new_encrypted' created" diff --git a/features/password.feature b/features/password.feature new file mode 100644 index 000000000..86fa7f6bf --- /dev/null +++ b/features/password.feature @@ -0,0 +1,87 @@ +Feature: Using the installed keyring + + Scenario: Storing a password in 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" + 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 + + 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 + # @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: Open encrypted journal when keyring exists but fails + # 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 + + @todo + Scenario: Decrypt journal when keyring exists but fails + # This should ask the user for the password after the keyring fails + + 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 + + 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" + Then we should be prompted for a password + And the output should contain "2013-06-10 15:40 Life is good" diff --git a/features/regression.feature b/features/regression.feature deleted file mode 100644 index 5342b746b..000000000 --- a/features/regression.feature +++ /dev/null @@ -1,175 +0,0 @@ -Feature: Zapped bugs should stay dead. - - Scenario: Writing an entry does not print the entire journal - # https://github.com/jrnl-org/jrnl/issues/87 - Given we use the config "basic.yaml" - 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" - When we run "jrnl -n 1" - Then the output should not contain "Life is good" - - Scenario: Date with time should be parsed correctly - # https://github.com/jrnl-org/jrnl/issues/117 - Given we use the config "basic.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: Loading entry with ambiguous time stamp - #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. - """ - - Scenario: Date in the future should be parsed correctly - # https://github.com/jrnl-org/jrnl/issues/185 - Given we use the config "basic.yaml" - When we run "jrnl 26/06/2019: Planet? Earth. Year? 2019." - Then we should see the message "Entry added" - and the journal should contain "[2019-06-26 09:00] Planet?" - - 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: Title with an embedded period. - Given we use the config "basic.yaml" - 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. - """ - - 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" - - Scenario: Journals with unreadable dates should still be viewable - Given we use the config "unreadabledates.yaml" - When we run "jrnl -2" - Then the output should contain "I've lost track of time." - Then 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." - When we run "jrnl -1" - 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: Viewing today's entries does not print the entire journal - # https://github.com/jrnl-org/jrnl/issues/741 - Given we use the config "basic.yaml" - When we run "jrnl -on today" - Then the output should not contain "Life is good" - Then the output should not contain "But I'm better." - - 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." - - # See issues #768 and #881 - # the "deletion" journal is used because it doesn't have a newline at the - # end of the last entry - Scenario: Add a blank line to Markdown export is there isn't one already - Given we use the config "deletion.yaml" - When we run "jrnl --export markdown" - Then the output should be - """ - # 2019 - - ## October - - ### 2019-10-29 11:11 First entry. - - - ### 2019-10-29 11:11 Second entry. - - - ### 2019-10-29 11:13 Third entry. - - """ - - # See issues #768 and #881 - Scenario: Add a blank line to YAML export is there isn't one already - Given we use the config "deletion.yaml" - And we create cache directory "bug768" - When we run "jrnl --export yaml -o {cache_dir}" with cache directory "bug768" - Then cache directory "bug768" should contain the files - """ - [ - "2019-10-29_first-entry.md", - "2019-10-29_second-entry.md", - "2019-10-29_third-entry.md" - ] - """ - And the content of file "2019-10-29_third-entry.md" in cache directory "bug768" should be - """ - title: Third entry. - date: 2019-10-29 11:13 - starred: False - tags: - - """ - - @deployment_tests - Scenario: Version numbers should stay in sync - Given we use the config "basic.yaml" - When we run "jrnl --version" - Then we should get no error - Then the output should contain pyproject.toml version diff --git a/features/search.feature b/features/search.feature new file mode 100644 index 000000000..9d31bb063 --- /dev/null +++ b/features/search.feature @@ -0,0 +1,228 @@ +Feature: Searching in a journal + + Scenario Outline: Displaying entries using -on today should display entries created today + Given we use the 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 ".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 ".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 ".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 ".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 ".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 ".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 ".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 ".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 ".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 ".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 ".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! + """ diff --git a/features/star.feature b/features/star.feature new file mode 100644 index 000000000..f01880564 --- /dev/null +++ b/features/star.feature @@ -0,0 +1,35 @@ +Feature: Starring entries + + Scenario Outline: Starring an entry will mark it in the journal file + Given we use the config ".yaml" + 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" + Then the output should contain "2013-07-20 09:00 Best day of my life!" + + Examples: configs + | config_file | + | simple | + | empty_folder | + | dayone | + + Scenario Outline: Filtering by starred entries will show only starred entries + Given we use the config ".yaml" + When we run "jrnl -starred" + Then the output should be empty + When we run "jrnl 20 july 2013 *: Best day of my life!" + When we run "jrnl -starred" + Then the output should be "2013-07-20 09:00 Best day of my life!" + + Examples: configs + | config_file | + | simple | + | empty_folder | + | dayone_empty | + + Scenario: Starring an entry will mark it in an encrypted journal + Given we use the config "encrypted.yaml" + When we run "jrnl 20 july 2013 *: Best day of my life!" and enter "bad doggie no biscuit" + Then we should see the message "Entry added" + When we run "jrnl -on 2013-07-20 -starred" and enter "bad doggie no biscuit" + Then the output should contain "2013-07-20 09:00 Best day of my life!" diff --git a/features/starring.feature b/features/starring.feature deleted file mode 100644 index 18113dc32..000000000 --- a/features/starring.feature +++ /dev/null @@ -1,20 +0,0 @@ -Feature: Starring entries - - Scenario: Starring an entry will mark it in the journal file - Given we use the config "basic.yaml" - When we run "jrnl 20 july 2013 *: Best day of my life!" - Then we should see the message "Entry added" - and the journal should contain "[2013-07-20 09:00] Best day of my life! *" - - Scenario: Filtering by starred entries - Given we use the config "basic.yaml" - When we run "jrnl -starred" - Then the output should be - """ - """ - When we run "jrnl 20 july 2013 *: Best day of my life!" - When we run "jrnl -starred" - Then the output should be - """ - 2013-07-20 09:00 Best day of my life! - """ diff --git a/features/steps/core.py b/features/steps/core.py index f82cf8cd0..8b49cbb27 100644 --- a/features/steps/core.py +++ b/features/steps/core.py @@ -64,7 +64,26 @@ def delete_password(self, servicename, username): raise keyring.errors.NoKeyringError -# set the keyring for keyring lib +class FailedKeyring(keyring.backend.KeyringBackend): + """ + A keyring that simulates an environment with a keyring that has passwords, but fails + to return them. + """ + + priority = 2 + keys = defaultdict(dict) + + def set_password(self, servicename, username, password): + self.keys[servicename][username] = password + + def get_password(self, servicename, username): + raise keyring.errors.NoKeyringError + + def delete_password(self, servicename, username): + self.keys[servicename][username] = None + + +# set a default keyring keyring.set_keyring(TestKeyring()) @@ -93,16 +112,48 @@ def open_journal(journal_name="default"): return Journal.open_journal(journal_name, config) +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) + install.CONFIG_FILE_PATH = os.path.abspath(full_path) - if config_file.endswith("yaml"): + if config_file.endswith("yaml") and os.path.exists(full_path): # Add jrnl version to file for 2.x journals with open(install.CONFIG_FILE_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") +def set_keyring(context): + 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) @@ -122,7 +173,7 @@ def open_editor_and_enter(context, method, text=""): else: file_method = "r+" - def _mock_editor_function(command): + def _mock_editor(command): context.editor_command = command tmpfile = command[-1] with open(tmpfile, file_method) as f: @@ -130,19 +181,36 @@ def _mock_editor_function(command): 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_function), \ + 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) \ : + context.editor = mock_editor + context.getpass = mock_getpass cli(["--edit"]) # 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): - assert len(context.editor_command) == int(num) +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}"') @@ -156,25 +224,32 @@ def contains_editor_arg(context, arg): @then('one editor argument should match "{regex}"') def matches_editor_arg(context, regex): args = context.editor_command - matches = list(filter(lambda x: re.match(regex, x), args)) + 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)}" def _mock_getpass(inputs): - def prompt_return(prompt="Password: "): - print(prompt) - return next(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=""): - val = next(inputs) - print(prompt, val) - return val + try: + val = next(inputs) + print(prompt, val) + return val + except StopIteration: + raise KeyboardInterrupt return prompt_return @@ -192,12 +267,24 @@ def run_with_input(context, command, inputs=""): args = ushlex(command)[1:] + def _mock_editor(command): + context.editor_command = command + tmpfile = command[-1] + context.editor_file = tmpfile + 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(text)) as mock_getpass, \ - patch("sys.stdin.read", side_effect=text) as mock_read \ + 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 \ : try: cli(args or []) @@ -205,26 +292,56 @@ def run_with_input(context, command, inputs=""): except SystemExit as e: context.exit_status = e.code - # at least one of the mocked input methods got called - assert mock_input.called or mock_getpass.called or mock_read.called - # all inputs were used - try: - next(text) - assert False, "Not all inputs were consumed" - except StopIteration: - pass + # 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}"') -@when('we run "{command}" with cache directory "{cache_dir}"') -def run(context, command, text="", cache_dir=None): +def run(context, command, text=""): text = text or context.text or "" - if cache_dir is not None: - cache_dir = os.path.join("features", "cache", cache_dir) + 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) @@ -232,12 +349,23 @@ def run(context, command, text="", cache_dir=None): def _mock_editor(command): context.editor_command = command + if "password" in context: + password = context.password + else: + password = iter(text) + try: - with patch("sys.argv", args), patch( - "subprocess.call", side_effect=_mock_editor - ), patch("sys.stdin.read", side_effect=lambda: text): + # fmt: off + # see: https://github.com/psf/black/issues/664 + with \ + patch("sys.argv", args), \ + patch("getpass.getpass", side_effect=_mock_getpass(password)), \ + patch("subprocess.call", side_effect=_mock_editor), \ + patch("sys.stdin.read", side_effect=lambda: text) \ + : cli(args[1:]) context.exit_status = 0 + # fmt: on except SystemExit as e: context.exit_status = e.code @@ -249,16 +377,11 @@ def load_template(context, filename): plugins.__exporter_types[exporter.names[0]] = exporter -@when('we set the keychain password of "{journal}" to "{password}"') -def set_keychain(context, journal, password): +@when('we set the keyring password of "{journal}" to "{password}"') +def set_keyring_password(context, journal, password): keyring.set_password("jrnl", journal, password) -@when("we disable the keychain") -def disable_keychain(context): - keyring.core.set_keyring(NoKeyring()) - - @then("we should get an error") def has_error(context): assert context.exit_status != 0, context.exit_status @@ -269,10 +392,33 @@ 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).strip().splitlines() + 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) @@ -316,7 +462,27 @@ def check_output_inline(context, text=None, text2=None): def check_error_output_inline(context, text=None, text2=None): text = text or context.text out = context.stderr_capture.getvalue() - assert text in out or text2 in out, text or text2 + 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}"') @@ -326,6 +492,7 @@ def check_output_not_inline(context, text): @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] @@ -351,33 +518,48 @@ def check_not_journal_content(context, text, journal_name="default"): 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"): - with open(install.CONFIG_FILE_PATH) as config_file: - config = yaml.load(config_file, Loader=yaml.FullLoader) + config = load_config(install.CONFIG_FILE_PATH) + journal_path = config["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"): + config = load_config(install.CONFIG_FILE_PATH) + + journal_path = config["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): - if not value[0] == "{": - t, value = value.split(":") - value = {"bool": lambda v: v.lower() == "true", "int": int, "str": str}[t]( - value - ) - else: - # Handle value being a dictionary - value = ast.literal_eval(value) - +def config_var(context, key, value="", journal=None): + value = read_value_from_string(value or context.text or "") config = load_config(install.CONFIG_FILE_PATH) + if journal: config = config["journals"][journal] + assert key in config assert config[key] == value +@then('the config for journal "{journal}" should not have "{key}" set') +def config_no_var(context, key, value="", journal=None): + config = load_config(install.CONFIG_FILE_PATH) + + if journal: + config = config["journals"][journal] + + assert key not in config + + @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') @@ -397,21 +579,6 @@ def list_journal_directory(context, journal="default"): print(os.path.join(root, file)) -@then("the Python version warning should appear if our version is below {version}") -def check_python_warning_if_version_low_enough(context, version): - import platform - - import packaging.version - - if packaging.version.parse(platform.python_version()) < packaging.version.parse( - version - ): - out = context.stderr_capture.getvalue() - assert "WARNING: Python versions" in out - else: - assert True - - @then("fail") def debug_fail(context): assert False diff --git a/features/steps/export_steps.py b/features/steps/export_steps.py index d75aea64e..9003c1bda 100644 --- a/features/steps/export_steps.py +++ b/features/steps/export_steps.py @@ -1,6 +1,8 @@ import json import os import shutil +import random +import string from xml.etree import ElementTree from behave import given @@ -116,23 +118,32 @@ def assert_xml_output_tags(context, expected_tags_json_list): @given('we create cache directory "{dir_name}"') -def create_directory(context, 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 directory "{dir_name}" should contain the files') -@then( - 'cache directory "{dir_name}" should contain the files {expected_files_json_list}' -) -def assert_dir_contains_files(context, dir_name, expected_files_json_list="[]"): +@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 = json.loads(expected_files) + expected_files = expected_files.split("\n") # sort to deal with inconsistent default file ordering on different OS's actual_files.sort() @@ -142,7 +153,11 @@ def assert_dir_contains_files(context, dir_name, expected_files_json_list="[]"): @then('the content of file "{file_path}" in cache directory "{cache_dir}" should be') -def assert_exported_yaml_file_content(context, file_path, cache_dir): +@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) diff --git a/features/tag.feature b/features/tag.feature new file mode 100644 index 000000000..b7b687b53 --- /dev/null +++ b/features/tag.feature @@ -0,0 +1,53 @@ +Feature: Tagging +# See search.feature for tag-related searches +# And format.feature for tag-related output + + Scenario Outline: Tags should allow certain special characters such as /, +, # + Given we use the config ".yaml" + 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 | + + Scenario Outline: Emails addresses should not be parsed as tags + Given we use the config ".yaml" + 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 | + + Scenario Outline: Entry can start and end with tags + Given we use the config ".yaml" + 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 | diff --git a/features/tagging.feature b/features/tagging.feature deleted file mode 100644 index c96273c5c..000000000 --- a/features/tagging.feature +++ /dev/null @@ -1,121 +0,0 @@ -Feature: Tagging - - Scenario: Displaying tags - Given we use the config "tags.yaml" - When we run "jrnl --tags" - Then we should get no error - and the output should be - """ - @idea : 2 - @journal : 1 - @dan : 1 - """ - - Scenario: Filtering journals should also filter tags - Given we use the config "tags.yaml" - When we run "jrnl -from 'may 2013' --tags" - Then we should get no error - and the output should be - """ - @idea : 1 - @dan : 1 - """ - - Scenario: Tags should allow certain special characters - Given we use the config "tags-216.yaml" - When we run "jrnl --tags" - Then we should get no error - and the output should be - """ - @os/2 : 1 - @c++ : 1 - @c# : 1 - """ - Scenario: An email should not be a tag - Given we use the config "tags-237.yaml" - When we run "jrnl --tags" - Then we should get no error - and the output should be - """ - @newline : 1 - @email : 1 - """ - - Scenario: Entry cans start and end with tags - Given we use the config "basic.yaml" - When we run "jrnl today: @foo came over, we went to a @bar" - When we run "jrnl --tags" - Then the output should be - """ - @foo : 1 - @bar : 1 - """ - - Scenario: Excluding a tag should filter it - Given we use the config "basic.yaml" - When we run "jrnl today: @foo came over, we went to a bar" - When we run "jrnl I have decided I did not enjoy that @bar" - When we run "jrnl --tags -not @bar" - Then the output should be - """ - @foo : 1 - """ - - Scenario: Excluding a tag should filter an entry, even if an unfiltered tag is in that entry - Given we use the config "basic.yaml" - When we run "jrnl today: I do @not think this will show up @thought" - When we run "jrnl today: I think this will show up @thought" - When we run "jrnl --tags -not @not" - Then the output should be - """ - @thought : 1 - """ - - Scenario: Excluding multiple tags should filter them - Given we use the config "basic.yaml" - When we run "jrnl today: I do @not think this will show up @thought" - When we run "jrnl today: I think this will show up @thought" - When we run "jrnl today: This should @never show up @thought" - When we run "jrnl today: What a nice day for filtering @thought" - When we run "jrnl --tags -not @not -not @never" - Then the output should be - """ - @thought : 2 - """ - - - Scenario: Printing a journal that has multiline entries with tags - Given we use the config "multiline-tags.yaml" - When we run "jrnl -n 1" - Then we should get no error - and the output should be - """ - 2013-06-09 15:39 Multiple @line entry with @tags. - | Tag with @punctuation. afterwards - | @TagOnLineAloneWithOutPunctuation - | @TagOnLineAloneWithPunctuation. - | Text before @tag. And After. - | @hi. Hello - | hi Hello - """ - - Scenario: Searching a journal for tags should display entries with that tag. - Given we use the config "tags.yaml" - When we run "jrnl @dan" - Then the output should be - """ - 2013-06-10 15:40 I met with @dan. - | As alway's he shared his latest @idea on how to rule the world with me. - | inst - """ - - Scenario: Searching a journal for multiple tags with -and should display entries with those tags. - Given we use the config "tags.yaml" - When we run "jrnl -and @idea @journal" - Then the output should be - """ - 2013-04-09 15:39 I have an @idea: - | (1) write a command line @journal software - | (2) ??? - | (3) PROFIT! - """ diff --git a/features/upgrade.feature b/features/upgrade.feature index b0a54c160..fda47363b 100644 --- a/features/upgrade.feature +++ b/features/upgrade.feature @@ -3,57 +3,69 @@ 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 output should contain - """ - 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". - """ Then the journal should have 2 entries + 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 the output should contain "Password" - and the output should contain "2013-06-10 15:40 Life is good" + """ + 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 have "colors" set to + """ + { + '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 output should contain - """ - 10.06.2010 15:00 A life without chocolate is like a bad analogy. - - 10.06.2013 15:40 He said "[this] is the best time to be alive". - """ Then the journal should have 2 entries + And 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 - """ + """" + 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 - """ - Y - bad doggie no biscuit - """ + """ + 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 we should get no error diff --git a/features/write.feature b/features/write.feature new file mode 100644 index 000000000..1e6a2d09b --- /dev/null +++ b/features/write.feature @@ -0,0 +1,182 @@ +Feature: Writing new entries. + + Scenario Outline: Multiline entry with punctuation should keep title punctuation + Given we use the config ".yaml" + 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" + Then the output should contain "This is. the title" + + Examples: configs + | config_file | + | simple | + | empty_folder | + | dayone | + | encrypted | + + Scenario Outline: Single line entry with period should be split at period + Given we use the config ".yaml" + And we use the password "test" if prompted + When we run "jrnl This is. the title" + And we run "jrnl -1" + Then the output should contain "| the title" + + Examples: configs + | config_file | + | basic_onefile | + | basic_encrypted | + | basic_folder | + | basic_dayone | + + Scenario Outline: Writing an entry from command line should store the entry + Given we use the config ".yaml" + 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" + When we run "jrnl -n 1" + Then the output should contain "2013-07-23 09:00 A cold and stormy day." + + Examples: configs + | config_file | + | simple | + | empty_folder | + | dayone | + | encrypted | + + Scenario Outline: Writing an empty entry from the editor should yield "Nothing saved to file" message + Given we use the config ".yaml" + And we use the password "test" if prompted + When we open the editor and enter nothing + Then the error output should contain "[Nothing saved to file]" + + Examples: configs + | config_file | + | editor | + | editor_empty_folder | + | dayone | + | basic_encrypted | + | basic_onefile | + + @skip_win # @todo This fails only on Travis Windows + Scenario Outline: Writing an empty entry from the command line with no editor should yield nothing + Given we use the config ".yaml" + And we use the password "bad doggie no biscuit" if prompted + When we run "jrnl" and enter nothing + Then the output should be empty + And the error output should contain "Writing Entry; on a blank line" + And the editor should not have been called + + Examples: configs + | config_file | + | simple | + | empty_folder | + | encrypted | + # | 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 ".yaml" + 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" + When we run "jrnl -n 1" + Then the output should not contain "Life is good" + + Examples: configs + | config_file | + | editor | + | editor_empty_folder | + | dayone | + | encrypted | + + Scenario Outline: Embedded period stays in title + Given we use the config ".yaml" + 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 | + + Scenario Outline: Write and read emoji support + Given we use the config ".yaml" + 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" + When we run "jrnl -n 1" + Then the output should contain "🌞" + And the output should contain "🐘" + + Examples: configs + | config_file | + | simple | + | empty_folder | + | dayone | + | encrypted | + + Scenario Outline: Writing an entry at the prompt (no editor) should store the entry + Given we use the config ".yaml" + 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 + When we run "jrnl -on '2013-07-25'" + Then the output should contain "2013-07-25 09:00 I saw Elvis." + And the output should contain "| He's alive." + + Examples: configs + | config_file | + | simple | + | empty_folder | + | encrypted | + + @todo + Scenario: Writing an entry at the prompt (no editor) in DayOne journal + # Need to test DayOne w/out an editor + + 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: Writing into Dayone adds extended metadata + 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" + + # 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."" + 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 should treat as folder journal + 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 we run "jrnl -1" + Then the output should be "2013-07-23 09:00 Testing folder journal."